10.1
Cerb (10.1) is a feature upgrade released on September 10, 2021. It includes more than 114 new features and improvements from community feedback.
- Important Release Notes
- Search
- Interactions
- Automations
- Automation Events
- Data Queries
- Time Tracking
- Full changelog
To check if you qualify for this release as a free update, view Setup » Configure » License. If your software updates expire on or after December 31, 2020 then you can upgrade without renewing your license. Cerb Cloud subscribers will be upgraded automatically.
Important Release Notes
-
Cerb 10.1 requires PHP 7.4+ and MySQL 5.6+ (or MariaDB 10.2+).
-
To upgrade your installation, follow these instructions.
-
NOTE: The branches in the
cerb/cerb-release
repository have changed. Themaster
branch has been removed, and new branches exist for each major version (e.g.v10.0
,v10.1
). -
Visit the community forums for tutorials and guides on new 10.x features.
Search
No record limit in searches
There is no longer a results limit when using fulltext search filter. Instead, inefficient searches are now automatically aborted by the existing query time limits.
Previously, fulltext searches were limited to 5,000 results.
Fulltext negation
All fulltext search filters now support negation. This returns records that don’t match the given terms.
text:!(not these words)
Error on unknown filter
Search queries with unknown filters now return an error and no results.
Previously, unknown filters were ignored, which could have unintentional effects.
Message header searching
Added a new fulltext search index for common message headers: from
, to
, cc
, delivered-to
, x-forwarded-to
, and x-mailer
.
On message records, added search filters for:
header.cc:
header.deliveredTo:
header.from:
header.to:
Previously, it was possible to filter by the message sender, but not by destination mailbox (without using custom fields).
Result count performance
In worklist search results, the total results are now calculated in parallel with retrieving the current page of results.
Previously, the count was calculated after the page of results was returned, which resulted in a longer wait.
The result count has a shorter query timeout, and no longer blocks the page of results from being returned.
In some environments, displaying an unfiltered total with millions of records can be slow with a cold cache. This no longer blocks a search popup from opening.
Search usability
When searching a worklist, a spinner and “Searching, please wait…” message is now displayed.
New searches will be ignored until the current search finishes. This makes it more difficult for workers to inadvertently burden the database server with many copies of the same query.
Previously, the worklist just dimmed to 50% opacity, which wasn’t intuitive for many users that anything was happening.
Subtotals performance
The subtotals sidebar on worklists now loads simultaneously (asynchronously). This also helps avoid the issue where a complex query prevents a worklist from finishing loading after closing and reopening.
Previously, the worklist results and subtotals sidebar were calculated before any output was shown, which combined their max timeouts and could result in no output if the webserver hung up first.
Deep search watchers
On worklists, all record types with a watchers:
filter now support deep searches using the worker records.
For instance:
watchers:(group:Support)
or:
watchers:(isDisabled:y)
It’s also still possible to use the shortcuts like watchers:any
and watchers:none
.
Interactions
await:duration: continuation
In interaction.worker automations, a new await:duration: continuation displays a message:
and waits until:
a given date/time or interval (e.g. 5 seconds
).
This can be used to display a status update while waiting for a long-running asynchronous task to finish. For instance, an AWS Step Function for new account provisioning.
start:
set:
isPlaying@bool: yes
while:
if@bool: {{isPlaying}}
do:
# A suspenseful wait
await/rolling:
duration:
message: Rolling...
until: 3 seconds
await/rolled:
form:
title: Results
elements:
say:
content@text:
You rolled a {{random(1,6)}}
====
submit/prompt_continue:
buttons:
continue/yes:
label: Roll again
icon: playing-dices
icon_at: start
value: roll
continue/no:
label: Quit
style: secondary
value: quit
# If quitting, break the loop
outcome/quit:
if@bool: {{prompt_continue == 'quit'}}
then:
set:
isPlaying@bool: no
await:form: custom submit buttons
In interaction.worker automations, during an await:form: continuation, a submit: may optionally specify any number of custom buttons.
Custom buttons can provide label, icon, icon position, style, and value. Each button is either a continue:
or reset:
type.
When clicked, a continue:
button sets the submit:
element’s key
to its value.
There may be multiple continue buttons (e.g. yes/no, allow/deny). This is much simpler than using a sheet, and it no longer requires a second click to continue.
When submit:buttons:
isn’t provided, the default ‘continue’ and ‘reset’ buttons are automatically added; and these can still be controlled with the submit:continue@bool:
and submit:reset@bool:
shortcuts.
The current alternative styles for buttons are ‘secondary’ (gray like reset) or ‘outline’ (blue like continue but not filled). This makes it easy to visually distinguish primary/default and secondary options.
start:
await:
form:
title: Confirmation
elements:
say:
content: # Are you sure?
submit/prompt_confirm:
buttons:
continue/yes:
label: Yes
value: yes
continue/no:
label: No
style: secondary
value: no
await:form: element validation
In interaction.worker automations, when using an await:form: continuation, form elements may now include a custom validation@raw:
option.
Automation scripting can run logic and any output is considered to be an error message.
The current element’s value can be compared by using its placeholder name (e.g. prompt_name
).
Multiple conditions can be checked by using if...elseif
logic.
start:
await:
form:
elements:
text/prompt_alias:
label: Alias
required@bool: yes
placeholder@text:
(letters, numbers, dashes; 3-32 characters)
validation@raw:
{% if prompt_alias != prompt_alias|alphanum('-') %}
An alias may only contain letters, numbers, and dashes.
{% elseif prompt_alias|length < 3 %}
An alias must be at least three characters long.
{% elseif prompt_alias > 32 %}
An alias must be 32 characters or less.
{% endif %}
await:form: fileUpload key expansion
In interaction.worker automations, in an await:form: continuation, fileUpload: elements now include full attachment record details for validation.
Previously, this kind of validation had to be done after the form validated and submitted.
start:
await:
form:
elements:
fileUpload/prompt_image:
label: File
required@bool: yes
validation@raw:
{% if prompt_image__context is empty or prompt_image_id is empty %}
The file must be a valid image.
{% elseif prompt_image_mime_type is not prefixed ('image/') %}
The file must be an image ({{prompt_image_mime_type}}).
{% elseif prompt_image_size > 50000 %}
The image must be smaller than 50KB ({{prompt_image_size|bytes_pretty}}).
{% endif %}
Improved sheet filtering
In interaction.worker automations, the search filter input is now more obvious in sheet: elements on await:form: continuations.
There is a magnifying glass icon with ‘Search’ placeholder text.
Previously, during usability testing, several users thought the filter input was used to create new interactions rather than choose them.
Improved interactions simulation
In the automations editor, when using the Run tab to simulate an interaction, copying the Output: to the Input: during an await:form: will automatically create placeholders for all form elements at the top of the new input.
This skips submit: elements without custom buttons, and sheet: elements without a selection:
column.
On sheet elements, it uses a string default for single
selection, and an array default for multiple
.
Previously these had to be discovered and added manually.
start:
await/ask:
form:
elements:
text/prompt_name:
label: Name:
required@bool: yes
type: freeform
text/prompt_email:
label: Email
required@bool: yes
type: email
await/confirm:
form:
elements:
say:
content@text:
Hi {{prompt_name}}!
We've sent a verification email to {{prompt_email}}
Improved interactions usability
In interaction.worker automations, pressing the escape
key asks for confirmation before closing the popup.
Previously, the interaction aborted, and the popup was closed as soon as escape
was pressed – whether accidental or not.
Automations
Sequential set action
In automations, the set: command now substitutes placeholders sequentially rather than all at once. This means you can set a value in the first key and refer to that value in the second key.
Previously this required two separate set:
commands.
start:
set:
a@int: 2
b@int: {{a*2}}
c@int: {{b*2}}
return:
answer@int: {{a + b + c}}
@optional annotation
In automations KATA, a new @optional
key annotation is available. This removes a key if its value evaluates to blank.
For instance, in a record.update:
a set of fields:
should only include those with new values.
Previously this required logic for every optional field (usually an outcome:
with a var.set:
).
start:
set:
task_name: Get coffee
task_status: open
record.create:
output: new_task
inputs:
record_type: task
# See: https://cerb.ai/docs/records/types/task/
fields:
title@key: task_name
importance@key,optional,int: task_importance
status@key: task_status
In the above example, there is no task_importance
placeholder. Since it’s marked @optional
, rather than throwing an error the key is simply ignored when it’s null.
HTTP headers array
In automations, using the http.request: command, the headers:
input now accepts either a string of newline-delimited header name/value pairs, or an array with header names as keys.
http.request:
inputs:
headers:
Content-Type: application/json
X-Requester: Cerb
http.request:
inputs:
headers@text:
{% if content_type %}
Content-Type: {{content_type}}
{% endif %}
X-Requester: Cerb
HTTP body auto-encoding
In automations, using the http.request: command, the body:
now accepts either a string or an array.
http.request:
inputs:
body@text:
This is the body content
on multiple indented lines.
If the body is defined as a dictionary of key: value
pairs, then it will automatically be encoded based on the Content-Type:
header:
- JSON (
application/json
) - YAML (
text/yaml
orapplication/x-yaml
) - URL-encoded (
application/x-www-form-urlencoded
); or if theContent-Type:
is omitted
This removes the need for extraneous set:
commands to prepare the HTTP request.
http.request:
inputs:
headers:
Content-Type: application/json
body:
person:
name: Kina Halpue
title: Customer Service Manager
Create events from chooser
In the Automation Events editor, when opening the automation chooser from the toolbar, it’s now possible to create a new automation from the sheet.
Previously, this only displayed existing automations and new ones had to be non-intuitively created from Search » Automations.
Error reporting
Improved error reporting in automations when siblings of the same type have duplicate names; including those with the same type and no names.
For instance, two sibling set: commands previously didn’t return an error, but only the second one was evaluated. This also catches the issue where someone accidentally provides more than one start:
command.
The fix for this is to add names to sibling commands of the same type:
start:
set/name:
name: Kina Halpue
set/title:
title: Customer Support Manager
Automation Events
mail.draft
Added a mail.draft automation event to modify any property on a new or resumed draft after a worker clicks on a ‘Compose’ or ‘Reply’ button, but before the editor popup opens. Previously, these workflows used ‘Before composing/replying’ behaviors, which required fragile Javascript.
Guide: Add a required custom fieldset on ticket replies using automations
mail.draft.validate
Added a mail.draft.validate automation event for implementing interactive custom validators when composing and replying to email. For example: no recipients, profanity filter, missing attachment, using formatting in plaintext, etc. These automations have the same functionality as worker interactions.
Guide: Add custom interactive validators when composing and replying to email
mail.received
Added a mail.received automation event to react to received messages after they are appended to a ticket. For instance, sending an auto-reply confirmation to new tickets.
The code editor toolbar now includes an option to automatically generate an example auto-reply automation.
Guide: Send an automatic confirmation on new tickets using automations
mail.send
Added a mail.send automation event to modify sent message drafts before they are delivered. For instance, appending a unique survey link to only the sent html message (not text, nor the copy Cerb saves).
mail.sent
Added a mail.sent automation event to perform actions after an outgoing message is sent by a worker.
Data Queries
Grouping by distinct values in worklist.subtotals
In worklist.subtotals data queries, a by.distinct:[...]
aggregate function is now available. This counts the number of distinct values in the final by:
field.
For instance, in an of:message
query, by.distinct:[created@day,worker,ticket]
would return the number of distinct tickets replied to per worker per day. Multiple replies on the same ticket, by the same worker, on the same day, would only count as 1
.
Custom fieldsets in pre-created drafts
Compose and reply drafts now properly save and resume custom fieldsets.
Keyboard shortcuts on compose and reply
In the compose and reply editors, added configurable keyboard shortcuts for common toolbar items (e.g. bold, insert link, insert image).
Download all attachments as ZIP
On messages on ticket profiles, in the ‘…’ menu, there’s a new ‘Download all (.zip)’ button when a message has attachments and the ‘zip’ PHP extension is installed/enabled. The button creates a single ZIP archive with the combined attachments and downloads it.
Time Tracking
See: https://github.com/cerb/cerb-release/discussions/38-Track-time-from-records-with-interactions
Timer persistence
Time tracking records are now created when the timer starts. Pausing a time tracking timer saves its progress to the server.
Resume existing time tracking entries
Time tracking timers can be resumed on previously saved records.
When resuming a time tracking timer from the editor, changes to fields and comments are now saved first. Previously these changes were lost when the timer was resumed.
Human-readable date entry
Time tracking time entry in human-readable units (e.g. “1 hour, 15 mins, 5 seconds”). Previously time entries only tracked in minutes.
Time tracking in seconds
Time tracking timers are now measured in seconds rather than minutes. This allows a previously saved timer to be resumed without rounding to the nearest minute. The elapsed minutes are still calculated and stored for backwards compatibility.
Full changelog
-
[Mail/Parser] Cerb now properly handles FormMail-style email headers, like
From: customer@example.com (Customer Name)
. -
[Toolbars] Added a draft.read toolbar for adding interactions to draft previews on ticket profiles.
-
[Mail/Compose] Compose drafts now properly save custom fields and fieldsets. [#1083]
-
[Mail/Drafts] Reply drafts properly save and reload custom fieldset values.
-
[Time Tracking] Time tracking records are now created when the timer starts. Pausing a time tracking timer saves its progress to the server. [#1243]
-
[Time Tracking] Time tracking timers can be resumed on previously saved records.
-
[Time Tracking] When resuming a time tracking timer from the editor, changes to fields and comments are now saved first. Previously these changes were lost when the timer was resumed.
-
[Time Tracking] Time tracking time entry in human-readable units (e.g. “1 hour, 15 mins, 5 seconds”). Previously time entries only tracked in minutes.
-
[Time Tracking] Time tracking timers are now measured in seconds rather than minutes. This allows a previously saved timer to be resumed without rounding to the nearest minute.
-
[Records/Search/Fulltext] In record search queries, fulltext filters can now use parentheses to specify a mix of phrases and terms. For instance,
text:("an exact phrase" other terms)
. -
[Sheets/Security] Sheets now automatically HTML escape all placeholders in templates by default. This can be overridden in certain situations with the
|raw
filter. -
[Records/Search] There is no longer a results limit when fulltext searching using a MySQL index (rather than Elasticsearch). Instead, inefficient searches are now automatically aborted by the existing query time limits. Every filter is now involved in queries that mix fulltext and field filters. [#1445]
-
[Records/Search] All fulltext search filters (on MySQL indexes) now support negation. For instance,
text:!(not these words)
-
[Search/Messages] Added a new fulltext search index for common message headers:
from
,to
,cc
,delivered-to
,x-forwarded-to
, andx-mailer
. -
[Records/Search/Messages] On message records, added a
header.deliveredTo:
search filter. -
[Records/Search/Messages] On message records, added a
header.from:
search filter. -
[Records/Search/Messages] On message records, added a
header.cc:
search filter. -
[Records/Search/Messages] On message records, added a
header.to:
search filter. -
[Storage/MySQL] Improved MySQL 8.x support by combining the
id
andchunk
indices on thestorage_
tables. -
[Records/Profiles] Added profile pages for message records. This makes it possible to isolate a single message, which simplifies workflows like printing.
-
[Records/Profiles/Messages] On message profile pages, the ‘Ticket Conversation’ widget can now display a single message with full functionality (e.g. toolbars, reply, comment, attachments).
-
[Records/Profiles/Drafts] On draft profile pages, the ‘Ticket Conversation’ widget can now display a single draft with full functionality (e.g. resume, comment). [#1360]
-
[Platform/Sessions] Increased the possible length of IPs in sessions. The limit was previously 40 characters, but some IPv6 (e.g. IPv4 mapping) can be slightly longer. [#1460]
-
[Automations/Mail] Implemented the mail.draft automation event. Automations can modify any property on a new or resumed draft after a worker clicks on a ‘Compose’ or ‘Reply’ button, but before the editor popup opens. Automations on this event are cumulative – multiple automations can modify the draft, with subsequent changes on the same fields overwriting earlier ones. When setting
custom_fields:
, theiruri
field can be used as the key instead of IDs. New custom field changes are merged with existing fields. This event drastically simplifies workflows that modify drafts (e.g. always adding aBcc:
field). Previously, these workflows used ‘Before composing/replying’ behaviors, which required fragile Javascript. -
[Automations/Mail] Implemented the mail.sent automation event. Automations can perform actions after an outgoing message is sent by a worker.
-
[Mail] Refactored mail sending functionality to generate content separately for every combination of saved/sent and html/text. For instance, a content modification can be targeted at only the ‘sent html’ part. Previously this was limited to saved/sent, but it couldn’t target html/text.
-
[Mail/Markdown/UX] When generating a plaintext body from a Markdown-formatted outgoing message, image tags with alt text are now properly matched and converted.
-
[Mail/Relays] Inbound messages from workers through the email relay now generate a draft before being parsed. If the message fails to send (e.g. SMTP issues), it will remain a draft and retry rather than disappearing.
-
[Automations/Mail] Implemented the mail.send automation event. Automations can modify sent message drafts before they are delivered. For instance, appending a unique survey link to only the sent html message (not text, nor the copy Cerb saves), setting custom fields, or adding custom mail headers to prevent Sendgrid from rewriting the
Message-Id:
header. Content modifications can target any combination of text/html on the sent/saved message. -
[Search/UX] Search queries with unknown filters now return an error and no results. Previously, unknown filters were ignored, which could have unintentional effects.
-
[Custom Records/Performance] On custom records, added an index to improve performance on filtering by the
name:
field. Some people use the name as a lookup field which could have >100K records. [#1466] -
[Records/Tickets/UX] On ticket profiles, messages in the conversation now have a button to open their card rather than directly opening the editor. This makes it easier to see message links, view the full profile, etc.
-
[Records/Tickets/UX] On ticket profiles, comments in the conversation now have a button to open their card rather than directly opening the editor. This makes it easier to see comment links, view the full profile, etc.
-
[Records/Tickets/UX] On ticket profiles, drafts in the conversation now have a button to open their card rather than directly opening the editor. This makes it easier to see draft links, view the full profile, etc.
-
[Worklists/Subtotals] In worklist subtotals, changed the default ‘none’ category to ‘(none)’ to differentiate it from a literal value of ‘none’.
-
[Worklists/Subtotals/Performance] The subtotals sidebar on worklists now loads simultaneously (asynchronously). Previously, the worklist results and subtotals sidebar were calculated before any output was shown, which combined their max timeouts and could result in no output if the webserver hung up first (usually after 30 seconds). This also helps avoid the issue where a complex query prevents a worklist from finishing loading after closing and reopening.
-
[Worklists/Subtotals/Performance] Fixed an issue where the worklist subtotals sidebar wasn’t constrained by a query timeout. On a complex query, this could result in a backlog of database queries and a worklist that doesn’t finish loading.
-
[Interactions/Await] On interaction.worker automations, added an await:duration: continuation. This displays a
message:
and waitsuntil:
a given date/time or interval (e.g.5 seconds
). This can be used to display a status update while waiting for a long-running asynchronous task to finish. For instance, an AWS Step Function for new account provisioning. [#1455] -
[Automations] In automations, the set: command now substitutes placeholders sequentially rather than all at once. This means you can set a value in the first key and refer to that value in the second key. Previously this required two separate
set:
commands. -
[Automations] In automations, using the http.request: command, the
headers:
input now accepts either a string of newline-delimited header name/value pairs, or an array with header names as keys. -
[Automations] In automations, using the http.request: command, the
body:
now accepts either a string or an array. Arrays are automatically encoded to JSON (application/json
) or YAML (application/x-yaml
/text/yaml
) with the appropriateContent-Type:
header; and are otherwise URL-encoded (application/x-www-form-urlencoded
). This removes the need for extraneousset:
commands to prepare the HTTP request. -
[Interactions] On
interaction.worker
automations, when using an await:form: continuation, form elements may now include a customvalidation@raw:
option. Automation scripting can run logic and any output is considered to be an error message. The current element’s value can be compared by using its placeholder name (e.g.prompt_name
). Multiple conditions can be checked by usingif...elseif
logic. -
[Interactions] On
interaction.website
automations, when using anawait:form:
continuation, form elements may now include a customvalidation@raw:
option. Automation scripting can run logic and any output is considered to be an error message. The current element’s value can be compared by using its placeholder name (e.g.prompt_name
). Multiple conditions can be checked by usingif...elseif
logic. -
[Interactions/Worker/UX] In worker interactions, the search filter input is now more obvious in
sheet
elements onawait:form:
continuations. There is a magnifying glass icon with ‘Search’ placeholder text. Previously, during usability testing, several users thought the filter input was used to create new interactions rather than choose them. -
[Automations/Errors] Improved error reporting in automations when siblings of the same type have duplicate names; including those with the same type and no names. For instance, two sibling
set:
commands previously didn’t return an error, but only the second one was evaluated. This also catches the issue where someone accidentally provides more than onestart:
command. [#1462] [#1463] -
[Automations/UX] In automations KATA, a new
@optional
key annotation is available. This removes a key if its value evaluates to blank. For instance, in arecord.update:
a set offields:
should only include those with new values. Previously this required logic for every optional field (usually anoutcome:
with avar.set:
). -
[Automations/Events] In the Automation Events editor, when opening the automation chooser from the toolbar, it’s now possible to create a new automation from the sheet. Previously, this only displayed existing automations and new ones had to be non-intuitively created from Search->Automations.
-
[Automations/Events/Mail] Implemented the mail.received automation event. Automations can react to received messages after they are appended to a ticket. For instance, sending an auto-reply confirmation to new tickets.
-
[Automations/Events/UX] In the
mail.received
automation event editor, the code editor toolbar now includes an option to generate an example auto-reply automation. -
[Mail/Toolbars/Keyboard] In the compose and reply editors, added configurable keyboard shortcuts for common toolbar items (e.g. bold, insert link, insert image). [#1479]
-
[Mail/Downloads] On messages on ticket profiles, in the ‘…’ menu, there’s a new ‘Download all (.zip)’ button when a message has attachments and the ‘zip’ PHP extension is installed/enabled. The button creates a single ZIP archive with the combined attachments and downloads it.
-
[Search/Timeouts] Improved how timed out queries are canceled on the database server. Previously, the browser was properly displaying ‘timed out’, but the database server continued to run some queries. A new database connection is now used to terminate other threads.
-
[Search/Timeouts] When a search query times out, the full query is now logged in the PHP error log, along with the database name, process ID, and timeout limit. Previously, the logged query was limited to the error log max line length default in PHP (usually 1024 characters).
-
[Worklists/Search/Performance] In worklist search results, the total results are calculated in parallel with retrieving the current page of results. Previously, the count was calculated after the page of results was returned, which resulted in a longer wait. The result count has a shorter query timeout, and no longer blocks the page of results from being returned. In some environments, displaying an unfiltered total with millions of records can be slow with a cold cache. This no longer blocks a search popup from opening.
-
[Records/Contacts] On contact records, the primary email address must now be unique. [#1113]
-
[Records/Custom Fields] In custom field records, the
URI
field no longer allows dots. This interferes with key expansion in dictionaries. -
[Records/Search/UX] When searching a worklist, a spinner and “Searching, please wait…” message is now displayed. Previously, the worklist just dimmed to 50% opacity, which wasn’t intuitive for many users that anything was happening.
-
[Records/Search/Performance] When searching a worklist, new searches will be ignored until the current search finishes. This makes it more difficult for workers to inadvertently burden the database server with many copies of the same query.
-
[Records/Watchers] On worklists, all record types with a
watchers:
filter now support deep searches using the worker records. For instance:watchers:(group:Support)
orwatchers:(isDisabled:y)
. It’s also still possible to use the shortcuts likewatchers:any
andwatchers:none
. [#591] -
[Dashboards/Library/UX] In the workspace widget library, the examples for ‘Categories’ and ‘Sheet’ now default to
created:"-1 year to now"
. Previously these used all available data, which could lead to timeouts in environments with millions of tickets. -
[Records/Webhooks] Added the ‘Watchers’ column to webhook listener worklists.
-
[Portals] In community portal URIs, dashes (
-
) are now valid characters. -
[Automations/Scripting] In automation scripting, added a strip_lines(prefixes) filter. This removes lines with the given prefixes. This is particularly useful for removing quoted text in compose/reply behaviors.
-
[Automations/Scripting] In automation scripting, added a tokenize filter. This converts a text block into an array of word tokens without spaces or punctuation.
-
[Automations/Syntax] On
interaction.worker
automations, added an obvious error when anawait:form:
element provides a non-string value to thevalidation:
option. Previously this caused the interaction to end unexpectedly. The error is also logged on the automation. -
[Automations/Interactions/UX] In
interaction.worker
automations, it’s no longer necessary to useheadless:
to denote that a non-interactive workflow shouldn’t open the interaction popup. This is now handled automatically when an automation exits in a state other thanawait:
. Interactions now run on the server before the popup opens, which now provides the option to not open it at all. -
[Automations/Interactions/UX] In
interaction.worker
automations, pressing theescape
key asks for confirmation before closing the popup. Previously, the interaction aborted, and the popup was closed as soon asescape
was pressed – whether accidental or not. -
[Automations/Interactions] In
interaction.worker
automations, during anawait:form:
continuation, asubmit:
element may now specify any number of custom buttons. Previously, thesubmit:
element was limited to the built-in ‘continue’ and ‘reset’ buttons. Custom buttons can provide label, icon, icon position, style, and value. Each button is either asubmit:buttons:continue:
orsubmit:buttons:reset:
type. When clicked, acontinue:
button sets the submit element’skey
to its value. There may be multiple continue buttons (e.g. yes/no, allow/deny). This is much simpler than using a sheet, and it no longer requires a second click to continue. Whensubmit:buttons:
isn’t provided, the default ‘continue’ and ‘reset’ buttons are automatically added; and these can still be controlled with thesubmit:continue@bool:
andsubmit:reset@bool:
shortcuts. The current alternative styles for buttons are ‘secondary’ (gray like reset) or ‘outline’ (blue like continue but not filled). This makes it easy to visually distinguish primary/default and secondary options. -
[Automations/Mail/Validation] Added
mail.draft.validate
automations for implementing interactive custom validators when composing and replying to email. For example: no recipients, profanity filter, missing attachment, using formatting in plaintext, and so on. These automations have the same functionality as worker interactions. Interactive validators are configured on themail.draft.validate
automation event, and all enabled automations will run in sequence. Any validator automation canreturn:reject:
to abort sending the message. Through interactivity, a validator can allow a worker to bypass a warning and continue sending; whereas non-interactive custom validators would reject with an error message that a worker would have to correct before continuing. For instance, A non-interactive validator would be problematic when suggesting that a worker may have omitted a mentioned “see attachment”, when the omission was intentional. Interactive validators can instead make suggestions which are accepted or ignored. While the most efficient option is to filter unneeded validators from the event, amail.draft.validate
automation that exits without anawait:
is silent and never opens the interaction popup. [#384] -
[Mail/Automations/Validation] When composing mail without recipients, the ‘no recipients’ warning is now an interactive validator pre-configured on the
mail.draft.validate
event. Previously, a warning about not having a recipient was shown above the ‘Send’ button, but this was easily overlooked and couldn’t be disabled. As an interaction it’s completely user configurable – some environments may keep the warning but still allow tickets with no recipients, while others require recipients, and others yet disregard the warning entirely. -
[Automations/Interactions/UX] In the automations editor, when using the ‘Run’ tab to simulate an interaction, copying the Output to the Input during an
await:form:
will automatically create placeholders for all form elements at the top of the new input. Previously these had to be discovered and added manually. This skipssubmit:
elements without custom buttons, andsheet:
elements without aselection:
column. On sheet elements, it uses a string default forsingle
selection, and an array default formultiple
. -
[Packages] Packages can now append bindings to existing automation events. This allows packages to fully configure an event-based workflow.
-
[Attachments/UX] Workers are now allowed to upload and send duplicate files with the same content and different filenames. Such dupes are still consolidated when received on inbound messages. This resolves an issue where early testers reported that filenames were mysteriously changing on uploaded files (because they were matching existing copies with a different name). A better solution would be to separate attachment metadata and content in the schema, then allow multiple metas to link to the same content ID while retaining their own name and owner.
-
[Automations/Interactions/UX] Moved 38 built-in helper interactions from
interaction.worker
tointeraction.internal
. This cleans up choosers oninteraction.worker
so users can find their own automations more easily. Removed theis_unlisted
field from automations. -
[Mail/Parser/Performance] When an HTML email message is received without a plaintext part, Cerb now generates up to 50KB of plaintext. Previously this was unbounded, so an erroneous or malicious HTML part could generate incredibly long messages.
-
[Automations/Validation] In automation inputs, increased the default max length of
inputs:text:
to 1,024 characters from 256. Withtype:url
the default is 2,048 characters. The max length can be changed with the newtype_options:max_length@int:
option. As well, a newtype_options:truncate@bool:
option determines whether a longer input is truncated to fit (true
, default) or returns a validation error (false
). -
[Automations/Validation] In automations, fixed an issue with the
http.request:
action where validation limitedurl:
and headers@text:` to 255 characters each. The URL now defaults to 2,048 max length, and headers to 8,192. -
[Automations/Validation] In automations, fine-tuned the max-length validation on text-based fields for all action
inputs:
. -
[Automations/Interactions] In
interaction.worker
automations, in anawait:form:
continuation,fileUpload:
elements now include full attachment record details for validation. If the element name ends with_id
, then a corresponding__context
key is added for key expansion. Otherwise, new keys are added for_id
and_context
with the name as the prefix. For example,fileUpload/prompt_file
will setprompt_file
andprompt_file_id
to the ID andprompt_file__context
toattachment
; whilefileUpload/prompt_file_id
will setprompt_file_id
andprompt_file__context
. In both cases, you can expand keys likeprompt_file_name
andprompt_file_size
in avalidation@raw:
script. Previously, this kind of validation had to be done after the form validated and submitted. -
[Automations/Interactions] In
interaction.worker
automations, withawait:record:
continuations, the record editor now always opens in edit mode for existing records. Previously, this was opening existing records as card popups, which required an extra ‘Edit’ click. -
[Mail/Automations] In
mail.route
automations, theparent_ticket_
placeholder is now available with key expansion. -
[Data Queries/Subtotals] In
type:worklist.subtotals
data queries, aby.distinct:[...]
aggregate function is now available. This counts the number of distinct values in the finalby:
field. For instance, on anof:message
query,by.distinct:[created@day,worker,ticket]
would return the number of distinct tickets replied to per worker per day. Multiple replies on the same ticket would count as+1
. Thanks to 1Password for the feature request! -
[Mail/Parser/Automations] When an empty ticket is first created through an automation or the API, and the first message is then later added (either through automations, API, or email parser), then new message events will now properly trigger. Previously, these events only triggered if the message created the ticket, but not when the ticket already existed and was empty. This simplifies procedural ticket creation workflows.
-
[Automations/Interactions] On
interaction.worker
automations, in aform:await:
continuation, atext:
element now has more permissive max lengths for URLs (2,048 characters) and freeform text (1,024). Themax_length:
option now properly controls validation. -
[Records/Custom Fields] In custom field record validation, the
uri
field can no longer be entirely numeric. This makes it easy to distinguish IDs and URIs. -
[Records/Drafts] Draft dictionaries now contain synthesized
params
keys forcustom_fields_uri
andmessage_custom_fields_uri
. These contain the same values ascustom_fields
andmessage_custom_fields
but are keyed by URI rather than ID. This makes comparisons much easier in automations. For instance, amail.draft.validate
automation that requires a custom field to be set before an email can be sent. -
[Records/Drafts] When creating a draft record, the
params
ofcustom_fields
andmessage_custom_fields
can key custom fields on either ID or URI. This makes it much easier to create draft records from automations or the API. -
[Webhooks/Automations] Fixed an issue with webhook portals where automation events didn’t have the
request_
placeholders. Thanks to @mryanb for reporting! -
[Records/Tasks] Task editors now prompt for confirmation before closing with the ESC key.
-
[Automations/Policies/UX] In the automation editor, the policy editor now autocompletes common
deny:
conditions for each action. For instance, denying anhttp.request:
based on URL or HTTP method. -
[Mail/HTML] Fixed an issue with displaying formatting on some HTML email messages (e.g. Outlook/Word). If the ‘tidy’ PHP extension was enabled, an XHTML conversion during cleanup could break the ‘css to inline styles’ step.
-
[Bots/Behaviors] In bot behaviors using the ‘Execute HTTP Request’ action, fixed an issue in the simulator when the ‘fileinfo’ PHP extension isn’t installed. Thanks to @abrenner for the bug report!
-
[KATA] Implemented the
@trim
annotation in KATA. This removes leading and trailing whitespace from a key’s value. -
[KATA/Validation] When parsing KATA, an error is now thrown for unknown
@attributes
. -
[KATA/Validation] KATA now returns an error on unexpected syntax. For instance, a non- key or value on a line, or indented multi-line text without
@text
on the key. -
[KATA/Validation] KATA validation now ensures key names contain only letters, numbers, and underscores.
-
[Snippets/Explore] Implemented ‘Explore’ mode for snippet worklists.
-
[Interactions/UX] In
interaction.worker
automations, during anawait:form:
continuation, atextarea:
element with amax_length:
option now displays a live character count. [#1502] -
[Automations/Validation] Before saving an automation record, the script syntax is now validated to catch common errors. [#1507]
-
[Automations/Validation] Before saving an automation record, the policy syntax is now validated to catch common errors. [#1461]
-
[Automations/Events/Validation] Before saving an automation event, the KATA syntax is validated to catch common errors. [#1506]
-
[KATA/Validation] KATA validation now properly returns an error when two sibling keys have the same type and name, but different
@annotations
. -
[Toolbars/Validation] Before saving a toolbar, the KATA syntax is now validated to catch common errors.
-
[Snippets/Validation] Before saving a snippet record, the prompts KATA syntax is validated to catch common errors.
-
[Maps/Validation] Before saving a map widget on profiles or workspaces, the Maps KATA and Events KATA syntax is validated to catch common errors.
-
[Sheets/Validation] Before saving a sheet widget on cards, profiles, or workspaces, the Sheets KATA and Toolbar KATA syntax is validated to catch common errors.
-
[Dashboards] Before saving a workspace dashboard tab, Prompts KATA syntax is validated to catch common errors.
-
[KATA/Platform] Implemented a KATA validation service. This takes a document and schema in KATA format. The schema describes valid keys, attributes, and types. Validation supports multiple types per key (for instance, if a key can be a
list
or astring
). Theobject
type supports namedattributes
and dynamicattributePatterns
. Attributes can be markedrequired@bool
andmultiple@bool
(duplicable, nameable). Recursive validation be accomplished with a top-leveldefinitions:
schema key, and attributes can use theref:
option to reference them. An attribute usingref:
can also define keys to override. -
[Interactions/Websites]
interaction.website
automations in anawait:form:
continuation now default to 1,024 character lengths ontext:
elements and 2,048 characters onurl:
. Previously these were limited to 255 characters by default. -
[Interactions/Automations] In
interaction.worker
automations, when using anawait:form:
continuation,text:
andtextarea:
elements now supportlength_min@int:
,length_max@int:
, andtruncate@bool
options. -
[Interactions/Automations] In
interaction.website
automations, when using anawait:form:
continuation,text:
andtextarea:
elements now supportlength_min@int:
,length_max@int:
, andtruncate@bool
options. -
[Interactions/Automations] In
interaction.worker
automations, when using anawait:form:
continuation,text:
elements now support Emoji characters by default. -
[Mail/Drafts] A
mail.compose
draft no longer requires agroup_id
parameter. This will use the default group if omitted. Thanks to @mryanb for the report. -
[PGP] Importing a PGP public key no longer requires the ability to verify signatures. A public key can be used for just encryption.
-
[PGP] When importing or using a public key, subkeys are now properly ignored if they are expired, revoked, or bypassed.