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'] = []; |
||
0 ignored issues
–
show
|
|||
135 | var toSplit = element.val().split(","); |
||
136 | for (var i = 0; i < toSplit.length; i++) { |
||
137 | users['users'].push(toSplit[i]); |
||
0 ignored issues
–
show
|
|||
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) { |
||
0 ignored issues
–
show
It is generally not recommended to make functions within a loop.
While making functions in a loop will not lead to any runtime error, the code might not behave as you expect as the variables in the scope are not imported by value, but by reference. Let’s take a look at an example: var funcs = [];
for (var i=0; i<10; i++) {
funcs.push(function() {
alert(i);
});
}
funcs[0](); // alert(10);
funcs[1](); // alert(10);
/// ...
funcs[9](); // alert(10);
If you would instead like to bind the function inside the loop to the value of the variable during that specific iteration, you can create the function from another function: var createFunc = function(i) {
return function() {
alert(i);
};
};
var funcs = [];
for (var i=0; i<10; i++) {
funcs.push(createFunc(i));
}
funcs[0](); // alert(0)
funcs[1](); // alert(1)
// ...
funcs[9](); // alert(9)
Loading history...
|
|||
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: