This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | /* |
||
2 | * Copyright (c) 2014 |
||
3 | * |
||
4 | * This file is licensed under the Affero General Public License version 3 |
||
5 | * or later. |
||
6 | * |
||
7 | * See the COPYING-README file. |
||
8 | * |
||
9 | */ |
||
10 | |||
11 | (function(){ |
||
12 | |||
13 | // TODO: move to a separate file |
||
14 | var MOUNT_OPTIONS_DROPDOWN_TEMPLATE = |
||
15 | '<div class="drop dropdown mountOptionsDropdown">' + |
||
16 | // FIXME: options are hard-coded for now |
||
17 | ' <div class="optionRow">' + |
||
18 | ' <input id="mountOptionsEncrypt" name="encrypt" type="checkbox" value="true" checked="checked"/>' + |
||
19 | ' <label for="mountOptionsEncrypt">{{t "files_external" "Enable encryption"}}</label>' + |
||
20 | ' </div>' + |
||
21 | ' <div class="optionRow">' + |
||
22 | ' <input id="mountOptionsPreviews" name="previews" type="checkbox" value="true" checked="checked"/>' + |
||
23 | ' <label for="mountOptionsPreviews">{{t "files_external" "Enable previews"}}</label>' + |
||
24 | ' </div>' + |
||
25 | ' <div class="optionRow">' + |
||
26 | ' <input id="mountOptionsSharing" name="enable_sharing" type="checkbox" value="true"/>' + |
||
27 | ' <label for="mountOptionsSharing">{{t "files_external" "Enable sharing"}}</label>' + |
||
28 | ' </div>' + |
||
29 | ' <div class="optionRow">' + |
||
30 | ' <label for="mountOptionsFilesystemCheck">{{t "files_external" "Check for changes"}}</label>' + |
||
31 | ' <select id="mountOptionsFilesystemCheck" name="filesystem_check_changes" data-type="int">' + |
||
32 | ' <option value="0">{{t "files_external" "Never"}}</option>' + |
||
33 | ' <option value="1" selected="selected">{{t "files_external" "Once every direct access"}}</option>' + |
||
34 | ' </select>' + |
||
35 | ' </div>' + |
||
36 | ' <div class="optionRow">' + |
||
37 | ' <input id="mountOptionsEncoding" name="encoding_compatibility" type="checkbox" value="true"/>' + |
||
38 | ' <label for="mountOptionsEncoding">{{mountOptionsEncodingLabel}}</label>' + |
||
39 | ' </div>' + |
||
40 | '</div>'; |
||
41 | |||
42 | /** |
||
43 | * Returns the selection of applicable users in the given configuration row |
||
44 | * |
||
45 | * @param $row configuration row |
||
46 | * @return array array of user names |
||
47 | */ |
||
48 | function getSelection($row) { |
||
49 | var values = $row.find('.applicableUsers').select2('val'); |
||
50 | if (!values || values.length === 0) { |
||
51 | values = []; |
||
52 | } |
||
53 | return values; |
||
54 | } |
||
55 | |||
56 | function highlightBorder($element, highlight) { |
||
57 | $element.toggleClass('warning-input', highlight); |
||
58 | return highlight; |
||
59 | } |
||
60 | |||
61 | function isInputValid($input) { |
||
62 | var optional = $input.hasClass('optional'); |
||
63 | switch ($input.attr('type')) { |
||
64 | case 'text': |
||
65 | case 'password': |
||
66 | if ($input.val() === '' && !optional) { |
||
67 | return false; |
||
68 | } |
||
69 | break; |
||
70 | } |
||
71 | return true; |
||
72 | } |
||
73 | |||
74 | function highlightInput($input) { |
||
75 | switch ($input.attr('type')) { |
||
76 | case 'text': |
||
77 | case 'password': |
||
78 | return highlightBorder($input, !isInputValid($input)); |
||
79 | } |
||
80 | } |
||
81 | |||
82 | /** |
||
83 | * Initialize select2 plugin on the given elements |
||
84 | * |
||
85 | * @param {Array<Object>} array of jQuery elements |
||
86 | * @param {int} userListLimit page size for result list |
||
87 | */ |
||
88 | function addSelect2 ($elements, userListLimit) { |
||
89 | if (!$elements.length) { |
||
90 | return; |
||
91 | } |
||
92 | $elements.select2({ |
||
93 | placeholder: t('files_external', 'All users. Type to select user or group.'), |
||
94 | allowClear: true, |
||
95 | multiple: true, |
||
96 | //minimumInputLength: 1, |
||
97 | ajax: { |
||
98 | url: OC.generateUrl('apps/files_external/applicable'), |
||
99 | dataType: 'json', |
||
100 | quietMillis: 100, |
||
101 | data: function (term, page) { // page is the one-based page number tracked by Select2 |
||
102 | return { |
||
103 | pattern: term, //search term |
||
104 | limit: userListLimit, // page size |
||
105 | offset: userListLimit*(page-1) // page number starts with 0 |
||
106 | }; |
||
107 | }, |
||
108 | results: function (data) { |
||
109 | if (data.status === 'success') { |
||
110 | |||
111 | var results = []; |
||
112 | var userCount = 0; // users is an object |
||
113 | |||
114 | // add groups |
||
115 | $.each(data.groups, function(i, group) { |
||
116 | results.push({name:group+'(group)', displayname:group, type:'group' }); |
||
117 | }); |
||
118 | // add users |
||
119 | $.each(data.users, function(id, user) { |
||
120 | userCount++; |
||
121 | results.push({name:id, displayname:user, type:'user' }); |
||
122 | }); |
||
123 | |||
124 | |||
125 | var more = (userCount >= userListLimit) || (data.groups.length >= userListLimit); |
||
126 | return {results: results, more: more}; |
||
127 | } else { |
||
128 | //FIXME add error handling |
||
129 | } |
||
130 | } |
||
131 | }, |
||
132 | initSelection: function(element, callback) { |
||
133 | var users = {}; |
||
134 | users['users'] = []; |
||
135 | var toSplit = element.val().split(","); |
||
136 | for (var i = 0; i < toSplit.length; i++) { |
||
137 | users['users'].push(toSplit[i]); |
||
138 | } |
||
139 | |||
140 | $.ajax(OC.generateUrl('displaynames'), { |
||
141 | type: 'POST', |
||
142 | contentType: 'application/json', |
||
143 | data: JSON.stringify(users), |
||
144 | dataType: 'json' |
||
145 | }).done(function(data) { |
||
146 | var results = []; |
||
147 | if (data.status === 'success') { |
||
148 | $.each(data.users, function(user, displayname) { |
||
149 | if (displayname !== false) { |
||
150 | results.push({name:user, displayname:displayname, type:'user'}); |
||
151 | } |
||
152 | }); |
||
153 | callback(results); |
||
154 | } else { |
||
155 | //FIXME add error handling |
||
156 | } |
||
157 | }); |
||
158 | }, |
||
159 | id: function(element) { |
||
160 | return element.name; |
||
161 | }, |
||
162 | formatResult: function (element) { |
||
163 | var $result = $('<span><div class="avatardiv"/><span>'+escapeHTML(element.displayname)+'</span></span>'); |
||
164 | var $div = $result.find('.avatardiv') |
||
165 | .attr('data-type', element.type) |
||
166 | .attr('data-name', element.name) |
||
167 | .attr('data-displayname', element.displayname); |
||
168 | if (element.type === 'group') { |
||
169 | var url = OC.imagePath('core','places/contacts-dark'); // TODO better group icon |
||
170 | $div.html('<img width="32" height="32" src="'+url+'">'); |
||
171 | } |
||
172 | return $result.get(0).outerHTML; |
||
173 | }, |
||
174 | formatSelection: function (element) { |
||
175 | if (element.type === 'group') { |
||
176 | return '<span title="'+escapeHTML(element.name)+'" class="group">'+escapeHTML(element.displayname+' '+t('files_external', '(group)'))+'</span>'; |
||
177 | } else { |
||
178 | return '<span title="'+escapeHTML(element.name)+'" class="user">'+escapeHTML(element.displayname)+'</span>'; |
||
179 | } |
||
180 | }, |
||
181 | escapeMarkup: function (m) { return m; } // we escape the markup in formatResult and formatSelection |
||
182 | }).on('select2-loaded', function() { |
||
183 | $.each($('.avatardiv'), function(i, div) { |
||
184 | var $div = $(div); |
||
185 | if ($div.data('type') === 'user') { |
||
186 | $div.avatar($div.data('name'),32); |
||
187 | } |
||
188 | }); |
||
189 | }); |
||
190 | } |
||
191 | |||
192 | /** |
||
193 | * @class OCA.External.Settings.StorageConfig |
||
194 | * |
||
195 | * @classdesc External storage config |
||
196 | */ |
||
197 | var StorageConfig = function(id) { |
||
198 | this.id = id; |
||
199 | this.backendOptions = {}; |
||
200 | }; |
||
201 | // Keep this in sync with \OC_Mount_Config::STATUS_* |
||
202 | StorageConfig.Status = { |
||
203 | IN_PROGRESS: -1, |
||
204 | SUCCESS: 0, |
||
205 | ERROR: 1, |
||
206 | INDETERMINATE: 2 |
||
207 | }; |
||
208 | StorageConfig.Visibility = { |
||
209 | NONE: 0, |
||
210 | PERSONAL: 1, |
||
211 | ADMIN: 2, |
||
212 | DEFAULT: 3 |
||
213 | }; |
||
214 | /** |
||
215 | * @memberof OCA.External.Settings |
||
216 | */ |
||
217 | StorageConfig.prototype = { |
||
218 | _url: null, |
||
219 | |||
220 | /** |
||
221 | * Storage id |
||
222 | * |
||
223 | * @type int |
||
224 | */ |
||
225 | id: null, |
||
226 | |||
227 | /** |
||
228 | * Mount point |
||
229 | * |
||
230 | * @type string |
||
231 | */ |
||
232 | mountPoint: '', |
||
233 | |||
234 | /** |
||
235 | * Backend |
||
236 | * |
||
237 | * @type string |
||
238 | */ |
||
239 | backend: null, |
||
240 | |||
241 | /** |
||
242 | * Authentication mechanism |
||
243 | * |
||
244 | * @type string |
||
245 | */ |
||
246 | authMechanism: null, |
||
247 | |||
248 | /** |
||
249 | * Backend-specific configuration |
||
250 | * |
||
251 | * @type Object.<string,object> |
||
252 | */ |
||
253 | backendOptions: null, |
||
254 | |||
255 | /** |
||
256 | * Mount-specific options |
||
257 | * |
||
258 | * @type Object.<string,object> |
||
259 | */ |
||
260 | mountOptions: null, |
||
261 | |||
262 | /** |
||
263 | * Creates or saves the storage. |
||
264 | * |
||
265 | * @param {Function} [options.success] success callback, receives result as argument |
||
266 | * @param {Function} [options.error] error callback |
||
267 | */ |
||
268 | save: function(options) { |
||
269 | var self = this; |
||
270 | var url = OC.generateUrl(this._url); |
||
271 | var method = 'POST'; |
||
272 | if (_.isNumber(this.id)) { |
||
273 | method = 'PUT'; |
||
274 | url = OC.generateUrl(this._url + '/{id}', {id: this.id}); |
||
275 | } |
||
276 | |||
277 | $.ajax({ |
||
278 | type: method, |
||
279 | url: url, |
||
280 | contentType: 'application/json', |
||
281 | data: JSON.stringify(this.getData()), |
||
282 | success: function(result) { |
||
283 | self.id = result.id; |
||
284 | if (_.isFunction(options.success)) { |
||
285 | options.success(result); |
||
286 | } |
||
287 | }, |
||
288 | error: options.error |
||
289 | }); |
||
290 | }, |
||
291 | |||
292 | /** |
||
293 | * Returns the data from this object |
||
294 | * |
||
295 | * @return {Array} JSON array of the data |
||
296 | */ |
||
297 | getData: function() { |
||
298 | var data = { |
||
299 | mountPoint: this.mountPoint, |
||
300 | backend: this.backend, |
||
301 | authMechanism: this.authMechanism, |
||
302 | backendOptions: this.backendOptions, |
||
303 | testOnly: true |
||
304 | }; |
||
305 | if (this.id) { |
||
306 | data.id = this.id; |
||
307 | } |
||
308 | if (this.mountOptions) { |
||
309 | data.mountOptions = this.mountOptions; |
||
310 | } |
||
311 | return data; |
||
312 | }, |
||
313 | |||
314 | /** |
||
315 | * Recheck the storage |
||
316 | * |
||
317 | * @param {Function} [options.success] success callback, receives result as argument |
||
318 | * @param {Function} [options.error] error callback |
||
319 | */ |
||
320 | recheck: function(options) { |
||
321 | if (!_.isNumber(this.id)) { |
||
322 | if (_.isFunction(options.error)) { |
||
323 | options.error(); |
||
324 | } |
||
325 | return; |
||
326 | } |
||
327 | $.ajax({ |
||
328 | type: 'GET', |
||
329 | url: OC.generateUrl(this._url + '/{id}', {id: this.id}), |
||
330 | data: {'testOnly': true}, |
||
331 | success: options.success, |
||
332 | error: options.error |
||
333 | }); |
||
334 | }, |
||
335 | |||
336 | /** |
||
337 | * Deletes the storage |
||
338 | * |
||
339 | * @param {Function} [options.success] success callback |
||
340 | * @param {Function} [options.error] error callback |
||
341 | */ |
||
342 | destroy: function(options) { |
||
343 | if (!_.isNumber(this.id)) { |
||
344 | // the storage hasn't even been created => success |
||
345 | if (_.isFunction(options.success)) { |
||
346 | options.success(); |
||
347 | } |
||
348 | return; |
||
349 | } |
||
350 | $.ajax({ |
||
351 | type: 'DELETE', |
||
352 | url: OC.generateUrl(this._url + '/{id}', {id: this.id}), |
||
353 | success: options.success, |
||
354 | error: options.error |
||
355 | }); |
||
356 | }, |
||
357 | |||
358 | /** |
||
359 | * Validate this model |
||
360 | * |
||
361 | * @return {boolean} false if errors exist, true otherwise |
||
362 | */ |
||
363 | validate: function() { |
||
364 | if (this.mountPoint === '') { |
||
365 | return false; |
||
366 | } |
||
367 | if (!this.backend) { |
||
368 | return false; |
||
369 | } |
||
370 | if (this.errors) { |
||
371 | return false; |
||
372 | } |
||
373 | return true; |
||
374 | } |
||
375 | }; |
||
376 | |||
377 | /** |
||
378 | * @class OCA.External.Settings.GlobalStorageConfig |
||
379 | * @augments OCA.External.Settings.StorageConfig |
||
380 | * |
||
381 | * @classdesc Global external storage config |
||
382 | */ |
||
383 | var GlobalStorageConfig = function(id) { |
||
384 | this.id = id; |
||
385 | this.applicableUsers = []; |
||
386 | this.applicableGroups = []; |
||
387 | }; |
||
388 | /** |
||
389 | * @memberOf OCA.External.Settings |
||
390 | */ |
||
391 | GlobalStorageConfig.prototype = _.extend({}, StorageConfig.prototype, |
||
392 | /** @lends OCA.External.Settings.GlobalStorageConfig.prototype */ { |
||
393 | _url: 'apps/files_external/globalstorages', |
||
394 | |||
395 | /** |
||
396 | * Applicable users |
||
397 | * |
||
398 | * @type Array.<string> |
||
399 | */ |
||
400 | applicableUsers: null, |
||
401 | |||
402 | /** |
||
403 | * Applicable groups |
||
404 | * |
||
405 | * @type Array.<string> |
||
406 | */ |
||
407 | applicableGroups: null, |
||
408 | |||
409 | /** |
||
410 | * Storage priority |
||
411 | * |
||
412 | * @type int |
||
413 | */ |
||
414 | priority: null, |
||
415 | |||
416 | /** |
||
417 | * Returns the data from this object |
||
418 | * |
||
419 | * @return {Array} JSON array of the data |
||
420 | */ |
||
421 | getData: function() { |
||
422 | var data = StorageConfig.prototype.getData.apply(this, arguments); |
||
423 | return _.extend(data, { |
||
424 | applicableUsers: this.applicableUsers, |
||
425 | applicableGroups: this.applicableGroups, |
||
426 | priority: this.priority, |
||
427 | }); |
||
428 | } |
||
429 | }); |
||
430 | |||
431 | /** |
||
432 | * @class OCA.External.Settings.UserStorageConfig |
||
433 | * @augments OCA.External.Settings.StorageConfig |
||
434 | * |
||
435 | * @classdesc User external storage config |
||
436 | */ |
||
437 | var UserStorageConfig = function(id) { |
||
438 | this.id = id; |
||
439 | }; |
||
440 | UserStorageConfig.prototype = _.extend({}, StorageConfig.prototype, |
||
441 | /** @lends OCA.External.Settings.UserStorageConfig.prototype */ { |
||
442 | _url: 'apps/files_external/userstorages' |
||
443 | }); |
||
444 | |||
445 | /** |
||
446 | * @class OCA.External.Settings.UserGlobalStorageConfig |
||
447 | * @augments OCA.External.Settings.StorageConfig |
||
448 | * |
||
449 | * @classdesc User external storage config |
||
450 | */ |
||
451 | var UserGlobalStorageConfig = function (id) { |
||
452 | this.id = id; |
||
453 | }; |
||
454 | UserGlobalStorageConfig.prototype = _.extend({}, StorageConfig.prototype, |
||
455 | /** @lends OCA.External.Settings.UserStorageConfig.prototype */ { |
||
456 | |||
457 | _url: 'apps/files_external/userglobalstorages' |
||
458 | }); |
||
459 | |||
460 | /** |
||
461 | * @class OCA.External.Settings.MountOptionsDropdown |
||
462 | * |
||
463 | * @classdesc Dropdown for mount options |
||
464 | * |
||
465 | * @param {Object} $container container DOM object |
||
466 | */ |
||
467 | var MountOptionsDropdown = function() { |
||
468 | }; |
||
469 | /** |
||
470 | * @memberof OCA.External.Settings |
||
471 | */ |
||
472 | MountOptionsDropdown.prototype = { |
||
473 | /** |
||
474 | * Dropdown element |
||
475 | * |
||
476 | * @var Object |
||
477 | */ |
||
478 | $el: null, |
||
479 | |||
480 | /** |
||
481 | * Show dropdown |
||
482 | * |
||
483 | * @param {Object} $container container |
||
484 | * @param {Object} mountOptions mount options |
||
485 | * @param {Array} visibleOptions enabled mount options |
||
486 | */ |
||
487 | show: function($container, mountOptions, visibleOptions) { |
||
488 | if (MountOptionsDropdown._last) { |
||
489 | MountOptionsDropdown._last.hide(); |
||
490 | } |
||
491 | |||
492 | var template = MountOptionsDropdown._template; |
||
493 | if (!template) { |
||
494 | template = Handlebars.compile(MOUNT_OPTIONS_DROPDOWN_TEMPLATE); |
||
495 | MountOptionsDropdown._template = template; |
||
496 | } |
||
497 | |||
498 | var $el = $(template({ |
||
499 | mountOptionsEncodingLabel: t('files_external', 'Compatibility with Mac NFD encoding (slow)'), |
||
500 | })); |
||
501 | this.$el = $el; |
||
502 | |||
503 | this.setOptions(mountOptions, visibleOptions); |
||
504 | |||
505 | this.$el.appendTo($container); |
||
506 | MountOptionsDropdown._last = this; |
||
507 | |||
508 | this.$el.trigger('show'); |
||
509 | }, |
||
510 | |||
511 | hide: function() { |
||
512 | if (this.$el) { |
||
513 | this.$el.trigger('hide'); |
||
514 | this.$el.remove(); |
||
515 | this.$el = null; |
||
516 | MountOptionsDropdown._last = null; |
||
517 | } |
||
518 | }, |
||
519 | |||
520 | /** |
||
521 | * Returns the mount options from the dropdown controls |
||
522 | * |
||
523 | * @return {Object} options mount options |
||
524 | */ |
||
525 | getOptions: function() { |
||
526 | var options = {}; |
||
527 | |||
528 | this.$el.find('input, select').each(function() { |
||
529 | var $this = $(this); |
||
530 | var key = $this.attr('name'); |
||
531 | var value = null; |
||
532 | if ($this.attr('type') === 'checkbox') { |
||
533 | value = $this.prop('checked'); |
||
534 | } else { |
||
535 | value = $this.val(); |
||
536 | } |
||
537 | if ($this.attr('data-type') === 'int') { |
||
538 | value = parseInt(value, 10); |
||
539 | } |
||
540 | options[key] = value; |
||
541 | }); |
||
542 | return options; |
||
543 | }, |
||
544 | |||
545 | /** |
||
546 | * Sets the mount options to the dropdown controls |
||
547 | * |
||
548 | * @param {Object} options mount options |
||
549 | * @param {Array} visibleOptions enabled mount options |
||
550 | */ |
||
551 | setOptions: function(options, visibleOptions) { |
||
552 | var $el = this.$el; |
||
553 | _.each(options, function(value, key) { |
||
554 | var $optionEl = $el.find('input, select').filterAttr('name', key); |
||
555 | if ($optionEl.attr('type') === 'checkbox') { |
||
556 | if (_.isString(value)) { |
||
557 | value = (value === 'true'); |
||
558 | } |
||
559 | $optionEl.prop('checked', !!value); |
||
560 | } else { |
||
561 | $optionEl.val(value); |
||
562 | } |
||
563 | }); |
||
564 | $el.find('.optionRow').each(function(i, row){ |
||
565 | var $row = $(row); |
||
566 | var optionId = $row.find('input, select').attr('name'); |
||
567 | if (visibleOptions.indexOf(optionId) === -1) { |
||
568 | $row.hide(); |
||
569 | } else { |
||
570 | $row.show(); |
||
571 | } |
||
572 | }); |
||
573 | } |
||
574 | }; |
||
575 | |||
576 | /** |
||
577 | * @class OCA.External.Settings.MountConfigListView |
||
578 | * |
||
579 | * @classdesc Mount configuration list view |
||
580 | * |
||
581 | * @param {Object} $el DOM object containing the list |
||
582 | * @param {Object} [options] |
||
583 | * @param {int} [options.userListLimit] page size in applicable users dropdown |
||
584 | */ |
||
585 | var MountConfigListView = function($el, options) { |
||
586 | this.initialize($el, options); |
||
587 | }; |
||
588 | |||
589 | MountConfigListView.ParameterFlags = { |
||
590 | OPTIONAL: 1, |
||
591 | USER_PROVIDED: 2 |
||
592 | }; |
||
593 | |||
594 | MountConfigListView.ParameterTypes = { |
||
595 | TEXT: 0, |
||
596 | BOOLEAN: 1, |
||
597 | PASSWORD: 2, |
||
598 | HIDDEN: 3 |
||
599 | }; |
||
600 | |||
601 | /** |
||
602 | * @memberOf OCA.External.Settings |
||
603 | */ |
||
604 | MountConfigListView.prototype = _.extend({ |
||
605 | |||
606 | /** |
||
607 | * jQuery element containing the config list |
||
608 | * |
||
609 | * @type Object |
||
610 | */ |
||
611 | $el: null, |
||
612 | |||
613 | /** |
||
614 | * Storage config class |
||
615 | * |
||
616 | * @type Class |
||
617 | */ |
||
618 | _storageConfigClass: null, |
||
619 | |||
620 | /** |
||
621 | * Flag whether the list is about user storage configs (true) |
||
622 | * or global storage configs (false) |
||
623 | * |
||
624 | * @type bool |
||
625 | */ |
||
626 | _isPersonal: false, |
||
627 | |||
628 | /** |
||
629 | * Page size in applicable users dropdown |
||
630 | * |
||
631 | * @type int |
||
632 | */ |
||
633 | _userListLimit: 30, |
||
634 | |||
635 | /** |
||
636 | * List of supported backends |
||
637 | * |
||
638 | * @type Object.<string,Object> |
||
639 | */ |
||
640 | _allBackends: null, |
||
641 | |||
642 | /** |
||
643 | * List of all supported authentication mechanisms |
||
644 | * |
||
645 | * @type Object.<string,Object> |
||
646 | */ |
||
647 | _allAuthMechanisms: null, |
||
648 | |||
649 | _encryptionEnabled: false, |
||
650 | |||
651 | /** |
||
652 | * @param {Object} $el DOM object containing the list |
||
653 | * @param {Object} [options] |
||
654 | * @param {int} [options.userListLimit] page size in applicable users dropdown |
||
655 | */ |
||
656 | initialize: function($el, options) { |
||
657 | this.$el = $el; |
||
658 | this._isPersonal = ($el.data('admin') !== true); |
||
659 | if (this._isPersonal) { |
||
660 | this._storageConfigClass = OCA.External.Settings.UserStorageConfig; |
||
661 | } else { |
||
662 | this._storageConfigClass = OCA.External.Settings.GlobalStorageConfig; |
||
663 | } |
||
664 | |||
665 | if (options && !_.isUndefined(options.userListLimit)) { |
||
666 | this._userListLimit = options.userListLimit; |
||
667 | } |
||
668 | |||
669 | this._encryptionEnabled = options.encryptionEnabled; |
||
670 | this._allowUserMountSharing = options.allowUserMountSharing; |
||
671 | |||
672 | // read the backend config that was carefully crammed |
||
673 | // into the data-configurations attribute of the select |
||
674 | this._allBackends = this.$el.find('.selectBackend').data('configurations'); |
||
675 | this._allAuthMechanisms = this.$el.find('#addMountPoint .authentication').data('mechanisms'); |
||
676 | }, |
||
677 | |||
678 | /** |
||
679 | * Custom JS event handlers |
||
680 | * Trigger callback for all existing configurations |
||
681 | */ |
||
682 | whenSelectBackend: function(callback) { |
||
683 | this.$el.find('tbody tr:not(#addMountPoint)').each(function(i, tr) { |
||
684 | var backend = $(tr).find('.backend').data('identifier'); |
||
685 | callback($(tr), backend); |
||
686 | }); |
||
687 | this.on('selectBackend', callback); |
||
688 | }, |
||
689 | whenSelectAuthMechanism: function(callback) { |
||
690 | var self = this; |
||
691 | this.$el.find('tbody tr:not(#addMountPoint)').each(function(i, tr) { |
||
692 | var authMechanism = $(tr).find('.selectAuthMechanism').val(); |
||
693 | if (authMechanism !== undefined) { |
||
694 | var onCompletion = jQuery.Deferred(); |
||
695 | callback($(tr), authMechanism, self._allAuthMechanisms[authMechanism]['scheme'], onCompletion); |
||
0 ignored issues
–
show
|
|||
696 | onCompletion.resolve(); |
||
697 | } |
||
698 | }); |
||
699 | this.on('selectAuthMechanism', callback); |
||
700 | }, |
||
701 | |||
702 | /** |
||
703 | * Initialize DOM event handlers |
||
704 | */ |
||
705 | _initEvents: function() { |
||
706 | var self = this; |
||
707 | |||
708 | var onChangeHandler = _.bind(this._onChange, this); |
||
709 | //this.$el.on('input', 'td input', onChangeHandler); |
||
710 | this.$el.on('keyup', 'td input', onChangeHandler); |
||
711 | this.$el.on('paste', 'td input', onChangeHandler); |
||
712 | this.$el.on('change', 'td input:checkbox', onChangeHandler); |
||
713 | this.$el.on('change', '.applicable', onChangeHandler); |
||
714 | |||
715 | this.$el.on('click', '.status>span', function() { |
||
716 | self.recheckStorageConfig($(this).closest('tr')); |
||
717 | }); |
||
718 | |||
719 | this.$el.on('click', 'td.remove>img', function() { |
||
720 | self.deleteStorageConfig($(this).closest('tr')); |
||
721 | }); |
||
722 | |||
723 | this.$el.on('click', 'td.mountOptionsToggle>img', function() { |
||
724 | self._showMountOptionsDropdown($(this).closest('tr')); |
||
725 | }); |
||
726 | |||
727 | this.$el.on('change', '.selectBackend', _.bind(this._onSelectBackend, this)); |
||
728 | this.$el.on('change', '.selectAuthMechanism', _.bind(this._onSelectAuthMechanism, this)); |
||
729 | }, |
||
730 | |||
731 | _onChange: function(event) { |
||
732 | var self = this; |
||
733 | var $target = $(event.target); |
||
734 | if ($target.closest('.dropdown').length) { |
||
735 | // ignore dropdown events |
||
736 | return; |
||
737 | } |
||
738 | highlightInput($target); |
||
739 | var $tr = $target.closest('tr'); |
||
740 | this.updateStatus($tr, null); |
||
741 | |||
742 | var timer = $tr.data('save-timer'); |
||
743 | clearTimeout(timer); |
||
744 | timer = setTimeout(function() { |
||
745 | self.saveStorageConfig($tr, null, timer); |
||
746 | }, 2000); |
||
747 | $tr.data('save-timer', timer); |
||
748 | }, |
||
749 | |||
750 | _onSelectBackend: function(event) { |
||
751 | var $target = $(event.target); |
||
752 | var $tr = $target.closest('tr'); |
||
753 | |||
754 | var storageConfig = new this._storageConfigClass(); |
||
755 | storageConfig.mountPoint = $tr.find('.mountPoint input').val(); |
||
756 | storageConfig.backend = $target.val(); |
||
757 | $tr.find('.mountPoint input').val(''); |
||
758 | |||
759 | var onCompletion = jQuery.Deferred(); |
||
760 | $tr = this.newStorage(storageConfig, onCompletion); |
||
761 | onCompletion.resolve(); |
||
762 | |||
763 | $tr.find('td.configuration').children().not('[type=hidden]').first().focus(); |
||
764 | this.saveStorageConfig($tr); |
||
765 | }, |
||
766 | |||
767 | _onSelectAuthMechanism: function(event) { |
||
768 | var $target = $(event.target); |
||
769 | var $tr = $target.closest('tr'); |
||
770 | var authMechanism = $target.val(); |
||
771 | |||
772 | var onCompletion = jQuery.Deferred(); |
||
773 | this.configureAuthMechanism($tr, authMechanism, onCompletion); |
||
774 | onCompletion.resolve(); |
||
775 | |||
776 | this.saveStorageConfig($tr); |
||
777 | }, |
||
778 | |||
779 | /** |
||
780 | * Execute the target callback when all the deferred objects have been |
||
781 | * finished, either resolved or rejected |
||
782 | * |
||
783 | * @param {Deferred[]} deferreds array with all the deferred objects to check |
||
784 | * this will usually be a list of ajax requests, but other deferred |
||
785 | * objects can be included |
||
786 | * @param {callback} callback the callback to be executed after all the |
||
787 | * deferred object have finished |
||
788 | */ |
||
789 | _executeCallbackWhenFinished: function(deferreds, callback) { |
||
790 | var self = this; |
||
791 | |||
792 | $.when.apply($, deferreds).always(function() { |
||
793 | var pendingDeferreds = []; |
||
794 | |||
795 | // some deferred objects can still be pending to be resolved |
||
796 | // or rejected. Get all of those which are still pending so we can |
||
797 | // make another call |
||
798 | if (deferreds.length > 0) { |
||
799 | for (i = 0 ; i < deferreds.length ; i++) { |
||
800 | if (deferreds[i].state() === 'pending') { |
||
801 | pendingDeferreds.push(deferreds[i]); |
||
802 | } |
||
803 | } |
||
804 | } |
||
805 | |||
806 | if (pendingDeferreds.length > 0) { |
||
807 | self._executeCallbackWhenFinished(pendingDeferreds, callback); |
||
808 | } else { |
||
809 | callback(); |
||
810 | } |
||
811 | }); |
||
812 | }, |
||
813 | |||
814 | /** |
||
815 | * Configure the storage config with a new authentication mechanism |
||
816 | * |
||
817 | * @param {jQuery} $tr config row |
||
818 | * @param {string} authMechanism |
||
819 | * @param {jQuery.Deferred} onCompletion |
||
820 | */ |
||
821 | configureAuthMechanism: function($tr, authMechanism, onCompletion) { |
||
822 | var authMechanismConfiguration = this._allAuthMechanisms[authMechanism]; |
||
823 | var $td = $tr.find('td.configuration'); |
||
824 | $td.find('.auth-param').remove(); |
||
825 | |||
826 | $.each(authMechanismConfiguration['configuration'], _.partial( |
||
0 ignored issues
–
show
|
|||
827 | this.writeParameterInput, $td, _, _, ['auth-param'] |
||
828 | ).bind(this)); |
||
829 | |||
830 | this.trigger('selectAuthMechanism', |
||
831 | $tr, authMechanism, authMechanismConfiguration['scheme'], onCompletion |
||
0 ignored issues
–
show
|
|||
832 | ); |
||
833 | }, |
||
834 | |||
835 | /** |
||
836 | * Create a config row for a new storage |
||
837 | * |
||
838 | * @param {StorageConfig} storageConfig storage config to pull values from |
||
839 | * @param {jQuery.Deferred} onCompletion |
||
840 | * @return {jQuery} created row |
||
841 | */ |
||
842 | newStorage: function(storageConfig, onCompletion) { |
||
843 | var mountPoint = storageConfig.mountPoint; |
||
844 | var backend = this._allBackends[storageConfig.backend]; |
||
845 | var isInvalidAuth = false; |
||
846 | |||
847 | if (!backend) { |
||
848 | backend = { |
||
849 | name: 'Unknown backend: ' + storageConfig.backend, |
||
850 | invalid: true |
||
851 | }; |
||
852 | } |
||
853 | if (backend && storageConfig.authMechanism && !this._allAuthMechanisms[storageConfig.authMechanism]) { |
||
854 | // mark invalid to prevent editing |
||
855 | backend.invalid = true; |
||
856 | isInvalidAuth = true; |
||
857 | } |
||
858 | |||
859 | // FIXME: Replace with a proper Handlebar template |
||
860 | var $tr = this.$el.find('tr#addMountPoint'); |
||
861 | var $trCloned = $tr.clone(); |
||
862 | $trCloned.find('td.mountPoint input').removeAttr('disabled'); |
||
863 | this.$el.find('tbody').append($trCloned); |
||
864 | |||
865 | $tr.data('storageConfig', storageConfig); |
||
866 | $tr.show(); |
||
867 | $tr.find('td').last().attr('class', 'remove'); |
||
868 | $tr.find('td.mountOptionsToggle').removeClass('hidden'); |
||
869 | $tr.find('td').last().removeAttr('style'); |
||
870 | $tr.removeAttr('id'); |
||
871 | addSelect2($tr.find('.applicableUsers'), this._userListLimit); |
||
872 | |||
873 | if (storageConfig.id) { |
||
874 | $tr.data('id', storageConfig.id); |
||
875 | } |
||
876 | |||
877 | $tr.find('.backend').text(backend.name); |
||
878 | if (mountPoint === '') { |
||
879 | mountPoint = this._suggestMountPoint(backend.name); |
||
880 | } |
||
881 | $tr.find('.mountPoint input').val(mountPoint); |
||
882 | $tr.addClass(backend.identifier); |
||
883 | $tr.find('.backend').data('identifier', backend.identifier); |
||
884 | |||
885 | if (backend.invalid || isInvalidAuth) { |
||
886 | $tr.addClass('invalid'); |
||
887 | $tr.find('[name=mountPoint]').prop('disabled', true); |
||
888 | $tr.find('.applicable,.mountOptionsToggle').empty(); |
||
889 | this.updateStatus($tr, false, 'Unknown backend: ' + backend.name); |
||
890 | if (isInvalidAuth) { |
||
891 | $tr.find('td.authentication').append('<span class="error-invalid">' + |
||
892 | t('files_external', 'Unknown auth backend "{b}"', {b: storageConfig.authMechanism}) + |
||
893 | '</span>' |
||
894 | ); |
||
895 | } |
||
896 | |||
897 | $tr.find('td.configuration').append('<span class="error-invalid">' + |
||
898 | t('files_external', 'Please make sure that the app that provides this backend is installed and enabled') + |
||
899 | '</span>' |
||
900 | ); |
||
901 | |||
902 | return $tr; |
||
903 | } |
||
904 | |||
905 | var selectAuthMechanism = $('<select class="selectAuthMechanism"></select>'); |
||
906 | var neededVisibility = (this._isPersonal) ? StorageConfig.Visibility.PERSONAL : StorageConfig.Visibility.ADMIN; |
||
907 | $.each(this._allAuthMechanisms, function(authIdentifier, authMechanism) { |
||
908 | if (backend.authSchemes[authMechanism.scheme] && (authMechanism.visibility & neededVisibility)) { |
||
909 | selectAuthMechanism.append( |
||
910 | $('<option value="'+authMechanism.identifier+'" data-scheme="'+authMechanism.scheme+'">'+authMechanism.name+'</option>') |
||
911 | ); |
||
912 | } |
||
913 | }); |
||
914 | if (storageConfig.authMechanism) { |
||
915 | selectAuthMechanism.val(storageConfig.authMechanism); |
||
916 | } else { |
||
917 | storageConfig.authMechanism = selectAuthMechanism.val(); |
||
918 | } |
||
919 | $tr.find('td.authentication').append(selectAuthMechanism); |
||
920 | |||
921 | var $td = $tr.find('td.configuration'); |
||
922 | $.each(backend.configuration, _.partial(this.writeParameterInput, $td).bind(this)); |
||
923 | |||
924 | this.trigger('selectBackend', $tr, backend.identifier, onCompletion); |
||
925 | this.configureAuthMechanism($tr, storageConfig.authMechanism, onCompletion); |
||
926 | |||
927 | if (storageConfig.backendOptions) { |
||
928 | $td.find('input, select').each(function() { |
||
929 | var input = $(this); |
||
930 | var val = storageConfig.backendOptions[input.data('parameter')]; |
||
931 | if (val !== undefined) { |
||
932 | if(input.is('input:checkbox')) { |
||
933 | input.prop('checked', val); |
||
934 | } |
||
935 | input.val(storageConfig.backendOptions[input.data('parameter')]); |
||
936 | highlightInput(input); |
||
937 | } |
||
938 | }); |
||
939 | } |
||
940 | |||
941 | var applicable = []; |
||
942 | if (storageConfig.applicableUsers) { |
||
943 | applicable = applicable.concat(storageConfig.applicableUsers); |
||
944 | } |
||
945 | if (storageConfig.applicableGroups) { |
||
946 | applicable = applicable.concat( |
||
947 | _.map(storageConfig.applicableGroups, function(group) { |
||
948 | return group+'(group)'; |
||
949 | }) |
||
950 | ); |
||
951 | } |
||
952 | $tr.find('.applicableUsers').val(applicable).trigger('change'); |
||
953 | |||
954 | var priorityEl = $('<input type="hidden" class="priority" value="' + backend.priority + '" />'); |
||
955 | $tr.append(priorityEl); |
||
956 | |||
957 | if (storageConfig.mountOptions) { |
||
958 | $tr.find('input.mountOptions').val(JSON.stringify(storageConfig.mountOptions)); |
||
959 | } else { |
||
960 | // FIXME default backend mount options |
||
961 | $tr.find('input.mountOptions').val(JSON.stringify({ |
||
962 | 'encrypt': true, |
||
963 | 'previews': true, |
||
964 | 'enable_sharing': false, |
||
965 | 'filesystem_check_changes': 1, |
||
966 | 'encoding_compatibility': false |
||
967 | })); |
||
968 | } |
||
969 | |||
970 | return $tr; |
||
971 | }, |
||
972 | |||
973 | /** |
||
974 | * Load storages into config rows |
||
975 | */ |
||
976 | loadStorages: function() { |
||
977 | var self = this; |
||
978 | var ajaxRequests = []; |
||
979 | |||
980 | if (this._isPersonal) { |
||
981 | // load userglobal storages |
||
982 | var fixedStoragesRequest = $.ajax({ |
||
983 | type: 'GET', |
||
984 | url: OC.generateUrl('apps/files_external/userglobalstorages'), |
||
985 | data: {'testOnly' : true}, |
||
986 | contentType: 'application/json', |
||
987 | success: function(result) { |
||
988 | var onCompletion = jQuery.Deferred(); |
||
989 | $.each(result, function(i, storageParams) { |
||
990 | var storageConfig; |
||
991 | var isUserGlobal = storageParams.type === 'system' && self._isPersonal; |
||
992 | storageParams.mountPoint = storageParams.mountPoint.substr(1); // trim leading slash |
||
993 | if (isUserGlobal) { |
||
994 | storageConfig = new UserGlobalStorageConfig(); |
||
995 | } else { |
||
996 | storageConfig = new self._storageConfigClass(); |
||
997 | } |
||
998 | _.extend(storageConfig, storageParams); |
||
999 | var $tr = self.newStorage(storageConfig, onCompletion); |
||
1000 | |||
1001 | // userglobal storages must be at the top of the list |
||
1002 | $tr.detach(); |
||
1003 | self.$el.prepend($tr); |
||
1004 | |||
1005 | var $authentication = $tr.find('.authentication'); |
||
1006 | $authentication.text($authentication.find('select option:selected').text()); |
||
1007 | |||
1008 | // disable any other inputs |
||
1009 | $tr.find('.mountOptionsToggle, .remove').empty(); |
||
1010 | $tr.find('input:not(.user_provided), select:not(.user_provided)').attr('disabled', 'disabled'); |
||
1011 | |||
1012 | if (isUserGlobal) { |
||
1013 | $tr.find('.configuration').find(':not(.user_provided)').remove(); |
||
1014 | } else { |
||
1015 | // userglobal storages do not expose configuration data |
||
1016 | $tr.find('.configuration').text(t('files_external', 'Admin defined')); |
||
1017 | } |
||
1018 | }); |
||
1019 | onCompletion.resolve(); |
||
1020 | } |
||
1021 | }); |
||
1022 | ajaxRequests.push(fixedStoragesRequest); |
||
1023 | } |
||
1024 | |||
1025 | var url = this._storageConfigClass.prototype._url; |
||
1026 | |||
1027 | var changeableStoragesRequest = $.ajax({ |
||
1028 | type: 'GET', |
||
1029 | url: OC.generateUrl(url), |
||
1030 | contentType: 'application/json', |
||
1031 | success: function(result) { |
||
1032 | var onCompletion = jQuery.Deferred(); |
||
1033 | $.each(result, function(i, storageParams) { |
||
1034 | storageParams.mountPoint = storageParams.mountPoint.substr(1); // trim leading slash |
||
1035 | var storageConfig = new self._storageConfigClass(); |
||
1036 | _.extend(storageConfig, storageParams); |
||
1037 | var $tr = self.newStorage(storageConfig, onCompletion); |
||
1038 | self.recheckStorageConfig($tr); |
||
1039 | }); |
||
1040 | onCompletion.resolve(); |
||
1041 | } |
||
1042 | }); |
||
1043 | ajaxRequests.push(changeableStoragesRequest); |
||
1044 | |||
1045 | this._executeCallbackWhenFinished(ajaxRequests, function() { |
||
1046 | $('#body-settings').trigger('mountConfigLoaded'); |
||
1047 | }); |
||
1048 | }, |
||
1049 | |||
1050 | /** |
||
1051 | * @param {jQuery} $td |
||
1052 | * @param {string} parameter |
||
1053 | * @param {string} placeholder |
||
1054 | * @param {Array} classes |
||
1055 | * @return {jQuery} newly created input |
||
1056 | */ |
||
1057 | writeParameterInput: function($td, parameter, placeholder, classes) { |
||
1058 | var hasFlag = function(flag) { |
||
1059 | return (placeholder.flags & flag) === flag; |
||
1060 | }; |
||
1061 | classes = $.isArray(classes) ? classes : []; |
||
1062 | classes.push('added'); |
||
1063 | if (hasFlag(MountConfigListView.ParameterFlags.OPTIONAL)) { |
||
1064 | classes.push('optional'); |
||
1065 | } |
||
1066 | |||
1067 | if (hasFlag(MountConfigListView.ParameterFlags.USER_PROVIDED)) { |
||
1068 | if (this._isPersonal) { |
||
1069 | classes.push('user_provided'); |
||
1070 | } else { |
||
1071 | return; |
||
1072 | } |
||
1073 | } |
||
1074 | |||
1075 | var newElement; |
||
1076 | |||
1077 | var trimmedPlaceholder = placeholder.value; |
||
1078 | if (placeholder.type === MountConfigListView.ParameterTypes.PASSWORD) { |
||
1079 | newElement = $('<input type="password" class="'+classes.join(' ')+'" data-parameter="'+parameter+'" placeholder="'+ trimmedPlaceholder+'" autocomplete="off" />'); |
||
1080 | } else if (placeholder.type === MountConfigListView.ParameterTypes.BOOLEAN) { |
||
1081 | var checkboxId = _.uniqueId('checkbox_'); |
||
1082 | newElement = $('<div><label><input type="checkbox" id="'+checkboxId+'" class="'+classes.join(' ')+'" data-parameter="'+parameter+'" />'+ trimmedPlaceholder+'</label></div>'); |
||
1083 | } else if (placeholder.type === MountConfigListView.ParameterTypes.HIDDEN) { |
||
1084 | newElement = $('<input type="hidden" class="'+classes.join(' ')+'" data-parameter="'+parameter+'" />'); |
||
1085 | } else { |
||
1086 | newElement = $('<input type="text" class="'+classes.join(' ')+'" data-parameter="'+parameter+'" placeholder="'+ trimmedPlaceholder+'" />'); |
||
1087 | } |
||
1088 | highlightInput(newElement); |
||
1089 | $td.append(newElement); |
||
1090 | return newElement; |
||
1091 | }, |
||
1092 | |||
1093 | /** |
||
1094 | * Gets the storage model from the given row |
||
1095 | * |
||
1096 | * @param $tr row element |
||
1097 | * @return {OCA.External.StorageConfig} storage model instance |
||
1098 | */ |
||
1099 | getStorageConfig: function($tr) { |
||
1100 | var storageId = $tr.data('id'); |
||
1101 | if (!storageId) { |
||
1102 | // new entry |
||
1103 | storageId = null; |
||
1104 | } |
||
1105 | |||
1106 | var storage = $tr.data('storageConfig'); |
||
1107 | if (!storage) { |
||
1108 | storage = new this._storageConfigClass(storageId); |
||
1109 | } |
||
1110 | storage.errors = null; |
||
1111 | storage.mountPoint = $tr.find('.mountPoint input').val(); |
||
1112 | storage.backend = $tr.find('.backend').data('identifier'); |
||
1113 | storage.authMechanism = $tr.find('.selectAuthMechanism').val(); |
||
1114 | |||
1115 | var classOptions = {}; |
||
1116 | var configuration = $tr.find('.configuration input'); |
||
1117 | var missingOptions = []; |
||
1118 | $.each(configuration, function(index, input) { |
||
1119 | var $input = $(input); |
||
1120 | var parameter = $input.data('parameter'); |
||
1121 | if ($input.attr('type') === 'button') { |
||
1122 | return; |
||
1123 | } |
||
1124 | if (!isInputValid($input) && !$input.hasClass('optional')) { |
||
1125 | missingOptions.push(parameter); |
||
1126 | return; |
||
1127 | } |
||
1128 | if ($(input).is(':checkbox')) { |
||
1129 | if ($(input).is(':checked')) { |
||
1130 | classOptions[parameter] = true; |
||
1131 | } else { |
||
1132 | classOptions[parameter] = false; |
||
1133 | } |
||
1134 | } else { |
||
1135 | classOptions[parameter] = $(input).val(); |
||
1136 | } |
||
1137 | }); |
||
1138 | |||
1139 | storage.backendOptions = classOptions; |
||
1140 | if (missingOptions.length) { |
||
1141 | storage.errors = { |
||
1142 | backendOptions: missingOptions |
||
1143 | }; |
||
1144 | } |
||
1145 | |||
1146 | // gather selected users and groups |
||
1147 | if (!this._isPersonal) { |
||
1148 | var groups = []; |
||
1149 | var users = []; |
||
1150 | var multiselect = getSelection($tr); |
||
1151 | $.each(multiselect, function(index, value) { |
||
1152 | var pos = (value.indexOf)?value.indexOf('(group)'): -1; |
||
1153 | if (pos !== -1) { |
||
1154 | groups.push(value.substr(0, pos)); |
||
1155 | } else { |
||
1156 | users.push(value); |
||
1157 | } |
||
1158 | }); |
||
1159 | // FIXME: this should be done in the multiselect change event instead |
||
1160 | $tr.find('.applicable') |
||
1161 | .data('applicable-groups', groups) |
||
1162 | .data('applicable-users', users); |
||
1163 | |||
1164 | storage.applicableUsers = users; |
||
1165 | storage.applicableGroups = groups; |
||
1166 | |||
1167 | storage.priority = parseInt($tr.find('input.priority').val() || '100', 10); |
||
1168 | } |
||
1169 | |||
1170 | var mountOptions = $tr.find('input.mountOptions').val(); |
||
1171 | if (mountOptions) { |
||
1172 | storage.mountOptions = JSON.parse(mountOptions); |
||
1173 | } |
||
1174 | |||
1175 | return storage; |
||
1176 | }, |
||
1177 | |||
1178 | /** |
||
1179 | * Deletes the storage from the given tr |
||
1180 | * |
||
1181 | * @param $tr storage row |
||
1182 | * @param Function callback callback to call after save |
||
1183 | */ |
||
1184 | deleteStorageConfig: function($tr) { |
||
1185 | var self = this; |
||
1186 | var configId = $tr.data('id'); |
||
1187 | if (!_.isNumber(configId)) { |
||
1188 | // deleting unsaved storage |
||
1189 | $tr.remove(); |
||
1190 | return; |
||
1191 | } |
||
1192 | var storage = new this._storageConfigClass(configId); |
||
1193 | this.updateStatus($tr, StorageConfig.Status.IN_PROGRESS); |
||
1194 | |||
1195 | storage.destroy({ |
||
1196 | success: function() { |
||
1197 | $tr.remove(); |
||
1198 | }, |
||
1199 | error: function() { |
||
1200 | self.updateStatus($tr, StorageConfig.Status.ERROR); |
||
1201 | } |
||
1202 | }); |
||
1203 | }, |
||
1204 | |||
1205 | /** |
||
1206 | * Saves the storage from the given tr |
||
1207 | * |
||
1208 | * @param $tr storage row |
||
1209 | * @param Function callback callback to call after save |
||
1210 | * @param concurrentTimer only update if the timer matches this |
||
1211 | */ |
||
1212 | saveStorageConfig:function($tr, callback, concurrentTimer) { |
||
1213 | var self = this; |
||
1214 | var storage = this.getStorageConfig($tr); |
||
1215 | if (!storage || !storage.validate()) { |
||
1216 | return false; |
||
1217 | } |
||
1218 | |||
1219 | this.updateStatus($tr, StorageConfig.Status.IN_PROGRESS); |
||
1220 | storage.save({ |
||
1221 | success: function(result) { |
||
1222 | if (concurrentTimer === undefined |
||
1223 | || $tr.data('save-timer') === concurrentTimer |
||
1224 | ) { |
||
1225 | self.updateStatus($tr, result.status); |
||
1226 | $tr.data('id', result.id); |
||
1227 | |||
1228 | if (_.isFunction(callback)) { |
||
1229 | callback(storage); |
||
1230 | } |
||
1231 | } |
||
1232 | }, |
||
1233 | error: function() { |
||
1234 | if (concurrentTimer === undefined |
||
1235 | || $tr.data('save-timer') === concurrentTimer |
||
1236 | ) { |
||
1237 | self.updateStatus($tr, StorageConfig.Status.ERROR); |
||
1238 | } |
||
1239 | } |
||
1240 | }); |
||
1241 | }, |
||
1242 | |||
1243 | /** |
||
1244 | * Recheck storage availability |
||
1245 | * |
||
1246 | * @param {jQuery} $tr storage row |
||
1247 | * @return {boolean} success |
||
1248 | */ |
||
1249 | recheckStorageConfig: function($tr) { |
||
1250 | var self = this; |
||
1251 | var storage = this.getStorageConfig($tr); |
||
1252 | if (!storage.validate()) { |
||
1253 | return false; |
||
1254 | } |
||
1255 | |||
1256 | this.updateStatus($tr, StorageConfig.Status.IN_PROGRESS); |
||
1257 | storage.recheck({ |
||
1258 | success: function(result) { |
||
1259 | self.updateStatus($tr, result.status, result.statusMessage); |
||
1260 | }, |
||
1261 | error: function() { |
||
1262 | self.updateStatus($tr, StorageConfig.Status.ERROR); |
||
1263 | } |
||
1264 | }); |
||
1265 | }, |
||
1266 | |||
1267 | /** |
||
1268 | * Update status display |
||
1269 | * |
||
1270 | * @param {jQuery} $tr |
||
1271 | * @param {int} status |
||
1272 | * @param {string} message |
||
1273 | */ |
||
1274 | updateStatus: function($tr, status, message) { |
||
1275 | var $statusSpan = $tr.find('.status span'); |
||
1276 | $statusSpan.removeClass('loading-small success indeterminate error'); |
||
1277 | switch (status) { |
||
1278 | case null: |
||
1279 | // remove status |
||
1280 | break; |
||
1281 | case StorageConfig.Status.IN_PROGRESS: |
||
1282 | $statusSpan.addClass('loading-small'); |
||
1283 | break; |
||
1284 | case StorageConfig.Status.SUCCESS: |
||
1285 | $statusSpan.addClass('success'); |
||
1286 | break; |
||
1287 | case StorageConfig.Status.INDETERMINATE: |
||
1288 | $statusSpan.addClass('indeterminate'); |
||
1289 | break; |
||
1290 | default: |
||
1291 | $statusSpan.addClass('error'); |
||
1292 | } |
||
1293 | $statusSpan.attr('data-original-title', (typeof message === 'string') ? message : ''); |
||
1294 | }, |
||
1295 | |||
1296 | /** |
||
1297 | * Suggest mount point name that doesn't conflict with the existing names in the list |
||
1298 | * |
||
1299 | * @param {string} defaultMountPoint default name |
||
1300 | */ |
||
1301 | _suggestMountPoint: function(defaultMountPoint) { |
||
1302 | var $el = this.$el; |
||
1303 | var pos = defaultMountPoint.indexOf('/'); |
||
1304 | if (pos !== -1) { |
||
1305 | defaultMountPoint = defaultMountPoint.substring(0, pos); |
||
1306 | } |
||
1307 | defaultMountPoint = defaultMountPoint.replace(/\s+/g, ''); |
||
1308 | var i = 1; |
||
1309 | var append = ''; |
||
1310 | var match = true; |
||
1311 | while (match && i < 20) { |
||
1312 | match = false; |
||
1313 | $el.find('tbody td.mountPoint input').each(function(index, mountPoint) { |
||
1314 | if ($(mountPoint).val() === defaultMountPoint+append) { |
||
1315 | match = true; |
||
1316 | return false; |
||
1317 | } |
||
1318 | }); |
||
1319 | if (match) { |
||
1320 | append = i; |
||
1321 | i++; |
||
1322 | } else { |
||
1323 | break; |
||
1324 | } |
||
1325 | } |
||
1326 | return defaultMountPoint + append; |
||
1327 | }, |
||
1328 | |||
1329 | /** |
||
1330 | * Toggles the mount options dropdown |
||
1331 | * |
||
1332 | * @param {Object} $tr configuration row |
||
1333 | */ |
||
1334 | _showMountOptionsDropdown: function($tr) { |
||
1335 | if (this._preventNextDropdown) { |
||
1336 | // prevented because the click was on the toggle |
||
1337 | this._preventNextDropdown = false; |
||
1338 | return; |
||
1339 | } |
||
1340 | var self = this; |
||
1341 | var storage = this.getStorageConfig($tr); |
||
1342 | var $toggle = $tr.find('.mountOptionsToggle'); |
||
1343 | var dropDown = new MountOptionsDropdown(); |
||
1344 | var visibleOptions = [ |
||
1345 | 'previews', |
||
1346 | 'filesystem_check_changes', |
||
1347 | 'encoding_compatibility' |
||
1348 | ]; |
||
1349 | if (this._encryptionEnabled) { |
||
1350 | visibleOptions.push('encrypt'); |
||
1351 | } |
||
1352 | if (!this._isPersonal || this._allowUserMountSharing) { |
||
1353 | visibleOptions.push('enable_sharing'); |
||
1354 | } |
||
1355 | |||
1356 | dropDown.show($toggle, storage.mountOptions || [], visibleOptions); |
||
1357 | $('body').on('mouseup.mountOptionsDropdown', function(event) { |
||
1358 | var $target = $(event.target); |
||
1359 | if ($toggle.has($target).length) { |
||
1360 | // why is it always so hard to make dropdowns behave ? |
||
1361 | // this prevents the click on the toggle to cause |
||
1362 | // the dropdown to reopen itself |
||
1363 | // (preventDefault doesn't work here because the click |
||
1364 | // event is already in the queue and cannot be cancelled) |
||
1365 | self._preventNextDropdown = true; |
||
1366 | } |
||
1367 | if ($target.closest('.dropdown').length) { |
||
1368 | return; |
||
1369 | } |
||
1370 | dropDown.hide(); |
||
1371 | }); |
||
1372 | |||
1373 | dropDown.$el.on('hide', function() { |
||
1374 | var mountOptions = dropDown.getOptions(); |
||
1375 | $('body').off('mouseup.mountOptionsDropdown'); |
||
1376 | $tr.find('input.mountOptions').val(JSON.stringify(mountOptions)); |
||
1377 | self.saveStorageConfig($tr); |
||
1378 | }); |
||
1379 | } |
||
1380 | }, OC.Backbone.Events); |
||
1381 | |||
1382 | $(document).ready(function() { |
||
1383 | var enabled = $('#files_external').attr('data-encryption-enabled'); |
||
1384 | var encryptionEnabled = (enabled ==='true')? true: false; |
||
1385 | var mountConfigListView = new MountConfigListView($('#externalStorage'), { |
||
1386 | encryptionEnabled: encryptionEnabled, |
||
1387 | allowUserMountSharing: (parseInt($('#allowUserMountSharing').val(), 10) === 1) |
||
1388 | }); |
||
1389 | |||
1390 | // init the mountConfigListView events after the storages are loaded |
||
1391 | $('#body-settings').on('mountConfigLoaded', function() { |
||
1392 | mountConfigListView._initEvents(); |
||
1393 | }); |
||
1394 | mountConfigListView.loadStorages(); |
||
1395 | |||
1396 | // TODO: move this into its own View class |
||
1397 | var $allowUserMounting = $('#allowUserMounting'); |
||
1398 | $allowUserMounting.bind('change', function() { |
||
1399 | OC.msg.startSaving('#userMountingMsg'); |
||
1400 | if (this.checked) { |
||
1401 | OC.AppConfig.setValue('files_external', 'allow_user_mounting', 'yes'); |
||
1402 | $('input[name="allowUserMountingBackends\\[\\]"]').prop('checked', true); |
||
1403 | $('#userMountingBackends').removeClass('hidden'); |
||
1404 | $('input[name="allowUserMountingBackends\\[\\]"]').eq(0).trigger('change'); |
||
1405 | } else { |
||
1406 | OC.AppConfig.setValue('files_external', 'allow_user_mounting', 'no'); |
||
1407 | $('#userMountingBackends').addClass('hidden'); |
||
1408 | } |
||
1409 | OC.msg.finishedSaving('#userMountingMsg', {status: 'success', data: {message: t('files_external', 'Saved')}}); |
||
1410 | }); |
||
1411 | |||
1412 | var $allowUserMountSharing = $('#allowUserMountSharing'); |
||
1413 | $allowUserMountSharing.bind('change', function() { |
||
1414 | OC.msg.startSaving('#userMountSharingMsg'); |
||
1415 | OC.AppConfig.setValue('core', 'allow_user_mount_sharing', this.checked ? 'yes' : 'no'); |
||
1416 | OC.msg.finishedSaving('#userMountSharingMsg', {status: 'success', data: {message: t('files_external', 'Saved')}}); |
||
1417 | }); |
||
1418 | |||
1419 | $('input[name="allowUserMountingBackends\\[\\]"]').bind('change', function() { |
||
1420 | OC.msg.startSaving('#userMountingMsg'); |
||
1421 | |||
1422 | var userMountingBackends = $('input[name="allowUserMountingBackends\\[\\]"]:checked').map(function(){ |
||
1423 | return $(this).val(); |
||
1424 | }).get(); |
||
1425 | var deprecatedBackends = $('input[name="allowUserMountingBackends\\[\\]"][data-deprecate-to]').map(function(){ |
||
1426 | if ($.inArray($(this).data('deprecate-to'), userMountingBackends) !== -1) { |
||
1427 | return $(this).val(); |
||
1428 | } |
||
1429 | return null; |
||
1430 | }).get(); |
||
1431 | userMountingBackends = userMountingBackends.concat(deprecatedBackends); |
||
1432 | |||
1433 | OC.AppConfig.setValue('files_external', 'user_mounting_backends', userMountingBackends.join()); |
||
1434 | OC.msg.finishedSaving('#userMountingMsg', {status: 'success', data: {message: t('files_external', 'Saved')}}); |
||
1435 | |||
1436 | // disable allowUserMounting |
||
1437 | if(userMountingBackends.length === 0) { |
||
1438 | $allowUserMounting.prop('checked', false); |
||
1439 | $allowUserMounting.trigger('change'); |
||
1440 | |||
1441 | } |
||
1442 | }); |
||
1443 | |||
1444 | $('#files_external input[name=enableExternalStorage]').on('change', function(event) { |
||
1445 | var $target = $(event.target); |
||
1446 | var checked = $target.is(':checked'); |
||
1447 | |||
1448 | var saveSetting = function(checked) { |
||
1449 | var value = checked ? 'yes' : 'no'; |
||
1450 | OC.AppConfig.setValue('core', 'enable_external_storage', value); |
||
1451 | $('#files_external_settings').toggleClass('hidden', !checked); |
||
1452 | }; |
||
1453 | |||
1454 | if (checked === false) { |
||
1455 | OC.dialogs.confirm( |
||
1456 | t('files_external', 'Disabling external storage will unmount all storages for all users, are you sure ?'), |
||
1457 | t('files_external', 'Disable external storage'), |
||
1458 | function(confirmation) { |
||
1459 | if (confirmation) { |
||
1460 | saveSetting(false); |
||
1461 | } else { |
||
1462 | $target.prop('checked', true); |
||
1463 | } |
||
1464 | }, |
||
1465 | true |
||
1466 | ); |
||
1467 | } else { |
||
1468 | saveSetting(true); |
||
1469 | } |
||
1470 | }); |
||
1471 | |||
1472 | // global instance |
||
1473 | OCA.External.Settings.mountConfig = mountConfigListView; |
||
1474 | |||
1475 | /** |
||
1476 | * Legacy |
||
1477 | * |
||
1478 | * @namespace |
||
1479 | * @deprecated use OCA.External.Settings.mountConfig instead |
||
1480 | */ |
||
1481 | OC.MountConfig = { |
||
1482 | saveStorage: _.bind(mountConfigListView.saveStorageConfig, mountConfigListView) |
||
1483 | }; |
||
1484 | }); |
||
1485 | |||
1486 | // export |
||
1487 | |||
1488 | OCA.External = OCA.External || {}; |
||
1489 | /** |
||
1490 | * @namespace |
||
1491 | */ |
||
1492 | OCA.External.Settings = OCA.External.Settings || {}; |
||
1493 | |||
1494 | OCA.External.Settings.GlobalStorageConfig = GlobalStorageConfig; |
||
1495 | OCA.External.Settings.UserStorageConfig = UserStorageConfig; |
||
1496 | OCA.External.Settings.MountConfigListView = MountConfigListView; |
||
1497 | |||
1498 | /** |
||
1499 | * @namespace OAuth2 namespace which is used to verify a storage adapter |
||
1500 | * using AuthMechanism as oauth2::oauth2 |
||
1501 | */ |
||
1502 | OCA.External.Settings.OAuth2 = OCA.External.Settings.OAuth2 || {}; |
||
1503 | |||
1504 | /** |
||
1505 | * This function sends a request to the given backendUrl and gets the OAuth2 URL |
||
1506 | * for any given backend storage, executes the callback if any, set the data-* parameters |
||
1507 | * of the storage and REDIRECTS the client to Authentication page |
||
1508 | * |
||
1509 | * @param {String} backendUrl The backend URL to which request will be sent |
||
1510 | * @param {Object} data Keys -> (backend_id, client_id, client_secret, redirect, tr) |
||
1511 | */ |
||
1512 | OCA.External.Settings.OAuth2.getAuthUrl = function (backendUrl, data) { |
||
1513 | var $tr = data['tr']; |
||
1514 | var configured = $tr.find('[data-parameter="configured"]'); |
||
1515 | var token = $tr.find('.configuration [data-parameter="token"]'); |
||
1516 | |||
1517 | $.post(backendUrl, { |
||
1518 | step: 1, |
||
1519 | client_id: data['client_id'], |
||
1520 | client_secret: data['client_secret'], |
||
1521 | redirect: data['redirect'], |
||
1522 | }, function (result) { |
||
1523 | if (result && result.status == 'success') { |
||
1524 | $(configured).val('false'); |
||
1525 | $(token).val('false'); |
||
1526 | |||
1527 | OCA.External.Settings.mountConfig.saveStorageConfig($tr, function(status) { |
||
1528 | if (!result.data.url) { |
||
1529 | OC.dialogs.alert('Auth URL not set', |
||
1530 | t('files_external', 'No URL provided by backend ' + data['backend_id']) |
||
1531 | ); |
||
1532 | $tr.trigger('oauth_step1_finished', [{ |
||
1533 | success: false, |
||
1534 | tr: $tr, |
||
1535 | data: data |
||
1536 | }]); |
||
1537 | } else { |
||
1538 | $tr.trigger('oauth_step1_finished', [{ |
||
1539 | success: true, |
||
1540 | tr: $tr, |
||
1541 | data: data |
||
1542 | }]); |
||
1543 | window.location = result.data.url; |
||
1544 | } |
||
1545 | }); |
||
1546 | } else { |
||
1547 | OC.dialogs.alert(result.data.message, |
||
1548 | t('files_external', 'Error getting OAuth2 URL for ' + data['backend_id']) |
||
1549 | ); |
||
1550 | $tr.trigger('oauth_step1_finished', [{ |
||
1551 | success: false, |
||
1552 | tr: $tr, |
||
1553 | data: data |
||
1554 | }]); |
||
1555 | } |
||
1556 | } |
||
1557 | ); |
||
1558 | } |
||
1559 | |||
1560 | /** |
||
1561 | * This function verifies the OAuth2 code returned to the client after verification |
||
1562 | * by sending request to the backend with the given CODE and if the code is verified |
||
1563 | * it sets the data-* params to configured and disables the authorize buttons |
||
1564 | * |
||
1565 | * @param {String} backendUrl The backend URL to which request will be sent |
||
1566 | * @param {Object} data Keys -> (backend_id, client_id, client_secret, redirect, tr, code) |
||
1567 | * @return {Promise} jQuery Deferred Promise object |
||
1568 | */ |
||
1569 | OCA.External.Settings.OAuth2.verifyCode = function (backendUrl, data) { |
||
1570 | var $tr = data['tr']; |
||
1571 | var configured = $tr.find('[data-parameter="configured"]'); |
||
1572 | var token = $tr.find('.configuration [data-parameter="token"]'); |
||
1573 | var statusSpan = $tr.find('.status span'); |
||
1574 | statusSpan.removeClass().addClass('waiting'); |
||
1575 | |||
1576 | var deferredObject = $.Deferred(); |
||
1577 | $.post(backendUrl, { |
||
1578 | step: 2, |
||
1579 | client_id: data['client_id'], |
||
1580 | client_secret: data['client_secret'], |
||
1581 | redirect: data['redirect'], |
||
1582 | code: data['code'], |
||
1583 | }, function (result) { |
||
1584 | if (result && result.status == 'success') { |
||
1585 | $(token).val(result.data.token); |
||
1586 | $(configured).val('true'); |
||
1587 | |||
1588 | OCA.External.Settings.mountConfig.saveStorageConfig($tr, function(status) { |
||
1589 | if (status) { |
||
1590 | $tr.find('.configuration input.auth-param') |
||
1591 | .attr('disabled', 'disabled') |
||
1592 | .addClass('disabled-success') |
||
1593 | } |
||
1594 | deferredObject.resolve(status); |
||
1595 | }); |
||
1596 | } else { |
||
1597 | deferredObject.reject(result.data.message); |
||
1598 | } |
||
1599 | } |
||
1600 | ); |
||
1601 | return deferredObject.promise(); |
||
1602 | } |
||
1603 | |||
1604 | })(); |
||
1605 |
You can rewrite this statement in dot notation: