## Description
Adds an API-only branded email layout feature for Email inbox replies.
Administrators can configure an account-level fallback layout and
per-email-inbox overrides with Liquid HTML using `{{ content_for_layout
}}`, and eligible outbound email replies/transcripts render through the
scoped layout when the account feature flag `branded_email_templates` is
enabled.
The feature is disabled by default and is manually controlled through
the normal account feature flag mechanism.
Fixes
https://linear.app/chatwoot/issue/CW-7514/branded-html-email-templates-per-inboxbrand
## Type of change
- [x] New feature (non-breaking change which adds functionality)
- [x] This change requires a documentation update
## How to test
1. Start Chatwoot locally and sign in as an administrator.
2. Enable the account feature flag for the account you are testing:
```ruby
account = Account.find(<account_id>)
account.enable_features!(:branded_email_templates)
```
3. Create or pick an Email inbox, then note the `account_id` and
`inbox_id`.
4. Configure an account-level fallback layout through the API using
authenticated admin headers:
```http
PATCH /api/v1/accounts/:account_id/branded_email_layout
Content-Type: application/json
{
"branded_email_layout": "<html><body><header>Account Brand</header>{{
content_for_layout }}<footer>Account footer</footer></body></html>"
}
```
5. Confirm `GET /api/v1/accounts/:account_id/branded_email_layout`
returns the saved account layout.
6. Configure an inbox-level override for the Email inbox:
```http
PATCH /api/v1/accounts/:account_id/inboxes/:inbox_id
Content-Type: application/json
{
"branded_email_layout": "<html><body><header>Inbox Brand</header>{{
content_for_layout }}<footer>Inbox footer</footer></body></html>"
}
```
7. Confirm `GET /api/v1/accounts/:account_id/inboxes/:inbox_id` returns
the inbox `branded_email_layout`.
8. Send an Email inbox reply and verify the outbound email body is
wrapped with the inbox layout around the generated reply content.
9. Clear the inbox layout by sending a blank value, then send another
reply and verify it falls back to the account layout:
```http
PATCH /api/v1/accounts/:account_id/inboxes/:inbox_id
Content-Type: application/json
{
"branded_email_layout": ""
}
```
10. Clear the account layout with a blank value and verify Email replies
return to the existing no-layout behavior.
11. Verify validation behavior:
- Updating either API with a layout that omits `{{ content_for_layout
}}` returns `422`.
- Updating either API with invalid Liquid returns `422`.
- Updating a non-Email inbox with `branded_email_layout` returns `422`.
- Disabling `branded_email_templates` and updating a layout returns
`422`.
## How Has This Been Tested?
Validation:
- `bundle exec rspec spec/models/email_template_spec.rb
spec/controllers/api/v1/accounts/branded_email_layouts_controller_spec.rb
spec/controllers/api/v1/accounts/inboxes_controller_spec.rb
spec/lib/email_templates/db_resolver_service_spec.rb
spec/mailers/conversation_reply_mailer_spec.rb`
- `bundle exec rspec
spec/enterprise/services/enterprise/billing/handle_stripe_event_service_spec.rb
spec/enterprise/services/internal/reconcile_plan_config_service_spec.rb`
- `bundle exec rubocop` on changed Ruby files, excluding generated
`db/schema.rb`
- `git diff --check` and `git diff --cached --check`
- YAML parsing for changed config/Swagger files
- `bundle exec rails routes -g branded_email_layout`
## Checklist:
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [x] I have made corresponding changes to the documentation
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [ ] I have commented on my code, particularly in hard-to-understand
areas
- [ ] My changes generate no new warnings
- [ ] Any dependent changes have been merged and published in downstream
modules
267 lines
7.3 KiB
YAML
267 lines
7.3 KiB
YAML
# DO NOT change the order of features EVER
|
|
############################################
|
|
# name: the name to be used internally in the code
|
|
# display_name: the name to be used in the UI
|
|
# enabled: whether the feature is enabled by default
|
|
# column: the account bitset column used to store the flag. Defaults to feature_flags.
|
|
# Use feature_flags_ext_1 for extension flags. Each bigint column supports 63 flags.
|
|
# help_url: the url to the help center article
|
|
# chatwoot_internal: whether the feature is internal to Chatwoot and should not be shown in the UI for other self hosted installations
|
|
# deprecated: purpose of feature flag is done, no need to show it in the UI anymore
|
|
#
|
|
# ADDING A NEW FEATURE FLAG:
|
|
# - The `feature_flags` column is FULL (63/63 slots used). Do NOT add to it.
|
|
# - New flags MUST set `column: feature_flags_ext_1` and be appended at the end.
|
|
# - Bit positions are persisted per column; never reorder or remove existing
|
|
# entries, and never change an existing feature's `column` after release.
|
|
- name: inbound_emails
|
|
display_name: Inbound Emails
|
|
enabled: true
|
|
- name: channel_email
|
|
display_name: Email Channel
|
|
enabled: true
|
|
help_url: https://chwt.app/hc/email
|
|
- name: channel_facebook
|
|
display_name: Facebook Channel
|
|
enabled: true
|
|
help_url: https://chwt.app/hc/fb
|
|
- name: conversation_unread_counts
|
|
display_name: Conversation Unread Counts
|
|
enabled: false
|
|
chatwoot_internal: true
|
|
- name: ip_lookup
|
|
display_name: IP Lookup
|
|
enabled: false
|
|
- name: disable_branding
|
|
display_name: Disable Branding
|
|
enabled: false
|
|
premium: true
|
|
- name: email_continuity_on_api_channel
|
|
display_name: Email Continuity on API Channel
|
|
enabled: false
|
|
- name: help_center
|
|
display_name: Help Center
|
|
enabled: true
|
|
help_url: https://chwt.app/hc/help-center
|
|
- name: agent_bots
|
|
display_name: Agent Bots
|
|
enabled: true
|
|
help_url: https://chwt.app/hc/agent-bots
|
|
- name: macros
|
|
display_name: Macros
|
|
enabled: true
|
|
- name: agent_management
|
|
display_name: Agent Management
|
|
enabled: true
|
|
- name: team_management
|
|
display_name: Team Management
|
|
enabled: true
|
|
help_url: https://chwt.app/hc/teams
|
|
- name: inbox_management
|
|
display_name: Inbox Management
|
|
enabled: true
|
|
- name: labels
|
|
display_name: Labels
|
|
enabled: true
|
|
help_url: https://chwt.app/hc/labels
|
|
- name: custom_attributes
|
|
display_name: Custom Attributes
|
|
enabled: true
|
|
help_url: https://chwt.app/hc/custom-attributes
|
|
- name: automations
|
|
display_name: Automations
|
|
enabled: true
|
|
- name: canned_responses
|
|
display_name: Canned Responses
|
|
enabled: true
|
|
help_url: https://chwt.app/hc/canned
|
|
- name: integrations
|
|
display_name: Integrations
|
|
enabled: true
|
|
help_url: https://chwt.app/hc/integrations
|
|
- name: voice_recorder
|
|
display_name: Voice Recorder
|
|
enabled: true
|
|
- name: report_rollup
|
|
display_name: Report Rollup
|
|
enabled: false
|
|
- name: channel_website
|
|
display_name: Website Channel
|
|
enabled: true
|
|
- name: campaigns
|
|
display_name: Campaigns
|
|
enabled: true
|
|
help_url: https://chwt.app/hc/campaigns
|
|
- name: reports
|
|
display_name: Reports
|
|
enabled: true
|
|
help_url: https://chwt.app/hc/reports
|
|
- name: crm
|
|
display_name: CRM
|
|
enabled: true
|
|
- name: auto_resolve_conversations
|
|
display_name: Auto Resolve Conversations
|
|
enabled: true
|
|
- name: custom_reply_email
|
|
display_name: Custom Reply Email
|
|
enabled: false
|
|
- name: custom_reply_domain
|
|
display_name: Custom Reply Domain
|
|
enabled: false
|
|
- name: audit_logs
|
|
display_name: Audit Logs
|
|
enabled: false
|
|
premium: true
|
|
- name: custom_tools
|
|
display_name: Custom Tools
|
|
enabled: false
|
|
premium: true
|
|
- name: message_reply_to
|
|
display_name: Message Reply To
|
|
enabled: false
|
|
deprecated: true
|
|
- name: branded_email_templates
|
|
display_name: Branded Email Templates
|
|
enabled: false
|
|
- name: inbox_view
|
|
display_name: Inbox View
|
|
enabled: false
|
|
chatwoot_internal: true
|
|
- name: sla
|
|
display_name: SLA
|
|
enabled: false
|
|
premium: true
|
|
help_url: https://chwt.app/hc/sla
|
|
- name: help_center_embedding_search
|
|
display_name: Help Center Embedding Search
|
|
enabled: false
|
|
premium: true
|
|
chatwoot_internal: true
|
|
- name: linear_integration
|
|
display_name: Linear Integration
|
|
enabled: false
|
|
- name: captain_integration
|
|
display_name: Captain
|
|
enabled: false
|
|
premium: true
|
|
- name: custom_roles
|
|
display_name: Custom Roles
|
|
enabled: false
|
|
premium: true
|
|
- name: chatwoot_v4
|
|
display_name: Chatwoot V4
|
|
enabled: true
|
|
- name: captain_v1_action_classifier
|
|
display_name: Captain V1 Action Classifier
|
|
enabled: false
|
|
premium: true
|
|
chatwoot_internal: true
|
|
- name: contact_chatwoot_support_team
|
|
display_name: Contact Chatwoot Support Team
|
|
enabled: true
|
|
chatwoot_internal: true
|
|
- name: shopify_integration
|
|
display_name: Shopify Integration
|
|
enabled: false
|
|
chatwoot_internal: true
|
|
- name: search_with_gin
|
|
display_name: Search messages with GIN
|
|
enabled: false
|
|
chatwoot_internal: true
|
|
- name: channel_instagram
|
|
display_name: Instagram Channel
|
|
enabled: true
|
|
- name: crm_integration
|
|
display_name: CRM Integration
|
|
enabled: false
|
|
- name: channel_voice
|
|
display_name: Voice Channel
|
|
enabled: false
|
|
premium: true
|
|
- name: notion_integration
|
|
display_name: Notion Integration
|
|
enabled: false
|
|
- name: captain_integration_v2
|
|
display_name: Captain V2
|
|
enabled: false
|
|
premium: true
|
|
- name: whatsapp_embedded_signup
|
|
display_name: WhatsApp Embedded Signup
|
|
enabled: false
|
|
deprecated: true
|
|
- name: whatsapp_campaign
|
|
display_name: WhatsApp Campaign
|
|
enabled: false
|
|
- name: crm_v2
|
|
display_name: CRM V2
|
|
enabled: false
|
|
chatwoot_internal: true
|
|
- name: assignment_v2
|
|
display_name: Assignment V2
|
|
enabled: true
|
|
- name: captain_document_auto_sync
|
|
display_name: Captain Document Auto Sync
|
|
enabled: false
|
|
premium: true
|
|
- name: advanced_search
|
|
display_name: Advanced Search
|
|
enabled: false
|
|
premium: true
|
|
- name: saml
|
|
display_name: SAML
|
|
enabled: false
|
|
premium: true
|
|
- name: advanced_search_indexing
|
|
display_name: Advanced Search Indexing
|
|
enabled: false
|
|
premium: true
|
|
chatwoot_internal: true
|
|
- name: reply_mailer_migration
|
|
# This feature is temporary only to migrate reply mailer to new email builder
|
|
# Once the migration is done, this feature can be removed
|
|
display_name: Reply Mailer Migration
|
|
enabled: false
|
|
chatwoot_internal: true
|
|
- name: unread_count_for_filters
|
|
display_name: Unread Count For Filters
|
|
enabled: false
|
|
chatwoot_internal: true
|
|
- name: companies
|
|
display_name: Companies
|
|
enabled: false
|
|
premium: true
|
|
- name: channel_tiktok
|
|
display_name: TikTok Channel
|
|
enabled: true
|
|
- name: csat_review_notes
|
|
display_name: CSAT Review Notes
|
|
enabled: false
|
|
premium: true
|
|
- name: captain_tasks
|
|
display_name: Captain Tasks
|
|
enabled: true
|
|
- name: conversation_required_attributes
|
|
display_name: Required Conversation Attributes
|
|
enabled: false
|
|
premium: true
|
|
- name: advanced_assignment
|
|
display_name: Advanced Assignment
|
|
enabled: false
|
|
premium: true
|
|
- name: whatsapp_manual_transfer
|
|
display_name: WhatsApp Manual Transfer
|
|
enabled: false
|
|
column: feature_flags_ext_1
|
|
- name: data_import
|
|
display_name: Data Import
|
|
enabled: false
|
|
column: feature_flags_ext_1
|
|
- name: api_and_webhooks
|
|
display_name: API and Webhooks
|
|
enabled: true
|
|
column: feature_flags_ext_1
|
|
- name: whatsapp_reconfigure
|
|
display_name: WhatsApp Reconfigure
|
|
enabled: false
|
|
column: feature_flags_ext_1
|