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