summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2019-11-11 01:04:46 -0500
committerMatt A. Tobin <email@mattatobin.com>2019-11-11 01:04:46 -0500
commit0707a51eaddec22ab760e27050e2fcefab2cdae5 (patch)
tree03577a3ee56df77cff3e8dee281b196bde8dc95b
parent0903ef35611c8d2a966f057d2e74437a73de71df (diff)
downloadUXP-0707a51eaddec22ab760e27050e2fcefab2cdae5.tar
UXP-0707a51eaddec22ab760e27050e2fcefab2cdae5.tar.gz
UXP-0707a51eaddec22ab760e27050e2fcefab2cdae5.tar.lz
UXP-0707a51eaddec22ab760e27050e2fcefab2cdae5.tar.xz
UXP-0707a51eaddec22ab760e27050e2fcefab2cdae5.zip
Bug 1423487 - Support multiple authors in RSS feeds.
Tag #1273
-rw-r--r--mailnews/base/src/nsMsgDBView.cpp14
-rw-r--r--mailnews/extensions/newsblog/content/FeedItem.js85
-rw-r--r--mailnews/extensions/newsblog/content/FeedUtils.jsm1
-rw-r--r--mailnews/extensions/newsblog/content/feed-parser.js84
-rw-r--r--mailnews/extensions/newsblog/content/feed-subscriptions.xul5
5 files changed, 133 insertions, 56 deletions
diff --git a/mailnews/base/src/nsMsgDBView.cpp b/mailnews/base/src/nsMsgDBView.cpp
index 76a843df7..a1867244b 100644
--- a/mailnews/base/src/nsMsgDBView.cpp
+++ b/mailnews/base/src/nsMsgDBView.cpp
@@ -400,10 +400,12 @@ nsresult nsMsgDBView::FetchAuthor(nsIMsgDBHdr * aHdr, nsAString &aSenderString)
nsCString headerCharset;
aHdr->GetEffectiveCharset(headerCharset);
- nsCString emailAddress;
nsString name;
- ExtractFirstAddress(EncodedHeader(author, headerCharset.get()), name,
- emailAddress);
+ nsCString emailAddress;
+ nsCOMArray<msgIAddressObject> addresses = EncodedHeader(author, headerCharset.get());
+ bool multipleAuthors = addresses.Length() > 1;
+
+ ExtractFirstAddress(addresses, name, emailAddress);
if (showCondensedAddresses)
GetDisplayNameInAddressBook(emailAddress, aSenderString);
@@ -429,6 +431,12 @@ nsresult nsMsgDBView::FetchAuthor(nsIMsgDBHdr * aHdr, nsAString &aSenderString)
}
}
+ if (multipleAuthors)
+ {
+ aSenderString.AppendLiteral(" ");
+ aSenderString.Append(GetString(u"andOthers"));
+ }
+
UpdateCachedName(aHdr, "sender_name", aSenderString);
return NS_OK;
diff --git a/mailnews/extensions/newsblog/content/FeedItem.js b/mailnews/extensions/newsblog/content/FeedItem.js
index 09e4eb861..5863c420a 100644
--- a/mailnews/extensions/newsblog/content/FeedItem.js
+++ b/mailnews/extensions/newsblog/content/FeedItem.js
@@ -22,7 +22,10 @@ FeedItem.prototype =
content: null,
enclosures: [],
title: null,
- author: "anonymous",
+ // Author must be angle bracket enclosed to function as an addr-spec, in the
+ // absence of an addr-spec portion of an RFC5322 email address, as other
+ // functionality (gloda search) depends on this.
+ author: "<anonymous>",
inReplyTo: "",
keywords: [],
mURL: null,
@@ -245,12 +248,10 @@ FeedItem.prototype =
{
FeedUtils.log.trace("FeedItem.writeToFolder: " + this.identity +
" writing to message folder " + this.feed.name);
- // Convert the title to UTF-16 before performing our HTML entity
- // replacement reg expressions.
- let title = this.title;
// The subject may contain HTML entities. Convert these to their unencoded
// state. i.e. &amp; becomes '&'.
+ let title = this.title;
title = this.mParserUtils.convertToPlainText(
title,
Ci.nsIDocumentEncoder.OutputSelectionOnly |
@@ -274,32 +275,11 @@ FeedItem.prototype =
("References: " + this.inReplyTo + "\n" +
"In-Reply-To: " + this.inReplyTo + "\n") : "";
- // If there are keywords (categories), create the headers. In the case of
- // a longer than RFC5322 recommended line length, create multiple folded
- // lines (easier to parse than multiple Keywords headers).
- let keywordsStr = "";
- if (this.keywords.length)
- {
- let HEADER = "Keywords: ";
- let MAXLEN = 78;
- keywordsStr = HEADER;
- let keyword;
- let keywords = [].concat(this.keywords);
- let lines = [];
- while (keywords.length)
- {
- keyword = keywords.shift();
- if (keywordsStr.length + keyword.length > MAXLEN)
- {
- lines.push(keywordsStr)
- keywordsStr = " ".repeat(HEADER.length);
- }
- keywordsStr += keyword + ",";
- }
- keywordsStr = keywordsStr.replace(/,$/,"\n");
- lines.push(keywordsStr)
- keywordsStr = lines.join("\n");
- }
+ // Support multiple authors in From.
+ let fromStr = this.createHeaderStrFromArray("From: ", this.author);
+
+ // If there are keywords (categories), create the headers.
+ let keywordsStr = this.createHeaderStrFromArray("Keywords: ", this.keywords);
// Escape occurrences of "From " at the beginning of lines of
// content per the mbox standard, since "From " denotes a new
@@ -323,7 +303,7 @@ FeedItem.prototype =
'Received: by localhost; ' + FeedUtils.getValidRFC5322Date() + '\n' +
'Date: ' + this.mDate + '\n' +
'Message-Id: ' + this.normalizeMessageID(this.id) + '\n' +
- 'From: ' + this.author + '\n' +
+ fromStr +
'MIME-Version: 1.0\n' +
'Subject: ' + this.title + '\n' +
inreplytoHdrsStr +
@@ -369,6 +349,49 @@ FeedItem.prototype =
},
/**
+ * Create a header string from an array. Intended for comma separated headers
+ * like From or Keywords. In the case of a longer than RFC5322 recommended
+ * line length, create multiple folded lines (easier to parse than multiple
+ * headers).
+ *
+ * @param {String} headerName - Name of the header.
+ * @param {String[]} headerItemsArray - An Array of strings to concatenate.
+ *
+ * @returns {String} - The header string.
+ */
+ createHeaderStrFromArray(headerName, headerItemsArray) {
+ let headerStr = "";
+ if (!headerItemsArray || headerItemsArray.length == 0) {
+ return headerStr;
+ }
+
+ const HEADER = headerName;
+ const LINELENGTH = 78;
+ const MAXLINELENGTH = 990;
+ let items = [].concat(headerItemsArray);
+ let lines = [];
+ headerStr = HEADER;
+ while (items.length) {
+ let item = items.shift();
+ if (headerStr.length + item.length > LINELENGTH &&
+ headerStr.length > HEADER.length) {
+ lines.push(headerStr);
+ headerStr = " ".repeat(HEADER.length);
+ }
+
+ headerStr += headerStr.length + item.length > MAXLINELENGTH ?
+ item.substr(0, MAXLINELENGTH - headerStr.length) + "…, " :
+ item + ", ";
+ }
+
+ headerStr = headerStr.replace(/,\s$/, "\n");
+ lines.push(headerStr);
+ headerStr = lines.join("\n");
+
+ return headerStr;
+ },
+
+/**
* Autotag messages.
*
* @param nsIMsgDBHdr aMsgDBHdr - message to tag
diff --git a/mailnews/extensions/newsblog/content/FeedUtils.jsm b/mailnews/extensions/newsblog/content/FeedUtils.jsm
index 6d5e64dd2..f8368ac25 100644
--- a/mailnews/extensions/newsblog/content/FeedUtils.jsm
+++ b/mailnews/extensions/newsblog/content/FeedUtils.jsm
@@ -42,6 +42,7 @@ var FeedUtils = {
},
DC_NS: "http://purl.org/dc/elements/1.1/",
+ get DC_PUBLISHER() { return this.rdf.GetResource(this.DC_NS + "publisher"); },
get DC_CREATOR() { return this.rdf.GetResource(this.DC_NS + "creator") },
get DC_SUBJECT() { return this.rdf.GetResource(this.DC_NS + "subject") },
get DC_DATE() { return this.rdf.GetResource(this.DC_NS + "date") },
diff --git a/mailnews/extensions/newsblog/content/feed-parser.js b/mailnews/extensions/newsblog/content/feed-parser.js
index 660333422..03fc72b22 100644
--- a/mailnews/extensions/newsblog/content/feed-parser.js
+++ b/mailnews/extensions/newsblog/content/feed-parser.js
@@ -222,9 +222,10 @@ FeedParser.prototype =
tags = this.childrenByTagNameNS(itemNode, nsURI, "author");
if (!tags)
tags = this.childrenByTagNameNS(itemNode, FeedUtils.DC_NS, "creator");
- item.author = this.getNodeValue(tags ? tags[0] : null) ||
- aFeed.title ||
- item.author;
+ let author = this.getNodeValue(tags ? tags[0] : null) ||
+ aFeed.title;
+ author = this.cleanAuthorName(author);
+ item.author = author ? ["<" + author + ">"] : item.author;
tags = this.childrenByTagNameNS(itemNode, nsURI, "pubDate");
if (!tags || !this.getNodeValue(tags[0]))
@@ -386,10 +387,12 @@ FeedParser.prototype =
item.id = item.url;
item.url = this.validLink(item.url);
- item.author = this.getRDFTargetValue(ds, itemResource, FeedUtils.DC_CREATOR) ||
- this.getRDFTargetValue(ds, channel, FeedUtils.DC_CREATOR) ||
- aFeed.title ||
- item.author;
+ let author = this.getRDFTargetValue(ds, itemResource, FeedUtils.DC_CREATOR) ||
+ this.getRDFTargetValue(ds, channel, FeedUtils.DC_CREATOR) ||
+ aFeed.title;
+ author = this.cleanAuthorName(author);
+ item.author = author ? ["<" + author + ">"] : item.author;
+
item.date = this.getRDFTargetValue(ds, itemResource, FeedUtils.DC_DATE) ||
item.date;
item.content = this.getRDFTargetValueFormatted(ds, itemResource,
@@ -608,7 +611,7 @@ FeedParser.prototype =
continue;
}
- // XXX Support multiple authors.
+ // Support multiple authors.
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "source");
let source = tags ? tags[0] : null;
@@ -618,22 +621,42 @@ FeedParser.prototype =
if (!tags)
tags = this.childrenByTagNameNS(channel, FeedUtils.ATOM_IETF_NS, "author");
- let authorEl = tags ? tags[0] : null;
-
- let author = "";
- if (authorEl)
- {
- tags = this.childrenByTagNameNS(authorEl, FeedUtils.ATOM_IETF_NS, "name");
+ let authorTags = tags || [];
+ let authors = [];
+ for (let authorTag of authorTags) {
+ let author = "";
+ tags = this.childrenByTagNameNS(authorTag, FeedUtils.ATOM_IETF_NS, "name");
let name = this.getNodeValue(tags ? tags[0] : null);
- tags = this.childrenByTagNameNS(authorEl, FeedUtils.ATOM_IETF_NS, "email");
+ tags = this.childrenByTagNameNS(authorTag, FeedUtils.ATOM_IETF_NS, "email");
let email = this.getNodeValue(tags ? tags[0] : null);
- if (name)
- author = name + (email ? " <" + email + ">" : "");
- else if (email)
+ if (name) {
+ name = this.cleanAuthorName(name);
+ if (email) {
+ if (!email.match(/^<.*>$/)) {
+ email = " <" + email + ">";
+ }
+ author = name + email;
+ } else {
+ author = "<" + name + ">";
+ }
+ } else if (email) {
author = email;
+ }
+ if (author) {
+ authors.push(author);
+ }
}
- item.author = author || item.author || aFeed.title;
+ if (authors.length == 0) {
+ tags = this.childrenByTagNameNS(channel, FeedUtils.DC_NS, "publisher");
+ let author = this.getNodeValue(tags ? tags[0] : null) ||
+ aFeed.title;
+ author = this.cleanAuthorName(author);
+ item.author = author ? ["<" + author + ">"] : item.author;
+ } else {
+ item.author = authors;
+ }
+ FeedUtils.log.trace("FeedParser.parseAsAtomIETF: author(s) - " + item.author);
tags = this.childrenByTagNameNS(itemNode, FeedUtils.ATOM_IETF_NS, "updated");
if (!tags || !this.getNodeValue(tags[0]))
@@ -801,6 +824,29 @@ FeedParser.prototype =
return content ? content : null;
},
+ /**
+ * Return a cleaned up author name value.
+ *
+ * @param {String} authorString - A string.
+ * @returns {String} - A clean string value.
+ */
+ cleanAuthorName(authorString) {
+ if (!authorString) {
+ return "";
+ }
+ FeedUtils.log.trace("FeedParser.cleanAuthor: author1 - " + authorString);
+ let author = authorString.replace(/[\n\r\t]+/g, " ")
+ .replace(/"/g, '\\"')
+ .trim();
+ // If the name contains special chars, quote it.
+ if (author.match(/[<>@,"]/)) {
+ author = '"' + author + '"';
+ }
+ FeedUtils.log.trace("FeedParser.cleanAuthor: author2 - " + author);
+
+ return author;
+ },
+
getRDFTargetValue: function(ds, source, property)
{
let nodeValue = this.getRDFTargetValueRaw(ds, source, property);
diff --git a/mailnews/extensions/newsblog/content/feed-subscriptions.xul b/mailnews/extensions/newsblog/content/feed-subscriptions.xul
index d6f4ea18f..d8dff29ee 100644
--- a/mailnews/extensions/newsblog/content/feed-subscriptions.xul
+++ b/mailnews/extensions/newsblog/content/feed-subscriptions.xul
@@ -17,12 +17,11 @@
]>
<window id="subscriptionsDialog"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
flex="1"
title="&feedSubscriptions.label;"
windowtype="Mail:News-BlogSubscriptions"
- xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:nc="http://home.netscape.com/NC-rdf#"
persist="width height screenX screenY sizemode"
onload="FeedSubscriptions.onLoad();"
onclose="return FeedSubscriptions.onClose();"