Commit 45f1f548 authored by Alexandre Lision's avatar Alexandre Lision Committed by Alexandre Lision

multi-device: add ability to export Ring account

This patch adds a Devices panel for Ring accounts.
This panel contains the list of devices linked with this Ring account,
and the possibility to export the Ring account on the DHT to setup a new
device

Change-Id: I7281b03d4376fbfc2d74c4e520b8cd0726b9166d
Tuleap: #959
parent f86edb3d
......@@ -140,6 +140,10 @@ SET(ringclient_CONTROLLERS
src/LoadingWCDelegate.h
src/AbstractLoadingWC.h
src/AbstractLoadingWC.mm
src/AccDevicesVC.mm
src/AccDevicesVC.h
src/ExportPasswordWC.mm
src/ExportPasswordWC.h
)
SET(ringclient_BACKENDS
......@@ -196,9 +200,11 @@ SET(ringclient_XIBS
RingWizard
CertificateWindow
PathPasswordWindow
ExportPasswordWindow
PersonLinker
Broker
Conversation)
Conversation
AccDevices)
# Icons
# This part tells CMake where to find and install the file itself
......
/*
* 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>
#import <account.h>
@interface AccDevicesVC : NSViewController
@property Account* account;
@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 "AccDevicesVC.h"
//Qt
#import <qitemselectionmodel.h>
//LRC
#import <accountmodel.h>
#import <ringdevicemodel.h>
#import <account.h>
#import "QNSTreeController.h"
#import "ExportPasswordWC.h"
@interface AccDevicesVC () <ExportPasswordDelegate>
@property QNSTreeController* devicesTreeController;
@property ExportPasswordWC* passwordWC;
@property (unsafe_unretained) IBOutlet NSOutlineView* deviceDetailsView;
@end
@implementation AccDevicesVC
@synthesize passwordWC;
NSInteger const TAG_NAME = 100;
NSInteger const TAG_DEVICE_IDS = 200;
- (void)awakeFromNib
{
NSLog(@"INIT Devices VC");
QObject::connect(AccountModel::instance().selectionModel(),
&QItemSelectionModel::currentChanged,
[=](const QModelIndex &current, const QModelIndex &previous) {
if(!current.isValid())
return;
[self loadAccount];
});
}
- (void)loadAccount
{
auto account = AccountModel::instance().selectedAccount();
self.devicesTreeController = [[QNSTreeController alloc] initWithQModel:(QAbstractItemModel*)account->ringDeviceModel()];
[self.devicesTreeController setAvoidsEmptySelection:NO];
[self.devicesTreeController setChildrenKeyPath:@"children"];
[self.deviceDetailsView bind:@"content" toObject:self.devicesTreeController withKeyPath:@"arrangedObjects" options:nil];
[self.deviceDetailsView bind:@"sortDescriptors" toObject:self.devicesTreeController withKeyPath:@"sortDescriptors" options:nil];
[self.deviceDetailsView bind:@"selectionIndexPaths" toObject:self.devicesTreeController withKeyPath:@"selectionIndexPaths" options:nil];
}
- (IBAction)startExportOnRing:(id)sender
{
NSButton* btbAdd = (NSButton *) sender;
self.account = AccountModel::instance().selectedAccount();
[self showPasswordPrompt];
}
#pragma mark - Export methods
- (void)showPasswordPrompt
{
auto account = AccountModel::instance().selectedAccount();
passwordWC = [[ExportPasswordWC alloc] initWithDelegate:self actionCode:1];
[passwordWC setAccount: account];
#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
}
#pragma mark - NSOutlineViewDelegate methods
- (BOOL)outlineView:(NSOutlineView *)outlineView shouldSelectItem:(id)item
{
return YES;
}
- (NSTableRowView *)outlineView:(NSOutlineView *)outlineView rowViewForItem:(id)item
{
return [outlineView makeViewWithIdentifier:@"HoverRowView" owner:nil];
}
- (NSView *)outlineView:(NSOutlineView *)outlineView viewForTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
NSTableView* result = [outlineView makeViewWithIdentifier:@"DeviceView" owner:self];
QModelIndex qIdx = [self.devicesTreeController toQIdx:((NSTreeNode*)item)];
if(!qIdx.isValid())
return result;
NSTextField* nameLabel = [result viewWithTag:TAG_NAME];
NSTextField* deviceIDLabel = [result viewWithTag:TAG_DEVICE_IDS];
auto account = AccountModel::instance().selectedAccount();
NSString* string = account->ringDeviceModel()->data(qIdx,Qt::DisplayRole).toString().toNSString();
[nameLabel setStringValue:account->alias().toNSString()];
[deviceIDLabel setStringValue:string];
return result;
}
@end
......@@ -36,6 +36,7 @@
#import "AccAdvancedVC.h"
#import "AccSecurityVC.h"
#import "AccRingVC.h"
#import "AccDevicesVC.h"
#import "PathPasswordWC.h"
@interface AccountsVC () <PathPasswordDelegate>
......@@ -48,6 +49,7 @@
@property (retain) IBOutlet NSTabViewItem *advancedTabItem;
@property (retain) IBOutlet NSTabViewItem *securityTabItem;
@property (retain) IBOutlet NSTabViewItem *ringTabItem;
@property (retain) IBOutlet NSTabViewItem *ringDevicesTabItem;
@property QNSTreeController *treeController;
@property (assign) IBOutlet NSOutlineView *accountsListView;
......@@ -55,6 +57,7 @@
@property (unsafe_unretained) IBOutlet NSButton* exportAccountButton;
@property AccRingVC* ringVC;
@property AccDevicesVC* devicesVC;
@property AccGeneralVC* generalVC;
@property AccMediaVC* audioVC;
@property AccAdvancedVC* advancedVC;
......@@ -71,6 +74,7 @@
@synthesize advancedTabItem;
@synthesize securityTabItem;
@synthesize ringTabItem;
@synthesize ringDevicesTabItem;
@synthesize accountsListView;
@synthesize accountDetailsView;
@synthesize treeController;
......@@ -153,6 +157,11 @@ NSInteger const TAG_TYPE = 400;
[[self.ringVC view] setFrame:[self.ringTabItem.view frame]];
[[self.ringVC view] setBounds:[self.ringTabItem.view bounds]];
[self.ringTabItem setView:self.ringVC.view];
self.devicesVC = [[AccDevicesVC alloc] initWithNibName:@"AccDevices" bundle:nil];
[[self.devicesVC view] setFrame:[self.ringDevicesTabItem.view frame]];
[[self.devicesVC view] setBounds:[self.ringDevicesTabItem.view bounds]];
[self.ringDevicesTabItem setView:self.devicesVC.view];
}
- (IBAction)addAccount:(id)sender {
......@@ -193,8 +202,9 @@ NSInteger const TAG_TYPE = 400;
}
[configPanels insertTabViewItem:ringTabItem atIndex:0];
[configPanels insertTabViewItem:mediaTabItem atIndex:1];
[configPanels insertTabViewItem:advancedTabItem atIndex:2];
[configPanels insertTabViewItem:ringDevicesTabItem atIndex:1];
[configPanels insertTabViewItem:mediaTabItem atIndex:2];
[configPanels insertTabViewItem:advancedTabItem atIndex:3];
}
- (IBAction)exportAccount:(id)sender
......
/*
* 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>
#import <account.h>
#import "LoadingWCDelegate.h"
#import "AbstractLoadingWC.h"
@protocol ExportPasswordDelegate <LoadingWCDelegate>
@optional
- (void)didCompleteWithPin:(NSString*) path Password:(NSString*) password;
- (void)didStartWithPassword:(NSString*) password;
@end
@interface ExportPasswordWC : AbstractLoadingWC
/**
* 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;
@property (assign) Account* account;
@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 "ExportPasswordWC.h"
//LRC
#import <account.h>
//Ring
#import "views/ITProgressIndicator.h"
@interface ExportPasswordWC() <NSTextFieldDelegate>{
__unsafe_unretained IBOutlet NSSecureTextField* passwordField;
__unsafe_unretained IBOutlet NSTextField* resultField;
__unsafe_unretained IBOutlet NSTextField* errorField;
__unsafe_unretained IBOutlet ITProgressIndicator* progressIndicator;
}
@end
@implementation ExportPasswordWC {
struct {
unsigned int didStart:1;
unsigned int didComplete:1;
} delegateRespondsTo;
}
@synthesize account;
QMetaObject::Connection accountConnection;
#pragma mark - Initialize
- (id)initWithDelegate:(id <ExportPasswordDelegate>) del actionCode:(NSInteger) code
{
return [super initWithWindowNibName:@"ExportPasswordWindow" delegate:del actionCode:code];
}
- (void)windowDidLoad
{
[super windowDidLoad];
}
- (void)setDelegate:(id <ExportPasswordDelegate>)aDelegate
{
if (super.delegate != aDelegate) {
[super setDelegate: aDelegate];
delegateRespondsTo.didStart = [aDelegate respondsToSelector:@selector(didStartWithPassword:)];
delegateRespondsTo.didComplete = [aDelegate respondsToSelector:@selector(didCompleteWithPin:Password:)];
}
}
- (void)showError:(NSString*) errorMessage
{
[errorField setStringValue:errorMessage];
[super showError];
}
- (void)showLoading
{
[progressIndicator setNumberOfLines:30];
[progressIndicator setWidthOfLine:2];
[progressIndicator setLengthOfLine:5];
[progressIndicator setInnerMargin:20];
[super showLoading];
}
#pragma mark - Events Handlers
- (IBAction)completeAction:(id)sender
{
// Check to avoid exporting an old account (not supported by daemon)
if (account->needsMigration()) {
[self showError:NSLocalizedString(@"You have to migrate your account before exporting", @"Error shown to user")];
} else {
NSString* password = passwordField.stringValue;
[self showLoading];
QObject::disconnect(accountConnection);
accountConnection = QObject::connect(account,
&Account::exportOnRingEnded,
[=](Account::ExportOnRingStatus status,const QString &pin) {
NSLog(@"Export ended!");
switch (status) {
case Account::ExportOnRingStatus::SUCCESS:{
NSString *nsPin = pin.toNSString();
NSLog(@"Export ended with Success, pin is %@",nsPin);
[resultField setAttributedStringValue:[self formatPinMessage:nsPin]];
[self showFinal];
}
break;
case Account::ExportOnRingStatus::WRONG_PASSWORD:{
NSLog(@"Export ended with Wrong Password");
[self showError:NSLocalizedString(@"Export ended with Wrong Password", @"Error shown to the user" )];
}
break;
case Account::ExportOnRingStatus::NETWORK_ERROR:{
NSLog(@"Export ended with NetworkError!");
[self showError:NSLocalizedString(@"A network error occured during the export", @"Error shown to the user" )];
}
break;
default:{
NSLog(@"Export ended with Unknown status!");
[self showError:NSLocalizedString(@"An error occured during the export", @"Error shown to the user" )];
}
break;
}
});
account->exportOnRing(QString::fromNSString(password));
}
}
//TODO: Move String formatting to a dedicated Utility Classes
- (NSAttributedString *)formatPinMessage:(NSString*) pin
{
NSMutableAttributedString* hereIsThePin = [[NSMutableAttributedString alloc] initWithString:NSLocalizedString(@"Your generated pin:","Title shown to user to concat with Pin")];
NSMutableAttributedString* thePin = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@" %@\n", pin]];
[thePin beginEditing];
NSRange range = NSMakeRange(0, [thePin length]);
[thePin addAttribute:NSFontAttributeName value:[NSFont fontWithName:@"Helvetica-Bold" size:12.0] range:range];
[hereIsThePin appendAttributedString:thePin];
NSMutableAttributedString* infos = [[NSMutableAttributedString alloc] initWithString:NSLocalizedString(@"This pin and the account password should be entered on your new device within 5 minutes. On most client, this is done from \"Existing Ring account\" menu. You may generate a new pin at any moment.","Infos on how to use the pin")];
[hereIsThePin appendAttributedString:infos];
return hereIsThePin;
}
@end
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9532" systemVersion="15D21" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="15G31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9532"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="AccountsVC">
......@@ -14,6 +14,7 @@
<outlet property="generalTabItem" destination="tPR-Ac-N5Y" id="39S-pz-1Xs"/>
<outlet property="mediaTabItem" destination="lxr-my-vH8" id="BhJ-cS-yVi"/>
<outlet property="protocolList" destination="rZv-qd-BGe" id="yU0-6C-Vt1"/>
<outlet property="ringDevicesTabItem" destination="cT1-A5-rbj" id="Sql-e4-mD8"/>
<outlet property="ringTabItem" destination="1HC-kF-Jun" id="FJZ-2g-Y1i"/>
<outlet property="securityTabItem" destination="Vp5-yV-ScC" id="FDx-0T-3t9"/>
<outlet property="view" destination="Hz6-mo-xeY" id="eBn-rZ-84z"/>
......@@ -59,6 +60,12 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</view>
</tabViewItem>
<tabViewItem label="Devices" identifier="" id="cT1-A5-rbj">
<view key="view" id="Ey1-5D-Zou">
<rect key="frame" x="10" y="33" width="576" height="565"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</view>
</tabViewItem>
</tabViewItems>
<connections>
<outlet property="delegate" destination="-2" id="hfK-WK-DJT"/>
......@@ -71,7 +78,7 @@
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<outlineView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" autosaveColumns="NO" rowHeight="55" rowSizeStyle="automatic" viewBased="YES" outlineTableColumn="eOe-f3-q88" id="jXv-6I-P9R" customClass="RingOutlineView">
<rect key="frame" x="0.0" y="0.0" width="250" height="500"/>
<rect key="frame" x="0.0" y="0.0" width="250" height="0.0"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
......
/* Class = "BindingConnection"; ibShadowedIsNilPlaceholder = "Password..."; ObjectID = "5cX-yS-4Sl"; */
"5cX-yS-4Sl.ibShadowedIsNilPlaceholder" = "Password...";
/* Class = "BindingConnection"; ibShadowedMultipleValuesPlaceholder = "Password..."; ObjectID = "5cX-yS-4Sl"; */
"5cX-yS-4Sl.ibShadowedMultipleValuesPlaceholder" = "Password...";
/* Class = "BindingConnection"; ibShadowedNoSelectionPlaceholder = "Password..."; ObjectID = "5cX-yS-4Sl"; */
"5cX-yS-4Sl.ibShadowedNoSelectionPlaceholder" = "Password...";
/* Class = "BindingConnection"; ibShadowedNotApplicablePlaceholder = "Password..."; ObjectID = "5cX-yS-4Sl"; */
"5cX-yS-4Sl.ibShadowedNotApplicablePlaceholder" = "Password...";
/* Class = "NSTextFieldCell"; title = "To add a new device to your Ring account, you export your account on the Ring. This will generate a pin that must be entered on your new device within 5 minutes of its generation."; ObjectID = "859-10-HEb"; */
"859-10-HEb.title" = "To add a new device to your Ring account, you export your account on the Ring. This will generate a pin that must be entered on your new device within 5 minutes of its generation.";
/* Class = "NSWindow"; title = "Window"; ObjectID = "QvC-M9-y7g"; */
"QvC-M9-y7g.title" = "Window";
/* Class = "NSTextFieldCell"; placeholderString = "error label"; ObjectID = "Ua9-fG-r6k"; */
"Ua9-fG-r6k.placeholderString" = "error label";
/* Class = "NSButtonCell"; title = "OK"; ObjectID = "VN1-A3-RIh"; */
"VN1-A3-RIh.title" = "OK";
/* Class = "NSButtonCell"; title = "Cancel"; ObjectID = "cSU-aD-OwX"; */
"cSU-aD-OwX.title" = "Cancel";
/* Class = "NSButtonCell"; title = "OK"; ObjectID = "cre-OL-lZy"; */
"cre-OL-lZy.title" = "OK";
/* Class = "NSTextFieldCell"; placeholderString = "error label"; ObjectID = "e7n-Ev-bK7"; */
"e7n-Ev-bK7.placeholderString" = "error label";
/* Class = "NSButtonCell"; title = "OK"; ObjectID = "rW5-Il-5YD"; */
"rW5-Il-5YD.title" = "OK";
/* Class = "NSTextFieldCell"; title = "Password"; ObjectID = "vwh-K9-3O9"; */
"vwh-K9-3O9.title" = "Password";
/* Class = "NSTextFieldCell"; title = "Adding new device"; ObjectID = "wmv-sA-Mlh"; */
"wmv-sA-Mlh.title" = "Adding new device";
/* Class = "NSSecureTextFieldCell"; placeholderString = "Password..."; ObjectID = "xqz-Uz-hqU"; */
"xqz-Uz-hqU.placeholderString" = "Password...";
This diff is collapsed.
/* Class = "BindingConnection"; ibShadowedIsNilPlaceholder = "Password..."; ObjectID = "5cX-yS-4Sl"; */
"5cX-yS-4Sl.ibShadowedIsNilPlaceholder" = "Password...";
/* Class = "BindingConnection"; ibShadowedMultipleValuesPlaceholder = "Password..."; ObjectID = "5cX-yS-4Sl"; */
"5cX-yS-4Sl.ibShadowedMultipleValuesPlaceholder" = "Password...";
/* Class = "BindingConnection"; ibShadowedNoSelectionPlaceholder = "Password..."; ObjectID = "5cX-yS-4Sl"; */
"5cX-yS-4Sl.ibShadowedNoSelectionPlaceholder" = "Password...";
/* Class = "BindingConnection"; ibShadowedNotApplicablePlaceholder = "Password..."; ObjectID = "5cX-yS-4Sl"; */
"5cX-yS-4Sl.ibShadowedNotApplicablePlaceholder" = "Password...";
/* Class = "NSTextFieldCell"; title = "To add a new device to your Ring account, you export your account on the Ring. This will generate a pin that must be entered on your new device within 5 minutes of its generation."; ObjectID = "859-10-HEb"; */
"859-10-HEb.title" = "To add a new device to your Ring account, you export your account on the Ring. This will generate a pin that must be entered on your new device within 5 minutes of its generation.";
/* Class = "NSWindow"; title = "Window"; ObjectID = "QvC-M9-y7g"; */
"QvC-M9-y7g.title" = "Window";
/* Class = "NSButtonCell"; title = "OK"; ObjectID = "VN1-A3-RIh"; */
"VN1-A3-RIh.title" = "OK";
/* Class = "NSButtonCell"; title = "Cancel"; ObjectID = "cSU-aD-OwX"; */
"cSU-aD-OwX.title" = "Cancel";
/* Class = "NSTextFieldCell"; placeholderString = "error label"; ObjectID = "e7n-Ev-bK7"; */
"e7n-Ev-bK7.placeholderString" = "error label";
/* Class = "NSButtonCell"; title = "OK"; ObjectID = "rW5-Il-5YD"; */
"rW5-Il-5YD.title" = "OK";
/* Class = "NSTextFieldCell"; title = "Password"; ObjectID = "vwh-K9-3O9"; */
"vwh-K9-3O9.title" = "Password";
/* Class = "NSTextFieldCell"; title = "Adding new device"; ObjectID = "wmv-sA-Mlh"; */
"wmv-sA-Mlh.title" = "Adding new device";
/* Class = "NSSecureTextFieldCell"; placeholderString = "Password..."; ObjectID = "xqz-Uz-hqU"; */
"xqz-Uz-hqU.placeholderString" = "Password...";
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