5 // Created by Uli Kusterer on Sun Nov 23 2003.
6 // Copyright (c) 2003 M. Uli Kusterer. All rights reserved.
9 #import "UKUpdateChecker.h"
12 @implementation UKUpdateChecker
15 // -----------------------------------------------------------------------------
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.
21 // If the user wants us to check for updates periodically, check whether
22 // it is time and if so, initiate the check.
25 // 2004-03-19 witness Documented.
26 // -----------------------------------------------------------------------------
30 NSNumber * doCheck = [[NSUserDefaults standardUserDefaults] objectForKey: @"UKUpdateChecker:CheckAtStartup"];
31 NSString * appName = [[NSFileManager defaultManager] displayNameAtPath: [[NSBundle mainBundle] bundlePath]];
32 NSNumber * lastCheckDateNum = [[NSUserDefaults standardUserDefaults] objectForKey: @"UKUpdateChecker:LastCheckDate"];
33 NSDate * lastCheckDate = nil;
35 if( doCheck == nil ) // No setting in prefs yet? First launch! Ask!
37 if( NSRunAlertPanel( NSLocalizedStringFromTable(@"CHECK_FOR_UPDATES_TITLE", @"UKUpdateChecker", @"Asking whether to check for updates at startup - dialog title"),
38 NSLocalizedStringFromTable(@"CHECK_FOR_UPDATES_TEXT", @"UKUpdateChecker", @"Asking whether to check for updates at startup - dialog text"),
39 NSLocalizedStringFromTable(@"Yes",@"UKUpdateChecker", nil), NSLocalizedStringFromTable(@"No",@"UKUpdateChecker", nil), nil, appName ) == NSAlertDefaultReturn )
40 doCheck = [NSNumber numberWithBool:YES];
42 doCheck = [NSNumber numberWithBool:NO];
44 // Save user's preference to prefs file:
45 [[NSUserDefaults standardUserDefaults] setObject: doCheck forKey: @"UKUpdateChecker:CheckAtStartup"];
48 [prefsButton setState: [doCheck boolValue]]; // Update prefs button, if we have one.
50 // If user wants us to check for updates at startup, do so:
51 if( [doCheck boolValue] )
53 NSTimeInterval timeSinceLastCheck;
55 // Determine how long since last check:
56 if( lastCheckDateNum == nil )
57 lastCheckDate = [NSDate distantPast]; // If there's no date in prefs, use something guaranteed to be past.
59 lastCheckDate = [NSDate dateWithTimeIntervalSinceReferenceDate: [lastCheckDateNum doubleValue]];
60 timeSinceLastCheck = -[lastCheckDate timeIntervalSinceNow];
62 // If last check was more than DAYS_BETWEEN_CHECKS days ago, check again now:
63 if( timeSinceLastCheck > (3600 *24 *DAYS_BETWEEN_CHECKS) )
65 [self checkForUpdatesAndNotifyIfUnsuccessful: NO];
66 [[NSUserDefaults standardUserDefaults] setObject: [NSNumber numberWithDouble: [NSDate timeIntervalSinceReferenceDate]] forKey: @"UKUpdateChecker:LastCheckDate"];
72 // -----------------------------------------------------------------------------
74 // IBAction to hook up to the "check for updates" menu item.
77 // 2004-03-19 witness Documented.
78 // -----------------------------------------------------------------------------
80 -(IBAction) checkForUpdates: (id)sender
82 [self checkForUpdatesAndNotifyIfUnsuccessful: YES]; // YES means we *also* tell the user about failure, since this is in response to a menu item.
86 // -----------------------------------------------------------------------------
87 // latestVersionsDictionary:
88 // Load a dictionary containing info on the latest versions of this app.
90 // This first tries to get MacPAD-compatible version information. If the
91 // developer didn't provide that, it will try the old UKUpdateChecker
95 // 2004-03-19 witness Documented.
96 // -----------------------------------------------------------------------------
98 -(NSDictionary*) latestVersionsDictionary
100 NSString* fpath = [[NSBundle mainBundle] pathForResource: UKUpdateCheckerURLFilename ofType: @"url"];
102 // Do we have a MacPAD.url file?
103 if( [[NSFileManager defaultManager] fileExistsAtPath: fpath] ) // MacPAD-compatible!
105 NSString* urlfile = [NSString stringWithContentsOfFile: fpath];
106 NSArray* lines = [urlfile componentsSeparatedByString: @"\n"];
107 NSString* urlString = [lines lastObject]; // Either this is the only line, or the line following [InternetShortcut]
109 if( [urlString characterAtIndex: [urlString length] -1] == '/' // Directory path? Append bundle identifier and .plist to get an actual file path to download.
110 || [urlString characterAtIndex: [urlString length] -1] == '=' ) // CGI parameter?
111 urlString = [[urlString stringByAppendingString: [[NSBundle mainBundle] bundleIdentifier]] stringByAppendingString: @".plist"];
113 return [NSDictionary dictionaryWithContentsOfURL: [NSURL URLWithString: urlString]]; // Download info from that URL.
115 else // Old-style UKUpdateChecker stuff:
117 NSURL* versDictURL = [NSURL URLWithString: NSLocalizedString(@"UPDATE_PLIST_URL", @"URL where the plist with the latest version numbers is.")];
118 NSDictionary* allVersionsDict = [NSDictionary dictionaryWithContentsOfURL: versDictURL];
119 return [allVersionsDict objectForKey: [[NSBundle mainBundle] bundleIdentifier]];
124 -(void) checkForUpdatesAndNotifyIfUnsuccessful: (BOOL)doNotify
126 // Load a .plist of application version info from a web URL:
127 NSString * appName = [[NSFileManager defaultManager] displayNameAtPath: [[NSBundle mainBundle] bundlePath]];
128 NSDictionary * appVersionDict = [self latestVersionsDictionary];
131 if( appVersionDict != nil ) // We were able to download a dictionary?
133 // Extract version number and URL from dictionary:
134 NSString *newVersion = [appVersionDict valueForKey: UKUpdateCheckerVersionPlistKey];
135 NSString *newUrl = [appVersionDict valueForKey: UKUpdateCheckerURLPlistKey];
137 if( !newVersion || !newUrl ) // Dictionary doesn't contain new MacPAD stuff? Use old UKUpdateChecker stuff instead.
139 newVersion = [appVersionDict valueForKey:UKUpdateCheckerOldVersionPlistKey];
140 newUrl = [appVersionDict valueForKey:UKUpdateCheckerOldURLPlistKey];
143 // Is it current? Then tell the user, or just quietly go on, depending on doNotify:
144 if( [newVersion isLessThanOrEqualTo: [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]] )
147 NSRunAlertPanel(NSLocalizedStringFromTable(@"UP_TO_DATE_TITLE", @"UKUpdateChecker", @"When soft is up-to-date - dialog title"),
148 NSLocalizedStringFromTable(@"UP_TO_DATE_TEXT", @"UKUpdateChecker", @"When soft is up-to-date - dialog text"),
149 NSLocalizedStringFromTable(@"OK", @"UKUpdateChecker", @""), nil, nil, appName );
152 else if( newVersion != nil ) // If there's an entry for this app:
154 // Ask user whether they'd like to open the URL for the new version:
155 int button = NSRunAlertPanel(
156 NSLocalizedStringFromTable(@"NEW_VERSION_TITLE", @"UKUpdateChecker", @"A New Version is Available - dialog title"),
157 NSLocalizedStringFromTable(@"NEW_VERSION_TEXT", @"UKUpdateChecker", @"A New Version is Available - dialog text"),
158 NSLocalizedStringFromTable(@"OK", @"UKUpdateChecker", @""), NSLocalizedStringFromTable(@"Cancel", @"UKUpdateChecker", @""), nil,
159 appName, newVersion );
160 if(NSOKButton == button) // Yes?
161 [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:newUrl]]; //Open!
163 succeeded = YES; // Otherwise, it's still a success.
167 // Failed? File not found, no internet, there is no entry for our app?
168 if( !succeeded && doNotify )
169 NSRunAlertPanel(NSLocalizedStringFromTable(@"UPDATE_ERROR_TITLE", @"UKUpdateChecker", @"When update test failed - dialog title"),
170 NSLocalizedStringFromTable(@"UPDATE_ERROR_TEXT", @"UKUpdateChecker", @"When update test failed - dialog text"),
171 @"OK", nil, nil, appName);
175 -(IBAction) takeBoolFromObject: (id)sender
177 if( [sender respondsToSelector: @selector(boolValue)] )
178 [self setCheckAtStartup: [sender boolValue]];
180 [self setCheckAtStartup: [sender state]];
184 -(void) setCheckAtStartup: (BOOL)shouldCheck
186 NSNumber* doCheck = [NSNumber numberWithBool: shouldCheck];
187 [[NSUserDefaults standardUserDefaults] setObject: doCheck forKey: @"UKUpdateChecker:CheckAtStartup"];
189 [prefsButton setState: shouldCheck];
190 [[NSUserDefaults standardUserDefaults] setObject: [NSNumber numberWithDouble: 0] forKey: @"UKUpdateChecker:LastCheckDate"];
194 -(BOOL) checkAtStartup
196 NSNumber * doCheck = [[NSUserDefaults standardUserDefaults] objectForKey: @"UKUpdateChecker:CheckAtStartup"];
199 return [doCheck boolValue];