ScriptRunner: Sending outlook invitation as custom mail

Christian Schlaefcke
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
December 14, 2016

Hi, 

I want to use the ScriptRunner built-in script send custom email in order to send MS Outlook invitations.

I am already able to create an ICS-Attachment on the issue with a custom scriptrunner script. And I am able to send this attachment with the built-in script but in Outlook it would not be detected as an invitation mail.

In this stackoverflow-entry I found a hint about how to create proper Outlook invitations and I wonder if it would be possible to adopt this somehow.

Kind Regards,

Christian

4 answers

1 accepted

1 vote
Answer accepted
Christian Schlaefcke
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
December 16, 2016

Hi Jamie,

thank you for the hint! Kind of helpful was also the recipes area (Validating Attachments/Links in Transitions) of the script runner docs.

I had to fiddle around quite a while but combining the SR docs with another stackoverflow-entry was actually the breakthrough.

Here is what I put in the Condition and Configuration area of the Send a custom email post-function:

import com.atlassian.jira.component.ComponentAccessor
import org.apache.log4j.Logger
import org.apache.log4j.Level


import java.io.File


import javax.activation.DataHandler;


import javax.mail.util.ByteArrayDataSource;
import javax.mail.internet.MimeBodyPart
import javax.mail.internet.MimeMultipart
import javax.mail.internet.MimeUtility


def log = Logger.getLogger("my.company.SendOutlookInvitation")


log.setLevel(Level.DEBUG)
log.debug("#####################################")
def last_attachment = issue.attachments.last()
def file_name = last_attachment.filename.toString()
def file_type = last_attachment.mimetype.toString()
def file_id = last_attachment.id


log.debug("FILE_NAME: " + file_name)
log.debug("FILE_TYPE: " + file_type)


def attachmentPathManager = ComponentAccessor.getAttachmentPathManager().attachmentPath.toString()
def projectKey = issue.getProjectObject().getKey()
def file_path = attachmentPathManager + "/" + projectKey + "/" + issue.key + "/" + file_id + "/"
log.debug("FILE_PATH: " + file_path)


File f = new File(file_path);
if(f.exists() && !f.isDirectory()) {
    mail.addHeader("method", "REQUEST");
    mail.addHeader("charset", "UTF-8");
    mail.addHeader("component", "VEVENT");
    log.debug("FILE_EXISTS: YES")
    log.debug("FILE_TEXT:" + f.text)
    def mp = new MimeMultipart("alternative")
    def calendarPart = new MimeBodyPart()
    calendarPart.setHeader("Content-Class", "urn:content-  classes:calendarmessage");
    calendarPart.setHeader("Content-ID", "calendar_message");
    calendarPart.setDataHandler(new DataHandler(new ByteArrayDataSource(f.text, "text/calendar")));
    mp.addBodyPart(calendarPart)
    mail.setMultipart(mp)
} else {
    log.debug("FILE_EXISTS: NO")
}


log.debug("#####################################")
true

I do not have to include any attachments with the general post-function config. This will be handled by this configuration script completely.

The above scripts expects that a proper ics file is already attached to the issue or will be attached during the current transition (in my case another SR script using the attachmentManager). In this second case the mail has to be send at the end of the post-functions so that attaching has already been performed.

 

Now the mail appears perfectly as an Outlook invitation thumbs-up smile

JamieA
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
December 16, 2016

Nice job... minor thing, I'm not sure that's the best way to get the attachment file. If the issue has been moved that may not work IIRC.

Christian Schlaefcke
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
December 16, 2016

Thank you! In my case the attachment will be created on the same transition with first post function using another script - so in my case it could not happen that the attachment location would not fit to the issue key.

But I put a warning regarding this handling/precondition and maybe for other cases someone else finds a more robust implementation smile

Matthew Harkins February 17, 2017

Christian and Jamie, Thanks for posting this!  This was almost exactly what I needed.  I was able to build on Christian's example above and now have a single block of code that injects a temporary attachment into a scriptrunner "send a custom email" post function to deliver an ics calendar invitation / reminder to an assignee for a task.  

Connor Sims July 11, 2017

Matthew,

 

Would you mind sharing your script to inject the temporary ICS attachment?

Matthew Harkins July 11, 2017

This code should get you going.

This requires a custom field to hold a UUID.  Jamie explains how to create a UUID in https://community.atlassian.com/t5/JIRA-Core-questions/How-can-I-generate-a-UUID-and-assign-to-a-custom-field/qaq-p/324170  I added this uuid creation script as a post script on the create transition for my workflow.

