settings.js 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513
  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. (function(){
  11. /**
  12. * Returns the selection of applicable users in the given configuration row
  13. *
  14. * @param $row configuration row
  15. * @return array array of user names
  16. */
  17. function getSelection($row) {
  18. var values = $row.find('.applicableUsers').select2('val');
  19. if (!values || values.length === 0) {
  20. values = [];
  21. }
  22. return values;
  23. }
  24. function getSelectedApplicable($row) {
  25. var users = [];
  26. var groups = [];
  27. var multiselect = getSelection($row);
  28. $.each(multiselect, function(index, value) {
  29. // FIXME: don't rely on string parts to detect groups...
  30. var pos = (value.indexOf)?value.indexOf('(group)'): -1;
  31. if (pos !== -1) {
  32. groups.push(value.substr(0, pos));
  33. } else {
  34. users.push(value);
  35. }
  36. });
  37. // FIXME: this should be done in the multiselect change event instead
  38. $row.find('.applicable')
  39. .data('applicable-groups', groups)
  40. .data('applicable-users', users);
  41. return { users, groups };
  42. }
  43. function highlightBorder($element, highlight) {
  44. $element.toggleClass('warning-input', highlight);
  45. return highlight;
  46. }
  47. function isInputValid($input) {
  48. var optional = $input.hasClass('optional');
  49. switch ($input.attr('type')) {
  50. case 'text':
  51. case 'password':
  52. if ($input.val() === '' && !optional) {
  53. return false;
  54. }
  55. break;
  56. }
  57. return true;
  58. }
  59. function highlightInput($input) {
  60. switch ($input.attr('type')) {
  61. case 'text':
  62. case 'password':
  63. return highlightBorder($input, !isInputValid($input));
  64. }
  65. }
  66. /**
  67. * Initialize select2 plugin on the given elements
  68. *
  69. * @param {Array<Object>} array of jQuery elements
  70. * @param {number} userListLimit page size for result list
  71. */
  72. function initApplicableUsersMultiselect($elements, userListLimit) {
  73. var escapeHTML = function (text) {
  74. return text.toString()
  75. .split('&').join('&amp;')
  76. .split('<').join('&lt;')
  77. .split('>').join('&gt;')
  78. .split('"').join('&quot;')
  79. .split('\'').join('&#039;');
  80. };
  81. if (!$elements.length) {
  82. return;
  83. }
  84. return $elements.select2({
  85. placeholder: t('files_external', 'Type to select user or group.'),
  86. allowClear: true,
  87. multiple: true,
  88. toggleSelect: true,
  89. dropdownCssClass: 'files-external-select2',
  90. //minimumInputLength: 1,
  91. ajax: {
  92. url: OC.generateUrl('apps/files_external/applicable'),
  93. dataType: 'json',
  94. quietMillis: 100,
  95. data: function (term, page) { // page is the one-based page number tracked by Select2
  96. return {
  97. pattern: term, //search term
  98. limit: userListLimit, // page size
  99. offset: userListLimit*(page-1) // page number starts with 0
  100. };
  101. },
  102. results: function (data) {
  103. if (data.status === 'success') {
  104. var results = [];
  105. var userCount = 0; // users is an object
  106. // add groups
  107. $.each(data.groups, function(gid, group) {
  108. results.push({name:gid+'(group)', displayname:group, type:'group' });
  109. });
  110. // add users
  111. $.each(data.users, function(id, user) {
  112. userCount++;
  113. results.push({name:id, displayname:user, type:'user' });
  114. });
  115. var more = (userCount >= userListLimit) || (data.groups.length >= userListLimit);
  116. return {results: results, more: more};
  117. } else {
  118. //FIXME add error handling
  119. }
  120. }
  121. },
  122. initSelection: function(element, callback) {
  123. var users = {};
  124. users['users'] = [];
  125. var toSplit = element.val().split(",");
  126. for (var i = 0; i < toSplit.length; i++) {
  127. users['users'].push(toSplit[i]);
  128. }
  129. $.ajax(OC.generateUrl('displaynames'), {
  130. type: 'POST',
  131. contentType: 'application/json',
  132. data: JSON.stringify(users),
  133. dataType: 'json'
  134. }).done(function(data) {
  135. var results = [];
  136. if (data.status === 'success') {
  137. $.each(data.users, function(user, displayname) {
  138. if (displayname !== false) {
  139. results.push({name:user, displayname:displayname, type:'user'});
  140. }
  141. });
  142. callback(results);
  143. } else {
  144. //FIXME add error handling
  145. }
  146. });
  147. },
  148. id: function(element) {
  149. return element.name;
  150. },
  151. formatResult: function (element) {
  152. var $result = $('<span><div class="avatardiv"></div><span>'+escapeHTML(element.displayname)+'</span></span>');
  153. var $div = $result.find('.avatardiv')
  154. .attr('data-type', element.type)
  155. .attr('data-name', element.name)
  156. .attr('data-displayname', element.displayname);
  157. if (element.type === 'group') {
  158. var url = OC.imagePath('core','actions/group');
  159. $div.html('<img width="32" height="32" src="'+url+'">');
  160. }
  161. return $result.get(0).outerHTML;
  162. },
  163. formatSelection: function (element) {
  164. if (element.type === 'group') {
  165. return '<span title="'+escapeHTML(element.name)+'" class="group">'+escapeHTML(element.displayname+' '+t('files_external', '(Group)'))+'</span>';
  166. } else {
  167. return '<span title="'+escapeHTML(element.name)+'" class="user">'+escapeHTML(element.displayname)+'</span>';
  168. }
  169. },
  170. escapeMarkup: function (m) { return m; } // we escape the markup in formatResult and formatSelection
  171. }).on('select2-loaded', function() {
  172. $.each($('.avatardiv'), function(i, div) {
  173. var $div = $(div);
  174. if ($div.data('type') === 'user') {
  175. $div.avatar($div.data('name'),32);
  176. }
  177. });
  178. }).on('change', function(event) {
  179. highlightBorder($(event.target).closest('.applicableUsersContainer').find('.select2-choices'), !event.val.length);
  180. });
  181. }
  182. /**
  183. * @class OCA.Files_External.Settings.StorageConfig
  184. *
  185. * @classdesc External storage config
  186. */
  187. var StorageConfig = function(id) {
  188. this.id = id;
  189. this.backendOptions = {};
  190. };
  191. // Keep this in sync with \OCA\Files_External\MountConfig::STATUS_*
  192. StorageConfig.Status = {
  193. IN_PROGRESS: -1,
  194. SUCCESS: 0,
  195. ERROR: 1,
  196. INDETERMINATE: 2
  197. };
  198. StorageConfig.Visibility = {
  199. NONE: 0,
  200. PERSONAL: 1,
  201. ADMIN: 2,
  202. DEFAULT: 3
  203. };
  204. /**
  205. * @memberof OCA.Files_External.Settings
  206. */
  207. StorageConfig.prototype = {
  208. _url: null,
  209. /**
  210. * Storage id
  211. *
  212. * @type int
  213. */
  214. id: null,
  215. /**
  216. * Mount point
  217. *
  218. * @type string
  219. */
  220. mountPoint: '',
  221. /**
  222. * Backend
  223. *
  224. * @type string
  225. */
  226. backend: null,
  227. /**
  228. * Authentication mechanism
  229. *
  230. * @type string
  231. */
  232. authMechanism: null,
  233. /**
  234. * Backend-specific configuration
  235. *
  236. * @type Object.<string,object>
  237. */
  238. backendOptions: null,
  239. /**
  240. * Mount-specific options
  241. *
  242. * @type Object.<string,object>
  243. */
  244. mountOptions: null,
  245. /**
  246. * Creates or saves the storage.
  247. *
  248. * @param {Function} [options.success] success callback, receives result as argument
  249. * @param {Function} [options.error] error callback
  250. */
  251. save: function(options) {
  252. var self = this;
  253. var url = OC.generateUrl(this._url);
  254. var method = 'POST';
  255. if (_.isNumber(this.id)) {
  256. method = 'PUT';
  257. url = OC.generateUrl(this._url + '/{id}', {id: this.id});
  258. }
  259. $.ajax({
  260. type: method,
  261. url: url,
  262. contentType: 'application/json',
  263. data: JSON.stringify(this.getData()),
  264. success: function(result) {
  265. self.id = result.id;
  266. if (_.isFunction(options.success)) {
  267. options.success(result);
  268. }
  269. },
  270. error: options.error
  271. });
  272. },
  273. /**
  274. * Returns the data from this object
  275. *
  276. * @return {Array} JSON array of the data
  277. */
  278. getData: function() {
  279. var data = {
  280. mountPoint: this.mountPoint,
  281. backend: this.backend,
  282. authMechanism: this.authMechanism,
  283. backendOptions: this.backendOptions,
  284. testOnly: true
  285. };
  286. if (this.id) {
  287. data.id = this.id;
  288. }
  289. if (this.mountOptions) {
  290. data.mountOptions = this.mountOptions;
  291. }
  292. return data;
  293. },
  294. /**
  295. * Recheck the storage
  296. *
  297. * @param {Function} [options.success] success callback, receives result as argument
  298. * @param {Function} [options.error] error callback
  299. */
  300. recheck: function(options) {
  301. if (!_.isNumber(this.id)) {
  302. if (_.isFunction(options.error)) {
  303. options.error();
  304. }
  305. return;
  306. }
  307. $.ajax({
  308. type: 'GET',
  309. url: OC.generateUrl(this._url + '/{id}', {id: this.id}),
  310. data: {'testOnly': true},
  311. success: options.success,
  312. error: options.error
  313. });
  314. },
  315. /**
  316. * Deletes the storage
  317. *
  318. * @param {Function} [options.success] success callback
  319. * @param {Function} [options.error] error callback
  320. */
  321. destroy: function(options) {
  322. if (!_.isNumber(this.id)) {
  323. // the storage hasn't even been created => success
  324. if (_.isFunction(options.success)) {
  325. options.success();
  326. }
  327. return;
  328. }
  329. $.ajax({
  330. type: 'DELETE',
  331. url: OC.generateUrl(this._url + '/{id}', {id: this.id}),
  332. success: options.success,
  333. error: options.error
  334. });
  335. },
  336. /**
  337. * Validate this model
  338. *
  339. * @return {boolean} false if errors exist, true otherwise
  340. */
  341. validate: function() {
  342. if (this.mountPoint === '') {
  343. return false;
  344. }
  345. if (!this.backend) {
  346. return false;
  347. }
  348. if (this.errors) {
  349. return false;
  350. }
  351. return true;
  352. }
  353. };
  354. /**
  355. * @class OCA.Files_External.Settings.GlobalStorageConfig
  356. * @augments OCA.Files_External.Settings.StorageConfig
  357. *
  358. * @classdesc Global external storage config
  359. */
  360. var GlobalStorageConfig = function(id) {
  361. this.id = id;
  362. this.applicableUsers = [];
  363. this.applicableGroups = [];
  364. };
  365. /**
  366. * @memberOf OCA.Files_External.Settings
  367. */
  368. GlobalStorageConfig.prototype = _.extend({}, StorageConfig.prototype,
  369. /** @lends OCA.Files_External.Settings.GlobalStorageConfig.prototype */ {
  370. _url: 'apps/files_external/globalstorages',
  371. /**
  372. * Applicable users
  373. *
  374. * @type Array.<string>
  375. */
  376. applicableUsers: null,
  377. /**
  378. * Applicable groups
  379. *
  380. * @type Array.<string>
  381. */
  382. applicableGroups: null,
  383. /**
  384. * Storage priority
  385. *
  386. * @type int
  387. */
  388. priority: null,
  389. /**
  390. * Returns the data from this object
  391. *
  392. * @return {Array} JSON array of the data
  393. */
  394. getData: function() {
  395. var data = StorageConfig.prototype.getData.apply(this, arguments);
  396. return _.extend(data, {
  397. applicableUsers: this.applicableUsers,
  398. applicableGroups: this.applicableGroups,
  399. priority: this.priority,
  400. });
  401. }
  402. });
  403. /**
  404. * @class OCA.Files_External.Settings.UserStorageConfig
  405. * @augments OCA.Files_External.Settings.StorageConfig
  406. *
  407. * @classdesc User external storage config
  408. */
  409. var UserStorageConfig = function(id) {
  410. this.id = id;
  411. };
  412. UserStorageConfig.prototype = _.extend({}, StorageConfig.prototype,
  413. /** @lends OCA.Files_External.Settings.UserStorageConfig.prototype */ {
  414. _url: 'apps/files_external/userstorages'
  415. });
  416. /**
  417. * @class OCA.Files_External.Settings.UserGlobalStorageConfig
  418. * @augments OCA.Files_External.Settings.StorageConfig
  419. *
  420. * @classdesc User external storage config
  421. */
  422. var UserGlobalStorageConfig = function (id) {
  423. this.id = id;
  424. };
  425. UserGlobalStorageConfig.prototype = _.extend({}, StorageConfig.prototype,
  426. /** @lends OCA.Files_External.Settings.UserStorageConfig.prototype */ {
  427. _url: 'apps/files_external/userglobalstorages'
  428. });
  429. /**
  430. * @class OCA.Files_External.Settings.MountOptionsDropdown
  431. *
  432. * @classdesc Dropdown for mount options
  433. *
  434. * @param {Object} $container container DOM object
  435. */
  436. var MountOptionsDropdown = function() {
  437. };
  438. /**
  439. * @memberof OCA.Files_External.Settings
  440. */
  441. MountOptionsDropdown.prototype = {
  442. /**
  443. * Dropdown element
  444. *
  445. * @var Object
  446. */
  447. $el: null,
  448. /**
  449. * Show dropdown
  450. *
  451. * @param {Object} $container container
  452. * @param {Object} mountOptions mount options
  453. * @param {Array} visibleOptions enabled mount options
  454. */
  455. show: function($container, mountOptions, visibleOptions) {
  456. if (MountOptionsDropdown._last) {
  457. MountOptionsDropdown._last.hide();
  458. }
  459. var $el = $(OCA.Files_External.Templates.mountOptionsDropDown({
  460. mountOptionsEncodingLabel: t('files_external', 'Compatibility with Mac NFD encoding (slow)'),
  461. mountOptionsEncryptLabel: t('files_external', 'Enable encryption'),
  462. mountOptionsPreviewsLabel: t('files_external', 'Enable previews'),
  463. mountOptionsSharingLabel: t('files_external', 'Enable sharing'),
  464. mountOptionsFilesystemCheckLabel: t('files_external', 'Check for changes'),
  465. mountOptionsFilesystemCheckOnce: t('files_external', 'Never'),
  466. mountOptionsFilesystemCheckDA: t('files_external', 'Once every direct access'),
  467. mountOptionsReadOnlyLabel: t('files_external', 'Read only'),
  468. deleteLabel: t('files_external', 'Disconnect')
  469. }));
  470. this.$el = $el;
  471. var storage = $container[0].parentNode.className;
  472. this.setOptions(mountOptions, visibleOptions, storage);
  473. this.$el.appendTo($container);
  474. MountOptionsDropdown._last = this;
  475. this.$el.trigger('show');
  476. },
  477. hide: function() {
  478. if (this.$el) {
  479. this.$el.trigger('hide');
  480. this.$el.remove();
  481. this.$el = null;
  482. MountOptionsDropdown._last = null;
  483. }
  484. },
  485. /**
  486. * Returns the mount options from the dropdown controls
  487. *
  488. * @return {Object} options mount options
  489. */
  490. getOptions: function() {
  491. var options = {};
  492. this.$el.find('input, select').each(function() {
  493. var $this = $(this);
  494. var key = $this.attr('name');
  495. var value = null;
  496. if ($this.attr('type') === 'checkbox') {
  497. value = $this.prop('checked');
  498. } else {
  499. value = $this.val();
  500. }
  501. if ($this.attr('data-type') === 'int') {
  502. value = parseInt(value, 10);
  503. }
  504. options[key] = value;
  505. });
  506. return options;
  507. },
  508. /**
  509. * Sets the mount options to the dropdown controls
  510. *
  511. * @param {Object} options mount options
  512. * @param {Array} visibleOptions enabled mount options
  513. */
  514. setOptions: function(options, visibleOptions, storage) {
  515. if (storage === 'owncloud') {
  516. var ind = visibleOptions.indexOf('encrypt');
  517. if (ind > 0) {
  518. visibleOptions.splice(ind, 1);
  519. }
  520. }
  521. var $el = this.$el;
  522. _.each(options, function(value, key) {
  523. var $optionEl = $el.find('input, select').filterAttr('name', key);
  524. if ($optionEl.attr('type') === 'checkbox') {
  525. if (_.isString(value)) {
  526. value = (value === 'true');
  527. }
  528. $optionEl.prop('checked', !!value);
  529. } else {
  530. $optionEl.val(value);
  531. }
  532. });
  533. $el.find('.optionRow').each(function(i, row){
  534. var $row = $(row);
  535. var optionId = $row.find('input, select').attr('name');
  536. if (visibleOptions.indexOf(optionId) === -1 && !$row.hasClass('persistent')) {
  537. $row.hide();
  538. } else {
  539. $row.show();
  540. }
  541. });
  542. }
  543. };
  544. /**
  545. * @class OCA.Files_External.Settings.MountConfigListView
  546. *
  547. * @classdesc Mount configuration list view
  548. *
  549. * @param {Object} $el DOM object containing the list
  550. * @param {Object} [options]
  551. * @param {number} [options.userListLimit] page size in applicable users dropdown
  552. */
  553. var MountConfigListView = function($el, options) {
  554. this.initialize($el, options);
  555. };
  556. MountConfigListView.ParameterFlags = {
  557. OPTIONAL: 1,
  558. USER_PROVIDED: 2
  559. };
  560. MountConfigListView.ParameterTypes = {
  561. TEXT: 0,
  562. BOOLEAN: 1,
  563. PASSWORD: 2,
  564. HIDDEN: 3
  565. };
  566. /**
  567. * @memberOf OCA.Files_External.Settings
  568. */
  569. MountConfigListView.prototype = _.extend({
  570. /**
  571. * jQuery element containing the config list
  572. *
  573. * @type Object
  574. */
  575. $el: null,
  576. /**
  577. * Storage config class
  578. *
  579. * @type Class
  580. */
  581. _storageConfigClass: null,
  582. /**
  583. * Flag whether the list is about user storage configs (true)
  584. * or global storage configs (false)
  585. *
  586. * @type bool
  587. */
  588. _isPersonal: false,
  589. /**
  590. * Page size in applicable users dropdown
  591. *
  592. * @type int
  593. */
  594. _userListLimit: 30,
  595. /**
  596. * List of supported backends
  597. *
  598. * @type Object.<string,Object>
  599. */
  600. _allBackends: null,
  601. /**
  602. * List of all supported authentication mechanisms
  603. *
  604. * @type Object.<string,Object>
  605. */
  606. _allAuthMechanisms: null,
  607. _encryptionEnabled: false,
  608. /**
  609. * @param {Object} $el DOM object containing the list
  610. * @param {Object} [options]
  611. * @param {number} [options.userListLimit] page size in applicable users dropdown
  612. */
  613. initialize: function($el, options) {
  614. var self = this;
  615. this.$el = $el;
  616. this._isPersonal = ($el.data('admin') !== true);
  617. if (this._isPersonal) {
  618. this._storageConfigClass = OCA.Files_External.Settings.UserStorageConfig;
  619. } else {
  620. this._storageConfigClass = OCA.Files_External.Settings.GlobalStorageConfig;
  621. }
  622. if (options && !_.isUndefined(options.userListLimit)) {
  623. this._userListLimit = options.userListLimit;
  624. }
  625. this._encryptionEnabled = options.encryptionEnabled;
  626. this._canCreateLocal = options.canCreateLocal;
  627. // read the backend config that was carefully crammed
  628. // into the data-configurations attribute of the select
  629. this._allBackends = this.$el.find('.selectBackend').data('configurations');
  630. this._allAuthMechanisms = this.$el.find('#addMountPoint .authentication').data('mechanisms');
  631. this._initEvents();
  632. },
  633. /**
  634. * Custom JS event handlers
  635. * Trigger callback for all existing configurations
  636. */
  637. whenSelectBackend: function(callback) {
  638. this.$el.find('tbody tr:not(#addMountPoint)').each(function(i, tr) {
  639. var backend = $(tr).find('.backend').data('identifier');
  640. callback($(tr), backend);
  641. });
  642. this.on('selectBackend', callback);
  643. },
  644. whenSelectAuthMechanism: function(callback) {
  645. var self = this;
  646. this.$el.find('tbody tr:not(#addMountPoint)').each(function(i, tr) {
  647. var authMechanism = $(tr).find('.selectAuthMechanism').val();
  648. callback($(tr), authMechanism, self._allAuthMechanisms[authMechanism]['scheme']);
  649. });
  650. this.on('selectAuthMechanism', callback);
  651. },
  652. /**
  653. * Initialize DOM event handlers
  654. */
  655. _initEvents: function() {
  656. var self = this;
  657. var onChangeHandler = _.bind(this._onChange, this);
  658. //this.$el.on('input', 'td input', onChangeHandler);
  659. this.$el.on('keyup', 'td input', onChangeHandler);
  660. this.$el.on('paste', 'td input', onChangeHandler);
  661. this.$el.on('change', 'td input:checkbox', onChangeHandler);
  662. this.$el.on('change', '.applicable', onChangeHandler);
  663. this.$el.on('click', '.status>span', function() {
  664. self.recheckStorageConfig($(this).closest('tr'));
  665. });
  666. this.$el.on('click', 'td.mountOptionsToggle .icon-delete', function() {
  667. self.deleteStorageConfig($(this).closest('tr'));
  668. });
  669. this.$el.on('click', 'td.save>.icon-checkmark', function () {
  670. self.saveStorageConfig($(this).closest('tr'));
  671. });
  672. this.$el.on('click', 'td.mountOptionsToggle>.icon-more', function() {
  673. $(this).attr('aria-expanded', 'true');
  674. self._showMountOptionsDropdown($(this).closest('tr'));
  675. });
  676. this.$el.on('change', '.selectBackend', _.bind(this._onSelectBackend, this));
  677. this.$el.on('change', '.selectAuthMechanism', _.bind(this._onSelectAuthMechanism, this));
  678. this.$el.on('change', '.applicableToAllUsers', _.bind(this._onChangeApplicableToAllUsers, this));
  679. },
  680. _onChange: function(event) {
  681. var self = this;
  682. var $target = $(event.target);
  683. if ($target.closest('.dropdown').length) {
  684. // ignore dropdown events
  685. return;
  686. }
  687. highlightInput($target);
  688. var $tr = $target.closest('tr');
  689. this.updateStatus($tr, null);
  690. },
  691. _onSelectBackend: function(event) {
  692. var $target = $(event.target);
  693. var $tr = $target.closest('tr');
  694. var storageConfig = new this._storageConfigClass();
  695. storageConfig.mountPoint = $tr.find('.mountPoint input').val();
  696. storageConfig.backend = $target.val();
  697. $tr.find('.mountPoint input').val('');
  698. var onCompletion = jQuery.Deferred();
  699. $tr = this.newStorage(storageConfig, onCompletion);
  700. $tr.find('.applicableToAllUsers').prop('checked', false).trigger('change');
  701. onCompletion.resolve();
  702. $tr.find('td.configuration').children().not('[type=hidden]').first().focus();
  703. this.saveStorageConfig($tr);
  704. },
  705. _onSelectAuthMechanism: function(event) {
  706. var $target = $(event.target);
  707. var $tr = $target.closest('tr');
  708. var authMechanism = $target.val();
  709. var onCompletion = jQuery.Deferred();
  710. this.configureAuthMechanism($tr, authMechanism, onCompletion);
  711. onCompletion.resolve();
  712. this.saveStorageConfig($tr);
  713. },
  714. _onChangeApplicableToAllUsers: function(event) {
  715. var $target = $(event.target);
  716. var $tr = $target.closest('tr');
  717. var checked = $target.is(':checked');
  718. $tr.find('.applicableUsersContainer').toggleClass('hidden', checked);
  719. if (!checked) {
  720. $tr.find('.applicableUsers').select2('val', '', true);
  721. }
  722. this.saveStorageConfig($tr);
  723. },
  724. /**
  725. * Configure the storage config with a new authentication mechanism
  726. *
  727. * @param {jQuery} $tr config row
  728. * @param {string} authMechanism
  729. * @param {jQuery.Deferred} onCompletion
  730. */
  731. configureAuthMechanism: function($tr, authMechanism, onCompletion) {
  732. var authMechanismConfiguration = this._allAuthMechanisms[authMechanism];
  733. var $td = $tr.find('td.configuration');
  734. $td.find('.auth-param').remove();
  735. $.each(authMechanismConfiguration['configuration'], _.partial(
  736. this.writeParameterInput, $td, _, _, ['auth-param']
  737. ).bind(this));
  738. this.trigger('selectAuthMechanism',
  739. $tr, authMechanism, authMechanismConfiguration['scheme'], onCompletion
  740. );
  741. },
  742. /**
  743. * Create a config row for a new storage
  744. *
  745. * @param {StorageConfig} storageConfig storage config to pull values from
  746. * @param {jQuery.Deferred} onCompletion
  747. * @param {boolean} deferAppend
  748. * @return {jQuery} created row
  749. */
  750. newStorage: function(storageConfig, onCompletion, deferAppend) {
  751. var mountPoint = storageConfig.mountPoint;
  752. var backend = this._allBackends[storageConfig.backend];
  753. if (!backend) {
  754. backend = {
  755. name: 'Unknown: ' + storageConfig.backend,
  756. invalid: true
  757. };
  758. }
  759. // FIXME: Replace with a proper Handlebar template
  760. var $template = this.$el.find('tr#addMountPoint');
  761. var $tr = $template.clone();
  762. if (!deferAppend) {
  763. $tr.insertBefore($template);
  764. }
  765. $tr.data('storageConfig', storageConfig);
  766. $tr.show();
  767. $tr.find('td.mountOptionsToggle, td.save, td.remove').removeClass('hidden');
  768. $tr.find('td').last().removeAttr('style');
  769. $tr.removeAttr('id');
  770. $tr.find('select#selectBackend');
  771. if (!deferAppend) {
  772. initApplicableUsersMultiselect($tr.find('.applicableUsers'), this._userListLimit);
  773. }
  774. if (storageConfig.id) {
  775. $tr.data('id', storageConfig.id);
  776. }
  777. $tr.find('.backend').text(backend.name);
  778. if (mountPoint === '') {
  779. mountPoint = this._suggestMountPoint(backend.name);
  780. }
  781. $tr.find('.mountPoint input').val(mountPoint);
  782. $tr.addClass(backend.identifier);
  783. $tr.find('.backend').data('identifier', backend.identifier);
  784. if (backend.invalid || (backend.identifier === 'local' && !this._canCreateLocal)) {
  785. $tr.find('[name=mountPoint]').prop('disabled', true);
  786. $tr.find('.applicable,.mountOptionsToggle').empty();
  787. $tr.find('.save').empty();
  788. if (backend.invalid) {
  789. this.updateStatus($tr, false, 'Unknown backend: ' + backend.name);
  790. }
  791. return $tr;
  792. }
  793. var selectAuthMechanism = $('<select class="selectAuthMechanism"></select>');
  794. var neededVisibility = (this._isPersonal) ? StorageConfig.Visibility.PERSONAL : StorageConfig.Visibility.ADMIN;
  795. $.each(this._allAuthMechanisms, function(authIdentifier, authMechanism) {
  796. if (backend.authSchemes[authMechanism.scheme] && (authMechanism.visibility & neededVisibility)) {
  797. selectAuthMechanism.append(
  798. $('<option value="'+authMechanism.identifier+'" data-scheme="'+authMechanism.scheme+'">'+authMechanism.name+'</option>')
  799. );
  800. }
  801. });
  802. if (storageConfig.authMechanism) {
  803. selectAuthMechanism.val(storageConfig.authMechanism);
  804. } else {
  805. storageConfig.authMechanism = selectAuthMechanism.val();
  806. }
  807. $tr.find('td.authentication').append(selectAuthMechanism);
  808. var $td = $tr.find('td.configuration');
  809. $.each(backend.configuration, _.partial(this.writeParameterInput, $td).bind(this));
  810. this.trigger('selectBackend', $tr, backend.identifier, onCompletion);
  811. this.configureAuthMechanism($tr, storageConfig.authMechanism, onCompletion);
  812. if (storageConfig.backendOptions) {
  813. $td.find('input, select').each(function() {
  814. var input = $(this);
  815. var val = storageConfig.backendOptions[input.data('parameter')];
  816. if (val !== undefined) {
  817. if(input.is('input:checkbox')) {
  818. input.prop('checked', val);
  819. }
  820. input.val(storageConfig.backendOptions[input.data('parameter')]);
  821. highlightInput(input);
  822. }
  823. });
  824. }
  825. var applicable = [];
  826. if (storageConfig.applicableUsers) {
  827. applicable = applicable.concat(storageConfig.applicableUsers);
  828. }
  829. if (storageConfig.applicableGroups) {
  830. applicable = applicable.concat(
  831. _.map(storageConfig.applicableGroups, function(group) {
  832. return group+'(group)';
  833. })
  834. );
  835. }
  836. if (applicable.length) {
  837. $tr.find('.applicableUsers').val(applicable).trigger('change')
  838. $tr.find('.applicableUsersContainer').removeClass('hidden');
  839. } else {
  840. // applicable to all
  841. $tr.find('.applicableUsersContainer').addClass('hidden');
  842. }
  843. $tr.find('.applicableToAllUsers').prop('checked', !applicable.length);
  844. var priorityEl = $('<input type="hidden" class="priority" value="' + backend.priority + '" />');
  845. $tr.append(priorityEl);
  846. if (storageConfig.mountOptions) {
  847. $tr.find('input.mountOptions').val(JSON.stringify(storageConfig.mountOptions));
  848. } else {
  849. // FIXME default backend mount options
  850. $tr.find('input.mountOptions').val(JSON.stringify({
  851. 'encrypt': true,
  852. 'previews': true,
  853. 'enable_sharing': false,
  854. 'filesystem_check_changes': 1,
  855. 'encoding_compatibility': false,
  856. 'readonly': false,
  857. }));
  858. }
  859. return $tr;
  860. },
  861. /**
  862. * Load storages into config rows
  863. */
  864. loadStorages: function() {
  865. var self = this;
  866. var onLoaded1 = $.Deferred();
  867. var onLoaded2 = $.Deferred();
  868. this.$el.find('.externalStorageLoading').removeClass('hidden');
  869. $.when(onLoaded1, onLoaded2).always(() => {
  870. self.$el.find('.externalStorageLoading').addClass('hidden');
  871. })
  872. if (this._isPersonal) {
  873. // load userglobal storages
  874. $.ajax({
  875. type: 'GET',
  876. url: OC.generateUrl('apps/files_external/userglobalstorages'),
  877. data: {'testOnly' : true},
  878. contentType: 'application/json',
  879. success: function(result) {
  880. var onCompletion = jQuery.Deferred();
  881. var $rows = $();
  882. Object.values(result).forEach(function(storageParams) {
  883. var storageConfig;
  884. var isUserGlobal = storageParams.type === 'system' && self._isPersonal;
  885. storageParams.mountPoint = storageParams.mountPoint.substr(1); // trim leading slash
  886. if (isUserGlobal) {
  887. storageConfig = new UserGlobalStorageConfig();
  888. } else {
  889. storageConfig = new self._storageConfigClass();
  890. }
  891. _.extend(storageConfig, storageParams);
  892. var $tr = self.newStorage(storageConfig, onCompletion,true);
  893. // userglobal storages must be at the top of the list
  894. $tr.detach();
  895. self.$el.prepend($tr);
  896. var $authentication = $tr.find('.authentication');
  897. $authentication.text($authentication.find('select option:selected').text());
  898. // disable any other inputs
  899. $tr.find('.mountOptionsToggle, .remove').empty();
  900. $tr.find('input:not(.user_provided), select:not(.user_provided)').attr('disabled', 'disabled');
  901. if (isUserGlobal) {
  902. $tr.find('.configuration').find(':not(.user_provided)').remove();
  903. } else {
  904. // userglobal storages do not expose configuration data
  905. $tr.find('.configuration').text(t('files_external', 'Admin defined'));
  906. }
  907. $rows = $rows.add($tr);
  908. });
  909. initApplicableUsersMultiselect(self.$el.find('.applicableUsers'), this._userListLimit);
  910. self.$el.find('tr#addMountPoint').before($rows);
  911. var mainForm = $('#files_external');
  912. if (result.length === 0 && mainForm.attr('data-can-create') === 'false') {
  913. mainForm.hide();
  914. $('a[href="#external-storage"]').parent().hide();
  915. $('.emptycontent').show();
  916. }
  917. onCompletion.resolve();
  918. onLoaded1.resolve();
  919. }
  920. });
  921. } else {
  922. onLoaded1.resolve();
  923. }
  924. var url = this._storageConfigClass.prototype._url;
  925. $.ajax({
  926. type: 'GET',
  927. url: OC.generateUrl(url),
  928. contentType: 'application/json',
  929. success: function(result) {
  930. result = Object.values(result);
  931. var onCompletion = jQuery.Deferred();
  932. var $rows = $();
  933. result.forEach(function(storageParams) {
  934. storageParams.mountPoint = (storageParams.mountPoint === '/')? '/' : storageParams.mountPoint.substr(1); // trim leading slash
  935. var storageConfig = new self._storageConfigClass();
  936. _.extend(storageConfig, storageParams);
  937. var $tr = self.newStorage(storageConfig, onCompletion, true);
  938. // don't recheck config automatically when there are a large number of storages
  939. if (result.length < 20) {
  940. self.recheckStorageConfig($tr);
  941. } else {
  942. self.updateStatus($tr, StorageConfig.Status.INDETERMINATE, t('files_external', 'Automatic status checking is disabled due to the large number of configured storages, click to check status'));
  943. }
  944. $rows = $rows.add($tr);
  945. });
  946. initApplicableUsersMultiselect($rows.find('.applicableUsers'), this._userListLimit);
  947. self.$el.find('tr#addMountPoint').before($rows);
  948. onCompletion.resolve();
  949. onLoaded2.resolve();
  950. }
  951. });
  952. },
  953. /**
  954. * @param {jQuery} $td
  955. * @param {string} parameter
  956. * @param {string} placeholder
  957. * @param {Array} classes
  958. * @return {jQuery} newly created input
  959. */
  960. writeParameterInput: function($td, parameter, placeholder, classes) {
  961. var hasFlag = function(flag) {
  962. return (placeholder.flags & flag) === flag;
  963. };
  964. classes = $.isArray(classes) ? classes : [];
  965. classes.push('added');
  966. if (hasFlag(MountConfigListView.ParameterFlags.OPTIONAL)) {
  967. classes.push('optional');
  968. }
  969. if (hasFlag(MountConfigListView.ParameterFlags.USER_PROVIDED)) {
  970. if (this._isPersonal) {
  971. classes.push('user_provided');
  972. } else {
  973. return;
  974. }
  975. }
  976. var newElement;
  977. var trimmedPlaceholder = placeholder.value;
  978. if (placeholder.type === MountConfigListView.ParameterTypes.PASSWORD) {
  979. newElement = $('<input type="password" class="'+classes.join(' ')+'" data-parameter="'+parameter+'" placeholder="'+ trimmedPlaceholder+'" />');
  980. } else if (placeholder.type === MountConfigListView.ParameterTypes.BOOLEAN) {
  981. var checkboxId = _.uniqueId('checkbox_');
  982. newElement = $('<div><label><input type="checkbox" id="'+checkboxId+'" class="'+classes.join(' ')+'" data-parameter="'+parameter+'" />'+ trimmedPlaceholder+'</label></div>');
  983. } else if (placeholder.type === MountConfigListView.ParameterTypes.HIDDEN) {
  984. newElement = $('<input type="hidden" class="'+classes.join(' ')+'" data-parameter="'+parameter+'" />');
  985. } else {
  986. newElement = $('<input type="text" class="'+classes.join(' ')+'" data-parameter="'+parameter+'" placeholder="'+ trimmedPlaceholder+'" />');
  987. }
  988. if (placeholder.defaultValue) {
  989. if (placeholder.type === MountConfigListView.ParameterTypes.BOOLEAN) {
  990. newElement.find('input').prop('checked', placeholder.defaultValue);
  991. } else {
  992. newElement.val(placeholder.defaultValue);
  993. }
  994. }
  995. if (placeholder.tooltip) {
  996. newElement.attr('title', placeholder.tooltip);
  997. }
  998. highlightInput(newElement);
  999. $td.append(newElement);
  1000. return newElement;
  1001. },
  1002. /**
  1003. * Gets the storage model from the given row
  1004. *
  1005. * @param $tr row element
  1006. * @return {OCA.Files_External.StorageConfig} storage model instance
  1007. */
  1008. getStorageConfig: function($tr) {
  1009. var storageId = $tr.data('id');
  1010. if (!storageId) {
  1011. // new entry
  1012. storageId = null;
  1013. }
  1014. var storage = $tr.data('storageConfig');
  1015. if (!storage) {
  1016. storage = new this._storageConfigClass(storageId);
  1017. }
  1018. storage.errors = null;
  1019. storage.mountPoint = $tr.find('.mountPoint input').val();
  1020. storage.backend = $tr.find('.backend').data('identifier');
  1021. storage.authMechanism = $tr.find('.selectAuthMechanism').val();
  1022. var classOptions = {};
  1023. var configuration = $tr.find('.configuration input');
  1024. var missingOptions = [];
  1025. $.each(configuration, function(index, input) {
  1026. var $input = $(input);
  1027. var parameter = $input.data('parameter');
  1028. if ($input.attr('type') === 'button') {
  1029. return;
  1030. }
  1031. if (!isInputValid($input) && !$input.hasClass('optional')) {
  1032. missingOptions.push(parameter);
  1033. return;
  1034. }
  1035. if ($(input).is(':checkbox')) {
  1036. if ($(input).is(':checked')) {
  1037. classOptions[parameter] = true;
  1038. } else {
  1039. classOptions[parameter] = false;
  1040. }
  1041. } else {
  1042. classOptions[parameter] = $(input).val();
  1043. }
  1044. });
  1045. storage.backendOptions = classOptions;
  1046. if (missingOptions.length) {
  1047. storage.errors = {
  1048. backendOptions: missingOptions
  1049. };
  1050. }
  1051. // gather selected users and groups
  1052. if (!this._isPersonal) {
  1053. var multiselect = getSelectedApplicable($tr);
  1054. var users = multiselect.users || [];
  1055. var groups = multiselect.groups || [];
  1056. var isApplicableToAllUsers = $tr.find('.applicableToAllUsers').is(':checked');
  1057. if (isApplicableToAllUsers) {
  1058. storage.applicableUsers = [];
  1059. storage.applicableGroups = [];
  1060. } else {
  1061. storage.applicableUsers = users;
  1062. storage.applicableGroups = groups;
  1063. if (!storage.applicableUsers.length && !storage.applicableGroups.length) {
  1064. if (!storage.errors) {
  1065. storage.errors = {};
  1066. }
  1067. storage.errors['requiredApplicable'] = true;
  1068. }
  1069. }
  1070. storage.priority = parseInt($tr.find('input.priority').val() || '100', 10);
  1071. }
  1072. var mountOptions = $tr.find('input.mountOptions').val();
  1073. if (mountOptions) {
  1074. storage.mountOptions = JSON.parse(mountOptions);
  1075. }
  1076. return storage;
  1077. },
  1078. /**
  1079. * Deletes the storage from the given tr
  1080. *
  1081. * @param $tr storage row
  1082. * @param Function callback callback to call after save
  1083. */
  1084. deleteStorageConfig: function($tr) {
  1085. var self = this;
  1086. var configId = $tr.data('id');
  1087. if (!_.isNumber(configId)) {
  1088. // deleting unsaved storage
  1089. $tr.remove();
  1090. return;
  1091. }
  1092. var storage = new this._storageConfigClass(configId);
  1093. OC.dialogs.confirm(t('files_external', 'Are you sure you want to disconnect this external storage? It will make the storage unavailable in Nextcloud and will lead to a deletion of these files and folders on any sync client that is currently connected but will not delete any files and folders on the external storage itself.', {
  1094. storage: this.mountPoint
  1095. }), t('files_external', 'Delete storage?'), function(confirm) {
  1096. if (confirm) {
  1097. self.updateStatus($tr, StorageConfig.Status.IN_PROGRESS);
  1098. storage.destroy({
  1099. success: function () {
  1100. $tr.remove();
  1101. },
  1102. error: function () {
  1103. self.updateStatus($tr, StorageConfig.Status.ERROR);
  1104. }
  1105. });
  1106. }
  1107. });
  1108. },
  1109. /**
  1110. * Saves the storage from the given tr
  1111. *
  1112. * @param $tr storage row
  1113. * @param Function callback callback to call after save
  1114. * @param concurrentTimer only update if the timer matches this
  1115. */
  1116. saveStorageConfig:function($tr, callback, concurrentTimer) {
  1117. var self = this;
  1118. var storage = this.getStorageConfig($tr);
  1119. if (!storage || !storage.validate()) {
  1120. return false;
  1121. }
  1122. this.updateStatus($tr, StorageConfig.Status.IN_PROGRESS);
  1123. storage.save({
  1124. success: function(result) {
  1125. if (concurrentTimer === undefined
  1126. || $tr.data('save-timer') === concurrentTimer
  1127. ) {
  1128. self.updateStatus($tr, result.status);
  1129. $tr.data('id', result.id);
  1130. if (_.isFunction(callback)) {
  1131. callback(storage);
  1132. }
  1133. }
  1134. },
  1135. error: function() {
  1136. if (concurrentTimer === undefined
  1137. || $tr.data('save-timer') === concurrentTimer
  1138. ) {
  1139. self.updateStatus($tr, StorageConfig.Status.ERROR);
  1140. }
  1141. }
  1142. });
  1143. },
  1144. /**
  1145. * Recheck storage availability
  1146. *
  1147. * @param {jQuery} $tr storage row
  1148. * @return {boolean} success
  1149. */
  1150. recheckStorageConfig: function($tr) {
  1151. var self = this;
  1152. var storage = this.getStorageConfig($tr);
  1153. if (!storage.validate()) {
  1154. return false;
  1155. }
  1156. this.updateStatus($tr, StorageConfig.Status.IN_PROGRESS);
  1157. storage.recheck({
  1158. success: function(result) {
  1159. self.updateStatus($tr, result.status, result.statusMessage);
  1160. },
  1161. error: function() {
  1162. self.updateStatus($tr, StorageConfig.Status.ERROR);
  1163. }
  1164. });
  1165. },
  1166. /**
  1167. * Update status display
  1168. *
  1169. * @param {jQuery} $tr
  1170. * @param {number} status
  1171. * @param {string} message
  1172. */
  1173. updateStatus: function($tr, status, message) {
  1174. var $statusSpan = $tr.find('.status span');
  1175. switch (status) {
  1176. case null:
  1177. // remove status
  1178. break;
  1179. case StorageConfig.Status.IN_PROGRESS:
  1180. $statusSpan.attr('class', 'icon-loading-small');
  1181. break;
  1182. case StorageConfig.Status.SUCCESS:
  1183. $statusSpan.attr('class', 'success icon-checkmark-white');
  1184. break;
  1185. case StorageConfig.Status.INDETERMINATE:
  1186. $statusSpan.attr('class', 'indeterminate icon-info-white');
  1187. break;
  1188. default:
  1189. $statusSpan.attr('class', 'error icon-error-white');
  1190. }
  1191. if (typeof message === 'string') {
  1192. $statusSpan.attr('title', message);
  1193. $statusSpan.tooltip();
  1194. } else {
  1195. $statusSpan.tooltip('dispose');
  1196. }
  1197. },
  1198. /**
  1199. * Suggest mount point name that doesn't conflict with the existing names in the list
  1200. *
  1201. * @param {string} defaultMountPoint default name
  1202. */
  1203. _suggestMountPoint: function(defaultMountPoint) {
  1204. var $el = this.$el;
  1205. var pos = defaultMountPoint.indexOf('/');
  1206. if (pos !== -1) {
  1207. defaultMountPoint = defaultMountPoint.substring(0, pos);
  1208. }
  1209. defaultMountPoint = defaultMountPoint.replace(/\s+/g, '');
  1210. var i = 1;
  1211. var append = '';
  1212. var match = true;
  1213. while (match && i < 20) {
  1214. match = false;
  1215. $el.find('tbody td.mountPoint input').each(function(index, mountPoint) {
  1216. if ($(mountPoint).val() === defaultMountPoint+append) {
  1217. match = true;
  1218. return false;
  1219. }
  1220. });
  1221. if (match) {
  1222. append = i;
  1223. i++;
  1224. } else {
  1225. break;
  1226. }
  1227. }
  1228. return defaultMountPoint + append;
  1229. },
  1230. /**
  1231. * Toggles the mount options dropdown
  1232. *
  1233. * @param {Object} $tr configuration row
  1234. */
  1235. _showMountOptionsDropdown: function($tr) {
  1236. var self = this;
  1237. var storage = this.getStorageConfig($tr);
  1238. var $toggle = $tr.find('.mountOptionsToggle');
  1239. var dropDown = new MountOptionsDropdown();
  1240. var visibleOptions = [
  1241. 'previews',
  1242. 'filesystem_check_changes',
  1243. 'enable_sharing',
  1244. 'encoding_compatibility',
  1245. 'readonly',
  1246. 'delete'
  1247. ];
  1248. if (this._encryptionEnabled) {
  1249. visibleOptions.push('encrypt');
  1250. }
  1251. dropDown.show($toggle, storage.mountOptions || [], visibleOptions);
  1252. $('body').on('mouseup.mountOptionsDropdown', function(event) {
  1253. var $target = $(event.target);
  1254. if ($target.closest('.popovermenu').length) {
  1255. return;
  1256. }
  1257. dropDown.hide();
  1258. });
  1259. dropDown.$el.on('hide', function() {
  1260. var mountOptions = dropDown.getOptions();
  1261. $('body').off('mouseup.mountOptionsDropdown');
  1262. $tr.find('input.mountOptions').val(JSON.stringify(mountOptions));
  1263. $tr.find('td.mountOptionsToggle>.icon-more').attr('aria-expanded', 'false');
  1264. self.saveStorageConfig($tr);
  1265. });
  1266. }
  1267. }, OC.Backbone.Events);
  1268. window.addEventListener('DOMContentLoaded', function() {
  1269. var enabled = $('#files_external').attr('data-encryption-enabled');
  1270. var canCreateLocal = $('#files_external').attr('data-can-create-local');
  1271. var encryptionEnabled = (enabled ==='true')? true: false;
  1272. var mountConfigListView = new MountConfigListView($('#externalStorage'), {
  1273. encryptionEnabled: encryptionEnabled,
  1274. canCreateLocal: (canCreateLocal === 'true') ? true: false,
  1275. });
  1276. mountConfigListView.loadStorages();
  1277. // TODO: move this into its own View class
  1278. var $allowUserMounting = $('#allowUserMounting');
  1279. $allowUserMounting.bind('change', function() {
  1280. OC.msg.startSaving('#userMountingMsg');
  1281. if (this.checked) {
  1282. OCP.AppConfig.setValue('files_external', 'allow_user_mounting', 'yes');
  1283. $('input[name="allowUserMountingBackends\\[\\]"]').prop('checked', true);
  1284. $('#userMountingBackends').removeClass('hidden');
  1285. $('input[name="allowUserMountingBackends\\[\\]"]').eq(0).trigger('change');
  1286. } else {
  1287. OCP.AppConfig.setValue('files_external', 'allow_user_mounting', 'no');
  1288. $('#userMountingBackends').addClass('hidden');
  1289. }
  1290. OC.msg.finishedSaving('#userMountingMsg', {status: 'success', data: {message: t('files_external', 'Saved')}});
  1291. });
  1292. $('input[name="allowUserMountingBackends\\[\\]"]').bind('change', function() {
  1293. OC.msg.startSaving('#userMountingMsg');
  1294. var userMountingBackends = $('input[name="allowUserMountingBackends\\[\\]"]:checked').map(function(){
  1295. return $(this).val();
  1296. }).get();
  1297. var deprecatedBackends = $('input[name="allowUserMountingBackends\\[\\]"][data-deprecate-to]').map(function(){
  1298. if ($.inArray($(this).data('deprecate-to'), userMountingBackends) !== -1) {
  1299. return $(this).val();
  1300. }
  1301. return null;
  1302. }).get();
  1303. userMountingBackends = userMountingBackends.concat(deprecatedBackends);
  1304. OCP.AppConfig.setValue('files_external', 'user_mounting_backends', userMountingBackends.join());
  1305. OC.msg.finishedSaving('#userMountingMsg', {status: 'success', data: {message: t('files_external', 'Saved')}});
  1306. // disable allowUserMounting
  1307. if(userMountingBackends.length === 0) {
  1308. $allowUserMounting.prop('checked', false);
  1309. $allowUserMounting.trigger('change');
  1310. }
  1311. });
  1312. $('#global_credentials').on('submit', function() {
  1313. var $form = $(this);
  1314. var uid = $form.find('[name=uid]').val();
  1315. var user = $form.find('[name=username]').val();
  1316. var password = $form.find('[name=password]').val();
  1317. var $submit = $form.find('[type=submit]');
  1318. $submit.val(t('files_external', 'Saving …'));
  1319. $.ajax({
  1320. type: 'POST',
  1321. contentType: 'application/json',
  1322. data: JSON.stringify({
  1323. uid: uid,
  1324. user: user,
  1325. password: password
  1326. }),
  1327. url: OC.generateUrl('apps/files_external/globalcredentials'),
  1328. dataType: 'json',
  1329. success: function() {
  1330. $submit.val(t('files_external', 'Saved'));
  1331. setTimeout(function(){
  1332. $submit.val(t('files_external', 'Save'));
  1333. }, 2500);
  1334. }
  1335. });
  1336. return false;
  1337. });
  1338. // global instance
  1339. OCA.Files_External.Settings.mountConfig = mountConfigListView;
  1340. /**
  1341. * Legacy
  1342. *
  1343. * @namespace
  1344. * @deprecated use OCA.Files_External.Settings.mountConfig instead
  1345. */
  1346. OC.MountConfig = {
  1347. saveStorage: _.bind(mountConfigListView.saveStorageConfig, mountConfigListView)
  1348. };
  1349. });
  1350. // export
  1351. OCA.Files_External = OCA.Files_External || {};
  1352. /**
  1353. * @namespace
  1354. */
  1355. OCA.Files_External.Settings = OCA.Files_External.Settings || {};
  1356. OCA.Files_External.Settings.GlobalStorageConfig = GlobalStorageConfig;
  1357. OCA.Files_External.Settings.UserStorageConfig = UserStorageConfig;
  1358. OCA.Files_External.Settings.MountConfigListView = MountConfigListView;
  1359. })();