Statistics
| Branch: | Tag: | Revision:

root / Classes / EditAccountGroupsViewController.m @ ea3a6bba

History | View | Annotate | Download (17 kB)

1
//
2
//  EditAccountGroupsViewController.m
3
//  pithos-ios
4
//
5
// Copyright 2011-2013 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 "EditAccountGroupsViewController.h"
39
#import "OpenStackAccount.h"
40
#import "ActivityIndicatorView.h"
41
#import "UIViewController+Conveniences.h"
42
#import "AccountManager.h"
43
#import "OpenStackRequest.h"
44
#import "APICallback.h"
45

    
46
#define kGroupName 0 
47
#define kGroupUsers 1
48
#define kSave 2
49
#define kRemove 3
50

    
51
@implementation EditAccountGroupsViewController
52

    
53
@synthesize account, groupName, groups, metadata;
54
@synthesize removeGroupEnabled, groupUUIDs, groupNameTextField, groupUsersTextFields, numberOfGroupUsersRows;
55

    
56
#pragma mark - View lifecycle
57

    
58
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
59
    return ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) || (toInterfaceOrientation == UIInterfaceOrientationPortrait);
60
}
61

    
62
- (void)viewDidLoad {
63
    [super viewDidLoad];
64
    self.groupUUIDs = [groups objectForKey:groupName];
65
    if (!self.groupUUIDs) {
66
        self.groupUUIDs = [NSArray array];
67
        numberOfGroupUsersRows = 2;
68
        self.groupUsersTextFields = [NSMutableDictionary dictionaryWithCapacity:1];
69
    } else {
70
        numberOfGroupUsersRows = groupUUIDs.count + 1;
71
        self.groupUsersTextFields = [NSMutableDictionary dictionaryWithCapacity:groupUUIDs.count];
72
    }
73
}
74

    
75
#pragma mark - Memory management
76

    
77
- (void)dealloc {
78
    [account release];
79
    [groupName release];
80
    [groups release];
81
    [metadata release];
82
    [groupUUIDs release];
83
    [groupNameTextField release];
84
    [groupUsersTextFields release];
85
    [super dealloc];
86
}
87

    
88
#pragma mark - Internal
89

    
90
- (BOOL)groupNameIsValid:(NSString *)input {
91
    if (!input.length) {
92
        [self alert:@"Invalid input" message:@"Group name cannot be empty."];
93
        return NO;
94
    } else if ([input rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@" -_~,;"]].location != NSNotFound) {
95
        [self alert:@"Invalid input" message:@"Group name cannot contain ' ', '-', '_', '~', ',' or ';'."];
96
        return NO;
97
    }
98
    return YES;
99
}
100

    
101
- (NSMutableDictionary *)groupsInfoForGroups:(NSMutableDictionary *)groupsDictionary {
102
    NSMutableDictionary *groupsInfo = [NSMutableDictionary dictionaryWithCapacity:groupsDictionary.count];
103
    for (NSString *groupUser in groupsDictionary) {
104
        [groupsInfo setObject:[[groupsDictionary objectForKey:groupUser] componentsJoinedByString:@","]
105
                       forKey:groupUser];
106
    }
107
    return groupsInfo;
108
}
109

    
110
- (void)applyGroups:(NSArray *)newGroupUUIDs {
111
    // Apply changes.
112
    [groups removeObjectForKey:groupName];
113
    [groups setObject:newGroupUUIDs forKey:groupNameTextField.text];
114
    __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Saving group..."
115
                                                                                                   andAddToView:self.view];
