app.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. /*
  2. * Copyright (c) 2014
  3. *
  4. * @author Vincent Petry
  5. * @copyright 2014 Vincent Petry <pvince81@owncloud.com>
  6. *
  7. * This file is licensed under the Affero General Public License version 3
  8. * or later.
  9. *
  10. * See the COPYING-README file.
  11. *
  12. */
  13. /* global dragOptions, folderDropOptions, OC */
  14. (function() {
  15. if (!OCA.Files) {
  16. /**
  17. * Namespace for the files app
  18. * @namespace OCA.Files
  19. */
  20. OCA.Files = {};
  21. }
  22. /**
  23. * @namespace OCA.Files.App
  24. */
  25. OCA.Files.App = {
  26. /**
  27. * Navigation control
  28. *
  29. * @member {OCA.Files.Navigation}
  30. */
  31. navigation: null,
  32. /**
  33. * File list for the "All files" section.
  34. *
  35. * @member {OCA.Files.FileList}
  36. */
  37. fileList: null,
  38. currentFileList: null,
  39. /**
  40. * Backbone model for storing files preferences
  41. */
  42. _filesConfig: null,
  43. /**
  44. * Initializes the files app
  45. */
  46. initialize: function() {
  47. this.navigation = new OCA.Files.Navigation($('#app-navigation'));
  48. this.$showHiddenFiles = $('input#showhiddenfilesToggle');
  49. var showHidden = $('#showHiddenFiles').val() === "1";
  50. this.$showHiddenFiles.prop('checked', showHidden);
  51. if ($('#fileNotFound').val() === "1") {
  52. OC.Notification.show(t('files', 'File could not be found'), {type: 'error'});
  53. }
  54. this._filesConfig = new OC.Backbone.Model({
  55. showhidden: showHidden
  56. });
  57. var urlParams = OC.Util.History.parseUrlQuery();
  58. var fileActions = new OCA.Files.FileActions();
  59. // default actions
  60. fileActions.registerDefaultActions();
  61. // regular actions
  62. fileActions.merge(OCA.Files.fileActions);
  63. this._onActionsUpdated = _.bind(this._onActionsUpdated, this);
  64. OCA.Files.fileActions.on('setDefault.app-files', this._onActionsUpdated);
  65. OCA.Files.fileActions.on('registerAction.app-files', this._onActionsUpdated);
  66. this.files = OCA.Files.Files;
  67. // TODO: ideally these should be in a separate class / app (the embedded "all files" app)
  68. this.fileList = new OCA.Files.FileList(
  69. $('#app-content-files'), {
  70. dragOptions: dragOptions,
  71. folderDropOptions: folderDropOptions,
  72. fileActions: fileActions,
  73. allowLegacyActions: true,
  74. scrollTo: urlParams.scrollto,
  75. filesClient: OC.Files.getClient(),
  76. multiSelectMenu: [
  77. {
  78. name: 'copyMove',
  79. displayName: t('files', 'Move or copy'),
  80. iconClass: 'icon-external',
  81. },
  82. {
  83. name: 'download',
  84. displayName: t('files', 'Download'),
  85. iconClass: 'icon-download',
  86. },
  87. OCA.Files.FileList.MultiSelectMenuActions.ToggleSelectionModeAction,
  88. {
  89. name: 'delete',
  90. displayName: t('files', 'Delete'),
  91. iconClass: 'icon-delete',
  92. },
  93. ],
  94. sorting: {
  95. mode: $('#defaultFileSorting').val(),
  96. direction: $('#defaultFileSortingDirection').val()
  97. },
  98. config: this._filesConfig,
  99. enableUpload: true,
  100. maxChunkSize: OC.appConfig.files && OC.appConfig.files.max_chunk_size
  101. }
  102. );
  103. this.updateCurrentFileList(this.fileList)
  104. this.files.initialize();
  105. // for backward compatibility, the global FileList will
  106. // refer to the one of the "files" view
  107. window.FileList = this.fileList;
  108. OC.Plugins.attach('OCA.Files.App', this);
  109. this._setupEvents();
  110. // trigger URL change event handlers
  111. this._onPopState(urlParams);
  112. $('#quota.has-tooltip').tooltip({
  113. placement: 'top'
  114. });
  115. this._debouncedPersistShowHiddenFilesState = _.debounce(this._persistShowHiddenFilesState, 1200);
  116. if (sessionStorage.getItem('WhatsNewServerCheck') < (Date.now() - 3600*1000)) {
  117. OCP.WhatsNew.query(); // for Nextcloud server
  118. sessionStorage.setItem('WhatsNewServerCheck', Date.now());
  119. }
  120. },
  121. /**
  122. * Destroy the app
  123. */
  124. destroy: function() {
  125. this.navigation = null;
  126. this.fileList.destroy();
  127. this.fileList = null;
  128. this.files = null;
  129. OCA.Files.fileActions.off('setDefault.app-files', this._onActionsUpdated);
  130. OCA.Files.fileActions.off('registerAction.app-files', this._onActionsUpdated);
  131. },
  132. _onActionsUpdated: function(ev) {
  133. // forward new action to the file list
  134. if (ev.action) {
  135. this.fileList.fileActions.registerAction(ev.action);
  136. } else if (ev.defaultAction) {
  137. this.fileList.fileActions.setDefault(
  138. ev.defaultAction.mime,
  139. ev.defaultAction.name
  140. );
  141. }
  142. },
  143. /**
  144. * Set the currently active file list
  145. *
  146. * Due to the file list implementations being registered after clicking the
  147. * navigation item for the first time, OCA.Files.App is not aware of those until
  148. * they have initialized themselves. Therefore the files list needs to call this
  149. * method manually
  150. *
  151. * @param {OCA.Files.FileList} newFileList
  152. */
  153. updateCurrentFileList: function(newFileList) {
  154. this.currentFileList = newFileList;
  155. },
  156. /**
  157. * Return the currently active file list
  158. * @return {?OCA.Files.FileList}
  159. */
  160. getCurrentFileList: function () {
  161. return this.currentFileList;
  162. },
  163. /**
  164. * Returns the container of the currently visible app.
  165. *
  166. * @return app container
  167. */
  168. getCurrentAppContainer: function() {
  169. return this.navigation.getActiveContainer();
  170. },
  171. /**
  172. * Sets the currently active view
  173. * @param viewId view id
  174. */
  175. setActiveView: function(viewId, options) {
  176. this.navigation.setActiveItem(viewId, options);
  177. },
  178. /**
  179. * Returns the view id of the currently active view
  180. * @return view id
  181. */
  182. getActiveView: function() {
  183. return this.navigation.getActiveItem();
  184. },
  185. /**
  186. *
  187. * @returns {Backbone.Model}
  188. */
  189. getFilesConfig: function() {
  190. return this._filesConfig;
  191. },
  192. /**
  193. * Setup events based on URL changes
  194. */
  195. _setupEvents: function() {
  196. OC.Util.History.addOnPopStateHandler(_.bind(this._onPopState, this));
  197. // detect when app changed their current directory
  198. $('#app-content').delegate('>div', 'changeDirectory', _.bind(this._onDirectoryChanged, this));
  199. $('#app-content').delegate('>div', 'afterChangeDirectory', _.bind(this._onAfterDirectoryChanged, this));
  200. $('#app-content').delegate('>div', 'changeViewerMode', _.bind(this._onChangeViewerMode, this));
  201. $('#app-navigation').on('itemChanged', _.bind(this._onNavigationChanged, this));
  202. this.$showHiddenFiles.on('change', _.bind(this._onShowHiddenFilesChange, this));
  203. },
  204. /**
  205. * Toggle showing hidden files according to the settings checkbox
  206. *
  207. * @returns {undefined}
  208. */
  209. _onShowHiddenFilesChange: function() {
  210. var show = this.$showHiddenFiles.is(':checked');
  211. this._filesConfig.set('showhidden', show);
  212. this._debouncedPersistShowHiddenFilesState();
  213. },
  214. /**
  215. * Persist show hidden preference on the server
  216. *
  217. * @returns {undefined}
  218. */
  219. _persistShowHiddenFilesState: function() {
  220. var show = this._filesConfig.get('showhidden');
  221. $.post(OC.generateUrl('/apps/files/api/v1/showhidden'), {
  222. show: show
  223. });
  224. },
  225. /**
  226. * Event handler for when the current navigation item has changed
  227. */
  228. _onNavigationChanged: function(e) {
  229. var params;
  230. if (e && e.itemId) {
  231. params = {
  232. view: typeof e.view === 'string' && e.view !== '' ? e.view : e.itemId,
  233. dir: e.dir ? e.dir : '/'
  234. };
  235. this._changeUrl(params.view, params.dir);
  236. OC.Apps.hideAppSidebar($('.detailsView'));
  237. this.navigation.getActiveContainer().trigger(new $.Event('urlChanged', params));
  238. }
  239. },
  240. /**
  241. * Event handler for when an app notified that its directory changed
  242. */
  243. _onDirectoryChanged: function(e) {
  244. if (e.dir) {
  245. this._changeUrl(this.navigation.getActiveItem(), e.dir, e.fileId);
  246. }
  247. },
  248. /**
  249. * Event handler for when an app notified that its directory changed
  250. */
  251. _onAfterDirectoryChanged: function(e) {
  252. if (e.dir && e.fileId) {
  253. this._changeUrl(this.navigation.getActiveItem(), e.dir, e.fileId);
  254. }
  255. },
  256. /**
  257. * Event handler for when an app notifies that it needs space
  258. * for viewer mode.
  259. */
  260. _onChangeViewerMode: function(e) {
  261. var state = !!e.viewerModeEnabled;
  262. if (e.viewerModeEnabled) {
  263. OC.Apps.hideAppSidebar($('.detailsView'));
  264. }
  265. $('#app-navigation').toggleClass('hidden', state);
  266. $('.app-files').toggleClass('viewer-mode no-sidebar', state);
  267. },
  268. /**
  269. * Event handler for when the URL changed
  270. */
  271. _onPopState: function(params) {
  272. params = _.extend({
  273. dir: '/',
  274. view: 'files'
  275. }, params);
  276. var lastId = this.navigation.getActiveItem();
  277. if (!this.navigation.itemExists(params.view)) {
  278. params.view = 'files';
  279. }
  280. this.navigation.setActiveItem(params.view, {silent: true});
  281. if (lastId !== this.navigation.getActiveItem()) {
  282. this.navigation.getActiveContainer().trigger(new $.Event('show'));
  283. }
  284. this.navigation.getActiveContainer().trigger(new $.Event('urlChanged', params));
  285. },
  286. /**
  287. * Encode URL params into a string, except for the "dir" attribute
  288. * that gets encoded as path where "/" is not encoded
  289. *
  290. * @param {Object.<string>} params
  291. * @return {string} encoded params
  292. */
  293. _makeUrlParams: function(params) {
  294. var dir = params.dir;
  295. delete params.dir;
  296. return 'dir=' + OC.encodePath(dir) + '&' + OC.buildQueryString(params);
  297. },
  298. /**
  299. * Change the URL to point to the given dir and view
  300. */
  301. _changeUrl: function(view, dir, fileId) {
  302. var params = {dir: dir};
  303. if (view !== 'files') {
  304. params.view = view;
  305. } else if (fileId) {
  306. params.fileid = fileId;
  307. }
  308. var currentParams = OC.Util.History.parseUrlQuery();
  309. if (currentParams.dir === params.dir && currentParams.view === params.view && currentParams.fileid !== params.fileid) {
  310. // if only fileid changed or was added, replace instead of push
  311. OC.Util.History.replaceState(this._makeUrlParams(params));
  312. } else {
  313. OC.Util.History.pushState(this._makeUrlParams(params));
  314. }
  315. }
  316. };
  317. })();
  318. window.addEventListener('DOMContentLoaded', function() {
  319. // wait for other apps/extensions to register their event handlers and file actions
  320. // in the "ready" clause
  321. _.defer(function() {
  322. OCA.Files.App.initialize();
  323. });
  324. });