Commit c1f96660 authored by Alexandre Lision's avatar Alexandre Lision

accounts: import/export

Allow import/export of accounts in the preferences.
PathPasswordWC is a generic modal window asking for a path and a
password. It is used for both export and import UI.

Tuleap: #335
Change-Id: Ic478140e64b51d10672ef466509326fc17be2712
parent 51ff149a
......@@ -96,6 +96,8 @@ SET(ringclient_CONTROLLERS
src/AccSecurityVC.h
src/CertificateWC.mm
src/CertificateWC.h
src/PathPasswordWC.mm
src/PathPasswordWC.h
src/AudioPrefsVC.mm
src/AudioPrefsVC.h
src/AccountsVC.mm
......@@ -169,6 +171,7 @@ SET(ringclient_XIBS
PreferencesWindow
RingWizard
CertificateWindow
PathPasswordWindow
PersonLinker
Broker
Conversation)
......
......@@ -16,62 +16,47 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#define ALIAS_TAG 0
#define HOSTNAME_TAG 1
#define USERNAME_TAG 2
#define PASSWORD_TAG 3
#define USERAGENT_TAG 4
#import "AccGeneralVC.h"
#import <accountmodel.h>
#import <protocolmodel.h>
#import <qitemselectionmodel.h>
@interface AccGeneralVC ()
@property (assign) IBOutlet NSView *boxingParameters;
@property (assign) IBOutlet NSView *boxingCommon;
@interface AccGeneralVC () {
@property (assign) IBOutlet NSTextField *aliasTextField;
@property (assign) IBOutlet NSTextField *typeLabel;
__unsafe_unretained IBOutlet NSTextField *aliasTextField;
@property (assign) IBOutlet NSTextField *serverHostTextField;
@property (assign) IBOutlet NSTextField *usernameTextField;
@property (assign) IBOutlet NSSecureTextField *passwordTextField;
@property (strong) NSTextField *clearTextField;
@property (assign) IBOutlet NSButton *upnpButton;
@property (assign) IBOutlet NSButton *autoAnswerButton;
@property (assign) IBOutlet NSButton *userAgentButton;
@property (assign) IBOutlet NSTextField *userAgentTextField;
__unsafe_unretained IBOutlet NSTextField *serverHostTextField;
__unsafe_unretained IBOutlet NSTextField *usernameTextField;
__unsafe_unretained IBOutlet NSSecureTextField *passwordTextField;
NSTextField *clearTextField;
__unsafe_unretained IBOutlet NSButton *upnpButton;
__unsafe_unretained IBOutlet NSButton *autoAnswerButton;
__unsafe_unretained IBOutlet NSButton *userAgentButton;
__unsafe_unretained IBOutlet NSTextField *userAgentTextField;
}
@end
@implementation AccGeneralVC
@synthesize typeLabel;
@synthesize boxingParameters;
@synthesize boxingCommon;
@synthesize aliasTextField;
@synthesize serverHostTextField;
@synthesize usernameTextField;
@synthesize passwordTextField;
@synthesize clearTextField;
@synthesize upnpButton;
@synthesize autoAnswerButton;
@synthesize userAgentButton;
@synthesize userAgentTextField;
//Tags for views
typedef NS_ENUM(NSInteger, TagViews) {
ALIAS = 0,
HOSTNAME = 1,
USERNAME = 2,
PASSWORD = 3,
USERAGENT = 4,
};
- (void)awakeFromNib
{
NSLog(@"INIT General VC");
[aliasTextField setTag:ALIAS_TAG];
[serverHostTextField setTag:HOSTNAME_TAG];
[usernameTextField setTag:USERNAME_TAG];
[passwordTextField setTag:PASSWORD_TAG];
[userAgentTextField setTag:USERAGENT_TAG];
[aliasTextField setTag:TagViews::ALIAS];
[serverHostTextField setTag:TagViews::HOSTNAME];
[usernameTextField setTag:TagViews::USERNAME];
[passwordTextField setTag:TagViews::PASSWORD];
[userAgentTextField setTag:TagViews::USERAGENT];
QObject::connect(AccountModel::instance().selectionModel(),
&QItemSelectionModel::currentChanged,
......@@ -82,61 +67,38 @@
});
}
- (Account*) currentAccount
{
auto accIdx = AccountModel::instance().selectionModel()->currentIndex();
return AccountModel::instance().getAccountByModelIndex(accIdx);
}
- (IBAction)toggleUpnp:(NSButton *)sender {
[self currentAccount]->setUpnpEnabled([sender state] == NSOnState);
AccountModel::instance().selectedAccount()->setUpnpEnabled([sender state] == NSOnState);
}
- (IBAction)toggleAutoAnswer:(NSButton *)sender {
[self currentAccount]->setAutoAnswer([sender state] == NSOnState);
AccountModel::instance().selectedAccount()->setAutoAnswer([sender state] == NSOnState);
}
- (IBAction)toggleCustomAgent:(NSButton *)sender {
[self.userAgentTextField setEnabled:[sender state] == NSOnState];
[self currentAccount]->setHasCustomUserAgent([sender state] == NSOnState);
[userAgentTextField setEnabled:[sender state] == NSOnState];
AccountModel::instance().selectedAccount()->setHasCustomUserAgent([sender state] == NSOnState);
}
- (void)loadAccount
{
auto account = [self currentAccount];
[boxingParameters.subviews setValue:@NO forKeyPath:@"hidden"];
[self.aliasTextField setStringValue:account->alias().toNSString()];
[self.serverHostTextField setStringValue:account->hostname().toNSString()];
[self.usernameTextField setStringValue:account->username().toNSString()];
[self.passwordTextField setStringValue:account->password().toNSString()];
[self.clearTextField setStringValue:account->password().toNSString()];
switch (account->protocol()) {
case Account::Protocol::SIP:
[self.typeLabel setStringValue:@"SIP"];
break;
case Account::Protocol::IAX:
[self.typeLabel setStringValue:@"IAX"];
break;
case Account::Protocol::RING:
[self.typeLabel setStringValue:@"RING"];
break;
default:
break;
}
[upnpButton setState:[self currentAccount]->isUpnpEnabled()];
[userAgentButton setState:[self currentAccount]->hasCustomUserAgent()];
[userAgentTextField setEnabled:[self currentAccount]->hasCustomUserAgent()];
[self.autoAnswerButton setState:[self currentAccount]->isAutoAnswer()];
[self.userAgentTextField setStringValue:account->userAgent().toNSString()];
auto account = AccountModel::instance().selectedAccount();
[aliasTextField setStringValue:account->alias().toNSString()];
[serverHostTextField setStringValue:account->hostname().toNSString()];
[usernameTextField setStringValue:account->username().toNSString()];
[passwordTextField setStringValue:account->password().toNSString()];
[clearTextField setStringValue:account->password().toNSString()];
[upnpButton setState:AccountModel::instance().selectedAccount()->isUpnpEnabled()];
[userAgentButton setState:AccountModel::instance().selectedAccount()->hasCustomUserAgent()];
[userAgentTextField setEnabled:AccountModel::instance().selectedAccount()->hasCustomUserAgent()];
[autoAnswerButton setState:AccountModel::instance().selectedAccount()->isAutoAnswer()];
[userAgentTextField setStringValue:account->userAgent().toNSString()];
}
- (IBAction)tryRegistration:(id)sender {
[self currentAccount] << Account::EditAction::SAVE;
AccountModel::instance().selectedAccount() << Account::EditAction::SAVE;
}
- (IBAction)showPassword:(NSButton *)sender {
......@@ -147,7 +109,7 @@
[clearTextField setBounds:passwordTextField.bounds];
[clearTextField setStringValue:passwordTextField.stringValue];
[clearTextField becomeFirstResponder];
[boxingParameters addSubview:clearTextField];
[self.view addSubview:clearTextField];
[passwordTextField setHidden:YES];
} else {
[passwordTextField setStringValue:clearTextField.stringValue];
......@@ -177,21 +139,21 @@
NSTextField *textField = [notif object];
switch ([textField tag]) {
case ALIAS_TAG:
[self currentAccount]->setAlias([[textField stringValue] UTF8String]);
[self currentAccount]->setDisplayName([[textField stringValue] UTF8String]);
case TagViews::ALIAS:
AccountModel::instance().selectedAccount()->setAlias([[textField stringValue] UTF8String]);
AccountModel::instance().selectedAccount()->setDisplayName([[textField stringValue] UTF8String]);
break;
case HOSTNAME_TAG:
[self currentAccount]->setHostname([[textField stringValue] UTF8String]);
case TagViews::HOSTNAME:
AccountModel::instance().selectedAccount()->setHostname([[textField stringValue] UTF8String]);
break;
case USERNAME_TAG:
[self currentAccount]->setUsername([[textField stringValue] UTF8String]);
case TagViews::USERNAME:
AccountModel::instance().selectedAccount()->setUsername([[textField stringValue] UTF8String]);
break;
case PASSWORD_TAG:
[self currentAccount]->setPassword([[textField stringValue] UTF8String]);
case TagViews::PASSWORD:
AccountModel::instance().selectedAccount()->setPassword([[textField stringValue] UTF8String]);
break;
case USERAGENT_TAG:
[self currentAccount]->setUserAgent([[textField stringValue] UTF8String]);
case TagViews::USERAGENT:
AccountModel::instance().selectedAccount()->setUserAgent([[textField stringValue] UTF8String]);
break;
default:
break;
......
......@@ -36,6 +36,7 @@
#import "AccAdvancedVC.h"
#import "AccSecurityVC.h"
#import "AccRingVC.h"
#import "PathPasswordWC.h"
// We disabled IAX protocol for now, so don't show it to the user
class ActiveProtocolModel : public QSortFilterProxyModel
......@@ -51,7 +52,8 @@ public:
}
};
@interface AccountsVC ()
@interface AccountsVC () <PathPasswordDelegate>
@property (assign) IBOutlet NSPopUpButton *protocolList;
@property (assign) IBOutlet NSTabView *configPanels;
......@@ -65,12 +67,14 @@ public:
@property ActiveProtocolModel* proxyProtocolModel;
@property (assign) IBOutlet NSOutlineView *accountsListView;
@property (assign) IBOutlet NSTabView *accountDetailsView;
@property (unsafe_unretained) IBOutlet NSButton* exportAccountButton;
@property AccRingVC* ringVC;
@property AccGeneralVC* generalVC;
@property AccMediaVC* audioVC;
@property AccAdvancedVC* advancedVC;
@property AccSecurityVC* securityVC;
@property PathPasswordWC* passwordWC;
@end
......@@ -86,12 +90,18 @@ public:
@synthesize accountDetailsView;
@synthesize treeController;
@synthesize proxyProtocolModel;
@synthesize passwordWC;
NSInteger const TAG_CHECK = 100;
NSInteger const TAG_NAME = 200;
NSInteger const TAG_STATUS = 300;
NSInteger const TAG_TYPE = 400;
typedef NS_ENUM(NSUInteger, Action) {
ACTION_EXPORT = 0,
ACTION_IMPORT = 1,
};
- (void)awakeFromNib
{
treeController = [[QNSTreeController alloc] initWithQModel:&AccountModel::instance()];
......@@ -233,6 +243,34 @@ NSInteger const TAG_TYPE = 400;
[configPanels insertTabViewItem:advancedTabItem atIndex:2];
}
- (IBAction)exportAccount:(id)sender
{
passwordWC = [[PathPasswordWC alloc] initWithDelegate:self actionCode:Action::ACTION_EXPORT];
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_9
[self.view.window beginSheet:passwordWC.window completionHandler:nil];
#else
[NSApp beginSheet: passwordWC.window
modalForWindow: self.view.window
modalDelegate: self
didEndSelector: nil
contextInfo: nil];
#endif
[passwordWC setAllowFileSelection:NO];
}
- (IBAction)importAccount:(id)sender
{
passwordWC = [[PathPasswordWC alloc] initWithDelegate:self actionCode:Action::ACTION_IMPORT];
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_9
[self.view.window beginSheet:passwordWC.window completionHandler:nil];
#else
[NSApp beginSheet: passwordWC.window
modalForWindow: self.view.window
modalDelegate: self
didEndSelector: nil
contextInfo: nil];
#endif
}
- (IBAction)toggleAccount:(NSButton*)sender {
NSInteger row = [accountsListView rowForView:sender];
......@@ -312,6 +350,7 @@ NSInteger const TAG_TYPE = 400;
- (void)outlineViewSelectionDidChange:(NSNotification *)notification
{
// ask the tree controller for the current selection
[self.exportAccountButton setEnabled:[[treeController selectedNodes] count] > 0];
if([[treeController selectedNodes] count] > 0) {
auto qIdx = [treeController toQIdx:[treeController selectedNodes][0]];
//Update details view
......@@ -338,6 +377,67 @@ NSInteger const TAG_TYPE = 400;
}
}
-(void) didCompleteWithPath:(NSURL*) path Password:(NSString*) password ActionCode:(NSInteger)requestCode
{
switch (requestCode) {
case Action::ACTION_EXPORT:
if(treeController.selectedNodes.count > 0) {
QStringList accounts;
for (id item : [treeController selectedNodes]) {
QModelIndex accIdx = [treeController toQIdx:item];
accounts << AccountModel::instance().getAccountByModelIndex(accIdx)->id();
}
auto finalURL = [path URLByAppendingPathComponent:@"accounts.ring"];
[passwordWC showLoading];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
int result = AccountModel::instance().exportAccounts(accounts, finalURL.path.UTF8String, password.UTF8String);
switch (result) {
case 0:
[[NSWorkspace sharedWorkspace] selectFile:finalURL.path inFileViewerRootedAtPath:@""];
[passwordWC close];
break;
default:
[passwordWC showError:NSLocalizedString(@"An error occured during the export", @"Error shown to the user" )];
break;
}
});
}
break;
case Action::ACTION_IMPORT: {
[passwordWC showLoading];
SEL sel = @selector(importAccountsWithPath:andPassword:);
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:sel]];
[inv setSelector:sel];
[inv setTarget:self];
//arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocation
[inv setArgument:&path atIndex:2];
[inv setArgument:&password atIndex:3];
// Schedule import for next iteration of event loop in order to let us start the loading anim
[inv performSelector:@selector(invoke) withObject:nil afterDelay:0];
}
break;
default:
NSLog(@"Unrecognized action %d", requestCode);
break;
}
}
- (void) importAccountsWithPath:(NSURL*) path andPassword:(NSString*) password
{
int result = AccountModel::instance().importAccounts(path.path.UTF8String, password.UTF8String);
switch (result) {
case 0:
[passwordWC close];
break;
default:
[passwordWC showError:NSLocalizedString(@"An error occured during the import", @"Error shown to the user" )];
break;
}
}
#pragma mark - NSMenuDelegate methods
- (BOOL)menu:(NSMenu *)menu updateItem:(NSMenuItem *)item atIndex:(NSInteger)index shouldCancel:(BOOL)shouldCancel
......
/*
* Copyright (C) 2016 Savoir-faire Linux Inc.
* Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#import <Cocoa/Cocoa.h>
@protocol PathPasswordDelegate <NSObject>
@optional
-(void) didCompleteWithPath:(NSURL*) path Password:(NSString*) password;
-(void) didCompleteWithPath:(NSURL*) path Password:(NSString*) password ActionCode:(NSInteger) requestCode;
@end
@interface PathPasswordWC : NSWindowController
/*
* Delegate to inform about completion of the linking process between
* a ContactMethod and a Person.
*/
@property (nonatomic) id <PathPasswordDelegate> delegate;
/*
* caller specific code to identify ongoing action
*/
@property (nonatomic) NSInteger actionCode;
/*
* Custom init
*/
- (id)initWithDelegate:(id <PathPasswordDelegate>) del actionCode:(NSInteger) code;
/**
* password string contained in passwordField.
* This is a KVO method to bind the text with the OK Button
* if password.length is > 0, button is enabled, otherwise disabled
*/
@property (retain) NSString* password;
/**
* Allow the NSPathControl of this window to select files or not
*/
@property (nonatomic) BOOL allowFileSelection;
/*
* Show progress during action completion
*/
- (void)showLoading;
/*
* Display error message to the user
*/
- (void)showError:(NSString*) error;
@end
/*
* Copyright (C) 2016 Savoir-faire Linux Inc.
* Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#import "PathPasswordWC.h"
#import "views/ITProgressIndicator.h"
@interface PathPasswordWC() <NSTextFieldDelegate>{
__unsafe_unretained IBOutlet NSView* errorContainer;
__unsafe_unretained IBOutlet NSTextField* errorLabel;
__unsafe_unretained IBOutlet ITProgressIndicator* progressView;
__unsafe_unretained IBOutlet NSView* pathPasswordContainer;
__unsafe_unretained IBOutlet NSSecureTextField* passwordField;
__unsafe_unretained IBOutlet NSPathControl* path;
}
@end
@implementation PathPasswordWC {
struct {
unsigned int didComplete:1;
unsigned int didCompleteWithActionCode:1;
} delegateRespondsTo;
}
- (id)initWithDelegate:(id <PathPasswordDelegate>) del actionCode:(NSInteger) code
{
if ((self = [super initWithWindowNibName:@"PathPasswordWindow"]) != nil) {
[self setDelegate:del];
self.actionCode = code;
}
return self;
}
- (void)windowDidLoad
{
[super windowDidLoad];
[path setURL: [NSURL fileURLWithPath:NSHomeDirectory()]];
[progressView setNumberOfLines:30];
[progressView setWidthOfLine:2];
[progressView setLengthOfLine:5];
[progressView setInnerMargin:20];
[progressView setHidden:YES];
}
- (void)setDelegate:(id <PathPasswordDelegate>)aDelegate
{
if (self.delegate != aDelegate) {
_delegate = aDelegate;
delegateRespondsTo.didComplete = [self.delegate respondsToSelector:@selector(didCompleteWithPath:Password:)];
delegateRespondsTo.didCompleteWithActionCode = [self.delegate respondsToSelector:@selector(didCompleteWithPath:Password:ActionCode:)];
}
}
- (void) setAllowFileSelection:(BOOL) b
{
_allowFileSelection = b;
[path setAllowedTypes:_allowFileSelection ? nil : [NSArray arrayWithObject:@"public.folder"]];
}
- (IBAction) cancelPressed:(id)sender
{
[NSApp endSheet:self.window];
[self.window orderOut:self];
}
- (IBAction)completeAction:(id)sender
{
if (delegateRespondsTo.didComplete)
[self.delegate didCompleteWithPath:path.URL Password:passwordField.stringValue];
else if (delegateRespondsTo.didCompleteWithActionCode)
[self.delegate didCompleteWithPath:path.URL Password:passwordField.stringValue ActionCode:self.actionCode];
}
- (void)showLoading
{
[progressView setHidden:NO];
[pathPasswordContainer setHidden:YES];
[errorContainer setHidden:YES];
[progressView setAnimates:YES];
}
- (void)showError:(NSString*) error
{
[progressView setHidden:YES];
[pathPasswordContainer setHidden:YES];
[errorContainer setHidden:NO];
[errorLabel setStringValue:error];
}
@end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment