feat(GUI): enable "unsafe" mode (#578)

This setting makes Etcher not filter non-removable drives, allowing you to
arbitrarily write to your system drives.

This is a dangerous option, therefore we present it in a separate section of
the settings page, and show an informative confirmation dialog.

Change-Type: minor
Changelog-Entry: Add an "unsafe" option to bypass drive protection.
Fixes: https://github.com/resin-io/etcher/issues/480
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
This commit is contained in:
Juan Cruz Viotti 2016-07-18 22:42:24 -04:00 committed by GitHub
parent e696d0722d
commit a3dc8624b1
10 changed files with 231 additions and 4 deletions

View File

@ -5945,6 +5945,9 @@ html {
margin-top: 10px;
margin-bottom: 10px; }
.space-top-large, .page-settings .subtitle {
margin-top: 30px; }
.space-top-medium {
margin-top: 15px; }
@ -5952,6 +5955,9 @@ html {
margin-top: 30px;
margin-bottom: 30px; }
.space-bottom-medium, .page-settings .subtitle {
margin-bottom: 15px; }
.space-bottom-large {
margin-bottom: 30px; }
@ -6515,6 +6521,36 @@ button.btn:focus, button.progress-button:focus {
.page-settings .checkbox input[type="checkbox"]:not(:checked) + * {
color: #ddd; }
.modal-settings-dangerous-modal {
max-width: 50%; }
.modal-settings-dangerous-modal .modal-header {
text-align: center;
font-weight: bold;
color: #555555;
text-transform: initial;
font-size: 15px;
border-bottom: none;
padding: 0 0 15px; }
.modal-settings-dangerous-modal .modal-title {
display: flex;
justify-content: center;
align-items: center; }
.modal-settings-dangerous-modal .modal-title .glyphicon, .modal-settings-dangerous-modal .modal-title .tick {
margin-right: 3px;
color: #d9534f; }
.modal-settings-dangerous-modal .modal-content {
padding: 25px 30px;
height: 220px; }
.modal-settings-dangerous-modal .modal-body {
padding: 0; }
.modal-settings-dangerous-modal .modal-footer {
padding: 0;
padding-top: 20px;
border-top: 1px solid #eeeeee;
display: flex; }
.modal-settings-dangerous-modal .modal-footer > .btn, .modal-settings-dangerous-modal .modal-footer > .progress-button {
flex-basis: 50%; }
/*
* Copyright 2016 Resin.io
*

View File

@ -37,6 +37,7 @@ const DEFAULT_STATE = Immutable.fromJS({
speed: 0
},
settings: {
unsafeMode: false,
errorReporting: true,
unmountOnSuccess: true,
validateWriteOnSuccess: true,

View File

@ -27,9 +27,11 @@ const EventEmitter = require('events').EventEmitter;
const drivelist = require('drivelist');
const MODULE_NAME = 'Etcher.Modules.DriveScanner';
const driveScanner = angular.module(MODULE_NAME, []);
const driveScanner = angular.module(MODULE_NAME, [
require('../models/settings')
]);
driveScanner.factory('DriveScannerService', () => {
driveScanner.factory('DriveScannerService', (SettingsModel) => {
const DRIVE_SCANNER_INTERVAL_MS = 2000;
const emitter = new EventEmitter();
@ -38,6 +40,10 @@ driveScanner.factory('DriveScannerService', () => {
return Rx.Observable.fromNodeCallback(drivelist.list)();
})
.map((drives) => {
if (SettingsModel.get('unsafeMode')) {
return drives;
}
return _.reject(drives, (drive) => {
return drive.system;
});

View File

@ -0,0 +1,52 @@
/*
* Copyright 2016 Resin.io
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
module.exports = function($uibModalInstance, message) {
/**
* @summary Modal message
* @property
* @public
*/
this.message = message;
/**
* @summary Reject the dangerous setting
* @function
* @public
*
* @example
* SettingsDangerousModalController.reject();
*/
this.reject = () => {
$uibModalInstance.close(false);
};
/**
* @summary Accept the dangerous setting
* @function
* @public
*
* @example
* SettingsDangerousModalController.accept();
*/
this.accept = () => {
$uibModalInstance.close(true);
};
};

View File

@ -16,14 +16,26 @@
'use strict';
module.exports = function(SettingsModel) {
const _ = require('lodash');
module.exports = function($uibModal, SettingsModel) {
/**
* @summary Refresh current settings
* @function
* @public
*/
this.refreshSettings = () => {
this.currentData = SettingsModel.getAll();
};
/**
* @summary Current settings value
* @type Object
* @public
*/
this.currentData = SettingsModel.getAll();
this.currentData = {};
this.refreshSettings();
/**
* @summary Settings model
@ -32,4 +44,36 @@ module.exports = function(SettingsModel) {
*/
this.model = SettingsModel;
/**
* @summary Enable a dangerous setting
* @function
* @public
*
* @param {String} name - setting name
* @param {String} message - danger message
* @returns {Undefined}
*
* @example
* SettingsController.enableDangerousSetting('unsafeMode', 'Don\'t do this!');
*/
this.enableDangerousSetting = (name, message) => {
if (!this.currentData[name]) {
this.model.set(name, false);
return this.refreshSettings();
}
$uibModal.open({
animation: true,
templateUrl: './pages/settings/templates/settings-dangerous-modal.tpl.html',
controller: 'SettingsDangerousModalController as modal',
size: 'settings-dangerous-modal',
resolve: {
message: _.constant(message)
}
}).result.then((userAccepted) => {
this.model.set(name, Boolean(userAccepted));
this.refreshSettings();
});
};
};

View File

@ -28,6 +28,7 @@ const SettingsPage = angular.module(MODULE_NAME, [
]);
SettingsPage.controller('SettingsController', require('./controllers/settings'));
SettingsPage.controller('SettingsDangerousModalController', require('./controllers/settings-dangerous-modal'));
SettingsPage.config(($stateProvider) => {
$stateProvider

View File

@ -18,3 +18,52 @@
color: $gray-light;
}
.page-settings .subtitle {
@extend .space-top-large;
@extend .space-bottom-medium;
}
.modal-settings-dangerous-modal {
max-width: 50%;
.modal-header {
text-align: center;
font-weight: bold;
color: $gray;
text-transform: initial;
font-size: 15px;
border-bottom: none;
padding: 0 0 15px;
}
.modal-title {
display: flex;
justify-content: center;
align-items: center;
.glyphicon {
margin-right: 3px;
color: $btn-danger-bg;
}
}
.modal-content {
padding: 25px 30px;
height: 220px;
}
.modal-body {
padding: 0;
}
.modal-footer {
padding: 0;
padding-top: 20px;
border-top: 1px solid $gray-lighter;
display: flex;
& > .btn {
flex-basis: 50%;
}
}
}

View File

@ -0,0 +1,18 @@
<div class="modal-header">
<h4 class="modal-title">
<span class="glyphicon glyphicon-exclamation-sign"></span>
<span>Warning!</span>
</h4>
</div>
<div class="modal-body">
<p>Are you sure you want to turn this on? {{ modal.message }}</p>
</div>
<div class="modal-footer">
<button class="btn btn-danger"
ng-click="modal.accept()">YES, CONTINUE</button>
<button class="btn btn-default"
ng-click="modal.reject()">CANCEL</button>
</div>

View File

@ -30,4 +30,16 @@
<span>Validate write on success</span>
</label>
</div>
<h3 class="subtitle">Advanced</h3>
<div class="checkbox">
<label>
<input type="checkbox"
ng-model="settings.currentData.unsafeMode"
ng-change="settings.enableDangerousSetting('unsafeMode', 'You will be able to burn to your system drives.')">
<span>Unsafe mode <span class="label label-danger">DANGEROUS</span></span>
</label>
</div>
</div>

View File

@ -33,6 +33,10 @@ $spacing-tiny: 5px;
margin-bottom: $spacing-small;
}
.space-top-large {
margin-top: $spacing-large;
}
.space-top-medium {
margin-top: $spacing-medium;
}
@ -42,6 +46,10 @@ $spacing-tiny: 5px;
margin-bottom: $spacing-large;
}
.space-bottom-medium {
margin-bottom: $spacing-medium;
}
.space-bottom-large {
margin-bottom: $spacing-large;
}