1.1 --- a/MoinShare.py Fri Jun 14 18:01:52 2013 +0200
1.2 +++ b/MoinShare.py Sat Jun 15 00:45:10 2013 +0200
1.3 @@ -115,6 +115,13 @@
1.4
1.5 self.page = None
1.6
1.7 + # Identification.
1.8 +
1.9 + self.path = []
1.10 +
1.11 + def unique_id(self):
1.12 + return "moinshare-tab-%s-%s" % (self.message_number, "-".join(map(str, self.path)))
1.13 +
1.14 def __cmp__(self, other):
1.15 if self.updated is None and other.updated is not None:
1.16 return 1
1.17 @@ -123,6 +130,20 @@
1.18 else:
1.19 return cmp(self.updated, other.updated)
1.20
1.21 + def copy(self, part_number=None):
1.22 + update = Update()
1.23 + update.title = self.title
1.24 + update.link = self.link
1.25 + update.updated = self.updated
1.26 + update.fragment = self.fragment
1.27 + update.preferred = self.preferred
1.28 + update.message_number = self.message_number
1.29 + update.page = self.page
1.30 + update.path = self.path[:]
1.31 + if part_number is not None:
1.32 + update.path.append(part_number)
1.33 + return update
1.34 +
1.35 # Update retrieval from pages.
1.36
1.37 def getUpdatesFromPage(page, request):
1.38 @@ -217,36 +238,56 @@
1.39 update.page = page
1.40 update.message_number = n
1.41
1.42 - # Determine whether the message has several representations.
1.43 -
1.44 - # For a single part, use it as the update content.
1.45 -
1.46 - if not message.is_multipart():
1.47 - charset = message.get_content_charset()
1.48 - payload = message.get_payload(decode=True)
1.49 - update.content = charset and unicode(payload, charset) or payload
1.50 - update.content_type = message.get_content_type()
1.51 -
1.52 - # For a collection of related parts, use the first as the update content
1.53 - # and assume that the formatter will reference the other parts.
1.54 -
1.55 - elif message.get_content_subtype() == "related":
1.56 - main_part = message.get_payload()[0]
1.57 - charset = main_part.get_content_charset()
1.58 - payload = main_part.get_payload(decode=True)
1.59 - update.content = charset and unicode(payload, charset) or payload
1.60 - update.content_type = main_part.get_content_type()
1.61 -
1.62 - # Otherwise, just obtain the parts for separate display.
1.63 -
1.64 - else:
1.65 - update.parts = message.get_payload()
1.66 - update.content_type = message.get_content_type()
1.67 + update.content, update.content_type, update.parts = getUpdateContentFromPart(message)
1.68
1.69 updates.append(update)
1.70
1.71 return updates
1.72
1.73 +def getUpdateContentFromPart(part):
1.74 +
1.75 + """
1.76 + Return decoded content, the content type and any subparts in a tuple for a
1.77 + given 'part'.
1.78 + """
1.79 +
1.80 + # Determine whether the part has several representations.
1.81 +
1.82 + # For a single part, use it as the update content.
1.83 +
1.84 + if not part.is_multipart():
1.85 + content, content_type = getPartContent(part)
1.86 + return content, content_type, None
1.87 +
1.88 + # For a collection of related parts, use the first as the update content
1.89 + # and assume that the formatter will reference the other parts.
1.90 +
1.91 + elif part.get_content_subtype() == "related":
1.92 + main_part = part.get_payload()[0]
1.93 + content, content_type = getPartContent(main_part)
1.94 + return content, content_type, [main_part]
1.95 +
1.96 + # Otherwise, just obtain the parts for separate display.
1.97 +
1.98 + else:
1.99 + return None, part.get_content_type(), part.get_payload()
1.100 +
1.101 +def getPartContent(part):
1.102 +
1.103 + "Decode the 'part', returning the decoded payload and the content type."
1.104 +
1.105 + charset = part.get_content_charset()
1.106 + payload = part.get_payload(decode=True)
1.107 + return (charset and unicode(payload, charset) or payload), part.get_content_type()
1.108 +
1.109 +def getUpdateFromPart(parent, part, part_number):
1.110 +
1.111 + "Using the 'parent' update, return an update object for the given 'part'."
1.112 +
1.113 + update = parent.copy(part_number)
1.114 + update.content, update.content_type, update.parts = getUpdateContentFromPart(part)
1.115 + return update
1.116 +
1.117 # Source management.
1.118
1.119 def getUpdateSources(pagename, request):
2.1 --- a/css/moinshare.css Fri Jun 14 18:01:52 2013 +0200
2.2 +++ b/css/moinshare.css Sat Jun 15 00:45:10 2013 +0200
2.3 @@ -14,9 +14,46 @@
2.4 margin: 1em;
2.5 border: 1px solid black;
2.6 padding: 0.5em;
2.7 + position: relative;
2.8 }
2.9
2.10 div.moinshare-date {
2.11 margin: 0.5em;
2.12 text-align: right;
2.13 }
2.14 +
2.15 +/* Put the alternative part navigation inside the update container, overlaying
2.16 + empty space belong to each of the parts. */
2.17 +
2.18 +div.moinshare-alternatives {
2.19 + position: absolute;
2.20 + bottom: 0;
2.21 + left: 0;
2.22 + padding: 0.5em;
2.23 +}
2.24 +
2.25 +div.moinshare-alternatives a {
2.26 + background: #eee;
2.27 + margin-right: 1em;
2.28 + padding: 0.5em;
2.29 +}
2.30 +
2.31 +div.moinshare-alternative {
2.32 + padding-bottom: 3em; /* empty space for the navigation */
2.33 +}
2.34 +
2.35 +/* Always show primary parts. */
2.36 +
2.37 +div.moinshare-alternative.moinshare-default {
2.38 + display: block;
2.39 +}
2.40 +
2.41 +/* Hide secondary parts unless selected. */
2.42 +
2.43 +div.moinshare-alternative.moinshare-other:not(:target) {
2.44 + display: none;
2.45 +}
2.46 +
2.47 +div.moinshare-alternative.moinshare-other:target {
2.48 + display: block;
2.49 +}
3.1 --- a/macros/SharedContent.py Fri Jun 14 18:01:52 2013 +0200
3.2 +++ b/macros/SharedContent.py Sat Jun 15 00:45:10 2013 +0200
3.3 @@ -11,7 +11,8 @@
3.4 from MoinRemoteSupport import *
3.5 from MoinSupport import parseMacroArguments, getParsersForContentType, formatText
3.6 from MoinShare import getUpdateSources, getUpdatesFromPage, \
3.7 - getUpdatesFromStore, Update, get_make_parser
3.8 + getUpdatesFromStore, getUpdateFromPart, \
3.9 + Update, get_make_parser
3.10 from email.utils import parsedate
3.11 import xml.dom.pulldom
3.12
3.13 @@ -206,6 +207,119 @@
3.14
3.15 return (feed_type, channel_title, channel_link), feed_updates
3.16
3.17 +# Update formatting.
3.18 +
3.19 +def getUpdatesForFormatting(update):
3.20 +
3.21 + "Get a list of updates for formatting given 'update'."
3.22 +
3.23 + updates = []
3.24 +
3.25 + # Handle multipart/alternative.
3.26 +
3.27 + if update.parts:
3.28 + for n, part in enumerate(update.parts):
3.29 + update_part = getUpdateFromPart(update, part, n)
3.30 + updates += getUpdatesForFormatting(update_part)
3.31 + else:
3.32 + updates.append(update)
3.33 +
3.34 + return updates
3.35 +
3.36 +def getFormattedUpdate(update, request, fmt):
3.37 +
3.38 + """
3.39 + Return the formatted form of the given 'update' using the given 'request'
3.40 + and 'fmt'.
3.41 + """
3.42 +
3.43 + # NOTE: Some control over the HTML and XHTML should be exercised.
3.44 +
3.45 + if update.content:
3.46 + if update.content_type == "text/html" and update.message_number is not None:
3.47 + parsers = [get_make_parser(update.page, update.message_number)]
3.48 + else:
3.49 + parsers = getParsersForContentType(request.cfg, update.content_type)
3.50 +
3.51 + if parsers:
3.52 + for parser_cls in parsers:
3.53 + return formatText(update.content, request, fmt, parser_cls=parser_cls)
3.54 + break
3.55 + else:
3.56 + return None
3.57 + else:
3.58 + return None
3.59 +
3.60 +def formatUpdate(update, request, fmt):
3.61 +
3.62 + "Format the given 'update' using the given 'request' and 'fmt'."
3.63 +
3.64 + result = []
3.65 + append = result.append
3.66 +
3.67 + updates = getUpdatesForFormatting(update)
3.68 + single = len(updates) == 1
3.69 +
3.70 + # Format some navigation tabs.
3.71 +
3.72 + if not single:
3.73 + append(fmt.div(on=1, css_class="moinshare-alternatives"))
3.74 +
3.75 + first = True
3.76 +
3.77 + for update_part in updates:
3.78 + append(fmt.url(1, "#%s" % update_part.unique_id()))
3.79 + append(fmt.text(update_part.content_type))
3.80 + append(fmt.url(0))
3.81 +
3.82 + first = False
3.83 +
3.84 + append(fmt.div(on=0))
3.85 +
3.86 + # Format the content.
3.87 +
3.88 + first = True
3.89 +
3.90 + for update_part in updates:
3.91 +
3.92 + # Encapsulate each alternative if many exist.
3.93 +
3.94 + if not single:
3.95 + css_class = first and "moinshare-default" or "moinshare-other"
3.96 + append(fmt.div(on=1, css_class="moinshare-alternative %s" % css_class, id=update_part.unique_id()))
3.97 +
3.98 + # Include the content.
3.99 +
3.100 + append(formatUpdatePart(update_part, request, fmt))
3.101 +
3.102 + if not single:
3.103 + append(fmt.div(on=0))
3.104 +
3.105 + first = False
3.106 +
3.107 + return "".join(result)
3.108 +
3.109 +def formatUpdatePart(update, request, fmt):
3.110 +
3.111 + "Format the given 'update' using the given 'request' and 'fmt'."
3.112 +
3.113 + _ = request.getText
3.114 +
3.115 + result = []
3.116 + append = result.append
3.117 +
3.118 + # Encapsulate the content.
3.119 +
3.120 + append(fmt.div(on=1, css_class="moinshare-content"))
3.121 + text = getFormattedUpdate(update, request, fmt)
3.122 + if text:
3.123 + append(text)
3.124 + else:
3.125 + append(fmt.text(_("Update cannot be shown for content of type %s.") % update.content_type))
3.126 + append(fmt.div(on=0))
3.127 +
3.128 + return "".join(result)
3.129 +
3.130 # The macro itself.
3.131
3.132 def execute(macro, args):
3.133 @@ -326,31 +440,16 @@
3.134 for update in updates:
3.135
3.136 # Emit content where appropriate.
3.137 - # NOTE: Some control over the HTML and XHTML should be exercised.
3.138
3.139 if show_content:
3.140 append(fmt.div(on=1, css_class="moinshare-update"))
3.141 - append(fmt.div(on=1, css_class="moinshare-content"))
3.142 -
3.143 - # NOTE: Handle multipart/alternative.
3.144
3.145 - if update.content:
3.146 - if update.content_type == "text/html" and update.message_number is not None:
3.147 - parsers = [get_make_parser(update.page, update.message_number)]
3.148 - else:
3.149 - parsers = getParsersForContentType(request.cfg, update.content_type)
3.150 + append(formatUpdate(update, request, fmt))
3.151
3.152 - if parsers:
3.153 - for parser_cls in parsers:
3.154 - append(formatText(update.content, request, fmt, parser_cls=parser_cls))
3.155 - break
3.156 - else:
3.157 - append(fmt.text(_("Update cannot be shown for content of type %s.") % update.content_type))
3.158 -
3.159 - append(fmt.div(on=0))
3.160 append(fmt.div(on=1, css_class="moinshare-date"))
3.161 append(fmt.text(str(update.updated)))
3.162 append(fmt.div(on=0))
3.163 +
3.164 append(fmt.div(on=0))
3.165
3.166 # Or emit title and link information for items.