From 25125063ef31ee3c46a260a03490055342a59a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Cutzach?= Date: Thu, 6 Jun 2024 11:38:56 +0200 Subject: [PATCH] feat: Image Docker custom de grist aux couleur de la Marianne --- .github/workflows/docker-build.yaml | 4 + dockerfiles/grist/Dockerfile | 41 +++++ dockerfiles/grist/ressources/custom.css | 59 +++++++ dockerfiles/grist/ressources/dinum-custom.js | 147 ++++++++++++++++++ .../grist/ressources/marianne-48x48.png | Bin 0 -> 5590 bytes 5 files changed, 251 insertions(+) create mode 100644 dockerfiles/grist/Dockerfile create mode 100644 dockerfiles/grist/ressources/custom.css create mode 100644 dockerfiles/grist/ressources/dinum-custom.js create mode 100644 dockerfiles/grist/ressources/marianne-48x48.png diff --git a/.github/workflows/docker-build.yaml b/.github/workflows/docker-build.yaml index eaf2225..94ef814 100644 --- a/.github/workflows/docker-build.yaml +++ b/.github/workflows/docker-build.yaml @@ -29,6 +29,10 @@ jobs: image: lasuite/keycloak-apps path: dockerfiles/keycloak-apps cmd_version: "echo \"$(cat dockerfiles/keycloak-apps/Dockerfile | head -n1 | cut -d' ' -f2)\" >> $GITHUB_ENV" + - dockerfile: dockerfiles/grist/Dockerfile + image: lasuite/grist + path: dockerfiles/grist + cmd_version: "grep -Po \"GRIST_\\KVERSION=.*\" dockerfiles/grist/Dockerfile | head -n 1 >> $GITHUB_ENV" steps: - uses: actions/create-github-app-token@v1 diff --git a/dockerfiles/grist/Dockerfile b/dockerfiles/grist/Dockerfile new file mode 100644 index 0000000..161a2a2 --- /dev/null +++ b/dockerfiles/grist/Dockerfile @@ -0,0 +1,41 @@ +ARG GRIST_VERSION=1.1.14 + +FROM gristlabs/grist:$GRIST_VERSION + +WORKDIR /grist/static + +ARG LASUITE_VERSION=1.0.1 +ARG LASUITE_ARCHIVE=gouvfr-lasuite-integration-$LASUITE_VERSION.tgz + +RUN apt-get update +RUN apt-get install -y wget + +RUN wget https://github.com/numerique-gouv/lasuite-integration/releases/download/integration-v$LASUITE_VERSION/$LASUITE_ARCHIVE +RUN tar -zxvf $LASUITE_ARCHIVE + +# Archive is extracted as "package" +# We move it to @gouvfr-lasuite/integration to be complient with +# https://integration.lasuite.numerique.gouv.fr/guides/gaufre/ +RUN mkdir @gouvfr-lasuite +RUN mv package @gouvfr-lasuite/integration + +RUN rm $LASUITE_ARCHIVE + +COPY ressources/* ./ +RUN mv marianne-48x48.png ui-icons/Logo/ + +RUN apt-get remove --purge -y wget + +WORKDIR /grist + +RUN \ + groupadd grist -g 10022 && \ + useradd -ms /bin/bash grist -g grist -u 10022 && \ + chown -R grist:grist /grist && \ + chown -R grist:grist /persist + +USER grist + +# Variable to force grist to use custom.css and dinum-custom.js +ENV APP_STATIC_INCLUDE_CUSTOM_CSS true +ENV GRIST_INCLUDE_CUSTOM_SCRIPT_URL /v/unknown/dinum-custom.js diff --git a/dockerfiles/grist/ressources/custom.css b/dockerfiles/grist/ressources/custom.css new file mode 100644 index 0000000..8cf0d7b --- /dev/null +++ b/dockerfiles/grist/ressources/custom.css @@ -0,0 +1,59 @@ +@import url("@gouvfr-lasuite/integration/dist/css/gaufre.css"); + +:root { + /* logo */ + --icon-GristLogo: url("ui-icons/Logo/marianne-48x48.png") !important; + --grist-logo-bg: #ffffff !important; + --grist-logo-size: 22px 22px !important; + + /* colors */ + --grist-color-light-grey: #f6f6f6 !important; + --grist-color-medium-grey: #dddddd99 !important; + --grist-color-medium-grey-opaque: #e5e5e5 !important; + --grist-color-dark-grey: #dddddd !important; + --grist-color-light: #ffffff !important; + --grist-color-dark: #2f2f2f !important; + --grist-color-dark-bg: #2f2f2f !important; + --grist-color-slate: #929292 !important; + --grist-color-light-green: #000091 !important; + --grist-color-dark-green: #2323ff !important; + --grist-color-darker-green: #2323ff !important; + --grist-color-lighter-green: #cacafb !important; + --grist-color-lighter-blue: #0078f3 !important; + --grist-color-light-blue: #0063cb !important; + --grist-color-cursor: #000091 !important; + --grist-color-selection: #ececfe !important; + --grist-color-selection-opaque: #ececfe !important; + --grist-color-selection-darker-opaque: #e3e3fd !important; + --grist-color-inactive-cursor: #cacafb !important; + --grist-color-hover: #cecece !important; + --grist-color-error: #d64d00 !important; + --grist-color-warning: #fea941 !important; + --grist-color-warning-bg: #dd962c !important; + --grist-color-backdrop: #3a3a3a !important; + --grist-label-text-bg: #ffffff !important; + --grist-label-active-bg: #f0f0f0 !important; + --grist-primary-fg: #000091 !important; + --grist-primary-fg-hover: #2323ff !important; + --grist-primary-bg: #ffffff !important; + --grist-control-bg: #ffffff !important; + --grist-control-fg: #000091 !important; + --grist-primary-fg-hover: #2323ff !important; + --grist-control-border: 1px solid #2323ff !important; + --grist-toast-bg: #040404 !important; + + /* Custom inputs colors*/ + --grist-actual-cell-color: #6e6ef2 !important; + --accent-color: #6e6ef2 !important; +} + +.test-rule-permissions [class*=deny] { + background-color: #ff0000; + background-image: linear-gradient(-45deg, #ff0000 14px, white 15px 16px, #ff0000 16px); + border-color: #ff0000; +} + +.test-rule-permissions [class*=allow] { + background-color: #16b378; + border-color: #16b378; +} diff --git a/dockerfiles/grist/ressources/dinum-custom.js b/dockerfiles/grist/ressources/dinum-custom.js new file mode 100644 index 0000000..27e0a2a --- /dev/null +++ b/dockerfiles/grist/ressources/dinum-custom.js @@ -0,0 +1,147 @@ +// ======================== +// START dialog +// ======================== +(function maintainanceDialog() { + // CONFIGURER ICI + const config = { + // CEST = heure d'été (+0200), CET = heure d'hiver (). + selectedTz: 'CEST', + + // Date du début de la maintenance + startDateWithoutTimezone: '2024-04-18T17:30:00', + + // Durée de la maitenance prévue (en minutes). + // ASTUCE: Si la maintenance dure moins longtemps que prévu, réduire a postériori la valeur de cette variable de sorte à ce qu'elle n'apparaisse plus. + // Pas besoin de committer ensuite le changement dans le repo. + maintainanceDurationMinutes: 60, + }; + // FIN CONFIGURER + + const tz = { CEST: '0200', CET: '0100' }; + + const maintainanceStartDate = new Date(`${config.startDateWithoutTimezone}+${tz[config.selectedTz]}`); + const maintainanceEndDate = (function () { + const date = new Date(maintainanceStartDate); // If I may, Date API is really ackward, especially because it is not immutable + date.setMinutes(maintainanceStartDate.getMinutes() + config.maintainanceDurationMinutes); + return date; + })(); + + + function showDialog() { + const dialog = document.createElement('dialog'); + dialog.id = 'maintenancePopin'; + const dateFormatter = new Intl.DateTimeFormat('fr-FR', { + dateStyle: 'full', + timeStyle: 'short', + timeZone: 'Europe/Paris', + }); + const timeFormatter = new Intl.DateTimeFormat('fr-FR', { + timeStyle: 'short', + timeZone: 'Europe/Paris', + }); + + dialog.innerHTML = ` +

