diff --git a/.bundler-audit.yml b/.bundler-audit.yml index 0a6c574ab..ffbfa18e0 100644 --- a/.bundler-audit.yml +++ b/.bundler-audit.yml @@ -2,6 +2,9 @@ ignore: - CVE-2021-41098 # https://github.com/chatwoot/chatwoot/issues/3097 (update once azure blob storage is updated) - GHSA-57hq-95w6-v4fc # Devise confirmable race condition — patched locally in User model (remove once on Devise 5+) + # Devise 5 is currently blocked by devise-secure_password/devise_token_auth/devise-two-factor. + # Chatwoot does not enable Timeoutable, so the timeout redirect path is not reachable. + - GHSA-jp94-3292-c3xv # Rails 7.1 has no patched release for the Active Storage proxy range # advisories. Chatwoot limits proxy range requests locally. - CVE-2026-33658 diff --git a/Gemfile b/Gemfile index 275848e4f..54ce18883 100644 --- a/Gemfile +++ b/Gemfile @@ -133,9 +133,9 @@ gem 'sentry-ruby', require: false gem 'sentry-sidekiq', '>= 5.19.0', require: false ##-- background job processing --## -gem 'sidekiq', '>= 7.3.1' +gem 'sidekiq', '~> 7.3', '>= 7.3.1' # We want cron jobs -gem 'sidekiq-cron', '>= 1.12.0' +gem 'sidekiq-cron', '>= 2.4.0' # for sidekiq healthcheck gem 'sidekiq_alive' diff --git a/Gemfile.lock b/Gemfile.lock index 44cba32e4..4f2443944 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -196,6 +196,9 @@ GEM bigdecimal rexml crass (1.0.6) + cronex (0.15.0) + tzinfo + unicode (>= 0.4.4.5) csv (3.3.0) csv-safe (3.3.1) csv (~> 3.0) @@ -903,10 +906,11 @@ GEM logger rack (>= 2.2.4) redis-client (>= 0.22.2) - sidekiq-cron (1.12.0) - fugit (~> 1.8) + sidekiq-cron (2.4.0) + cronex (>= 0.13.0) + fugit (~> 1.8, >= 1.11.1) globalid (>= 1.0.1) - sidekiq (>= 6) + sidekiq (>= 6.5.0) sidekiq_alive (2.5.0) gserver (~> 0.0.1) sidekiq (>= 5, < 9) @@ -980,6 +984,7 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.8.2) + unicode (0.4.4.5) unicode-display_width (3.1.4) unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (4.0.4) @@ -1156,8 +1161,8 @@ DEPENDENCIES sentry-sidekiq (>= 5.19.0) shopify_api shoulda-matchers - sidekiq (>= 7.3.1) - sidekiq-cron (>= 1.12.0) + sidekiq (~> 7.3, >= 7.3.1) + sidekiq-cron (>= 2.4.0) sidekiq_alive simplecov (>= 0.21) simplecov_json_formatter diff --git a/app/controllers/api/v1/accounts/portals_controller.rb b/app/controllers/api/v1/accounts/portals_controller.rb index 27c4126a6..770018e3c 100644 --- a/app/controllers/api/v1/accounts/portals_controller.rb +++ b/app/controllers/api/v1/accounts/portals_controller.rb @@ -78,7 +78,9 @@ class Api::V1::Accounts::PortalsController < Api::V1::Accounts::BaseController def portal_params params.require(:portal).permit( :id, :color, :custom_domain, :header_text, :homepage_link, - :name, :page_title, :slug, :archived, { config: [:default_locale, { allowed_locales: [] }, { draft_locales: [] }] } + :name, :page_title, :slug, :archived, + { config: [:default_locale, :layout, { allowed_locales: [] }, { draft_locales: [] }, + { social_profiles: %i[facebook x instagram linkedin youtube tiktok github whatsapp] }] } ) end diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalLayoutContentSettings.vue b/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalLayoutContentSettings.vue new file mode 100644 index 000000000..20016dfc1 --- /dev/null +++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalLayoutContentSettings.vue @@ -0,0 +1,290 @@ + + + diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalSettings.vue b/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalSettings.vue index 83800e002..eccf988cc 100644 --- a/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalSettings.vue +++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalSettings.vue @@ -7,6 +7,7 @@ import { useMapGetter } from 'dashboard/composables/store.js'; import HelpCenterLayout from 'dashboard/components-next/HelpCenter/HelpCenterLayout.vue'; import PortalBaseSettings from 'dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/PortalBaseSettings.vue'; import PortalConfigurationSettings from './PortalConfigurationSettings.vue'; +import PortalLayoutContentSettings from './PortalLayoutContentSettings.vue'; import ConfirmDeletePortalDialog from 'dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/ConfirmDeletePortalDialog.vue'; import Spinner from 'dashboard/components-next/spinner/Spinner.vue'; import Button from 'dashboard/components-next/button/Button.vue'; @@ -102,6 +103,12 @@ const handleDeletePortal = () => { @send-cname-instructions="handleSendCnameInstructions" />
+ +
diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/classic-layout-preview.svg b/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/classic-layout-preview.svg new file mode 100644 index 000000000..d9fe9078a --- /dev/null +++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/classic-layout-preview.svg @@ -0,0 +1,10 @@ + diff --git a/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/documentation-layout-preview.svg b/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/documentation-layout-preview.svg new file mode 100644 index 000000000..efda93a6a --- /dev/null +++ b/app/javascript/dashboard/components-next/HelpCenter/Pages/PortalSettingsPage/documentation-layout-preview.svg @@ -0,0 +1,18 @@ + diff --git a/app/javascript/dashboard/components-next/radioCard/RadioCard.vue b/app/javascript/dashboard/components-next/radioCard/RadioCard.vue index 182325ed8..5f6528f90 100644 --- a/app/javascript/dashboard/components-next/radioCard/RadioCard.vue +++ b/app/javascript/dashboard/components-next/radioCard/RadioCard.vue @@ -1,4 +1,5 @@ diff --git a/app/javascript/dashboard/components/widgets/WootWriter/FullEditor.vue b/app/javascript/dashboard/components/widgets/WootWriter/FullEditor.vue index 8bd3e9457..1c7787de6 100644 --- a/app/javascript/dashboard/components/widgets/WootWriter/FullEditor.vue +++ b/app/javascript/dashboard/components/widgets/WootWriter/FullEditor.vue @@ -7,6 +7,7 @@ import { ArticleMarkdownTransformer, EditorState, Selection, + imageResizeView, } from '@chatwoot/prosemirror-schema'; import { suggestionsPlugin, @@ -235,6 +236,7 @@ export default { const tr = editorView.state.tr.replaceSelectionWith(tableNode); editorView.dispatch(tr.scrollIntoView()); }, + imageUpload: () => this.openFileBrowser(), }; const command = commandMap[actionKey]; @@ -332,6 +334,9 @@ export default { createEditorView() { editorView = new EditorView(this.$refs.editor, { state: state, + nodeViews: { + image: imageResizeView, + }, dispatchTransaction: tx => { state = state.apply(tx); editorView.updateState(state); diff --git a/app/javascript/dashboard/components/widgets/WootWriter/SlashCommandMenu.vue b/app/javascript/dashboard/components/widgets/WootWriter/SlashCommandMenu.vue index d74d889b2..fd168e7a6 100644 --- a/app/javascript/dashboard/components/widgets/WootWriter/SlashCommandMenu.vue +++ b/app/javascript/dashboard/components/widgets/WootWriter/SlashCommandMenu.vue @@ -60,6 +60,12 @@ const EDITOR_ACTIONS = [ icon: 'i-lucide-table', menuKey: 'insertTable', }, + { + value: 'imageUpload', + labelKey: 'SLASH_COMMANDS.IMAGE', + icon: 'i-lucide-image', + menuKey: 'imageUpload', + }, { value: 'strike', labelKey: 'SLASH_COMMANDS.STRIKETHROUGH', diff --git a/app/javascript/dashboard/i18n/locale/en/components.json b/app/javascript/dashboard/i18n/locale/en/components.json index 68db1cc85..5e84ac024 100644 --- a/app/javascript/dashboard/i18n/locale/en/components.json +++ b/app/javascript/dashboard/i18n/locale/en/components.json @@ -63,6 +63,7 @@ "CODE": "Code", "BULLET_LIST": "Bullet List", "ORDERED_LIST": "Ordered List", - "TABLE": "Table" + "TABLE": "Table", + "IMAGE": "Image" } } diff --git a/app/javascript/dashboard/i18n/locale/en/helpCenter.json b/app/javascript/dashboard/i18n/locale/en/helpCenter.json index 9ae849d25..758bb1dca 100644 --- a/app/javascript/dashboard/i18n/locale/en/helpCenter.json +++ b/app/javascript/dashboard/i18n/locale/en/helpCenter.json @@ -866,6 +866,28 @@ }, "EDIT_CONFIGURATION": "Edit configuration" }, + "LAYOUT_CONTENT": { + "HEADER": "Appearance", + "DESCRIPTION": "Pick the layout that fits how your visitors read.", + "LAYOUT": { + "CLASSIC": { + "TITLE": "Classic", + "DESCRIPTION": "A welcoming home page with search and featured topics." + }, + "SIDEBAR": { + "TITLE": "Documentation", + "DESCRIPTION": "Side-by-side navigation that keeps every guide a click away." + } + }, + "SOCIAL_LINKS": { + "HEADER": "Social links", + "DESCRIPTION": "Add the handle for each network and your help center builds the full link. Shown in the documentation layout footer.", + "PLACEHOLDER": "handle", + "ADD": "Add social link", + "REMOVE": "Remove" + }, + "SAVE": "Save changes" + }, "API": { "CREATE_PORTAL": { "SUCCESS_MESSAGE": "Portal created successfully", diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/components/LockToSingleConversationPreview.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/components/LockToSingleConversationPreview.vue index 77cbc2216..d2109ba9c 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/inbox/components/LockToSingleConversationPreview.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/components/LockToSingleConversationPreview.vue @@ -13,7 +13,7 @@ defineEmits(['update']);