1// 2// UKUpdateChecker.m 3// NiftyFeatures 4// 5// Created by Uli Kusterer on Sun Nov 23 2003. 6// Copyright (c) 2003 M. Uli Kusterer. All rights reserved. 7// 8 9#import "UKUpdateChecker.h" 10 11 12@implementation UKUpdateChecker 13 14 15// ----------------------------------------------------------------------------- 16// awakeFromNib: 17// This object has been created and loaded at startup. If this is first 18// launch, ask user whether we should check for updates periodically at 19// startup and adjust the prefs accurately. 20// 21// If the user wants us to check for updates periodically, check whether 22// it is time and if so, initiate the check. 23// 24// REVISIONS: 25// 2004-03-19 witness Documented. 26// ----------------------------------------------------------------------------- 27 28-(void) awakeFromNib 29{ 30 NSNumber * doCheck = [[NSUserDefaults standardUserDefaults] objectForKey: @"UKUpdateCheckerCheckAtStartup"]; 31 NSNumber * lastCheckDateNum = [[NSUserDefaults standardUserDefaults] objectForKey: @"UKUpdateChecker:LastCheckDate"]; 32 NSDate * lastCheckDate = nil; 33 34 /*if( doCheck == nil ) // No setting in prefs yet? First launch! Ask! 35 { 36 if( NSRunAlertPanel( NSLocalizedStringFromTable(@"CHECK_FOR_UPDATES_TITLE", @"UKUpdateChecker", @"Asking whether to check for updates at startup - dialog title"), 37 NSLocalizedStringFromTable(@"CHECK_FOR_UPDATES_TEXT", @"UKUpdateChecker", @"Asking whether to check for updates at startup - dialog text"), 38 NSLocalizedString(@"Yes",nil), NSLocalizedString(@"No",nil), nil, appName ) == NSAlertDefaultReturn ) 39 doCheck = [NSNumber numberWithBool:YES]; 40 else 41 doCheck = [NSNumber numberWithBool:NO]; 42 43 // Save user's preference to prefs file: 44 [[NSUserDefaults standardUserDefaults] setObject: doCheck forKey: @"UKUpdateCheckerCheckAtStartup"]; 45 }*/ 46 47 [prefsButton setState: [doCheck boolValue]]; // Update prefs button, if we have one. 48 49 // If user wants us to check for updates at startup, do so: 50 if( [doCheck boolValue] ) 51 { 52 NSTimeInterval timeSinceLastCheck; 53 54 // Determine how long since last check: 55 if( lastCheckDateNum == nil ) 56 lastCheckDate = [NSDate distantPast]; // If there's no date in prefs, use something guaranteed to be past. 57 else 58 lastCheckDate = [NSDate dateWithTimeIntervalSinceReferenceDate: [lastCheckDateNum doubleValue]]; 59 timeSinceLastCheck = -[lastCheckDate timeIntervalSinceNow]; 60 61 // If last check was more than DAYS_BETWEEN_CHECKS days ago, check again now: 62 if( timeSinceLastCheck > (3600 *24 *DAYS_BETWEEN_CHECKS) ) 63 { 64 [self checkForUpdatesAndNotifyIfUnsuccessful: NO]; 65 [[NSUserDefaults standardUserDefaults] setObject: [NSNumber numberWithDouble: [NSDate timeIntervalSinceReferenceDate]] forKey: @"UKUpdateChecker:LastCheckDate"]; 66 } 67 } 68} 69 70 71// ----------------------------------------------------------------------------- 72// checkForUpdates: 73// IBAction to hook up to the "check for updates" menu item. 74// 75// REVISIONS: 76// 2004-03-19 witness Documented. 77// ----------------------------------------------------------------------------- 78 79-(IBAction) checkForUpdates: (id)sender 80{ 81 [self checkForUpdatesAndNotifyIfUnsuccessful: YES]; // YES means we *also* tell the user about failure, since this is in response to a menu item. 82} 83 84 85// ----------------------------------------------------------------------------- 86// latestVersionsDictionary: 87// Load a dictionary containing info on the latest versions of this app. 88// 89// This first tries to get MacPAD-compatible version information. If the 90// developer didn't provide that, it will try the old UKUpdateChecker 91// scheme instead. 92// 93// REVISIONS: 94// 2004-03-19 witness Documented. 95// ----------------------------------------------------------------------------- 96 97-(NSDictionary*) latestVersionsDictionary 98{ 99 NSString* fpath = [[NSBundle mainBundle] pathForResource: UKUpdateCheckerURLFilename ofType: @"url"]; 100 101 // Do we have a MacPAD.url file? 102 if( [[NSFileManager defaultManager] fileExistsAtPath: fpath] ) // MacPAD-compatible! 103 { 104 NSString* urlfile = [NSString stringWithContentsOfFile: fpath]; 105 NSArray* lines = [urlfile componentsSeparatedByString: @"\n"]; 106 NSString* urlString = [lines lastObject]; // Either this is the only line, or the line following [InternetShortcut] 107 108 if( [urlString characterAtIndex: [urlString length] -1] == '/' // Directory path? Append bundle identifier and .plist to get an actual file path to download. 109 || [urlString characterAtIndex: [urlString length] -1] == '=' ) // CGI parameter? 110 urlString = [[urlString stringByAppendingString: [[NSBundle mainBundle] bundleIdentifier]] stringByAppendingString: @".plist"]; 111 112 return [NSDictionary dictionaryWithContentsOfURL: [NSURL URLWithString: urlString]]; // Download info from that URL. 113 } 114 else // Old-style UKUpdateChecker stuff: 115 { 116 NSURL* versDictURL = [NSURL URLWithString: NSLocalizedString(@"UPDATE_PLIST_URL", @"URL where the plist with the latest version numbers is.")]; 117 NSDictionary* allVersionsDict = [NSDictionary dictionaryWithContentsOfURL: versDictURL]; 118 return [allVersionsDict objectForKey: [[NSBundle mainBundle] bundleIdentifier]]; 119 } 120} 121 122 123-(void) checkForUpdatesAndNotifyIfUnsuccessful: (BOOL)doNotify 124{ 125 // Load a .plist of application version info from a web URL: 126 NSString * appName = [[NSFileManager defaultManager] displayNameAtPath: [[NSBundle mainBundle] bundlePath]]; 127 NSDictionary * appVersionDict = [self latestVersionsDictionary]; 128 BOOL succeeded = NO; 129 130 if( appVersionDict != nil ) // We were able to download a dictionary? 131 { 132 // Extract version number and URL from dictionary: 133 NSString *newVersion = [appVersionDict valueForKey: UKUpdateCheckerVersionPlistKey]; 134 NSString *newUrl = [appVersionDict valueForKey: UKUpdateCheckerURLPlistKey]; 135 136 if( !newVersion || !newUrl ) // Dictionary doesn't contain new MacPAD stuff? Use old UKUpdateChecker stuff instead. 137 { 138 newVersion = [appVersionDict valueForKey:UKUpdateCheckerOldVersionPlistKey]; 139 newUrl = [appVersionDict valueForKey:UKUpdateCheckerOldURLPlistKey]; 140 } 141 142 // Is it current? Then tell the user, or just quietly go on, depending on doNotify: 143 if( [newVersion isEqualToString: [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]] ) 144 { 145 if( doNotify ) 146 NSRunAlertPanel(NSLocalizedStringFromTable(@"UP_TO_DATE_TITLE", @"UKUpdateChecker", @"When soft is up-to-date - dialog title"), 147 NSLocalizedStringFromTable(@"UP_TO_DATE_TEXT", @"UKUpdateChecker", @"When soft is up-to-date - dialog text"), 148 NSLocalizedStringFromTable(@"OK", @"UKUpdateChecker", @""), nil, nil, appName ); 149 succeeded = YES; 150 } 151 else if( newVersion != nil ) // If there's an entry for this app: 152 { 153 // Ask user whether they'd like to open the URL for the new version: 154 int button = NSRunAlertPanel( 155 NSLocalizedStringFromTable(@"NEW_VERSION_TITLE", @"UKUpdateChecker", @"A New Version is Available - dialog title"), 156 NSLocalizedStringFromTable(@"NEW_VERSION_TEXT", @"UKUpdateChecker", @"A New Version is Available - dialog text"), 157 NSLocalizedStringFromTable(@"OK", @"UKUpdateChecker", @""), NSLocalizedStringFromTable(@"Cancel", @"UKUpdateChecker", @""), nil, 158 appName, newVersion ); 159 if(NSOKButton == button) // Yes? 160 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:newUrl]]; //Open! 161 162 succeeded = YES; // Otherwise, it's still a success. 163 } 164 } 165 166 // Failed? File not found, no internet, there is no entry for our app? 167 if( !succeeded && doNotify ) 168 NSRunAlertPanel(NSLocalizedStringFromTable(@"UPDATE_ERROR_TITLE", @"UKUpdateChecker", @"When update test failed - dialog title"), 169 NSLocalizedStringFromTable(@"UPDATE_ERROR_TEXT", @"UKUpdateChecker", @"When update test failed - dialog text"), 170 @"OK", nil, nil, appName); 171} 172 173 174-(IBAction) takeBoolFromObject: (id)sender 175{ 176 if( [sender respondsToSelector: @selector(boolValue)] ) 177 [self setCheckAtStartup: [sender boolValue]]; 178 else 179 [self setCheckAtStartup: [sender state]]; 180} 181 182 183-(void) setCheckAtStartup: (BOOL)shouldCheck 184{ 185 NSNumber* doCheck = [NSNumber numberWithBool: shouldCheck]; 186 [[NSUserDefaults standardUserDefaults] setObject: doCheck forKey: @"UKUpdateCheckerCheckAtStartup"]; 187 188 [prefsButton setState: shouldCheck]; 189 [[NSUserDefaults standardUserDefaults] setObject: [NSNumber numberWithDouble: 0] forKey: @"UKUpdateChecker:LastCheckDate"]; 190} 191 192 193-(BOOL) checkAtStartup 194{ 195 NSNumber * doCheck = [[NSUserDefaults standardUserDefaults] objectForKey: @"UKUpdateCheckerCheckAtStartup"]; 196 197 if( doCheck ) 198 return [doCheck boolValue]; 199 else 200 return YES; 201} 202 203 204@end 205