Skip to content

Commit

Permalink
Merge pull request #36087 from nextcloud/enh/noid/improve-applicable-…
Browse files Browse the repository at this point in the history
…ext-storage

Improve saving applicable users in ext storage
  • Loading branch information
PVince81 authored Jan 16, 2023
2 parents f0b9b6e + 4f35cd5 commit bfcb269
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 32 deletions.
2 changes: 1 addition & 1 deletion apps/files_external/css/settings.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion apps/files_external/css/settings.css.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion apps/files_external/css/settings.scss
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
}

#externalStorage td {
& > input, & > select {
& > input:not(.applicableToAllUsers), & > select {
width: 100%;
}
}
Expand Down Expand Up @@ -101,6 +101,10 @@
background-color: rgba(134, 255, 110, 0.9);
}

#externalStorage td.applicable label {
display: inline-flex;
align-items: center;
}

#externalStorage td.applicable div.chzn-container {
position: relative;
Expand Down
96 changes: 72 additions & 24 deletions apps/files_external/js/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,28 @@ function getSelection($row) {
return values;
}

function getSelectedApplicable($row) {
var users = [];
var groups = [];
var multiselect = getSelection($row);
$.each(multiselect, function(index, value) {
// FIXME: don't rely on string parts to detect groups...
var pos = (value.indexOf)?value.indexOf('(group)'): -1;
if (pos !== -1) {
groups.push(value.substr(0, pos));
} else {
users.push(value);
}
});

// FIXME: this should be done in the multiselect change event instead
$row.find('.applicable')
.data('applicable-groups', groups)
.data('applicable-users', users);

return { users, groups };
}

