From df66abc9657bdb74fa5d4eac9086dd85fa5f1783 Mon Sep 17 00:00:00 2001 From: <> Date: Wed, 1 Jan 2025 07:28:37 +0000 Subject: [PATCH] Deployed fb58c67 with MkDocs version: 1.6.1 --- .nojekyll | 0 404.html | 3288 ++++++++ DPL-CMS/Taskfile.yml | 63 + DPL-CMS/api-development/index.html | 3482 +++++++++ .../index.html | 3661 +++++++++ .../index.html | 3629 +++++++++ .../adr-002-user-handling/index.html | 3526 +++++++++ .../adr-003-ddb-react-integration/index.html | 3467 +++++++++ .../adr-004-ddb-react-caching/index.html | 3489 +++++++++ .../adr-005-api-mocking/index.html | 3600 +++++++++ .../adr-006-api-specification/index.html | 3537 +++++++++ .../index.html | 3525 +++++++++ .../index.html | 3536 +++++++++ .../index.html | 3516 +++++++++ .../adr-009-translation-system/index.html | 3608 +++++++++ .../adr-010-recurring-events/index.html | 3820 ++++++++++ .../index.html | 3638 +++++++++ .../adr-012-api-versioning/index.html | 3613 +++++++++ .../adr-013-javascript-logging/index.html | 3552 +++++++++ DPL-CMS/bnf/index.html | 3459 +++++++++ DPL-CMS/caching/index.html | 3464 +++++++++ DPL-CMS/code-guidelines/index.html | 4071 ++++++++++ DPL-CMS/config-import/index.html | 3485 +++++++++ DPL-CMS/configuration-management/index.html | 3753 +++++++++ .../diagrams/add-or-update-translation.puml | 6 + DPL-CMS/diagrams/adgangsplatform-login.puml | 56 + DPL-CMS/diagrams/adgangsplatform-logout.puml | 30 + .../render-png/adgangsplatform-login.png | Bin 0 -> 74299 bytes .../render-png/adgangsplatform-logout.png | Bin 0 -> 43257 bytes .../render-svg/adgangsplatform-login.svg | 66 + .../render-svg/adgangsplatform-logout.svg | 40 + DPL-CMS/event-field-config/index.html | 3518 +++++++++ DPL-CMS/event-integration/index.html | 3517 +++++++++ DPL-CMS/example-content/index.html | 3504 +++++++++ DPL-CMS/images/backup_tab.png | Bin 0 -> 8829 bytes DPL-CMS/images/devel-uuid.png | Bin 0 -> 82683 bytes DPL-CMS/images/retrieve.png | Bin 0 -> 59691 bytes DPL-CMS/images/virtiofs.png | Bin 0 -> 333032 bytes DPL-CMS/index.html | 3420 +++++++++ DPL-CMS/lagoon-environments/index.html | 3723 +++++++++ DPL-CMS/local-development/index.html | 3817 ++++++++++ DPL-CMS/logging/index.html | 3546 +++++++++ DPL-CMS/releases/index.html | 3452 +++++++++ DPL-CMS/translation-system/index.html | 3368 +++++++++ DPL-CMS/translation/index.html | 3623 +++++++++ DPL-Design-System/Icon-guidelines/index.html | 3392 +++++++++ .../adr-001-skeleton-screens/index.html | 3548 +++++++++ .../adr-002-form-styling/index.html | 3543 +++++++++ DPL-Design-System/index.html | 3854 ++++++++++ .../layout-documentation/index.html | 3660 +++++++++ DPL-Design-System/scss/index.html | 3592 +++++++++ DPL-Platform/Taskfile.yml | 63 + .../adr/adr-001-lagoon/index.html | 3511 +++++++++ .../adr/adr-002-rightsizing/index.html | 3716 +++++++++ .../adr/adr-003-system-alerts/index.html | 3496 +++++++++ .../index.html | 3549 +++++++++ .../index.html | 3516 +++++++++ DPL-Platform/architecture/adr/index.html | 3423 +++++++++ .../alertmanager-setup/index.html | 3540 +++++++++ DPL-Platform/architecture/index.html | 3384 +++++++++ .../performance-strategy/index.html | 3514 +++++++++ .../index.html | 3808 ++++++++++ DPL-Platform/backup/index.html | 3452 +++++++++ DPL-Platform/code-guidelines/index.html | 3672 +++++++++ .../diagrams/build-release-deploy.drawio | 1 + .../diagrams/cluster-support-workloads.drawio | 1 + .../diagrams/dpl-platform-azure.drawio | 1 + .../github-environment-repositories.drawio | 1 + DPL-Platform/diagrams/profiles.drawio | 1 + .../render-png/build-release-deploy.png | Bin 0 -> 823071 bytes .../render-png/cluster-support-workloads.png | Bin 0 -> 294591 bytes .../render-png/dpl-platform-azure.png | Bin 0 -> 486690 bytes .../github-environment-repositories.png | Bin 0 -> 121204 bytes DPL-Platform/diagrams/render-png/profiles.png | Bin 0 -> 157415 bytes .../diagrams/render-png/request-path.png | Bin 0 -> 447820 bytes .../render-png/terraform_overview.png | Bin 0 -> 37988 bytes .../render-svg/build-release-deploy.svg | 2 + .../render-svg/cluster-support-workloads.svg | 2 + .../render-svg/dpl-platform-azure.svg | 2 + .../github-environment-repositories.svg | 2 + DPL-Platform/diagrams/render-svg/profiles.svg | 2 + .../diagrams/render-svg/request-path.svg | 2 + .../render-svg/terraform_overview.svg | 54 + DPL-Platform/diagrams/request-path.drawio | 1 + DPL-Platform/diagrams/terraform-setup.puml | 28 + DPL-Platform/images/lagoon-ui-tasks-page.png | Bin 0 -> 70500 bytes .../images/loki-grafana-download-logs.png | Bin 0 -> 36231 bytes DPL-Platform/index.html | 3399 +++++++++ DPL-Platform/infrastructure/index.html | 3656 +++++++++ .../infrastructure/terraform/index.html | 3560 +++++++++ DPL-Platform/lagoon-projects/index.html | 3470 +++++++++ DPL-Platform/platform-environments/index.html | 3543 +++++++++ .../runbooks/access-kubernetes/index.html | 3494 +++++++++ .../add-generic-site-to-platform/index.html | 3537 +++++++++ .../add-library-site-to-platform/index.html | 3629 +++++++++ .../index.html | 3476 +++++++++ .../connecting-the-lagoon-cli/index.html | 3603 +++++++++ .../index.html | 3528 +++++++++ DPL-Platform/runbooks/index.html | 3400 +++++++++ .../index.html | 3546 +++++++++ .../runbooks/rabbitmq-broker/index.html | 3503 +++++++++ .../remove-site-from-platform/index.html | 3629 +++++++++ .../runbooks/reset-moduletest/index.html | 3489 +++++++++ .../retrieve-restore-backup/index.html | 3697 +++++++++ .../runbooks/retrieve-sites-logs/index.html | 3507 +++++++++ .../runbooks/run-a-lagoon-task/index.html | 3486 +++++++++ DPL-Platform/runbooks/scale-aks/index.html | 3549 +++++++++ .../set-environment-variable/index.html | 3520 +++++++++ .../index.html | 3618 +++++++++ .../index.html | 3767 +++++++++ .../runbooks/ui-reset-moduletest/index.html | 3481 +++++++++ .../runbooks/ui-sync-site-state/index.html | 3547 +++++++++ .../runbooks/update-upgrade-status/index.html | 3491 +++++++++ .../runbooks/upgrading-aks/index.html | 3635 +++++++++ .../runbooks/upgrading-lagoon/index.html | 3553 +++++++++ .../upgrading-support-workloads/index.html | 4488 +++++++++++ DPL-Platform/runbooks/using-dplsh/index.html | 3516 +++++++++ .../index.html | 3539 +++++++++ .../adr-001-rehydration/index.html | 3674 +++++++++ .../adr-002-ui-text-handling/index.html | 3516 +++++++++ .../architecture/adr-003-downshift/index.html | 3581 +++++++++ .../adr-004-relative-ci/index.html | 3594 +++++++++ .../architecture/adr-005-react-use/index.html | 3513 +++++++++ .../adr-006-unit-tests/index.html | 3512 +++++++++ .../adr-007-configuration/index.html | 3565 +++++++++ .../adr-008-error-handling/index.html | 3580 +++++++++ DPL-React/architecture/index.html | 3374 +++++++++ DPL-React/campaigns/index.html | 3537 +++++++++ DPL-React/code_guidelines/index.html | 4146 ++++++++++ DPL-React/error_handling/index.html | 3593 +++++++++ DPL-React/fbs-adapter-client/index.html | 3544 +++++++++ DPL-React/images/wiremock-studio-stub.png | Bin 0 -> 799902 bytes DPL-React/index.html | 4221 +++++++++++ DPL-React/request_mocking_wiremock/index.html | 3555 +++++++++ DPL-React/skeleton_screens/index.html | 3513 +++++++++ DPL-React/ui_text_handling/index.html | 3629 +++++++++ General/index.html | 3440 +++++++++ assets/images/favicon.png | Bin 0 -> 1870 bytes assets/javascripts/bundle.83f73b43.min.js | 16 + assets/javascripts/bundle.83f73b43.min.js.map | 7 + assets/javascripts/lunr/min/lunr.ar.min.js | 1 + assets/javascripts/lunr/min/lunr.da.min.js | 18 + assets/javascripts/lunr/min/lunr.de.min.js | 18 + assets/javascripts/lunr/min/lunr.du.min.js | 18 + assets/javascripts/lunr/min/lunr.el.min.js | 1 + assets/javascripts/lunr/min/lunr.es.min.js | 18 + assets/javascripts/lunr/min/lunr.fi.min.js | 18 + assets/javascripts/lunr/min/lunr.fr.min.js | 18 + assets/javascripts/lunr/min/lunr.he.min.js | 1 + assets/javascripts/lunr/min/lunr.hi.min.js | 1 + assets/javascripts/lunr/min/lunr.hu.min.js | 18 + assets/javascripts/lunr/min/lunr.hy.min.js | 1 + assets/javascripts/lunr/min/lunr.it.min.js | 18 + assets/javascripts/lunr/min/lunr.ja.min.js | 1 + assets/javascripts/lunr/min/lunr.jp.min.js | 1 + assets/javascripts/lunr/min/lunr.kn.min.js | 1 + assets/javascripts/lunr/min/lunr.ko.min.js | 1 + assets/javascripts/lunr/min/lunr.multi.min.js | 1 + assets/javascripts/lunr/min/lunr.nl.min.js | 18 + assets/javascripts/lunr/min/lunr.no.min.js | 18 + assets/javascripts/lunr/min/lunr.pt.min.js | 18 + assets/javascripts/lunr/min/lunr.ro.min.js | 18 + assets/javascripts/lunr/min/lunr.ru.min.js | 18 + assets/javascripts/lunr/min/lunr.sa.min.js | 1 + .../lunr/min/lunr.stemmer.support.min.js | 1 + assets/javascripts/lunr/min/lunr.sv.min.js | 18 + assets/javascripts/lunr/min/lunr.ta.min.js | 1 + assets/javascripts/lunr/min/lunr.te.min.js | 1 + assets/javascripts/lunr/min/lunr.th.min.js | 1 + assets/javascripts/lunr/min/lunr.tr.min.js | 18 + assets/javascripts/lunr/min/lunr.vi.min.js | 1 + assets/javascripts/lunr/min/lunr.zh.min.js | 1 + assets/javascripts/lunr/tinyseg.js | 206 + assets/javascripts/lunr/wordcut.js | 6708 +++++++++++++++++ .../workers/search.6ce7567c.min.js | 42 + .../workers/search.6ce7567c.min.js.map | 7 + assets/stylesheets/main.6f8fc17f.min.css | 1 + assets/stylesheets/main.6f8fc17f.min.css.map | 1 + assets/stylesheets/palette.06af60db.min.css | 1 + .../stylesheets/palette.06af60db.min.css.map | 1 + index.html | 3426 +++++++++ search/search_index.json | 1 + sitemap.xml | 3 + sitemap.xml.gz | Bin 0 -> 127 bytes stylesheets/extra.css | 5 + 185 files changed, 365456 insertions(+) create mode 100644 .nojekyll create mode 100644 404.html create mode 100644 DPL-CMS/Taskfile.yml create mode 100644 DPL-CMS/api-development/index.html create mode 100644 DPL-CMS/architecture/adr-001a-configuration-management-OLD/index.html create mode 100644 DPL-CMS/architecture/adr-001b-configuration-management/index.html create mode 100644 DPL-CMS/architecture/adr-002-user-handling/index.html create mode 100644 DPL-CMS/architecture/adr-003-ddb-react-integration/index.html create mode 100644 DPL-CMS/architecture/adr-004-ddb-react-caching/index.html create mode 100644 DPL-CMS/architecture/adr-005-api-mocking/index.html create mode 100644 DPL-CMS/architecture/adr-006-api-specification/index.html create mode 100644 DPL-CMS/architecture/adr-007-breadcrumb-and-url-patterns/index.html create mode 100644 DPL-CMS/architecture/adr-007-cypress-functional-testing/index.html create mode 100644 DPL-CMS/architecture/adr-008-external-system-integration/index.html create mode 100644 DPL-CMS/architecture/adr-009-translation-system/index.html create mode 100644 DPL-CMS/architecture/adr-010-recurring-events/index.html create mode 100644 DPL-CMS/architecture/adr-011-configuration-translation-system/index.html create mode 100644 DPL-CMS/architecture/adr-012-api-versioning/index.html create mode 100644 DPL-CMS/architecture/adr-013-javascript-logging/index.html create mode 100644 DPL-CMS/bnf/index.html create mode 100644 DPL-CMS/caching/index.html create mode 100644 DPL-CMS/code-guidelines/index.html create mode 100644 DPL-CMS/config-import/index.html create mode 100644 DPL-CMS/configuration-management/index.html create mode 100644 DPL-CMS/diagrams/add-or-update-translation.puml create mode 100644 DPL-CMS/diagrams/adgangsplatform-login.puml create mode 100644 DPL-CMS/diagrams/adgangsplatform-logout.puml create mode 100644 DPL-CMS/diagrams/render-png/adgangsplatform-login.png create mode 100644 DPL-CMS/diagrams/render-png/adgangsplatform-logout.png create mode 100644 DPL-CMS/diagrams/render-svg/adgangsplatform-login.svg create mode 100644 DPL-CMS/diagrams/render-svg/adgangsplatform-logout.svg create mode 100644 DPL-CMS/event-field-config/index.html create mode 100644 DPL-CMS/event-integration/index.html create mode 100644 DPL-CMS/example-content/index.html create mode 100644 DPL-CMS/images/backup_tab.png create mode 100644 DPL-CMS/images/devel-uuid.png create mode 100644 DPL-CMS/images/retrieve.png create mode 100644 DPL-CMS/images/virtiofs.png create mode 100644 DPL-CMS/index.html create mode 100644 DPL-CMS/lagoon-environments/index.html create mode 100644 DPL-CMS/local-development/index.html create mode 100644 DPL-CMS/logging/index.html create mode 100644 DPL-CMS/releases/index.html create mode 100644 DPL-CMS/translation-system/index.html create mode 100644 DPL-CMS/translation/index.html create mode 100644 DPL-Design-System/Icon-guidelines/index.html create mode 100644 DPL-Design-System/architecture/adr-001-skeleton-screens/index.html create mode 100644 DPL-Design-System/architecture/adr-002-form-styling/index.html create mode 100644 DPL-Design-System/index.html create mode 100644 DPL-Design-System/layout-documentation/index.html create mode 100644 DPL-Design-System/scss/index.html create mode 100644 DPL-Platform/Taskfile.yml create mode 100644 DPL-Platform/architecture/adr/adr-001-lagoon/index.html create mode 100644 DPL-Platform/architecture/adr/adr-002-rightsizing/index.html create mode 100644 DPL-Platform/architecture/adr/adr-003-system-alerts/index.html create mode 100644 DPL-Platform/architecture/adr/adr-004-declarative-site-management/index.html create mode 100644 DPL-Platform/architecture/adr/adr-005-declarative-site-management-2/index.html create mode 100644 DPL-Platform/architecture/adr/index.html create mode 100644 DPL-Platform/architecture/alertmanager-setup/index.html create mode 100644 DPL-Platform/architecture/index.html create mode 100644 DPL-Platform/architecture/performance-strategy/index.html create mode 100644 DPL-Platform/architecture/platform-environment-architecture/index.html create mode 100644 DPL-Platform/backup/index.html create mode 100644 DPL-Platform/code-guidelines/index.html create mode 100644 DPL-Platform/diagrams/build-release-deploy.drawio create mode 100644 DPL-Platform/diagrams/cluster-support-workloads.drawio create mode 100644 DPL-Platform/diagrams/dpl-platform-azure.drawio create mode 100644 DPL-Platform/diagrams/github-environment-repositories.drawio create mode 100644 DPL-Platform/diagrams/profiles.drawio create mode 100644 DPL-Platform/diagrams/render-png/build-release-deploy.png create mode 100644 DPL-Platform/diagrams/render-png/cluster-support-workloads.png create mode 100644 DPL-Platform/diagrams/render-png/dpl-platform-azure.png create mode 100644 DPL-Platform/diagrams/render-png/github-environment-repositories.png create mode 100644 DPL-Platform/diagrams/render-png/profiles.png create mode 100644 DPL-Platform/diagrams/render-png/request-path.png create mode 100644 DPL-Platform/diagrams/render-png/terraform_overview.png create mode 100644 DPL-Platform/diagrams/render-svg/build-release-deploy.svg create mode 100644 DPL-Platform/diagrams/render-svg/cluster-support-workloads.svg create mode 100644 DPL-Platform/diagrams/render-svg/dpl-platform-azure.svg create mode 100644 DPL-Platform/diagrams/render-svg/github-environment-repositories.svg create mode 100644 DPL-Platform/diagrams/render-svg/profiles.svg create mode 100644 DPL-Platform/diagrams/render-svg/request-path.svg create mode 100644 DPL-Platform/diagrams/render-svg/terraform_overview.svg create mode 100644 DPL-Platform/diagrams/request-path.drawio create mode 100644 DPL-Platform/diagrams/terraform-setup.puml create mode 100644 DPL-Platform/images/lagoon-ui-tasks-page.png create mode 100644 DPL-Platform/images/loki-grafana-download-logs.png create mode 100644 DPL-Platform/index.html create mode 100644 DPL-Platform/infrastructure/index.html create mode 100644 DPL-Platform/infrastructure/terraform/index.html create mode 100644 DPL-Platform/lagoon-projects/index.html create mode 100644 DPL-Platform/platform-environments/index.html create mode 100644 DPL-Platform/runbooks/access-kubernetes/index.html create mode 100644 DPL-Platform/runbooks/add-generic-site-to-platform/index.html create mode 100644 DPL-Platform/runbooks/add-library-site-to-platform/index.html create mode 100644 DPL-Platform/runbooks/changing-and-releasing-new-dplsh-version/index.html create mode 100644 DPL-Platform/runbooks/connecting-the-lagoon-cli/index.html create mode 100644 DPL-Platform/runbooks/how-release-a-new-version-for-approval-testing/index.html create mode 100644 DPL-Platform/runbooks/index.html create mode 100644 DPL-Platform/runbooks/monthly-release-to-editors-and-webmasters/index.html create mode 100644 DPL-Platform/runbooks/rabbitmq-broker/index.html create mode 100644 DPL-Platform/runbooks/remove-site-from-platform/index.html create mode 100644 DPL-Platform/runbooks/reset-moduletest/index.html create mode 100644 DPL-Platform/runbooks/retrieve-restore-backup/index.html create mode 100644 DPL-Platform/runbooks/retrieve-sites-logs/index.html create mode 100644 DPL-Platform/runbooks/run-a-lagoon-task/index.html create mode 100644 DPL-Platform/runbooks/scale-aks/index.html create mode 100644 DPL-Platform/runbooks/set-environment-variable/index.html create mode 100644 DPL-Platform/runbooks/troubleshoot-release-deployment/index.html create mode 100644 DPL-Platform/runbooks/troubleshoot-statuscake-monitor/index.html create mode 100644 DPL-Platform/runbooks/ui-reset-moduletest/index.html create mode 100644 DPL-Platform/runbooks/ui-sync-site-state/index.html create mode 100644 DPL-Platform/runbooks/update-upgrade-status/index.html create mode 100644 DPL-Platform/runbooks/upgrading-aks/index.html create mode 100644 DPL-Platform/runbooks/upgrading-lagoon/index.html create mode 100644 DPL-Platform/runbooks/upgrading-support-workloads/index.html create mode 100644 DPL-Platform/runbooks/using-dplsh/index.html create mode 100644 DPL-Platform/runbooks/weekly-release-to-editors-and-moduletests/index.html create mode 100644 DPL-React/architecture/adr-001-rehydration/index.html create mode 100644 DPL-React/architecture/adr-002-ui-text-handling/index.html create mode 100644 DPL-React/architecture/adr-003-downshift/index.html create mode 100644 DPL-React/architecture/adr-004-relative-ci/index.html create mode 100644 DPL-React/architecture/adr-005-react-use/index.html create mode 100644 DPL-React/architecture/adr-006-unit-tests/index.html create mode 100644 DPL-React/architecture/adr-007-configuration/index.html create mode 100644 DPL-React/architecture/adr-008-error-handling/index.html create mode 100644 DPL-React/architecture/index.html create mode 100644 DPL-React/campaigns/index.html create mode 100644 DPL-React/code_guidelines/index.html create mode 100644 DPL-React/error_handling/index.html create mode 100644 DPL-React/fbs-adapter-client/index.html create mode 100644 DPL-React/images/wiremock-studio-stub.png create mode 100644 DPL-React/index.html create mode 100644 DPL-React/request_mocking_wiremock/index.html create mode 100644 DPL-React/skeleton_screens/index.html create mode 100644 DPL-React/ui_text_handling/index.html create mode 100644 General/index.html create mode 100644 assets/images/favicon.png create mode 100644 assets/javascripts/bundle.83f73b43.min.js create mode 100644 assets/javascripts/bundle.83f73b43.min.js.map create mode 100644 assets/javascripts/lunr/min/lunr.ar.min.js create mode 100644 assets/javascripts/lunr/min/lunr.da.min.js create mode 100644 assets/javascripts/lunr/min/lunr.de.min.js create mode 100644 assets/javascripts/lunr/min/lunr.du.min.js create mode 100644 assets/javascripts/lunr/min/lunr.el.min.js create mode 100644 assets/javascripts/lunr/min/lunr.es.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.fr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.he.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hu.min.js create mode 100644 assets/javascripts/lunr/min/lunr.hy.min.js create mode 100644 assets/javascripts/lunr/min/lunr.it.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ja.min.js create mode 100644 assets/javascripts/lunr/min/lunr.jp.min.js create mode 100644 assets/javascripts/lunr/min/lunr.kn.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ko.min.js create mode 100644 assets/javascripts/lunr/min/lunr.multi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.nl.min.js create mode 100644 assets/javascripts/lunr/min/lunr.no.min.js create mode 100644 assets/javascripts/lunr/min/lunr.pt.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ro.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ru.min.js create mode 100644 assets/javascripts/lunr/min/lunr.sa.min.js create mode 100644 assets/javascripts/lunr/min/lunr.stemmer.support.min.js create mode 100644 assets/javascripts/lunr/min/lunr.sv.min.js create mode 100644 assets/javascripts/lunr/min/lunr.ta.min.js create mode 100644 assets/javascripts/lunr/min/lunr.te.min.js create mode 100644 assets/javascripts/lunr/min/lunr.th.min.js create mode 100644 assets/javascripts/lunr/min/lunr.tr.min.js create mode 100644 assets/javascripts/lunr/min/lunr.vi.min.js create mode 100644 assets/javascripts/lunr/min/lunr.zh.min.js create mode 100644 assets/javascripts/lunr/tinyseg.js create mode 100644 assets/javascripts/lunr/wordcut.js create mode 100644 assets/javascripts/workers/search.6ce7567c.min.js create mode 100644 assets/javascripts/workers/search.6ce7567c.min.js.map create mode 100644 assets/stylesheets/main.6f8fc17f.min.css create mode 100644 assets/stylesheets/main.6f8fc17f.min.css.map create mode 100644 assets/stylesheets/palette.06af60db.min.css create mode 100644 assets/stylesheets/palette.06af60db.min.css.map create mode 100644 index.html create mode 100644 search/search_index.json create mode 100644 sitemap.xml create mode 100644 sitemap.xml.gz create mode 100644 stylesheets/extra.css diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/404.html b/404.html new file mode 100644 index 0000000..51b5f77 --- /dev/null +++ b/404.html @@ -0,0 +1,3288 @@ + + + +
+ + + + + + + + + + + + + + +We use the RESTful Web Services and OpenAPI REST Drupal modules +to expose endpoints from Drupal as an API to be consumed by external parties.
+Drupal\rest\Plugin\ResourceBase
and annotating it with @RestResource
uri_paths
, route_parameters
and responses
in the annotation as
+ detailed as possible to create a strong specification.drush pm-enable restui
dpl_login_user_token
authentication provider for all resources which will
+ be used by the frontend this will provide a library or user token by default./openapi/rest?_format=json
to
+ ensure looks as intendedtask ci:openapi:validate
to validate the updated OpenAPI specificationtask ci:openapi:download
to download the updated OpenAPI specificationdrush pm-uninstall restui
drush config-export
openapi.json
See the new ADR in adr-001b-configuration-management.md
+Configuration management for DPL CMS is a complex issue. The complexity stems +from different types of DPL CMS sites.
+There are two approaches to the problem:
+A solution to configuration management must live up to the following test:
+drush site-install --existing-config -y
drush config-import -y
and see that the change is rolled back and the
+ configuration value is back to default. This shows that Core configuration
+ will remain managed by the configuration system.drush config-import -y
to see that no configuration is imported. This
+ shows that local configuration which can be managed by Editor libraries will
+ be left unchanged.drush config-import -y
to see that the module is not disabled and the
+ configuration remains unchanged. This shows that local configuration in the
+ form of new modules added by Webmaster libraries will be left unchanged.We use the Configuration Ignore module +to manage configuration.
+The module maintains a list of patterns for configuration which will be ignored +during the configuration import process. This allows us to avoid updating local +configuration.
+By adding the wildcard *
at the top of this list we choose an approach where
+all configuration is considered local by default.
Core configuration which should not be ignored can then be added to subsequent
+lines with the ~
which prefix. On a site these configuration entries will be
+updated to match what is in the core configuration.
Config Ignore also has the option of ignoring specific values within settings.
+This is relevant for settings such as system.site
where we consider the site
+name local configuration but 404 page paths core configuration.
The Deconfig module allows developers +to mark configuration entries as exempt from import/export. This would allow us +to exempt configuration which can be managed by the library.
+This does not handle configuration coming from new modules uploaded on webmaster +sites. Since we cannot know which configuration entities such modules will +provide and Deconfig has no concept of wildcards we cannot exempt the +configuration from these modules. Their configuration will be removed again at +deployment.
+We could use partial imports through drush config-import --partial
to not
+remove configuration which is not present in the configuration filesystem.
We prefer Config Ignore as it provides a single solution to handle the entire +problem space.
+The Config Ignore Auto module +extends the Config Ignore module. Config Ignore Auto registers configuration +changes and adds them to an ignore list. This way they are not overridden on +future deployments.
+The module is based on the assumption that if an user has access to a +configuration form they should also be allowed to modify that configuration for +their site.
+This turns the approach from Config Ignore on its head. All configuration is now +considered core until it is changed on the individual site.
+We prefer Config Ignore as it only has local configuration which may vary +between sites. With Config Ignore Auto we would have local configuration and +the configuration of Config Ignore Auto.
+Config Ignore Auto also have special handling of the core.extensions
+configuration which manages the set of installed modules. Since webmaster sites
+can have additional modules installed we would need workarounds to handle these.
The Config Split module allows +developers to split configurations into multiple groups called settings.
+This would allow us to map the different types of configuration to different +settings.
+We have not been able to configure this module in a meaningful way which also +passed the provided test.
+drush config-export
+ and have the appropriate configuration not ignored.core.extension
is ignored Core developers will have to explicitly
+ enable and uninstall modules through code as a part of the development
+ process.Configuration management for DPL CMS is a complex issue. The complexity stems +from different types of DPL CMS sites.
+There are two approaches to the problem:
+A solution to configuration management must live up to the following test:
+drush site-install --existing-config -y
drush config-import -y
and see that the change is rolled back and the
+ configuration value is back to default. This shows that Core configuration
+ will remain managed by the configuration system.drush config-import -y
to see that no configuration is imported. This
+ shows that local configuration which can be managed by Editor libraries will
+ be left unchanged.drush config-import -y
to see that the module is not disabled and the
+ configuration remains unchanged. This shows that local configuration in the
+ form of new modules added by Webmaster libraries will be left unchanged.We use the +Configuration Ignore module +and the Config Ignore Auto module +to manage configuration.
+The base module maintains a list of patterns for configuration which will be +ignored during the configuration import process. This allows us to avoid +updating local configuration.
+Here, we can add some of the settings files that we already know needs to be +ignored and admin-respected. +But in reality, we don't need to do this manually, because of the second module:
+Config Ignore Auto is only enabled on non-development sites.
+It works by treating any settings that are updated (site settings, module
+settings etc.) as to be ignored.
+These settings will NOT be overriden on next deploy by drush config-import
.
The consequences of using this setup is
+1) We need to ignore core.extension.yml
, for administrators to manage modules
+ This means that we need to enable/disable new modules using code.
+ See dpl_base.install
for how to do this, through Drupal update hooks.
+2) If a faulty permission has been added, or if a decision has been made to
+ remove an existing permission, there might be config that we dont want to
+ ignore, that is ignored on some libraries.
This means we'll first have to detect which libraries have overriden config
+
+```bash
+ drush config:get config_ignore_auto.settings ignored_config_entities
+ --format json
+```
+
+and then either decide to override it, or migrate the existing.
+
3) A last, and final consequence, is that we need to treat permissions more + strictly that we do now.
+An example is `adminster site settings` also both allows stuff we want to
+ignore (site name), but also things we don't want to ignore (404 node ID).
+
The Deconfig module allows developers +to mark configuration entries as exempt from import/export. This would allow us +to exempt configuration which can be managed by the library.
+This does not handle configuration coming from new modules uploaded on webmaster +sites. Since we cannot know which configuration entities such modules will +provide and Deconfig has no concept of wildcards we cannot exempt the +configuration from these modules. Their configuration will be removed again at +deployment.
+We could use partial imports through drush config-import --partial
to not
+remove configuration which is not present in the configuration filesystem.
We prefer Config Ignore as it provides a single solution to handle the entire +problem space.
+The Config Split module allows +developers to split configurations into multiple groups called settings.
+This would allow us to map the different types of configuration to different +settings.
+We have not been able to configure this module in a meaningful way which also +passed the provided test.
+drush config-export
+ and have the appropriate configuration not ignored.core.extension
is ignored Core developers will have to explicitly
+ enable and uninstall modules through code as a part of the development
+ process.There are different types of users that are interacticting with the CMS system:
+We need to be able to handle that both type of users can be authenticated and +authorized in the scope of permissions that are tied to the user type.
+We had some discussions wether the Adgangsplatform users should be tied to a +Drupal user or not. As we saw it we had two options when a user logs in:
+We ended up with desicion no. 2 mentioned above. So we create a Drupal user upon +login if it is not existing already.
+We use the OpeOpenID Connect / OAuth client module +to manage patron authentication and authorization. And we have developed a +plugin for the module called: Adgangsplatformen which connects the external +oauth service with dpl-cms.
+Editors and administrators a.k.a normal Drupal users and does not require +additional handling.
+Instead of creating a new user for every single user logging in via +Adgangsplatformen you could consider having just one Drupal user for all the +external users. That would get rid of the UUID -> Drupal user id mapping that +has been implemented as it is now. And it would prevent creation of a lot +of users. The decision depends on if it is necessary to distinguish between the +different users on a server side level.
+ + + + + + + + + + + + + +The DPL React components needs to be integrated and available for rendering in +Drupal. The components are depending on a library token and an access token +being set in javascript.
+We decided to download the components with composer and integrate them as Drupal +libraries.
+As described in adr-002-user-handling +we are setting an access token in the user session when a user has been through +a succesful login at Adgangsplatformen.
+We decided that the library token is fetched by a cron job on a regular basis
+and saved in a KeyValueExpirable
store which automatically expires the token
+when it is outdated.
The library token and the access token are set in javascript on the endpoint:
+/dpl-react/user.js
. By loading the script asynchronically when mounting the
+components i javascript we are able to complete the rendering.
The general caching strategy is defined in another document and this focused on +describing the caching strategy of DPL react and other js resources.
+We need to have a caching strategy that makes sure that:
+We have created a purger in the Drupal Varnish/Purge setup that is able to purge
+everything. The purger is being used in the deploy routine by the command:
+drush cache:rebuild-external -y
PURGE
request we found out, by studing the vcl of Lagoon, that the PURGE
+ request actually is being translated into a BAN
on req.url
.DPL CMS integrates with a range of other business systems through APIs. These +APIs are called both clientside (from Browsers) and serverside (from +Drupal/PHP).
+Historically these systems have provided setups accessible from automated +testing environments. Two factors make this approach problematic going forward:
+To address these problems and help achieve a goal of a high degree of test +coverage the project needs a way to decouple from these external APIs during +testing.
+We use WireMock to mock API calls. Wiremock provides +the following feature relevant to the project:
+wiremock-php
+ client library to instrument WireMock from PHP code. We modernized the
+ behat-wiremock-extension
+ to instrument with Behat tests which we use for integration testing.Software for mocking API requests generally provide two approaches:
+Generally record/replay makes it easy to setup a lot of mock data quickly. +However, it can be hard to maintain these records as it is not obvious what part +of the data is important for the test and the relationship between the +individual tests and the corresponding data is hard to determine.
+Consequently, this project prefers instrumentation.
+There are many other tools which provide features similar to Wiremock. These +include:
+wiremock-php
and
+ behat-wiremock-extension
libraryInstrumentation of Wiremock with PHP is made obsolete with the migration from +Behat to Cypress.
+ + + + + + + + + + + + + +DPL CMS provides HTTP end points which are consumed by the React components. We +want to document these in an established structured format.
+Documenting endpoints in a established structured format allows us to use tools +to generate client code for these end points. This makes consumption easier and +is a practice which is already used with other +services +in the React components.
+Currently these end points expose business logic tied to configuration in the +CMS. There might be a future where we also need to expose editorial content +through APIs.
+We use the RESTful Web Services Drupal module +to expose an API from DPL CMS and document the API using the OpenAPI 2.0/Swagger +2.0 specification as supported by the +OpenAPI and OpenAPI REST +Drupal modules.
+This is a technically manageable, standards compliant and performant solution +which supports our initial use cases and can be expanded to support future +needs.
+There are two other approaches to working with APIs and specifications for +Drupal:
+The tagging system we have (tags & categories) considers content as 'islands': +Two peices of content may be tagged with the same, but they do not know about +each other.
+DDF however needs a way to structure content hierarchies. +After a lot of discussion, we reached the conclusion that this can be +materialized through the breadcrumb - the breadcrumb is basically the frontend +version of the content structure tree that editors will create and manage.
+Because of this, the breadcrumb is "static" and not "dynamic" - e.g., on some +sites, the breadcrumb is built dynamically, based on the route that the user +takes through the site, but in this case, the whole structure is built by +the editors.
+However, some content is considered "flat islands" - e.g. articles and events +should not know anything about each other, but still be categorized.
+Either way, the breadcrumb also defines the URL alias.
+There are two types of breadcrumbs:
+All of this is managed by dpl_breadcrumb
module.
We tried using menus instead of taxonomy, based on experience from another +project, but it caused too much confusion and a general poor admin experience. +More info about +that in the ticket comments:
+A functional breadcrumb, that is very hard to replace/migrate if we choose a +different direction.
+ + + + + + + + + + + + + +DPL CMS employs functional testing to ensure the functional integrity of the +project.
+This is currently implemented using Behat +which allows developers to instrument a browser navigating through different use +cases using Gherkin, a +business readable, domain specific language. Behat is used within the project +based on experience using it from the previous generation of DPL CMS.
+Several factors have caused us to reevaluate that decision:
+We choose to replace Behat with Cypress for functional testing.
+There are other prominent tools which can be used for browser based functional +testing:
+DPL CMS is only intended to integrate with one external system: +Adgangsplatformen. This integration is necessary to obtain patron and library +tokens needed for authentication with other business systems. All these +integrations should occur in the browser through React components.
+The purpose of this is to avoid having data passing through the CMS as an +intermediary. This way the CMS avoids storing or transmitting sensitive data. +It may also improve performance.
+In some situations it may be beneficiary to let the CMS access external systems +to provide a better experience for business users e.g. by displaying options +with understandable names instead of technical ids or validating data before it +reaches end users.
+We choose to allow CMS to access external systems server-side using PHP. +This must be done on behalf of the library - never the patron.
+dpl_library_token.handler
service.The current translation system for UI strings in DPL CMS is based solely on code
+deployment of .po
files.
However DPL CMS is expected to be deployed in about 100 instances just to cover +the Danish Public Library institutions. Making small changes to the UI texts in +the codebase would require a new deployment for each of the instances.
+Requiring code changes to update translations also makes it difficult for
+non-technical participants to manage the process themselves. They have to find a
+suitable tool to edit .po
files and then pass the updated files to a
+developer.
This process could be optimized if:
+We keep using GitHub as a central source for translation files.
+We configure Drupal to consume translations from GitHub. The Drupal translation +system already supports runtime updates and consuming translations from a +remote source.
+We use POEditor to perform translations. POEditor is a
+translation management tool that supports .po
files and integrates with
+GitHub. To detect new UI strings a GitHub Actions workflow scans the codebase
+for new strings and notifies POEditor. Here they can be translated by
+non-technical users. POEditor supports committing translations back to GitHub
+where they can be consumed by DPL CMS instances.
This approach has a number of benefits apart from addressing the original +issue:
+.po
files.A consequence of this approach is that developers have to write code that
+supports scanning. This is partly supported by the Drupal Code Standards. To
+support contexts developers also have to include these as a part of the t()
+function call e.g.
// Good
+$this->t('A string to be translated', [], ['context' => 'The context']);
+$this->t('Another string', [], ['context' => 'The context']);
+// Bad
+$c = ['context' => 'The context']
+$this->t('A string to be translated', [], $c);
+$this->t('Another string', [], $c);
+
We could consider writing a custom sniff or PHPStan rule to enforce this
+For covering the functionality of scanning the code we had two potential +projects that could solve the case:
+ +Both projects can scan the codebase and generate a .po
or .pot
file with the
+translation strings and context.
At first it made most sense to go for Potx since it is used by
+localize.drupal.org and it has a long history.
+But Potx is extracting strings to a .pot
file without having the possibility
+of filling in the existing translations. So we ended up using Potion which can
+fill in the existing strings.
A flip side using Potion is that it is not being maintained anymore. But it +seems quite stable and a lot of work has been put into it. We could consider to +back it ourselves.
+We considered the following alternatives:
+Events make up an important part of the overall activities the Danish Public +Libraries. They aim to promote information, education and cultural activity. +Events held at the library have many different formats like book readings, +theater for children, tutoring and exhibitions of art. Some of these events +are singular and some are recurring.
+We define a recurring event as an event that occurs more than once over the +course of a period of time where the primary different between each occurrence +of the event is the event start and end time.
+A simple solution to this would be to use a multi-value date range field but +there are a number of factors that makes this more challenging:
+We have decided to base our solution on the Recurring Events Drupal module.
+The purpose of the module overlaps with our need in regards to handling +recurring events. The module is based on a construct where a recurring event +consists of an event series entity which has one or more related event +instances. Each instance corresponds to an specific date when the event +occurs.
+The module solves our requirements the following way:
+The module supports creating shedules for events with daily, weekly, monthly, +and annual repetitions. Each frequency can be customized, for example, which +days of the week the weekly event should repeat on (e.g., every Tuesday and +Thursday), which days of the month events should repeat on (e.g., the first +Wednesday, the third Friday).
+Event series and instances are fieldable entities. The module relies on the +Field Inheritance Drupal module +which allows data to be set on event series and reuse it on individual +entities.
+Recurring events support exceptions in two ways:
+The Field Inheritance module supports different modes of reuse. By using the +fallback method we can allow editors override values from event series on +individual instances.
+Recurring events creates a relationship between an event series and individual +instances. Through this relationsship we can determine what other instances +might be for an individual instance.
+It is possible to create lists of individual instances of events using Views.
+Recurring events uses a lot of vertical screen real estate on form elements +needed to define schedules (recurrence type, date/time, schedule, excluded +dates).
+The module supports defining event duration (30 minutes, 1 hour, 2 hours). +This is simpler than having to potentially repeat date and time.
+Recurring events lists six maintainers on Drupal.org and is supported by +three companies. Among them is Lullabot, a well known company within the +community.
+The module has over 1.000 sites reported using the module. The latest +version, 2.0.0-rc16, was recently released on December 1th 2023.
+The dependency, Field Inheritance, currently requires two patches to +Drupal Core.
+By introducing new entity types for event series and instance has some +consequences:
+For future work related to events we have to consider when it is appropriate to +use event series and when to use event instances.
+To create a consistent data structure for events we have to use recurring +events - even for singular events.
+We may have to do work to improve the editorial experience. This should +preferably be upstreamed to the Recurring Events module.
+Going forward we will have to participate in keeping Field Inheritance patches +to Drupal Core updated to match future versions until they are merged.
+In the process two alternative Drupal modules were considered:
+The translation system described in +adr-009-translation-system +handles solely the translations added through Drupals traditional translation handling.
+But there was a wish for being able to translate configuration related strings +as well. This includes titles, labels, descriptions, +and other elements in the admin area.
+We needed to find a way to handle translation of that kind as well.
+We went for a solution where we activated the Configuration Translation +Drupal core module and added the Configuration Translation PO contrib module.
+And we added a range of custom drush commands to handle the various +configuration translation tasks.
+By sticking to the handling of PO files in configuration handling that we are +already using in our general translation handling, +we can keep the current Github workflows with some alterations.
+Unfortunately handling translations of configuration on local sites +is still as difficult as before. +Translation of configuration texts cannot be found in the standard UI strings +translation list.
+With the config translation PO files added we tried to uncover if POEditor was +able to handle two PO files simultaneously in both import and export context. +It could not.
+But we still needed, in Drupal, to be able to import two different files: +One for general translations and one for configuration translations.
+We came up with the idea that we could merge the two files going when importing +into POEditor and split it again when exporting from POEditor.
+We tried it out and it worked so that was the solution we ended up with.
+We could activate only the config_translate module and add a danish translations +in config/sync. +But then:
+We could keep the machine names of the config in English but write the titles, +labels, descriptions in Danish.
+But that would have the following bad consequences:
+Change the Potion module to be able to scan configuration translations as well.
+We did not have a clear view of the concept of localizing configuration +translations in the same manner as the Potion module scans the codebase. +It could either be cumbersome to get the two worlds to meet in the same Potion +functionalities or simply incompatible.
+ + + + + + + + + + + + + +DPL CMS exposes data and functionality through HTTP endpoints which are +documented by an OpenAPI specification as described in +a previous ADR.
+Over time this API may need to be updated as the amount of data and +functionality increases and changes. Handling changes is an important aspect +of an API as such changes may affect third parties consuming this API.
+We use URI versioning of the API exposed +by DPL CMS.
+This is a simple approach to versioning which works well with the +RESTful Web Services Drupal module +that we use to develop HTTP endpoints with. Through the specification of the +paths provided by the endpoints we can define which version of an API the +endpoint corresponds to.
+When a breaking change is made the version of the API is increased by one e.g.
+from /api/v1/events
to /api/v2/events
.
We consider the following changes breaking:
+The following changes are not considered breaking:
+The existing version will continue to exist.
+Header based versioning is used by +other systems exposing REST APIs +in the infrastructure of the Danish Public Libraries. However we cannot see +this approach working well with the RESTful Web Services Drupal module. +It does not deal with multiple versions of an endpoint which different +specifications.
+Versionless GraphQL APIs are a common practice +Drupal can support GraphQL through a third party module +but using this would require us to reverse our approach to API development and +specification.
+Based on this approach we can provide updated versions of our API by leveraging +our existing toolchain.
+ + + + + + + + + + + + + +In the DPL CMS, we integrate React applications within a Drupal system as +outlined in a previous ADR. Effective +JavaScript error logging is crucial for timely detection, diagnosis, and +resolution of issues. It's essential that our logging setup not only captures +client-side errors efficiently but also integrates seamlessly with Drupal's +Watchdog logging system. This allows for logs to be forwarded to Grafana for +real-time monitoring and analysis, enhancing our system's reliability and +performance monitoring capabilities.
+After evaluating several options, we decided to integrate JSNLog +via the JSNLog Drupal module for +logging JavaScript errors. This integration allows us to capture and log +client-side errors directly from our React components into our server-side +logging infrastructure.
+The module does not have a stable release at the time of writing, which + poses risks regarding reliability and ongoing support.
+During testing, it generated excessively large numbers of log entries, + which could overwhelm our logging infrastructure and complicate error analysis.
+Custom built solution:
+Significant development time and resources required to build, test, and + maintain the module.
+Lacks the community support and proven stability found in established + third-party solutions, potentially introducing risks in terms of long-term + reliability and scalability.
+Third-party services:
+Bibliotekernes Nationale Formidling (henceforth "BNF") is a national
+team handling sharing of content for libraries. The bnf_server
and
+bnf_client
modules support their work.
The server module is enabled on the main BNF site, which acts as a hub +for content sharing. The BNF team uses this site to create and edit +content provided for the libraries.
+The client module is enabled on library sites and handles pushing and +fetching content to/from the BNF site.
+ + + + + + + + + + + + + +DPL-CMS relies on two levels of caching. Standard Drupal Core caching, and +Varnish as an accelerating HTTP cache.
+The Drupal Core cache uses Redis as its storage backend. This takes the load off +of the database-server that is typically shared with other sites.
+Further more, as we rely on Varnish for all caching of anonymous traffic, the +core Internal Page Cache module has been disabled.
+Varnish uses the standard Drupal VCL from lagoon.
+The site is configured with the Varnish Purge module and configured with a +cache-tags based purger that ensures that changes made to the site, is purged +from Varnish instantly.
+The configuration follows the Lagoon best practices - reference the +Lagoon documentation on Varnish +for further details.
+ + + + + + + + + + + + + +The following guidelines describe best practices for developing code for the DPL +CMS project. The guidelines should help achieve:
+Contributions to the core DPL CMS project will be reviewed by members of the +Core team. These guidelines should inform contributors about what to expect in +such a review. If a review comment cannot be traced back to one of these +guidelines it indicates that the guidelines should be updated to ensure +transparency.
+The project follows the Drupal Coding Standards +and best practices for all parts of the project: PHP, JavaScript and CSS. This +makes the project recognizable for developers with experience from other Drupal +projects. All developers are expected to make themselves familiar with these +standards.
+The following lists significant areas where the project either intentionally +expands or deviates from the official standards or areas which developers should +be especially aware of.
+js-
.
+ Example: <div class="gallery js-gallery">
- .gallery
must only be used in
+ CSS, js-gallery
must only be used in JS.is-active
, has-child
or similar are classes that can be used as
+ an interlink between JS and CSS.gallery.scss
which contains
+ .gallery, .gallery__container, .gallery__*
and so on. Avoid referencing
+ other components when possible.component.scss
, there must be a comment at the top, describing
+ the purpose of the component..pane-content
. Use
+ the Drupal theme system to write custom HTML and use precise, descriptive
+ class names. It is better to have several class names on the same element,
+ rather than reuse the same class name for several components.$layout-width * 0.60
) or give it a descriptive variable name
+ ($side-bar-width // 350px works well with the current $layout-width_
).class &
). The use of parent selector
+ results in complex deeply nested code which is very hard to maintain. There
+ are times where it makes sense, but for the most part it can and should be
+ avoided.dpl
.dpl
prefix is not required for modules which provide functionality deemed
+ relevant outside the DPL community and are intended for publication on
+ Drupal.org.Files provided by modules must be placed in the following folders and have the +extensions defined here.
+MODULENAME.*.yml
MODULENAME.module
MODULENAME.install
templates/*.html.twig
src/**/*.php
tests/**/*.php
/css/COMPONENTNAME.css
/scss/COMPONENTNAME.scss
js/*.js
img/*.(png|jpeg|gif|svg)
Programmatic elements such as settings, state values and views modules must +comply to a set of common guidelines.
+As there is no finite set of programmatic elements for a DPL CMS site these +apply to all types unless explicitly specified.
+The project follows the code structure suggested by the +drupal/recommended-project Composer template.
+Modules, themes etc. must be placed within the corresponding folder in this +repository. If a module developed in relation to this project is of general +purpose to the Drupal community it should be placed on Drupal.org and included +as an external dependency.
+A module must provide all required code and resources for it to work on its own +or through dependencies. This includes all configuration, theming, CSS, images +and JavaScript libraries.
+All default configuration required for a module to function should be +implemented using the Drupal configuration system and stored in the version +control with the rest of the project source code.
+If an existing module is expanded with updates to current functionality the +default behavior must be the same as previous versions or as close to this as +possible. This also includes new modules which replaces current modules.
+If an update does not provide a way to reuse existing content and/or +configuration then the decision on whether to include the update resides with +the business.
+Modules which alter or extend functionality provided by other modules should use +appropriate methods for overriding these e.g. by implementing alter hooks or +overriding dependencies.
+All interface text in modules must be in English. Localization of such texts +must be handled using the Drupal translation API.
+All interface texts must be provided with a context. This supports separation +between the same text used in different contexts. Unless explicitly stated +otherwise the module machine name should be used as the context.
+The project uses package managers to handle code which is developed outside of +the Core project repository. Such code must not be committed to the Core project +repository.
+The project uses two package manages for this:
+When specifying third party package versions the project follows these +guidelines:
+The project uses patches rather than forks to modify third party packages. This +makes maintenance of modified packages easier and avoids a collection of forked +repositories within the project.
+Code may return null or an empty array for empty results but must throw +exceptions for signalling errors.
+When throwing an exception the exception must include a meaningful error message +to make debugging easier. When rethrowing an exception then the original +exception must be included to expose the full stack trace.
+When handling an exception code must either log the exception and continue +execution or (re)throw the exception - not both. This avoids duplicate log +content.
+Drupal modules must use the Logging API. +When logging data the module must use its name as the logging channel and +an appropriate logging level.
+Modules integrating with third party services must implement a Drupal setting +for logging requests and responses and provide a way to enable and disable this +at runtime using the administration interface. Sensitive information (such as +passwords, CPR-numbers or the like) must be stripped or obfuscated in the logged +data.
+Code comments which describe what an implementation does should only be used +for complex implementations usually consisting of multiple loops, conditional +statements etc.
+Inline code comments should focus on why an unusual implementation has been +implemented the way it is. This may include references to such things as +business requirements, odd system behavior or browser inconsistencies.
+Commit messages in the version control system help all developers understand the +current state of the code base, how it has evolved and the context of each +change. This is especially important for a project which is expected to have a +long lifetime.
+Commit messages must follow these guidelines:
+When creating a pull request the pull request description should not contain any +information that is not already available in the commit messages.
+Developers are encouraged to read How to Write a Git Commit Message +by Chris Beams.
+The project aims to automate compliance checks as much as possible using static +code analysis tools. This should make it easier for developers to check +contributions before submitting them for review and thus make the review process +easier.
+The following tools pay a key part here:
+In general all tools must be able to run locally. This allows developers to get +quick feedback on their work.
+Tools which provide automated fixes are preferred. This reduces the burden of +keeping code compliant for developers.
+Code which is to be exempt from these standards must be marked accordingly in +the codebase - usually through inline comments (Eslint, +PHP Codesniffer). +This must also include a human readable reasoning. This ensures that deviations +do not affect future analysis and the Core project should always pass through +static analysis.
+If there are discrepancies between the automated checks and the standards +defined here then developers are encouraged to point this out so the automated +checks or these standards can be updated accordingly.
+ + + + + + + + + + + + + +Setting up a new site for testing certain scenarios can be repetitive. To avoid +this the project provides a module: DPL Config Import. This module can be used +to import configuration changes into the site and install/uninstall modules in a +single step.
+The configuration changes are described in a YAML file with configuration entry +keys and values as well as module ids to install or uninstall.
+/admin/config/configuration/import
The yaml file has two root elements configuration
and modules
.
A basic file looks like this:
+configuration:
+ # Add keys for configuration entries to set.
+ # Values will be merged with existing values.
+ system.site:
+ # Configuration values can be set directly
+ slogan: 'Imported by DPL config import'
+ # Nested configuration is also supported
+ page:
+ # All values in nested configuration must have a key. This is required to
+ # support numeric configuration keys.
+ 403: '/user/login'
+
+modules:
+ # Add module ids to install or uninstall
+ install:
+ - menu_ui
+ uninstall:
+ - redis
+
We use the Configuration Ignore Auto module +to manage configuration.
+In general all configuration is ignored except for configuration which should +explicitly be managed by DPL CMS core.
+Configuration management for DPL CMS is a complex issue. The complexity stems +from the following factors:
+There are multiple types of DPL CMS sites all using the same code base:
+All these site types must support the following properties:
+This can be split into different types of configuration:
+drush site-install --existing-config -y
drush config-export -y
config_ignore.settings.ignored_config_entities
with the ~
prefixconfig_ignore.settings
+ filedrush config-export -y
NB: The keys for these configuration files should already be in
+config_ignore.settings.ignored_config_entities
.
drush config-export -y
dpl_update.install
.function dpl_update_update_9000() {
+ \Drupal::service('module_installer')->install(['shortcut']);
+}
+
drush updatedb -y
drush config-export -y
function dpl_cms_update_9001() {
+ \Drupal::service('module_installer')->uninstall(['shortcut']);
+}
+
drush updatedb -y
drush config-export -y
drush deploy
NB: It is important that the official Drupal deployment procedure is followed. +Database updates must be executed before configuration is imported. Otherwise +we risk ending up in a situation where the configuration contains references +to modules which are not enabled.
+ + + + + + + + + + + + + + +4SD+v9mg{?~1j&-}P(W~q8oGkLZq?VArrDBW(k#^7>eC1~iE z(z~>uH!npci>xi)S%pL_&J>V6^6{no@|IToR-Ul&gZWme?-En}DJ=7N<*-;9AC(aY zQ>kMyh>}%!=1|xYv#nhi1i|LORIMJ&cV7tk9Ood(sIkOx#=n$*-R XRlB$nD$<;c8*HfYn?Z|s{8S=DLPk&tQWd} zsd9H@dh0Tq4sj!{av!*vKKRu}i@*CM`^xSP^VlVN-bC%(*D4-llsn9KcoMqaR5iWw zq04v))|TnQRxg?7m>k6;9Fw3=9tpF(H0M2knjN>kjzC z2-NQDyB*g&-(2FS@Vs**oMWnleTO>b`CGccXNvj4G=Z3&NIHDY(sR@4HF=fS8MMBy zRW U7pl*g(Wf4R7LO2pUNNGvgVFzBNO+5|3m|<0{YG3GmxD8P zq};XKy2=h7s-WL3JBuA$GJZNwRgGBu@x#`+=XkxqB5?g^<|syGL2dpTdI0J>sd#pr z@Y9f?e0YB3iCg^l)nmyq-pyC1FS_%X4)ZWY`C!IVH^Dn!3qNIUW(|Kx;A0b`f3j;^ zjryDvo{aKo6bn7*E)NuN`b>nLTHPH&-O&AZnLOx2wqWAp`BUgEe>s-b7(;`^$Q2~k z(K!Pf8yf=yl>H8Pv&Wv0kvC5j&-q7q(C%CflzTpHWI^rFaXybvvis-HpIu$gIMFM? z_wU0dTG09Wl|8Lmh#v|QKQt{1DX2+NZmwl9VPa%F+M14!h!8YK+lu;HDjqBQd1thQ z`)StZ6X++SiRfxVht=&|-re)H;d~`?V0@O_=&+hX3?}QvQ>+Ew*(v9f-uda5Wwy^F zR)(yzml2beR<3zPLlcOLUX};WYa{PlcHAry0>4I3$Da&&nay-0lc?9B4Q5kZ+w~FQ z;brF2wLgDK@|s>u0s~Cq`4&}hG7`yuC^kQ7(ZpjhQy)d6@?KwmVPWB6`&ICK*%7co z{2!VIzh;cnl_8r@oC*Tz@RC<_baXT{-R$(>Egoee;K4X3CrDbTI}eNDer71evftqs z)E4*kVC$uyV4B+1=F>9`&cX9&OFcL778%o (36^9(2SyjWvuJ7p=>csYMnENFijx;(0IVK6Z7 zL~=HI^S*6)#)I|x`o(WDaFniX^QU39sY-I>fKKD-DpOl?>+_|ax`RX! b`U&jSYx-(zKM&BVksTBsI(K4y*D zVwaptJ3l`jirt4LX8#br9zgvnFZ9u9p$1iahi_Xc_!vecivXEvbWBW4R8&-0SSc`s zupreNqXe!M&*~dJ)EUuA?cUd5z&doawY8mN^;oAJo6EG;dWC!B;zps+&DNOY=9 z)x^cS(gez$;8M`P2VVvToe**n^u(I)5Y9f)k#-dp(1R~5^PO#!%kUv=w4SPc_T%8- zXcb1kXAX~04)1p1^xp8nXrQTy(+pLroLdp;F`MANrdk{G7CH#LRwH9hd-kS{32E_A zVp?Bv-W>e!!c^3kk}u*8y|9sxFxQ8 !3C{X3Hu!TIRXa_)c_hn >8kK#-id3;MIz?)Pw1=)Ku zwXRV`IgDP#e-F`9CO^usv(4G09)s#})gcjQ=LTD~)C3JrIvuXdXgCSM2_6tL_kLk( zFnzee @kq@rVoRw*Q8G1&EA#RQP4SiQ~8;VJs(|;|u zp0wcdy%9gxpBfx!dinK?V7q| dVBu#R7Ww(&a&q`&r$7TpyJ3{d8yoq z>~s3|Dr$`k5R8@%8ZS$vTSPWbo}aZVhKdJ`kB^7nwcnaL{r-*Ik(Fc5_whQLSaw%% zOem(}1WkS|t?N$c=-|KrBlu!sDlaPo$2_ NAJOWxWIMn-JS7TuSB(Eg}yUN6=58 zOF$kE4Gj(Ft7K 4px{N>8I^y0}y)T|eaw~??UR2R;9IQ32H^(ZB+=$TV@nVxty1_}z{BFRUO9;vCR zwasH~D5za*xnSy7;>kSIu)84T^fb9|3@rQgvn9>gr@TH^5}|w5C2#~ia;viaps1sU znuke;yERT>dv!S(D8*!QxVu@66sTDlh)YRHNk~lA*%$AgV4nS#traW4jtK^AHuQO? zCvVgCQHUI(ht9LiN$NU(+miN~q-+?DWeAZ8BTD_l5MPdhG^5``lNu~%n}InD-@hje zB`uNpw^IoDz+qFz%PQ+-- P@me3Clkd=K{ietpPT|Ve~;`+;(WfmTtM#S)@Y0A#mZr)IKD)ObyFVj}b-!QmN zKj*#1W&K_uUE~(4g ==CpA*+14rQ%eMUwKs9S#3yB~qOyTOnl z?2VZQujO*P$_v+1{5+NDOdxs?#@PMFsp{KuYmN~OYB^M<1ABXTwZTLxZn!H4Qm?6o zaC< >^4fsM0xw+eDD=~Xg~MlY>P=-&?BeCC^?Wz zKLwkyf0#>zSUctfXQ^w?EP-TVM6f+w7eh{Icl*{Y^S)mJx-D?+`jeXFUrI-1R|t0H zUGoeKTy3B{UPP=xH*enjEM9{`!X9&0OjuVAFkcUrZ`P=$KXB=?Y&TUesj4tcb~T!0 zjkvz7aB`v+_JhLgbdO-+rcTXXa*|B$Qf;5n^Wd*v^RuLgTsOv?9I19S$BUE=0wY1h zwLI$UHweZO3q~xDyg21Us-~RjFhbnV+nli#YhEqy%!XPJGtARAu4P#?jTM=_( G^<3+h73ANg;&m{1ieW 0S$T(79(ru)WZD2IQ0CXtbGNuuFR(OHj0EvuPR< XsGqYG|09qD2nZhBFm;Mk}f>B176U7tRIU z7bQwsTeS{L@=t!&ox6AD@V-9?e~2GNgu4yZ?w`H=XtRZ=-I!F`X&fgi39SApGLd~Z zxh=TZWuuhOk(bM`_nz(g$kE2}0CF#ekJ ECk~nQ4(~iqh zhEVf?kx0(qfPhay1 Q$5=w9EinZ9+f_+|hcORmZL7_4=qev%%*uGOP|9#tj&X zSB{1=|080zoo+BIIC?MUXdyzc!j0jKaT8koW&cNDa`$dqtB{}XwQjn`15NvB7RFBn zcEDf3zP ?{#9o2 zUD$j3d?-haLSx(T_>d>5MSATItmGp0)$$~XzE@NnX0Fwk2pUg0%pXp)xD;nnGnv8~ zV+(RgiZoAUVJgJ@=61;AmR8#$4ryF4CGbhc&*L?5lWN7@`Hch=g)@y(FB}HF9nb zz91MLDIM)6J;blvL{)8Svi$m<7~k}Kq$;NXeL}hCVE1IW`)X|ymdh;~Ikbx0lMvc* znga`$mc|+4i`5Syrlu{4Tm<<^+fArnDov2n-FDR8+3DPLnSox7xTAT##h*uTU?KWL zThIsJ-s~j8{NnE>MaJ~@X#;Vr4e-_M_IzZGb+T#yGrP$uA8-o9YeI+ILKR11UJVW| zlW?Exe)Ht1Eaq6<7BaY#+gRM_B*PlRUy)W$Jr<+mrfNsGE)j8cb6ym^AJuXDe8}C; zQ(qcUNC}urM6J_dQw4tkuSM!kssjVPaRg;>?cvBw*-Hn@;?4+BW3UgFh{MsGyI*(_ zyqon=HzE=O!Vzu3MD#y$I18n%2J!TDLiv#2`qS5W&o;CWkIdC4;7l;cD14xg??67U z?6ld3X_!StikHsp2m~tRjvv3S?$dC@LMFTA+hGsPY3GBLoT@Tv)qK6t>fiB^`7kio zw{_(#+lS&T@@~`@a0rq`&o$~#w)y#4DhyRXFrc?J8EvFjN#R4C(&raSYV^dhEcL|C zju*2ga~_gx&fl*|Y20bPzdA%e(;d?!n=X=~*|5;O#PJdGr@c>oSQy=ru$zek>BZmK zX1-)aqmak`o0Dt`-CA{3u82d?p|YtPtdODC8aih`wxeiJFRH!No9ql eH&b4R&ZBa)3DdH zb}qn@V{q>Vlua#~ |^so#AuM+==5io_w~YvgQ?pE8zh!H`D^ zltXb!#_>s~zFHbSA`F>%$sTTd;nxv11U5Q2BWq)ag{2+ zD(C|4d8l$@c*c4A(Wd;eN(ZdT3HV~u`68~PV|-y1qgfVP>nWc_qx13I8f>lllhaT> zbK~K>PF%i%VLuV$I=jr9ghTltc_`}? uHFb@+1qyKqpMDWs8I`?8NFK_ zScZ(=R_X>(NqI3>F<)6qLua~tNe5{_$2XmrIps>PwpXBz{N)u>VOxNsY)4(E++CJ} zVzJG4jXl!nQM+p;-o*zDb#RZB8HY6Bc+P3Pa5da;s8~ZnGjh;jq@aCBL7Aej%53nJ zo^RKWH*R(tV*<9w W(~Q{2UDrxl6sYiy&%#TH6cZ>|UD7^yEPh<`pFV#tlQx}!*_T~y#} z;$P8(Jk$n;J1sbN6EvCO9@)R_?J~Utwm8BB8pl$6>#>s;H^Az{Bu+*dLdsD7z5E?C zILL(W-7r}I!I+RUGg~S?3!>)kPT5J#%`HjS{ZU_{27e^3s?iS17@yE&7{1smkK&qD zzN(QeO}BYlV=1NS9-WmA+%2DDefw8s7HMlAKz%cT`3l)1ZZYdqr`Zex8d0yE;F=th zmoJ@j*%*}?baM~Ke%{G%ZS4g)+69Hsnaf0;4<#>Tk-h2e)n;+r%ZXz#;oj>Yt;Ca* z(BQKQt7GLp$W*+At7LS(Rd+lr_)i~N B22&)cy;X^PdtQ%x2D|L87(a(k!@| z1c1()Q14oo^}C-R+`W6SS>1HrnQ-0=V^?yIlDexHMpd(ltsV~`0fH(ZE3=Sv+-B5w zps#paBA1ZF>3Xp8)%C@(%0KXm-JBJLv=cR3bf>H(3)&jZ7IaCx4RS?~te3d^zwSmg zIN4I~jmi!yPy9HUs I;hj?&9&ey(N3*TP(^aiQP$|8$|D+?gCXFJ9ML)5)rrZ1ycjn}P77%`B9T z`5WO1<3XGDkRLkkiL{Hkh{~YTeUx&xbfIt{5>&s(Y-Vv03Fr1^u7hVBt|*Y3?vTRX zv_(#?+ zD@{` GdALd1c zrAObP860au2@a>TfMXdmN650a#P8+wh!gAf#_1Uh7H*++X WLSS9L-bB4eeoQ>O(r+7%UtY!wpY^M>60bHp>t?|F5?$q{Kd40{F4)w_kO^^E zp aOlqz*~+a%BJy+Mr-qty`weYs&)ec11&C3++HE+t?>;z~+B@9dO(_i{EcEZ!sL zXu{En{`E{dPllx?zJ59ByF8hvC#H(_BU==rDPkv!hX((teS{U+^GeI@hUzuGYc89W zO;tvC`BekFth?o3lLwEJdFsTDo?q@N*={%vs*-LL{*FAEk6s ol})<|DBR~_-;ojwVppViO*lU^~l-rE}3I$s>n zH!t#`mer!&**m-C@(*GRBQ&dx2Z<{aqFFW0l;mOY+hmGc14Zca`^qnK12=<1Q>#p7 zW~gmfz0!F1UCoThVU?O~T8R-5rZQ>ip(AO+8`e;%a36*2fvvCeZIkBf^EDyIOJ~?o zbrt#O8dEOh9D&IEe87{hTo_(`*A-V*91Lsw=Fiy_Tfx+#3qYYRuBMqpM`vc&9zsXR z8Wa?4<^akQ_Y>X1WzLzurq#8{)fEQIY^tdjhY1=9#nSB4 iP4PE}G)=ZXIM=;r_{dsHm{8u!zWvJ <=7)3BqJjO^n0$mvd!kiVKCc_Z1-I}A{IzSv;hq? z%JXBF^Z!)LqCdRc6Hh=)JXL3JfgtV9)u=8?mac>hD>g05ddRC47hE8q+=5|4Jy5X9 zYKE9yq`~K+{QUehDn&!b1*)Y2oRxl9w~4=SJ6Zu`Me4YKH++wTgx4K;Ue1}PBQWoK zi>Trjrj)_|Y#Q5@zFB{&q`qUxc-BpoCU{h%=+9WDY`e_~NR9P!uR^wT!}+NMU?bCt z3hI!%LG__3S(KuREDREsVO{F3tgJYsI6oH0eg|T!0{~-&^OPh4sJXb#zIk4@XhPVj zs*Vk0N-XB;wTFl)M |Hx^(*5M0tJ)Q=zkbptE0Dk@AQBjpj z-_ee1rGn;cuzoD=C|^aFrh8FwaTA3aY@MFpzkk=NwFzU+frpav&7_Osc79GxO)cA5 zjM0I_*CXNUbm|q@H*ePOb 3wB!G0A`D*?I+BT7_6qz$wQ6y)U>TwUx8p0+;|C2yw?1&px)_xo>;s0|LsKQ;UL z&RJ#UDgXWeAZ4ALYU{nSv$E(5Xctr$J}rD*Sh2FQ3TnT}E|^)sOC)#FC#%`3I2;h! zhi!NYfv>mGe=`L1(B4d9SXfwebX6^PPwlp@o=Q!aUKm;Ue0X{{=uwa`{kn;-?3yVT z7NsG6pO?AlIla!;91K^v-{Zt?kB*OBJ&~NLw6?NZ*mT~WX^351J^dEaR%Au?=d5F5 z3@>I~02fbxMpGU6+{H*!PP5Tc4ilc1hY#RvuYi#^jb+*;bD_mctZ5ip!C;27x2_g_ z^T+v$7j cK3p_#dr&PH-LJ@C;_dW@*N7}{~m^z>0iUJ->`UYc~x5RTX2r%H<9}( z`FMo0wDqaq@Y&@s+0v{qiR3(r0RLaU<9kWvm(bOzPhD3{g)nPDC!@DHCSw*SA;*JA zuErzy`D+z2=H}`1^K-kcDdUZXgc#d;?nQYkb}QL7?f4VqUXf-EBQGy6GjoL@G^uSF z%BE;J(S^uBU +`wloXEa1wS1)VPa2QRS!0otK7KMHU8~PD5 zo0HWHjEsA|T-)c{?x?f*P`<61hBH95(mtn-ey%;TyQg@S-5u#-YHC_aSz |kHLV(kWa$YU;g9dxH6 yZtzC5+0Mix3?D_9!~fmj@z+Jh_Lb_H_g*RgYM{Xh`vE5 zM@>!5gfbF|?Ck6Wf$7GrTbpC0^I1~y2VMj#ZERYA{XmupxVX4vODEzKET^!UPkmLv z34Y9_ypHCcJHp7wo;{nJn|lvptB=TFCqVM~O8LsVy1HdBd>Z8(7O8AQmSzo$2K>j5 z>FVlG$g|2G5KTuj#3E{K*TqXldFh63!>qI6dZdi{pVUEX&DQg9{m#ROUy_pOBm4sb z KOP9jKZM?4C z(stft(JNLniqv=LrmH6Auo61EFOh`K6jusOiW`nV=*)_j0i>5ov35H!tkXeGYsMp{ z_c{3FxsB(0-DNv#`Nbf%4wppH9&C&=fhalmBTykr(iql=keNpobg)$7qYc903a?mr z#=)${|3EsCGZWq(O3HBk)F`#l^$e2QZ?gyJ;n>*N)6-KZ_kK_-_pmVw&mdlNOAE!9 zPMQ~j#__ns?6LwAyYuafcPOP3at`y;%O`S?u$+!j{*l$`x?_+yGj(`~d0oCr>YeT_ zVq#(*gU}5keiDe4*S#UGv2DM;dr^E00h4e2gg5adOH^G`nTi^ND*Zp_M-{>GUhFDh zOd3m%_Y|`pJR>7hDbh4n6%`24k3yUsub&MoKxiCUSy+^8>D9_IK(d8!u(O|ZGc?gY z58`?H^r>&5{;U@+uWaJo_m?hT9xlu3U^&$7gxroB)p6I7|E%7)Sv6U4|M9(hph21z z4R} _eipoaJWazr92(g$D-Hlx1RlU)aI0=w6QuKv>h zTa3zQi-aGpH=QoUx6O1b#H#NcAEP$Pp*PoURtGXc*AATf;p6=5A?5Ipfx$tgWso8k zQ`OBUt2TokF#)=H(Ga-7Esf5PN7K!T@7+EBTp(fW>l|rtk`+@i1_$q|Fs0nzGp| z3*>c$3keI0VGqf)*f?d80YaJ!*ol)<{Zd}lsfvzkX0!n%008PuoSgZIalRt?U_<-e zx8+c>lbU>vvA(AH+W93VFU_O|$>`|w@8Nqy{1M4$W^dh-l`fc(1(<*`w`I)Pky_yf zsKy3)#Ro>l!Kvv>6$Dm3K&-u}RB~}S2RU0Qo7Sjl?)hmsaKXEL P|+ch%EHf-4yI3f0O*& m2IJ*s~E=apNK0a7~NCN4GcXqGu@3xbgtkWp} z%kPw%i6~PNR2xz|5U!gOmD`PHTL5GnwUfF>o}|hLL}OO?)NR$8?ELyJ8bUl?W)L`` z%wr3_qbd6O4go)`m2)3o>lY ^PNXh&Z=*#1}_C(kza@d{t%;S zGu!NQso46C{fy)C!Rp{452zS)z=$ty02m`BDaq}2{ Qcz6IDZ~@o_ zoH?h^n5E%TC?5LU@(ycO7= 2fgdiZ zHi+0@qdVV&;10Pu*x3Z|)$HtSSs*~uDpp_B2O&;LWN#QZ#!AzFsvT82?7lPG2Qh>h zBtsB|$wY#7&ewCZxQ0pV+%72>d$~Ps5j0cdb3FAUVSy+*q_&ed|FotVbY~=W -lD za1$5M{7u0jW4<&aBqWrPnVGu?VhSS8q^RcHQIrO;;7a^`=TS=jW2C->mBV($tb5P6 z D3Tbk#HM2tcV@_FpZmlM$0Y#^{5u-S zake4s@ifzY%XS*8N%iUsQE_@cZTZdTpBU!pQLlDsiW31oL5v9OYX(0GzsEh>b)e(F z;1;{5c+ic@b#x|-Ck-V@M52;RF4 *-A~3q46Yl$85E1&S<^KZ5h;aTd*bdCJ z=-1DC!%(Qja&KbWOjqX3P`S3S{-B0lWnVuX7LIZ&PwI~ViN+6GYh zQL2wfcrh> !e1wMYdzpSUHClP^plT1-R z?KxsWA`46nX)At*aFf$A^Cm7S1_liO%;`Et3FEugPawS$I^RfS|L|_CL>KV1=tyJv z%j4i+8>Ykf`Zr!WFzNjZ4ebpD;L|8(^U@iShCLGbyhxz!kg8Vx9uz{4x$K7aAV5JP zFE8IB5J4pqJ(do?eEIU=;Gjm8=_7pnOxTajYS;`2{2*tk05Z66m4gDRm2Xyfw79*U zoRF}=!4|xV`9M`hex9TH()x5~Q!~J26l3i_e)J@83^Fkj+Nmrw#f1)5#f7%Df8Yj7 z@xXaouYCzayub|sw`e@gDy{Bo^}mOOOF|n9`T6BlIGs|q@wE={1+-aEsQXf^VH~q@ z8fT$^Poqw>^U+3ZVxsHr+>aLuSrryA?ZbZnDI)=aW{^`W4xNv;8f@2 aB#F56P z+7F2ML|Ke$Y_HV(B1R#l@!K!)t6T5hy#qTlv?ogHnz1RYH>MSaf;7rZ+VB#>+DkcI zPMY(qFcmRCr4%$O@lE@j }}a1^Io_ww6NX;bWuXS0a*n}BoKZOC@7rLqo@g>p06 zZK!VM{= S(Y8@^&4~o=(rPaeS}n{26-^)lC;er5X%s3{bVT ztv`6nFeCd5N1193IF#F@S+z0lVd^VZ)+11pj3miRGQEzVlp=9EnY~9PZ8}}6G|Bzs z$=k^)Gsr2ZR)+j=1~JlQD0g>vC1U86y30UUm U)}R(sA2J!n`JyM_XCv{FXdo>y6;i3hDU>g zpeK!LVh(`IU|U0Z;VZynaB$qS7CoK?mLg^D?>r;=+RISP8%FNU@Gi_%Jy56?h