Fix bugs. Show version in settings. Update icons. Update version.
[pithos-ios] / Classes / EditMetadataViewController.m
1 //
2 //  EditMetadataViewController.m
3 //  pithos-ios
4 //
5 // Copyright 2011 GRNET S.A. All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or
8 // without modification, are permitted provided that the following
9 // conditions are met:
10 // 
11 //   1. Redistributions of source code must retain the above
12 //      copyright notice, this list of conditions and the following
13 //      disclaimer.
14 // 
15 //   2. Redistributions in binary form must reproduce the above
16 //      copyright notice, this list of conditions and the following
17 //      disclaimer in the documentation and/or other materials
18 //      provided with the distribution.
19 // 
20 // THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
21 // OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
24 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
27 // USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 // POSSIBILITY OF SUCH DAMAGE.
32 // 
33 // The views and conclusions contained in the software and
34 // documentation are those of the authors and should not be
35 // interpreted as representing official policies, either expressed
36 // or implied, of GRNET S.A.
37
38 #import "EditMetadataViewController.h"
39 #import "AccountManager.h"
40 #import "UIViewController+Conveniences.h"
41 #import "FolderViewController.h"
42 #import "Folder.h"
43 #import "PithosUtilities.h"
44 #import "APICallback.h"
45
46
47
48 @implementation EditMetadataViewController
49
50
51 @synthesize container, account, object;
52
53 @synthesize metadataKey;
54 @synthesize metadataValue;
55 @synthesize userInputMetaKey, userInputMetaValue;
56 @synthesize removeMetadataEnabled, objectIsFolder, folderViewController, objectIsContainer;
57
58 #define kMetadata 0 
59 #define kSaveMedata 1
60 #define kDeleteMetadata 2
61
62
63 - (void)dealloc
64 {
65     [account release];
66     [container release];
67     [object release];
68     [metadataKey release];
69     [metadataValue release];
70     [userInputMetaKey release];
71     [userInputMetaValue release];
72     [activityIndicatorView release];
73     [super dealloc];
74 }
75
76 - (void)didReceiveMemoryWarning
77 {
78     // Releases the view if it doesn't have a superview.
79     [super didReceiveMemoryWarning];
80     
81     // Release any cached data, images, etc that aren't in use.
82 }
83
84 #pragma mark - View lifecycle
85
86 - (void)viewDidLoad
87 {
88     [super viewDidLoad];
89 }
90
91 - (void)viewDidUnload
92 {
93     [super viewDidUnload];
94 }
95
96 - (void)viewWillAppear:(BOOL)animated
97 {    
98     [super viewWillAppear:animated];
99 }
100
101 - (void)viewDidAppear:(BOOL)animated
102 {
103     [super viewDidAppear:animated];
104 }
105
106 - (void)viewWillDisappear:(BOOL)animated
107 {
108     [super viewWillDisappear:animated];
109 }
110
111 - (void)viewDidDisappear:(BOOL)animated
112 {
113     [super viewDidDisappear:animated];
114 }
115
116 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
117     return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
118 }
119
120 #pragma mark - Table view data source
121
122 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
123 {
124     return (removeMetadataEnabled)? 3:2;
125 }
126
127 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
128 {
129     if (section == kMetadata)
130         return 2;
131     else
132         return 1;
133 }
134
135 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
136 {
137     static NSString *CellIdentifier = @"Cell";
138     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
139     if (cell == nil) {
140         cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
141     }
142                 
143     if (indexPath.section == kMetadata) {
144         UITextField *textField = nil;
145         for (id subView in cell.contentView.subviews) {
146             if ([subView isKindOfClass:[UITextField class]]) {
147                 textField = (UITextField *)subView;
148             }
149         }
150         
151         if (textField == nil) {
152             CGRect bounds = [cell.contentView bounds];
153             CGRect rect = CGRectInset(bounds, 10.0, 10.0);                        
154             textField = [[UITextField alloc] initWithFrame:rect];
155             [textField setFrame:rect];
156         }
157         [textField setClearButtonMode:UITextFieldViewModeWhileEditing];
158         [textField setBackgroundColor:[UIColor clearColor]];
159         [textField setOpaque:YES];
160         [textField setAutocorrectionType:UITextAutocorrectionTypeNo];
161         [textField setAutocapitalizationType:UITextAutocapitalizationTypeNone];
162         [textField setDelegate:self];
163         textField.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
164
165         if (indexPath.row == 0) {
166             [textField setReturnKeyType:UIReturnKeyNext];
167             textField.placeholder = @"Key";
168             textField.text = self.metadataKey;
169             textField.tag = 0;
170         }
171         else if (indexPath.row == 1) {
172             textField.placeholder = @"Value";
173             textField.text = self.metadataValue;
174             textField.tag = 1;
175         }
176             
177         cell.selectionStyle = UITableViewCellSelectionStyleNone;
178         [cell.contentView addSubview:textField];
179     }
180     else if (indexPath.section == kSaveMedata) 
181         cell.textLabel.text = @"Save";
182     else if (indexPath.section == kDeleteMetadata)
183         cell.textLabel.text = @"Remove";
184     
185     return cell;
186 }
187
188
189 #pragma mark - Table view delegate
190
191 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
192 {
193
194     
195     if (indexPath.section != kMetadata)
196         [self.view endEditing:YES];
197     
198     NSString *activityMessage;
199     NSIndexPath *keyCellIndexPath;
200     NSIndexPath *valueCellIndexPath;
201     UITableViewCell *cell;
202     
203     switch (indexPath.section) {
204         case kSaveMedata:
205             keyCellIndexPath = [NSIndexPath indexPathForRow:0 inSection:kMetadata];
206             cell = [self.tableView cellForRowAtIndexPath:keyCellIndexPath];
207             UITextField *textField = [[cell.contentView subviews] objectAtIndex:0];
208             self.userInputMetaKey = textField.text;
209         
210             if ([userInputMetaKey length] == 0
211                 || [[userInputMetaKey stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0) {
212                 [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
213                 [self alert:@"Invalid input" message:@"Metadata key cannot be empty"];
214                 return;
215             }
216             
217             valueCellIndexPath = [NSIndexPath indexPathForRow:1 inSection:kMetadata];
218             cell = [self.tableView cellForRowAtIndexPath:valueCellIndexPath];
219             textField = [[cell.contentView subviews] objectAtIndex:0];
220             self.userInputMetaValue = textField.text;
221             [object.metadata removeObjectForKey:metadataKey];
222             [object.metadata setObject:userInputMetaValue forKey:userInputMetaKey];
223             
224             if (objectIsFolder && ![PithosUtilities isContentTypeDirectory:object.contentType]) {
225                 if (((UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) &&
226                      [folderViewController.parentFolderViewController.folder.objects objectForKey:object.name]) || 
227                     ((UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPad) &&
228                      [folderViewController.folder.objects objectForKey:object.name])) {
229                     NSString *alertMessage = [NSString stringWithFormat:@"In order to apply the changes in '%@', the object at the same path must be replaced. Continue?",object.name];
230                     NSString *alertTitle = @"Apply changes";
231                     UIAlertView *alert = [[UIAlertView alloc] initWithTitle:alertTitle
232                                                                     message:alertMessage
233                                                                    delegate:self
234                                                           cancelButtonTitle:@"Cancel"
235                                                           otherButtonTitles:@"OK", nil];
236                     [alert show];
237                     [alert release];
238                     return;
239                 } else {
240                     activityMessage = @"Saving metadata...";
241                     [activityIndicatorView release];
242                     activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
243                     [activityIndicatorView addToView:self.view];
244                     
245                     object.name = object.fullPath;
246                     object.contentType = @"application/directory";
247                     object.data = [NSData data];
248                     [self createNewFolder];
249                     return;
250                 }
251             }
252             
253             activityMessage = @"Saving metadata";
254             [activityIndicatorView release];
255             activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
256             [activityIndicatorView addToView:self.view];
257             [[self.account.manager writeObjectMetadata:container object:object] 
258              success:^(OpenStackRequest *request) {
259                  self.metadataKey = userInputMetaKey;
260                  self.metadataValue = userInputMetaValue;
261                  removeMetadataEnabled = YES;
262                  [self.tableView reloadData];
263                  [activityIndicatorView removeFromSuperview];
264                  [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
265                  if (objectIsContainer) {
266                      container.metadata = object.metadata;
267                      [self.account.containers setObject:container forKey:container.name];
268                  }
269              }
270              failure:^(OpenStackRequest *request) { 
271                  [object.metadata removeObjectForKey:userInputMetaKey];
272                  [object.metadata setObject:metadataValue forKey:metadataKey];
273                  [activityIndicatorView removeFromSuperview];
274                  [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
275                  [self alert:@"There was a problem saving the metadata." request:request];
276              }];
277                         
278             break;
279         case kDeleteMetadata:
280             activityMessage = @"Deleting metadata";
281             [activityIndicatorView release];
282             activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
283             [activityIndicatorView addToView:self.view];
284
285             [object.metadata removeObjectForKey:metadataKey];
286             [[self.account.manager writeObjectMetadata:container object:object] 
287              success:^(OpenStackRequest *request) {
288                  [activityIndicatorView removeFromSuperview];
289                  [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
290                  self.metadataKey = @"";
291                  self.metadataValue = @"";
292                  removeMetadataEnabled = NO;
293                  [self.tableView reloadData];
294                  if (objectIsContainer) {
295                      container.metadata = object.metadata;
296                      [self.account.containers setObject:container forKey:container.name];
297                  }
298              }
299              failure:^(OpenStackRequest *request) {
300                  [object.metadata setObject:metadataValue forKey:metadataKey];
301                  [activityIndicatorView removeFromSuperview];
302                  [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
303                  [self alert:@"There was a problem saving the metadata." request:request];
304
305              }];
306             break;
307     }
308 }
309
310 #pragma mark - Alertview delegate
311
312 - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
313 {
314     if (buttonIndex == 1) {
315         NSString *activityMessage = @"Applying permissions...";
316         [activityIndicatorView release];
317         activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
318         [activityIndicatorView addToView:self.view];
319         
320         object.name = object.fullPath;
321         object.contentType = @"application/directory";
322         object.data = [NSData data];
323         [self createNewFolder];      
324     } else {
325         [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
326     }
327 }
328
329 #pragma mark - Textfield delegate
330
331
332 - (BOOL)textFieldShouldReturn:(UITextField *)textField
333 {
334     
335     if ([textField returnKeyType] == UIReturnKeyNext)
336     {
337         if ([textField.text length] == 0
338             || [[textField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length] == 0) {
339             [self alert:@"Invalid input" message:@"Metadata key cannot be empty"];
340         }
341
342         NSInteger nextTag = [textField tag] + 1;
343         UIView *nextTextField = [self.tableView viewWithTag:nextTag];
344         [nextTextField becomeFirstResponder];
345     }
346     else
347     {
348         [textField resignFirstResponder];
349     }
350     
351     return YES;
352 }
353
354 #pragma mark - add observers
355
356 - (void)createNewFolder {
357     [[self.account.manager writeObject:container object:object downloadProgressDelegate:nil] 
358      success:^(OpenStackRequest *request) {
359          Folder *newFolder = [[Folder alloc] init];
360          newFolder.name = [[object.name componentsSeparatedByString:@"/"] lastObject];
361          newFolder.parent = folderViewController.folder;
362          newFolder.sharing = folderViewController.folder.sharing;
363          newFolder.metadata = object.metadata;
364          [folderViewController.folder.folders setObject:newFolder forKey:newFolder.name];
365          [activityIndicatorView removeFromSuperview];
366          [newFolder release];
367          self.metadataKey = userInputMetaKey;
368          self.metadataValue = userInputMetaValue;
369          removeMetadataEnabled = YES;
370          [self.tableView reloadData];
371          [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
372          if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
373              [self.folderViewController refreshButtonPressed:nil];
374          }
375          else {
376              self.folderViewController.needsRefreshing = YES;
377              self.folderViewController.refreshWhenAppeared = YES;
378          }
379      }
380      failure:^(OpenStackRequest *request) {
381          [object.metadata removeObjectForKey:userInputMetaKey];
382          [object.metadata setObject:metadataValue forKey:metadataKey];
383          [activityIndicatorView removeFromSuperview];
384          [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
385          [self.tableView reloadData];
386          [self alert:@"There was a problem saving the metadata." request:request];
387      }];
388 }
389
390 @end