mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-28 11:27:32 +09:00
Issue #1258 - Part 1: Import mailnews, ldap, and mork from comm-esr52.9.1
This commit is contained in:
parent
23e0d82436
commit
e400f4130a
1564 changed files with 510348 additions and 0 deletions
620
mailnews/extensions/newsblog/content/Feed.js
Normal file
620
mailnews/extensions/newsblog/content/Feed.js
Normal file
|
|
@ -0,0 +1,620 @@
|
|||
/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Cache for all of the feeds currently being downloaded, indexed by URL,
|
||||
// so the load event listener can access the Feed objects after it finishes
|
||||
// downloading the feed.
|
||||
var FeedCache =
|
||||
{
|
||||
mFeeds: {},
|
||||
|
||||
putFeed: function (aFeed)
|
||||
{
|
||||
this.mFeeds[this.normalizeHost(aFeed.url)] = aFeed;
|
||||
},
|
||||
|
||||
getFeed: function (aUrl)
|
||||
{
|
||||
let index = this.normalizeHost(aUrl);
|
||||
if (index in this.mFeeds)
|
||||
return this.mFeeds[index];
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
removeFeed: function (aUrl)
|
||||
{
|
||||
let index = this.normalizeHost(aUrl);
|
||||
if (index in this.mFeeds)
|
||||
delete this.mFeeds[index];
|
||||
},
|
||||
|
||||
normalizeHost: function (aUrl)
|
||||
{
|
||||
try
|
||||
{
|
||||
let normalizedUrl = Services.io.newURI(aUrl, null, null);
|
||||
normalizedUrl.host = normalizedUrl.host.toLowerCase();
|
||||
return normalizedUrl.spec
|
||||
}
|
||||
catch (ex)
|
||||
{
|
||||
return aUrl;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function Feed(aResource, aRSSServer)
|
||||
{
|
||||
this.resource = aResource.QueryInterface(Ci.nsIRDFResource);
|
||||
this.server = aRSSServer;
|
||||
}
|
||||
|
||||
Feed.prototype =
|
||||
{
|
||||
description: null,
|
||||
author: null,
|
||||
request: null,
|
||||
server: null,
|
||||
downloadCallback: null,
|
||||
resource: null,
|
||||
items: new Array(),
|
||||
itemsStored: 0,
|
||||
mFolder: null,
|
||||
mInvalidFeed: false,
|
||||
mFeedType: null,
|
||||
mLastModified: null,
|
||||
|
||||
get folder()
|
||||
{
|
||||
return this.mFolder;
|
||||
},
|
||||
|
||||
set folder (aFolder)
|
||||
{
|
||||
this.mFolder = aFolder;
|
||||
},
|
||||
|
||||
get name()
|
||||
{
|
||||
// Used for the feed's title in Subcribe dialog and opml export.
|
||||
let name = this.title || this.description || this.url;
|
||||
return name.replace(/[\n\r\t]+/g, " ").replace(/[\x00-\x1F]+/g, "");
|
||||
},
|
||||
|
||||
get folderName()
|
||||
{
|
||||
if (this.mFolderName)
|
||||
return this.mFolderName;
|
||||
|
||||
// Get a unique sanitized name. Use title or description as a base;
|
||||
// these are mandatory by spec. Length of 80 is plenty.
|
||||
let folderName = (this.title || this.description || "").substr(0,80);
|
||||
let defaultName = FeedUtils.strings.GetStringFromName("ImportFeedsNew");
|
||||
return this.mFolderName = FeedUtils.getSanitizedFolderName(this.server.rootMsgFolder,
|
||||
folderName,
|
||||
defaultName,
|
||||
true);
|
||||
},
|
||||
|
||||
download: function(aParseItems, aCallback)
|
||||
{
|
||||
// May be null.
|
||||
this.downloadCallback = aCallback;
|
||||
|
||||
// Whether or not to parse items when downloading and parsing the feed.
|
||||
// Defaults to true, but setting to false is useful for obtaining
|
||||
// just the title of the feed when the user subscribes to it.
|
||||
this.parseItems = aParseItems == null ? true : aParseItems ? true : false;
|
||||
|
||||
// Before we do anything, make sure the url is an http url. This is just
|
||||
// a sanity check so we don't try opening mailto urls, imap urls, etc. that
|
||||
// the user may have tried to subscribe to as an rss feed.
|
||||
if (!FeedUtils.isValidScheme(this.url))
|
||||
{
|
||||
// Simulate an invalid feed error.
|
||||
FeedUtils.log.info("Feed.download: invalid protocol for - " + this.url);
|
||||
this.onParseError(this);
|
||||
return;
|
||||
}
|
||||
|
||||
// Before we try to download the feed, make sure we aren't already
|
||||
// processing the feed by looking up the url in our feed cache.
|
||||
if (FeedCache.getFeed(this.url))
|
||||
{
|
||||
if (this.downloadCallback)
|
||||
this.downloadCallback.downloaded(this, FeedUtils.kNewsBlogFeedIsBusy);
|
||||
// Return, the feed is already in use.
|
||||
return;
|
||||
}
|
||||
|
||||
if (Services.io.offline) {
|
||||
// If offline and don't want to go online, just add the feed subscription;
|
||||
// it can be verified later (the folder name will be the url if not adding
|
||||
// to an existing folder). Only for subscribe actions; passive biff and
|
||||
// active get new messages are handled prior to getting here.
|
||||
let win = Services.wm.getMostRecentWindow("mail:3pane");
|
||||
if (!win.MailOfflineMgr.getNewMail()) {
|
||||
this.storeNextItem();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
||||
createInstance(Ci.nsIXMLHttpRequest);
|
||||
// Must set onProgress before calling open.
|
||||
this.request.onprogress = this.onProgress;
|
||||
this.request.open("GET", this.url, true);
|
||||
this.request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE |
|
||||
Ci.nsIRequest.INHIBIT_CACHING;
|
||||
|
||||
// Some servers, if sent If-Modified-Since, will send 304 if subsequently
|
||||
// not sent If-Modified-Since, as in the case of an unsubscribe and new
|
||||
// subscribe. Send start of century date to force a download; some servers
|
||||
// will 304 on older dates (such as epoch 1970).
|
||||
let lastModified = this.lastModified || "Sat, 01 Jan 2000 00:00:00 GMT";
|
||||
this.request.setRequestHeader("If-Modified-Since", lastModified);
|
||||
|
||||
// Only order what you're going to eat...
|
||||
this.request.responseType = "document";
|
||||
this.request.overrideMimeType("text/xml");
|
||||
this.request.setRequestHeader("Accept", FeedUtils.REQUEST_ACCEPT);
|
||||
this.request.timeout = FeedUtils.REQUEST_TIMEOUT;
|
||||
this.request.onload = this.onDownloaded;
|
||||
this.request.onerror = this.onDownloadError;
|
||||
this.request.ontimeout = this.onDownloadError;
|
||||
FeedCache.putFeed(this);
|
||||
this.request.send(null);
|
||||
},
|
||||
|
||||
onDownloaded: function(aEvent)
|
||||
{
|
||||
let request = aEvent.target;
|
||||
let isHttp = request.channel.originalURI.scheme.startsWith("http");
|
||||
let url = request.channel.originalURI.spec;
|
||||
if (isHttp && (request.status < 200 || request.status >= 300))
|
||||
{
|
||||
Feed.prototype.onDownloadError(aEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
FeedUtils.log.debug("Feed.onDownloaded: got a download - " + url);
|
||||
let feed = FeedCache.getFeed(url);
|
||||
if (!feed)
|
||||
throw new Error("Feed.onDownloaded: error - couldn't retrieve feed " +
|
||||
"from cache");
|
||||
|
||||
// If the server sends a Last-Modified header, store the property on the
|
||||
// feed so we can use it when making future requests, to avoid downloading
|
||||
// and parsing feeds that have not changed. Don't update if merely checking
|
||||
// the url, as for subscribe move/copy, as a subsequent refresh may get a 304.
|
||||
// Save the response and persist it only upon successful completion of the
|
||||
// refresh cycle (i.e. not if the request is cancelled).
|
||||
let lastModifiedHeader = request.getResponseHeader("Last-Modified");
|
||||
feed.mLastModified = (lastModifiedHeader && feed.parseItems) ?
|
||||
lastModifiedHeader : null;
|
||||
|
||||
// The download callback is called asynchronously when parse() is done.
|
||||
feed.parse();
|
||||
},
|
||||
|
||||
onProgress: function(aEvent)
|
||||
{
|
||||
let request = aEvent.target;
|
||||
let url = request.channel.originalURI.spec;
|
||||
let feed = FeedCache.getFeed(url);
|
||||
|
||||
if (feed.downloadCallback)
|
||||
feed.downloadCallback.onProgress(feed, aEvent.loaded, aEvent.total,
|
||||
aEvent.lengthComputable);
|
||||
},
|
||||
|
||||
onDownloadError: function(aEvent)
|
||||
{
|
||||
let request = aEvent.target;
|
||||
let url = request.channel.originalURI.spec;
|
||||
let feed = FeedCache.getFeed(url);
|
||||
if (feed.downloadCallback)
|
||||
{
|
||||
// Generic network or 'not found' error initially.
|
||||
let error = FeedUtils.kNewsBlogRequestFailure;
|
||||
|
||||
if (request.status == 304) {
|
||||
// If the http status code is 304, the feed has not been modified
|
||||
// since we last downloaded it and does not need to be parsed.
|
||||
error = FeedUtils.kNewsBlogNoNewItems;
|
||||
}
|
||||
else {
|
||||
let [errType, errName] = FeedUtils.createTCPErrorFromFailedXHR(request);
|
||||
FeedUtils.log.info("Feed.onDownloaded: request errType:errName:statusCode - " +
|
||||
errType + ":" + errName + ":" + request.status);
|
||||
if (errType == "SecurityCertificate")
|
||||
// This is the code for nsINSSErrorsService.ERROR_CLASS_BAD_CERT
|
||||
// overrideable security certificate errors.
|
||||
error = FeedUtils.kNewsBlogBadCertError;
|
||||
|
||||
if (request.status == 401 || request.status == 403)
|
||||
// Unauthorized or Forbidden.
|
||||
error = FeedUtils.kNewsBlogNoAuthError;
|
||||
}
|
||||
|
||||
feed.downloadCallback.downloaded(feed, error);
|
||||
}
|
||||
|
||||
FeedCache.removeFeed(url);
|
||||
},
|
||||
|
||||
onParseError: function(aFeed)
|
||||
{
|
||||
if (!aFeed)
|
||||
return;
|
||||
|
||||
aFeed.mInvalidFeed = true;
|
||||
if (aFeed.downloadCallback)
|
||||
aFeed.downloadCallback.downloaded(aFeed, FeedUtils.kNewsBlogInvalidFeed);
|
||||
|
||||
FeedCache.removeFeed(aFeed.url);
|
||||
},
|
||||
|
||||
onUrlChange: function(aFeed, aOldUrl)
|
||||
{
|
||||
if (!aFeed)
|
||||
return;
|
||||
|
||||
// Simulate a cancel after a url update; next cycle will check the new url.
|
||||
aFeed.mInvalidFeed = true;
|
||||
if (aFeed.downloadCallback)
|
||||
aFeed.downloadCallback.downloaded(aFeed, FeedUtils.kNewsBlogCancel);
|
||||
|
||||
FeedCache.removeFeed(aOldUrl);
|
||||
},
|
||||
|
||||
get url()
|
||||
{
|
||||
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
||||
let url = ds.GetTarget(this.resource, FeedUtils.DC_IDENTIFIER, true);
|
||||
if (url)
|
||||
url = url.QueryInterface(Ci.nsIRDFLiteral).Value;
|
||||
else
|
||||
url = this.resource.ValueUTF8;
|
||||
|
||||
return url;
|
||||
},
|
||||
|
||||
get title()
|
||||
{
|
||||
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
||||
let title = ds.GetTarget(this.resource, FeedUtils.DC_TITLE, true);
|
||||
if (title)
|
||||
title = title.QueryInterface(Ci.nsIRDFLiteral).Value;
|
||||
|
||||
return title;
|
||||
},
|
||||
|
||||
set title (aNewTitle)
|
||||
{
|
||||
if (!aNewTitle)
|
||||
return;
|
||||
|
||||
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
||||
aNewTitle = FeedUtils.rdf.GetLiteral(aNewTitle);
|
||||
let old_title = ds.GetTarget(this.resource, FeedUtils.DC_TITLE, true);
|
||||
if (old_title)
|
||||
ds.Change(this.resource, FeedUtils.DC_TITLE, old_title, aNewTitle);
|
||||
else
|
||||
ds.Assert(this.resource, FeedUtils.DC_TITLE, aNewTitle, true);
|
||||
},
|
||||
|
||||
get lastModified()
|
||||
{
|
||||
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
||||
let lastModified = ds.GetTarget(this.resource,
|
||||
FeedUtils.DC_LASTMODIFIED,
|
||||
true);
|
||||
if (lastModified)
|
||||
lastModified = lastModified.QueryInterface(Ci.nsIRDFLiteral).Value;
|
||||
|
||||
return lastModified;
|
||||
},
|
||||
|
||||
set lastModified(aLastModified)
|
||||
{
|
||||
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
||||
aLastModified = FeedUtils.rdf.GetLiteral(aLastModified);
|
||||
let old_lastmodified = ds.GetTarget(this.resource,
|
||||
FeedUtils.DC_LASTMODIFIED,
|
||||
true);
|
||||
if (old_lastmodified)
|
||||
ds.Change(this.resource, FeedUtils.DC_LASTMODIFIED,
|
||||
old_lastmodified, aLastModified);
|
||||
else
|
||||
ds.Assert(this.resource, FeedUtils.DC_LASTMODIFIED, aLastModified, true);
|
||||
},
|
||||
|
||||
get quickMode ()
|
||||
{
|
||||
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
||||
let quickMode = ds.GetTarget(this.resource, FeedUtils.FZ_QUICKMODE, true);
|
||||
if (quickMode)
|
||||
{
|
||||
quickMode = quickMode.QueryInterface(Ci.nsIRDFLiteral);
|
||||
quickMode = quickMode.Value == "true";
|
||||
}
|
||||
|
||||
return quickMode;
|
||||
},
|
||||
|
||||
set quickMode (aNewQuickMode)
|
||||
{
|
||||
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
||||
aNewQuickMode = FeedUtils.rdf.GetLiteral(aNewQuickMode);
|
||||
let old_quickMode = ds.GetTarget(this.resource,
|
||||
FeedUtils.FZ_QUICKMODE,
|
||||
true);
|
||||
if (old_quickMode)
|
||||
ds.Change(this.resource, FeedUtils.FZ_QUICKMODE,
|
||||
old_quickMode, aNewQuickMode);
|
||||
else
|
||||
ds.Assert(this.resource, FeedUtils.FZ_QUICKMODE,
|
||||
aNewQuickMode, true);
|
||||
},
|
||||
|
||||
get options ()
|
||||
{
|
||||
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
||||
let options = ds.GetTarget(this.resource, FeedUtils.FZ_OPTIONS, true);
|
||||
if (options)
|
||||
return JSON.parse(options.QueryInterface(Ci.nsIRDFLiteral).Value);
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
set options (aOptions)
|
||||
{
|
||||
let newOptions = aOptions ? FeedUtils.newOptions(aOptions) :
|
||||
FeedUtils._optionsDefault;
|
||||
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
||||
newOptions = FeedUtils.rdf.GetLiteral(JSON.stringify(newOptions));
|
||||
let oldOptions = ds.GetTarget(this.resource, FeedUtils.FZ_OPTIONS, true);
|
||||
if (oldOptions)
|
||||
ds.Change(this.resource, FeedUtils.FZ_OPTIONS, oldOptions, newOptions);
|
||||
else
|
||||
ds.Assert(this.resource, FeedUtils.FZ_OPTIONS, newOptions, true);
|
||||
},
|
||||
|
||||
categoryPrefs: function ()
|
||||
{
|
||||
let categoryPrefsAcct = FeedUtils.getOptionsAcct(this.server).category;
|
||||
if (!this.options)
|
||||
return categoryPrefsAcct;
|
||||
|
||||
return this.options.category;
|
||||
},
|
||||
|
||||
get link ()
|
||||
{
|
||||
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
||||
let link = ds.GetTarget(this.resource, FeedUtils.RSS_LINK, true);
|
||||
if (link)
|
||||
link = link.QueryInterface(Ci.nsIRDFLiteral).Value;
|
||||
|
||||
return link;
|
||||
},
|
||||
|
||||
set link (aNewLink)
|
||||
{
|
||||
if (!aNewLink)
|
||||
return;
|
||||
|
||||
let ds = FeedUtils.getSubscriptionsDS(this.server);
|
||||
aNewLink = FeedUtils.rdf.GetLiteral(aNewLink);
|
||||
let old_link = ds.GetTarget(this.resource, FeedUtils.RSS_LINK, true);
|
||||
if (old_link)
|
||||
ds.Change(this.resource, FeedUtils.RSS_LINK, old_link, aNewLink);
|
||||
else
|
||||
ds.Assert(this.resource, FeedUtils.RSS_LINK, aNewLink, true);
|
||||
},
|
||||
|
||||
parse: function()
|
||||
{
|
||||
// Create a feed parser which will parse the feed.
|
||||
let parser = new FeedParser();
|
||||
this.itemsToStore = parser.parseFeed(this, this.request.responseXML);
|
||||
parser = null;
|
||||
|
||||
if (this.mInvalidFeed)
|
||||
{
|
||||
this.request = null;
|
||||
this.mInvalidFeed = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// storeNextItem() will iterate through the parsed items, storing each one.
|
||||
this.itemsToStoreIndex = 0;
|
||||
this.itemsStored = 0;
|
||||
this.storeNextItem();
|
||||
},
|
||||
|
||||
invalidateItems: function ()
|
||||
{
|
||||
let ds = FeedUtils.getItemsDS(this.server);
|
||||
FeedUtils.log.debug("Feed.invalidateItems: for url - " + this.url);
|
||||
let items = ds.GetSources(FeedUtils.FZ_FEED, this.resource, true);
|
||||
let item;
|
||||
|
||||
while (items.hasMoreElements())
|
||||
{
|
||||
item = items.getNext();
|
||||
item = item.QueryInterface(Ci.nsIRDFResource);
|
||||
FeedUtils.log.trace("Feed.invalidateItems: item - " + item.Value);
|
||||
let valid = ds.GetTarget(item, FeedUtils.FZ_VALID, true);
|
||||
if (valid)
|
||||
ds.Unassert(item, FeedUtils.FZ_VALID, valid, true);
|
||||
}
|
||||
},
|
||||
|
||||
removeInvalidItems: function(aDeleteFeed)
|
||||
{
|
||||
let ds = FeedUtils.getItemsDS(this.server);
|
||||
FeedUtils.log.debug("Feed.removeInvalidItems: for url - " + this.url);
|
||||
let items = ds.GetSources(FeedUtils.FZ_FEED, this.resource, true);
|
||||
let item;
|
||||
let currentTime = new Date().getTime();
|
||||
while (items.hasMoreElements())
|
||||
{
|
||||
item = items.getNext();
|
||||
item = item.QueryInterface(Ci.nsIRDFResource);
|
||||
|
||||
if (ds.HasAssertion(item, FeedUtils.FZ_VALID,
|
||||
FeedUtils.RDF_LITERAL_TRUE, true))
|
||||
continue;
|
||||
|
||||
let lastSeenTime = ds.GetTarget(item, FeedUtils.FZ_LAST_SEEN_TIMESTAMP, true);
|
||||
if (lastSeenTime)
|
||||
lastSeenTime = parseInt(lastSeenTime.QueryInterface(Ci.nsIRDFLiteral).Value)
|
||||
else
|
||||
lastSeenTime = 0;
|
||||
|
||||
if ((currentTime - lastSeenTime) < FeedUtils.INVALID_ITEM_PURGE_DELAY &&
|
||||
!aDeleteFeed)
|
||||
// Don't immediately purge items in active feeds; do so for deleted feeds.
|
||||
continue;
|
||||
|
||||
FeedUtils.log.trace("Feed.removeInvalidItems: item - " + item.Value);
|
||||
ds.Unassert(item, FeedUtils.FZ_FEED, this.resource, true);
|
||||
if (ds.hasArcOut(item, FeedUtils.FZ_FEED))
|
||||
FeedUtils.log.debug("Feed.removeInvalidItems: " + item.Value +
|
||||
" is from more than one feed; only the reference to" +
|
||||
" this feed removed");
|
||||
else
|
||||
FeedUtils.removeAssertions(ds, item);
|
||||
}
|
||||
},
|
||||
|
||||
createFolder: function()
|
||||
{
|
||||
if (this.folder)
|
||||
return;
|
||||
|
||||
try {
|
||||
this.folder = this.server.rootMsgFolder
|
||||
.QueryInterface(Ci.nsIMsgLocalMailFolder)
|
||||
.createLocalSubfolder(this.folderName);
|
||||
}
|
||||
catch (ex) {
|
||||
// An error creating.
|
||||
FeedUtils.log.info("Feed.createFolder: error creating folder - '"+
|
||||
this.folderName+"' in parent folder "+
|
||||
this.server.rootMsgFolder.filePath.path + " -- "+ex);
|
||||
// But its remnants are still there, clean up.
|
||||
let xfolder = this.server.rootMsgFolder.getChildNamed(this.folderName);
|
||||
this.server.rootMsgFolder.propagateDelete(xfolder, true, null);
|
||||
}
|
||||
},
|
||||
|
||||
// Gets the next item from itemsToStore and forces that item to be stored
|
||||
// to the folder. If more items are left to be stored, fires a timer for
|
||||
// the next one, otherwise triggers a download done notification to the UI.
|
||||
storeNextItem: function()
|
||||
{
|
||||
if (FeedUtils.CANCEL_REQUESTED)
|
||||
{
|
||||
FeedUtils.CANCEL_REQUESTED = false;
|
||||
this.cleanupParsingState(this, FeedUtils.kNewsBlogCancel);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.itemsToStore || !this.itemsToStore.length)
|
||||
{
|
||||
let code = FeedUtils.kNewsBlogSuccess;
|
||||
this.createFolder();
|
||||
if (!this.folder)
|
||||
code = FeedUtils.kNewsBlogFileError;
|
||||
this.cleanupParsingState(this, code);
|
||||
return;
|
||||
}
|
||||
|
||||
let item = this.itemsToStore[this.itemsToStoreIndex];
|
||||
|
||||
if (item.store())
|
||||
this.itemsStored++;
|
||||
|
||||
if (!this.folder)
|
||||
{
|
||||
this.cleanupParsingState(this, FeedUtils.kNewsBlogFileError);
|
||||
return;
|
||||
}
|
||||
|
||||
this.itemsToStoreIndex++;
|
||||
|
||||
// If the listener is tracking progress for each item, report it here.
|
||||
if (item.feed.downloadCallback && item.feed.downloadCallback.onFeedItemStored)
|
||||
item.feed.downloadCallback.onFeedItemStored(item.feed,
|
||||
this.itemsToStoreIndex,
|
||||
this.itemsToStore.length);
|
||||
|
||||
// Eventually we'll report individual progress here.
|
||||
|
||||
if (this.itemsToStoreIndex < this.itemsToStore.length)
|
||||
{
|
||||
if (!this.storeItemsTimer)
|
||||
this.storeItemsTimer = Cc["@mozilla.org/timer;1"].
|
||||
createInstance(Ci.nsITimer);
|
||||
this.storeItemsTimer.initWithCallback(this, 50, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have just finished downloading one or more feed items into the
|
||||
// destination folder; if the folder is still listed as having new
|
||||
// messages in it, then we should set the biff state on the folder so the
|
||||
// right RDF UI changes happen in the folder pane to indicate new mail.
|
||||
if (item.feed.folder.hasNewMessages)
|
||||
{
|
||||
item.feed.folder.biffState = Ci.nsIMsgFolder.nsMsgBiffState_NewMail;
|
||||
// Run the bayesian spam filter, if enabled.
|
||||
item.feed.folder.callFilterPlugins(null);
|
||||
}
|
||||
|
||||
this.cleanupParsingState(this, FeedUtils.kNewsBlogSuccess);
|
||||
}
|
||||
},
|
||||
|
||||
cleanupParsingState: function(aFeed, aCode)
|
||||
{
|
||||
// Now that we are done parsing the feed, remove the feed from the cache.
|
||||
FeedCache.removeFeed(aFeed.url);
|
||||
|
||||
if (aFeed.parseItems)
|
||||
{
|
||||
// Do this only if we're in parse/store mode.
|
||||
aFeed.removeInvalidItems(false);
|
||||
|
||||
if (aCode == FeedUtils.kNewsBlogSuccess && aFeed.mLastModified)
|
||||
aFeed.lastModified = aFeed.mLastModified;
|
||||
|
||||
// Flush any feed item changes to disk.
|
||||
let ds = FeedUtils.getItemsDS(aFeed.server);
|
||||
ds.Flush();
|
||||
FeedUtils.log.debug("Feed.cleanupParsingState: items stored - " + this.itemsStored);
|
||||
}
|
||||
|
||||
// Force the xml http request to go away. This helps reduce some nasty
|
||||
// assertions on shut down.
|
||||
this.request = null;
|
||||
this.itemsToStore = "";
|
||||
this.itemsToStoreIndex = 0;
|
||||
this.itemsStored = 0;
|
||||
this.storeItemsTimer = null;
|
||||
|
||||
if (aFeed.downloadCallback)
|
||||
aFeed.downloadCallback.downloaded(aFeed, aCode);
|
||||
},
|
||||
|
||||
// nsITimerCallback
|
||||
notify: function(aTimer)
|
||||
{
|
||||
this.storeNextItem();
|
||||
}
|
||||
};
|
||||
490
mailnews/extensions/newsblog/content/FeedItem.js
Normal file
490
mailnews/extensions/newsblog/content/FeedItem.js
Normal file
|
|
@ -0,0 +1,490 @@
|
|||
/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
function FeedItem()
|
||||
{
|
||||
this.mDate = FeedUtils.getValidRFC5322Date();
|
||||
this.mUnicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].
|
||||
createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
this.mParserUtils = Cc["@mozilla.org/parserutils;1"].
|
||||
getService(Ci.nsIParserUtils);
|
||||
}
|
||||
|
||||
FeedItem.prototype =
|
||||
{
|
||||
// Only for IETF Atom.
|
||||
xmlContentBase: null,
|
||||
id: null,
|
||||
feed: null,
|
||||
description: null,
|
||||
content: null,
|
||||
enclosures: [],
|
||||
title: null,
|
||||
author: "anonymous",
|
||||
inReplyTo: "",
|
||||
keywords: [],
|
||||
mURL: null,
|
||||
characterSet: "UTF-8",
|
||||
|
||||
ENCLOSURE_BOUNDARY_PREFIX: "--------------", // 14 dashes
|
||||
ENCLOSURE_HEADER_BOUNDARY_PREFIX: "------------", // 12 dashes
|
||||
MESSAGE_TEMPLATE: '\n' +
|
||||
'<html>\n' +
|
||||
' <head>\n' +
|
||||
' <title>%TITLE%</title>\n' +
|
||||
' <base href="%BASE%">\n' +
|
||||
' </head>\n' +
|
||||
' <body id="msgFeedSummaryBody" selected="false">\n' +
|
||||
' %CONTENT%\n' +
|
||||
' </body>\n' +
|
||||
'</html>\n',
|
||||
|
||||
get url()
|
||||
{
|
||||
return this.mURL;
|
||||
},
|
||||
|
||||
set url(aVal)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.mURL = Services.io.newURI(aVal, null, null).spec;
|
||||
}
|
||||
catch(ex)
|
||||
{
|
||||
// The url as published or constructed can be a non url. It's used as a
|
||||
// feeditem identifier in feeditems.rdf, as a messageId, and as an href
|
||||
// and for the content-base header. Save as is; ensure not null.
|
||||
this.mURL = aVal ? aVal : "";
|
||||
}
|
||||
},
|
||||
|
||||
get date()
|
||||
{
|
||||
return this.mDate;
|
||||
},
|
||||
|
||||
set date (aVal)
|
||||
{
|
||||
this.mDate = aVal;
|
||||
},
|
||||
|
||||
get identity ()
|
||||
{
|
||||
return this.feed.name + ": " + this.title + " (" + this.id + ")"
|
||||
},
|
||||
|
||||
normalizeMessageID: function(messageID)
|
||||
{
|
||||
// Escape occurrences of message ID meta characters <, >, and @.
|
||||
messageID.replace(/</g, "%3C");
|
||||
messageID.replace(/>/g, "%3E");
|
||||
messageID.replace(/@/g, "%40");
|
||||
messageID = "<" + messageID.trim() + "@" + "localhost.localdomain" + ">";
|
||||
|
||||
FeedUtils.log.trace("FeedItem.normalizeMessageID: messageID - " + messageID);
|
||||
return messageID;
|
||||
},
|
||||
|
||||
get itemUniqueURI()
|
||||
{
|
||||
return this.createURN(this.id);
|
||||
},
|
||||
|
||||
get contentBase()
|
||||
{
|
||||
if(this.xmlContentBase)
|
||||
return this.xmlContentBase
|
||||
else
|
||||
return this.mURL;
|
||||
},
|
||||
|
||||
store: function()
|
||||
{
|
||||
// this.title and this.content contain HTML.
|
||||
// this.mUrl and this.contentBase contain plain text.
|
||||
|
||||
let stored = false;
|
||||
let resource = this.findStoredResource();
|
||||
if (!this.feed.folder)
|
||||
return stored;
|
||||
|
||||
if (resource == null)
|
||||
{
|
||||
resource = FeedUtils.rdf.GetResource(this.itemUniqueURI);
|
||||
if (!this.content)
|
||||
{
|
||||
FeedUtils.log.trace("FeedItem.store: " + this.identity +
|
||||
" no content; storing description or title");
|
||||
this.content = this.description || this.title;
|
||||
}
|
||||
|
||||
let content = this.MESSAGE_TEMPLATE;
|
||||
content = content.replace(/%TITLE%/, this.title);
|
||||
content = content.replace(/%BASE%/, this.htmlEscape(this.contentBase));
|
||||
content = content.replace(/%CONTENT%/, this.content);
|
||||
this.content = content;
|
||||
this.writeToFolder();
|
||||
this.markStored(resource);
|
||||
stored = true;
|
||||
}
|
||||
this.markValid(resource);
|
||||
return stored;
|
||||
},
|
||||
|
||||
findStoredResource: function()
|
||||
{
|
||||
// Checks to see if the item has already been stored in its feed's
|
||||
// message folder.
|
||||
FeedUtils.log.trace("FeedItem.findStoredResource: checking if stored - " +
|
||||
this.identity);
|
||||
|
||||
let server = this.feed.server;
|
||||
let folder = this.feed.folder;
|
||||
|
||||
if (!folder)
|
||||
{
|
||||
FeedUtils.log.debug("FeedItem.findStoredResource: folder '" +
|
||||
this.feed.folderName +
|
||||
"' doesn't exist; creating as child of " +
|
||||
server.rootMsgFolder.prettyName + "\n");
|
||||
this.feed.createFolder();
|
||||
return null;
|
||||
}
|
||||
|
||||
let ds = FeedUtils.getItemsDS(server);
|
||||
let itemURI = this.itemUniqueURI;
|
||||
let itemResource = FeedUtils.rdf.GetResource(itemURI);
|
||||
|
||||
let downloaded = ds.GetTarget(itemResource, FeedUtils.FZ_STORED, true);
|
||||
|
||||
if (!downloaded ||
|
||||
downloaded.QueryInterface(Ci.nsIRDFLiteral).Value == "false")
|
||||
{
|
||||
FeedUtils.log.trace("FeedItem.findStoredResource: not stored");
|
||||
return null;
|
||||
}
|
||||
|
||||
FeedUtils.log.trace("FeedItem.findStoredResource: already stored");
|
||||
return itemResource;
|
||||
},
|
||||
|
||||
markValid: function(resource)
|
||||
{
|
||||
let ds = FeedUtils.getItemsDS(this.feed.server);
|
||||
|
||||
let newTimeStamp = FeedUtils.rdf.GetLiteral(new Date().getTime());
|
||||
let currentTimeStamp = ds.GetTarget(resource,
|
||||
FeedUtils.FZ_LAST_SEEN_TIMESTAMP,
|
||||
true);
|
||||
if (currentTimeStamp)
|
||||
ds.Change(resource, FeedUtils.FZ_LAST_SEEN_TIMESTAMP,
|
||||
currentTimeStamp, newTimeStamp);
|
||||
else
|
||||
ds.Assert(resource, FeedUtils.FZ_LAST_SEEN_TIMESTAMP,
|
||||
newTimeStamp, true);
|
||||
|
||||
if (!ds.HasAssertion(resource, FeedUtils.FZ_FEED,
|
||||
FeedUtils.rdf.GetResource(this.feed.url), true))
|
||||
ds.Assert(resource, FeedUtils.FZ_FEED,
|
||||
FeedUtils.rdf.GetResource(this.feed.url), true);
|
||||
|
||||
if (ds.hasArcOut(resource, FeedUtils.FZ_VALID))
|
||||
{
|
||||
let currentValue = ds.GetTarget(resource, FeedUtils.FZ_VALID, true);
|
||||
ds.Change(resource, FeedUtils.FZ_VALID,
|
||||
currentValue, FeedUtils.RDF_LITERAL_TRUE);
|
||||
}
|
||||
else
|
||||
ds.Assert(resource, FeedUtils.FZ_VALID, FeedUtils.RDF_LITERAL_TRUE, true);
|
||||
},
|
||||
|
||||
markStored: function(resource)
|
||||
{
|
||||
let ds = FeedUtils.getItemsDS(this.feed.server);
|
||||
|
||||
if (!ds.HasAssertion(resource, FeedUtils.FZ_FEED,
|
||||
FeedUtils.rdf.GetResource(this.feed.url), true))
|
||||
ds.Assert(resource, FeedUtils.FZ_FEED,
|
||||
FeedUtils.rdf.GetResource(this.feed.url), true);
|
||||
|
||||
let currentValue;
|
||||
if (ds.hasArcOut(resource, FeedUtils.FZ_STORED))
|
||||
{
|
||||
currentValue = ds.GetTarget(resource, FeedUtils.FZ_STORED, true);
|
||||
ds.Change(resource, FeedUtils.FZ_STORED,
|
||||
currentValue, FeedUtils.RDF_LITERAL_TRUE);
|
||||
}
|
||||
else
|
||||
ds.Assert(resource, FeedUtils.FZ_STORED,
|
||||
FeedUtils.RDF_LITERAL_TRUE, true);
|
||||
},
|
||||
|
||||
mimeEncodeSubject: function(aSubject, aCharset)
|
||||
{
|
||||
// This routine sometimes throws exceptions for mis-encoded data so
|
||||
// wrap it with a try catch for now.
|
||||
let newSubject;
|
||||
try
|
||||
{
|
||||
newSubject = mailServices.mimeConverter.encodeMimePartIIStr_UTF8(aSubject,
|
||||
false,
|
||||
aCharset, 9, 72);
|
||||
}
|
||||
catch (ex)
|
||||
{
|
||||
newSubject = aSubject;
|
||||
}
|
||||
|
||||
return newSubject;
|
||||
},
|
||||
|
||||
writeToFolder: function()
|
||||
{
|
||||
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. & becomes '&'.
|
||||
title = this.mParserUtils.convertToPlainText(
|
||||
title,
|
||||
Ci.nsIDocumentEncoder.OutputSelectionOnly |
|
||||
Ci.nsIDocumentEncoder.OutputAbsoluteLinks,
|
||||
0);
|
||||
|
||||
// Compress white space in the subject to make it look better. Trim
|
||||
// leading/trailing spaces to prevent mbox header folding issue at just
|
||||
// the right subject length.
|
||||
title = title.replace(/[\t\r\n]+/g, " ").trim();
|
||||
|
||||
this.title = this.mimeEncodeSubject(title, this.characterSet);
|
||||
|
||||
// If the date looks like it's in W3C-DTF format, convert it into
|
||||
// an IETF standard date. Otherwise assume it's in IETF format.
|
||||
if (this.mDate.search(/^\d\d\d\d/) != -1)
|
||||
this.mDate = new Date(this.mDate).toUTCString();
|
||||
|
||||
// If there is an inreplyto value, create the headers.
|
||||
let inreplytoHdrsStr = this.inReplyTo ?
|
||||
("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");
|
||||
}
|
||||
|
||||
// Escape occurrences of "From " at the beginning of lines of
|
||||
// content per the mbox standard, since "From " denotes a new
|
||||
// message, and add a line break so we know the last line has one.
|
||||
this.content = this.content.replace(/([\r\n]+)(>*From )/g, "$1>$2");
|
||||
this.content += "\n";
|
||||
|
||||
// The opening line of the message, mandated by standards to start
|
||||
// with "From ". It's useful to construct this separately because
|
||||
// we not only need to write it into the message, we also need to
|
||||
// use it to calculate the offset of the X-Mozilla-Status lines from
|
||||
// the front of the message for the statusOffset property of the
|
||||
// DB header object.
|
||||
let openingLine = 'From - ' + this.mDate + '\n';
|
||||
|
||||
let source =
|
||||
openingLine +
|
||||
'X-Mozilla-Status: 0000\n' +
|
||||
'X-Mozilla-Status2: 00000000\n' +
|
||||
'X-Mozilla-Keys: ' + " ".repeat(80) + '\n' +
|
||||
'Received: by localhost; ' + FeedUtils.getValidRFC5322Date() + '\n' +
|
||||
'Date: ' + this.mDate + '\n' +
|
||||
'Message-Id: ' + this.normalizeMessageID(this.id) + '\n' +
|
||||
'From: ' + this.author + '\n' +
|
||||
'MIME-Version: 1.0\n' +
|
||||
'Subject: ' + this.title + '\n' +
|
||||
inreplytoHdrsStr +
|
||||
keywordsStr +
|
||||
'Content-Transfer-Encoding: 8bit\n' +
|
||||
'Content-Base: ' + this.mURL + '\n';
|
||||
|
||||
if (this.enclosures.length)
|
||||
{
|
||||
let boundaryID = source.length;
|
||||
source += 'Content-Type: multipart/mixed; boundary="' +
|
||||
this.ENCLOSURE_HEADER_BOUNDARY_PREFIX + boundaryID + '"' + '\n\n' +
|
||||
'This is a multi-part message in MIME format.\n' +
|
||||
this.ENCLOSURE_BOUNDARY_PREFIX + boundaryID + '\n' +
|
||||
'Content-Type: text/html; charset=' + this.characterSet + '\n' +
|
||||
'Content-Transfer-Encoding: 8bit\n' +
|
||||
this.content;
|
||||
|
||||
this.enclosures.forEach(function(enclosure) {
|
||||
source += enclosure.convertToAttachment(boundaryID);
|
||||
});
|
||||
|
||||
source += this.ENCLOSURE_BOUNDARY_PREFIX + boundaryID + '--' + '\n\n\n';
|
||||
}
|
||||
else
|
||||
source += 'Content-Type: text/html; charset=' + this.characterSet + '\n' +
|
||||
this.content;
|
||||
|
||||
FeedUtils.log.trace("FeedItem.writeToFolder: " + this.identity +
|
||||
" is " + source.length + " characters long");
|
||||
|
||||
// Get the folder and database storing the feed's messages and headers.
|
||||
let folder = this.feed.folder.QueryInterface(Ci.nsIMsgLocalMailFolder);
|
||||
let msgFolder = folder.QueryInterface(Ci.nsIMsgFolder);
|
||||
msgFolder.gettingNewMessages = true;
|
||||
// Source is a unicode string, we want to save a char * string in
|
||||
// the original charset. So convert back.
|
||||
this.mUnicodeConverter.charset = this.characterSet;
|
||||
let msgDBHdr = folder.addMessage(this.mUnicodeConverter.ConvertFromUnicode(source));
|
||||
msgDBHdr.OrFlags(Ci.nsMsgMessageFlags.FeedMsg);
|
||||
msgFolder.gettingNewMessages = false;
|
||||
this.tagItem(msgDBHdr, this.keywords);
|
||||
},
|
||||
|
||||
/**
|
||||
* Autotag messages.
|
||||
*
|
||||
* @param nsIMsgDBHdr aMsgDBHdr - message to tag
|
||||
* @param array aKeywords - keywords (tags)
|
||||
*/
|
||||
tagItem: function(aMsgDBHdr, aKeywords)
|
||||
{
|
||||
let categoryPrefs = this.feed.categoryPrefs();
|
||||
if (!aKeywords.length || !categoryPrefs.enabled)
|
||||
return;
|
||||
|
||||
let msgArray = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
|
||||
msgArray.appendElement(aMsgDBHdr, false);
|
||||
|
||||
let prefix = categoryPrefs.prefixEnabled ? categoryPrefs.prefix : "";
|
||||
let rtl = Services.prefs.getIntPref("bidi.direction") == 2;
|
||||
|
||||
let keys = [];
|
||||
for (let keyword of aKeywords)
|
||||
{
|
||||
keyword = rtl ? keyword + prefix : prefix + keyword;
|
||||
let keyForTag = MailServices.tags.getKeyForTag(keyword);
|
||||
if (!keyForTag)
|
||||
{
|
||||
// Add the tag if it doesn't exist.
|
||||
MailServices.tags.addTag(keyword, "", FeedUtils.AUTOTAG);
|
||||
keyForTag = MailServices.tags.getKeyForTag(keyword);
|
||||
}
|
||||
|
||||
// Add the tag key to the keys array.
|
||||
keys.push(keyForTag);
|
||||
}
|
||||
|
||||
if (keys.length)
|
||||
// Add the keys to the message.
|
||||
aMsgDBHdr.folder.addKeywordsToMessages(msgArray, keys.join(" "));
|
||||
},
|
||||
|
||||
htmlEscape: function(s)
|
||||
{
|
||||
s = s.replace(/&/g, "&");
|
||||
s = s.replace(/>/g, ">");
|
||||
s = s.replace(/</g, "<");
|
||||
s = s.replace(/'/g, "'");
|
||||
s = s.replace(/"/g, """);
|
||||
return s;
|
||||
},
|
||||
|
||||
createURN: function(aName)
|
||||
{
|
||||
// Returns name as a URN in the 'feeditem' namespace. The returned URN is
|
||||
// (or is intended to be) RFC2141 compliant.
|
||||
// The builtin encodeURI provides nearly the exact encoding functionality
|
||||
// required by the RFC. The exceptions are that NULL characters should not
|
||||
// appear, and that #, /, ?, &, and ~ should be escaped.
|
||||
// NULL characters are removed before encoding.
|
||||
|
||||
let name = aName.replace(/\0/g, "");
|
||||
let encoded = encodeURI(name);
|
||||
encoded = encoded.replace(/\#/g, "%23");
|
||||
encoded = encoded.replace(/\//g, "%2f");
|
||||
encoded = encoded.replace(/\?/g, "%3f");
|
||||
encoded = encoded.replace(/\&/g, "%26");
|
||||
encoded = encoded.replace(/\~/g, "%7e");
|
||||
|
||||
return FeedUtils.FZ_ITEM_NS + encoded;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// A feed enclosure is to RSS what an attachment is for e-mail. We make
|
||||
// enclosures look like attachments in the UI.
|
||||
function FeedEnclosure(aURL, aContentType, aLength, aTitle)
|
||||
{
|
||||
this.mURL = aURL;
|
||||
// Store a reasonable mimetype if content-type is not present.
|
||||
this.mContentType = aContentType || "application/unknown";
|
||||
this.mLength = aLength;
|
||||
this.mTitle = aTitle;
|
||||
|
||||
// Generate a fileName from the URL.
|
||||
if (this.mURL)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.mFileName = Services.io.newURI(this.mURL, null, null).
|
||||
QueryInterface(Ci.nsIURL).
|
||||
fileName;
|
||||
}
|
||||
catch(ex)
|
||||
{
|
||||
this.mFileName = this.mURL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FeedEnclosure.prototype =
|
||||
{
|
||||
mURL: "",
|
||||
mContentType: "",
|
||||
mLength: 0,
|
||||
mFileName: "",
|
||||
mTitle: "",
|
||||
ENCLOSURE_BOUNDARY_PREFIX: "--------------", // 14 dashes
|
||||
|
||||
// Returns a string that looks like an e-mail attachment which represents
|
||||
// the enclosure.
|
||||
convertToAttachment: function(aBoundaryID)
|
||||
{
|
||||
return '\n' +
|
||||
this.ENCLOSURE_BOUNDARY_PREFIX + aBoundaryID + '\n' +
|
||||
'Content-Type: ' + this.mContentType +
|
||||
'; name="' + (this.mTitle || this.mFileName) +
|
||||
(this.mLength ? '"; size=' + this.mLength : '"') + '\n' +
|
||||
'X-Mozilla-External-Attachment-URL: ' + this.mURL + '\n' +
|
||||
'Content-Disposition: attachment; filename="' + this.mFileName + '"\n\n' +
|
||||
FeedUtils.strings.GetStringFromName("externalAttachmentMsg") + '\n';
|
||||
}
|
||||
};
|
||||
1608
mailnews/extensions/newsblog/content/FeedUtils.jsm
Normal file
1608
mailnews/extensions/newsblog/content/FeedUtils.jsm
Normal file
File diff suppressed because it is too large
Load diff
63
mailnews/extensions/newsblog/content/am-newsblog.js
Normal file
63
mailnews/extensions/newsblog/content/am-newsblog.js
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
Components.utils.import("resource:///modules/FeedUtils.jsm");
|
||||
|
||||
var gServer, autotagEnable, autotagUsePrefix, autotagPrefix;
|
||||
|
||||
function onInit(aPageId, aServerId)
|
||||
{
|
||||
var accountName = document.getElementById("server.prettyName");
|
||||
var title = document.getElementById("am-newsblog-title");
|
||||
var defaultTitle = title.getAttribute("defaultTitle");
|
||||
|
||||
var titleValue;
|
||||
if (accountName.value)
|
||||
titleValue = defaultTitle + " - <" + accountName.value + ">";
|
||||
else
|
||||
titleValue = defaultTitle;
|
||||
|
||||
title.setAttribute("title", titleValue);
|
||||
document.title = titleValue;
|
||||
|
||||
onCheckItem("server.biffMinutes", ["server.doBiff"]);
|
||||
|
||||
autotagEnable = document.getElementById("autotagEnable");
|
||||
autotagUsePrefix = document.getElementById("autotagUsePrefix");
|
||||
autotagPrefix = document.getElementById("autotagPrefix");
|
||||
|
||||
let categoryPrefsAcct = FeedUtils.getOptionsAcct(gServer).category;
|
||||
autotagEnable.checked = categoryPrefsAcct.enabled;
|
||||
autotagUsePrefix.disabled = !autotagEnable.checked;
|
||||
autotagUsePrefix.checked = categoryPrefsAcct.prefixEnabled;
|
||||
autotagPrefix.disabled = autotagUsePrefix.disabled || !autotagUsePrefix.checked;
|
||||
autotagPrefix.value = categoryPrefsAcct.prefix;
|
||||
}
|
||||
|
||||
function onPreInit(account, accountValues)
|
||||
{
|
||||
gServer = account.incomingServer;
|
||||
}
|
||||
|
||||
function setCategoryPrefs(aNode)
|
||||
{
|
||||
let options = FeedUtils.getOptionsAcct(gServer);
|
||||
switch (aNode.id) {
|
||||
case "autotagEnable":
|
||||
options.category.enabled = aNode.checked;
|
||||
autotagUsePrefix.disabled = !aNode.checked;
|
||||
autotagPrefix.disabled = !aNode.checked || !autotagUsePrefix.checked;
|
||||
break;
|
||||
case "autotagUsePrefix":
|
||||
options.category.prefixEnabled = aNode.checked;
|
||||
autotagPrefix.disabled = aNode.disabled || !aNode.checked;
|
||||
break;
|
||||
case "autotagPrefix":
|
||||
options.category.prefix = aNode.value;
|
||||
break;
|
||||
}
|
||||
|
||||
FeedUtils.setOptionsAcct(gServer, options)
|
||||
}
|
||||
155
mailnews/extensions/newsblog/content/am-newsblog.xul
Normal file
155
mailnews/extensions/newsblog/content/am-newsblog.xul
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://messenger/skin/accountManage.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://messenger-newsblog/skin/feed-subscriptions.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE page [
|
||||
<!ENTITY % newsblogDTD SYSTEM "chrome://messenger-newsblog/locale/am-newsblog.dtd" >
|
||||
%newsblogDTD;
|
||||
<!ENTITY % feedDTD SYSTEM "chrome://messenger-newsblog/locale/feed-subscriptions.dtd" >
|
||||
%feedDTD;
|
||||
<!ENTITY % accountNoIdentDTD SYSTEM "chrome://messenger/locale/am-serverwithnoidentities.dtd" >
|
||||
%accountNoIdentDTD;
|
||||
<!ENTITY % accountServerTopDTD SYSTEM "chrome://messenger/locale/am-server-top.dtd">
|
||||
%accountServerTopDTD;
|
||||
]>
|
||||
|
||||
<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
class="color-dialog"
|
||||
title="&accountTitle.label;"
|
||||
onload="parent.onPanelLoaded('am-newsblog.xul');">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger/content/AccountManager.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger-newsblog/content/am-newsblog.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger-newsblog/content/newsblogOverlay.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger/content/amUtils.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger/content/am-prefs.js"/>
|
||||
|
||||
<vbox flex="1" style="overflow: auto;">
|
||||
|
||||
<dialogheader id="am-newsblog-title" defaultTitle="&accountTitle.label;"/>
|
||||
|
||||
<description class="secDesc">&accountSettingsDesc.label;</description>
|
||||
|
||||
<hbox align="center">
|
||||
<label value="&accountName.label;"
|
||||
accesskey="&accountName.accesskey;"
|
||||
control="server.prettyName"/>
|
||||
<textbox id="server.prettyName"
|
||||
wsm_persist="true"
|
||||
size="30"
|
||||
prefstring="mail.server.%serverkey%.name"/>
|
||||
</hbox>
|
||||
|
||||
<separator class="thin"/>
|
||||
|
||||
<groupbox>
|
||||
<caption label="&serverSettings.label;"/>
|
||||
|
||||
<checkbox id="server.loginAtStartUp"
|
||||
wsm_persist="true"
|
||||
label="&loginAtStartup.label;"
|
||||
accesskey="&loginAtStartup.accesskey;"
|
||||
prefattribute="value"
|
||||
prefstring="mail.server.%serverkey%.login_at_startup"/>
|
||||
|
||||
<hbox align="center">
|
||||
<checkbox id="server.doBiff"
|
||||
wsm_persist="true"
|
||||
label="&biffStart.label;"
|
||||
accesskey="&biffStart.accesskey;"
|
||||
oncommand="onCheckItem('server.biffMinutes', [this.id]);"
|
||||
prefattribute="value"
|
||||
prefstring="mail.server.%serverkey%.check_new_mail"/>
|
||||
<textbox id="server.biffMinutes"
|
||||
wsm_persist="true"
|
||||
type="number"
|
||||
size="3"
|
||||
min="1"
|
||||
increment="1"
|
||||
preftype="int"
|
||||
prefstring="mail.server.%serverkey%.check_time"
|
||||
aria-labelledby="server.doBiff server.biffMinutes biffEnd"/>
|
||||
<label id="biffEnd"
|
||||
value="&biffEnd.label;"
|
||||
control="server.biffMinutes"/>
|
||||
</hbox>
|
||||
|
||||
<checkbox id="server.quickMode"
|
||||
wsm_persist="true"
|
||||
genericattr="true"
|
||||
label="&useQuickMode.label;"
|
||||
accesskey="&useQuickMode.accesskey;"
|
||||
preftype="bool"
|
||||
prefattribute="value"
|
||||
prefstring="mail.server.%serverkey%.quickMode"/>
|
||||
|
||||
<checkbox id="autotagEnable"
|
||||
accesskey="&autotagEnable.accesskey;"
|
||||
label="&autotagEnable.label;"
|
||||
oncommand="setCategoryPrefs(this)"/>
|
||||
<hbox>
|
||||
<checkbox id="autotagUsePrefix"
|
||||
class="indent"
|
||||
accesskey="&autotagUsePrefix.accesskey;"
|
||||
label="&autotagUsePrefix.label;"
|
||||
oncommand="setCategoryPrefs(this)"/>
|
||||
<textbox id="autotagPrefix"
|
||||
placeholder="&autoTagPrefix.placeholder;"
|
||||
clickSelectsAll="true"
|
||||
onchange="setCategoryPrefs(this)"/>
|
||||
</hbox>
|
||||
</groupbox>
|
||||
|
||||
<separator class="thin"/>
|
||||
|
||||
<groupbox>
|
||||
<caption label="&messageStorage.label;"/>
|
||||
|
||||
<checkbox id="server.emptyTrashOnExit"
|
||||
wsm_persist="true"
|
||||
label="&emptyTrashOnExit.label;"
|
||||
accesskey="&emptyTrashOnExit.accesskey;"
|
||||
prefattribute="value"
|
||||
prefstring="mail.server.%serverkey%.empty_trash_on_exit"/>
|
||||
|
||||
<separator class="thin"/>
|
||||
|
||||
<vbox>
|
||||
<label value="&localPath.label;" control="server.localPath"/>
|
||||
<hbox align="center">
|
||||
<textbox readonly="true"
|
||||
wsm_persist="true"
|
||||
flex="1"
|
||||
id="server.localPath"
|
||||
datatype="nsIFile"
|
||||
prefstring="mail.server.%serverkey%.directory"
|
||||
class="uri-element"/>
|
||||
<button id="browseForLocalFolder"
|
||||
label="&browseFolder.label;"
|
||||
filepickertitle="&localFolderPicker.label;"
|
||||
accesskey="&browseFolder.accesskey;"
|
||||
oncommand="BrowseForLocalFolders();"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
</groupbox>
|
||||
|
||||
<separator class="thin"/>
|
||||
|
||||
<hbox align="center">
|
||||
<spacer flex="1"/>
|
||||
<button label="&manageSubscriptions.label;"
|
||||
accesskey="&manageSubscriptions.accesskey;"
|
||||
oncommand="openSubscriptionsDialog(gServer.rootFolder);"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</page>
|
||||
1034
mailnews/extensions/newsblog/content/feed-parser.js
Normal file
1034
mailnews/extensions/newsblog/content/feed-parser.js
Normal file
File diff suppressed because it is too large
Load diff
2703
mailnews/extensions/newsblog/content/feed-subscriptions.js
Normal file
2703
mailnews/extensions/newsblog/content/feed-subscriptions.js
Normal file
File diff suppressed because it is too large
Load diff
235
mailnews/extensions/newsblog/content/feed-subscriptions.xul
Normal file
235
mailnews/extensions/newsblog/content/feed-subscriptions.xul
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://messenger/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://messenger/skin/folderPane.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://messenger/skin/folderMenus.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://messenger-newsblog/skin/feed-subscriptions.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % feedDTD SYSTEM "chrome://messenger-newsblog/locale/feed-subscriptions.dtd">
|
||||
%feedDTD;
|
||||
<!ENTITY % certDTD SYSTEM "chrome://pippki/locale/certManager.dtd">
|
||||
%certDTD;
|
||||
]>
|
||||
|
||||
<window id="subscriptionsDialog"
|
||||
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();"
|
||||
onkeypress="FeedSubscriptions.onKeyPress(event);"
|
||||
onmousedown="FeedSubscriptions.onMouseDown(event);">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger/content/specialTabs.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger-newsblog/content/feed-subscriptions.js"/>
|
||||
|
||||
<keyset id="extensionsKeys">
|
||||
<key id="key_close"
|
||||
key="&cmd.close.commandKey;"
|
||||
modifiers="accel"
|
||||
oncommand="window.close();"/>
|
||||
<key id="key_close2"
|
||||
keycode="VK_ESCAPE"
|
||||
oncommand="window.close();"/>
|
||||
</keyset>
|
||||
|
||||
<stringbundle id="bundle_newsblog"
|
||||
src="chrome://messenger-newsblog/locale/newsblog.properties"/>
|
||||
<stringbundle id="bundle_brand"
|
||||
src="chrome://branding/locale/brand.properties"/>
|
||||
|
||||
<vbox flex="1" id="contentPane">
|
||||
<hbox align="right">
|
||||
<label id="learnMore"
|
||||
class="text-link"
|
||||
crop="end"
|
||||
value="&learnMore.label;"
|
||||
href="https://support.mozilla.org/kb/how-subscribe-news-feeds-and-blogs"/>
|
||||
</hbox>
|
||||
|
||||
<tree id="rssSubscriptionsList"
|
||||
treelines="true"
|
||||
flex="1"
|
||||
hidecolumnpicker="true"
|
||||
onselect="FeedSubscriptions.onSelect();"
|
||||
seltype="single">
|
||||
<treecols>
|
||||
<treecol id="folderNameCol"
|
||||
flex="2"
|
||||
primary="true"
|
||||
hideheader="true"/>
|
||||
</treecols>
|
||||
<treechildren id="subscriptionChildren"
|
||||
ondragstart="FeedSubscriptions.onDragStart(event);"
|
||||
ondragover="FeedSubscriptions.onDragOver(event);"/>
|
||||
</tree>
|
||||
|
||||
<hbox id="rssFeedInfoBox">
|
||||
<vbox flex="1">
|
||||
<grid flex="1">
|
||||
<columns>
|
||||
<column/>
|
||||
<column flex="1"/>
|
||||
</columns>
|
||||
<rows>
|
||||
<row>
|
||||
<hbox align="right" valign="middle">
|
||||
<label id="nameLabel"
|
||||
accesskey="&feedTitle.accesskey;"
|
||||
control="nameValue"
|
||||
value="&feedTitle.label;"/>
|
||||
</hbox>
|
||||
<textbox id="nameValue"
|
||||
clickSelectsAll="true"/>
|
||||
</row>
|
||||
<row>
|
||||
<hbox align="right" valign="middle">
|
||||
<label id="locationLabel"
|
||||
accesskey="&feedLocation.accesskey;"
|
||||
control="locationValue"
|
||||
value="&feedLocation.label;"/>
|
||||
</hbox>
|
||||
<hbox>
|
||||
<textbox id="locationValue"
|
||||
flex="1"
|
||||
class="uri-element"
|
||||
placeholder="&feedLocation.placeholder;"
|
||||
clickSelectsAll="true"
|
||||
onfocus="FeedSubscriptions.setSummaryFocus();"
|
||||
onblur="FeedSubscriptions.setSummaryFocus();"/>
|
||||
<hbox align="center">
|
||||
<label id="locationValidate"
|
||||
collapsed="true"
|
||||
class="text-link"
|
||||
crop="end"
|
||||
value="&locationValidate.label;"
|
||||
onclick="FeedSubscriptions.checkValidation(event);"/>
|
||||
</hbox>
|
||||
</hbox>
|
||||
</row>
|
||||
<row>
|
||||
<hbox align="right" valign="middle">
|
||||
<label id="feedFolderLabel"
|
||||
value="&feedFolder.label;"
|
||||
accesskey="&feedFolder.accesskey;"
|
||||
control="selectFolder"/>
|
||||
</hbox>
|
||||
<hbox>
|
||||
<menulist id="selectFolder"
|
||||
flex="1"
|
||||
class="folderMenuItem"
|
||||
hidden="true">
|
||||
<menupopup id="selectFolderPopup"
|
||||
class="menulist-menupopup"
|
||||
type="folder"
|
||||
mode="feeds"
|
||||
showFileHereLabel="true"
|
||||
showAccountsFileHere="true"
|
||||
oncommand="FeedSubscriptions.setNewFolder(event)"/>
|
||||
</menulist>
|
||||
<textbox id="selectFolderValue"
|
||||
flex="1"
|
||||
readonly="true"
|
||||
onkeypress="FeedSubscriptions.onClickSelectFolderValue(event)"
|
||||
onclick="FeedSubscriptions.onClickSelectFolderValue(event)"/>
|
||||
</hbox>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
<checkbox id="quickMode"
|
||||
accesskey="&quickMode.accesskey;"
|
||||
label="&quickMode.label;"
|
||||
oncommand="FeedSubscriptions.setSummary(this.checked)"/>
|
||||
<checkbox id="autotagEnable"
|
||||
accesskey="&autotagEnable.accesskey;"
|
||||
label="&autotagEnable.label;"
|
||||
oncommand="FeedSubscriptions.setCategoryPrefs(this)"/>
|
||||
<hbox>
|
||||
<checkbox id="autotagUsePrefix"
|
||||
class="indent"
|
||||
accesskey="&autotagUsePrefix.accesskey;"
|
||||
label="&autotagUsePrefix.label;"
|
||||
oncommand="FeedSubscriptions.setCategoryPrefs(this)"/>
|
||||
<textbox id="autotagPrefix"
|
||||
placeholder="&autoTagPrefix.placeholder;"
|
||||
clickSelectsAll="true"/>
|
||||
</hbox>
|
||||
<separator class="thin"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
|
||||
<hbox id="statusContainerBox"
|
||||
align="center"
|
||||
valign="middle">
|
||||
<vbox flex="1">
|
||||
<description id="statusText"/>
|
||||
</vbox>
|
||||
<spacer flex="1"/>
|
||||
<label id="validationText"
|
||||
collapsed="true"
|
||||
class="text-link"
|
||||
crop="end"
|
||||
value="&validateText.label;"
|
||||
onclick="FeedSubscriptions.checkValidation(event);"/>
|
||||
<button id="addCertException"
|
||||
collapsed="true"
|
||||
label="&certmgr.addException.label;"
|
||||
accesskey="&certmgr.addException.accesskey;"
|
||||
oncommand="FeedSubscriptions.addCertExceptionDialog();"/>
|
||||
<progressmeter id="progressMeter"
|
||||
collapsed="true"
|
||||
mode="determined"
|
||||
value="0"/>
|
||||
</hbox>
|
||||
|
||||
<hbox align="end">
|
||||
<hbox class="actionButtons" flex="1">
|
||||
<button id="addFeed"
|
||||
label="&button.addFeed.label;"
|
||||
accesskey="&button.addFeed.accesskey;"
|
||||
oncommand="FeedSubscriptions.addFeed();"/>
|
||||
|
||||
<button id="editFeed"
|
||||
disabled="true"
|
||||
label="&button.updateFeed.label;"
|
||||
accesskey="&button.updateFeed.accesskey;"
|
||||
oncommand="FeedSubscriptions.editFeed();"/>
|
||||
|
||||
<button id="removeFeed"
|
||||
disabled="true"
|
||||
label="&button.removeFeed.label;"
|
||||
accesskey="&button.removeFeed.accesskey;"
|
||||
oncommand="FeedSubscriptions.removeFeed(true);"/>
|
||||
|
||||
<button id="importOPML"
|
||||
label="&button.importOPML.label;"
|
||||
accesskey="&button.importOPML.accesskey;"
|
||||
oncommand="FeedSubscriptions.importOPML();"/>
|
||||
|
||||
<button id="exportOPML"
|
||||
label="&button.exportOPML.label;"
|
||||
accesskey="&button.exportOPML.accesskey;"
|
||||
tooltiptext="&button.exportOPML.tooltip;"
|
||||
oncommand="FeedSubscriptions.exportOPML(event);"/>
|
||||
|
||||
<spacer flex="1"/>
|
||||
|
||||
<button id="close"
|
||||
label="&button.close.label;"
|
||||
icon="close"
|
||||
oncommand="if (FeedSubscriptions.onClose()) window.close();"/>
|
||||
</hbox>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</window>
|
||||
45
mailnews/extensions/newsblog/content/feedAccountWizard.js
Normal file
45
mailnews/extensions/newsblog/content/feedAccountWizard.js
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
Components.utils.import("resource:///modules/FeedUtils.jsm");
|
||||
|
||||
/* Feed account standalone wizard functions */
|
||||
var FeedAccountWizard = {
|
||||
accountName: "",
|
||||
|
||||
accountSetupPageInit: function() {
|
||||
this.accountSetupPageValidate();
|
||||
},
|
||||
|
||||
accountSetupPageValidate: function() {
|
||||
this.accountName = document.getElementById("prettyName").value.trim();
|
||||
document.documentElement.canAdvance = this.accountName;
|
||||
},
|
||||
|
||||
accountSetupPageUnload: function() {
|
||||
return;
|
||||
},
|
||||
|
||||
donePageInit: function() {
|
||||
document.getElementById("account.name.text").value = this.accountName;
|
||||
},
|
||||
|
||||
onCancel: function() {
|
||||
return true;
|
||||
},
|
||||
|
||||
onFinish: function() {
|
||||
let account = FeedUtils.createRssAccount(this.accountName);
|
||||
if ("gFolderTreeView" in window.opener.top)
|
||||
// Opened from 3pane File->New or Appmenu New Message, or
|
||||
// Account Central link.
|
||||
window.opener.top.gFolderTreeView.selectFolder(account.incomingServer.rootMsgFolder);
|
||||
else if ("selectServer" in window.opener)
|
||||
// Opened from Account Settings.
|
||||
window.opener.selectServer(account.incomingServer);
|
||||
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
79
mailnews/extensions/newsblog/content/feedAccountWizard.xul
Normal file
79
mailnews/extensions/newsblog/content/feedAccountWizard.xul
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://messenger/skin/accountWizard.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE wizard [
|
||||
<!ENTITY % accountDTD SYSTEM "chrome://messenger/locale/AccountWizard.dtd">
|
||||
%accountDTD;
|
||||
<!ENTITY % newsblogDTD SYSTEM "chrome://messenger-newsblog/locale/am-newsblog.dtd" >
|
||||
%newsblogDTD;
|
||||
<!ENTITY % imDTD SYSTEM "chrome://messenger/locale/imAccountWizard.dtd" >
|
||||
%imDTD;
|
||||
]>
|
||||
|
||||
<wizard id="FeedAccountWizard"
|
||||
title="&feedWindowTitle.label;"
|
||||
onwizardcancel="return FeedAccountWizard.onCancel();"
|
||||
onwizardfinish="return FeedAccountWizard.onFinish();"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://messenger-newsblog/content/feedAccountWizard.js"/>
|
||||
|
||||
<!-- Account setup page : User gets a choice to enter a name for the account -->
|
||||
<!-- Defaults : Feed account name -> default string -->
|
||||
<wizardpage id="accountsetuppage"
|
||||
pageid="accountsetuppage"
|
||||
label="&accnameTitle.label;"
|
||||
onpageshow="return FeedAccountWizard.accountSetupPageInit();"
|
||||
onpageadvanced="return FeedAccountWizard.accountSetupPageUnload();">
|
||||
<vbox flex="1">
|
||||
<description>&accnameDesc.label;</description>
|
||||
<separator class="thin"/>
|
||||
<hbox align="center">
|
||||
<label class="label"
|
||||
value="&accnameLabel.label;"
|
||||
accesskey="&accnameLabel.accesskey;"
|
||||
control="prettyName"/>
|
||||
<textbox id="prettyName"
|
||||
flex="1"
|
||||
value="&feeds.accountName;"
|
||||
oninput="FeedAccountWizard.accountSetupPageValidate();"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</wizardpage>
|
||||
|
||||
<!-- Done page : Summarizes information collected to create a feed account -->
|
||||
<wizardpage id="done"
|
||||
pageid="done"
|
||||
label="&accountSummaryTitle.label;"
|
||||
onpageshow="return FeedAccountWizard.donePageInit();">
|
||||
<vbox flex="1">
|
||||
<description>&accountSummaryInfo.label;</description>
|
||||
<separator class="thin"/>
|
||||
<grid>
|
||||
<columns>
|
||||
<column/>
|
||||
<column flex="1"/>
|
||||
</columns>
|
||||
<rows>
|
||||
<row id="account.name"
|
||||
align="center">
|
||||
<label id="account.name.label"
|
||||
class="label"
|
||||
flex="1"
|
||||
value="&accnameLabel.label;"/>
|
||||
<label id="account.name.text"
|
||||
class="label"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
<separator/>
|
||||
<spacer flex="1"/>
|
||||
</vbox>
|
||||
</wizardpage>
|
||||
|
||||
</wizard>
|
||||
363
mailnews/extensions/newsblog/content/newsblogOverlay.js
Normal file
363
mailnews/extensions/newsblog/content/newsblogOverlay.js
Normal file
|
|
@ -0,0 +1,363 @@
|
|||
/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
Components.utils.import("resource:///modules/gloda/mimemsg.js");
|
||||
Components.utils.import("resource:///modules/mailServices.js");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// This global is for SeaMonkey compatibility.
|
||||
var gShowFeedSummary;
|
||||
|
||||
var FeedMessageHandler = {
|
||||
gShowSummary: true,
|
||||
gToggle: false,
|
||||
kSelectOverrideWebPage: 0,
|
||||
kSelectOverrideSummary: 1,
|
||||
kSelectFeedDefault: 2,
|
||||
kOpenWebPage: 0,
|
||||
kOpenSummary: 1,
|
||||
kOpenToggleInMessagePane: 2,
|
||||
kOpenLoadInBrowser: 3,
|
||||
|
||||
/**
|
||||
* How to load message on threadpane select.
|
||||
*/
|
||||
get onSelectPref() {
|
||||
return Services.prefs.getIntPref("rss.show.summary");
|
||||
},
|
||||
|
||||
set onSelectPref(val) {
|
||||
Services.prefs.setIntPref("rss.show.summary", val);
|
||||
ReloadMessage();
|
||||
},
|
||||
|
||||
/**
|
||||
* Load web page on threadpane select.
|
||||
*/
|
||||
get loadWebPageOnSelectPref() {
|
||||
return Services.prefs.getIntPref("rss.message.loadWebPageOnSelect") ? true : false;
|
||||
},
|
||||
|
||||
/**
|
||||
* How to load message on open (enter/dbl click in threadpane, contextmenu).
|
||||
*/
|
||||
get onOpenPref() {
|
||||
return Services.prefs.getIntPref("rss.show.content-base");
|
||||
},
|
||||
|
||||
set onOpenPref(val) {
|
||||
Services.prefs.setIntPref("rss.show.content-base", val);
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine if a message is a feed message. Prior to Tb15, a message had to
|
||||
* be in an rss acount type folder. In Tb15 and later, a flag is set on the
|
||||
* message itself upon initial store; the message can be moved to any folder.
|
||||
*
|
||||
* @param nsIMsgDBHdr aMsgHdr - the message.
|
||||
*
|
||||
* @return true if message is a feed, false if not.
|
||||
*/
|
||||
isFeedMessage: function(aMsgHdr) {
|
||||
return (aMsgHdr instanceof Components.interfaces.nsIMsgDBHdr) &&
|
||||
((aMsgHdr.flags & Components.interfaces.nsMsgMessageFlags.FeedMsg) ||
|
||||
(aMsgHdr.folder && aMsgHdr.folder.server.type == "rss"));
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine whether to show a feed message summary or load a web page in the
|
||||
* message pane.
|
||||
*
|
||||
* @param nsIMsgDBHdr aMsgHdr - the message.
|
||||
* @param bool aToggle - true if in toggle mode, false otherwise.
|
||||
*
|
||||
* @return true if summary is to be displayed, false if web page.
|
||||
*/
|
||||
shouldShowSummary: function(aMsgHdr, aToggle) {
|
||||
// Not a feed message, always show summary (the message).
|
||||
if (!this.isFeedMessage(aMsgHdr))
|
||||
return true;
|
||||
|
||||
// Notified of a summary reload when toggling, reset toggle and return.
|
||||
if (!aToggle && this.gToggle)
|
||||
return !(this.gToggle = false);
|
||||
|
||||
let showSummary = true;
|
||||
this.gToggle = aToggle;
|
||||
|
||||
// Thunderbird 2 rss messages with 'Show article summary' not selected,
|
||||
// ie message body constructed to show web page in an iframe, can't show
|
||||
// a summary - notify user.
|
||||
let browser = getBrowser();
|
||||
let contentDoc = browser ? browser.contentDocument : null;
|
||||
let rssIframe = contentDoc ? contentDoc.getElementById("_mailrssiframe") : null;
|
||||
if (rssIframe) {
|
||||
if (this.gToggle || this.onSelectPref == this.kSelectOverrideSummary)
|
||||
this.gToggle = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aToggle)
|
||||
// Toggle mode, flip value.
|
||||
return gShowFeedSummary = this.gShowSummary = !this.gShowSummary;
|
||||
|
||||
let wintype = document.documentElement.getAttribute("windowtype");
|
||||
let tabMail = document.getElementById("tabmail");
|
||||
let messageTab = tabMail && tabMail.currentTabInfo.mode.type == "message";
|
||||
let messageWindow = wintype == "mail:messageWindow";
|
||||
|
||||
switch (this.onSelectPref) {
|
||||
case this.kSelectOverrideWebPage:
|
||||
showSummary = false;
|
||||
break;
|
||||
case this.kSelectOverrideSummary:
|
||||
showSummary = true
|
||||
break;
|
||||
case this.kSelectFeedDefault:
|
||||
// Get quickmode per feed folder pref from feeds.rdf. If the feed
|
||||
// message is not in a feed account folder (hence the folder is not in
|
||||
// the feeds database), or FZ_QUICKMODE property is not found (possible
|
||||
// in pre renovation urls), err on the side of showing the summary.
|
||||
// For the former, toggle or global override is necessary; for the
|
||||
// latter, a show summary checkbox toggle in Subscribe dialog will set
|
||||
// one on the path to bliss.
|
||||
let folder = aMsgHdr.folder, targetRes;
|
||||
try {
|
||||
targetRes = FeedUtils.getParentTargetForChildResource(
|
||||
folder.URI, FeedUtils.FZ_QUICKMODE, folder.server);
|
||||
}
|
||||
catch (ex) {
|
||||
// Not in a feed account folder or other error.
|
||||
FeedUtils.log.info("FeedMessageHandler.shouldShowSummary: could not " +
|
||||
"get summary pref for this folder");
|
||||
}
|
||||
|
||||
showSummary = targetRes && targetRes.QueryInterface(Ci.nsIRDFLiteral).
|
||||
Value == "false" ? false : true;
|
||||
break;
|
||||
}
|
||||
|
||||
gShowFeedSummary = this.gShowSummary = showSummary;
|
||||
|
||||
if (messageWindow || messageTab) {
|
||||
// Message opened in either standalone window or tab, due to either
|
||||
// message open pref (we are here only if the pref is 0 or 1) or
|
||||
// contextmenu open.
|
||||
switch (this.onOpenPref) {
|
||||
case this.kOpenToggleInMessagePane:
|
||||
// Opened by contextmenu, use the value derived above.
|
||||
// XXX: allow a toggle via crtl?
|
||||
break;
|
||||
case this.kOpenWebPage:
|
||||
showSummary = false;
|
||||
break;
|
||||
case this.kOpenSummary:
|
||||
showSummary = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Auto load web page in browser on select, per pref; shouldShowSummary() is
|
||||
// always called first to 1)test if feed, 2)get summary pref, so do it here.
|
||||
if (this.loadWebPageOnSelectPref)
|
||||
setTimeout(FeedMessageHandler.loadWebPage, 20, aMsgHdr, {browser:true});
|
||||
|
||||
return showSummary;
|
||||
},
|
||||
|
||||
/**
|
||||
* Load a web page for feed messages. Use MsgHdrToMimeMessage() to get
|
||||
* the content-base url from the message headers. We cannot rely on
|
||||
* currentHeaderData; it has not yet been streamed at our entry point in
|
||||
* displayMessageChanged(), and in the case of a collapsed message pane it
|
||||
* is not streamed.
|
||||
*
|
||||
* @param nsIMsgDBHdr aMessageHdr - the message.
|
||||
* @param {obj} aWhere - name value=true pair, where name is in:
|
||||
* 'messagepane', 'browser', 'tab', 'window'.
|
||||
*/
|
||||
loadWebPage: function(aMessageHdr, aWhere) {
|
||||
MsgHdrToMimeMessage(aMessageHdr, null, function(aMsgHdr, aMimeMsg) {
|
||||
if (aMimeMsg && aMimeMsg.headers["content-base"] &&
|
||||
aMimeMsg.headers["content-base"][0]) {
|
||||
let url = aMimeMsg.headers["content-base"], uri;
|
||||
try {
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
url = converter.ConvertToUnicode(url);
|
||||
uri = Services.io.newURI(url, null, null);
|
||||
url = uri.spec;
|
||||
}
|
||||
catch (ex) {
|
||||
FeedUtils.log.info("FeedMessageHandler.loadWebPage: " +
|
||||
"invalid Content-Base header url - " + url);
|
||||
return;
|
||||
}
|
||||
if (aWhere.browser)
|
||||
Components.classes["@mozilla.org/uriloader/external-protocol-service;1"]
|
||||
.getService(Components.interfaces.nsIExternalProtocolService)
|
||||
.loadURI(uri);
|
||||
else if (aWhere.messagepane) {
|
||||
let loadFlag = getBrowser().webNavigation.LOAD_FLAGS_NONE;
|
||||
getBrowser().webNavigation.loadURI(url, loadFlag, null, null, null);
|
||||
}
|
||||
else if (aWhere.tab)
|
||||
openContentTab(url, "tab", "^");
|
||||
else if (aWhere.window)
|
||||
openContentTab(url, "window", "^");
|
||||
}
|
||||
else
|
||||
FeedUtils.log.info("FeedMessageHandler.loadWebPage: could not get " +
|
||||
"Content-Base header url for this message");
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Display summary or load web page for feed messages. Caller should already
|
||||
* know if the message is a feed message.
|
||||
*
|
||||
* @param nsIMsgDBHdr aMsgHdr - the message.
|
||||
* @param bool aShowSummary - true if summary is to be displayed, false if
|
||||
* web page.
|
||||
*/
|
||||
setContent: function(aMsgHdr, aShowSummary) {
|
||||
if (aShowSummary) {
|
||||
// Only here if toggling to summary in 3pane.
|
||||
if (this.gToggle && gDBView && GetNumSelectedMessages() == 1)
|
||||
ReloadMessage();
|
||||
}
|
||||
else {
|
||||
let browser = getBrowser();
|
||||
if (browser && browser.contentDocument && browser.contentDocument.body)
|
||||
browser.contentDocument.body.hidden = true;
|
||||
// If in a non rss folder, hide possible remote content bar on a web
|
||||
// page load, as it doesn't apply.
|
||||
if ("msgNotificationBar" in window)
|
||||
gMessageNotificationBar.clearMsgNotifications();
|
||||
|
||||
this.loadWebPage(aMsgHdr, {messagepane:true});
|
||||
this.gToggle = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function openSubscriptionsDialog(aFolder)
|
||||
{
|
||||
// Check for an existing feed subscriptions window and focus it.
|
||||
let subscriptionsWindow =
|
||||
Services.wm.getMostRecentWindow("Mail:News-BlogSubscriptions");
|
||||
|
||||
if (subscriptionsWindow)
|
||||
{
|
||||
if (aFolder)
|
||||
{
|
||||
subscriptionsWindow.FeedSubscriptions.selectFolder(aFolder);
|
||||
subscriptionsWindow.FeedSubscriptions.mView.treeBox.ensureRowIsVisible(
|
||||
subscriptionsWindow.FeedSubscriptions.mView.selection.currentIndex);
|
||||
}
|
||||
|
||||
subscriptionsWindow.focus();
|
||||
}
|
||||
else
|
||||
{
|
||||
window.openDialog("chrome://messenger-newsblog/content/feed-subscriptions.xul",
|
||||
"", "centerscreen,chrome,dialog=no,resizable",
|
||||
{ folder: aFolder});
|
||||
}
|
||||
}
|
||||
|
||||
// Special case attempts to reply/forward/edit as new RSS articles. For
|
||||
// messages stored prior to Tb15, we are here only if the message's folder's
|
||||
// account server is rss and feed messages moved to other types will have their
|
||||
// summaries loaded, as viewing web pages only happened in an rss account.
|
||||
// The user may choose whether to load a summary or web page link by ensuring
|
||||
// the current feed message is being viewed as either a summary or web page.
|
||||
function openComposeWindowForRSSArticle(aMsgComposeWindow, aMsgHdr, aMessageUri,
|
||||
aType, aFormat, aIdentity, aMsgWindow)
|
||||
{
|
||||
// Ensure right content is handled for web pages in window/tab.
|
||||
let tabmail = document.getElementById("tabmail");
|
||||
let is3pane = tabmail && tabmail.selectedTab && tabmail.selectedTab.mode ?
|
||||
tabmail.selectedTab.mode.type == "folder" : false;
|
||||
let showingwebpage = ("FeedMessageHandler" in window) && !is3pane &&
|
||||
FeedMessageHandler.onOpenPref == FeedMessageHandler.kOpenWebPage;
|
||||
|
||||
if (gShowFeedSummary && !showingwebpage)
|
||||
{
|
||||
// The user is viewing the summary.
|
||||
MailServices.compose.OpenComposeWindow(aMsgComposeWindow, aMsgHdr, aMessageUri,
|
||||
aType, aFormat, aIdentity, aMsgWindow);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set up the compose message and get the feed message's web page link.
|
||||
let Cc = Components.classes;
|
||||
let Ci = Components.interfaces;
|
||||
let msgHdr = aMsgHdr;
|
||||
let type = aType;
|
||||
let msgComposeType = Ci.nsIMsgCompType;
|
||||
let subject = msgHdr.mime2DecodedSubject;
|
||||
let fwdPrefix = Services.prefs.getCharPref("mail.forward_subject_prefix");
|
||||
fwdPrefix = fwdPrefix ? fwdPrefix + ": " : "";
|
||||
|
||||
let params = Cc["@mozilla.org/messengercompose/composeparams;1"]
|
||||
.createInstance(Ci.nsIMsgComposeParams);
|
||||
|
||||
let composeFields = Cc["@mozilla.org/messengercompose/composefields;1"]
|
||||
.createInstance(Ci.nsIMsgCompFields);
|
||||
|
||||
if (type == msgComposeType.Reply ||
|
||||
type == msgComposeType.ReplyAll ||
|
||||
type == msgComposeType.ReplyToSender ||
|
||||
type == msgComposeType.ReplyToGroup ||
|
||||
type == msgComposeType.ReplyToSenderAndGroup)
|
||||
{
|
||||
subject = "Re: " + subject;
|
||||
}
|
||||
else if (type == msgComposeType.ForwardInline ||
|
||||
type == msgComposeType.ForwardAsAttachment)
|
||||
{
|
||||
subject = fwdPrefix + subject;
|
||||
}
|
||||
|
||||
params.composeFields = composeFields;
|
||||
params.composeFields.subject = subject;
|
||||
params.composeFields.characterSet = msgHdr.Charset;
|
||||
params.composeFields.body = "";
|
||||
params.bodyIsLink = false;
|
||||
params.identity = aIdentity;
|
||||
|
||||
try
|
||||
{
|
||||
// The feed's web page url is stored in the Content-Base header.
|
||||
MsgHdrToMimeMessage(msgHdr, null, function(aMsgHdr, aMimeMsg) {
|
||||
if (aMimeMsg && aMimeMsg.headers["content-base"] &&
|
||||
aMimeMsg.headers["content-base"][0])
|
||||
{
|
||||
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
|
||||
.createInstance(Ci.nsIScriptableUnicodeConverter);
|
||||
converter.charset = "UTF-8";
|
||||
let url = converter.ConvertToUnicode(aMimeMsg.headers["content-base"]);
|
||||
params.composeFields.body = url;
|
||||
params.bodyIsLink = true;
|
||||
MailServices.compose.OpenComposeWindowWithParams(null, params);
|
||||
}
|
||||
else
|
||||
// No content-base url, use the summary.
|
||||
MailServices.compose.OpenComposeWindow(aMsgComposeWindow, aMsgHdr, aMessageUri,
|
||||
aType, aFormat, aIdentity, aMsgWindow);
|
||||
|
||||
}, false, {saneBodySize: true});
|
||||
}
|
||||
catch (ex)
|
||||
{
|
||||
// Error getting header, use the summary.
|
||||
MailServices.compose.OpenComposeWindow(aMsgComposeWindow, aMsgHdr, aMessageUri,
|
||||
aType, aFormat, aIdentity, aMsgWindow);
|
||||
}
|
||||
}
|
||||
}
|
||||
16
mailnews/extensions/newsblog/jar.mn
Normal file
16
mailnews/extensions/newsblog/jar.mn
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
newsblog.jar:
|
||||
% content messenger-newsblog %content/messenger-newsblog/
|
||||
content/messenger-newsblog/newsblogOverlay.js (content/newsblogOverlay.js)
|
||||
content/messenger-newsblog/Feed.js (content/Feed.js)
|
||||
content/messenger-newsblog/FeedItem.js (content/FeedItem.js)
|
||||
content/messenger-newsblog/feed-parser.js (content/feed-parser.js)
|
||||
* content/messenger-newsblog/feed-subscriptions.js (content/feed-subscriptions.js)
|
||||
content/messenger-newsblog/feed-subscriptions.xul (content/feed-subscriptions.xul)
|
||||
content/messenger-newsblog/am-newsblog.js (content/am-newsblog.js)
|
||||
content/messenger-newsblog/am-newsblog.xul (content/am-newsblog.xul)
|
||||
content/messenger-newsblog/feedAccountWizard.js (content/feedAccountWizard.js)
|
||||
content/messenger-newsblog/feedAccountWizard.xul (content/feedAccountWizard.xul)
|
||||
99
mailnews/extensions/newsblog/js/newsblog.js
Normal file
99
mailnews/extensions/newsblog/js/newsblog.js
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource:///modules/FeedUtils.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
var nsNewsBlogFeedDownloader =
|
||||
{
|
||||
downloadFeed: function(aFolder, aUrlListener, aIsBiff, aMsgWindow)
|
||||
{
|
||||
FeedUtils.downloadFeed(aFolder, aUrlListener, aIsBiff, aMsgWindow);
|
||||
},
|
||||
|
||||
subscribeToFeed: function(aUrl, aFolder, aMsgWindow)
|
||||
{
|
||||
FeedUtils.subscribeToFeed(aUrl, aFolder, aMsgWindow);
|
||||
},
|
||||
|
||||
updateSubscriptionsDS: function(aFolder, aOrigFolder, aAction)
|
||||
{
|
||||
FeedUtils.updateSubscriptionsDS(aFolder, aOrigFolder, aAction);
|
||||
},
|
||||
|
||||
QueryInterface: function(aIID)
|
||||
{
|
||||
if (aIID.equals(Ci.nsINewsBlogFeedDownloader) ||
|
||||
aIID.equals(Ci.nsISupports))
|
||||
return this;
|
||||
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
var nsNewsBlogAcctMgrExtension =
|
||||
{
|
||||
name: "newsblog",
|
||||
chromePackageName: "messenger-newsblog",
|
||||
showPanel: function (server)
|
||||
{
|
||||
return false;
|
||||
},
|
||||
QueryInterface: function(aIID)
|
||||
{
|
||||
if (aIID.equals(Ci.nsIMsgAccountManagerExtension) ||
|
||||
aIID.equals(Ci.nsISupports))
|
||||
return this;
|
||||
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
function FeedDownloader() {}
|
||||
|
||||
FeedDownloader.prototype =
|
||||
{
|
||||
classID: Components.ID("{5c124537-adca-4456-b2b5-641ab687d1f6}"),
|
||||
_xpcom_factory:
|
||||
{
|
||||
createInstance: function (aOuter, aIID)
|
||||
{
|
||||
if (aOuter != null)
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
if (!aIID.equals(Ci.nsINewsBlogFeedDownloader) &&
|
||||
!aIID.equals(Ci.nsISupports))
|
||||
throw Cr.NS_ERROR_INVALID_ARG;
|
||||
|
||||
// return the singleton
|
||||
return nsNewsBlogFeedDownloader.QueryInterface(aIID);
|
||||
}
|
||||
} // factory
|
||||
}; // feed downloader
|
||||
|
||||
function AcctMgrExtension() {}
|
||||
|
||||
AcctMgrExtension.prototype =
|
||||
{
|
||||
classID: Components.ID("{E109C05F-D304-4ca5-8C44-6DE1BFAF1F74}"),
|
||||
_xpcom_factory:
|
||||
{
|
||||
createInstance: function (aOuter, aIID)
|
||||
{
|
||||
if (aOuter != null)
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
if (!aIID.equals(Ci.nsIMsgAccountManagerExtension) &&
|
||||
!aIID.equals(Ci.nsISupports))
|
||||
throw Cr.NS_ERROR_INVALID_ARG;
|
||||
|
||||
// return the singleton
|
||||
return nsNewsBlogAcctMgrExtension.QueryInterface(aIID);
|
||||
}
|
||||
} // factory
|
||||
}; // account manager extension
|
||||
|
||||
var components = [FeedDownloader, AcctMgrExtension];
|
||||
var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
|
||||
5
mailnews/extensions/newsblog/js/newsblog.manifest
Normal file
5
mailnews/extensions/newsblog/js/newsblog.manifest
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
component {5c124537-adca-4456-b2b5-641ab687d1f6} newsblog.js
|
||||
contract @mozilla.org/newsblog-feed-downloader;1 {5c124537-adca-4456-b2b5-641ab687d1f6}
|
||||
component {E109C05F-D304-4ca5-8C44-6DE1BFAF1F74} newsblog.js
|
||||
contract @mozilla.org/accountmanager/extension;1?name=newsblog {E109C05F-D304-4ca5-8C44-6DE1BFAF1F74}
|
||||
category mailnews-accountmanager-extensions newsblog @mozilla.org/accountmanager/extension;1?name=newsblog
|
||||
18
mailnews/extensions/newsblog/moz.build
Normal file
18
mailnews/extensions/newsblog/moz.build
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'js/newsblog.js',
|
||||
'js/newsblog.manifest',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'content/FeedUtils.jsm',
|
||||
]
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
FINAL_TARGET_FILES.isp += [
|
||||
'rss.rdf',
|
||||
]
|
||||
43
mailnews/extensions/newsblog/rss.rdf
Normal file
43
mailnews/extensions/newsblog/rss.rdf
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE RDF SYSTEM "chrome://messenger-newsblog/locale/am-newsblog.dtd">
|
||||
<RDF:RDF
|
||||
xmlns:NC="http://home.netscape.com/NC-rdf#"
|
||||
xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
||||
<RDF:Description about="NC:ispinfo">
|
||||
<NC:providers>
|
||||
<NC:nsIMsgAccount about="newsblog">
|
||||
|
||||
<!-- server info -->
|
||||
<NC:incomingServer>
|
||||
<NC:nsIMsgIncomingServer>
|
||||
<NC:hostName>Feeds</NC:hostName>
|
||||
<NC:type>rss</NC:type>
|
||||
<NC:biffMinutes>100</NC:biffMinutes>
|
||||
<NC:username>nobody</NC:username>
|
||||
</NC:nsIMsgIncomingServer>
|
||||
</NC:incomingServer>
|
||||
|
||||
<!-- identity defaults -->
|
||||
<NC:identity>
|
||||
<NC:nsIMsgIdentity>
|
||||
</NC:nsIMsgIdentity>
|
||||
</NC:identity>
|
||||
|
||||
<NC:wizardAutoGenerateUniqueHostname>true</NC:wizardAutoGenerateUniqueHostname>
|
||||
<NC:wizardHideIncoming>true</NC:wizardHideIncoming>
|
||||
<NC:wizardAccountName>&feeds.accountName;</NC:wizardAccountName>
|
||||
<NC:wizardSkipPanels>identitypage,incomingpage,outgoingpage</NC:wizardSkipPanels>
|
||||
<NC:wizardShortName>&feeds.wizardShortName;</NC:wizardShortName>
|
||||
<NC:wizardLongName>&feeds.wizardLongName;</NC:wizardLongName>
|
||||
<NC:wizardLongNameAccesskey>&feeds.wizardLongName.accesskey;</NC:wizardLongNameAccesskey>
|
||||
<NC:wizardShow>true</NC:wizardShow>
|
||||
<NC:emailProviderName>RSS</NC:emailProviderName>
|
||||
<NC:showServerDetailsOnWizardSummary>false</NC:showServerDetailsOnWizardSummary>
|
||||
</NC:nsIMsgAccount>
|
||||
</NC:providers>
|
||||
</RDF:Description>
|
||||
</RDF:RDF>
|
||||
Loading…
Add table
Add a link
Reference in a new issue