From 779ba78259e46cfdb97acc5ab5e2b835ecae8c0c Mon Sep 17 00:00:00 2001 From: "scott%scott-macgregor.org" Date: Tue, 27 Jul 2004 19:34:05 +0000 Subject: [PATCH] Bug #253055 --> Use a folder listener to track folder name changes and folder deletions of RSS folders so we can keep our subscription data source up to date. Part of the Thunderbird RSS work. sr=bienvenu --- mail/extensions/newsblog/content/Feed.js | 4 + .../newsblog/content/feed-properties.xul | 40 +++-- mail/extensions/newsblog/content/utils.js | 3 + mail/extensions/newsblog/js/newsblog.js | 39 +++++ .../public/nsINewsBlogFeedDownloader.idl | 5 + mailnews/local/src/nsRssIncomingServer.cpp | 137 +++++++++++++++++- mailnews/local/src/nsRssIncomingServer.h | 7 +- 7 files changed, 214 insertions(+), 21 deletions(-) diff --git a/mail/extensions/newsblog/content/Feed.js b/mail/extensions/newsblog/content/Feed.js index 89ff844be50..7c2ddebef9d 100755 --- a/mail/extensions/newsblog/content/Feed.js +++ b/mail/extensions/newsblog/content/Feed.js @@ -481,6 +481,10 @@ function storeNextItem() { item.feed.removeInvalidItems(); + // let's be sure to flush any feed item changes back to disk + var ds = getItemsDS(item.feed.server); + ds.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource).Flush(); // flush any changes + if (item.feed.downloadCallback) item.feed.downloadCallback.downloaded(item.feed, kNewsBlogSuccess); diff --git a/mail/extensions/newsblog/content/feed-properties.xul b/mail/extensions/newsblog/content/feed-properties.xul index 1c6302b125c..20ec0c28441 100644 --- a/mail/extensions/newsblog/content/feed-properties.xul +++ b/mail/extensions/newsblog/content/feed-properties.xul @@ -37,6 +37,7 @@ - - diff --git a/mail/extensions/newsblog/content/utils.js b/mail/extensions/newsblog/content/utils.js index 52c15faf6c4..0429fa69d11 100755 --- a/mail/extensions/newsblog/content/utils.js +++ b/mail/extensions/newsblog/content/utils.js @@ -106,6 +106,9 @@ function updateFolderFeedUrl(aFolder, aFeedUrl, aRemoveUrl) } else folderInfo.setCharPtrProperty("feedUrl", oldFeedUrl + kFeedUrlDelimiter + aFeedUrl); + + // commit the db to preserve our changes + msgdb.Close(true); } function getNodeValue(node) { diff --git a/mail/extensions/newsblog/js/newsblog.js b/mail/extensions/newsblog/js/newsblog.js index c27861f16eb..4c256662891 100755 --- a/mail/extensions/newsblog/js/newsblog.js +++ b/mail/extensions/newsblog/js/newsblog.js @@ -85,6 +85,45 @@ var nsNewsBlogFeedDownloader = feed.download(true, progressNotifier); }, + updateSubscriptionsDS: function(aFolder, aUnsubscribe) + { + if (!gExternalScriptsLoaded) + loadScripts(); + + // an rss folder was just renamed...we need to update our feed data source + var msgdb = aFolder.QueryInterface(Components.interfaces.nsIMsgFolder).getMsgDatabase(null); + var folderInfo = msgdb.dBFolderInfo; + var feedurls = folderInfo.getCharPtrProperty("feedUrl"); + var feedUrlArray = feedurls.split("|"); + + var rdf = Components.classes["@mozilla.org/rdf/rdf-service;1"].getService(Components.interfaces.nsIRDFService); + var ds = getSubscriptionsDS(aFolder.server); + + for (url in feedUrlArray) + { + if (feedUrlArray[url]) + { + id = rdf.GetResource(feedUrlArray[url]); + // get the node for the current folder URI + var node = ds.GetTarget(id, FZ_DESTFOLDER, true); + + // we need to check and see if the folder is a child of the trash...if it is, then we can + // treat this as an unsubscribe action + if (aUnsubscribe) + { + var feeds = getSubscriptionsList(aFolder.server); + var index = feeds.IndexOf(id); + if (index != -1) + feeds.RemoveElementAt(index, false); + removeAssertions(ds, id); + } + ds.Change(id, FZ_DESTFOLDER, node, rdf.GetResource(aFolder.URI)); + } + } // for each feed url in the folder property + + ds.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource).Flush(); // flush any changes + }, + QueryInterface: function(aIID) { if (aIID.equals(Components.interfaces.nsINewsBlogFeedDownloader) || diff --git a/mailnews/local/public/nsINewsBlogFeedDownloader.idl b/mailnews/local/public/nsINewsBlogFeedDownloader.idl index a12241fc52b..ec530988a8d 100755 --- a/mailnews/local/public/nsINewsBlogFeedDownloader.idl +++ b/mailnews/local/public/nsINewsBlogFeedDownloader.idl @@ -50,5 +50,10 @@ interface nsINewsBlogFeedDownloader : nsISupports /* A convient method to subscribe to feeds without going through the subscribe UI used by drag and drop */ void subscribeToFeed(in string aUrl, in nsIMsgFolder aFolder, in nsIMsgWindow aMsgWindow); + + /* called when the RSS Incoming Server detects a change to an RSS folder. For instance, the user just + deleted an RSS folder and we need to update the subscriptions data source. Or the user renamed an RSS folder... + */ + void updateSubscriptionsDS(in nsIMsgFolder aFolder, in boolean aUnsubscribe); }; diff --git a/mailnews/local/src/nsRssIncomingServer.cpp b/mailnews/local/src/nsRssIncomingServer.cpp index b1424478f33..20ac39e8f29 100755 --- a/mailnews/local/src/nsRssIncomingServer.cpp +++ b/mailnews/local/src/nsRssIncomingServer.cpp @@ -39,22 +39,44 @@ #include "nsRssIncomingServer.h" #include "nsMsgFolderFlags.h" #include "nsINewsBlogFeedDownloader.h" +#include "nsIMsgMailSession.h" +#include "nsMsgBaseCID.h" #include "nsIMsgLocalMailFolder.h" #include "nsIDBFolderInfo.h" -NS_IMPL_ISUPPORTS_INHERITED2(nsRssIncomingServer, +nsrefcnt nsRssIncomingServer::gInstanceCount = 0; + +NS_IMPL_ISUPPORTS_INHERITED3(nsRssIncomingServer, nsMsgIncomingServer, nsIRssIncomingServer, + nsIFolderListener, nsILocalMailIncomingServer) nsRssIncomingServer::nsRssIncomingServer() { m_canHaveFilters = PR_TRUE; + + if (gInstanceCount == 0) + { + nsresult rv; + nsCOMPtr mailSession = do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv); + + if (NS_SUCCEEDED(rv)) + mailSession->AddFolderListener(this, nsIFolderListener::added); + } + + gInstanceCount++; } nsRssIncomingServer::~nsRssIncomingServer() { + gInstanceCount--; + + // I used to have code here which unregistered the global rss folder listener with the + // mail session. But the rss incoming server is held until shutdown when we shut down the + // account datasource. And at shutdown the mail session explicitly releases all of its folder listeners + // anyway so this was effectively a no-op... } nsresult nsRssIncomingServer::FillInDataSourcePath(const nsAString& aDataSourceName, nsILocalFile ** aLocation) @@ -135,25 +157,28 @@ NS_IMETHODIMP nsRssIncomingServer::PerformBiff(nsIMsgWindow *aMsgWindow) GetRootMsgFolder(getter_AddRefs(rootRSSFolder)); // enumerate over the RSS folders and ping each one - nsCOMPtr folderEnumerator; - rv = rootRSSFolder->GetSubFolders(getter_AddRefs(folderEnumerator)); + nsCOMPtr allDescendents; + NS_NewISupportsArray(getter_AddRefs(allDescendents)); + rv = rootRSSFolder->ListDescendents(allDescendents); NS_ENSURE_SUCCESS(rv, rv); - nsresult more = folderEnumerator->First(); + PRUint32 cnt =0; + allDescendents->Count(&cnt); + nsCOMPtr supports; nsCOMPtr urlListener; + nsCOMPtr rssFolder; - while (NS_SUCCEEDED(more)) + for (PRUint32 index = 0; index < cnt; index++) { - rv = folderEnumerator->CurrentItem(getter_AddRefs(supports)); - nsCOMPtr rssFolder = do_QueryInterface(supports); + supports = getter_AddRefs(allDescendents->ElementAt(index)); + rssFolder = do_QueryInterface(supports, &rv); if (rssFolder) { urlListener = do_QueryInterface(rssFolder); // WARNING: Never call GetNewMail with the root folder or you will trigger an infinite loop... GetNewMail(aMsgWindow, urlListener, rssFolder, nsnull); } - more = folderEnumerator->Next(); } return NS_OK; @@ -226,3 +251,99 @@ NS_IMETHODIMP nsRssIncomingServer::GetSupportsDiskSpace(PRBool *aSupportsDiskSpa *aSupportsDiskSpace = PR_FALSE; return NS_OK; } + +NS_IMETHODIMP nsRssIncomingServer::OnItemAdded(nsIRDFResource *parentItem, nsISupports *item) +{ + nsCOMPtr folder = do_QueryInterface(item); + NS_ENSURE_TRUE(folder, NS_OK); // just kick out with a success code if the item in question is not a folder + + nsCOMPtr server; + nsresult rv = folder->GetServer(getter_AddRefs(server)); + NS_ENSURE_SUCCESS(rv, rv); + + nsXPIDLCString type; + rv = server->GetType(getter_Copies(type)); + NS_ENSURE_SUCCESS(rv, rv); + + if (type.Equals("rss")) + { + nsCOMPtr rssDownloader = do_GetService("@mozilla.org/newsblog-feed-downloader;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // did the user just delete this folder (adding it to trash?) + nsCOMPtr rootMsgFolder; + nsCOMPtr trashFolder; + rv = GetRootFolder(getter_AddRefs(rootMsgFolder)); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 numFolders; + rv = rootMsgFolder->GetFoldersWithFlag(MSG_FOLDER_FLAG_TRASH, 1, &numFolders, getter_AddRefs(trashFolder)); + + PRBool unsubscribe = PR_FALSE; + if (trashFolder) + trashFolder->IsAncestorOf(folder, &unsubscribe); + + rssDownloader->UpdateSubscriptionsDS(folder, unsubscribe); + + // if the user was moving or deleting a set of nested folders, we only seem to get a single OnItemAdded + // notification. So we need to iterate over all of the descedent folders of the folder whose location has + // changed. + + nsCOMPtr allDescendents; + NS_NewISupportsArray(getter_AddRefs(allDescendents)); + rv = folder->ListDescendents(allDescendents); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 cnt =0; + allDescendents->Count(&cnt); + + nsCOMPtr supports; + nsCOMPtr rssFolder; + + for (PRUint32 index = 0; index < cnt; index++) + { + supports = getter_AddRefs(allDescendents->ElementAt(index)); + rssFolder = do_QueryInterface(supports, &rv); + if (rssFolder) + rssDownloader->UpdateSubscriptionsDS(rssFolder, unsubscribe); + } + } + + return rv; +} + +NS_IMETHODIMP nsRssIncomingServer::OnItemRemoved(nsIRDFResource *parentItem, nsISupports *item) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsRssIncomingServer::OnItemPropertyChanged(nsIRDFResource *item, nsIAtom *property, const char *oldValue, const char *newValue) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsRssIncomingServer::OnItemIntPropertyChanged(nsIRDFResource *item, nsIAtom *property, PRInt32 oldValue, PRInt32 newValue) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsRssIncomingServer::OnItemBoolPropertyChanged(nsIRDFResource *item, nsIAtom *property, PRBool oldValue, PRBool newValue) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsRssIncomingServer::OnItemUnicharPropertyChanged(nsIRDFResource *item, nsIAtom *property, const PRUnichar *oldValue, const PRUnichar *newValue) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + + +NS_IMETHODIMP nsRssIncomingServer::OnItemPropertyFlagChanged(nsISupports *item, nsIAtom *property, PRUint32 oldFlag, PRUint32 newFlag) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsRssIncomingServer::OnItemEvent(nsIMsgFolder *aFolder, nsIAtom *aEvent) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} diff --git a/mailnews/local/src/nsRssIncomingServer.h b/mailnews/local/src/nsRssIncomingServer.h index 1f1381c5d77..bd07bdc80e9 100755 --- a/mailnews/local/src/nsRssIncomingServer.h +++ b/mailnews/local/src/nsRssIncomingServer.h @@ -40,17 +40,19 @@ #include "nsIRssIncomingServer.h" #include "nsILocalMailIncomingServer.h" #include "nsMsgIncomingServer.h" - +#include "nsIFolderListener.h" class nsRssIncomingServer : public nsMsgIncomingServer, public nsIRssIncomingServer, - public nsILocalMailIncomingServer + public nsILocalMailIncomingServer, + public nsIFolderListener { public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIRSSINCOMINGSERVER NS_DECL_NSILOCALMAILINCOMINGSERVER + NS_DECL_NSIFOLDERLISTENER NS_IMETHOD GetLocalStoreType(char **); NS_IMETHOD GetOfflineSupportLevel(PRInt32 *aSupportLevel); @@ -62,6 +64,7 @@ public: virtual ~nsRssIncomingServer(); protected: nsresult FillInDataSourcePath(const nsAString& aDataSourceName, nsILocalFile ** aLocation); + static nsrefcnt gInstanceCount; }; #endif /* __nsRssIncomingServer_h */