chore!(core): remove legacy OC.Files.Client

Use the DAV client from `@nextcloud/files/dav` instead.

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
Ferdinand Thiessen 2026-01-19 18:03:44 +01:00
parent fd5108b68b
commit a32168b01e
No known key found for this signature in database
GPG key ID: 7E849AE05218500F
8 changed files with 54 additions and 1171 deletions

View file

@ -1,24 +0,0 @@
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { getCSPNonce } from '@nextcloud/auth'
__webpack_nonce__ = getCSPNonce()
window.OCP.Collaboration.registerType('file', {
action: () => {
return new Promise((resolve, reject) => {
OC.dialogs.filepicker(t('files_sharing', 'Link to a file'), function(f) {
const client = OC.Files.getClient()
client.getFileInfo(f).then((status, fileInfo) => {
resolve(fileInfo.id)
}).fail(() => {
reject(new Error('Cannot get fileinfo'))
})
}, false, null, false, OC.dialogs.FILEPICKER_TYPE_CHOOSE, '', { allowDirectoryChooser: true })
})
},
typeString: t('files_sharing', 'Link to a file'),
typeIconClass: 'icon-files-dark',
})

View file

@ -0,0 +1,28 @@
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { getCSPNonce } from '@nextcloud/auth'
import { FilePickerType, getFilePickerBuilder } from '@nextcloud/dialogs'
import { t } from '@nextcloud/l10n'
__webpack_nonce__ = getCSPNonce()
window.OCP.Collaboration.registerType('file', {
typeString: t('files_sharing', 'Link to a file'),
typeIconClass: 'icon-files-dark',
async action() {
const filePicker = getFilePickerBuilder(t('files_sharing', 'Link to a file'))
.setType(FilePickerType.Choose)
.allowDirectories(true)
.build()
try {
const [node] = await filePicker.pickNodes()
return node!.fileid
} catch {
throw new Error('Cannot get fileinfo')
}
},
})

View file

@ -5,6 +5,8 @@
import type { Attribute, INode } from '@nextcloud/files'
import { Permission } from '@nextcloud/files'
interface RawLegacyFileInfo {
id: number
path: string
@ -57,13 +59,31 @@ export default function(node: INode): LegacyFileInfo {
attributes: node.attributes,
}
const fileInfo = new OC.Files.FileInfo(rawFileInfo)
// TODO remove when no more legacy backbone is used
fileInfo.get = (key) => fileInfo[key]
fileInfo.isDirectory = () => fileInfo.mimetype === 'httpd/unix-directory'
fileInfo.canEdit = () => Boolean(fileInfo.permissions & OC.PERMISSION_UPDATE)
fileInfo.node = node
const fileInfo: LegacyFileInfo = {
...rawFileInfo,
node,
get(key) {
return this[key]
},
isDirectory() {
return this.mimetype === 'httpd/unix-directory'
},
canEdit() {
return Boolean(this.permissions & Permission.UPDATE)
},
canDownload() {
for (const i in this.shareAttributes) {
const attr = this.shareAttributes[i]
if (attr.scope === 'permissions' && attr.key === 'download') {
return attr.value === true
}
}
return true
},
}
return fileInfo
}

View file