Note: This solution is for MS Outlook Client recipients. This will need modifications for the iCal syntax if your intended recipient uses a different mail client

I use this block in the Condition and Configuration section :

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.Issue
import com.atlassian.jira.issue.util.DefaultIssueChangeHolder
import com.atlassian.jira.issue.issuetype.IssueType
import com.atlassian.jira.issue.IssueImpl
import com.atlassian.jira.issue.IssueFieldConstants.*

import org.apache.log4j.Logger
import org.apache.log4j.Level
 
import java.text.SimpleDateFormat; 
 
import javax.activation.DataHandler;
 
import javax.mail.util.ByteArrayDataSource;
import javax.mail.internet.MimeBodyPart
import javax.mail.internet.MimeMultipart
import javax.mail.internet.MimeUtility
 
 
def log = Logger.getLogger("company.com.SendOutlookInvitation")
 
log.setLevel(Level.WARN)
log.debug("#####################################")

def optionsManager = ComponentAccessor.getOptionsManager()
def customFieldManager = ComponentAccessor.getCustomFieldManager()

def fieldName1 = "Custom Start DateTime"
def field1 = customFieldManager.getCustomFieldObjectByName(fieldName1).getValue(issue)
log.debug("Custom Start DateTime: " + field1)
def fieldName2 = "Custom End DateTime"
def field2 = customFieldManager.getCustomFieldObjectByName(fieldName2).getValue(issue)
log.debug("Custom End DateTime: " + field1)
def fieldName3 = "Custom UUID"
def field3 = customFieldManager.getCustomFieldObjectByName(fieldName3).getValue(issue)
def fieldName4 = "Change Type"
def field4 = customFieldManager.getCustomFieldObjectByName(fieldName4).getValue(issue)
def busystatus = "BUSY"
if (field4.toString() == "Free") {
busystatus = "FREE"
} def summary = issue.getSummary() def description = issue.getDescription() def issueowner = issue.getAssigneeId() def uuid = field3.toString()
def starttimeValue = field1 def endtimeValue = field2 TimeZone tz = TimeZone.getTimeZone ("Etc/GMT") SimpleDateFormat outsdf = new SimpleDateFormat ("yyyyMMdd'T'HHmmss'Z'"); SimpleDateFormat insdf = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss.S"); Date starttime = insdf.parse("${starttimeValue}"); outsdf.setTimeZone (tz); def UTCstarttime = outsdf.format(starttime) Date endtime = insdf.parse("${endtimeValue}"); outsdf.setTimeZone (tz); def UTCendtime = outsdf.format(endtime) outsdf.setTimeZone (tz); Calendar currentcal = Calendar.getInstance(tz) Date currenttime = currentcal.getTime() def UTCcurrenttime = outsdf.format(currenttime) //change METHOD:PUBLISH to make it automatic //change METHOD:REQUEST to make it provide buttons def iCal = """BEGIN:VCALENDAR PRODID:-//Microsoft Corporation//Outlook 14.0 MIMEDIR//EN VERSION:2.0 METHOD:REQUEST X-MS-OLK-FORCEINSPECTOROPEN:TRUE BEGIN:VEVENT CLASS:PUBLIC CREATED:${UTCcurrenttime} DESCRIPTION:${description} DTEND:${UTCendtime} DTSTAMP:${UTCcurrenttime} DTSTART:${UTCstarttime} LAST-MODIFIED:${UTCcurrenttime} LOCATION:Company Name PRIORITY:5 SEQUENCE:0 SUMMARY;LANGUAGE=en-us:${summary} TRANSP:OPAQUE UID:${uuid} X-MICROSOFT-CDO-BUSYSTATUS:${busystatus} X-MICROSOFT-CDO-IMPORTANCE:1 X-MICROSOFT-DISALLOW-COUNTER:FALSE X-MS-OLK-AUTOFILLLOCATION:FALSE X-MS-OLK-CONFTYPE:0 BEGIN:VALARM TRIGGER:-PT15M ACTION:DISPLAY DESCRIPTION:Reminder END:VALARM END:VEVENT END:VCALENDAR""" mail.addHeader("method", "REQUEST"); mail.addHeader("charset", "UTF-8"); mail.addHeader("component", "VEVENT"); log.debug("FILE_EXISTS: YES") log.debug("FILE_TEXT:" + iCal) def mp = new MimeMultipart("alternative") def calendarPart = new MimeBodyPart() calendarPart.setHeader("Content-Class", "urn:content- classes:calendarmessage"); calendarPart.setHeader("Content-ID", "calendar_message"); calendarPart.setDataHandler(new DataHandler(new ByteArrayDataSource(iCal, "text/calendar"))); mp.addBodyPart(calendarPart) mail.setMultipart(mp) log.debug("#####################################") true

