diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 8dfaf58fc..555604310 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -50,7 +50,7 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} docker-image-name: ${{ vars.DOCKER_IMAGE_NAME }} - update-proof-of-concept: + update-ds-infrastructure-web: runs-on: ubuntu-latest needs: - build @@ -59,7 +59,7 @@ jobs: steps: - uses: actions/checkout@v4 with: - repository: nationalarchives/ds-etna-deployments-proof-of-concept + repository: nationalarchives/ds-infrastructure-web ref: main token: ${{ secrets.ACTIONS_GITHUB_TOKEN }} - name: Set up git config @@ -69,10 +69,10 @@ jobs: - name: Install jq run: sudo apt-get install jq - name: Update config - run: jq --indent 4 '(.services.cms.version|="${{ needs.version.outputs.version }}")' config-aws-develop.json > tmp.$$.json && mv tmp.$$.json config-aws-develop.json + run: jq --indent 4 '(.services.cms.version|="${{ needs.version.outputs.version }}")' config/develop.json > tmp.$$.json && mv tmp.$$.json config/develop.json - name: Push new version run: | - git add config-aws-develop.json + git add config/develop.json git commit -m "Update cms to v${{ needs.version.outputs.version }}" git push origin main diff --git a/.platform.app.yaml b/.platform.app.yaml index 1395d0ab3..c4e588bec 100644 --- a/.platform.app.yaml +++ b/.platform.app.yaml @@ -9,6 +9,10 @@ name: app # to find the supported versions for the 'python' type. type: 'python:3.12' +dependencies: + python3: + poetry: '1.8.1' + variables: env: DJANGO_SETTINGS_MODULE: 'config.settings.platform' @@ -18,7 +22,7 @@ variables: POETRY_VERSION: 1.8.1 # The size of the persistent disk of the application (in MB). -disk: 4096 +disk: 4608 # The relationships of the application with services or other applications. # @@ -37,15 +41,6 @@ hooks: # Download the latest version of pip python3.12 -m pip install --upgrade pip - # Install and configure Poetry - # Set user to false to install Poetry globally - export PIP_USER=false - curl -sSL https://install.python-poetry.org | python3 - --version $POETRY_VERSION - # Update PATH to make Poetry available in this hook - export PATH="/app/.local/bin:$PATH" - # Set user to true to install dependencies only in the virtual environment - export PIP_USER=true - # Install dependencies poetry install --only main @@ -63,7 +58,6 @@ hooks: poetry run python manage.py collectstatic --no-input deploy: | - poetry run python manage.py rename_app authors people poetry run python manage.py migrate web: diff --git a/.platform/routes.yaml b/.platform/routes.yaml index e1b9c29b3..415dd0a9e 100644 --- a/.platform/routes.yaml +++ b/.platform/routes.yaml @@ -4,7 +4,7 @@ type: upstream upstream: "app:http" cache: - enabled: false + enabled: true default_ttl: 300 cookies: - sessionid diff --git a/config/settings/base.py b/config/settings/base.py index 0aadcaee3..7ce429e55 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -50,6 +50,7 @@ "etna.analytics", "etna.articles", "etna.people", + "etna.cookies", "etna.categories", "etna.ciim", "etna.collections", @@ -80,7 +81,7 @@ "wagtailmedia", "wagtail.contrib.settings", "generic_chooser", - "wagtailmetadata", + "wagtailmetadata", # TODO: Remove this package when we reset migrations and remove the dependency from the pyproject.toml "modelcluster", "taggit", "django.contrib.admin", @@ -119,6 +120,8 @@ ROOT_URLCONF = "config.urls" +ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "").split(",") + TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", diff --git a/config/settings/dev.py b/config/settings/dev.py index 49e704e9d..d65003910 100644 --- a/config/settings/dev.py +++ b/config/settings/dev.py @@ -29,10 +29,9 @@ } # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = "@6gce61jt^(pyj5+l**&*_#zyxfj5v1*71cs5yoetg-!fsz826" +SECRET_KEY = "abc123" -# SECURITY WARNING: define the correct hosts in production! -ALLOWED_HOSTS = ["*"] +ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS", "*").split(",") EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" diff --git a/config/settings/production.py b/config/settings/production.py index f37e5f630..76e654e80 100644 --- a/config/settings/production.py +++ b/config/settings/production.py @@ -3,19 +3,10 @@ from .base import * # noqa: F401 # TODO: Temporary until the static files can be served via S3 or a CDN -DEBUG = True +# DEBUG = True # TODO: Ensure that certificates are always checked by the Client API in production # CLIENT_VERIFY_CERTIFICATES = True -# TODO: Generate SECRET_KEY SECRET_KEY = os.getenv("SECRET_KEY", "") # Need to get the IP of the load balancer or reverse proxy -ALLOWED_HOSTS = ["*"] -STATICFILES_STORAGE = "django.contrib.staticfiles.storage.ManifestStaticFilesStorage" -STATIC_ROOT = "/app/static" - -try: - from .local import * # noqa: F401 -except ImportError: - pass diff --git a/config/settings/test.py b/config/settings/test.py index 1775dd950..61fcb0977 100644 --- a/config/settings/test.py +++ b/config/settings/test.py @@ -6,7 +6,7 @@ pass # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = "@6gce61jt^(pyj5+l**&*_#zyxfj5v1*71cs5yoetg-!fsz826" +SECRET_KEY = "abc123" ALLOWED_HOSTS = ["localhost", "127.0.0.1"] diff --git a/config/settings/util.py b/config/settings/util/__init__.py similarity index 100% rename from config/settings/util.py rename to config/settings/util/__init__.py diff --git a/etna/api/tests/expected_results/article.json b/etna/api/tests/expected_results/article.json index f01dff5ac..839a371b4 100644 --- a/etna/api/tests/expected_results/article.json +++ b/etna/api/tests/expected_results/article.json @@ -21,46 +21,48 @@ }, "privacy": "public", "last_published_at": "2000-01-01T00:00:00Z", - "url": "/article_index/article/" + "url": "/article_index/article/", + "teaser_text": "Teaser text", + "teaser_image": { + "id": 12, + "title": "An image", + "jpeg": { + "url": "/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_Qqqrhoo.jpg", + "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_Qqqrhoo.jpg", + "width": 100, + "height": 68 + }, + "webp": { + "url": "/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_EW7Ow5o.webp", + "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_EW7Ow5o.webp", + "width": 100, + "height": 68 + } + }, + "teaser_image_square": { + "id": 12, + "title": "An image", + "jpeg": { + "url": "/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_3mqgkw3.jpg", + "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_3mqgkw3.jpg", + "width": 100, + "height": 100 + }, + "webp": { + "url": "/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iIlAHIz.webp", + "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iIlAHIz.webp", + "width": 100, + "height": 100 + } + }, + "search_image": null, + "twitter_og_title": null, + "twitter_og_description": null, + "twitter_og_image": null }, "title": "article", "global_alert": null, "type_label": "The story of", - "teaser_text": "Teaser text", - "teaser_image": { - "id": 12, - "title": "An image", - "jpeg": { - "url": "/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_HJzpFFv.jpg", - "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_HJzpFFv.jpg", - "width": 100, - "height": 68 - }, - "webp": { - "url": "/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_q1PKELE.webp", - "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_q1PKELE.webp", - "width": 100, - "height": 68 - } - }, - "teaser_image_square": { - "id": 12, - "title": "An image", - "jpeg": { - "url": "/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_dN8zZYf.jpg", - "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_dN8zZYf.jpg", - "width": 100, - "height": 100 - }, - "webp": { - "url": "/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_8zcBeAq.webp", - "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_8zcBeAq.webp", - "width": 100, - "height": 100 - } - }, - "facebook_og_image": null, - "twitter_og_image": null, "mourning_notice": { "title": "Test title", "message": "
Test message
" diff --git a/etna/api/tests/expected_results/article_index.json b/etna/api/tests/expected_results/article_index.json index a1914cc3c..ff9531b0e 100644 --- a/etna/api/tests/expected_results/article_index.json +++ b/etna/api/tests/expected_results/article_index.json @@ -21,46 +21,48 @@ }, "privacy": "public", "last_published_at": "2000-01-01T00:00:00Z", - "url": "/article_index/" + "url": "/article_index/", + "teaser_text": "Teaser text", + "teaser_image": { + "id": 11, + "title": "An image", + "jpeg": { + "url": "/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_Qqqrhoo.jpg", + "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_Qqqrhoo.jpg", + "width": 100, + "height": 68 + }, + "webp": { + "url": "/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_EW7Ow5o.webp", + "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_EW7Ow5o.webp", + "width": 100, + "height": 68 + } + }, + "teaser_image_square": { + "id": 11, + "title": "An image", + "jpeg": { + "url": "/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_3mqgkw3.jpg", + "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_3mqgkw3.jpg", + "width": 100, + "height": 100 + }, + "webp": { + "url": "/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iIlAHIz.webp", + "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iIlAHIz.webp", + "width": 100, + "height": 100 + } + }, + "search_image": null, + "twitter_og_title": null, + "twitter_og_description": null, + "twitter_og_image": null }, "title": "article_index", "global_alert": null, "type_label": "Article index", - "teaser_text": "Teaser text", - "teaser_image": { - "id": 11, - "title": "An image", - "jpeg": { - "url": "/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_4ShM0BL.jpg", - "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_4ShM0BL.jpg", - "width": 100, - "height": 68 - }, - "webp": { - "url": "/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_7hLyRgB.webp", - "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_7hLyRgB.webp", - "width": 100, - "height": 68 - } - }, - "teaser_image_square": { - "id": 11, - "title": "An image", - "jpeg": { - "url": "/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_faOjnDM.jpg", - "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_faOjnDM.jpg", - "width": 100, - "height": 100 - }, - "webp": { - "url": "/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_ppQbvU2.webp", - "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_ppQbvU2.webp", - "width": 100, - "height": 100 - } - }, - "facebook_og_image": null, - "twitter_og_image": null, "mourning_notice": { "title": "Test title", "message": "Test message
" diff --git a/etna/api/tests/expected_results/arts.json b/etna/api/tests/expected_results/arts.json index d3e86c63a..dd90029ab 100644 --- a/etna/api/tests/expected_results/arts.json +++ b/etna/api/tests/expected_results/arts.json @@ -21,7 +21,44 @@ }, "privacy": "public", "last_published_at": "2000-01-01T00:00:00Z", - "url": "/arts/" + "url": "/arts/", + "teaser_text": "Teaser text", + "teaser_image": { + "id": 2, + "title": "An image", + "jpeg": { + "url": "/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_Qqqrhoo.jpg", + "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_Qqqrhoo.jpg", + "width": 100, + "height": 68 + }, + "webp": { + "url": "/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_EW7Ow5o.webp", + "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_EW7Ow5o.webp", + "width": 100, + "height": 68 + } + }, + "teaser_image_square": { + "id": 2, + "title": "An image", + "jpeg": { + "url": "/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_3mqgkw3.jpg", + "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_3mqgkw3.jpg", + "width": 100, + "height": 100 + }, + "webp": { + "url": "/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iIlAHIz.webp", + "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iIlAHIz.webp", + "width": 100, + "height": 100 + } + }, + "search_image": null, + "twitter_og_title": null, + "twitter_og_description": null, + "twitter_og_image": null }, "title": "arts", "hero_image_caption": "Hero image caption
", @@ -69,41 +106,6 @@ }, "global_alert": null, "type_label": "Topic", - "teaser_text": "Teaser text", - "teaser_image": { - "id": 2, - "title": "An image", - "jpeg": { - "url": "/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_Qqqrhoo.jpg", - "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_Qqqrhoo.jpg", - "width": 100, - "height": 68 - }, - "webp": { - "url": "/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_EW7Ow5o.webp", - "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_EW7Ow5o.webp", - "width": 100, - "height": 68 - } - }, - "teaser_image_square": { - "id": 2, - "title": "An image", - "jpeg": { - "url": "/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_3mqgkw3.jpg", - "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_3mqgkw3.jpg", - "width": 100, - "height": 100 - }, - "webp": { - "url": "/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iIlAHIz.webp", - "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iIlAHIz.webp", - "width": 100, - "height": 100 - } - }, - "facebook_og_image": null, - "twitter_og_image": null, "mourning_notice": { "title": "Test title", "message": "Test message
" diff --git a/etna/api/tests/expected_results/author.json b/etna/api/tests/expected_results/author.json index c8f8509c1..3b8ec30d3 100644 --- a/etna/api/tests/expected_results/author.json +++ b/etna/api/tests/expected_results/author.json @@ -21,7 +21,44 @@ }, "privacy": "public", "last_published_at": "2000-01-01T00:00:00Z", - "url": "/people/author/" + "url": "/people/author/", + "teaser_text": "Teaser text", + "teaser_image": { + "id": 10, + "title": "An image", + "jpeg": { + "url": "/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_Qqqrhoo.jpg", + "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_Qqqrhoo.jpg", + "width": 100, + "height": 68 + }, + "webp": { + "url": "/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_EW7Ow5o.webp", + "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_EW7Ow5o.webp", + "width": 100, + "height": 68 + } + }, + "teaser_image_square": { + "id": 10, + "title": "An image", + "jpeg": { + "url": "/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_3mqgkw3.jpg", + "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_3mqgkw3.jpg", + "width": 100, + "height": 100 + }, + "webp": { + "url": "/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iIlAHIz.webp", + "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iIlAHIz.webp", + "width": 100, + "height": 100 + } + }, + "search_image": null, + "twitter_og_title": null, + "twitter_og_description": null, + "twitter_og_image": null }, "title": "author", "global_alert": { @@ -32,47 +69,53 @@ "uid": ALERT_UID }, "type_label": "Person", - "teaser_text": "Teaser text", - "teaser_image": { - "id": 10, + "mourning_notice": { + "title": "Test title", + "message": "Test message
" + }, + "first_name": "John", + "last_name": "Smith", + "role": "Test Author", + "role_tags": [ + { + "slug": "author", + "name": "Author" + } + ], + "image": { + "id": 9, "title": "An image", "jpeg": { - "url": "/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_bcFMPn8.jpg", - "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_bcFMPn8.jpg", + "url": "/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_CJj7ytu.jpg", + "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_CJj7ytu.jpg", "width": 100, - "height": 68 + "height": 100 }, "webp": { - "url": "/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_ljNBufq.webp", - "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_ljNBufq.webp", + "url": "/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_nJ9965Y.webp", + "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_nJ9965Y.webp", "width": 100, - "height": 68 + "height": 100 } }, - "teaser_image_square": { - "id": 10, + "image_small": { + "id": 9, "title": "An image", "jpeg": { - "url": "/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_h0sQhSU.jpg", - "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_h0sQhSU.jpg", + "url": "/media/images/example.2e16d0ba.fill-128x128.format-jpeg.jpegquality-60_3tl8s6s.jpg", + "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-128x128.format-jpeg.jpegquality-60_3tl8s6s.jpg", "width": 100, "height": 100 }, "webp": { - "url": "/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_g9tHw4i.webp", - "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_g9tHw4i.webp", + "url": "/media/images/exampl.2e16d0ba.fill-128x128.format-webp.webpquality-60_WHn2XPR.webp", + "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-128x128.format-webp.webpquality-60_WHn2XPR.webp", "width": 100, "height": 100 } }, - "facebook_og_image": null, - "twitter_og_image": null, - "mourning_notice": { - "title": "Test title", - "message": "Test message
" - }, - "role": "Test Author", "summary": "Summary text
", + "research_summary": [], "authored_focused_articles": [ { "id": FOCUSED_ID, @@ -100,36 +143,5 @@ "is_newly_published": true } ], - "image": { - "id": 9, - "title": "An image", - "jpeg": { - "url": "/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_CJj7ytu.jpg", - "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_CJj7ytu.jpg", - "width": 100, - "height": 100 - }, - "webp": { - "url": "/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_nJ9965Y.webp", - "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_nJ9965Y.webp", - "width": 100, - "height": 100 - } - }, - "image_small": { - "id": 9, - "title": "An image", - "jpeg": { - "url": "/media/images/example.2e16d0ba.fill-128x128.format-jpeg.jpegquality-60_3tl8s6s.jpg", - "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-128x128.format-jpeg.jpegquality-60_3tl8s6s.jpg", - "width": 100, - "height": 100 - }, - "webp": { - "url": "/media/images/exampl.2e16d0ba.fill-128x128.format-webp.webpquality-60_WHn2XPR.webp", - "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-128x128.format-webp.webpquality-60_WHn2XPR.webp", - "width": 100, - "height": 100 - } - } + "shop_items": [] } \ No newline at end of file diff --git a/etna/api/tests/expected_results/early_modern.json b/etna/api/tests/expected_results/early_modern.json index 30e08e0c2..581c821c1 100644 --- a/etna/api/tests/expected_results/early_modern.json +++ b/etna/api/tests/expected_results/early_modern.json @@ -21,7 +21,44 @@ }, "privacy": "public", "last_published_at": "2000-01-01T00:00:00Z", - "url": "/early_modern/" + "url": "/early_modern/", + "teaser_text": "Teaser text", + "teaser_image": { + "id": 4, + "title": "An image", + "jpeg": { + "url": "/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_Qqqrhoo.jpg", + "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_Qqqrhoo.jpg", + "width": 100, + "height": 68 + }, + "webp": { + "url": "/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_EW7Ow5o.webp", + "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_EW7Ow5o.webp", + "width": 100, + "height": 68 + } + }, + "teaser_image_square": { + "id": 4, + "title": "An image", + "jpeg": { + "url": "/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_3mqgkw3.jpg", + "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_3mqgkw3.jpg", + "width": 100, + "height": 100 + }, + "webp": { + "url": "/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iIlAHIz.webp", + "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iIlAHIz.webp", + "width": 100, + "height": 100 + } + }, + "search_image": null, + "twitter_og_title": null, + "twitter_og_description": null, + "twitter_og_image": null }, "title": "early_modern", "hero_image_caption": "Hero image caption
", @@ -69,41 +106,6 @@ }, "global_alert": null, "type_label": "Time period", - "teaser_text": "Teaser text", - "teaser_image": { - "id": 4, - "title": "An image", - "jpeg": { - "url": "/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_uNQeuSn.jpg", - "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_uNQeuSn.jpg", - "width": 100, - "height": 68 - }, - "webp": { - "url": "/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_hHTWoNv.webp", - "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_hHTWoNv.webp", - "width": 100, - "height": 68 - } - }, - "teaser_image_square": { - "id": 4, - "title": "An image", - "jpeg": { - "url": "/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_sxVr2s4.jpg", - "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_sxVr2s4.jpg", - "width": 100, - "height": 100 - }, - "webp": { - "url": "/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_NJOgycO.webp", - "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_NJOgycO.webp", - "width": 100, - "height": 100 - } - }, - "facebook_og_image": null, - "twitter_og_image": null, "mourning_notice": { "title": "Test title", "message": "Test message
" diff --git a/etna/api/tests/expected_results/focused_article.json b/etna/api/tests/expected_results/focused_article.json index 6692b4eb5..e63f68b61 100644 --- a/etna/api/tests/expected_results/focused_article.json +++ b/etna/api/tests/expected_results/focused_article.json @@ -21,46 +21,48 @@ }, "privacy": "public", "last_published_at": "2000-01-02T00:00:00Z", - "url": "/article_index/focused_article/" + "url": "/article_index/focused_article/", + "teaser_text": "Teaser text", + "teaser_image": { + "id": 14, + "title": "An image", + "jpeg": { + "url": "/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_Qqqrhoo.jpg", + "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_Qqqrhoo.jpg", + "width": 100, + "height": 68 + }, + "webp": { + "url": "/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_EW7Ow5o.webp", + "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_EW7Ow5o.webp", + "width": 100, + "height": 68 + } + }, + "teaser_image_square": { + "id": 14, + "title": "An image", + "jpeg": { + "url": "/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_3mqgkw3.jpg", + "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_3mqgkw3.jpg", + "width": 100, + "height": 100 + }, + "webp": { + "url": "/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iIlAHIz.webp", + "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iIlAHIz.webp", + "width": 100, + "height": 100 + } + }, + "search_image": null, + "twitter_og_title": null, + "twitter_og_description": null, + "twitter_og_image": null }, "title": "focused_article", "global_alert": null, "type_label": "Focus on", - "teaser_text": "Teaser text", - "teaser_image": { - "id": 14, - "title": "An image", - "jpeg": { - "url": "/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_iKXvc5j.jpg", - "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_iKXvc5j.jpg", - "width": 100, - "height": 68 - }, - "webp": { - "url": "/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_moF3lTy.webp", - "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_moF3lTy.webp", - "width": 100, - "height": 68 - } - }, - "teaser_image_square": { - "id": 14, - "title": "An image", - "jpeg": { - "url": "/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_hYgSKKd.jpg", - "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_hYgSKKd.jpg", - "width": 100, - "height": 100 - }, - "webp": { - "url": "/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_5hj4CDl.webp", - "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_5hj4CDl.webp", - "width": 100, - "height": 100 - } - }, - "facebook_og_image": null, - "twitter_og_image": null, "mourning_notice": { "title": "Test title", "message": "Test message
" @@ -253,7 +255,15 @@ "width": 100, "height": 100 } - } + }, + "first_name": "John", + "last_name": "Smith", + "role_tags": [ + { + "slug": "author", + "name": "Author" + } + ] } ] } \ No newline at end of file diff --git a/etna/api/tests/expected_results/highlight_gallery.json b/etna/api/tests/expected_results/highlight_gallery.json index 31f94c9be..7fe4d7e4f 100644 --- a/etna/api/tests/expected_results/highlight_gallery.json +++ b/etna/api/tests/expected_results/highlight_gallery.json @@ -21,46 +21,48 @@ }, "privacy": "public", "last_published_at": "2000-01-03T00:00:00Z", - "url": "/arts/highlight_gallery/" + "url": "/arts/highlight_gallery/", + "teaser_text": "Teaser text", + "teaser_image": { + "id": 16, + "title": "An image", + "jpeg": { + "url": "/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_Qqqrhoo.jpg", + "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_Qqqrhoo.jpg", + "width": 100, + "height": 68 + }, + "webp": { + "url": "/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_EW7Ow5o.webp", + "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_EW7Ow5o.webp", + "width": 100, + "height": 68 + } + }, + "teaser_image_square": { + "id": 16, + "title": "An image", + "jpeg": { + "url": "/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_3mqgkw3.jpg", + "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_3mqgkw3.jpg", + "width": 100, + "height": 100 + }, + "webp": { + "url": "/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iIlAHIz.webp", + "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iIlAHIz.webp", + "width": 100, + "height": 100 + } + }, + "search_image": null, + "twitter_og_title": null, + "twitter_og_description": null, + "twitter_og_image": null }, "title": "highlight_gallery", "global_alert": null, "type_label": "In pictures", - "teaser_text": "Teaser text", - "teaser_image": { - "id": 16, - "title": "An image", - "jpeg": { - "url": "/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_USJnKya.jpg", - "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_USJnKya.jpg", - "width": 100, - "height": 68 - }, - "webp": { - "url": "/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_zRNQgeA.webp", - "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_zRNQgeA.webp", - "width": 100, - "height": 68 - } - }, - "teaser_image_square": { - "id": 16, - "title": "An image", - "jpeg": { - "url": "/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_RvpfygA.jpg", - "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_RvpfygA.jpg", - "width": 100, - "height": 100 - }, - "webp": { - "url": "/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_4qVjMdZ.webp", - "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_4qVjMdZ.webp", - "width": 100, - "height": 100 - } - }, - "facebook_og_image": null, - "twitter_og_image": null, "mourning_notice": { "title": "Test title", "message": "Test message
" diff --git a/etna/api/tests/expected_results/pages.json b/etna/api/tests/expected_results/pages.json index 8dc9e1c36..3a492fb3f 100644 --- a/etna/api/tests/expected_results/pages.json +++ b/etna/api/tests/expected_results/pages.json @@ -188,7 +188,15 @@ "width": 100, "height": 100 } - } + }, + "first_name": "John", + "last_name": "Smith", + "role_tags": [ + { + "slug": "author", + "name": "Author" + } + ] }, { "id": ARTICLE_INDEX_ID, diff --git a/etna/api/tests/expected_results/people.json b/etna/api/tests/expected_results/people.json index 8bd61fe20..edeee1323 100644 --- a/etna/api/tests/expected_results/people.json +++ b/etna/api/tests/expected_results/people.json @@ -21,7 +21,44 @@ }, "privacy": "public", "last_published_at": null, - "url": "/people/" + "url": "/people/", + "teaser_text": "Teaser text", + "teaser_image": { + "id": 8, + "title": "An image", + "jpeg": { + "url": "/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_Qqqrhoo.jpg", + "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_Qqqrhoo.jpg", + "width": 100, + "height": 68 + }, + "webp": { + "url": "/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_EW7Ow5o.webp", + "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_EW7Ow5o.webp", + "width": 100, + "height": 68 + } + }, + "teaser_image_square": { + "id": 8, + "title": "An image", + "jpeg": { + "url": "/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_3mqgkw3.jpg", + "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_3mqgkw3.jpg", + "width": 100, + "height": 100 + }, + "webp": { + "url": "/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iIlAHIz.webp", + "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iIlAHIz.webp", + "width": 100, + "height": 100 + } + }, + "search_image": null, + "twitter_og_title": null, + "twitter_og_description": null, + "twitter_og_image": null }, "title": "people", "global_alert": { @@ -32,41 +69,6 @@ "uid": ALERT_UID }, "type_label": "People index", - "teaser_text": "Teaser text", - "teaser_image": { - "id": 8, - "title": "An image", - "jpeg": { - "url": "/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_l15ah9y.jpg", - "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_l15ah9y.jpg", - "width": 100, - "height": 68 - }, - "webp": { - "url": "/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_28UNLbC.webp", - "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_28UNLbC.webp", - "width": 100, - "height": 68 - } - }, - "teaser_image_square": { - "id": 8, - "title": "An image", - "jpeg": { - "url": "/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_GOiDUvu.jpg", - "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_GOiDUvu.jpg", - "width": 100, - "height": 100 - }, - "webp": { - "url": "/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_52SwDTo.webp", - "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_52SwDTo.webp", - "width": 100, - "height": 100 - } - }, - "facebook_og_image": null, - "twitter_og_image": null, "mourning_notice": { "title": "Test title", "message": "Test message
" @@ -127,7 +129,15 @@ "width": 100, "height": 100 } - } + }, + "first_name": "John", + "last_name": "Smith", + "role_tags": [ + { + "slug": "author", + "name": "Author" + } + ] } ] } \ No newline at end of file diff --git a/etna/api/tests/expected_results/postwar.json b/etna/api/tests/expected_results/postwar.json index fc1e4f396..8fa36de3e 100644 --- a/etna/api/tests/expected_results/postwar.json +++ b/etna/api/tests/expected_results/postwar.json @@ -21,7 +21,44 @@ }, "privacy": "public", "last_published_at": "2000-01-01T00:00:00Z", - "url": "/postwar/" + "url": "/postwar/", + "teaser_text": "Teaser text", + "teaser_image": { + "id": 6, + "title": "An image", + "jpeg": { + "url": "/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_Qqqrhoo.jpg", + "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_Qqqrhoo.jpg", + "width": 100, + "height": 68 + }, + "webp": { + "url": "/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_EW7Ow5o.webp", + "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_EW7Ow5o.webp", + "width": 100, + "height": 68 + } + }, + "teaser_image_square": { + "id": 6, + "title": "An image", + "jpeg": { + "url": "/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_3mqgkw3.jpg", + "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_3mqgkw3.jpg", + "width": 100, + "height": 100 + }, + "webp": { + "url": "/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iIlAHIz.webp", + "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iIlAHIz.webp", + "width": 100, + "height": 100 + } + }, + "search_image": null, + "twitter_og_title": null, + "twitter_og_description": null, + "twitter_og_image": null }, "title": "postwar", "hero_image_caption": "Hero image caption
", @@ -69,41 +106,6 @@ }, "global_alert": null, "type_label": "Time period", - "teaser_text": "Teaser text", - "teaser_image": { - "id": 6, - "title": "An image", - "jpeg": { - "url": "/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_EFiJD6C.jpg", - "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-600x400.format-jpeg.jpegquality-60_EFiJD6C.jpg", - "width": 100, - "height": 68 - }, - "webp": { - "url": "/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_yuGViLV.webp", - "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-600x400.format-webp.webpquality-60_yuGViLV.webp", - "width": 100, - "height": 68 - } - }, - "teaser_image_square": { - "id": 6, - "title": "An image", - "jpeg": { - "url": "/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_t3bT2Ac.jpg", - "full_url": "https://nationalarchives.gov.uk/media/images/example.2e16d0ba.fill-512x512.format-jpeg.jpegquality-60_t3bT2Ac.jpg", - "width": 100, - "height": 100 - }, - "webp": { - "url": "/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iXmD76N.webp", - "full_url": "https://nationalarchives.gov.uk/media/images/exampl.2e16d0ba.fill-512x512.format-webp.webpquality-60_iXmD76N.webp", - "width": 100, - "height": 100 - } - }, - "facebook_og_image": null, - "twitter_og_image": null, "mourning_notice": { "title": "Test title", "message": "Test message
" diff --git a/etna/api/tests/test_pages.py b/etna/api/tests/test_pages.py index 19b7c761a..2f65bbb54 100644 --- a/etna/api/tests/test_pages.py +++ b/etna/api/tests/test_pages.py @@ -126,6 +126,8 @@ def setUpTestData(cls): summary="Summary text
", first_published_at=DATE_1, parent=cls.author_index_page, + first_name="John", + last_name="Smith", ) cls.article_index = ArticleIndexPageFactory( diff --git a/etna/articles/migrations/0109_articleindexpage_twitter_og_description_and_more.py b/etna/articles/migrations/0109_articleindexpage_twitter_og_description_and_more.py new file mode 100644 index 000000000..5ee96a492 --- /dev/null +++ b/etna/articles/migrations/0109_articleindexpage_twitter_og_description_and_more.py @@ -0,0 +1,204 @@ +# Generated by Django 5.0.8 on 2024-08-08 11:44 +# etna:allowAlterField + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("articles", "0108_articleindexpage_alert_articlepage_alert_and_more"), + ("images", "0009_alter_customimage_custom_sensitive_image_warning"), + ] + + operations = [ + migrations.AddField( + model_name="articleindexpage", + name="twitter_og_description", + field=models.TextField( + blank=True, + help_text="If left blank, the OpenGraph description will be used.", + null=True, + verbose_name="Twitter OpenGraph description", + ), + ), + migrations.AddField( + model_name="articleindexpage", + name="twitter_og_image", + field=models.ForeignKey( + blank=True, + help_text="If left blank, the OpenGraph image will be used.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="Twitter OpenGraph image", + ), + ), + migrations.AddField( + model_name="articleindexpage", + name="twitter_og_title", + field=models.CharField( + blank=True, + help_text="If left blank, the OpenGraph title will be used.", + max_length=255, + null=True, + verbose_name="Twitter OpenGraph title", + ), + ), + migrations.AddField( + model_name="articlepage", + name="twitter_og_description", + field=models.TextField( + blank=True, + help_text="If left blank, the OpenGraph description will be used.", + null=True, + verbose_name="Twitter OpenGraph description", + ), + ), + migrations.AddField( + model_name="articlepage", + name="twitter_og_image", + field=models.ForeignKey( + blank=True, + help_text="If left blank, the OpenGraph image will be used.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="Twitter OpenGraph image", + ), + ), + migrations.AddField( + model_name="articlepage", + name="twitter_og_title", + field=models.CharField( + blank=True, + help_text="If left blank, the OpenGraph title will be used.", + max_length=255, + null=True, + verbose_name="Twitter OpenGraph title", + ), + ), + migrations.AddField( + model_name="focusedarticlepage", + name="twitter_og_description", + field=models.TextField( + blank=True, + help_text="If left blank, the OpenGraph description will be used.", + null=True, + verbose_name="Twitter OpenGraph description", + ), + ), + migrations.AddField( + model_name="focusedarticlepage", + name="twitter_og_image", + field=models.ForeignKey( + blank=True, + help_text="If left blank, the OpenGraph image will be used.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="Twitter OpenGraph image", + ), + ), + migrations.AddField( + model_name="focusedarticlepage", + name="twitter_og_title", + field=models.CharField( + blank=True, + help_text="If left blank, the OpenGraph title will be used.", + max_length=255, + null=True, + verbose_name="Twitter OpenGraph title", + ), + ), + migrations.AddField( + model_name="recordarticlepage", + name="twitter_og_description", + field=models.TextField( + blank=True, + help_text="If left blank, the OpenGraph description will be used.", + null=True, + verbose_name="Twitter OpenGraph description", + ), + ), + migrations.AddField( + model_name="recordarticlepage", + name="twitter_og_image", + field=models.ForeignKey( + blank=True, + help_text="If left blank, the OpenGraph image will be used.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="Twitter OpenGraph image", + ), + ), + migrations.AddField( + model_name="recordarticlepage", + name="twitter_og_title", + field=models.CharField( + blank=True, + help_text="If left blank, the OpenGraph title will be used.", + max_length=255, + null=True, + verbose_name="Twitter OpenGraph title", + ), + ), + migrations.AlterField( + model_name="articleindexpage", + name="search_image", + field=models.ForeignKey( + blank=True, + help_text="Image that will appear when this page is shared on social media. This will default to the teaser image if left blank.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="OpenGraph image", + ), + ), + migrations.AlterField( + model_name="articlepage", + name="search_image", + field=models.ForeignKey( + blank=True, + help_text="Image that will appear when this page is shared on social media. This will default to the teaser image if left blank.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="OpenGraph image", + ), + ), + migrations.AlterField( + model_name="focusedarticlepage", + name="search_image", + field=models.ForeignKey( + blank=True, + help_text="Image that will appear when this page is shared on social media. This will default to the teaser image if left blank.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="OpenGraph image", + ), + ), + migrations.AlterField( + model_name="recordarticlepage", + name="search_image", + field=models.ForeignKey( + blank=True, + help_text="Image that will appear when this page is shared on social media. This will default to the teaser image if left blank.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="OpenGraph image", + ), + ), + ] diff --git a/etna/articles/models.py b/etna/articles/models.py index e6bb2f030..eaf68bb7f 100755 --- a/etna/articles/models.py +++ b/etna/articles/models.py @@ -45,6 +45,7 @@ from etna.core.utils import skos_id_from_text from etna.people.models import AuthorPageMixin from etna.records.fields import RecordField +from etna.records.serializers import RecordSerializer from .blocks import ArticlePageStreamBlock @@ -755,7 +756,7 @@ class Meta: APIField("type_label"), APIField("date_text"), APIField("about", serializer=RichTextSerializer()), - APIField("record"), + APIField("record", serializer=RecordSerializer()), APIField("gallery_heading"), APIField("gallery_items", serializer=GallerySerializer(many=True)), APIField("image_library_link"), diff --git a/etna/collections/migrations/0059_explorerindexpage_twitter_og_description_and_more.py b/etna/collections/migrations/0059_explorerindexpage_twitter_og_description_and_more.py new file mode 100644 index 000000000..101ebef6c --- /dev/null +++ b/etna/collections/migrations/0059_explorerindexpage_twitter_og_description_and_more.py @@ -0,0 +1,298 @@ +# Generated by Django 5.0.8 on 2024-08-08 11:44 +# etna:allowAlterField + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("collections", "0058_highlightgallerypage_alert_and_more"), + ("images", "0009_alter_customimage_custom_sensitive_image_warning"), + ] + + operations = [ + migrations.AddField( + model_name="explorerindexpage", + name="twitter_og_description", + field=models.TextField( + blank=True, + help_text="If left blank, the OpenGraph description will be used.", + null=True, + verbose_name="Twitter OpenGraph description", + ), + ), + migrations.AddField( + model_name="explorerindexpage", + name="twitter_og_image", + field=models.ForeignKey( + blank=True, + help_text="If left blank, the OpenGraph image will be used.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="Twitter OpenGraph image", + ), + ), + migrations.AddField( + model_name="explorerindexpage", + name="twitter_og_title", + field=models.CharField( + blank=True, + help_text="If left blank, the OpenGraph title will be used.", + max_length=255, + null=True, + verbose_name="Twitter OpenGraph title", + ), + ), + migrations.AddField( + model_name="highlightgallerypage", + name="twitter_og_description", + field=models.TextField( + blank=True, + help_text="If left blank, the OpenGraph description will be used.", + null=True, + verbose_name="Twitter OpenGraph description", + ), + ), + migrations.AddField( + model_name="highlightgallerypage", + name="twitter_og_image", + field=models.ForeignKey( + blank=True, + help_text="If left blank, the OpenGraph image will be used.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="Twitter OpenGraph image", + ), + ), + migrations.AddField( + model_name="highlightgallerypage", + name="twitter_og_title", + field=models.CharField( + blank=True, + help_text="If left blank, the OpenGraph title will be used.", + max_length=255, + null=True, + verbose_name="Twitter OpenGraph title", + ), + ), + migrations.AddField( + model_name="timeperiodexplorerindexpage", + name="twitter_og_description", + field=models.TextField( + blank=True, + help_text="If left blank, the OpenGraph description will be used.", + null=True, + verbose_name="Twitter OpenGraph description", + ), + ), + migrations.AddField( + model_name="timeperiodexplorerindexpage", + name="twitter_og_image", + field=models.ForeignKey( + blank=True, + help_text="If left blank, the OpenGraph image will be used.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="Twitter OpenGraph image", + ), + ), + migrations.AddField( + model_name="timeperiodexplorerindexpage", + name="twitter_og_title", + field=models.CharField( + blank=True, + help_text="If left blank, the OpenGraph title will be used.", + max_length=255, + null=True, + verbose_name="Twitter OpenGraph title", + ), + ), + migrations.AddField( + model_name="timeperiodexplorerpage", + name="twitter_og_description", + field=models.TextField( + blank=True, + help_text="If left blank, the OpenGraph description will be used.", + null=True, + verbose_name="Twitter OpenGraph description", + ), + ), + migrations.AddField( + model_name="timeperiodexplorerpage", + name="twitter_og_image", + field=models.ForeignKey( + blank=True, + help_text="If left blank, the OpenGraph image will be used.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="Twitter OpenGraph image", + ), + ), + migrations.AddField( + model_name="timeperiodexplorerpage", + name="twitter_og_title", + field=models.CharField( + blank=True, + help_text="If left blank, the OpenGraph title will be used.", + max_length=255, + null=True, + verbose_name="Twitter OpenGraph title", + ), + ), + migrations.AddField( + model_name="topicexplorerindexpage", + name="twitter_og_description", + field=models.TextField( + blank=True, + help_text="If left blank, the OpenGraph description will be used.", + null=True, + verbose_name="Twitter OpenGraph description", + ), + ), + migrations.AddField( + model_name="topicexplorerindexpage", + name="twitter_og_image", + field=models.ForeignKey( + blank=True, + help_text="If left blank, the OpenGraph image will be used.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="Twitter OpenGraph image", + ), + ), + migrations.AddField( + model_name="topicexplorerindexpage", + name="twitter_og_title", + field=models.CharField( + blank=True, + help_text="If left blank, the OpenGraph title will be used.", + max_length=255, + null=True, + verbose_name="Twitter OpenGraph title", + ), + ), + migrations.AddField( + model_name="topicexplorerpage", + name="twitter_og_description", + field=models.TextField( + blank=True, + help_text="If left blank, the OpenGraph description will be used.", + null=True, + verbose_name="Twitter OpenGraph description", + ), + ), + migrations.AddField( + model_name="topicexplorerpage", + name="twitter_og_image", + field=models.ForeignKey( + blank=True, + help_text="If left blank, the OpenGraph image will be used.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="Twitter OpenGraph image", + ), + ), + migrations.AddField( + model_name="topicexplorerpage", + name="twitter_og_title", + field=models.CharField( + blank=True, + help_text="If left blank, the OpenGraph title will be used.", + max_length=255, + null=True, + verbose_name="Twitter OpenGraph title", + ), + ), + migrations.AlterField( + model_name="explorerindexpage", + name="search_image", + field=models.ForeignKey( + blank=True, + help_text="Image that will appear when this page is shared on social media. This will default to the teaser image if left blank.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="OpenGraph image", + ), + ), + migrations.AlterField( + model_name="highlightgallerypage", + name="search_image", + field=models.ForeignKey( + blank=True, + help_text="Image that will appear when this page is shared on social media. This will default to the teaser image if left blank.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="OpenGraph image", + ), + ), + migrations.AlterField( + model_name="timeperiodexplorerindexpage", + name="search_image", + field=models.ForeignKey( + blank=True, + help_text="Image that will appear when this page is shared on social media. This will default to the teaser image if left blank.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="OpenGraph image", + ), + ), + migrations.AlterField( + model_name="timeperiodexplorerpage", + name="search_image", + field=models.ForeignKey( + blank=True, + help_text="Image that will appear when this page is shared on social media. This will default to the teaser image if left blank.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="OpenGraph image", + ), + ), + migrations.AlterField( + model_name="topicexplorerindexpage", + name="search_image", + field=models.ForeignKey( + blank=True, + help_text="Image that will appear when this page is shared on social media. This will default to the teaser image if left blank.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="OpenGraph image", + ), + ), + migrations.AlterField( + model_name="topicexplorerpage", + name="search_image", + field=models.ForeignKey( + blank=True, + help_text="Image that will appear when this page is shared on social media. This will default to the teaser image if left blank.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="OpenGraph image", + ), + ), + ] diff --git a/etna/cookies/__init__.py b/etna/cookies/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/etna/cookies/apps.py b/etna/cookies/apps.py new file mode 100644 index 000000000..77f152582 --- /dev/null +++ b/etna/cookies/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + + +class CookiesAppConfig(AppConfig): + default_auto_field = "django.db.models.AutoField" + name = "etna.cookies" + verbose_name = "Cookies" diff --git a/etna/cookies/blocks.py b/etna/cookies/blocks.py new file mode 100644 index 000000000..38a5811aa --- /dev/null +++ b/etna/cookies/blocks.py @@ -0,0 +1,36 @@ +from wagtail import blocks + +from etna.core.blocks import ( + ContentTableBlock, + DescriptionListBlock, + InsetTextBlock, + ParagraphBlock, + SectionDepthAwareStructBlock, + SubHeadingBlock, +) + + +class SectionContentBlock(blocks.StreamBlock): + inset_text = InsetTextBlock() + paragraph = ParagraphBlock() + description_list = DescriptionListBlock() + sub_heading = SubHeadingBlock() + table = ContentTableBlock() + + +class ContentSectionBlock(SectionDepthAwareStructBlock): + heading = blocks.CharBlock(max_length=100, label="Heading") + content = SectionContentBlock(required=False) + + class Meta: + label = "Section" + template = "articles/blocks/section.html" + + +class CookieDetailsStreamBlock(SectionContentBlock): + """ + A block for the CookieDetailsPage model. + """ + + content_section = ContentSectionBlock() + sub_heading = None diff --git a/etna/cookies/factories.py b/etna/cookies/factories.py new file mode 100644 index 000000000..5431e0b30 --- /dev/null +++ b/etna/cookies/factories.py @@ -0,0 +1,7 @@ +from etna.cookies import models as app_models +from etna.core.factories import BasePageFactory + + +class CookiesPageFactory(BasePageFactory): + class Meta: + model = app_models.CookiesPage diff --git a/etna/cookies/migrations/0001_initial.py b/etna/cookies/migrations/0001_initial.py new file mode 100644 index 000000000..e54c84b75 --- /dev/null +++ b/etna/cookies/migrations/0001_initial.py @@ -0,0 +1,339 @@ +# Generated by Django 5.0.8 on 2024-08-28 13:32 + +import django.db.models.deletion +import etna.analytics.mixins +import uuid +import wagtail.fields +import wagtail_headless_preview.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ("alerts", "0003_alert_name_alter_alert_title"), + ("images", "0009_alter_customimage_custom_sensitive_image_warning"), + ("wagtailcore", "0094_alter_page_locale"), + ] + + operations = [ + migrations.CreateModel( + name="CookieDetailsPage", + fields=[ + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.page", + ), + ), + ( + "twitter_og_title", + models.CharField( + blank=True, + help_text="If left blank, the OpenGraph title will be used.", + max_length=255, + null=True, + verbose_name="Twitter OpenGraph title", + ), + ), + ( + "twitter_og_description", + models.TextField( + blank=True, + help_text="If left blank, the OpenGraph description will be used.", + null=True, + verbose_name="Twitter OpenGraph description", + ), + ), + ( + "teaser_text", + models.TextField( + help_text="A short, enticing description of this page. This will appear in promos and under thumbnails around the site.", + max_length=160, + verbose_name="teaser text", + ), + ), + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, + editable=False, + unique=True, + verbose_name="UUID", + ), + ), + ( + "intro", + wagtail.fields.RichTextField( + help_text="1-2 sentences introducing the subject of the page, and explaining why a user should read on.", + max_length=300, + verbose_name="introductory text", + ), + ), + ( + "body", + wagtail.fields.StreamField( + [ + ("inset_text", 1), + ("paragraph", 1), + ("description_list", 6), + ("table", 8), + ("content_section", 13), + ], + blank=True, + block_lookup={ + 0: ( + "etna.core.blocks.paragraph.APIRichTextBlock", + (), + {"features": ["bold", "italic", "link", "ol", "ul"]}, + ), + 1: ("wagtail.blocks.StructBlock", [[("text", 0)]], {}), + 2: ("wagtail.blocks.CharBlock", (), {"required": True}), + 3: ( + "etna.core.blocks.paragraph.APIRichTextBlock", + (), + {"features": ["bold", "italic", "link"]}, + ), + 4: ( + "wagtail.blocks.StructBlock", + [[("term", 2), ("detail", 3)]], + {}, + ), + 5: ("wagtail.blocks.ListBlock", (4,), {}), + 6: ("wagtail.blocks.StructBlock", [[("items", 5)]], {}), + 7: ( + "wagtail.contrib.table_block.blocks.TableBlock", + (), + { + "table_options": { + "contextMenu": [ + "row_above", + "row_below", + "---------", + "col_left", + "col_right", + "---------", + "remove_row", + "remove_col", + "---------", + "undo", + "redo", + "---------", + "alignment", + ] + } + }, + ), + 8: ("wagtail.blocks.StructBlock", [[("table", 7)]], {}), + 9: ( + "wagtail.blocks.CharBlock", + (), + {"label": "Heading", "max_length": 100}, + ), + 10: ( + "wagtail.blocks.CharBlock", + (), + {"label": "Sub-heading", "max_length": 100}, + ), + 11: ("wagtail.blocks.StructBlock", [[("heading", 10)]], {}), + 12: ( + "wagtail.blocks.StreamBlock", + [ + [ + ("inset_text", 1), + ("paragraph", 1), + ("description_list", 6), + ("sub_heading", 11), + ("table", 8), + ] + ], + {"required": False}, + ), + 13: ( + "wagtail.blocks.StructBlock", + [[("heading", 9), ("content", 12)]], + {}, + ), + }, + null=True, + ), + ), + ( + "alert", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="alerts.alert", + ), + ), + ( + "search_image", + models.ForeignKey( + blank=True, + help_text="Image that will appear when this page is shared on social media. This will default to the teaser image if left blank.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="OpenGraph image", + ), + ), + ( + "teaser_image", + models.ForeignKey( + blank=True, + help_text="Image that will appear on thumbnails and promos around the site.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + ), + ), + ( + "twitter_og_image", + models.ForeignKey( + blank=True, + help_text="If left blank, the OpenGraph image will be used.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="Twitter OpenGraph image", + ), + ), + ], + options={ + "verbose_name": "Cookie details page", + "verbose_name_plural": "Cookie details pages", + }, + bases=( + etna.analytics.mixins.DataLayerMixin, + wagtail_headless_preview.models.HeadlessPreviewMixin, + "wagtailcore.page", + models.Model, + ), + ), + migrations.CreateModel( + name="CookiesPage", + fields=[ + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.page", + ), + ), + ( + "twitter_og_title", + models.CharField( + blank=True, + help_text="If left blank, the OpenGraph title will be used.", + max_length=255, + null=True, + verbose_name="Twitter OpenGraph title", + ), + ), + ( + "twitter_og_description", + models.TextField( + blank=True, + help_text="If left blank, the OpenGraph description will be used.", + null=True, + verbose_name="Twitter OpenGraph description", + ), + ), + ( + "teaser_text", + models.TextField( + help_text="A short, enticing description of this page. This will appear in promos and under thumbnails around the site.", + max_length=160, + verbose_name="teaser text", + ), + ), + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, + editable=False, + unique=True, + verbose_name="UUID", + ), + ), + ( + "intro", + wagtail.fields.RichTextField( + help_text="1-2 sentences introducing the subject of the page, and explaining why a user should read on.", + max_length=300, + verbose_name="introductory text", + ), + ), + ( + "alert", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="alerts.alert", + ), + ), + ( + "search_image", + models.ForeignKey( + blank=True, + help_text="Image that will appear when this page is shared on social media. This will default to the teaser image if left blank.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="OpenGraph image", + ), + ), + ( + "teaser_image", + models.ForeignKey( + blank=True, + help_text="Image that will appear on thumbnails and promos around the site.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + ), + ), + ( + "twitter_og_image", + models.ForeignKey( + blank=True, + help_text="If left blank, the OpenGraph image will be used.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="Twitter OpenGraph image", + ), + ), + ], + options={ + "abstract": False, + }, + bases=( + etna.analytics.mixins.DataLayerMixin, + wagtail_headless_preview.models.HeadlessPreviewMixin, + "wagtailcore.page", + models.Model, + ), + ), + ] diff --git a/etna/cookies/migrations/__init__.py b/etna/cookies/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/etna/cookies/models.py b/etna/cookies/models.py new file mode 100755 index 000000000..d15e1093c --- /dev/null +++ b/etna/cookies/models.py @@ -0,0 +1,31 @@ +from wagtail.admin.panels import FieldPanel +from wagtail.api import APIField +from wagtail.fields import StreamField + +from etna.core.models import BasePageWithRequiredIntro + +from .blocks import CookieDetailsStreamBlock + + +class CookiesPage(BasePageWithRequiredIntro): + max_count = 1 + subpage_types = ["cookies.CookieDetailsPage"] + + +class CookieDetailsPage(BasePageWithRequiredIntro): + max_count = 1 + parent_page_types = ["cookies.CookiesPage"] + subpage_types = [] + + body = StreamField(CookieDetailsStreamBlock, blank=True, null=True) + + content_panels = BasePageWithRequiredIntro.content_panels + [ + FieldPanel("body"), + ] + + class Meta: + verbose_name = "Cookie details page" + verbose_name_plural = "Cookie details pages" + verbose_name_public = "cookies" + + api_fields = BasePageWithRequiredIntro.api_fields + [APIField("body")] diff --git a/etna/core/management/commands/rename_app.py b/etna/core/management/commands/rename_app.py index 4be7cbcf4..ba6aab6ed 100644 --- a/etna/core/management/commands/rename_app.py +++ b/etna/core/management/commands/rename_app.py @@ -60,7 +60,6 @@ def handle(self, old_app_name, new_app_name, *args, **options): f"WHERE app_label='{new_app_name}'" ) app_content_types = cursor.fetchall() - app_content_types.pop(0) for content_type in app_content_types: old_table_name = truncate_name( diff --git a/etna/core/models/basepage.py b/etna/core/models/basepage.py index cd882c32d..b79d2cfe4 100644 --- a/etna/core/models/basepage.py +++ b/etna/core/models/basepage.py @@ -18,7 +18,6 @@ from wagtail.search import index from wagtail_headless_preview.models import HeadlessPreviewMixin -from wagtailmetadata.models import MetadataPageMixin from etna.alerts.models import AlertMixin from etna.analytics.mixins import DataLayerMixin @@ -32,6 +31,8 @@ RichTextSerializer, ) +from .mixins import SocialMixin + __all__ = [ "BasePage", "BasePageWithIntro", @@ -46,9 +47,7 @@ @method_decorator(apply_default_vary_headers, name="serve") @method_decorator(apply_default_cache_control, name="serve") -class BasePage( - AlertMixin, MetadataPageMixin, DataLayerMixin, HeadlessPreviewMixin, Page -): +class BasePage(AlertMixin, SocialMixin, DataLayerMixin, HeadlessPreviewMixin, Page): """ An abstract base model that is used for all Page models within the project. Any common fields, Wagtail overrides or custom @@ -62,7 +61,6 @@ class BasePage( ), max_length=160, ) - teaser_image = models.ForeignKey( get_image_model_string(), null=True, @@ -90,31 +88,10 @@ class BasePage( ), widget=SlugInput, ), - FieldPanel("seo_title"), - FieldPanel( - "search_description", - help_text=_( - "The descriptive text displayed underneath a headline in search engine results and when shared on social media." - ), - ), - FieldPanel( - "search_image", - help_text=_( - "Image that will appear as a promo when this page is shared on social media." - ), - ), ], _("For search engines"), ), - MultiFieldPanel( - [ - FieldPanel("show_in_menus"), - ], - _("For site menus"), - ), - FieldPanel("teaser_image"), - FieldPanel("teaser_text"), - ] + ] + SocialMixin.promote_panels settings_panels = Page.settings_panels + AlertMixin.settings_panels @@ -183,25 +160,16 @@ def mourning_notice(self): api_fields = AlertMixin.api_fields + [ APIField("type_label"), + APIField("mourning_notice", serializer=MourningSerializer()), + ] + + api_meta_fields = [ APIField("teaser_text"), APIField( "teaser_image", serializer=ImageSerializer("fill-600x400"), ), - APIField( - "teaser_image_square", - serializer=ImageSerializer("fill-512x512", source="teaser_image"), - ), - APIField( - "facebook_og_image", - serializer=ImageSerializer("fill-1200x630", source="search_image"), - ), - APIField( - "twitter_og_image", - serializer=ImageSerializer("fill-1200x600", source="search_image"), - ), - APIField("mourning_notice", serializer=MourningSerializer()), - ] + ] + SocialMixin.api_meta_fields class BasePageWithIntro(BasePage): diff --git a/etna/core/models/mixins.py b/etna/core/models/mixins.py index c2f8dfeb3..25470bf3f 100644 --- a/etna/core/models/mixins.py +++ b/etna/core/models/mixins.py @@ -1,210 +1,315 @@ -from datetime import timedelta - -from django.db import models -from django.utils import timezone -from django.utils.functional import cached_property -from django.utils.safestring import mark_safe - -from wagtail.admin.panels import FieldPanel, MultiFieldPanel -from wagtail.api import APIField -from wagtail.fields import RichTextField -from wagtail.images import get_image_model_string - -from etna.core.serializers import DetailedImageSerializer, RichTextSerializer - -from .forms import RequiredHeroImagePageForm - -__all__ = [ - "ContentWarningMixin", - "NewLabelMixin", - "HeroImageMixin", - "RequiredHeroImageMixin", - "SidebarMixin", -] - - -class ContentWarningMixin(models.Model): - """Mixin to allow editors to toggle content warnings on and off""" - - display_content_warning = models.BooleanField( - verbose_name="display a content warning on this page", - default=False, - ) - - custom_warning_text = RichTextField( - verbose_name="custom content warning text (optional)", - features=["link"], - blank=True, - help_text=( - "If specified, will be used for the content warning. " - "Otherwise the default text will be used." - ), - ) - - api_fields = [ - APIField("display_content_warning"), - APIField("custom_warning_text", serializer=RichTextSerializer()), - ] - - class Meta: - abstract = True - - -class NewLabelMixin(models.Model): - """Mixin to allow editors to toggle 'new' label to be applied on-publish""" - - mark_new_on_next_publish = models.BooleanField( - verbose_name="mark this page as 'new' when published", - default=True, - help_text="This will set the 'new' label for 21 days", - ) - - newly_published_at = models.DateField( - editable=False, - verbose_name="Page marked as new on", - default=None, - null=True, - ) - - new_label_display_for_days = 21 - - def with_content_json(self, content): - """ - Overrides Page.with_content_json() to ensure page's `newly_published_at` - value is always preserved between revisions. - """ - obj = super().with_content_json(content) - obj.newly_published_at = self.newly_published_at - return obj - - def save(self, *args, **kwargs): - """ - Overrides Page.save() to set `newly_published_at` under the right - circumstances, and to ensure `mark_new_on_next_publish` is unset - once that wish has been fulfilled. - """ - # Set/reset newly_published_at where requested - if self.live and self.mark_new_on_next_publish: - self.newly_published_at = timezone.now().date() - self.mark_new_on_next_publish = False - - # Save page changes to the database - super().save(*args, **kwargs) - - if self.live and self.mark_new_on_next_publish and self.latest_revision: - # If `mark_new_on_next_publish` is still 'True' in the latest revision, - # The checkbox will remain checked when the page is next edited in Wagtail. - # Checking the box has had the desired effect now, so we 'uncheck' it - # in the revision content to avoid unexpected resetting. - self.latest_revision.content["mark_new_on_next_publish"] = False - self.latest_revision.save() - - @cached_property - def is_newly_published(self): - expiry_date = timezone.now().date() - timedelta( - days=self.new_label_display_for_days - ) - if self.newly_published_at: - if self.newly_published_at > expiry_date: - return True - return False - - promote_panels = [ - MultiFieldPanel( - [ - FieldPanel("mark_new_on_next_publish"), - FieldPanel("newly_published_at", read_only=True), - ], - heading="New label", - ) - ] - - class Meta: - abstract = True - - api_fields = [ - APIField("is_newly_published"), - ] - - -class HeroImageMixin(models.Model): - """Mixin to add hero_image attribute to a Page.""" - - hero_image = models.ForeignKey( - get_image_model_string(), - null=True, - blank=True, - on_delete=models.SET_NULL, - related_name="+", - ) - - hero_image_caption = RichTextField( - verbose_name="hero image caption (optional)", - features=["bold", "italic", "link"], - blank=True, - help_text=( - "An optional caption for hero images. This could be used for image sources or for other useful metadata." - ), - ) - - class Meta: - abstract = True - - content_panels = [ - MultiFieldPanel( - [ - FieldPanel("hero_image"), - FieldPanel("hero_image_caption"), - ], - heading="Hero image", - ) - ] - - api_fields = [ - APIField("hero_image_caption", serializer=RichTextSerializer()), - APIField( - "hero_image", - serializer=DetailedImageSerializer("fill-1200x480"), - ), - APIField( - "hero_image_small", - serializer=DetailedImageSerializer("fill-600x400", source="hero_image"), - ), - ] - - -class RequiredHeroImageMixin(HeroImageMixin): - """Mixin to add hero_image attribute to a Page, and make it required.""" - - class Meta: - abstract = True - - base_form_class = RequiredHeroImagePageForm - - -class SidebarMixin(models.Model): - """Mixin to add sidebar options to a Page.""" - - page_sidebar = models.CharField( - choices=[ - ("contents", "Contents"), - ("sections", "Sections"), - ("pages", "Pages"), - ], - help_text=mark_safe( - "Select the sidebar style for this page. For more information, see the sidebar documentation." - ), - null=True, - blank=True, - ) - - class Meta: - abstract = True - - settings_panels = [ - FieldPanel("page_sidebar"), - ] - - api_fields = [ - APIField("page_sidebar"), - ] +from datetime import timedelta + +from django.db import models +from django.utils import timezone +from django.utils.functional import cached_property +from django.utils.safestring import mark_safe +from django.utils.translation import gettext_lazy as _ + +from wagtail.admin.panels import FieldPanel, MultiFieldPanel +from wagtail.api import APIField +from wagtail.fields import RichTextField +from wagtail.images import get_image_model_string + +from etna.core.serializers import ( + DetailedImageSerializer, + ImageSerializer, + RichTextSerializer, +) + +from .forms import RequiredHeroImagePageForm + +__all__ = [ + "ContentWarningMixin", + "NewLabelMixin", + "HeroImageMixin", + "RequiredHeroImageMixin", + "SidebarMixin", + "SocialMixin", +] + + +class ContentWarningMixin(models.Model): + """Mixin to allow editors to toggle content warnings on and off""" + + display_content_warning = models.BooleanField( + verbose_name="display a content warning on this page", + default=False, + ) + + custom_warning_text = RichTextField( + verbose_name="custom content warning text (optional)", + features=["link"], + blank=True, + help_text=( + "If specified, will be used for the content warning. " + "Otherwise the default text will be used." + ), + ) + + api_fields = [ + APIField("display_content_warning"), + APIField("custom_warning_text", serializer=RichTextSerializer()), + ] + + class Meta: + abstract = True + + +class NewLabelMixin(models.Model): + """Mixin to allow editors to toggle 'new' label to be applied on-publish""" + + mark_new_on_next_publish = models.BooleanField( + verbose_name="mark this page as 'new' when published", + default=True, + help_text="This will set the 'new' label for 21 days", + ) + + newly_published_at = models.DateField( + editable=False, + verbose_name="Page marked as new on", + default=None, + null=True, + ) + + new_label_display_for_days = 21 + + def with_content_json(self, content): + """ + Overrides Page.with_content_json() to ensure page's `newly_published_at` + value is always preserved between revisions. + """ + obj = super().with_content_json(content) + obj.newly_published_at = self.newly_published_at + return obj + + def save(self, *args, **kwargs): + """ + Overrides Page.save() to set `newly_published_at` under the right + circumstances, and to ensure `mark_new_on_next_publish` is unset + once that wish has been fulfilled. + """ + # Set/reset newly_published_at where requested + if self.live and self.mark_new_on_next_publish: + self.newly_published_at = timezone.now().date() + self.mark_new_on_next_publish = False + + # Save page changes to the database + super().save(*args, **kwargs) + + if self.live and self.mark_new_on_next_publish and self.latest_revision: + # If `mark_new_on_next_publish` is still 'True' in the latest revision, + # The checkbox will remain checked when the page is next edited in Wagtail. + # Checking the box has had the desired effect now, so we 'uncheck' it + # in the revision content to avoid unexpected resetting. + self.latest_revision.content["mark_new_on_next_publish"] = False + self.latest_revision.save() + + @cached_property + def is_newly_published(self): + expiry_date = timezone.now().date() - timedelta( + days=self.new_label_display_for_days + ) + if self.newly_published_at: + if self.newly_published_at > expiry_date: + return True + return False + + promote_panels = [ + MultiFieldPanel( + [ + FieldPanel("mark_new_on_next_publish"), + FieldPanel("newly_published_at", read_only=True), + ], + heading="New label", + ) + ] + + class Meta: + abstract = True + + api_fields = [ + APIField("is_newly_published"), + ] + + +class HeroImageMixin(models.Model): + """Mixin to add hero_image attribute to a Page.""" + + hero_image = models.ForeignKey( + get_image_model_string(), + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="+", + ) + + hero_image_caption = RichTextField( + verbose_name="hero image caption (optional)", + features=["bold", "italic", "link"], + blank=True, + help_text=( + "An optional caption for hero images. This could be used for image sources or for other useful metadata." + ), + ) + + class Meta: + abstract = True + + content_panels = [ + MultiFieldPanel( + [ + FieldPanel("hero_image"), + FieldPanel("hero_image_caption"), + ], + heading="Hero image", + ) + ] + + api_fields = [ + APIField("hero_image_caption", serializer=RichTextSerializer()), + APIField( + "hero_image", + serializer=DetailedImageSerializer("fill-1200x480"), + ), + APIField( + "hero_image_small", + serializer=DetailedImageSerializer("fill-600x400", source="hero_image"), + ), + ] + + +class RequiredHeroImageMixin(HeroImageMixin): + """Mixin to add hero_image attribute to a Page, and make it required.""" + + class Meta: + abstract = True + + base_form_class = RequiredHeroImagePageForm + + +class SidebarMixin(models.Model): + """Mixin to add sidebar options to a Page.""" + + page_sidebar = models.CharField( + choices=[ + ("contents", "Contents"), + ("sections", "Sections"), + ("section_tabs", "Section tabs"), + ("pages", "Pages"), + ("pages_tabs", "Pages tabs"), + ], + help_text=mark_safe( + "Select the sidebar style for this page. For more information, see the sidebar documentation." + ), + null=True, + blank=True, + ) + + class Meta: + abstract = True + + settings_panels = [ + FieldPanel("page_sidebar"), + ] + + api_fields = [ + APIField("page_sidebar"), + ] + + +class SocialMixin(models.Model): + """Mixin to add social media sharing options to a Page.""" + + search_image = models.ForeignKey( + get_image_model_string(), + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="+", + verbose_name=_("OpenGraph image"), + help_text=_( + "Image that will appear when this page is shared on social media. This will default to the teaser image if left blank." + ), + ) + + twitter_og_title = models.CharField( + verbose_name=_("Twitter OpenGraph title"), + max_length=255, + blank=True, + null=True, + help_text=_("If left blank, the OpenGraph title will be used."), + ) + twitter_og_description = models.TextField( + verbose_name=_("Twitter OpenGraph description"), + blank=True, + null=True, + help_text=_("If left blank, the OpenGraph description will be used."), + ) + twitter_og_image = models.ForeignKey( + get_image_model_string(), + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name="+", + verbose_name=_("Twitter OpenGraph image"), + help_text=_("If left blank, the OpenGraph image will be used."), + ) + + class Meta: + abstract = True + + promote_panels = [ + MultiFieldPanel( + [ + FieldPanel("teaser_text"), + FieldPanel("teaser_image"), + ], + heading="Internal data", + ), + MultiFieldPanel( + [ + FieldPanel( + "seo_title", + help_text=_( + "The name of the page displayed on search engine results as the clickable headline and when shared on social media." + ), + ), + FieldPanel( + "search_description", + help_text=_( + "The descriptive text displayed underneath a headline in search engine results and when shared on social media." + ), + ), + FieldPanel("search_image"), + ], + heading="Base OpenGraph data", + ), + MultiFieldPanel( + [ + FieldPanel("twitter_og_title"), + FieldPanel("twitter_og_description"), + FieldPanel("twitter_og_image"), + ], + heading="Twitter OpenGraph data", + ), + ] + + api_meta_fields = [ + APIField( + "teaser_image_square", + serializer=ImageSerializer("fill-512x512", source="teaser_image"), + ), + APIField("seo_title"), + APIField("search_description"), + APIField( + "search_image", + serializer=ImageSerializer("fill-1200x630"), + ), + APIField("twitter_og_title"), + APIField("twitter_og_description"), + APIField( + "twitter_og_image", + serializer=ImageSerializer("fill-1200x630"), + ), + ] diff --git a/etna/generic_pages/migrations/0032_generalpage_twitter_og_description_and_more.py b/etna/generic_pages/migrations/0032_generalpage_twitter_og_description_and_more.py new file mode 100644 index 000000000..7a5efb51e --- /dev/null +++ b/etna/generic_pages/migrations/0032_generalpage_twitter_og_description_and_more.py @@ -0,0 +1,110 @@ +# Generated by Django 5.0.8 on 2024-08-08 11:44 +# etna:allowAlterField + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("generic_pages", "0031_alter_generalpage_body"), + ("images", "0009_alter_customimage_custom_sensitive_image_warning"), + ] + + operations = [ + migrations.AddField( + model_name="generalpage", + name="twitter_og_description", + field=models.TextField( + blank=True, + help_text="If left blank, the OpenGraph description will be used.", + null=True, + verbose_name="Twitter OpenGraph description", + ), + ), + migrations.AddField( + model_name="generalpage", + name="twitter_og_image", + field=models.ForeignKey( + blank=True, + help_text="If left blank, the OpenGraph image will be used.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="Twitter OpenGraph image", + ), + ), + migrations.AddField( + model_name="generalpage", + name="twitter_og_title", + field=models.CharField( + blank=True, + help_text="If left blank, the OpenGraph title will be used.", + max_length=255, + null=True, + verbose_name="Twitter OpenGraph title", + ), + ), + migrations.AddField( + model_name="hubpage", + name="twitter_og_description", + field=models.TextField( + blank=True, + help_text="If left blank, the OpenGraph description will be used.", + null=True, + verbose_name="Twitter OpenGraph description", + ), + ), + migrations.AddField( + model_name="hubpage", + name="twitter_og_image", + field=models.ForeignKey( + blank=True, + help_text="If left blank, the OpenGraph image will be used.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="Twitter OpenGraph image", + ), + ), + migrations.AddField( + model_name="hubpage", + name="twitter_og_title", + field=models.CharField( + blank=True, + help_text="If left blank, the OpenGraph title will be used.", + max_length=255, + null=True, + verbose_name="Twitter OpenGraph title", + ), + ), + migrations.AlterField( + model_name="generalpage", + name="search_image", + field=models.ForeignKey( + blank=True, + help_text="Image that will appear when this page is shared on social media. This will default to the teaser image if left blank.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="OpenGraph image", + ), + ), + migrations.AlterField( + model_name="hubpage", + name="search_image", + field=models.ForeignKey( + blank=True, + help_text="Image that will appear when this page is shared on social media. This will default to the teaser image if left blank.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="OpenGraph image", + ), + ), + ] diff --git a/etna/generic_pages/migrations/0033_alter_generalpage_page_sidebar.py b/etna/generic_pages/migrations/0033_alter_generalpage_page_sidebar.py new file mode 100644 index 000000000..bdc87efcd --- /dev/null +++ b/etna/generic_pages/migrations/0033_alter_generalpage_page_sidebar.py @@ -0,0 +1,29 @@ +# Generated by Django 5.0.8 on 2024-08-28 10:32 +# etna:allowAlterField + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("generic_pages", "0032_generalpage_twitter_og_description_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="generalpage", + name="page_sidebar", + field=models.CharField( + blank=True, + choices=[ + ("contents", "Contents"), + ("sections", "Sections"), + ("section_tabs", "Section tabs"), + ("pages", "Pages"), + ], + help_text="Select the sidebar style for this page. For more information, see the sidebar documentation.", + null=True, + ), + ), + ] diff --git a/etna/generic_pages/migrations/0034_alter_generalpage_page_sidebar.py b/etna/generic_pages/migrations/0034_alter_generalpage_page_sidebar.py new file mode 100644 index 000000000..5c5153c12 --- /dev/null +++ b/etna/generic_pages/migrations/0034_alter_generalpage_page_sidebar.py @@ -0,0 +1,30 @@ +# Generated by Django 5.0.8 on 2024-08-28 17:24 +# etna:allowAlterField + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("generic_pages", "0033_alter_generalpage_page_sidebar"), + ] + + operations = [ + migrations.AlterField( + model_name="generalpage", + name="page_sidebar", + field=models.CharField( + blank=True, + choices=[ + ("contents", "Contents"), + ("sections", "Sections"), + ("section_tabs", "Section tabs"), + ("pages", "Pages"), + ("pages_tabs", "Pages tabs"), + ], + help_text="Select the sidebar style for this page. For more information, see the sidebar documentation.", + null=True, + ), + ), + ] diff --git a/etna/home/migrations/0029_homepage_twitter_og_description_and_more.py b/etna/home/migrations/0029_homepage_twitter_og_description_and_more.py new file mode 100644 index 000000000..52b050d87 --- /dev/null +++ b/etna/home/migrations/0029_homepage_twitter_og_description_and_more.py @@ -0,0 +1,63 @@ +# Generated by Django 5.0.8 on 2024-08-08 11:44 +# etna:allowAlterField + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("home", "0028_rename_name_mourningnotice_title_and_more"), + ("images", "0009_alter_customimage_custom_sensitive_image_warning"), + ] + + operations = [ + migrations.AddField( + model_name="homepage", + name="twitter_og_description", + field=models.TextField( + blank=True, + help_text="If left blank, the OpenGraph description will be used.", + null=True, + verbose_name="Twitter OpenGraph description", + ), + ), + migrations.AddField( + model_name="homepage", + name="twitter_og_image", + field=models.ForeignKey( + blank=True, + help_text="If left blank, the OpenGraph image will be used.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="Twitter OpenGraph image", + ), + ), + migrations.AddField( + model_name="homepage", + name="twitter_og_title", + field=models.CharField( + blank=True, + help_text="If left blank, the OpenGraph title will be used.", + max_length=255, + null=True, + verbose_name="Twitter OpenGraph title", + ), + ), + migrations.AlterField( + model_name="homepage", + name="search_image", + field=models.ForeignKey( + blank=True, + help_text="Image that will appear when this page is shared on social media. This will default to the teaser image if left blank.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="OpenGraph image", + ), + ), + ] diff --git a/etna/images/models.py b/etna/images/models.py index f185f204e..3913e3187 100644 --- a/etna/images/models.py +++ b/etna/images/models.py @@ -10,6 +10,7 @@ from etna.core.serializers import RichTextSerializer from etna.records.fields import RecordField +from etna.records.serializers import RecordSerializer DEFAULT_SENSITIVE_IMAGE_WARNING = ( "This image contains content which some people may find offensive or distressing." @@ -144,7 +145,7 @@ class CustomImage(ClusterableModel, AbstractImage): APIField("translation_heading"), APIField("translation", serializer=RichTextSerializer()), APIField("record_dates"), - APIField("record"), + APIField("record", serializer=RecordSerializer()), ] @property diff --git a/etna/people/blocks.py b/etna/people/blocks.py new file mode 100644 index 000000000..8ec7b9323 --- /dev/null +++ b/etna/people/blocks.py @@ -0,0 +1,29 @@ +from wagtail import blocks + +from etna.core.blocks import ( + InsetTextBlock, + ParagraphBlock, + SectionDepthAwareStructBlock, +) + + +class SectionContentBlock(blocks.StreamBlock): + inset_text = InsetTextBlock() + paragraph = ParagraphBlock() + + +class ContentSectionBlock(SectionDepthAwareStructBlock): + heading = blocks.CharBlock(max_length=100, label="Heading") + content = SectionContentBlock(required=False) + + class Meta: + label = "Section" + template = "articles/blocks/section.html" + + +class ResearchSummaryStreamBlock(SectionContentBlock): + """ + A block for the GeneralPage model. + """ + + content_section = ContentSectionBlock() diff --git a/etna/people/migrations/0007_peopleindexpage_twitter_og_description_and_more.py b/etna/people/migrations/0007_peopleindexpage_twitter_og_description_and_more.py new file mode 100644 index 000000000..a91b9b430 --- /dev/null +++ b/etna/people/migrations/0007_peopleindexpage_twitter_og_description_and_more.py @@ -0,0 +1,110 @@ +# Generated by Django 5.0.8 on 2024-08-08 11:44 +# etna:allowAlterField + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("images", "0009_alter_customimage_custom_sensitive_image_warning"), + ("people", "0006_alter_authortag_author"), + ] + + operations = [ + migrations.AddField( + model_name="peopleindexpage", + name="twitter_og_description", + field=models.TextField( + blank=True, + help_text="If left blank, the OpenGraph description will be used.", + null=True, + verbose_name="Twitter OpenGraph description", + ), + ), + migrations.AddField( + model_name="peopleindexpage", + name="twitter_og_image", + field=models.ForeignKey( + blank=True, + help_text="If left blank, the OpenGraph image will be used.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="Twitter OpenGraph image", + ), + ), + migrations.AddField( + model_name="peopleindexpage", + name="twitter_og_title", + field=models.CharField( + blank=True, + help_text="If left blank, the OpenGraph title will be used.", + max_length=255, + null=True, + verbose_name="Twitter OpenGraph title", + ), + ), + migrations.AddField( + model_name="personpage", + name="twitter_og_description", + field=models.TextField( + blank=True, + help_text="If left blank, the OpenGraph description will be used.", + null=True, + verbose_name="Twitter OpenGraph description", + ), + ), + migrations.AddField( + model_name="personpage", + name="twitter_og_image", + field=models.ForeignKey( + blank=True, + help_text="If left blank, the OpenGraph image will be used.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="Twitter OpenGraph image", + ), + ), + migrations.AddField( + model_name="personpage", + name="twitter_og_title", + field=models.CharField( + blank=True, + help_text="If left blank, the OpenGraph title will be used.", + max_length=255, + null=True, + verbose_name="Twitter OpenGraph title", + ), + ), + migrations.AlterField( + model_name="peopleindexpage", + name="search_image", + field=models.ForeignKey( + blank=True, + help_text="Image that will appear when this page is shared on social media. This will default to the teaser image if left blank.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="OpenGraph image", + ), + ), + migrations.AlterField( + model_name="personpage", + name="search_image", + field=models.ForeignKey( + blank=True, + help_text="Image that will appear when this page is shared on social media. This will default to the teaser image if left blank.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="OpenGraph image", + ), + ), + ] diff --git a/etna/people/migrations/0008_personpage_first_name_personpage_last_name.py b/etna/people/migrations/0008_personpage_first_name_personpage_last_name.py new file mode 100644 index 000000000..6b6b0bcc2 --- /dev/null +++ b/etna/people/migrations/0008_personpage_first_name_personpage_last_name.py @@ -0,0 +1,25 @@ +# Generated by Django 5.0.8 on 2024-08-12 12:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("people", "0007_peopleindexpage_twitter_og_description_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="personpage", + name="first_name", + field=models.CharField(default="first", max_length=255), + preserve_default=False, + ), + migrations.AddField( + model_name="personpage", + name="last_name", + field=models.CharField(default="name", max_length=255), + preserve_default=False, + ), + ] diff --git a/etna/people/migrations/0009_personpage_research_summary.py b/etna/people/migrations/0009_personpage_research_summary.py new file mode 100644 index 000000000..4dc6d86d8 --- /dev/null +++ b/etna/people/migrations/0009_personpage_research_summary.py @@ -0,0 +1,19 @@ +# Generated by Django 5.0.8 on 2024-08-15 14:54 + +import wagtail.fields +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("people", "0008_personpage_first_name_personpage_last_name"), + ] + + operations = [ + migrations.AddField( + model_name="personpage", + name="research_summary", + field=wagtail.fields.RichTextField(blank=True, null=True), + ), + ] diff --git a/etna/people/migrations/0010_alter_personpage_research_summary.py b/etna/people/migrations/0010_alter_personpage_research_summary.py new file mode 100644 index 000000000..f692c4f89 --- /dev/null +++ b/etna/people/migrations/0010_alter_personpage_research_summary.py @@ -0,0 +1,46 @@ +# Generated by Django 5.0.8 on 2024-08-19 09:53 +# etna:allowAlterField +import wagtail.fields +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("people", "0009_personpage_research_summary"), + ] + + operations = [ + migrations.AlterField( + model_name="personpage", + name="research_summary", + field=wagtail.fields.StreamField( + [("inset_text", 1), ("paragraph", 1), ("content_section", 4)], + blank=True, + block_lookup={ + 0: ( + "etna.core.blocks.paragraph.APIRichTextBlock", + (), + {"features": ["bold", "italic", "link", "ol", "ul"]}, + ), + 1: ("wagtail.blocks.StructBlock", [[("text", 0)]], {}), + 2: ( + "wagtail.blocks.CharBlock", + (), + {"label": "Heading", "max_length": 100}, + ), + 3: ( + "wagtail.blocks.StreamBlock", + [[("inset_text", 1), ("paragraph", 1)]], + {"required": False}, + ), + 4: ( + "wagtail.blocks.StructBlock", + [[("heading", 2), ("content", 3)]], + {}, + ), + }, + null=True, + ), + ), + ] diff --git a/etna/people/migrations/0011_alter_personpage_role_alter_personpage_summary.py b/etna/people/migrations/0011_alter_personpage_role_alter_personpage_summary.py new file mode 100644 index 000000000..a953f9d56 --- /dev/null +++ b/etna/people/migrations/0011_alter_personpage_role_alter_personpage_summary.py @@ -0,0 +1,27 @@ +# Generated by Django 5.0.8 on 2024-08-19 13:20 +# etna:allowAlterField + +import wagtail.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("people", "0010_alter_personpage_research_summary"), + ] + + operations = [ + migrations.AlterField( + model_name="personpage", + name="role", + field=models.CharField(default="role", max_length=100), + preserve_default=False, + ), + migrations.AlterField( + model_name="personpage", + name="summary", + field=wagtail.fields.RichTextField(default="summary"), + preserve_default=False, + ), + ] diff --git a/etna/people/migrations/0012_shopitem.py b/etna/people/migrations/0012_shopitem.py new file mode 100644 index 000000000..1d471d614 --- /dev/null +++ b/etna/people/migrations/0012_shopitem.py @@ -0,0 +1,54 @@ +# Generated by Django 5.0.8 on 2024-08-20 10:03 + +import django.db.models.deletion +import modelcluster.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("images", "0009_alter_customimage_custom_sensitive_image_warning"), + ("people", "0011_alter_personpage_role_alter_personpage_summary"), + ] + + operations = [ + migrations.CreateModel( + name="ShopItem", + fields=[ + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title", models.CharField(max_length=255)), + ("url", models.URLField()), + ("price", models.DecimalField(decimal_places=2, max_digits=6)), + ( + "image", + models.ForeignKey( + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + ), + ), + ( + "page", + modelcluster.fields.ParentalKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="shop_items", + to="people.personpage", + ), + ), + ], + options={ + "verbose_name": "Shop item", + "verbose_name_plural": "Shop items", + }, + ), + ] diff --git a/etna/people/models.py b/etna/people/models.py index a0ba1c57e..5a93acaeb 100644 --- a/etna/people/models.py +++ b/etna/people/models.py @@ -6,9 +6,9 @@ from django.utils.functional import cached_property from modelcluster.fields import ParentalKey -from wagtail.admin.panels import FieldPanel, InlinePanel +from wagtail.admin.panels import FieldPanel, InlinePanel, MultiFieldPanel from wagtail.api import APIField -from wagtail.fields import RichTextField +from wagtail.fields import RichTextField, StreamField from wagtail.images import get_image_model_string from wagtail.models import Page @@ -19,6 +19,13 @@ RichTextSerializer, ) +from .blocks import ResearchSummaryStreamBlock + +ROLE_CHOICES = { + "author": "Author", + "researcher": "Researcher", +} + class PeopleIndexPage(BasePage): """People index page @@ -42,13 +49,49 @@ def people_pages(self): return ( self.get_children() .type(PersonPage) - .order_by("title") + .order_by("personpage__last_name") .live() .public() .specific() ) +class ShopItem(models.Model): + """Shop item model + + This model is used to represent a shop item. + """ + + page = ParentalKey( + "PersonPage", + on_delete=models.CASCADE, + related_name="shop_items", + ) + title = models.CharField(max_length=255) + url = models.URLField() + price = models.DecimalField(max_digits=6, decimal_places=2) + image = models.ForeignKey( + get_image_model_string(), + on_delete=models.SET_NULL, + related_name="+", + null=True, + ) + + def __str__(self): + return self.title + + class Meta: + verbose_name = "Shop item" + verbose_name_plural = "Shop items" + + api_fields = [ + APIField("title"), + APIField("url"), + APIField("price"), + APIField("image", serializer=ImageSerializer(rendition_size="fill-600x400")), + ] + + class PersonPage(BasePage): """Person page @@ -57,10 +100,6 @@ class PersonPage(BasePage): to link pages to a person, or as a reference to the person. """ - role = models.CharField(blank=True, null=True, max_length=100) - summary = RichTextField( - blank=True, null=True, features=settings.RESTRICTED_RICH_TEXT_FEATURES - ) image = models.ForeignKey( get_image_model_string(), blank=True, @@ -68,13 +107,33 @@ class PersonPage(BasePage): on_delete=models.SET_NULL, related_name="+", ) + role = models.CharField(max_length=100) + summary = RichTextField(features=settings.RESTRICTED_RICH_TEXT_FEATURES) + + research_summary = StreamField(ResearchSummaryStreamBlock, blank=True, null=True) + + first_name = models.CharField( + max_length=255, + ) + last_name = models.CharField( + max_length=255, + ) content_panels = BasePage.content_panels + [ FieldPanel("image"), FieldPanel("role"), FieldPanel("summary"), + FieldPanel("research_summary"), + InlinePanel("shop_items", label="Shop items"), ] + promote_panels = [ + MultiFieldPanel( + [FieldPanel("first_name"), FieldPanel("last_name")], + heading="Person details", + ), + ] + BasePage.promote_panels + class Meta: verbose_name = "Person page" verbose_name_plural = "People pages" @@ -93,21 +152,31 @@ class Meta: "image_small", serializer=ImageSerializer(rendition_size="fill-128x128", source="image"), ), + APIField("first_name"), + APIField("last_name"), + APIField("role_tags"), ] api_fields = BasePage.api_fields + [ + APIField("first_name"), + APIField("last_name"), APIField("role"), + APIField("role_tags"), + APIField("image", serializer=ImageSerializer(rendition_size="fill-512x512")), + APIField( + "image_small", + serializer=ImageSerializer(rendition_size="fill-128x128", source="image"), + ), APIField("summary", serializer=RichTextSerializer()), + APIField("research_summary"), APIField( "authored_focused_articles", serializer=DefaultPageSerializer( required_api_fields=["teaser_image"], many=True ), ), - APIField("image", serializer=ImageSerializer(rendition_size="fill-512x512")), APIField( - "image_small", - serializer=ImageSerializer(rendition_size="fill-128x128", source="image"), + "shop_items", ), ] @@ -124,7 +193,7 @@ def authored_focused_articles(self): ) @cached_property - def related_page_pks(self): + def related_page_pks(self) -> tuple[int]: """ Returns a list of ids of pages that have used the `AuthorTag` inline to indicate a relationship with this author. The values are ordered by @@ -136,6 +205,31 @@ def related_page_pks(self): ) ) + @cached_property + def is_author(self) -> bool: + """ + def is_X() is going to be a value to help with + the logic behind the role tags - an easier way + to check if a person is X role. + """ + if self.authored_focused_articles: + return True + return False + + @cached_property + def is_researcher(self) -> bool: + if self.research_summary: + return True + return False + + @cached_property + def role_tags(self) -> list[Dict[str, str]]: + roles = [] + for role, value in ROLE_CHOICES.items(): + if getattr(self, f"is_{role}"): + roles.append({"slug": role, "name": value}) + return roles + def get_datalayer_data(self, request: HttpRequest) -> Dict[str, Any]: data = super().get_datalayer_data(request) data.update(customDimension3="Author page") diff --git a/etna/people/tests/test_models.py b/etna/people/tests/test_models.py index 67b58a52c..2bd0defea 100644 --- a/etna/people/tests/test_models.py +++ b/etna/people/tests/test_models.py @@ -24,6 +24,8 @@ def setUp(self): summary="Test summary", image=self.image, teaser_text="Test teaser text", + first_name="John", + last_name="Doe", ) self.author_index_page.add_child(instance=self.author_page) diff --git a/etna/records/blocks.py b/etna/records/blocks.py index edfd1b984..70f5dd246 100644 --- a/etna/records/blocks.py +++ b/etna/records/blocks.py @@ -106,6 +106,13 @@ def extract_references(self, value): Wagtail's reference index from rebuilding this block""" return [] + def get_api_representation(self, value, context=None): + return { + "title": value.summary_title, + "iaid": value.iaid, + "reference_number": value.reference_number, + } + class Meta: icon = "archive" diff --git a/etna/records/serializers.py b/etna/records/serializers.py new file mode 100644 index 000000000..62841034d --- /dev/null +++ b/etna/records/serializers.py @@ -0,0 +1,10 @@ +from rest_framework import serializers + + +class RecordSerializer(serializers.Serializer): + def to_representation(self, instance): + return { + "title": instance.summary_title, + "iaid": instance.iaid, + "reference_number": instance.reference_number, + } diff --git a/etna/whatson/migrations/0012_eventpage_twitter_og_description_and_more.py b/etna/whatson/migrations/0012_eventpage_twitter_og_description_and_more.py new file mode 100644 index 000000000..81cabf4ed --- /dev/null +++ b/etna/whatson/migrations/0012_eventpage_twitter_og_description_and_more.py @@ -0,0 +1,157 @@ +# Generated by Django 5.0.8 on 2024-08-08 11:44 +# etna:allowAlterField + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("images", "0009_alter_customimage_custom_sensitive_image_warning"), + ("whatson", "0011_eventpage_alert_exhibitionpage_alert_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="eventpage", + name="twitter_og_description", + field=models.TextField( + blank=True, + help_text="If left blank, the OpenGraph description will be used.", + null=True, + verbose_name="Twitter OpenGraph description", + ), + ), + migrations.AddField( + model_name="eventpage", + name="twitter_og_image", + field=models.ForeignKey( + blank=True, + help_text="If left blank, the OpenGraph image will be used.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="Twitter OpenGraph image", + ), + ), + migrations.AddField( + model_name="eventpage", + name="twitter_og_title", + field=models.CharField( + blank=True, + help_text="If left blank, the OpenGraph title will be used.", + max_length=255, + null=True, + verbose_name="Twitter OpenGraph title", + ), + ), + migrations.AddField( + model_name="exhibitionpage", + name="twitter_og_description", + field=models.TextField( + blank=True, + help_text="If left blank, the OpenGraph description will be used.", + null=True, + verbose_name="Twitter OpenGraph description", + ), + ), + migrations.AddField( + model_name="exhibitionpage", + name="twitter_og_image", + field=models.ForeignKey( + blank=True, + help_text="If left blank, the OpenGraph image will be used.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="Twitter OpenGraph image", + ), + ), + migrations.AddField( + model_name="exhibitionpage", + name="twitter_og_title", + field=models.CharField( + blank=True, + help_text="If left blank, the OpenGraph title will be used.", + max_length=255, + null=True, + verbose_name="Twitter OpenGraph title", + ), + ), + migrations.AddField( + model_name="whatsonpage", + name="twitter_og_description", + field=models.TextField( + blank=True, + help_text="If left blank, the OpenGraph description will be used.", + null=True, + verbose_name="Twitter OpenGraph description", + ), + ), + migrations.AddField( + model_name="whatsonpage", + name="twitter_og_image", + field=models.ForeignKey( + blank=True, + help_text="If left blank, the OpenGraph image will be used.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="Twitter OpenGraph image", + ), + ), + migrations.AddField( + model_name="whatsonpage", + name="twitter_og_title", + field=models.CharField( + blank=True, + help_text="If left blank, the OpenGraph title will be used.", + max_length=255, + null=True, + verbose_name="Twitter OpenGraph title", + ), + ), + migrations.AlterField( + model_name="eventpage", + name="search_image", + field=models.ForeignKey( + blank=True, + help_text="Image that will appear when this page is shared on social media. This will default to the teaser image if left blank.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="OpenGraph image", + ), + ), + migrations.AlterField( + model_name="exhibitionpage", + name="search_image", + field=models.ForeignKey( + blank=True, + help_text="Image that will appear when this page is shared on social media. This will default to the teaser image if left blank.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="OpenGraph image", + ), + ), + migrations.AlterField( + model_name="whatsonpage", + name="search_image", + field=models.ForeignKey( + blank=True, + help_text="Image that will appear when this page is shared on social media. This will default to the teaser image if left blank.", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="+", + to="images.customimage", + verbose_name="OpenGraph image", + ), + ), + ] diff --git a/package-lock.json b/package-lock.json index 438ab8c21..50a0bf26c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,7 @@ "name": "ds-wagtail", "license": "ISC", "dependencies": { - "@nationalarchives/frontend": "0.1.43", + "@nationalarchives/frontend": "0.1.50", "jquery": "~3.7", "openseadragon": "~4.1" }, @@ -2743,9 +2743,9 @@ } }, "node_modules/@nationalarchives/frontend": { - "version": "0.1.43", - "resolved": "https://registry.npmjs.org/@nationalarchives/frontend/-/frontend-0.1.43.tgz", - "integrity": "sha512-y8Y6YCYR6pBSGLWacquM37PGjw8X2Pt23sRrQu/TxNoiy3/i5IUmcdbumoL0nRTKYnG91h6mlUKCD4QLerkFGg==" + "version": "0.1.50", + "resolved": "https://registry.npmjs.org/@nationalarchives/frontend/-/frontend-0.1.50.tgz", + "integrity": "sha512-ops5n4VJB2ykJYnuLs/DJHQ+zYNg2eTp35dnXBNbJN5xpKDwjG9a+ng27OdB8UJFI5hEoOhaqkDR1G3WIOUySQ==" }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", diff --git a/package.json b/package.json index c53198020..844be5d40 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "webpack-cli": "~5.1" }, "dependencies": { - "@nationalarchives/frontend": "0.1.43", + "@nationalarchives/frontend": "0.1.50", "jquery": "~3.7", "openseadragon": "~4.1" }, diff --git a/poetry.lock b/poetry.lock index 3bb9dcc68..54b3c7010 100644 --- a/poetry.lock +++ b/poetry.lock @@ -367,13 +367,13 @@ files = [ [[package]] name = "django" -version = "5.0.7" +version = "5.0.8" description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." optional = false python-versions = ">=3.10" files = [ - {file = "Django-5.0.7-py3-none-any.whl", hash = "sha256:f216510ace3de5de01329463a315a629f33480e893a9024fc93d8c32c22913da"}, - {file = "Django-5.0.7.tar.gz", hash = "sha256:bd4505cae0b9bd642313e8fb71810893df5dc2ffcacaa67a33af2d5cd61888f2"}, + {file = "Django-5.0.8-py3-none-any.whl", hash = "sha256:333a7988f7ca4bc14d360d3d8f6b793704517761ae3813b95432043daec22a45"}, + {file = "Django-5.0.8.tar.gz", hash = "sha256:ebe859c9da6fead9c9ee6dbfa4943b04f41342f4cea2c4d8c978ef0d10694f2b"}, ] [package.dependencies] @@ -453,13 +453,13 @@ Django = ">=3.2" [[package]] name = "django-filter" -version = "24.2" +version = "24.3" description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically." optional = false python-versions = ">=3.8" files = [ - {file = "django-filter-24.2.tar.gz", hash = "sha256:48e5fc1da3ccd6ca0d5f9bb550973518ce977a4edde9d2a8a154a7f4f0b9f96e"}, - {file = "django_filter-24.2-py3-none-any.whl", hash = "sha256:df2ee9857e18d38bed203c8745f62a803fa0f31688c9fe6f8e868120b1848e48"}, + {file = "django_filter-24.3-py3-none-any.whl", hash = "sha256:c4852822928ce17fb699bcfccd644b3574f1a2d80aeb2b4ff4f16b02dd49dc64"}, + {file = "django_filter-24.3.tar.gz", hash = "sha256:d8ccaf6732afd21ca0542f6733b11591030fa98669f8d15599b358e24a2cd9c3"}, ] [package.dependencies] @@ -636,13 +636,13 @@ doc = ["Sphinx", "sphinx-rtd-theme", "sphinxcontrib-spelling"] [[package]] name = "faker" -version = "26.0.0" +version = "26.2.0" description = "Faker is a Python package that generates fake data for you." optional = false python-versions = ">=3.8" files = [ - {file = "Faker-26.0.0-py3-none-any.whl", hash = "sha256:886ee28219be96949cd21ecc96c4c742ee1680e77f687b095202c8def1a08f06"}, - {file = "Faker-26.0.0.tar.gz", hash = "sha256:0f60978314973de02c00474c2ae899785a42b2cf4f41b7987e93c132a2b8a4a9"}, + {file = "Faker-26.2.0-py3-none-any.whl", hash = "sha256:7b123090774deff5f2cd3eb92a84dcbbf1e163f30a6d07321b7852c11bfe6a75"}, + {file = "Faker-26.2.0.tar.gz", hash = "sha256:81768de19012147521140f0d8bf5353e501ac42c1065d25e0cac455d3615c0a7"}, ] [package.dependencies] @@ -1185,13 +1185,13 @@ pyyaml = ">=5.1" [[package]] name = "mkdocs-material" -version = "9.5.30" +version = "9.5.31" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.30-py3-none-any.whl", hash = "sha256:fc070689c5250a180e9b9d79d8491ef9a3a7acb240db0728728d6c31eeb131d4"}, - {file = "mkdocs_material-9.5.30.tar.gz", hash = "sha256:3fd417dd42d679e3ba08b9e2d72cd8b8af142cc4a3969676ad6b00993dd182ec"}, + {file = "mkdocs_material-9.5.31-py3-none-any.whl", hash = "sha256:1b1f49066fdb3824c1e96d6bacd2d4375de4ac74580b47e79ff44c4d835c5fcb"}, + {file = "mkdocs_material-9.5.31.tar.gz", hash = "sha256:31833ec664772669f5856f4f276bf3fdf0e642a445e64491eda459249c3a1ca8"}, ] [package.dependencies] @@ -1758,62 +1758,64 @@ files = [ [[package]] name = "pyyaml" -version = "6.0.1" +version = "6.0.2" description = "YAML parser and emitter for Python" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] [[package]] diff --git a/scripts/src/all.js b/scripts/src/all.js index 3b5d43cd9..7e11adbec 100644 --- a/scripts/src/all.js +++ b/scripts/src/all.js @@ -1,3 +1,5 @@ import { initAll } from "../../node_modules/@nationalarchives/frontend/nationalarchives/all.mjs"; +import { GA4 } from "../../node_modules/@nationalarchives/frontend/nationalarchives/analytics.mjs"; initAll(); +new GA4({ addTrackingCode: false }); diff --git a/templates/articles/article_index_page.html b/templates/articles/article_index_page.html index 3cab45454..3328738b5 100644 --- a/templates/articles/article_index_page.html +++ b/templates/articles/article_index_page.html @@ -1,6 +1,6 @@ {% extends 'base_page.html' %} -{% load static i18n wagtailcore_tags wagtailimages_tags wagtailmetadata_tags %} +{% load static i18n wagtailcore_tags wagtailimages_tags %} {% block content %} {% include "includes/generic-intro.html" with title=page.title intro=page.intro %} diff --git a/templates/articles/article_page.html b/templates/articles/article_page.html index 3002e5bea..8755d60f2 100644 --- a/templates/articles/article_page.html +++ b/templates/articles/article_page.html @@ -1,6 +1,6 @@ {% extends 'base_page.html' %} -{% load static wagtailcore_tags sections_tags wagtailmetadata_tags %} +{% load static wagtailcore_tags sections_tags %} {% block breadcrumb %}{% endblock %} {% block content %} diff --git a/templates/articles/focused_article_page.html b/templates/articles/focused_article_page.html index fb2dbd71f..2555342f5 100644 --- a/templates/articles/focused_article_page.html +++ b/templates/articles/focused_article_page.html @@ -1,6 +1,6 @@ {% extends 'base_page.html' %} -{% load static wagtailcore_tags sections_tags wagtailmetadata_tags wagtailimages_tags %} +{% load static wagtailcore_tags sections_tags wagtailimages_tags %} {% block body_class %}template-focused-article{% endblock %} {% block breadcrumb %}{% endblock %} diff --git a/templates/articles/record_article_page.html b/templates/articles/record_article_page.html index 9baec6486..5f0fb109b 100644 --- a/templates/articles/record_article_page.html +++ b/templates/articles/record_article_page.html @@ -1,6 +1,6 @@ {% extends "base_page.html" %} -{% load static wagtailcore_tags sections_tags wagtailmetadata_tags %} +{% load static wagtailcore_tags sections_tags %} {% block content %} {% include "includes/record-revealed-intro.html" with display_content_warning=page.display_content_warning custom_warning_text=page.custom_warning_text %} diff --git a/templates/base.html b/templates/base.html index c1cb16268..87dbe9523 100644 --- a/templates/base.html +++ b/templates/base.html @@ -1,4 +1,4 @@ -{% load static wagtailuserbar wagtailcore_tags wagtailsettings_tags wagtailmetadata_tags robots_meta %} +{% load static wagtailuserbar wagtailcore_tags wagtailsettings_tags robots_meta %} {% get_settings %} @@ -6,21 +6,23 @@