root / pithos-macos / PithosPreferencesController.m @ fb27f368
History | View | Annotate | Download (43.4 kB)
1 |
// |
---|---|
2 |
// PithosPreferencesController.m |
3 |
// pithos-macos |
4 |
// |
5 |
// Copyright 2011-2013 GRNET S.A. All rights reserved. |
6 |
// |
7 |
// Redistribution and use in source and binary forms, with or |
8 |
// without modification, are permitted provided that the following |
9 |
// conditions are met: |
10 |
// |
11 |
// 1. Redistributions of source code must retain the above |
12 |
// copyright notice, this list of conditions and the following |
13 |
// disclaimer. |
14 |
// |
15 |
// 2. Redistributions in binary form must reproduce the above |
16 |
// copyright notice, this list of conditions and the following |
17 |
// disclaimer in the documentation and/or other materials |
18 |
// provided with the distribution. |
19 |
// |
20 |
// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS |
21 |
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
22 |
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
23 |
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR |
24 |
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
25 |
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
26 |
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
27 |
// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
28 |
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
29 |
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
30 |
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
31 |
// POSSIBILITY OF SUCH DAMAGE. |
32 |
// |
33 |
// The views and conclusions contained in the software and |
34 |
// documentation are those of the authors and should not be |
35 |
// interpreted as representing official policies, either expressed |
36 |
// or implied, of GRNET S.A. |
37 |
|
38 |
#import "PithosPreferencesController.h" |
39 |
#import "PithosBrowserController.h" |
40 |
#import "PithosAccountNode.h" |
41 |
#import "PithosSharingAccountsNode.h" |
42 |
#import "PithosContainerNode.h" |
43 |
#import "PithosSubdirNode.h" |
44 |
#import "PithosObjectNode.h" |
45 |
#import "PithosEmptyNode.h" |
46 |
#import "PithosAccount.h" |
47 |
#import "ASIPithosAccount.h" |
48 |
#import "ASIPithosRequest.h" |
49 |
#import "ASIPithosAccountRequest.h" |
50 |
#import "PithosUtilities.h" |
51 |
#import "pithos_macosAppDelegate.h" |
52 |
#import "ImageAndTextCell.h" |
53 |
|
54 |
@interface NSString(Additions) |
55 |
- (BOOL)isValidURL; |
56 |
@end |
57 |
|
58 |
@implementation NSString(Additions) |
59 |
- (BOOL)isValidURL { |
60 |
NSURL *URL = [NSURL URLWithString:self]; |
61 |
return (URL && URL.scheme && URL.host); |
62 |
} |
63 |
@end |
64 |
|
65 |
@interface PithosPreferencesSyncOutlineViewCell : ImageAndTextCell {} |
66 |
@end |
67 |
|
68 |
@implementation PithosPreferencesSyncOutlineViewCell |
69 |
|
70 |
- (void)setObjectValue:(id)object { |
71 |
if ([object isKindOfClass:[PithosNode class]]) { |
72 |
PithosNode *node = (PithosNode *)object; |
73 |
[self setStringValue:node.displayName]; |
74 |
[self setImage:node.icon]; |
75 |
[self setEditable:NO]; |
76 |
} else { |
77 |
[super setObjectValue:object]; |
78 |
} |
79 |
} |
80 |
|
81 |
@end |
82 |
|
83 |
@implementation PithosPreferencesController |
84 |
@synthesize selectedPithosAccount; |
85 |
@synthesize accountRemoveEnable; |
86 |
@synthesize authURLString, manual, pithosObjectStoreURLString, astakosAccountURLString, astakosWebloginURLString, |
87 |
authUser, authToken, displayname, ignoreSSLErrors, retrieveTokenEnable, authorizationSaveEnable, authorizationCancelEnable; |
88 |
@synthesize syncActive, syncSkipHidden, syncDirectoryPath, syncAccountsDictionary, syncApplyEnable, syncCancelEnable, |
89 |
syncAccountsRootFilesNodes; |
90 |
|
91 |
#pragma mark - |
92 |
#pragma mark Object Lifecycle |
93 |
|
94 |
- (id)init { |
95 |
return [super initWithWindowNibName:@"PithosPreferencesController"]; |
96 |
} |
97 |
|
98 |
- (void)windowDidLoad { |
99 |
[super windowDidLoad]; |
100 |
|
101 |
NSWindow *window = [self window]; |
102 |
[window setHidesOnDeactivate:NO]; |
103 |
[window setExcludedFromWindowsMenu:YES]; |
104 |
|
105 |
// // Select the first tab when the window is loaded for the first time. |
106 |
// [[window valueForKeyPath:@"toolbar"] setSelectedItemIdentifier:@"0"]; |
107 |
|
108 |
[[[syncAccountsOutlineView tableColumns] objectAtIndex:1] setDataCell:[[PithosPreferencesSyncOutlineViewCell alloc] init]]; |
109 |
syncAccountsMyAccountNode = [[PithosEmptyNode alloc] initWithDisplayName:@"<my account>" |
110 |
icon:[[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeForHFSTypeCode(kUserIcon)]]; |
111 |
|
112 |
[groupsDictionaryController setInitialKey:@"group"]; |
113 |
[groupsDictionaryController setInitialValue:[NSMutableArray arrayWithObject:@"user"]]; |
114 |
[selectedGroupMembersDictionaryController setInitialKey:@"user"]; |
115 |
[selectedGroupMembersDictionaryController setInitialValue:@""]; |
116 |
|
117 |
[window setDelegate:self]; |
118 |
|
119 |
self.selectedPithosAccount = [[accountsArrayController selectedObjects] objectAtIndex:0]; |
120 |
[accountsArrayController addObserver:self forKeyPath:@"selection" options:NSKeyValueObservingOptionNew context:NULL]; |
121 |
[[NSNotificationCenter defaultCenter] addObserver:self |
122 |
selector:@selector(selectedPithosAccountNodeChildrenUpdated:) |
123 |
name:@"SelectedPithosAccountNodeChildrenUpdated" |
124 |
object:nil]; |
125 |
} |
126 |
|
127 |
- (BOOL)windowShouldClose:(id)sender { |
128 |
return [(pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate] activated]; |
129 |
} |
130 |
|
131 |
//- (void)windowWillClose:(NSNotification *)notification { |
132 |
//} |
133 |
|
134 |
//- (IBAction)toolbarItemSelected:(id)sender { |
135 |
//} |
136 |
|
137 |
#pragma mark - |
138 |
#pragma Observers |
139 |
|
140 |
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { |
141 |
if ([object isEqualTo:accountsArrayController] && |
142 |
[keyPath isEqualToString:@"selection"] && |
143 |
[[accountsArrayController selectedObjects] count]) { |
144 |
self.selectedPithosAccount = [[accountsArrayController selectedObjects] objectAtIndex:0]; |
145 |
} |
146 |
} |
147 |
|
148 |
- (void)selectedPithosAccountNodeChildrenUpdated:(NSNotification *)notification { |
149 |
[syncAccountsOutlineView reloadData]; |
150 |
// [syncAccountsOutlineView expandItem:nil expandChildren:YES]; |
151 |
} |
152 |
|
153 |
#pragma mark - |
154 |
#pragma Update |
155 |
|
156 |
- (void)updateAccounts { |
157 |
pithos_macosAppDelegate *delegate = (pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate]; |
158 |
self.accountRemoveEnable = (delegate.activated && ([delegate.pithosAccounts count] > 1)); |
159 |
} |
160 |
|
161 |
- (void)updateAuthorization { |
162 |
self.retrieveTokenEnable = ((!manual && [authURLString isValidURL]) || |
163 |
(manual && [astakosWebloginURLString isValidURL])); |
164 |
self.authorizationSaveEnable = (authToken.length && |
165 |
((!manual && [authURLString isValidURL]) || |
166 |
(manual && authUser.length && [pithosObjectStoreURLString isValidURL] && |
167 |
[astakosAccountURLString isValidURL] && [astakosWebloginURLString isValidURL]))); |
168 |
self.authorizationCancelEnable = (selectedPithosAccount.active && |
169 |
(![selectedPithosAccount.authURLString isEqualToString:authURLString] || |
170 |
(selectedPithosAccount.manual != manual) || |
171 |
![selectedPithosAccount.pithosObjectStoreURLString isEqualToString:pithosObjectStoreURLString] || |
172 |
![selectedPithosAccount.astakosAccountURLString isEqualToString:astakosAccountURLString] || |
173 |
![selectedPithosAccount.astakosWebloginURLString isEqualToString:astakosWebloginURLString] || |
174 |
![selectedPithosAccount.authToken isEqualToString:authToken] || |
175 |
![selectedPithosAccount.authUser isEqualToString:authUser] || |
176 |
(selectedPithosAccount.ignoreSSLErrors != ignoreSSLErrors))); |
177 |
} |
178 |
|
179 |
- (void)updateSync { |
180 |
BOOL isDirectory; |
181 |
self.syncApplyEnable = (selectedPithosAccount.active && |
182 |
((selectedPithosAccount.syncActive != syncActive) || |
183 |
(selectedPithosAccount.syncSkipHidden != syncSkipHidden) || |
184 |
(![selectedPithosAccount.syncDirectoryPath isEqualToString:syncDirectoryPath] && |
185 |
(![[NSFileManager defaultManager] fileExistsAtPath:syncDirectoryPath isDirectory:&isDirectory] || |
186 |
isDirectory)) || |
187 |
![selectedPithosAccount.syncAccountsDictionary isEqualToDictionary:syncAccountsDictionary])); |
188 |
self.syncCancelEnable = (selectedPithosAccount.active && |
189 |
((selectedPithosAccount.syncActive != syncActive) || |
190 |
(selectedPithosAccount.syncSkipHidden != syncSkipHidden) || |
191 |
![selectedPithosAccount.syncDirectoryPath isEqualToString:syncDirectoryPath] || |
192 |
![selectedPithosAccount.syncAccountsDictionary isEqualToDictionary:syncAccountsDictionary])); |
193 |
} |
194 |
|
195 |
#pragma mark - |
196 |
#pragma Properties |
197 |
|
198 |
- (void)setSelectedPithosAccount:(PithosAccount *)aSelectedPithosAccount { |
199 |
if (aSelectedPithosAccount && ![aSelectedPithosAccount isEqualTo:selectedPithosAccount]) { |
200 |
selectedPithosAccount.accountNode.childrenUpdatedNotificationName = nil; |
201 |
selectedPithosAccount.sharingAccountsNode.childrenUpdatedNotificationName = nil; |
202 |
selectedPithosAccount = aSelectedPithosAccount; |
203 |
selectedPithosAccount.accountNode.childrenUpdatedNotificationName = @"SelectedPithosAccountNodeChildrenUpdated"; |
204 |
selectedPithosAccount.sharingAccountsNode.childrenUpdatedNotificationName = @"SelectedPithosAccountNodeChildrenUpdated"; |
205 |
|
206 |
[self updateAccounts]; |
207 |
[self authorizationCancel:self]; |
208 |
[self syncCancel:self]; |
209 |
[self groupsRevert:self]; |
210 |
} |
211 |
} |
212 |
|
213 |
#pragma Authorization Properties |
214 |
|
215 |
- (void)setAuthURLString:(NSString *)anAuthURLString { |
216 |
authURLString = [anAuthURLString copy]; |
217 |
[self updateAuthorization]; |
218 |
} |
219 |
|
220 |
- (void)setManual:(BOOL)aManual { |
221 |
manual = aManual; |
222 |
[self updateAuthorization]; |
223 |
} |
224 |
|
225 |
- (void)setPithosObjectStoreURLString:(NSString *)aPithosObjectStoreURLString { |
226 |
pithosObjectStoreURLString = [aPithosObjectStoreURLString copy]; |
227 |
[self updateAuthorization]; |
228 |
} |
229 |
|
230 |
- (void)setAstakosAccountURLString:(NSString *)aAstakosAccountURLString { |
231 |
astakosAccountURLString = [aAstakosAccountURLString copy]; |
232 |
[self updateAuthorization]; |
233 |
} |
234 |
|
235 |
- (void)setAstakosWebloginURLString:(NSString *)aAstakosWebloginURLString { |
236 |
astakosWebloginURLString = [aAstakosWebloginURLString copy]; |
237 |
[self updateAuthorization]; |
238 |
} |
239 |
|
240 |
- (void)setAuthUser:(NSString *)anAuthUser { |
241 |
authUser = [anAuthUser copy]; |
242 |
[self updateAuthorization]; |
243 |
self.displayname = [self.selectedPithosAccount displaynameForUUID:authUser safe:NO]; |
244 |
} |
245 |
|
246 |
- (void)setAuthToken:(NSString *)anAuthToken { |
247 |
authToken = [anAuthToken copy]; |
248 |
[self updateAuthorization]; |
249 |
} |
250 |
|
251 |
#pragma Sync Properties |
252 |
|
253 |
- (void)setSyncActive:(BOOL)aSyncActive { |
254 |
syncActive = aSyncActive; |
255 |
[self updateSync]; |
256 |
} |
257 |
|
258 |
- (void)setSyncSkipHidden:(BOOL)aSyncSkipHidden { |
259 |
syncSkipHidden = aSyncSkipHidden; |
260 |
[self updateSync]; |
261 |
[self selectedPithosAccountNodeChildrenUpdated:nil]; |
262 |
} |
263 |
|
264 |
- (void)setSyncDirectoryPath:(NSString *)aSyncDirectoryPath { |
265 |
syncDirectoryPath = [aSyncDirectoryPath copy]; |
266 |
[self updateSync]; |
267 |
} |
268 |
|
269 |
- (void)setSyncAccountsDictionary:(NSMutableDictionary *)aSyncAccountsDictionary { |
270 |
syncAccountsDictionary = [[NSMutableDictionary alloc] initWithCapacity:[aSyncAccountsDictionary count]]; |
271 |
for (NSString *accountName in aSyncAccountsDictionary) { |
272 |
NSDictionary *aSyncContainersDictionary = [aSyncAccountsDictionary objectForKey:accountName]; |
273 |
NSMutableDictionary *syncContainersDictionary = [NSMutableDictionary dictionary]; |
274 |
for (NSString *containerName in aSyncContainersDictionary) { |
275 |
if (![accountName isEqualToString:@""] || ![[containerName lowercaseString] isEqualToString:@"shared with me"]) |
276 |
[syncContainersDictionary setObject:[NSMutableSet setWithSet:[aSyncContainersDictionary objectForKey:containerName]] |
277 |
forKey:containerName]; |
278 |
} |
279 |
if ([syncContainersDictionary count]) |
280 |
[syncAccountsDictionary setObject:syncContainersDictionary forKey:accountName]; |
281 |
} |
282 |
[self updateSync]; |
283 |
} |
284 |
|
285 |
#pragma mark - |
286 |
#pragma Actions |
287 |
|
288 |
- (IBAction)addAccount:(id)sender { |
289 |
[accountsArrayController addObject:[PithosAccount pithosAccount]]; |
290 |
[self updateAccounts]; |
291 |
pithos_macosAppDelegate *delegate = (pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate]; |
292 |
[delegate.pithosAccountsDictionary setObject:selectedPithosAccount forKey:selectedPithosAccount.name]; |
293 |
[delegate savePithosAccounts:self]; |
294 |
} |
295 |
|
296 |
- (IBAction)removeAccount:(id)sender { |
297 |
[self updateAccounts]; |
298 |
if (!accountRemoveEnable) |
299 |
return; |
300 |
PithosAccount *removedPithosAccount = selectedPithosAccount; |
301 |
pithos_macosAppDelegate *delegate = (pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate]; |
302 |
if ([delegate.currentPithosAccount isEqualTo:removedPithosAccount] && [delegate.pithosBrowserController operationsPending]) { |
303 |
NSAlert *alert = [[NSAlert alloc] init]; |
304 |
[alert setMessageText:@"Operations Pending"]; |
305 |
[alert setInformativeText:@"There are pending operations in the browser, do you want to remove the account and cancel them?"]; |
306 |
[alert addButtonWithTitle:@"OK"]; |
307 |
[alert addButtonWithTitle:@"Cancel"]; |
308 |
NSInteger choice = [alert runModal]; |
309 |
if (choice == NSAlertSecondButtonReturn) { |
310 |
return; |
311 |
} |
312 |
} |
313 |
[accountsArrayController removeObject:selectedPithosAccount]; |
314 |
[delegate.pithosAccountsDictionary removeObjectForKey:removedPithosAccount.name]; |
315 |
[delegate removedPithosAccount:removedPithosAccount]; |
316 |
[delegate savePithosAccounts:self]; |
317 |
[self updateAccounts]; |
318 |
} |
319 |
|
320 |
#pragma Authorization Actions |
321 |
|
322 |
- (IBAction)retrieveToken:(id)sender { |
323 |
PithosAccount *tmpPithosAccount = [PithosAccount pithosAccount]; |
324 |
tmpPithosAccount.manual = manual; |
325 |
tmpPithosAccount.ignoreSSLErrors = ignoreSSLErrors; |
326 |
if (!manual) { |
327 |
tmpPithosAccount.authURLString = authURLString; |
328 |
ASIPithosRequest *serviceCatalogRequest = [ASIPithosRequest serviceCatalogRequestWithPithos:tmpPithosAccount.pithos]; |
329 |
[PithosUtilities startAndWaitForRequest:serviceCatalogRequest]; |
330 |
if (serviceCatalogRequest.error || ((serviceCatalogRequest.responseStatusCode != 200) && (serviceCatalogRequest.responseStatusCode != 404))) { |
331 |
[PithosUtilities httpRequestErrorAlertWithRequest:serviceCatalogRequest]; |
332 |
return; |
333 |
} else { |
334 |
[tmpPithosAccount updateServicesFromServiceCatalogRequest:serviceCatalogRequest]; |
335 |
self.authURLString = tmpPithosAccount.authURLString; |
336 |
self.manual = tmpPithosAccount.manual; |
337 |
self.pithosObjectStoreURLString = tmpPithosAccount.pithosObjectStoreURLString; |
338 |
self.astakosAccountURLString = tmpPithosAccount.astakosAccountURLString; |
339 |
self.astakosWebloginURLString = tmpPithosAccount.astakosWebloginURLString; |
340 |
} |
341 |
} else { |
342 |
tmpPithosAccount.astakosWebloginURLString = astakosWebloginURLString; |
343 |
} |
344 |
NSString *protocol = [[[[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleURLTypes"] objectAtIndex:0] objectForKey:@"CFBundleURLSchemes"] objectAtIndex:0]; |
345 |
NSProcessInfo *processInfo = [NSProcessInfo processInfo]; |
346 |
NSString *loginURLString = [NSString stringWithFormat:@"%@?next=%@://%d/%@&force=", |
347 |
tmpPithosAccount.loginURL, |
348 |
protocol, |
349 |
[processInfo processIdentifier], |
350 |
[ASIPithosRequest encodeToPercentEscape:selectedPithosAccount.name]]; |
351 |
DLog(@"Account: %@\nloginURL: %@", self, loginURLString); |
352 |
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:loginURLString]]; |
353 |
} |
354 |
|
355 |
- (IBAction)authorizationSave:(id)sender { |
356 |
if (!manual) { |
357 |
PithosAccount *tmpPithosAccount = [PithosAccount pithosAccount]; |
358 |
tmpPithosAccount.authURLString = authURLString; |
359 |
tmpPithosAccount.manual = manual; |
360 |
tmpPithosAccount.authToken = authToken; |
361 |
tmpPithosAccount.authUser = authUser; |
362 |
tmpPithosAccount.ignoreSSLErrors = ignoreSSLErrors; |
363 |
ASIPithosRequest *serviceCatalogRequest = [ASIPithosRequest serviceCatalogRequestWithPithos:tmpPithosAccount.pithos]; |
364 |
[PithosUtilities startAndWaitForRequest:serviceCatalogRequest]; |
365 |
if (serviceCatalogRequest.error || ((serviceCatalogRequest.responseStatusCode != 200) && (serviceCatalogRequest.responseStatusCode != 404))) { |
366 |
[PithosUtilities httpRequestErrorAlertWithRequest:serviceCatalogRequest]; |
367 |
return; |
368 |
} else { |
369 |
[tmpPithosAccount updateServicesFromServiceCatalogRequest:serviceCatalogRequest]; |
370 |
self.authURLString = tmpPithosAccount.authURLString; |
371 |
self.manual = tmpPithosAccount.manual; |
372 |
self.pithosObjectStoreURLString = tmpPithosAccount.pithosObjectStoreURLString; |
373 |
self.astakosAccountURLString = tmpPithosAccount.astakosAccountURLString; |
374 |
self.astakosWebloginURLString = tmpPithosAccount.astakosWebloginURLString; |
375 |
if (serviceCatalogRequest.responseStatusCode == 200) { |
376 |
if (tmpPithosAccount.authToken) { |
377 |
self.authToken = tmpPithosAccount.authToken; |
378 |
self.authUser = tmpPithosAccount.authUser; |
379 |
} |
380 |
self.syncAccountsRootFilesNodes = [NSMutableDictionary dictionary]; |
381 |
[selectedPithosAccount updateWithAuthURLString:authURLString |
382 |
pithosObjectStoreURLString:pithosObjectStoreURLString |
383 |
astakosAccountURLString:astakosAccountURLString |
384 |
astakosWebloginURLString:astakosWebloginURLString |
385 |
manual:manual |
386 |
authToken:authToken |
387 |
authUser:authUser |
388 |
ignoreSSLErrors:ignoreSSLErrors]; |
389 |
self.displayname = [selectedPithosAccount displaynameForUUID:authUser safe:NO]; |
390 |
[self updateAuthorization]; |
391 |
} else { |
392 |
NSAlert *alert = [[NSAlert alloc] init]; |
393 |
[alert setMessageText:@"Authentication Failure"]; |
394 |
[alert setInformativeText:@"Please enter also your UUID."]; |
395 |
[alert addButtonWithTitle:@"OK"]; |
396 |
[alert runModal]; |
397 |
return; |
398 |
} |
399 |
} |
400 |
} else { |
401 |
self.syncAccountsRootFilesNodes = [NSMutableDictionary dictionary]; |
402 |
self.authURLString = nil; |
403 |
// There is not a proper way to authenticate if the pair of token and UUID is valid. |
404 |
[selectedPithosAccount updateWithAuthURLString:authURLString |
405 |
pithosObjectStoreURLString:pithosObjectStoreURLString |
406 |
astakosAccountURLString:astakosAccountURLString |
407 |
astakosWebloginURLString:astakosWebloginURLString |
408 |
manual:manual |
409 |
authToken:authToken |
410 |
authUser:authUser |
411 |
ignoreSSLErrors:ignoreSSLErrors]; |
412 |
self.pithosObjectStoreURLString = selectedPithosAccount.pithosObjectStoreURLString; |
413 |
self.astakosAccountURLString = selectedPithosAccount.astakosAccountURLString; |
414 |
self.astakosWebloginURLString = selectedPithosAccount.astakosWebloginURLString; |
415 |
self.displayname = [selectedPithosAccount displaynameForUUID:authUser safe:NO]; |
416 |
[self updateAuthorization]; |
417 |
} |
418 |
|
419 |
pithos_macosAppDelegate *delegate = (pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate]; |
420 |
[delegate savePithosAccounts:self]; |
421 |
if (!delegate.activated) { |
422 |
delegate.activated = YES; |
423 |
[delegate showPithosBrowser:self]; |
424 |
} |
425 |
if ([selectedPithosAccount isEqualTo:delegate.currentPithosAccount]) |
426 |
delegate.pithosBrowserController.pithosAccountManager = selectedPithosAccount; |
427 |
} |
428 |
|
429 |
- (IBAction)authorizationCancel:(id)server { |
430 |
self.authURLString = selectedPithosAccount.authURLString; |
431 |
self.manual = selectedPithosAccount.manual; |
432 |
self.pithosObjectStoreURLString = selectedPithosAccount.pithosObjectStoreURLString; |
433 |
self.astakosAccountURLString = selectedPithosAccount.astakosAccountURLString; |
434 |
self.astakosWebloginURLString = selectedPithosAccount.astakosWebloginURLString; |
435 |
self.authToken = selectedPithosAccount.authToken; |
436 |
self.authUser = selectedPithosAccount.authUser; |
437 |
self.ignoreSSLErrors = selectedPithosAccount.ignoreSSLErrors; |
438 |
} |
439 |
|
440 |
#pragma Sync Actions |
441 |
|
442 |
- (IBAction)syncApply:(id)sender { |
443 |
[selectedPithosAccount updateSyncWithSyncActive:syncActive |
444 |
syncDirectoryPath:syncDirectoryPath |
445 |
syncAccountsDictionary:syncAccountsDictionary |
446 |
syncSkipHidden:syncSkipHidden]; |
447 |
[self updateSync]; |
448 |
pithos_macosAppDelegate *delegate = (pithos_macosAppDelegate *)[[NSApplication sharedApplication] delegate]; |
449 |
[delegate savePithosAccounts:self]; |
450 |
[delegate sync]; |
451 |
} |
452 |
|
453 |
- (IBAction)syncCancel:(id)sender { |
454 |
self.syncActive = selectedPithosAccount.syncActive; |
455 |
self.syncDirectoryPath = selectedPithosAccount.syncDirectoryPath; |
456 |
self.syncAccountsDictionary = selectedPithosAccount.syncAccountsDictionary; |
457 |
self.syncAccountsRootFilesNodes = [NSMutableDictionary dictionary]; |
458 |
self.syncSkipHidden = selectedPithosAccount.syncSkipHidden; |
459 |
} |
460 |
|
461 |
- (IBAction)syncRefresh:(id)sender { |
462 |
selectedPithosAccount.accountNode.forcedRefresh = YES; |
463 |
[selectedPithosAccount.accountNode invalidateChildrenRecursive]; |
464 |
selectedPithosAccount.sharingAccountsNode.forcedRefresh = YES; |
465 |
[selectedPithosAccount.sharingAccountsNode invalidateChildrenRecursive]; |
466 |
if (selectedPithosAccount.accountNode.children && selectedPithosAccount.sharingAccountsNode.children) { |
467 |
} |
468 |
} |
469 |
|
470 |
#pragma mark Groups Actions |
471 |
|
472 |
- (IBAction)groupsApply:(id)sender { |
473 |
[[self window] makeFirstResponder:nil]; |
474 |
if (selectedPithosAccount.active) |
475 |
[selectedPithosAccount.accountNode applyInfo]; |
476 |
} |
477 |
|
478 |
- (IBAction)groupsRevert:(id)sender { |
479 |
if (selectedPithosAccount.active && selectedPithosAccount.accountNode) |
480 |
[selectedPithosAccount.accountNode refreshInfo]; |
481 |
} |
482 |
|
483 |
#pragma mark - |
484 |
#pragma mark NSOutlineViewDataSource |
485 |
|
486 |
// <my account> [PithosEmptyNode] |
487 |
// - <container>+ [PithosContainerNode] |
488 |
// -- <subdir>+ [PithosSubdirNode] |
489 |
// -- <root files> [PithosEmptyNode] |
490 |
// <sharing account>+ [PithosSharingAccountNode] |
491 |
// - <container>+ [PithosContainerNode] |
492 |
// -- <subdir>+ [PithosSubdirNode] |
493 |
// -- <root files> [PithosEmptyNode] |
494 |
|
495 |
- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item { |
496 |
if (!selectedPithosAccount.active) |
497 |
return 0; |
498 |
if (outlineView == syncAccountsOutlineView) { |
499 |
if (item == nil) { |
500 |
// root: <my account> + #<sharing account> |
501 |
NSInteger accountsCount = 0; |
502 |
if ([selectedPithosAccount.accountNode.children count]) |
503 |
accountsCount = 1; |
504 |
if (selectedPithosAccount.sharingAccountsNode.children) |
505 |
accountsCount += selectedPithosAccount.sharingAccountsNode.children.count; |
506 |
return accountsCount; |
507 |
} else if (item == syncAccountsMyAccountNode) { |
508 |
// root/<my account>: #<container> |
509 |
if (selectedPithosAccount.accountNode.children) { |
510 |
NSInteger containersCount = 0; |
511 |
for (PithosContainerNode *node in selectedPithosAccount.accountNode.children) { |
512 |
if (![[node.displayName lowercaseString] isEqualToString:@"shared with me"]) |
513 |
containersCount++; |
514 |
} |
515 |
return containersCount; |
516 |
} |
517 |
} else if ([item class] == [PithosAccountNode class]) { |
518 |
// root/<sharing account>: #<container> |
519 |
PithosAccountNode *accountNode = (PithosAccountNode *)item; |
520 |
if (accountNode.children) |
521 |
return accountNode.children.count; |
522 |
} else if ([item class] == [PithosContainerNode class]) { |
523 |
// root/{<my account>, <sharing account>}/<container>: #<subdir> + <root files> |
524 |
PithosContainerNode *containerNode = (PithosContainerNode *)item; |
525 |
if (containerNode.children) { |
526 |
// We add 1 for the root files node |
527 |
NSInteger subdirCount = 1; |
528 |
for (PithosNode *node in containerNode.children) { |
529 |
if (([node class] == [PithosSubdirNode class]) && (!syncSkipHidden || ![node.displayName hasPrefix:@"."])) |
530 |
subdirCount++; |
531 |
} |
532 |
return subdirCount; |
533 |
} |
534 |
} |
535 |
} |
536 |
return 0; |
537 |
} |
538 |
|
539 |
- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item { |
540 |
if (outlineView == syncAccountsOutlineView) { |
541 |
if (item == nil) { |
542 |
// root: [ <my account>, <sharing account>+ ] |
543 |
if ([selectedPithosAccount.accountNode.children count]) { |
544 |
if (index == 0) |
545 |
return syncAccountsMyAccountNode; |
546 |
else |
547 |
return [selectedPithosAccount.sharingAccountsNode.children objectAtIndex:(index - 1)]; |
548 |
} else { |
549 |
return [selectedPithosAccount.sharingAccountsNode.children objectAtIndex:index]; |
550 |
} |
551 |
} else if (item == syncAccountsMyAccountNode) { |
552 |
// root/<my account>: [ <container>+ ] |
553 |
NSInteger currentContainerIndex = -1; |
554 |
for (PithosContainerNode *node in selectedPithosAccount.accountNode.children) { |
555 |
if (![[node.displayName lowercaseString] isEqualToString:@"shared with me"]) { |
556 |
currentContainerIndex++; |
557 |
if (currentContainerIndex == index) |
558 |
return node; |
559 |
} |
560 |
} |
561 |
} else if ([item class] == [PithosAccountNode class]) { |
562 |
// root/<sharing account>: [ <container>+ ] |
563 |
return [((PithosAccountNode *)item).children objectAtIndex:index]; |
564 |
} else if ([item class] == [PithosContainerNode class]) { |
565 |
// root/{<my account>, <sharing account>}/<container>: [ <subdir>+, <root files> ] |
566 |
PithosContainerNode *containerNode = (PithosContainerNode *)item; |
567 |
NSInteger currentSubdirIndex = -1; |
568 |
for (PithosNode *node in containerNode.children) { |
569 |
if (([node class] == [PithosSubdirNode class]) && (!syncSkipHidden || ![node.displayName hasPrefix:@"."])) { |
570 |
currentSubdirIndex++; |
571 |
if (currentSubdirIndex == index) |
572 |
return node; |
573 |
} |
574 |
} |
575 |
if (++currentSubdirIndex == index) { |
576 |
NSString *accountName = containerNode.sharingAccount; |
577 |
if (!accountName) |
578 |
accountName = @""; |
579 |
PithosEmptyNode *rootFilesNode = [[syncAccountsRootFilesNodes objectForKey:accountName] |
580 |
objectForKey:containerNode.displayName]; |
581 |
if (!rootFilesNode) { |
582 |
if (![syncAccountsRootFilesNodes objectForKey:accountName]) |
583 |
[syncAccountsRootFilesNodes setObject:[NSMutableDictionary dictionary] forKey:accountName]; |
584 |
rootFilesNode = [[PithosEmptyNode alloc] initWithDisplayName:@"<root files>" |
585 |
icon:[[NSWorkspace sharedWorkspace] iconForFileType:@""]]; |
586 |
rootFilesNode.parent = containerNode; |
587 |
[[syncAccountsRootFilesNodes objectForKey:accountName] setObject:rootFilesNode forKey:containerNode.displayName]; |
588 |
} |
589 |
return rootFilesNode; |
590 |
} |
591 |
} |
592 |
} |
593 |
return nil; |
594 |
} |
595 |
|
596 |
- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item { |
597 |
if (outlineView == syncAccountsOutlineView) { |
598 |
if ((item == syncAccountsMyAccountNode) || |
599 |
([item class] == [PithosAccountNode class]) || |
600 |
([item class] == [PithosContainerNode class])) |
601 |
return YES; |
602 |
} |
603 |
return NO; |
604 |
} |
605 |
|
606 |
- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item { |
607 |
if (outlineView == syncAccountsOutlineView) { |
608 |
if ([[tableColumn identifier] isEqualToString:@"sync"]) { |
609 |
if (item == syncAccountsMyAccountNode) { |
610 |
// root/<my account> |
611 |
// My account is |
612 |
// off if not in dictionary |
613 |
// mixed if in dictionary with exclusions |
614 |
// on if in dictionary without exclusions |
615 |
NSMutableDictionary *syncContainersDictionary = [syncAccountsDictionary objectForKey:@""]; |
616 |
if (syncContainersDictionary) { |
617 |
for (PithosContainerNode *node in selectedPithosAccount.accountNode.children) { |
618 |
if (![[node.displayName lowercaseString] isEqualToString:@"shared with me"]) { |
619 |
NSMutableSet *containerExcludedDirectories = [syncContainersDictionary objectForKey:node.displayName]; |
620 |
if (!containerExcludedDirectories || [containerExcludedDirectories count]) |
621 |
return [NSNumber numberWithUnsignedInteger:NSMixedState]; |
622 |
} |
623 |
} |
624 |
return [NSNumber numberWithUnsignedInteger:NSOnState]; |
625 |
} |
626 |
return [NSNumber numberWithUnsignedInteger:NSOffState]; |
627 |
} else if ([item class] == [PithosAccountNode class]) { |
628 |
// root/<sharing account> |
629 |
// A sharing account is |
630 |
// off if not in dictionary |
631 |
// mixed if in dictionary with exclusions |
632 |
// on if in dictionary without exclusions |
633 |
PithosAccountNode *accountNode = (PithosAccountNode *)item; |
634 |
NSMutableDictionary *syncContainersDictionary = [syncAccountsDictionary objectForKey:accountNode.sharingAccount]; |
635 |
if (syncContainersDictionary) { |
636 |
for (PithosContainerNode *node in accountNode.children) { |
637 |
NSMutableSet *containerExcludedDirectories = [syncContainersDictionary objectForKey:node.displayName]; |
638 |
if (!containerExcludedDirectories || [containerExcludedDirectories count]) |
639 |
return [NSNumber numberWithUnsignedInteger:NSMixedState]; |
640 |
} |
641 |
return [NSNumber numberWithUnsignedInteger:NSOnState]; |
642 |
} |
643 |
return [NSNumber numberWithUnsignedInteger:NSOffState]; |
644 |
} else if ([item class] == [PithosContainerNode class]) { |
645 |
// root/{<my account>, <sharing account>}/<container> |
646 |
// A container is |
647 |
// off if not in dictionary |
648 |
// mixed if in dictionary with exclusions |
649 |
// on if in dictionary without exclusions |
650 |
PithosContainerNode *node = (PithosContainerNode *)item; |
651 |
NSString *accountName = node.sharingAccount; |
652 |
if (!accountName) |
653 |
accountName = @""; |
654 |
NSMutableSet *containerExcludedDirectories = [[syncAccountsDictionary objectForKey:accountName] |
655 |
objectForKey:node.displayName]; |
656 |
if (containerExcludedDirectories) { |
657 |
if ([containerExcludedDirectories count]) |
658 |
return [NSNumber numberWithUnsignedInteger:NSMixedState]; |
659 |
else |
660 |
return [NSNumber numberWithUnsignedInteger:NSOnState]; |
661 |
} |
662 |
return [NSNumber numberWithUnsignedInteger:NSOffState]; |
663 |
} else if ([item class] == [PithosSubdirNode class]) { |
664 |
// root/{<my account>, <sharing account>}/<container>/<subdir> |
665 |
// Directory is off if parent container not in dictionary or if excluded |
666 |
// displayName should be localized and lowercased |
667 |
PithosSubdirNode *node = (PithosSubdirNode *)item; |
668 |
NSString *accountName = node.sharingAccount; |
669 |
if (!accountName) |
670 |
accountName = @""; |
671 |
NSMutableSet *containerExcludedDirectories = [[syncAccountsDictionary objectForKey:accountName] |
672 |
objectForKey:node.parent.displayName]; |
673 |
if (!containerExcludedDirectories || |
674 |
[containerExcludedDirectories |
675 |
containsObject:[[node.displayName lowercaseString] stringByReplacingOccurrencesOfString:@"/" withString:@":"]]) |
676 |
return [NSNumber numberWithUnsignedInteger:NSOffState]; |
677 |
else |
678 |
return [NSNumber numberWithUnsignedInteger:NSOnState]; |
679 |
} else if ([item class] == [PithosEmptyNode class]) { |
680 |
// root/{<my account>, <sharing account>}/<container>/<root files> |
681 |
// Root files is off if parent container not in dictionary or if excluded |
682 |
PithosEmptyNode *node = (PithosEmptyNode *)item; |
683 |
NSString *accountName = node.parent.sharingAccount; |
684 |
if (!accountName) |
685 |
accountName = @""; |
686 |
NSMutableSet *containerExcludedDirectories = [[syncAccountsDictionary objectForKey:accountName] |
687 |
objectForKey:node.parent.displayName]; |
688 |
if (!containerExcludedDirectories || [containerExcludedDirectories containsObject:@""]) |
689 |
return [NSNumber numberWithUnsignedInteger:NSOffState]; |
690 |
else |
691 |
return [NSNumber numberWithUnsignedInteger:NSOnState]; |
692 |
} |
693 |
return [NSNumber numberWithUnsignedInteger:NSOffState]; |
694 |
} else if ([[tableColumn identifier] isEqualToString:@"path"]) { |
695 |
return (PithosNode *)item; |
696 |
} |
697 |
} |
698 |
return nil; |
699 |
} |
700 |
|
701 |
- (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item { |
702 |
if (outlineView == syncAccountsOutlineView) { |
703 |
if ([[tableColumn identifier] isEqualToString:@"sync"]) { |
704 |
NSCellStateValue newState = [object unsignedIntegerValue]; |
705 |
if (item == syncAccountsMyAccountNode) { |
706 |
// root/<my account> |
707 |
// If new state is |
708 |
// mixed/on include my account with no exclusions |
709 |
// off exclude my account |
710 |
if ((newState == NSOnState) || (newState == NSMixedState)) { |
711 |
NSMutableDictionary *syncContainersDictionary = [NSMutableDictionary dictionary]; |
712 |
for (PithosContainerNode *node in selectedPithosAccount.accountNode.children) { |
713 |
if (![[node.displayName lowercaseString] isEqualToString:@"shared with me"]) |
714 |
[syncContainersDictionary setObject:[NSMutableSet set] forKey:node.displayName]; |
715 |
} |
716 |
[syncAccountsDictionary setObject:syncContainersDictionary forKey:@""]; |
717 |
} else { |
718 |
[syncAccountsDictionary removeObjectForKey:@""]; |
719 |
} |
720 |
[outlineView reloadItem:item reloadChildren:YES]; |
721 |
} else if ([item class] == [PithosAccountNode class]) { |
722 |
// root/<sharing account> |
723 |
// If new state is |
724 |
// mixed/on include sharing account with no exclusions |
725 |
// off exclude sharing account |
726 |
PithosAccountNode *accountNode = (PithosAccountNode *)item; |
727 |
if ((newState == NSOnState) || (newState == NSMixedState)) { |
728 |
NSMutableDictionary *syncContainersDictionary = [NSMutableDictionary dictionary]; |
729 |
for (PithosContainerNode *node in accountNode.children) { |
730 |
[syncContainersDictionary setObject:[NSMutableSet set] forKey:node.displayName]; |
731 |
} |
732 |
[syncAccountsDictionary setObject:syncContainersDictionary forKey:accountNode.sharingAccount]; |
733 |
} else { |
734 |
[syncAccountsDictionary removeObjectForKey:accountNode.sharingAccount]; |
735 |
} |
736 |
[outlineView reloadItem:item reloadChildren:YES]; |
737 |
} else if ([item class] == [PithosContainerNode class]) { |
738 |
// root/{<my account>, <sharing account>}/<container> |
739 |
// If new state is |
740 |
// mixed/on include container with no excluded directories |
741 |
// off exclude container |
742 |
PithosContainerNode *node = (PithosContainerNode *)item; |
743 |
NSString *accountName = node.sharingAccount; |
744 |
PithosNode *accountNode = node.parent; |
745 |
if (!accountName) { |
746 |
accountName = @""; |
747 |
accountNode = syncAccountsMyAccountNode; |
748 |
} |
749 |
NSMutableDictionary *syncContainersDictionary = [syncAccountsDictionary objectForKey:accountName]; |
750 |
if ((newState == NSOnState) || (newState == NSMixedState)) { |
751 |
if (!syncContainersDictionary) { |
752 |
syncContainersDictionary = [NSMutableDictionary dictionary]; |
753 |
[syncAccountsDictionary setObject:syncContainersDictionary forKey:accountName]; |
754 |
} |
755 |
[syncContainersDictionary setObject:[NSMutableSet set] forKey:node.displayName]; |
756 |
} else if (syncContainersDictionary) { |
757 |
[syncContainersDictionary removeObjectForKey:node.displayName]; |
758 |
if (![syncContainersDictionary count]) |
759 |
[syncAccountsDictionary removeObjectForKey:accountName]; |
760 |
} |
761 |
[outlineView reloadItem:accountNode reloadChildren:YES]; |
762 |
} else if ([item class] == [PithosSubdirNode class]) { |
763 |
// root/{<my account>, <sharing account>}/<container>/<subdir> |
764 |
// If new state is |
765 |
// mixed/on include directory (if container not included, include and exclude all others) |
766 |
// off exclude directory |
767 |
PithosSubdirNode *node = (PithosSubdirNode *)item; |
768 |
NSString *accountName = node.sharingAccount; |
769 |
PithosNode *accountNode = node.parent.parent; |
770 |
if (!accountName) { |
771 |
accountName = @""; |
772 |
accountNode = syncAccountsMyAccountNode; |
773 |
} |
774 |
NSMutableDictionary *syncContainersDictionary = [syncAccountsDictionary objectForKey:accountName]; |
775 |
NSMutableSet *containerExcludedDirectories = [syncContainersDictionary objectForKey:node.parent.displayName]; |
776 |
NSString *directoryName = [[node.displayName lowercaseString] stringByReplacingOccurrencesOfString:@"/" withString:@":"]; |
777 |
if ((newState == NSOnState) || (newState == NSMixedState)) { |
778 |
if (containerExcludedDirectories) { |
779 |
[containerExcludedDirectories removeObject:directoryName]; |
780 |
} else { |
781 |
if (!syncContainersDictionary) { |
782 |
syncContainersDictionary = [NSMutableDictionary dictionary]; |
783 |
[syncAccountsDictionary setObject:syncContainersDictionary forKey:accountName]; |
784 |
} |
785 |
NSMutableSet *newContainerExcludeDirectories = [NSMutableSet setWithObject:@""]; |
786 |
for (PithosNode *siblingNode in node.parent.children) { |
787 |
if (([siblingNode class] == [PithosSubdirNode class]) && |
788 |
(!syncSkipHidden || ![siblingNode.displayName hasPrefix:@"."])) { |
789 |
NSString *siblingDirectoryName = [[siblingNode.displayName lowercaseString] |
790 |
stringByReplacingOccurrencesOfString:@"/" withString:@":"]; |
791 |
if (![siblingDirectoryName isEqualToString:directoryName] && |
792 |
![newContainerExcludeDirectories containsObject:siblingDirectoryName]) |
793 |
[newContainerExcludeDirectories addObject:siblingDirectoryName]; |
794 |
} |
795 |
} |
796 |
[syncContainersDictionary setObject:newContainerExcludeDirectories forKey:node.parent.displayName]; |
797 |
} |
798 |
} else if (syncContainersDictionary && |
799 |
containerExcludedDirectories && |
800 |
![containerExcludedDirectories containsObject:directoryName]) { |
801 |
[containerExcludedDirectories addObject:directoryName]; |
802 |
} |
803 |
[outlineView reloadItem:accountNode reloadChildren:YES]; |
804 |
} else if ([item class] == [PithosEmptyNode class]) { |
805 |
// If new state is |
806 |
// mixed/on include root files (if container not included, include and exclude all others) |
807 |
// off exclude root files |
808 |
PithosEmptyNode *node = (PithosEmptyNode *)item; |
809 |
NSString *accountName = node.parent.sharingAccount; |
810 |
PithosNode *accountNode = node.parent.parent; |
811 |
if (!accountName) { |
812 |
accountName = @""; |
813 |
accountNode = syncAccountsMyAccountNode; |
814 |
} |
815 |
NSMutableDictionary *syncContainersDictionary = [syncAccountsDictionary objectForKey:accountName]; |
816 |
NSMutableSet *containerExcludedDirectories = [syncContainersDictionary objectForKey:node.parent.displayName]; |
817 |
if ((newState == NSOnState) || (newState == NSMixedState)) { |
818 |
if (containerExcludedDirectories) { |
819 |
[containerExcludedDirectories removeObject:@""]; |
820 |
} else { |
821 |
if (!syncContainersDictionary) { |
822 |
syncContainersDictionary = [NSMutableDictionary dictionary]; |
823 |
[syncAccountsDictionary setObject:syncContainersDictionary forKey:accountName]; |
824 |
} |
825 |
NSMutableSet *newContainerExcludeDirectories = [NSMutableSet set]; |
826 |
for (PithosNode *siblingNode in node.parent.children) { |
827 |
if (([siblingNode class] == [PithosSubdirNode class]) && |
828 |
(!syncSkipHidden || ![siblingNode.displayName hasPrefix:@"."])) { |
829 |
NSString *siblingDirectoryName = [[siblingNode.displayName lowercaseString] |
830 |
stringByReplacingOccurrencesOfString:@"/" withString:@":"]; |
831 |
if (![newContainerExcludeDirectories containsObject:siblingDirectoryName]) |
832 |
[newContainerExcludeDirectories addObject:siblingDirectoryName]; |
833 |
} |
834 |
} |
835 |
[syncContainersDictionary setObject:newContainerExcludeDirectories forKey:node.parent.displayName]; |
836 |
} |
837 |
} else if (syncContainersDictionary && |
838 |
containerExcludedDirectories && |
839 |
![containerExcludedDirectories containsObject:@""]) { |
840 |
[containerExcludedDirectories addObject:@""]; |
841 |
} |
842 |
[outlineView reloadItem:accountNode reloadChildren:YES]; |
843 |
} |
844 |
[self updateSync]; |
845 |
} |
846 |
} |
847 |
} |
848 |
|
849 |
@end |