diff options
Diffstat (limited to 'core/src/OC')
41 files changed, 470 insertions, 2785 deletions
diff --git a/core/src/OC/admin.js b/core/src/OC/admin.js index 5c939415266..d29e4cf676b 100644 --- a/core/src/OC/admin.js +++ b/core/src/OC/admin.js @@ -1,24 +1,6 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ const isAdmin = !!window._oc_isadmin diff --git a/core/src/OC/appconfig.js b/core/src/OC/appconfig.js index 4b03f8db5fb..350ffc3f21c 100644 --- a/core/src/OC/appconfig.js +++ b/core/src/OC/appconfig.js @@ -1,32 +1,11 @@ /** - * @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2014 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ /* eslint-disable */ - import { getValue, setValue, getApps, getKeys, deleteKey } from '../OCP/appconfig' + import { getValue, setValue, getApps, getKeys, deleteKey } from '../OCP/appconfig.js' export const appConfig = window.oc_appconfig || {} diff --git a/core/src/OC/apps.js b/core/src/OC/apps.js index bbda177409e..dec2b94bfbb 100644 --- a/core/src/OC/apps.js +++ b/core/src/OC/apps.js @@ -1,24 +1,7 @@ /** - * @copyright Bernhard Posselt 2014 - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2014 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ import $ from 'jquery' @@ -90,6 +73,7 @@ export const registerAppsSlideToggle = () => { }) area.removeClass('opened') $(button).removeClass('opened') + $(button).attr('aria-expanded', 'false') } /** @@ -101,6 +85,7 @@ export const registerAppsSlideToggle = () => { }) area.addClass('opened') $(button).addClass('opened') + $(button).attr('aria-expanded', 'true') const input = $(areaSelector + ' [autofocus]') if (input.length === 1) { input.focus() diff --git a/core/src/OC/appsettings.js b/core/src/OC/appsettings.js deleted file mode 100644 index a81708ca461..00000000000 --- a/core/src/OC/appsettings.js +++ /dev/null @@ -1,97 +0,0 @@ -/** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -/* eslint-disable */ -import $ from 'jquery' -import { filePath } from './routing' -import { generateFilePath } from "@nextcloud/router" - -/** - * Opens a popup with the setting for an app. - * @param {string} appid The ID of the app e.g. 'calendar', 'contacts' or 'files'. - * @param {boolean|string} loadJS If true 'js/settings.js' is loaded. If it's a string - * it will attempt to load a script by that name in the 'js' directory. - * @param {boolean} [cache] If true the javascript file won't be forced refreshed. Defaults to true. - * @param {string} [scriptName] The name of the PHP file to load. Defaults to 'settings.php' in - * the root of the app directory hierarchy. - * - * @deprecated 17.0.0 this method is unused and will be removed with Nextcloud 18 - */ -export const appSettings = args => { - console.warn('OC.appSettings is deprecated and will be removed with Nextcloud 18') - - if (typeof args === 'undefined' || typeof args.appid === 'undefined') { - throw { - name: 'MissingParameter', - message: 'The parameter appid is missing' - } - } - var props = { scriptName: 'settings.php', cache: true } - $.extend(props, args) - var settings = $('#appsettings') - if (settings.length === 0) { - throw { - name: 'MissingDOMElement', - message: 'There has be be an element with id "appsettings" for the popup to show.' - } - } - var popup = $('#appsettings_popup') - if (popup.length === 0) { - $('body').prepend('<div class="popup hidden" id="appsettings_popup"></div>') - popup = $('#appsettings_popup') - popup.addClass(settings.hasClass('topright') ? 'topright' : 'bottomleft') - } - if (popup.is(':visible')) { - popup.hide().remove() - } else { - const arrowclass = settings.hasClass('topright') ? 'up' : 'left' - $.get(generateFilePath(props.appid, '', props.scriptName), function(data) { - popup.html(data).ready(function() { - popup.prepend('<span class="arrow ' + arrowclass + '"></span><h2>' + t('core', 'Settings') + '</h2><a class="close"></a>').show() - popup.find('.close').bind('click', function() { - popup.remove() - }) - if (typeof props.loadJS !== 'undefined') { - var scriptname - if (props.loadJS === true) { - scriptname = 'settings.js' - } else if (typeof props.loadJS === 'string') { - scriptname = props.loadJS - } else { - throw { - name: 'InvalidParameter', - message: 'The "loadJS" parameter must be either boolean or a string.' - } - } - if (props.cache) { - $.ajaxSetup({ cache: true }) - } - $.getScript(generateFilePath(props.appid, 'js', scriptname)) - .fail(function(jqxhr, settings, e) { - throw e - }) - } - }).show() - }, 'html') - } -} diff --git a/core/src/OC/appswebroots.js b/core/src/OC/appswebroots.js index ec2420eeed5..debbd2084bf 100644 --- a/core/src/OC/appswebroots.js +++ b/core/src/OC/appswebroots.js @@ -1,23 +1,6 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ const appswebroots = (window._oc_appswebroots !== undefined) ? window._oc_appswebroots : false diff --git a/core/src/OC/backbone-webdav.js b/core/src/OC/backbone-webdav.js index ab234e22005..318c50e8ee5 100644 --- a/core/src/OC/backbone-webdav.js +++ b/core/src/OC/backbone-webdav.js @@ -1,25 +1,6 @@ /** - * Copyright (c) 2015 - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Julius Härtl <jus@bitgrid.net> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ /* eslint-disable */ diff --git a/core/src/OC/backbone.js b/core/src/OC/backbone.js index 17ef1c87109..08520e278f6 100644 --- a/core/src/OC/backbone.js +++ b/core/src/OC/backbone.js @@ -1,28 +1,10 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ import VendorBackbone from 'backbone' -import { davCall, davSync } from './backbone-webdav' +import { davCall, davSync } from './backbone-webdav.js' const Backbone = VendorBackbone.noConflict() diff --git a/core/src/OC/capabilities.js b/core/src/OC/capabilities.js index c7df9b4f3d1..10623229625 100644 --- a/core/src/OC/capabilities.js +++ b/core/src/OC/capabilities.js @@ -1,25 +1,6 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ import { getCapabilities as realGetCapabilities } from '@nextcloud/capabilities' @@ -32,6 +13,6 @@ import { getCapabilities as realGetCapabilities } from '@nextcloud/capabilities' * @since 14.0.0 */ export const getCapabilities = () => { - console.warn('OC.getCapabilities is deprecated and will be removed in Nextcloud 21. See @nextcloud/capabilities') + OC.debug && console.warn('OC.getCapabilities is deprecated and will be removed in Nextcloud 21. See @nextcloud/capabilities') return realGetCapabilities() } diff --git a/core/src/OC/config.js b/core/src/OC/config.js index 702105a4836..c47df61f6e6 100644 --- a/core/src/OC/config.js +++ b/core/src/OC/config.js @@ -1,23 +1,6 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ const config = window._oc_config || {} diff --git a/core/src/OC/constants.js b/core/src/OC/constants.js index f2ba7bf7a97..5298107e94d 100644 --- a/core/src/OC/constants.js +++ b/core/src/OC/constants.js @@ -1,24 +1,6 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ export const coreApps = ['', 'admin', 'log', 'core/search', 'core', '3rdparty'] diff --git a/core/src/OC/contactsmenu.js b/core/src/OC/contactsmenu.js deleted file mode 100644 index b9f4b0fc064..00000000000 --- a/core/src/OC/contactsmenu.js +++ /dev/null @@ -1,468 +0,0 @@ -/** - * @copyright 2017 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -/* eslint-disable */ -import _ from 'underscore' -import $ from 'jquery' -import { Collection, Model, View } from 'backbone' - -import OC from './index' - -/** - * @class Contact - */ -const Contact = Model.extend({ - defaults: { - fullName: '', - lastMessage: '', - actions: [], - hasOneAction: false, - hasTwoActions: false, - hasManyActions: false - }, - - /** - * @returns {undefined} - */ - initialize: function() { - // Add needed property for easier template rendering - if (this.get('actions').length === 0) { - this.set('hasOneAction', true) - } else if (this.get('actions').length === 1) { - this.set('hasTwoActions', true) - this.set('secondAction', this.get('actions')[0]) - } else { - this.set('hasManyActions', true) - } - } -}) - -/** - * @class ContactCollection - * @private - */ -const ContactCollection = Collection.extend({ - model: Contact -}) - -/** - * @class ContactsListView - * @private - */ -const ContactsListView = View.extend({ - - /** @type {ContactCollection} */ - _collection: undefined, - - /** @type {array} */ - _subViews: [], - - /** - * @param {object} options - * @returns {undefined} - */ - initialize: function(options) { - this._collection = options.collection - }, - - /** - * @returns {self} - */ - render: function() { - var self = this - self.$el.html('') - self._subViews = [] - - self._collection.forEach(function(contact) { - var item = new ContactsListItemView({ - model: contact - }) - item.render() - self.$el.append(item.$el) - item.on('toggle:actionmenu', self._onChildActionMenuToggle, self) - self._subViews.push(item) - }) - - return self - }, - - /** - * Event callback to propagate opening (another) entry's action menu - * - * @param {type} $src - * @returns {undefined} - */ - _onChildActionMenuToggle: function($src) { - this._subViews.forEach(function(view) { - view.trigger('parent:toggle:actionmenu', $src) - }) - } -}) - -/** - * @class ContactsListItemView - * @private - */ -const ContactsListItemView = View.extend({ - - /** @type {string} */ - className: 'contact', - - /** @type {undefined|function} */ - _template: undefined, - - /** @type {Contact} */ - _model: undefined, - - /** @type {boolean} */ - _actionMenuShown: false, - - events: { - 'click .icon-more': '_onToggleActionsMenu' - }, - - contactTemplate: require('./contactsmenu/contact.handlebars'), - - /** - * @param {object} data - * @returns {undefined} - */ - template: function(data) { - return this.contactTemplate(data) - }, - - /** - * @param {object} options - * @returns {undefined} - */ - initialize: function(options) { - this._model = options.model - this.on('parent:toggle:actionmenu', this._onOtherActionMenuOpened, this) - }, - - /** - * @returns {self} - */ - render: function() { - this.$el.html(this.template({ - contact: this._model.toJSON() - })) - this.delegateEvents() - - // Show placeholder if no avatar is available (avatar is rendered as img, not div) - this.$('div.avatar').imageplaceholder(this._model.get('fullName')) - - // Show tooltip for top action - this.$('.top-action').tooltip({ placement: 'left' }) - // Show tooltip for second action - this.$('.second-action').tooltip({ placement: 'left' }) - - return this - }, - - /** - * Toggle the visibility of the action popover menu - * - * @private - * @returns {undefined} - */ - _onToggleActionsMenu: function() { - this._actionMenuShown = !this._actionMenuShown - if (this._actionMenuShown) { - this.$('.menu').show() - } else { - this.$('.menu').hide() - } - this.trigger('toggle:actionmenu', this.$el) - }, - - /** - * @private - * @argument {jQuery} $src - * @returns {undefined} - */ - _onOtherActionMenuOpened: function($src) { - if (this.$el.is($src)) { - // Ignore - return - } - this._actionMenuShown = false - this.$('.menu').hide() - } -}) - -/** - * @class ContactsMenuView - * @private - */ -const ContactsMenuView = View.extend({ - - /** @type {undefined|function} */ - _loadingTemplate: undefined, - - /** @type {undefined|function} */ - _errorTemplate: undefined, - - /** @type {undefined|function} */ - _contentTemplate: undefined, - - /** @type {undefined|function} */ - _contactsTemplate: undefined, - - /** @type {undefined|ContactCollection} */ - _contacts: undefined, - - /** @type {string} */ - _searchTerm: '', - - events: { - 'input #contactsmenu-search': '_onSearch' - }, - - templates: { - loading: require('./contactsmenu/loading.handlebars'), - error: require('./contactsmenu/error.handlebars'), - menu: require('./contactsmenu/menu.handlebars'), - list: require('./contactsmenu/list.handlebars') - }, - - /** - * @returns {undefined} - */ - _onSearch: _.debounce(function(e) { - var searchTerm = this.$('#contactsmenu-search').val() - // IE11 triggers an 'input' event after the view has been rendered - // resulting in an endless loading loop. To prevent this, we remember - // the last search term to savely ignore some events - // See https://github.com/nextcloud/server/issues/5281 - if (searchTerm !== this._searchTerm) { - this.trigger('search', this.$('#contactsmenu-search').val()) - this._searchTerm = searchTerm - } - }, 700), - - /** - * @param {object} data - * @returns {string} - */ - loadingTemplate: function(data) { - return this.templates.loading(data) - }, - - /** - * @param {object} data - * @returns {string} - */ - errorTemplate: function(data) { - return this.templates.error( - _.extend({ - couldNotLoadText: t('core', 'Could not load your contacts') - }, data) - ) - }, - - /** - * @param {object} data - * @returns {string} - */ - contentTemplate: function(data) { - return this.templates.menu( - _.extend({ - searchContactsText: t('core', 'Search contacts …') - }, data) - ) - }, - - /** - * @param {object} data - * @returns {string} - */ - contactsTemplate: function(data) { - return this.templates.list( - _.extend({ - noContactsFoundText: t('core', 'No contacts found'), - showAllContactsText: t('core', 'Show all contacts …'), - contactsAppMgmtText: t('core', 'Install the Contacts app') - }, data) - ) - }, - - /** - * @param {object} options - * @returns {undefined} - */ - initialize: function(options) { - this.options = options - }, - - /** - * @param {string} text - * @returns {undefined} - */ - showLoading: function(text) { - this.render() - this._contacts = undefined - this.$('.content').html(this.loadingTemplate({ - loadingText: text - })) - }, - - /** - * @returns {undefined} - */ - showError: function() { - this.render() - this._contacts = undefined - this.$('.content').html(this.errorTemplate()) - }, - - /** - * @param {object} viewData - * @param {string} searchTerm - * @returns {undefined} - */ - showContacts: function(viewData, searchTerm) { - this._contacts = viewData.contacts - this.render({ - contacts: viewData.contacts - }) - - var list = new ContactsListView({ - collection: viewData.contacts - }) - list.render() - this.$('.content').html(this.contactsTemplate({ - contacts: viewData.contacts, - searchTerm: searchTerm, - contactsAppEnabled: viewData.contactsAppEnabled, - contactsAppURL: OC.generateUrl('/apps/contacts'), - canInstallApp: OC.isUserAdmin(), - contactsAppMgmtURL: OC.generateUrl('/settings/apps/social/contacts') - })) - this.$('#contactsmenu-contacts').html(list.$el) - }, - - /** - * @param {object} data - * @returns {self} - */ - render: function(data) { - var searchVal = this.$('#contactsmenu-search').val() - this.$el.html(this.contentTemplate(data)) - - // Focus search - this.$('#contactsmenu-search').val(searchVal) - this.$('#contactsmenu-search').focus() - return this - } - -}) - -/** - * @param {Object} options - * @param {string} options.el - * @class ContactsMenu - * @memberOf OC - */ -const ContactsMenu = function(options) { - this.initialize(options) -} - -ContactsMenu.prototype = { - /** @type {string} */ - $el: undefined, - - /** @type {ContactsMenuView} */ - _view: undefined, - - /** @type {Promise} */ - _contactsPromise: undefined, - - /** - * @param {Object} options - * @param {string} options.el - the selector of the element to render the menu in - * @returns {undefined} - */ - initialize: function(options) { - this.$el = $(options.el) - - this._view = new ContactsMenuView({ - el: this.$el, - }) - - this._view.on('search', function(searchTerm) { - this.loadContacts(searchTerm) - }, this) - }, - - /** - * @private - * @param {string|undefined} searchTerm - * @returns {Promise} - */ - _getContacts: function(searchTerm) { - var url = OC.generateUrl('/contactsmenu/contacts') - return Promise.resolve($.ajax(url, { - method: 'POST', - data: { - filter: searchTerm - } - })) - }, - - /** - * @param {string|undefined} searchTerm - * @returns {undefined} - */ - loadContacts: function(searchTerm) { - var self = this - - if (!self._contactsPromise) { - self._contactsPromise = self._getContacts(searchTerm) - } - - if (_.isUndefined(searchTerm) || searchTerm === '') { - self._view.showLoading(t('core', 'Loading your contacts …')) - } else { - self._view.showLoading(t('core', 'Looking for {term} …', { - term: searchTerm - })) - } - return self._contactsPromise.then(function(data) { - // Convert contact entries to Backbone collection - data.contacts = new ContactCollection(data.contacts) - - self._view.showContacts(data, searchTerm) - }, function(e) { - self._view.showError() - console.error('There was an error loading your contacts', e) - }).then(function() { - // Delete promise, so that contacts are fetched again when the - // menu is opened the next time. - delete self._contactsPromise - }).catch(console.error.bind(this)) - } -} - -export default ContactsMenu diff --git a/core/src/OC/contactsmenu/contact.handlebars b/core/src/OC/contactsmenu/contact.handlebars deleted file mode 100644 index afb2f627663..00000000000 --- a/core/src/OC/contactsmenu/contact.handlebars +++ /dev/null @@ -1,70 +0,0 @@ -{{#if contact.avatar}} - {{#if contact.profileUrl}} - {{#if contact.profileTitle}} - <a class="profile-link--avatar" href="{{contact.profileUrl}}"> - <img src="{{contact.avatar}}&size=32" class="avatar" srcset="{{contact.avatar}}&size=32 1x, {{contact.avatar}}&size=64 2x, {{contact.avatar}}&size=128 4x" alt=""> - </a> - {{/if}} - {{else}} - <img src="{{contact.avatar}}&size=32" class="avatar" srcset="{{contact.avatar}}&size=32 1x, {{contact.avatar}}&size=64 2x, {{contact.avatar}}&size=128 4x" alt=""> - {{/if}} -{{else}} - {{#if contact.profileUrl}} - {{#if contact.profileTitle}} - <a class="profile-link--avatar" href="{{contact.profileUrl}}"> - <div class="avatar"></div> - </a> - {{/if}} - {{else}} - <div class="avatar"></div> - {{/if}} -{{/if}} -{{#if contact.profileUrl}} - {{#if contact.profileTitle}} - <a class="body profile-link--full-name" href="{{contact.profileUrl}}"> - <div class="full-name">{{contact.fullName}}</div> - <div class="last-message">{{contact.lastMessage}}</div> - <div class="email-address">{{contact.emailAddresses}}</div> - </a> - {{/if}} - {{#if contact.topAction}} - <a class="top-action" href="{{contact.topAction.hyperlink}}" title="{{contact.topAction.title}}"> - <img src="{{contact.topAction.icon}}" alt="{{contact.topAction.title}}"> - </a> - {{/if}} -{{else if contact.topAction}} - <a class="body" href="{{contact.topAction.hyperlink}}"> - <div class="full-name">{{contact.fullName}}</div> - <div class="last-message">{{contact.lastMessage}}</div> - <div class="email-address">{{contact.emailAddresses}}</div> - </a> - <a class="top-action" href="{{contact.topAction.hyperlink}}" title="{{contact.topAction.title}}"> - <img src="{{contact.topAction.icon}}" alt="{{contact.topAction.title}}"> - </a> -{{else}} - <div class="body"> - <div class="full-name">{{contact.fullName}}</div> - <div class="last-message">{{contact.lastMessage}}</div> - <div class="email-address">{{contact.emailAddresses}}</div> - </div> -{{/if}} -{{#if contact.hasTwoActions}} -<a class="second-action" href="{{contact.secondAction.hyperlink}}" title="{{contact.secondAction.title}}"> - <img src="{{contact.secondAction.icon}}" alt="{{contact.secondAction.title}}"> -</a> -{{/if}} -{{#if contact.hasManyActions}} - <button class="other-actions icon-more"></button> - <div class="menu popovermenu"> - <ul> - {{#each contact.actions}} - <li> - <a href="{{hyperlink}}"> - <img src="{{icon}}" alt=""> - <span>{{title}}</span> - </a> - </li> - {{/each}} - </ul> - </div> -{{/if}} diff --git a/core/src/OC/contactsmenu/error.handlebars b/core/src/OC/contactsmenu/error.handlebars deleted file mode 100644 index 5115595b4e1..00000000000 --- a/core/src/OC/contactsmenu/error.handlebars +++ /dev/null @@ -1,4 +0,0 @@ -<div class="emptycontent"> - <div class="icon-search"></div> - <h2>{{couldNotLoadText}}</h2> -</div> diff --git a/core/src/OC/contactsmenu/list.handlebars b/core/src/OC/contactsmenu/list.handlebars deleted file mode 100644 index 0bcff7d1a85..00000000000 --- a/core/src/OC/contactsmenu/list.handlebars +++ /dev/null @@ -1,12 +0,0 @@ -{{#unless contacts.length}} -<div class="emptycontent"> - <div class="icon-search"></div> - <h2>{{noContactsFoundText}}</h2> -</div> -{{/unless}} -<div id="contactsmenu-contacts"></div> -{{#if contactsAppEnabled}} -<div class="footer"><a href="{{contactsAppURL}}">{{showAllContactsText}}</a></div> -{{else if canInstallApp}} -<div class="footer"><a href="{{contactsAppMgmtURL}}">{{contactsAppMgmtText}}</a></div> -{{/if}} diff --git a/core/src/OC/contactsmenu/loading.handlebars b/core/src/OC/contactsmenu/loading.handlebars deleted file mode 100644 index 7fb22a6ed8e..00000000000 --- a/core/src/OC/contactsmenu/loading.handlebars +++ /dev/null @@ -1,4 +0,0 @@ -<div class="emptycontent"> - <div class="icon-loading"></div> - <h2>{{loadingText}}</h2> -</div> diff --git a/core/src/OC/contactsmenu/menu.handlebars b/core/src/OC/contactsmenu/menu.handlebars deleted file mode 100644 index 7d7697e780c..00000000000 --- a/core/src/OC/contactsmenu/menu.handlebars +++ /dev/null @@ -1,4 +0,0 @@ -<label class="hidden-visually" for="contactsmenu-search">{{searchContactsText}}</label> -<input id="contactsmenu-search" type="search" placeholder="{{searchContactsText}}" value="{{searchTerm}}"> -<div class="content"> -</div> diff --git a/core/src/OC/currentuser.js b/core/src/OC/currentuser.js index c6e8a8ee62c..a022698eab0 100644 --- a/core/src/OC/currentuser.js +++ b/core/src/OC/currentuser.js @@ -1,24 +1,6 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ const rawUid = document diff --git a/core/src/OC/debug.js b/core/src/OC/debug.js index 25a6eae9597..52a9ef28145 100644 --- a/core/src/OC/debug.js +++ b/core/src/OC/debug.js @@ -1,23 +1,6 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ const base = window._oc_debug diff --git a/core/src/OC/dialogs.js b/core/src/OC/dialogs.js index 286f9848290..5c6934e67a2 100644 --- a/core/src/OC/dialogs.js +++ b/core/src/OC/dialogs.js @@ -1,79 +1,51 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * @copyright Copyright (c) 2019 Gary Kim <gary@garykim.dev> - * - * @author Bartek Przybylski <bart.p.pl@gmail.com> - * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Calviño Sánchez <danxuliu@gmail.com> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Florian Schunk <florian.schunk@rwth-aachen.de> - * @author Gary Kim <gary@garykim.dev> - * @author Hendrik Leppelsack <hendrik@leppelsack.de> - * @author Jan-Christoph Borchardt <hey@jancborchardt.net> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Loïc Hermann <loic.hermann@sciam.fr> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sujith Haridasan <Sujith_Haridasan@mentor.com> - * @author Thomas Citharel <nextcloud@tcit.fr> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2015 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ /* eslint-disable */ import _ from 'underscore' import $ from 'jquery' -import OC from './index' -import OCA from '../OCA/index' -import { isA11yActivation } from '../Util/a11y' +import IconMove from '@mdi/svg/svg/folder-move.svg?raw' +import IconCopy from '@mdi/svg/svg/folder-multiple-outline.svg?raw' + +import OC from './index.js' +import { DialogBuilder, FilePickerType, getFilePickerBuilder, spawnDialog } from '@nextcloud/dialogs' +import { translate as t } from '@nextcloud/l10n' +import { basename } from 'path' +import { defineAsyncComponent } from 'vue' /** * this class to ease the usage of jquery dialogs */ const Dialogs = { // dialog button types + /** @deprecated use `@nextcloud/dialogs` */ YES_NO_BUTTONS: 70, + /** @deprecated use `@nextcloud/dialogs` */ OK_BUTTONS: 71, + /** @deprecated use FilePickerType from `@nextcloud/dialogs` */ FILEPICKER_TYPE_CHOOSE: 1, + /** @deprecated use FilePickerType from `@nextcloud/dialogs` */ FILEPICKER_TYPE_MOVE: 2, + /** @deprecated use FilePickerType from `@nextcloud/dialogs` */ FILEPICKER_TYPE_COPY: 3, + /** @deprecated use FilePickerType from `@nextcloud/dialogs` */ FILEPICKER_TYPE_COPY_MOVE: 4, + /** @deprecated use FilePickerType from `@nextcloud/dialogs` */ FILEPICKER_TYPE_CUSTOM: 5, - // used to name each dialog - dialogsCounter: 0, - /** * displays alert dialog * @param {string} text content of dialog * @param {string} title dialog title * @param {function} callback which will be triggered when user presses OK * @param {boolean} [modal] make the dialog modal + * + * @deprecated 30.0.0 Use `@nextcloud/dialogs` instead or build your own with `@nextcloud/vue` NcDialog */ alert: function(text, title, callback, modal) { this.message( @@ -85,12 +57,15 @@ const Dialogs = { modal ) }, + /** * displays info dialog * @param {string} text content of dialog * @param {string} title dialog title * @param {function} callback which will be triggered when user presses OK * @param {boolean} [modal] make the dialog modal + * + * @deprecated 30.0.0 Use `@nextcloud/dialogs` instead or build your own with `@nextcloud/vue` NcDialog */ info: function(text, title, callback, modal) { this.message(text, title, 'info', Dialogs.OK_BUTTON, callback, modal) @@ -103,6 +78,8 @@ const Dialogs = { * @param {function} callback which will be triggered when user presses OK (true or false would be passed to callback respectively) * @param {boolean} [modal] make the dialog modal * @returns {Promise} + * + * @deprecated 30.0.0 Use `@nextcloud/dialogs` instead or build your own with `@nextcloud/vue` NcDialog */ confirm: function(text, title, callback, modal) { return this.message( @@ -122,16 +99,34 @@ const Dialogs = { * @param {function} callback which will be triggered when user presses OK (true or false would be passed to callback respectively) * @param {boolean} [modal] make the dialog modal * @returns {Promise} + * + * @deprecated 30.0.0 Use `@nextcloud/dialogs` instead or build your own with `@nextcloud/vue` NcDialog */ - confirmDestructive: function(text, title, buttons, callback, modal) { - return this.message( - text, - title, - 'none', - buttons, - callback, - modal === undefined ? true : modal - ) + confirmDestructive: function(text, title, buttons = Dialogs.OK_BUTTONS, callback = () => {}, modal) { + return (new DialogBuilder()) + .setName(title) + .setText(text) + .setButtons( + buttons === Dialogs.OK_BUTTONS + ? [ + { + label: t('core', 'Yes'), + type: 'error', + callback: () => { + callback.clicked = true + callback(true) + }, + } + ] + : Dialogs._getLegacyButtons(buttons, callback) + ) + .build() + .show() + .then(() => { + if (!callback.clicked) { + callback(false) + } + }) }, /** * displays confirmation dialog @@ -140,17 +135,35 @@ const Dialogs = { * @param {function} callback which will be triggered when user presses OK (true or false would be passed to callback respectively) * @param {boolean} [modal] make the dialog modal * @returns {Promise} + * + * @deprecated 30.0.0 Use `@nextcloud/dialogs` instead or build your own with `@nextcloud/vue` NcDialog */ confirmHtml: function(text, title, callback, modal) { - return this.message( - text, - title, - 'notice', - Dialogs.YES_NO_BUTTONS, - callback, - modal, - true - ) + return (new DialogBuilder()) + .setName(title) + .setText('') + .setButtons([ + { + label: t('core', 'No'), + callback: () => {}, + }, + { + label: t('core', 'Yes'), + type: 'primary', + callback: () => { + callback.clicked = true + callback(true) + }, + }, + ]) + .build() + .setHTML(text) + .show() + .then(() => { + if (!callback.clicked) { + callback(false) + } + }) }, /** * displays prompt dialog @@ -161,73 +174,32 @@ const Dialogs = { * @param {string} name name of the input field * @param {boolean} password whether the input should be a password input * @returns {Promise} + * + * @deprecated Use NcDialog from `@nextcloud/vue` instead */ prompt: function(text, title, callback, modal, name, password) { - return $.when(this._getMessageTemplate()).then(function($tmpl) { - var dialogName = 'oc-dialog-' + Dialogs.dialogsCounter + '-content' - var dialogId = '#' + dialogName - var $dlg = $tmpl.octemplate({ - dialog_name: dialogName, - title: title, - message: text, - type: 'notice' - }) - var input = $('<input/>') - input.attr('type', password ? 'password' : 'text').attr('id', dialogName + '-input').attr('placeholder', name) - var label = $('<label/>').attr('for', dialogName + '-input').text(name + ': ') - $dlg.append(label) - $dlg.append(input) - if (modal === undefined) { - modal = false - } - $('body').append($dlg) - - // wrap callback in _.once(): - // only call callback once and not twice (button handler and close - // event) but call it for the close event, if ESC or the x is hit - if (callback !== undefined) { - callback = _.once(callback) - } - - var buttonlist = [{ - text: t('core', 'No'), - click: function() { - if (callback !== undefined) { - // eslint-disable-next-line standard/no-callback-literal - callback(false, input.val()) - } - $(dialogId).ocdialog('close') - } - }, { - text: t('core', 'Yes'), - click: function() { - if (callback !== undefined) { - // eslint-disable-next-line standard/no-callback-literal - callback(true, input.val()) - } - $(dialogId).ocdialog('close') + return new Promise((resolve) => { + spawnDialog( + defineAsyncComponent(() => import('../components/LegacyDialogPrompt.vue')), + { + text, + name: title, + callback, + inputName: name, + isPassword: !!password }, - defaultButton: true - }] - - $(dialogId).ocdialog({ - closeOnEscape: true, - modal: modal, - buttons: buttonlist, - close: function() { - // callback is already fired if Yes/No is clicked directly - if (callback !== undefined) { - // eslint-disable-next-line standard/no-callback-literal - callback(false, input.val()) - } - } - }) - input.focus() - Dialogs.dialogsCounter++ + (...args) => { + callback(...args) + resolve() + }, + ) }) }, + /** - * show a file picker to pick a file from + * Legacy wrapper to the new Vue based filepicker from `@nextcloud/dialogs` + * + * Prefer to use the Vue filepicker directly instead. * * In order to pick several types of mime types they need to be passed as an * array of strings. @@ -237,441 +209,196 @@ const Dialogs = { * should be used instead. * * @param {string} title dialog title - * @param {function} callback which will be triggered when user presses Choose + * @param {Function} callback which will be triggered when user presses Choose * @param {boolean} [multiselect] whether it should be possible to select multiple files - * @param {string[]} [mimetypeFilter] mimetype to filter by - directories will always be included - * @param {boolean} [modal] make the dialog modal + * @param {string[]} [mimetype] mimetype to filter by - directories will always be included + * @param {boolean} [_modal] do not use * @param {string} [type] Type of file picker : Choose, copy, move, copy and move * @param {string} [path] path to the folder that the the file can be picket from - * @param {Object} [options] additonal options that need to be set + * @param {object} [options] additonal options that need to be set * @param {Function} [options.filter] filter function for advanced filtering + * @param {boolean} [options.allowDirectoryChooser] Allow to select directories + * @deprecated since 27.1.0 use the filepicker from `@nextcloud/dialogs` instead */ - filepicker: function(title, callback, multiselect, mimetypeFilter, modal, type, path, options) { - var self = this - - this.filepicker.sortField = 'name' - this.filepicker.sortOrder = 'asc' - // avoid opening the picker twice - if (this.filepicker.loading) { - return - } + filepicker(title, callback, multiselect = false, mimetype = undefined, _modal = undefined, type = FilePickerType.Choose, path = undefined, options = undefined) { - if (type === undefined) { - type = this.FILEPICKER_TYPE_CHOOSE - } - - var emptyText = t('core', 'No files in here') - var newText = t('files', 'New folder') - if (type === this.FILEPICKER_TYPE_COPY || type === this.FILEPICKER_TYPE_MOVE || type === this.FILEPICKER_TYPE_COPY_MOVE) { - emptyText = t('core', 'No more subfolders in here') - } - - this.filepicker.loading = true - this.filepicker.filesClient = (OCA.Sharing && OCA.Sharing.PublicApp && OCA.Sharing.PublicApp.fileList) ? OCA.Sharing.PublicApp.fileList.filesClient : OC.Files.getClient() - - this.filelist = null - path = path || '' - options = Object.assign({ - allowDirectoryChooser: false - }, options) - - $.when(this._getFilePickerTemplate()).then(function($tmpl) { - self.filepicker.loading = false - var dialogName = 'oc-dialog-filepicker-content' - if (self.$filePicker) { - self.$filePicker.ocdialog('close') - } - - if (mimetypeFilter === undefined || mimetypeFilter === null) { - mimetypeFilter = [] - } - if (typeof (mimetypeFilter) === 'string') { - mimetypeFilter = [mimetypeFilter] - } - - self.$filePicker = $tmpl.octemplate({ - dialog_name: dialogName, - title: title, - emptytext: emptyText, - newtext: newText, - nameCol: t('core', 'Name'), - sizeCol: t('core', 'Size'), - modifiedCol: t('core', 'Modified') - }).data('path', path).data('multiselect', multiselect).data('mimetype', mimetypeFilter).data('allowDirectoryChooser', options.allowDirectoryChooser) - if (typeof(options.filter) === 'function') { - self.$filePicker.data('filter', options.filter) + /** + * Create legacy callback wrapper to support old filepicker syntax + * @param fn The original callback + * @param type The file picker type which was used to pick the file(s) + */ + const legacyCallback = (fn, type) => { + const getPath = (node) => { + const root = node?.root || '' + let path = node?.path || '' + // TODO: Fix this in @nextcloud/files + if (path.startsWith(root)) { + path = path.slice(root.length) || '/' + } + return path } - if (modal === undefined) { - modal = false - } - if (multiselect === undefined) { - multiselect = false + if (multiselect) { + return (nodes) => fn(nodes.map(getPath), type) + } else { + return (nodes) => fn(getPath(nodes[0]), type) } + } - $('body').prepend(self.$filePicker) - - self.$showGridView = $('button#picker-showgridview') - self.$showGridView.on('click keydown', function(event) { - if (isA11yActivation(event)) { - self._onGridviewChange() - } - }) - self._getGridSettings() + /** + * Coverting a Node into a legacy file info to support the OC.dialogs.filepicker filter function + * @param node The node to convert + */ + const nodeToLegacyFile = (node) => ({ + id: node.fileid || null, + path: node.path, + mimetype: node.mime || null, + mtime: node.mtime?.getTime() || null, + permissions: node.permissions, + name: node.attributes?.displayName || node.basename, + etag: node.attributes?.etag || null, + hasPreview: node.attributes?.hasPreview || null, + mountType: node.attributes?.mountType || null, + quotaAvailableBytes: node.attributes?.quotaAvailableBytes || null, + icon: null, + sharePermissions: null, + }) - var newButton = self.$filePicker.find('.actions.creatable .button-add') - if (type === self.FILEPICKER_TYPE_CHOOSE && !options.allowDirectoryChooser) { - self.$filePicker.find('.actions.creatable').hide() - } - newButton.on('focus', function() { - self.$filePicker.ocdialog('setEnterCallback', function(event) { - event.stopImmediatePropagation() - event.preventDefault() - newButton.click() - }) - }) - newButton.on('blur', function() { - self.$filePicker.ocdialog('unsetEnterCallback') - }) + const builder = getFilePickerBuilder(title) - OC.registerMenu(newButton, self.$filePicker.find('.menu'), function() { - $input.tooltip('hide') - $input.focus() - self.$filePicker.ocdialog('setEnterCallback', function(event) { - event.stopImmediatePropagation() - event.preventDefault() - self.$filePicker.submit() + // Setup buttons + if (type === this.FILEPICKER_TYPE_CUSTOM) { + (options.buttons || []).forEach((button) => { + builder.addButton({ + callback: legacyCallback(callback, button.type), + label: button.text, + type: button.defaultButton ? 'primary' : 'secondary', }) - var newName = $input.val() - var lastPos = newName.lastIndexOf('.') - if (lastPos === -1) { - lastPos = newName.length - } - $input.selectRange(0, lastPos) }) - var $form = self.$filePicker.find('.filenameform') - var $input = $form.find('input[type=\'text\']') - var $submit = $form.find('input[type=\'submit\']') - $input.on('keydown', function(event) { - if (isA11yActivation(event)) { - event.stopImmediatePropagation() - event.preventDefault() - $form.submit() - } - }) - $submit.on('click', function(event) { - event.stopImmediatePropagation() - event.preventDefault() - $form.submit() - }) - - /** - * Checks whether the given file name is valid. - * - * @param name file name to check - * @return true if the file name is valid. - * @throws a string exception with an error message if - * the file name is not valid - * - * NOTE: This function is duplicated in the files app: - * https://github.com/nextcloud/server/blob/b9bc2417e7a8dc81feb0abe20359bedaf864f790/apps/files/js/files.js#L127-L148 - */ - var isFileNameValid = function (name) { - var trimmedName = name.trim(); - if (trimmedName === '.' || trimmedName === '..') - { - throw t('files', '"{name}" is an invalid file name.', {name: name}) - } else if (trimmedName.length === 0) { - throw t('files', 'File name cannot be empty.') - } else if (trimmedName.indexOf('/') !== -1) { - throw t('files', '"/" is not allowed inside a file name.') - } else if (!!(trimmedName.match(OC.config.blacklist_files_regex))) { - throw t('files', '"{name}" is not an allowed filetype', {name: name}) + } else { + builder.setButtonFactory((nodes, path) => { + const buttons = [] + const [node] = nodes + const target = node?.displayname || node?.basename || basename(path) + + if (type === FilePickerType.Choose) { + buttons.push({ + callback: legacyCallback(callback, FilePickerType.Choose), + label: node && !this.multiSelect ? t('core', 'Choose {file}', { file: target }) : t('core', 'Choose'), + type: 'primary', + }) } - - return true - } - - var checkInput = function() { - var filename = $input.val() - try { - if (!isFileNameValid(filename)) { - // isFileNameValid(filename) throws an exception itself - } else if (self.filelist.find(function(file) { - return file.name === this - }, filename)) { - throw t('files', '{newName} already exists', { newName: filename }, undefined, { - escape: false - }) - } else { - return true - } - } catch (error) { - $input.attr('title', error) - $input.tooltip({ - placement: 'right', - trigger: 'manual', - 'container': '.newFolderMenu' + if (type === FilePickerType.CopyMove || type === FilePickerType.Copy) { + buttons.push({ + callback: legacyCallback(callback, FilePickerType.Copy), + label: target ? t('core', 'Copy to {target}', { target }) : t('core', 'Copy'), + type: 'primary', + icon: IconCopy, }) - $input.tooltip('_fixTitle') - $input.tooltip('show') - $input.addClass('error') } - return false - } - - $form.on('submit', function(event) { - event.stopPropagation() - event.preventDefault() - - if (checkInput()) { - var newname = $input.val() - self.filepicker.filesClient.createDirectory(self.$filePicker.data('path') + "/" + newname).always(function (status) { - self._fillFilePicker(self.$filePicker.data('path') + "/" + newname) + if (type === FilePickerType.Move || type === FilePickerType.CopyMove) { + buttons.push({ + callback: legacyCallback(callback, FilePickerType.Move), + label: target ? t('core', 'Move to {target}', { target }) : t('core', 'Move'), + type: type === FilePickerType.Move ? 'primary' : 'secondary', + icon: IconMove, }) - OC.hideMenus() - self.$filePicker.ocdialog('unsetEnterCallback') - self.$filePicker.click() - $input.val(newText) } + return buttons }) - $input.on('input', function(event) { - $input.tooltip('hide') - }) - - self.$filePicker.ready(function() { - self.$fileListHeader = self.$filePicker.find('.filelist thead tr') - self.$filelist = self.$filePicker.find('.filelist tbody') - self.$filelistContainer = self.$filePicker.find('.filelist-container') - self.$dirTree = self.$filePicker.find('.dirtree') - self.$dirTree.on('click keydown', 'div:not(:last-child)', self, function(event) { - if (isA11yActivation(event)) { - self._handleTreeListSelect(event, type) - } - }) - self.$filelist.on('click keydown', 'tr', function(event) { - if (isA11yActivation(event)) { - self._handlePickerClick(event, $(this), type) - } - }) - self.$fileListHeader.on('click keydown', 'a', function(event) { - if (isA11yActivation(event)) { - var dir = self.$filePicker.data('path') - self.filepicker.sortField = $(event.currentTarget).data('sort') - self.filepicker.sortOrder = self.filepicker.sortOrder === 'asc' ? 'desc' : 'asc' - self._fillFilePicker(dir) - } - }) - self._fillFilePicker(path) - }) - - // build buttons - var functionToCall = function(returnType) { - if (callback !== undefined) { - var datapath - if (multiselect === true) { - datapath = [] - self.$filelist.find('tr.filepicker_element_selected').each(function(index, element) { - datapath.push(self.$filePicker.data('path') + '/' + $(element).data('entryname')) - }) - } else { - datapath = self.$filePicker.data('path') - var selectedName = self.$filelist.find('tr.filepicker_element_selected').data('entryname') - if (selectedName) { - datapath += '/' + selectedName - } - } - callback(datapath, returnType) - self.$filePicker.ocdialog('close') - } - } - - var chooseCallback = function() { - functionToCall(Dialogs.FILEPICKER_TYPE_CHOOSE) - } - - var copyCallback = function() { - functionToCall(Dialogs.FILEPICKER_TYPE_COPY) - } + } - var moveCallback = function() { - functionToCall(Dialogs.FILEPICKER_TYPE_MOVE) - } + if (mimetype) { + builder.setMimeTypeFilter(typeof mimetype === 'string' ? [mimetype] : (mimetype || [])) + } + if (typeof options?.filter === 'function') { + builder.setFilter((node) => options.filter(nodeToLegacyFile(node))) + } + builder.allowDirectories(options?.allowDirectoryChooser === true || mimetype?.includes('httpd/unix-directory') || false) + .setMultiSelect(multiselect) + .startAt(path) + .build() + .pick() + }, - var buttonlist = [] - if (type === Dialogs.FILEPICKER_TYPE_CHOOSE) { - buttonlist.push({ - text: t('core', 'Choose'), - click: chooseCallback, - defaultButton: true - }) - } else if (type === Dialogs.FILEPICKER_TYPE_CUSTOM) { - options.buttons.forEach(function(button) { - buttonlist.push({ - text: button.text, - click: function() { - functionToCall(button.type) - }, - defaultButton: button.defaultButton - }) - }) - } else { - if (type === Dialogs.FILEPICKER_TYPE_COPY || type === Dialogs.FILEPICKER_TYPE_COPY_MOVE) { - buttonlist.push({ - text: t('core', 'Copy'), - click: copyCallback, - defaultButton: false - }) - } - if (type === Dialogs.FILEPICKER_TYPE_MOVE || type === Dialogs.FILEPICKER_TYPE_COPY_MOVE) { - buttonlist.push({ - text: t('core', 'Move'), - click: moveCallback, - defaultButton: true - }) - } - } + /** + * Displays raw dialog + * You better use a wrapper instead ... + * + * @deprecated 30.0.0 Use `@nextcloud/dialogs` instead or build your own with `@nextcloud/vue` NcDialog + */ + message: function(content, title, dialogType, buttons, callback = () => {}, modal, allowHtml) { + const builder = (new DialogBuilder()) + .setName(title) + .setText(allowHtml ? '' : content) + .setButtons(Dialogs._getLegacyButtons(buttons, callback)) + + switch (dialogType) { + case 'alert': + builder.setSeverity('warning') + break + case 'notice': + builder.setSeverity('info') + break + default: + break + } - self.$filePicker.ocdialog({ - closeOnEscape: true, - // max-width of 600 - width: 600, - height: 500, - modal: modal, - buttons: buttonlist, - style: { - buttons: 'aside' - }, - close: function() { - try { - $(this).ocdialog('destroy').remove() - } catch (e) { - } - self.$filePicker = null - } - }) + const dialog = builder.build() + + if (allowHtml) { + dialog.setHTML(content) + } - // We can access primary class only from oc-dialog. - // Hence this is one of the approach to get the choose button. - var getOcDialog = self.$filePicker.closest('.oc-dialog') - var buttonEnableDisable = getOcDialog.find('.primary') - if (self.$filePicker.data('mimetype').indexOf('httpd/unix-directory') !== -1 || self.$filePicker.data('allowDirectoryChooser')) { - buttonEnableDisable.prop('disabled', false) - } else { - buttonEnableDisable.prop('disabled', true) + return dialog.show().then(() => { + if(!callback._clicked) { + callback(false) } }) - .fail(function(status, error) { - // If the method is called while navigating away - // from the page, it is probably not needed ;) - self.filepicker.loading = false - if (status !== 0) { - alert(t('core', 'Error loading file picker template: {error}', { error: error })) - } - }) }, + /** - * Displays raw dialog - * You better use a wrapper instead ... + * Helper for legacy API + * @deprecated */ - message: function(content, title, dialogType, buttons, callback, modal, allowHtml) { - return $.when(this._getMessageTemplate()).then(function($tmpl) { - var dialogName = 'oc-dialog-' + Dialogs.dialogsCounter + '-content' - var dialogId = '#' + dialogName - var $dlg = $tmpl.octemplate({ - dialog_name: dialogName, - title: title, - message: content, - type: dialogType - }, allowHtml ? { escapeFunction: '' } : {}) - if (modal === undefined) { - modal = false - } - $('body').append($dlg) - var buttonlist = [] - switch (buttons) { + _getLegacyButtons(buttons, callback) { + const buttonList = [] + + switch (typeof buttons === 'object' ? buttons.type : buttons) { case Dialogs.YES_NO_BUTTONS: - buttonlist = [{ - text: t('core', 'No'), - click: function() { - if (callback !== undefined) { - callback(false) - } - $(dialogId).ocdialog('close') - } - }, - { - text: t('core', 'Yes'), - click: function() { - if (callback !== undefined) { - callback(true) - } - $(dialogId).ocdialog('close') + buttonList.push({ + label: buttons?.cancel ?? t('core', 'No'), + callback: () => { + callback._clicked = true + callback(false) }, - defaultButton: true - }] + }) + buttonList.push({ + label: buttons?.confirm ?? t('core', 'Yes'), + type: 'primary', + callback: () => { + callback._clicked = true + callback(true) + }, + }) break - case Dialogs.OK_BUTTON: - var functionToCall = function() { - $(dialogId).ocdialog('close') - if (callback !== undefined) { - callback() - } - } - buttonlist[0] = { - text: t('core', 'OK'), - click: functionToCall, - defaultButton: true - } + case Dialogs.OK_BUTTONS: + buttonList.push({ + label: buttons?.confirm ?? t('core', 'OK'), + type: 'primary', + callback: () => { + callback._clicked = true + callback(true) + }, + }) break default: - if (typeof(buttons) === 'object') { - switch (buttons.type) { - case Dialogs.YES_NO_BUTTONS: - buttonlist = [{ - text: buttons.cancel || t('core', 'No'), - click: function() { - if (callback !== undefined) { - callback(false) - } - $(dialogId).ocdialog('close') - } - }, - { - text: buttons.confirm || t('core', 'Yes'), - click: function() { - if (callback !== undefined) { - callback(true) - } - $(dialogId).ocdialog('close') - }, - defaultButton: true, - classes: buttons.confirmClasses - }] - break - } - } + console.error('Invalid call to OC.dialogs') break - } - - $(dialogId).ocdialog({ - closeOnEscape: true, - closeCallback: () => { callback && callback(false) }, - modal: modal, - buttons: buttonlist - }) - Dialogs.dialogsCounter++ - }) - .fail(function(status, error) { - // If the method is called while navigating away from - // the page, we still want to deliver the message. - if (status === 0) { - alert(title + ': ' + content) - } else { - alert(t('core', 'Error loading message template: {error}', { error: error })) - } - }) + } + return buttonList }, + _fileexistsshown: false, /** * Displays file exists dialog @@ -680,6 +407,8 @@ const Dialogs = { * @param {object} replacement file with name, size and mtime * @param {object} controller with onCancel, onSkip, onReplace and onRename methods * @returns {Promise} jquery promise that resolves after the dialog template was loaded + * + * @deprecated 29.0.0 Use openConflictPicker from the @nextcloud/upload package instead */ fileexists: function(data, original, replacement, controller) { var self = this @@ -1038,73 +767,12 @@ const Dialogs = { // } return dialogDeferred.promise() }, - // get the gridview setting and set the input accordingly - _getGridSettings: function() { - const self = this - $.get(OC.generateUrl('/apps/files/api/v1/showgridview'), function(response) { - self.$showGridView - .removeClass('icon-toggle-filelist icon-toggle-pictures') - .addClass(response.gridview ? 'icon-toggle-filelist' : 'icon-toggle-pictures') - self.$showGridView.attr( - 'aria-label', - response.gridview ? t('files', 'Show list view') : t('files', 'Show grid view'), - ) - $('.list-container').toggleClass('view-grid', response.gridview) - }) - }, - _onGridviewChange: function() { - const isGridView = this.$showGridView.hasClass('icon-toggle-filelist') - // only save state if user is logged in - if (OC.currentUser) { - $.post(OC.generateUrl('/apps/files/api/v1/showgridview'), { show: !isGridView }) - } - this.$showGridView - .removeClass('icon-toggle-filelist icon-toggle-pictures') - .addClass(isGridView ? 'icon-toggle-pictures' : 'icon-toggle-filelist') - this.$showGridView.attr( - 'aria-label', - isGridView ? t('files', 'Show grid view') : t('files', 'Show list view'), - ) - this.$filePicker.find('.list-container').toggleClass('view-grid', !isGridView) - }, - _getFilePickerTemplate: function() { - var defer = $.Deferred() - if (!this.$filePickerTemplate) { - var self = this - $.get(OC.filePath('core', 'templates', 'filepicker.html'), function(tmpl) { - self.$filePickerTemplate = $(tmpl) - self.$listTmpl = self.$filePickerTemplate.find('.filelist tbody tr:first-child').detach() - defer.resolve(self.$filePickerTemplate) - }) - .fail(function(jqXHR, textStatus, errorThrown) { - defer.reject(jqXHR.status, errorThrown) - }) - } else { - defer.resolve(this.$filePickerTemplate) - } - return defer.promise() - }, - _getMessageTemplate: function() { - var defer = $.Deferred() - if (!this.$messageTemplate) { - var self = this - $.get(OC.filePath('core', 'templates', 'message.html'), function(tmpl) { - self.$messageTemplate = $(tmpl) - defer.resolve(self.$messageTemplate) - }) - .fail(function(jqXHR, textStatus, errorThrown) { - defer.reject(jqXHR.status, errorThrown) - }) - } else { - defer.resolve(this.$messageTemplate) - } - return defer.promise() - }, + _getFileExistsTemplate: function() { var defer = $.Deferred() if (!this.$fileexistsTemplate) { var self = this - $.get(OC.filePath('files', 'templates', 'fileexists.html'), function(tmpl) { + $.get(OC.filePath('core', 'templates/legacy', 'fileexists.html'), function(tmpl) { self.$fileexistsTemplate = $(tmpl) defer.resolve(self.$fileexistsTemplate) }) @@ -1116,270 +784,6 @@ const Dialogs = { } return defer.promise() }, - - /** - * fills the filepicker with files - */ - _fillFilePicker: async function(dir) { - var self = this - this.$filelist.empty() - this.$filePicker.find('.emptycontent').hide() - this.$filelistContainer.addClass('icon-loading') - this.$filePicker.data('path', dir) - var filter = this.$filePicker.data('mimetype') - var advancedFilter = this.$filePicker.data('filter') - if (typeof (filter) === 'string') { - filter = [filter] - } - self.$fileListHeader.find('.sort-indicator').addClass('hidden').removeClass('icon-triangle-n').removeClass('icon-triangle-s') - self.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').removeClass('hidden') - if (self.filepicker.sortOrder === 'asc') { - self.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').addClass('icon-triangle-n') - } else { - self.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').addClass('icon-triangle-s') - } - - // Wrap within a method because a promise cannot return multiple values - // But the client impleemntation still does it... - var getFolderContents = async function(dir) { - return self.filepicker.filesClient.getFolderContents(dir) - .then((status, files) => { - return files - }) - } - - try { - var files = await getFolderContents(dir) - } catch (error) { - // fallback to root if requested dir is non-existent - console.error('Requested path does not exists, falling back to root') - var files = await getFolderContents('/') - this.$filePicker.data('path', '/') - } - - self.filelist = files - if (filter && filter.length > 0 && filter.indexOf('*') === -1) { - files = files.filter(function(file) { - return file.type === 'dir' || filter.indexOf(file.mimetype) !== -1 - }) - } - - if (advancedFilter) { - files = files.filter(advancedFilter) - } - - // Check if the showHidden input field exist and if it exist follow it - // Otherwise just show the hidden files - const showHiddenInput = document.getElementById('showHiddenFiles') - const showHidden = showHiddenInput === null || showHiddenInput.value === "1" - if (!showHidden) { - files = files.filter(function(file) { - return !file.name.startsWith('.') - }) - } - - var Comparators = { - name: function(fileInfo1, fileInfo2) { - if (fileInfo1.type === 'dir' && fileInfo2.type !== 'dir') { - return -1 - } - if (fileInfo1.type !== 'dir' && fileInfo2.type === 'dir') { - return 1 - } - return OC.Util.naturalSortCompare(fileInfo1.name, fileInfo2.name) - }, - size: function(fileInfo1, fileInfo2) { - return fileInfo1.size - fileInfo2.size - }, - mtime: function(fileInfo1, fileInfo2) { - return fileInfo1.mtime - fileInfo2.mtime - } - } - var comparator = Comparators[self.filepicker.sortField] || Comparators.name - files = files.sort(function(file1, file2) { - var isFavorite = function(fileInfo) { - return fileInfo.tags && fileInfo.tags.indexOf(OC.TAG_FAVORITE) >= 0 - } - - if (isFavorite(file1) && !isFavorite(file2)) { - return -1 - } else if (!isFavorite(file1) && isFavorite(file2)) { - return 1 - } - - return self.filepicker.sortOrder === 'asc' ? comparator(file1, file2) : -comparator(file1, file2) - }) - - self._fillSlug() - - if (files.length === 0) { - self.$filePicker.find('.emptycontent').show() - self.$fileListHeader.hide() - } else { - self.$filePicker.find('.emptycontent').hide() - self.$fileListHeader.show() - } - - self.$filelist.empty(); - - $.each(files, function(idx, entry) { - if (entry.isEncrypted && entry.mimetype === 'httpd/unix-directory') { - entry.icon = OC.MimeType.getIconUrl('dir-encrypted') - } else { - entry.icon = OC.MimeType.getIconUrl(entry.mimetype) - } - - var simpleSize, sizeColor - if (typeof (entry.size) !== 'undefined' && entry.size >= 0) { - simpleSize = OC.Util.humanFileSize(parseInt(entry.size, 10), true) - sizeColor = Math.round(160 - Math.pow((entry.size / (1024 * 1024)), 2)) - } else { - simpleSize = t('files', 'Pending') - sizeColor = 80 - } - - // split the filename in half if the size is bigger than 20 char - // for ellipsis - if (entry.name.length >= 10) { - // leave maximum 10 letters - var split = Math.min(Math.floor(entry.name.length / 2), 10) - var filename1 = entry.name.substr(0, entry.name.length - split) - var filename2 = entry.name.substr(entry.name.length - split) - } else { - var filename1 = entry.name - var filename2 = '' - } - - var $row = self.$listTmpl.octemplate({ - type: entry.type, - dir: dir, - filename: entry.name, - filename1: filename1, - filename2: filename2, - date: OC.Util.relativeModifiedDate(entry.mtime), - size: simpleSize, - sizeColor: sizeColor, - icon: entry.icon - }) - if (entry.type === 'file') { - var urlSpec = { - file: dir + '/' + entry.name, - x: 100, - y: 100 - } - var img = new Image() - var previewUrl = OC.generateUrl('/core/preview.png?') + $.param(urlSpec) - img.onload = function() { - if (img.width > 5) { - $row.find('td.filename').attr('style', 'background-image:url(' + previewUrl + ')') - } - } - img.src = previewUrl - } - self.$filelist.append($row) - }) - - self.$filelistContainer.removeClass('icon-loading') - }, - /** - * fills the tree list with directories - */ - _fillSlug: function() { - var addButton = this.$dirTree.find('.actions.creatable').detach() - this.$dirTree.empty() - var self = this - - self.$dirTree.append(addButton) - - var dir - var path = this.$filePicker.data('path') - var $template = $('<div data-dir="{dir}" tabindex="0"><a>{name}</a></div>').addClass('crumb') - if (path) { - var paths = path.split('/') - $.each(paths, function(index, dir) { - dir = paths.pop() - if (dir === '') { - return false - } - self.$dirTree.prepend($template.octemplate({ - dir: paths.join('/') + '/' + dir, - name: dir - })) - }) - } - - $template.octemplate({ - dir: '', - name: '' // Ugly but works ;) - }, { escapeFunction: null }).prependTo(this.$dirTree) - - }, - /** - * handle selection made in the tree list - */ - _handleTreeListSelect: function(event, type) { - var self = event.data - var dir = $(event.target).closest('.crumb').data('dir') - self._fillFilePicker(dir) - var getOcDialog = (event.target).closest('.oc-dialog') - var buttonEnableDisable = $('.primary', getOcDialog) - this._changeButtonsText(type, dir.split(/[/]+/).pop()) - if (this.$filePicker.data('mimetype').indexOf('httpd/unix-directory') !== -1 || this.$filePicker.data('allowDirectoryChooser')) { - buttonEnableDisable.prop('disabled', false) - } else { - buttonEnableDisable.prop('disabled', true) - } - }, - /** - * handle clicks made in the filepicker - */ - _handlePickerClick: function(event, $element, type) { - var getOcDialog = this.$filePicker.closest('.oc-dialog') - var buttonEnableDisable = getOcDialog.find('.primary') - if ($element.data('type') === 'file') { - if (this.$filePicker.data('multiselect') !== true || !event.ctrlKey) { - this.$filelist.find('.filepicker_element_selected').removeClass('filepicker_element_selected') - } - $element.toggleClass('filepicker_element_selected') - buttonEnableDisable.prop('disabled', false) - } else if ($element.data('type') === 'dir') { - this._fillFilePicker(this.$filePicker.data('path') + '/' + $element.data('entryname')) - this._changeButtonsText(type, $element.data('entryname')) - if (this.$filePicker.data('mimetype').indexOf('httpd/unix-directory') !== -1 || this.$filePicker.data('allowDirectoryChooser')) { - buttonEnableDisable.prop('disabled', false) - } else { - buttonEnableDisable.prop('disabled', true) - } - } - }, - - /** - * Handle - * @param type of action - * @param dir on which to change buttons text - * @private - */ - _changeButtonsText: function(type, dir) { - var copyText = dir === '' ? t('core', 'Copy') : t('core', 'Copy to {folder}', { folder: dir }) - var moveText = dir === '' ? t('core', 'Move') : t('core', 'Move to {folder}', { folder: dir }) - var buttons = $('.oc-dialog-buttonrow button') - switch (type) { - case this.FILEPICKER_TYPE_CHOOSE: - break - case this.FILEPICKER_TYPE_CUSTOM: - break - case this.FILEPICKER_TYPE_COPY: - buttons.text(copyText) - break - case this.FILEPICKER_TYPE_MOVE: - buttons.text(moveText) - break - case this.FILEPICKER_TYPE_COPY_MOVE: - buttons.eq(0).text(copyText) - buttons.eq(1).text(moveText) - break - } - } } export default Dialogs diff --git a/core/src/OC/eventsource.js b/core/src/OC/eventsource.js index 537d68cb434..090c351c057 100644 --- a/core/src/OC/eventsource.js +++ b/core/src/OC/eventsource.js @@ -1,35 +1,13 @@ /** - * @copyright 2012 Robin Appelman icewind1991@gmail.com - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Robin Appelman <robin@icewind.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2015 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later */ /* eslint-disable */ import $ from 'jquery' -import { getToken } from './requesttoken' +import { getRequestToken } from './requesttoken.ts' /** * Create a new event source @@ -50,7 +28,7 @@ const OCEventSource = function(src, data) { dataStr += name + '=' + encodeURIComponent(data[name]) + '&' } } - dataStr += 'requesttoken=' + encodeURIComponent(getToken()) + dataStr += 'requesttoken=' + encodeURIComponent(getRequestToken()) if (!this.useFallBack && typeof EventSource !== 'undefined') { joinChar = '&' if (src.indexOf('?') === -1) { diff --git a/core/src/OC/get_set.js b/core/src/OC/get_set.js index 32f202ad35b..0c909ad04fd 100644 --- a/core/src/OC/get_set.js +++ b/core/src/OC/get_set.js @@ -1,24 +1,6 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ export const get = context => name => { diff --git a/core/src/OC/host.js b/core/src/OC/host.js index 31f13d01a7f..75c7d63804b 100644 --- a/core/src/OC/host.js +++ b/core/src/OC/host.js @@ -1,24 +1,6 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ export const getProtocol = () => window.location.protocol.split(':')[0] diff --git a/core/src/OC/index.js b/core/src/OC/index.js index 5267e2491f4..5afc941b396 100644 --- a/core/src/OC/index.js +++ b/core/src/OC/index.js @@ -1,40 +1,19 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ import { subscribe } from '@nextcloud/event-bus' -import { addScript, addStyle } from './legacy-loader' import { ajaxConnectionLostHandler, processAjaxError, registerXHRForErrorProcessing, -} from './xhr-error' -import Apps from './apps' -import { AppConfig, appConfig } from './appconfig' -import { appSettings } from './appsettings' -import appswebroots from './appswebroots' -import Backbone from './backbone' +} from './xhr-error.js' +import Apps from './apps.js' +import { AppConfig, appConfig } from './appconfig.js' +import appswebroots from './appswebroots.js' +import Backbone from './backbone.js' import { basename, dirname, @@ -45,8 +24,8 @@ import { import { build as buildQueryString, parse as parseQueryString, -} from './query-string' -import Config from './config' +} from './query-string.js' +import Config from './config.js' import { coreApps, menuSpeed, @@ -58,35 +37,31 @@ import { PERMISSION_SHARE, PERMISSION_UPDATE, TAG_FAVORITE, -} from './constants' -import ContactsMenu from './contactsmenu' -import { currentUser, getCurrentUser } from './currentuser' -import Dialogs from './dialogs' -import EventSource from './eventsource' -import { get, set } from './get_set' -import { getCapabilities } from './capabilities' +} from './constants.js' +import { currentUser, getCurrentUser } from './currentuser.js' +import Dialogs from './dialogs.js' +import EventSource from './eventsource.js' +import { get, set } from './get_set.js' +import { getCapabilities } from './capabilities.js' import { getHost, getHostName, getPort, getProtocol, -} from './host' -import { - getToken as getRequestToken, -} from './requesttoken' +} from './host.js' +import { getRequestToken } from './requesttoken.ts' import { hideMenus, registerMenu, showMenu, unregisterMenu, -} from './menu' -import { isUserAdmin } from './admin' -import L10N, { - getLanguage, - getLocale, -} from './l10n' +} from './menu.js' +import { isUserAdmin } from './admin.js' +import L10N from './l10n.js' import { getCanonicalLocale, + getLanguage, + getLocale, } from '@nextcloud/l10n' import { @@ -101,16 +76,16 @@ import { import { linkToRemoteBase, -} from './routing' -import msg from './msg' -import Notification from './notification' -import PasswordConfirmation from './password-confirmation' -import Plugins from './plugins' -import { theme } from './theme' -import Util from './util' -import { debug } from './debug' -import { redirect, reload } from './navigation' -import webroot from './webroot' +} from './routing.js' +import msg from './msg.js' +import Notification from './notification.js' +import PasswordConfirmation from './password-confirmation.js' +import Plugins from './plugins.js' +import { theme } from './theme.js' +import Util from './util.js' +import { debug } from './debug.js' +import { redirect, reload } from './navigation.js' +import webroot from './webroot.js' /** @namespace OC */ export default { @@ -139,16 +114,11 @@ export default { * @deprecated 17.0.0 */ fileIsBlacklisted: file => !!(file.match(Config.blacklist_files_regex)), - - addScript, - addStyle, Apps, AppConfig, appConfig, - appSettings, appswebroots, Backbone, - ContactsMenu, config: Config, /** * Currently logged in user or null if none @@ -231,17 +201,14 @@ export default { * @deprecated 20.0.0 use `getCanonicalLocale` from https://www.npmjs.com/package/@nextcloud/l10n */ getCanonicalLocale, + /** + * @deprecated 26.0.0 use `getLocale` from https://www.npmjs.com/package/@nextcloud/l10n + */ getLocale, - getLanguage, /** - * Loads translations for the given app asynchronously. - * - * @param {string} app app name - * @param {Function} callback callback to call after loading - * @return {Promise} - * @deprecated 17.0.0 use OC.L10N.load instead + * @deprecated 26.0.0 use `getLanguage` from https://www.npmjs.com/package/@nextcloud/l10n */ - addTranslations: L10N.load, + getLanguage, /** * Query string helpers @@ -251,6 +218,9 @@ export default { msg, Notification, + /** + * @deprecated 28.0.0 use methods from '@nextcloud/password-confirmation' + */ PasswordConfirmation, Plugins, theme, diff --git a/core/src/OC/l10n-registry.js b/core/src/OC/l10n-registry.js deleted file mode 100644 index 9e542b1aa8c..00000000000 --- a/core/src/OC/l10n-registry.js +++ /dev/null @@ -1,90 +0,0 @@ -/** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -// This var is global because it's shared across webpack bundles -window._oc_l10n_registry_translations = window._oc_l10n_registry_translations || {} -window._oc_l10n_registry_plural_functions = window._oc_l10n_registry_plural_functions || {} - -/** - * @param {string} appId the app id - * @param {object} translations the translations list - * @param {Function} pluralFunction the translations list - */ -const register = (appId, translations, pluralFunction) => { - window._oc_l10n_registry_translations[appId] = translations - window._oc_l10n_registry_plural_functions[appId] = pluralFunction -} - -/** - * @param {string} appId the app id - * @param {object} translations the translations list - * @param {Function} pluralFunction the translations list - */ -const extend = (appId, translations, pluralFunction) => { - window._oc_l10n_registry_translations[appId] = Object.assign( - window._oc_l10n_registry_translations[appId], - translations - ) - window._oc_l10n_registry_plural_functions[appId] = pluralFunction -} - -/** - * @param {string} appId the app id - * @param {object} translations the translations list - * @param {Function} pluralFunction the translations list - */ -export const registerAppTranslations = (appId, translations, pluralFunction) => { - if (!hasAppTranslations(appId)) { - register(appId, translations, pluralFunction) - } else { - extend(appId, translations, pluralFunction) - } -} - -/** - * @param {string} appId the app id - */ -export const unregisterAppTranslations = appId => { - delete window._oc_l10n_registry_translations[appId] - delete window._oc_l10n_registry_plural_functions[appId] -} - -/** - * @param {string} appId the app id - * @return {boolean} - */ -export const hasAppTranslations = appId => { - return window._oc_l10n_registry_translations[appId] !== undefined - && window._oc_l10n_registry_plural_functions[appId] !== undefined -} - -/** - * @param {string} appId the app id - * @return {object} - */ -export const getAppTranslations = appId => { - return { - translations: window._oc_l10n_registry_translations[appId] || {}, - pluralFunction: window._oc_l10n_registry_plural_functions[appId], - } -} diff --git a/core/src/OC/l10n.js b/core/src/OC/l10n.js index 2a4569ee272..02f912d6a99 100644 --- a/core/src/OC/l10n.js +++ b/core/src/OC/l10n.js @@ -1,106 +1,60 @@ /** - * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com> - * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) - * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2014 ownCloud, Inc. + * SPDX-FileCopyrightText: 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) + * SPDX-License-Identifier: AGPL-3.0-or-later */ -import _ from 'underscore' -import $ from 'jquery' -import DOMPurify from 'dompurify' import Handlebars from 'handlebars' -import identity from 'lodash/fp/identity' -import escapeHTML from 'escape-html' -import { generateFilePath } from '@nextcloud/router' - -import OC from './index' import { - getAppTranslations, - hasAppTranslations, - registerAppTranslations, - unregisterAppTranslations, -} from './l10n-registry' + loadTranslations, + translate, + translatePlural, + register, + unregister, +} from '@nextcloud/l10n' /** * L10N namespace with localization functions. * * @namespace OC.L10n + * @deprecated 26.0.0 use https://www.npmjs.com/package/@nextcloud/l10n */ const L10n = { /** * Load an app's translation bundle if not loaded already. * + * @deprecated 26.0.0 use `loadTranslations` from https://www.npmjs.com/package/@nextcloud/l10n + * * @param {string} appName name of the app * @param {Function} callback callback to be called when * the translations are loaded * @return {Promise} promise */ - load(appName, callback) { - // already available ? - if (hasAppTranslations(appName) || OC.getLocale() === 'en') { - const deferred = $.Deferred() - const promise = deferred.promise() - promise.then(callback) - deferred.resolve() - return promise - } - - const self = this - const url = generateFilePath(appName, 'l10n', OC.getLocale() + '.json') - - // load JSON translation bundle per AJAX - return $.get(url) - .then( - function(result) { - if (result.translations) { - self.register(appName, result.translations, result.pluralForm) - } - }) - .then(callback) - }, + load: loadTranslations, /** * Register an app's translation bundle. * + * @deprecated 26.0.0 use `register` from https://www.npmjs.com/package/@nextcloud/l10 + * * @param {string} appName name of the app - * @param {Object<string, string>} bundle bundle + * @param {Record<string, string>} bundle bundle */ - register(appName, bundle) { - registerAppTranslations(appName, bundle, this._getPlural) - }, + register, /** * @private + * @deprecated 26.0.0 use `unregister` from https://www.npmjs.com/package/@nextcloud/l10n */ - _unregister: unregisterAppTranslations, + _unregister: unregister, /** * Translate a string * + * @deprecated 26.0.0 use `translate` from https://www.npmjs.com/package/@nextcloud/l10n + * * @param {string} app the id of the app for which to translate the string * @param {string} text the string to translate * @param {object} [vars] map of placeholder key to value @@ -110,49 +64,13 @@ const L10n = { * @param {boolean} [options.sanitize=true] enable/disable sanitization (by default enabled) * @return {string} */ - translate(app, text, vars, count, options) { - const defaultOptions = { - escape: true, - sanitize: true, - } - const allOptions = options || {} - _.defaults(allOptions, defaultOptions) - - const optSanitize = allOptions.sanitize ? DOMPurify.sanitize : identity - const optEscape = allOptions.escape ? escapeHTML : identity - - // TODO: cache this function to avoid inline recreation - // of the same function over and over again in case - // translate() is used in a loop - const _build = function(text, vars, count) { - return text.replace(/%n/g, count).replace(/{([^{}]*)}/g, - function(a, b) { - const r = vars[b] - if (typeof r === 'string' || typeof r === 'number') { - return optSanitize(optEscape(r)) - } else { - return optSanitize(a) - } - } - ) - } - let translation = text - const bundle = getAppTranslations(app) - const value = bundle.translations[text] - if (typeof (value) !== 'undefined') { - translation = value - } - - if (typeof vars === 'object' || count !== undefined) { - return optSanitize(_build(translation, vars, count)) - } else { - return optSanitize(translation) - } - }, + translate, /** * Translate a plural string * + * @deprecated 26.0.0 use `translatePlural` from https://www.npmjs.com/package/@nextcloud/l10n + * * @param {string} app the id of the app for which to translate the string * @param {string} textSingular the string to translate for exactly one object * @param {string} textPlural the string to translate for n objects @@ -162,203 +80,11 @@ const L10n = { * @param {boolean} [options.escape=true] enable/disable auto escape of placeholders (by default enabled) * @return {string} Translated string */ - translatePlural(app, textSingular, textPlural, count, vars, options) { - const identifier = '_' + textSingular + '_::_' + textPlural + '_' - const bundle = getAppTranslations(app) - const value = bundle.translations[identifier] - if (typeof (value) !== 'undefined') { - const translation = value - if ($.isArray(translation)) { - const plural = bundle.pluralFunction(count) - return this.translate(app, translation[plural], vars, count, options) - } - } - - if (count === 1) { - return this.translate(app, textSingular, vars, count, options) - } else { - return this.translate(app, textPlural, vars, count, options) - } - }, - - /** - * The plural function taken from symfony - * - * @param {number} number the number of elements - * @return {number} - * @private - */ - _getPlural(number) { - let language = OC.getLanguage() - if (language === 'pt-BR') { - // temporary set a locale for brazilian - language = 'xbr' - } - - if (typeof language === 'undefined' || language === '') { - return (number === 1) ? 0 : 1 - } - - if (language.length > 3) { - language = language.substring(0, language.lastIndexOf('-')) - } - - /* - * The plural rules are derived from code of the Zend Framework (2010-09-25), - * which is subject to the new BSD license (http://framework.zend.com/license/new-bsd). - * Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) - */ - switch (language) { - case 'az': - case 'bo': - case 'dz': - case 'id': - case 'ja': - case 'jv': - case 'ka': - case 'km': - case 'kn': - case 'ko': - case 'ms': - case 'th': - case 'tr': - case 'vi': - case 'zh': - return 0 - - case 'af': - case 'bn': - case 'bg': - case 'ca': - case 'da': - case 'de': - case 'el': - case 'en': - case 'eo': - case 'es': - case 'et': - case 'eu': - case 'fa': - case 'fi': - case 'fo': - case 'fur': - case 'fy': - case 'gl': - case 'gu': - case 'ha': - case 'he': - case 'hu': - case 'is': - case 'it': - case 'ku': - case 'lb': - case 'ml': - case 'mn': - case 'mr': - case 'nah': - case 'nb': - case 'ne': - case 'nl': - case 'nn': - case 'no': - case 'oc': - case 'om': - case 'or': - case 'pa': - case 'pap': - case 'ps': - case 'pt': - case 'so': - case 'sq': - case 'sv': - case 'sw': - case 'ta': - case 'te': - case 'tk': - case 'ur': - case 'zu': - return (number === 1) ? 0 : 1 - - case 'am': - case 'bh': - case 'fil': - case 'fr': - case 'gun': - case 'hi': - case 'hy': - case 'ln': - case 'mg': - case 'nso': - case 'xbr': - case 'ti': - case 'wa': - return ((number === 0) || (number === 1)) ? 0 : 1 - - case 'be': - case 'bs': - case 'hr': - case 'ru': - case 'sh': - case 'sr': - case 'uk': - return ((number % 10 === 1) && (number % 100 !== 11)) ? 0 : (((number % 10 >= 2) && (number % 10 <= 4) && ((number % 100 < 10) || (number % 100 >= 20))) ? 1 : 2) - - case 'cs': - case 'sk': - return (number === 1) ? 0 : (((number >= 2) && (number <= 4)) ? 1 : 2) - - case 'ga': - return (number === 1) ? 0 : ((number === 2) ? 1 : 2) - - case 'lt': - return ((number % 10 === 1) && (number % 100 !== 11)) ? 0 : (((number % 10 >= 2) && ((number % 100 < 10) || (number % 100 >= 20))) ? 1 : 2) - - case 'sl': - return (number % 100 === 1) ? 0 : ((number % 100 === 2) ? 1 : (((number % 100 === 3) || (number % 100 === 4)) ? 2 : 3)) - - case 'mk': - return (number % 10 === 1) ? 0 : 1 - - case 'mt': - return (number === 1) ? 0 : (((number === 0) || ((number % 100 > 1) && (number % 100 < 11))) ? 1 : (((number % 100 > 10) && (number % 100 < 20)) ? 2 : 3)) - - case 'lv': - return (number === 0) ? 0 : (((number % 10 === 1) && (number % 100 !== 11)) ? 1 : 2) - - case 'pl': - return (number === 1) ? 0 : (((number % 10 >= 2) && (number % 10 <= 4) && ((number % 100 < 12) || (number % 100 > 14))) ? 1 : 2) - - case 'cy': - return (number === 1) ? 0 : ((number === 2) ? 1 : (((number === 8) || (number === 11)) ? 2 : 3)) - - case 'ro': - return (number === 1) ? 0 : (((number === 0) || ((number % 100 > 0) && (number % 100 < 20))) ? 1 : 2) - - case 'ar': - return (number === 0) ? 0 : ((number === 1) ? 1 : ((number === 2) ? 2 : (((number % 100 >= 3) && (number % 100 <= 10)) ? 3 : (((number % 100 >= 11) && (number % 100 <= 99)) ? 4 : 5)))) - - default: - return 0 - } - }, + translatePlural, } export default L10n -/** - * Returns the user's locale - * - * @return {string} locale string - */ -export const getLocale = () => $('html').data('locale') ?? 'en' - -/** - * Returns the user's language - * - * @return {string} language string - */ -export const getLanguage = () => $('html').prop('lang') - Handlebars.registerHelper('t', function(app, text) { - return L10n.translate(app, text) + return translate(app, text) }) diff --git a/core/src/OC/legacy-loader.js b/core/src/OC/legacy-loader.js deleted file mode 100644 index e5bbac8f3a0..00000000000 --- a/core/src/OC/legacy-loader.js +++ /dev/null @@ -1,79 +0,0 @@ -/** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -/** @typedef {import('jquery')} jQuery */ -import $ from 'jquery' -import { generateFilePath } from '@nextcloud/router' - -const loadedScripts = {} -const loadedStyles = [] - -/** - * Load a script for the server and load it. If the script is already loaded, - * the event handler will be called directly - * - * @param {string} app the app id to which the script belongs - * @param {string} script the filename of the script - * @param {Function} ready event handler to be called when the script is loaded - * @return {jQuery.Deferred} - * @deprecated 16.0.0 Use OCP.Loader.loadScript - */ -export const addScript = (app, script, ready) => { - console.warn('OC.addScript is deprecated, use OCP.Loader.loadScript instead') - - let deferred - const path = generateFilePath(app, 'js', script + '.js') - if (!loadedScripts[path]) { - deferred = $.Deferred() - $.getScript(path, () => deferred.resolve()) - loadedScripts[path] = deferred - } else { - if (ready) { - ready() - } - } - return loadedScripts[path] -} - -/** - * Loads a CSS file - * - * @param {string} app the app id to which the css style belongs - * @param {string} style the filename of the css file - * @deprecated 16.0.0 Use OCP.Loader.loadStylesheet - */ -export const addStyle = (app, style) => { - console.warn('OC.addStyle is deprecated, use OCP.Loader.loadStylesheet instead') - - const path = generateFilePath(app, 'css', style + '.css') - if (loadedStyles.indexOf(path) === -1) { - loadedStyles.push(path) - if (document.createStyleSheet) { - document.createStyleSheet(path) - } else { - style = $('<link rel="stylesheet" type="text/css" href="' + path + '"/>') - $('head').append(style) - } - } -} diff --git a/core/src/OC/menu.js b/core/src/OC/menu.js index 7d4d2f91a6c..4b4eb658592 100644 --- a/core/src/OC/menu.js +++ b/core/src/OC/menu.js @@ -1,32 +1,13 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Calviño Sánchez <danxuliu@gmail.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ import _ from 'underscore' /** @typedef {import('jquery')} jQuery */ import $ from 'jquery' -import { menuSpeed } from './constants' +import { menuSpeed } from './constants.js' export let currentMenu = null export let currentMenuToggle = null @@ -123,7 +104,7 @@ export const hideMenus = function(complete) { /** * Shows a given element as menu * - * @param {object} [$toggle=null] menu toggle + * @param {object} [$toggle] menu toggle * @param {object} $menuEl menu element * @param {Function} complete callback when the showing animation is done */ diff --git a/core/src/OC/msg.js b/core/src/OC/msg.js index ef6f9ec0f09..655631a03ff 100644 --- a/core/src/OC/msg.js +++ b/core/src/OC/msg.js @@ -1,25 +1,6 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author rakekniven <mark.ziegler@rakekniven.de> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ import $ from 'jquery' diff --git a/core/src/OC/navigation.js b/core/src/OC/navigation.js index 2102c37b3f5..b279b9a60f3 100644 --- a/core/src/OC/navigation.js +++ b/core/src/OC/navigation.js @@ -1,24 +1,6 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ export const redirect = targetURL => { window.location = targetURL } diff --git a/core/src/OC/notification.js b/core/src/OC/notification.js index 949df6a519c..b658f4163bb 100644 --- a/core/src/OC/notification.js +++ b/core/src/OC/notification.js @@ -1,28 +1,6 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Calviño Sánchez <danxuliu@gmail.com> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author npmbuildbot[bot] "npmbuildbot[bot]@users.noreply.github.com" - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ import _ from 'underscore' @@ -98,7 +76,7 @@ export default { * @param {string} html Message to display * @param {object} [options] options * @param {string} [options.type] notification type - * @param {number} [options.timeout=0] timeout value, defaults to 0 (permanent) + * @param {number} [options.timeout] timeout value, defaults to 0 (permanent) * @return {jQuery} jQuery element for notification row * @deprecated 17.0.0 use the `@nextcloud/dialogs` package */ @@ -117,7 +95,7 @@ export default { * @param {string} text Message to display * @param {object} [options] options * @param {string} [options.type] notification type - * @param {number} [options.timeout=0] timeout value, defaults to 0 (permanent) + * @param {number} [options.timeout] timeout value, defaults to 0 (permanent) * @return {jQuery} jQuery element for notification row * @deprecated 17.0.0 use the `@nextcloud/dialogs` package */ @@ -142,7 +120,7 @@ export default { * Updates (replaces) a sanitized notification. * * @param {string} text Message to display - * @return {jQuery} JQuery element for notificaiton row + * @return {jQuery} JQuery element for notification row * @deprecated 17.0.0 use the `@nextcloud/dialogs` package */ showUpdate(text) { @@ -160,10 +138,10 @@ export default { * * @param {string} text Message to show * @param {Array} [options] options array - * @param {number} [options.timeout=7] timeout in seconds, if this is 0 it will show the message permanently - * @param {boolean} [options.isHTML=false] an indicator for HTML notifications (true) or text (false) + * @param {number} [options.timeout] timeout in seconds, if this is 0 it will show the message permanently + * @param {boolean} [options.isHTML] an indicator for HTML notifications (true) or text (false) * @param {string} [options.type] notification type - * @return {JQuery} the toast element + * @return {jQuery} the toast element * @deprecated 17.0.0 use the `@nextcloud/dialogs` package */ showTemporary(text, options) { diff --git a/core/src/OC/password-confirmation.js b/core/src/OC/password-confirmation.js index 4aa643c19fb..621f7a0695f 100644 --- a/core/src/OC/password-confirmation.js +++ b/core/src/OC/password-confirmation.js @@ -1,130 +1,26 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ -import _ from 'underscore' -import $ from 'jquery' -import moment from 'moment' -import { generateUrl } from '@nextcloud/router' - -import OC from './index' +import { confirmPassword, isPasswordConfirmationRequired } from '@nextcloud/password-confirmation' +import '@nextcloud/password-confirmation/dist/style.css' /** * @namespace OC.PasswordConfirmation */ export default { - callback: null, - - pageLoadTime: null, - - init() { - $('.password-confirm-required').on('click', _.bind(this.requirePasswordConfirmation, this)) - this.pageLoadTime = moment.now() - }, requiresPasswordConfirmation() { - const serverTimeDiff = this.pageLoadTime - (window.nc_pageLoad * 1000) - const timeSinceLogin = moment.now() - (serverTimeDiff + (window.nc_lastLogin * 1000)) - - // if timeSinceLogin > 30 minutes and user backend allows password confirmation - return (window.backendAllowsPasswordConfirmation && timeSinceLogin > 30 * 60 * 1000) + return isPasswordConfirmationRequired() }, /** * @param {Function} callback success callback function - * @param {object} options options + * @param {object} options options currently not used by confirmPassword * @param {Function} rejectCallback error callback function */ requirePasswordConfirmation(callback, options, rejectCallback) { - options = typeof options !== 'undefined' ? options : {} - const defaults = { - title: t('core', 'Authentication required'), - text: t( - 'core', - 'This action requires you to confirm your password' - ), - confirm: t('core', 'Confirm'), - label: t('core', 'Password'), - error: '', - } - - const config = _.extend(defaults, options) - - const self = this - - if (this.requiresPasswordConfirmation()) { - OC.dialogs.prompt( - config.text, - config.title, - function(result, password) { - if (result && password !== '') { - self._confirmPassword(password, config) - } else if (_.isFunction(rejectCallback)) { - rejectCallback() - } - }, - true, - config.label, - true - ).then(function() { - const $dialog = $('.oc-dialog:visible') - $dialog.find('.ui-icon').remove() - $dialog.addClass('password-confirmation') - if (config.error !== '') { - const $error = $('<p></p>').addClass('msg warning').text(config.error) - $dialog.find('.oc-dialog-content').append($error) - } - $dialog.find('.oc-dialog-buttonrow').addClass('aside') - - const $buttons = $dialog.find('button') - $buttons.eq(0).hide() - $buttons.eq(1).text(config.confirm) - }) - } - - this.callback = callback - }, - - _confirmPassword(password, config) { - const self = this - - $.ajax({ - url: generateUrl('/login/confirm'), - data: { - password, - }, - type: 'POST', - success(response) { - window.nc_lastLogin = response.lastLogin - - if (_.isFunction(self.callback)) { - self.callback() - } - }, - error() { - config.error = t('core', 'Failed to authenticate, try again') - OC.PasswordConfirmation.requirePasswordConfirmation(self.callback, config) - }, - }) + confirmPassword().then(callback, rejectCallback) }, } diff --git a/core/src/OC/plugins.js b/core/src/OC/plugins.js index 4425c118589..8212fc0b4ee 100644 --- a/core/src/OC/plugins.js +++ b/core/src/OC/plugins.js @@ -1,24 +1,6 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ export default { diff --git a/core/src/OC/query-string.js b/core/src/OC/query-string.js index 56bf85186fb..df0f366133a 100644 --- a/core/src/OC/query-string.js +++ b/core/src/OC/query-string.js @@ -1,25 +1,6 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ import $ from 'jquery' @@ -28,7 +9,7 @@ import $ from 'jquery' * Parses a URL query string into a JS map * * @param {string} queryString query string in the format param1=1234¶m2=abcde¶m3=xyz - * @return {Object<string, string>} map containing key/values matching the URL parameters + * @return {Record<string, string>} map containing key/values matching the URL parameters */ export const parse = queryString => { let pos @@ -77,7 +58,7 @@ export const parse = queryString => { /** * Builds a URL query from a JS map. * - * @param {Object<string, string>} params map containing key/values matching the URL parameters + * @param {Record<string, string>} params map containing key/values matching the URL parameters * @return {string} String containing a URL query (without question) mark */ export const build = params => { diff --git a/core/src/OC/requesttoken.js b/core/src/OC/requesttoken.js deleted file mode 100644 index eba15e88e08..00000000000 --- a/core/src/OC/requesttoken.js +++ /dev/null @@ -1,57 +0,0 @@ -/** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -import { emit } from '@nextcloud/event-bus' - -/** - * @private - * @param {Document} global the document to read the initial value from - * @param {Function} emit the function to invoke for every new token - * @return {object} - */ -export const manageToken = (global, emit) => { - let token = global.getElementsByTagName('head')[0].getAttribute('data-requesttoken') - - return { - getToken: () => token, - setToken: newToken => { - token = newToken - - emit('csrf-token-update', { - token, - }) - }, - } -} - -const manageFromDocument = manageToken(document, emit) - -/** - * @return {string} - */ -export const getToken = manageFromDocument.getToken - -/** - * @param {string} newToken new token - */ -export const setToken = manageFromDocument.setToken diff --git a/core/src/OC/requesttoken.ts b/core/src/OC/requesttoken.ts new file mode 100644 index 00000000000..8ecf0b3de7e --- /dev/null +++ b/core/src/OC/requesttoken.ts @@ -0,0 +1,49 @@ +/** + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { emit } from '@nextcloud/event-bus' +import { generateUrl } from '@nextcloud/router' + +/** + * Get the current CSRF token. + */ +export function getRequestToken(): string { + return document.head.dataset.requesttoken! +} + +/** + * Set a new CSRF token (e.g. because of session refresh). + * This also emits an event bus event for the updated token. + * + * @param token - The new token + * @fires Error - If the passed token is not a potential valid token + */ +export function setRequestToken(token: string): void { + if (!token || typeof token !== 'string') { + throw new Error('Invalid CSRF token given', { cause: { token } }) + } + + document.head.dataset.requesttoken = token + emit('csrf-token-update', { token }) +} + +/** + * Fetch the request token from the API. + * This does also set it on the current context, see `setRequestToken`. + * + * @fires Error - If the request failed + */ +export async function fetchRequestToken(): Promise<string> { + const url = generateUrl('/csrftoken') + + const response = await fetch(url) + if (!response.ok) { + throw new Error('Could not fetch CSRF token from API', { cause: response }) + } + + const { token } = await response.json() + setRequestToken(token) + return token +} diff --git a/core/src/OC/routing.js b/core/src/OC/routing.js index 8752aa3883e..4b81714d6f0 100644 --- a/core/src/OC/routing.js +++ b/core/src/OC/routing.js @@ -1,25 +1,6 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ import { diff --git a/core/src/OC/theme.js b/core/src/OC/theme.js index b7fcfd8ce4d..af45c37de7e 100644 --- a/core/src/OC/theme.js +++ b/core/src/OC/theme.js @@ -1,23 +1,6 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ export const theme = window._theme || {} diff --git a/core/src/OC/util-history.js b/core/src/OC/util-history.js index a7398b4a2fc..7ecd0e098c6 100644 --- a/core/src/OC/util-history.js +++ b/core/src/OC/util-history.js @@ -1,28 +1,10 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ import _ from 'underscore' -import OC from './index' +import OC from './index.js' /** * Utility class for the history API, @@ -45,7 +27,7 @@ export default { * or a map * @param {string} [url] URL to be used, otherwise the current URL will be used, * using the params as query string - * @param {boolean} [replace=false] whether to replace instead of pushing + * @param {boolean} [replace] whether to replace instead of pushing */ _pushState(params, url, replace) { let strParams diff --git a/core/src/OC/util.js b/core/src/OC/util.js index e1a2f8f0687..c46d9a141b1 100644 --- a/core/src/OC/util.js +++ b/core/src/OC/util.js @@ -1,30 +1,12 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ import moment from 'moment' -import History from './util-history' -import OC from './index' +import History from './util-history.js' +import OC from './index.js' import { formatFileSize as humanFileSize } from '@nextcloud/files' /** @@ -64,7 +46,7 @@ export default { History, /** - * @deprecated use https://nextcloud.github.io/nextcloud-files/modules/_humanfilesize_.html#formatfilesize + * @deprecated use https://nextcloud.github.io/nextcloud-files/functions/formatFileSize.html */ humanFileSize, @@ -73,7 +55,7 @@ export default { * Makes 2kB to 2048. * Inspired by computerFileSize in helper.php * - * @param {string} string file size in human readable format + * @param {string} string file size in human-readable format * @return {number} or null if string could not be parsed * * @@ -124,7 +106,7 @@ export default { */ formatDate(timestamp, format) { if (window.TESTING === undefined) { - console.warn('OC.Util.formatDate is deprecated and will be removed in Nextcloud 21. See @nextcloud/moment') + OC.debug && console.warn('OC.Util.formatDate is deprecated and will be removed in Nextcloud 21. See @nextcloud/moment') } format = format || 'LLL' return moment(timestamp).format(format) @@ -136,7 +118,7 @@ export default { */ relativeModifiedDate(timestamp) { if (window.TESTING === undefined) { - console.warn('OC.Util.relativeModifiedDate is deprecated and will be removed in Nextcloud 21. See @nextcloud/moment') + OC.debug && console.warn('OC.Util.relativeModifiedDate is deprecated and will be removed in Nextcloud 21. See @nextcloud/moment') } const diff = moment().diff(moment(timestamp)) if (diff >= 0 && diff < 45000) { diff --git a/core/src/OC/webroot.js b/core/src/OC/webroot.js index f5d063d6b50..cbe5a6190e1 100644 --- a/core/src/OC/webroot.js +++ b/core/src/OC/webroot.js @@ -1,23 +1,6 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ let webroot = window._oc_webroot diff --git a/core/src/OC/xhr-error.js b/core/src/OC/xhr-error.js index 990340a1fda..233aaf60350 100644 --- a/core/src/OC/xhr-error.js +++ b/core/src/OC/xhr-error.js @@ -1,41 +1,25 @@ /** - * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ import _ from 'underscore' import $ from 'jquery' -import OC from './index' -import Notification from './notification' +import OC from './index.js' +import Notification from './notification.js' +import { getCurrentUser } from '@nextcloud/auth' +import { showWarning } from '@nextcloud/dialogs' /** * Warn users that the connection to the server was lost temporarily * - * This function is throttled to prevent stacked notfications. + * This function is throttled to prevent stacked notifications. * After 7sec the first notification is gone, then we can show another one * if necessary. */ export const ajaxConnectionLostHandler = _.throttle(() => { - Notification.showTemporary(t('core', 'Connection to server lost')) + showWarning(t('core', 'Connection to server lost')) }, 7 * 1000, { trailing: false }) /** @@ -46,13 +30,13 @@ export const ajaxConnectionLostHandler = _.throttle(() => { */ export const processAjaxError = xhr => { // purposefully aborted request ? - // OC._userIsNavigatingAway needed to distinguish ajax calls cancelled by navigating away - // from calls cancelled by failed cross-domain ajax due to SSO redirect + // OC._userIsNavigatingAway needed to distinguish Ajax calls cancelled by navigating away + // from calls cancelled by failed cross-domain Ajax due to SSO redirect if (xhr.status === 0 && (xhr.statusText === 'abort' || xhr.statusText === 'timeout' || OC._reloadCalled)) { return } - if (_.contains([302, 303, 307, 401], xhr.status) && OC.currentUser) { + if ([302, 303, 307, 401].includes(xhr.status) && getCurrentUser()) { // sometimes "beforeunload" happens later, so need to defer the reload a bit setTimeout(function() { if (!OC._userIsNavigatingAway && !OC._reloadCalled) { @@ -65,7 +49,7 @@ export const processAjaxError = xhr => { OC.reload() } timer++ - }, 1000 // 1 second interval + }, 1000, // 1 second interval ) // only call reload once |