mirror of
https://github.com/nextcloud/passman.git
synced 2025-09-24 13:54:31 +08:00
Improvements in credential field, showing items
This commit is contained in:
parent
1225848212
commit
7f0f297aa3
11 changed files with 222 additions and 108 deletions
28
css/app.css
28
css/app.css
|
@ -260,7 +260,7 @@
|
|||
-webkit-box-shadow: 0px -4px 3px rgba(150, 150, 150, 0.75);
|
||||
-moz-box-shadow: 0px -4px 3px rgba(150, 150, 150, 0.75);
|
||||
box-shadow: 0px -4px 3px rgba(150, 150, 150, 0.75);
|
||||
padding-top: 10px;
|
||||
padding: 10px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
background-color: #eee;
|
||||
|
@ -268,12 +268,30 @@
|
|||
right: 0;
|
||||
display: none;
|
||||
overflow-y: auto; }
|
||||
#app-content #app-content-wrapper .view_pane .row {
|
||||
margin-left: 0;
|
||||
margin-right: 0; }
|
||||
#app-content #app-content-wrapper .view_pane.item_selected {
|
||||
height: 20%;
|
||||
height: 25%;
|
||||
display: inline-block; }
|
||||
#app-content #app-content-wrapper .view_pane table {
|
||||
width: 100%; }
|
||||
#app-content #app-content-wrapper .view_pane table tr td:first-child {
|
||||
width: 15%; }
|
||||
#app-content #app-content-wrapper .view_pane table tr td {
|
||||
width: auto;
|
||||
white-space: nowrap;
|
||||
height: 20px;
|
||||
vertical-align: middle; }
|
||||
|
||||
.credential_field {
|
||||
overflow: hidden; }
|
||||
.credential_field .cell, .credential_field .value {
|
||||
float: left; }
|
||||
.credential_field .tools {
|
||||
margin-left: 10px;
|
||||
overflow: hidden;
|
||||
float: left; }
|
||||
.credential_field .tools .cell {
|
||||
cursor: pointer;
|
||||
margin-right: 4px; }
|
||||
|
||||
.settings-container div {
|
||||
padding-left: 15px; }
|
||||
|
|
File diff suppressed because one or more lines are too long
55
js/app/directives/credentialfield.js
Normal file
55
js/app/directives/credentialfield.js
Normal file
|
@ -0,0 +1,55 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc directive
|
||||
* @name passmanApp.directive:passwordGen
|
||||
* @description
|
||||
* # passwordGen
|
||||
*/
|
||||
|
||||
angular.module('passmanApp')
|
||||
.directive('credentialField', ['$timeout', function ($timeout) {
|
||||
return {
|
||||
scope: {
|
||||
value: '=value',
|
||||
secret: '=secret'
|
||||
},
|
||||
restrict: 'A',
|
||||
replace: 'true',
|
||||
template: "" +
|
||||
'<span class="credential_field">' +
|
||||
'<div class="value">' +
|
||||
'<span ng-repeat="n in [] | range:value.length" ng-if="!valueVisible">*</span>' +
|
||||
'<span ng-if="valueVisible">{{value}}</span>' +
|
||||
'</div>' +
|
||||
'<div class="tools">' +
|
||||
'<div class="cell" ng-if="toggle" tooltip="\'Toggle visibility\'" ng-click="toggleVisibility()"><i class="fa" ng-class="{\'fa-eye\': !valueVisible, \'fa-eye-slash\': valueVisible }"></i></div>' +
|
||||
'<div class="cell" tooltip="copy_msg"><i class="fa fa-clipboard" ngclipboard-success="onSuccess(e);" ngclipboard-error="onError(e);" ngclipboard data-clipboard-text="{{value}}"></i></div>' +
|
||||
'</div></span>',
|
||||
link: function (scope, elem, attrs, modelCtrl) {
|
||||
scope.$watch("value", function () {
|
||||
if (scope.secret) {
|
||||
scope.valueVisible = false;
|
||||
}
|
||||
});
|
||||
if (!scope.toggle) {
|
||||
if (scope.secret) {
|
||||
scope.toggle = true;
|
||||
}
|
||||
}
|
||||
scope.copy_msg = 'Copy to clipboard';
|
||||
var timer;
|
||||
scope.onSuccess = function () {
|
||||
scope.copy_msg = 'Copied to clipboard!';
|
||||
$timeout.cancel(timer);
|
||||
timer = $timeout(function () {
|
||||
scope.copy_msg = 'Copy to clipboard';
|
||||
}, 5000)
|
||||
}
|
||||
scope.valueVisible = true;
|
||||
scope.toggleVisibility = function () {
|
||||
scope.valueVisible = !scope.valueVisible;
|
||||
};
|
||||
}
|
||||
};
|
||||
}]);
|
|
@ -15,9 +15,16 @@ angular.module('passmanApp')
|
|||
},
|
||||
|
||||
link: function (scope, el, attr, ctrl) {
|
||||
scope.$watch('tooltip', function(){
|
||||
$(el).attr('title', scope.tooltip);
|
||||
$(el).tooltip()
|
||||
scope.$watch('tooltip', function (newVal, old) {
|
||||
if (scope.tooltip) {
|
||||
$(el).attr('title', scope.tooltip);
|
||||
$(el).tooltip();
|
||||
$(el).attr('title', scope.tooltip).tooltip('fixTitle');
|
||||
if($(el).is(':visible')){
|
||||
$(el).tooltip('show')
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
|
|
20
js/app/filters/byte.js
Normal file
20
js/app/filters/byte.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc filter
|
||||
* @name passmanApp.filter:decrypt
|
||||
* @function
|
||||
* @description
|
||||
* # decrypt
|
||||
* Filter in the passmanApp.
|
||||
*/
|
||||
angular.module('passmanApp')
|
||||
.filter('bytes', function () {
|
||||
return function (bytes, precision) {
|
||||
if (isNaN(parseFloat(bytes)) || !isFinite(bytes)) return '-';
|
||||
if (typeof precision === 'undefined') precision = 1;
|
||||
var units = ['bytes', 'kB', 'MB', 'GB', 'TB', 'PB'],
|
||||
number = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||
return (bytes / Math.pow(1024, Math.floor(number))).toFixed(precision) + ' ' + units[number];
|
||||
}
|
||||
});
|
|
@ -1,20 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* @ngdoc filter
|
||||
* @name passmanApp.filter:decrypt
|
||||
* @function
|
||||
* @description
|
||||
* # decrypt
|
||||
* Filter in the passmanApp.
|
||||
*/
|
||||
angular.module('passmanApp')
|
||||
.filter('decrypt',['EncryptService', function (EncryptService) {
|
||||
return function (input) {
|
||||
if(input) {
|
||||
var string = EncryptService.decryptString(input).toString();
|
||||
console.log(string);
|
||||
return string;
|
||||
}
|
||||
};
|
||||
}]);
|
|
@ -23,7 +23,7 @@ angular.module('views/partials/forms/edit_credential/custom_fields.html', []).ru
|
|||
angular.module('views/partials/forms/edit_credential/files.html', []).run(['$templateCache', function($templateCache) {
|
||||
'use strict';
|
||||
$templateCache.put('views/partials/forms/edit_credential/files.html',
|
||||
'<div class="row file_tab"><div class="col-xs-12 col-md-6"><input type="file" file-select success="fileLoaded" error="fileLoadError" progress="fileSelectProgress"> <span ng-if="fileprogress.file_percent > 0"><div progress-bar="fileprogress.file_percent"></div></span></div></div><div class="row files" ng-if="storedCredential.files.length > 0"><div class="col-xs-12 table"><table><thead use-theme><tr><th class="field_label">Filename</th><th class="field_value">Upload date</th><th class="field_secret">Size</th><th class="field_actions">Actions</th></tr></thead><tr ng-repeat="file in storedCredential.files"><td><a href="#" editable-text="file.filename">{{ file.filename || "empty" }}</a></td><td>{{file.created}}</td><td>{{file.size}}</td><td class="field_actions"><i class="fa fa-trash" ng-click="deleteFile(file)"></i></td></tr></table></div></div>');
|
||||
'<div class="row file_tab"><div class="col-xs-12 col-md-6"><input type="file" file-select success="fileLoaded" error="fileLoadError" progress="fileSelectProgress"> <span ng-if="fileprogress.file_percent > 0"><div progress-bar="fileprogress.file_percent"></div></span></div></div><div class="row files" ng-if="storedCredential.files.length > 0"><div class="col-xs-12 table"><table><thead use-theme><tr><th class="field_label">Filename</th><th class="field_value">Upload date</th><th class="field_secret">Size</th><th class="field_actions">Actions</th></tr></thead><tr ng-repeat="file in storedCredential.files"><td><a href="#" editable-text="file.filename">{{ file.filename || "empty" }}</a></td><td>{{file.created}}</td><td>{{file.size | bytes}}</td><td class="field_actions"><i class="fa fa-trash" ng-click="deleteFile(file)"></i></td></tr></table></div></div>');
|
||||
}]);
|
||||
|
||||
angular.module('views/partials/forms/edit_credential/otp.html', []).run(['$templateCache', function($templateCache) {
|
||||
|
@ -47,7 +47,7 @@ angular.module('views/partials/password-meter.html', []).run(['$templateCache',
|
|||
angular.module('views/show_vault.html', []).run(['$templateCache', function($templateCache) {
|
||||
'use strict';
|
||||
$templateCache.put('views/show_vault.html',
|
||||
'<div id="controls"><div class="breadcrumb"></div><div class="actions creatable"><span ng-click="menuOpen = !menuOpen" class="button new" ng-init="menuOpen = false" off-click="menuOpen = false;"><span>New</span></span><div class="actionList popovermenu bubble menu" ng-show="menuOpen"><ul><li><span ng-click="addCredential()" class="menuitem action"><span class="icon icon-rename"></span> <span>New credential</span></span></li><li><span href="#" class="menuitem action"><span class="icon icon-shared"></span> <span>New shared credential</span></span></li></ul></div></div></div><table class="credential-table" ng-init="menuOpen = false;"><tr ng-repeat="credential in credentials" ng-if="credential.hidden == 0 && credential.delete_time == 0" ng-click="selectCredential(credential)" ng-class="{\'selected\': selectedCredential.credential_id == credential.credential_id}"><td><span class="label">{{credential.label}}</span> <span class="icon icon-more" ng-click="menuOpen = !menuOpen" off-click="menuOpen = false;"></span> <span class="tags"><span class="tag">Tag 1</span> <span class="tag">Tag 2</span> <span class="tag">Tag 4</span> <span class="tag">Tag 4</span> <span class="tag">Long tag xD</span></span><div class="actionList popovermenu bubble menu" ng-show="menuOpen"><ul><li ng-click="editCredential(credential)"><span class="menuitem action"><span class="icon icon-rename"></span><span>Edit</span></span></li><li><span href="#" class="menuitem action"><span class="icon icon-share"></span><span>Share</span></span></li><li><span class="menuitem action" data-action="Delete"><span class="icon icon-delete"></span><span>Delete</span></span></li></ul></div></td></tr></table><div class="view_pane" ng-class="{\'item_selected\': selectedCredential}"><div class="row"><div class="col-xs-2 col-sm-2 col-md-2">Label</div><div class="col-xs-6 col-sm-4 col-md-4">{{selectedCredential.label}}</div></div><div class="row"><div class="col-xs-2 col-sm-2 col-md-2">Account</div><div class="col-xs-6 col-sm-4 col-md-4">{{selectedCredential.username}}</div></div><div class="row"><div class="col-xs-2 col-sm-2 col-md-2">Password</div><div class="col-xs-6 col-sm-4 col-md-4">{{selectedCredential.password}}</div></div><div class="row"><div class="col-xs-2 col-sm-2 col-md-2">E-mail</div><div class="col-xs-6 col-sm-4 col-md-4">{{selectedCredential.email}}</div></div><div class="row"><div class="col-xs-2 col-sm-2 col-md-2">Files</div><div class="col-xs-6 col-sm-4 col-md-4"><div ng-repeat="file in selectedCredential.files" class="link">{{file.filename}} ({{file.size}})</div></div></div><div class="row" ng-repeat="field in selectedCredential.custom_fields"><div class="col-xs-2 col-sm-2 col-md-2">{{field.label}}</div><div class="col-xs-6 col-sm-4 col-md-4">{{field.value}} {{field.secret}}</div></div><div class="row"><div class="col-xs-2 col-sm-2 col-md-2">Changed</div><div class="col-xs-6 col-sm-4 col-md-4">{{selectedCredential.changed * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}</div></div><div class="row"><div class="col-xs-2 col-sm-2 col-md-2">Created</div><div class="col-xs-6 col-sm-4 col-md-4">{{selectedCredential.created * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}</div></div></div>');
|
||||
'<div id="controls"><div class="breadcrumb"></div><div class="actions creatable"><span ng-click="menuOpen = !menuOpen" class="button new" ng-init="menuOpen = false" off-click="menuOpen = false;"><span>New</span></span><div class="actionList popovermenu bubble menu" ng-show="menuOpen"><ul><li><span ng-click="addCredential()" class="menuitem action"><span class="icon icon-rename"></span> <span>New credential</span></span></li><li><span href="#" class="menuitem action"><span class="icon icon-shared"></span> <span>New shared credential</span></span></li></ul></div></div></div><table class="credential-table" ng-init="menuOpen = false;"><tr ng-repeat="credential in credentials" ng-if="credential.hidden == 0 && credential.delete_time == 0" ng-click="selectCredential(credential)" ng-class="{\'selected\': selectedCredential.credential_id == credential.credential_id}"><td><span class="label">{{credential.label}}</span> <span class="icon icon-more" ng-click="menuOpen = !menuOpen" off-click="menuOpen = false;"></span> <span class="tags"><span class="tag">Tag 1</span> <span class="tag">Tag 2</span> <span class="tag">Tag 4</span> <span class="tag">Tag 4</span> <span class="tag">Long tag xD</span></span><div class="actionList popovermenu bubble menu" ng-show="menuOpen"><ul><li ng-click="editCredential(credential)"><span class="menuitem action"><span class="icon icon-rename"></span><span>Edit</span></span></li><li><span href="#" class="menuitem action"><span class="icon icon-share"></span><span>Share</span></span></li><li><span class="menuitem action" data-action="Delete"><span class="icon icon-delete"></span><span>Delete</span></span></li></ul></div></td></tr></table><div class="view_pane" ng-class="{\'item_selected\': selectedCredential}"><table><tr><td>Label</td><td>{{selectedCredential.label}}</td></tr><tr ng-if="selectedCredential.username"><td>Account</td><td><span credential-field value="selectedCredential.username"></span></td></tr><tr ng-if="selectedCredential.password"><td>Password</td><td><span credential-field value="selectedCredential.password" secret="\'true\'"></span></td></tr><tr ng-if="selectedCredential.email"><td>E-mail</td><td><span credential-field value="selectedCredential.email"></span></td></tr><tr ng-if="selectedCredential.url"><td>URL</td><td><span credential-field value="selectedCredential.url"></span></td></tr><tr ng-if="selectedCredential.files.length > 0"><td>Files</td><td><div ng-repeat="file in selectedCredential.files" class="link">{{file.filename}} ({{file.size | bytes}})</div></td></tr><tr ng-repeat="field in selectedCredential.custom_fields"><td>{{field.label}}</td><td><span credential-field value="field.value" secret="field.secret"></span></td></tr><tr><td>Changed</td><td>{{selectedCredential.changed * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}</td></tr><tr><td>Created</td><td>{{selectedCredential.created * 1000 | date:\'dd-MM-yyyy @ HH:mm:ss\'}}</td></tr></table></div>');
|
||||
}]);
|
||||
|
||||
angular.module('views/vaults.html', []).run(['$templateCache', function($templateCache) {
|
||||
|
|
|
@ -175,13 +175,9 @@
|
|||
-webkit-box-shadow: 0px -4px 3px rgba(150, 150, 150, 0.75);
|
||||
-moz-box-shadow: 0px -4px 3px rgba(150, 150, 150, 0.75);
|
||||
box-shadow: 0px -4px 3px rgba(150, 150, 150, 0.75);
|
||||
padding-top: 10px;
|
||||
.row {
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
padding: 10px;
|
||||
&.item_selected {
|
||||
height: 20%;
|
||||
height: 25%;
|
||||
display: inline-block;
|
||||
}
|
||||
position: absolute;
|
||||
|
@ -191,6 +187,36 @@
|
|||
right: 0;
|
||||
display: none;
|
||||
overflow-y: auto;
|
||||
table {
|
||||
width: 100%;
|
||||
tr {
|
||||
td:first-child {
|
||||
width: 15%;
|
||||
}
|
||||
td {
|
||||
width: auto;
|
||||
white-space: nowrap;
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.credential_field {
|
||||
overflow: hidden;
|
||||
.cell, .value {
|
||||
float: left;
|
||||
}
|
||||
.tools {
|
||||
margin-left: 10px;
|
||||
overflow: hidden;
|
||||
float: left;
|
||||
.cell{
|
||||
cursor: pointer;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,7 +30,7 @@ script('passman', 'app/controllers/credential');
|
|||
script('passman', 'app/controllers/edit_credential');
|
||||
script('passman', 'app/filters/range');
|
||||
script('passman', 'app/filters/propsfilter');
|
||||
script('passman', 'app/filters/decrypt');
|
||||
script('passman', 'app/filters/byte');
|
||||
script('passman', 'app/services/cacheservice');
|
||||
script('passman', 'app/services/vaultservice');
|
||||
script('passman', 'app/services/credentialservice');
|
||||
|
@ -44,6 +44,7 @@ script('passman', 'app/directives/otp');
|
|||
script('passman', 'app/directives/qrreader');
|
||||
script('passman', 'app/directives/tooltip');
|
||||
script('passman', 'app/directives/use-theme');
|
||||
script('passman', 'app/directives/credentialfield');
|
||||
|
||||
|
||||
/*
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<td>
|
||||
{{file.created}}
|
||||
</td>
|
||||
<td>{{file.size}}</td>
|
||||
<td>{{file.size | bytes}}</td>
|
||||
<td class="field_actions">
|
||||
<i class="fa fa-trash" ng-click="deleteFile(file)"></i>
|
||||
</td>
|
||||
|
|
|
@ -66,75 +66,82 @@
|
|||
</table>
|
||||
|
||||
<div class="view_pane" ng-class="{'item_selected': selectedCredential}" >
|
||||
<div class="row">
|
||||
<div class="col-xs-2 col-sm-2 col-md-2">
|
||||
Label
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-4 col-md-4">
|
||||
{{selectedCredential.label}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-2 col-sm-2 col-md-2">
|
||||
Account
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-4 col-md-4">
|
||||
{{selectedCredential.username}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-2 col-sm-2 col-md-2">
|
||||
Password
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-4 col-md-4">
|
||||
{{selectedCredential.password}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-2 col-sm-2 col-md-2">
|
||||
E-mail
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-4 col-md-4">
|
||||
{{selectedCredential.email}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xs-2 col-sm-2 col-md-2">
|
||||
Files
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-4 col-md-4">
|
||||
<div ng-repeat="file in selectedCredential.files" class="link">
|
||||
{{file.filename}} ({{file.size}})
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" ng-repeat="field in selectedCredential.custom_fields">
|
||||
<div class="col-xs-2 col-sm-2 col-md-2">
|
||||
{{field.label}}
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-4 col-md-4">
|
||||
{{field.value}}
|
||||
{{field.secret}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-2 col-sm-2 col-md-2">
|
||||
Changed
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-4 col-md-4">
|
||||
{{selectedCredential.changed * 1000 | date:'dd-MM-yyyy @ HH:mm:ss'}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-xs-2 col-sm-2 col-md-2">
|
||||
Created
|
||||
</div>
|
||||
<div class="col-xs-6 col-sm-4 col-md-4">
|
||||
{{selectedCredential.created * 1000 | date:'dd-MM-yyyy @ HH:mm:ss'}}
|
||||
</div>
|
||||
</div>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
Label
|
||||
</td>
|
||||
<td>
|
||||
{{selectedCredential.label}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="selectedCredential.username">
|
||||
<td>
|
||||
Account
|
||||
</td>
|
||||
<td>
|
||||
<span credential-field value="selectedCredential.username"></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="selectedCredential.password">
|
||||
<td>
|
||||
Password
|
||||
</td>
|
||||
<td>
|
||||
<span credential-field value="selectedCredential.password" secret="'true'"></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="selectedCredential.email">
|
||||
<td>
|
||||
E-mail
|
||||
</td>
|
||||
<td>
|
||||
<span credential-field value="selectedCredential.email"></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="selectedCredential.url">
|
||||
<td>
|
||||
URL
|
||||
</td>
|
||||
<td>
|
||||
<span credential-field value="selectedCredential.url"></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-if="selectedCredential.files.length > 0">
|
||||
<td>
|
||||
Files
|
||||
</td>
|
||||
<td>
|
||||
<div ng-repeat="file in selectedCredential.files" class="link">
|
||||
{{file.filename}} ({{file.size | bytes}})
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-repeat="field in selectedCredential.custom_fields">
|
||||
<td>
|
||||
{{field.label}}
|
||||
</td>
|
||||
<td>
|
||||
<span credential-field value="field.value" secret="field.secret"></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Changed
|
||||
</td>
|
||||
<td>
|
||||
{{selectedCredential.changed * 1000 | date:'dd-MM-yyyy @ HH:mm:ss'}}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Created
|
||||
</td>
|
||||
<td>
|
||||
{{selectedCredential.created * 1000 | date:'dd-MM-yyyy @ HH:mm:ss'}}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
</div>
|
Loading…
Add table
Reference in a new issue