diff --git a/dist/.publish b/dist/.publish index 697d06985c..d425a19baf 160000 --- a/dist/.publish +++ b/dist/.publish @@ -1 +1 @@ -Subproject commit 697d06985c1b57fdc445195b31391bd99fa1d7e9 +Subproject commit d425a19baf91e8a81b27df881600b5a73416dce3 diff --git a/src/app/config.js b/src/app/config.js index 8b447a5b4e..fc8b4b55f6 100644 --- a/src/app/config.js +++ b/src/app/config.js @@ -124,12 +124,6 @@ angular refreshFromServer: false } }) - .state('backend.user.shared', { - url: '^/shared', - templateUrl: 'app/vault/views/vaultShared.html', - controller: 'vaultSharedController', - data: { pageTitle: 'Shared' } - }) .state('backend.user.settings', { url: '^/settings', templateUrl: 'app/settings/views/settings.html', @@ -375,7 +369,7 @@ angular // user is guaranteed to be authenticated becuase of previous check if (toState.name.indexOf('backend.org.') > -1 && toParams.orgId) { // clear vault rootScope when visiting org admin section - $rootScope.vaultCiphers = $rootScope.vaultFolders = null; + $rootScope.vaultCiphers = $rootScope.vaultGroupings = null; authService.getUserProfile().then(function (profile) { var orgs = profile.organizations; diff --git a/src/app/services/authService.js b/src/app/services/authService.js index 91b3ec245e..8ee9297a47 100644 --- a/src/app/services/authService.js +++ b/src/app/services/authService.js @@ -95,7 +95,7 @@ angular _service.logOut = function () { tokenService.clearTokens(); cryptoService.clearKeys(); - $rootScope.vaultFolders = $rootScope.vaultCiphers = null; + $rootScope.vaultGroupings = $rootScope.vaultCiphers = null; _userProfile = null; }; diff --git a/src/app/vault/vaultAddCipherController.js b/src/app/vault/vaultAddCipherController.js index b2401eceb8..8aafa45636 100644 --- a/src/app/vault/vaultAddCipherController.js +++ b/src/app/vault/vaultAddCipherController.js @@ -2,9 +2,9 @@ .module('bit.vault') .controller('vaultAddCipherController', function ($scope, apiService, $uibModalInstance, cryptoService, cipherService, - passwordService, selectedFolder, $analytics, checkedFavorite, $rootScope, authService, $uibModal, constants) { + passwordService, selectedFolder, $analytics, checkedFavorite, $rootScope, authService, $uibModal, constants, $filter) { $analytics.eventTrack('vaultAddCipherController', { category: 'Modal' }); - $scope.folders = $rootScope.vaultFolders; + $scope.folders = $filter('filter')($rootScope.vaultGroupings, { folder: true }); $scope.constants = constants; $scope.selectedType = constants.cipherType.login.toString(); $scope.cipher = { diff --git a/src/app/vault/vaultController.js b/src/app/vault/vaultController.js index 4646680709..5ddd940b96 100644 --- a/src/app/vault/vaultController.js +++ b/src/app/vault/vaultController.js @@ -5,19 +5,22 @@ cipherService, $q, $localStorage, $timeout, $rootScope, $state, $analytics, constants) { $scope.loading = true; $scope.ciphers = []; + $scope.folderCount = 0; + $scope.collectionCount = 0; + $scope.firstCollectionId = null; $scope.constants = constants; $scope.favoriteCollapsed = $localStorage.collapsedFolders && 'favorite' in $localStorage.collapsedFolders; - $scope.folderIdFilter = undefined; + $scope.groupingIdFilter = undefined; $scope.typeFilter = undefined; if ($state.params.refreshFromServer) { - $rootScope.vaultFolders = $rootScope.vaultCiphers = null; + $rootScope.vaultGroupings = $rootScope.vaultCiphers = null; } $scope.$on('$viewContentLoaded', function () { - if ($rootScope.vaultFolders && $rootScope.vaultCiphers) { + if ($rootScope.vaultGroupings && $rootScope.vaultCiphers) { $scope.loading = false; - loadFolderData($rootScope.vaultFolders); + loadGroupingData($rootScope.vaultGroupings); loadCipherData($rootScope.vaultCiphers); return; } @@ -26,20 +29,34 @@ }); function loadDataFromServer() { - var folderPromise = apiService.folders.list({}, function (folders) { - var decFolders = [{ - id: null, - name: 'No Folder' - }]; + var decGroupings = [{ + id: null, + name: 'No Folder', + folder: true + }]; + var collectionPromise = apiService.collections.listMe({ writeOnly: false }, function (collections) { + for (var i = 0; i < collections.Data.length; i++) { + var decCollection = cipherService.decryptCollection(collections.Data[i], null, true); + decCollection.collection = true; + decGroupings.push(decCollection); + } + $scope.collectionCount = collections.Data.length; + }).$promise; + + var folderPromise = apiService.folders.list({}, function (folders) { for (var i = 0; i < folders.Data.length; i++) { var decFolder = cipherService.decryptFolderPreview(folders.Data[i]); - decFolders.push(decFolder); + decFolder.folder = true; + decGroupings.push(decFolder); } - - loadFolderData(decFolders); + $scope.folderCount = folders.Data.length; }).$promise; + var groupingPromise = $q.all([collectionPromise, folderPromise]).then(function () { + loadGroupingData(decGroupings); + }); + var cipherPromise = apiService.ciphers.list({}, function (ciphers) { var decCiphers = []; @@ -48,31 +65,38 @@ decCiphers.push(decCipher); } - folderPromise.then(function () { + groupingPromise.then(function () { loadCipherData(decCiphers); }); }).$promise; - $q.all([cipherPromise, folderPromise]).then(function () { + $q.all([cipherPromise, groupingPromise]).then(function () { $scope.loading = false; }); } - function loadFolderData(decFolders) { - $rootScope.vaultFolders = $filter('orderBy')(decFolders, folderSort); + function loadGroupingData(decGroupings) { + $rootScope.vaultGroupings = $filter('orderBy')(decGroupings, ['folder', groupingSort]); + var collections = $filter('filter')($rootScope.vaultGroupings, { collection: true }); + if (collections && collections.length) { + $scope.firstCollectionId = collections[0].id; + } } function loadCipherData(decCiphers) { - angular.forEach($rootScope.vaultFolders, function (folderValue, folderIndex) { - folderValue.collapsed = $localStorage.collapsedFolders && - (folderValue.id || 'none') in $localStorage.collapsedFolders; + angular.forEach($rootScope.vaultGroupings, function (grouping, groupingIndex) { + grouping.collapsed = $localStorage.collapsedFolders && + (grouping.id || 'none') in $localStorage.collapsedFolders; angular.forEach(decCiphers, function (cipherValue) { if (cipherValue.favorite) { cipherValue.sort = -1; } - else if (cipherValue.folderId == folderValue.id) { - cipherValue.sort = folderIndex; + else if (grouping.folder && cipherValue.folderId == grouping.id) { + cipherValue.sort = groupingIndex; + } + else if (grouping.collection && cipherValue.collectionIds.indexOf(grouping.id) > -1) { + cipherValue.sort = groupingIndex; } }); }); @@ -110,7 +134,7 @@ return chunks; } - function folderSort(item) { + function groupingSort(item) { if (!item.id) { return ''; } @@ -123,12 +147,12 @@ 'Edit the item and copy it manually instead.'); }; - $scope.collapseExpand = function (folder, favorite) { + $scope.collapseExpand = function (grouping, favorite) { if (!$localStorage.collapsedFolders) { $localStorage.collapsedFolders = {}; } - var id = favorite ? 'favorite' : (folder.id || 'none'); + var id = favorite ? 'favorite' : (grouping.id || 'none'); if (id in $localStorage.collapsedFolders) { delete $localStorage.collapsedFolders[id]; } @@ -276,8 +300,8 @@ }); addModel.result.then(function (addedFolder) { - $rootScope.vaultFolders.push(addedFolder); - loadFolderData($rootScope.vaultFolders); + $rootScope.vaultGroupings.push(addedFolder); + loadGroupingData($rootScope.vaultGroupings); }); }; @@ -288,9 +312,9 @@ apiService.folders.del({ id: folder.id }, function () { $analytics.eventTrack('Deleted Folder'); - var index = $rootScope.vaultFolders.indexOf(folder); + var index = $rootScope.vaultGroupings.indexOf(folder); if (index > -1) { - $rootScope.vaultFolders.splice(index, 1); + $rootScope.vaultGroupings.splice(index, 1); } }); }; @@ -319,7 +343,7 @@ }); }; - $scope.collections = function (cipher) { + $scope.editCollections = function (cipher) { var modal = $uibModal.open({ animation: true, templateUrl: 'app/vault/views/vaultCipherCollections.html', @@ -333,11 +357,14 @@ if (response.collectionIds && !response.collectionIds.length) { removeCipherFromScopes(cipher); } + else if (response.collectionIds) { + cipher.collectionIds = response.collectionIds; + } }); }; - $scope.filterFolder = function (folder) { - $scope.folderIdFilter = folder.id; + $scope.filterGrouping = function (grouping) { + $scope.groupingIdFilter = grouping.id; if ($.AdminLTE && $.AdminLTE.layout) { $timeout(function () { @@ -357,7 +384,7 @@ }; $scope.clearFilters = function () { - $scope.folderIdFilter = undefined; + $scope.groupingIdFilter = undefined; $scope.typeFilter = undefined; if ($.AdminLTE && $.AdminLTE.layout) { @@ -367,12 +394,22 @@ } }; - $scope.folderFilter = function (folder) { - return $scope.folderIdFilter === undefined || folder.id === $scope.folderIdFilter; + $scope.groupingFilter = function (grouping) { + return $scope.groupingIdFilter === undefined || grouping.id === $scope.groupingIdFilter; }; - $scope.cipherFilter = function (cipher) { - return $scope.typeFilter === undefined || cipher.type === $scope.typeFilter; + $scope.cipherFilter = function (grouping) { + return function (cipher) { + var matchesGrouping = grouping === null; + if (!matchesGrouping && grouping.folder && cipher.folderId === grouping.id) { + matchesGrouping = true; + } + else if (!matchesGrouping && grouping.collection && cipher.collectionIds.indexOf(grouping.id) > -1) { + matchesGrouping = true; + } + + return matchesGrouping && ($scope.typeFilter === undefined || cipher.type === $scope.typeFilter); + }; }; $scope.unselectAll = function () { diff --git a/src/app/vault/vaultEditCipherController.js b/src/app/vault/vaultEditCipherController.js index 09430b052d..03be2c9925 100644 --- a/src/app/vault/vaultEditCipherController.js +++ b/src/app/vault/vaultEditCipherController.js @@ -2,9 +2,9 @@ .module('bit.vault') .controller('vaultEditCipherController', function ($scope, apiService, $uibModalInstance, cryptoService, cipherService, - passwordService, cipherId, $analytics, $rootScope, authService, $uibModal, constants) { + passwordService, cipherId, $analytics, $rootScope, authService, $uibModal, constants, $filter) { $analytics.eventTrack('vaultEditCipherController', { category: 'Modal' }); - $scope.folders = $rootScope.vaultFolders; + $scope.folders = $filter('filter')($rootScope.vaultGroupings, { folder: true }); $scope.cipher = {}; $scope.readOnly = false; $scope.constants = constants; diff --git a/src/app/vault/vaultMoveCiphersController.js b/src/app/vault/vaultMoveCiphersController.js index 3e9573c0e7..0a117b8deb 100644 --- a/src/app/vault/vaultMoveCiphersController.js +++ b/src/app/vault/vaultMoveCiphersController.js @@ -2,9 +2,9 @@ .module('bit.vault') .controller('vaultMoveCiphersController', function ($scope, apiService, $uibModalInstance, ids, $analytics, - $rootScope) { + $rootScope, $filter) { $analytics.eventTrack('vaultMoveCiphersController', { category: 'Modal' }); - $scope.folders = $rootScope.vaultFolders; + $scope.folders = $filter('filter')($rootScope.vaultGroupings, { folder: true }); $scope.count = ids.length; $scope.save = function () { diff --git a/src/app/vault/vaultSharedController.js b/src/app/vault/vaultSharedController.js deleted file mode 100644 index b37f7bd8a3..0000000000 --- a/src/app/vault/vaultSharedController.js +++ /dev/null @@ -1,239 +0,0 @@ -angular - .module('bit.vault') - - .controller('vaultSharedController', function ($scope, apiService, cipherService, $analytics, $q, $localStorage, - $uibModal, $filter, $rootScope, authService, cryptoService) { - $scope.ciphers = []; - $scope.collections = []; - $scope.loading = true; - - $scope.$on('$viewContentLoaded', function () { - var collectionPromise = apiService.collections.listMe({ writeOnly: false }, function (collections) { - var decCollections = []; - - for (var i = 0; i < collections.Data.length; i++) { - var decCollection = cipherService.decryptCollection(collections.Data[i], null, true); - decCollection.collapsed = $localStorage.collapsedCollections && - decCollection.id in $localStorage.collapsedCollections; - decCollections.push(decCollection); - } - - $scope.collections = decCollections; - }).$promise; - - var cipherPromise = apiService.ciphers.listDetails({}, function (ciphers) { - var decCiphers = []; - - for (var i = 0; i < ciphers.Data.length; i++) { - var decCipher = cipherService.decryptCipherPreview(ciphers.Data[i]); - decCiphers.push(decCipher); - } - - if (decCiphers.length) { - $scope.collections.push({ - id: null, - name: 'Unassigned', - collapsed: $localStorage.collapsedCollections && 'unassigned' in $localStorage.collapsedCollections - }); - } - - $scope.ciphers = decCiphers; - }).$promise; - - $q.all([collectionPromise, cipherPromise]).then(function () { - $scope.loading = false; - }); - }); - - $scope.clipboardError = function (e) { - alert('Your web browser does not support easy clipboard copying. ' + - 'Edit the item and copy it manually instead.'); - }; - - $scope.attachments = function (cipher) { - authService.getUserProfile().then(function (profile) { - return { - isPremium: profile.premium, - orgUseStorage: cipher.organizationId && !!profile.organizations[cipher.organizationId].maxStorageGb - }; - }).then(function (perms) { - if (cipher.organizationId && !perms.orgUseStorage) { - $uibModal.open({ - animation: true, - templateUrl: 'app/views/paidOrgRequired.html', - controller: 'paidOrgRequiredController', - resolve: { - orgId: function () { return cipher.organizationId; } - } - }); - return; - } - - if (!cipher.organizationId && !perms.isPremium) { - $uibModal.open({ - animation: true, - templateUrl: 'app/views/premiumRequired.html', - controller: 'premiumRequiredController' - }); - return; - } - - if (!cipher.organizationId && !cryptoService.getEncKey()) { - toastr.error('You cannot use this feature until you update your encryption key.', 'Feature Unavailable'); - return; - } - - var attachmentModel = $uibModal.open({ - animation: true, - templateUrl: 'app/vault/views/vaultAttachments.html', - controller: 'vaultAttachmentsController', - resolve: { - cipherId: function () { return cipher.id; } - } - }); - - attachmentModel.result.then(function (hasAttachments) { - cipher.hasAttachments = hasAttachments; - }); - }); - }; - - $scope.filterByCollection = function (collection) { - return function (cipher) { - if (!cipher.collectionIds || !cipher.collectionIds.length) { - return collection.id === null; - } - - return cipher.collectionIds.indexOf(collection.id) > -1; - }; - }; - - $scope.collectionSort = function (item) { - if (!item.id) { - return ''; - } - - return item.name.toLowerCase(); - }; - - $scope.collapseExpand = function (collection) { - if (!$localStorage.collapsedCollections) { - $localStorage.collapsedCollections = {}; - } - - var id = collection.id || 'unassigned'; - - if (id in $localStorage.collapsedCollections) { - delete $localStorage.collapsedCollections[id]; - } - else { - $localStorage.collapsedCollections[id] = true; - } - }; - - $scope.editCipher = function (cipher) { - var editModel = $uibModal.open({ - animation: true, - templateUrl: 'app/vault/views/vaultEditCipher.html', - controller: 'vaultEditCipherController', - resolve: { - cipherId: function () { return cipher.id; } - } - }); - - editModel.result.then(function (returnVal) { - var rootCipher = findRootCipher(cipher) || { meta: {} }, - index; - - if (returnVal.action === 'edit') { - index = $scope.ciphers.indexOf(cipher); - if (index > -1) { - returnVal.data.collectionIds = $scope.ciphers[index].collectionIds; - $scope.ciphers[index] = returnVal.data; - - if ($rootScope.vaultCiphers) { - index = $rootScope.vaultCiphers.indexOf(rootCipher); - if (index > -1) { - $rootScope.vaultCiphers[index] = returnVal.data; - } - } - } - } - else if (returnVal.action === 'partialEdit') { - cipher.folderId = rootCipher.folderId = returnVal.data.folderId; - cipher.favorite = rootCipher.favorite = returnVal.data.favorite; - } - else if (returnVal.action === 'delete') { - index = $scope.ciphers.indexOf(cipher); - if (index > -1) { - $scope.ciphers.splice(index, 1); - } - - removeRootCipher(rootCipher); - } - }); - }; - - $scope.editCollections = function (cipher) { - var modal = $uibModal.open({ - animation: true, - templateUrl: 'app/vault/views/vaultCipherCollections.html', - controller: 'vaultCipherCollectionsController', - resolve: { - cipherId: function () { return cipher.id; } - } - }); - - modal.result.then(function (response) { - if (response.collectionIds) { - cipher.collectionIds = response.collectionIds; - // TODO: if there are no collectionIds now, it is possible that the user no longer has access to this cipher - // which means it should be removed by calling removeRootCipher(findRootCipher(cipher)) - } - }); - }; - - $scope.removeCipher = function (cipher, collection) { - if (!confirm('Are you sure you want to remove this item (' + cipher.name + ') from the ' + - 'collection (' + collection.name + ') ?')) { - return; - } - - var request = { - collectionIds: [] - }; - - for (var i = 0; i < cipher.collectionIds.length; i++) { - if (cipher.collectionIds[i] !== collection.id) { - request.collectionIds.push(cipher.collectionIds[i]); - } - } - - apiService.ciphers.putCollections({ id: cipher.id }, request).$promise.then(function (response) { - $analytics.eventTrack('Removed From Collection'); - cipher.collectionIds = request.collectionIds; - // TODO: if there are no collectionIds now, it is possible that the user no longer has access to this cipher - // which means it should be removed by calling removeRootCipher(findRootCipher(cipher)) - }); - }; - - function findRootCipher(cipher) { - if ($rootScope.vaultCiphers) { - var rootCiphers = $filter('filter')($rootScope.vaultCiphers, { id: cipher.id }); - if (rootCiphers && rootCiphers.length) { - return rootCiphers[0]; - } - } - - return null; - } - - function removeRootCipher(rootCipher) { - if (rootCipher && rootCipher.id) { - var index = $rootScope.vaultCiphers.indexOf(rootCipher); - if (index > -1) { - $rootScope.vaultCiphers.splice(index, 1); - } - } - } - }); diff --git a/src/app/vault/views/vault.html b/src/app/vault/views/vault.html index def3d5afa0..c11bb3b9a3 100644 --- a/src/app/vault/views/vault.html +++ b/src/app/vault/views/vault.html @@ -26,20 +26,19 @@

My Vault - - , + + , + , &

-
+

Loading...

+ ng-show="vaultGroupings.length && groupingIdFilter === undefined && (!main.searchVaultText || favoriteCiphers.length)">

@@ -74,7 +73,7 @@ + filter: cipherFilter(null) | filter: (main.searchVaultText || '')) track by cipher.id">
-
+

- - {{folder.name}} - + + + {{grouping.name}} +

-
-
-

No items in this folder.

- +
+
+

No items in this collection/folder.

+
-
+
- +
-
- -
-
-

No items in this collection.

-

- Share an item to this collection by selecting Share or - Collections from the item's options () menu. -

-
-
No unassigned items.
-
- - - - - - - - -
- - - - - - {{cipher.name}} - -
-
{{cipher.subTitle}}
-
-
-
- - diff --git a/src/app/views/userLayout.html b/src/app/views/userLayout.html index 74419e1798..36388ff2b0 100644 --- a/src/app/views/userLayout.html +++ b/src/app/views/userLayout.html @@ -57,11 +57,6 @@ -
  • - - Shared - -
  • Tools diff --git a/src/index.html b/src/index.html index 33fe8abc5a..13ccc0f451 100644 --- a/src/index.html +++ b/src/index.html @@ -223,7 +223,6 @@ -