def test_CalendarFormatting(self):

        with translationTo('English', localeDir=localeDir) as t:

            comp = data[0][1]
            self.assertEquals(t.date(comp), "Saturday, October 25, 2008")
            self.assertEquals(t.time(comp),
                (u'9:15 AM to 10:15 AM PDT', u'1 hour 1 second'))

            comp = data[1][1]
            self.assertEquals(t.time(comp),
                (u'1:15 PM to 3:15 PM PDT', u'2 hours 2 seconds'))

            comp = data[2][1]
            self.assertEquals(t.time(comp),
                (u'11:05 AM to 2:15 PM PDT', u'3 hours 10 minutes'))

            comp = data[3][1]
            self.assertEquals(t.time(comp),
                ("", u'All day'))

            comp = data[4][1]
            self.assertEquals(t.time(comp),
                (u'1:15 PM PDT', ""))

            comp = data[5][1]
            self.assertEquals(t.time(comp),
                (u'11:05 AM PDT to 6:15 PM EDT', u'4 hours 10 minutes'))

            self.assertEquals(t.monthAbbreviation(1), "JAN")

        with translationTo('pig', localeDir=localeDir) as t:

            comp = data[0][1]
            self.assertEquals(t.date(comp), 'Aturdaysay, Octoberway 25, 2008')
            self.assertEquals(t.time(comp),
                (u'09:15 otay 10:15 PDT', u'1 ourhay 1 econdsay'))

            comp = data[1][1]
            self.assertEquals(t.time(comp),
                (u'13:15 otay 15:15 PDT', u'2 ourshay 2 econdsay'))

            comp = data[2][1]
            self.assertEquals(t.time(comp),
                (u'11:05 otay 14:15 PDT', u'3 ourshay 10 inutesmay'))

            comp = data[3][1]
            self.assertEquals(t.time(comp),
                ("", u'Allway ayday'))

            comp = data[4][1]
            self.assertEquals(t.time(comp),
                (u'13:15 PDT', ""))

            comp = data[5][1]
            self.assertEquals(t.time(comp),
                (u'11:05 PDT otay 18:15 EDT', u'4 ourshay 10 inutesmay'))

            self.assertEquals(t.monthAbbreviation(1), "ANJAY")
    def test_TimeFormatting24Hour(self):

        with translationTo('pig', localeDir=localeDir) as t:

            self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1,  0,  0, 0)), "00:00")
            self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 12,  0, 0)), "12:00")
            self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 23, 59, 0)), "23:59")
            self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1,  6,  5, 0)), "06:05")
            self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 16,  5, 0)), "16:05")
    def test_TimeFormatting24Hour(self):

        with translationTo('pig', localeDir=localeDir) as t:

            self.assertEquals(t.dtTime(time(0,0)), "00:00")
            self.assertEquals(t.dtTime(time(12,0)), "12:00")
            self.assertEquals(t.dtTime(time(23,59)), "23:59")
            self.assertEquals(t.dtTime(time(6,5)), "06:05")
            self.assertEquals(t.dtTime(time(16,5)), "16:05")
    def test_TimeFormattingAMPM(self):

        with translationTo('English', localeDir=localeDir) as t:

            self.assertEquals(t.dtTime(time(0,0)), "12:00 AM")
            self.assertEquals(t.dtTime(time(12,0)), "12:00 PM")
            self.assertEquals(t.dtTime(time(23,59)), "11:59 PM")
            self.assertEquals(t.dtTime(time(6,5)), "6:05 AM")
            self.assertEquals(t.dtTime(time(16,5)), "4:05 PM")
    def test_TimeFormattingAMPM(self):

        with translationTo('en', localeDir=localeDir) as t:

            self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 0, 0, 0)), "12:00 AM")
            self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 12, 0, 0)), "12:00 PM")
            self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 23, 59, 0)), "11:59 PM")
            self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 6, 5, 0)), "6:05 AM")
            self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 16, 5, 0)), "4:05 PM")
    def test_TimeFormatting24Hour(self):

        with translationTo('pig', localeDir=localeDir) as t:

            self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 0, 0, 0)), "00:00")
            self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 12, 0, 0)), "12:00")
            self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 23, 59, 0)), "23:59")
            self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 6, 5, 0)), "06:05")
            self.assertEquals(t.dtTime(DateTime(2000, 1, 1, 16, 5, 0)), "16:05")
    def test_TimeFormattingAMPM(self):

        with translationTo('en', localeDir=localeDir) as t:

            self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1,  0,  0, 0)), "12:00 AM")
            self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 12,  0, 0)), "12:00 PM")
            self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 23, 59, 0)), "11:59 PM")
            self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1,  6,  5, 0)), "6:05 AM")
            self.assertEquals(t.dtTime(PyCalendarDateTime(2000, 1, 1, 16,  5, 0)), "4:05 PM")
    def test_BasicStringLocalization(self):

        with translationTo('pig', localeDir=localeDir):

            self.assertEquals(_("All day"), "Allway ayday")

            self.assertEquals(_("%(startTime)s to %(endTime)s") %
                { 'startTime' : 'a', 'endTime' : 'b' },
                "a otay b"
            )
    def test_BasicStringLocalization(self):

        with translationTo('pig', localeDir=localeDir):

            self.assertEquals(_("All day"), "Allway ayday")

            self.assertEquals(
                _("%(startTime)s to %(endTime)s") %
                {'startTime': 'a', 'endTime': 'b'},
                "a otay b"
            )
