diff --git a/content/messagecontent.js b/content/messagecontent.js index 7b0c10a..e48120c 100644 --- a/content/messagecontent.js +++ b/content/messagecontent.js @@ -61,12 +61,12 @@ var messageContent = { title: null, message: null, - _attributes: null, - get attributes() { - if (this._attributes) - return this._attributes; + _headers: null, + get headers() { + if (this._headers) + return this._headers; - return this._attributes = this.message.attributes; + return this._headers = this.message.headers; }, init: function() { @@ -97,7 +97,7 @@ var messageContent = { }, createTitle: function() { - this.title = this.message ? this.message.subject : + this.title = this.message ? this.message.subject || this.message.excerpt : strings.get("messageNotFoundTitle", [this.id]); top.document.title = this.title; }, @@ -116,7 +116,7 @@ var messageContent = { if (message) { // Brief headers var subjectLink = document.getElementById("subject"); - subjectLink.appendChild(document.createTextNode(message.subject)); + subjectLink.appendChild(document.createTextNode(message.subject || message.excerpt)); if (message.link) { SnowlUtils.safelySetURIAttribute(subjectLink, "href", @@ -151,14 +151,17 @@ var messageContent = { }, createFullHeader: function(headerDeck) { -//window.SnowlUtils._log.info("createHeader: attributes - "+this.attributes.toSource()); - // Iterate through message attributes object and create full header. +//window.SnowlUtils._log.info("createHeader: headers - "+this.headers.toSource()); + if (!this.headers) + return; + + // Iterate through message headers object and create full header. var name, value, headerRow, headerRowLabel, headerRowData; var fullHeaderTable = headerDeck.parentNode.getElementsByClassName("fullHeaderTable")[0]; if (fullHeaderTable.className != "fullHeaderTable") return; - for ([name, value] in Iterator(this.attributes)) { + for ([name, value] in Iterator(this.headers)) { headerRow = document.createElementNS(HTML_NS, "tr"); headerRow.className = "fullHeaderRow"; headerRowLabel = document.createElementNS(HTML_NS, "td"); @@ -322,7 +325,7 @@ var messageHeaderUtils = { // XXX: set index to 1, as full header removed for now (createFullHeader // will not run, nor will button toggle to full). headerDeck = document.getElementById("headerDeck"); - headerIndex = ++headerIndex > 1 ? 0 : headerIndex++; + headerIndex = ++headerIndex > 2 ? 0 : headerIndex++; headerBcaster.setAttribute("headerIndex", headerIndex); } @@ -334,7 +337,7 @@ var messageHeaderUtils = { // The message is found in the scope of the parent frameset document. var messageContent = parent.wrappedJSObject.messageContent; - if (headerIndex == 2 && !messageContent._attributes) + if (headerIndex == 2 && !messageContent._headers) messageContent.createFullHeader(headerDeck); }, diff --git a/modules/collection.js b/modules/collection.js index d21330d..efeaa90 100644 --- a/modules/collection.js +++ b/modules/collection.js @@ -279,6 +279,7 @@ this._log.info("got " + groups.length + " groups"); link: statement.row.link ? URI.get(statement.row.link) : null, current: statement.row.current, read: statement.row.read, + headers: JSON.parse(statement.row.headers), content: content }); @@ -318,6 +319,7 @@ this._log.info("got " + groups.length + " groups"); "messages.link", "messages.current", "messages.read", + "messages.headers", "identities.id AS identities_id", "identities.sourceID AS identities_sourceID", "identities.externalID AS identities_externalID", diff --git a/modules/collection2.js b/modules/collection2.js index 7ba64d9..dbe2e9a 100644 --- a/modules/collection2.js +++ b/modules/collection2.js @@ -176,6 +176,7 @@ StorageCollection.prototype = { "messages.link", "messages.current", "messages.read", + "messages.headers", "identities.id AS identities_id", "identities.sourceID AS identities_sourceID", "identities.externalID AS identities_externalID", @@ -295,6 +296,7 @@ StorageCollection.prototype = { received: SnowlDateUtils.julianToJSDate(row.getResultByName("received")), read: row.getResultByName("read") ? true : false, current: row.getResultByName("current"), + headers: JSON.parse(row.getResultByName("headers")), content: content, summary: summary }); diff --git a/modules/feed.js b/modules/feed.js index 6486797..44315b9 100644 --- a/modules/feed.js +++ b/modules/feed.js @@ -458,7 +458,101 @@ SnowlFeed.prototype = { }); } - return message; + // Add headers. + message.headers = {}; + let fields = aEntry.QueryInterface(Ci.nsIFeedContainer). + fields.QueryInterface(Ci.nsIPropertyBag).enumerator; + while (fields.hasMoreElements()) { + let field = fields.getNext().QueryInterface(Ci.nsIProperty); + + // FIXME: create people records for these. + if (field.name == "authors") { + let count = 1; + let values = field.value.QueryInterface(Ci.nsIArray).enumerate(); + while (values.hasMoreElements()) { + let value = values.getNext().QueryInterface(Ci.nsIFeedPerson); + // FIXME: store people records in a separate table with individual + // columns for each person attribute (i.e. name, email, url)? + if (value.name) + message.headers["atom:author" + (count == 1 ? "" : count) + "_name"] = value.name; + if (value.email) + message.headers["atom:author" + (count == 1 ? "" : count) + "_email"] = value.email; + if (value.uri && value.uri.spec) + message.headers["atom:author" + (count == 1 ? "" : count) + "_uri"] = value.uri.spec; + count++; +//this._log.info("header-authors: name:value - "+field.name+" : "+value.toSource()); + } + } + + else if (field.name == "links") { + let rel, count = 1; + let values = field.value.QueryInterface(Ci.nsIArray).enumerate(); + while (values.hasMoreElements()) { + let value = values.getNext().QueryInterface(Ci.nsIPropertyBag2); + // FIXME: store link records in a separate table with individual + // colums for each link attribute (i.e. href, type, rel, title)? + rel = value.get("rel") ? value.get("rel") : ""; + message.headers["atom:link" + count + (rel ? "_" + rel : rel)] = value.get("href"); + if (value.get("title")) + message.headers["atom:link" + count + "_title"] = value.get("title"); + if (value.get("type")) + message.headers["atom:link" + count + "_type"] = value.get("type"); + if (value.get("hreflang")) + message.headers["atom:link" + count + "_hreflang"] = value.get("hreflang"); + if (value.get("length")) + message.headers["atom:link" + count + "_length"] = value.get("length"); + count++; +//this._log.info("header-links: name:value - "+"atom:link_" + value.get('rel')+" : "+value.get("href")); + } + } + + else if (field.name == "categories") { + // Categories don't work, Bug 493175. + let count = 1; + let values = field.value.QueryInterface(Ci.nsIArray).enumerate(); + while (values.hasMoreElements()) { + let value = values.getNext().QueryInterface(Ci.nsIPropertyBag2); + if (value.term) + message.headers["category" + count + "_term"] = value.term; + if (value.scheme) + message.headers["category" + count + "_scheme"] = value.scheme; + if (value.label) + message.headers["category" + count + "_label"] = value.label; + count++; +//this._log.info("header-categories: name:value - "+field.name+" : "+value.toSource()); + } + } + + // For some reason, the values of certain simple fields (like RSS2 guid) + // are property bags containing the value instead of the value itself. + // For those, we need to unwrap the extra layer. This strange behavior + // has been filed as bug 427907. + else if (typeof field.value == "object") { + if (field.value instanceof Ci.nsIPropertyBag2) { + let value = field.value.QueryInterface(Ci.nsIPropertyBag2).get(field.name); + message.headers[field.name] = value; +//this._log.info("header-nsIPropertyBag2: name:value - "+field.name+" : "+value); + } + else if (field.value instanceof Ci.nsIArray) { + let values = field.value.QueryInterface(Ci.nsIArray).enumerate(); + while (values.hasMoreElements()) { + // FIXME: values might not always have this interface. + let value = values.getNext().QueryInterface(Ci.nsIPropertyBag2); + message.headers[field.name] = value.get(field.name); +//this._log.info("header-nsIArray: name:value - "+field.name+" : "+value); + } + } + } + + else { + message.headers[field.name] = field.value.substring(0, 500) + + (field.value.length > 500 ? " [...]" : ""); +//this._log.info("header: name:value - "+field.name+" : "+message.headers[field.name]); + } + } + +//this._log.info("headers: end - "+message.headers.toSource()); + return message; }, /** diff --git a/modules/message.js b/modules/message.js index 386da95..b17e4c6 100644 --- a/modules/message.js +++ b/modules/message.js @@ -69,7 +69,7 @@ SnowlMessage.retrieve = function(id) { // FIXME: memoize this. let statement = SnowlDatastore.createStatement( "SELECT sourceID, externalID, subject, authorID, " + - " timestamp, received, link, current, read " + + " timestamp, received, link, current, read, headers " + "FROM messages " + "WHERE messages.id = :id" ); @@ -86,7 +86,8 @@ SnowlMessage.retrieve = function(id) { received: SnowlDateUtils.julianToJSDate(statement.row.received), link: statement.row.link ? URI.get(statement.row.link) : null, current: statement.row.current, - read: statement.row.read + read: statement.row.read, + headers: JSON.parse(statement.row.headers) }); if (statement.row.authorID) @@ -198,6 +199,8 @@ SnowlMessage.prototype = { current: null, summary: null, content: null, + headers: {}, + attributes: {}, get excerpt() { let construct = this.content || this.summary; @@ -247,9 +250,11 @@ SnowlMessage.prototype = { // FIXME: persist message.current. let statement = SnowlDatastore.createStatement( "INSERT INTO messages " + - "( sourceID, externalID, subject, authorID, timestamp, received, link, " + /*current, */ " read) " + + "( sourceID, externalID, subject, authorID, timestamp, received, link, " + + /*current, */ " read, headers ) " + "VALUES " + - "(:sourceID, :externalID, :subject, :authorID, :timestamp, :received, :link, " + /*:current, */ ":read)" + "(:sourceID, :externalID, :subject, :authorID, :timestamp, :received, :link, " + + /*:current, */ ":read, :headers )" ); this.__defineGetter__("_insertMessageStmt", function() statement); return this._insertMessageStmt; @@ -266,7 +271,8 @@ SnowlMessage.prototype = { "link = :link, " + // FIXME: persist message.current. //"current = :current, " + - "read = :read " + + "read = :read, " + + "headers = :headers " + "WHERE id = :id" ); this.__defineGetter__("_updateMessageStmt", function() statement); @@ -333,6 +339,7 @@ SnowlMessage.prototype = { // FIXME: persist message.current. //statement.params.current = this.current; statement.params.read = this.read; + statement.params.headers = JSON.stringify(this.headers); statement.execute(); diff --git a/modules/twitter.js b/modules/twitter.js index b74c240..9e477ff 100644 --- a/modules/twitter.js +++ b/modules/twitter.js @@ -436,8 +436,14 @@ SnowlTwitter.prototype = { message.externalID = item.id; message.timestamp = new Date(item.created_at); message.received = received || new Date(); - message.author = new SnowlIdentity(null, this.id, item.user.id); - message.author.person = new SnowlPerson(null, item.user.screen_name, null, item.user.url, item.user.profile_image_url); + message.author = new SnowlIdentity(null, + this.id, + item.user.id); + message.author.person = new SnowlPerson(null, + item.user.screen_name, + null, + item.user.url, + item.user.profile_image_url); message.content = new SnowlMessagePart({ @@ -446,6 +452,18 @@ SnowlTwitter.prototype = { mediaType: "text/plain" }); + // Add headers. + message.headers = {}; + for (let [name, value] in Iterator(item)) { + // FIXME: populate a "recipient" field with in_reply_to_user_id. + if (name == "user") { + for (let [uname, uvalue] in Iterator(value)) + message.headers["user:" + uname] = uvalue; + } + else + message.headers[name] = value; + } + return message; },