function highlightBorder($element, highlight) {
$element.toggleClass('warning-input', highlight);
return highlight;
Expand Down Expand Up @@ -56,7 +78,7 @@ function highlightInput($input) {
* @param {Array<Object>} array of jQuery elements
* @param {number} userListLimit page size for result list
*/
function addSelect2 ($elements, userListLimit) {
function initApplicableUsersMultiselect($elements, userListLimit) {
var escapeHTML = function (text) {
return text.toString()
.split('&').join('&amp;')
Expand All @@ -68,8 +90,8 @@ function addSelect2 ($elements, userListLimit) {
if (!$elements.length) {
return;
}
$elements.select2({
placeholder: t('files_external', 'All users. Type to select user or group.'),
return $elements.select2({
placeholder: t('files_external', 'Type to select user or group.'),
allowClear: true,
multiple: true,
toggleSelect: true,
Expand Down Expand Up @@ -167,6 +189,8 @@ function addSelect2 ($elements, userListLimit) {
$div.avatar($div.data('name'),32);
}
});
}).on('change', function(event) {
highlightBorder($(event.target).closest('.applicableUsersContainer').find('.select2-choices'), !event.val.length);
});
}

Expand Down Expand Up @@ -721,6 +745,8 @@ MountConfigListView.prototype = _.extend({

this.$el.on('change', '.selectBackend', _.bind(this._onSelectBackend, this));
this.$el.on('change', '.selectAuthMechanism', _.bind(this._onSelectAuthMechanism, this));

this.$el.on('change', '.applicableToAllUsers', _.bind(this._onChangeApplicableToAllUsers, this));
},

_onChange: function(event) {
Expand All @@ -746,6 +772,7 @@ MountConfigListView.prototype = _.extend({

var onCompletion = jQuery.Deferred();
$tr = this.newStorage(storageConfig, onCompletion);
$tr.find('.applicableToAllUsers').prop('checked', false).trigger('change');
onCompletion.resolve();

$tr.find('td.configuration').children().not('[type=hidden]').first().focus();
Expand All @@ -764,6 +791,19 @@ MountConfigListView.prototype = _.extend({
this.saveStorageConfig($tr);
},

_onChangeApplicableToAllUsers: function(event) {
var $target = $(event.target);
var $tr = $target.closest('tr');
var checked = $target.is(':checked');

$tr.find('.applicableUsersContainer').toggleClass('hidden', checked);
if (!checked) {
$tr.find('.applicableUsers').select2('val', '', true);
}

this.saveStorageConfig($tr);
},

/**
* Configure the storage config with a new authentication mechanism
*
Expand Down Expand Up @@ -818,7 +858,7 @@ MountConfigListView.prototype = _.extend({
$tr.removeAttr('id');
$tr.find('select#selectBackend');
if (!deferAppend) {
addSelect2($tr.find('.applicableUsers'), this._userListLimit);
initApplicableUsersMultiselect($tr.find('.applicableUsers'), this._userListLimit);
}

if (storageConfig.id) {
Expand Down Expand Up @@ -890,7 +930,14 @@ MountConfigListView.prototype = _.extend({
})
);
}
$tr.find('.applicableUsers').val(applicable).trigger('change');
if (applicable.length) {
$tr.find('.applicableUsers').val(applicable).trigger('change')
$tr.find('.applicableUsersContainer').removeClass('hidden');
} else {
// applicable to all
$tr.find('.applicableUsersContainer').addClass('hidden');
}
$tr.find('.applicableToAllUsers').prop('checked', !applicable.length);

var priorityEl = $('<input type="hidden" class="priority" value="' + backend.priority + '" />');
$tr.append(priorityEl);
Expand Down Expand Up @@ -967,7 +1014,7 @@ MountConfigListView.prototype = _.extend({
}
$rows = $rows.add($tr);
});
addSelect2(self.$el.find('.applicableUsers'), this._userListLimit);
initApplicableUsersMultiselect(self.$el.find('.applicableUsers'), this._userListLimit);
self.$el.find('tr#addMountPoint').before($rows);
var mainForm = $('#files_external');
if (result.length === 0 && mainForm.attr('data-can-create') === 'false') {
Expand Down Expand Up @@ -1007,7 +1054,7 @@ MountConfigListView.prototype = _.extend({
}
$rows = $rows.add($tr);
});
addSelect2($rows.find('.applicableUsers'), this._userListLimit);
initApplicableUsersMultiselect($rows.find('.applicableUsers'), this._userListLimit);
self.$el.find('tr#addMountPoint').before($rows);
onCompletion.resolve();
onLoaded2.resolve();
Expand Down Expand Up @@ -1126,24 +1173,25 @@ MountConfigListView.prototype = _.extend({

// gather selected users and groups
if (!this._isPersonal) {
var groups = [];
var users = [];
var multiselect = getSelection($tr);
$.each(multiselect, function(index, value) {
var pos = (value.indexOf)?value.indexOf('(group)'): -1;
if (pos !== -1) {
groups.push(value.substr(0, pos));
} else {
users.push(value);
}
});
// FIXME: this should be done in the multiselect change event instead
$tr.find('.applicable')
.data('applicable-groups', groups)
.data('applicable-users', users);
var multiselect = getSelectedApplicable($tr);
var users = multiselect.users || [];
var groups = multiselect.groups || [];
var isApplicableToAllUsers = $tr.find('.applicableToAllUsers').is(':checked');

if (isApplicableToAllUsers) {
storage.applicableUsers = [];
storage.applicableGroups = [];
} else {
storage.applicableUsers = users;
storage.applicableGroups = groups;

storage.applicableUsers = users;
storage.applicableGroups = groups;
if (!storage.applicableUsers.length && !storage.applicableGroups.length) {
if (!storage.errors) {
storage.errors = {};
}
storage.errors['requiredApplicable'] = true;
}
}

storage.priority = parseInt($tr.find('input.priority').val() || '100', 10);
}
Expand Down
5 changes: 4 additions & 1 deletion apps/files_external/templates/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,10 @@ function writeParameterInput($parameter, $options, $classes = []) {
<td class="configuration"></td>
<?php if ($_['visibilityType'] === BackendService::VISIBILITY_ADMIN): ?>
<td class="applicable" align="right">
<input type="hidden" class="applicableUsers" style="width:20em;" value="" />
<label><input type="checkbox" class="applicableToAllUsers" checked="" /><?php p($l->t('All users')); ?></label>
<div class="applicableUsersContainer">
<input type="hidden" class="applicableUsers" style="width:20em;" value="" />
</div>
</td>
<?php endif; ?>
<td class="mountOptionsToggle hidden">
Expand Down
71 changes: 67 additions & 4 deletions apps/files_external/tests/js/settingsSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,13 @@ describe('OCA.Files_External.Settings tests', function() {

beforeEach(function() {
clock = sinon.useFakeTimers();
select2ApplicableUsers = [];
select2Stub = sinon.stub($.fn, 'select2').callsFake(function(args) {
if (args === 'val') {
return select2ApplicableUsers;
}
return {
on: function() {}
on: function() { return this; }
};
});

Expand All @@ -63,6 +64,7 @@ describe('OCA.Files_External.Settings tests', function() {
'<td class="authentication"></td>' +
'<td class="configuration"></td>' +
'<td class="applicable">' +
'<input type="checkbox" class="applicableToAllUsers">' +
'<input type="hidden" class="applicableUsers">' +
'</td>' +
'<td class="mountOptionsToggle">'+
Expand Down Expand Up @@ -172,6 +174,7 @@ describe('OCA.Files_External.Settings tests', function() {

function selectBackend(backendName) {
view.$el.find('.selectBackend:first').val(backendName).trigger('change');
view.$el.find('.applicableToAllUsers').prop('checked', true).trigger('change');
}

beforeEach(function() {
Expand Down Expand Up @@ -255,6 +258,59 @@ describe('OCA.Files_External.Settings tests', function() {

// TODO: respond and check data-id
});
it('saves storage with applicable users', function() {
var $field1 = $tr.find('input[data-parameter=field1]');
expect($field1.length).toEqual(1);
$field1.val('test');
$field1.trigger(new $.Event('keyup', {keyCode: 97}));

$tr.find('.applicableToAllUsers').prop('checked', false).trigger('change');
select2ApplicableUsers = ['user1', 'user2', 'group1(group)', 'group2(group)'];

var $saveButton = $tr.find('td.save .icon-checkmark');
$saveButton.click();

expect(fakeServer.requests.length).toEqual(1);
var request = fakeServer.requests[0];
expect(request.url).toEqual(OC.getRootPath() + '/index.php/apps/files_external/globalstorages');
expect(JSON.parse(request.requestBody)).toEqual({
backend: '\\OC\\TestBackend',
authMechanism: 'mechanism1',
backendOptions: {
'field1': 'test',
'field2': ''
},
mountPoint: 'TestBackend',
priority: 11,
applicableUsers: ['user1', 'user2'],
applicableGroups: ['group1', 'group2'],
mountOptions: {
encrypt: true,
previews: true,
enable_sharing: false,
filesystem_check_changes: 1,
encoding_compatibility: false,
readonly: false,
},
testOnly: true
});

// TODO: respond and check data-id
});
it('does not saves storage without applicable users and unchecked all users checkbox', function() {
var $field1 = $tr.find('input[data-parameter=field1]');
expect($field1.length).toEqual(1);
$field1.val('test');
$field1.trigger(new $.Event('keyup', {keyCode: 97}));

$tr.find('.applicableToAllUsers').prop('checked', false).trigger('change');

var $saveButton = $tr.find('td.save .icon-checkmark');
$saveButton.click();

expect(fakeServer.requests.length).toEqual(0);
});

it('saves storage after closing mount options popovermenu', function() {
$tr.find('.mountOptionsToggle .icon-more').click();
$tr.find('[name=previews]').trigger(new $.Event('keyup', {keyCode: 97}));
Expand All @@ -279,6 +335,16 @@ describe('OCA.Files_External.Settings tests', function() {
});

it('lists missing fields in storage errors', function() {
$tr.find('.applicableToAllUsers').prop('checked', false).trigger('change');
var storage = view.getStorageConfig($tr);

expect(storage.errors).toEqual({
backendOptions: ['field_text', 'field_password'],
requiredApplicable: true,
});
});

it('does not list applicable when all users checkbox is ticked', function() {
var storage = view.getStorageConfig($tr);

expect(storage.errors).toEqual({
Expand Down Expand Up @@ -399,9 +465,6 @@ describe('OCA.Files_External.Settings tests', function() {
});
});
});
describe('applicable user list', function() {
// TODO: test select2 retrieval logic
});
describe('allow user mounts section', function() {
// TODO: test allowUserMounting section
});
Expand Down

0 comments on commit bfcb269

Please sign in to comment.