@ -18,8 +18,6 @@ use OCP\WorkflowEngine\Events\LoadSettingsScriptsEvent;
/** @template-implements IEventListener<LoadSettingsScriptsEvent> */
class LoadAdditionalSettingsScriptsListener implements IEventListener {
public function handle(Event $event): void {
Util::addScript('core', 'files_fileinfo');
Util::addScript('core', 'files_client');
Util::addScript('core', 'systemtags');
Util::addScript(Application::APP_ID, 'workflowengine');
}

View file

@ -12,8 +12,6 @@ module.exports = {
},
core: {
'ajax-cron': path.join(__dirname, 'core/src', 'ajax-cron.ts'),
files_client: path.join(__dirname, 'core/src', 'files/client.js'),
files_fileinfo: path.join(__dirname, 'core/src', 'files/fileinfo.js'),
install: path.join(__dirname, 'core/src', 'install.ts'),
login: path.join(__dirname, 'core/src', 'login.js'),
login_flow: path.join(__dirname, 'core/src', 'login-flow.ts'),

View file

@ -54,9 +54,6 @@ class BeforeTemplateRenderedListener implements IEventListener {
if ($event->getResponse()->getRenderAs() !== TemplateResponse::RENDER_AS_ERROR) {
Util::addScript('core', 'merged-template-prepend', 'core', true);
Util::addScript('core', 'files_client', 'core', true);
Util::addScript('core', 'files_fileinfo', 'core', true);
// If installed and background job is set to ajax, add dedicated script
if ($this->appConfig->getValueString('core', 'backgroundjobs_mode', 'ajax') === 'ajax') {

View file

@ -1,972 +0,0 @@
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { getCurrentUser } from '@nextcloud/auth'
import escapeHTML from 'escape-html'
import $ from 'jquery'
import _ from 'underscore'
import logger from '../logger.js'
/* global dav */
(function(OC, FileInfo) {
/**
* @class OC.Files.Client
* @classdesc Client to access files on the server
*
* @param {object} options
* @param {string} options.host host name
* @param {number} [options.port] port
* @param {boolean} [options.useHTTPS] whether to use https
* @param {string} [options.root] root path
* @param {string} [options.userName] user name
* @param {string} [options.password] password
*
* @since 8.2
*/
const Client = function(options) {
this._root = options.root
if (this._root.charAt(this._root.length - 1) === '/') {
this._root = this._root.substr(0, this._root.length - 1)
}
let url = Client.PROTOCOL_HTTP + '://'
if (options.useHTTPS) {
url = Client.PROTOCOL_HTTPS + '://'
}
url += options.host + this._root
this._host = options.host
this._defaultHeaders = options.defaultHeaders || {
'X-Requested-With': 'XMLHttpRequest',
requesttoken: OC.requestToken,
}
this._baseUrl = url
const clientOptions = {
baseUrl: this._baseUrl,
xmlNamespaces: {
'DAV:': 'd',
'http://owncloud.org/ns': 'oc',
'http://nextcloud.org/ns': 'nc',
'http://open-collaboration-services.org/ns': 'ocs',
},
}
if (options.userName) {
clientOptions.userName = options.userName
}
if (options.password) {
clientOptions.password = options.password
}
this._client = new dav.Client(clientOptions)
this._client.xhrProvider = _.bind(this._xhrProvider, this)
this._fileInfoParsers = []
}
Client.NS_OWNCLOUD = 'http://owncloud.org/ns'
Client.NS_NEXTCLOUD = 'http://nextcloud.org/ns'
Client.NS_DAV = 'DAV:'
Client.NS_OCS = 'http://open-collaboration-services.org/ns'
Client.PROPERTY_GETLASTMODIFIED = '{' + Client.NS_DAV + '}getlastmodified'
Client.PROPERTY_GETETAG = '{' + Client.NS_DAV + '}getetag'
Client.PROPERTY_GETCONTENTTYPE = '{' + Client.NS_DAV + '}getcontenttype'
Client.PROPERTY_RESOURCETYPE = '{' + Client.NS_DAV + '}resourcetype'
Client.PROPERTY_INTERNAL_FILEID = '{' + Client.NS_OWNCLOUD + '}fileid'
Client.PROPERTY_PERMISSIONS = '{' + Client.NS_OWNCLOUD + '}permissions'
Client.PROPERTY_SIZE = '{' + Client.NS_OWNCLOUD + '}size'
Client.PROPERTY_GETCONTENTLENGTH = '{' + Client.NS_DAV + '}getcontentlength'
Client.PROPERTY_ISENCRYPTED = '{' + Client.NS_DAV + '}is-encrypted'
Client.PROPERTY_SHARE_PERMISSIONS = '{' + Client.NS_OCS + '}share-permissions'
Client.PROPERTY_SHARE_ATTRIBUTES = '{' + Client.NS_NEXTCLOUD + '}share-attributes'
Client.PROPERTY_QUOTA_AVAILABLE_BYTES = '{' + Client.NS_DAV + '}quota-available-bytes'
Client.PROTOCOL_HTTP = 'http'
Client.PROTOCOL_HTTPS = 'https'
Client._PROPFIND_PROPERTIES = [
/**
* Modified time
*/
[Client.NS_DAV, 'getlastmodified'],
/**
* Etag
*/
[Client.NS_DAV, 'getetag'],
/**
* Mime type
*/
[Client.NS_DAV, 'getcontenttype'],
/**
* Resource type "collection" for folders, empty otherwise
*/
[Client.NS_DAV, 'resourcetype'],
/**
* File id
*/
[Client.NS_OWNCLOUD, 'fileid'],
/**
* Letter-coded permissions
*/
[Client.NS_OWNCLOUD, 'permissions'],
// [Client.NS_OWNCLOUD, 'downloadURL'],
/**
* Folder sizes
*/
[Client.NS_OWNCLOUD, 'size'],
/**
* File sizes
*/
[Client.NS_DAV, 'getcontentlength'],
[Client.NS_DAV, 'quota-available-bytes'],
/**
* Preview availability
*/
[Client.NS_NEXTCLOUD, 'has-preview'],
/**
* Mount type
*/
[Client.NS_NEXTCLOUD, 'mount-type'],
/**
* Encryption state
*/
[Client.NS_NEXTCLOUD, 'is-encrypted'],
/**
* Share permissions
*/
[Client.NS_OCS, 'share-permissions'],
/**
* Share attributes
*/
[Client.NS_NEXTCLOUD, 'share-attributes'],
]
/**
* @memberof OC.Files
*/
Client.prototype = {
/**
* Root path of the Webdav endpoint
*
* @type string
*/
_root: null,
/**
* Client from the library
*
* @type dav.Client
*/
_client: null,
/**
* Array of file info parsing functions.
*
* @type Array<OC.Files.Client~parseFileInfo>
*/
_fileInfoParsers: [],
/**
* Returns the configured XHR provider for davclient
*
* @return {XMLHttpRequest}
*/
_xhrProvider: function() {
const headers = this._defaultHeaders
const xhr = new XMLHttpRequest()
const oldOpen = xhr.open
// override open() method to add headers
xhr.open = function() {
const result = oldOpen.apply(this, arguments)
_.each(headers, function(value, key) {
xhr.setRequestHeader(key, value)
})
return result
}
OC.registerXHRForErrorProcessing(xhr)
return xhr
},
/**
* Prepends the base url to the given path sections
*
* @param {...string} path sections
*
* @return {string} base url + joined path, any leading or trailing slash
* will be kept
*/
_buildUrl: function() {
let path = this._buildPath.apply(this, arguments)
if (path.charAt([path.length - 1]) === '/') {
path = path.substr(0, path.length - 1)
}
if (path.charAt(0) === '/') {
path = path.substr(1)
}
return this._baseUrl + '/' + path
},
/**
* Append the path to the root and also encode path
* sections
*
* @param {...string} path sections
*
* @return {string} joined path, any leading or trailing slash
* will be kept
*/
_buildPath: function() {
let path = OC.joinPaths.apply(this, arguments)
const sections = path.split('/')
let i
for (i = 0; i < sections.length; i++) {
sections[i] = encodeURIComponent(sections[i])
}
path = sections.join('/')
return path
},
/**
* Parse headers string into a map
*
* @param {string} headersString headers list as string
*
* @return {Object<string, Array>} map of header name to header contents
*/
_parseHeaders: function(headersString) {
const headerRows = headersString.split('\n')
const headers = {}
for (let i = 0; i < headerRows.length; i++) {
const sepPos = headerRows[i].indexOf(':')
if (sepPos < 0) {
continue
}
const headerName = headerRows[i].substr(0, sepPos)
const headerValue = headerRows[i].substr(sepPos + 2)
if (!headers[headerName]) {
// make it an array
headers[headerName] = []
}
headers[headerName].push(headerValue)
}
return headers
},
/**
* Parses the etag response which is in double quotes.
*
* @param {string} etag etag value in double quotes
*
* @return {string} etag without double quotes
*/
_parseEtag: function(etag) {
if (etag.charAt(0) === '"') {
return etag.split('"')[1]
}
return etag
},
/**
* Parse Webdav result
*
* @param {object} response XML object
*
* @return {Array.<FileInfo>} array of file info
*/
_parseFileInfo: function(response) {
let path = decodeURIComponent(response.href)
if (path.substr(0, this._root.length) === this._root) {
path = path.substr(this._root.length)
}
if (path.charAt(path.length - 1) === '/') {
path = path.substr(0, path.length - 1)
}
if (response.propStat.length === 0 || response.propStat[0].status !== 'HTTP/1.1 200 OK') {
return null
}
const props = response.propStat[0].properties
const data = {
id: props[Client.PROPERTY_INTERNAL_FILEID],
path: OC.dirname(path) || '/',
name: OC.basename(path),
mtime: (new Date(props[Client.PROPERTY_GETLASTMODIFIED])).getTime(),
}
const etagProp = props[Client.PROPERTY_GETETAG]
if (!_.isUndefined(etagProp)) {
data.etag = this._parseEtag(etagProp)
}
let sizeProp = props[Client.PROPERTY_GETCONTENTLENGTH]
if (!_.isUndefined(sizeProp)) {
data.size = parseInt(sizeProp, 10)
}
sizeProp = props[Client.PROPERTY_SIZE]
if (!_.isUndefined(sizeProp)) {
data.size = parseInt(sizeProp, 10)
}
const hasPreviewProp = props['{' + Client.NS_NEXTCLOUD + '}has-preview']
if (!_.isUndefined(hasPreviewProp)) {
data.hasPreview = hasPreviewProp === 'true'
} else {
data.hasPreview = true
}
const isEncryptedProp = props['{' + Client.NS_NEXTCLOUD + '}is-encrypted']
if (!_.isUndefined(isEncryptedProp)) {
data.isEncrypted = isEncryptedProp === '1'
} else {
data.isEncrypted = false
}
const isFavouritedProp = props['{' + Client.NS_OWNCLOUD + '}favorite']
if (!_.isUndefined(isFavouritedProp)) {
data.isFavourited = isFavouritedProp === '1'
} else {
data.isFavourited = false
}
const contentType = props[Client.PROPERTY_GETCONTENTTYPE]
if (!_.isUndefined(contentType)) {
data.mimetype = contentType
}
const resType = props[Client.PROPERTY_RESOURCETYPE]
if (!data.mimetype && resType) {
const xmlvalue = resType[0]
if (xmlvalue.namespaceURI === Client.NS_DAV && xmlvalue.nodeName.split(':')[1] === 'collection') {
data.mimetype = 'httpd/unix-directory'
}
}
data.permissions = OC.PERMISSION_NONE
const permissionProp = props[Client.PROPERTY_PERMISSIONS]
if (!_.isUndefined(permissionProp)) {
const permString = permissionProp || ''
data.mountType = null
for (let i = 0; i < permString.length; i++) {
const c = permString.charAt(i)
switch (c) {
// FIXME: twisted permissions
case 'C':
case 'K':
data.permissions |= OC.PERMISSION_CREATE
break
case 'G':
data.permissions |= OC.PERMISSION_READ
break
case 'W':
case 'N':
case 'V':
data.permissions |= OC.PERMISSION_UPDATE
break
case 'D':
data.permissions |= OC.PERMISSION_DELETE
break
case 'R':
data.permissions |= OC.PERMISSION_SHARE
break
case 'M':
if (!data.mountType) {
// TODO: how to identify external-root ?
data.mountType = 'external'
}
break
case 'S':
// TODO: how to identify shared-root ?
data.mountType = 'shared'
break
}
}
}
const sharePermissionsProp = props[Client.PROPERTY_SHARE_PERMISSIONS]
if (!_.isUndefined(sharePermissionsProp)) {
data.sharePermissions = parseInt(sharePermissionsProp)
}
const shareAttributesProp = props[Client.PROPERTY_SHARE_ATTRIBUTES]
if (!_.isUndefined(shareAttributesProp)) {
try {
data.shareAttributes = JSON.parse(shareAttributesProp)
} catch {
logger.warn('Could not parse share attributes returned by server: "' + shareAttributesProp + '"')
data.shareAttributes = []
}
} else {
data.shareAttributes = []
}
const mounTypeProp = props['{' + Client.NS_NEXTCLOUD + '}mount-type']
if (!_.isUndefined(mounTypeProp)) {
data.mountType = mounTypeProp
}
const quotaAvailableBytes = props['{' + Client.NS_DAV + '}quota-available-bytes']
if (!_.isUndefined(quotaAvailableBytes)) {
data.quotaAvailableBytes = quotaAvailableBytes
}
// extend the parsed data using the custom parsers
_.each(this._fileInfoParsers, function(parserFunction) {
_.extend(data, parserFunction(response, data) || {})
})
return new FileInfo(data)
},
/**
* Parse Webdav multistatus
*
* @param {Array} responses
*/
_parseResult: function(responses) {
const self = this
return _.map(responses, function(response) {
return self._parseFileInfo(response)
})
},
/**
* Returns whether the given status code means success
*
* @param {number} status status code
*
* @return true if status code is between 200 and 299 included
*/
_isSuccessStatus: function(status) {
return status >= 200 && status <= 299
},
/**
* Parse the Sabre exception out of the given response, if any
*
* @param {object} response object
* @return {object} array of parsed message and exception (only the first one)
*/
_getSabreException: function(response) {
const result = {}
const xml = response.xhr.responseXML
if (xml === null) {
return result
}
const messages = xml.getElementsByTagNameNS('http://sabredav.org/ns', 'message')
const exceptions = xml.getElementsByTagNameNS('http://sabredav.org/ns', 'exception')
if (messages.length) {
result.message = messages[0].textContent
}
if (exceptions.length) {
result.exception = exceptions[0].textContent
}
return result
},
/**
* Returns the default PROPFIND properties to use during a call.
*
* @return {Array.<object>} array of properties
*/
getPropfindProperties: function() {
if (!this._propfindProperties) {
this._propfindProperties = _.map(Client._PROPFIND_PROPERTIES, function(propDef) {
return '{' + propDef[0] + '}' + propDef[1]
})
}
return this._propfindProperties
},
/**
* Lists the contents of a directory
*
* @param {string} path path to retrieve
* @param {object} [options] options
* @param {boolean} [options.includeParent] set to true to keep
* the parent folder in the result list
* @param {Array} [options.properties] list of Webdav properties to retrieve
*
* @return {Promise} promise
*/
getFolderContents: function(path, options) {
if (!path) {
path = ''
}
options = options || {}
const self = this
const deferred = $.Deferred()
const promise = deferred.promise()
let properties
if (_.isUndefined(options.properties)) {
properties = this.getPropfindProperties()
} else {
properties = options.properties
}
this._client.propFind(
this._buildUrl(path),
properties,
1,
).then(function(result) {
if (self._isSuccessStatus(result.status)) {
const results = self._parseResult(result.body)
if (!options || !options.includeParent) {
// remove root dir, the first entry
results.shift()
}
deferred.resolve(result.status, results)
} else {
result = _.extend(result, self._getSabreException(result))
deferred.reject(result.status, result)
}
})
return promise
},
/**
* Fetches a flat list of files filtered by a given filter criteria.
* (currently system tags and circles are supported)
*
* @param {object} filter filter criteria
* @param {object} [filter.systemTagIds] list of system tag ids to filter by
* @param {boolean} [filter.favorite] set it to filter by favorites
* @param {object} [options] options
* @param {Array} [options.properties] list of Webdav properties to retrieve
*
* @return {Promise} promise
*/
getFilteredFiles: function(filter, options) {
options = options || {}
const self = this
const deferred = $.Deferred()
const promise = deferred.promise()
let properties
if (_.isUndefined(options.properties)) {
properties = this.getPropfindProperties()
} else {
properties = options.properties
}
if (!filter
|| (!filter.systemTagIds && _.isUndefined(filter.favorite) && !filter.circlesIds)) {
throw 'Missing filter argument'
}
// root element with namespaces
let body = '<oc:filter-files '
let namespace
for (namespace in this._client.xmlNamespaces) {
body += ' xmlns:' + this._client.xmlNamespaces[namespace] + '="' + namespace + '"'
}
body += '>\n'
// properties query
body += ' <' + this._client.xmlNamespaces['DAV:'] + ':prop>\n'
_.each(properties, function(prop) {
const property = self._client.parseClarkNotation(prop)
body += ' <' + self._client.xmlNamespaces[property.namespace] + ':' + property.name + ' />\n'
})
body += ' </' + this._client.xmlNamespaces['DAV:'] + ':prop>\n'
// rules block
body += ' <oc:filter-rules>\n'
_.each(filter.systemTagIds, function(systemTagIds) {
body += ' <oc:systemtag>' + escapeHTML(systemTagIds) + '</oc:systemtag>\n'
})
_.each(filter.circlesIds, function(circlesIds) {
body += ' <oc:circle>' + escapeHTML(circlesIds) + '</oc:circle>\n'
})
if (filter.favorite) {
body += ' <oc:favorite>' + (filter.favorite ? '1' : '0') + '</oc:favorite>\n'
}
body += ' </oc:filter-rules>\n'
// end of root
body += '</oc:filter-files>\n'
this._client.request(
'REPORT',
this._buildUrl(),
{},
body,
).then(function(result) {
if (self._isSuccessStatus(result.status)) {
const results = self._parseResult(result.body)
deferred.resolve(result.status, results)
} else {
result = _.extend(result, self._getSabreException(result))
deferred.reject(result.status, result)
}
})
return promise
},
/**
* Returns the file info of a given path.
*
* @param {string} path path
* @param {Array} [options.properties] list of Webdav properties to retrieve
*
* @param options
* @return {Promise} promise
*/
getFileInfo: function(path, options) {
if (!path) {
path = ''
}
options = options || {}
const self = this
const deferred = $.Deferred()
const promise = deferred.promise()
let properties
if (_.isUndefined(options.properties)) {
properties = this.getPropfindProperties()
} else {
properties = options.properties
}
// TODO: headers
this._client.propFind(
this._buildUrl(path),
properties,
0,
).then(function(result) {
if (self._isSuccessStatus(result.status)) {
deferred.resolve(result.status, self._parseResult([result.body])[0])
} else {
result = _.extend(result, self._getSabreException(result))
deferred.reject(result.status, result)
}
})
return promise
},
/**
* Returns the contents of the given file.
*
* @param {string} path path to file
*
* @return {Promise}
*/
getFileContents: function(path) {
if (!path) {
throw 'Missing argument "path"'
}
const self = this
const deferred = $.Deferred()
const promise = deferred.promise()
this._client.request(
'GET',
this._buildUrl(path),
).then(function(result) {
if (self._isSuccessStatus(result.status)) {
deferred.resolve(result.status, result.body)
} else {
result = _.extend(result, self._getSabreException(result))
deferred.reject(result.status, result)
}
})
return promise
},
/**
* Puts the given data into the given file.
*
* @param {string} path path to file
* @param {string} body file body
* @param {object} [options]
* @param {string} [options.contentType] content type
* @param {boolean} [options.overwrite] whether to overwrite an existing file
*
* @return {Promise}
*/
putFileContents: function(path, body, options) {
if (!path) {
throw 'Missing argument "path"'
}
const self = this
const deferred = $.Deferred()
const promise = deferred.promise()
options = options || {}
const headers = {}
let contentType = 'text/plain;charset=utf-8'
if (options.contentType) {
contentType = options.contentType
}
headers['Content-Type'] = contentType
if (_.isUndefined(options.overwrite) || options.overwrite) {
// will trigger 412 precondition failed if a file already exists
headers['If-None-Match'] = '*'
}
this._client.request(
'PUT',
this._buildUrl(path),
headers,
body || '',
).then(function(result) {
if (self._isSuccessStatus(result.status)) {
deferred.resolve(result.status)
} else {
result = _.extend(result, self._getSabreException(result))
deferred.reject(result.status, result)
}
})
return promise
},
_simpleCall: function(method, path, headers) {
if (!path) {
throw 'Missing argument "path"'
}
const self = this
const deferred = $.Deferred()
const promise = deferred.promise()
this._client.request(
method,
this._buildUrl(path),
headers ? headers : {},
).then(function(result) {
if (self._isSuccessStatus(result.status)) {
deferred.resolve(result.status)
} else {
result = _.extend(result, self._getSabreException(result))
deferred.reject(result.status, result)
}
})
return promise
},
/**
* Creates a directory
*
* @param {string} path path to create
*
* @param headers
* @return {Promise}
*/
createDirectory: function(path, headers) {
return this._simpleCall('MKCOL', path, headers)
},
/**
* Deletes a file or directory
*
* @param {string} path path to delete
*
* @return {Promise}
*/
remove: function(path) {
return this._simpleCall('DELETE', path)
},
/**
* Moves path to another path
*
* @param {string} path path to move
* @param {string} destinationPath destination path
* @param {boolean} [allowOverwrite] true to allow overwriting,
* false otherwise
* @param {object} [headers] additional headers
*
* @return {Promise} promise
*/
move: function(path, destinationPath, allowOverwrite, headers) {
if (!path) {
throw 'Missing argument "path"'
}
if (!destinationPath) {
throw 'Missing argument "destinationPath"'
}
const self = this
const deferred = $.Deferred()
const promise = deferred.promise()
headers = _.extend({}, headers, {
Destination: this._buildUrl(destinationPath),
})
if (!allowOverwrite) {
headers.Overwrite = 'F'
}
this._client.request(
'MOVE',
this._buildUrl(path),
headers,
).then(function(result) {
if (self._isSuccessStatus(result.status)) {
deferred.resolve(result.status)
} else {
result = _.extend(result, self._getSabreException(result))
deferred.reject(result.status, result)
}
})
return promise
},
/**
* Copies path to another path
*
* @param {string} path path to copy
* @param {string} destinationPath destination path
* @param {boolean} [allowOverwrite] true to allow overwriting,
* false otherwise
*
* @return {Promise} promise
*/
copy: function(path, destinationPath, allowOverwrite) {
if (!path) {
throw 'Missing argument "path"'
}
if (!destinationPath) {
throw 'Missing argument "destinationPath"'
}
const self = this
const deferred = $.Deferred()
const promise = deferred.promise()
const headers = {
Destination: this._buildUrl(destinationPath),
}
if (!allowOverwrite) {
headers.Overwrite = 'F'
}
this._client.request(
'COPY',
this._buildUrl(path),
headers,
).then(function(response) {
if (self._isSuccessStatus(response.status)) {
deferred.resolve(response.status)
} else {
deferred.reject(response.status)
}
})
return promise
},
/**
* Add a file info parser function
*
* @param {OC.Files.Client~parseFileInfo} parserFunction
*/
addFileInfoParser: function(parserFunction) {
this._fileInfoParsers.push(parserFunction)
},
/**
* Returns the dav.Client instance used internally
*
* @since 11.0.0
* @return {dav.Client}
*/
getClient: function() {
return this._client
},
/**
* Returns the user name
*
* @since 11.0.0
* @return {string} userName
*/
getUserName: function() {
return this._client.userName
},
/**
* Returns the password
*
* @since 11.0.0
* @return {string} password
*/
getPassword: function() {
return this._client.password
},
/**
* Returns the base URL
*
* @since 11.0.0
* @return {string} base URL
*/
getBaseUrl: function() {
return this._client.baseUrl
},
/**
* Returns the host
*
* @since 13.0.0
* @return {string} base URL
*/
getHost: function() {
return this._host
},
}
/**
* File info parser function
*
* This function receives a list of Webdav properties as input and
* should return a hash array of parsed properties, if applicable.
*
* @callback OC.Files.Client~parseFileInfo
* @param {object} XML Webdav properties
* @return {Array} array of parsed property values
*/
if (!OC.Files) {
/**
* @namespace OC.Files
*
* @since 8.2
*/
OC.Files = {}
}
/**
* Returns the default instance of the files client
*
* @return {OC.Files.Client} default client
*
* @since 8.2
*/
OC.Files.getClient = function() {
if (OC.Files._defaultClient) {
return OC.Files._defaultClient
}
const client = new OC.Files.Client({
host: window.location.host,
port: window.location.port,
root: OC.linkToRemoteBase('dav') + '/files/' + getCurrentUser().uid,
useHTTPS: window.location.protocol.startsWith('https'),
})
OC.Files._defaultClient = client
return client
}
OC.Files.Client = Client
})(OC, OC.Files.FileInfo)

View file

@ -1,162 +0,0 @@
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import _ from 'underscore'
(function(OC) {
/**
* @class OC.Files.FileInfo
* @classdesc File information
*
* @param {object} data file data, see attributes for details
*
* @since 8.2
*/
const FileInfo = function(data) {
const self = this
_.each(data, function(value, key) {
if (!_.isFunction(value)) {
self[key] = value
}
})
if (!_.isUndefined(this.id)) {
this.id = parseInt(data.id, 10)
}
// TODO: normalize path
this.path = data.path || ''
if (this.type === 'dir') {
this.mimetype = 'httpd/unix-directory'
} else {
this.mimetype = this.mimetype || 'application/octet-stream'
}
if (!this.type) {
if (this.mimetype === 'httpd/unix-directory') {
this.type = 'dir'
} else {
this.type = 'file'
}
}
}
/**
* @memberof OC.Files
*/
FileInfo.prototype = {
/**
* File id
*
* @type int
*/
id: null,
/**
* File name
*
* @type String
*/
name: null,
/**
* Path leading to the file, without the file name,
* and with a leading slash.
*
* @type String
*/
path: null,
/**
* Mime type
*
* @type String
*/
mimetype: null,
/**
* Icon URL.
*
* Can be used to override the mime type icon.
*
* @type String
*/
icon: null,
/**
* File type. 'file' for files, 'dir' for directories.
*
* @type String
* @deprecated rely on mimetype instead
*/
type: null,
/**
* Permissions.
*
* @see OC#PERMISSION_ALL for permissions
* @type int
*/
permissions: null,
/**
* Modification time
*
* @type int
*/
mtime: null,
/**
* Etag
*
* @type String
*/
etag: null,
/**
* Mount type.
*
* One of null, "external-root", "shared" or "shared-root"
*
* @type string
*/
mountType: null,
/**
* @type boolean
*/
hasPreview: true,
/**
* @type int
*/
sharePermissions: null,
/**
* @type Array
*/
shareAttributes: [],
quotaAvailableBytes: -1,
canDownload: function() {
for (const i in this.shareAttributes) {
const attr = this.shareAttributes[i]
if (attr.scope === 'permissions' && attr.key === 'download') {
return attr.value === true
}
}
return true
},
}
if (!OC.Files) {
OC.Files = {}
}
OC.Files.FileInfo = FileInfo
})(OC)