+ Une maintenance de Grist est prévue le ${dateFormatter.format(maintainanceStartDate)} heure de Paris + jusqu'à ${timeFormatter.format(maintainanceEndDate)}, période durant laquelle le service sera momentanément indisponible.
+ Pour toute remarque ou question, merci de nous contacter via donnees@anct.gouv.fr

+

Merci de votre compréhension

+
+

+ + +

+ +
+ `; + document.body.appendChild(dialog); + document.getElementById('fermerPopinMaintenance').onclick = function onMessageUnderstood() { + localStorage.maintainanceStartDateAgreement = maintainanceStartDate.toISOString(); + } + document.getElementById('jaiCompris').onchange = function (ev) { + document.getElementById('fermerPopinMaintenance').disabled = !ev.target.checked; + } + dialog.showModal(); + } + + if (Date.now() < maintainanceEndDate.getTime() && + (!localStorage.maintainanceStartDateAgreement || localStorage.maintainanceStartDateAgreement !== maintainanceStartDate.toISOString())) { + window.addEventListener('load', showDialog); + } +})(); + +// ======================== +// END dialog +// ======================== + +// ======================== +// START gauffre +// ======================== + + +window.addEventListener('load', async (event) => { + await waitForElm('body.interface-full'); + // gristApp.topAppModel.appObs is not populated at that point, listen for the observer to their first change + + let listener; + listener = gristApp.topAppModel.appObs.addListener( async (appObs) => { + // Gauffre must be displayed only in home pages + if (appObs.pageType.get() !== "home"){ + return 1; + } + // We wait for header bar to be available in DOM to insert Gauffre in it + await waitForElm('.test-top-header'); + + // Create gauffre button Tag + const gristBar = document.getElementsByClassName('test-top-header')[0]; + const gauffreDiv = document.createElement('div'); + const gauffreButton = document.createElement('button'); + const gauffreText = "Les services de La Suite numérique"; + + gauffreDiv.className = 'gauffre-container'; + gauffreButton.type = "button"; + gauffreButton.className = "lasuite-gaufre-btn lasuite-gaufre-btn--vanilla js-lasuite-gaufre-btn"; + gauffreButton.title = gauffreText; + gauffreButton.text = gauffreText; + gauffreDiv.appendChild(gauffreButton); + gristBar.insertBefore(gauffreDiv, gristBar.lastChild); + + // Create gauffre script Tag + const gauffreScript = document.createElement('script'); + + gauffreScript.id = "lasuite-gaufre-script"; + gauffreScript.setAttribute('async', true); + gauffreScript.setAttribute('defer', true); + gauffreScript.src = "https://integration.lasuite.numerique.gouv.fr/api/v1/gaufre.js"; + document.head.insertBefore(gauffreScript, document.head.lastChild); + + // Should be disposed so we only listen for changes once, but failed to achieve doing that. + // listener.dispose(); + }); +}); + +// from https://stackoverflow.com/a/61511955 +function waitForElm(selector) { + return new Promise(resolve => { + if (document.querySelector(selector)) { + return resolve(document.querySelector(selector)); + } + + const observer = new MutationObserver(mutations => { + if (document.querySelector(selector)) { + observer.disconnect(); + resolve(document.querySelector(selector)); + } + }); + + // If you get "parameter 1 is not of type 'Node'" error, see https://stackoverflow.com/a/77855838/492336 + observer.observe(document.body, { + childList: true, + subtree: true + }); + }); +} + +// ======================== +// END gauffre +// ======================== diff --git a/dockerfiles/grist/ressources/marianne-48x48.png b/dockerfiles/grist/ressources/marianne-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..894bbe32a7deecae431de8b1d3cf2c57c69220f2 GIT binary patch literal 5590 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 literal 0 HcmV?d00001