Messaging

The Axelor Open Platform provides integrated messaging & stream features:

  • change tracking

  • document discussions

  • group discussions

  • email integration

Change Tracking

The change tracking feature can be used to record audit trail in the form of stream of messages when document/record gets changed.

To enable change tracking on an object, the object definition has to provide tracking details.

Here is an example:

<entity name="SaleOrder">
  <string name="name" ... />
  ...
  ...

  <track>
    <field name="name" />
    <field name="createDate" on="CREATE" />
    <field name="customer" />
    <field name="confirmDate" on="UPDATE" if="status == 'confirmed'" />
    <field name="totalAmount" />
    <message if="true" on="CREATE">Order created.</message>
    <message if="status == 'confirmed'" on="UPDATE">Order confirmed.</message>
    <message if="status == 'draft'" tag="important">Draft</message>
    <message if="status == 'confirmed'" tag="success" fields="status">Confirmed</message>
  </track>

</entity>

The track messages are generated on following operations:

  • CREATE - generate when record is created

  • UPDATE - generate when record is updated

  • ALWAYS - generate in all cases

The track information is provided with:

  • <field> - specify which field to track

    • name - name of the field to track

    • on - on which event this field should be tracked

    • if - a boolean expression to test before tracking the field (JEL)

  • <message> - specify messages to display

    • if - a boolean expression to test before using the message

    • on - on which event this messages should be used

    • tag - specify predefined tag class if this message is a tag

    • fields - only use this message as tag if any of the given field is changed

If a <message> is supposed to be used as a tag, following predefined css class names should be used:

  • important - important style (red)

  • success - success style (green)

  • warning - warning style (yellow)

  • info - information style (blue)

The if expressions are simple JEL boolean expressions evaluated against current values of the record.

By default, <field> are tracked only if it’s value is changed and given if expression evaluates to true.

A root message is created when record is created. All the followup messages are considered as replies to this root message. This done to keep change tracking messages in threaded format.

Streams & Discussions

The change tracking streams can be shown on any form views with following markups:

<form name="sale-order-form" title="Sale Order" ....>
  ...
  ...
  <panel-mail>
    <mail-messages limit="4" />
    <mail-followers />
  </panel-mail>
</form>

The <panel-mail> can be used to show change tracking streams or discussion messages in threaded layout.

  • <mail-messages> - shows the messages in threaded layout

  • <mail-followers> - shows the list of followers/subscribers

The messages are shown according the given limit on the <mail-messages> which is default to 30 messages.

The most recent messages are shown first. More messages can be loaded with a link provided at the end of message list (if there are more messages).

Discussion Groups

Discussion groups can be used to create messaging groups where users can subscribe and post messages.

Messaging Menu

The messaging menu provides quick links to see the messages.

  • Messaging → Inbox - shows all the non-archived messages

  • Messaging → Important - shows all the messages marked as important

  • Messaging → Archived - shows all the archived messages

  • Messaging → Groups → All Groups - show all the available groups

Besides theses, when user subscribes to a messaging group, a personal menu is added as Messaging → Groups → Group Name

Email integration

The messaging & stream feature can be configured to send/receive email messages from SMTP/IMAP servers.

The default implementation would configure mail sender and fetches from the application configuration settings:

# Quartz Scheduler
# ~~~~~
# quartz job scheduler

# Specify whether to enable quartz scheduler
quartz.enable = true

# SMPT configuration
# ~~~~~
# SMTP server configuration
#mail.smtp.host = smtp.gmail.com
#mail.smtp.port = 587
#mail.smtp.channel = starttls
#mail.smtp.user = user@gmail.com
#mail.smtp.pass = secret

# timeout settings
#mail.smtp.timeout = 60000
#mail.smtp.connectionTimeout = 60000

# IMAP configuration
# ~~~~~
# IMAP server configuration
# (quartz scheduler should be enabled for fetching stream replies)
#mail.imap.host = imap.gmail.com
#mail.imap.port = 993
#mail.imap.channel = ssl
#mail.imap.user = user@gmail.com
#mail.imap.pass = secret

# timeout settings
#mail.imap.timeout = 60000
#mail.imap.connectionTimeout = 60000

The quartz scheduler should be enabled to fetch incoming messages from the configured IMAP server.

The default implementation sends email notifications to the followers of a record/document.

The mail service can be extended by providing alternative implementation of mail service api:

public interface MailService {

  void send(MailMessage message) throws MailException; (1)

  void fetch() throws MailException; (2)

  Model resolve(String email); (3)

  List<InternetAddress> findEmails(String matching, List<String> selected, int maxResults); (4)
}
1 send an email for the given message
2 fetch email messages
3 resolve the given email address to it’s associated record
4 find email addresses for the matching string

The API is designed to work with any kind of contact models. The implementation should provide a way to list email addresses and resolve an email address to it’s associated record.

The default implementation provides email addresses of users and resolves email address to user records only.

The default implementation MailServiceImpl provides some additional overridable methods to customize default implementation.

For example:

public class MyMailService extends MailServiceImpl {

  public Model resolve(String email) {
    // find contact by the email
    // if not found, find another contact like object (depends on your requirements)
    // if not found, find with default implementation
  }

  public List<InternetAddress> findEmails(String matching, List<String> selected, int maxResults) {
    // search all contacts matching the given email pattern
    // prepare list of InternetAddress and return
  }
}

See javadocs, for other overridable methods of the default implementation.