1.1 --- a/imiptools/handlers/__init__.py Sun Oct 23 23:21:01 2016 +0200
1.2 +++ b/imiptools/handlers/__init__.py Mon Dec 19 23:39:20 2016 +0100
1.3 @@ -74,12 +74,20 @@
1.4
1.5 texts = []
1.6 texts.append(text)
1.7 +
1.8 + # Add a link to the manager application if available and requested.
1.9 +
1.10 if link and self.have_manager():
1.11 texts.append(_("If your mail program cannot handle this "
1.12 - "message, you may view the details here:\n\n%s") %
1.13 + "message, you may view the details here:\n\n%s\n") %
1.14 get_object_url(self.uid, self.recurrenceid))
1.15
1.16 - return self.add_result(None, None, MIMEText("\n".join(texts)))
1.17 + # Create the text part, tagging it with a header that allows this part
1.18 + # to be merged with other calendar information.
1.19 +
1.20 + text_part = MIMEText("\n\n".join(texts))
1.21 + text_part["X-IMIP-Agent"] = "info"
1.22 + return self.add_result(None, None, text_part)
1.23
1.24 # Result registration.
1.25
2.1 --- a/imiptools/mail.py Sun Oct 23 23:21:01 2016 +0200
2.2 +++ b/imiptools/mail.py Mon Dec 19 23:39:20 2016 +0100
2.3 @@ -3,7 +3,7 @@
2.4 """
2.5 Mail preparation support.
2.6
2.7 -Copyright (C) 2014, 2015 Paul Boddie <paul@boddie.org.uk>
2.8 +Copyright (C) 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
2.9
2.10 This program is free software; you can redistribute it and/or modify it under
2.11 the terms of the GNU General Public License as published by the Free Software
2.12 @@ -128,41 +128,104 @@
2.13
2.14 """
2.15 Return a simple summary using details from 'msg' and the given 'parts'.
2.16 + Information messages provided amongst the parts by the handlers will be
2.17 + merged into the preamble so that mail programs will show them
2.18 + immediately.
2.19 """
2.20
2.21 - message = self._make_summary_for_parts(parts)
2.22 + message = self._make_summary_for_parts(parts, True)
2.23 self._copy_headers(message, msg)
2.24 return message
2.25
2.26 def wrap_message(self, msg, parts):
2.27
2.28 - "Wrap 'msg' and provide the given 'parts' as the primary content."
2.29 + """
2.30 + Wrap 'msg' and provide the given 'parts' as the primary content.
2.31 + Information messages provided amongst the parts by the handlers will be
2.32 + merged into the preamble so that mail programs will show them
2.33 + immediately.
2.34 + """
2.35
2.36 - message = self._make_container_for_parts(parts)
2.37 + message = self._make_container_for_parts(parts, True)
2.38 payload = message.get_payload()
2.39 payload.append(MIMEMessage(msg))
2.40 self._copy_headers(message, msg)
2.41 return message
2.42
2.43 - def _make_summary_for_parts(self, parts):
2.44 + def _make_summary_for_parts(self, parts, merge=False):
2.45
2.46 - "Return a simple summary for the given 'parts'."
2.47 + """
2.48 + Return a simple summary for the given 'parts', merging information parts if
2.49 + 'merge' is specified and set to a true value.
2.50 + """
2.51
2.52 if len(parts) == 1:
2.53 return parts[0]
2.54 else:
2.55 - return self._make_container_for_parts(parts)
2.56 + return self._make_container_for_parts(parts, merge)
2.57 +
2.58 + def _make_container_for_parts(self, parts, merge=False):
2.59 +
2.60 + """
2.61 + Return a container for the given 'parts', merging information parts if
2.62 + 'merge' is specified and set to a true value.
2.63 + """
2.64
2.65 - def _make_container_for_parts(self, parts):
2.66 + # Merge calendar information if requested.
2.67
2.68 - "Return a container for the given 'parts'."
2.69 + if merge:
2.70 + info, parts = self._merge_calendar_info_parts(parts)
2.71 + else:
2.72 + info = []
2.73 +
2.74 + # Insert a preamble message before any calendar information messages.
2.75 +
2.76 + info.insert(0, self.preamble_text or
2.77 + self.gettext and self.gettext(PREAMBLE_TEXT) or PREAMBLE_TEXT)
2.78
2.79 message = MIMEMultipart("mixed", _subparts=parts)
2.80 - message.preamble = self.preamble_text or \
2.81 - self.gettext and self.gettext(PREAMBLE_TEXT) or PREAMBLE_TEXT
2.82 + message.preamble = "\n\n".join(info)
2.83 return message
2.84
2.85 + def _merge_calendar_info_parts(self, parts):
2.86 +
2.87 + """
2.88 + Return a collection of plain text calendar information messages from
2.89 + 'parts', together with a collection of the remaining parts.
2.90 + """
2.91 +
2.92 + info = []
2.93 + remaining = []
2.94 +
2.95 + for part in parts:
2.96 +
2.97 + # Attempt to acquire informational messages.
2.98 +
2.99 + if part.get("X-IMIP-Agent") == "info":
2.100 +
2.101 + # Ignore the preamble of any multipart message and just
2.102 + # collect its parts.
2.103 +
2.104 + if part.is_multipart():
2.105 + i, r = self._merge_calendar_info_parts(part.get_payload())
2.106 + remaining += r
2.107 +
2.108 + # Obtain any single-part messages.
2.109 +
2.110 + else:
2.111 + info.append(part.get_payload(decode=True))
2.112 +
2.113 + # Accumulate other parts regardless of their purpose.
2.114 +
2.115 + else:
2.116 + remaining.append(part)
2.117 +
2.118 + return info, remaining
2.119 +
2.120 def _copy_headers(self, message, msg):
2.121 +
2.122 + "Copy to 'message' certain headers from 'msg'."
2.123 +
2.124 message["From"] = msg["From"]
2.125 message["To"] = msg["To"]
2.126 message["Subject"] = msg["Subject"]