如何创建一个多部分的iCal电子邮件?[Rails]

4

我想要实现以下内容:

使用delayed_job发送电子邮件,包含以下内容:

  1. 纯文本
  2. HTML(将由普通客户端显示,这些客户端不理解内联ical)
  3. 被Outlook和Thunderbird(带有Lightning)识别的“内联”ical。 Thunderbird Accept/Decline/Tentative Buttons
  4. 一个“常规”的ical附件(用于#2)

到目前为止可以工作/无法正常工作:

我能够通过delayed_job发送包含所有部分的电子邮件,但是:

  • 在Apple的Mail中,会出现2个附件(而不是一个):

Apple Mail

(HTML正常显示)

  • 在Thunderbird(Lightning)中,我确实得到了邀请,就像我想要的那样。 但是闹钟没有显示。

Lightning

  • 我必须对渲染的iCal进行一些非常恶心的gsubs,以便ATTENDEES显示出来。 (见代码段)

我的想法:

首先要记住的是:为了从delayed_job发送带附件的电子邮件

要解决此问题,请记住在您的邮件程序中添加此行:content_type“multipart/mixed”

据我所知,正确的MIME类型层次结构因此应该是:

  • multipart/mixed
    • multipart/alternative
      • text/plain
      • text/html
      • text/calendar(具有:method = REQUEST)
    • application/ics

警告!代码即将到来。

我目前是以下方式构建此电子邮件:

编辑:我已更新Rails 4.2的邮件程序(必须在mail之前放置attachments

在我的mailer.rb中

def invitation_email(...)  
  subject = "I suck at email..."
  attachments["invite.ics"] = { mime_type: "application/ics",
                                content: ical_attachment }
  email = mail(from: me, to: you, subject: subject)
  add_ical_part_to(email)
  email
end

def add_ical_part_to(mail)
  outlook_body = ical_attachment
  mail.add_part(Mail::Part.new do
    content_type "text/calendar; method=REQUEST"
    body outlook_body
  end)
end

这是我构建ical附件的方法:

def ical_attachment
  params_participant = {
        "ROLE"   => "REQ-PARTICIPANT",
        "RSVP" => "FALSE",
        "PARTSTAT" => "ACCEPTED"
  }

  params_invite = {
        "CUTYPE" => 'INDIVIDUAL',
        "ROLE"   => "REQ-PARTICIPANT",
        "PARTSTAT" => "NEEDS-ACTION",
        "RSVP" => "TRUE"
  }

  cal = Icalendar::Calendar.new

  event = Icalendar::Event.new
  event.dtstart      @party.from.to_datetime, { "VALUE" => "DATE" }
  event.dtend        @party.to.to_datetime, { "VALUE" => "DATE" }
  event.summary      @party.title
  event.description  @party.description
  event.klass        "PRIVATE"
  event.organizer    "cn=#{@user.name} #{@user.surname}:mailto:#{@user.email}"

    # THIS DOES NOT WORK
  event.alarm.trigger =  "-PT5M" # 5 Minutes before...

  @party.participations.each do |participation|
        str = "cn=#{participation.user.name} #{participation.user.surname}:mailto:#{participation.user.email}"
        event.add_attendee(str, params_participant)
  end

  @party.invitations.each do |invitee|
        event.add_attendee("mailto:#{invitee.email}", params_invite)
  end
  cal.add_event(event)
  cal.publish
  
  # I KNOW THIS IS HORRIBLE AND I HATE IT, BUT OTHERWISE THE ATTENDEES DO NOT SHOW UP
  cal.to_ical.gsub("ORGANIZER:", "ORGANIZER;").gsub("ACCEPTED:", "ACCEPTED;").gsub("TRUE:", "TRUE;").gsub("PUBLISH", "REQUEST")
end

非常感谢您的帮助!

正在生成的电子邮件: http://pastebin.com/patf05zd

此外,我使用的是:

2个回答

2
如果有其他人遇到类似情况,请看我是如何做的:
我不再使用icalendar gem,而是改为使用ri_cal。尽管我对这个repo的最后一次提交是3年前感到怀疑,但Google Group是一个非常有帮助的资源。
以下是我生成ical附件(内联和常规)的方法,看起来运行良好(虽然显然需要进行一些重构 :))
def to_ical
# this is horrible
klass = self

cal = RiCal.Calendar do
  event = event do
    organizer   "CN=#{klass.user.name} #{klass.user.surname}:mailto:#{klass.user.email}"
    summary     klass.party.title

    description klass.ical_description
    dtstart     klass.party.from.utc.to_datetime
    dtend       klass.party.to.utc.to_datetime
    location    "See url in description"
    security_class klass.security_class

    # this is horrible
    h = self

    klass.party.participations.each do |participation|
      h.add_attendee klass.prepare_participant(participation)
    end

    klass.party.invitations.each do |invitee|
      h.add_attendee klass.prepare_invitee(invitee.email)
    end

    unless klass.party.reminder == 0
      alarm do
        description "Alarm description"
        trigger klass.convert_trigger # -PT1H
        action "DISPLAY"
      end
    end
  end
end

# THE HORROR
cal.to_s.gsub("ATTENDEE:", "ATTENDEE")
        .gsub("ORGANIZER:", "ORGANIZER;")
        .gsub("CALSCALE:GREGORIAN", "CALSCALE:GREGORIAN\nMETHOD:REQUEST\n")

“end”的翻译是“结束”或“末尾”。
“Apples Mail中的2个附件仍然显示,我认为这是无法修复的。”

1
你的第二个B64编码附件末尾有很多垃圾内容(参与者字段)。这可能是Thunderbird问题的原因。
请注意,一些客户端将忽略您在REQUEST上设置的任何提醒:作为组织者,您不应该指定每个参与者何时应该被提醒会议。那将是相当无礼的事情。
关于Apple iCal的问题,恐怕没有太多可以做的:某些客户端希望ics在内部,某些客户端希望作为附件,因此您必须同时提供两者。iCal是否显示接受/拒绝面板?

感谢您的回复。实际上,我手动“编辑”了附件的某些部分。但是,这两个附件都是以相同的方式生成的:“ical_attachment”方法。是的,在iCal中(将附件保存并在OSX上使用ical文件处理程序打开时),接受/拒绝面板会显示出来。 - wpp
我不确定我理解了这个问题。你的 pastebin 是否对应于你在 Thunderbird 中遇到的错误? - Arnaud Quillaud

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接