aboutsummaryrefslogtreecommitdiffstats
path: root/apps/files/src
diff options
context:
space:
mode:
authorJohn Molakvoæ <skjnldsv@protonmail.com>2023-08-09 12:31:54 +0200
committerJohn Molakvoæ <skjnldsv@protonmail.com>2023-08-17 18:56:37 +0200
commit998b3a2581fb873b03bcf4dc02eafb19390b3cd6 (patch)
tree9c90078211fa2af1e3f570540d38bec47a634bae /apps/files/src
parenta820e3d036741ad1194361eca11bc1cbcdda0a47 (diff)
downloadnextcloud-server-998b3a2581fb873b03bcf4dc02eafb19390b3cd6.tar.gz
nextcloud-server-998b3a2581fb873b03bcf4dc02eafb19390b3cd6.zip
feat(f2v): migrate Files
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
Diffstat (limited to 'apps/files/src')
-rw-r--r--apps/files/src/components/FileEntry.vue13
-rw-r--r--apps/files/src/components/FilesListVirtual.vue14
-rw-r--r--apps/files/src/main.ts2
-rw-r--r--apps/files/src/services/Favorites.ts4
-rw-r--r--apps/files/src/services/Files.ts102
-rw-r--r--apps/files/src/services/Recent.ts4
-rw-r--r--apps/files/src/views/files.ts42
7 files changed, 177 insertions, 4 deletions
diff --git a/apps/files/src/components/FileEntry.vue b/apps/files/src/components/FileEntry.vue
index 775c868b18e..c540cc4e824 100644
--- a/apps/files/src/components/FileEntry.vue
+++ b/apps/files/src/components/FileEntry.vue
@@ -22,6 +22,8 @@
<template>
<Fragment>
+ <span v-if="source.attributes.failed" class="files-list__row--failed" />
+
<td class="files-list__row-checkbox">
<NcCheckboxRadioSwitch v-if="active"
:aria-label="t('files', 'Select the row for {displayName}', { displayName })"
@@ -342,6 +344,13 @@ export default Vue.extend({
},
linkTo() {
+ if (this.source.attributes.failed) {
+ return {
+ title: this.t('files', 'This node is unavailable'),
+ is: 'span',
+ }
+ }
+
if (this.enabledDefaultActions.length > 0) {
const action = this.enabledDefaultActions[0]
const displayName = action.displayName([this.source], this.currentView)
@@ -404,6 +413,10 @@ export default Vue.extend({
// Sorted actions that are enabled for this node
enabledActions() {
+ if (this.source.attributes.failed) {
+ return []
+ }
+
return actions
.filter(action => !action.enabled || action.enabled([this.source], this.currentView))
.sort((a, b) => (a.order || 0) - (b.order || 0))
diff --git a/apps/files/src/components/FilesListVirtual.vue b/apps/files/src/components/FilesListVirtual.vue
index 0488e8ef190..014f0a89f00 100644
--- a/apps/files/src/components/FilesListVirtual.vue
+++ b/apps/files/src/components/FilesListVirtual.vue
@@ -197,6 +197,7 @@ export default Vue.extend({
align-items: center;
width: 100%;
border-bottom: 1px solid var(--color-border);
+ user-select: none;
}
td, th {
@@ -221,8 +222,21 @@ export default Vue.extend({
}
}
+ .files-list__row--failed {
+ position: absolute;
+ display: block;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ opacity: .1;
+ z-index: -1;
+ background: var(--color-error);
+ }
+
.files-list__row-checkbox {
justify-content: center;
+
.checkbox-radio-switch {
display: flex;
justify-content: center;
diff --git a/apps/files/src/main.ts b/apps/files/src/main.ts
index 9317f437368..2fbae13693a 100644
--- a/apps/files/src/main.ts
+++ b/apps/files/src/main.ts
@@ -20,6 +20,7 @@ import NavigationView from './views/Navigation.vue'
import processLegacyFilesViews from './legacy/navigationMapper.js'
import registerFavoritesView from './views/favorites'
import registerRecentView from './views/recent'
+import registerFilesView from './views/files'
import registerPreviewServiceWorker from './services/ServiceWorker.js'
import router from './router/router.js'
import RouterService from './services/RouterService'
@@ -80,6 +81,7 @@ FilesList.$mount('#app-content-vue')
// Init legacy and new files views
processLegacyFilesViews()
registerFavoritesView()
+registerFilesView()
registerRecentView()
// Register preview service worker
diff --git a/apps/files/src/services/Favorites.ts b/apps/files/src/services/Favorites.ts
index 0e5411f08d3..2328a9e8af5 100644
--- a/apps/files/src/services/Favorites.ts
+++ b/apps/files/src/services/Favorites.ts
@@ -19,7 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-import { File, Folder, parseWebdavPermissions } from '@nextcloud/files'
+import { File, Folder, davParsePermissions } from '@nextcloud/files'
import { generateRemoteUrl } from '@nextcloud/router'
import { getClient, rootPath } from './WebdavClient'
import { getCurrentUser } from '@nextcloud/auth'
@@ -47,7 +47,7 @@ interface ResponseProps extends DAVResultResponseProps {
const resultToNode = function(node: FileStat): File | Folder {
const props = node.props as ResponseProps
- const permissions = parseWebdavPermissions(props?.permissions)
+ const permissions = davParsePermissions(props?.permissions)
const owner = getCurrentUser()?.uid as string
const nodeData = {
diff --git a/apps/files/src/services/Files.ts b/apps/files/src/services/Files.ts
new file mode 100644
index 00000000000..b9d6f65a0b9
--- /dev/null
+++ b/apps/files/src/services/Files.ts
@@ -0,0 +1,102 @@
+/**
+ * @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 { ContentsWithRoot } from './Navigation'
+import type { FileStat, ResponseDataDetailed, DAVResultResponseProps } from 'webdav'
+
+import { File, Folder, davParsePermissions } from '@nextcloud/files'
+import { generateRemoteUrl } from '@nextcloud/router'
+import { getCurrentUser } from '@nextcloud/auth'
+
+import { getClient, rootPath } from './WebdavClient'
+import { getDefaultPropfind } from './DavProperties'
+import { hashCode } from '../utils/hashUtils'
+import logger from '../logger'
+
+const client = getClient()
+
+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 = davParsePermissions(props?.permissions)
+ const owner = getCurrentUser()?.uid as string
+
+ const source = generateRemoteUrl('dav' + rootPath + node.filename)
+ const id = props?.fileid < 0
+ ? hashCode(source)
+ : props?.fileid as number || 0
+
+ const nodeData = {
+ id,
+ source,
+ 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'],
+ failed: props?.fileid < 0,
+ },
+ }
+
+ delete nodeData.attributes.props
+
+ return node.type === 'file'
+ ? new File(nodeData)
+ : new Folder(nodeData)
+}
+
+export const getContents = async (path = '/'): Promise<ContentsWithRoot> => {
+ const propfindPayload = getDefaultPropfind()
+
+ const contentsResponse = await client.getDirectoryContents(path, {
+ details: true,
+ data: propfindPayload,
+ includeSelf: true,
+ }) as ResponseDataDetailed<FileStat[]>
+
+ const root = contentsResponse.data[0]
+ const contents = contentsResponse.data.slice(1)
+ if (root.filename !== path) {
+ throw new Error('Root node does not match requested path')
+ }
+
+ return {
+ folder: resultToNode(root) as Folder,
+ contents: contents.map(result => {
+ try {
+ return resultToNode(result)
+ } catch (error) {
+ logger.error(`Invalid node detected '${result.basename}'`, { error })
+ return null
+ }
+ }).filter(Boolean) as File[],
+ }
+}
diff --git a/apps/files/src/services/Recent.ts b/apps/files/src/services/Recent.ts
index d6468d6a60e..75044fb4579 100644
--- a/apps/files/src/services/Recent.ts
+++ b/apps/files/src/services/Recent.ts
@@ -19,7 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
-import { File, Folder, Permission, parseWebdavPermissions } from '@nextcloud/files'
+import { File, Folder, Permission, davParsePermissions } from '@nextcloud/files'
import { generateRemoteUrl } from '@nextcloud/router'
import { getClient, rootPath } from './WebdavClient'
import { getCurrentUser } from '@nextcloud/auth'
@@ -94,7 +94,7 @@ interface ResponseProps extends DAVResultResponseProps {
const resultToNode = function(node: FileStat): File | Folder {
const props = node.props as ResponseProps
- const permissions = parseWebdavPermissions(props?.permissions)
+ const permissions = davParsePermissions(props?.permissions)
const owner = getCurrentUser()?.uid as string
const nodeData = {
diff --git a/apps/files/src/views/files.ts b/apps/files/src/views/files.ts
new file mode 100644
index 00000000000..1a174f54e42
--- /dev/null
+++ b/apps/files/src/views/files.ts
@@ -0,0 +1,42 @@
+/**
+ * @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 FolderSvg from '@mdi/svg/svg/folder.svg?raw'
+
+import { getContents } from '../services/Files'
+
+export default () => {
+ const Navigation = window.OCP.Files.Navigation as NavigationService
+ Navigation.register({
+ id: 'files',
+ name: t('files', 'All files'),
+ caption: t('files', 'List of your files and folders.'),
+
+ icon: FolderSvg,
+ order: 0,
+
+ getContents,
+ } as Navigation)
+}