Then in the Email template section, I use this block:

Dear ${issue.assignee?.displayName},
<p></p>
The ${issue.issueType.name} ${issue.key} with summary ${issue.summary} has been scheduled.
<p></p>
Description: $issue.description
<p></p>
<% if (mostRecentComment)
    out << "Last comment: " << mostRecentComment
%>
<p></p>
CustomFieldName: <% out <<
 issue.getCustomFieldValue(
     com.atlassian.jira.component.ComponentAccessor.getCustomFieldManager().getCustomFieldObjectByName("CustomFieldName")
    ) %>
<p></p>
Regards,<br></br>
${issue.reporter?.displayName}

The Subject template section gets this block:

Scheduled $issue $issue.summary

I select the HTML format and add the appropriate recipients.

The important portion of this is to choose "New" for the Include Attachments section, otherwise your temporary attachment that is built in the Condition and Configuration section will not get added to the email. 

Good Luck!

-edit: We needed the ability to denote Free vs Busy in the appt, so we added a custom field that would toggle this in the ICS file.

Like Ruben Navarro likes this
Connor Sims July 11, 2017

Wow, thank you! This has been incredible! This is probably the most thorough & effective response I've ever received on Atlassian.

 

I used your script almost verbatim & it returns no errors. I've substituted my 3 custom fields (Custom Start Date Time - PresentationStart, Custom End Date Time - PresentationEnd, Custom UUID - UUID) and used a post function in a previous transition to populate the UUID field just like Jamie indicated. 

 

For some reason although the post-function says 'No failures in last -- executions', the email does not send, and the Attachments field in the preview is 'null'

 

I feel like I'm very close & already appreciate your help tremendously; any ideas on why it may not be sending though?

 

Thanks!

Matthew Harkins July 11, 2017

The attachment field in the preview is null for me as well, but it still attaches to the issue.   

Something that I overlooked early in my trials was the last line of the Condition and Configuration section.  The single line with the word 'true' as in the example is required or the email is not sent because it is the resultant return code of the condition block.  The execution must reach this point or the condition evaluates to false and won't send.

If you really meant 'No failures in last -- executions' and didn't just redact the number of executions, it is possible that your post-function is not being executed.  I try to go back to the basic steps  in troubleshooting, is your workflow published? , are you certain your custom workflow is attached to the correct issue type in your workflow scheme? ...

I would suggest changing the debug level from WARN to DEBUG and then watch your catalina.out file for debugging messages. You might need to add some additional log.debug statements to determine where it is failing in the conditional execution.

 

Connor Sims July 11, 2017

You are correct - the problem was that this post function was step #1 in this transition for me. Once I moved it to the last step, it works now.

 

HUGE shoutout to you Matthew - thanks for the innovative solution!

roniz January 17, 2018

@Christian Schlaefcke, I really tried making this work- with no success. can you please explain how you created the ICS-Attachment on the issue with a custom scriptrunner script.

 

thanks

Christian Schlaefcke
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 17, 2018

Well, it´s been a while but as I am quite good organized I managed to find the script :-) Here you go:

import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.attachment.CreateAttachmentParamsBean
import com.atlassian.jira.issue.CustomFieldManager

import java.io.BufferedWriter
import java.io.File
import java.io.FileWriter
import java.io.IOException

import java.text.SimpleDateFormat
import java.util.Calendar
import java.sql.Timestamp

import java.util.UUID

import org.apache.log4j.Logger
import org.apache.log4j.Level

def log = Logger.getLogger("CreateICSAttachment")
log.setLevel(Level.DEBUG)

def attachmentManager = ComponentAccessor.getAttachmentManager()

def user = ComponentAccessor.getJiraAuthenticationContext()?.getUser()

def rendererManager = ComponentAccessor.getRendererManager()
def fieldLayoutItem = ComponentAccessor.getFieldLayoutManager().getFieldLayout(issue).getFieldLayoutItem("comment")
def renderer = rendererManager.getRendererForField(fieldLayoutItem)

