Statistics
| Branch: | Tag: | Revision:

root / Classes / StorageObjectViewController.m @ 29cc4957

History | View | Annotate | Download (38.8 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

    
30
#define kDetails 0
31
#define kMetadata 2
32

    
33
#define maxMetadataViewableLength 12
34

    
35

    
36
// TODO: use etag to reset download
37
// TODO: try downloading directly to the file to save memory.  don't use object.data
38

    
39
/*
40
 Name                           whatever.txt
41
 Full Path
42
 Size                           123 KB
43
 Content Type                   text/plain
44
 
45
 Metadata
46
 Key                            Value -> tap goes to a metadata item VC to edit or delete
47
 Key                            Value
48
 Add Metadata... (if max not already reached)
49
 
50
 CDN URL sections (action sheet to copy, open in safari, and email link)
51
 
52
 Download File (if downloaded, Open File and Mail File as Attachment)
53
 "After you download the file, you'll be able to attempt to open it and mail is as an attachment."
54
 
55
 Delete Object
56
 */
57

    
58
@implementation StorageObjectViewController
59

    
60
@synthesize account, container, folder, object, tableView, folderViewController;
61
@synthesize oldPubicURI, documentInteractionController, actionSelectedIndexPath, objectIsReadOnly, versionID;
62

    
63
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
64
    return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
65
}
66

    
67
- (void)setBackgroundView {
68
    
69
    UIImageView *logo = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"cloudfiles-large.png"]];
70
    
71
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
72
        UIView *backgroundContainer = [[UIView alloc] init];
73
        backgroundContainer.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
74
        backgroundContainer.backgroundColor = [UIColor iPadTableBackgroundColor];
75
        logo.contentMode = UIViewContentModeScaleAspectFit;
76
        logo.frame = CGRectMake(100.0, 100.0, 1000.0, 1000.0);
77
        logo.alpha = 0.5;        
78
        [backgroundContainer addSubview:logo];
79
        tableView.backgroundView = backgroundContainer;
80
        [backgroundContainer release];
81
    } else {        
82
        self.tableView.backgroundView = nil;
83
    }
84
    [logo release];    
