Statistics
| Branch: | Tag: | Revision:

root / Classes / StorageObjectViewController.m @ 3a8071d4

History | View | Annotate | Download (38 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 1
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
    publicLinkSection = 2;
161
    permissionsSection = 3;
162
    if (self.container.cdnEnabled) {
163
        cdnURLSection = 4;
164
        actionsSection = 5;
165
        deleteSection = 6;
166
        versionsSection = 7;
167
    } else {
168
        cdnURLSection = -1;
169
        actionsSection = 4;
170
        deleteSection = 5;
171
        versionsSection = 6;
172
    }
173
    
174
    if (versionID) {
175
        publicLinkSection = -1;
176
        permissionsSection = -1;
177
        versionsSection = -1;
178
        deleteSection = -1;
179
        actionsSection = 2;
180
        objectIsReadOnly = YES;
181
    }
182
    if (account.sharingAccount || account.shared)
183
            deleteSection = -1;
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
    
301
    if (indexPath.section == publicLinkSection) {
302
        if (indexPath.row == 1) {
303
            TextViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:TextViewCellIdentifier];
304
            if (cell == nil) {
305
                cell = [[[TextViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:TextViewCellIdentifier] autorelease];
306
                cell.textView.frame = cell.contentView.frame;
307

    
308
            }
309
            cell.selectionStyle = UITableViewCellSelectionStyleNone;
310
            cell.textView.backgroundColor = [UIColor clearColor];
311
            cell.textView.font = [UIFont systemFontOfSize:15.0];
312
            cell.textView.dataDetectorTypes = UIDataDetectorTypeLink;
313
            cell.textView.text = [NSString stringWithFormat:@"%@%@",
314
                                  account.pithosPublicLinkURLPrefix,
315
                                  [NSString encodeToPercentEscape:self.object.publicURI charactersToEncode:@"!*'();:@&=+$,?%#[]"]];
316
            
317
            cell.selectionStyle = UITableViewCellSelectionStyleNone;
318
            cell.accessoryView = UITableViewCellAccessoryNone;
319
            return cell;
320
        }
321
    }
322
    
323
    UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
324
    if (cell == nil) {
325
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
326
        cell.textLabel.backgroundColor = [UIColor clearColor];
327
        cell.detailTextLabel.backgroundColor = [UIColor clearColor];
328
        cell.detailTextLabel.numberOfLines = 0;
329
        cell.detailTextLabel.lineBreakMode = UILineBreakModeWordWrap;
330
    }
331
    cell.textLabel.textColor = [UIColor blackColor];
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 = @"Add Permissions";
412
            cell.detailTextLabel.text = @""; 
413
        }
414
        else {
415
            NSString *user = [[permissions allKeys] objectAtIndex:indexPath.row];
416
            cell.textLabel.text = user;
417
            cell.detailTextLabel.text = [permissions objectForKey:user];
418
        }
