/* Project: RSSReader Copyright (C) 2006 Yen-Ju Chen Copyright (C) 2005 Guenther Noack Author: Yen-Ju Chen Author: Guenther Noack Created: 2005-03-25 21:07:58 +0100 by guenther This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This application is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #import #import "FeedList.h" #import "FetchingProgressManager.h" #import "RSSReaderFeed.h" #import "Global.h" #import "GNUstep.h" static FeedList *feedList = nil; int articleSortByDate( id articleA, id articleB, void* context ) { NSDate* dateA; NSDate* dateB; int result; dateA = [articleA date]; dateB = [articleB date]; if (dateA == nil) { if (dateB == nil) { result = NSOrderedSame; } else { result = NSOrderedDescending; } } else if (dateB == nil) { result = NSOrderedAscending; } else { result = [dateB compare: dateA]; } return result; } @implementation FeedList + (void) initialize { /* Make sure CKItem has right property */ NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt: CKStringProperty], kArticleHeadlineProperty, [NSNumber numberWithInt: CKStringProperty], kArticleURLProperty, [NSNumber numberWithInt: CKStringProperty], kArticleDescriptionProperty, [NSNumber numberWithInt: CKDateProperty], kArticleDateProperty, [NSNumber numberWithInt: CKIntegerProperty], kArticleReadProperty, [NSNumber numberWithInt: CKStringProperty], kArticleGroupProperty, nil]; [CKItem addPropertiesAndTypes: dict]; dict = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt: CKStringProperty], kArticleGroupURLProperty, nil]; [CKGroup addPropertiesAndTypes: dict]; } + (FeedList *) feedList; { if (feedList == nil) { feedList = [[FeedList alloc] init]; } return feedList; } + (NSString *) articleCacheLocation { NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); if ([paths count] == 0) return nil; return [[[paths objectAtIndex: 0] stringByAppendingPathComponent: @"RSSReader"] stringByAppendingPathComponent: @"ArticleCache.mollusk"]; } - (id) init { if ((self = [super init])) { list = [[NSMutableDictionary alloc] init]; ASSIGN(feedStore, [BKBookmarkStore sharedBookmarkWithDomain: BKRSSBookmarkStore]); ASSIGN(articleCollection, AUTORELEASE([[CKCollection alloc] initWithLocation: [FeedList articleCacheLocation]])); /* Remove old articles */ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; int number = [defaults integerForKey: RSSReaderRemoveArticlesAfterDefaults]; if (number > 0) { ASSIGN(keepDate, [[NSCalendarDate date] dateByAddingYears: 0 months: 0 days: -number hours: 0 minutes: 0 seconds: 0]); [self removeArticlesOlderThanDay: keepDate]; } [self buildFeeds]; } return self; } - (void) dealloc { [[NSNotificationCenter defaultCenter] removeObserver: self]; RELEASE(list); RELEASE(feedStore); RELEASE(articleCollection); [super dealloc]; } - (void) buildFeeds { /* Build feeds based on feedStore */ NSEnumerator *e = [[feedStore items] objectEnumerator]; BKBookmark *bk; while ((bk = [e nextObject])) { RSSFeed *feed = [RSSReaderFeed feedWithURL: [bk URL]]; [feed setAutoClear: YES]; [list setObject: feed forKey: [bk URL]]; } } - (BKBookmarkStore *) feedStore { return feedStore; } - (CKCollection *) articleCollection { return articleCollection; } // returns a NSArray, please do *not* change the array, this is // an internal structure of the class! - (NSArray*) feeds { return [list allValues]; } - (RSSFeed *) feedForURL: (NSURL *) url { return [list objectForKey: url]; } - (BKBookmark *) feedBookmarkForURL: (NSURL *) url { NSEnumerator *e = [[feedStore items] objectEnumerator]; BKBookmark *bk; while ((bk = [e nextObject])) { if ([[bk URL] isEqual: url]) { return bk; } } return nil; } - (CKGroup *) articleGroupForURL: (NSURL *) url { /* Find the right feed */ NSEnumerator *e = [[articleCollection groups] objectEnumerator]; CKGroup *group = nil; while ((group = [e nextObject])) { if ([[group valueForProperty: kArticleGroupURLProperty] isEqualToString: [url absoluteString]]) { return group; } } return nil; } - (void) removeFeed: (RSSFeed*) feed { BKBookmark *bk = [self feedBookmarkForURL: [feed feedURL]]; if (bk) { [feedStore removeBookmark: bk]; } [list removeObjectForKey: [feed feedURL]]; } - (void) addFeedWithURL: (NSURL*) url { [self addFeedsWithURLs: [NSArray arrayWithObject: url]]; } - (void) addFeedsWithURLs: (NSArray*) urls { NSMutableArray* feedArray; int i; RSSReaderFeed* feed; feedArray = [NSMutableArray arrayWithCapacity: [urls count]]; for (i=0; i<[urls count]; i++) { feed = AUTORELEASE([[RSSReaderFeed alloc] initWithURL: [urls objectAtIndex: i]]); [feedArray addObject: feed]; } [self addFeeds: feedArray]; } - (void) addFeed: (RSSFeed*) feed { [self addFeeds: [NSArray arrayWithObject: feed]]; } - (void) addFeeds: (NSArray*) feeds { int i; // first fetch them [[FetchingProgressManager defaultManager] fetchFeeds: feeds]; for (i=0; i<[feeds count]; i++) { RSSFeed* feed = (RSSFeed*) [feeds objectAtIndex: i]; BKBookmark *bk = [BKBookmark bookmarkWithURL: [feed feedURL]]; [bk setTitle: [[feed feedURL] absoluteString]]; [list setObject: feed forKey: [feed feedURL]]; [feedStore addBookmark: bk]; } } - (void) save { [feedStore save]; [articleCollection save]; } - (void) removeArticlesOlderThanDay: (NSDate *) date { NSEnumerator *e = [[articleCollection items] objectEnumerator]; CKItem *item; while ((item = [e nextObject])) { NSDate *d = [item valueForProperty: kArticleDateProperty]; if (d) { if ([d compare: date] == NSOrderedAscending) { RETAIN(item); [articleCollection removeRecord: item]; [[NSNotificationCenter defaultCenter] postNotificationName: RSSReaderLogNotification object: item]; DESTROY(item); } } } } - (void) updateFeed: (RSSFeed *) feed { /* Need to update name on feedStore */ BKBookmark *bk = [self feedBookmarkForURL: [feed feedURL]]; [bk setTitle: [feed feedName]]; /* Update articleCollection. * Remove old articles under this group and add new. */ NSEnumerator *e = [[articleCollection groups] objectEnumerator]; CKGroup *group = nil; CKItem *item = nil; BOOL found = NO; NSString *URLString = [[feed feedURL] absoluteString]; while ((group = [e nextObject])) { if ([[group valueForProperty: kArticleGroupURLProperty] isEqualToString: URLString]) { found = YES; break; } } if (found == NO) { /* Feed exist, but no group. Make one */ group = AUTORELEASE([[CKGroup alloc] init]); [articleCollection addRecord: group]; [group setValue: URLString forProperty: kArticleGroupURLProperty]; } e = [feed articleEnumerator]; RSSArticle *article = nil; while ((article = [e nextObject])) { /* If older then keepDate, skip it */ if ((keepDate != nil) && ([[article date] compare: keepDate] == NSOrderedAscending)) { continue; } /* Find existing one */ BOOL found = NO; NSEnumerator *e1 = [[group items] objectEnumerator]; while ((item = [e1 nextObject])) { NSString *headline = [item valueForProperty: kArticleHeadlineProperty]; NSString *url = [item valueForProperty: kArticleURLProperty]; if (([headline isEqualToString: [article headline]]) && ([url isEqualToString: [article url]])) { found = YES; } } if (found == NO) { /* Add new article */ item = AUTORELEASE([[CKItem alloc] init]); [item setValue: [NSNumber numberWithInt: 0] /* Unread for new article */ forProperty: kArticleReadProperty]; [articleCollection addRecord: item]; [group addItem: item]; } [item setValue: [article headline] forProperty: kArticleHeadlineProperty]; [item setValue: [article url] forProperty: kArticleURLProperty]; [item setValue: [article content] forProperty: kArticleDescriptionProperty]; [item setValue: [article date] forProperty: kArticleDateProperty]; [item setValue: [feed feedName] forProperty: kArticleGroupProperty]; } } @end