]> source.dussan.org Git - nextcloud-server.git/commitdiff
Added ext storage mount options GUI
authorVincent Petry <pvince81@owncloud.com>
Mon, 16 Mar 2015 13:07:53 +0000 (14:07 +0100)
committerVincent Petry <pvince81@owncloud.com>
Thu, 26 Mar 2015 10:21:03 +0000 (11:21 +0100)
Added option to disable autoscan of external storages
Mount option file system scan is now an int
Move priority field to avoid undefined field in storage options

All input elements inside the storage options block get parsed into
storage options.

Moving the priority field outside prevents it to appear in the storage
config, as expected. It is still parsed by special code.

apps/files_external/css/settings.css
apps/files_external/js/settings.js
apps/files_external/templates/settings.php
apps/files_external/tests/js/settingsSpec.js
core/css/apps.css
core/js/l10n.js

index 93689f78c5286b3c4d5e342c847230f3bc364fe6..fb7789a537d9cee963d3732839c8bb273d6a09ea 100644 (file)
@@ -6,10 +6,11 @@ td.status > span {
 }
 
 td.mountPoint, td.backend { width:160px; }
-td.remove>img { visibility:hidden; padding-top:7px; }
-tr:hover>td.remove>img { visibility:visible; cursor:pointer; }
+#externalStorage td>img.action { visibility:hidden; padding-top:7px; }
+#externalStorage tr:hover>td>img.action { visibility:visible; cursor:pointer; }
 #addMountPoint>td { border:none; }
 #addMountPoint>td.applicable { visibility:hidden; }
+#addMountPoint>td.hidden { visibility:hidden; }
 
 #selectBackend {
        margin-left: -10px;
@@ -67,3 +68,11 @@ tr:hover>td.remove>img { visibility:visible; cursor:pointer; }
        top: 6px;
        position: relative;
 }
+
+#externalStorage .mountOptionsToggle .dropdown {
+       width: auto;
+}
+
+#externalStorage .mountOptionsDropdown {
+       margin-right: 40px;
+}
index 254bab30c5667a5f5edb07a573a8f226c245f395..ae3aef4ef35cc2b76fdad67361ca6bac8263a0ff 100644 (file)
@@ -7,8 +7,27 @@
  * See the COPYING-README file.
  *
  */
