aboutsummaryrefslogtreecommitdiffstats
path: root/core/src/OCP
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/OCP')
-rw-r--r--core/src/OCP/accessibility.js28
-rw-r--r--core/src/OCP/appconfig.js102
-rw-r--r--core/src/OCP/collaboration.js65
-rw-r--r--core/src/OCP/comments.js61
-rw-r--r--core/src/OCP/index.js35
-rw-r--r--core/src/OCP/loader.js64
-rw-r--r--core/src/OCP/toast.js67
-rw-r--r--core/src/OCP/whatsnew.js151
8 files changed, 573 insertions, 0 deletions
diff --git a/core/src/OCP/accessibility.js b/core/src/OCP/accessibility.js
new file mode 100644
index 00000000000..4a1399f3f96
--- /dev/null
+++ b/core/src/OCP/accessibility.js
@@ -0,0 +1,28 @@
+/**
+ * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import { loadState } from '@nextcloud/initial-state'
+
+/**
+ * Set the page heading
+ *
+ * @param {string} heading page title from the history api
+ * @since 27.0.0
+ */
+export function setPageHeading(heading) {
+ const headingEl = document.getElementById('page-heading-level-1')
+ if (headingEl) {
+ headingEl.textContent = heading
+ }
+}
+export default {
+ /**
+ * @return {boolean} Whether the user opted-out of shortcuts so that they should not be registered
+ */
+ disableKeyboardShortcuts() {
+ return loadState('theming', 'shortcutsDisabled', false)
+ },
+ setPageHeading,
+}
diff --git a/core/src/OCP/appconfig.js b/core/src/OCP/appconfig.js
new file mode 100644
index 00000000000..78f94922d53
--- /dev/null
+++ b/core/src/OCP/appconfig.js
@@ -0,0 +1,102 @@
+/**
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import $ from 'jquery'
+import { generateOcsUrl } from '@nextcloud/router'
+
+import OC from '../OC/index.js'
+
+/**
+ * @param {string} method 'post' or 'delete'
+ * @param {string} endpoint endpoint
+ * @param {object} [options] destructuring object
+ * @param {object} [options.data] option data
+ * @param {Function} [options.success] success callback
+ * @param {Function} [options.error] error callback
+ */
+function call(method, endpoint, options) {
+ if ((method === 'post' || method === 'delete') && OC.PasswordConfirmation.requiresPasswordConfirmation()) {
+ OC.PasswordConfirmation.requirePasswordConfirmation(_.bind(call, this, method, endpoint, options))
+ return
+ }
+
+ options = options || {}
+ $.ajax({
+ type: method.toUpperCase(),
+ url: generateOcsUrl('apps/provisioning_api/api/v1/config/apps') + endpoint,
+ data: options.data || {},
+ success: options.success,
+ error: options.error,
+ })
+}
+
+/**
+ * @param {object} [options] destructuring object
+ * @param {Function} [options.success] success callback
+ * @since 11.0.0
+ */
+export function getApps(options) {
+ call('get', '', options)
+}
+
+/**
+ * @param {string} app app id
+ * @param {object} [options] destructuring object
+ * @param {Function} [options.success] success callback
+ * @param {Function} [options.error] error callback
+ * @since 11.0.0
+ */
+export function getKeys(app, options) {
+ call('get', '/' + app, options)
+}
+
+/**
+ * @param {string} app app id
+ * @param {string} key key
+ * @param {string | Function} defaultValue default value
+ * @param {object} [options] destructuring object
+ * @param {Function} [options.success] success callback
+ * @param {Function} [options.error] error callback
+ * @since 11.0.0
+ */
+export function getValue(app, key, defaultValue, options) {
+ options = options || {}
+ options.data = {
+ defaultValue,
+ }
+
+ call('get', '/' + app + '/' + key, options)
+}
+
+/**
+ * @param {string} app app id
+ * @param {string} key key
+ * @param {string} value value
+ * @param {object} [options] destructuring object
+ * @param {Function} [options.success] success callback
+ * @param {Function} [options.error] error callback
+ * @since 11.0.0
+ */
+export function setValue(app, key, value, options) {
+ options = options || {}
+ options.data = {
+ value,
+ }
+
+ call('post', '/' + app + '/' + key, options)
+}
+
+/**
+ * @param {string} app app id
+ * @param {string} key key
+ * @param {object} [options] destructuring object
+ * @param {Function} [options.success] success callback
+ * @param {Function} [options.error] error callback
+ * @since 11.0.0
+ */
+export function deleteKey(app, key, options) {
+ call('delete', '/' + app + '/' + key, options)
+}
diff --git a/core/src/OCP/collaboration.js b/core/src/OCP/collaboration.js
new file mode 100644
index 00000000000..82ff34392cf
--- /dev/null
+++ b/core/src/OCP/collaboration.js
@@ -0,0 +1,65 @@
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import escapeHTML from 'escape-html'
+
+/**
+ * @typedef TypeDefinition
+ * @function action This action is executed to let the user select a resource
+ * @param {string} icon Contains the icon css class for the type
+ * @function Object() { [native code] }
+ */
+
+/**
+ * @type {TypeDefinition[]}
+ */
+const types = {}
+
+/**
+ * Those translations will be used by the vue component but they should be shipped with the server
+ * FIXME: Those translations should be added to the library
+ *
+ * @return {Array}
+ */
+export const l10nProjects = () => {
+ return [
+ t('core', 'Add to a project'),
+ t('core', 'Show details'),
+ t('core', 'Hide details'),
+ t('core', 'Rename project'),
+ t('core', 'Failed to rename the project'),
+ t('core', 'Failed to create a project'),
+ t('core', 'Failed to add the item to the project'),
+ t('core', 'Connect items to a project to make them easier to find'),
+ t('core', 'Type to search for existing projects'),
+ ]
+}
+
+export default {
+ /**
+ *
+ * @param {string} type type
+ * @param {TypeDefinition} typeDefinition typeDefinition
+ */
+ registerType(type, typeDefinition) {
+ types[type] = typeDefinition
+ },
+ trigger(type) {
+ return types[type].action()
+ },
+ getTypes() {
+ return Object.keys(types)
+ },
+ getIcon(type) {
+ return types[type].typeIconClass || ''
+ },
+ getLabel(type) {
+ return escapeHTML(types[type].typeString || type)
+ },
+ getLink(type, id) {
+ /* TODO: Allow action to be executed instead of href as well */
+ return typeof types[type] !== 'undefined' ? types[type].link(id) : ''
+ },
+}
diff --git a/core/src/OCP/comments.js b/core/src/OCP/comments.js
new file mode 100644
index 00000000000..34699a477d1
--- /dev/null
+++ b/core/src/OCP/comments.js
@@ -0,0 +1,61 @@
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import $ from 'jquery'
+
+/*
+ * Detects links:
+ * Either the http(s) protocol is given or two strings, basically limited to ascii with the last
+ * word being at least one digit long,
+ * followed by at least another character
+ *
+ * The downside: anything not ascii is excluded. Not sure how common it is in areas using different
+ * alphabets… the upside: fake domains with similar looking characters won't be formatted as links
+ *
+ * This is a copy of the backend regex in IURLGenerator, make sure to adjust both when changing
+ */
+const urlRegex = /(\s|^)(https?:\/\/)([-A-Z0-9+_.]+(?::[0-9]+)?(?:\/[-A-Z0-9+&@#%?=~_|!:,.;()]*)*)(\s|$)/ig
+
+/**
+ * @param {any} content -
+ */
+export function plainToRich(content) {
+ return this.formatLinksRich(content)
+}
+
+/**
+ * @param {any} content -
+ */
+export function richToPlain(content) {
+ return this.formatLinksPlain(content)
+}
+
+/**
+ * @param {any} content -
+ */
+export function formatLinksRich(content) {
+ return content.replace(urlRegex, function(_, leadingSpace, protocol, url, trailingSpace) {
+ let linkText = url
+ if (!protocol) {
+ protocol = 'https://'
+ } else if (protocol === 'http://') {
+ linkText = protocol + url
+ }
+
+ return leadingSpace + '<a class="external" target="_blank" rel="noopener noreferrer" href="' + protocol + url + '">' + linkText + '</a>' + trailingSpace
+ })
+}
+
+/**
+ * @param {any} content -
+ */
+export function formatLinksPlain(content) {
+ const $content = $('<div></div>').html(content)
+ $content.find('a').each(function() {
+ const $this = $(this)
+ $this.html($this.attr('href'))
+ })
+ return $content.html()
+}
diff --git a/core/src/OCP/index.js b/core/src/OCP/index.js
new file mode 100644
index 00000000000..94f4e8e5eb3
--- /dev/null
+++ b/core/src/OCP/index.js
@@ -0,0 +1,35 @@
+/**
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import { loadState } from '@nextcloud/initial-state'
+
+import * as AppConfig from './appconfig.js'
+import * as Comments from './comments.js'
+import * as WhatsNew from './whatsnew.js'
+
+import Accessibility from './accessibility.js'
+import Collaboration from './collaboration.js'
+import Loader from './loader.js'
+import Toast from './toast.js'
+
+/** @namespace OCP */
+export default {
+ Accessibility,
+ AppConfig,
+ Collaboration,
+ Comments,
+ InitialState: {
+ /**
+ * @deprecated 18.0.0 add https://www.npmjs.com/package/@nextcloud/initial-state to your app
+ */
+ loadState,
+ },
+ Loader,
+ /**
+ * @deprecated 19.0.0 use the `@nextcloud/dialogs` package instead
+ */
+ Toast,
+ WhatsNew,
+}
diff --git a/core/src/OCP/loader.js b/core/src/OCP/loader.js
new file mode 100644
index 00000000000..d307eb27996
--- /dev/null
+++ b/core/src/OCP/loader.js
@@ -0,0 +1,64 @@
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import { generateFilePath } from '@nextcloud/router'
+
+const loadedScripts = {}
+const loadedStylesheets = {}
+/**
+ * @namespace OCP
+ * @class Loader
+ */
+export default {
+
+ /**
+ * Load a script asynchronously
+ *
+ * @param {string} app the app name
+ * @param {string} file the script file name
+ * @return {Promise}
+ */
+ loadScript(app, file) {
+ const key = app + file
+ if (Object.prototype.hasOwnProperty.call(loadedScripts, key)) {
+ return Promise.resolve()
+ }
+ loadedScripts[key] = true
+ return new Promise(function(resolve, reject) {
+ const scriptPath = generateFilePath(app, 'js', file)
+ const script = document.createElement('script')
+ script.src = scriptPath
+ script.setAttribute('nonce', btoa(OC.requestToken))
+ script.onload = () => resolve()
+ script.onerror = () => reject(new Error(`Failed to load script from ${scriptPath}`))
+ document.head.appendChild(script)
+ })
+ },
+
+ /**
+ * Load a stylesheet file asynchronously
+ *
+ * @param {string} app the app name
+ * @param {string} file the script file name
+ * @return {Promise}
+ */
+ loadStylesheet(app, file) {
+ const key = app + file
+ if (Object.prototype.hasOwnProperty.call(loadedStylesheets, key)) {
+ return Promise.resolve()
+ }
+ loadedStylesheets[key] = true
+ return new Promise(function(resolve, reject) {
+ const stylePath = generateFilePath(app, 'css', file)
+ const link = document.createElement('link')
+ link.href = stylePath
+ link.type = 'text/css'
+ link.rel = 'stylesheet'
+ link.onload = () => resolve()
+ link.onerror = () => reject(new Error(`Failed to load stylesheet from ${stylePath}`))
+ document.head.appendChild(link)
+ })
+ },
+}
diff --git a/core/src/OCP/toast.js b/core/src/OCP/toast.js
new file mode 100644
index 00000000000..f93344bbc8e
--- /dev/null
+++ b/core/src/OCP/toast.js
@@ -0,0 +1,67 @@
+/**
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import {
+ showError,
+ showInfo, showMessage,
+ showSuccess,
+ showWarning,
+} from '@nextcloud/dialogs'
+
+/** @typedef {import('toastify-js')} Toast */
+
+export default {
+ /**
+ * @deprecated 19.0.0 use `showSuccess` from the `@nextcloud/dialogs` package instead
+ *
+ * @param {string} text the toast text
+ * @param {object} options options
+ * @return {Toast}
+ */
+ success(text, options) {
+ return showSuccess(text, options)
+ },
+ /**
+ * @deprecated 19.0.0 use `showWarning` from the `@nextcloud/dialogs` package instead
+ *
+ * @param {string} text the toast text
+ * @param {object} options options
+ * @return {Toast}
+ */
+ warning(text, options) {
+ return showWarning(text, options)
+ },
+ /**
+ * @deprecated 19.0.0 use `showError` from the `@nextcloud/dialogs` package instead
+ *
+ * @param {string} text the toast text
+ * @param {object} options options
+ * @return {Toast}
+ */
+ error(text, options) {
+ return showError(text, options)
+ },
+ /**
+ * @deprecated 19.0.0 use `showInfo` from the `@nextcloud/dialogs` package instead
+ *
+ * @param {string} text the toast text
+ * @param {object} options options
+ * @return {Toast}
+ */
+ info(text, options) {
+ return showInfo(text, options)
+ },
+ /**
+ * @deprecated 19.0.0 use `showMessage` from the `@nextcloud/dialogs` package instead
+ *
+ * @param {string} text the toast text
+ * @param {object} options options
+ * @return {Toast}
+ */
+ message(text, options) {
+ return showMessage(text, options)
+ },
+
+}
diff --git a/core/src/OCP/whatsnew.js b/core/src/OCP/whatsnew.js
new file mode 100644
index 00000000000..acada6a8383
--- /dev/null
+++ b/core/src/OCP/whatsnew.js
@@ -0,0 +1,151 @@
+/**
+ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import _ from 'underscore'
+import $ from 'jquery'
+import { generateOcsUrl } from '@nextcloud/router'
+
+/**
+ * @param {any} options -
+ */
+export function query(options) {
+ options = options || {}
+ const dismissOptions = options.dismiss || {}
+ $.ajax({
+ type: 'GET',
+ url: options.url || generateOcsUrl('core/whatsnew?format=json'),
+ success: options.success || function(data, statusText, xhr) {
+ onQuerySuccess(data, statusText, xhr, dismissOptions)
+ },
+ error: options.error || onQueryError,
+ })
+}
+
+/**
+ * @param {any} version -
+ * @param {any} options -
+ */
+export function dismiss(version, options) {
+ options = options || {}
+ $.ajax({
+ type: 'POST',
+ url: options.url || generateOcsUrl('core/whatsnew'),
+ data: { version: encodeURIComponent(version) },
+ success: options.success || onDismissSuccess,
+ error: options.error || onDismissError,
+ })
+ // remove element immediately
+ $('.whatsNewPopover').remove()
+}
+
+/**
+ * @param {any} data -
+ * @param {any} statusText -
+ * @param {any} xhr -
+ * @param {any} dismissOptions -
+ */
+function onQuerySuccess(data, statusText, xhr, dismissOptions) {
+ console.debug('querying Whats New data was successful: ' + statusText)
+ console.debug(data)
+
+ if (xhr.status !== 200) {
+ return
+ }
+
+ let item, menuItem, text, icon
+
+ const div = document.createElement('div')
+ div.classList.add('popovermenu', 'open', 'whatsNewPopover', 'menu-left')
+
+ const list = document.createElement('ul')
+
+ // header
+ item = document.createElement('li')
+ menuItem = document.createElement('span')
+ menuItem.className = 'menuitem'
+
+ text = document.createElement('span')
+ text.innerText = t('core', 'New in') + ' ' + data.ocs.data.product
+ text.className = 'caption'
+ menuItem.appendChild(text)
+
+ icon = document.createElement('span')
+ icon.className = 'icon-close'
+ icon.onclick = function() {
+ dismiss(data.ocs.data.version, dismissOptions)
+ }
+ menuItem.appendChild(icon)
+
+ item.appendChild(menuItem)
+ list.appendChild(item)
+
+ // Highlights
+ for (const i in data.ocs.data.whatsNew.regular) {
+ const whatsNewTextItem = data.ocs.data.whatsNew.regular[i]
+ item = document.createElement('li')
+
+ menuItem = document.createElement('span')
+ menuItem.className = 'menuitem'
+
+ icon = document.createElement('span')
+ icon.className = 'icon-checkmark'
+ menuItem.appendChild(icon)
+
+ text = document.createElement('p')
+ text.innerHTML = _.escape(whatsNewTextItem)
+ menuItem.appendChild(text)
+
+ item.appendChild(menuItem)
+ list.appendChild(item)
+ }
+
+ // Changelog URL
+ if (!_.isUndefined(data.ocs.data.changelogURL)) {
+ item = document.createElement('li')
+
+ menuItem = document.createElement('a')
+ menuItem.href = data.ocs.data.changelogURL
+ menuItem.rel = 'noreferrer noopener'
+ menuItem.target = '_blank'
+
+ icon = document.createElement('span')
+ icon.className = 'icon-link'
+ menuItem.appendChild(icon)
+
+ text = document.createElement('span')
+ text.innerText = t('core', 'View changelog')
+ menuItem.appendChild(text)
+
+ item.appendChild(menuItem)
+ list.appendChild(item)
+ }
+
+ div.appendChild(list)
+ document.body.appendChild(div)
+}
+
+/**
+ * @param {any} x -
+ * @param {any} t -
+ * @param {any} e -
+ */
+function onQueryError(x, t, e) {
+ console.debug('querying Whats New Data resulted in an error: ' + t + e)
+ console.debug(x)
+}
+
+/**
+ * @param {any} data -
+ */
+function onDismissSuccess(data) {
+ // noop
+}
+
+/**
+ * @param {any} data -
+ */
+function onDismissError(data) {
+ console.debug('dismissing Whats New data resulted in an error: ' + data)
+}