2 // EditMetadataViewController.m
5 // Copyright 2011-2012 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"
42 #import "OpenStackAccount.h"
45 #import "StorageObject.h"
46 #import "ActivityIndicatorView.h"
47 #import "PithosUtilities.h"
48 #import "APICallback.h"
50 @implementation EditMetadataViewController
52 @synthesize container, account, object;
54 @synthesize metadataKey;
55 @synthesize metadataValue;
56 @synthesize userInputMetaKey, userInputMetaValue;
57 @synthesize removeMetadataEnabled, objectIsFolder, folderViewController, objectIsContainer;
61 #define kDeleteMetadata 2
68 [metadataKey release];
69 [metadataValue release];
70 [userInputMetaKey release];
71 [userInputMetaValue release];
75 #pragma mark - View lifecycle
77 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
78 return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
81 #pragma mark - Internal
83 - (void)createNewFolder {
84 __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Saving metadata..."
85 andAddToView:self.view];
86 [[self.account.manager writeObject:container object:object downloadProgressDelegate:nil]
87 success:^(OpenStackRequest *request) {
88 Folder *newFolder = [[Folder alloc] init];
89 newFolder.name = [[object.name componentsSeparatedByString:@"/"] lastObject];
90 newFolder.parent = folderViewController.folder;
91 newFolder.sharing = folderViewController.folder.sharing;
92 newFolder.metadata = object.metadata;
93 [folderViewController.folder addFolder:newFolder];
94 // XXX increase container.count if in rootFolder?
95 [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
97 self.metadataKey = userInputMetaKey;
98 self.metadataValue = userInputMetaValue;
99 removeMetadataEnabled = YES;
100 [self.tableView reloadData];
101 [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
102 if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
103 [self.folderViewController refreshButtonPressed:nil];
105 self.folderViewController.refreshWhenAppeared = YES;
108 failure:^(OpenStackRequest *request) {
109 [object.metadata removeObjectForKey:userInputMetaKey];
110 [object.metadata setObject:metadataValue forKey:metadataKey];
111 [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
112 [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
113 [self.tableView reloadData];
114 [self alert:@"There was a problem saving the metadata." request:request];
118 #pragma mark - Table view data source
120 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
121 return (removeMetadataEnabled ? 3 : 2);
124 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
125 return ((section == kMetadata) ? 2 : 1);
128 - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
129 static NSString *CellIdentifier = @"Cell";
130 UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
132 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
135 if (indexPath.section == kMetadata) {
136 UITextField *textField = nil;
137 for (id subView in cell.contentView.subviews) {
138 if ([subView isKindOfClass:[UITextField class]]) {
139 textField = (UITextField *)subView;
143 if (textField == nil) {
144 CGRect bounds = [cell.contentView bounds];
145 CGRect rect = CGRectInset(bounds, 10.0, 10.0);
146 textField = [[UITextField alloc] initWithFrame:rect];
147 [textField setFrame:rect];
149 [textField setClearButtonMode:UITextFieldViewModeWhileEditing];
150 [textField setBackgroundColor:[UIColor clearColor]];
151 [textField setOpaque:YES];
152 [textField setAutocorrectionType:UITextAutocorrectionTypeNo];
153 [textField setAutocapitalizationType:UITextAutocapitalizationTypeNone];
154 [textField setDelegate:self];
155 textField.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
157 if (indexPath.row == 0) {
158 [textField setReturnKeyType:UIReturnKeyNext];
159 textField.placeholder = @"Key";
160 textField.text = self.metadataKey;
162 } else if (indexPath.row == 1) {
163 textField.placeholder = @"Value";
164 textField.text = self.metadataValue;
168 cell.selectionStyle = UITableViewCellSelectionStyleNone;
169 [cell.contentView addSubview:textField];
170 } else if (indexPath.section == kSaveMedata) {
171 cell.textLabel.text = @"Save";
172 } else if (indexPath.section == kDeleteMetadata) {
173 cell.textLabel.text = @"Remove";
179 #pragma mark - Table view delegate
181 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
182 if (indexPath.section != kMetadata)
183 [self.view endEditing:YES];
185 NSIndexPath *keyCellIndexPath;
186 NSIndexPath *valueCellIndexPath;
187 UITableViewCell *cell;
189 if (indexPath.section == kSaveMedata) {
190 keyCellIndexPath = [NSIndexPath indexPathForRow:0 inSection:kMetadata];
191 cell = [self.tableView cellForRowAtIndexPath:keyCellIndexPath];
192 UITextField *textField = [[cell.contentView subviews] objectAtIndex:0];
193 self.userInputMetaKey = textField.text;
195 if (!userInputMetaKey.length || ![userInputMetaKey stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].length) {
196 [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
197 [self alert:@"Invalid input" message:@"Metadata key cannot be empty"];
199 valueCellIndexPath = [NSIndexPath indexPathForRow:1 inSection:kMetadata];
200 cell = [self.tableView cellForRowAtIndexPath:valueCellIndexPath];
201 textField = [[cell.contentView subviews] objectAtIndex:0];
202 self.userInputMetaValue = textField.text;
203 [object.metadata removeObjectForKey:metadataKey];
204 [object.metadata setObject:userInputMetaValue forKey:userInputMetaKey];
206 if (objectIsFolder && ![PithosUtilities isContentTypeDirectory:object.contentType]) {
207 if ((([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) &&
208 [folderViewController.parentFolderViewController.folder.objects objectForKey:object.name]) ||
209 (([UIDevice currentDevice].userInterfaceIdiom != UIUserInterfaceIdiomPad) &&
210 [folderViewController.folder.objects objectForKey:object.name])) {
211 NSString *alertMessage = [NSString stringWithFormat:@"In order to apply the changes in '%@', the object at the same path must be replaced. Continue?", object.name];
212 NSString *alertTitle = @"Apply changes";
213 UIAlertView *alert = [[UIAlertView alloc] initWithTitle:alertTitle
216 cancelButtonTitle:@"Cancel"
217 otherButtonTitles:@"OK", nil];
221 object.name = object.fullPath;
222 object.contentType = @"application/directory";
223 object.data = [NSData data];
224 [self createNewFolder];
227 __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Saving metadata..."
228 andAddToView:self.view];
229 [[self.account.manager writeObjectMetadata:container object:object]
230 success:^(OpenStackRequest *request) {
231 self.metadataKey = userInputMetaKey;
232 self.metadataValue = userInputMetaValue;
233 removeMetadataEnabled = YES;
234 [self.tableView reloadData];
235 [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
236 [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
237 if (objectIsContainer) {
238 container.metadata = object.metadata;
239 [self.account.containers setObject:container forKey:container.name];
242 failure:^(OpenStackRequest *request) {
243 [object.metadata removeObjectForKey:userInputMetaKey];
244 [object.metadata setObject:metadataValue forKey:metadataKey];
245 [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
246 [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
247 [self alert:@"There was a problem saving the metadata." request:request];
251 } else if (indexPath.section == kDeleteMetadata) {
252 __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Deleting metadata..."
253 andAddToView:self.view];
254 [object.metadata removeObjectForKey:metadataKey];
255 [[self.account.manager writeObjectMetadata:container object:object]
256 success:^(OpenStackRequest *request) {
257 [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
258 [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
259 self.metadataKey = @"";
260 self.metadataValue = @"";
261 removeMetadataEnabled = NO;
262 [self.tableView reloadData];
263 if (objectIsContainer) {
264 container.metadata = object.metadata;
265 [self.account.containers setObject:container forKey:container.name];
268 failure:^(OpenStackRequest *request) {
269 [object.metadata setObject:metadataValue forKey:metadataKey];
270 [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
271 [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
272 [self alert:@"There was a problem saving the metadata." request:request];
278 #pragma mark - Alertview delegate
280 - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
281 if (buttonIndex == 1) {
282 object.name = object.fullPath;
283 object.contentType = @"application/directory";
284 object.data = [NSData data];
285 [self createNewFolder];
287 [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
291 #pragma mark - Textfield delegate
293 - (BOOL)textFieldShouldReturn:(UITextField *)textField {
294 if ([textField returnKeyType] == UIReturnKeyNext) {
295 if (!textField.text.length || ![textField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]) {
296 [self alert:@"Invalid input" message:@"Metadata key cannot be empty"];
299 NSInteger nextTag = [textField tag] + 1;
300 UIView *nextTextField = [self.tableView viewWithTag:nextTag];
301 [nextTextField becomeFirstResponder];
303 [textField resignFirstResponder];