+
 (function(){
 
+// TODO: move to a separate file
+var MOUNT_OPTIONS_DROPDOWN_TEMPLATE =
+       '<div class="drop dropdown mountOptionsDropdown">' +
+       // FIXME: options are hard-coded for now
+       '       <div class="optionRow">' +
+       '               <label for="mountOptionsPreviews">{{t "files_external" "Enable previews"}}</label>' +
+       '               <input id="mountOptionsPreviews" name="previews" type="checkbox" value="true" checked="checked"/>' +
+       '       </div>' +
+       '       <div class="optionRow">' +
+       '               <label for="mountOptionsFilesystemCheck">{{t "files_external" "Check for changes"}}</label>' +
+       '               <select id="mountOptionsFilesystemCheck" name="filesystem_check_changes" data-type="int">' +
+       '                       <option value="0">{{t "files_external" "Never"}}</option>' +
+       '                       <option value="1" selected="selected">{{t "files_external" "Once every direct access"}}</option>' +
+       '                       <option value="2">{{t "files_external" "Every time the filesystem is used"}}</option>' +
+       '               </select>' +
+       '       </div>' +
+       '</div>';
+
 /**
  * Returns the selection of applicable users in the given configuration row
  *
@@ -219,7 +238,8 @@ StorageConfig.prototype = {
                $.ajax({
                        type: method,
                        url: url,
-                       data: this.getData(),
+                       contentType: 'application/json',
+                       data: JSON.stringify(this.getData()),
                        success: function(result) {
                                self.id = result.id;
                                if (_.isFunction(options.success)) {
@@ -285,7 +305,6 @@ StorageConfig.prototype = {
                        }
                        return;
                }
-               var self = this;
                $.ajax({
                        type: 'DELETE',
                        url: OC.generateUrl(this._url + '/{id}', {id: this.id}),
@@ -378,6 +397,110 @@ UserStorageConfig.prototype = _.extend({}, StorageConfig.prototype,
        _url: 'apps/files_external/userstorages'
 });
 
+/**
+ * @class OCA.External.Settings.MountOptionsDropdown
+ *
+ * @classdesc Dropdown for mount options
+ *
+ * @param {Object} $container container DOM object
+ */
+var MountOptionsDropdown = function() {
+};
+/**
+ * @memberof OCA.External.Settings
+ */
+MountOptionsDropdown.prototype = {
+       /**
+        * Dropdown element
+        *
+        * @var Object
+        */
+       $el: null,
+
+       /**
+        * Show dropdown
+        *
+        * @param {Object} $container container
+        * @param {Object} mountOptions mount options
+        */
+       show: function($container, mountOptions) {
+               if (MountOptionsDropdown._last) {
+                       MountOptionsDropdown._last.hide();
+               }
+
+               var template = MountOptionsDropdown._template;
+               if (!template) {
+                       template = Handlebars.compile(MOUNT_OPTIONS_DROPDOWN_TEMPLATE);
+                       MountOptionsDropdown._template = template;
+               }
+
+               var $el = $(template());
+               this.$el = $el;
+               $el.addClass('hidden');
+
+               this.setOptions(mountOptions);
+
+               this.$el.appendTo($container);
+               MountOptionsDropdown._last = this;
+
+               this.$el.trigger('show');
+       },
+
+       hide: function() {
+               if (this.$el) {
+                       this.$el.trigger('hide');
+                       this.$el.remove();
+                       this.$el = null;
+                       MountOptionsDropdown._last = null;
+               }
+       },
+
+       /**
+        * Returns the mount options from the dropdown controls
+        *
+        * @return {Object} options mount options
+        */
+       getOptions: function() {
+               var options = {};
+
+               this.$el.find('input, select').each(function() {
+                       var $this = $(this);
+                       var key = $this.attr('name');
+                       var value = null;
+                       if ($this.attr('type') === 'checkbox') {
+                               value = $this.prop('checked');
+                       } else {
+                               value = $this.val();
+                       }
+                       if ($this.attr('data-type') === 'int') {
+                               value = parseInt(value, 10);
+                       }
+                       options[key] = value;
+               });
+               return options;
+       },
+
+       /**
+        * Sets the mount options to the dropdown controls
+        *
+        * @param {Object} options mount options
+        */
+       setOptions: function(options) {
+               var $el = this.$el;
+               _.each(options, function(value, key) {
+                       var $optionEl = $el.find('input, select').filterAttr('name', key);
+                       if ($optionEl.attr('type') === 'checkbox') {
+                               if (_.isString(value)) {
+                                       value = (value === 'true');
+                               }
+                               $optionEl.prop('checked', !!value);
+                       } else {
+                               $optionEl.val(value);
+                       }
+               });
+       }
+};
+
 /**
  * @class OCA.External.Settings.MountConfigListView
  *
@@ -503,12 +626,20 @@ MountConfigListView.prototype = {
                        self.deleteStorageConfig($(this).closest('tr'));
                });
 
+               this.$el.on('click', 'td.mountOptionsToggle>img', function() {
+                       self._showMountOptionsDropdown($(this).closest('tr'));
+               });
+
                this.$el.on('change', '.selectBackend', _.bind(this._onSelectBackend, this));
        },
 
        _onChange: function(event) {
                var self = this;
                var $target = $(event.target);
+               if ($target.closest('.dropdown').length) {
+                       // ignore dropdown events
+                       return;
+               }
                highlightInput($target);
                var $tr = $target.closest('tr');
 
@@ -569,6 +700,7 @@ MountConfigListView.prototype = {
                        }
                });
                $tr.find('td').last().attr('class', 'remove');
+               $tr.find('td.mountOptionsToggle').removeClass('hidden');
                $tr.find('td').last().removeAttr('style');
                $tr.removeAttr('id');
                $target.remove();
@@ -643,7 +775,7 @@ MountConfigListView.prototype = {
                        storage.applicableUsers = users;
                        storage.applicableGroups = groups;
 
-                       storage.priority = $tr.find('input.priority').val();
+                       storage.priority = parseInt($tr.find('input.priority').val() || '100', 10);
                }
 
                var mountOptions = $tr.find('input.mountOptions').val();
@@ -786,6 +918,47 @@ MountConfigListView.prototype = {
                        }
                }
                return defaultMountPoint + append;
+       },
+       
+       /**
+        * Toggles the mount options dropdown
+        *
+        * @param {Object} $tr configuration row
+        */     
+       _showMountOptionsDropdown: function($tr) {
+               if (this._preventNextDropdown) {
+                       // prevented because the click was on the toggle
+                       this._preventNextDropdown = false;
+                       return;
+               }
+               var self = this;
+               var storage = this.getStorageConfig($tr);
+               var $toggle = $tr.find('.mountOptionsToggle');
+               var dropDown = new MountOptionsDropdown();
+               dropDown.show($toggle, storage.mountOptions || []);
+
+               $('body').on('mouseup.mountOptionsDropdown', function(event) {
+                       var $target = $(event.target);
+                       if ($toggle.has($target).length) {
+                               // why is it always so hard to make dropdowns behave ?
+                               // this prevents the click on the toggle to cause
+                               // the dropdown to reopen itself
+                               // (preventDefault doesn't work here because the click
+                               // event is already in the queue and cannot be cancelled)
+                               self._preventNextDropdown = true;
+                       }
+                       if ($target.closest('.dropdown').length) {
+                               return;
+                       }
+                       dropDown.hide();
+               });
+
+               dropDown.$el.on('hide', function() {
+                       var mountOptions = dropDown.getOptions();
+                       $('body').off('mouseup.mountOptionsDropdown');
+                       $tr.find('input.mountOptions').val(JSON.stringify(mountOptions));
+                       self.saveStorageConfig($tr);
+               });
        }
 };
 
index 4866d177634e345df6b1890a9d175a6716f6f89b..967c59debe670e5840d7c05a02c86a19c31f4c86 100644 (file)
@@ -10,6 +10,7 @@
                                <th><?php p($l->t('Configuration')); ?></th>
                                <?php if ($_['isAdminPage']) print_unescaped('<th>'.$l->t('Available for').'</th>'); ?>
                                <th>&nbsp;</th>
+                               <th>&nbsp;</th>
                        </tr>
                </thead>
                <tbody>
                                                        <?php OCP\Util::addScript('files_external', $_['backends'][$mount['class']]['custom']); ?>
                                                <?php endif; ?>
                                        <?php endif; ?>
-                                       <?php if (isset($mount['mountOptions'])): ?>
-                                       <input type="hidden" class="mountOptions" value="<?php p(json_encode($mount['mountOptions'])) ?>" />
-                                       <?php endif; ?>
-                                       <?php if ($_['isAdminPage']): ?>
-                                       <?php if (isset($mount['priority'])): ?>
-                                       <input type="hidden" class="priority" value="<?php p($mount['priority']) ?>" />
-                                       <?php endif; ?>
-                                       <?php endif; ?>
                                </td>
                                <?php if ($_['isAdminPage']): ?>
                                <td class="applicable"
                                        <input type="hidden" class="applicableUsers" style="width:20em;" value=""/>
                                </td>
                                <?php endif; ?>
+                               <td class="mountOptionsToggle <?php if (!isset($mount['mountpoint'])) { p('hidden'); } ?>"
+                                       ><img
+                                               class="svg action"
+                                               title="<?php p($l->t('Advanced settings')); ?>"
+                                               alt="<?php p($l->t('Advanced settings')); ?>"
+                                               src="<?php print_unescaped(image_path('core', 'actions/settings.svg')); ?>" />
+                                       <input type="hidden" class="mountOptions" value="<?php isset($mount['mountOptions']) ? p(json_encode($mount['mountOptions'])) : '' ?>" />
+                                       <?php if ($_['isAdminPage']): ?>
+                                       <?php if (isset($mount['priority'])): ?>
+                                       <input type="hidden" class="priority" value="<?php p($mount['priority']) ?>" />
+                                       <?php endif; ?>
+                                       <?php endif; ?>
+                               </td>
                                <td <?php if (isset($mount['mountpoint'])): ?>class="remove"
-                                       <?php else: ?>style="visibility:hidden;"
+                                       <?php else: ?>class="hidden"
                                        <?php endif ?>><img alt="<?php p($l->t('Delete')); ?>"
                                                                                title="<?php p($l->t('Delete')); ?>"
                                                                                class="svg action"
index 5a3ee2cb5f17ca97c2e376daa8bc7ac94493a03d..f030965835a72196579e273dc55407ec3967a1bb 100644 (file)
@@ -43,6 +43,7 @@ describe('OCA.External.Settings tests', function() {
                        '<td class="applicable">' +
                        '<input type="hidden" class="applicableUsers">' +
                        '</td>' +
+                       '<td class="mountOptionsToggle"><input type="hidden" class="mountOptions"/><img class="svg action"/></td>' +
                        '<td><img alt="Delete" title="Delete" class="svg action"/></td>' +
                        '</tr>' +
                        '</tbody>' +
@@ -116,30 +117,57 @@ describe('OCA.External.Settings tests', function() {
                        // TODO: test suggested mount point logic
                });
                describe('saving storages', function() {
-                       it('saves storage after editing config', function() {
-                               var $tr = view.$el.find('tr:first');
-                               selectBackend('\\OC\\TestBackend');
+                       var $tr;
 
+                       beforeEach(function() {
+                               $tr = view.$el.find('tr:first');
+                               selectBackend('\\OC\\TestBackend');
+                       });
+                       it('saves storage after editing config', function() {
                                var $field1 = $tr.find('input[data-parameter=field1]');
                                expect($field1.length).toEqual(1);
                                $field1.val('test');
                                $field1.trigger(new $.Event('keyup', {keyCode: 97}));
 
+                               var $mountOptionsField = $tr.find('input.mountOptions');
+                               expect($mountOptionsField.length).toEqual(1);
+                               $mountOptionsField.val(JSON.stringify({previews:true}));
+
                                clock.tick(4000);
 
                                expect(fakeServer.requests.length).toEqual(1);
                                var request = fakeServer.requests[0];
                                expect(request.url).toEqual(OC.webroot + '/index.php/apps/files_external/globalstorages');
-                               expect(OC.parseQueryString(request.requestBody)).toEqual({
+                               expect(JSON.parse(request.requestBody)).toEqual({
                                        backendClass: '\\OC\\TestBackend',
-                                       'backendOptions[field1]': 'test',
-                                       'backendOptions[field2]': '',
+                                       backendOptions: {
+                                               'field1': 'test',
+                                               'field2': ''
+                                       },
                                        mountPoint: 'TestBackend',
-                                       priority: '11'
+                                       priority: 11,
+                                       applicableUsers: [],
+                                       applicableGroups: [],
+                                       mountOptions: {
+                                               'previews': true
+                                       }
                                });
 
                                // TODO: respond and check data-id
                        });
+                       it('saves storage after closing mount options dropdown', function() {
+                               $tr.find('.mountOptionsToggle img').click();
+                               $tr.find('[name=previews]').trigger(new $.Event('keyup', {keyCode: 97}));
+                               $tr.find('input[data-parameter=field1]').val('test');
+
+                               // does not save inside the dropdown
+                               expect(fakeServer.requests.length).toEqual(0);
+
+                               $('body').mouseup();
+
+                               // but after closing the dropdown
+                               expect(fakeServer.requests.length).toEqual(1);
+                       });
                        // TODO: tests with "applicableUsers" and "applicableGroups"
                        // TODO: test with non-optional config parameters
                        // TODO: test with missing mount point value
@@ -157,6 +185,52 @@ describe('OCA.External.Settings tests', function() {
                describe('recheck storages', function() {
                        // TODO
                });
+               describe('mount options dropdown', function() {
+                       var $tr;
+                       var $td;
+
+                       beforeEach(function() {
+                               $tr = view.$el.find('tr:first');
+                               $td = $tr.find('.mountOptionsToggle');
+                               selectBackend('\\OC\\TestBackend');
+                       });
+
+                       it('shows dropdown when clicking on toggle button, hides when clicking outside', function() {
+                               $td.find('img').click();
+
+                               expect($td.find('.dropdown').length).toEqual(1);
+
+                               $('body').mouseup();
+
+                               expect($td.find('.dropdown').length).toEqual(0);
+                       });
+
+                       it('reads config from mountOptions field', function() {
+                               $tr.find('input.mountOptions').val(JSON.stringify({previews:false}));
+
+                               $td.find('img').click();
+                               expect($td.find('.dropdown [name=previews]').prop('checked')).toEqual(false);
+                               $('body').mouseup();
+
+                               $tr.find('input.mountOptions').val(JSON.stringify({previews:true}));
+                               $td.find('img').click();
+                               expect($td.find('.dropdown [name=previews]').prop('checked')).toEqual(true);
+                       });
+
+                       it('writes config into mountOptions field', function() {
+                               $td.find('img').click();
+                               // defaults to true
+                               var $field = $td.find('.dropdown [name=previews]');
+                               expect($field.prop('checked')).toEqual(true);
+                               $td.find('.dropdown [name=filesystem_check_changes]').val(2);
+                               $('body').mouseup();
+
+                               expect(JSON.parse($tr.find('input.mountOptions').val())).toEqual({
+                                       previews: true,
+                                       filesystem_check_changes: 2
+                               });
+                       });
+               });
        });
        describe('applicable user list', function() {
                // TODO: test select2 retrieval logic
index c2816decc5d798ba4d5d21d17a817e453048db2f..2bac961d36f3cfbeb42ea631a61a3f39cbcb5c2b 100644 (file)
@@ -528,3 +528,19 @@ em {
        -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
        opacity: .5;
 }
+
+/* generic dropdown style */
+.dropdown {
+       background:#eee;
+       border-bottom-left-radius: 5px;
+       border-bottom-right-radius: 5px;
+       box-shadow:0 1px 1px #777;
+       display:block;
+       margin-right: 0;
+       position:absolute;
+       right:0;
+       width:420px;
+       z-index:500;
+       padding:16px;
+}
+
index 60ffa9491914ab07f5934888dfed5f934c099a62..fb93d7b789e166ccc490ef406a407358ecaf99a1 100644 (file)
@@ -226,3 +226,7 @@ window.t = _.bind(OC.L10N.translate, OC.L10N);
  */
 window.n = _.bind(OC.L10N.translatePlural, OC.L10N);
 
+Handlebars.registerHelper('t', function(app, text) {
+       return OC.L10N.translate(app, text);
+});
+