419
    } else if (indexPath.section == cdnURLSection) {
420
        cell.detailTextLabel.textAlignment = UITextAlignmentLeft;
421
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
422
        cell.textLabel.text = @"";
423
        cell.detailTextLabel.text = [[NSString stringWithFormat:@"%@/%@", self.container.cdnURL, self.object.fullPath] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
424
    } else if (indexPath.section == actionsSection) {
425
        if (fileDownloading) {
426
            if (actionSelectedIndexPath.row == indexPath.row) {
427
                cell.accessoryView = downloadProgressView;
428
            } else {
429
                cell.textLabel.textColor = [UIColor grayColor];
430
                cell.selectionStyle = UITableViewCellSelectionStyleNone;
431
            }
432
        } else {
433
            cell.selectionStyle = UITableViewCellSelectionStyleBlue;
434
            cell.accessoryView = nil;
435
        }
436
        if (indexPath.row == 0) {
437
            cell.textLabel.text = @"Open File"; 
438
            cell.accessoryType = UITableViewCellAccessoryNone;
439
            cell.detailTextLabel.text = @"";
440
            
441
        } else if (indexPath.row == 1) {
442
            cell.textLabel.text = @"Email File as Attachment";
443
            cell.detailTextLabel.text = @"";
444
            cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
445
        }        
446
    } else if (indexPath.section == deleteSection) {
447
        cell.selectionStyle = UITableViewCellSelectionStyleBlue;
448
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
449
        cell.textLabel.text = @"Delete Object";
450
        cell.detailTextLabel.text = @"";
451
        
452
    } else if (indexPath.section == versionsSection) {
453
        cell.selectionStyle = UITableViewCellSelectionStyleBlue;
454
        cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
455
        cell.textLabel.text = @"Versions";
456
        cell.detailTextLabel.text = @"";
457
    }
458
    
459
    return cell;
460
}
461

    
462

    
463

    
464
#pragma mark -
465
#pragma mark Table view delegate
466

    
467
- (void)reloadActionsTitleRow:(NSTimer *)timer {
468
    [[timer.userInfo objectForKey:@"tableView"] reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:0 inSection:actionsSection]] withRowAnimation:UITableViewRowAnimationNone];
469
}
470

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

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

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

    
626
- (void)setProgress:(float)newProgress {
627
    [downloadProgressView setProgress:newProgress animated:YES];    
628
    if (newProgress >= 1.0) {
629
        fileDownloading = NO;
630
        fileDownloaded = YES;
631
        [self.tableView reloadData];
632
    }
633
}
634

    
635

    
636
#pragma mark -
637
#pragma mark Document Interation Controller Delegate
638

    
639
- (UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *) controller {
640
    return self.navigationController;
641
}
642

    
643
- (void)documentInteractionControllerDidEndPreview:(UIDocumentInteractionController *)controllers {
644
    [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:actionsSection] animated:YES];
645
}
646

    
647
#pragma mark -
648
#pragma mark Action Sheet Delegate
649

    
650
- (void)deleteObjectRow {
651
    [self.folder.objects removeObjectForKey:self.object.name];
652
    [self.folderViewController.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:selectedIndexPath] withRowAnimation:UITableViewRowAnimationLeft];
653
}
654

    
655
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
656
    if ([actionSheet isEqual:deleteActionSheet]) {
657
        if (buttonIndex == 0) {
658
            // delete the file and pop out
659
            
660
            NSString *activityMessage = @"Deleting file";
661
            [activityIndicatorView release];
662
            activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
663
            [activityIndicatorView addToView:self.view];
664

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

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

    
737
#pragma mark -
738
#pragma mark Mail Composer Delegate
739

    
740
// Dismisses the email composition interface when users tap Cancel or Send.
741
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {	
742
	[self dismissModalViewControllerAnimated:YES];
743
    [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:actionsSection] animated:YES];
744
}
745

    
746
#pragma mark -
747
#pragma mark Switches
748

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

    
796
     }];    
797
}
798

    
799

    
800

    
801
#pragma mark -
802
#pragma mark Memory management
803

    
804
- (void)dealloc {
805
    if (fileDownloading) {
806
        OpenStackRequest *request = [self.account.manager.objectDownloadRequests objectForKey:object.fullPath];
807
        [request setDownloadProgressDelegate:nil];
808
    }
809
    [account release];
810
    [downloadProgressView release];
811
    [deleteActionSheet release];
812
    [cdnURLActionSheet release];
813
    [tableView release];
814
    [folderViewController release];
815
    [objectIsPublicSwitch release];
816
    [permissions release];
817
    [documentInteractionController release];
818
    [actionSelectedIndexPath release];
819
    [versionID release];
820
    [activityIndicatorView release];
821
    [super dealloc];
822
}
823

    
824
#pragma mark -
825
#pragma mark Helper functions
826

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

    
854

    
855
@end
856