mirror of
https://github.com/moparisthebest/mail
synced 2024-11-25 18:32:20 -05:00
[WO-233] Implement opt-in for terms of service
This commit is contained in:
parent
680ed6e0c3
commit
becff37b4b
10
README.md
10
README.md
@ -59,8 +59,8 @@ Then visit [http://localhost:8580/dist/chrome.html#/desktop](http://localhost:85
|
||||
|
||||
We work together with existing open source projects wherever possible and contribute any changes we make back upstream. Many of theses libraries are licensed under an open source license. Here are some of them:
|
||||
|
||||
* [OpenPGP.js](http://openpgpjs.org): An implementation of OpenPGP in Javascript
|
||||
* [Inbox](https://github.com/andris9/inbox): Simple IMAP client for node.js
|
||||
* [Nodemailer](http://www.nodemailer.com): SMTP client for node.js
|
||||
* [Forge](https://github.com/digitalbazaar/forge): An implementation of TLS in Javascript
|
||||
* [node-shims](https://github.com/whiteout-io/node-shims): Shims for wrapping node's net/tls (TCP socket) APIs around chrome.socket
|
||||
* [OpenPGP.js](http://openpgpjs.org) (LGPL license): An implementation of OpenPGP in Javascript
|
||||
* [Inbox](https://github.com/andris9/inbox) (MIT license): Simple IMAP client for node.js
|
||||
* [Nodemailer](http://www.nodemailer.com) (MIT license): SMTP client for node.js
|
||||
* [Forge](https://github.com/digitalbazaar/forge) (BSD license): An implementation of TLS in Javascript
|
||||
* [node-shims](https://github.com/whiteout-io/node-shims) (MIT license): Shims for wrapping node's net/tls (TCP socket) APIs around chrome.socket
|
||||
|
@ -3,7 +3,7 @@ define(function(require) {
|
||||
|
||||
var _ = require('underscore'),
|
||||
app = {},
|
||||
cloudUrl, clientId;
|
||||
appVersion, cloudUrl, clientId;
|
||||
|
||||
// parse manifest to get configurations for current runtime
|
||||
try {
|
||||
@ -16,6 +16,8 @@ define(function(require) {
|
||||
cloudUrl = cloudUrl.substring(0, cloudUrl.length - 1);
|
||||
// get client ID for OAuth requests
|
||||
clientId = manifest.oauth2.client_id;
|
||||
// get the app version
|
||||
appVersion = manifest.version;
|
||||
} catch (e) {}
|
||||
|
||||
/**
|
||||
@ -44,7 +46,8 @@ define(function(require) {
|
||||
iconPath: '/img/icon.png',
|
||||
verificationUrl: '/verify/',
|
||||
verificationUuidLength: 36,
|
||||
dbVersion: 1
|
||||
dbVersion: 1,
|
||||
appVersion: appVersion
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -9,6 +9,7 @@ requirejs([
|
||||
'js/controller/account',
|
||||
'js/controller/set-passphrase',
|
||||
'js/controller/contacts',
|
||||
'js/controller/about',
|
||||
'js/controller/login',
|
||||
'js/controller/login-initial',
|
||||
'js/controller/login-new-device',
|
||||
@ -29,6 +30,7 @@ requirejs([
|
||||
AccountCtrl,
|
||||
SetPassphraseCtrl,
|
||||
ContactsCtrl,
|
||||
AboutCtrl,
|
||||
LoginCtrl,
|
||||
LoginInitialCtrl,
|
||||
LoginNewDeviceCtrl,
|
||||
@ -96,6 +98,7 @@ requirejs([
|
||||
app.controller('AccountCtrl', AccountCtrl);
|
||||
app.controller('SetPassphraseCtrl', SetPassphraseCtrl);
|
||||
app.controller('ContactsCtrl', ContactsCtrl);
|
||||
app.controller('AboutCtrl', AboutCtrl);
|
||||
app.controller('DialogCtrl', DialogCtrl);
|
||||
app.controller('PopoverCtrl', PopoverCtrl);
|
||||
|
||||
|
32
src/js/controller/about.js
Normal file
32
src/js/controller/about.js
Normal file
@ -0,0 +1,32 @@
|
||||
define(function(require) {
|
||||
'use strict';
|
||||
|
||||
var cfg = require('js/app-config').config;
|
||||
|
||||
//
|
||||
// Controller
|
||||
//
|
||||
|
||||
var AboutCtrl = function($scope) {
|
||||
|
||||
$scope.state.about = {
|
||||
open: false,
|
||||
toggle: function(to) {
|
||||
this.open = to;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// scope variables
|
||||
//
|
||||
|
||||
$scope.version = cfg.appVersion;
|
||||
$scope.date = new Date();
|
||||
|
||||
//
|
||||
// scope functions
|
||||
//
|
||||
};
|
||||
|
||||
return AboutCtrl;
|
||||
});
|
@ -6,7 +6,7 @@ define(function(require) {
|
||||
|
||||
var LoginInitialCtrl = function($scope, $location) {
|
||||
var emailDao = appController._emailDao,
|
||||
states;
|
||||
states, termsMsg = 'You must accept the Terms of Service to continue.';
|
||||
|
||||
// global state... inherited to all child scopes
|
||||
$scope.$root.state = {};
|
||||
@ -25,6 +25,13 @@ define(function(require) {
|
||||
//
|
||||
|
||||
$scope.importKey = function() {
|
||||
if (!$scope.state.agree) {
|
||||
$scope.onError({
|
||||
message: termsMsg
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
$location.path('/login-new-device');
|
||||
};
|
||||
|
||||
@ -89,6 +96,13 @@ define(function(require) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$scope.state.agree) {
|
||||
$scope.onError({
|
||||
message: termsMsg
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.setState(states.PROCESSING);
|
||||
setTimeout(function() {
|
||||
emailDao.unlock({
|
||||
|
@ -27,6 +27,7 @@
|
||||
@import "views/account";
|
||||
@import "views/set-passphrase";
|
||||
@import "views/contacts";
|
||||
@import "views/about";
|
||||
@import "views/dialog";
|
||||
@import "views/navigation";
|
||||
@import "views/mail-list";
|
||||
|
@ -2,11 +2,6 @@
|
||||
padding: 0px;
|
||||
color: $color-grey-dark;
|
||||
|
||||
@include respond-to(mobile) {
|
||||
top: 0;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.control {
|
||||
float: right;
|
||||
|
||||
|
15
src/sass/views/_about.scss
Normal file
15
src/sass/views/_about.scss
Normal file
@ -0,0 +1,15 @@
|
||||
.view-about {
|
||||
@include respond-to(desktop) {
|
||||
max-width: 550px;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin: 30px auto;
|
||||
text-align: center;
|
||||
|
||||
a {
|
||||
color: $color-blue;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,10 @@
|
||||
.view-dialog {
|
||||
max-width: 350px;
|
||||
height: auto;
|
||||
|
||||
@include respond-to(desktop) {
|
||||
max-width: 350px;
|
||||
top: 30%;
|
||||
}
|
||||
|
||||
p {
|
||||
text-align: center;
|
||||
|
@ -24,12 +24,12 @@
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
||||
p {
|
||||
line-height: 150%;
|
||||
|
||||
b, a {
|
||||
color: $color-blue;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
div {
|
||||
@ -69,6 +69,13 @@
|
||||
.passphrase-label-ok {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.opt-in-terms {
|
||||
.checkbox-wrapper {
|
||||
margin-top: 0;
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,7 +83,9 @@
|
||||
}
|
||||
|
||||
.view-login-initial {
|
||||
.content {
|
||||
button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
15
src/tpl/about.html
Normal file
15
src/tpl/about.html
Normal file
@ -0,0 +1,15 @@
|
||||
<div class="lightbox-body" ng-controller="AboutCtrl">
|
||||
<header>
|
||||
<button class="close" ng-click="state.about.toggle(false)" data-action="lightbox-close"></button>
|
||||
</header>
|
||||
|
||||
<div class="content">
|
||||
|
||||
<img src="img/icon.png">
|
||||
<h2>Whiteout Mail</h2>
|
||||
<p>Version {{version}}</p>
|
||||
<p>© {{ date | date:'yyyy'}} Whiteout Networks GmbH</p>
|
||||
<p><a href="https://whiteout.io/imprint.html" target="_blank">Imprint</a> · <a href="https://whiteout.io/privacy.html" target="_blank">Privacy</a> · <a href="https://whiteout.io/terms.html" target="_blank">Terms</a> · <a href="https://github.com/whiteout-io/mail-html5#license" target="_blank">Licenses</a></p>
|
||||
|
||||
</div><!-- /.content -->
|
||||
</div><!-- /.lightbox-body -->
|
@ -31,6 +31,9 @@
|
||||
<div class="lightbox-overlay" ng-class="{'show': state.contacts.open}">
|
||||
<div class="lightbox lightbox-effect" ng-include="'tpl/contacts.html'"></div>
|
||||
</div>
|
||||
<div class="lightbox-overlay" ng-class="{'show': state.about.open}">
|
||||
<div class="lightbox lightbox-effect view-about" ng-include="'tpl/about.html'"></div>
|
||||
</div>
|
||||
<div class="lightbox-overlay" ng-class="{'show': state.dialog.open}">
|
||||
<div class="lightbox lightbox-effect dialog view-dialog" ng-include="'tpl/dialog.html'"></div>
|
||||
</div>
|
@ -20,7 +20,7 @@
|
||||
|
||||
<!-- lightbox -->
|
||||
<div class="lightbox-overlay" ng-class="{'show': state.dialog.open}">
|
||||
<div class="lightbox lightbox-effect view-dialog" ng-include="'tpl/dialog.html'"></div>
|
||||
<div class="lightbox lightbox-effect dialog view-dialog" ng-include="'tpl/dialog.html'"></div>
|
||||
</div>
|
||||
|
||||
<!-- popovers -->
|
||||
|
@ -14,6 +14,12 @@
|
||||
<input class="input-text" type="password" ng-model="state.confirmation" ng-class="{'input-text-error': (state.confirmation || state.passphrase) && state.confirmation !== state.passphrase}" placeholder="Confirm passphrase" tabindex="2">
|
||||
<span class="popover-info" data-icon-append="" popover="#passphrase-info"></span>
|
||||
</div>
|
||||
|
||||
<div class="opt-in-terms">
|
||||
<div class="checkbox-wrapper"><input type="checkbox" ng-model="state.agree"></div>
|
||||
<div>I agree to the Whiteout Networks <a href="https://whiteout.io/terms.html" target="_blank">Terms of Service</a> and have read the <a href="https://whiteout.io/privacy.html" target="_blank">Privacy Policy</a>.</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button type="submit" ng-click="confirmPassphrase()" class="btn" ng-disabled="(state.confirmation || state.passphrase) && state.confirmation !== state.passphrase" tabindex="3">Continue</button>
|
||||
</div>
|
||||
@ -29,7 +35,7 @@
|
||||
|
||||
<!-- lightbox -->
|
||||
<div class="lightbox-overlay" ng-class="{'show': state.dialog.open}">
|
||||
<div class="lightbox lightbox-effect view-dialog" ng-include="'tpl/dialog.html'"></div>
|
||||
<div class="lightbox lightbox-effect dialog view-dialog" ng-include="'tpl/dialog.html'"></div>
|
||||
</div>
|
||||
|
||||
<!-- popovers -->
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
<!-- lightbox -->
|
||||
<div class="lightbox-overlay" ng-class="{'show': state.dialog.open}">
|
||||
<div class="lightbox lightbox-effect view-dialog" ng-include="'tpl/dialog.html'"></div>
|
||||
<div class="lightbox lightbox-effect dialog view-dialog" ng-include="'tpl/dialog.html'"></div>
|
||||
</div>
|
||||
|
||||
<!-- popovers -->
|
||||
|
@ -12,5 +12,5 @@
|
||||
|
||||
<!-- lightbox -->
|
||||
<div class="lightbox-overlay" ng-class="{'show': state.dialog.open}">
|
||||
<div class="lightbox lightbox-effect view-dialog" ng-include="'tpl/dialog.html'"></div>
|
||||
<div class="lightbox lightbox-effect dialog view-dialog" ng-include="'tpl/dialog.html'"></div>
|
||||
</div>
|
@ -17,7 +17,7 @@
|
||||
<ul class="nav-secondary">
|
||||
<li><a href="#" ng-click="state.account.toggle(true); $event.preventDefault()">Account</a></li>
|
||||
<li><a href="#" ng-click="state.contacts.toggle(true); $event.preventDefault()">Contacts</a></li>
|
||||
<li><a href="http://whiteout.io" target="_blank">About</a></li>
|
||||
<li><a href="#" ng-click="state.about.toggle(true); $event.preventDefault()">About</a></li>
|
||||
</ul>
|
||||
|
||||
<footer>
|
||||
|
@ -58,6 +58,25 @@ define(function(require) {
|
||||
});
|
||||
});
|
||||
|
||||
describe('go to import key', function() {
|
||||
it('should not continue if terms are not accepted', function(done) {
|
||||
scope.state.agree = undefined;
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err.message).to.contain('Terms');
|
||||
done();
|
||||
};
|
||||
|
||||
scope.importKey();
|
||||
});
|
||||
|
||||
it('should work', function() {
|
||||
scope.state.agree = true;
|
||||
scope.importKey();
|
||||
expect(location.$$path).to.equal('/login-new-device');
|
||||
});
|
||||
});
|
||||
|
||||
describe('check passphrase quality', function() {
|
||||
it('should be too short', function() {
|
||||
scope.state.passphrase = '&§DG36';
|
||||
@ -103,9 +122,24 @@ define(function(require) {
|
||||
describe('confirm passphrase', function() {
|
||||
var setStateStub;
|
||||
|
||||
it('should not continue if terms are not accepted', function(done) {
|
||||
scope.state.passphrase = passphrase;
|
||||
scope.state.confirmation = passphrase;
|
||||
scope.state.agree = undefined;
|
||||
|
||||
scope.onError = function(err) {
|
||||
expect(err.message).to.contain('Terms');
|
||||
done();
|
||||
};
|
||||
|
||||
scope.confirmPassphrase();
|
||||
});
|
||||
|
||||
it('should unlock crypto', function(done) {
|
||||
scope.state.passphrase = passphrase;
|
||||
scope.state.confirmation = passphrase;
|
||||
scope.state.agree = true;
|
||||
|
||||
emailDaoMock.unlock.withArgs({
|
||||
passphrase: passphrase
|
||||
}).yields();
|
||||
@ -129,6 +163,8 @@ define(function(require) {
|
||||
it('should not work when keypair generation fails', function(done) {
|
||||
scope.state.passphrase = passphrase;
|
||||
scope.state.confirmation = passphrase;
|
||||
scope.state.agree = true;
|
||||
|
||||
emailDaoMock.unlock.withArgs({
|
||||
passphrase: passphrase
|
||||
}).yields(new Error('asd'));
|
||||
|
Loading…
Reference in New Issue
Block a user