Example #10
0
def localizedLabels(language, canceled, inviteState):
    """
    Generate localized labels for an email in the given language.

    @param language: a 2-letter language code

    @type language: C{str}

    @return: a 2-tuple of (subjectFormatString, labelDict), where the first is a
        format string for use in the subject, and the latter is a dictionary
        with labels suitable for filling out HTML and plain-text templates.  All
        values are C{str}s.
    """
    with translationTo(language):
        if canceled:
            subjectFormatString = _("Event canceled: %(summary)s")
        elif inviteState == "new":
            subjectFormatString = _("Event invitation: %(summary)s")
        elif inviteState == "update":
            subjectFormatString = _("Event update: %(summary)s")
        else:
            subjectFormatString = _("Event reply: %(summary)s")

        if canceled:
            inviteLabel = _("Event Canceled")
        else:
            if inviteState == "new":
                inviteLabel = _("Event Invitation")
            elif inviteState == "update":
                inviteLabel = _("Event Update")
            else:
                inviteLabel = _("Event Reply")

        labels = dict(
            dateLabel=_("Date"),
            timeLabel=_("Time"),
            durationLabel=_("Duration"),
            recurrenceLabel=_("Occurs"),
            descLabel=_("Description"),
            urlLabel=_("URL"),
            orgLabel=_("Organizer"),
            attLabel=_("Attendees"),
            locLabel=_("Location"),
            inviteLabel=inviteLabel,
        )

        # The translations we get back from gettext are utf-8 encoded
        # strings, so convert to unicode
        for key in labels.keys():
            if isinstance(labels[key], str):
                labels[key] = labels[key].decode("utf-8")

    return subjectFormatString.decode("utf-8"), labels
Example #11
0
def localizedLabels(language, canceled, inviteState):
    """
    Generate localized labels for an email in the given language.

    @param language: a 2-letter language code

    @type language: C{str}

    @return: a 2-tuple of (subjectFormatString, labelDict), where the first is a
        format string for use in the subject, and the latter is a dictionary
        with labels suitable for filling out HTML and plain-text templates.  All
        values are C{str}s.
    """
    with translationTo(language):
        if canceled:
            subjectFormatString = _("Event canceled: %(summary)s")
        elif inviteState == "new":
            subjectFormatString = _("Event invitation: %(summary)s")
        elif inviteState == "update":
            subjectFormatString = _("Event update: %(summary)s")
        else:
            subjectFormatString = _("Event reply: %(summary)s")

        if canceled:
            inviteLabel = _("Event Canceled")
        else:
            if inviteState == "new":
                inviteLabel = _("Event Invitation")
            elif inviteState == "update":
                inviteLabel = _("Event Update")
            else:
                inviteLabel = _("Event Reply")

        labels = dict(
            dateLabel=_("Date"),
            timeLabel=_("Time"),
            durationLabel=_("Duration"),
            recurrenceLabel=_("Occurs"),
            descLabel=_("Description"),
            urlLabel=_("URL"),
            orgLabel=_("Organizer"),
            attLabel=_("Attendees"),
            locLabel=_("Location"),
            inviteLabel=inviteLabel,
        )

        # The translations we get back from gettext are utf-8 encoded
        # strings, so convert to unicode
        for key in labels.keys():
            if isinstance(labels[key], str):
                labels[key] = labels[key].decode("utf-8")

    return subjectFormatString.decode("utf-8"), labels
