Browse Source

Maintenance: Improve handling of inline images.

Co-authored-by: Dominik Klein <dk@zammad.com>
Co-authored-by: Florian Liebe <fl@zammad.com>
Co-authored-by: Mantas Masalskis <mm@zammad.com>
Co-authored-by: Tobias Schäfer <ts@zammad.com>
Florian Liebe 8 months ago
parent
commit
520285bba4

+ 6 - 2
app/controllers/concerns/creates_ticket_articles.rb

@@ -64,7 +64,12 @@ module CreatesTicketArticles # rubocop:disable Metrics/ModuleLength
     # find attachments in upload cache
     attachments = []
     if form_id
-      attachments += UploadCache.new(form_id).attachments
+      attachments += UploadCache
+        .new(form_id)
+        .attachments
+        .reject do |elem|
+          UploadCache.files_include_attachment?(attachments_inline, elem)
+        end
     end
 
     # store inline attachments
@@ -148,5 +153,4 @@ module CreatesTicketArticles # rubocop:disable Metrics/ModuleLength
 
     article
   end
-
 end

+ 2 - 2
app/controllers/tickets_shared_draft_starts_controller.rb

@@ -17,7 +17,7 @@ class TicketsSharedDraftStartsController < ApplicationController
 
     render json: {
       shared_draft_id:      object.id,
-      shared_draft_content: object.content,
+      shared_draft_content: object.content_with_base64,
       assets:               object.assets,
     }
   end
@@ -59,7 +59,7 @@ class TicketsSharedDraftStartsController < ApplicationController
   def import_attachments
     object = scope.find params[:id]
 