116
    [[self.account.manager writeAccountMetadata:[NSDictionary dictionaryWithObjectsAndKeys:
117
                                                 [self groupsInfoForGroups:groups], @"groups",
118
                                                 metadata, @"metadata",
119
                                                 nil]]
120
     success:^(OpenStackRequest *request) {
121
         self.groupName = groupNameTextField.text;
122
         self.groupUUIDs = newGroupUUIDs;
123
         numberOfGroupUsersRows = groupUUIDs.count + 1;
124
         self.groupUsersTextFields = [NSMutableDictionary dictionaryWithCapacity:groupUUIDs.count];
125
         removeGroupEnabled = YES;
126
         self.navigationItem.title = @"Edit Group";
127
         [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
128
         [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
129
         [self.tableView reloadData];
130
     }
131
     failure:^(OpenStackRequest *request) {
132
         [groups removeObjectForKey:groupNameTextField.text];
133
         if (groupUUIDs.count) {
134
             [groups setObject:groupUUIDs forKey:groupName];
135
         }
136
         [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
137
         [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
138
         [self alert:@"There was a problem saving the group." request:request];
139
     }];
140

    
141
}
142

    
143
#pragma mark - UITableViewDataSource
144

    
145
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
146
    return (removeGroupEnabled ? 4 : 3);
147
}
148

    
149
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
150
    if (section == kGroupUsers) {
151
        return numberOfGroupUsersRows;
152
    }
153
    return 1;
154
}
155

    
156
- (UITextField *)textFieldForCell:(UITableViewCell *)cell {
157
    CGRect rect = CGRectInset(cell.contentView.bounds, 10.0, 10.0);
158
    UITextField *textField = [[[UITextField alloc] initWithFrame:rect] autorelease];
159
    textField.frame = rect;
160
    textField.clearButtonMode = UITextFieldViewModeWhileEditing;
161
    textField.backgroundColor = [UIColor clearColor];
162
    textField.opaque = YES;
163
    textField.autocorrectionType = UITextAutocorrectionTypeNo;
164
    textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
165
    textField.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
166
    return textField;
167
}
168

    
169
- (UITextField *)textFieldForCell:(UITableViewCell *)cell withIndexPath:(NSIndexPath *)indexPath {
170
    if (indexPath.section == kGroupName) {
171
        if (!groupNameTextField) {
172
            self.groupNameTextField = [self textFieldForCell:cell];
173
            groupNameTextField.placeholder = @"Group name";
174
            groupNameTextField.text = groupName;
175
            groupNameTextField.delegate = self;
176
        }
177
        return groupNameTextField;
178
    } else if (indexPath.section == kGroupUsers) {
179
        UITextField *groupUserTextField = [groupUsersTextFields objectForKey:indexPath];
180
        if (!groupUserTextField) {
181
            groupUserTextField = [self textFieldForCell:cell];
182
            groupUserTextField.placeholder = @"User";
183
            NSString *groupUser = (indexPath.row < groupUUIDs.count) ? [self.account displaynameForUUID:[groupUUIDs objectAtIndex:indexPath.row] safe:YES] : nil;
184
            groupUserTextField.text = (groupUser && groupUser.length) ? groupUser : nil;
185
            groupUserTextField.tag = indexPath.row;
186
            [groupUsersTextFields setObject:groupUserTextField forKey:indexPath];
187
            groupUserTextField.delegate = self;
188
        }
189
        return groupUserTextField;
190
    }
191
    return nil;
192
}
193

    
194
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
195
    if ((indexPath.section == kGroupName) || ((indexPath.section == kGroupUsers) && (indexPath.row < numberOfGroupUsersRows - 1))) {
196
        static NSString *CellIdentifier = @"TextFieldCell";
197
        UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
198
        if (cell == nil) {
199
            cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
200
        }
201
        cell.textLabel.text = nil;
202
        cell.accessoryType = UITableViewCellAccessoryNone;
203
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
204
        for (UIView *view in cell.contentView.subviews) {
205
            [view removeFromSuperview];
206
        }
207
        UITextField *textField = [self textFieldForCell:cell withIndexPath:indexPath];
208
        textField.returnKeyType = (((indexPath.section == kGroupUsers) && (indexPath.row == numberOfGroupUsersRows - 2)) ?
209
                                   UIReturnKeyDefault : UIReturnKeyNext);
210
        [cell.contentView addSubview:textField];
211
        return cell;
212
    }
213
    
214
    static NSString *CellIdentifier = @"Cell";
215
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
216
    if (cell == nil) {
217
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
218
    }
219
    cell.accessoryType = UITableViewCellAccessoryNone;
220

    
221
    if ((indexPath.section == kGroupUsers) && (indexPath.row == numberOfGroupUsersRows - 1)) {
222
        cell.textLabel.text = @"Add User";
223
        cell.selectionStyle = UITableViewCellSelectionStyleBlue;
224
    } else if (indexPath.section == kSave) {
225
        cell.textLabel.text = @"Save Group";
226
        cell.selectionStyle = UITableViewCellSelectionStyleBlue;
227
    } else if (indexPath.section == kRemove) {
228
        cell.textLabel.text = @"Remove Group";
229
        cell.selectionStyle = UITableViewCellSelectionStyleBlue;
230
    }
231
    
232
    return cell;
233
}
234

    
235
#pragma mark - UITableViewDelegate
236

    
237
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
238
    if (((indexPath.section == kGroupUsers) && (indexPath.row == numberOfGroupUsersRows - 1)) ||
239
        (indexPath.section == kSave) || (indexPath.section == kRemove))
240
        [self.view endEditing:YES];
