diff options
author | brantje <brantje@gmail.com> | 2017-08-13 15:23:23 +0300 |
---|---|---|
committer | brantje <brantje@gmail.com> | 2017-10-29 15:31:18 +0300 |
commit | c2c4d613d82f4cf45321da3b7ca26dd2a1d6d442 (patch) | |
tree | 92a9755415e5643cf354f96638792d5b719cbdbb /js | |
parent | d1103e6db369e0198bc89517c2cfc6225b23b14a (diff) |
Add icons to credentials
Diffstat (limited to 'js')
-rw-r--r-- | js/app/directives/icon.js | 55 | ||||
-rw-r--r-- | js/app/directives/iconpicker.js | 75 | ||||
-rw-r--r-- | js/app/services/credentialservice.js | 4 | ||||
-rw-r--r-- | js/app/services/iconservice.js | 48 | ||||
-rw-r--r-- | js/templates.js | 12 |
5 files changed, 191 insertions, 3 deletions
diff --git a/js/app/directives/icon.js b/js/app/directives/icon.js new file mode 100644 index 00000000..de0c00e3 --- /dev/null +++ b/js/app/directives/icon.js @@ -0,0 +1,55 @@ +/** + * Nextcloud - passman + * + * @copyright Copyright (c) 2016, Sander Brand (brantje@gmail.com) + * @copyright Copyright (c) 2016, Marcos Zuriaga Miguel (wolfi@wolfi.es) + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +(function() { + 'use strict'; + + /** + * @ngdoc directive + * @name passmanApp.directive:passwordGen + * @description + * # passwordGen + */ + angular.module('passmanApp').directive('credentialIcon', [ + '$window', function($window) { + return { + template: '<img ng-src="{{ iconUrl }}" width="16">', + restrict: 'E', + scope: { + credential: '=', + }, + link: function(scope, element) { + if (!scope.credential) { + return; + } + scope.$watch('credential', function() { + if(scope.credential.icon && scope.credential.icon.type){ + scope.iconUrl = 'data:image/'+ scope.credential.icon.type +';base64,' + scope.credential.icon.content; + } else { + var url = window.btoa(angular.copy(scope.credential.url)).replace('/','_'); + scope.iconUrl = OC.generateUrl('apps/passman/api/v2/icon/') + url + '/'+ scope.credential.credential_id; + } + }, true); + } + }; + }]); +}());
\ No newline at end of file diff --git a/js/app/directives/iconpicker.js b/js/app/directives/iconpicker.js new file mode 100644 index 00000000..57667875 --- /dev/null +++ b/js/app/directives/iconpicker.js @@ -0,0 +1,75 @@ +/** + * Nextcloud - passman + * + * @copyright Copyright (c) 2016, Sander Brand (brantje@gmail.com) + * @copyright Copyright (c) 2016, Marcos Zuriaga Miguel (wolfi@wolfi.es) + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +(function() { + 'use strict'; + + /** + * @ngdoc directive + * @name passmanApp.directive:passwordGen + * @description + * # passwordGen + */ + angular.module('passmanApp').directive('iconPicker', [ + '$window', 'IconService', '$http', function($window, IconService, $http) { + return { + templateUrl: 'views/partials/icon-picker.html', + restrict: 'A', + scope: { + credential: '=iconPicker' + }, + link: function(scope, element) { + + IconService.getIcons().then(function(icons) { + scope.icons = icons; + }); + + scope.selectIcon = function(icon) { + scope.selectedIcon = icon; + }; + + scope.useIcon = function() { + $http.get(scope.selectedIcon.url).then(function(result) { + var base64Data = window.btoa(result.data); + var mimeType = 'svg+xml'; + if(!scope.credential.icon){ + scope.credential.icon = {}; + } + scope.credential.icon.type = mimeType; + scope.credential.icon.content = base64Data; + $('#iconPicker').dialog('close'); + }); + }; + + $(element).click(function() { + $('#iconPicker').dialog({ + width: 800, + height: 380, + close: function() { + $(this).dialog('destroy'); + } + }); + }); + } + }; + }]); +}());
\ No newline at end of file diff --git a/js/app/services/credentialservice.js b/js/app/services/credentialservice.js index 77ebe7b1..4c5d5f3b 100644 --- a/js/app/services/credentialservice.js +++ b/js/app/services/credentialservice.js @@ -41,6 +41,10 @@ 'changed': null, 'tags': [], 'email': null, + 'icon':{ + 'type': false, + 'content': '' + }, 'username': null, 'password': null, 'url': null, diff --git a/js/app/services/iconservice.js b/js/app/services/iconservice.js new file mode 100644 index 00000000..323d484b --- /dev/null +++ b/js/app/services/iconservice.js @@ -0,0 +1,48 @@ +/** + * Nextcloud - passman + * + * @copyright Copyright (c) 2016, Sander Brand (brantje@gmail.com) + * @copyright Copyright (c) 2016, Marcos Zuriaga Miguel (wolfi@wolfi.es) + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +(function () { + 'use strict'; + + /** + * @ngdoc service + * @name passmanApp.FileService + * @description + * # FileService + * Service in the passmanApp. + */ + angular.module('passmanApp') + .service('IconService', ['$http', function ($http) { + return { + getIcons: function () { + var queryUrl = OC.generateUrl('apps/passman/api/v2/icon/list'); + return $http.get(queryUrl).then(function (response) { + if (response.data) { + return response.data; + } else { + return response; + } + }); + } + }; + }]); +}());
\ No newline at end of file diff --git a/js/templates.js b/js/templates.js index 67d6c2e1..c09bfb34 100644 --- a/js/templates.js +++ b/js/templates.js @@ -1,4 +1,4 @@ -angular.module('templates-main', ['views/credential_revisions.html', 'views/edit_credential.html', 'views/partials/credential_template.html', 'views/partials/forms/edit_credential/basics.html', 'views/partials/forms/edit_credential/custom_fields.html', 'views/partials/forms/edit_credential/files.html', 'views/partials/forms/edit_credential/otp.html', 'views/partials/forms/edit_credential/password.html', 'views/partials/forms/settings/export.html', 'views/partials/forms/settings/general_settings.html', 'views/partials/forms/settings/generic_csv_import.html', 'views/partials/forms/settings/import.html', 'views/partials/forms/settings/password_settings.html', 'views/partials/forms/settings/sharing.html', 'views/partials/forms/settings/tool.html', 'views/partials/forms/share_credential/basics.html', 'views/partials/forms/share_credential/link_sharing.html', 'views/partials/password-meter.html', 'views/settings.html', 'views/share_credential.html', 'views/show_vault.html', 'views/vault_req_deletion.html', 'views/vaults.html']); +angular.module('templates-main', ['views/credential_revisions.html', 'views/edit_credential.html', 'views/partials/credential_template.html', 'views/partials/forms/edit_credential/basics.html', 'views/partials/forms/edit_credential/custom_fields.html', 'views/partials/forms/edit_credential/files.html', 'views/partials/forms/edit_credential/otp.html', 'views/partials/forms/edit_credential/password.html', 'views/partials/forms/settings/export.html', 'views/partials/forms/settings/general_settings.html', 'views/partials/forms/settings/generic_csv_import.html', 'views/partials/forms/settings/import.html', 'views/partials/forms/settings/password_settings.html', 'views/partials/forms/settings/sharing.html', 'views/partials/forms/settings/tool.html', 'views/partials/forms/share_credential/basics.html', 'views/partials/forms/share_credential/link_sharing.html', 'views/partials/icon-picker.html', 'views/partials/password-meter.html', 'views/settings.html', 'views/share_credential.html', 'views/show_vault.html', 'views/vault_req_deletion.html', 'views/vaults.html']); angular.module('views/credential_revisions.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; @@ -21,7 +21,7 @@ angular.module('views/partials/credential_template.html', []).run(['$templateCac angular.module('views/partials/forms/edit_credential/basics.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/partials/forms/edit_credential/basics.html', - '<div class="row"><div class="col-xs-12 col-md-6"><label>{{ \'label\' | translate}}</label><div><input type="text" ng-model="storedCredential.label"></div><label>{{ \'username\' | translate}}</label><div><input type="text" ng-model="storedCredential.username"></div><label>{{ \'email\' | translate}}</label><div><input type="text" ng-model="storedCredential.email"></div><label>{{ \'password\' | translate}}</label><div><password-gen ng-model="storedCredential.password" settings="pwSettings" callback="pwGenerated"></password-gen><ng-password-meter password="storedCredential.password"></ng-password-meter></div><div><label>{{ \'password.r\' | translate}}</label><input type="password" ng-model="storedCredential.password_repeat"></div><label>{{ \'url\' | translate}}</label><div><input type="text" ng-model="storedCredential.url"></div></div><div class="col-xs-12 col-md-6"><label>{{ \'notes\' | translate}}</label><div><textarea class="credential_textarea" ng-model="storedCredential.description"></textarea></div><label>{{ \'add.tag\' | translate}}</label><div class="tags_input"><tags-input ng-model="storedCredential.tags" replace-spaces-with-dashes="false"><auto-complete source="getTags($query)" min-length="0"></auto-complete></tags-input></div></div></div>'); + '<div class="row"><div class="col-xs-12 col-md-6"><label>{{ \'label\' | translate}}</label><div class="icon-label"><div class="icon-picker" icon-picker="storedCredential"></div><input type="text" class="form-control" ng-model="storedCredential.label"></div><label>{{ \'username\' | translate}}</label><div><input type="text" ng-model="storedCredential.username"></div><label>{{ \'email\' | translate}}</label><div><input type="text" ng-model="storedCredential.email"></div><label>{{ \'password\' | translate}}</label><div><password-gen ng-model="storedCredential.password" settings="pwSettings" callback="pwGenerated"></password-gen><ng-password-meter password="storedCredential.password"></ng-password-meter></div><div><label>{{ \'password.r\' | translate}}</label><input type="password" ng-model="storedCredential.password_repeat"></div><label>{{ \'url\' | translate}}</label><div><input type="text" ng-model="storedCredential.url"></div></div><div class="col-xs-12 col-md-6"><label>{{ \'notes\' | translate}}</label><div><textarea class="credential_textarea" ng-model="storedCredential.description"></textarea></div><label>{{ \'add.tag\' | translate}}</label><div class="tags_input"><tags-input ng-model="storedCredential.tags" replace-spaces-with-dashes="false"><auto-complete source="getTags($query)" min-length="0"></auto-complete></tags-input></div></div></div>'); }]); angular.module('views/partials/forms/edit_credential/custom_fields.html', []).run(['$templateCache', function ($templateCache) { @@ -104,6 +104,12 @@ angular.module('views/partials/forms/share_credential/link_sharing.html', []).ru '<div class="row"><div class="col-xs-12 col-md-6"><label><input type="checkbox" ng-model="share_settings.linkSharing.enabled"> {{ \'enable.link.sharing\' | translate}}.</label><br><div class="pull-left col-xs-6 nopadding"><span credential-field value="share_link" secret="false" use-input="true" input-placeholder="\'click.share\' | translate"></span></div><div ng-show="share_settings.linkSharing.enabled" class="clearfix">{{ \'share.until.date\' | translate}} <span datetime-picker ng-model="share_settings.linkSharing.settings.expire_time" class="link" future-only close-on-select="false" timestamp="true">{{ share_settings.linkSharing.settings.expire_time | date:\'dd-MM-yyyy @ HH:mm:ss\' }}</span></div><div ng-show="share_settings.linkSharing.enabled">{{ \'expire.views\' | translate}}<br><input type="number" ng-model="share_settings.linkSharing.settings.expire_views"></div><div ng-if="share_settings.linkSharing.enabled"><table><tr><td><input type="checkbox" ng-click="setPermission(share_settings.linkSharing.settings.acl, default_permissions.permissions.FILES)" ng-checked="hasPermission(share_settings.linkSharing.settings.acl, default_permissions.permissions.FILES)"></td><td>{{ \'show.files\' | translate}}</td></tr></table></div></div></div>'); }]); +angular.module('views/partials/icon-picker.html', []).run(['$templateCache', function ($templateCache) { + 'use strict'; + $templateCache.put('views/partials/icon-picker.html', + '<div><div class="cell"><credential-icon credential="credential"></credential-icon></div><div style="display: none" id="iconPicker" title="{{ \'pick.icon\' | translate }}"><div class="iconList"><div class="icon" ng-repeat="icon in icons" ng-click="selectIcon(icon)"><img ng-src="{{icon.url}}" height="32"></div></div><div class="iconModifier">Showing {{ icons.length }} icons<div ng-if="selectedIcon">{{ \'selected.icon\' | translate}}:<br><img ng-src="{{selectedIcon.url}}" height="32"><br><button ng-click="useIcon()" ng-if="selectedIcon">{{ \'use.icon\' | translate}}</button></div></div></div></div>'); +}]); + angular.module('views/partials/password-meter.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/partials/password-meter.html', @@ -125,7 +131,7 @@ angular.module('views/share_credential.html', []).run(['$templateCache', functio angular.module('views/show_vault.html', []).run(['$templateCache', function ($templateCache) { 'use strict'; $templateCache.put('views/show_vault.html', - '<div off-click="closeSelected()" off-click-filter="\'.download-js-link, .sidebar-shown\'"><div id="passman-controls" ng-class="{ \'sidebar-shown\': selectedCredential }"><div class="breadcrumb"><div class="breadcrumb"><div class="crumb svg ui-droppable" data-dir="/"><a><i class="fa fa-home"></i></a></div><div class="crumb svg last" ng-click="clearState()"><span>{{active_vault.name}}</span></div></div></div><div class="actions creatable"><span ng-click="addCredential()" class="button new"><span>+</span></span></div><div class="title" credential-counter="filtered_credentials" vault="active_vault" delete-time="delete_time" filters="filterOptions"></div><div class="searchboxContainer" ng-init="filterOptionShown = false;" off-click="filterOptionShown = false;"><input type="text" ng-model="filterOptions.filterText" class="searchbox" id="searchBox" placeholder="{{\'search.credential\' | translate}}" select-on-click clear-btn ng-click="filterOptionShown = true;"><div class="searchOptions" ng-show="filterOptionShown"><input type="checkbox" ng-model="filterOptions.useRegex"> {{ \'use.regex\' | translate }}</div></div><div class="viewModes"><div class="view-mode" ng-class="{\'active\': view_mode === \'list\' }" ng-click="switchViewMode(\'list\')"><i class="fa fa-list"></i></div><div class="view-mode" ng-class="{\'active\': view_mode === \'grid\' }" ng-click="switchViewMode(\'grid\')"><i class="fa fa-th-large"></i></div></div></div><div class="loaderContainer" ng-if="show_spinner"><div class="loader" use-theme type="\'border-bottom-color\'"></div></div><div ng-init="menuOpen = false;"><table class="credential-table" ng-if="view_mode === \'list\'"><tr ng-repeat="credential in filtered_credentials | orderBy:\'label\'" ng-if="showCredentialRow(credential)" ng-click="selectCredential(credential)" ng-dblclick="editCredential(credential)" ng-class="{\'selected\': selectedCredential.credential_id == credential.credential_id}"><td><span class="tags"><span class="tag" ng-repeat="tag in credential.tags_raw">{{ ::tag.text}}</span> </span><span class="icon"><i class="fa fa-lock" ng-if="!credential.acl && !credential.shared_key"></i> <i class="fa fa-share-alt" ng-if="credential.acl"></i> <i class="fa fa-share-alt-square" ng-if="credential.shared_key"></i> </span><span class="label">{{ ::credential.label}}</span></td></tr></table><ul class="grid-view" ng-if="view_mode === \'grid\'"><li class="credential" ng-repeat="credential in filtered_credentials | orderBy:\'label\'" ng-if="credential.hidden == 0 && showCredentialRow(credential)" ng-click="selectCredential(credential)" use-theme type="\'border-color\'"><div class="credential_content"><div class="label">{{ ::credential.label}}</div><div class="tags"><div class="tag" ng-repeat="tag in credential.tags_raw">{{ ::tag.text}}</div></div></div></li></ul></div><div id="app-sidebar" class="detailsView scroll-container app_sidebar" ng-show="selectedCredential"><h2>{{selectedCredential.label}}</h2><span class="close icon-close" ng-click="closeSelected()" alt="Close"></span><div credential-template="selectedCredential"></div><div ng-show="selectedCredential"><div><button class="button" ng-click="editCredential(selectedCredential)" ng-if="selectedCredential.delete_time == 0 && hasPermission(selectedCredential.acl.permissions, permissions.permissions.WRITE)"><span class="fa fa-edit"></span> {{ \'edit\' | translate}}</button> <button class="button" ng-click="deleteCredential(selectedCredential)" ng-if="selectedCredential.delete_time == 0 && hasPermission(selectedCredential.acl.permissions, permissions.permissions.WRITE)"><span class="fa fa-trash"></span> {{ \'delete\' | translate}}</button> <button class="button" ng-click="shareCredential(selectedCredential)" ng-if="selectedCredential.delete_time == 0 && selectedCredential.acl === undefined &&\n' + + '<div off-click="closeSelected()" off-click-filter="\'.download-js-link, .sidebar-shown\'"><div id="passman-controls" ng-class="{ \'sidebar-shown\': selectedCredential }"><div class="breadcrumb"><div class="breadcrumb"><div class="crumb svg ui-droppable" data-dir="/"><a><i class="fa fa-home"></i></a></div><div class="crumb svg last" ng-click="clearState()"><span>{{active_vault.name}}</span></div></div></div><div class="actions creatable"><span ng-click="addCredential()" class="button new"><span>+</span></span></div><div class="title" credential-counter="filtered_credentials" vault="active_vault" delete-time="delete_time" filters="filterOptions"></div><div class="searchboxContainer" ng-init="filterOptionShown = false;" off-click="filterOptionShown = false;"><input type="text" ng-model="filterOptions.filterText" class="searchbox" id="searchBox" placeholder="{{\'search.credential\' | translate}}" select-on-click clear-btn ng-click="filterOptionShown = true;"><div class="searchOptions" ng-show="filterOptionShown"><input type="checkbox" ng-model="filterOptions.useRegex"> {{ \'use.regex\' | translate }}</div></div><div class="viewModes"><div class="view-mode" ng-class="{\'active\': view_mode === \'list\' }" ng-click="switchViewMode(\'list\')"><i class="fa fa-list"></i></div><div class="view-mode" ng-class="{\'active\': view_mode === \'grid\' }" ng-click="switchViewMode(\'grid\')"><i class="fa fa-th-large"></i></div></div></div><div class="loaderContainer" ng-if="show_spinner"><div class="loader" use-theme type="\'border-bottom-color\'"></div></div><div ng-init="menuOpen = false;"><table class="credential-table" ng-if="view_mode === \'list\'"><tr ng-repeat="credential in filtered_credentials | orderBy:\'label\'" ng-if="showCredentialRow(credential)" ng-click="selectCredential(credential)" ng-dblclick="editCredential(credential)" ng-class="{\'selected\': selectedCredential.credential_id == credential.credential_id}"><td><span class="tags"><span class="tag" ng-repeat="tag in credential.tags_raw">{{ ::tag.text}}</span> </span><span class="icon" ng-if="credential.url"><credential-icon credential="credential"></credential-icon></span><span class="icon" ng-if="!credential.url"><i class="fa fa-lock" ng-if="!credential.acl && !credential.shared_key"></i> <i class="fa fa-share-alt" ng-if="credential.acl"></i> <i class="fa fa-share-alt-square" ng-if="credential.shared_key"></i> </span><span class="label">{{ ::credential.label}}</span></td></tr></table><ul class="grid-view" ng-if="view_mode === \'grid\'"><li class="credential" ng-repeat="credential in filtered_credentials | orderBy:\'label\'" ng-if="credential.hidden == 0 && showCredentialRow(credential)" ng-click="selectCredential(credential)" use-theme type="\'border-color\'"><div class="credential_content"><div class="label">{{ ::credential.label}}</div><div class="tags"><div class="tag" ng-repeat="tag in credential.tags_raw">{{ ::tag.text}}</div></div></div></li></ul></div><div id="app-sidebar" class="detailsView scroll-container app_sidebar" ng-show="selectedCredential"><h2>{{selectedCredential.label}}</h2><span class="close icon-close" ng-click="closeSelected()" alt="Close"></span><div credential-template="selectedCredential"></div><div ng-show="selectedCredential"><div><button class="button" ng-click="editCredential(selectedCredential)" ng-if="selectedCredential.delete_time == 0 && hasPermission(selectedCredential.acl.permissions, permissions.permissions.WRITE)"><span class="fa fa-edit"></span> {{ \'edit\' | translate}}</button> <button class="button" ng-click="deleteCredential(selectedCredential)" ng-if="selectedCredential.delete_time == 0 && hasPermission(selectedCredential.acl.permissions, permissions.permissions.WRITE)"><span class="fa fa-trash"></span> {{ \'delete\' | translate}}</button> <button class="button" ng-click="shareCredential(selectedCredential)" ng-if="selectedCredential.delete_time == 0 && selectedCredential.acl === undefined &&\n' + ' (settings.user_sharing_enabled === 1 || settings.user_sharing_enabled === \'1\' || settings.link_sharing_enabled === 1 || settings.link_sharing_enabled === \'1\')"><span class="fa fa-share"></span> {{ \'share\' | translate}}</button> <button class="button" ng-click="getRevisions(selectedCredential)" ng-if="selectedCredential.delete_time == 0 && hasPermission(selectedCredential.acl.permissions, permissions.permissions.HISTORY)"><span class="fa fa-undo"></span> {{ \'revisions\' | translate}}</button> <button class="button" ng-if="selectedCredential.delete_time > 0" ng-click="recoverCredential(selectedCredential) && hasPermission(selectedCredential.acl.permissions, permissions.permissions.WRITE)"><span class="fa fa-recycle"></span> {{\'recover\' | translate}}</button> <button class="button" ng-if="selectedCredential.delete_time > 0" ng-click="destroyCredential(selectedCredential)"><span class="fa fa-bomb"></span> {{\'destroy\' | translate}}</button></div></div></div></div><div class="share_popup" style="display: none">{{ \'sharereq.title\' | translate}}<br><p>{{ \'sharereq.line1\' | translate}}</p>{{active_vault.vault_id}}<table class="table"><thead><tr><td>{{ \'label\' | translate}}</td><td>{{ \'permissions\' | translate}}</td><td>{{ \'received.from\' | translate}}</td><td>{{ \'date\' | translate}}</td></tr></thead><tr ng-repeat="share_request in incoming_share_requests" ng-if="share_request.target_vault_id == active_vault.vault_id"><td>{{share_request.credential_label}}</td><td>{{share_request.permissions}}</td><td>{{share_request.from_user_id}}</td><td>{{share_request.created * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}</td><td><span class="link" ng-click="acceptShareRequest(share_request)">{{ \'accept\' | translate}}</span> | <span class="link" ng-click="declineShareRequest(share_request)">{{ \'decline\' | translate}}</span></td></tr></table></div>'); }]); |