Statistics
| Branch: | Tag: | Revision:

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