YouTrack Standalone 2018.3 Help

Notify Multiple Unregistered Users

This workflow is used as part of the Mailbox Integration that enables using YouTrack as a help desk. For a detailed description of this integration, see Use YouTrack as a Help Desk

You can use this workflow to send email messages to any email address, including unregistered users. If overused, sending email messages to unregistered users can slow down the performance of your YouTrack server, which is not designed for use as a bulk email service. You can configure a system property to set a daily email message limit. For more information, see Configuration Parameters.

This workflow manages a of email addresses that are stored in the Last message related emails and All related emails fields. It also includes a rule that sends email messages to unregistered users when a comment is added to an issue that was reported by sending an email to a specific mailbox.

Name

@jetbrains/youtrack-workflow-notify-multiple-unregistered-users

Auto-attached

no

Modules

Collect related emails on issue creation (on-change rule)
Send notifications to all unregistered users (on-change rule)

To enable this workflow:

  1. Add a string-type field with the name Last message related emails to your project.

  2. Add a string-type field with the name All related emails to your project.

  3. Attach the Notify Multiple Unregistered Users workflow to your project.

For this workflow to function properly, there are a few additional settings you need to configure in your YouTrack instance:

  • Configure your project to send notifications with the email address of your feedback or support account.

  • Configure and enable the Mailbox Integration feature.

For a complete description of this setup, see Use YouTrack as a Help Desk.

Use Case

This workflow supports using YouTrack as a help desk. When an issue is reported by email, the email addresses in the From and CC fields are added to the issue. These email addresses then receive email notifications when the issue is updated.

Modules

This workflow includes two modules. The first module contains a rule that manages the list of email addresses. The second module contains a rule that sends notifications to these email addresses when an issue is updated.

Collect related emails on issue creation

This rule collects the email addresses stored in the Last message related emails field and copies them to the All related emails field.

var entities = require('@jetbrains/youtrack-scripting-api/entities'); var workflow = require('@jetbrains/youtrack-scripting-api/workflow'); exports.rule = entities.Issue.onChange({ title: workflow.i18n('Collect related emails on issue creation'), guard: function(ctx) { return ctx.issue.becomesReported; }, action: function(ctx) { var issue = ctx.issue; var lastMessageRelatedEmailsStr = issue.fields.lastEmails; var isBlank = function(str) { return !str || str.trim().length === 0; }; var lastMessageRelatedEmails = isBlank(lastMessageRelatedEmailsStr) ? [] : lastMessageRelatedEmailsStr.split(' '); if (lastMessageRelatedEmails.length) { var fromServiceEmail = issue.project.notificationEmail; var isEmailAllowed = function(email) { return email && email.length && email.toUpperCase() !== fromServiceEmail.toUpperCase(); }; var allRelatedEmailsStr = issue.fields.allEmails; var allRelatedEmails = isBlank(allRelatedEmailsStr) ? [] : allRelatedEmailsStr.split(' '); var newUsersEmails = lastMessageRelatedEmails.filter(function(email) { return isEmailAllowed(email) && allRelatedEmails.indexOf(email) === -1; }); if (newUsersEmails.length) { issue.fields.allEmails = allRelatedEmails.concat(newUsersEmails).join(' '); } issue.fields.lastEmails = null; } }, requirements: { lastEmails: { name: 'Last message related emails', type: entities.Field.stringType }, allEmails: { name: 'All related emails', type: entities.Field.stringType } } });

Send notifications to all unregistered users

The next rule sends notification to unregistered users whose email address is stored in the All related emails field. This is used to send notification to the email address of a person who reported an issue by sending an email to a specific inbox. Additional email addresses that were set in the To and CC fields are also notified.

The email message is formatted as a reply to the original email.

