1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-27 12:36:14 +01:00

upload new attachments

This commit is contained in:
Kyle Spearrin 2017-07-12 09:57:08 -04:00
parent 89b1639dda
commit 0574c538e7
8 changed files with 225 additions and 65 deletions

View File

@ -739,8 +739,20 @@
"message": "No attachments.", "message": "No attachments.",
"description": "No attachments." "description": "No attachments."
}, },
"attachmentSaved": {
"message": "The attachment has been saved.",
"description": "The attachment has been saved."
},
"file": { "file": {
"message": "File", "message": "File",
"description": "File" "description": "File"
},
"selectFile": {
"message": "Select a file.",
"description": "Select a file."
},
"fileTooLarge": {
"message": "Maximum file size is 100 MB.",
"description": "Maximum file size is 100 MB."
} }
} }

View File

@ -145,7 +145,7 @@
templateUrl: 'app/vault/views/vaultAttachments.html', templateUrl: 'app/vault/views/vaultAttachments.html',
controller: 'vaultAttachmentsController', controller: 'vaultAttachmentsController',
data: { authorize: true }, data: { authorize: true },
params: { animation: null, fromView: true, login: null, from: 'vault' } params: { animation: null, fromView: true, from: 'vault' }
}) })
.state('passwordGenerator', { .state('passwordGenerator', {

View File

@ -1,15 +1,44 @@
angular angular
.module('bit.vault') .module('bit.vault')
.controller('vaultAttachmentsController', function ($scope, $state, $stateParams, loginService, folderService, .controller('vaultAttachmentsController', function ($scope, $state, $stateParams, loginService, $q, toastr,
cryptoService, $q, toastr, SweetAlert, utilsService, $analytics, i18nService) { SweetAlert, utilsService, $analytics, i18nService) {
$scope.i18n = i18nService; $scope.i18n = i18nService;
$scope.login = $stateParams.login;
utilsService.initListSectionItemListeners($(document), angular); utilsService.initListSectionItemListeners($(document), angular);
loginService.get($stateParams.id, function (login) {
$q.when(login.decrypt()).then(function (model) {
$scope.login = model;
});
});
$scope.submitPromise = null; $scope.submitPromise = null;
$scope.submit = function () { $scope.submit = function () {
$scope.close(true); var files = document.getElementById('file').files;
if (!files || !files.length) {
toastr.error(i18nService.selectFile, i18nService.errorsOccurred);
return;
}
if (files[0].size > 104857600) { // 100 MB
toastr.error(i18nService.fileTooLarge, i18nService.errorsOccurred);
return deferred.promise;
}
$scope.submitPromise = $q.when(loginService.saveAttachmentWithServer($scope.login, files[0])).then(function (login) {
$q.when(login.decrypt()).then(function (model) {
$scope.login = model;
});
$analytics.eventTrack('Added Attachment');
toastr.success(i18nService.attachmentSaved);
}, function (err) {
if (err) {
toastr.error(err);
}
else {
toastr.error(i18nService.errorsOccurred);
}
});
}; };
$scope.delete = function (attachment) { $scope.delete = function (attachment) {
@ -22,6 +51,10 @@ angular
}, function (confirmed) { }, function (confirmed) {
if (confirmed) { if (confirmed) {
$q.when(loginService.deleteAttachmentWithServer($stateParams.id, attachment.id)).then(function () { $q.when(loginService.deleteAttachmentWithServer($stateParams.id, attachment.id)).then(function () {
var index = $scope.login.attachments.indexOf(attachment);
if (index > -1) {
$scope.login.attachments.splice(index, 1);
}
$analytics.eventTrack('Deleted Attachment'); $analytics.eventTrack('Deleted Attachment');
toastr.success(i18nService.deletedAttachment); toastr.success(i18nService.deletedAttachment);
}); });
@ -29,8 +62,7 @@ angular
}); });
}; };
$scope.close = function (allOut) { $scope.close = function () {
if (!allOut) {
$state.go('editLogin', { $state.go('editLogin', {
loginId: $stateParams.id, loginId: $stateParams.id,
animation: 'out-slide-down', animation: 'out-slide-down',
@ -39,19 +71,5 @@ angular
}); });
return; return;
}
if ($stateParams.fromView) {
$state.go('viewLogin', {
loginId: $stateParams.id,
animation: 'out-slide-down',
from: $stateParams.from
});
}
else {
$state.go('tabs.vault', {
animation: 'out-slide-down'
});
}
}; };
}); });

View File

@ -71,7 +71,6 @@ angular
$scope.attachments = function () { $scope.attachments = function () {
$state.go('attachments', { $state.go('attachments', {
id: loginId, id: loginId,
login: $scope.login,
animation: 'in-slide-up', animation: 'in-slide-up',
from: from, from: from,
fromView: fromView fromView: fromView

View File

@ -1,13 +1,15 @@
<div class="header"> <form name="theForm" ng-submit="submit()" bit-form="submitPromise">
<div class="header">
<div class="left"> <div class="left">
<a href="" ng-click="close()">{{i18n.close}}</a> <a href="" ng-click="close()">{{i18n.close}}</a>
</div> </div>
<div class="right"> <div class="right">
<a href="" ng-click="submit()">{{i18n.submit}}</a> <button type="submit" class="btn btn-link" ng-show="!theForm.$loading">{{i18n.submit}}</button>
<i class="fa fa-spinner fa-lg fa-spin no-animation" ng-show="theForm.$loading"></i>
</div> </div>
<div class="title">{{i18n.attachments}}</div> <div class="title">{{i18n.attachments}}</div>
</div> </div>
<div class="content"> <div class="content">
<div class="list list-no-selection"> <div class="list list-no-selection">
<div class="list-section"> <div class="list-section">
<div class="list-section-items"> <div class="list-section-items">
@ -37,4 +39,5 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</form>

View File

@ -403,6 +403,47 @@ function initApiService() {
}); });
}; };
ApiService.prototype.postCipherAttachment = function (id, formData, success, error) {
var self = this;
handleTokenState(self).then(function (token) {
$.ajax({
type: 'POST',
url: self.baseUrl + '/ciphers/' + id + '/attachment?' + token,
data: formData,
processData: false,
contentType: false,
dataType: 'json',
success: function (response) {
success(new CipherResponse(response));
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, false, self);
}
});
}, function (jqXHR) {
handleError(error, jqXHR, true, self);
});
};
ApiService.prototype.deleteCipherAttachment = function (id, attachmentId, success, error) {
var self = this;
handleTokenState(self).then(function (token) {
$.ajax({
type: 'POST',
url: self.baseUrl + '/ciphers/' + id + '/attachment/' + attachmentId + '/delete?' + token,
dataType: 'text',
success: function (response) {
success();
},
error: function (jqXHR, textStatus, errorThrown) {
handleError(error, jqXHR, false, self);
}
});
}, function (jqXHR) {
handleError(error, jqXHR, true, self);
});
};
// Helpers // Helpers
function handleError(errorCallback, jqXHR, tokenError, self) { function handleError(errorCallback, jqXHR, tokenError, self) {

View File

@ -322,7 +322,7 @@ function initLoginService() {
deferred.resolve(); deferred.resolve();
}); });
}, function (response) { }, function (response) {
handleError(response, deferred) handleError(response, deferred);
}); });
return deferred.promise; return deferred.promise;
@ -354,6 +354,93 @@ function initLoginService() {
return deferred.promise; return deferred.promise;
}; };
LoginService.prototype.saveAttachmentWithServer = function (login, unencryptedFile) {
var deferred = Q.defer();
var self = this;
var key, encFileName;
var reader = new FileReader();
reader.readAsArrayBuffer(unencryptedFile);
reader.onload = function (evt) {
self.cryptoService.getOrgKey(login.organizationId).then(function (theKey) {
key = theKey;
return self.cryptoService.encrypt(unencryptedFile.name, key);
}).then(function (fileName) {
encFileName = fileName;
return self.cryptoService.encryptToBytes(evt.target.result, key);
}).then(function (encData) {
var fd = new FormData();
var blob = new Blob([encData], { type: 'application/octet-stream' });
fd.append('data', blob, encFileName.encryptedString);
self.apiService.postCipherAttachment(login.id, fd,
function (response) {
self.userService.getUserId(function (userId) {
var data = new LoginData(response, userId);
self.upsert(data, function () {
deferred.resolve(new Login(data));
});
});
},
function (response) {
handleError(response, deferred);
});
});
};
reader.onerror = function (evt) {
deferred.reject('Error reading file.');
};
return deferred.promise;
};
LoginService.prototype.deleteAttachment = function (id, attachmentId, callback) {
if (!callback || typeof callback !== 'function') {
throw 'callback function required';
}
var self = this;
self.userService.getUserId(function (userId) {
var loginsKey = 'sites_' + userId;
chrome.storage.local.get(loginsKey, function (obj) {
var logins = obj[loginsKey];
if (logins && id in logins && logins[id].attachments) {
for (var i = 0; i < logins[id].attachments.length; i++) {
if (logins[id].attachments[i].id === attachmentId) {
logins[id].attachments.splice(i, 1);
}
}
obj[loginsKey] = logins;
chrome.storage.local.set(obj, function () {
self.decryptedLoginCache = null;
callback();
});
}
else {
callback()
}
});
});
};
LoginService.prototype.deleteAttachmentWithServer = function (id, attachmentId) {
var deferred = Q.defer();
var self = this;
self.apiService.deleteCipherAttachment(id, attachmentId, function () {
self.deleteAttachment(id, attachmentId, function () {
deferred.resolve();
});
}, function (response) {
handleError(response, deferred);
});
return deferred.promise;
};
function handleError(error, deferred) { function handleError(error, deferred) {
deferred.reject(error); deferred.reject(error);
} }