/// /// Copyright © 2003-2008 JetBrains s.r.o. /// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder). /// using System; using System.Collections; using System.Diagnostics; using System.IO; using System.Text; using System.Xml; using JetBrains.Omea.HTML; using JetBrains.Omea.OpenAPI; namespace JetBrains.Omea.RSSPlugin { /// /// Summary description for Bloglines. /// internal class FeedDemonImporter : IFeedImporter, IFeedElementParser { private const string _progressMessage = "Importing FeedDemon subscriptions"; private const string _progressMessageCache = "Importing FeedDemon cache"; private const string _fdNS = "http://www.bradsoft.com/feeddemon/xmlns/1.0/"; private string _channelsPath = null; private string _groupsPath = null; private IResource _flag = null; private ArrayList _readItems = null; private Hashtable _name2url = new Hashtable(); public FeedDemonImporter() { bool FeedDemonFound = true; string basePath = Environment.GetFolderPath( Environment.SpecialFolder.LocalApplicationData ); string daemonPath = Path.Combine( basePath, @"Bradsoft.com\FeedDemon\1.0" ); _channelsPath = Path.Combine( daemonPath, "Channels" ); _groupsPath = Path.Combine( daemonPath, "Groups" ); FeedDemonFound = Directory.Exists( _channelsPath ) && Directory.Exists( _groupsPath ); if( ! FeedDemonFound ) { // don't build additional data structures return; } RSSPlugin.GetInstance().RegisterFeedImporter( "FeedDemon", this ); _flag = Core.ResourceStore.FindUniqueResource( "Flag", "FlagId", "RedFlag" ); } /// /// Check if importer needs configuration before import starts. /// public bool HasSettings { get { return false; } } /// /// Returns creator of options pane. /// public OptionsPaneCreator GetSettingsPaneCreator() { return null; } /// /// Import subscription /// public void DoImport( IResource importRoot, bool addToWorkspace ) { RSSPlugin plugin = RSSPlugin.GetInstance(); importRoot = plugin.FindOrCreateGroup( "FeedDemon subscriptions", importRoot ); // Count full count of resources string[] allFiles = Directory.GetFiles( _groupsPath, "*.opml" ); int totalFiles = Math.Max( allFiles.Length, 1 ); int processedFiles = 0; ImportUtils.UpdateProgress( processedFiles / totalFiles, _progressMessage ); foreach( string file in allFiles ) { IResource group = null; string name = Path.GetFileNameWithoutExtension( file ); group = plugin.FindOrCreateGroup( name, importRoot ); try { Hashtable ns = new Hashtable(); Stream stream = new FileStream( file, FileMode.Open, FileAccess.Read ); // Fix bugs in OPML ns[ "fd" ] = _fdNS; OPMLProcessor.Import( new StreamReader(stream), group, addToWorkspace, ns ); } catch( Exception ex ) { RemoveFeedsAndGroupsAction.DeleteFeedGroup( group ); ImportUtils.ReportError( "FeedDemon Subscription Import", "Import of FeedDemon group '" + name + "' failed:\n" + ex.Message ); } processedFiles += 100; ImportUtils.UpdateProgress( processedFiles / totalFiles, _progressMessage ); } // Read summary.xml string summary = Path.Combine( _channelsPath, "summary.xml" ); if( File.Exists( summary ) ) { try { XmlDocument xdoc = new XmlDocument(); xdoc.Load( summary ); foreach( XmlElement channel in xdoc.GetElementsByTagName( "channel" ) ) { string title = null; string url = null; XmlNodeList l = null; l = channel.GetElementsByTagName( "title" ); if( l.Count < 1 ) { continue; } title = l[0].InnerText; l = channel.GetElementsByTagName( "newsFeed" ); if( l.Count < 1 ) { continue; } url = l[0].InnerText; _name2url.Add( title, url ); } } catch( Exception ex ) { Trace.WriteLine( "FeedDemon subscrption load failed: '" + ex.Message + "'" ); } } return; } /// /// Import cached items, flags, etc. /// public void DoImportCache() { RSSPlugin plugin = RSSPlugin.GetInstance(); _readItems = new ArrayList(); // Register us for special tags plugin.RegisterItemElementParser( FeedType.Rss, _fdNS, "state", this ); plugin.RegisterItemElementParser( FeedType.Atom, _fdNS, "state", this ); string[] allFiles = Directory.GetFiles( _channelsPath, "*.rss" ); int totalFiles = Math.Max( allFiles.Length, 1 ); int processedFiles = 0; foreach( string file in allFiles ) { ImportUtils.UpdateProgress( processedFiles / totalFiles, _progressMessageCache ); processedFiles += 100; IResource feed = null; string name = HtmlTools.SafeHtmlDecode( Path.GetFileNameWithoutExtension( file ) ); if( _name2url.ContainsKey( name ) ) { IResourceList feeds = Core.ResourceStore.FindResources( "RSSFeed", Props.URL, _name2url[ name ] ); if( feeds.Count > 0 ) { feed = feeds[0]; } } if( feed == null ) { IResourceList feeds = Core.ResourceStore.FindResources( "RSSFeed", Core.Props.Name, name ); if( feeds.Count > 0 ) { feed = feeds[0]; } } // Not found (import of this feed was canceled?) if( feed == null ) { continue; } _readItems.Clear(); using( Stream rss = new FileStream( file, FileMode.Open ) ) { try { RSSParser parser = new RSSParser( feed ); parser.Parse( rss, Encoding.UTF8, true ); } catch( Exception ex ) { Trace.WriteLine( "FeedDemon cache '" + file + "' load failed: '" + ex.Message + "'" ); } } foreach( IResource r in _readItems ) { if( ! r.IsDeleted ) { r.DeleteProp( Core.Props.IsUnread ); } } } ImportUtils.UpdateProgress( processedFiles / totalFiles, _progressMessageCache ); } /// /// Parses the value for the element. /// /// The resource in which the element data should be stored /// (of type RSSFeed for channel element parsers or RSSItem for item element /// parsers). /// The reader positioned after the starting tag of the element. public void ParseValue( IResource resource, XmlReader reader ) { string a = null; a = reader.GetAttribute( "read" ); if( a != null && a != "0" ) { _readItems.Add( resource ); } a = reader.GetAttribute( "flagged" ); if( a != null && a != "0" ) { resource.AddLink( "Flag", _flag ); } } /// /// Checks if a Read() call is needed to move to the next element after /// is completed. /// public bool SkipNextRead { get { return false; } } } }