1
0
mirror of https://gitlab.com/bramw/baserow.git synced 2024-11-21 23:37:55 +00:00
bramw_baserow/web-frontend/modules/builder/utils/urlResolution.js
2024-10-17 15:49:51 +00:00

107 lines
3.3 KiB
JavaScript

import { compile } from 'path-to-regexp'
import {
ALLOWED_LINK_PROTOCOLS,
PAGE_PARAM_TYPE_VALIDATION_FUNCTIONS,
} from '@baserow/modules/builder/enums'
import { ensureString } from '@baserow/modules/core/utils/validator'
/**
* Responsible for generating the data necessary to resolve an application builder
* element URL. Used by LinkElement and LinkField to generate URLs, based on the
* designer's navigation type / page / url choices.
*
* @param {Object} element The element's properties we'll use for generating the URL.
* @param {Object} builder A builder application.
* @param {Array} pages A list of pages of this application.
* @param {Function} resolveFormula A resolveFormula function we'll use if the
* element has page parameters which need to be resolved.
* @param {String} editorMode A builder application's editor mode.
* @returns {String} A resolved URL.
*/
export default function resolveElementUrl(
element,
builder,
pages,
resolveFormula,
editorMode
) {
let resolvedUrl = ''
if (element.navigation_type === 'page') {
if (!isNaN(element.navigate_to_page_id)) {
const page = pages.find(({ id }) => id === element.navigate_to_page_id)
// The builder page list might be empty or the page has been deleted
if (!page) {
return resolvedUrl
}
const paramTypeMap = Object.fromEntries(
page.path_params.map(({ name, type }) => [name, type])
)
const toPath = compile(page.path, { encode: encodeURIComponent })
const pageParams = Object.fromEntries(
element.page_parameters.map(({ name, value }) => [
name,
PAGE_PARAM_TYPE_VALIDATION_FUNCTIONS[paramTypeMap[name]](
resolveFormula(value)
),
])
)
resolvedUrl = toPath(pageParams)
}
} else {
resolvedUrl = ensureString(resolveFormula(element.navigate_to_url))
}
resolvedUrl = prefixInternalResolvedUrl(
resolvedUrl,
builder,
element.navigation_type,
editorMode
)
// If the protocol is a supported one, return early.
const protocolRegex = /^[A-Za-z]+:/
if (protocolRegex.test(resolvedUrl)) {
for (const protocol of ALLOWED_LINK_PROTOCOLS) {
if (resolvedUrl.toLowerCase().startsWith(protocol)) {
return resolvedUrl
}
}
// Disallow unsupported protocols, e.g. `javascript:`
return ''
}
return resolvedUrl
}
/**
* Responsible for prefixing a resolvedUrl with the builder application's preview
* URI, if it meets certain conditions.
*
* @param {String} resolvedUrl A URL which `resolveElementUrl` has generated.
* @param {Object} builder A builder application.
* @param {String} navigationType An element's `navigation_type` (custom / page).
* @param {String} editorMode A builder application's editor mode.
* @returns {String} A URL we may have prefixed with the application's preview URI.
*/
export function prefixInternalResolvedUrl(
resolvedUrl,
builder,
navigationType,
editorMode
) {
if (
resolvedUrl &&
editorMode === 'preview' &&
(navigationType === 'page' ||
(navigationType === 'custom' && resolvedUrl.startsWith('/')))
) {
// Add prefix in preview mode for page navigation or custom URL starting with `/`
return `/builder/${builder.id}/preview${resolvedUrl}`
} else {
return resolvedUrl
}
}