From cc5974da9bec82fccf7fcba1ed14b7326dc486c8 Mon Sep 17 00:00:00 2001 From: Muhsin Keloth Date: Wed, 6 May 2026 09:54:00 +0400 Subject: [PATCH 1/5] feat(inbox): Add beta badge for TikTok and Voice channels (#14378) TikTok and Voice channels in the inbox creation flow now display a small "Beta" badge next to their title, signaling that these integrations are still being polished while keeping them available for users to try. Fixes https://linear.app/chatwoot/issue/CW-7026/add-beta-label-for-tiktok-and-voice-inboxes --------- Co-authored-by: Muhsin <12408980+muhsin-k@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 (1M context) --- .../dashboard/components/ChannelSelector.vue | 25 ++++++++++++++++--- .../components/widgets/ChannelItem.vue | 5 ++++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/app/javascript/dashboard/components/ChannelSelector.vue b/app/javascript/dashboard/components/ChannelSelector.vue index 2e3ea7d86..ef5256577 100644 --- a/app/javascript/dashboard/components/ChannelSelector.vue +++ b/app/javascript/dashboard/components/ChannelSelector.vue @@ -1,5 +1,7 @@ diff --git a/app/javascript/dashboard/routes/dashboard/settings/macros/MacroEditor.vue b/app/javascript/dashboard/routes/dashboard/settings/macros/MacroEditor.vue index 0b916a4ab..b941598fe 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/macros/MacroEditor.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/macros/MacroEditor.vue @@ -8,6 +8,7 @@ import { MACRO_ACTION_TYPES } from './constants'; import { useAlert } from 'dashboard/composables'; import actionQueryGenerator from 'dashboard/helper/actionQueryGenerator.js'; import { useMacros } from 'dashboard/composables/useMacros'; +import { useAdmin } from 'dashboard/composables/useAdmin'; const store = useStore(); const getters = useStoreGetters(); @@ -18,6 +19,7 @@ const router = useRouter(); const { t } = useI18n(); const { getMacroDropdownValues } = useMacros(); +const { isAdmin } = useAdmin(); const macro = ref(null); const mode = ref('CREATE'); @@ -33,6 +35,9 @@ provide('macroActionTypes', macroActionTypes); const uiFlags = computed(() => getters['macros/getUIFlags'].value); const macroId = computed(() => route.params.macroId); +const isPublicMacroReadOnly = computed( + () => macro.value?.visibility === 'global' && !isAdmin.value +); const fetchDropdownData = () => { store.dispatch('agents/get'); @@ -92,7 +97,7 @@ const initNewMacro = () => { action_params: [], }, ], - visibility: 'global', + visibility: isAdmin.value ? 'global' : 'personal', }; }; @@ -110,6 +115,8 @@ watch( ); const saveMacro = async macroData => { + if (isPublicMacroReadOnly.value) return; + try { const action = mode.value === 'EDIT' ? 'macros/update' : 'macros/create'; const successMessage = @@ -136,6 +143,8 @@ const saveMacro = async macroData => { diff --git a/app/javascript/dashboard/routes/dashboard/settings/macros/MacroForm.vue b/app/javascript/dashboard/routes/dashboard/settings/macros/MacroForm.vue index 432b0d46b..b8b00aac9 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/macros/MacroForm.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/macros/MacroForm.vue @@ -16,6 +16,14 @@ export default { type: Object, default: () => ({}), }, + canManagePublicMacros: { + type: Boolean, + default: true, + }, + readOnly: { + type: Boolean, + default: false, + }, }, emits: ['submit'], setup() { @@ -112,19 +120,23 @@ export default {
- +
+ +
@@ -55,8 +89,13 @@ export default {

diff --git a/app/javascript/dashboard/routes/dashboard/conversation/SharedFiles.vue b/app/javascript/dashboard/routes/dashboard/conversation/SharedFiles.vue new file mode 100644 index 000000000..272407a53 --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/conversation/SharedFiles.vue @@ -0,0 +1,421 @@ + + + diff --git a/app/javascript/dashboard/store/modules/conversations/getters.js b/app/javascript/dashboard/store/modules/conversations/getters.js index 333009707..5e85c423c 100644 --- a/app/javascript/dashboard/store/modules/conversations/getters.js +++ b/app/javascript/dashboard/store/modules/conversations/getters.js @@ -57,6 +57,8 @@ const getters = { getSelectedChatAttachments: ({ selectedChatId, attachments }) => { return attachments[selectedChatId] || []; }, + getSelectedChatAttachmentsLoaded: ({ selectedChatId, attachments }) => + selectedChatId !== null && attachments[selectedChatId] !== undefined, getChatListFilters: ({ conversationFilters }) => conversationFilters, getLastEmailInSelectedChat: (stage, _getters) => { const selectedChat = _getters.getSelectedChat; diff --git a/app/javascript/dashboard/store/modules/specs/conversations/getters.spec.js b/app/javascript/dashboard/store/modules/specs/conversations/getters.spec.js index 69bf9c2ac..a7ee3833e 100644 --- a/app/javascript/dashboard/store/modules/specs/conversations/getters.spec.js +++ b/app/javascript/dashboard/store/modules/specs/conversations/getters.spec.js @@ -328,6 +328,31 @@ describe('#getters', () => { }); }); + describe('#getSelectedChatAttachmentsLoaded', () => { + it('returns true when attachments have been fetched for the selected chat', () => { + const state = { selectedChatId: 1, attachments: { 1: [] } }; + expect(getters.getSelectedChatAttachmentsLoaded(state)).toBe(true); + }); + + it('returns true when the fetched attachment list is non-empty', () => { + const state = { + selectedChatId: 1, + attachments: { 1: [{ id: 1, file_name: 'test' }] }, + }; + expect(getters.getSelectedChatAttachmentsLoaded(state)).toBe(true); + }); + + it('returns false when attachments have not been fetched yet', () => { + const state = { selectedChatId: 1, attachments: {} }; + expect(getters.getSelectedChatAttachmentsLoaded(state)).toBe(false); + }); + + it('returns false when no chat is selected', () => { + const state = { selectedChatId: null, attachments: {} }; + expect(getters.getSelectedChatAttachmentsLoaded(state)).toBe(false); + }); + }); + describe('#getContextMenuChatId', () => { it('returns the context menu chat id', () => { const state = { contextMenuChatId: 1 }; diff --git a/app/views/api/v1/accounts/conversations/attachments.json.jbuilder b/app/views/api/v1/accounts/conversations/attachments.json.jbuilder index 167b18390..8bd647f27 100644 --- a/app/views/api/v1/accounts/conversations/attachments.json.jbuilder +++ b/app/views/api/v1/accounts/conversations/attachments.json.jbuilder @@ -3,6 +3,7 @@ json.meta do end json.payload @attachments do |attachment| + json.id attachment.push_event_data[:id] json.message_id attachment.push_event_data[:message_id] json.thumb_url attachment.push_event_data[:thumb_url] json.data_url attachment.push_event_data[:data_url] diff --git a/spec/controllers/api/v1/accounts/conversations_controller_spec.rb b/spec/controllers/api/v1/accounts/conversations_controller_spec.rb index e007f4900..19d080b47 100644 --- a/spec/controllers/api/v1/accounts/conversations_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/conversations_controller_spec.rb @@ -1039,6 +1039,8 @@ RSpec.describe 'Conversations API', type: :request do expect(response).to have_http_status(:success) response_body = response.parsed_body + attachment = conversation.messages.last.attachments.first + expect(response_body['payload'].first['id']).to eq(attachment.id) expect(response_body['payload'].first['file_type']).to eq('image') expect(response_body['payload'].first['sender']['id']).to eq(conversation.messages.last.sender.id) end From 9c8cfc40b6380be66e3e1c426a84ff8b1b21342e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 15:14:54 +0530 Subject: [PATCH 5/5] chore(deps): bump dompurify from 3.3.2 to 3.4.0 (#14074) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.3.2 to 3.4.0.
Release notes

Sourced from dompurify's releases.

DOMPurify 3.4.0

Most relevant changes:

  • Fixed a problem with FORBID_TAGS not winning over ADD_TAGS, thanks @​kodareef5
  • Fixed several minor problems and typos regarding MathML attributes, thanks @​DavidOliver
  • Fixed ADD_ATTR/ADD_TAGS function leaking into subsequent array-based calls, thanks @​1Jesper1
  • Fixed a missing SAFE_FOR_TEMPLATES scrub in RETURN_DOM path, thanks @​bencalif
  • Fixed a prototype pollution via CUSTOM_ELEMENT_HANDLING, thanks @​trace37labs
  • Fixed an issue with ADD_TAGS function form bypassing FORBID_TAGS, thanks @​eddieran
  • Fixed an issue with ADD_ATTR predicates skipping URI validation, thanks @​christos-eth
  • Fixed an issue with USE_PROFILES prototype pollution, thanks @​christos-eth
  • Fixed an issue leading to possible mXSS via Re-Contextualization, thanks @​researchatfluidattacks and others
  • Fixed an issue with closing tags leading to possible mXSS, thanks @​frevadiscor
  • Fixed a problem with the type dentition patcher after Node version bump
  • Fixed freezing BS runs by reducing the tested browsers array
  • Bumped several dependencies where possible
  • Added needed files for OpenSSF scorecard checks

Published Advisories are here: https://github.com/cure53/DOMPurify/security/advisories?state=published

DOMPurify 3.3.3

  • Fixed an engine requirement for Node 20 which caused hiccups, thanks @​Rotzbua
Commits
  • 5b16e0b Getting 3.x branch ready for 3.4.0 release (#1250)
  • 8bcbf73 chore: Preparing 3.3.3 release
  • 5faddd6 fix: engine requirement (#1210)
  • 0f91e3a Update README.md
  • d5ff1a8 Merge branch 'main' of github.com:cure53/DOMPurify
  • c3efd48 fix: moved back from jsdom 28 to jsdom 20
  • 988b888 fix: moved back from jsdom 28 to jsdom 20
  • 2726c74 chore: Preparing 3.3.2 release
  • 6202c7e build(deps): bump @​tootallnate/once and jsdom (#1204)
  • 302b51d fix: Expanded the regex ever so slightly to also cover script
  • Additional commits viewable in compare view

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=dompurify&package-manager=npm_and_yarn&previous-version=3.3.2&new-version=3.4.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/chatwoot/chatwoot/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com> --- package.json | 2 +- pnpm-lock.yaml | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index bb5b59ba3..35e09cfbc 100644 --- a/package.json +++ b/package.json @@ -68,7 +68,7 @@ "countries-and-timezones": "^3.6.0", "date-fns": "2.21.1", "date-fns-tz": "^1.3.3", - "dompurify": "3.3.2", + "dompurify": "3.4.0", "flag-icons": "^7.2.3", "floating-vue": "^5.2.2", "highlight.js": "^11.10.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2a3aee897..d76d0a061 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -128,8 +128,8 @@ importers: specifier: ^1.3.3 version: 1.3.8(date-fns@2.21.1) dompurify: - specifier: 3.3.2 - version: 3.3.2 + specifier: 3.4.0 + version: 3.4.0 flag-icons: specifier: ^7.2.3 version: 7.2.3 @@ -2194,9 +2194,8 @@ packages: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} - dompurify@3.3.2: - resolution: {integrity: sha512-6obghkliLdmKa56xdbLOpUZ43pAR6xFy1uOrxBaIDjT+yaRuuybLjGS9eVBoSR/UPU5fq3OXClEHLJNGvbxKpQ==} - engines: {node: '>=20'} + dompurify@3.4.0: + resolution: {integrity: sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==} domutils@3.1.0: resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} @@ -6861,7 +6860,7 @@ snapshots: dependencies: domelementtype: 2.3.0 - dompurify@3.3.2: + dompurify@3.4.0: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -9656,7 +9655,7 @@ snapshots: vue-dompurify-html@5.3.0(vue@3.5.12(typescript@5.6.2)): dependencies: - dompurify: 3.3.2 + dompurify: 3.4.0 vue: 3.5.12(typescript@5.6.2) vue-eslint-parser@9.4.3(eslint@8.57.0):