2 // EditMetadataViewController.m
5 // Copyright 2011 GRNET S.A. All rights reserved.
7 // Redistribution and use in source and binary forms, with or
8 // without modification, are permitted provided that the following
11 // 1. Redistributions of source code must retain the above
12 // copyright notice, this list of conditions and the following
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.
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.
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.
38 #import "EditMetadataViewController.h"
39 #import "AccountManager.h"
40 #import "UIViewController+Conveniences.h"
41 #import "FolderViewController.h"
43 #import "PithosUtilities.h"
44 #import "APICallback.h"
48 @implementation EditMetadataViewController
51 @synthesize container, account, object;
53 @synthesize metadataKey;
54 @synthesize metadataValue;
55 @synthesize userInputMetaKey, userInputMetaValue;
56 @synthesize removeMetadataEnabled, objectIsFolder, folderViewController, objectIsContainer;
60 #define kDeleteMetadata 2
68 [metadataKey release];
69 [metadataValue release];
70 [userInputMetaKey release];
71 [userInputMetaValue release];
72 [activityIndicatorView release];
76 - (void)didReceiveMemoryWarning
78 // Releases the view if it doesn't have a superview.
79 [super didReceiveMemoryWarning];
81 // Release any cached data, images, etc that aren't in use.
84 #pragma mark - View lifecycle
93 [super viewDidUnload];
96 - (void)viewWillAppear:(BOOL)animated
98 [super viewWillAppear:animated];
101 - (void)viewDidAppear:(BOOL)animated
103 [super viewDidAppear:animated];
106 - (void)viewWillDisappear:(BOOL)animated
108 [super viewWillDisappear:animated];
111 - (void)viewDidDisappear:(BOOL)animated
113 [super viewDidDisappear:animated];
116 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
117 return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
120 #pragma mark - Table view data source
122 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
124 return (removeMetadataEnabled)? 3:2;
127 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
129 if (section == kMetadata)
135 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
137 static NSString *CellIdentifier = @"Cell";
138 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
140 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
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;
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];
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;
165 if (indexPath.row == 0) {
166 [textField setReturnKeyType:UIReturnKeyNext];
167 textField.placeholder = @"Key";
168 textField.text = self.metadataKey;
171 else if (indexPath.row == 1) {
172 textField.placeholder = @"Value";
173 textField.text = self.metadataValue;
177 cell.selectionStyle = UITableViewCellSelectionStyleNone;
178 [cell.contentView addSubview:textField];
180 else if (indexPath.section == kSaveMedata)
181 cell.textLabel.text = @"Save";
182 else if (indexPath.section == kDeleteMetadata)
183 cell.textLabel.text = @"Remove";
189 #pragma mark - Table view delegate
191 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
195 if (indexPath.section != kMetadata)
196 [self.view endEditing:YES];
198 NSString *activityMessage;
199 NSIndexPath *keyCellIndexPath;
200 NSIndexPath *valueCellIndexPath;
201 UITableViewCell *cell;
203 switch (indexPath.section) {
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;
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"];
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];
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
234 cancelButtonTitle:@"Cancel"
235 otherButtonTitles:@"OK", nil];
240 activityMessage = @"Saving metadata...";
241 [activityIndicatorView release];
242 activityIndicatorView = [[ActivityIndicatorView alloc] initWithFrame:[ActivityIndicatorView frameForText:activityMessage] text:activityMessage];
243 [activityIndicatorView addToView:self.view];
245 object.name = object.fullPath;
246 object.contentType = @"application/directory";
247 object.data = [NSData data];
248 [self createNewFolder];
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];
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];
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];
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];
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];
310 #pragma mark - Alertview delegate
312 - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
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];
320 object.name = object.fullPath;
321 object.contentType = @"application/directory";
322 object.data = [NSData data];
323 [self createNewFolder];
325 [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
329 #pragma mark - Textfield delegate
332 - (BOOL)textFieldShouldReturn:(UITextField *)textField
335 if ([textField returnKeyType] == UIReturnKeyNext)
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"];
342 NSInteger nextTag = [textField tag] + 1;
343 UIView *nextTextField = [self.tableView viewWithTag:nextTag];
344 [nextTextField becomeFirstResponder];
348 [textField resignFirstResponder];
354 #pragma mark - add observers
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 addFolder:newFolder];
365 [activityIndicatorView removeFromSuperview];
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];
376 self.folderViewController.needsRefreshing = YES;
377 self.folderViewController.refreshWhenAppeared = YES;
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];