Example #12
0
    def getEventDetails(self, calendar, language='en'):
        """
        Create a dictionary mapping slot names - specifically: summary,
        description, location, dateInfo, timeInfo, durationInfo, recurrenceInfo,
        url - with localized string values that should be placed into the HTML
        and plain-text templates.

        @param calendar: a L{Component} upon which to base the language.
        @type calendar: L{Component}

        @param language: a 2-letter language code.
        @type language: C{str}

        @return: a mapping from template slot name to localized text.
        @rtype: a C{dict} mapping C{bytes} to C{unicode}.
        """

        # Get the most appropriate component
        component = calendar.mainComponent()

        results = {}

        dtStart = component.propertyValue('DTSTART')
        results['month'] = dtStart.getMonth()
        results['day'] = dtStart.getDay()

        for propertyToResult in ['summary', 'description', 'location', 'url']:
            result = component.propertyValue(propertyToResult.upper())
            if result is None:
                result = u""
            else:
                result = result.decode('utf-8')
            results[propertyToResult] = result

        with translationTo(language) as trans:
            results['dateInfo'] = trans.date(component).decode('utf-8')
            results['timeInfo'], duration = (x.decode('utf-8')
                                             for x in trans.time(component))
            results['durationInfo'] = u"(%s)" % (
                duration, ) if duration else u""

            for propertyName in ('RRULE', 'RDATE', 'EXRULE', 'EXDATE',
                                 'RECURRENCE-ID'):
                if component.hasProperty(propertyName):
                    results['recurrenceInfo'] = _("(Repeating)").decode(
                        'utf-8')
                    break
            else:
                results['recurrenceInfo'] = u""

        return results
Example #13
0
    def getEventDetails(self, calendar, language='en'):
        """
        Create a dictionary mapping slot names - specifically: summary,
        description, location, dateInfo, timeInfo, durationInfo, recurrenceInfo,
        url - with localized string values that should be placed into the HTML
        and plain-text templates.

        @param calendar: a L{Component} upon which to base the language.
        @type calendar: L{Component}

        @param language: a 2-letter language code.
        @type language: C{str}

        @return: a mapping from template slot name to localized text.
        @rtype: a C{dict} mapping C{bytes} to C{unicode}.
        """

        # Get the most appropriate component
        component = calendar.masterComponent()
        if component is None:
            component = calendar.mainComponent(True)

        results = {}

        dtStart = component.propertyValue('DTSTART')
        results['month'] = dtStart.getMonth()
        results['day'] = dtStart.getDay()

        for propertyToResult in ['summary', 'description', 'location', 'url']:
            result = component.propertyValue(propertyToResult.upper())
            if result is None:
                result = u""
            else:
                result = result.decode('utf-8')
            results[propertyToResult] = result

        with translationTo(language) as trans:
            results['dateInfo'] = trans.date(component).decode('utf-8')
            results['timeInfo'], duration = (x.decode('utf-8') for x in trans.time(component))
            results['durationInfo'] = u"(%s)" % (duration,) if duration else u""

            for propertyName in ('RRULE', 'RDATE', 'EXRULE', 'EXDATE',
                                 'RECURRENCE-ID'):
                if component.hasProperty(propertyName):
                    results['recurrenceInfo'] = _("(Repeating)").decode('utf-8')
                    break
            else:
                results['recurrenceInfo'] = u""

        return results
    def test_processLocalizationFiles(self):
        """
        Make sure that on OS X the .lproj files are converted properly.
        """
        if sys.platform == "darwin":
            tmpdir = self.mktemp()

            settings = ConfigDict({
                "TranslationsDirectory": os.path.join(os.path.dirname(__file__), "data", "translations"),
                "LocalesDirectory": tmpdir,
            })

            processLocalizationFiles(settings)

            self.assertTrue(os.path.exists(os.path.join(tmpdir, "Testlang", "LC_MESSAGES", "calendarserver.mo")))

            with translationTo('Testlang', localeDir=tmpdir):
                self.assertEquals(_("String1"), "string 1")
                self.assertEquals(_("String2"), "string 2")
