feat(files): migrate recent view

Signed-off-by: John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com>
This commit is contained in:
John Molakvoæ (skjnldsv) 2023-07-28 14:52:30 +02:00 committed by John Molakvoæ
parent 6ec35e3799
commit 87b1719c88
No known key found for this signature in database
GPG key ID: 60C25B8C072916CF
25 changed files with 495 additions and 248 deletions

View file

@ -20,7 +20,6 @@
"newfilemenu.js",
"operationprogressbar.js",
"recentfilelist.js",
"recentplugin.js",
"semaphore.js",
"sidebarpreviewmanager.js",
"sidebarpreviewtext.js",

View file

@ -1,121 +0,0 @@
/*
* Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com>
*
* This file is licensed under the Affero General Public License version 3
* or later.
*
* See the COPYING-README file.
*
*/
(function (OCA) {
/**
* Registers the recent file list from the files app sidebar.
*
* @namespace OCA.Files.RecentPlugin
*/
OCA.Files.RecentPlugin = {
name: 'Recent',
/**
* @type OCA.Files.RecentFileList
*/
recentFileList: null,
attach: function () {
var self = this;
$('#app-content-recent').on('show.plugin-recent', function (e) {
self.showFileList($(e.target));
});
$('#app-content-recent').on('hide.plugin-recent', function () {
self.hideFileList();
});
},
detach: function () {
if (this.recentFileList) {
this.recentFileList.destroy();
OCA.Files.fileActions.off('setDefault.plugin-recent', this._onActionsUpdated);
OCA.Files.fileActions.off('registerAction.plugin-recent', this._onActionsUpdated);
$('#app-content-recent').off('.plugin-recent');
this.recentFileList = null;
}
},
showFileList: function ($el) {
if (!this.recentFileList) {
this.recentFileList = this._createRecentFileList($el);
}
return this.recentFileList;
},
hideFileList: function () {
if (this.recentFileList) {
this.recentFileList.$fileList.empty();
}
},
/**
* Creates the recent file list.
*
* @param $el container for the file list
* @return {OCA.Files.RecentFileList} file list
*/
_createRecentFileList: function ($el) {
var fileActions = this._createFileActions();
// register recent list for sidebar section
return new OCA.Files.RecentFileList(
$el, {
fileActions: fileActions,
// The file list is created when a "show" event is handled,
// so it should be marked as "shown" like it would have been
// done if handling the event with the file list already
// created.
shown: true
}
);
},
_createFileActions: function () {
// inherit file actions from the files app
var fileActions = new OCA.Files.FileActions();
// note: not merging the legacy actions because legacy apps are not
// compatible with the sharing overview and need to be adapted first
fileActions.registerDefaultActions();
fileActions.merge(OCA.Files.fileActions);
if (!this._globalActionsInitialized) {
// in case actions are registered later
this._onActionsUpdated = _.bind(this._onActionsUpdated, this);
OCA.Files.fileActions.on('setDefault.plugin-recent', this._onActionsUpdated);
OCA.Files.fileActions.on('registerAction.plugin-recent', this._onActionsUpdated);
this._globalActionsInitialized = true;
}
// when the user clicks on a folder, redirect to the corresponding
// folder in the files app instead of opening it directly
fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) {
OCA.Files.App.setActiveView('files', {silent: true});
var path = OC.joinPaths(context.$file.attr('data-path'), filename);
OCA.Files.App.fileList.changeDirectory(path, true, true);
});
fileActions.setDefault('dir', 'Open');
return fileActions;
},
_onActionsUpdated: function (ev) {
if (ev.action) {
this.recentFileList.fileActions.registerAction(ev.action);
} else if (ev.defaultAction) {
this.recentFileList.fileActions.setDefault(
ev.defaultAction.mime,
ev.defaultAction.name
);
}
}
};
})(OCA);
OC.Plugins.register('OCA.Files.App', OCA.Files.RecentPlugin);

View file

