mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-26 12:25:20 +01:00
show collection and folder groupings together
This commit is contained in:
parent
7f0d8c99e3
commit
d42e6ca3fd
2
dist/.publish
vendored
2
dist/.publish
vendored
@ -1 +1 @@
|
||||
Subproject commit 697d06985c1b57fdc445195b31391bd99fa1d7e9
|
||||
Subproject commit d425a19baf91e8a81b27df881600b5a73416dce3
|
@ -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;
|
||||
|
@ -95,7 +95,7 @@ angular
|
||||
_service.logOut = function () {
|
||||
tokenService.clearTokens();
|
||||
cryptoService.clearKeys();
|
||||
$rootScope.vaultFolders = $rootScope.vaultCiphers = null;
|
||||
$rootScope.vaultGroupings = $rootScope.vaultCiphers = null;
|
||||
_userProfile = null;
|
||||
};
|
||||
|
||||
|
@ -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 = {
|
||||
|
@ -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 () {
|
||||
|
@ -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;
|
||||
|
@ -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 () {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
@ -26,20 +26,19 @@
|
||||
</div>
|
||||
<h1>
|
||||
My Vault
|
||||
<small>
|
||||
<span ng-pluralize
|
||||
count="vaultFolders.length > 0 ? vaultFolders.length - 1 : 0"
|
||||
when="{'1': '{} folder', 'other': '{} folders'}"></span>,
|
||||
<small class="visible-md-inline visible-lg-inline">
|
||||
<span ng-pluralize count="folderCount" when="{'1': '{} folder', 'other': '{} folders'}"></span>,
|
||||
<span ng-pluralize count="collectionCount" when="{'1': '{} collection', 'other': '{} collections'}"></span>, &
|
||||
<span ng-pluralize count="ciphers.length" when="{'1': '{} item', 'other': '{} items'}"></span>
|
||||
</small>
|
||||
</h1>
|
||||
</section>
|
||||
<section class="content">
|
||||
<div ng-show="loading && !vaultFolders.length">
|
||||
<div ng-show="loading && !vaultGroupings.length">
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
<div class="box box-primary" ng-class="{'collapsed-box': favoriteCollapsed}" style="margin-bottom: 40px;"
|
||||
ng-show="vaultFolders.length && folderIdFilter === undefined && (!main.searchVaultText || favoriteCiphers.length)">
|
||||
ng-show="vaultGroupings.length && groupingIdFilter === undefined && (!main.searchVaultText || favoriteCiphers.length)">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">
|
||||
<i class="fa fa-star"></i>
|
||||
@ -74,7 +73,7 @@
|
||||
<table class="table table-striped table-hover table-vmiddle">
|
||||
<tbody>
|
||||
<tr ng-repeat="cipher in favoriteCiphers = (ciphers | filter: { favorite: true } |
|
||||
filter: cipherFilter | filter: (main.searchVaultText || '')) track by cipher.id">
|
||||
filter: cipherFilter(null) | filter: (main.searchVaultText || '')) track by cipher.id">
|
||||
<td style="width: 70px;">
|
||||
<div class="btn-group" data-append-to="body">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
||||
@ -97,7 +96,7 @@
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="cipher.organizationId && cipher.edit">
|
||||
<a href="#" stop-click ng-click="collections(cipher)">
|
||||
<a href="#" stop-click ng-click="editCollections(cipher)">
|
||||
<i class="fa fa-fw fa-cubes"></i> Collections
|
||||
</a>
|
||||
</li>
|
||||
@ -137,59 +136,69 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box" ng-class="{'collapsed-box': folder.collapsed}"
|
||||
ng-repeat="folder in filteredVaultFolders = (vaultFolders | filter: folderFilter) track by folder.id"
|
||||
ng-show="vaultFolders.length && (!main.searchVaultText || folderCiphers.length)">
|
||||
<div class="box" ng-class="{'collapsed-box': grouping.collapsed}"
|
||||
ng-repeat="grouping in filteredVaultGroupings = (vaultGroupings | filter: groupingFilter) track by grouping.id"
|
||||
ng-show="vaultGroupings.length && (!main.searchVaultText || groupingCiphers.length)"
|
||||
ng-style="firstCollectionId && grouping.id === firstCollectionId && {'margin-top': '40px'}">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">
|
||||
<i class="fa" ng-class="{'fa-folder-open': folder.id !== null, 'fa-folder-open-o': folder.id === null}"></i>
|
||||
{{folder.name}}
|
||||
<small ng-pluralize count="folderCiphers.length" when="{'1': '{} item', 'other': '{} items'}"></small>
|
||||
<i class="fa" ng-if="grouping.folder"
|
||||
ng-class="{'fa-folder-open': grouping.id !== null, 'fa-folder-open-o': grouping.id === null}"></i>
|
||||
<i class="fa fa-cubes" ng-if="grouping.collection"></i>
|
||||
{{grouping.name}}
|
||||
<small ng-pluralize count="groupingCiphers.length" when="{'1': '{} item', 'other': '{} items'}"></small>
|
||||
</h3>
|
||||
<div class="box-tools">
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-box-tool dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-cog"></i> <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
<ul class="dropdown-menu dropdown-menu-right" ng-if="grouping.folder">
|
||||
<li>
|
||||
<a href="#" stop-click ng-click="addCipher(folder)">
|
||||
<a href="#" stop-click ng-click="addCipher(grouping)">
|
||||
<i class="fa fa-fw fa-plus-circle"></i> New Item
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="folder.id">
|
||||
<a href="#" stop-click ng-click="editFolder(folder)">
|
||||
<li ng-show="grouping.id">
|
||||
<a href="#" stop-click ng-click="editFolder(grouping)">
|
||||
<i class="fa fa-fw fa-pencil"></i> Edit Folder
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="canDeleteFolder(grouping)">
|
||||
<a href="#" stop-click ng-click="deleteFolder(grouping)" class="text-red">
|
||||
<i class="fa fa-fw fa-trash"></i> Delete Folder
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" stop-click ng-click="selectFolder(folder, $event)">
|
||||
<a href="#" stop-click ng-click="selectFolder(grouping, $event)">
|
||||
<i class="fa fa-fw fa-check-square-o"></i> Select All
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="canDeleteFolder(folder)">
|
||||
<a href="#" stop-click ng-click="deleteFolder(folder)" class="text-red">
|
||||
<i class="fa fa-fw fa-trash"></i> Delete Folder
|
||||
</ul>
|
||||
<ul class="dropdown-menu dropdown-menu-right" ng-if="grouping.collection">
|
||||
<li>
|
||||
<a href="#" stop-click ng-click="selectFolder(grouping, $event)">
|
||||
<i class="fa fa-fw fa-check-square-o"></i> Select All
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<button type="button" class="btn btn-box-tool" data-widget="collapse" title="Collapse/Expand"
|
||||
ng-click="collapseExpand(folder)">
|
||||
<i class="fa" ng-class="{'fa-minus': !folder.collapsed, 'fa-plus': folder.collapsed}"></i>
|
||||
ng-click="collapseExpand(grouping)">
|
||||
<i class="fa" ng-class="{'fa-minus': !grouping.collapsed, 'fa-plus': grouping.collapsed}"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body" ng-class="{'no-padding': folderCiphers.length}">
|
||||
<div ng-show="!folderCiphers.length">
|
||||
<p>No items in this folder.</p>
|
||||
<button type="button" ng-click="addCipher(folder)" class="btn btn-default btn-flat">Add an Item</button>
|
||||
<div class="box-body" ng-class="{'no-padding': groupingCiphers.length}">
|
||||
<div ng-show="!groupingCiphers.length">
|
||||
<p>No items in this collection/folder.</p>
|
||||
<button type="button" ng-click="addCipher(grouping)" class="btn btn-default btn-flat">Add an Item</button>
|
||||
</div>
|
||||
<div class="table-responsive" ng-show="folderCiphers.length">
|
||||
<div class="table-responsive" ng-show="groupingCiphers.length">
|
||||
<table class="table table-striped table-hover table-vmiddle">
|
||||
<tbody>
|
||||
<tr ng-repeat="cipher in folderCiphers = (ciphers | filter: { folderId: folder.id } |
|
||||
filter: cipherFilter | filter: (main.searchVaultText || '')) track by cipher.id">
|
||||
<tr ng-repeat="cipher in groupingCiphers = (ciphers | filter: cipherFilter(grouping) |
|
||||
filter: (main.searchVaultText || '')) track by cipher.id">
|
||||
<td style="width: 70px;">
|
||||
<div class="btn-group" data-append-to="body">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
||||
@ -212,7 +221,7 @@
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="cipher.organizationId && cipher.edit">
|
||||
<a href="#" stop-click ng-click="collections(cipher)">
|
||||
<a href="#" stop-click ng-click="editCollections(cipher)">
|
||||
<i class="fa fa-fw fa-cubes"></i> Collections
|
||||
</a>
|
||||
</li>
|
||||
@ -298,19 +307,37 @@
|
||||
<h3 class="control-sidebar-heading">
|
||||
<i class="fa fa-folder fa-fw"></i> Folders
|
||||
</h3>
|
||||
<div ng-show="loading && !vaultFolders.length">
|
||||
<div ng-show="loading && !vaultGroupings.length">
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
<div class="control-sidebar-section">
|
||||
<ul class="control-sidebar-menu" ng-show="!loading && vaultFolders.length">
|
||||
<li ng-repeat="folder in vaultFolders track by folder.id">
|
||||
<a href="#" stop-click ng-click="filterFolder(folder)">
|
||||
<i class="fa fa-check fa-fw" ng-if="folder.id === folderIdFilter"></i>
|
||||
<i class="fa fa-caret-right fa-fw" ng-if="folder.id !== folderIdFilter"></i>
|
||||
<ul class="control-sidebar-menu" ng-show="!loading && folders.length">
|
||||
<li ng-repeat="folder in folders = (vaultGroupings | filter: {folder: true}) track by folder.id">
|
||||
<a href="#" stop-click ng-click="filterGrouping(folder)">
|
||||
<i class="fa fa-check fa-fw" ng-if="folder.id === groupingIdFilter"></i>
|
||||
<i class="fa fa-caret-right fa-fw" ng-if="folder.id !== groupingIdFilter"></i>
|
||||
{{folder.name}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<h3 class="control-sidebar-heading">
|
||||
<i class="fa fa-cubes fa-fw"></i> Collections
|
||||
</h3>
|
||||
<div ng-show="loading && !vaultGroupings.length">
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
<div class="control-sidebar-section">
|
||||
<ul class="control-sidebar-menu" ng-show="!loading && collections.length">
|
||||
<li ng-repeat="collection in collections =
|
||||
(vaultGroupings | filter: {collection: true}) track by collection.id">
|
||||
<a href="#" stop-click ng-click="filterGrouping(collection)">
|
||||
<i class="fa fa-check fa-fw" ng-if="collection.id === groupingIdFilter"></i>
|
||||
<i class="fa fa-caret-right fa-fw" ng-if="collection.id !== groupingIdFilter"></i>
|
||||
{{collection.name}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
@ -1,108 +0,0 @@
|
||||
<section class="content-header">
|
||||
<h1>
|
||||
Shared
|
||||
<small>
|
||||
<span ng-pluralize
|
||||
count="collections.length > 0 && ciphers.length ? collections.length - 1 : collections.length"
|
||||
when="{'1': '{} collection', 'other': '{} collections'}"></span>,
|
||||
<span ng-pluralize count="ciphers.length" when="{'1': '{} item', 'other': '{} items'}"></span>
|
||||
</small>
|
||||
</h1>
|
||||
</section>
|
||||
<section class="content">
|
||||
<p ng-show="loading && !collections.length">Loading...</p>
|
||||
<div class="callout callout-default" style="background: #fff;" ng-show="!loading && !collections.length && !ciphers.length">
|
||||
<h4>Nothing shared <i class="fa fa-frown-o"></i></h4>
|
||||
<p>
|
||||
You do not have any items or collections being shared with you.
|
||||
To start sharing, create an organization or ask an existing organization to invite you.
|
||||
</p>
|
||||
<a ui-sref="backend.user.settingsCreateOrg" class="btn btn-default btn-flat">
|
||||
Create an Organization
|
||||
</a>
|
||||
</div>
|
||||
<div class="box" ng-class="{'collapsed-box': collection.collapsed}" ng-repeat="collection in collections |
|
||||
orderBy: collectionSort track by collection.id"
|
||||
ng-show="collections.length">
|
||||
<div class="box-header with-border">
|
||||
<h3 class="box-title">
|
||||
<i class="fa" ng-class="{'fa-cubes': collection.id, 'fa-sitemap': !collection.id}"></i>
|
||||
{{collection.name}}
|
||||
<small ng-pluralize count="collectionCiphers.length" when="{'1': '{} item', 'other': '{} items'}"></small>
|
||||
</h3>
|
||||
<div class="box-tools">
|
||||
<button type="button" class="btn btn-box-tool" data-widget="collapse" title="Collapse/Expand"
|
||||
ng-click="collapseExpand(collection)">
|
||||
<i class="fa" ng-class="{'fa-minus': !collection.collapsed, 'fa-plus': collection.collapsed}"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body" ng-class="{'no-padding': collectionCiphers.length}">
|
||||
<div ng-show="!collectionCiphers.length && collection.id">
|
||||
<p>No items in this collection.</p>
|
||||
<p>
|
||||
Share an item to this collection by selecting <i class="fa fa-share-alt"></i> <b>Share</b> or
|
||||
<i class="fa fa-cubes"></i> <b>Collections</b> from the item's options (<i class="fa fa-cog"></i>) menu.
|
||||
</p>
|
||||
</div>
|
||||
<div ng-show="!collectionCiphers.length && !collection.id">No unassigned items.</div>
|
||||
<div class="table-responsive" ng-show="collectionCiphers.length">
|
||||
<table class="table table-striped table-hover table-vmiddle">
|
||||
<tbody>
|
||||
<tr ng-repeat="cipher in collectionCiphers = (ciphers | filter: filterByCollection(collection) |
|
||||
orderBy: ['name', 'subTitle']) track by cipher.id">
|
||||
<td style="width: 70px;">
|
||||
<div class="btn-group" data-append-to="body">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
||||
<i class="fa fa-cog"></i> <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a href="#" stop-click ng-click="editCipher(cipher)">
|
||||
<i class="fa fa-fw fa-pencil"></i> Edit
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" stop-click ng-click="attachments(cipher)">
|
||||
<i class="fa fa-fw fa-paperclip"></i> Attachments
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="cipher.edit">
|
||||
<a href="#" stop-click ng-click="editCollections(cipher)">
|
||||
<i class="fa fa-fw fa-cubes"></i> Collections
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="cipher.meta.password">
|
||||
<a href="#" stop-click ngclipboard ngclipboard-error="clipboardError(e)"
|
||||
data-clipboard-text="{{cipher.meta.password}}">
|
||||
<i class="fa fa-fw fa-clipboard"></i> Copy Password
|
||||
</a>
|
||||
</li>
|
||||
<li ng-show="cipher.edit">
|
||||
<a href="#" stop-click ng-click="removeCipher(cipher, collection)"
|
||||
ng-if="collection.id" class="text-red">
|
||||
<i class="fa fa-fw fa-remove"></i> Remove
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
<td class="vault-icon">
|
||||
<i class="fa fa-fw fa-lg {{::cipher.icon}}" ng-if="!cipher.meta.image"></i>
|
||||
<img alt="" ng-if="cipher.meta.image" ng-src="{{cipher.meta.image}}"
|
||||
fallback-src="images/fa-globe.png" />
|
||||
</td>
|
||||
<td>
|
||||
<a href="#" stop-click ng-click="editCipher(cipher)">{{cipher.name}}</a>
|
||||
<i class="fa fa-star text-muted" title="Favorite" ng-show="cipher.favorite"></i>
|
||||
<i class="fa fa-paperclip text-muted" title="Attachments" ng-if="cipher.hasAttachments"
|
||||
stop-prop></i><br />
|
||||
<div class="text-sm text-muted">{{cipher.subTitle}}</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
@ -57,11 +57,6 @@
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="treeview" ng-class="{active: $state.is('backend.user.shared')}">
|
||||
<a ui-sref="backend.user.shared">
|
||||
<i class="fa fa-share-alt fa-fw"></i> <span>Shared</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="treeview" ng-class="{active: $state.is('backend.user.tools') ||
|
||||
$state.is('backend.user.reportsBreach')}">
|
||||
<a ui-sref="backend.user.tools"><i class="fa fa-wrench fa-fw"></i> <span>Tools</span></a>
|
||||
|
@ -223,7 +223,6 @@
|
||||
<script src="app/vault/vaultEditFolderController.js"></script>
|
||||
<script src="app/vault/vaultAddFolderController.js"></script>
|
||||
<script src="app/vault/vaultShareCipherController.js"></script>
|
||||
<script src="app/vault/vaultSharedController.js"></script>
|
||||
<script src="app/vault/vaultCipherCollectionsController.js"></script>
|
||||
<script src="app/vault/vaultMoveCiphersController.js"></script>
|
||||
<script src="app/vault/vaultAttachmentsController.js"></script>
|
||||
|
Loading…
Reference in New Issue
Block a user