[california/wip/740088-invite] Better mail body when sending invitation
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [california/wip/740088-invite] Better mail body when sending invitation
- Date: Thu, 20 Nov 2014 22:11:51 +0000 (UTC)
commit 5d160d6cc118601c364298cf177038feab805c95
Author: Jim Nelson <jim yorba org>
Date: Thu Nov 20 14:11:40 2014 -0800
Better mail body when sending invitation
src/calendar/calendar-exact-time-span.vala | 60 +++++++++++++++--
src/component/component-instance.vala | 7 +-
src/component/component-recurrence-rule.vala | 2 +
src/host/host-create-update-event.vala | 90 +++++++++++++++++++++++++-
4 files changed, 147 insertions(+), 12 deletions(-)
---
diff --git a/src/calendar/calendar-exact-time-span.vala b/src/calendar/calendar-exact-time-span.vala
index 20ba263..4db2ca1 100644
--- a/src/calendar/calendar-exact-time-span.vala
+++ b/src/calendar/calendar-exact-time-span.vala
@@ -25,7 +25,11 @@ public class ExactTimeSpan : BaseObject, Gee.Comparable<ExactTimeSpan>, Gee.Hash
/**
* Use multiple lines to format string if lengthy.
*/
- ALLOW_MULTILINE
+ ALLOW_MULTILINE,
+ /**
+ * Include timezone information in the string.
+ */
+ INCLUDE_TIMEZONE
}
/**
@@ -144,6 +148,7 @@ public class ExactTimeSpan : BaseObject, Gee.Comparable<ExactTimeSpan>, Gee.Hash
*/
public string to_pretty_string(Calendar.Date.PrettyFlag date_flags, PrettyFlag time_flags) {
bool allow_multiline = (time_flags & PrettyFlag.ALLOW_MULTILINE) != 0;
+ bool include_timezone = (time_flags & PrettyFlag.INCLUDE_TIMEZONE) != 0;
if (!start_date.year.equal_to(Calendar.System.today.year)
|| !end_date.year.equal_to(Calendar.System.today.year)) {
@@ -151,17 +156,32 @@ public class ExactTimeSpan : BaseObject, Gee.Comparable<ExactTimeSpan>, Gee.Hash
}
if (is_same_day) {
- // A span of time, i.e. "3:30pm to 4:30pm"
- string timespan = _("%s to %s").printf(
- start_exact_time.to_pretty_time_string(Calendar.WallTime.PrettyFlag.NONE),
- end_exact_time.to_pretty_time_string(Calendar.WallTime.PrettyFlag.NONE));
+ string pretty_start_time =
start_exact_time.to_pretty_time_string(Calendar.WallTime.PrettyFlag.NONE);
+ string pretty_end_time = end_exact_time.to_pretty_time_string(Calendar.WallTime.PrettyFlag.NONE);
+
+ string timespan;
+ if (!include_timezone) {
+ // A span of time, i.e. "3:30pm to 4:30pm"
+ timespan = _("%s to %s").printf(pretty_start_time, pretty_end_time);
+ } else if (start_exact_time.tzid == end_exact_time.tzid) {
+ // A span of time followed by the timezone, i.e. "3:30pm to 4:30pm EST"
+ timespan = _("%s to %s %s").printf(pretty_start_time, pretty_end_time,
+ start_exact_time.tzid);
+ } else {
+ // A span of time with each timezone's indicated, i.e.
+ // "12:30AM EDT to 2:30PM EST"
+ timespan = _("%s %s to %s %s").printf(pretty_start_time, start_exact_time.tzid,
+ pretty_end_time, end_exact_time.tzid);
+ }
// Single-day timed event, print "<full date>, <full start time> to <full end time>",
// including year if not current year
- return "%s, %s".printf(start_date.to_pretty_string(date_flags), timespan);
+
+ // Date and time, i.e. "September 13, 4:30pm"
+ return _("%s, %s").printf(start_date.to_pretty_string(date_flags), timespan);
}
- if (allow_multiline) {
+ if (allow_multiline && !include_timezone) {
// Multi-day timed event, print "<full time>, <full date>" on both lines,
// including year if either not current year
// Prints two full time and date strings on separate lines, i.e.:
@@ -172,6 +192,32 @@ public class ExactTimeSpan : BaseObject, Gee.Comparable<ExactTimeSpan>, Gee.Hash
start_exact_time.to_pretty_time_string(Calendar.WallTime.PrettyFlag.NONE),
end_exact_time.to_pretty_date_string(date_flags),
end_exact_time.to_pretty_time_string(Calendar.WallTime.PrettyFlag.NONE));
+ } else if (allow_multiline && include_timezone) {
+ // Multi-day timed event, print "<full time>, <full date>" on both lines,
+ // including year if either not current year,
+ // *and* including timezone
+ // Prints two full time and date strings on separate lines, i.e.:
+ // 12 January 2012, 3:30pm PST
+ // 13 January 2013, 6:30am PST
+ return _("%s, %s %s\n%s, %s %s").printf(
+ start_exact_time.to_pretty_date_string(date_flags),
+ start_exact_time.to_pretty_time_string(Calendar.WallTime.PrettyFlag.NONE),
+ start_exact_time.tzid,
+ end_exact_time.to_pretty_date_string(date_flags),
+ end_exact_time.to_pretty_time_string(Calendar.WallTime.PrettyFlag.NONE),
+ end_exact_time.tzid);
+ }
+
+ if (include_timezone) {
+ // Prints full time and date strings on a single line with timezone, i.e.:
+ // 12 January 2012, 3:30pm PST to 13 January 2013, 6:30am PST
+ return _("%s, %s %s to %s, %s %s").printf(
+ start_exact_time.to_pretty_date_string(date_flags),
+ start_exact_time.to_pretty_time_string(Calendar.WallTime.PrettyFlag.NONE),
+ start_exact_time.tzid,
+ end_exact_time.to_pretty_date_string(date_flags),
+ end_exact_time.to_pretty_time_string(Calendar.WallTime.PrettyFlag.NONE),
+ end_exact_time.tzid);
}
// Prints full time and date strings on a single line, i.e.:
diff --git a/src/component/component-instance.vala b/src/component/component-instance.vala
index 2c534f2..b65602b 100644
--- a/src/component/component-instance.vala
+++ b/src/component/component-instance.vala
@@ -581,14 +581,15 @@ public abstract class Instance : BaseObject, Gee.Hashable<Instance> {
}
/**
- * Export this { link Instance}'s master as an iCalendar.
+ * Export this { link Instance}'s { link master} as an iCalendar.
+ *
+ * If this Instance is the master, this is functionally the same as { link export}.
*
- * @see export
* @see is_master
*/
public iCalendar export_master(iCal.icalproperty_method method) {
return new iCalendar(method, ICAL_PRODID, ICAL_VERSION, null,
- iterate<Instance>(master).to_array_list());
+ iterate<Instance>(master ?? this).to_array_list());
}
/**
diff --git a/src/component/component-recurrence-rule.vala b/src/component/component-recurrence-rule.vala
index abfffed..cd94c8b 100644
--- a/src/component/component-recurrence-rule.vala
+++ b/src/component/component-recurrence-rule.vala
@@ -589,6 +589,8 @@ public class RecurrenceRule : BaseObject {
/**
* Returns a natural-language string explaining the { link RecurrenceRule} for the user.
*
+ * The start_date should be the starting date of the associated { link Instance}.
+ *
* Returns null if the RRULE is beyond the comprehension of this parser.
*/
public string? explain(Calendar.Date start_date) {
diff --git a/src/host/host-create-update-event.vala b/src/host/host-create-update-event.vala
index 5cb62c5..27d9af5 100644
--- a/src/host/host-create-update-event.vala
+++ b/src/host/host-create-update-event.vala
@@ -480,11 +480,18 @@ public class CreateUpdateEvent : Gtk.Grid, Toolkit.Card {
argv += invitee.mailbox;
argv += "--subject";
- argv += String.is_empty(event.summary) ? _("Event invitation") : _("Invitation:
%s").printf(event.summary);
+ if (String.is_empty(event.summary)) {
+ argv += _("Event invitation");
+ } else if (String.is_empty(event.location)) {
+ argv += _("Invitation: %s").printf(event.summary);
+ } else {
+ // Invitation: <summary> at <location>
+ argv += _("Invitation: %s at %s").printf(event.summary, event.location);
+ }
// TODO: Generate a better body for the email (w/ event summary)
argv += "--body";
- argv += _("Attached is an invitation to a new event.");
+ argv += generate_invite_body(event);
argv += "--attach";
argv += temporary_filename;
@@ -498,6 +505,85 @@ public class CreateUpdateEvent : Gtk.Grid, Toolkit.Card {
_("Unable to launch mail client: %s").printf(err.message));
}
}
+
+ private static string generate_invite_body(Component.Event event) {
+ StringBuilder builder = new StringBuilder();
+
+ // Salutations for an email
+ append_line(builder, _("Hello,"));
+ append_line(builder);
+ append_line(builder, _("Attached is an invitation to a new event:"));
+ append_line(builder);
+
+ // Summary
+ if (!String.is_empty(event.summary))
+ append_line(builder, event.summary);
+
+ // Date/Time span
+ string? pretty_time = event.get_event_time_pretty_string(
+ Calendar.Date.PrettyFlag.NO_TODAY | Calendar.Date.PrettyFlag.INCLUDE_OTHER_YEAR,
+ Calendar.ExactTimeSpan.PrettyFlag.INCLUDE_TIMEZONE,
+ Calendar.Timezone.local
+ );
+ if (!String.is_empty(pretty_time)) {
+ // Date/time of an event
+ append_line(builder, _("When: %s").printf(pretty_time));
+ }
+
+ // Recurrences
+ if (event.rrule != null) {
+ string? rrule_explanation =
event.rrule.explain(event.get_event_date_span(Calendar.Timezone.local).start_date);
+ if (!String.is_empty(rrule_explanation))
+ append_line(builder, rrule_explanation);
+ }
+
+ // Location
+ if (!String.is_empty(event.location)) {
+ // Location of an event
+ append_line(builder, _("Where: %s").printf(event.location));
+ }
+
+ // Organizer (only list one)
+ Component.Person? organizer = null;
+ if (!event.organizers.is_empty) {
+ organizer = traverse<Component.Person>(event.organizers)
+ .sort()
+ .first();
+ // Who organized (scheduled or planned) the event
+ append_line(builder, _("Organizer: %s").printf(organizer.full_mailbox));
+ }
+
+ // Attendees (strip Organizer from list)
+ Gee.List<Component.Person> attendees = traverse<Component.Person>(event.attendees)
+ .filter(person => organizer == null || !person.equal_to(organizer))
+ .sort()
+ .to_array_list();
+ if (attendees.size > 0) {
+ // People attending event
+ append_line(builder, ngettext("Guest: %s", "Guests: %s", attendees.size).printf(
+ traverse<Component.Person>(attendees).to_string(stringify_people)));
+ }
+
+ // Description
+ if (!String.is_empty(event.description)) {
+ append_line(builder);
+ append_line(builder, event.description);
+ }
+
+ return builder.str;
+ }
+
+ private static void append_line(StringBuilder builder, string? str = null) {
+ if (!String.is_empty(str))
+ builder.append(str);
+
+ builder.append("\n");
+ }
+
+ private static string? stringify_people(Component.Person person, bool is_first, bool is_last) {
+ // Email separator, i.e. "alice example com, bob example com"
+ return !is_last ? _("%s, ").printf(person.full_mailbox) : person.full_mailbox;
+ }
}
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]