85
}
86

    
87
- (IBAction)homeButtonPressed:(id)sender {
88
    [self.navigationController popToRootViewControllerAnimated:YES];
89
}
90

    
91
#pragma mark -
92
#pragma mark View lifecycle
93

    
94
- (void)viewDidLoad {
95
    [super viewDidLoad];
96
        
97
    objectIsPublicSwitch = [[UISwitch alloc] init];
98
    [objectIsPublicSwitch addTarget:self action:@selector(objectIsPublicSwitchChanged:) forControlEvents:UIControlEventValueChanged];
99
    
100
    deleteActionSheet = [[UIActionSheet alloc] initWithTitle:@"Are you sure you want to delete this file?  This operation cannot be undone." delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Delete File" otherButtonTitles:nil];
101
    permissions = [[NSMutableDictionary alloc] init];
102
    if (object.sharing.length > 0) {
103
        NSArray *sharingArray = [object.sharing componentsSeparatedByString:@";"];
104
        for (NSString *typeSpecificPermissions in sharingArray) { 
105
            NSArray *array=[typeSpecificPermissions componentsSeparatedByString:@"="];
106
            NSString *permissionsType = [array objectAtIndex:0];
107
            if ([permissionsType hasPrefix:@" "])
108
                permissionsType = [permissionsType substringFromIndex:1];
109
                            
110
            NSArray *users = [[array objectAtIndex:1] componentsSeparatedByString:@","];
111
            for (NSString *user in users) {
112
                [permissions setObject:permissionsType forKey:user];
113
            }
114
        }
115
    }
116
    objectIsReadOnly = NO;
117
    if (account.sharingAccount) { 
118
        if ([permissions count] > 0) {
119
            objectIsReadOnly = [[permissions objectForKey:[account username]] isEqualToString:@"read"];
120
        }
121
        objectIsPublicSwitch.enabled = NO;
122
    }
123
    
124
    downloadProgressView = [[AnimatedProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
125
    fileDownloading = ([self.account.manager.objectDownloadRequests objectForKey:object.fullPath] != nil) ? YES : NO;
126
    if (fileDownloading) {
127
        OpenStackRequest *request = [self.account.manager.objectDownloadRequests objectForKey:object.fullPath];
128
        self.actionSelectedIndexPath = [request.userInfo objectForKey:@"actionSelectedIndexPath"];
129
        [request setDownloadProgressDelegate:self];
130
    }
131
    appDelegate = [[UIApplication sharedApplication] delegate];
132
}
133

    
134
- (void)viewWillAppear:(BOOL)animated {
135
    [super viewWillAppear:animated];
136
    self.navigationItem.title = object.name;
137
    
138
    NSIndexPath *selection = [self.tableView indexPathForSelectedRow];
139
	if (selection)
140
		[self.tableView deselectRowAtIndexPath:selection animated:YES];
141
    if ([appDelegate.cachedObjectsDictionary objectForKey:object.hash] != nil) {
142
        if ([[NSFileManager defaultManager] fileExistsAtPath:[appDelegate.cachedObjectsDictionary objectForKey:object.hash]])
143
            fileDownloaded = YES;
144
        else {
145
            fileDownloaded = NO;
146
            [appDelegate.cachedObjectsDictionary removeObjectForKey:object.hash];
147
            [appDelegate saveCacheDictionary];
148
        }
149
    } else {
150
        fileDownloaded = NO;
151
    }
152
    
153
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
154
        CGRect rect = downloadProgressView.frame;
155
        rect.size.width = 440.0;
156
        downloadProgressView.frame = rect;
157
    }
158
    
159
    //[self setBackgroundView];
160
    actionsSection = 1;
161
    publicLinkSection = 3;
162
    permissionsSection = 4;
163
    if (self.container.cdnEnabled) {
164
        cdnURLSection = 5;
165
        deleteSection = 6;
166
        versionsSection = 7;
167
    } else {
168
        cdnURLSection = -1;
169
        deleteSection = 5;
170
        versionsSection = 6;
171
    }
172
    
173
    if (versionID) {
174
        publicLinkSection = -1;
175
        permissionsSection = -1;
176
        versionsSection = -1;
177
        deleteSection = -1;
178
        objectIsReadOnly = YES;
179
    }
180
    if (account.sharingAccount || account.shared) {
181
        versionsSection = -1;
182
        deleteSection = -1;
183
    }
184
    
185
    
186
    // let's see if we can tweet
187
    UIApplication *app = [UIApplication sharedApplication];
188
    NSURL *twitterURL = [NSURL URLWithString:@"twitter://post?message=test"];
189

    
190
    if ([app canOpenURL:twitterURL]) {
191
        cdnURLActionSheet = [[UIActionSheet alloc] initWithTitle:[[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Copy to Pasteboard", @"Open in Safari", @"Email Link to File", @"Tweet Link to File", nil];
192
    } else {
193
        cdnURLActionSheet = [[UIActionSheet alloc] initWithTitle:[[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:nil otherButtonTitles:@"Copy to Pasteboard", @"Open in Safari", @"Email Link to File", nil];
194
    }
195
    objectIsPublic = ([object.publicURI length]);
196
    objectIsPublicSwitch.on = objectIsPublic;
197
    [self.tableView reloadData];
198
}
199

    
200
- (void)viewDidAppear:(BOOL)animated {
201
    if (object.metadata == nil) {
202
        [self reloadMetadataSection];
203
    }
204
}
205

    
206
#pragma mark -
207
#pragma mark Table view data source
208

    
209
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
210

    
211
    int numberOfSections = 8;
212
    if (!self.container.cdnEnabled)
213
        numberOfSections--;
214
    if (deleteSection < 0)
215
        numberOfSections--;
216
    if (versionsSection < 0)
217
        numberOfSections--;
218
    if (publicLinkSection < 0)
219
        numberOfSections--;
220
    if (permissionsSection < 0)
221
        numberOfSections--;
222

    
223
    return numberOfSections;
224
}
225

    
226
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
227
    if (section == kDetails) {
228
        return 4;
229
    } else if (section == kMetadata) {
230
        if (objectIsReadOnly) {
231
            return [object.metadata count];
232
        } else {
233
            return 1 + [object.metadata count];
234
        }
235
    } else if (section == cdnURLSection) {
236
        return 1;
237
    } else if (section == actionsSection) {
238
        return 2;
239
    } else if (section == publicLinkSection) {
240
        return objectIsPublic ? 2 : 1;
241
    } else if (section == permissionsSection) {
242
        if (account.sharingAccount || objectIsReadOnly)
243
            return [permissions count];
244
        else
245
            return 1 + [permissions count];
246
    } else {
247
        return 1;
248
    }
249
}
250

    
251
- (CGFloat)findLabelHeight:(NSString*)text font:(UIFont *)font {
252
    CGSize textLabelSize;    
253
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
254
        // 616, 678
255
        textLabelSize = CGSizeMake(596.0, 9000.0f);
256
    } else {
257
        textLabelSize = CGSizeMake(280.0, 9000.0f);
258
    }
259
    CGSize stringSize = [text sizeWithFont:font constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeCharacterWrap];
260
    return stringSize.height;
261
}
262

    
263
- (CGFloat)tableView:(UITableView *)aTableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
264
    CGFloat result = aTableView.rowHeight;
265

    
266
    if (indexPath.section == cdnURLSection) {
267
        result = 22.0 + [self findLabelHeight:[[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] font:[UIFont systemFontOfSize:18.0]];
268
    } else if (indexPath.section == kDetails && indexPath.row == 1) {
269
        CGSize textLabelSize;
270
        if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
271
            textLabelSize = CGSizeMake(537.0, 9000.0f);
272
        } else {
273
            textLabelSize = CGSizeMake(221.0, 9000.0f);
274
        }
275
        CGSize stringSize = [object.fullPath sizeWithFont:[UIFont systemFontOfSize:18.0] constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeWordWrap];
276
        return 22.0 + stringSize.height;
277
    } else if (indexPath.section == kDetails && indexPath.row == 0) {
278
        CGSize textLabelSize;
279
        if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
280
            textLabelSize = CGSizeMake(537.0, 9000.0f);
281
        } else {
282
            textLabelSize = CGSizeMake(221.0, 9000.0f);
283
        }
284
        CGSize stringSize = [object.name sizeWithFont:[UIFont systemFontOfSize:18.0] constrainedToSize:textLabelSize lineBreakMode:UILineBreakModeWordWrap];
285
        return 22.0 + stringSize.height;
286
    } else if (indexPath.section == publicLinkSection && indexPath.row == 1) {
287
        NSString *publicLinkUrl = [NSString stringWithFormat:@"%@%@",
288
                                   account.pithosPublicLinkURLPrefix,
289
                                   self.object.publicURI];
290
                                   
291
        result = 30.0 + [self findLabelHeight:publicLinkUrl font:[UIFont systemFontOfSize:15.0]];
292
    }
293
    return MAX(aTableView.rowHeight, result);
294
}
295

    
296
- (UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
297
    
298
    static NSString *CellIdentifier = @"Cell";
299
    static NSString *TextViewCellIdentifier = @"TextViewCell";
300
    if (indexPath.section == publicLinkSection) {
301
        if (indexPath.row == 1) {
302
            TextViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:TextViewCellIdentifier];
303
            if (cell == nil) {
304
                cell = [[[TextViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TextViewCellIdentifier] autorelease];
305
                cell.textView.frame = cell.contentView.frame;
306

    
307
            }
308
            cell.selectionStyle = UITableViewCellSelectionStyleNone;
309
            cell.textView.backgroundColor = [UIColor clearColor];
310
            cell.textView.font = [UIFont systemFontOfSize:15.0];
311
            cell.textView.dataDetectorTypes = UIDataDetectorTypeLink;
312
            cell.textView.text = [NSString stringWithFormat:@"%@%@",
313
                                  account.pithosPublicLinkURLPrefix,
314
                                  [NSString encodeToPercentEscape:self.object.publicURI charactersToEncode:@"!*'();:@&=+$,?%#[]"]];
315
            
316
            cell.selectionStyle = UITableViewCellSelectionStyleNone;
317
            cell.accessoryView = UITableViewCellAccessoryNone;
318
            return cell;
319
        }
320
    }
321
    
322
    UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
323
    if (cell == nil) {
324
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
325
        cell.textLabel.backgroundColor = [UIColor clearColor];
326
        cell.detailTextLabel.backgroundColor = [UIColor clearColor];
327
        cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
328
    }
329
    cell.textLabel.textColor = [UIColor blackColor];
330
    cell.detailTextLabel.numberOfLines = 0;
331

    
332
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
333
        cell.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.8];
334
    }
335
    cell.userInteractionEnabled = YES;
336
    cell.detailTextLabel.textAlignment = UITextAlignmentRight;
337
    cell.accessoryView = nil;
338
    cell.textLabel.font = [UIFont boldSystemFontOfSize:17.0];
339
    
340
    // Configure the cell...
341
    if (indexPath.section == kDetails) {
342
        cell.accessoryType = UITableViewCellAccessoryNone;
343
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
344
        cell.accessoryView = nil;
345
        if (indexPath.row == 0) {
346
            cell.textLabel.text = @"Name";
347
            cell.detailTextLabel.text = object.name;
348
        } else if (indexPath.row == 1) {
349
            cell.textLabel.text = @"Full Path";
350
            cell.detailTextLabel.text = object.fullPath;
351
        } else if (indexPath.row == 2) {
352
            cell.textLabel.text = @"Size";
353
            cell.detailTextLabel.text = [object humanizedBytes];
354
        } else if (indexPath.row == 3) {
355
            cell.textLabel.text = @"Type";
356
            cell.detailTextLabel.text = object.contentType;
357
        }
358
    } else if (indexPath.section == kMetadata) {
359
        if (objectIsReadOnly) {
360
            cell.accessoryType = UITableViewCellAccessoryNone;
361
            cell.selectionStyle = UITableViewCellSelectionStyleNone;
362
            cell.userInteractionEnabled = NO;
363
        }
364
        else {
365
            cell.selectionStyle = UITableViewCellSelectionStyleBlue;
366
            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
367
        }
368
        
369
        cell.accessoryView = nil;
370
        if (indexPath.row == [object.metadata count]) {
371
            cell.textLabel.text = @"Add Metadata";
372
            cell.detailTextLabel.text = @"";
373
        } else {
374
            NSString *key = [[object.metadata allKeys] objectAtIndex:indexPath.row];
375
            NSString *value = [object.metadata objectForKey:key];
376
            NSString *metadataKeyCellText = key;
377
            NSString *metadataValueCellText = value;
378
            if ([metadataKeyCellText length] > maxMetadataViewableLength) {
379
                metadataKeyCellText = [metadataKeyCellText substringToIndex:(maxMetadataViewableLength - 3)];
380
                metadataKeyCellText = [metadataKeyCellText stringByAppendingString:@"..."];
381
            }
382
            if ([metadataValueCellText length] > maxMetadataViewableLength) {
383
                metadataValueCellText = [metadataValueCellText substringToIndex:(maxMetadataViewableLength - 3)];
384
                metadataValueCellText = [metadataValueCellText stringByAppendingString:@"..."];
385
            }
386
            
387
            cell.textLabel.text = metadataKeyCellText;
388
            cell.detailTextLabel.text = metadataValueCellText;
389
        }
390
    } else if (indexPath.section == publicLinkSection) {
391
        if (indexPath.row == 0) {
392
            cell.textLabel.text = @"Public URL";        
393
            cell.detailTextLabel.text = @"";
394
            cell.accessoryView = objectIsPublicSwitch;
395
            cell.accessoryType = UITableViewCellAccessoryNone;
396
            cell.selectionStyle = UITableViewCellSelectionStyleNone;
397
        }
398
    } else if (indexPath.section == permissionsSection) {
399
        if (account.sharingAccount) {
400
            cell.accessoryType = UITableViewCellAccessoryNone;
401
            cell.selectionStyle = UITableViewCellSelectionStyleNone;
402
            cell.userInteractionEnabled = NO;
403
        }
404
        else {
405
            cell.selectionStyle = UITableViewCellSelectionStyleBlue;
406
            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
407
        }
408
        cell.accessoryView = nil;
409
        
410
        if (indexPath.row == [permissions count]) {
411
            cell.textLabel.text = @"Share";
412
            cell.detailTextLabel.text = @""; 
413
        } else {
414
            NSString *user = [[permissions allKeys] objectAtIndex:indexPath.row];
415
            cell.textLabel.text = user;
416
            NSString *accessType;
417
            if ([[permissions objectForKey:user] isEqualToString:@"write"])
418
                accessType = @"Read/Write";
419
            else
420
                accessType = @"Read Only";
421
            cell.detailTextLabel.numberOfLines = 1;
422
            cell.detailTextLabel.lineBreakMode = UILineBreakModeTailTruncation;
423
            cell.detailTextLabel.text = accessType;
424
        }
425
    } else if (indexPath.section == cdnURLSection) {
426
        cell.detailTextLabel.textAlignment = UITextAlignmentLeft;
427
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
428
        cell.textLabel.text = @"";
429
        cell.detailTextLabel.text = [[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
430
    } else if (indexPath.section == actionsSection) {
431
        if (fileDownloading) {
432
            if (actionSelectedIndexPath.row == indexPath.row) {
433
                cell.accessoryView = downloadProgressView;
434
            } else {
435
                cell.textLabel.textColor = [UIColor grayColor];
436
                cell.selectionStyle = UITableViewCellSelectionStyleNone;
437
            }
438
        } else {
439
            cell.selectionStyle = UITableViewCellSelectionStyleBlue;
440
            cell.accessoryView = nil;
441
        }
442
        if (indexPath.row == 0) {
443
            cell.textLabel.text = @"Open File"; 
444
            cell.accessoryType = UITableViewCellAccessoryNone;
445
            cell.detailTextLabel.text = @"";
446
            
447
        } else if (indexPath.row == 1) {
448
            cell.textLabel.text = @"Email File as Attachment";
449
            cell.detailTextLabel.text = @"";
450
            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
451
        }        
452
    } else if (indexPath.section == deleteSection) {
453
        cell.selectionStyle = UITableViewCellSelectionStyleBlue;
454
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
455
        cell.textLabel.text = @"Delete Object";
456
        cell.detailTextLabel.text = @"";
457
        
458
    } else if (indexPath.section == versionsSection) {
459
        cell.selectionStyle = UITableViewCellSelectionStyleBlue;
460
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
461
        cell.textLabel.text = @"Versions";
462
        cell.detailTextLabel.text = @"";
463
    }
464
    
465
    return cell;
466
}
467

    
468

    
469

    
470
#pragma mark -
471
#pragma mark Table view delegate
472

    
473
- (void)reloadActionsTitleRow:(NSTimer *)timer {
474
    [[timer.userInfo objectForKey:@"tableView"] reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
475
}
476

    
477
- (void)tableView:(UITableView *)aTableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
478
    if (indexPath.section == kMetadata) {
479
        EditMetadataViewController *vc = [[EditMetadataViewController alloc] initWithNibName:@"EditMetadataViewController" bundle:nil];
480
        NSString *metadataKey;
481
        NSString *metadataValue;
482
        
483
        if (indexPath.row == [self.object.metadata count]) {
484
            metadataKey = @"";
485
            metadataValue = @"";
486
            vc.removeMetadataEnabled = FALSE;
487
            vc.navigationItem.title = @"Add Metadata";
488
        }
489
        else {
490
            metadataKey = [[self.object.metadata allKeys] objectAtIndex:indexPath.row];
491
            metadataValue = [self.object.metadata objectForKey:metadataKey];
492
            vc.removeMetadataEnabled = YES;
493
            vc.navigationItem.title = @"Edit Metadata";
494
        }
495

    
496
        vc.metadataKey = metadataKey;
497
        vc.metadataValue = metadataValue;
498
        vc.account = account;
499
        vc.container = container;
500
        vc.object = object;
501
        [self.navigationController pushViewController:vc animated:YES];
502
        [vc release];
503
    } else if (indexPath.section == permissionsSection) {
504
        EditPermissionsViewController *vc = [[EditPermissionsViewController alloc] initWithNibName:@"EditPermissionsViewController" bundle:nil];
505
        NSString *user;
506
        
507
        if (indexPath.row == [permissions count]) {
508
            user = @"";
509
            vc.removePermissionsEnabled = NO;
510
            vc.navigationItem.title = @"Share";
511
        }
512
        else {
513
            user = [[permissions allKeys] objectAtIndex:indexPath.row];
514
            NSString *userPermissions = [permissions objectForKey:user];
515
            if ([userPermissions rangeOfString:@"read"].location != NSNotFound)
516
                vc.readPermissionSelected = YES;
517
            else
518
                vc.readPermissionSelected = NO;
519
            
520
            if ([userPermissions rangeOfString:@"write"].location != NSNotFound)
521
                vc.writePermissionSelected = YES;
522
            else
523
                vc.writePermissionSelected = NO;
524
            
525
            vc.removePermissionsEnabled = YES;
526
            vc.navigationItem.title = @"Edit Sharing";
527
        }
528
        
529
        vc.user = user;
530
        vc.permissions = permissions;
531
        vc.account = account;
532
        vc.container = container;
533
        vc.object = object;
534
        vc.folderViewController = folderViewController;
535
        [self.navigationController pushViewController:vc animated:YES];
536
        [vc release];
537
    } else if (indexPath.section == cdnURLSection) {
538
        [cdnURLActionSheet showInView:self.view];
539
    } else if (indexPath.section == actionsSection) {
540
        if (!fileDownloaded) {
541
            if (!fileDownloading) {
542
                // download the file
543
                fileDownloading = YES;
544
                self.actionSelectedIndexPath = indexPath;
545
                [self.tableView reloadData];
546
                NSMutableDictionary *requestUserInfo = [NSDictionary dictionaryWithObject:indexPath forKey:@"actionSelectedIndexPath"];
547
                [[self.account.manager getObject:self.container object:self.object downloadProgressDelegate:self requestUserInfo:requestUserInfo version:versionID]
548
                 success:^(OpenStackRequest *request) {
549
                     fileDownloaded = YES;
550
                     fileDownloading = NO;
551
                     if ([self.navigationController.visibleViewController isEqual:self])
552
                         [self.tableView.delegate tableView:self.tableView didSelectRowAtIndexPath:indexPath]; 
553
                     [self.tableView reloadData];
554
                     [self.folderViewController.tableView reloadData];
555
                 }
556
                 failure:^(OpenStackRequest *request) {
557
                     fileDownloaded = NO;
558
                     fileDownloading = NO;
559
                     [self alert:@"File failed to download" request:request];
560
                     [self.tableView reloadData];
561
                 }];
562
                [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:indexPath.row inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
563
            }
564
        } else if (indexPath.row == 0) {        
565
            if (fileDownloaded) {
566
                NSString *filePath = [appDelegate.cachedObjectsDictionary objectForKey:object.hash];                                 
567
                self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
568
                self.documentInteractionController.delegate = self;
569
                self.documentInteractionController.name = object.name;
570

    
571
                UITableViewCell *openFileCell = [self.tableView cellForRowAtIndexPath:indexPath];
572
                CGRect frameToPresentMenuFrom = CGRectMake(openFileCell.frame.origin.x,
573
                                                           openFileCell.frame.origin.y - self.tableView.contentOffset.y,
574
                                                           openFileCell.frame.size.width, 
575
                                                           openFileCell.frame.size.height);
576
                if (![self.documentInteractionController presentOptionsMenuFromRect:frameToPresentMenuFrom inView:self.view animated:YES]) {
577
                    if ([self.object isPlayableMedia]) {
578
                        MediaViewController *vc = [[MediaViewController alloc] initWithNibName:@"MediaViewController" bundle:nil];
579
                        vc.container = self.container;
580
                        vc.object = self.object;
581
                        [self.navigationController pushViewController:vc animated:YES];
582
                        [vc release];
583
                    } else {
584
                        [self alert:@"Error" message:@"This file could not be opened."];
585
                        [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
586
                    }
587
                } 
588
                
589
                [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:actionsSection] animated:YES];                
590
                
591
            } 
592
        } else if (indexPath.row == 1) {
593
            
594
            NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
595
            NSString *documentsDirectory = [paths objectAtIndex:0];        
596
            NSString *shortPath = @"";
597
            
598
            if (self.container && [self.container respondsToSelector:@selector(name)] && self.object && [self.object respondsToSelector:@selector(fullPath)]) {
599
                shortPath = [NSString stringWithFormat:@"/%@/%@", self.container.name, self.object.fullPath];
600
            }
601
            
602
            NSString *filePath = [documentsDirectory stringByAppendingString:shortPath];
603
            NSData *data = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:filePath]];
604
            
605
            
606
            if (![MFMailComposeViewController canSendMail]) {
607
                [self alert:@"Cannot send mail" message:@"Your device has not been configured for sending email"];
608
                [self.tableView deselectRowAtIndexPath:indexPath animated:NO];
609
            }
610
            else {
611
                MFMailComposeViewController *vc = [[MFMailComposeViewController alloc] init];
612
            
613
                vc.mailComposeDelegate = self;		
614
                [vc setSubject:self.object.name];
615
                [vc addAttachmentData:data mimeType:self.object.contentType fileName:self.object.name];
616
                [vc setMessageBody:@"" isHTML:NO];    
617
                if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
618
                    vc.modalPresentationStyle = UIModalPresentationPageSheet;
619
                }                
620
                [self presentModalViewController:vc animated:YES];
621
                [vc release];        
622
            }
623
        }
624
    } else if (indexPath.section == deleteSection) {
625
        [deleteActionSheet showInView:self.view];
626
    } else if (indexPath.section == versionsSection) {
627
        ObjectVersionsViewController *vc = [[ObjectVersionsViewController alloc] initWithNibName:@"ObjectVersionsViewController" bundle:nil];
628
        vc.account = account;
629
        vc.container = container;
630
        vc.object = object;
631
        [self.navigationController pushViewController:vc animated:YES];
632
        [vc release];
633
    }
634
}
635

    
636
- (void)setProgress:(float)newProgress {
637
    [downloadProgressView setProgress:newProgress animated:YES];    
638
    if (newProgress >= 1.0) {
639
        fileDownloading = NO;
640
        fileDownloaded = YES;
641
        [self.tableView reloadData];
642
    }
643
}
644

    
645

    
646
#pragma mark -
647
#pragma mark Document Interation Controller Delegate
648

    
649
- (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *) controller {
650
    return self.navigationController;
651
}
652

    
653
- (void)documentInteractionControllerDidEndPreview:(UIDocumentInteractionController *)controllers {
654
    [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:actionsSection] animated:YES];
