diff --git a/packages/zammad-addon-bridge/README.md b/packages/zammad-addon-bridge/README.md index 67a05e7..ca8be3b 100644 --- a/packages/zammad-addon-bridge/README.md +++ b/packages/zammad-addon-bridge/README.md @@ -1,63 +1,150 @@ -# zammad-addon-bridge +# CDR Bridge Zammad Addon -An addon that adds [bridge](https://gitlab.com/digiresilience/link/link-stack) channels to Zammad. +## Overview -## Channels +The CDR Bridge addon integrates external communication channels (Signal, WhatsApp, Voice) into Zammad, supporting both the classic UI and the new Vue-based desktop/mobile interfaces. -This channel creates a three channels: "Voice", "Signal" and "Whatsapp". +## Features -To submit a ticket: make a POST to the Submission Endpoint with the header -`Authorization: SUBMISSION_TOKEN`. +### Signal Channel Integration -The payload for the Voice channel must be a json object with the keys: +- Reply button on customer Signal messages +- "Add Signal message" button in ticket reply area +- 10,000 character limit with warning at 5,000 +- Plain text format with attachment support +- Full integration with both classic and new Vue-based UI -- `startTime` - string containing ISO date -- `endTime` - string containing ISO date -- `to` - fully qualified phone number -- `from` - fully qualified phone number -- `duration` - string containing the recording duration -- `callSid` - the unique identifier for the call -- `recording` - string base64 encoded binary of the recording -- `mimeType` - string of the binary mime-type +### WhatsApp Channel Integration -The payload for the Signal channel must be a json object with the keys: +- Reply button on customer WhatsApp messages +- "Add WhatsApp message" button in ticket reply area +- 4,096 character limit with warning at 3,000 +- Plain text format with attachment support +- Full integration with both classic and new Vue-based UI -- TBD +### Voice Channel Support -The payload for the Whatsapp channel must be a json object with the keys: +- Classic UI implementation maintained +- New UI support ready for future implementation -- TBD +### Channel Restriction Settings (NEW) + +- Control which reply channels appear in the UI +- Configurable via `cdr_link_allowed_channels` setting +- Acts as a whitelist while preserving contextual logic +- Empty setting falls back to default Zammad behavior + +## Installation + +### Prerequisites + +- Zammad 6.0+ (for new UI support) +- CDR Bridge backend services configured +- Signal/WhatsApp/Voice services running + +### Installation Steps + +1. Build the addon package: + +```bash +cd packages/zammad-addon-bridge +npm run build +``` + +2. Install in Zammad: + +```bash +# Copy the generated .zpm file to your Zammad installation +cp dist/bridge-vX.X.X.zpm /opt/zammad/ + +# Install using Zammad package manager +zammad run rails r "Package.install(file: '/opt/zammad/bridge-vX.X.X.zpm')" + +# Restart Zammad +systemctl restart zammad +``` + +## Configuration + +### Channel Restriction Settings + +Control which reply channels are available in the ticket interface: + +```ruby +# Rails console +Setting.set('cdr_link_allowed_channels', 'note,signal message') # Signal only +Setting.set('cdr_link_allowed_channels', 'note,whatsapp message') # WhatsApp only +Setting.set('cdr_link_allowed_channels', 'note,signal message,whatsapp message') # Both +Setting.set('cdr_link_allowed_channels', '') # Default behavior (all channels) +``` + +**How it works:** + +- The setting acts as a whitelist of allowed channels +- Channels must be both in the whitelist AND contextually appropriate +- For example, Signal replies only appear for tickets that originated from Signal +- Empty or unset falls back to default Zammad behavior +- Changes take effect immediately (browser refresh required) ## Development -1. Edit the files in `src/` +### Adding New Channels - Migration files should go in `src/db/addon/CHANNEL_NAME` ([see this post](https://community.zammad.org/t/automating-creation-of-custom-object-attributes/3831/2?u=abelxluck)) +1. Create TypeScript plugin in `app/frontend/shared/entities/ticket-article/action/plugins/` +2. Add desktop UI plugin in `app/frontend/apps/desktop/pages/ticket/components/TicketDetailView/article-type/plugins/` +3. Add corresponding backend implementation +4. Create database migrations in `src/db/addon/bridge/` -2. Update version and changelog in `bridge-skeleton.szpm` -3. Build a new package `make` - - This outputs `dist/bridge-vXXX.szpm` - -4. Install the szpm using the zammad package manager. - -5. Repeat - -### Create a new migration - -Included is a helper script to create new migrations. You must have the python -`inflection` library installed. - -- debian/ubuntu: `apt install python3-inflection` -- pip: `pip install --user inflection` -- or create your own venv - -To make a new migration simply run: +### Building the Package +```bash +# Update version and changelog in bridge-skeleton.szpm +# Build the package +make +# Output: dist/bridge-vX.X.X.szpm ``` + +### Create a New Migration + +Helper script to create new migrations (requires python `inflection` library): + +```bash +# Install dependency +apt install python3-inflection # Debian/Ubuntu +# Or: pip install --user inflection + +# Create migration make new-migration ``` +## Compatibility + +- **Zammad 6.0+**: Both Classic and New UI +- **Browser Support**: All modern browsers + +## API Endpoints + +### Voice Channel + +POST to submission endpoint with `Authorization: SUBMISSION_TOKEN` header: + +```json +{ + "startTime": "ISO date string", + "endTime": "ISO date string", + "to": "fully qualified phone number", + "from": "fully qualified phone number", + "duration": "recording duration string", + "callSid": "unique call identifier", + "recording": "base64 encoded binary", + "mimeType": "binary mime-type string" +} +``` + +### Signal/WhatsApp Channels + +Handled via CDR Bridge backend services - see bridge documentation for API details. + ## License [![License GNU AGPL v3.0](https://img.shields.io/badge/License-AGPL%203.0-lightgrey.svg)](https://gitlab.com/digiresilience/link/zamamd-addon-bridge/blob/master/LICENSE.md) @@ -66,5 +153,3 @@ This is a free software project licensed under the GNU Affero General Public License v3.0 (GNU AGPLv3) by [The Center for Digital Resilience](https://digiresilience.org) and [Guardian Project](https://guardianproject.info). - -🐻 diff --git a/packages/zammad-addon-bridge/src/app/frontend/apps/desktop/pages/ticket/components/TicketDetailView/ArticleReply.vue b/packages/zammad-addon-bridge/src/app/frontend/apps/desktop/pages/ticket/components/TicketDetailView/ArticleReply.vue new file mode 100644 index 0000000..0313bc0 --- /dev/null +++ b/packages/zammad-addon-bridge/src/app/frontend/apps/desktop/pages/ticket/components/TicketDetailView/ArticleReply.vue @@ -0,0 +1,355 @@ + + + + + + + diff --git a/packages/zammad-addon-bridge/src/app/frontend/apps/desktop/pages/ticket/components/TicketDetailView/article-type/plugins/signalMessage.ts b/packages/zammad-addon-bridge/src/app/frontend/apps/desktop/pages/ticket/components/TicketDetailView/article-type/plugins/signalMessage.ts new file mode 100644 index 0000000..1f4210b --- /dev/null +++ b/packages/zammad-addon-bridge/src/app/frontend/apps/desktop/pages/ticket/components/TicketDetailView/article-type/plugins/signalMessage.ts @@ -0,0 +1,7 @@ +import type { ChannelModule } from "#desktop/pages/ticket/components/TicketDetailView/article-type/types.ts"; + +export default { + name: "signal message", + label: __("Signal Message"), + icon: "cdr-signal", +}; diff --git a/packages/zammad-addon-bridge/src/app/frontend/apps/desktop/pages/ticket/components/TicketDetailView/article-type/plugins/whatsappMessage.ts b/packages/zammad-addon-bridge/src/app/frontend/apps/desktop/pages/ticket/components/TicketDetailView/article-type/plugins/whatsappMessage.ts new file mode 100644 index 0000000..8814ffa --- /dev/null +++ b/packages/zammad-addon-bridge/src/app/frontend/apps/desktop/pages/ticket/components/TicketDetailView/article-type/plugins/whatsappMessage.ts @@ -0,0 +1,7 @@ +import type { ChannelModule } from "#desktop/pages/ticket/components/TicketDetailView/article-type/types.ts"; + +export default { + name: "whatsapp message", + label: __("WhatsApp Message"), + icon: "whatsapp", +}; diff --git a/packages/zammad-addon-bridge/src/db/addon/bridge/20250105000001_add_channel_restriction_setting.rb b/packages/zammad-addon-bridge/src/db/addon/bridge/20250105000001_add_channel_restriction_setting.rb new file mode 100644 index 0000000..76046f4 --- /dev/null +++ b/packages/zammad-addon-bridge/src/db/addon/bridge/20250105000001_add_channel_restriction_setting.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +class AddChannelRestrictionSetting < ActiveRecord::Migration[5.2] + def self.up + Setting.create_if_not_exists( + title: 'CDR Link - Allowed Reply Channels', + name: 'cdr_link_allowed_channels', + area: 'Integration::CDRLink', + description: 'Comma-separated whitelist of allowed reply channels (e.g., "note,signal message,email"). Leave empty to allow all channels.', + options: { + form: [ + { + display: 'Allowed Channels', + null: true, + name: 'cdr_link_allowed_channels', + tag: 'input', + } + ], + }, + state: '', # Empty by default (allows all) + frontend: true, # Available to frontend + preferences: { + permission: ['admin'], + } + ) + end + + def self.down + Setting.find_by(name: 'cdr_link_allowed_channels')&.destroy + end +end \ No newline at end of file