Example #15
0
    def getEventDetails(self, calendar, language='en'):

        # Get the most appropriate component
        component = calendar.masterComponent()
        if component is None:
            component = calendar.mainComponent(True)

        results = { }

        dtStart = component.propertyNativeValue("DTSTART")
        results['month'] = dtStart.month
        results['day'] = dtStart.day

        summary = component.propertyValue("SUMMARY")
        if summary is None:
            summary = ""
        results['summary'] = summary

        description = component.propertyValue("DESCRIPTION")
        if description is None:
            description = ""
        results['description'] = description

        location = component.propertyValue("LOCATION")
        if location is None:
            location = ""
        results['location'] = location

        with translationTo(language) as trans:
            results['dateInfo'] = trans.date(component)
            results['timeInfo'], duration = trans.time(component)
            results['durationInfo'] = "(%s)" % (duration,) if duration else ""

            for propertyName in ("RRULE", "RDATE", "EXRULE", "EXDATE",
                "RECURRENCE-ID"):
                if component.hasProperty(propertyName):
                    results['recurrenceInfo'] = _("(Repeating)")
                    break
            else:
                results['recurrenceInfo'] = ""

        return results
Example #16
0
    def getIconPath(self, details, canceled, language='en'):
        iconDir = config.Scheduling.iMIP.MailIconsDirectory.rstrip("/")

        if canceled:
            iconName = "canceled.png"
            iconPath = os.path.join(iconDir, iconName)
            if os.path.exists(iconPath):
                return iconPath
            else:
                return None

        else:
            month = int(details['month'])
            day = int(details['day'])
            with translationTo(language) as trans:
                monthName = trans.monthAbbreviation(month)
            iconName = "%02d.png" % (day,)
            iconPath = os.path.join(iconDir, monthName, iconName)
            if not os.path.exists(iconPath):
                # Try the generic (numeric) version
                iconPath = os.path.join(iconDir, "%02d" % (month,), iconName)
                if not os.path.exists(iconPath):
                    return None
            return iconPath
    def test_CalendarFormatting(self):

        with translationTo('en', localeDir=localeDir) as t:

            comp = data[0][1]
            self.assertEquals(t.date(comp), "Saturday, October 25, 2008")
            self.assertEquals(
                t.time(comp),
                (u'9:15 AM to 10:15 AM (PDT)', u'1 hour 1 second'))

            comp = data[1][1]
            self.assertEquals(
                t.time(comp),
                (u'1:15 PM to 3:15 PM (PDT)', u'2 hours 2 seconds'))

            comp = data[2][1]
            self.assertEquals(
                t.time(comp),
                (u'11:05 AM to 2:15 PM (PDT)', u'3 hours 10 minutes'))

            comp = data[3][1]
            self.assertEquals(
                t.time(comp),
                ("", u'All day'))

            comp = data[4][1]
            self.assertEquals(
                t.time(comp),
                (u'1:15 PM (PDT)', ""))

            comp = data[5][1]
            self.assertEquals(
                t.time(comp),
                (u'11:05 AM (PDT) to 6:15 PM (EDT)', u'4 hours 10 minutes'))

            comp = data[6][1]
            self.assertEquals(
                t.time(comp),
                (u'11:05 AM to 5:15 PM (PDT)', u'1 day 6 hours 10 minutes'))

            comp = data[7][1]
            self.assertEquals(
                t.time(comp),
                (u'11:05 AM to 12:15 PM (PDT)', u'2 days 1 hour 10 minutes'))

            self.assertEquals(t.monthAbbreviation(1), "JAN")

        with translationTo('pig', localeDir=localeDir) as t:

            comp = data[0][1]
            self.assertEquals(t.date(comp), 'Aturdaysay, Octoberway 25, 2008')
            self.assertEquals(
                t.time(comp),
                (u'09:15 otay 10:15 (PDT)', u'1 ourhay 1 econdsay'))

            comp = data[1][1]
            self.assertEquals(
                t.time(comp),
                (u'13:15 otay 15:15 (PDT)', u'2 ourshay 2 econdsay'))

            comp = data[2][1]
            self.assertEquals(
                t.time(comp),
                (u'11:05 otay 14:15 (PDT)', u'3 ourshay 10 inutesmay'))

            comp = data[3][1]
            self.assertEquals(
                t.time(comp),
                ("", u'Allway ayday'))

            comp = data[4][1]
            self.assertEquals(
                t.time(comp),
                (u'13:15 (PDT)', ""))

            comp = data[5][1]
            self.assertEquals(
                t.time(comp),
                (u'11:05 (PDT) otay 18:15 (EDT)', u'4 ourshay 10 inutesmay'))

            comp = data[6][1]
            self.assertEquals(
                t.time(comp),
                (u'11:05 otay 17:15 (PDT)', u'1 ayday 6 ourshay 10 inutesmay'))

            comp = data[7][1]
            self.assertEquals(
                t.time(comp),
                (u'11:05 otay 12:15 (PDT)', u'2 aysday 1 ourhay 10 inutesmay'))

            self.assertEquals(t.monthAbbreviation(1), "ANJAY")
