You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

settings.js 38KB

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