123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 |
- /*
- * Copyright (c) 2014
- *
- * @author Vincent Petry
- * @copyright 2014 Vincent Petry <pvince81@owncloud.com>
- *
- * This file is licensed under the Affero General Public License version 3
- * or later.
- *
- * See the COPYING-README file.
- *
- */
- /* global dragOptions, folderDropOptions, OC */
- (function() {
- if (!OCA.Files) {
- /**
- * Namespace for the files app
- * @namespace OCA.Files
- */
- OCA.Files = {};
- }
- /**
- * @namespace OCA.Files.App
- */
- OCA.Files.App = {
- /**
- * Navigation control
- *
- * @member {OCA.Files.Navigation}
- */
- navigation: null,
- /**
- * File list for the "All files" section.
- *
- * @member {OCA.Files.FileList}
- */
- fileList: null,
- currentFileList: null,
- /**
- * Backbone model for storing files preferences
- */
- _filesConfig: null,
- /**
- * Initializes the files app
- */
- initialize: function() {
- this.navigation = new OCA.Files.Navigation($('#app-navigation'));
- this.$showHiddenFiles = $('input#showhiddenfilesToggle');
- var showHidden = $('#showHiddenFiles').val() === "1";
- this.$showHiddenFiles.prop('checked', showHidden);
- // crop image previews
- this.$cropImagePreviews = $('input#cropimagepreviewsToggle');
- var cropImagePreviews = $('#cropImagePreviews').val() === "1";
- this.$cropImagePreviews.prop('checked', cropImagePreviews);
- // Toggle for grid view
- this.$showGridView = $('input#showgridview');
- this.$showGridView.on('change', _.bind(this._onGridviewChange, this));
- if ($('#fileNotFound').val() === "1") {
- OC.Notification.show(t('files', 'File could not be found'), {type: 'error'});
- }
- this._filesConfig = new OC.Backbone.Model({
- showhidden: showHidden,
- cropimagepreviews: cropImagePreviews,
- });
- var urlParams = OC.Util.History.parseUrlQuery();
- var fileActions = new OCA.Files.FileActions();
- // default actions
- fileActions.registerDefaultActions();
- // regular actions
- fileActions.merge(OCA.Files.fileActions);
- this._onActionsUpdated = _.bind(this._onActionsUpdated, this);
- OCA.Files.fileActions.on('setDefault.app-files', this._onActionsUpdated);
- OCA.Files.fileActions.on('registerAction.app-files', this._onActionsUpdated);
- this.files = OCA.Files.Files;
- // TODO: ideally these should be in a separate class / app (the embedded "all files" app)
- this.fileList = new OCA.Files.FileList(
- $('#app-content-files'), {
- dragOptions: dragOptions,
- folderDropOptions: folderDropOptions,
- fileActions: fileActions,
- allowLegacyActions: true,
- scrollTo: urlParams.scrollto,
- openFile: urlParams.openfile,
- filesClient: OC.Files.getClient(),
- multiSelectMenu: [
- {
- name: 'copyMove',
- displayName: t('files', 'Move or copy'),
- iconClass: 'icon-external',
- order: 10,
- },
- {
- name: 'download',
- displayName: t('files', 'Download'),
- iconClass: 'icon-download',
- order: 10,
- },
- OCA.Files.FileList.MultiSelectMenuActions.ToggleSelectionModeAction,
- {
- name: 'delete',
- displayName: t('files', 'Delete'),
- iconClass: 'icon-delete',
- order: 99,
- },
- ...(
- OCA?.SystemTags === undefined ? [] : ([{
- name: 'tags',
- displayName: t('files', 'Tags'),
- iconClass: 'icon-tag',
- order: 100,
- }])
- ),
- ],
- sorting: {
- mode: $('#defaultFileSorting').val(),
- direction: $('#defaultFileSortingDirection').val()
- },
- config: this._filesConfig,
- enableUpload: true,
- maxChunkSize: OC.appConfig.files && OC.appConfig.files.max_chunk_size
- }
- );
- this.updateCurrentFileList(this.fileList)
- this.files.initialize();
- // for backward compatibility, the global FileList will
- // refer to the one of the "files" view
- window.FileList = this.fileList;
- OC.Plugins.attach('OCA.Files.App', this);
- this._setupEvents();
- // trigger URL change event handlers
- this._onPopState(urlParams);
- this._debouncedPersistShowHiddenFilesState = _.debounce(this._persistShowHiddenFilesState, 1200);
- this._debouncedPersistCropImagePreviewsState = _.debounce(this._persistCropImagePreviewsState, 1200);
- if (sessionStorage.getItem('WhatsNewServerCheck') < (Date.now() - 3600*1000)) {
- OCP.WhatsNew.query(); // for Nextcloud server
- sessionStorage.setItem('WhatsNewServerCheck', Date.now());
- }
- },
- /**
- * Destroy the app
- */
- destroy: function() {
- this.navigation = null;
- this.fileList.destroy();
- this.fileList = null;
- this.files = null;
- OCA.Files.fileActions.off('setDefault.app-files', this._onActionsUpdated);
- OCA.Files.fileActions.off('registerAction.app-files', this._onActionsUpdated);
- },
- _onActionsUpdated: function(ev) {
- // forward new action to the file list
- if (ev.action) {
- this.fileList.fileActions.registerAction(ev.action);
- } else if (ev.defaultAction) {
- this.fileList.fileActions.setDefault(
- ev.defaultAction.mime,
- ev.defaultAction.name
- );
- }
- },
- /**
- * Set the currently active file list
- *
- * Due to the file list implementations being registered after clicking the
- * navigation item for the first time, OCA.Files.App is not aware of those until
- * they have initialized themselves. Therefore the files list needs to call this
- * method manually
- *
- * @param {OCA.Files.FileList} newFileList -
- */
- updateCurrentFileList: function(newFileList) {
- if (this.currentFileList === newFileList) {
- return
- }
- this.currentFileList = newFileList;
- if (this.currentFileList !== null) {
- // update grid view to the current value
- const isGridView = this.$showGridView.is(':checked');
- this.currentFileList.setGridView(isGridView);
- }
- window._nc_event_bus.emit('files:navigation:changed')
- },
- /**
- * Return the currently active file list
- * @return {?OCA.Files.FileList}
- */
- getCurrentFileList: function () {
- return this.currentFileList;
- },
- /**
- * Returns the container of the currently visible app.
- *
- * @return app container
- */
- getCurrentAppContainer: function() {
- return this.navigation.getActiveContainer();
- },
- /**
- * Sets the currently active view
- * @param viewId view id
- */
- setActiveView: function(viewId, options) {
- this.navigation.setActiveItem(viewId, options);
- },
- /**
- * Returns the view id of the currently active view
- * @return view id
- */
- getActiveView: function() {
- return this.navigation.getActiveItem();
- },
- /**
- *
- * @returns {Backbone.Model}
- */
- getFilesConfig: function() {
- return this._filesConfig;
- },
- /**
- * Setup events based on URL changes
- */
- _setupEvents: function() {
- OC.Util.History.addOnPopStateHandler(_.bind(this._onPopState, this));
- // detect when app changed their current directory
- $('#app-content').delegate('>div', 'changeDirectory', _.bind(this._onDirectoryChanged, this));
- $('#app-content').delegate('>div', 'afterChangeDirectory', _.bind(this._onAfterDirectoryChanged, this));
- $('#app-content').delegate('>div', 'changeViewerMode', _.bind(this._onChangeViewerMode, this));
- $('#app-navigation').on('itemChanged', _.bind(this._onNavigationChanged, this));
- this.$showHiddenFiles.on('change', _.bind(this._onShowHiddenFilesChange, this));
- this.$cropImagePreviews.on('change', _.bind(this._onCropImagePreviewsChange, this));
- },
- /**
- * Toggle showing hidden files according to the settings checkbox
- *
- * @returns {undefined}
- */
- _onShowHiddenFilesChange: function() {
- var show = this.$showHiddenFiles.is(':checked');
- this._filesConfig.set('showhidden', show);
- this._debouncedPersistShowHiddenFilesState();
- },
- /**
- * Persist show hidden preference on the server
- *
- * @returns {undefined}
- */
- _persistShowHiddenFilesState: function() {
- var show = this._filesConfig.get('showhidden');
- $.post(OC.generateUrl('/apps/files/api/v1/showhidden'), {
- show: show
- });
- },
- /**
- * Toggle cropping image previews according to the settings checkbox
- *
- * @returns void
- */
- _onCropImagePreviewsChange: function() {
- var crop = this.$cropImagePreviews.is(':checked');
- this._filesConfig.set('cropimagepreviews', crop);
- this._debouncedPersistCropImagePreviewsState();
- },
- /**
- * Persist crop image previews preference on the server
- *
- * @returns void
- */
- _persistCropImagePreviewsState: function() {
- var crop = this._filesConfig.get('cropimagepreviews');
- $.post(OC.generateUrl('/apps/files/api/v1/cropimagepreviews'), {
- crop: crop
- });
- },
- /**
- * Event handler for when the current navigation item has changed
- */
- _onNavigationChanged: function(e) {
- var params;
- if (e && e.itemId) {
- params = {
- view: typeof e.view === 'string' && e.view !== '' ? e.view : e.itemId,
- dir: e.dir ? e.dir : '/'
- };
- this._changeUrl(params.view, params.dir);
- OCA.Files.Sidebar.close();
- this.navigation.getActiveContainer().trigger(new $.Event('urlChanged', params));
- window._nc_event_bus.emit('files:navigation:changed')
- }
- },
- /**
- * Event handler for when an app notified that its directory changed
- */
- _onDirectoryChanged: function(e) {
- if (e.dir && !e.changedThroughUrl) {
- this._changeUrl(this.navigation.getActiveItem(), e.dir, e.fileId);
- }
- },
- /**
- * Event handler for when an app notified that its directory changed
- */
- _onAfterDirectoryChanged: function(e) {
- if (e.dir && e.fileId) {
- this._changeUrl(this.navigation.getActiveItem(), e.dir, e.fileId);
- }
- },
- /**
- * Event handler for when an app notifies that it needs space
- * for viewer mode.
- */
- _onChangeViewerMode: function(e) {
- var state = !!e.viewerModeEnabled;
- if (e.viewerModeEnabled) {
- OCA.Files.Sidebar.close();
- }
- $('#app-navigation').toggleClass('hidden', state);
- $('.app-files').toggleClass('viewer-mode no-sidebar', state);
- },
- /**
- * Event handler for when the URL changed
- */
- _onPopState: function(params) {
- params = _.extend({
- dir: '/',
- view: 'files'
- }, params);
- var lastId = this.navigation.getActiveItem();
- if (!this.navigation.itemExists(params.view)) {
- params.view = 'files';
- }
- this.navigation.setActiveItem(params.view, {silent: true});
- if (lastId !== this.navigation.getActiveItem()) {
- this.navigation.getActiveContainer().trigger(new $.Event('show'));
- window._nc_event_bus.emit('files:navigation:changed')
- }
- this.navigation.getActiveContainer().trigger(new $.Event('urlChanged', params));
- },
- /**
- * Encode URL params into a string, except for the "dir" attribute
- * that gets encoded as path where "/" is not encoded
- *
- * @param {Object.<string>} params
- * @return {string} encoded params
- */
- _makeUrlParams: function(params) {
- var dir = params.dir;
- delete params.dir;
- return 'dir=' + OC.encodePath(dir) + '&' + OC.buildQueryString(params);
- },
- /**
- * Change the URL to point to the given dir and view
- */
- _changeUrl: function(view, dir, fileId) {
- var params = {dir: dir};
- if (view !== 'files') {
- params.view = view;
- } else if (fileId) {
- params.fileid = fileId;
- }
- var currentParams = OC.Util.History.parseUrlQuery();
- if (currentParams.dir === params.dir && currentParams.view === params.view) {
- if (currentParams.fileid !== params.fileid) {
- // if only fileid changed or was added, replace instead of push
- OC.Util.History.replaceState(this._makeUrlParams(params));
- }
- } else {
- OC.Util.History.pushState(this._makeUrlParams(params));
- }
- },
- /**
- * Toggle showing gridview by default or not
- *
- * @returns {undefined}
- */
- _onGridviewChange: function() {
- const isGridView = this.$showGridView.is(':checked');
- // only save state if user is logged in
- if (OC.currentUser) {
- $.post(OC.generateUrl('/apps/files/api/v1/showgridview'), {
- show: isGridView,
- });
- }
- this.$showGridView.next('#view-toggle')
- .removeClass('icon-toggle-filelist icon-toggle-pictures')
- .addClass(isGridView ? 'icon-toggle-filelist' : 'icon-toggle-pictures')
- this.$showGridView.next('#view-toggle')
- .attr('title', isGridView ? t('files', 'Show list view') : t('files', 'Show grid view'))
- this.$showGridView.attr('aria-label', isGridView ? t('files', 'Show list view') : t('files', 'Show grid view'))
- if (this.currentFileList) {
- this.currentFileList.setGridView(isGridView);
- }
- },
- };
- })();
- window.addEventListener('DOMContentLoaded', function() {
- // wait for other apps/extensions to register their event handlers and file actions
- // in the "ready" clause
- _.defer(function() {
- OCA.Files.App.initialize();
- });
- });
|