Codebase list agenda.app / HEAD iCalStore.m
HEAD

Tree @HEAD (Download .tar.gz)

iCalStore.m @HEADraw · history · blame

#import <AppKit/AppKit.h>
#import "Event.h"
#import "Task.h"
#import "AgendaStore.h"
#import "WebDAVResource.h"
#import "iCalTree.h"
#import "NSString+SimpleAgenda.h"
#import "InvocationOperation.h"
#import "StoreManager.h"
#import "defines.h"

@interface iCalStoreDialog : NSObject
{
  IBOutlet id panel;
  IBOutlet id name;
  IBOutlet id url;
  IBOutlet id ok;
  IBOutlet id error;
  IBOutlet id warning;
}
- (BOOL)show;
- (NSString *)url;
@end
@implementation iCalStoreDialog
- (id)initWithName:(NSString *)storeName
{
  if ((self = [super init])) {
    if (![NSBundle loadNibNamed:@"iCalendar" owner:self])
      return nil;
    [warning setHidden:YES];
    [name setStringValue:storeName];
    [url setStringValue:@"http://"];
  }
  return self;
}
- (void)dealloc
{
  [panel close];
  [super dealloc];
}
- (BOOL)show
{
  [ok setEnabled:NO];
  return [NSApp runModalForWindow:panel];
}
- (void)okClicked:(id)sender
{
  BOOL readable;
  WebDAVResource *resource;

  resource = AUTORELEASE([[WebDAVResource alloc] initWithURL:[NSURL URLWithString:[url stringValue]]]);
  readable = [resource readable];
  /* Read will fail if there's no resource yet, try to create an empty one */
  if (!readable && [resource httpStatus] != 401) {
    [resource writableWithData:[NSData data]];
    readable = [resource readable];
  }
  if (readable)
    [NSApp stopModalWithCode:1];
  else {
    [error setStringValue:[NSString stringWithFormat:@"Unable to read from this URL : %@", [[resource url] propertyForKey:NSHTTPPropertyStatusReasonKey]]];
    [warning setHidden:NO];
  }
}
- (void)cancelClicked:(id)sender
{
  [NSApp stopModalWithCode:0];
}
- (void)controlTextDidChange:(NSNotification *)notification
{
  NS_DURING
    {
      [ok setEnabled:[[url stringValue] isValidURL]];
    }
  NS_HANDLER
    {
      [ok setEnabled:NO];
    }
  NS_ENDHANDLER
}
- (NSString *)url
{
  return [url stringValue];
}
@end


@interface iCalStore : MemoryStore <SharedStore>
{
  iCalTree *_tree;
  NSURL *_url;
  NSTimer *_refreshTimer;
  WebDAVResource *_resource;
}
- (void)initTimer;
- (void)doRead;
@end
@implementation iCalStore
- (NSDictionary *)defaults
{
  return [NSDictionary dictionaryWithObjectsAndKeys:[[NSColor blueColor] description], ST_COLOR,
		       [[NSColor whiteColor] description], ST_TEXT_COLOR,
		       [NSNumber numberWithBool:NO], ST_RW,
		       [NSNumber numberWithBool:YES], ST_DISPLAY,
		       [NSNumber numberWithBool:NO], ST_REFRESH,
		       [NSNumber numberWithBool:YES], ST_ENABLED,
		       nil, nil];
}

- (id)initWithName:(NSString *)name
{
  if ((self = [super initWithName:name])) {
    _tree = [iCalTree new];
    assert(_tree != nil);
    _url = [[NSURL alloc] initWithString:[[self config] objectForKey:ST_URL]];
    _resource = [[WebDAVResource alloc] initWithURL:_url];
    [self read];
    [self initTimer];
    [[NSNotificationCenter defaultCenter] addObserver:self 
					     selector:@selector(configChanged:) 
						 name:SAConfigManagerValueChanged 
					       object:[self config]];
  }
  return self;
}

+ (BOOL)isUserInstanciable
{
  return YES;
}

+ (BOOL)registerWithName:(NSString *)name
{
  ConfigManager *cm;
  iCalStoreDialog *dialog;
  NSURL *storeURL;
  BOOL writable = NO;
  WebDAVResource *resource;

  dialog = [[iCalStoreDialog alloc] initWithName:name];
  if ([dialog show] == YES) {
    storeURL = [NSURL URLWithString:[dialog url]];
    resource = [[WebDAVResource alloc] initWithURL:storeURL];
    writable = NO;
    if ([resource get])
      writable = [resource writableWithData:[resource data]];
    [resource release];
    [dialog release];
    cm = [[ConfigManager alloc] initForKey:name];
    [cm setObject:[storeURL description] forKey:ST_URL];
    [cm setObject:[[self class] description] forKey:ST_CLASS];
    [cm setObject:[NSNumber numberWithBool:writable] forKey:ST_RW];
    [cm release];
    return YES;
  }
  [dialog release];
  return NO;
}