var entities = require('@jetbrains/youtrack-scripting-api/entities'); var workflow = require('@jetbrains/youtrack-scripting-api/workflow'); var notifications = require('@jetbrains/youtrack-scripting-api/notifications'); exports.rule = entities.Issue.onChange({ title: workflow.i18n('Send notifications to all unregistered users'), guard: function(ctx) { return ctx.issue.comments.added.isNotEmpty(); }, action: function(ctx) { var issue = ctx.issue; var comment = issue.comments.added.first(); if (comment && comment.permittedGroups.isEmpty() && comment.permittedUsers.isEmpty()) { var isBlank = function(str) { return !str || str.trim().length === 0; }; var allRelatedEmailsStr = issue.fields.allEmails; if (isBlank(allRelatedEmailsStr)) { return; } var allRelatedEmails = allRelatedEmailsStr.split(' '); var lastMessageRelatedEmailsStr = issue.fields.lastEmails; var lastMessageRelatedEmails = isBlank(lastMessageRelatedEmailsStr) ? [] : lastMessageRelatedEmailsStr.split(' '); var fromServiceEmail = issue.project.notificationEmail; var isEmailAllowed = function(email) { return email && email.length && email.toUpperCase() !== fromServiceEmail.toUpperCase(); }; var emailsToNotify; if (lastMessageRelatedEmails.length) { //case 1: comment from unregistered user var newUsersEmails = lastMessageRelatedEmails.filter(function(email) { return isEmailAllowed(email) && allRelatedEmails.indexOf(email) === -1; }); if (newUsersEmails.length) { issue.fields.allEmails = allRelatedEmailsStr + ' ' + newUsersEmails.join(' '); } issue.fields.lastEmails = null; emailsToNotify = allRelatedEmails.filter(function(email) { return isEmailAllowed(email) && lastMessageRelatedEmails.indexOf(email) === -1; }); } else { //case 2: comment from agent emailsToNotify = allRelatedEmails.filter(function(email) { return isEmailAllowed(email); }); } if (emailsToNotify.length) { var message = { fromName: lastMessageRelatedEmails.length ? lastMessageRelatedEmails[0] : comment.author.fullName, toEmails: emailsToNotify, subject: createSubject(issue), body: lastMessageRelatedEmails.length ? createGeneralMessage(issue) : createInReplyToMessage(issue) }; notifications.sendEmail(message, issue); } } }, requirements: { lastEmails: { name: 'Last message related emails', type: entities.Field.stringType }, allEmails: { name: 'All related emails', type: entities.Field.stringType } } }); function createGeneralMessage(issue) { var text = issue.comments.added.first().text; issue.attachments.added.forEach(function(attachment) { text = text + '\n[file:' + attachment.name + ']'; }); return issue.wikify(text).trim(); } function createInReplyToMessage(issue) { var messageText = createGeneralMessage(issue); var addedComments = issue.comments.added; var lastVisibleComment; issue.comments.forEach(function(comment) { if (!addedComments.has(comment) && comment.permittedGroups.isEmpty() && comment.permittedUsers.isEmpty()) { lastVisibleComment = comment; } }); var quotedText = lastVisibleComment ? issue.wikify(lastVisibleComment.text).trim() : issue.wikify(issue.description).trim(); return [ '<div style="font-family: sans-serif">', ' <div style="padding: 10px 10px; font-size: 13px; border-bottom: 1px solid #D4D5D6;">', ' ' + messageText, ' </div>', ' <blockquote type="cite">', ' <div style="font-size: 13px; color: #888;">', ' ' + workflow.i18n('In reply to:') + '<br><br>' + quotedText, ' </div>', ' </blockquote>', ' <div style="margin: 20px 0 20px 44px; padding: 4px 0 8px 0; color: #888; font-size: 11px; border-top: 1px solid #D4D5D6;">', ' ' + workflow.i18n('You have received this message because you are a participant of the conversation in the issue {0}. Sincerely yours, YouTrack', issue.id), ' </div>', '</div>' ].join('\n'); } function createSubject(issue) { var reStr = workflow.i18n('Re:'); var summary = issue.summary; return summary.startsWith(reStr) ? summary : reStr + ' ' + summary; }
Last modified: 7 March 2019