Skip to content

Commit

Permalink
Merge pull request #50680 from nextcloud/fix/files-public-share
Browse files Browse the repository at this point in the history
fix(files): only send config update requests if user is logged in
  • Loading branch information
susnux authored Feb 8, 2025
2 parents beca0a1 + 48bf91a commit 50536e6
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 133 deletions.
3 changes: 2 additions & 1 deletion apps/files/src/eventbus.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import type { IFileListFilter, Node } from '@nextcloud/files'
declare module '@nextcloud/event-bus' {
export interface NextcloudEvents {
// mapping of 'event name' => 'event type'
'files:config:updated': { key: string, value: unknown }
'files:config:updated': { key: string, value: boolean }
'files:view-config:updated': { key: string, value: string|number|boolean, view: string }

'files:favorites:removed': Node
'files:favorites:added': Node
Expand Down
78 changes: 36 additions & 42 deletions apps/files/src/store/userconfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,61 +2,55 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { UserConfig, UserConfigStore } from '../types'
import { defineStore } from 'pinia'
import type { UserConfig } from '../types'
import { getCurrentUser } from '@nextcloud/auth'
import { emit, subscribe } from '@nextcloud/event-bus'
import { generateUrl } from '@nextcloud/router'
import { loadState } from '@nextcloud/initial-state'
import { generateUrl } from '@nextcloud/router'
import { defineStore } from 'pinia'
import { ref, set } from 'vue'
import axios from '@nextcloud/axios'
import Vue from 'vue'

const userConfig = loadState<UserConfig>('files', 'config', {
const initialUserConfig = loadState<UserConfig>('files', 'config', {
show_hidden: false,
crop_image_previews: true,
sort_favorites_first: true,
sort_folders_first: true,
grid_view: false,
})

export const useUserConfigStore = function(...args) {
const store = defineStore('userconfig', {
state: () => ({
userConfig,
} as UserConfigStore),
export const useUserConfigStore = defineStore('userconfig', () => {
const userConfig = ref<UserConfig>({ ...initialUserConfig })

actions: {
/**
* Update the user config local store
* @param key
* @param value
*/
onUpdate(key: string, value: boolean) {
Vue.set(this.userConfig, key, value)
},
/**
* Update the user config local store
* @param key The config key
* @param value The new value
*/
function onUpdate(key: string, value: boolean): void {
set(userConfig.value, key, value)
}

/**
* Update the user config local store AND on server side
* @param key
* @param value
*/
async update(key: string, value: boolean) {
await axios.put(generateUrl('/apps/files/api/v1/config/' + key), {
value,
})
emit('files:config:updated', { key, value })
},
},
})
/**
* Update the user config local store AND on server side
* @param key The config key
* @param value The new value
*/
async function update(key: string, value: boolean): Promise<void> {
// only update if a user is logged in (not the case for public shares)
if (getCurrentUser() !== null) {
await axios.put(generateUrl('/apps/files/api/v1/config/{key}', { key }), {
value,
})
}
emit('files:config:updated', { key, value })
}

const userConfigStore = store(...args)
// Register the event listener
subscribe('files:config:updated', ({ key, value }) => onUpdate(key, value))

// Make sure we only register the listeners once
if (!userConfigStore._initialized) {
subscribe('files:config:updated', function({ key, value }: { key: string, value: boolean }) {
userConfigStore.onUpdate(key, value)
})
userConfigStore._initialized = true
return {
userConfig,
update,
}

return userConfigStore
}
})
150 changes: 75 additions & 75 deletions apps/files/src/store/viewConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,95 +2,95 @@
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { defineStore } from 'pinia'
import type { ViewConfigs, ViewId, ViewConfig } from '../types'

import { getCurrentUser } from '@nextcloud/auth'
import { emit, subscribe } from '@nextcloud/event-bus'
import { generateUrl } from '@nextcloud/router'
import { loadState } from '@nextcloud/initial-state'
import { generateUrl } from '@nextcloud/router'
import { defineStore } from 'pinia'
import { ref, set } from 'vue'
import axios from '@nextcloud/axios'
import Vue from 'vue'

import type { ViewConfigs, ViewConfigStore, ViewId, ViewConfig } from '../types'
const initialViewConfig = loadState('files', 'viewConfigs', {}) as ViewConfigs

const viewConfig = loadState('files', 'viewConfigs', {}) as ViewConfigs
export const useViewConfigStore = defineStore('viewconfig', () => {

export const useViewConfigStore = function(...args) {
const store = defineStore('viewconfig', {
state: () => ({
viewConfig,
} as ViewConfigStore),
const viewConfigs = ref({ ...initialViewConfig })

getters: {
getConfig: (state) => (view: ViewId): ViewConfig => state.viewConfig[view] || {},
/**
* Get the config for a specific view
* @param viewid Id of the view to fet the config for
*/
function getConfig(viewid: ViewId): ViewConfig {
return viewConfigs.value[viewid] || {}
}

getConfigs: (state) => (): ViewConfigs => state.viewConfig,
},
/**
* Update the view config local store
* @param viewId The id of the view to update
* @param key The config key to update
* @param value The new value
*/
function onUpdate(viewId: ViewId, key: string, value: string | number | boolean): void {
if (!(viewId in viewConfigs.value)) {
set(viewConfigs.value, viewId, {})
}
set(viewConfigs.value[viewId], key, value)
}

actions: {
/**
* Update the view config local store
* @param view
* @param key
* @param value
*/
onUpdate(view: ViewId, key: string, value: string | number | boolean) {
if (!this.viewConfig[view]) {
Vue.set(this.viewConfig, view, {})
}
Vue.set(this.viewConfig[view], key, value)
},
/**
* Update the view config local store AND on server side
* @param view Id of the view to update
* @param key Config key to update
* @param value New value
*/
async function update(view: ViewId, key: string, value: string | number | boolean): Promise<void> {
if (getCurrentUser() !== null) {
await axios.put(generateUrl('/apps/files/api/v1/views'), {
value,
view,
key,
})
}

/**
* Update the view config local store AND on server side
* @param view
* @param key
* @param value
*/
async update(view: ViewId, key: string, value: string | number | boolean) {
axios.put(generateUrl('/apps/files/api/v1/views'), {
value,
view,
key,
})
emit('files:view-config:updated', { view, key, value })
}

emit('files:viewconfig:updated', { view, key, value })
},
/**
* Set the sorting key AND sort by ASC
* The key param must be a valid key of a File object
* If not found, will be searched within the File attributes
* @param key Key to sort by
* @param view View to set the sorting key for
*/
function setSortingBy(key = 'basename', view = 'files'): void {
// Save new config
update(view, 'sorting_mode', key)
update(view, 'sorting_direction', 'asc')
}

/**
* Set the sorting key AND sort by ASC
* The key param must be a valid key of a File object
* If not found, will be searched within the File attributes
* @param key Key to sort by
* @param view View to set the sorting key for
*/
setSortingBy(key = 'basename', view = 'files') {
// Save new config
this.update(view, 'sorting_mode', key)
this.update(view, 'sorting_direction', 'asc')
},
/**
* Toggle the sorting direction
* @param viewId id of the view to set the sorting order for
*/
function toggleSortingDirection(viewId = 'files'): void {
const config = viewConfigs.value[viewId] || { sorting_direction: 'asc' }
const newDirection = config.sorting_direction === 'asc' ? 'desc' : 'asc'

/**
* Toggle the sorting direction
* @param view view to set the sorting order for
*/
toggleSortingDirection(view = 'files') {
const config = this.getConfig(view) || { sorting_direction: 'asc' }
const newDirection = config.sorting_direction === 'asc' ? 'desc' : 'asc'
// Save new config
update(viewId, 'sorting_direction', newDirection)
}

// Save new config
this.update(view, 'sorting_direction', newDirection)
},
},
})
// Initialize event listener
subscribe('files:view-config:updated', ({ view, key, value }) => onUpdate(view, key, value))

const viewConfigStore = store(...args)
return {
viewConfigs,

// Make sure we only register the listeners once
if (!viewConfigStore._initialized) {
subscribe('files:viewconfig:updated', function({ view, key, value }: { view: ViewId, key: string, value: boolean }) {
viewConfigStore.onUpdate(view, key, value)
})
viewConfigStore._initialized = true
getConfig,
setSortingBy,
toggleSortingDirection,
update,
}

return viewConfigStore
}
})
8 changes: 7 additions & 1 deletion apps/files/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,13 @@ export interface PathOptions {

// User config store
export interface UserConfig {
[key: string]: boolean
[key: string]: boolean|undefined

show_hidden: boolean
crop_image_previews: boolean
sort_favorites_first: boolean
sort_folders_first: boolean
grid_view: boolean
}
export interface UserConfigStore {
userConfig: UserConfig
Expand Down
14 changes: 6 additions & 8 deletions apps/files/src/views/Navigation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,12 @@ export default defineComponent({

methods: {
async loadExpandedViews() {
const viewConfigs = this.viewConfigStore.getConfigs()
const viewsToLoad: View[] = (Object.entries(viewConfigs) as Array<[string, ViewConfig]>)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.filter(([viewId, config]) => config.expanded === true)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.map(([viewId, config]) => this.views.find(view => view.id === viewId))
.filter(Boolean) // Only registered views
.filter(view => view.loadChildViews && !view.loaded)
const viewsToLoad: View[] = (Object.entries(this.viewConfigStore.viewConfigs) as Array<[string, ViewConfig]>)
.filter(([, config]) => config.expanded === true)
.map(([viewId]) => this.views.find(view => view.id === viewId))
// eslint-disable-next-line no-use-before-define
.filter(Boolean as unknown as ((u: unknown) => u is View))
.filter((view) => view.loadChildViews && !view.loaded)
for (const view of viewsToLoad) {
await view.loadChildViews(view)
}
Expand Down
4 changes: 2 additions & 2 deletions dist/files-init.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/files-init.js.map

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions dist/files-main.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/files-main.js.map

Large diffs are not rendered by default.

0 comments on commit 50536e6

Please sign in to comment.