settings.js 39 KB

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