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" )
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
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
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")
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
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")
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()