-    new_attachments = object.clone_attachments 'UploadCache', params[:form_id]
+    new_attachments = object.clone_attachments 'UploadCache', params[:form_id], only_attached_attachments: true
 
     render json: {
       attachments: new_attachments

+ 3 - 0
app/frontend/apps/desktop/pages/ticket/__tests__/ticket-create-shared-drafts.spec.ts

@@ -54,6 +54,9 @@ describe('ticket create view - shared drafts sidebar', async () => {
 
       await view.events.type(view.getByLabelText('Text'), 'foobar')
 
+      const formUpdaterCalls = await waitForFormUpdaterQueryCalls()
+      await vi.waitUntil(() => formUpdaterCalls.length === 2)
+
       mockTicketSharedDraftStartListQuery({
         ticketSharedDraftStartList: [],
       })

+ 1 - 0
app/frontend/apps/desktop/pages/ticket/components/TicketSidebar/TicketSidebarSharedDraftStartContent.vue

@@ -167,6 +167,7 @@ const openFlyout = (sharedDraftStartId: string) => {
       link-icon="plus-square-fill"
       :link-label="__('Create Shared Draft')"
       @link-click.prevent="createSharedDraft"
+      @keypress.enter.prevent="createSharedDraft"
     />
 
     <div class="py-1">

+ 0 - 1
app/frontend/apps/desktop/pages/ticket/views/TicketCreate.vue

@@ -174,7 +174,6 @@ const formSchema = defineFormSchema([
                 },
               },
             },
-            triggerFormUpdater: false,
           },
           {
             type: 'file',

+ 26 - 6
app/frontend/cypress/shared/components/Form/fields/FieldEditor/editor-actions.cy.ts

@@ -1,5 +1,9 @@
 // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
 
+import { mockApolloClient } from '#cy/utils.ts'
+
+import { FormUploadCacheAddDocument } from '#shared/components/Form/fields/FieldFile/graphql/mutations/uploadCache/add.api.ts'
+
 import { mountEditor } from './utils.ts'
 
 const testAction = (
@@ -174,6 +178,26 @@ describe('testing actions', { retries: { runMode: 2 } }, () => {
   })
 
   it('inline image', () => {
+    const client = mockApolloClient()
+    client.setRequestHandler(FormUploadCacheAddDocument, async () => ({
+      data: {
+        formUploadCacheAdd: {
+          __typename: 'FormUploadCacheAddPayload',
+          uploadedFiles: [
+            {
+              __typename: 'StoredFile',
+              id: 'gid://zammad/Store/2062',
+              name: 'file.png',
+              size: 12393,
+              type: 'image/png',
+            },
+          ],
+        },
+      },
+    }))
+
+    cy.intercept('GET', '/api/v1/attachments/2062', { fixture: 'example.png' })
+
     mountEditor()
 
     const imageBuffer = Cypress.Buffer.from('some image')
@@ -196,11 +220,7 @@ describe('testing actions', { retries: { runMode: 2 } }, () => {
 
     cy.findByRole('textbox')
       .find('img')
-      .should(
-        'have.attr',
-        'src',
-        `data:image/png;base64,${btoa(imageBuffer.toString())}`,
-      )
+      .should('have.attr', 'src', '/api/v1/attachments/2062')
   })
 
   describe('table', () => {
@@ -221,7 +241,7 @@ describe('testing actions', { retries: { runMode: 2 } }, () => {
       cy.findByRole('table').find('tr').should('have.length', 3)
     })
 
-    describe.only('actions', () => {
+    describe('actions', () => {
       testTableAction('Insert row above', { trCount: 4, tdCount: 9 })
       testTableAction('Insert row below', { trCount: 4, tdCount: 9 })
       testTableAction('Delete row', { trCount: 2, tdCount: 3 })

+ 24 - 0
app/frontend/cypress/shared/components/Form/fields/FieldEditor/editor-image-resize.cy.ts

@@ -1,9 +1,33 @@
 // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
 
+import { mockApolloClient } from '#cy/utils.ts'
+
+import { FormUploadCacheAddDocument } from '#shared/components/Form/fields/FieldFile/graphql/mutations/uploadCache/add.api.ts'
+
 import { mountEditor } from './utils.ts'
 
 describe('resizing image within editor', () => {
   it('can be resized', { retries: 2 }, () => {
+    const client = mockApolloClient()
+    client.setRequestHandler(FormUploadCacheAddDocument, async () => ({
+      data: {
+        formUploadCacheAdd: {
+          __typename: 'FormUploadCacheAddPayload',
+          uploadedFiles: [
+            {
+              __typename: 'StoredFile',
+              id: 'gid://zammad/Store/2062',
+              name: 'file.png',
+              size: 12393,
+              type: 'image/png',
+            },
+          ],
+        },
+      },
+    }))
+
+    cy.intercept('GET', '/api/v1/attachments/2062', { fixture: 'example.png' })
+
     mountEditor()
     cy.findByRole('textbox').click()
     cy.findByTestId('action-bar')

+ 10 - 2
app/frontend/shared/components/Form/Form.vue

@@ -285,9 +285,13 @@ const formUpdaterProcessing = computed(
   () => formNode.value?.context?.state.formUpdaterProcessing || false,
 )
 
+const uploadProcessing = computed(
+  () => formNode.value?.context?.state.uploadProcessing || false,
+)
+
 let delayedSubmit = false
 const onSubmitRaw = () => {
-  if (formUpdaterProcessing.value) {
+  if (formUpdaterProcessing.value || uploadProcessing.value) {
     delayedSubmit = true
   }
 }
@@ -357,7 +361,11 @@ const triggerFormUpdater = (options?: FormUpdaterOptions) => {
 
 const delayedSubmitPlugin = (node: FormKitNode) => {
   node.on('message-removed', async ({ payload }) => {
-    if (payload.key === 'formUpdaterProcessing' && delayedSubmit) {
+    if (
+      (payload.key === 'formUpdaterProcessing' ||
+        payload.key === 'uploadProcessing') &&
+      delayedSubmit
+    ) {
       // We need to wait on the "next tick", so that the validation for updated fields is ready.
       setTimeout(() => {
         delayedSubmit = false

+ 29 - 0
app/frontend/shared/components/Form/composables/useFileUploadProcessing.ts

@@ -0,0 +1,29 @@
+// Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
+
+import { createMessage } from '@formkit/core'
+
+import { getNodeByName } from '../utils.ts'
+
+export const useFileUploadProcessing = (formId: string, fieldName: string) => {
+  const fieldNode = getNodeByName(formId, fieldName)
+
+  const setFileUploadProcessing = () => {
+    fieldNode?.root?.store.set(
+      createMessage({
+        blocking: true,
+        key: 'uploadProcessing',
+        value: true,
+        visible: false,
+      }),
+    )
+  }
+
+  const removeFileUploadProcessing = () => {
+    fieldNode?.root?.store.remove('uploadProcessing')
+  }
+
+  return {
+    setFileUploadProcessing,
+    removeFileUploadProcessing,
+  }
+}

+ 1 - 0
app/frontend/shared/components/Form/fields/FieldEditor/FieldEditorActionBar.vue

@@ -21,6 +21,7 @@ const props = defineProps<{
   contentType: EditorContentType
   visible: boolean
   disabledPlugins: EditorCustomPlugins[]
+  formId: string
 }>()
 
 defineEmits<{

Some files were not shown because too many files changed in this diff