diff --git a/app/javascript/dashboard/components-next/icon/FileIcon.vue b/app/javascript/dashboard/components-next/icon/FileIcon.vue index 8dd9e7ce1..66a971bc6 100644 --- a/app/javascript/dashboard/components-next/icon/FileIcon.vue +++ b/app/javascript/dashboard/components-next/icon/FileIcon.vue @@ -18,6 +18,7 @@ const fileTypeIcon = computed(() => { json: 'i-woot-file-txt', odt: 'i-woot-file-doc', pdf: 'i-woot-file-pdf', + pfx: 'i-woot-file-pfx', ppt: 'i-woot-file-ppt', pptx: 'i-woot-file-ppt', rar: 'i-woot-file-zip', @@ -26,6 +27,7 @@ const fileTypeIcon = computed(() => { txt: 'i-woot-file-txt', xls: 'i-woot-file-xls', xlsx: 'i-woot-file-xls', + xml: 'i-woot-file-txt', zip: 'i-woot-file-zip', }; diff --git a/app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue b/app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue index ff569d763..2e5a9aab2 100644 --- a/app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue +++ b/app/javascript/dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue @@ -7,7 +7,6 @@ import * as ActiveStorage from 'activestorage'; import inboxMixin from 'shared/mixins/inboxMixin'; import { FEATURE_FLAGS } from 'dashboard/featureFlags'; import { getAllowedFileTypesByChannel } from '@chatwoot/utils'; -import { ALLOWED_FILE_TYPES } from 'shared/constants/messages'; import VideoCallButton from '../VideoCallButton.vue'; import { INBOX_TYPES } from 'dashboard/helper/inbox'; import { mapGetters } from 'vuex'; @@ -166,11 +165,6 @@ export default { uploadRef, }; }, - data() { - return { - ALLOWED_FILE_TYPES, - }; - }, computed: { ...mapGetters({ accountId: 'getCurrentAccountId', @@ -212,13 +206,11 @@ export default { return this.conversationType === 'instagram_direct_message'; }, allowedFileTypes() { - // Use default file types for private notes if (this.isOnPrivateNote) { - return this.ALLOWED_FILE_TYPES; + return getAllowedFileTypesByChannel(); } let channelType = this.channelType || this.inbox?.channel_type; - if (this.isAnInstagramChannel || this.isInstagramDM) { channelType = INBOX_TYPES.INSTAGRAM; } diff --git a/app/javascript/shared/constants/messages.js b/app/javascript/shared/constants/messages.js index 989aa12ca..18bc380ec 100644 --- a/app/javascript/shared/constants/messages.js +++ b/app/javascript/shared/constants/messages.js @@ -39,12 +39,14 @@ export const ALLOWED_FILE_TYPES = 'audio/*,' + 'video/*,' + '.3gpp,' + + '.xls, .xlsx, .xml, .pfx,' + 'text/csv, text/plain, application/json, application/pdf, text/rtf,' + 'application/xml, text/xml,' + 'application/zip, application/x-7z-compressed application/vnd.rar application/x-tar,' + 'application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/vnd.oasis.opendocument.text,' + 'application/vnd.openxmlformats-officedocument.presentationml.presentation, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,' + - 'application/vnd.openxmlformats-officedocument.wordprocessingml.document,'; + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document,' + + 'application/x-pkcs12, application/pkcs12,'; export const CSAT_RATINGS = [ { diff --git a/app/javascript/shared/helpers/FileHelper.js b/app/javascript/shared/helpers/FileHelper.js index 2616c868a..93c8a7156 100644 --- a/app/javascript/shared/helpers/FileHelper.js +++ b/app/javascript/shared/helpers/FileHelper.js @@ -1,6 +1,5 @@ import { getAllowedFileTypesByChannel } from '@chatwoot/utils'; import { INBOX_TYPES } from 'dashboard/helper/inbox'; -import { ALLOWED_FILE_TYPES } from 'shared/constants/messages'; export const DEFAULT_MAXIMUM_FILE_UPLOAD_SIZE = 40; @@ -58,9 +57,8 @@ export const isFileTypeAllowedForChannel = (file, options = {}) => { isOnPrivateNote, } = options; - // Use broader file types for private notes (matches file picker behavior) const allowedFileTypes = isOnPrivateNote - ? ALLOWED_FILE_TYPES + ? getAllowedFileTypesByChannel() : getAllowedFileTypesByChannel({ channelType: isInstagramChannel || conversationType === 'instagram_direct_message' diff --git a/app/models/attachment.rb b/app/models/attachment.rb index 2d46f3b7e..79a8021b3 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -25,15 +25,19 @@ class Attachment < ApplicationRecord include Rails.application.routes.url_helpers ACCEPTABLE_FILE_TYPES = %w[ - text/csv text/plain text/rtf + text/csv text/plain text/rtf text/xml application/json application/pdf + application/xml application/zip application/x-7z-compressed application/vnd.rar application/x-tar application/msword application/vnd.ms-excel application/vnd.ms-powerpoint application/rtf application/vnd.oasis.opendocument.text application/vnd.openxmlformats-officedocument.presentationml.presentation application/vnd.openxmlformats-officedocument.spreadsheetml.sheet application/vnd.openxmlformats-officedocument.wordprocessingml.document + application/x-pkcs12 application/pkcs12 ].freeze + ACCEPTABLE_FILE_EXTENSIONS = %w[pfx xml].freeze + GENERIC_FILE_CONTENT_TYPES = %w[application/octet-stream].freeze belongs_to :account belongs_to :message has_one_attached :file @@ -195,7 +199,10 @@ class Attachment < ApplicationRecord end def validate_file_content_type(file_content_type) - errors.add(:file, 'type not supported') unless media_file?(file_content_type) || ACCEPTABLE_FILE_TYPES.include?(file_content_type) + return if media_file?(file_content_type) || ACCEPTABLE_FILE_TYPES.include?(file_content_type) + return if generic_file_content_type?(file_content_type) && ACCEPTABLE_FILE_EXTENSIONS.include?(file_extension) + + errors.add(:file, 'type not supported') end def validate_file_size(byte_size) @@ -206,7 +213,15 @@ class Attachment < ApplicationRecord end def media_file?(file_content_type) - file_content_type.start_with?('image/', 'video/', 'audio/') + file_content_type.to_s.start_with?('image/', 'video/', 'audio/') + end + + def generic_file_content_type?(file_content_type) + file_content_type.blank? || GENERIC_FILE_CONTENT_TYPES.include?(file_content_type) + end + + def file_extension + File.extname(file.filename.to_s).delete_prefix('.').downcase end end diff --git a/package.json b/package.json index ed8bbf00f..1e506d6c5 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "@breezystack/lamejs": "^1.2.7", "@chatwoot/ninja-keys": "1.2.3", "@chatwoot/prosemirror-schema": "1.3.13", - "@chatwoot/utils": "^0.0.52", + "@chatwoot/utils": "^0.0.55", "@formkit/core": "^1.7.2", "@formkit/vue": "^1.7.2", "@hcaptcha/vue3-hcaptcha": "^1.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3f1e9e2cf..3a9efa7eb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,8 +29,8 @@ importers: specifier: 1.3.13 version: 1.3.13 '@chatwoot/utils': - specifier: ^0.0.52 - version: 0.0.52 + specifier: ^0.0.55 + version: 0.0.55 '@formkit/core': specifier: ^1.7.2 version: 1.7.2 @@ -462,8 +462,8 @@ packages: '@chatwoot/prosemirror-schema@1.3.13': resolution: {integrity: sha512-T6FBUinMJbwDCD7975g8M/Tsn2+G3O2pTGIXdcLkMRpbAAC6mVdl4ZcZektlt5y/PVmPVqNHPsfee1XB/C3vAw==} - '@chatwoot/utils@0.0.52': - resolution: {integrity: sha512-e57uVqyVW4tj1gql4YJPNMykqMJPkETn5Y9AmHdhc6Y7oxDXfRXBq27fZrrDadLkZdn5RYVCZjfIhXOumyYv2Q==} + '@chatwoot/utils@0.0.55': + resolution: {integrity: sha512-8G6HYQe1ZEYfJEsSYfDVvE+uhf98JDRjtGlpB+bzMko+yltbrk4yACSo/ImC3jSaJ6K8yPTSjJToSRmsQbL2iQ==} engines: {node: '>=10'} '@codemirror/commands@6.7.0': @@ -5011,7 +5011,7 @@ snapshots: prosemirror-utils: 1.2.2(prosemirror-model@1.22.3)(prosemirror-state@1.4.3) prosemirror-view: 1.34.1 - '@chatwoot/utils@0.0.52': + '@chatwoot/utils@0.0.55': dependencies: date-fns: 2.30.0 diff --git a/swagger/definitions/request/agent/create_payload.yml b/swagger/definitions/request/agent/create_payload.yml index 1daeae83a..77180d282 100644 --- a/swagger/definitions/request/agent/create_payload.yml +++ b/swagger/definitions/request/agent/create_payload.yml @@ -17,12 +17,12 @@ properties: enum: ['agent', 'administrator'] description: Whether its administrator or agent example: 'agent' - availability_status: + availability: type: string - enum: ['available', 'busy', 'offline'] - description: The availability setting of the agent. - example: 'available' + enum: ['online', 'busy', 'offline'] + description: The configured availability of the agent. + example: 'online' auto_offline: type: boolean - description: Whether the availability status of agent is configured to go offline automatically when away. + description: Whether the agent is automatically marked offline when they are away. example: true diff --git a/swagger/definitions/request/agent/update_payload.yml b/swagger/definitions/request/agent/update_payload.yml index fc8d1457d..168d46f49 100644 --- a/swagger/definitions/request/agent/update_payload.yml +++ b/swagger/definitions/request/agent/update_payload.yml @@ -7,12 +7,12 @@ properties: enum: ['agent', 'administrator'] description: Whether its administrator or agent example: 'agent' - availability_status: + availability: type: string - enum: ['available', 'busy', 'offline'] - description: The availability status of the agent. - example: 'available' + enum: ['online', 'busy', 'offline'] + description: The configured availability of the agent. + example: 'online' auto_offline: type: boolean - description: Whether the availability status of agent is configured to go offline automatically when away. + description: Whether the agent is automatically marked offline when they are away. example: true diff --git a/swagger/definitions/resource/agent.yml b/swagger/definitions/resource/agent.yml index 1d7b2b4c3..cabd1ee27 100644 --- a/swagger/definitions/resource/agent.yml +++ b/swagger/definitions/resource/agent.yml @@ -6,11 +6,15 @@ properties: type: integer availability_status: type: string - enum: ['available', 'busy', 'offline'] - description: The availability status of the agent computed by Chatwoot. + enum: ['online', 'busy', 'offline'] + readOnly: true + description: >- + The effective availability status of the agent, derived from the configured availability, + auto-offline setting, and current presence. To update an agent's configured availability, + use the availability field in create or update requests. auto_offline: type: boolean - description: Whether the availability status of agent is configured to go offline automatically when away. + description: Whether the agent is automatically marked offline when they are away. confirmed: type: boolean description: Whether the agent has confirmed their email address. diff --git a/swagger/swagger.json b/swagger/swagger.json index 94d1f04d3..b8b64b009 100644 --- a/swagger/swagger.json +++ b/swagger/swagger.json @@ -9892,15 +9892,16 @@ "availability_status": { "type": "string", "enum": [ - "available", + "online", "busy", "offline" ], - "description": "The availability status of the agent computed by Chatwoot." + "readOnly": true, + "description": "The effective availability status of the agent, derived from the configured availability, auto-offline setting, and current presence. To update an agent's configured availability, use the availability field in create or update requests." }, "auto_offline": { "type": "boolean", - "description": "Whether the availability status of agent is configured to go offline automatically when away." + "description": "Whether the agent is automatically marked offline when they are away." }, "confirmed": { "type": "boolean", @@ -11595,19 +11596,19 @@ "description": "Whether its administrator or agent", "example": "agent" }, - "availability_status": { + "availability": { "type": "string", "enum": [ - "available", + "online", "busy", "offline" ], - "description": "The availability setting of the agent.", - "example": "available" + "description": "The configured availability of the agent.", + "example": "online" }, "auto_offline": { "type": "boolean", - "description": "Whether the availability status of agent is configured to go offline automatically when away.", + "description": "Whether the agent is automatically marked offline when they are away.", "example": true } } @@ -11627,19 +11628,19 @@ "description": "Whether its administrator or agent", "example": "agent" }, - "availability_status": { + "availability": { "type": "string", "enum": [ - "available", + "online", "busy", "offline" ], - "description": "The availability status of the agent.", - "example": "available" + "description": "The configured availability of the agent.", + "example": "online" }, "auto_offline": { "type": "boolean", - "description": "Whether the availability status of agent is configured to go offline automatically when away.", + "description": "Whether the agent is automatically marked offline when they are away.", "example": true } } diff --git a/swagger/tag_groups/application_swagger.json b/swagger/tag_groups/application_swagger.json index a013b3694..17e015139 100644 --- a/swagger/tag_groups/application_swagger.json +++ b/swagger/tag_groups/application_swagger.json @@ -8399,15 +8399,16 @@ "availability_status": { "type": "string", "enum": [ - "available", + "online", "busy", "offline" ], - "description": "The availability status of the agent computed by Chatwoot." + "readOnly": true, + "description": "The effective availability status of the agent, derived from the configured availability, auto-offline setting, and current presence. To update an agent's configured availability, use the availability field in create or update requests." }, "auto_offline": { "type": "boolean", - "description": "Whether the availability status of agent is configured to go offline automatically when away." + "description": "Whether the agent is automatically marked offline when they are away." }, "confirmed": { "type": "boolean", @@ -10102,19 +10103,19 @@ "description": "Whether its administrator or agent", "example": "agent" }, - "availability_status": { + "availability": { "type": "string", "enum": [ - "available", + "online", "busy", "offline" ], - "description": "The availability setting of the agent.", - "example": "available" + "description": "The configured availability of the agent.", + "example": "online" }, "auto_offline": { "type": "boolean", - "description": "Whether the availability status of agent is configured to go offline automatically when away.", + "description": "Whether the agent is automatically marked offline when they are away.", "example": true } } @@ -10134,19 +10135,19 @@ "description": "Whether its administrator or agent", "example": "agent" }, - "availability_status": { + "availability": { "type": "string", "enum": [ - "available", + "online", "busy", "offline" ], - "description": "The availability status of the agent.", - "example": "available" + "description": "The configured availability of the agent.", + "example": "online" }, "auto_offline": { "type": "boolean", - "description": "Whether the availability status of agent is configured to go offline automatically when away.", + "description": "Whether the agent is automatically marked offline when they are away.", "example": true } } diff --git a/swagger/tag_groups/client_swagger.json b/swagger/tag_groups/client_swagger.json index 763e090b1..7bc7227fb 100644 --- a/swagger/tag_groups/client_swagger.json +++ b/swagger/tag_groups/client_swagger.json @@ -1664,15 +1664,16 @@ "availability_status": { "type": "string", "enum": [ - "available", + "online", "busy", "offline" ], - "description": "The availability status of the agent computed by Chatwoot." + "readOnly": true, + "description": "The effective availability status of the agent, derived from the configured availability, auto-offline setting, and current presence. To update an agent's configured availability, use the availability field in create or update requests." }, "auto_offline": { "type": "boolean", - "description": "Whether the availability status of agent is configured to go offline automatically when away." + "description": "Whether the agent is automatically marked offline when they are away." }, "confirmed": { "type": "boolean", @@ -3367,19 +3368,19 @@ "description": "Whether its administrator or agent", "example": "agent" }, - "availability_status": { + "availability": { "type": "string", "enum": [ - "available", + "online", "busy", "offline" ], - "description": "The availability setting of the agent.", - "example": "available" + "description": "The configured availability of the agent.", + "example": "online" }, "auto_offline": { "type": "boolean", - "description": "Whether the availability status of agent is configured to go offline automatically when away.", + "description": "Whether the agent is automatically marked offline when they are away.", "example": true } } @@ -3399,19 +3400,19 @@ "description": "Whether its administrator or agent", "example": "agent" }, - "availability_status": { + "availability": { "type": "string", "enum": [ - "available", + "online", "busy", "offline" ], - "description": "The availability status of the agent.", - "example": "available" + "description": "The configured availability of the agent.", + "example": "online" }, "auto_offline": { "type": "boolean", - "description": "Whether the availability status of agent is configured to go offline automatically when away.", + "description": "Whether the agent is automatically marked offline when they are away.", "example": true } } diff --git a/swagger/tag_groups/other_swagger.json b/swagger/tag_groups/other_swagger.json index 50bf2212b..6dbfbdd8e 100644 --- a/swagger/tag_groups/other_swagger.json +++ b/swagger/tag_groups/other_swagger.json @@ -1079,15 +1079,16 @@ "availability_status": { "type": "string", "enum": [ - "available", + "online", "busy", "offline" ], - "description": "The availability status of the agent computed by Chatwoot." + "readOnly": true, + "description": "The effective availability status of the agent, derived from the configured availability, auto-offline setting, and current presence. To update an agent's configured availability, use the availability field in create or update requests." }, "auto_offline": { "type": "boolean", - "description": "Whether the availability status of agent is configured to go offline automatically when away." + "description": "Whether the agent is automatically marked offline when they are away." }, "confirmed": { "type": "boolean", @@ -2782,19 +2783,19 @@ "description": "Whether its administrator or agent", "example": "agent" }, - "availability_status": { + "availability": { "type": "string", "enum": [ - "available", + "online", "busy", "offline" ], - "description": "The availability setting of the agent.", - "example": "available" + "description": "The configured availability of the agent.", + "example": "online" }, "auto_offline": { "type": "boolean", - "description": "Whether the availability status of agent is configured to go offline automatically when away.", + "description": "Whether the agent is automatically marked offline when they are away.", "example": true } } @@ -2814,19 +2815,19 @@ "description": "Whether its administrator or agent", "example": "agent" }, - "availability_status": { + "availability": { "type": "string", "enum": [ - "available", + "online", "busy", "offline" ], - "description": "The availability status of the agent.", - "example": "available" + "description": "The configured availability of the agent.", + "example": "online" }, "auto_offline": { "type": "boolean", - "description": "Whether the availability status of agent is configured to go offline automatically when away.", + "description": "Whether the agent is automatically marked offline when they are away.", "example": true } } diff --git a/swagger/tag_groups/platform_swagger.json b/swagger/tag_groups/platform_swagger.json index f1e471e79..952813f62 100644 --- a/swagger/tag_groups/platform_swagger.json +++ b/swagger/tag_groups/platform_swagger.json @@ -1840,15 +1840,16 @@ "availability_status": { "type": "string", "enum": [ - "available", + "online", "busy", "offline" ], - "description": "The availability status of the agent computed by Chatwoot." + "readOnly": true, + "description": "The effective availability status of the agent, derived from the configured availability, auto-offline setting, and current presence. To update an agent's configured availability, use the availability field in create or update requests." }, "auto_offline": { "type": "boolean", - "description": "Whether the availability status of agent is configured to go offline automatically when away." + "description": "Whether the agent is automatically marked offline when they are away." }, "confirmed": { "type": "boolean", @@ -3543,19 +3544,19 @@ "description": "Whether its administrator or agent", "example": "agent" }, - "availability_status": { + "availability": { "type": "string", "enum": [ - "available", + "online", "busy", "offline" ], - "description": "The availability setting of the agent.", - "example": "available" + "description": "The configured availability of the agent.", + "example": "online" }, "auto_offline": { "type": "boolean", - "description": "Whether the availability status of agent is configured to go offline automatically when away.", + "description": "Whether the agent is automatically marked offline when they are away.", "example": true } } @@ -3575,19 +3576,19 @@ "description": "Whether its administrator or agent", "example": "agent" }, - "availability_status": { + "availability": { "type": "string", "enum": [ - "available", + "online", "busy", "offline" ], - "description": "The availability status of the agent.", - "example": "available" + "description": "The configured availability of the agent.", + "example": "online" }, "auto_offline": { "type": "boolean", - "description": "Whether the availability status of agent is configured to go offline automatically when away.", + "description": "Whether the agent is automatically marked offline when they are away.", "example": true } } diff --git a/theme/icons.js b/theme/icons.js index 266c7ddfa..281e21c23 100644 --- a/theme/icons.js +++ b/theme/icons.js @@ -113,6 +113,23 @@ export const icons = { width: 16, height: 20, }, + 'file-pfx': { + body: ` + + + + + + + + + + + + `, + width: 16, + height: 20, + }, bin: { body: ``, width: 16,