@ -163,15 +163,6 @@ class Application extends App implements IBootstrap {
'name' => $l10n->t('All files')
];
});
\OCA\Files\App::getNavigationManager()->add(function () use ($l10n) {
return [
'id' => 'recent',
'appname' => 'files',
'script' => 'recentlist.php',
'order' => 2,
'name' => $l10n->t('Recent')
];
});
}
private function registerHooks(): void {

View file

@ -1,37 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.nl>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author John Molakvoæ <skjnldsv@protonmail.com>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Robin Appelman <robin@icewind.nl>
*
* @license GNU AGPL version 3 or any later version
*
* 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/>.
*
*/
$config = \OC::$server->getConfig();
$userSession = \OC::$server->getUserSession();
$showgridview = $config->getUserValue($userSession->getUser()->getUID(), 'files', 'show_grid', false);
$tmpl = new OCP\Template('files', 'recentlist', '');
// gridview not available for ie
$tmpl->assign('showgridview', $showgridview);
$tmpl->printPage();

View file

@ -0,0 +1,103 @@
/**
* @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.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/>.
*
*/
import { action } from './openInFilesAction'
import { expect } from '@jest/globals'
import { File, Folder, Permission } from '@nextcloud/files'
import { DefaultType, FileAction } from '../../../files/src/services/FileAction'
import type { Navigation } from '../../../files/src/services/Navigation'
const view = {
id: 'files',
name: 'Files',
} as Navigation
const recentView = {
id: 'recent',
name: 'Recent',
} as Navigation
describe('Open in files action conditions tests', () => {
test('Default values', () => {
expect(action).toBeInstanceOf(FileAction)
expect(action.id).toBe('open-in-files-recent')
expect(action.displayName([], recentView)).toBe('Open in Files')
expect(action.iconSvgInline([], recentView)).toBe('')
expect(action.default).toBe(DefaultType.HIDDEN)
expect(action.order).toBe(-1000)
expect(action.inline).toBeUndefined()
})
})
describe('Open in files action enabled tests', () => {
test('Enabled with on valid view', () => {
expect(action.enabled).toBeDefined()
expect(action.enabled!([], recentView)).toBe(true)
})
test('Disabled on wrong view', () => {
expect(action.enabled).toBeDefined()
expect(action.enabled!([], view)).toBe(false)
})
})
describe('Open in files action execute tests', () => {
test('Open in files', async () => {
const goToRouteMock = jest.fn()
window.OCP = { Files: { Router: { goToRoute: goToRouteMock } } }
const file = new File({
id: 1,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/Foo/foobar.txt',
owner: 'admin',
mime: 'text/plain',
root: '/files/admin',
permissions: Permission.ALL,
})
const exec = await action.exec(file, view, '/')
// Silent action
expect(exec).toBe(null)
expect(goToRouteMock).toBeCalledTimes(1)
expect(goToRouteMock).toBeCalledWith(null, { fileid: 1, view: 'files' }, { fileid: 1, dir: '/Foo', openfile: true })
})
test('Open in files with folder', async () => {
const goToRouteMock = jest.fn()
window.OCP = { Files: { Router: { goToRoute: goToRouteMock } } }
const file = new Folder({
id: 1,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/Foo/Bar',
owner: 'admin',
root: '/files/admin',
permissions: Permission.ALL,
})
const exec = await action.exec(file, view, '/')
// Silent action
expect(exec).toBe(null)
expect(goToRouteMock).toBeCalledTimes(1)
expect(goToRouteMock).toBeCalledWith(null, { fileid: 1, view: 'files' }, { fileid: 1, dir: '/Foo/Bar', openfile: true })
})
})

View file

@ -0,0 +1,57 @@
/**
* @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.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/>.
*
*/
import { translate as t } from '@nextcloud/l10n'
import { FileType, type Node } from '@nextcloud/files'
import { registerFileAction, FileAction, DefaultType } from '../../../files/src/services/FileAction'
/**
* TODO: Move away from a redirect and handle
* navigation straight out of the recent view
*/
export const action = new FileAction({
id: 'open-in-files-recent',
displayName: () => t('files', 'Open in Files'),
iconSvgInline: () => '',
enabled: (nodes, view) => view.id === 'recent',
async exec(node: Node) {
let dir = node.dirname
if (node.type === FileType.Folder) {
dir = dir + '/' + node.basename
}
window.OCP.Files.Router.goToRoute(
null, // use default route
{ view: 'files', fileid: node.fileid },
{ dir, fileid: node.fileid, openfile: true },
)
return null
},
// Before openFolderAction
order: -1000,
default: DefaultType.HIDDEN,
})
registerFileAction(action)

View file

@ -159,6 +159,7 @@ import { formatFileSize, Permission } from '@nextcloud/files'
import { Fragment } from 'vue-frag'
import { showError, showSuccess } from '@nextcloud/dialogs'
import { translate } from '@nextcloud/l10n'
import { generateUrl } from '@nextcloud/router'
import { vOnClickOutside } from '@vueuse/components'
import axios from '@nextcloud/axios'
import CancelablePromise from 'cancelable-promise'
@ -367,10 +368,16 @@ export default Vue.extend({
},
previewUrl() {
try {
const url = new URL(window.location.origin + this.source.attributes.previewUrl)
const previewUrl = this.source.attributes.previewUrl
|| generateUrl('/core/preview?fileId={fileid}', {
fileid: this.source.fileid,
})
const url = new URL(window.location.origin + previewUrl)
// Request tiny previews
url.searchParams.set('x', '32')
url.searchParams.set('y', '32')
// Handle cropping
url.searchParams.set('a', this.cropPreviews === true ? '0' : '1')
return url.href

View file

@ -6,6 +6,7 @@ import './actions/downloadAction'
import './actions/editLocallyAction'
import './actions/favoriteAction'
import './actions/openFolderAction'
import './actions/openInFilesAction.js'
import './actions/renameAction'
import './actions/sidebarAction'
import './actions/viewInFolderAction'
@ -18,6 +19,7 @@ import NavigationService from './services/Navigation'
import NavigationView from './views/Navigation.vue'
import processLegacyFilesViews from './legacy/navigationMapper.js'
import registerFavoritesView from './views/favorites'
import registerRecentView from './views/recent'
import registerPreviewServiceWorker from './services/ServiceWorker.js'
import router from './router/router.js'
import RouterService from './services/RouterService'
@ -78,6 +80,7 @@ FilesList.$mount('#app-content-vue')
// Init legacy and new files views
processLegacyFilesViews()
registerFavoritesView()
registerRecentView()
// Register preview service worker
registerPreviewServiceWorker()

View file

@ -0,0 +1,148 @@
/**
* @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.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/>.
*
*/
import { File, Folder, Permission, parseWebdavPermissions } from '@nextcloud/files'
import { generateRemoteUrl } from '@nextcloud/router'
import { getClient, rootPath } from './WebdavClient'
import { getCurrentUser } from '@nextcloud/auth'
import { getDavNameSpaces, getDavProperties } from './DavProperties'
import type { ContentsWithRoot } from './Navigation'
import type { FileStat, ResponseDataDetailed, DAVResultResponseProps } from 'webdav'
const client = getClient(generateRemoteUrl('dav'))
const lastTwoWeeksTimestamp = Math.round((Date.now() / 1000) - (60 * 60 * 24 * 14))
const searchPayload = `<?xml version="1.0" encoding="UTF-8"?>
<d:searchrequest ${getDavNameSpaces()}
xmlns:ns="https://github.com/icewind1991/SearchDAV/ns">
<d:basicsearch>
<d:select>
<d:prop>
${getDavProperties()}
</d:prop>
</d:select>
<d:from>
<d:scope>
<d:href>/files/${getCurrentUser()?.uid}/</d:href>
<d:depth>infinity</d:depth>
</d:scope>
</d:from>
<d:where>
<d:and>
<d:or>
<d:not>
<d:eq>
<d:prop>
<d:getcontenttype/>
</d:prop>
<d:literal>httpd/unix-directory</d:literal>
</d:eq>
</d:not>
<d:eq>
<d:prop>
<oc:size/>
</d:prop>
<d:literal>0</d:literal>
</d:eq>
</d:or>
<d:gt>
<d:prop>
<d:getlastmodified/>
</d:prop>
<d:literal>${lastTwoWeeksTimestamp}</d:literal>
</d:gt>
</d:and>
</d:where>
<d:orderby>
<d:order>
<d:prop>
<d:getlastmodified/>
</d:prop>
<d:descending/>
</d:order>
</d:orderby>
<d:limit>
<d:nresults>100</d:nresults>
<ns:firstresult>0</ns:firstresult>
</d:limit>
</d:basicsearch>
</d:searchrequest>`
interface ResponseProps extends DAVResultResponseProps {
permissions: string,
fileid: number,
size: number,
}
const resultToNode = function(node: FileStat): File | Folder {
const props = node.props as ResponseProps
const permissions = parseWebdavPermissions(props?.permissions)
const owner = getCurrentUser()?.uid as string
const nodeData = {
id: props?.fileid as number || 0,
source: generateRemoteUrl('dav' + node.filename),
mtime: new Date(node.lastmod),
mime: node.mime as string,
size: props?.size as number || 0,
permissions,
owner,
root: rootPath,
attributes: {
...node,
...props,
hasPreview: props?.['has-preview'],
},
}
delete nodeData.attributes.props
return node.type === 'file'
? new File(nodeData)
: new Folder(nodeData)
}
export const getContents = async (path = '/'): Promise<ContentsWithRoot> => {
const contentsResponse = await client.getDirectoryContents(path, {
details: true,
data: searchPayload,
headers: {
// Patched in WebdavClient.ts
method: 'SEARCH',
// Somehow it's needed to get the correct response
'Content-Type': 'application/xml; charset=utf-8',
},
deep: true,
}) as ResponseDataDetailed<FileStat[]>
const contents = contentsResponse.data
return {
folder: new Folder({
id: 0,
source: generateRemoteUrl('dav' + rootPath),
root: rootPath,
owner: getCurrentUser()?.uid || null,
permissions: Permission.READ,
}),
contents: contents.map(resultToNode),
}
}

View file

@ -0,0 +1,47 @@
/**
* @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.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/>.
*
*/
import type NavigationService from '../services/Navigation'
import type { Navigation } from '../services/Navigation'
import { translate as t } from '@nextcloud/l10n'
import HistorySvg from '@mdi/svg/svg/history.svg?raw'
import { getContents } from '../services/Recent'
export default () => {
const Navigation = window.OCP.Files.Navigation as NavigationService
Navigation.register({
id: 'recent',
name: t('files', 'Recent'),
caption: t('files', 'List of recently modified files and folders.'),
emptyTitle: t('files', 'No recently modified files'),
emptyCaption: t('files', 'Files and folders you recently modified will show up here.'),
icon: HistorySvg,
order: 2,
defaultSortKey: 'mtime',
getContents,
} as Navigation)
}

View file

@ -1,41 +0,0 @@
<?php /** @var \OCP\IL10N $l */ ?>
<div class="emptyfilelist emptycontent hidden"></div>
<div class="nofilterresults emptycontent hidden">
<div class="icon-search"></div>
<h2><?php p($l->t('No entries found in this folder')); ?></h2>
<p></p>
</div>
<table class="files-filestable list-container <?php p($_['showgridview'] ? 'view-grid' : '') ?>">
<thead>
<tr>
<th class="hidden column-name">
<div class="column-name-container">
<a class="name sort columntitle" href="#" onclick="event.preventDefault()"
data-sort="name"><span><?php p($l->t('Name')); ?></span></a>
</div>
</th>
<th class="hidden column-size">
<a class="size sort columntitle" href="#" onclick="event.preventDefault()"
data-sort="size"><span><?php p($l->t('Size')); ?></span></a>
</th>
<th class="hidden column-mtime">
<a class="columntitle" href="#" onclick="event.preventDefault()"
data-sort="mtime"><span><?php p($l->t('Modified')); ?></span><span
class="sort-indicator"></span></a>
<span class="selectedActions">
<a href="#" onclick="event.preventDefault()" class="delete-selected">
<span class="icon icon-delete"></span>
<span><?php p($l->t('Delete')) ?></span>
</a>
</span>
</th>
</tr>
</thead>
<tbody class="files-fileList">
</tbody>
<tfoot>
</tfoot>
</table>

View file

@ -185,19 +185,6 @@ class ViewControllerTest extends TestCase {
'expanded' => false,
'unread' => 0,
],
'recent' => [
'id' => 'recent',
'appname' => 'files',
'script' => 'recentlist.php',
'order' => 2,
'name' => \OC::$server->getL10N('files')->t('Recent'),
'active' => false,
'icon' => '',
'type' => 'link',
'classes' => '',
'expanded' => false,
'unread' => 0,
],
'systemtagsfilter' => [
'id' => 'systemtagsfilter',
'appname' => 'systemtags',
@ -233,10 +220,6 @@ class ViewControllerTest extends TestCase {
'id' => 'files',
'content' => null,
],
'recent' => [
'id' => 'recent',
'content' => null,
],
'systemtagsfilter' => [
'id' => 'systemtagsfilter',
'content' => null,

View file

@ -110,12 +110,27 @@ describe('Enter credentials action enabled tests', () => {
},
})
const notAStorage = new Folder({
const missingConfig = new Folder({
id: 1,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/Foo/',
owner: 'admin',
root: '/files/admin',
permissions: Permission.ALL,
attributes: {
scope: 'system',
backend: 'SFTP',
config: {
} as StorageConfig,
},
})
const notAStorage = new File({
id: 1,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/Foo/test.txt',
mime: 'text/plain',
owner: 'admin',
root: '/files/admin',
permissions: Permission.ALL,
})
test('Disabled with on success storage', () => {
@ -138,6 +153,11 @@ describe('Enter credentials action enabled tests', () => {
expect(action.enabled!([globalAuthUserStorage], view)).toBe(true)
})
test('Disabled for missing config', () => {
expect(action.enabled).toBeDefined()
expect(action.enabled!([missingConfig], view)).toBe(false)
})
test('Disabled for normal nodes', () => {
expect(action.enabled).toBeDefined()
expect(action.enabled!([notAStorage], view)).toBe(false)

View file

@ -1,9 +1,9 @@
.files-list__row-status {
display: flex;
width: 44px;
justify-content: center;
align-items: center;
height: 100%;
width: 44px;
justify-content: center;
align-items: center;
height: 100%;
svg {
width: 24px;
@ -33,4 +33,4 @@
&--warning {
background: var(--color-warning);
}
}
}

View file

@ -35,7 +35,7 @@ export const rootPath = `/files/${getCurrentUser()?.uid}`
export type StorageConfig = {
applicableUsers?: string[]
applicableGroups?: string[]
applicableGroups?: string[]
authMechanism: string
backend: string
backendOptions: Record<string, string>

View file

@ -0,0 +1,91 @@
/**
* @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.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/>.
*
*/
import { File, Folder, Permission } from '@nextcloud/files'
import { isNodeExternalStorage } from './externalStorageUtils'
import { expect } from '@jest/globals'
describe('Is node an external storage', () => {
test('A Folder with a backend and a valid scope is an external storage', () => {
const folder = new Folder({
id: 1,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/Foo/',
owner: 'admin',
permissions: Permission.ALL,
attributes: {
scope: 'personal',
backend: 'SFTP',
},
})
expect(isNodeExternalStorage(folder)).toBe(true)
})
test('a File is not a valid storage', () => {
const file = new File({
id: 1,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/foobar.txt',
owner: 'admin',
mime: 'text/plain',
permissions: Permission.ALL,
})
expect(isNodeExternalStorage(file)).toBe(false)
})
test('A Folder without a backend is not a storage', () => {
const folder = new Folder({
id: 1,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/Foo/',
owner: 'admin',
permissions: Permission.ALL,
attributes: {
scope: 'personal',
},
})
expect(isNodeExternalStorage(folder)).toBe(false)
})
test('A Folder without a scope is not a storage', () => {
const folder = new Folder({
id: 1,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/Foo/',
owner: 'admin',
permissions: Permission.ALL,
attributes: {
backend: 'SFTP',
},
})
expect(isNodeExternalStorage(folder)).toBe(false)
})
test('A Folder with an invalid scope is not a storage', () => {
const folder = new Folder({
id: 1,
source: 'https://cloud.domain.com/remote.php/dav/files/admin/Foo/',
owner: 'admin',
permissions: Permission.ALL,
attributes: {
scope: 'null',
backend: 'SFTP',
},
})
expect(isNodeExternalStorage(folder)).toBe(false)
})
})

View file

@ -313,7 +313,6 @@ describe('SharingService share to Node mapping', () => {
expect(file.root).toBe('/files/test')
expect(file.attributes).toBeInstanceOf(Object)
expect(file.attributes['has-preview']).toBe(true)
expect(file.attributes.previewUrl).toBe('/index.php/core/preview?fileId=530936&x=32&y=32&forceIcon=0')
expect(file.attributes.favorite).toBe(0)
})

View file

@ -53,7 +53,6 @@ const ocsEntryToNode = function(ocsEntry: any): Folder | File | null {
const Node = isFolder ? Folder : File
const fileid = ocsEntry.file_source
const previewUrl = hasPreview ? generateUrl('/core/preview?fileId={fileid}&x=32&y=32&forceIcon=0', { fileid }) : undefined
// Generate path and strip double slashes
const path = ocsEntry?.path || ocsEntry.file_target
@ -76,7 +75,6 @@ const ocsEntryToNode = function(ocsEntry: any): Folder | File | null {
root: rootPath,
attributes: {
...ocsEntry,
previewUrl,
'has-preview': hasPreview,
favorite: ocsEntry?.tags?.includes(window.OC.TAG_FAVORITE) ? 1 : 0,
},

View file

@ -28,7 +28,7 @@ import AccountGroupSvg from '@mdi/svg/svg/account-group.svg?raw'
import AccountSvg from '@mdi/svg/svg/account.svg?raw'
import DeleteSvg from '@mdi/svg/svg/delete.svg?raw'
import LinkSvg from '@mdi/svg/svg/link.svg?raw'
import AccouontPlusSvg from '@mdi/svg/svg/account-plus.svg?raw'
import AccountPlusSvg from '@mdi/svg/svg/account-plus.svg?raw'
import { getContents } from '../services/SharingService'
@ -49,7 +49,7 @@ export default () => {
emptyTitle: t('files_sharing', 'No shares'),
emptyCaption: t('files_sharing', 'Files and folders you shared or have been shared with you will show up here'),
icon: AccouontPlusSvg,
icon: AccountPlusSvg,
order: 20,
columns: [],

4
dist/files-main.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long