blob: 254e544d25969fd674e3319f1316f10b339e2c69 [file] [log] [blame]
Loïc Siretfcb4ca62016-09-21 17:12:09 -04001/*
2 * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
3 * Author: Loïc Siret <loic.siret@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#import "RingWizardNewAccountVC.h"
21
22
23//Cocoa
24#import <AddressBook/AddressBook.h>
25#import <Quartz/Quartz.h>
26
27//Qt
28#import <QUrl>
29#import <QPixmap>
30
31//LRC
32#import <accountmodel.h>
33#import <protocolmodel.h>
34#import <profilemodel.h>
35#import <QItemSelectionModel>
36#import <account.h>
37#import <certificate.h>
38#import <profilemodel.h>
39#import <profile.h>
40#import <person.h>
41
42#import "AppDelegate.h"
43#import "Constants.h"
44#import "views/NSImage+Extensions.h"
45#import "views/NSColor+RingTheme.h"
46
47@interface RingWizardNewAccountVC ()
48@end
49
50@implementation RingWizardNewAccountVC
51{
Loïc Siret3652cfb2016-10-27 10:12:07 -040052 __unsafe_unretained IBOutlet NSView* loadingView;
53 __unsafe_unretained IBOutlet NSView* creationView;
54
Loïc Siretfcb4ca62016-09-21 17:12:09 -040055 __unsafe_unretained IBOutlet NSButton* photoView;
56 __unsafe_unretained IBOutlet NSTextField* nicknameField;
57 __unsafe_unretained IBOutlet NSSecureTextField* passwordField;
Loïc Siret3652cfb2016-10-27 10:12:07 -040058 __unsafe_unretained IBOutlet NSSecureTextField* passwordRepeatField;
Loïc Siretfcb4ca62016-09-21 17:12:09 -040059 __unsafe_unretained IBOutlet NSTextField* passwordLabel;
Loïc Siret3652cfb2016-10-27 10:12:07 -040060 __unsafe_unretained IBOutlet NSTextField* passwordRepeatLabel;
61 __unsafe_unretained IBOutlet NSImageView* passwordCheck;
62 __unsafe_unretained IBOutlet NSImageView* passwordRepeatCheck;
Loïc Siretfcb4ca62016-09-21 17:12:09 -040063 __unsafe_unretained IBOutlet NSButton* createButton;
64 __unsafe_unretained IBOutlet NSButton* cancelButton;
Loïc Siret3652cfb2016-10-27 10:12:07 -040065
66 __unsafe_unretained IBOutlet NSProgressIndicator* progressBar;
Loïc Siretfcb4ca62016-09-21 17:12:09 -040067 Account* accountToCreate;
68 NSTimer* errorTimer;
69 QMetaObject::Connection stateChanged;
70}
71
Loïc Siret3652cfb2016-10-27 10:12:07 -040072NSInteger const NICKNAME_TAG = 1;
Loïc Siretfcb4ca62016-09-21 17:12:09 -040073
Loïc Siret3652cfb2016-10-27 10:12:07 -040074//ERROR CODE for textfields validations
75NSInteger const ERROR_PASSWORD_TOO_SHORT = -1;
76NSInteger const ERROR_REPEAT_MISMATCH = -2;
77
78
79- (BOOL)produceError:(NSError**)error withCode:(NSInteger)code andMessage:(NSString*)message
80{
81 if (error != NULL){
82 NSDictionary *errorDetail = @{NSLocalizedDescriptionKey: message};
83 *error = [NSError errorWithDomain:@"Input" code:code userInfo:errorDetail];
84 }
85 return NO;
86}
Loïc Siretfcb4ca62016-09-21 17:12:09 -040087
88- (void)show
89{
90 AppDelegate* appDelegate = (AppDelegate *)[[NSApplication sharedApplication] delegate];
91 [nicknameField setTag:NICKNAME_TAG];
92 [nicknameField setStringValue:NSFullUserName()];
93 [self controlTextDidChange:[NSNotification notificationWithName:@"PlaceHolder" object:nicknameField]];
94
95 NSData* imgData = [[[ABAddressBook sharedAddressBook] me] imageData];
96 if (imgData != nil) {
97 [photoView setImage:[[NSImage alloc] initWithData:imgData]];
98 } else
99 [photoView setImage:[NSImage imageNamed:@"default_user_icon"]];
100
101 [photoView setWantsLayer: YES];
102 photoView.layer.cornerRadius = photoView.frame.size.width / 2;
103 photoView.layer.masksToBounds = YES;
Loïc Siret3652cfb2016-10-27 10:12:07 -0400104
105 [self display:creationView];
106}
107
108- (void)removeSubviews
109{
110 while ([self.view.subviews count] > 0){
111 [[self.view.subviews firstObject] removeFromSuperview];
112 }
113}
114
115- (void)display:(NSView *)view
116{
117 [self.delegate showView:view];
Loïc Siretfcb4ca62016-09-21 17:12:09 -0400118}
119
120- (IBAction)editPhoto:(id)sender
121{
Loïc Siret3652cfb2016-10-27 10:12:07 -0400122 auto pictureTaker = [IKPictureTaker pictureTaker];
Loïc Siretfcb4ca62016-09-21 17:12:09 -0400123
124 [pictureTaker beginPictureTakerSheetForWindow:[self.delegate window]
125 withDelegate:self
126 didEndSelector:@selector(pictureTakerDidEnd:returnCode:contextInfo:)
127 contextInfo:nil];
128
129}
130
131- (void)pictureTakerDidEnd:(IKPictureTaker *) picker
132 returnCode:(NSInteger) code
133 contextInfo:(void*) contextInfo
134{
135 if (auto outputImage = [picker outputImage]) {
136 [photoView setImage:outputImage];
137 } else
138 [photoView setImage:[NSImage imageNamed:@"default_user_icon"]];
139}
140
Loïc Siret3652cfb2016-10-27 10:12:07 -0400141#pragma mark - Input validation
142- (BOOL)isPasswordValid
143{
144 return self.password.length >= 6;
145}
146
147- (BOOL)isRepeatPasswordValid
148{
149 return [self.password isEqualToString:self.repeatPassword];
150}
151
152- (BOOL)validateRepeatPassword:(NSError **)error
153{
154 if (!self.isRepeatPasswordValid){
155 return [self produceError:error
156 withCode:ERROR_REPEAT_MISMATCH
157 andMessage:NSLocalizedString(@"Passwords don't match",
158 @"Indication for user")];
159 }
160 return YES;
161}
162
163- (BOOL)validatePassword:(NSError **)error
164{
165 if (!self.isRepeatPasswordValid){
166 return [self produceError:error
167 withCode:ERROR_PASSWORD_TOO_SHORT
168 andMessage:NSLocalizedString(@"Password is too short",
169 @"Indication for user")];
170 }
171 return YES;
172}
173
174- (BOOL)validateUserInputPassword:(NSError **)error
175{
176 return [self validatePassword:error] && [self validateRepeatPassword:error];
177}
178
Loïc Siretfcb4ca62016-09-21 17:12:09 -0400179- (IBAction)createRingAccount:(id)sender
180{
Loïc Siret3652cfb2016-10-27 10:12:07 -0400181 NSError *error = nil;
182 if (![self validateUserInputPassword:&error]){
183 NSAlert* alert = [NSAlert alertWithMessageText:[error localizedDescription]
184 defaultButton:NSLocalizedString(@"Revise Input",
185 @"Button title")
186 alternateButton:nil
187 otherButton:nil
188 informativeTextWithFormat:@"%@",error];
189
190 [alert beginSheetModalForWindow:passwordField.window
191 modalDelegate:nil
192 didEndSelector:NULL
193 contextInfo:NULL];
194
195 return;
196 }
197
198 [self display:loadingView];
Loïc Siretfcb4ca62016-09-21 17:12:09 -0400199 [progressBar startAnimation:nil];
Loïc Siret3652cfb2016-10-27 10:12:07 -0400200
Loïc Siretfcb4ca62016-09-21 17:12:09 -0400201
202 if ([self.alias isEqualToString:@""]) {
203 self.alias = NSLocalizedString(@"Unknown", @"Name used when user leave field empty");
204 }
205 accountToCreate = AccountModel::instance().add(QString::fromNSString(self.alias), Account::Protocol::RING);
206
207 accountToCreate->setAlias([self.alias UTF8String]);
208 accountToCreate->setDisplayName([self.alias UTF8String]);
209
210 if (auto profile = ProfileModel::instance().selectedProfile()) {
211 profile->person()->setFormattedName([self.alias UTF8String]);
212 QPixmap p;
213 auto smallImg = [NSImage imageResize:[photoView image] newSize:{100,100}];
214 if (p.loadFromData(QByteArray::fromNSData([smallImg TIFFRepresentation]))) {
215 profile->person()->setPhoto(QVariant(p));
Alexandre Lision849514f2016-10-25 14:07:51 -0400216 } else {
217 auto defaultAvatar = [NSImage imageResize:[NSImage imageNamed:@"default_user_icon"] newSize:{100,100}];
218 p.loadFromData(QByteArray::fromNSData([defaultAvatar TIFFRepresentation]));
219 profile->person()->setPhoto(QVariant(p));
Loïc Siretfcb4ca62016-09-21 17:12:09 -0400220 }
221 profile->save();
222 }
223
Loïc Siret3652cfb2016-10-27 10:12:07 -0400224 QModelIndex qIdx = AccountModel::instance().protocolModel()->selectionModel()->currentIndex();
Loïc Siretfcb4ca62016-09-21 17:12:09 -0400225
226 [self setCallback];
227
228 [self performSelector:@selector(saveAccount) withObject:nil afterDelay:1];
229 [self registerDefaultPreferences];
230}
231
232/**
233 * Set default values for preferences
234 */
235- (void)registerDefaultPreferences
236{
237 // enable AutoStartup
238 LSSharedFileListRef loginItemsRef = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
239 if (loginItemsRef == nil) return;
240 CFURLRef appUrl = (__bridge CFURLRef)[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
241 LSSharedFileListItemRef itemRef = LSSharedFileListInsertItemURL(loginItemsRef, kLSSharedFileListItemLast, NULL, NULL, appUrl, NULL, NULL);
242 if (itemRef) CFRelease(itemRef);
243
244 // enable Notifications
245 [[NSUserDefaults standardUserDefaults] setBool:YES forKey:Preferences::Notifications];
246}
247
248- (void)saveAccount
249{
250 accountToCreate->setArchivePassword(QString::fromNSString(passwordField.stringValue));
251 accountToCreate->setUpnpEnabled(YES); // Always active upnp
252 accountToCreate << Account::EditAction::SAVE;
253}
254
255- (void)setCallback
256{
257 errorTimer = [NSTimer scheduledTimerWithTimeInterval:30
258 target:self
259 selector:@selector(didCreateFailed) userInfo:nil
260 repeats:NO];
261 stateChanged = QObject::connect(&AccountModel::instance(),
262 &AccountModel::accountStateChanged,
263 [=](Account *account, const Account::RegistrationState state) {
264 switch(state){
265 case Account::RegistrationState::READY:
266 case Account::RegistrationState::TRYING:
267 case Account::RegistrationState::UNREGISTERED:{
268 accountToCreate<< Account::EditAction::RELOAD;
269 QObject::disconnect(stateChanged);
270 [self.delegate didCreateAccountWithSuccess:YES];
271 break;
272 }
273 case Account::RegistrationState::ERROR:
274 QObject::disconnect(stateChanged);
275 [self.delegate didCreateAccountWithSuccess:NO];
276 break;
277 case Account::RegistrationState::INITIALIZING:
278 case Account::RegistrationState::COUNT__:{
279 //DO Nothing
280 break;
281 }
282 }
283 });
284}
285
286- (void)didCreateFailed
287{
288 [self.delegate didCreateAccountWithSuccess:NO];
289}
290
291- (IBAction)cancel:(id)sender
292{
293 [self.delegate didCreateAccountWithSuccess:NO];
294}
295
296#pragma mark - NSOpenSavePanelDelegate delegate methods
297
298- (BOOL)panel:(id)sender validateURL:(NSURL *)url error:(NSError **)outError
299{
300 return YES;
301}
302
303-(void)controlTextDidChange:(NSNotification *)notif
304{
305 NSTextField* textField = [notif object];
306 // else it is NICKNAME_TAG field
307 NSString* alias = textField.stringValue;
308 if ([alias isEqualToString:@""]) {
309 alias = NSLocalizedString(@"Unknown", @"Name used when user leave field empty");
310 }
311 self.alias = alias;
312}
313
Loïc Siret3652cfb2016-10-27 10:12:07 -0400314+ (NSSet *)keyPathsForValuesAffectingIsPasswordValid
315{
316 return [NSSet setWithObjects:@"password", nil];
317}
318
319+ (NSSet *)keyPathsForValuesAffectingIsRepeatPasswordValid
320{
321 return [NSSet setWithObjects:@"password", @"repeatPassword", nil];
322}
323
Loïc Siretfcb4ca62016-09-21 17:12:09 -0400324@end