241

    
242
    if ((indexPath.section == kGroupUsers) && (indexPath.row == numberOfGroupUsersRows - 1)) {
243
        numberOfGroupUsersRows++;
244
        [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
245
        [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:kGroupUsers] withRowAnimation:UITableViewRowAnimationNone];
246
    } else if (indexPath.section == kSave) {
247
        if (![self groupNameIsValid:groupNameTextField.text]) {
248
            [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
249
        } else {
250
            NSMutableArray *newGroupUsers = [NSMutableArray arrayWithCapacity:groupUsersTextFields.count];
251
            for (UITextField *groupUserTextField in [groupUsersTextFields allValues]) {
252
                if (groupUserTextField.text && groupUserTextField.text.length)
253
                    [newGroupUsers addObject:groupUserTextField.text];
254
            }
255
            if (!newGroupUsers.count) {
256
                [self alert:@"Cannot save group" message:@"Please input at least one user."];
257
                [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
258
            } else {
259
                __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Checking users..."
260
                                                                                                               andAddToView:self.view];
261
                [[self.account.manager userCatalogForDisplaynames:newGroupUsers UUIDs:nil]
262
                 success:^(OpenStackRequest *request) {
263
                     NSDictionary *displaynameCatalog = [request displaynameCatalog];
264
                     NSMutableArray *newGroupUUIDs = [NSMutableArray arrayWithCapacity:newGroupUsers.count];
265
                     for (NSString *groupUser in newGroupUsers) {
266
                         NSString *UUID = ([groupUser isEqualToString:@"*"] ? @"*" : [displaynameCatalog objectForKey:groupUser]);
267
                         if (!UUID) {
268
                             [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
269
                             [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
270
                             [self alert:@"Invalid user" message:[NSString stringWithFormat:@"User '%@' doesn't exist.", groupUser]];
271
                             return;
272
                         }
273
                         [newGroupUUIDs addObject:UUID];
274
                     }
275
                     [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
276
                     [self applyGroups:newGroupUUIDs];
277
                 }
278
                 failure:^(OpenStackRequest *request) {
279
                     [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
280
                     if (request.responseStatusCode != 404) {
281
                         // Don't show alert on 404, since it can be a pre-UUID server.
282
                         [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
283
                         [self alert:@"There was a problem checking the users." request:request];
284
                     } else {
285
                         // Since we don't translate to UUIDs, check for invalid chars.
286
                         BOOL valid = YES;
287
                         for (NSString *groupUser in newGroupUsers) {
288
                             if ([groupUser rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@" ~,;:"]].location != NSNotFound) {
289
                                 valid = NO;
290
                                 break;
291
                             }
292
                         }
293
                         if (valid) {
294
                             [self applyGroups:newGroupUsers];
295
                         } else {
296
                             [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
297
                             [self alert:@"Invalid input" message:@"Users cannot contain ' ', '~', ',', ';' or ':'."];
298
                         }
299
                     }
300
                 }];
301
            }
302
        }
303
    } else if (indexPath.section == kRemove) {
304
        [groups removeObjectForKey:groupName];
305
        __block ActivityIndicatorView *activityIndicatorView = [ActivityIndicatorView activityIndicatorViewWithText:@"Removing group..."
306
                                                                                                       andAddToView:self.view];
307
        [[self.account.manager writeAccountMetadata:[NSDictionary dictionaryWithObjectsAndKeys:
308
                                                     [self groupsInfoForGroups:groups], @"groups",
309
                                                     metadata, @"metadata",
310
                                                     nil]]
311
         success:^(OpenStackRequest *request) {
312
             self.groupName = @"";
313
             self.groupUUIDs = [NSArray array];
314
             numberOfGroupUsersRows = 2;
315
             self.groupUsersTextFields = [NSMutableDictionary dictionaryWithCapacity:1];
316
             removeGroupEnabled = NO;
317
             self.navigationItem.title = @"Add Group";
318
             [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
319
             [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
320
             [self.tableView reloadData];
321
         }
322
         failure:^(OpenStackRequest *request) {
323
             if (groupUUIDs.count) {
324
                 [groups setObject:groupUUIDs forKey:groupName];
325
             }
326
             [activityIndicatorView stopAnimatingAndRemoveFromSuperview];
327
             [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
328
             [self alert:@"There was a problem removing the group." request:request];
329
         }];
330
    }
331
}
332

    
333
#pragma mark - UITextFieldDelegate
334

    
335
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
336
    if ((textField == groupNameTextField) &&
337
        ([string rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@" -_~,;"]].location != NSNotFound)) {
338
        [self alert:@"Invalid input" message:@"Group name cannot contain ' ', '-', '_', '~', ',' or ';'."];
339
        return NO;
340
    }
341
    return YES;
342
}
343

    
344
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
345
    if (textField == groupNameTextField) {
346
        if (![self groupNameIsValid:textField.text]) {
347
            return NO;
348
        }
349
        [[groupUsersTextFields objectForKey:[NSIndexPath indexPathForRow:0 inSection:kGroupUsers]] becomeFirstResponder];
350
    } else if (textField.tag == numberOfGroupUsersRows - 2) {
351
        [textField resignFirstResponder];
352
    } else {
353
        [[groupUsersTextFields objectForKey:[NSIndexPath indexPathForRow:(textField.tag + 1) inSection:kGroupUsers]] becomeFirstResponder];
354
    }    
355
    return YES;
356
}
357

    
358
@end