655
}
656

    
657
#pragma mark -
658
#pragma mark Action Sheet Delegate
659

    
660
- (void)deleteObjectRow {
661
    [self.folder.objects removeObjectForKey:self.object.name];
662
    [self.folderViewController.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:selectedIndexPath] withRowAnimation:UITableViewRowAnimationLeft];
663
    self.folderViewController.refreshButton.enabled = YES;
664
}
665

    
666
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
667
    if ([actionSheet isEqual:deleteActionSheet]) {
668
        if (buttonIndex == 0) {
669
            // delete the file and pop out
670
            
671
            NSString *activityMessage = @"Deleting file";
672
            [activityIndicatorView release];
673
            activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
674
            [activityIndicatorView addToView:self.view];
675

    
676
            self.folderViewController.refreshButton.enabled = NO;
677
            [[self.account.manager deleteObject:self.container object:self.object] 
678
             success:^(OpenStackRequest *request) {
679
                 [activityIndicatorView removeFromSuperview];
680
                 performingAction = NO;
681
                 if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
682
                     [self.navigationController popViewControllerAnimated:YES];
683
                     if (account.shared)
684
                         self.folderViewController.needsRefreshing = YES;
685
                 } else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
686
                     self.folderViewController.selectedObjectViewController = nil;
687
                     if (!account.shared)
688
                         [self.folderViewController setDetailViewController];
689
                     else 
690
                        [self.folderViewController refreshButtonPressed:nil];
691
                 }
692
                 if (self.folder.objects.count == 1)
693
                     [self.folder.objects removeObjectForKey:self.object.name];
694
                 
695
                 if ([self.folder.objects count] + [self.folder.folders count] == 0) {
696
                     [self.folderViewController.tableView reloadData];
697
                 } else {
698
                     [self.folderViewController.tableView selectRowAtIndexPath:selectedIndexPath animated:YES scrollPosition:UITableViewScrollPositionNone];
699
                     [NSTimer scheduledTimerWithTimeInterval:0.75 target:self selector:@selector(deleteObjectRow) userInfo:nil repeats:NO];
700
                 }
701
             }
702
             failure:^(OpenStackRequest *request) {
703
                 [activityIndicatorView removeFromSuperview];
704
                 [self hideToolbarActivityMessage];
705
                 performingAction = NO;
706
                 [self alert:@"There was a problem deleting this file." request:request]; 
707
             }];
