root / Classes / StorageObjectViewController.m @ c2940e36
History | View | Annotate | Download (36.9 kB)
1 |
// |
---|---|
2 |
// StorageObjectViewController.m |
3 |
// OpenStack |
4 |
// |
5 |
// Created by Mike Mayo on 12/19/10. |
6 |
// The OpenStack project is provided under the Apache 2.0 license. |
7 |
// |
8 |
|
9 |
#import "StorageObjectViewController.h" |
10 |
#import "OpenStackAccount.h" |
11 |
#import "OpenStackRequest.h" |
12 |
#import "Container.h" |
13 |
#import "Folder.h" |
14 |
#import "StorageObject.h" |
15 |
#import "AccountManager.h" |
16 |
#import "AnimatedProgressView.h" |
17 |
#import "UIViewController+Conveniences.h" |
18 |
#import "MediaViewController.h" |
19 |
#import "FolderViewController.h" |
20 |
#import "UIColor+MoreColors.h" |
21 |
#import "OpenStackAppDelegate.h" |
22 |
#import "EditMetadataViewController.h" |
23 |
#import "Provider.h" |
24 |
#import "EditPermissionsViewController.h" |
25 |
#import "TextViewCell.h" |
26 |
#import "NSString+Conveniences.h" |
27 |
#import "APICallback.h" |
28 |
#import "ObjectVersionsViewController.h" |
29 |
#import <MediaPlayer/MPMoviePlayerController.h> |
30 |
#import <MessageUI/MessageUI.h> |
31 |
#import "ActivityIndicatorView.h" |
32 |
|
33 |
#define kDetails 0 |
34 |
#define kActions 1 |
35 |
#define kMetadata 2 |
36 |
|
37 |
#define maxMetadataViewableLength 12 |
38 |
|
39 |
// TODO: use etag to reset download |
40 |
// TODO: try downloading directly to the file to save memory. don't use object.data |
41 |
|
42 |
/* |
43 |
Name whatever.txt |
44 |
Full Path folder/whatever.txt |
45 |
Size 123 KB |
46 |
Type text/plain |
47 |
Last Modified 2012-12-21 11:11:00 |
48 |
|
49 |
if fileDownloaded: |
50 |
Open File |
51 |
Mail File |
52 |
Delete from Cache |
53 |
else if fileDownloading: |
54 |
Open File (disabled) |
55 |
Mail File (disabled) |
56 |
Downloading (progress view) |
57 |
Cancel |
58 |
else: |
59 |
Open File |
60 |
Mail File |
61 |
|
62 |
Metadata |
63 |
Key Value -> tap goes to a metadata item VC to edit or delete |
64 |
Key Value |
65 |
Add Metadata... (if max not already reached) |
66 |
|
67 |
Public URL On/Off |
68 |
|
69 |
Share |
70 |
|
71 |
Versions |
72 |
|
73 |
Delete Object |
74 |
*/ |
75 |
|
76 |
@implementation StorageObjectViewController |
77 |
|
78 |
@synthesize account, container, folder, object, folderViewController; |
79 |
@synthesize oldPublicURI, permissions, documentInteractionController, objectIsReadOnly, versionID; |
80 |
|
81 |
#pragma mark - View lifecycle |
82 |
|
83 |
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation { |
84 |
return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait); |
85 |
} |
86 |
|
87 |
- (void)viewDidLoad { |
88 |
[super viewDidLoad]; |
89 |
|
90 |
objectIsPublicSwitch = [[UISwitch alloc] init]; |
91 |
[objectIsPublicSwitch addTarget:self action:@selector(objectIsPublicSwitchChanged:) forControlEvents:UIControlEventValueChanged]; |
92 |
|
93 |
self.permissions = [object permissions]; |
94 |
objectIsReadOnly = NO; |
95 |
if (account.sharingAccount) { |
96 |
if (permissions.count) { |
97 |
objectIsReadOnly = ![[permissions objectForKey:account.username] isEqualToString:@"write"]; |
98 |
} |
99 |
objectIsPublicSwitch.enabled = NO; |
100 |
} |
101 |
|
102 |
downloadProgressView = [[AnimatedProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault]; |
103 |
fileDownloading = NO; |
104 |
OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate]; |
105 |
OpenStackRequest *request = [app objectDownloadRequestForAccount:account container:container object:object]; |
106 |
if (request) { |
107 |
fileDownloading = YES; |
108 |
request.downloadProgressDelegate = self; |
109 |
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone]; |
110 |
} |
111 |
|
112 |
} |
113 |
|
114 |
- (void)viewWillAppear:(BOOL)animated { |
115 |
[super viewWillAppear:animated]; |
116 |
self.navigationItem.title = object.name; |
117 |
|
118 |
NSIndexPath *selection = [self.tableView indexPathForSelectedRow]; |
119 |
if (selection) |
120 |
[self.tableView deselectRowAtIndexPath:selection animated:YES]; |
121 |
|
122 |
OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate]; |
123 |
if ([app cacheFilePathForHash:object.hash]) { |
124 |
fileDownloaded = YES; |
125 |
} else { |
126 |
fileDownloaded = NO; |
127 |
} |
128 |
|
129 |
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) { |
130 |
CGRect rect = downloadProgressView.frame; |
131 |
rect.size.width = 440.0; |
132 |
downloadProgressView.frame = rect; |
133 |
} |
134 |
|
135 |
publicLinkSection = 3; |
136 |
permissionsSection = 4; |
137 |
versionsSection = 5; |
138 |
deleteSection = 6; |
139 |
|
140 |
if (versionID) { |
141 |
publicLinkSection = -1; |
142 |
permissionsSection = -1; |
143 |
versionsSection = -1; |
144 |
deleteSection = -1; |
145 |
objectIsReadOnly = YES; |
146 |
} |
147 |
if (account.sharingAccount || account.shared) { |
148 |
versionsSection = -1; |
149 |
deleteSection = -1; |
150 |
} |
151 |
|
152 |
objectIsPublic = ([object.publicURI length]); |
153 |
objectIsPublicSwitch.on = objectIsPublic; |
154 |
[self.tableView reloadData]; |
155 |
} |
156 |
|
157 |
- (void)viewDidAppear:(BOOL)animated { |
158 |
[super viewDidAppear:animated]; |
159 |
if (!object.metadata) { |
160 |
[self reloadMetadataSection]; |
161 |
} else { |
162 |
[self updatePermissionsUserCatalog]; |
163 |
} |
164 |
if (fileDownloading) { |
165 |
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:kActions] |
166 |
atScrollPosition:UITableViewScrollPositionMiddle |
167 |
animated:YES]; |
168 |
} |
169 |
} |
170 |
|
171 |
#pragma mark - Memory management |
172 |
|
173 |
- (void)dealloc { |
174 |
if (fileDownloading) { |
175 |
OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate]; |
176 |
OpenStackRequest *request = [app objectDownloadRequestForAccount:account container:container object:object]; |
177 |
request.downloadProgressDelegate = nil; |
178 |
} |
179 |
[account release]; |
180 |
[downloadProgressView release]; |
181 |
[cdnURLActionSheet release]; |
182 |
[folderViewController release]; |
183 |
[oldPublicURI release]; |
184 |
[permissions release]; |
185 |
[objectIsPublicSwitch release]; |
186 |
[documentInteractionController release]; |
187 |
[versionID release]; |
188 |
[super dealloc]; |
189 |
} |
190 |
|
191 |
#pragma mark - Internal |
192 |
|
193 |
- (void)objectIsPublicSwitchChanged:(id)sender { |
194 |
NSString *activityMessage = [NSString stringWithFormat:@"Enabling public link.."]; |
195 |
self.oldPublicURI = object.publicURI; |
196 |
|
197 |
if (objectIsPublic) { |
198 |
activityMessage = [NSString stringWithFormat:@"Disabling public link.."]; |
199 |
object.publicURI = @""; |
200 |
} else { |
201 |
object.publicURI = @"TRUE"; |
202 |
} |
203 |
__block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:activityMessage |
204 |
andAddToView:self.view |
205 |
scrollOffset:self.tableView.contentOffset.y]; |
206 |
objectIsPublic = !objectIsPublic; |
207 |
[[self.account.manager writeObjectMetadata:container object:object] |
208 |
success:^(OpenStackRequest *request) { |
209 |
NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:publicLinkSection]; |
210 |
if (objectIsPublic) { |
211 |
[[self.account.manager getObjectInfo:container object:object version:versionID] |
212 |
success:^(OpenStackRequest *request) { |
213 |
[activityIndicatorView stopAnimatingAndRemoveFromSuperview]; |
214 |
object.publicURI = [request.responseHeaders objectForKey:@"X-Object-Public"]; |
215 |
NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:publicLinkSection]; |
216 |
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath] |
217 |
withRowAnimation:UITableViewRowAnimationBottom]; |
218 |
} |
219 |
failure:^(OpenStackRequest *request) { |
220 |
[activityIndicatorView stopAnimatingAndRemoveFromSuperview]; |
221 |
[self alert:@"There was a problem retrieving the public link from the server." request:request]; |
222 |
}]; |
223 |
} else { |
224 |
[activityIndicatorView stopAnimatingAndRemoveFromSuperview]; |
225 |
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath] |
226 |
withRowAnimation:UITableViewRowAnimationTop]; |
227 |
} |
228 |
} |
229 |
failure:^(OpenStackRequest *request) { |
230 |
[activityIndicatorView stopAnimatingAndRemoveFromSuperview]; |
231 |
objectIsPublic = !objectIsPublic; |
232 |
objectIsPublicSwitch.on = !objectIsPublicSwitch.on; |
233 |
object.publicURI = oldPublicURI; |
234 |
[self alert:@"There was a problem enabling the public link." request:request]; |
235 |
}]; |
236 |
} |
237 |
|
238 |
- (void)openFile { |
239 |
OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate]; |
240 |
NSString *filePath = [app cacheFilePathForHash:object.hash]; |
241 |
self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]]; |
242 |
self.documentInteractionController.delegate = self; |
243 |
self.documentInteractionController.name = object.name; |
244 |
|
245 |
CGRect frameToPresentMenuFrom; |
246 |
if ([[UIDevice currentDevice].systemVersion hasPrefix:@"6"]) { |
247 |
frameToPresentMenuFrom = CGRectMake(0.0, |
248 |
self.tableView.contentOffset.y, |
249 |
self.view.frame.size.width, |
250 |
1.0); |
251 |
} else { |
252 |
UITableViewCell *openFileCell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:kActions]]; |
253 |
frameToPresentMenuFrom = CGRectMake(openFileCell.frame.origin.x, |
254 |
openFileCell.frame.origin.y - self.tableView.contentOffset.y, |
255 |
openFileCell.frame.size.width, |
256 |
openFileCell.frame.size.height); |
257 |
} |
258 |
if (![self.documentInteractionController presentOptionsMenuFromRect:frameToPresentMenuFrom inView:self.view animated:YES]) { |
259 |
if ([self.object isPlayableMedia]) { |
260 |
MediaViewController *vc = [[MediaViewController alloc] initWithNibName:@"MediaViewController" bundle:nil]; |
261 |
vc.container = self.container; |
262 |
vc.object = self.object; |
263 |
[self.navigationController pushViewController:vc animated:YES]; |
264 |
[vc release]; |
265 |
} else { |
266 |
[self alert:@"Error" message:@"This file could not be opened."]; |
267 |
} |
268 |
} |
269 |
} |
270 |
|
271 |
- (void)mailFile { |
272 |
if (![MFMailComposeViewController canSendMail]) { |
273 |
[self alert:@"Cannot send mail" message:@"Your device has not been configured for sending mail"]; |
274 |
} else { |
275 |
OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate]; |
276 |
NSString *filePath = [app cacheFilePathForHash:object.hash]; |
277 |
NSData *data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:filePath]]; |
278 |
|
279 |
MFMailComposeViewController *vc = [[MFMailComposeViewController alloc] init]; |
280 |
vc.mailComposeDelegate = self; |
281 |
[vc setSubject:object.name]; |
282 |
[vc addAttachmentData:data mimeType:object.contentType fileName:object.name]; |
283 |
[vc setMessageBody:@"" isHTML:NO]; |
284 |
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) |
285 |
vc.modalPresentationStyle = UIModalPresentationPageSheet; |
286 |
[self presentModalViewController:vc animated:YES]; |
287 |
[vc release]; |
288 |
} |
289 |
} |
290 |
|
291 |
- (void)updatePermissionsUserCatalog { |
292 |
NSMutableArray *UUIDs = [NSMutableArray arrayWithCapacity:permissions.count]; |
293 |
for (NSString *user in permissions) { |
294 |
NSRange rangeOfColumn = [user rangeOfString:@":"]; |
295 |
NSString *UUID = (rangeOfColumn.location == NSNotFound) ? user : [user substringToIndex:rangeOfColumn.location]; |
296 |
if (![UUID isEqualToString:@"*"]) { |
297 |
[UUIDs addObject:UUID]; |
298 |
} |
299 |
} |
300 |
if (UUIDs.count) { |
301 |
__block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Loading permissions..." |
302 |
andAddToView:self.view]; |
303 |
[[self.account.manager userCatalogForDisplaynames:nil UUIDs:UUIDs] |
304 |
success:^(OpenStackRequest *request) { |
305 |
[activityIndicatorView stopAnimatingAndRemoveFromSuperview]; |
306 |
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:permissionsSection] withRowAnimation:UITableViewRowAnimationNone]; |
307 |
} |
308 |
failure:^(OpenStackRequest *request) { |
309 |
[activityIndicatorView stopAnimatingAndRemoveFromSuperview]; |
310 |
[self alert:@"Failed to translate sharing UUIDs." request:request]; |
311 |
}]; |
312 |
} |
313 |
} |
314 |
|
315 |
#pragma mark - Actions |
316 |
|
317 |
- (void)reloadMetadataSection { |
318 |
__block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Loading metadata..." |
319 |
andAddToView:self.view]; |
320 |
[[self.account.manager getObjectInfo:container object:object version:versionID] |
321 |
success:^(OpenStackRequest *request) { |
322 |
[activityIndicatorView stopAnimatingAndRemoveFromSuperview]; |
323 |
object.metadata = [NSMutableDictionary dictionary]; |
324 |
for (NSString *header in request.responseHeaders) { |
325 |
NSString *metadataKey; |
326 |
NSString *metadataValue; |
327 |
if ([header rangeOfString:@"X-Object-Meta-"].location != NSNotFound) { |
328 |
metadataKey = [NSString decodeFromPercentEscape:[header substringFromIndex:14]]; |
329 |
metadataValue = [NSString decodeFromPercentEscape:[request.responseHeaders objectForKey:header]]; |
330 |
[object.metadata setObject:metadataValue forKey:metadataKey]; |
331 |
} |
332 |
} |
333 |
[self.tableView reloadData]; |
334 |
[self updatePermissionsUserCatalog]; |
335 |
} |
336 |
failure:^(OpenStackRequest *request) { |
337 |
[activityIndicatorView stopAnimatingAndRemoveFromSuperview]; |
338 |
[self alert:@"There was a problem retrieving the object's metadata." request:request]; |
339 |
}]; |
340 |
} |
341 |
|
342 |
- (void)downloadFileForAction:(StorageObjectAction)action { |
343 |
if (fileDownloading) |
344 |
return; |
345 |
fileDownloading = YES; |
346 |
[downloadProgressView setProgress:0.0 animated:NO]; |
347 |
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone]; |
348 |
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:kActions] |
349 |
atScrollPosition:UITableViewScrollPositionMiddle |
350 |
animated:YES]; |
351 |
NSMutableDictionary *requestUserInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithInteger:action] forKey:@"action"]; |
352 |
[[self.account.manager getObject:self.container object:self.object downloadProgressDelegate:self requestUserInfo:requestUserInfo version:versionID] |
353 |
success:^(OpenStackRequest *request) { |
354 |
if (request.isCancelled) { |
355 |
fileDownloaded = NO; |
356 |
fileDownloading = NO; |
357 |
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone]; |
358 |
} else { |
359 |
fileDownloaded = YES; |
360 |
fileDownloading = NO; |
361 |
if ([self.navigationController.visibleViewController isEqual:self]) { |
362 |
if ([[request.userInfo objectForKey:@"action"] integerValue] == StorageObjectActionMailFile) { |
363 |
[self mailFile]; |
364 |
} else { |
365 |
[self openFile]; |
366 |
} |
367 |
} |
368 |
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone]; |
369 |
[self.folderViewController reloadData]; |
370 |
} |
371 |
} |
372 |
failure:^(OpenStackRequest *request) { |
373 |
fileDownloaded = NO; |
374 |
fileDownloading = NO; |
375 |
[self alert:@"File failed to download." request:request]; |
376 |
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone]; |
377 |
}]; |
378 |
} |
379 |
|
380 |
#pragma mark - UITableViewDataSource |
381 |
|
382 |
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { |
383 |
int numberOfSections = 7; |
384 |
if (publicLinkSection < 0) |
385 |
numberOfSections--; |
386 |
if (permissionsSection < 0) |
387 |
numberOfSections--; |
388 |
if (versionsSection < 0) |
389 |
numberOfSections--; |
390 |
if (deleteSection < 0) |
391 |
numberOfSections--; |
392 |
|
393 |
return numberOfSections; |
394 |
} |
395 |
|
396 |
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { |
397 |
if (section == kDetails) { |
398 |
return (object.lastModifiedString ? 5: 4); |
399 |
} else if (section == kActions) { |
400 |
if (fileDownloaded) { |
401 |
return 3; |
402 |
} else if (fileDownloading) { |
403 |
return 4; |
404 |
} else { |
405 |
return 2; |
406 |
} |
407 |
} else if (section == kMetadata) { |
408 |
if (objectIsReadOnly) { |
409 |
return [object.metadata count]; |
410 |
} else { |
411 |
return ([object.metadata count] + 1); |
412 |
} |
413 |
} else if (section == publicLinkSection) { |
414 |
return (objectIsPublic ? 2 : 1); |
415 |
} else if (section == permissionsSection) { |
416 |
if (account.sharingAccount || objectIsReadOnly) { |
417 |
return permissions.count; |
418 |
} else { |
419 |
return (permissions.count + 1); |
420 |
} |
421 |
} else { |
422 |
return 1; |
423 |
} |
424 |
} |
425 |
|
426 |
- (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { |
427 |
CGFloat result = aTableView.rowHeight; |
428 |
if ((indexPath.section == kDetails) && ((indexPath.row == 0) || (indexPath.row == 1))) { |
429 |
NSString *text; |
430 |
if (indexPath.row == 0) { |
431 |
text = object.name; |
432 |
} else { |
433 |
text = object.fullPath; |
434 |
} |
435 |
result = 22.0 + [text sizeWithFont:[UIFont systemFontOfSize:18.0] |
436 |
constrainedToSize:(([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) ? |
437 |
CGSizeMake(537.0, 9000.0) : |
438 |
CGSizeMake(221.0, 9000.0)) |
439 |
lineBreakMode:UILineBreakModeCharacterWrap].height; |
440 |
} else if ((indexPath.section == publicLinkSection) && (indexPath.row == 1)) { |
441 |
NSURL *publicLinkURL = [account.provider.publicLinkURLPrefix URLByAppendingPathComponent: |
442 |
[NSString encodeToPercentEscape:[self.object.publicURI substringFromIndex:1] |
443 |
charactersToEncode:@"!*'();:@&=+$,?%#[]"]]; |
444 |
result = 30.0 + [[publicLinkURL description] sizeWithFont:[UIFont systemFontOfSize:18.0] |
445 |
constrainedToSize:(([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) ? |
446 |
CGSizeMake(596.0, 9000.0) : |
447 |
CGSizeMake(280.0, 9000.0)) |
448 |
lineBreakMode:UILineBreakModeCharacterWrap].height; |
449 |
} |
450 |
return MAX(aTableView.rowHeight, result); |
451 |
} |
452 |
|
453 |
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { |
454 |
if ((indexPath.section == publicLinkSection) && (indexPath.row == 1)) { |
455 |
static NSString *CellIdentifier = @"TextViewCell"; |
456 |
TextViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier]; |
457 |
if (cell == nil) { |
458 |
cell = [[[TextViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; |
459 |
cell.textView.frame = cell.contentView.frame; |
460 |
|
461 |
} |
462 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
463 |
cell.textView.backgroundColor = [UIColor clearColor]; |
464 |
cell.textView.font = [UIFont systemFontOfSize:15.0]; |
465 |
cell.textView.dataDetectorTypes = UIDataDetectorTypeLink; |
466 |
cell.textView.text = [[account.provider.publicLinkURLPrefix URLByAppendingPathComponent: |
467 |
[NSString encodeToPercentEscape:[self.object.publicURI substringFromIndex:1] |
468 |
charactersToEncode:@"!*'();:@&=+$,?%#[]"]] description]; |
469 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
470 |
cell.accessoryView = UITableViewCellAccessoryNone; |
471 |
return cell; |
472 |
} |
473 |
|
474 |
if (indexPath.section == deleteSection) { |
475 |
static NSString *CellIdentifier = @"DeleteCell"; |
476 |
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:CellIdentifier]; |
477 |
if (cell == nil) { |
478 |
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; |
479 |
cell.selectionStyle = UITableViewCellSelectionStyleBlue; |
480 |
cell.textLabel.textAlignment = UITextAlignmentCenter; |
481 |
cell.textLabel.text = @"Delete Object"; |
482 |
} |
483 |
return cell; |
484 |
} |
485 |
|
486 |
static NSString *CellIdentifier = @"Cell"; |
487 |
UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier]; |
488 |
if (cell == nil) { |
489 |
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease]; |
490 |
cell.textLabel.backgroundColor = [UIColor clearColor]; |
491 |
cell.detailTextLabel.backgroundColor = [UIColor clearColor]; |
492 |
cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap; |
493 |
} |
494 |
cell.textLabel.textColor = [UIColor blackColor]; |
495 |
cell.detailTextLabel.numberOfLines = 0; |
496 |
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) { |
497 |
cell.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.8]; |
498 |
} |
499 |
cell.userInteractionEnabled = YES; |
500 |
cell.detailTextLabel.textAlignment = UITextAlignmentRight; |
501 |
cell.accessoryView = nil; |
502 |
cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0]; |
503 |
|
504 |
// Configure the cell... |
505 |
if (indexPath.section == kDetails) { |
506 |
cell.accessoryType = UITableViewCellAccessoryNone; |
507 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
508 |
cell.accessoryView = nil; |
509 |
if (indexPath.row == 0) { |
510 |
cell.textLabel.text = @"Name"; |
511 |
cell.detailTextLabel.text = object.name; |
512 |
} else if (indexPath.row == 1) { |
513 |
cell.textLabel.text = @"Full Path"; |
514 |
cell.detailTextLabel.text = object.fullPath; |
515 |
} else if (indexPath.row == 2) { |
516 |
cell.textLabel.text = @"Size"; |
517 |
cell.detailTextLabel.text = [object humanizedBytes]; |
518 |
} else if (indexPath.row == 3) { |
519 |
cell.textLabel.text = @"Type"; |
520 |
cell.detailTextLabel.text = object.contentType; |
521 |
} else if (indexPath.row == 4) { |
522 |
cell.textLabel.text = @"Last Modified"; |
523 |
cell.detailTextLabel.text = object.lastModifiedString; |
524 |
} |
525 |
} else if (indexPath.section == kMetadata) { |
526 |
if (objectIsReadOnly) { |
527 |
cell.accessoryType = UITableViewCellAccessoryNone; |
528 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
529 |
cell.userInteractionEnabled = NO; |
530 |
} |
531 |
else { |
532 |
cell.selectionStyle = UITableViewCellSelectionStyleBlue; |
533 |
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; |
534 |
} |
535 |
|
536 |
cell.accessoryView = nil; |
537 |
if (indexPath.row == [object.metadata count]) { |
538 |
cell.textLabel.text = @"Add Metadata"; |
539 |
cell.detailTextLabel.text = @""; |
540 |
} else { |
541 |
NSString *key = [[object.metadata allKeys] objectAtIndex:indexPath.row]; |
542 |
NSString *value = [object.metadata objectForKey:key]; |
543 |
NSString *metadataKeyCellText = key; |
544 |
NSString *metadataValueCellText = value; |
545 |
if ([metadataKeyCellText length] > maxMetadataViewableLength) { |
546 |
metadataKeyCellText = [metadataKeyCellText substringToIndex:(maxMetadataViewableLength - 3)]; |
547 |
metadataKeyCellText = [metadataKeyCellText stringByAppendingString:@"..."]; |
548 |
} |
549 |
if ([metadataValueCellText length] > maxMetadataViewableLength) { |
550 |
metadataValueCellText = [metadataValueCellText substringToIndex:(maxMetadataViewableLength - 3)]; |
551 |
metadataValueCellText = [metadataValueCellText stringByAppendingString:@"..."]; |
552 |
} |
553 |
|
554 |
cell.textLabel.text = metadataKeyCellText; |
555 |
cell.detailTextLabel.text = metadataValueCellText; |
556 |
} |
557 |
} else if (indexPath.section == publicLinkSection) { |
558 |
if (indexPath.row == 0) { |
559 |
cell.textLabel.text = @"Public URL"; |
560 |
cell.detailTextLabel.text = @""; |
561 |
cell.accessoryView = objectIsPublicSwitch; |
562 |
cell.accessoryType = UITableViewCellAccessoryNone; |
563 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
564 |
} |
565 |
} else if (indexPath.section == permissionsSection) { |
566 |
if (account.sharingAccount) { |
567 |
cell.accessoryType = UITableViewCellAccessoryNone; |
568 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
569 |
cell.userInteractionEnabled = NO; |
570 |
} else { |
571 |
cell.selectionStyle = UITableViewCellSelectionStyleBlue; |
572 |
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; |
573 |
} |
574 |
cell.accessoryView = nil; |
575 |
|
576 |
if (indexPath.row == permissions.count) { |
577 |
cell.textLabel.text = @"Share"; |
578 |
cell.detailTextLabel.text = @""; |
579 |
} else { |
580 |
NSString *user = [[[permissions allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] objectAtIndex:indexPath.row]; |
581 |
NSRange rangeOfColumn = [user rangeOfString:@":"]; |
582 |
NSString *UUID = (rangeOfColumn.location == NSNotFound) ? user : [user substringToIndex:rangeOfColumn.location]; |
583 |
NSString *group = ((rangeOfColumn.location == NSNotFound) || (rangeOfColumn.location == user.length - 1)) ? nil : [user substringFromIndex:(rangeOfColumn.location + 1)]; |
584 |
NSMutableString *displayname = [NSMutableString stringWithString:[account displaynameForUUID:UUID safe:YES]]; |
585 |
if (group) { |
586 |
[displayname appendFormat:@":%@", group]; |
587 |
} |
588 |
cell.textLabel.text = displayname; |
589 |
|
590 |
cell.detailTextLabel.numberOfLines = 1; |
591 |
cell.detailTextLabel.lineBreakMode = UILineBreakModeTailTruncation; |
592 |
cell.detailTextLabel.text = ([[permissions objectForKey:user] isEqualToString:@"write"] ? @"Read/Write" : @"Read Only"); |
593 |
} |
594 |
} else if (indexPath.section == kActions) { |
595 |
if (indexPath.row == 0) { |
596 |
cell.textLabel.text = @"Open File"; |
597 |
cell.accessoryType = UITableViewCellAccessoryNone; |
598 |
cell.detailTextLabel.text = @""; |
599 |
if (fileDownloading) { |
600 |
cell.textLabel.textColor = [UIColor grayColor]; |
601 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
602 |
} else { |
603 |
cell.selectionStyle = UITableViewCellSelectionStyleBlue; |
604 |
} |
605 |
} else if (indexPath.row == 1) { |
606 |
cell.textLabel.text = @"Email File as Attachment"; |
607 |
cell.detailTextLabel.text = @""; |
608 |
cell.accessoryType = UITableViewCellAccessoryNone; |
609 |
if (fileDownloading) { |
610 |
cell.textLabel.textColor = [UIColor grayColor]; |
611 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
612 |
} else { |
613 |
cell.selectionStyle = UITableViewCellSelectionStyleBlue; |
614 |
} |
615 |
} else if (indexPath.row == 2) { |
616 |
if (fileDownloaded) { |
617 |
cell.textLabel.text = @"Remove from Cache"; |
618 |
cell.accessoryType = UITableViewCellAccessoryNone; |
619 |
cell.detailTextLabel.text = @""; |
620 |
} else if (fileDownloading) { |
621 |
cell.textLabel.text = @"Downloading"; |
622 |
cell.accessoryType = UITableViewCellAccessoryNone; |
623 |
cell.detailTextLabel.text = @""; |
624 |
cell.selectionStyle = UITableViewCellSelectionStyleNone; |
625 |
cell.accessoryView = downloadProgressView; |
626 |
} |
627 |
} else if (indexPath.row == 3) { |
628 |
if (fileDownloading) { |
629 |
cell.textLabel.text = @"Cancel"; |
630 |
cell.accessoryType = UITableViewCellAccessoryNone; |
631 |
cell.detailTextLabel.text = @""; |
632 |
} |
633 |
} |
634 |
} else if (indexPath.section == versionsSection) { |
635 |
cell.selectionStyle = UITableViewCellSelectionStyleBlue; |
636 |
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; |
637 |
cell.textLabel.text = @"Versions"; |
638 |
cell.detailTextLabel.text = @""; |
639 |
} |
640 |
|
641 |
return cell; |
642 |
} |
643 |
|
644 |
#pragma mark - UITableViewDelegate |
645 |
|
646 |
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { |
647 |
if (indexPath.section == kMetadata) { |
648 |
EditMetadataViewController *vc = [[EditMetadataViewController alloc] initWithNibName:@"EditMetadataViewController" bundle:nil]; |
649 |
NSString *metadataKey; |
650 |
NSString *metadataValue; |
651 |
|
652 |
if (indexPath.row == [self.object.metadata count]) { |
653 |
metadataKey = @""; |
654 |
metadataValue = @""; |
655 |
vc.removeMetadataEnabled = FALSE; |
656 |
vc.navigationItem.title = @"Add Metadata"; |
657 |
} else { |
658 |
metadataKey = [[self.object.metadata allKeys] objectAtIndex:indexPath.row]; |
659 |
metadataValue = [self.object.metadata objectForKey:metadataKey]; |
660 |
vc.removeMetadataEnabled = YES; |
661 |
vc.navigationItem.title = @"Edit Metadata"; |
662 |
} |
663 |
|
664 |
vc.metadataKey = metadataKey; |
665 |
vc.metadataValue = metadataValue; |
666 |
vc.account = account; |
667 |
vc.container = container; |
668 |
vc.object = object; |
669 |
[self.navigationController pushViewController:vc animated:YES]; |
670 |
[vc release]; |
671 |
} else if (indexPath.section == permissionsSection) { |
672 |
EditPermissionsViewController *vc = [[EditPermissionsViewController alloc] initWithNibName:@"EditPermissionsViewController" bundle:nil]; |
673 |
NSString *user; |
674 |
|
675 |
if (indexPath.row == permissions.count) { |
676 |
user = @""; |
677 |
vc.removePermissionsEnabled = NO; |
678 |
vc.navigationItem.title = @"Add Permission"; |
679 |
} else { |
680 |
user = [[[permissions allKeys] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] objectAtIndex:indexPath.row]; |
681 |
NSString *userPermissions = [permissions objectForKey:user]; |
682 |
if ([userPermissions rangeOfString:@"read"].location != NSNotFound) { |
683 |
vc.readPermissionSelected = YES; |
684 |
} else { |
685 |
vc.readPermissionSelected = NO; |
686 |
} |
687 |
if ([userPermissions rangeOfString:@"write"].location != NSNotFound) { |
688 |
vc.writePermissionSelected = YES; |
689 |
} else { |
690 |
vc.writePermissionSelected = NO; |
691 |
} |
692 |
vc.removePermissionsEnabled = YES; |
693 |
vc.navigationItem.title = @"Edit Permission"; |
694 |
} |
695 |
|
696 |
vc.permissionUser = user; |
697 |
vc.permissions = permissions; |
698 |
vc.account = account; |
699 |
vc.container = container; |
700 |
vc.object = object; |
701 |
vc.folderViewController = folderViewController; |
702 |
[self.navigationController pushViewController:vc animated:YES]; |
703 |
[vc release]; |
704 |
} else if (indexPath.section == kActions) { |
705 |
if (indexPath.row == 0) { |
706 |
if (fileDownloaded) { |
707 |
[self openFile]; |
708 |
[self.tableView deselectRowAtIndexPath:indexPath animated:YES]; |
709 |
} else if (!fileDownloading) { |
710 |
[self downloadFileForAction:StorageObjectActionOpenFile]; |
711 |
[self.tableView deselectRowAtIndexPath:indexPath animated:YES]; |
712 |
} |
713 |
} else if (indexPath.row == 1) { |
714 |
if (fileDownloaded) { |
715 |
[self mailFile]; |
716 |
[self.tableView deselectRowAtIndexPath:indexPath animated:YES]; |
717 |
} else if (!fileDownloading) { |
718 |
[self downloadFileForAction:StorageObjectActionMailFile]; |
719 |
[self.tableView deselectRowAtIndexPath:indexPath animated:YES]; |
720 |
} |
721 |
} else if ((indexPath.row == 2) && fileDownloaded) { |
722 |
OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate]; |
723 |
BOOL removed = [app removeCacheObjectForHash:object.hash]; |
724 |
[self.tableView deselectRowAtIndexPath:indexPath animated:YES]; |
725 |
if (removed) { |
726 |
fileDownloaded = NO; |
727 |
fileDownloading = NO; |
728 |
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone]; |
729 |
} |
730 |
} else if ((indexPath.row == 3) && fileDownloading) { |
731 |
OpenStackAppDelegate *app = [[UIApplication sharedApplication] delegate]; |
732 |
OpenStackRequest *request = [app objectDownloadRequestForAccount:account container:container object:object]; |
733 |
[self.tableView deselectRowAtIndexPath:indexPath animated:YES]; |
734 |
if (request) { |
735 |
request.downloadProgressDelegate = nil; |
736 |
[request cancel]; |
737 |
fileDownloaded = NO; |
738 |
fileDownloading = NO; |
739 |
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone]; |
740 |
} |
741 |
} |
742 |
} else if (indexPath.section == deleteSection) { |
743 |
UIActionSheet *deleteActionSheet = [[[UIActionSheet alloc] initWithTitle:@"Are you sure you want to delete this file? This operation cannot be undone." |
744 |
delegate:self |
745 |
cancelButtonTitle:@"Cancel" |
746 |
destructiveButtonTitle:@"Delete File" |
747 |
otherButtonTitles:nil] autorelease]; |
748 |
[deleteActionSheet showInView:self.view]; |
749 |
} else if (indexPath.section == versionsSection) { |
750 |
ObjectVersionsViewController *vc = [[ObjectVersionsViewController alloc] initWithNibName:@"ObjectVersionsViewController" bundle:nil]; |
751 |
vc.account = account; |
752 |
vc.container = container; |
753 |
vc.object = object; |
754 |
[self.navigationController pushViewController:vc animated:YES]; |
755 |
[vc release]; |
756 |
} |
757 |
} |
758 |
|
759 |
- (void)setProgress:(float)newProgress { |
760 |
[downloadProgressView setProgress:newProgress animated:YES]; |
761 |
if (newProgress >= 1.0) { |
762 |
fileDownloading = NO; |
763 |
fileDownloaded = YES; |
764 |
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kActions] withRowAnimation:UITableViewRowAnimationNone]; |
765 |
} |
766 |
} |
767 |
|
768 |
#pragma mark - UIDocumentInteractionControllerDelegate |
769 |
|
770 |
- (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *) controller { |
771 |
return self.navigationController; |
772 |
} |
773 |
|
774 |
#pragma mark - UIActionSheetDelegate |
775 |
|
776 |
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { |
777 |
if (buttonIndex == 0) { |
778 |
// delete the file and pop out |
779 |
self.folderViewController.refreshButton.enabled = NO; |
780 |
__block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Deleting file..." |
781 |
andAddToView:self.view]; |
782 |
[[self.account.manager deleteObject:self.container object:self.object] |
783 |
success:^(OpenStackRequest *request) { |
784 |
[activityIndicatorView stopAnimatingAndRemoveFromSuperview]; |
785 |
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone) { |
786 |
[self.navigationController popViewControllerAnimated:YES]; |
787 |
if (account.shared) |
788 |
self.folderViewController.needsRefreshing = YES; |
789 |
} else if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) { |
790 |
self.folderViewController.selectedObjectViewController = nil; |
791 |
if (!account.shared) |
792 |
[self.folderViewController setDetailViewController]; |
793 |
else |
794 |
[self.folderViewController refreshButtonPressed:nil]; |
795 |
// self.folderViewController.refreshButton.enabled = YES; |
796 |
} |
797 |
if (self.folder.objectsAndFoldersCount == 1) { |
798 |
[self.folder removeObject:self.object]; |
799 |
self.folderViewController.folder = self.folderViewController.folder; |
800 |
} else { |
801 |
[self.folderViewController deleteAnimatedObject:self.object]; |
802 |
} |
803 |
self.folderViewController.refreshButton.enabled = YES; |
804 |
} |
805 |
failure:^(OpenStackRequest *request) { |
806 |
[activityIndicatorView stopAnimatingAndRemoveFromSuperview]; |
807 |
[self alert:@"There was a problem deleting this file." request:request]; |
808 |
self.folderViewController.refreshButton.enabled = YES; |
809 |
}]; |
810 |
} |
811 |
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:deleteSection]; |
812 |
[self.tableView deselectRowAtIndexPath:indexPath animated:YES]; |
813 |
} |
814 |
|
815 |
#pragma mark - MFMailComposeViewControllerDelegate |
816 |
|
817 |
// Dismisses the email composition interface when users tap Cancel or Send. |
818 |
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error { |
819 |
[self dismissModalViewControllerAnimated:YES]; |
820 |
} |
821 |
|
822 |
@end |
823 |
|