def templateText = '''\
BEGIN:VCALENDAR
PRODID:-//Microsoft Corporation//Outlook 14.0 MIMEDIR//EN
VERSION:2.0
METHOD:REQUEST
X-MS-OLK-FORCEINSPECTOROPEN:TRUE
BEGIN:VTIMEZONE
TZID:W. Europe Standard Time
BEGIN:STANDARD
DTSTART:${CREATION_TIME}
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
TZOFFSETFROM:+0100
TZOFFSETTO:+0200
END:DAYLIGHT
END:VTIMEZONE
BEGIN:VEVENT
ATTENDEE;CN="${ORGANIZER}";RSVP=TRUE:mailto:${ORGANIZER_MAIL}
ATTENDEE;CN="${DEPLOYER}":mailto:${DEPLOYER_MAIL}
CLASS:PUBLIC
CREATED:${CREATION_TIME}
DESCRIPTION:${DESCRIPTION}\n
DTEND;TZID="W. Europe Standard Time":${END_TIME}
DTSTAMP:${CREATION_TIME}
DTSTART;TZID="W. Europe Standard Time":${START_TIME}
LAST-MODIFIED:${MOD_TIME}
LOCATION:${LOCATION}
ORGANIZER;CN="${ORGANIZER}";RSVP=TRUE:mailto:${ORGANIZER_MAIL}
PRIORITY:5
SEQUENCE:0
SUMMARY;LANGUAGE=de-ch:${SUMMARY}
TRANSP:OPAQUE
UID:${UID}
X-ALT-DESC;FMTTYPE=text/html:<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//E
N"><HTML><HEAD><META NAME="Generator" CONTENT="MS Exchange Server version
14.02.5004.000"><TITLE></TITLE></HEAD><BODY><!-- Converted from text/rtf
format -->${DESCRIPTION_HTML}<P DIR=LTR><SPAN LANG="de-ch"></SPAN></P></B
ODY></HTML>
X-MICROSOFT-CDO-BUSYSTATUS:BUSY
X-MICROSOFT-CDO-IMPORTANCE:1
X-MICROSOFT-DISALLOW-COUNTER:FALSE
X-MS-OLK-APPTSEQTIME:${CREATION_TIME}
X-MS-OLK-AUTOFILLLOCATION:FALSE
X-MS-OLK-CONFTYPE:0
END:VEVENT
END:VCALENDAR'''

def template = new groovy.text.StreamingTemplateEngine().createTemplate(templateText)

Calendar cal = Calendar.getInstance()
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'")
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyyMMdd'T'HHmmss")

def customFieldManager = ComponentAccessor.getComponent(CustomFieldManager)
def cfDeploymentTime = customFieldManager.getCustomFieldObjectByName("Deployment Time")
Timestamp cfDeploymentTimeVal = (Timestamp) issue.getCustomFieldValue(cfDeploymentTime)

def cfDeploymentDuration = customFieldManager.getCustomFieldObjectByName("Deployment Duration")
int cfDeploymentDurationVal = (int) issue.getCustomFieldValue(cfDeploymentDuration)

log.info("START_TIME: " + sdf2.format(cfDeploymentTimeVal))
Timestamp endTime = new Timestamp(cfDeploymentTimeVal.getTime() + (cfDeploymentDurationVal * 60 * 1000))
log.info("END_TIME: " + sdf2.format(endTime))

String organizer = issue.reporter.getDisplayName()
String organizerMail = issue.reporter.getEmailAddress()

String deployer = issue.assignee.getDisplayName()
String deployerMail = issue.assignee.getEmailAddress()

String description = issue.description = (null) ? '':issue.description

String descriptionHtml = renderer.render(description, null)
descriptionHtml = descriptionHtml.replaceAll("\\n", "\\\\n")
String descriptionHtmlWrapped = ""

int index = 0
int lineLength = 80
while (index < descriptionHtml.length()) {
descriptionHtmlWrapped += descriptionHtml.substring(index, Math.min(index + lineLength,descriptionHtml.length())) + "\n\t";
index += lineLength;
}

log.info("DESCRIPTION: " + descriptionHtmlWrapped)