708
        }
709
        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:deleteSection];
710
        [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
711
    } else if ([actionSheet isEqual:cdnURLActionSheet]) {
712
        NSURL *url = [NSURL URLWithString:[[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
713

    
714
        if (buttonIndex == 0) {
715
            // copy to pasteboard
716
            UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
717
            [pasteboard setString:[url description]];
718
        } else if (buttonIndex == 1) {
719
            // open in safari
720
            UIApplication *application = [UIApplication sharedApplication];
721
            if ([application canOpenURL:url]) {
722
                [application openURL:url];
723
            } else {
724
                [self alert:@"Error" message:[NSString stringWithFormat:@"This URL cannot be opened.\n%@", url]];
725
            }
726
        } else if (buttonIndex == 2) {
727
            // email link to file
728
            MFMailComposeViewController *vc = [[MFMailComposeViewController alloc] init];
729
            vc.mailComposeDelegate = self;		
730
            [vc setSubject:self.object.name];
731
            [vc setMessageBody:[url description] isHTML:NO];
732
            if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
733
                vc.modalPresentationStyle = UIModalPresentationPageSheet;
734
            }                
735
            [self presentModalViewController:vc animated:YES];
736
            [vc release];        
737
        } else if (buttonIndex == 3) {
738
            // tweet link to file
739
            UIApplication *app = [UIApplication sharedApplication];            
740
            NSURL *twitterURL = [NSURL URLWithString:[NSString stringWithFormat:@"twitter://post?message=%@", [[url description] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
741
            if ([app canOpenURL:twitterURL]) {
742
                [app openURL:twitterURL];
743
            }
744
        }
745
        [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:cdnURLSection] animated:YES];
746
    }
747
}
748

    
749
#pragma mark -
750
#pragma mark Mail Composer Delegate
751

    
752
// Dismisses the email composition interface when users tap Cancel or Send.
753
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {	
754
	[self dismissModalViewControllerAnimated:YES];
755
    [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:actionsSection] animated:YES];
756
}
757

    
758
#pragma mark -
759
#pragma mark Switches
760

    
761
- (void)objectIsPublicSwitchChanged:(id)sender
762
{
763
    NSString *activityMessage = [NSString stringWithFormat:@"Enabling public link.."];
764
    self.oldPubicURI = object.publicURI;
765
    
766
    if (objectIsPublic) {
767
        activityMessage = [NSString stringWithFormat:@"Disabling public link.."];
768
        object.publicURI = @"";
769
    }
770
    else {
771
        object.publicURI = @"TRUE";
772
    }
773
    [activityIndicatorView release];
774
    activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
775
    [activityIndicatorView addToView:self.view scrollOffset:self.tableView.contentOffset.y];    
776
    
777
    objectIsPublic = !objectIsPublic;
778
    [[self.account.manager writeObjectMetadata:container object:object] 
779
     success:^(OpenStackRequest *request) {
780
         NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:publicLinkSection];
781
         if (objectIsPublic) {
782
             [[self.account.manager getObjectInfo:container object:object version:versionID] 
783
              success:^(OpenStackRequest *request) {
784
                  [activityIndicatorView removeFromSuperview];
785
                  object.publicURI = [request.responseHeaders objectForKey:@"X-Object-Public"];
786
                  NSIndexPath *publicURICellIndexPath = [NSIndexPath indexPathForRow:1 inSection:publicLinkSection];
787
                  [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
788
                                        withRowAnimation:UITableViewRowAnimationBottom];
789
              }
790
              failure:^(OpenStackRequest *request) {
791
                  [activityIndicatorView removeFromSuperview];
792
                  [self alert:@"There was a problem retrieving the public link from the server." request:request]; 
793
              }];
794
         }
795
         else {
796
             [activityIndicatorView removeFromSuperview];
797
             [self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:publicURICellIndexPath]
798
                                   withRowAnimation:UITableViewRowAnimationTop];
799
         }
800
     }
801
     failure:^(OpenStackRequest *request) {
802
         [activityIndicatorView removeFromSuperview];
803
         objectIsPublic = !objectIsPublic;
804
         objectIsPublicSwitch.on = !objectIsPublicSwitch.on;
805
         object.publicURI = oldPubicURI;
806
         [self alert:@"There was a problem enabling the public link." request:request];           
807

    
808
     }];    
809
}
810

    
811

    
812

    
813
#pragma mark -
814
#pragma mark Memory management
815

    
816
- (void)dealloc {
817
    if (fileDownloading) {
818
        OpenStackRequest *request = [self.account.manager.objectDownloadRequests objectForKey:object.fullPath];
819
        [request setDownloadProgressDelegate:nil];
820
    }
821
    [account release];
822
    [downloadProgressView release];
823
    [deleteActionSheet release];
824
    [cdnURLActionSheet release];
825
    [tableView release];
826
    [folderViewController release];
827
    [objectIsPublicSwitch release];
828
    [permissions release];
829
    [documentInteractionController release];
830
    [actionSelectedIndexPath release];
831
    [versionID release];
832
    [activityIndicatorView release];
833
    [super dealloc];
834
}
835

    
836
#pragma mark -
837
#pragma mark Helper functions
838

    
839
- (void)reloadMetadataSection {
840
    NSString *activityMessage = @"Loading metadata...";
841
    [activityIndicatorView release];
842
    activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
843
    [activityIndicatorView addToView:self.view];         
844
    [[self.account.manager getObjectInfo:container object:object version:versionID] 
845
     success:^(OpenStackRequest *request) {
846
         [activityIndicatorView removeFromSuperview];         
847
         object.metadata = [NSMutableDictionary dictionary];
848
         for (NSString *header in request.responseHeaders) {
849
             NSString *metadataKey;
850
             NSString *metadataValue;
851
             if ([header rangeOfString:@"X-Object-Meta-"].location != NSNotFound) {
852
                 metadataKey = [NSString decodeFromPercentEscape:[header substringFromIndex:14]];
853
                 metadataValue = [NSString decodeFromPercentEscape:[request.responseHeaders objectForKey:header]];
854
                 [object.metadata setObject:metadataValue forKey:metadataKey];
855
             }
856
         }   
857
         NSIndexSet *metadataSections = [NSIndexSet indexSetWithIndex:kMetadata];
858
         [self.tableView reloadSections:metadataSections withRowAnimation:UITableViewRowAnimationFade];
859
     }
860
     failure:^(OpenStackRequest *request) {
861
         [activityIndicatorView removeFromSuperview];
862
         [self alert:@"There was a problem retrieving the object's metadata." request:request]; 
863
     }];
864
}
865

    
866

    
867
@end
868