Ensure view names are properly encoded to handle certain special characters (#9145)
This commit is contained in:
parent
f59de7841d
commit
911f37572a
|
@ -10,6 +10,7 @@
|
||||||
$: views = $tables.list.flatMap(table => Object.keys(table.views || {}))
|
$: views = $tables.list.flatMap(table => Object.keys(table.views || {}))
|
||||||
|
|
||||||
const saveView = async () => {
|
const saveView = async () => {
|
||||||
|
name = name?.trim()
|
||||||
if (views.includes(name)) {
|
if (views.includes(name)) {
|
||||||
notifications.error(`View exists with name ${name}`)
|
notifications.error(`View exists with name ${name}`)
|
||||||
return
|
return
|
||||||
|
@ -21,7 +22,7 @@
|
||||||
field,
|
field,
|
||||||
})
|
})
|
||||||
notifications.success(`View ${name} created`)
|
notifications.success(`View ${name} created`)
|
||||||
$goto(`../../view/${name}`)
|
$goto(`../../view/${encodeURIComponent(name)}`)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifications.error("Error creating view")
|
notifications.error("Error creating view")
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,9 +36,8 @@
|
||||||
indentLevel={2}
|
indentLevel={2}
|
||||||
icon="Remove"
|
icon="Remove"
|
||||||
text={viewName}
|
text={viewName}
|
||||||
selected={$isActive("./view/:viewName") &&
|
selected={$isActive("./view") && $views.selected?.name === viewName}
|
||||||
$views.selected?.name === viewName}
|
on:click={() => $goto(`./view/${encodeURIComponent(viewName)}`)}
|
||||||
on:click={() => $goto(`./view/${viewName}`)}
|
|
||||||
>
|
>
|
||||||
<EditViewPopover
|
<EditViewPopover
|
||||||
view={{ name: viewName, ...table.views[viewName] }}
|
view={{ name: viewName, ...table.views[viewName] }}
|
||||||
|
|
|
@ -33,7 +33,8 @@
|
||||||
|
|
||||||
async function deleteView() {
|
async function deleteView() {
|
||||||
try {
|
try {
|
||||||
const isSelected = $params.viewName === $views.selectedViewName
|
const isSelected =
|
||||||
|
decodeURIComponent($params.viewName) === $views.selectedViewName
|
||||||
const name = view.name
|
const name = view.name
|
||||||
const id = view.tableId
|
const id = view.tableId
|
||||||
await views.delete(name)
|
await views.delete(name)
|
||||||
|
|
|
@ -12,6 +12,7 @@ export const syncURLToState = options => {
|
||||||
store,
|
store,
|
||||||
routify,
|
routify,
|
||||||
beforeNavigate,
|
beforeNavigate,
|
||||||
|
decode,
|
||||||
} = options || {}
|
} = options || {}
|
||||||
if (
|
if (
|
||||||
!urlParam ||
|
!urlParam ||
|
||||||
|
@ -29,11 +30,23 @@ export const syncURLToState = options => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decodes encoded URL params if required
|
||||||
|
const decodeParams = urlParams => {
|
||||||
|
if (!decode) {
|
||||||
|
return urlParams
|
||||||
|
}
|
||||||
|
let decoded = {}
|
||||||
|
Object.keys(urlParams || {}).forEach(key => {
|
||||||
|
decoded[key] = decode(urlParams[key])
|
||||||
|
})
|
||||||
|
return decoded
|
||||||
|
}
|
||||||
|
|
||||||
// We can't dynamically fetch the value of stateful routify stores so we need
|
// We can't dynamically fetch the value of stateful routify stores so we need
|
||||||
// to just subscribe and cache the latest versions.
|
// to just subscribe and cache the latest versions.
|
||||||
// We can grab their initial values as this is during component
|
// We can grab their initial values as this is during component
|
||||||
// initialisation.
|
// initialisation.
|
||||||
let cachedParams = get(routify.params)
|
let cachedParams = decodeParams(get(routify.params))
|
||||||
let cachedGoto = get(routify.goto)
|
let cachedGoto = get(routify.goto)
|
||||||
let cachedRedirect = get(routify.redirect)
|
let cachedRedirect = get(routify.redirect)
|
||||||
let cachedPage = get(routify.page)
|
let cachedPage = get(routify.page)
|
||||||
|
@ -77,7 +90,7 @@ export const syncURLToState = options => {
|
||||||
// Check if new value is valid
|
// Check if new value is valid
|
||||||
if (validate && fallbackUrl) {
|
if (validate && fallbackUrl) {
|
||||||
if (!validate(urlValue)) {
|
if (!validate(urlValue)) {
|
||||||
log("Invalid URL param!")
|
log("Invalid URL param!", urlValue)
|
||||||
redirectUrl(fallbackUrl)
|
redirectUrl(fallbackUrl)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -109,7 +122,7 @@ export const syncURLToState = options => {
|
||||||
log(`url.${urlParam} (${urlValue}) <= state.${stateKey} (${stateValue})`)
|
log(`url.${urlParam} (${urlValue}) <= state.${stateKey} (${stateValue})`)
|
||||||
if (validate && fallbackUrl) {
|
if (validate && fallbackUrl) {
|
||||||
if (!validate(stateValue)) {
|
if (!validate(stateValue)) {
|
||||||
log("Invalid state param!")
|
log("Invalid state param!", stateValue)
|
||||||
redirectUrl(fallbackUrl)
|
redirectUrl(fallbackUrl)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -137,6 +150,7 @@ export const syncURLToState = options => {
|
||||||
|
|
||||||
// Subscribe to URL changes and cache them
|
// Subscribe to URL changes and cache them
|
||||||
const unsubscribeParams = routify.params.subscribe($urlParams => {
|
const unsubscribeParams = routify.params.subscribe($urlParams => {
|
||||||
|
$urlParams = decodeParams($urlParams)
|
||||||
cachedParams = $urlParams
|
cachedParams = $urlParams
|
||||||
mapUrlToState($urlParams)
|
mapUrlToState($urlParams)
|
||||||
})
|
})
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
fallbackUrl: "../",
|
fallbackUrl: "../",
|
||||||
store: views,
|
store: views,
|
||||||
routify,
|
routify,
|
||||||
|
decode: decodeURIComponent,
|
||||||
})
|
})
|
||||||
|
|
||||||
onDestroy(stopSyncing)
|
onDestroy(stopSyncing)
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
const { list, selected } = $views
|
const { list, selected } = $views
|
||||||
if (selected) {
|
if (selected) {
|
||||||
$redirect(`./${selected?.name}`)
|
$redirect(`./${encodeURIComponent(selected?.name)}`)
|
||||||
} else if (list?.length) {
|
} else if (list?.length) {
|
||||||
$redirect(`./${list[0].name}`)
|
$redirect(`./${encodeURIComponent(list[0].name)}`)
|
||||||
} else {
|
} else {
|
||||||
$redirect("../")
|
$redirect("../")
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,8 @@ export const buildViewEndpoints = API => ({
|
||||||
params.set("group", groupBy)
|
params.set("group", groupBy)
|
||||||
}
|
}
|
||||||
const QUERY_VIEW_URL = field
|
const QUERY_VIEW_URL = field
|
||||||
? `/api/views/${name}?${params}`
|
? `/api/views/${encodeURIComponent(name)}?${params}`
|
||||||
: `/api/views/${name}`
|
: `/api/views/${encodeURIComponent(name)}`
|
||||||
return await API.get({ url: QUERY_VIEW_URL })
|
return await API.get({ url: QUERY_VIEW_URL })
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ export const buildViewEndpoints = API => ({
|
||||||
*/
|
*/
|
||||||
deleteView: async viewName => {
|
deleteView: async viewName => {
|
||||||
return await API.delete({
|
return await API.delete({
|
||||||
url: `/api/views/${viewName}`,
|
url: `/api/views/${encodeURIComponent(viewName)}`,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -187,7 +187,7 @@ export async function save(ctx: UserCtx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchView(ctx: Ctx) {
|
export async function fetchView(ctx: Ctx) {
|
||||||
const viewName = ctx.params.viewName
|
const viewName = decodeURIComponent(ctx.params.viewName)
|
||||||
|
|
||||||
// if this is a table view being looked for just transfer to that
|
// if this is a table view being looked for just transfer to that
|
||||||
if (viewName.startsWith(DocumentType.TABLE)) {
|
if (viewName.startsWith(DocumentType.TABLE)) {
|
||||||
|
|
|
@ -113,7 +113,7 @@ async function handleViewEvents(existingView: View, newView: View) {
|
||||||
|
|
||||||
export async function destroy(ctx: BBContext) {
|
export async function destroy(ctx: BBContext) {
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const viewName = decodeURI(ctx.params.viewName)
|
const viewName = decodeURIComponent(ctx.params.viewName)
|
||||||
const view = await deleteView(viewName)
|
const view = await deleteView(viewName)
|
||||||
const table = await db.get(view.meta.tableId)
|
const table = await db.get(view.meta.tableId)
|
||||||
delete table.views[viewName]
|
delete table.views[viewName]
|
||||||
|
@ -124,7 +124,7 @@ export async function destroy(ctx: BBContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function exportView(ctx: BBContext) {
|
export async function exportView(ctx: BBContext) {
|
||||||
const viewName = decodeURI(ctx.query.view as string)
|
const viewName = decodeURIComponent(ctx.query.view as string)
|
||||||
const view = await getView(viewName)
|
const view = await getView(viewName)
|
||||||
|
|
||||||
const format = ctx.query.format as string
|
const format = ctx.query.format as string
|
||||||
|
|
Loading…
Reference in New Issue