def binding = [
UID : UUID.randomUUID(),
CREATION_TIME : sdf1.format(cal.getTime()),
MOD_TIME : sdf1.format(cal.getTime()),
START_TIME : sdf2.format(cfDeploymentTimeVal),
END_TIME : sdf2.format(endTime),
ORGANIZER : organizer,
ORGANIZER_MAIL : organizerMail,
DEPLOYER : deployer,
DEPLOYER_MAIL : deployerMail,
LOCATION : '',
SUMMARY : issue.summary,
DESCRIPTION : description,
DESCRIPTION_HTML : descriptionHtmlWrapped
]

String attachmentContent = template.make(binding)


File attachment = new File("<PATH_TO_JIRA_INST_DIR>/temp/sample.ics")

// if file doesnt exists, then create it
if (!attachment.exists()) {
log.info("CREATING NEW ATTACHMENT!!!")
attachment.createNewFile();
}

try {
log.info("POPULATING ATTACHMENT ...")
FileWriter fw = new FileWriter(attachment.getAbsoluteFile());
BufferedWriter bw = new BufferedWriter(fw);
bw.write(attachmentContent);
bw.close();
log.info("... POPULATING ATTACHMENT - DONE!!!")
} catch(IOException exc) {
log.error("ATTACHMENT COULD NOT BE CREATED!!!", exc)
}

log.info("ADDING ATTACHMENT ...")
def bean = new CreateAttachmentParamsBean.Builder()
.file(attachment)
.filename(issue.summary + ".ics")
.contentType("text/plain")
.author(user)
.issue(issue)
.build()

attachmentManager.createAttachment(bean)
log.info("... ADDING ATTACHMENT - DONE!!!")

I hope that you can make it working for you case. Please let me know if you have any further questions.

roniz January 31, 2018
roniz January 31, 2018

.

roniz January 31, 2018

so so sorry for the late response and thank yo so much for posting this!

 

what do you mean in your text when it is written: CREATION_TIME? - Im stuck on that ):

Christian Schlaefcke
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 31, 2018

Hi @roniz

the CREATION_TIME a placeholder in the template, represented by

def templateText = '''\
...
CREATED:${CREATION_TIME}
...
END:VCALENDAR'''

The placeholder in the template needs to be mapped with something useful which is happening in the binding section:

def binding = [
...
CREATION_TIME : sdf1.format(cal.getTime()),
...
]

And finally the template will be processed for filling up all placeholders with the content of the binding:

String attachmentContent = template.make(binding)

Next I am writing the attachmentContent to a file on the disk and attach it to the JIRA issue.

I hope I was able to explain the core of the implementation in a comprehensive way.

Best Regards,

Christian

roniz January 31, 2018

@Christian Schlaefcke- thanks! im trying it

 

roniz January 31, 2018

@Christian Schlaefckeand- what about the UUID?

how did you create it?

Christian Schlaefcke
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
January 31, 2018

Exactly the same way like the CREATION_TIME - on-the-fly in the template/binding without CF:

def binding = [
UID : UUID.randomUUID(),
...
]

 

roniz January 31, 2018

thanks! (and thanks for the patience) i will try it!

roniz February 4, 2018

@Christian Schlaefcke, I really tried, hope it is not out of my scope..

I made a new screen +workflow + etc to match to your definitions in the code (see screenshot attached) but when I try to create a ticket I get the following error:

Error creating issue: Property 'CREATION_TIME' not found

 

how do I define a property?

roniz February 5, 2018

Capture.PNG

Christian Schlaefcke
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
February 5, 2018

Hi @roniz,

where are you located? Do you have a Skype or whatever account? I think the stuff you´re having trouble with is a bit off-topic here. I would like to help and to share my knowledge but this seem to be more efficient in a 1:1 session. What do you think?

Best Regards,

Christian

roniz February 5, 2018

@Christian Schlaefckethat would be super awsom- my skype is Roni Zeilig- Sapiens

and my mail is roni.zeilig@sapiens.com

 

thanks so so much!

roniz February 5, 2018

@Christian Schlaefcke- I sent you a skype invitation from roni Zeilig

Tilman Mürle July 5, 2019

@Christian Schlaefcke or @Matthew Harkins , any idea why (after working beautifully for a year or so) now the ICS turns out as "not supported calendar message"?

Matthew Harkins July 5, 2019

This is not occurring for me.

I think that the answer to your question is that you may have updated your Outlook Client software or MS Exchange Server. Newer versions of Outlook and Exchange handle the RDATE and RRULE fields of the ics file differently than previous versions.