+ (NSString *)storeTypeName
{
  return @"iCalendar";
}

- (void)dealloc
{
  [[NSNotificationCenter defaultCenter] removeObserver:self];
  [_refreshTimer invalidate];
  [_refreshTimer release];
  [_resource release];
  [_url release];
  [_tree release];
  [super dealloc];
}

- (void)refreshData:(NSTimer *)timer
{
  [self read];
}

- (void)add:(Element *)elt
{
  if ([_tree add:elt]) {
    [super add:elt];
    [self write];
  }
}

- (void)remove:(Element *)elt
{
  if ([_tree remove:elt]) {
    [super remove:elt];
    [self write];
  }
}

- (void)update:(Element *)elt
{
  if ([_tree update:(Event *)elt]) {
    [super update:elt];
    [self write];
  }
}

- (void)read
{
  if ([self enabled])
    [[[StoreManager globalManager] operationQueue] addOperation:[[[InvocationOperation alloc] initWithTarget:self 
												    selector:@selector(doRead) 
												      object:nil] autorelease]];
}

- (void)write
{
  NSData *data;

  if (![self modified] || ![self writable])
    return;
  data = [_tree iCalTreeAsData];
  if (data)
    [[[StoreManager globalManager] operationQueue] addOperation:[[[InvocationOperation alloc] initWithTarget:self 
												    selector:@selector(doWrite:)
												      object:[data retain]] autorelease]];
}


- (BOOL)periodicRefresh
{
  if ([[self config] objectForKey:ST_REFRESH])
    return [[[self config] objectForKey:ST_REFRESH] boolValue];
  return NO;
}
- (void)setPeriodicRefresh:(BOOL)periodic
{
  [[self config] setObject:[NSNumber numberWithBool:periodic] forKey:ST_REFRESH];
}
- (NSTimeInterval)refreshInterval
{
  if ([[self config] objectForKey:ST_REFRESH_INTERVAL])
    return [[self config] integerForKey:ST_REFRESH_INTERVAL];
  return 60 * 30;
}
- (void)setRefreshInterval:(NSTimeInterval)interval
{
  [[self config] setInteger:interval forKey:ST_REFRESH_INTERVAL];
}

- (void)configChanged:(NSNotification *)not
{
  NSString *key = [[not userInfo] objectForKey:@"key"];
  if ([key isEqualToString:ST_ENABLED]) {
    if ([self enabled])
      [self read];
    [self initTimer];
  }
  if ([key isEqualToString:ST_REFRESH] || [key isEqualToString:ST_REFRESH_INTERVAL]) 
    [self initTimer];
}

- (void)initTimer
{
  if (nil != _refreshTimer) {
    [_refreshTimer invalidate];
    DESTROY(_refreshTimer);
  }
  if (![self enabled])
    return;
  if ([self periodicRefresh]) {
    _refreshTimer = [[NSTimer alloc] initWithFireDate:nil
				             interval:[self refreshInterval]
                   			       target:self
				             selector:@selector(refreshData:) 
				             userInfo:nil 
				              repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:_refreshTimer forMode:NSDefaultRunLoopMode];
    NSLog(@"Store %@ will refresh every %d seconds", [self description], (int)[self refreshInterval]);
  } else {
    NSLog(@"Store %@ automatic refresh disabled", [self description]);
  }
}
- (void)doRead
{
  if ([_resource get]) {
    if ([_tree parseData:[_resource data]]) {
      [self performSelectorOnMainThread:@selector(fillWithElements:)
			     withObject:[_tree components]
			  waitUntilDone:YES];
      NSLog(@"iCalStore from %@ : loaded %lu appointment(s)", [_url anonymousAbsoluteString], [[self events] count]);
      NSLog(@"iCalStore from %@ : loaded %lu tasks(s)", [_url anonymousAbsoluteString], [[self tasks] count]);
    } else{
      NSLog(@"Couldn't parse data from %@", [_url anonymousAbsoluteString]);
    }
  } else {
    [[NSNotificationCenter defaultCenter] postNotificationName:SAErrorReadingStore
							object:self
						      userInfo:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:[_resource httpStatus]] forKey:@"errorCode"]];
  }
}
- (void)doWrite:(NSData *)data
{
  BOOL ret = [_resource put:data attributes:nil];

  [data release];
  if (ret) {
    [_resource updateAttributes];
    [self setModified:NO];
    NSLog(@"iCalStore written to %@", [_url anonymousAbsoluteString]);
  } else {
    NSLog(@"Unable to write to calendar %@, make it read only and reread the data", [self description]);
    [[NSNotificationCenter defaultCenter] postNotificationName:SAErrorWritingStore
							object:self
						      userInfo:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:[_resource httpStatus]] forKey:@"errorCode"]];
  }
}
@end