Merge branch 'develop' of github.com:Budibase/budibase into feature/autocolumns

This commit is contained in:
mike12345567 2021-02-16 16:38:11 +00:00
commit 4ccd46e2a1
35 changed files with 1086 additions and 1163 deletions

View File

@ -1,5 +1,5 @@
{ {
"version": "0.7.7", "version": "0.7.8",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "0.7.7", "version": "0.7.8",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -64,9 +64,9 @@
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^1.58.8", "@budibase/bbui": "^1.58.8",
"@budibase/client": "^0.7.7", "@budibase/client": "^0.7.8",
"@budibase/colorpicker": "1.0.1", "@budibase/colorpicker": "1.0.1",
"@budibase/string-templates": "^0.7.7", "@budibase/string-templates": "^0.7.8",
"@budibase/svelte-ag-grid": "^0.0.16", "@budibase/svelte-ag-grid": "^0.0.16",
"@sentry/browser": "5.19.1", "@sentry/browser": "5.19.1",
"@svelteschool/svelte-forms": "0.7.0", "@svelteschool/svelte-forms": "0.7.0",

View File

@ -11,21 +11,24 @@ const CAPTURE_VAR_INSIDE_TEMPLATE = /{{([^}]+)}}/g
/** /**
* Gets all bindable data context fields and instance fields. * Gets all bindable data context fields and instance fields.
*/ */
export const getBindableProperties = (rootComponent, componentId) => { export const getBindableProperties = (asset, componentId) => {
return getContextBindings(rootComponent, componentId) const contextBindings = getContextBindings(asset, componentId)
const userBindings = getUserBindings()
const urlBindings = getUrlBindings(asset, componentId)
return [...contextBindings, ...userBindings, ...urlBindings]
} }
/** /**
* Gets all data provider components above a component. * Gets all data provider components above a component.
*/ */
export const getDataProviderComponents = (rootComponent, componentId) => { export const getDataProviderComponents = (asset, componentId) => {
if (!rootComponent || !componentId) { if (!asset || !componentId) {
return [] return []
} }
// Get the component tree leading up to this component, ignoring the component // Get the component tree leading up to this component, ignoring the component
// itself // itself
const path = findComponentPath(rootComponent, componentId) const path = findComponentPath(asset.props, componentId)
path.pop() path.pop()
// Filter by only data provider components // Filter by only data provider components
@ -38,18 +41,14 @@ export const getDataProviderComponents = (rootComponent, componentId) => {
/** /**
* Gets all data provider components above a component. * Gets all data provider components above a component.
*/ */
export const getActionProviderComponents = ( export const getActionProviderComponents = (asset, componentId, actionType) => {
rootComponent, if (!asset || !componentId) {
componentId,
actionType
) => {
if (!rootComponent || !componentId) {
return [] return []
} }
// Get the component tree leading up to this component, ignoring the component // Get the component tree leading up to this component, ignoring the component
// itself // itself
const path = findComponentPath(rootComponent, componentId) const path = findComponentPath(asset.props, componentId)
path.pop() path.pop()
// Filter by only data provider components // Filter by only data provider components
@ -92,13 +91,12 @@ export const getDatasourceForProvider = component => {
} }
/** /**
* Gets all bindable data contexts. These are fields of schemas of data contexts * Gets all bindable data properties from component data contexts.
* provided by data provider components, such as lists or row detail components.
*/ */
export const getContextBindings = (rootComponent, componentId) => { const getContextBindings = (asset, componentId) => {
// Extract any components which provide data contexts // Extract any components which provide data contexts
const dataProviders = getDataProviderComponents(rootComponent, componentId) const dataProviders = getDataProviderComponents(asset, componentId)
let contextBindings = [] let bindings = []
// Create bindings for each data provider // Create bindings for each data provider
dataProviders.forEach(component => { dataProviders.forEach(component => {
@ -109,7 +107,7 @@ export const getContextBindings = (rootComponent, componentId) => {
// Forms are an edge case which do not need table schemas // Forms are an edge case which do not need table schemas
if (isForm) { if (isForm) {
schema = buildFormSchema(component) schema = buildFormSchema(component)
tableName = "Schema" tableName = "Fields"
} else { } else {
if (!datasource) { if (!datasource) {
return return
@ -143,7 +141,7 @@ export const getContextBindings = (rootComponent, componentId) => {
runtimeBoundKey = `${key}_first` runtimeBoundKey = `${key}_first`
} }
contextBindings.push({ bindings.push({
type: "context", type: "context",
runtimeBinding: `${makePropSafe(component._id)}.${makePropSafe( runtimeBinding: `${makePropSafe(component._id)}.${makePropSafe(
runtimeBoundKey runtimeBoundKey
@ -157,7 +155,14 @@ export const getContextBindings = (rootComponent, componentId) => {
}) })
}) })
// Add logged in user bindings return bindings
}
/**
* Gets all bindable properties from the logged in user.
*/
const getUserBindings = () => {
let bindings = []
const tables = get(backendUiStore).tables const tables = get(backendUiStore).tables
const userTable = tables.find(table => table._id === TableNames.USERS) const userTable = tables.find(table => table._id === TableNames.USERS)
const schema = { const schema = {
@ -176,7 +181,7 @@ export const getContextBindings = (rootComponent, componentId) => {
runtimeBoundKey = `${key}_first` runtimeBoundKey = `${key}_first`
} }
contextBindings.push({ bindings.push({
type: "context", type: "context",
runtimeBinding: `user.${runtimeBoundKey}`, runtimeBinding: `user.${runtimeBoundKey}`,
readableBinding: `Current User.${key}`, readableBinding: `Current User.${key}`,
@ -187,7 +192,26 @@ export const getContextBindings = (rootComponent, componentId) => {
}) })
}) })
return contextBindings return bindings
}
/**
* Gets all bindable properties from URL parameters.
*/
const getUrlBindings = asset => {
const url = asset?.routing?.route ?? ""
const split = url.split("/")
let params = []
split.forEach(part => {
if (part.startsWith(":") && part.length > 1) {
params.push(part.replace(/:/g, "").replace(/\?/g, ""))
}
})
return params.map(param => ({
type: "context",
runtimeBinding: `url.${param}`,
readableBinding: `URL.${param}`,
}))
} }
/** /**

View File

@ -0,0 +1,84 @@
<script>
import { Icon, Input, Drawer, Body, Button } from "@budibase/bbui"
import {
readableToRuntimeBinding,
runtimeToReadableBinding,
} from "builderStore/dataBinding"
import BindingPanel from "components/design/PropertiesPanel/BindingPanel.svelte"
import { createEventDispatcher } from "svelte"
const dispatch = createEventDispatcher()
export let value = ""
export let bindings = []
let bindingDrawer
let tempValue = value
$: readableValue = runtimeToReadableBinding(bindings, value)
const handleClose = () => {
onChange(tempValue)
bindingDrawer.hide()
}
const onChange = value => {
dispatch("change", readableToRuntimeBinding(bindings, value))
}
</script>
<div class="control">
<Input
thin
value={readableValue}
on:change={event => onChange(event.target.value)}
placeholder="/screen" />
<div class="icon" on:click={bindingDrawer.show}>
<Icon name="lightning" />
</div>
</div>
<Drawer bind:this={bindingDrawer} title="Bindings">
<div slot="description">
<Body extraSmall grey>
Add the objects on the left to enrich your text.
</Body>
</div>
<heading slot="buttons">
<Button thin blue on:click={handleClose}>Save</Button>
</heading>
<div slot="body">
<BindingPanel
value={readableValue}
close={handleClose}
on:update={event => (tempValue = event.detail)}
bindableProperties={bindings} />
</div>
</Drawer>
<style>
.control {
flex: 1;
margin-left: var(--spacing-l);
position: relative;
}
.icon {
right: 2px;
top: 2px;
bottom: 2px;
position: absolute;
align-items: center;
display: flex;
box-sizing: border-box;
padding-left: 7px;
border-left: 1px solid var(--grey-4);
background-color: var(--grey-2);
border-top-right-radius: var(--border-radius-m);
border-bottom-right-radius: var(--border-radius-m);
color: var(--grey-7);
font-size: 14px;
}
.icon:hover {
color: var(--ink);
cursor: pointer;
}
</style>

View File

@ -24,7 +24,7 @@
$: value && checkValid() $: value && checkValid()
$: bindableProperties = getBindableProperties( $: bindableProperties = getBindableProperties(
$currentAsset.props, $currentAsset,
$store.selectedComponentId $store.selectedComponentId
) )
$: dispatch("update", value) $: dispatch("update", value)

View File

@ -47,7 +47,7 @@
type: "query", type: "query",
})) }))
$: bindableProperties = getBindableProperties( $: bindableProperties = getBindableProperties(
$currentAsset.props, $currentAsset,
$store.selectedComponentId $store.selectedComponentId
) )
$: queryBindableProperties = bindableProperties.map(property => ({ $: queryBindableProperties = bindableProperties.map(property => ({

View File

@ -10,7 +10,7 @@
export let parameters export let parameters
$: dataProviderComponents = getDataProviderComponents( $: dataProviderComponents = getDataProviderComponents(
$currentAsset.props, $currentAsset,
$store.selectedComponentId $store.selectedComponentId
) )
$: { $: {

View File

@ -10,7 +10,7 @@
ds => ds._id === parameters.datasourceId ds => ds._id === parameters.datasourceId
) )
$: bindableProperties = getBindableProperties( $: bindableProperties = getBindableProperties(
$currentAsset.props, $currentAsset,
$store.selectedComponentId $store.selectedComponentId
).map(property => ({ ).map(property => ({
...property, ...property,

View File

@ -1,18 +1,23 @@
<script> <script>
import { DataList, Label } from "@budibase/bbui" import { Label } from "@budibase/bbui"
import { allScreens } from "builderStore" import { getBindableProperties } from "builderStore/dataBinding"
import { currentAsset, store } from "builderStore"
import DrawerBindableInput from "components/common/DrawerBindableInput.svelte"
export let parameters export let parameters
let bindingDrawer
let tempValue = parameters.url
$: bindings = getBindableProperties($currentAsset, $store.selectedComponentId)
</script> </script>
<div class="root"> <div class="root">
<Label size="m" color="dark">Screen</Label> <Label size="m" color="dark">Screen</Label>
<DataList secondary bind:value={parameters.url}> <DrawerBindableInput
<option value="" /> value={parameters.url}
{#each $allScreens as screen} on:change={value => (parameters.url = value.detail)}
<option value={screen.routing.route}>{screen.props._instanceName}</option> {bindings} />
{/each}
</DataList>
</div> </div>
<style> <style>

View File

@ -6,7 +6,7 @@
export let parameters export let parameters
$: dataProviders = getDataProviderComponents( $: dataProviders = getDataProviderComponents(
$currentAsset.props, $currentAsset,
$store.selectedComponentId $store.selectedComponentId
) )
</script> </script>

View File

@ -25,7 +25,7 @@
const emptyField = () => ({ name: "", value: "" }) const emptyField = () => ({ name: "", value: "" })
$: bindableProperties = getBindableProperties( $: bindableProperties = getBindableProperties(
$currentAsset.props, $currentAsset,
$store.selectedComponentId $store.selectedComponentId
) )

View File

@ -11,7 +11,7 @@
export let parameters export let parameters
$: dataProviderComponents = getDataProviderComponents( $: dataProviderComponents = getDataProviderComponents(
$currentAsset.props, $currentAsset,
$store.selectedComponentId $store.selectedComponentId
) )
$: providerComponent = dataProviderComponents.find( $: providerComponent = dataProviderComponents.find(

View File

@ -6,7 +6,7 @@
export let parameters export let parameters
$: actionProviders = getActionProviderComponents( $: actionProviders = getActionProviderComponents(
$currentAsset.props, $currentAsset,
$store.selectedComponentId, $store.selectedComponentId,
"ValidateForm" "ValidateForm"
) )

View File

@ -24,7 +24,7 @@
let valid let valid
$: bindableProperties = getBindableProperties( $: bindableProperties = getBindableProperties(
$currentAsset.props, $currentAsset,
$store.selectedComponentId $store.selectedComponentId
) )
$: safeValue = getSafeValue(value, props.defaultValue, bindableProperties) $: safeValue = getSafeValue(value, props.defaultValue, bindableProperties)

View File

@ -1,80 +0,0 @@
<script>
import { DataList } from "@budibase/bbui"
import { createEventDispatcher } from "svelte"
import { store, allScreens, currentAsset } from "builderStore"
import { getBindableProperties } from "builderStore/dataBinding"
export let value = ""
$: urls = getUrls($allScreens, $currentAsset, $store.selectedComponentId)
// Update value on blur
const dispatch = createEventDispatcher()
const handleBlur = () => dispatch("change", value)
// Get all valid screen URL, as well as detail screens which can be used in
// the current data context
const getUrls = (screens, asset, componentId) => {
// Get all screens which aren't detail screens
let urls = screens
.filter(screen => !screen.props._component.endsWith("/rowdetail"))
.map(screen => ({
name: screen.props._instanceName,
url: screen.routing.route,
sort: screen.props._component,
}))
// Add detail screens enriched with the current data context
const bindableProperties = getBindableProperties(asset.props, componentId)
screens
.filter(screen => screen.props._component.endsWith("/rowdetail"))
.forEach(detailScreen => {
// Find any _id bindings that match the detail screen's table
const binding = bindableProperties.find(p => {
return (
p.type === "context" &&
p.runtimeBinding.endsWith("._id") &&
p.tableId === detailScreen.props.table
)
})
if (binding) {
urls.push({
name: detailScreen.props._instanceName,
url: detailScreen.routing.route.replace(
":id",
`{{ ${binding.runtimeBinding} }}`
),
sort: detailScreen.props._component,
})
}
})
return urls
}
</script>
<div>
<DataList
editable
secondary
extraThin
on:blur={handleBlur}
on:change
bind:value>
<option value="" />
{#each urls as url}
<option value={url.url}>{url.name}</option>
{/each}
</DataList>
</div>
<style>
div {
flex: 1 1 auto;
display: flex;
flex-direction: row;
}
div :global(> div) {
flex: 1 1 auto;
}
</style>

View File

@ -18,7 +18,6 @@
import MultiFieldSelect from "./PropertyControls/MultiFieldSelect.svelte" import MultiFieldSelect from "./PropertyControls/MultiFieldSelect.svelte"
import SchemaSelect from "./PropertyControls/SchemaSelect.svelte" import SchemaSelect from "./PropertyControls/SchemaSelect.svelte"
import EventsEditor from "./PropertyControls/EventsEditor" import EventsEditor from "./PropertyControls/EventsEditor"
import ScreenSelect from "./PropertyControls/ScreenSelect.svelte"
import DetailScreenSelect from "./PropertyControls/DetailScreenSelect.svelte" import DetailScreenSelect from "./PropertyControls/DetailScreenSelect.svelte"
import { IconSelect } from "./PropertyControls/IconSelect" import { IconSelect } from "./PropertyControls/IconSelect"
import ColorPicker from "./PropertyControls/ColorPicker.svelte" import ColorPicker from "./PropertyControls/ColorPicker.svelte"
@ -62,7 +61,6 @@
text: Input, text: Input,
select: OptionSelect, select: OptionSelect,
datasource: DatasourceSelect, datasource: DatasourceSelect,
screen: ScreenSelect,
detailScreen: DetailScreenSelect, detailScreen: DetailScreenSelect,
boolean: Checkbox, boolean: Checkbox,
number: Input, number: Input,

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/client", "name": "@budibase/client",
"version": "0.7.7", "version": "0.7.8",
"license": "MPL-2.0", "license": "MPL-2.0",
"main": "dist/budibase-client.js", "main": "dist/budibase-client.js",
"module": "dist/budibase-client.js", "module": "dist/budibase-client.js",
@ -9,14 +9,14 @@
"dev:builder": "rollup -cw" "dev:builder": "rollup -cw"
}, },
"dependencies": { "dependencies": {
"@budibase/string-templates": "^0.7.7", "@budibase/string-templates": "^0.7.8",
"deep-equal": "^2.0.1", "deep-equal": "^2.0.1",
"regexparam": "^1.3.0", "regexparam": "^1.3.0",
"shortid": "^2.2.15", "shortid": "^2.2.15",
"svelte-spa-router": "^3.0.5" "svelte-spa-router": "^3.0.5"
}, },
"devDependencies": { "devDependencies": {
"@budibase/standard-components": "^0.7.7", "@budibase/standard-components": "^0.7.8",
"@rollup/plugin-commonjs": "^16.0.0", "@rollup/plugin-commonjs": "^16.0.0",
"@rollup/plugin-node-resolve": "^10.0.0", "@rollup/plugin-node-resolve": "^10.0.0",
"fs-extra": "^8.1.0", "fs-extra": "^8.1.0",

View File

@ -1,5 +1,5 @@
<script> <script>
import { getContext, setContext } from "svelte" import { getContext } from "svelte"
import Router from "svelte-spa-router" import Router from "svelte-spa-router"
import { routeStore } from "../store" import { routeStore } from "../store"
import Screen from "./Screen.svelte" import Screen from "./Screen.svelte"
@ -10,12 +10,10 @@
// Only wrap this as an array to take advantage of svelte keying, // Only wrap this as an array to take advantage of svelte keying,
// to ensure the svelte-spa-router is fully remounted when route config // to ensure the svelte-spa-router is fully remounted when route config
// changes // changes
$: configs = [ $: config = {
{ routes: getRouterConfig($routeStore.routes),
routes: getRouterConfig($routeStore.routes), id: $routeStore.routeSessionId,
id: $routeStore.routeSessionId, }
},
]
const getRouterConfig = routes => { const getRouterConfig = routes => {
let config = {} let config = {}
@ -33,11 +31,11 @@
} }
</script> </script>
{#each configs as config (config.id)} {#key config.id}
<div use:styleable={$component.styles}> <div use:styleable={$component.styles}>
<Router on:routeLoading={onRouteLoading} routes={config.routes} /> <Router on:routeLoading={onRouteLoading} routes={config.routes} />
</div> </div>
{/each} {/key}
<style> <style>
div { div {

View File

@ -2,6 +2,7 @@
import { fade } from "svelte/transition" import { fade } from "svelte/transition"
import { screenStore, routeStore } from "../store" import { screenStore, routeStore } from "../store"
import Component from "./Component.svelte" import Component from "./Component.svelte"
import Provider from "./Provider.svelte"
// Keep route params up to date // Keep route params up to date
export let params = {} export let params = {}
@ -12,16 +13,16 @@
// Redirect to home layout if no matching route // Redirect to home layout if no matching route
$: screenDefinition == null && routeStore.actions.navigate("/") $: screenDefinition == null && routeStore.actions.navigate("/")
// Make a screen array so we can use keying to properly re-render each screen
$: screens = screenDefinition ? [screenDefinition] : []
</script> </script>
{#each screens as screen (screen._id)} <!-- Ensure to fully remount when screen changes -->
<div in:fade> {#key screenDefinition?._id}
<Component definition={screen} /> <Provider key="url" data={params}>
</div> <div in:fade>
{/each} <Component definition={screenDefinition} />
</div>
</Provider>
{/key}
<style> <style>
div { div {

View File

@ -44,7 +44,7 @@ export const enrichProps = async (props, context) => {
let enrichedProps = await enrichDataBindings(validProps, totalContext) let enrichedProps = await enrichDataBindings(validProps, totalContext)
// Enrich button actions if they exist // Enrich button actions if they exist
if (props._component.endsWith("/button") && enrichedProps.onClick) { if (props._component?.endsWith("/button") && enrichedProps.onClick) {
enrichedProps.onClick = enrichButtonActions( enrichedProps.onClick = enrichButtonActions(
enrichedProps.onClick, enrichedProps.onClick,
totalContext totalContext

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/server", "name": "@budibase/server",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "0.7.7", "version": "0.7.8",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/electron.js", "main": "src/electron.js",
"repository": { "repository": {
@ -50,8 +50,8 @@
"author": "Budibase", "author": "Budibase",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@budibase/client": "^0.7.7", "@budibase/client": "^0.7.8",
"@budibase/string-templates": "^0.7.7", "@budibase/string-templates": "^0.7.8",
"@elastic/elasticsearch": "7.10.0", "@elastic/elasticsearch": "7.10.0",
"@koa/router": "8.0.0", "@koa/router": "8.0.0",
"@sendgrid/mail": "7.1.1", "@sendgrid/mail": "7.1.1",

View File

@ -1,5 +1,5 @@
const { const {
BUILTIN_PERMISSIONS, getBuiltinPermissions,
PermissionLevels, PermissionLevels,
isPermissionLevelHigherThanRead, isPermissionLevelHigherThanRead,
higherPermission, higherPermission,
@ -8,11 +8,10 @@ const {
isBuiltin, isBuiltin,
getDBRoleID, getDBRoleID,
getExternalRoleID, getExternalRoleID,
BUILTIN_ROLES, getBuiltinRoles,
} = require("../../utilities/security/roles") } = require("../../utilities/security/roles")
const { getRoleParams } = require("../../db/utils") const { getRoleParams } = require("../../db/utils")
const CouchDB = require("../../db") const CouchDB = require("../../db")
const { cloneDeep } = require("lodash/fp")
const { const {
CURRENTLY_SUPPORTED_LEVELS, CURRENTLY_SUPPORTED_LEVELS,
getBasePermissions, getBasePermissions,
@ -65,7 +64,7 @@ async function updatePermissionOnRole(
// the permission is for a built in, make sure it exists // the permission is for a built in, make sure it exists
if (isABuiltin && !dbRoles.some(role => role._id === dbRoleId)) { if (isABuiltin && !dbRoles.some(role => role._id === dbRoleId)) {
const builtin = cloneDeep(BUILTIN_ROLES[roleId]) const builtin = getBuiltinRoles()[roleId]
builtin._id = getDBRoleID(builtin._id) builtin._id = getDBRoleID(builtin._id)
dbRoles.push(builtin) dbRoles.push(builtin)
} }
@ -110,7 +109,7 @@ async function updatePermissionOnRole(
} }
exports.fetchBuiltin = function(ctx) { exports.fetchBuiltin = function(ctx) {
ctx.body = Object.values(BUILTIN_PERMISSIONS) ctx.body = Object.values(getBuiltinPermissions())
} }
exports.fetchLevels = function(ctx) { exports.fetchLevels = function(ctx) {

View File

@ -1,6 +1,6 @@
const CouchDB = require("../../db") const CouchDB = require("../../db")
const { const {
BUILTIN_ROLES, getBuiltinRoles,
BUILTIN_ROLE_IDS, BUILTIN_ROLE_IDS,
Role, Role,
getRole, getRole,
@ -58,10 +58,11 @@ exports.fetch = async function(ctx) {
}) })
) )
let roles = body.rows.map(row => row.doc) let roles = body.rows.map(row => row.doc)
const builtinRoles = getBuiltinRoles()
// need to combine builtin with any DB record of them (for sake of permissions) // need to combine builtin with any DB record of them (for sake of permissions)
for (let builtinRoleId of EXTERNAL_BUILTIN_ROLE_IDS) { for (let builtinRoleId of EXTERNAL_BUILTIN_ROLE_IDS) {
const builtinRole = BUILTIN_ROLES[builtinRoleId] const builtinRole = builtinRoles[builtinRoleId]
const dbBuiltin = roles.filter( const dbBuiltin = roles.filter(
dbRole => getExternalRoleID(dbRole._id) === builtinRoleId dbRole => getExternalRoleID(dbRole._id) === builtinRoleId
)[0] )[0]

View File

@ -1,6 +1,6 @@
const jwt = require("jsonwebtoken") const jwt = require("jsonwebtoken")
const STATUS_CODES = require("../utilities/statusCodes") const STATUS_CODES = require("../utilities/statusCodes")
const { getRole, BUILTIN_ROLES } = require("../utilities/security/roles") const { getRole, getBuiltinRoles } = require("../utilities/security/roles")
const { AuthTypes } = require("../constants") const { AuthTypes } = require("../constants")
const { const {
getAppId, getAppId,
@ -20,6 +20,7 @@ module.exports = async (ctx, next) => {
// we hold it in state as a // we hold it in state as a
let appId = getAppId(ctx) let appId = getAppId(ctx)
const cookieAppId = ctx.cookies.get(getCookieName("currentapp")) const cookieAppId = ctx.cookies.get(getCookieName("currentapp"))
const builtinRoles = getBuiltinRoles()
if (appId && cookieAppId !== appId) { if (appId && cookieAppId !== appId) {
setCookie(ctx, appId, "currentapp") setCookie(ctx, appId, "currentapp")
} else if (cookieAppId) { } else if (cookieAppId) {
@ -40,7 +41,7 @@ module.exports = async (ctx, next) => {
ctx.appId = appId ctx.appId = appId
ctx.user = { ctx.user = {
appId, appId,
role: BUILTIN_ROLES.PUBLIC, role: builtinRoles.PUBLIC,
} }
await next() await next()
return return

View File

@ -1,4 +1,5 @@
const { flatten } = require("lodash") const { flatten } = require("lodash")
const { cloneDeep } = require("lodash/fp")
const PermissionLevels = { const PermissionLevels = {
READ: "read", READ: "read",
@ -70,7 +71,7 @@ exports.BUILTIN_PERMISSION_IDS = {
POWER: "power", POWER: "power",
} }
exports.BUILTIN_PERMISSIONS = { const BUILTIN_PERMISSIONS = {
PUBLIC: { PUBLIC: {
_id: exports.BUILTIN_PERMISSION_IDS.PUBLIC, _id: exports.BUILTIN_PERMISSION_IDS.PUBLIC,
name: "Public", name: "Public",
@ -121,8 +122,12 @@ exports.BUILTIN_PERMISSIONS = {
}, },
} }
exports.getBuiltinPermissions = () => {
return cloneDeep(BUILTIN_PERMISSIONS)
}
exports.getBuiltinPermissionByID = id => { exports.getBuiltinPermissionByID = id => {
const perms = Object.values(exports.BUILTIN_PERMISSIONS) const perms = Object.values(BUILTIN_PERMISSIONS)
return perms.find(perm => perm._id === id) return perms.find(perm => perm._id === id)
} }
@ -155,7 +160,7 @@ exports.doesHaveResourcePermission = (
} }
exports.doesHaveBasePermission = (permType, permLevel, permissionIds) => { exports.doesHaveBasePermission = (permType, permLevel, permissionIds) => {
const builtins = Object.values(exports.BUILTIN_PERMISSIONS) const builtins = Object.values(BUILTIN_PERMISSIONS)
let permissions = flatten( let permissions = flatten(
builtins builtins
.filter(builtin => permissionIds.indexOf(builtin._id) !== -1) .filter(builtin => permissionIds.indexOf(builtin._id) !== -1)

View File

@ -26,7 +26,7 @@ Role.prototype.addInheritance = function(inherits) {
return this return this
} }
exports.BUILTIN_ROLES = { const BUILTIN_ROLES = {
ADMIN: new Role(BUILTIN_IDS.ADMIN, "Admin") ADMIN: new Role(BUILTIN_IDS.ADMIN, "Admin")
.addPermission(BUILTIN_PERMISSION_IDS.ADMIN) .addPermission(BUILTIN_PERMISSION_IDS.ADMIN)
.addInheritance(BUILTIN_IDS.POWER), .addInheritance(BUILTIN_IDS.POWER),
@ -44,11 +44,15 @@ exports.BUILTIN_ROLES = {
), ),
} }
exports.BUILTIN_ROLE_ID_ARRAY = Object.values(exports.BUILTIN_ROLES).map( exports.getBuiltinRoles = () => {
return cloneDeep(BUILTIN_ROLES)
}
exports.BUILTIN_ROLE_ID_ARRAY = Object.values(BUILTIN_ROLES).map(
role => role._id role => role._id
) )
exports.BUILTIN_ROLE_NAME_ARRAY = Object.values(exports.BUILTIN_ROLES).map( exports.BUILTIN_ROLE_NAME_ARRAY = Object.values(BUILTIN_ROLES).map(
role => role.name role => role.name
) )
@ -60,17 +64,18 @@ function isBuiltin(role) {
* Works through the inheritance ranks to see how far up the builtin stack this ID is. * Works through the inheritance ranks to see how far up the builtin stack this ID is.
*/ */
function builtinRoleToNumber(id) { function builtinRoleToNumber(id) {
const builtins = exports.getBuiltinRoles()
const MAX = Object.values(BUILTIN_IDS).length + 1 const MAX = Object.values(BUILTIN_IDS).length + 1
if (id === BUILTIN_IDS.ADMIN || id === BUILTIN_IDS.BUILDER) { if (id === BUILTIN_IDS.ADMIN || id === BUILTIN_IDS.BUILDER) {
return MAX return MAX
} }
let role = exports.BUILTIN_ROLES[id], let role = builtins[id],
count = 0 count = 0
do { do {
if (!role) { if (!role) {
break break
} }
role = exports.BUILTIN_ROLES[role.inherits] role = builtins[role.inherits]
count++ count++
} while (role !== null) } while (role !== null)
return count return count
@ -107,7 +112,7 @@ exports.getRole = async (appId, roleId) => {
// but can be extended by a doc stored about them (e.g. permissions) // but can be extended by a doc stored about them (e.g. permissions)
if (isBuiltin(roleId)) { if (isBuiltin(roleId)) {
role = cloneDeep( role = cloneDeep(
Object.values(exports.BUILTIN_ROLES).find(role => role._id === roleId) Object.values(BUILTIN_ROLES).find(role => role._id === roleId)
) )
} }
try { try {

View File

@ -6,7 +6,7 @@ const {
} = require("../../utilities/security/permissions") } = require("../../utilities/security/permissions")
const { const {
lowerBuiltinRoleID, lowerBuiltinRoleID,
BUILTIN_ROLES, getBuiltinRoles,
} = require("../../utilities/security/roles") } = require("../../utilities/security/roles")
const { DocumentTypes } = require("../../db/utils") const { DocumentTypes } = require("../../db/utils")
@ -44,7 +44,7 @@ exports.getPermissionType = resourceId => {
exports.getBasePermissions = resourceId => { exports.getBasePermissions = resourceId => {
const type = exports.getPermissionType(resourceId) const type = exports.getPermissionType(resourceId)
const permissions = {} const permissions = {}
for (let [roleId, role] of Object.entries(BUILTIN_ROLES)) { for (let [roleId, role] of Object.entries(getBuiltinRoles())) {
if (!role.permissionId) { if (!role.permissionId) {
continue continue
} }

View File

@ -176,9 +176,10 @@
"key": "subheading" "key": "subheading"
}, },
{ {
"type": "screen", "type": "text",
"label": "Link URL", "label": "Link URL",
"key": "destinationUrl" "key": "destinationUrl",
"placeholder": "/screen"
} }
] ]
}, },
@ -209,9 +210,10 @@
"key": "linkText" "key": "linkText"
}, },
{ {
"type": "screen", "type": "text",
"label": "Link Url", "label": "Link URL",
"key": "linkUrl" "key": "linkUrl",
"placeholder": "/screen"
}, },
{ {
"type": "color", "type": "color",
@ -378,9 +380,10 @@
"key": "text" "key": "text"
}, },
{ {
"type": "screen", "type": "text",
"label": "URL", "label": "URL",
"key": "url" "key": "url",
"placeholder": "/screen"
}, },
{ {
"type": "boolean", "type": "boolean",
@ -451,9 +454,10 @@
"key": "linkText" "key": "linkText"
}, },
{ {
"type": "screen", "type": "text",
"label": "Link URL", "label": "Link URL",
"key": "linkUrl" "key": "linkUrl",
"placeholder": "/screen"
}, },
{ {
"type": "color", "type": "color",

View File

@ -35,7 +35,7 @@
"keywords": [ "keywords": [
"svelte" "svelte"
], ],
"version": "0.7.7", "version": "0.7.8",
"license": "MIT", "license": "MIT",
"gitHead": "1a80b09fd093f2599a68f7db72ad639dd50922dd", "gitHead": "1a80b09fd093f2599a68f7db72ad639dd50922dd",
"dependencies": { "dependencies": {

View File

@ -1,59 +0,0 @@
<script>
import { Label, Multiselect } from "@budibase/bbui"
import { capitalise } from "./helpers"
import { getContext } from "svelte"
const { API } = getContext("sdk")
export let schema = {}
export let linkedRows = []
export let showLabel = true
export let secondary
let linkedTable
let allRows = []
$: label = capitalise(schema.name)
$: linkedTableId = schema.tableId
$: fetchRows(linkedTableId)
$: fetchTable(linkedTableId)
async function fetchTable(id) {
if (id != null) {
linkedTable = await API.fetchTableDefinition(id)
}
}
async function fetchRows(id) {
if (id != null) {
allRows = await API.fetchTableData(id)
}
}
function getPrettyName(row) {
return row[(linkedTable && linkedTable.primaryDisplay) || "_id"]
}
</script>
{#if linkedTable != null}
{#if linkedTable.primaryDisplay == null}
{#if showLabel}
<Label extraSmall grey>{label}</Label>
{/if}
<Label small black>
Please choose a display column for the
<b>{linkedTable.name}</b>
table.
</Label>
{:else}
<Multiselect
{secondary}
bind:value={linkedRows}
label={showLabel ? label : null}
placeholder="Choose some options">
{#each allRows as row}
<option value={row._id}>{getPrettyName(row)}</option>
{/each}
</Multiselect>
{/if}
{/if}

View File

@ -18,16 +18,16 @@
let table let table
let fieldMap = {} let fieldMap = {}
// Checks if the closest data context matches the model for this forms // Returns the closes data context which isn't a built in context
// datasource, and use it as the initial form values if so
const getInitialValues = context => { const getInitialValues = context => {
return context && context.tableId === datasource?.tableId ? context : {} if (["user", "url"].includes(context.closestComponentId)) {
return {}
}
return context[`${context.closestComponentId}`] || {}
} }
// Use the closest data context as the initial form values if it matches // Use the closest data context as the initial form values
const initialValues = getInitialValues( const initialValues = getInitialValues($context)
$context[`${$context.closestComponentId}`]
)
// Form state contains observable data about the form // Form state contains observable data about the form
const formState = writable({ values: initialValues, errors: {}, valid: true }) const formState = writable({ values: initialValues, errors: {}, valid: true })

View File

@ -24,7 +24,7 @@
$: fetchTable(linkedTableId) $: fetchTable(linkedTableId)
const fetchTable = async id => { const fetchTable = async id => {
if (id != null) { if (id) {
const result = await API.fetchTableDefinition(id) const result = await API.fetchTableDefinition(id)
if (!result.error) { if (!result.error) {
tableDefinition = result tableDefinition = result
@ -33,7 +33,7 @@
} }
const fetchRows = async id => { const fetchRows = async id => {
if (id != null) { if (id) {
const rows = await API.fetchTableData(id) const rows = await API.fetchTableData(id)
options = rows && !rows.error ? rows : [] options = rows && !rows.error ? rows : []
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/string-templates", "name": "@budibase/string-templates",
"version": "0.7.7", "version": "0.7.8",
"description": "Handlebars wrapper for Budibase templating.", "description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.js", "main": "src/index.js",
"module": "src/index.js", "module": "src/index.js",

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/deployment", "name": "@budibase/deployment",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "0.7.7", "version": "0.7.8",
"description": "Budibase Deployment Server", "description": "Budibase Deployment Server",
"main": "src/index.js", "main": "src/index.js",
"repository": { "repository": {