Example #18
0
    def generateEmail(self, inviteState, calendar, orgEmail, orgCN,
        attendees, fromAddress, replyToAddress, toAddress, language='en'):

        details = self.getEventDetails(calendar, language=language)
        canceled = (calendar.propertyValue("METHOD") == "CANCEL")
        iconPath = self.getIconPath(details, canceled, language=language)

        with translationTo(language):
            msg = MIMEMultipart()
            msg["From"] = fromAddress
            msg["Reply-To"] = replyToAddress
            msg["To"] = toAddress
            msg["Date"] = rfc822date()
            msgId = messageid()
            msg["Message-ID"] = msgId

            if canceled:
                formatString = _("Event canceled: %(summary)s")
            elif inviteState == "new":
                formatString = _("Event invitation: %(summary)s")
            elif inviteState == "update":
                formatString = _("Event update: %(summary)s")
            else:
                formatString = _("Event reply: %(summary)s")

            # The translations we get back from gettext are utf-8 encoded
            # strings, so convert to unicode
            formatString = formatString.decode("utf-8")

            details['subject'] = msg['Subject'] = formatString % {
                'summary' : details['summary']
            }

            msgAlt = MIMEMultipart("alternative")
            msg.attach(msgAlt)

            # Get localized labels
            if canceled:
                details['inviteLabel'] = _("Event Canceled")
            else:
                if inviteState == "new":
                    details['inviteLabel'] = _("Event Invitation")
                if inviteState == "update":
                    details['inviteLabel'] = _("Event Update")
                else:
                    details['inviteLabel'] = _("Event Reply")

            details['dateLabel'] = _("Date")
            details['timeLabel'] = _("Time")
            details['durationLabel'] = _("Duration")
            details['recurrenceLabel'] = _("Occurs")
            details['descLabel'] = _("Description")
            details['orgLabel'] = _("Organizer")
            details['attLabel'] = _("Attendees")
            details['locLabel'] = _("Location")


            plainAttendeeList = []
            for cn, mailto in attendees:
                if cn:
                    plainAttendeeList.append(cn if not mailto else
                        "%s <%s>" % (cn, mailto))
                elif mailto:
                    plainAttendeeList.append("<%s>" % (mailto,))

            details['plainAttendees'] = ", ".join(plainAttendeeList)

            details['plainOrganizer'] = (orgCN if not orgEmail else
                "%s <%s>" % (orgCN, orgEmail))

            # The translations we get back from gettext are utf-8 encoded
            # strings, so convert to unicode
            for key in details.keys():
                if isinstance(details[key], str):
                    details[key] = details[key].decode("utf-8")

            # plain text version
            if canceled:
                plainTemplate = u"""%(subject)s

%(orgLabel)s: %(plainOrganizer)s
%(dateLabel)s: %(dateInfo)s %(recurrenceInfo)s
%(timeLabel)s: %(timeInfo)s %(durationInfo)s
"""
            else:
                plainTemplate = u"""%(subject)s

%(orgLabel)s: %(plainOrganizer)s
%(locLabel)s: %(location)s
%(dateLabel)s: %(dateInfo)s %(recurrenceInfo)s
%(timeLabel)s: %(timeInfo)s %(durationInfo)s
%(descLabel)s: %(description)s
%(attLabel)s: %(plainAttendees)s
"""

            plainText = plainTemplate % details

            msgPlain = MIMEText(plainText.encode("UTF-8"), "plain", "UTF-8")
            msgAlt.attach(msgPlain)

            # html version
            msgHtmlRelated = MIMEMultipart("related", type="text/html")
            msgAlt.attach(msgHtmlRelated)


            htmlAttendees = []
            for cn, mailto in attendees:
                if mailto:
                    htmlAttendees.append('<a href="mailto:%s">%s</a>' %
                        (mailto, cn))
                else:
                    htmlAttendees.append(cn)

            details['htmlAttendees'] = ", ".join(htmlAttendees)

            if orgEmail:
                details['htmlOrganizer'] = '<a href="mailto:%s">%s</a>' % (
                    orgEmail, orgCN)
            else:
                details['htmlOrganizer'] = orgCN

            details['iconName'] = iconName = "calicon.png"

            templateDir = config.Scheduling.iMIP.MailTemplatesDirectory.rstrip("/")
            templateName = "cancel.html" if canceled else "invite.html"
            templatePath = os.path.join(templateDir, templateName)

            if not os.path.exists(templatePath):
                # Fall back to built-in simple templates:
                if canceled:

                    htmlTemplate = u"""<html>
    <body><div>

    <h1>%(subject)s</h1>
    <p>
    <h3>%(orgLabel)s:</h3> %(htmlOrganizer)s
    </p>
    <p>
    <h3>%(dateLabel)s:</h3> %(dateInfo)s %(recurrenceInfo)s
    </p>
    <p>
    <h3>%(timeLabel)s:</h3> %(timeInfo)s %(durationInfo)s
    </p>

    """

                else:

                    htmlTemplate = u"""<html>
    <body><div>
    <p>%(inviteLabel)s</p>

    <h1>%(summary)s</h1>
    <p>
    <h3>%(orgLabel)s:</h3> %(htmlOrganizer)s
    </p>
    <p>
    <h3>%(locLabel)s:</h3> %(location)s
    </p>
    <p>
    <h3>%(dateLabel)s:</h3> %(dateInfo)s %(recurrenceInfo)s
    </p>
    <p>
    <h3>%(timeLabel)s:</h3> %(timeInfo)s %(durationInfo)s
    </p>
    <p>
    <h3>%(descLabel)s:</h3> %(description)s
    </p>
    <p>
    <h3>%(attLabel)s:</h3> %(htmlAttendees)s
    </p>

    """
            else: # HTML template file exists

                with open(templatePath) as templateFile:
                    htmlTemplate = templateFile.read()

            htmlText = htmlTemplate % details

        msgHtml = MIMEText(htmlText.encode("UTF-8"), "html", "UTF-8")
        msgHtmlRelated.attach(msgHtml)

        # an image for html version
        if (iconPath != None and os.path.exists(iconPath) and
            htmlTemplate.find("cid:%(iconName)s") != -1):

            with open(iconPath) as iconFile:
                msgIcon = MIMEImage(iconFile.read(),
                    _subtype='png;x-apple-mail-type=stationery;name="%s"' %
                    (iconName,))

            msgIcon.add_header("Content-ID", "<%s>" % (iconName,))
            msgIcon.add_header("Content-Disposition", "inline;filename=%s" %
                (iconName,))
            msgHtmlRelated.attach(msgIcon)

        # the icalendar attachment
        self.log_debug("Mail gateway sending calendar body: %s" % (str(calendar)))
        msgIcal = MIMEText(str(calendar), "calendar", "UTF-8")
        method = calendar.propertyValue("METHOD").lower()
        msgIcal.set_param("method", method)
        msgIcal.add_header("Content-ID", "<invitation.ics>")
        msgIcal.add_header("Content-Disposition",
            "inline;filename=invitation.ics")
        msg.attach(msgIcal)

        return msgId, msg.as_string()