Also there is a bug in a particular version of MS Exchange 2016 regarding the parsing of VTIMEZONE

See the following MS support bug notice -- 

https://support.microsoft.com/en-us/help/4456241/you-receive-a-meeting-request-that-has-not-supported-calendar-message

In @Christian Schlaefcke 's example he included an RRULE, but no RDATE.  This appears to be supported by the RFC, but I do not know if Outlook is complaining about the RRULE itself (Outlook only supports a subset of possible RRULEs) or the absence of the RDATE field when an RRULE is supplied.

RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3

Recurrence every Year on the last Sunday of the 3rd Month   - Counting days backwards could be a problem

See the following MS support article --

https://support.microsoft.com/en-us/help/2643084/outlook-receives-a-message-that-has-an-attachment-that-is-named-not-su

 

See the following patterns are broken in Outlook-- 

Some of these broken recurrence patterns and options are;

  • Exceptions for weekends
    • Move to Friday
    • Move to Monday
    • Move to Nearest Weekday
    • Delete
  • Recur monthly; Every xth and xth xday of the month
    • Example: Every first and third Monday of the month.
  • Recur monthly; xth day counting from the end of the month
    • Not broken but doesn’t count backwards so results in the wrong dates in Outlook.
  • Recur monthly; every xth and xth day of the month
    • Example: Every 3rd and 10th day of the month

Under some circumstances, Meeting Updates or Cancelation messages could also end up broken;

  • When sending an update/cancellation
    • This instance and all previous instances
    • This instance and all future instances
  • When there are exceptions to a supported recurrence and then add an Outlook user for all instances.

From: https://www.msoutlook.info/question/meeting-invitation-contains-not-supported-calendar-message-ics-attachment 

Also See the following broken patterns -- 

1. Exceptions for weekends

  • Delete
  • Move to Friday
  • Move to Nearest Weekday
  • Move to Monday

2. Recur monthly: xth day counting from the backward of month
In this case, it is not broken but unable to count backward so display wrong dates in MS Outlook.

From: https://www.systoolsgroup.com/updates/fix-outlook-not-supported-calendar-message-ics-issue/

Like # people like this
Tilman Mürle July 12, 2019

@Matthew Harkins , what a great and highly appreciated answer.

Below you can find the details on the "non-supported" ICS.

Could it be the TRIGGER:-PT15M?

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Microsoft Corporation//Outlook 14.0 MIMEDIR//EN
METHOD:REQUEST
X-MS-OLK-FORCEINSPECTOROPEN:TRUE
BEGIN:VEVENT
CLASS:PUBLIC
CREATED:20190624T092030Z
DESCRIPTION:Keine weiteren Hinweise oder Informationen zur Abwesenheit übermittelt.
DTEND:20190805T220000Z
DTSTAMP:20190624T092030Z
DTSTART:20190721T220000Z
LAST-MODIFIED:20190624T092030Z
ORGANIZER;CN=Lastname, Firstname:MAILTO:abc.def@abc.de
ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=TENTATIVE;CN=CN=Lastname, Firstname:MAILTO:abc.def@abc.de
PRIORITY:5
SEQUENCE:0
SUMMARY;LANGUAGE=de-de:Urlaub - Muerle, Tilman: 22.07.2019 - 05.08.2019 (11.0 Tage) - ABC-123
TRANSP:OPAQUE
STATUS:TENTATIVE
UID:eb573c5c-ced3-4799-a5a1-b199ec8c7bb7
TRANSP:TRANSPARENT
X-MICROSOFT-CDO-BUSYSTATUS:FREE
X-MICROSOFT-CDO-IMPORTANCE:1
X-MICROSOFT-DISALLOW-COUNTER:FALSE
X-MS-OLK-AUTOFILLLOCATION:FALSE
X-MS-OLK-CONFTYPE:0
BEGIN:VALARM
TRIGGER:-PT15M
ACTION:DISPLAY
DESCRIPTION:Reminder
END:VALARM
END:VEVENT
END:VCALENDAR
Matthew Harkins July 12, 2019

I could not find any relevant support article referencing the TRIGGER value.

There are just a few differences I note between your "non-supported" ICS and a working ICS from my system.

You have defined the TRANSP twice with opposing values.  Please try your ICS again removing one of the contradictory settings (TRANSP:OPAQUE or TRANSP:TRANSPARENT).

 

If that does not solve the non-supported ICS problem, I would focus on the other differences:

ORGANIZER, ATTENDEE, STATUS

You might try removing these fields one at a time to see if that resolves the problem.

 

If none of those changes solve the problem, then I would check your MS Exchange routing.  We have another service that sends similar ICS which recently started appearing as non-supported.  We currently think that this may be due to an older internal MS Exchange server that routes mail to the new upgraded primary MS Exchange server that hosts the destination mailboxes. This other service is configured to use the older MS Exchange server as its smtp endpoint. I am curious if you are also in the process of upgrading your Exchange infrastructure and have your environment configured in a similar way with Jira configured to use an older version of MS Exchange wherein your mailboxes have been migrated to a host with a newer MS Exchange version.  We are operating in this manner for only a short time to allow for incremental migration as services are adjusted in turn from using the older MS Exchange host to using the more recent versioned MS Exchange host.  I expect to see the non-supported ics clear up for us once this additional service is configured to use the newer versioned MS Exchange endpoint.

Eimear Larkin July 16, 2019

@Matthew Harkins sorry to jump in on this thread! I've just tried out your code and although a mail gets sent it's just sending as a mail not a meeting invite, is there anything else that needs to be configured or that I haven't done? The only difference from your code is I set the busystatus to be BUSY all the time, would there be any other bits of the script I would need to change?

Thanks,

Eimear :) 

2 votes
Chris Dunne _Raledo_
Marketplace Partner
Marketplace Partners provide apps and integrations available on the Atlassian Marketplace that extend the power of Atlassian products.
January 16, 2020

This solution worked well for me but I had some problems with Outlook in Office 365. The email invitations were not being recognised and the email just looked like an ordinary email (no RSVP buttons), and there was no ICS file attached.

I changed one line from

calendarPart.setDataHandler(new DataHandler(new ByteArrayDataSource(iCal, 
"text/calendar")));

To

calendarPart.setDataHandler(new DataHandler(new ByteArrayDataSource(iCal, "text/calendar;method=REQUEST;name=\"meeting.ics\"")));

 Initial tests look good and both Outlook and Gmail receive valid invitations.

Chris

Matthew Harkins January 16, 2020

Nice work @Chris Dunne _Raledo_! I can second your fix.  This enabled usable ical notifications for our o365 users and continued to function normally for our on premises users.

1 vote
JamieA
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
December 14, 2016

You have full control over the Mail object, so you should be able to do it. The closest thing in the docs is https://scriptrunner.adaptavist.com/latest/jira/builtin-scripts.html#_adding_generated_attachments.

In the linked SO.com answer it says outlook ignores the ICS attachment. So you need to create and add a new mail part of type "text/calendar" with the contents of the ics file.

roniz January 14, 2018

Hi @Jamie Echlin@Matthew Harkins, @Christian Schlaefcke- great post!!

I tried making this work, unfortunately with out much success:

  1. I created a new custom field (type Text Field (single line)) by the name "UUID" and set is as a Custom script post-function:

    import com.atlassian.jira.component.ComponentAccessor

    def customFieldManager = ComponentAccessor.getCustomFieldManager()
    def cf = customFieldManager.getCustomFieldObjectByName("UUID")
    issue.setCustomFieldValue(cf, UUID.randomUUID().toString())

    Is this ok? should I do any thing further? does this field need to be attached to a screen in the workflow? did not quirt understand its purpose..
  2. In the next Status I created a post function "Send a custom email" i took @Matthew Harkins's scripts and in the condition changed  

"Custom Start DateTime"--> "Estimated start time"
"Custom End DateTime" --> "Estimated complition time"
"Custom UUID" --> "UUID" 

all the rest is the same

 

 

I did not do any thing with a ICS file- sould I?

 

and it did not work ):

 

Any thoughts please?

Many many thanks in advance!

0 votes
Connor Sims July 11, 2017

Christian,

Would you share the script you used to create an ICS file and attach it to the issue?

 

Thanks,

Connor

Normann P_ Nielsen _Netic_
Rising Star
Rising Star
Rising Stars are recognized for providing high-quality answers to other users. Rising Stars receive a certificate of achievement and are on the path to becoming Community Leaders.
March 14, 2018

I like Matthews scripts works well - but how do I set the filename?

In Mac's Mail.app its becoming "Mail Attachment" and people cant see its an invite

Worksbetter in GMail.

 

BR,

 

Normann

Suggest an answer

Log in or Sign up to answer