From 92f3b770efed38064e3a2dd8ca628f10b011fe29 Mon Sep 17 00:00:00 2001 From: Jakob Wierzba Date: Thu, 22 May 2025 13:25:03 +0200 Subject: [PATCH 01/19] email attachment flow, implementation --- froide/foirequest/models/message.py | 7 +- .../foirequest/upload_postal_message_new.html | 8 +- froide/foirequest/views/message.py | 5 +- .../docupload/attachment-manager.vue | 3 + .../docupload/attachments-table.vue | 20 +- .../docupload/lib/attachments-store.js | 11 +- .../components/postupload/post-upload.vue | 265 +++++++++++++++--- 7 files changed, 259 insertions(+), 60 deletions(-) diff --git a/froide/foirequest/models/message.py b/froide/foirequest/models/message.py index 3925b1332..1b0b25c16 100644 --- a/froide/foirequest/models/message.py +++ b/froide/foirequest/models/message.py @@ -94,7 +94,12 @@ class MessageKind(models.TextChoices): MessageKind.IMPORT: "cloud-download", } -MANUAL_MESSAGE_KINDS = {MessageKind.POST, MessageKind.PHONE, MessageKind.VISIT} +MANUAL_MESSAGE_KINDS = { + MessageKind.POST, + MessageKind.PHONE, + MessageKind.VISIT, + MessageKind.EMAIL, +} MESSAGE_ID_PREFIX = "foimsg." diff --git a/froide/foirequest/templates/foirequest/upload_postal_message_new.html b/froide/foirequest/templates/foirequest/upload_postal_message_new.html index 2dfd5bd50..a45d23fa7 100644 --- a/froide/foirequest/templates/foirequest/upload_postal_message_new.html +++ b/froide/foirequest/templates/foirequest/upload_postal_message_new.html @@ -4,6 +4,8 @@ {% load block_helper %} {% load frontendbuild %} {% load form_helper %} +{% load content_helper %} +{% load foirequest_tags %} {% block title %} {% blocktrans with title=object.title %}Upload postal mail for “{{ title }}”{% endblocktrans %} {% endblock title %} @@ -19,10 +21,14 @@
{% csrf_token %} {# mw-100 crucial for pdf-redaction's dynamic maxWidth calculations #} - + + {# TODO if kind=Email, if message.plaintext... #} + {# avoid whitespace, since this will be pre-wrap. spaceless didn't work... #} + + {% endblock body %} diff --git a/froide/foirequest/views/message.py b/froide/foirequest/views/message.py index ab6c197de..bc7434b1d 100644 --- a/froide/foirequest/views/message.py +++ b/froide/foirequest/views/message.py @@ -180,7 +180,8 @@ def edit_postal_message(request, foirequest, message_id): FoiMessage.with_drafts, request=foirequest, pk=int(message_id) ) if not message.can_edit: - return render_400(request) + print("would 400 here, but continuiuing") + # return render_400(request) filingcabinet_js_config = get_js_config(request) ctx = { "settings": { @@ -559,6 +560,7 @@ def edit_postal_message(request, foirequest, message_id): "foirequest_json": json.dumps( { "id": foirequest.id, + "title": foirequest.title, "url": foirequest.url, "public_body": { "id": foirequest.public_body.id, @@ -572,6 +574,7 @@ def edit_postal_message(request, foirequest, message_id): "date_min": foirequest.created_at.date().isoformat(), "user_is_staff": json.dumps(request.user.is_staff), "schemas_json": json.dumps(schemas), + "message": message, "message_json": "null" if message is None else json.dumps( diff --git a/frontend/javascript/components/docupload/attachment-manager.vue b/frontend/javascript/components/docupload/attachment-manager.vue index feb8e3c3b..86318380f 100644 --- a/frontend/javascript/components/docupload/attachment-manager.vue +++ b/frontend/javascript/components/docupload/attachment-manager.vue @@ -68,6 +68,7 @@ const onlineHelp = ref() :subset="attachments.approved" actions table-selection + cards-selection selection-action-delete selection-action-make-result nudge-redaction @@ -82,6 +83,7 @@ const onlineHelp = ref() :subset="attachments.notApproved" actions table-selection + cards-selection selection-action-delete selection-action-approve nudge-redaction @@ -95,6 +97,7 @@ const onlineHelp = ref() :subset="attachments.irrelevant" actions table-selection + cards-selection selection-action-delete badges-type /> diff --git a/frontend/javascript/components/docupload/attachments-table.vue b/frontend/javascript/components/docupload/attachments-table.vue index e8d2e14e8..49cbd99de 100644 --- a/frontend/javascript/components/docupload/attachments-table.vue +++ b/frontend/javascript/components/docupload/attachments-table.vue @@ -12,7 +12,7 @@ import AttachmentBadgeFiletype from './attachment-badge-filetype.vue' const i18n = inject('i18n') -const { subset, asCardThreshold, actions, actionDelete, cardsSelection, tableSelection, selectionButtons, selectionActionApprove, selectionActionDelete, selectionActionMakeResult, nudgeRedaction, badgesNew, badgesRedaction, badgesType, badgesResolution, cardsBgTransparent, dense } = defineProps({ +const { subset, asCardThreshold, asTableOnly, actions, actionDelete, cardsSelection, tableSelection, selectionButtons, selectionActionApprove, selectionActionDelete, selectionActionMakeResult, nudgeRedaction, badgesNew, badgesRedaction, badgesType, badgesResolution, cardsBgTransparent, dense } = defineProps({ subset: { type: Array, required: true @@ -21,6 +21,9 @@ const { subset, asCardThreshold, actions, actionDelete, cardsSelection, tableSel type: Number, default: 6 }, + asTableOnly: { + type: Boolean + }, actions: Boolean, actionDelete: { type: Boolean, @@ -73,7 +76,7 @@ const { subset, asCardThreshold, actions, actionDelete, cardsSelection, tableSel dense: Boolean }) -const asCards = ref(subset.length < asCardThreshold) +const asCards = ref((subset.length < asCardThreshold) && !asTableOnly) watch( () => subset.length, @@ -84,13 +87,6 @@ watch( } ) -/* when asCard flips over/under threshold (when something is uploaded/deleted) - * we need to prevent things being still selected without the possibility to - * change the selection */ -watch(asCards, (newValue) => { - if ((newValue && !cardsSelection) || (!newValue && !tableSelection)) selectNone() -}) - const selected = computed(() => subset.filter(_ => attachments.selectedIds.has(_.id))) const selectAllEl = ref() @@ -358,8 +354,8 @@ const makeResultSelected = async () => { v-for="att in subset" :key="att.id" class="d-flex flex-column px-md-1 py-1 position-relative flex-md-row align-items-md-center px-5" :class="{ - 'bg-primary-subtle': attachments.selectedIds.has(att.id), - 'border-top': subset.length > 1 + 'bg-primary-subtle': tableSelection && attachments.selectedIds.has(att.id), + 'border-top': (subset.length > 1) && !dense }" @click.self="toggleSelection('table', att.id)" > @@ -427,7 +423,7 @@ const makeResultSelected = async () => { -
+
diff --git a/frontend/javascript/components/docupload/lib/attachments-store.js b/frontend/javascript/components/docupload/lib/attachments-store.js index a8be89915..234bff9e0 100644 --- a/frontend/javascript/components/docupload/lib/attachments-store.js +++ b/frontend/javascript/components/docupload/lib/attachments-store.js @@ -10,6 +10,7 @@ const useAttachmentsStore = defineStore('attachments', { isFetching: false, allRaw: [], images: [], + selectedOnceIds: new Set, selectedIds: new Set, approvingIds: new Set, availableIds: new Set, @@ -44,7 +45,7 @@ const useAttachmentsStore = defineStore('attachments', { return this.all.filter((d) => d.canRedact) }, redactNudgable() { - return this.all.filter((d) => d.canRedact && !d.is_redacted && !d.redacted) + return this.all.filter((d) => d.canRedact && !d.is_redacted && !d.redacted && !d.approved) }, convertable() { return this.all.filter((d) => (d.is_irrelevant || d.is_image) && !d.converted) @@ -106,6 +107,14 @@ const useAttachmentsStore = defineStore('attachments', { }, unselectSubset(subset) { subset.forEach(_ => this.selectedIds.delete(_.id)) + }, + selectAllNewRedactableAttachments() { + this.redactable.forEach(att => { + if (!this.selectedOnceIds.has(att.id)) { + this.selectedOnceIds.add(att.id) + this.selectedIds.add(att.id) + } + }) } } }) diff --git a/frontend/javascript/components/postupload/post-upload.vue b/frontend/javascript/components/postupload/post-upload.vue index f6e5d66f3..ea9fcf0c9 100644 --- a/frontend/javascript/components/postupload/post-upload.vue +++ b/frontend/javascript/components/postupload/post-upload.vue @@ -21,6 +21,8 @@ import AttachmentsTable from '../docupload/attachments-table.vue' const props = defineProps({ config: Object, message: Object, + message_timestamp_relative: String, + message_timestamp_local: String, schemas: Object, foirequest: Object, date_min: String, @@ -61,6 +63,7 @@ window.FDSdebug = (val) => { debug.value = typeof val === 'boolean' ? val : !debug.value localStorage.setItem('fds-postupload-debug', debug.value ? 'yes' : '') } +window.__fds_atta = attachments const debugSkipDate = () => { values.date = document.forms.postupload.elements.date.max @@ -401,9 +404,23 @@ const pdfRedactionUploaded = () => { refreshAttachments() } +/* --- state/flow --- */ + +const isEmailResponse = (props.message.kind === 'email' && props.message.is_response) + +const questionTotal = isEmailResponse ? 2 : 5 + +// TODO i18n +const progressSteps = isEmailResponse + ? ['Lesen & Schwärzen', 'Infos eingeben'] + : ['Hochladen', 'Infos eingeben', 'Schwärzen'] + /* --- state machine, functionality --- */ -const firstStep = STEP_INTRO +const firstStep = isEmailResponse + ? STEP_INTRO_EMAIL + : STEP_INTRO +console.log('### #') const stepHistory = reactive([firstStep]) const step = computed(() => stepHistory.length ? stepHistory[stepHistory.length - 1] : false @@ -444,6 +461,7 @@ const isGotoValid = computed(() => { // vuex mutation style constants/"symbols" const STEP_INTRO = 'STEP_INTRO' // 1100 +const STEP_INTRO_EMAIL = 'STEP_INTRO_EMAIL' const STEP_DOCUMENTS_CONVERT = 'STEP_DOCUMENTS_CONVERT' // 1201 const STEP_DOCUMENTS_SORT = 'STEP_DOCUMENTS_SORT' // 1201 const STEP_DOCUMENTS_CONVERT_PDF = 'STEP_DOCUMENTS_CONVERT_PDF' // 1202 @@ -467,10 +485,10 @@ const stepsConfig = { [STEP_INTRO]: { next: () => { if (attachments.images.length) { - return STEP_DOCUMENTS_SORT + return STEP_DOCUMENTS_SORT // dot:label="has images" } if (attachments.convertable.length > 0) { - return STEP_DOCUMENTS_CONVERT + return STEP_DOCUMENTS_CONVERT // dot:label="has convertables" } console.log('uploads were documents, not images, passing by image sorting') return STEP_DOCUMENTS_OVERVIEW @@ -480,8 +498,32 @@ const stepsConfig = { mobileHeaderTitle: i18n.value.addLetter } }, + [STEP_INTRO_EMAIL]: { + next: () => { + if (attachments.convertable.length > 0) { + return STEP_DOCUMENTS_CONVERT // dot:style=dotted + } + else if (attachments.redactNudgable.length === 0) { + return STEP_MESSAGE_STATUS // dot:style=dotted + } + console.log('uploads were documents, not images, passing by image sorting') + return STEP_REDACTION_REDACT // dot:style=dotted + }, + context: { + // TODO + progressStep: 0, + mobileHeaderTitle: 'Neue Antwort der Behöde' // i18n.value.addLetter TODO + } + }, [STEP_DOCUMENTS_CONVERT]: { - next: STEP_DOCUMENTS_OVERVIEW, + next: () => { + if (isEmailResponse) { + return attachments.redactNudgable.length > 0 + ? STEP_REDACTION_REDACT // dot:style=dotted + : STEP_MESSAGE_STATUS // dot:style=dotted + } + return STEP_DOCUMENTS_OVERVIEW + }, onEnter: () => { guardBeforeunload(true) }, @@ -505,7 +547,11 @@ const stepsConfig = { } }, [STEP_DOCUMENTS_CONVERT_PDF]: { - next: STEP_DOCUMENTS_OVERVIEW, + next: () => { + return isEmailResponse + ? STEP_REDACTION_REDACT + : STEP_DOCUMENTS_OVERVIEW + }, context: { progressStep: 0, mobileHeaderTitle: i18n.value.letterUploadOrScan @@ -514,6 +560,7 @@ const stepsConfig = { [STEP_DOCUMENTS_OVERVIEW]: { next: STEP_MESSAGE_SENT_OR_RECEIVED, onEnter: () => { + guardBeforeunload(true) pdfRedactionUploaded() }, context: { @@ -524,8 +571,9 @@ const stepsConfig = { [STEP_MESSAGE_SENT_OR_RECEIVED]: { next: STEP_MESSAGE_PUBLICBODY_CHECK, context: { - progressStep: 1, - mobileHeaderTitle: i18n.value.enterInformation + progressStep: 1, // n/a for isEmailResponse + mobileHeaderTitle: i18n.value.enterInformation, + questionCurrent: 1, // n/a for isEmailResponse } }, [STEP_MESSAGE_PUBLICBODY_CHECK]: { @@ -536,14 +584,15 @@ const stepsConfig = { : STEP_MESSAGE_PUBLICBODY_UPDATE }, context: { - progressStep: 1, - mobileHeaderTitle: i18n.value.enterInformation + progressStep: 1, // n/a for isEmailResponse + mobileHeaderTitle: i18n.value.enterInformation, + questionCurrent: 2, // n/a for isEmailResponse } }, [STEP_MESSAGE_PUBLICBODY_UPDATE]: { next: STEP_MESSAGE_DATE, context: { - progressStep: 1, + progressStep: 1, // n/a for isEmailResponse mobileHeaderTitle: i18n.value.enterInformation } }, @@ -559,8 +608,9 @@ const stepsConfig = { updateValidity('registered_mail_date') }, context: { - progressStep: 1, + progressStep: 1, // n/a for isEmailResponse mobileHeaderTitle: i18n.value.enterInformation, + questionCurrent: 3, // n/a for isEmailResponse isGotoValid: () => { if (isDesktop.value && values.is_registered_mail) return validity.date && validity.registered_mail_date @@ -575,7 +625,7 @@ const stepsConfig = { updateValidity('registered_mail_date') }, context: { - progressStep: 1, + progressStep: 1, // n/a for isEmailResponse mobileHeaderTitle: i18n.value.enterInformation, isGotoValid: () => validity.registered_mail_date } @@ -583,9 +633,10 @@ const stepsConfig = { [STEP_MESSAGE_STATUS]: { next: () => { if (!isDesktop.value && requestIsResolved.value) - return STEP_MESSAGE_MESSAGE_RESOLUTION + return STEP_MESSAGE_MESSAGE_RESOLUTION // dot:label="mobile&unresolved" // TODO: replace all STEP_REDACTION_PICKER with `if uploadedDocuments.length === 1 ? ... : STEP_REDACTION_PICKER` - if (!values.is_response) return STEP_REDACTION_PICKER + // if is not response, can't have cost, so skip over the cost flow + if (!values.is_response && !isEmailResponse) return STEP_REDACTION_PICKER // dot:label="no cost" return requestHadCosts.value ? STEP_MESSAGE_COST_CHECK_LAST : STEP_MESSAGE_COST_CHECK_ANY @@ -595,8 +646,9 @@ const stepsConfig = { updateValidity('resolution') }, context: { - progressStep: 1, + progressStep: 1, // same for isEmailResponse mobileHeaderTitle: i18n.value.enterInformation, + questionCurrent: isEmailResponse ? 1 : 4, isGotoValid: () => { if (isDesktop.value && requestIsResolved.value) { return validity.status && validity.resolution @@ -607,7 +659,7 @@ const stepsConfig = { }, [STEP_MESSAGE_MESSAGE_RESOLUTION]: { next: () => { - if (values.is_reponse) return STEP_REDACTION_PICKER + if (!values.is_response && !isEmailResponse) return STEP_REDACTION_PICKER // dot:label="no cost" return requestHadCosts.value ? STEP_MESSAGE_COST_CHECK_LAST : STEP_MESSAGE_COST_CHECK_ANY @@ -616,7 +668,7 @@ const stepsConfig = { updateValidity('resolution') }, context: { - progressStep: 1, + progressStep: 1, // same for isEmailResponse mobileHeaderTitle: i18n.value.enterInformation, isGotoValid: () => validity.resolution } @@ -625,22 +677,28 @@ const stepsConfig = { next: () => { if (!isDesktop.value && requestUpdateCosts.value) return STEP_MESSAGE_COST_UPDATE - return STEP_REDACTION_PICKER + return isEmailResponse + ? STEP_DOCUMENTS_OVERVIEW_REDACTED // dot:style=dotted + : STEP_REDACTION_PICKER }, context: { - progressStep: 1, - mobileHeaderTitle: i18n.value.enterInformation + progressStep: 1, // same for isEmailResponse + mobileHeaderTitle: i18n.value.enterInformation, + questionCurrent: isEmailResponse ? 2 : 5, } }, [STEP_MESSAGE_COST_CHECK_LAST]: { next: () => { if (!isDesktop.value && requestUpdateCosts.value) return STEP_MESSAGE_COST_UPDATE - return STEP_REDACTION_PICKER + return isEmailResponse + ? STEP_DOCUMENTS_OVERVIEW_REDACTED // dot:style=dotted + : STEP_REDACTION_PICKER }, context: { - progressStep: 1, + progressStep: 1, // same for isEmailResponse mobileHeaderTitle: i18n.value.enterInformation, + questionCurrent: isEmailResponse ? 2 : 5, isGotoValid: () => { if (isDesktop.value && requestUpdateCosts.value) return validity.costs return true @@ -648,13 +706,17 @@ const stepsConfig = { } }, [STEP_MESSAGE_COST_UPDATE]: { - next: STEP_REDACTION_PICKER, + next: () => { + return isEmailResponse + ? STEP_DOCUMENTS_OVERVIEW_REDACTED // dot:style=dotted + : STEP_REDACTION_PICKER + }, onEnter: () => { updateValidity('costs') }, context: { isGotoValid: () => validity.costs, - progressStep: 1, + progressStep: 1, // same for isEmailResponse mobileHeaderTitle: i18n.value.enterInformation } }, @@ -667,9 +729,11 @@ const stepsConfig = { onEnter: () => { attachments.selectSubset(attachments.redactNudgable) pdfRedactionUploaded() + // TODO is this still correct? + if (!isEmailResponse) attachments.selectAllNewRedactableAttachments() }, context: { - progressStep: 2, + progressStep: 2, // n/a for isEmailResponse mobileHeaderTitle: i18n.value.redact, } }, @@ -681,11 +745,18 @@ const stepsConfig = { ) { return STEP_REDACTION_REDACT } - return STEP_DOCUMENTS_OVERVIEW_REDACTED + return isEmailResponse + ? STEP_MESSAGE_STATUS // dot:style=dotted + : STEP_DOCUMENTS_OVERVIEW_REDACTED }, context: { - progressStep: 2, + progressStep: isEmailResponse ? 0 : 2, mobileHeaderTitle: i18n.value.redact + }, + onEnter: () => { + if (isEmailResponse && attachments.selected.length === 0) { + attachments.selectSubset(attachments.redactNudgable) + } } }, [STEP_DOCUMENTS_OVERVIEW_REDACTED]: { @@ -696,7 +767,7 @@ const stepsConfig = { pdfRedactionUploaded() }, context: { - progressStep: 2, + progressStep: isEmailResponse ? 1 : 2, // TODO check mobileHeaderTitle: i18n.value.preview, } }, @@ -706,7 +777,7 @@ const stepsConfig = { guardBeforeunload(false) }, context: { - progressStep: 2, + progressStep: isEmailResponse ? 1 : 2, // TODO check mobileHeaderTitle: i18n.value.done } } @@ -732,13 +803,14 @@ const stepsConfigVisualize = (c) => .map((state) => ` "${state}";\n`) .join('') + Object.keys(c) - .map((from) => - c[from].next - .toString() - .match(/\bSTEP_\w+/g) - .map((to) => ` "${from}" -> "${to}";\n`) + .map((fromState) => { + const nextSourcecode = c[fromState].next.toString() + // match all STEP_* with optional "dot edge tag" in a comment, like so: + // some.code(); return STEP_FOO; // dot:key="attr" + return [...nextSourcecode.matchAll(/\b(STEP_\w+)(?:.*\/\/.*dot:(.*))?/g)] + .map(([, toState, dotEdgeTag]) => ` "${fromState}" -> "${toState}" [${dotEdgeTag || ''}];\n`) .join('') - ) + }) .join('') + '}\n' + 'eot\n' @@ -785,7 +857,7 @@ addEventListener('hashchange', () => {
- Es sind Bilddateien angehängt - im nächsten Schritt können diese in PDFs konvertiert werden. - {{ i18n.TODO }} + {{ i18n.imageAttachments }} + {{ i18n.nextStepConvertImages }}
diff --git a/frontend/javascript/lib/i18n-mixin.js b/frontend/javascript/lib/i18n-mixin.js index 12cbf17af..c154a68d6 100644 --- a/frontend/javascript/lib/i18n-mixin.js +++ b/frontend/javascript/lib/i18n-mixin.js @@ -17,6 +17,7 @@ const I18nMixin = { _(key, params) { const trans = this[key] if (Array.isArray(trans)) { + // note that this will fail for languages with multiple plurals, like PL if ( params.count !== undefined && (params.count > 1 || params.count === 0) From 6808a92133e493402fad668ac31304410a69e4b8 Mon Sep 17 00:00:00 2001 From: Jakob Wierzba Date: Mon, 21 Jul 2025 15:51:28 +0200 Subject: [PATCH 04/19] email attachment flow DE translations --- froide/locale/de/LC_MESSAGES/django.po | 77 ++++++++++++++++++++++---- 1 file changed, 66 insertions(+), 11 deletions(-) diff --git a/froide/locale/de/LC_MESSAGES/django.po b/froide/locale/de/LC_MESSAGES/django.po index a50df6b49..6ac751b7d 100644 --- a/froide/locale/de/LC_MESSAGES/django.po +++ b/froide/locale/de/LC_MESSAGES/django.po @@ -8273,6 +8273,72 @@ msgstr "Konnte Original nicht herunterladen." msgid "The message has been re-sent." msgstr "Ihre Nachricht wurde erneut gesendet." +#: froide/foirequest/templates/foirequest/upload_postal_message_new.html +#, python-format +msgid "" +"This message has no redactable attachments. The next step will be skipped." +"
You can review the attachments later at Manage " +"attachments.
If the message hat attachments that need to be " +"converted (e.g. docx Word documents), please wait and refresh this page." +msgstr "" +"Diese Nachricht hat keine zu schwärzenden Anhänge. Deshalb entfällt der nächste Schritt." +"
Sie können die Anhänge anschließend unter Anhänge verwalten überprüfen." +"
Falls die Nachricht Anhänge enthielt, die konvertiert werden müssen" +"(z.B. docx Word-Dokumente) " +"warten Sie bitte kurz und laden diese Seite neu." + +#: froide/foirequest/templates/foirequest/upload_postal_message_new.html +msgid "Read the full message thread on the request page:" +msgstr "Den vollständigen Nachrichtenverlauf finden Sie auf Ihrer Anfrageseite:" + +#: froide/foirequest/views/message.py +msgctxt "e-mail flow" +msgid "New reply to your request" +msgstr "Neue Antwort der Behörde" + + +#: froide/foirequest/views/message.py +msgid "Read & Redact" +msgstr "Lesen & Schwärzen" + +#: froide/foirequest/views/message.py +msgid "Received from" +msgstr "Erhalten von" + +#: froide/foirequest/views/message.py +msgid "There are image attachments." +msgstr "Es sind Bilddateien angehängt." + +#: froide/foirequest/views/message.py +msgid "On the next step you can convert image attachments to PDF documents." +msgstr "Im nächsten Schritt können Sie die Bild-Anhänge in PDFs konvertieren." + +#: froide/foirequest/views/message.py +msgid "On the following step you can read and redact the attachments." +msgstr "Im folgenden Schritt lesen und schwärzen Sie die Anhänge." + +#: froide/foirequest/views/message.py +msgid "I do not know. Please show the documents again." +msgstr "Ich weiß nicht. Bitte die Dokumente erneut anzeigen." + +#: froide/foirequest/views/message.py +msgid "Click the icons to preview." +msgstr "Klicken Sie auf die Icons um eine Vorschau anzuzeigen." + +#: froide/foirequest/views/message.py +msgid "Read and redact the attachments" +msgstr "Anhänge lesen und schwärzen" + +#: froide/foirequest/views/message.py +msgid "One attachment" +msgstr "Ein Anhang" + +#: froide/foirequest/views/message.py +#, python-brace-format +msgid "${count} attachments" +msgstr "${count} Anhänge" + #: froide/foirequest/views/project.py msgid "Publish selected requests." msgstr "Ausgewählte Anfragen veröffentlichen." @@ -14997,14 +15063,3 @@ msgstr "zustaendigkeit//" #~ msgid "Upload and manage attachments" #~ msgstr "Anhänge hochladen und verwalten" - -### Diese Nachricht hat keine zu schwärzenden Anhänge. Deshalb entfällt der nächste Schritt.
Sie können die Anhänge anschließend unter Anhänge verwalten überprüfen.
Falls die Nachricht Anhänge enthielt, die konvertiert werden -### Den vollständigen Nachrichtenverlauf finden Sie auf Ihrer Anfrageseite: -### Lesen & Schwärzen -### Erhalten von -### Es sind Bilddateien angehängt. -### Im nächsten Schritt können Sie die Bild-Anhänge in PDFs konvertieren. -### Im folgenden Schritt lesen und schwärzen Sie die Anhänge. -### Ich weiß nicht. Bitte die Dokumente erneut anzeigen. -### Klicken Sie auf die Icons um eine Vorschau anzuzeigen. -### Anhänge lesen und schwärzen \ No newline at end of file From 7fe40499e69755c089226a63a20e65c00c8b7de3 Mon Sep 17 00:00:00 2001 From: Jakob Wierzba Date: Wed, 23 Jul 2025 15:51:01 +0200 Subject: [PATCH 05/19] do not update FoiMessage when post-upload-submitting an email response --- .../components/postupload/post-upload.vue | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/frontend/javascript/components/postupload/post-upload.vue b/frontend/javascript/components/postupload/post-upload.vue index 91cf2cd01..33b37998f 100644 --- a/frontend/javascript/components/postupload/post-upload.vue +++ b/frontend/javascript/components/postupload/post-upload.vue @@ -236,8 +236,8 @@ retrieveValues() /* Form API interaction: update/submit --- */ -const requestAndMessageUpdateValues = async () => { - await requestPartialUpdate({ +const requestUpdateValues = () => { + return requestPartialUpdate({ path: { id: props.foirequest.id }, body: { costs: values.costs, @@ -246,9 +246,9 @@ const requestAndMessageUpdateValues = async () => { }, throwOnError: true }) - .then(() => { - console.info('requestPartialUpdate successful') - }) +} + +const messageUpdateValues = () => { const messageBody = { is_response: values.is_response, sender_public_body: values.is_response ? values.public_body : null, @@ -256,7 +256,7 @@ const requestAndMessageUpdateValues = async () => { } if (values.date) messageBody.timestamp = isoPrepareDate(values.date) if (values.registered_mail_date) messageBody.registered_mail_date = isoPrepareDate(values.registered_mail_date) - await messagePartialUpdate({ + return messagePartialUpdate({ path: { id: props.message.id }, body: messageBody, throwOnError: true @@ -281,8 +281,11 @@ const approveAndPublish = async () => { .filter((kv) => kv[1] === false) .map((kv) => +kv[0]) await approveAllUnredactedAttachments(excludeIds) - await requestAndMessageUpdateValues() - await messagePublishDraft() + await requestUpdateValues() + if (!isEmailResponse) { + await messageUpdateValues() + await messagePublishDraft() + } gotoStep() } catch (err) { // error from the partialUpdates/PATCHes looks like From 90b31ba4fab9c1b455acdb5b9672e6ee0dc4aad1 Mon Sep 17 00:00:00 2001 From: Jakob Wierzba Date: Wed, 23 Jul 2025 15:52:54 +0200 Subject: [PATCH 06/19] add seperate route for email responses --- froide/foirequest/urls/request_urls.py | 7 +++++++ froide/locale/de/LC_MESSAGES/django.po | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/froide/foirequest/urls/request_urls.py b/froide/foirequest/urls/request_urls.py index 82b889d61..fec73606a 100644 --- a/froide/foirequest/urls/request_urls.py +++ b/froide/foirequest/urls/request_urls.py @@ -115,8 +115,15 @@ path( pgettext_lazy("url part", "//edit-postal-message/"), edit_postal_message, + {"is_email": False}, name="foirequest-edit_postal_message", ), + path( + pgettext_lazy("url part", "//edit-email-response/"), + edit_postal_message, + {"is_email": True}, + name="foirequest-edit_email_message", + ), path( "/apply-moderation/", apply_moderation, diff --git a/froide/locale/de/LC_MESSAGES/django.po b/froide/locale/de/LC_MESSAGES/django.po index 6ac751b7d..771606bf7 100644 --- a/froide/locale/de/LC_MESSAGES/django.po +++ b/froide/locale/de/LC_MESSAGES/django.po @@ -7076,6 +7076,11 @@ msgctxt "url part" msgid "//edit-postal-message/" msgstr "//postnachricht-bearbeiten/" +#: froide/foirequest/urls/request_urls.py +msgctxt "url part" +msgid "//edit-email-response/" +msgstr "//email-antwort-bearbeiten/" + #: froide/foirequest/urls/request_urls.py msgctxt "url part" msgid "//attachment/" From 2ac4b86c983815256fa48d0dc12902ffcf799cd9 Mon Sep 17 00:00:00 2001 From: Jakob Wierzba Date: Wed, 23 Jul 2025 15:59:39 +0200 Subject: [PATCH 07/19] update permission check for email responses, assert their properties --- froide/foirequest/models/message.py | 2 +- froide/foirequest/views/message.py | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/froide/foirequest/models/message.py b/froide/foirequest/models/message.py index 1b0b25c16..0c8549485 100644 --- a/froide/foirequest/models/message.py +++ b/froide/foirequest/models/message.py @@ -98,8 +98,8 @@ class MessageKind(models.TextChoices): MessageKind.POST, MessageKind.PHONE, MessageKind.VISIT, - MessageKind.EMAIL, } + MESSAGE_ID_PREFIX = "foimsg." diff --git a/froide/foirequest/views/message.py b/froide/foirequest/views/message.py index 3896e6f6b..00e33a7c0 100644 --- a/froide/foirequest/views/message.py +++ b/froide/foirequest/views/message.py @@ -175,13 +175,20 @@ def upload_postal_message_create(request, foirequest): @allow_write_foirequest -def edit_postal_message(request, foirequest, message_id): +# todo name doesnt match +def edit_postal_message(request, foirequest, message_id, is_email=False): message = get_object_or_404( FoiMessage.with_drafts, request=foirequest, pk=int(message_id) ) - if not message.can_edit: - print("would 400 here, but continuiuing") - # return render_400(request) + if is_email: + # for emails we just update the request (status etc.) + # message does not need can_edit, but we make sure it has the correct properties + if not (message.kind == MessageKind.EMAIL and message.is_response): + return render_400(request) + else: + # for editing+creating postal messages we need the message to be editable + if not message.can_edit: + return render_400(request) filingcabinet_js_config = get_js_config(request) convert_attachments_url = reverse( "foirequest-manage_attachments", From a44e9ef1790feda9e9cd4762a3bef66d0005ae88 Mon Sep 17 00:00:00 2001 From: Jakob Wierzba Date: Wed, 23 Jul 2025 16:00:12 +0200 Subject: [PATCH 08/19] update mail notification text to link to e-mail response flow --- froide/foirequest/signals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/froide/foirequest/signals.py b/froide/foirequest/signals.py index 324ff6095..bdc39c444 100644 --- a/froide/foirequest/signals.py +++ b/froide/foirequest/signals.py @@ -137,7 +137,7 @@ def notify_user_message_received(sender, message=None, **kwargs): "user": sender.user, "publicbody": message.sender_public_body, "action_url": sender.user.get_autologin_url( - message.get_absolute_short_url() + short_request_url("foirequest-edit_email_message", sender, message) ), }, priority=False, From 52208355a78c4872a3781c5c57557d46e6908e44 Mon Sep 17 00:00:00 2001 From: Jakob Wierzba Date: Wed, 23 Jul 2025 16:41:20 +0200 Subject: [PATCH 09/19] rename+refactor postupload generically, better match email and postal plus translation update --- froide/foirequest/signals.py | 2 +- ..._new.html => edit_message_flow_email.html} | 15 ++++---- .../foirequest/edit_message_flow_postal.html | 37 +++++++++++++++++++ froide/foirequest/urls/request_urls.py | 10 ++--- froide/foirequest/views/__init__.py | 4 +- froide/foirequest/views/message.py | 12 ++++-- froide/locale/de/LC_MESSAGES/django.po | 19 +++++++--- froide/settings.py | 2 +- .../components/docupload/lib/attachments.js | 2 +- .../edit-message-flow.vue} | 20 +++++----- .../simple-stepper.vue | 0 frontend/javascript/editmessageflow.js | 9 +++++ frontend/javascript/postupload.js | 9 ----- vite.config.ts | 2 +- 14 files changed, 96 insertions(+), 47 deletions(-) rename froide/foirequest/templates/foirequest/{upload_postal_message_new.html => edit_message_flow_email.html} (70%) create mode 100644 froide/foirequest/templates/foirequest/edit_message_flow_postal.html rename frontend/javascript/components/{postupload/post-upload.vue => editmessageflow/edit-message-flow.vue} (99%) rename frontend/javascript/components/{postupload => editmessageflow}/simple-stepper.vue (100%) create mode 100644 frontend/javascript/editmessageflow.js delete mode 100644 frontend/javascript/postupload.js diff --git a/froide/foirequest/signals.py b/froide/foirequest/signals.py index bdc39c444..f9c6bd402 100644 --- a/froide/foirequest/signals.py +++ b/froide/foirequest/signals.py @@ -137,7 +137,7 @@ def notify_user_message_received(sender, message=None, **kwargs): "user": sender.user, "publicbody": message.sender_public_body, "action_url": sender.user.get_autologin_url( - short_request_url("foirequest-edit_email_message", sender, message) + short_request_url("foirequest-edit_message_flow_email", sender, message) ), }, priority=False, diff --git a/froide/foirequest/templates/foirequest/upload_postal_message_new.html b/froide/foirequest/templates/foirequest/edit_message_flow_email.html similarity index 70% rename from froide/foirequest/templates/foirequest/upload_postal_message_new.html rename to froide/foirequest/templates/foirequest/edit_message_flow_email.html index a998c3480..045896b7c 100644 --- a/froide/foirequest/templates/foirequest/upload_postal_message_new.html +++ b/froide/foirequest/templates/foirequest/edit_message_flow_email.html @@ -7,25 +7,24 @@ {% load content_helper %} {% load foirequest_tags %} {% block title %} - {% blocktrans with title=object.title %}Upload postal mail for “{{ title }}”{% endblocktrans %} + {% blocktrans with title=object.title %}Edit e-mail response for “{{ title }}”{% endblocktrans %} {% endblock title %} {% block navbar %} {% include "header_reduced.html" %} {% endblock navbar %} {% block body %} -
- {% translate "Upload postal mail" as breadcrumb_label %} +
+ {% translate "Edit e-mail response" as breadcrumb_label %} {% include "foirequest/header/breadcrumb.html" with last_item=breadcrumb_label %}
{# the form never submits, but is useful for checkValidity #} -
+ {% csrf_token %} {# mw-100 crucial for pdf-redaction's dynamic maxWidth calculations #} - + - {# TODO if kind=Email, if message.plaintext... #} {# avoid whitespace, since this will be pre-wrap. spaceless didn't work... #} @@ -38,13 +37,13 @@ - +
{% endblock body %} {% block scripts %} {{ block.super }} {% addfrontendbuild "request.js" %} {% addfrontendbuild "publicbody.js" %} - {% addfrontendbuild "postupload.js" %} + {% addfrontendbuild "editmessageflow.js" %} {% addfrontendbuild "fileuploader.js" %} {% endblock scripts %} diff --git a/froide/foirequest/templates/foirequest/edit_message_flow_postal.html b/froide/foirequest/templates/foirequest/edit_message_flow_postal.html new file mode 100644 index 000000000..f839978bb --- /dev/null +++ b/froide/foirequest/templates/foirequest/edit_message_flow_postal.html @@ -0,0 +1,37 @@ +{% extends "fullscreen_app.html" %} +{% load i18n %} +{% load static %} +{% load block_helper %} +{% load frontendbuild %} +{% load form_helper %} +{% load content_helper %} +{% load foirequest_tags %} +{% block title %} + {% blocktrans with title=object.title %}Upload postal mail for “{{ title }}”{% endblocktrans %} +{% endblock title %} +{% block navbar %} + {% include "header_reduced.html" %} +{% endblock navbar %} +{% block body %} +
+ {% translate "Upload postal mail" as breadcrumb_label %} + {% include "foirequest/header/breadcrumb.html" with last_item=breadcrumb_label %} +
+ {# the form never submits, but is useful for checkValidity #} +
+ {% csrf_token %} + {# mw-100 crucial for pdf-redaction's dynamic maxWidth calculations #} + + + +
+{% endblock body %} +{% block scripts %} + {{ block.super }} + {% addfrontendbuild "request.js" %} + {% addfrontendbuild "publicbody.js" %} + {% addfrontendbuild "editmessageflow.js" %} + {% addfrontendbuild "fileuploader.js" %} +{% endblock scripts %} diff --git a/froide/foirequest/urls/request_urls.py b/froide/foirequest/urls/request_urls.py index fec73606a..34e571043 100644 --- a/froide/foirequest/urls/request_urls.py +++ b/froide/foirequest/urls/request_urls.py @@ -24,7 +24,7 @@ download_message_pdf, download_original_email, edit_message, - edit_postal_message, + edit_message_flow, escalation_message, extend_deadline, make_public, @@ -114,15 +114,15 @@ ), path( pgettext_lazy("url part", "//edit-postal-message/"), - edit_postal_message, + edit_message_flow, {"is_email": False}, - name="foirequest-edit_postal_message", + name="foirequest-edit_message_flow_postal", ), path( pgettext_lazy("url part", "//edit-email-response/"), - edit_postal_message, + edit_message_flow, {"is_email": True}, - name="foirequest-edit_email_message", + name="foirequest-edit_message_flow_email", ), path( "/apply-moderation/", diff --git a/froide/foirequest/views/__init__.py b/froide/foirequest/views/__init__.py index c18219a78..bdf6701b1 100644 --- a/froide/foirequest/views/__init__.py +++ b/froide/foirequest/views/__init__.py @@ -29,7 +29,7 @@ download_message_pdf, download_original_email, edit_message, - edit_postal_message, + edit_message_flow, escalation_message, message_shortlink, redact_message, @@ -108,7 +108,7 @@ "resend_message", "upload_attachments", "edit_message", - "edit_postal_message", + "edit_message_flow", "redact_message", "download_message_pdf", "download_message_letter_pdf", diff --git a/froide/foirequest/views/message.py b/froide/foirequest/views/message.py index 00e33a7c0..2cb88bd0c 100644 --- a/froide/foirequest/views/message.py +++ b/froide/foirequest/views/message.py @@ -168,7 +168,7 @@ def upload_postal_message_create(request, foirequest): return redirect( reverse( - "foirequest-edit_postal_message", + "foirequest-edit_message_flow_postal", kwargs={"slug": foirequest.slug, "message_id": message.id}, ) ) @@ -176,7 +176,7 @@ def upload_postal_message_create(request, foirequest): @allow_write_foirequest # todo name doesnt match -def edit_postal_message(request, foirequest, message_id, is_email=False): +def edit_message_flow(request, foirequest, message_id, is_email=False): message = get_object_or_404( FoiMessage.with_drafts, request=foirequest, pk=int(message_id) ) @@ -543,7 +543,9 @@ def edit_postal_message(request, foirequest, message_id, is_email=False): "foirequest-redact_attachment", kwargs={"slug": foirequest.slug, "attachment_id": 0}, ), - "helpPostuploadRedaction": get_content_url("help_postupload_redaction"), + "helpEditmessageflowRedaction": get_content_url( + "help_editmessageflow_redaction" + ), "mobileAppInstall": settings.FROIDE_CONFIG.get("mobile_app_install_url"), "mobileAppContent": settings.FROIDE_CONFIG.get("mobile_app_content_url") if request.user.is_staff @@ -578,7 +580,9 @@ def edit_postal_message(request, foirequest, message_id, is_email=False): } return render( request, - "foirequest/upload_postal_message_new.html", + "foirequest/edit_message_flow_email.html" + if is_email + else "foirequest/edit_message_flow_postal.html", { "message_id": message_id, "object": foirequest, diff --git a/froide/locale/de/LC_MESSAGES/django.po b/froide/locale/de/LC_MESSAGES/django.po index 771606bf7..13385fc2c 100644 --- a/froide/locale/de/LC_MESSAGES/django.po +++ b/froide/locale/de/LC_MESSAGES/django.po @@ -6868,16 +6868,24 @@ msgid "I already told them" msgstr "Mitteilung ist schon erfolgt" #: froide/foirequest/templates/foirequest/upload_postal_message.html -#: froide/foirequest/templates/foirequest/upload_postal_message_new.html +#: froide/foirequest/templates/foirequest/edit_message_flow_postal.html #, python-format msgid "Upload postal mail for “%(title)s”" msgstr "Post zur Anfrage „%(title)s“ hochladen" #: froide/foirequest/templates/foirequest/upload_postal_message.html -#: froide/foirequest/templates/foirequest/upload_postal_message_new.html +#: froide/foirequest/templates/foirequest/edit_message_flow_postal.html msgid "Upload postal mail" msgstr "Post hochladen" +#: froide/foirequest/templates/foirequest/edit_message_flow_email.html +msgid "Edit e-mail response for “%(title)s”" +msgstr "E-Mail-Antwort zur Anfrage „%(title)s“ bearbeiten" + +#: froide/foirequest/templates/foirequest/edit_message_flow_email.html +msgid "Edit e-mail response" +msgstr "E-Mail-Antwort bearbeiten" + #: froide/foirequest/templates/foirequest/upload_postal_message.html #, python-format msgid "Upload postal mail for your request “%(title)s”" @@ -7888,7 +7896,7 @@ msgid "Done" msgstr "Abgeschlossen" # #: froide/foirequest/views/message.py -# #: frontend/javascript/components/postupload/post-upload.vue +# #: frontend/javascript/components/editmessageflow/edit-message-flow.vue # msgid "Done" # msgstr "Fertig" #: froide/foirequest/views/message.py @@ -8278,7 +8286,7 @@ msgstr "Konnte Original nicht herunterladen." msgid "The message has been re-sent." msgstr "Ihre Nachricht wurde erneut gesendet." -#: froide/foirequest/templates/foirequest/upload_postal_message_new.html +#: froide/foirequest/templates/foirequest/edit_message_flow_email.html #, python-format msgid "" "This message has no redactable attachments. The next step will be skipped." @@ -8293,7 +8301,8 @@ msgstr "" "(z.B. docx Word-Dokumente) " "warten Sie bitte kurz und laden diese Seite neu." -#: froide/foirequest/templates/foirequest/upload_postal_message_new.html +#: froide/foirequest/templates/foirequest/edit_message_flow_email.html +#: froide/foirequest/templates/foirequest/edit_message_flow_postal.html msgid "Read the full message thread on the request page:" msgstr "Den vollständigen Nachrichtenverlauf finden Sie auf Ihrer Anfrageseite:" diff --git a/froide/settings.py b/froide/settings.py index f175bcfd8..4965dbc30 100644 --- a/froide/settings.py +++ b/froide/settings.py @@ -576,7 +576,7 @@ def is_pkce_required(client_id): "help": "/help/", "throttled": "/help/", # TODO english? - "help_postupload_redaction": "/hilfe/plain/funktionen-der-plattform/schwaerzungen-durchfuehren/", + "help_editmessageflow_redaction": "/hilfe/plain/funktionen-der-plattform/schwaerzungen-durchfuehren/", "help_attachments_management": "/hilfe/plain/funktionen-der-plattform/anhange-verwalten/", }, "mobile_app_install_url": None, # TODO diff --git a/frontend/javascript/components/docupload/lib/attachments.js b/frontend/javascript/components/docupload/lib/attachments.js index e778e69f8..3110223d1 100644 --- a/frontend/javascript/components/docupload/lib/attachments.js +++ b/frontend/javascript/components/docupload/lib/attachments.js @@ -330,7 +330,7 @@ const monitorAttachments = () => { export function useAttachments({ message = null, urls = null, csrfToken = null, i18n = null} = {}) { // urls, token and i18n could possibly overwrite what has been set before // they shall only be used in the most-parent, ancestral component, - // like and + // like and // they could also Object.extend, or throw an error/warning if already set... if (message) config.message = message if (urls) config.urls = urls diff --git a/frontend/javascript/components/postupload/post-upload.vue b/frontend/javascript/components/editmessageflow/edit-message-flow.vue similarity index 99% rename from frontend/javascript/components/postupload/post-upload.vue rename to frontend/javascript/components/editmessageflow/edit-message-flow.vue index 33b37998f..fd376ac70 100644 --- a/frontend/javascript/components/postupload/post-upload.vue +++ b/frontend/javascript/components/editmessageflow/edit-message-flow.vue @@ -58,15 +58,15 @@ const { isDesktop } = useIsDesktop() /* --- debug --- */ -const debug = ref(!!localStorage.getItem('fds-postupload-debug')) +const debug = ref(!!localStorage.getItem('fds-editmessageflow-debug')) window.FDSdebug = (val) => { debug.value = typeof val === 'boolean' ? val : !debug.value - localStorage.setItem('fds-postupload-debug', debug.value ? 'yes' : '') + localStorage.setItem('fds-editmessageflow-debug', debug.value ? 'yes' : '') } const debugSkipDate = () => { - values.date = document.forms.postupload.elements.date.max - values.registered_mail_date = document.forms.postupload.elements.date.max + values.date = document.forms.editmessageflow.elements.date.max + values.registered_mail_date = document.forms.editmessageflow.elements.date.max updateValidity('date') updateValidity('registered_mail_date') gotoStep() @@ -192,7 +192,7 @@ const validity = reactive({ // TODO updateValidity should (maybe) be called on gotoStep(STEP_MESSAGE_DATE), too const updateValidity = (key) => { - let el = document.forms.postupload.elements[key] + let el = document.forms.editmessageflow.elements[key] // checkValidity is same for any radio button of a given name, we pick the first if (el instanceof RadioNodeList) el = el[0] // just assume true if browser doesn't support checkValidity @@ -816,7 +816,7 @@ const fieldErrorStep = { const stepsConfigVisualize = (c) => 'dot -Tpng << eot | display -\n' + - 'digraph "postupload" {\n' + + 'digraph "editmessageflow" {\n' + Object.keys(c) .map((state) => ` "${state}";\n`) .join('') + @@ -1284,7 +1284,7 @@ addEventListener('hashchange', () => { - { type="button" class="btn btn-link ps-0" data-bs-toggle="collapse" - data-bs-target="#postuploadPeekAttachments" + data-bs-target="#editmessageflowPeekAttachments" aria-expanded="false" - aria-controls="postuploadPeekAttachments"> + aria-controls="editmessageflowPeekAttachments"> {{ i18n.pleasePeek }} -
+
{{ i18n.clickIconsForPreview }} + createAppWithProps(element, MessageEditFlow).use(store).mount(element) + ) diff --git a/frontend/javascript/postupload.js b/frontend/javascript/postupload.js deleted file mode 100644 index 31452109e..000000000 --- a/frontend/javascript/postupload.js +++ /dev/null @@ -1,9 +0,0 @@ -import { createAppWithProps } from './lib/vue-helper' -import store from './store' -import PostUpload from './components/postupload/post-upload.vue' - -document - .querySelectorAll('post-upload') - .forEach((element) => - createAppWithProps(element, PostUpload).use(store).mount(element) - ) diff --git a/vite.config.ts b/vite.config.ts index cdc6d4b89..30cafc10f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -35,7 +35,7 @@ export default defineConfig({ makerequest: './frontend/javascript/makerequest.js', messageredaction: './frontend/javascript/messageredaction.js', moderation: './frontend/javascript/moderation.js', - postupload: './frontend/javascript/postupload.js', + editmessageflow: './frontend/javascript/editmessageflow.js', proofupload: './frontend/javascript/proofupload.js', publicbody: './frontend/javascript/publicbody.js', publicbodyupload: './frontend/javascript/publicbodyupload.js', From 9e8c5fcf2218c85c15723343764479bca938a837 Mon Sep 17 00:00:00 2001 From: Jakob Wierzba Date: Mon, 28 Jul 2025 11:57:56 +0200 Subject: [PATCH 10/19] fix pdf redaction error on incomplete drag operations --- frontend/javascript/components/redaction/pdf-redaction.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/javascript/components/redaction/pdf-redaction.vue b/frontend/javascript/components/redaction/pdf-redaction.vue index f0e3ecc2a..4c8d08f97 100644 --- a/frontend/javascript/components/redaction/pdf-redaction.vue +++ b/frontend/javascript/components/redaction/pdf-redaction.vue @@ -975,7 +975,7 @@ export default { return } - if (this.startDrag === null) { + if (this.startDrag === null || this.endDrag === null) { return } From 55614b3c5d6004c88ba2220a113b02de17daa5de Mon Sep 17 00:00:00 2001 From: Jakob Wierzba Date: Wed, 6 Aug 2025 13:43:43 +0200 Subject: [PATCH 11/19] cleanup --- .../templates/foirequest/edit_message_flow_email.html | 7 ++----- froide/foirequest/views/message.py | 1 - .../components/editmessageflow/edit-message-flow.vue | 5 ++++- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/froide/foirequest/templates/foirequest/edit_message_flow_email.html b/froide/foirequest/templates/foirequest/edit_message_flow_email.html index 045896b7c..30d121d95 100644 --- a/froide/foirequest/templates/foirequest/edit_message_flow_email.html +++ b/froide/foirequest/templates/foirequest/edit_message_flow_email.html @@ -28,15 +28,12 @@ {# avoid whitespace, since this will be pre-wrap. spaceless didn't work... #} -