Working commit
This commit is contained in:
parent
fa6f5caa75
commit
5ce52cad06
|
@ -0,0 +1,133 @@
|
||||||
|
<script>
|
||||||
|
import ActionButton from "../../ActionButton/ActionButton.svelte"
|
||||||
|
import { uuid } from "../../helpers"
|
||||||
|
import Icon from "../../Icon/Icon.svelte"
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
|
||||||
|
export let value = null //support multi?
|
||||||
|
export let title = "Upload file"
|
||||||
|
export let disabled = false
|
||||||
|
export let extensions = "*"
|
||||||
|
export let handleFileTooLarge = null
|
||||||
|
export let handleTooManyFiles = null
|
||||||
|
export let fileSizeLimit = BYTES_IN_MB * 20 // Centralise
|
||||||
|
export let id = null
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
|
//Is this necessary?
|
||||||
|
const fieldId = id || uuid()
|
||||||
|
|
||||||
|
// Centralise all in new file section?
|
||||||
|
const BYTES_IN_KB = 1000
|
||||||
|
const BYTES_IN_MB = 1000000
|
||||||
|
|
||||||
|
let fileInput
|
||||||
|
// $: file = value[0] || null
|
||||||
|
|
||||||
|
// const imageExtensions = [
|
||||||
|
// "png",
|
||||||
|
// "tiff",
|
||||||
|
// "gif",
|
||||||
|
// "raw",
|
||||||
|
// "jpg",
|
||||||
|
// "jpeg",
|
||||||
|
// "svg",
|
||||||
|
// "bmp",
|
||||||
|
// "jfif",
|
||||||
|
// ]
|
||||||
|
|
||||||
|
// Should support only 1 file for now.
|
||||||
|
// Files[0]
|
||||||
|
|
||||||
|
//What is the limit? 50mb?
|
||||||
|
|
||||||
|
async function processFileList(fileList) {
|
||||||
|
if (
|
||||||
|
handleFileTooLarge &&
|
||||||
|
Array.from(fileList).some(file => file.size >= fileSizeLimit)
|
||||||
|
) {
|
||||||
|
handleFileTooLarge(fileSizeLimit, value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileCount = fileList.length + value.length
|
||||||
|
if (handleTooManyFiles && maximum && fileCount > maximum) {
|
||||||
|
handleTooManyFiles(maximum)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (processFiles) {
|
||||||
|
const processedFiles = await processFiles(fileList)
|
||||||
|
const newValue = [...value, ...processedFiles]
|
||||||
|
dispatch("change", newValue)
|
||||||
|
selectedImageIdx = newValue.length - 1
|
||||||
|
} else {
|
||||||
|
dispatch("change", fileList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleFile(evt) {
|
||||||
|
console.log("Hello ", evt.target.files[0])
|
||||||
|
dispatch("change", evt.target.files[0])
|
||||||
|
//processFileList(evt.target.files)
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearFile() {
|
||||||
|
dispatch("change", null)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<input
|
||||||
|
id={fieldId}
|
||||||
|
{disabled}
|
||||||
|
type="file"
|
||||||
|
accept={extensions}
|
||||||
|
bind:this={fileInput}
|
||||||
|
on:change={handleFile}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
{#if value}
|
||||||
|
<div class="file-view">
|
||||||
|
<!-- <img alt="" src={value.url} /> -->
|
||||||
|
<div class="filename">{value.name}</div>
|
||||||
|
{#if value.size}
|
||||||
|
<div class="filesize">
|
||||||
|
{#if value.size <= BYTES_IN_MB}
|
||||||
|
{`${value.size / BYTES_IN_KB} KB`}
|
||||||
|
{:else}
|
||||||
|
{`${value.size / BYTES_IN_MB} MB`}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if !disabled}
|
||||||
|
<div class="delete-button" on:click={clearFile}>
|
||||||
|
<Icon name="Close" size="XS" />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
<ActionButton on:click={fileInput.click()}>{title}</ActionButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.field {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
.file-view {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--spacing-l);
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid var(--spectrum-alias-border-color);
|
||||||
|
border-radius: var(--spectrum-global-dimension-size-50);
|
||||||
|
padding: 0px var(--spectrum-alias-item-padding-m);
|
||||||
|
}
|
||||||
|
input[type="file"] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.delete-button {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -13,3 +13,4 @@ export { default as CoreDropzone } from "./Dropzone.svelte"
|
||||||
export { default as CoreStepper } from "./Stepper.svelte"
|
export { default as CoreStepper } from "./Stepper.svelte"
|
||||||
export { default as CoreRichTextField } from "./RichTextField.svelte"
|
export { default as CoreRichTextField } from "./RichTextField.svelte"
|
||||||
export { default as CoreSlider } from "./Slider.svelte"
|
export { default as CoreSlider } from "./Slider.svelte"
|
||||||
|
export { default as CoreFile } from "./File.svelte"
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
<script>
|
||||||
|
import Field from "./Field.svelte"
|
||||||
|
import { CoreFile } from "./Core"
|
||||||
|
import { createEventDispatcher } from "svelte"
|
||||||
|
|
||||||
|
export let label = null
|
||||||
|
export let labelPosition = "above"
|
||||||
|
export let disabled = false
|
||||||
|
export let error = null
|
||||||
|
export let title = null
|
||||||
|
export let value = null
|
||||||
|
export let tooltip = null
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
const onChange = e => {
|
||||||
|
value = e.detail
|
||||||
|
dispatch("change", e.detail)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Field {label} {labelPosition} {error} {tooltip}>
|
||||||
|
<CoreFile {error} {disabled} {title} {value} on:change={onChange} />
|
||||||
|
</Field>
|
|
@ -78,6 +78,7 @@ export { default as IconSideNav } from "./IconSideNav/IconSideNav.svelte"
|
||||||
export { default as IconSideNavItem } from "./IconSideNav/IconSideNavItem.svelte"
|
export { default as IconSideNavItem } from "./IconSideNav/IconSideNavItem.svelte"
|
||||||
export { default as Slider } from "./Form/Slider.svelte"
|
export { default as Slider } from "./Form/Slider.svelte"
|
||||||
export { default as Accordion } from "./Accordion/Accordion.svelte"
|
export { default as Accordion } from "./Accordion/Accordion.svelte"
|
||||||
|
export { default as File } from "./Form/File.svelte"
|
||||||
|
|
||||||
// Renderers
|
// Renderers
|
||||||
export { default as BoldRenderer } from "./Table/BoldRenderer.svelte"
|
export { default as BoldRenderer } from "./Table/BoldRenderer.svelte"
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html class="spectrum spectrum--medium spectrum--darkest" lang="en" dir="ltr">
|
<html class="spectrum spectrum--medium spectrum--darkest" lang="en" dir="ltr">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset='utf8'>
|
<meta charset='utf8'>
|
||||||
<meta name='viewport' content='width=device-width'>
|
<meta name='viewport' content='width=device-width'>
|
||||||
<title>Budibase</title>
|
<title>Budibase</title>
|
||||||
<link rel='icon' href='/src/favicon.png'>
|
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
<link rel="preconnect" href="https://fonts.gstatic.com" />
|
||||||
<link
|
<link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;600;700&display=swap"
|
||||||
href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;600;700&display=swap"
|
rel="stylesheet" />
|
||||||
rel="stylesheet"
|
|
||||||
/>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body id="app">
|
<body id="app">
|
||||||
<script type="module" src='/src/main.js'></script>
|
<script type="module" src='/src/main.js'></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -4,6 +4,7 @@
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { CookieUtils, Constants } from "@budibase/frontend-core"
|
import { CookieUtils, Constants } from "@budibase/frontend-core"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
|
import Branding from "./Branding.svelte"
|
||||||
|
|
||||||
let loaded = false
|
let loaded = false
|
||||||
|
|
||||||
|
@ -146,6 +147,9 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!--Portal branding overrides -->
|
||||||
|
<Branding />
|
||||||
|
|
||||||
{#if loaded}
|
{#if loaded}
|
||||||
<slot />
|
<slot />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -64,99 +64,106 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window on:keydown={handleKeydown} />
|
<svelte:window on:keydown={handleKeydown} />
|
||||||
|
{#if loaded}
|
||||||
<TestimonialPage>
|
<TestimonialPage enabled={$organisation.testimonialsEnabled}>
|
||||||
<Layout gap="L" noPadding>
|
<Layout gap="L" noPadding>
|
||||||
<Layout justifyItems="center" noPadding>
|
<Layout justifyItems="center" noPadding>
|
||||||
{#if loaded}
|
{#if loaded}
|
||||||
<img alt="logo" src={$organisation.logoUrl || Logo} />
|
<img alt="logo" src={$organisation.logoUrl || Logo} />
|
||||||
{/if}
|
{/if}
|
||||||
<Heading size="M">Log in to Budibase</Heading>
|
<Heading size="M">
|
||||||
</Layout>
|
{$organisation.loginHeading || "Log in to Budibase"}
|
||||||
<Layout gap="S" noPadding>
|
</Heading>
|
||||||
{#if loaded && ($organisation.google || $organisation.oidc)}
|
</Layout>
|
||||||
<FancyForm>
|
<Layout gap="S" noPadding>
|
||||||
<OIDCButton oidcIcon={$oidc.logo} oidcName={$oidc.name} />
|
{#if loaded && ($organisation.google || $organisation.oidc)}
|
||||||
<GoogleButton />
|
<FancyForm>
|
||||||
</FancyForm>
|
<OIDCButton oidcIcon={$oidc.logo} oidcName={$oidc.name} />
|
||||||
{/if}
|
<GoogleButton />
|
||||||
|
</FancyForm>
|
||||||
|
{/if}
|
||||||
|
{#if !$organisation.isSSOEnforced}
|
||||||
|
<Divider />
|
||||||
|
<FancyForm bind:this={form}>
|
||||||
|
<FancyInput
|
||||||
|
label="Your work email"
|
||||||
|
value={formData.username}
|
||||||
|
on:change={e => {
|
||||||
|
formData = {
|
||||||
|
...formData,
|
||||||
|
username: e.detail,
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
validate={() => {
|
||||||
|
let fieldError = {
|
||||||
|
username: !formData.username
|
||||||
|
? "Please enter a valid email"
|
||||||
|
: undefined,
|
||||||
|
}
|
||||||
|
errors = handleError({ ...errors, ...fieldError })
|
||||||
|
}}
|
||||||
|
error={errors.username}
|
||||||
|
/>
|
||||||
|
<FancyInput
|
||||||
|
label="Password"
|
||||||
|
value={formData.password}
|
||||||
|
type="password"
|
||||||
|
on:change={e => {
|
||||||
|
formData = {
|
||||||
|
...formData,
|
||||||
|
password: e.detail,
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
validate={() => {
|
||||||
|
let fieldError = {
|
||||||
|
password: !formData.password
|
||||||
|
? "Please enter your password"
|
||||||
|
: undefined,
|
||||||
|
}
|
||||||
|
errors = handleError({ ...errors, ...fieldError })
|
||||||
|
}}
|
||||||
|
error={errors.password}
|
||||||
|
/>
|
||||||
|
</FancyForm>
|
||||||
|
{/if}
|
||||||
|
</Layout>
|
||||||
{#if !$organisation.isSSOEnforced}
|
{#if !$organisation.isSSOEnforced}
|
||||||
<Divider />
|
<Layout gap="XS" noPadding justifyItems="center">
|
||||||
<FancyForm bind:this={form}>
|
<Button
|
||||||
<FancyInput
|
size="L"
|
||||||
label="Your work email"
|
cta
|
||||||
value={formData.username}
|
disabled={Object.keys(errors).length > 0}
|
||||||
on:change={e => {
|
on:click={login}
|
||||||
formData = {
|
>
|
||||||
...formData,
|
{$organisation.loginButton || `Log in to {company}`}
|
||||||
username: e.detail,
|
</Button>
|
||||||
}
|
</Layout>
|
||||||
}}
|
<Layout gap="XS" noPadding justifyItems="center">
|
||||||
validate={() => {
|
<div class="user-actions">
|
||||||
let fieldError = {
|
<ActionButton size="L" quiet on:click={() => $goto("./forgot")}>
|
||||||
username: !formData.username
|
Forgot password?
|
||||||
? "Please enter a valid email"
|
</ActionButton>
|
||||||
: undefined,
|
</div>
|
||||||
}
|
</Layout>
|
||||||
errors = handleError({ ...errors, ...fieldError })
|
{/if}
|
||||||
}}
|
|
||||||
error={errors.username}
|
{#if cloud && $organisation.licenceAgreementEnabled}
|
||||||
/>
|
<Body size="xs" textAlign="center">
|
||||||
<FancyInput
|
By using Budibase Cloud
|
||||||
label="Password"
|
<br />
|
||||||
value={formData.password}
|
you are agreeing to our
|
||||||
type="password"
|
<Link
|
||||||
on:change={e => {
|
href="https://budibase.com/eula"
|
||||||
formData = {
|
target="_blank"
|
||||||
...formData,
|
secondary={true}
|
||||||
password: e.detail,
|
>
|
||||||
}
|
License Agreement
|
||||||
}}
|
</Link>
|
||||||
validate={() => {
|
</Body>
|
||||||
let fieldError = {
|
|
||||||
password: !formData.password
|
|
||||||
? "Please enter your password"
|
|
||||||
: undefined,
|
|
||||||
}
|
|
||||||
errors = handleError({ ...errors, ...fieldError })
|
|
||||||
}}
|
|
||||||
error={errors.password}
|
|
||||||
/>
|
|
||||||
</FancyForm>
|
|
||||||
{/if}
|
{/if}
|
||||||
</Layout>
|
</Layout>
|
||||||
{#if !$organisation.isSSOEnforced}
|
</TestimonialPage>
|
||||||
<Layout gap="XS" noPadding justifyItems="center">
|
{/if}
|
||||||
<Button
|
|
||||||
size="L"
|
|
||||||
cta
|
|
||||||
disabled={Object.keys(errors).length > 0}
|
|
||||||
on:click={login}
|
|
||||||
>
|
|
||||||
Log in to {company}
|
|
||||||
</Button>
|
|
||||||
</Layout>
|
|
||||||
<Layout gap="XS" noPadding justifyItems="center">
|
|
||||||
<div class="user-actions">
|
|
||||||
<ActionButton size="L" quiet on:click={() => $goto("./forgot")}>
|
|
||||||
Forgot password?
|
|
||||||
</ActionButton>
|
|
||||||
</div>
|
|
||||||
</Layout>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if cloud}
|
|
||||||
<Body size="xs" textAlign="center">
|
|
||||||
By using Budibase Cloud
|
|
||||||
<br />
|
|
||||||
you are agreeing to our
|
|
||||||
<Link href="https://budibase.com/eula" target="_blank" secondary={true}>
|
|
||||||
License Agreement
|
|
||||||
</Link>
|
|
||||||
</Body>
|
|
||||||
{/if}
|
|
||||||
</Layout>
|
|
||||||
</TestimonialPage>
|
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.user-actions {
|
.user-actions {
|
||||||
|
|
|
@ -4,53 +4,328 @@
|
||||||
Heading,
|
Heading,
|
||||||
Body,
|
Body,
|
||||||
Divider,
|
Divider,
|
||||||
Label,
|
File,
|
||||||
Dropzone,
|
|
||||||
notifications,
|
notifications,
|
||||||
|
Tags,
|
||||||
|
Tag,
|
||||||
|
Button,
|
||||||
|
Toggle,
|
||||||
|
Input,
|
||||||
|
Label,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { auth, organisation } from "stores/portal"
|
import { auth, organisation, licensing, admin } from "stores/portal"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
$: logo: $organisation.logoUrl
|
let loaded = false
|
||||||
? { url: $organisation.logoUrl, type: "image", name: "Logo" }
|
let saving = false
|
||||||
: null,
|
let logoFile = null
|
||||||
async function uploadLogo(file) {
|
let faviconFile = null
|
||||||
try {
|
|
||||||
let data = new FormData()
|
let config = {}
|
||||||
data.append("file", file)
|
let updated = false
|
||||||
await API.uploadLogo(data)
|
$: onConfigUpdate(config)
|
||||||
} catch (error) {
|
|
||||||
notifications.error("Error uploading logo")
|
const onConfigUpdate = config => {
|
||||||
|
if (!loaded || updated) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
updated = true
|
||||||
|
console.log("config updated ", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
$: logo = config.logoUrl
|
||||||
|
? { url: config.logoUrl, type: "image", name: "Logo" }
|
||||||
|
: null
|
||||||
|
|
||||||
|
$: favicon = config.faviconUrl
|
||||||
|
? { url: config.logoUrl, type: "image", name: "Favicon" }
|
||||||
|
: null
|
||||||
|
|
||||||
|
async function uploadLogo(file) {
|
||||||
|
let response = {}
|
||||||
|
try {
|
||||||
|
let data = new FormData()
|
||||||
|
data.append("file", file)
|
||||||
|
response = await API.uploadLogo(data)
|
||||||
|
} catch (error) {
|
||||||
|
notifications.error("Error uploading logo")
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit file types
|
||||||
|
// PNG, GIF, or ICO?
|
||||||
|
async function uploadFavicon(file) {
|
||||||
|
let response = {}
|
||||||
|
try {
|
||||||
|
let data = new FormData()
|
||||||
|
data.append("file", file)
|
||||||
|
response = await API.uploadFavicon(data)
|
||||||
|
} catch (error) {
|
||||||
|
notifications.error("Error uploading favicon")
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveConfig() {
|
||||||
|
saving = true
|
||||||
|
|
||||||
|
console.log("Save Config")
|
||||||
|
|
||||||
|
if (logoFile) {
|
||||||
|
const logoResp = await uploadLogo(logoFile)
|
||||||
|
if (logoResp.url) {
|
||||||
|
config = {
|
||||||
|
...config,
|
||||||
|
logoUrl: logoResp.url,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//would have to delete
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (faviconFile) {
|
||||||
|
const faviconResp = await uploadFavicon(faviconFile)
|
||||||
|
if (faviconResp.url) {
|
||||||
|
config = {
|
||||||
|
...config,
|
||||||
|
faviconUrl: faviconResp.url,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("SAVE CONFIG ", config)
|
||||||
|
try {
|
||||||
|
// Update settings
|
||||||
|
await organisation.save(config)
|
||||||
|
await organisation.init()
|
||||||
|
notifications.success("Branding settings updated")
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Branding updated failed", e)
|
||||||
|
notifications.error("Branding updated failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
saving = false
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
await organisation.init()
|
||||||
|
|
||||||
|
const {
|
||||||
|
faviconUrl,
|
||||||
|
logoUrl,
|
||||||
|
platformTitle,
|
||||||
|
emailBrandingEnabled,
|
||||||
|
appFooterEnabled,
|
||||||
|
loginHeading,
|
||||||
|
loginButton,
|
||||||
|
licenceAgreementEnabled,
|
||||||
|
testimonialsEnabled,
|
||||||
|
} = $organisation
|
||||||
|
|
||||||
|
config = {
|
||||||
|
faviconUrl,
|
||||||
|
logoUrl,
|
||||||
|
platformTitle,
|
||||||
|
emailBrandingEnabled,
|
||||||
|
appFooterEnabled,
|
||||||
|
loginHeading,
|
||||||
|
loginButton,
|
||||||
|
licenceAgreementEnabled,
|
||||||
|
testimonialsEnabled,
|
||||||
|
}
|
||||||
|
|
||||||
|
loaded = true
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $auth.isAdmin}
|
{#if $auth.isAdmin && loaded}
|
||||||
<Layout noPadding>
|
<Layout noPadding>
|
||||||
<Layout gap="XS" noPadding>
|
<Layout gap="XS" noPadding>
|
||||||
<Heading size="M">Branding</Heading>
|
<div class="title">
|
||||||
<Body />
|
<Heading size="M">Branding</Heading>
|
||||||
|
{#if !$licensing.isBusinessPlan}
|
||||||
|
<Tags>
|
||||||
|
<Tag icon="LockClosed">Business</Tag>
|
||||||
|
</Tags>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<Body>Remove all Budibase branding and use your own.</Body>
|
||||||
</Layout>
|
</Layout>
|
||||||
<Divider />
|
<Divider />
|
||||||
<div class="fields">
|
<div class="branding fields">
|
||||||
<div class="field logo">
|
<div class="field">
|
||||||
<Label size="L">Logo</Label>
|
<Label size="L">Logo</Label>
|
||||||
<div class="file">
|
<File
|
||||||
<Dropzone
|
title="Upload image"
|
||||||
value={[logo]}
|
on:change={e => {
|
||||||
on:change={e => {
|
console.log("Updated Logo")
|
||||||
if (!e.detail || e.detail.length === 0) {
|
let clone = { ...config }
|
||||||
logo = null
|
if (e.detail) {
|
||||||
} else {
|
logoFile = e.detail
|
||||||
logo = e.detail[0]
|
} else {
|
||||||
}
|
clone.logoUrl = ""
|
||||||
}}
|
}
|
||||||
/>
|
config = clone
|
||||||
|
}}
|
||||||
|
value={logo}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<Label size="L">Favicon</Label>
|
||||||
|
<File
|
||||||
|
title="Upload image"
|
||||||
|
on:change={e => {
|
||||||
|
console.log("Updated Favicon")
|
||||||
|
let clone = { ...config }
|
||||||
|
if (e.detail) {
|
||||||
|
faviconFile = e.detail
|
||||||
|
} else {
|
||||||
|
clone.faviconUrl = ""
|
||||||
|
}
|
||||||
|
config = clone
|
||||||
|
}}
|
||||||
|
value={favicon}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<Label size="L">Title</Label>
|
||||||
|
<Input
|
||||||
|
on:change={e => {
|
||||||
|
let clone = { ...config }
|
||||||
|
clone.platformTitle = e.detail ? e.detail : ""
|
||||||
|
config = clone
|
||||||
|
}}
|
||||||
|
value={config.platformTitle || "Budibase"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Toggle
|
||||||
|
text={"Remove Buidbase brand from emails"}
|
||||||
|
on:change={e => {
|
||||||
|
let clone = { ...config }
|
||||||
|
clone.emailBrandingEnabled = !e.detail
|
||||||
|
config = clone
|
||||||
|
}}
|
||||||
|
value={!config.emailBrandingEnabled}
|
||||||
|
/>
|
||||||
|
<Toggle
|
||||||
|
text={"Remove Budibase footer from apps"}
|
||||||
|
on:change={e => {
|
||||||
|
let clone = { ...config }
|
||||||
|
clone.appFooterEnabled = !e.detail
|
||||||
|
config = clone
|
||||||
|
}}
|
||||||
|
value={!config.appFooterEnabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Should this be displayed? -->
|
||||||
|
{#if !$admin.cloud}
|
||||||
|
<Divider />
|
||||||
|
<Layout gap="XS" noPadding>
|
||||||
|
<Heading size="S">Login page (Self host)</Heading>
|
||||||
|
<Body>You can only customise your login page in self host</Body>
|
||||||
|
</Layout>
|
||||||
|
<div class="login">
|
||||||
|
<div class="fields">
|
||||||
|
<div class="field">
|
||||||
|
<Label size="L">Header</Label>
|
||||||
|
<Input
|
||||||
|
on:change={e => {
|
||||||
|
let clone = { ...config }
|
||||||
|
clone.loginHeading = e.detail ? e.detail : ""
|
||||||
|
config = clone
|
||||||
|
}}
|
||||||
|
value={config.loginHeading || "Log in to Budibase"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<Label size="L">Button</Label>
|
||||||
|
<Input
|
||||||
|
on:change={e => {
|
||||||
|
let clone = { ...config }
|
||||||
|
clone.loginButton = e.detail ? e.detail : ""
|
||||||
|
config = clone
|
||||||
|
}}
|
||||||
|
value={config.loginButton || "Log in to Budibase"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Toggle
|
||||||
|
text={"Remove customer testimonials"}
|
||||||
|
on:change={e => {
|
||||||
|
let clone = { ...config }
|
||||||
|
clone.testimonialsEnabled = !e.detail
|
||||||
|
config = clone
|
||||||
|
}}
|
||||||
|
value={!config.testimonialsEnabled}
|
||||||
|
/>
|
||||||
|
<Toggle
|
||||||
|
text={"Remove licence agreement"}
|
||||||
|
on:change={e => {
|
||||||
|
let clone = { ...config }
|
||||||
|
clone.licenceAgreementEnabled = !e.detail
|
||||||
|
config = clone
|
||||||
|
}}
|
||||||
|
value={!config.licenceAgreementEnabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
|
<Divider />
|
||||||
|
<Layout gap="XS" noPadding>
|
||||||
|
<Heading size="S">Application previews</Heading>
|
||||||
|
<Body>Customise the meta tags on your app preview</Body>
|
||||||
|
</Layout>
|
||||||
|
<div class="app-previews">
|
||||||
|
<div class="fields">
|
||||||
|
<div class="field">
|
||||||
|
<!-- <Label size="L">Header</Label>
|
||||||
|
<Input
|
||||||
|
on:change={e => {
|
||||||
|
let clone = { ...config }
|
||||||
|
clone.loginHeading = e.detail ? e.detail : ""
|
||||||
|
config = clone
|
||||||
|
}}
|
||||||
|
value={config.loginHeading || "Log in to Budibase"}
|
||||||
|
/> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Button on:click={saveConfig} cta disabled={saving || !updated}>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
.title {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
|
||||||
|
.branding,
|
||||||
|
.login {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
.fields {
|
||||||
|
display: grid;
|
||||||
|
grid-gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
.field {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 80px 1fr;
|
||||||
|
grid-gap: var(--spacing-l);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -28,21 +28,21 @@
|
||||||
company: $organisation.company,
|
company: $organisation.company,
|
||||||
platformUrl: $organisation.platformUrl,
|
platformUrl: $organisation.platformUrl,
|
||||||
analyticsEnabled: $organisation.analyticsEnabled,
|
analyticsEnabled: $organisation.analyticsEnabled,
|
||||||
logo: $organisation.logoUrl
|
// logo: $organisation.logoUrl
|
||||||
? { url: $organisation.logoUrl, type: "image", name: "Logo" }
|
// ? { url: $organisation.logoUrl, type: "image", name: "Logo" }
|
||||||
: null,
|
// : null,
|
||||||
})
|
})
|
||||||
let loading = false
|
let loading = false
|
||||||
|
|
||||||
async function uploadLogo(file) {
|
// async function uploadLogo(file) {
|
||||||
try {
|
// try {
|
||||||
let data = new FormData()
|
// let data = new FormData()
|
||||||
data.append("file", file)
|
// data.append("file", file)
|
||||||
await API.uploadLogo(data)
|
// await API.uploadLogo(data)
|
||||||
} catch (error) {
|
// } catch (error) {
|
||||||
notifications.error("Error uploading logo")
|
// notifications.error("Error uploading logo")
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
async function saveConfig() {
|
async function saveConfig() {
|
||||||
loading = true
|
loading = true
|
||||||
|
@ -87,21 +87,7 @@
|
||||||
<Label size="L">Org. name</Label>
|
<Label size="L">Org. name</Label>
|
||||||
<Input thin bind:value={$values.company} />
|
<Input thin bind:value={$values.company} />
|
||||||
</div>
|
</div>
|
||||||
<!-- <div class="field logo">
|
|
||||||
<Label size="L">Logo</Label>
|
|
||||||
<div class="file">
|
|
||||||
<Dropzone
|
|
||||||
value={[$values.logo]}
|
|
||||||
on:change={e => {
|
|
||||||
if (!e.detail || e.detail.length === 0) {
|
|
||||||
$values.logo = null
|
|
||||||
} else {
|
|
||||||
$values.logo = e.detail[0]
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
{#if !$admin.cloud}
|
{#if !$admin.cloud}
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<Label
|
<Label
|
||||||
|
@ -137,10 +123,4 @@
|
||||||
grid-gap: var(--spacing-l);
|
grid-gap: var(--spacing-l);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.file {
|
|
||||||
max-width: 30ch;
|
|
||||||
}
|
|
||||||
.logo {
|
|
||||||
align-items: start;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -13,6 +13,7 @@ export const createLicensingStore = () => {
|
||||||
license: undefined,
|
license: undefined,
|
||||||
isFreePlan: true,
|
isFreePlan: true,
|
||||||
isEnterprisePlan: true,
|
isEnterprisePlan: true,
|
||||||
|
isBusinessPlan: true,
|
||||||
// features
|
// features
|
||||||
groupsEnabled: false,
|
groupsEnabled: false,
|
||||||
backupsEnabled: false,
|
backupsEnabled: false,
|
||||||
|
@ -57,6 +58,7 @@ export const createLicensingStore = () => {
|
||||||
const planType = license?.plan.type
|
const planType = license?.plan.type
|
||||||
const isEnterprisePlan = planType === Constants.PlanType.ENTERPRISE
|
const isEnterprisePlan = planType === Constants.PlanType.ENTERPRISE
|
||||||
const isFreePlan = planType === Constants.PlanType.FREE
|
const isFreePlan = planType === Constants.PlanType.FREE
|
||||||
|
const isBusinessPlan = planType === Constants.PlanType.BUSINESS
|
||||||
const groupsEnabled = license.features.includes(
|
const groupsEnabled = license.features.includes(
|
||||||
Constants.Features.USER_GROUPS
|
Constants.Features.USER_GROUPS
|
||||||
)
|
)
|
||||||
|
@ -79,6 +81,7 @@ export const createLicensingStore = () => {
|
||||||
license,
|
license,
|
||||||
isEnterprisePlan,
|
isEnterprisePlan,
|
||||||
isFreePlan,
|
isFreePlan,
|
||||||
|
isBusinessPlan,
|
||||||
groupsEnabled,
|
groupsEnabled,
|
||||||
backupsEnabled,
|
backupsEnabled,
|
||||||
environmentVariablesEnabled,
|
environmentVariablesEnabled,
|
||||||
|
|
|
@ -5,7 +5,19 @@ import _ from "lodash"
|
||||||
|
|
||||||
const DEFAULT_CONFIG = {
|
const DEFAULT_CONFIG = {
|
||||||
platformUrl: "",
|
platformUrl: "",
|
||||||
|
|
||||||
logoUrl: undefined,
|
logoUrl: undefined,
|
||||||
|
faviconUrl: undefined,
|
||||||
|
|
||||||
|
emailBrandingEnabled: true,
|
||||||
|
appFooterEnabled: true,
|
||||||
|
// Self host only
|
||||||
|
testimonialsEnabled: true,
|
||||||
|
licenceAgreementEnabled: true,
|
||||||
|
platformTitle: "Budibase",
|
||||||
|
loginHeading: undefined,
|
||||||
|
loginButton: undefined,
|
||||||
|
|
||||||
docsUrl: undefined,
|
docsUrl: undefined,
|
||||||
company: "Budibase",
|
company: "Budibase",
|
||||||
oidc: undefined,
|
oidc: undefined,
|
||||||
|
|
|
@ -73,6 +73,18 @@ export const buildConfigEndpoints = API => ({
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the company favicon for the environment.
|
||||||
|
* @param data the favicon form data
|
||||||
|
*/
|
||||||
|
uploadFavicon: async data => {
|
||||||
|
return await API.post({
|
||||||
|
url: "/api/global/configs/upload/settings/faviconUrl",
|
||||||
|
body: data,
|
||||||
|
json: false,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uploads a logo for an OIDC provider.
|
* Uploads a logo for an OIDC provider.
|
||||||
* @param name the name of the OIDC provider
|
* @param name the name of the OIDC provider
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
import Covanta from "../../assets/covanta.png"
|
import Covanta from "../../assets/covanta.png"
|
||||||
import Schnellecke from "../../assets/schnellecke.png"
|
import Schnellecke from "../../assets/schnellecke.png"
|
||||||
|
|
||||||
|
export let enabled = true
|
||||||
|
|
||||||
const testimonials = [
|
const testimonials = [
|
||||||
{
|
{
|
||||||
text: "Budibase was the only solution that checked all the boxes for Covanta. Covanta expects to realize $3.2MM in savings due to the elimination of redundant data entry.",
|
text: "Budibase was the only solution that checked all the boxes for Covanta. Covanta expects to realize $3.2MM in savings due to the elimination of redundant data entry.",
|
||||||
|
@ -33,23 +35,25 @@
|
||||||
|
|
||||||
<SplitPage>
|
<SplitPage>
|
||||||
<slot />
|
<slot />
|
||||||
<div class="wrapper" slot="right">
|
<div class:wrapper={enabled} slot="right">
|
||||||
<div class="testimonial">
|
{#if enabled}
|
||||||
<Layout noPadding gap="S">
|
<div class="testimonial">
|
||||||
<img
|
<Layout noPadding gap="S">
|
||||||
width={testimonial.imageSize}
|
<img
|
||||||
alt="a-happy-budibase-user"
|
width={testimonial.imageSize}
|
||||||
src={testimonial.image}
|
alt="a-happy-budibase-user"
|
||||||
/>
|
src={testimonial.image}
|
||||||
<div class="text">
|
/>
|
||||||
"{testimonial.text}"
|
<div class="text">
|
||||||
</div>
|
"{testimonial.text}"
|
||||||
<div class="author">
|
</div>
|
||||||
<div class="name">{testimonial.name}</div>
|
<div class="author">
|
||||||
<div class="company">{testimonial.role}</div>
|
<div class="name">{testimonial.name}</div>
|
||||||
</div>
|
<div class="company">{testimonial.role}</div>
|
||||||
</Layout>
|
</div>
|
||||||
</div>
|
</Layout>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</SplitPage>
|
</SplitPage>
|
||||||
|
|
||||||
|
|
|
@ -105,14 +105,16 @@ export const serveApp = async function (ctx: any) {
|
||||||
if (!env.isJest()) {
|
if (!env.isJest()) {
|
||||||
const App = require("./templates/BudibaseApp.svelte").default
|
const App = require("./templates/BudibaseApp.svelte").default
|
||||||
const plugins = objectStore.enrichPluginURLs(appInfo.usedPlugins)
|
const plugins = objectStore.enrichPluginURLs(appInfo.usedPlugins)
|
||||||
|
console.log(appInfo)
|
||||||
const { head, html, css } = App.render({
|
const { head, html, css } = App.render({
|
||||||
metaImage:
|
metaImage:
|
||||||
"https://res.cloudinary.com/daog6scxm/image/upload/v1666109324/meta-images/budibase-meta-image_uukc1m.png",
|
"https://res.cloudinary.com/daog6scxm/image/upload/v1666109324/meta-images/budibase-meta-image_uukc1m.png",
|
||||||
title: appInfo.name,
|
title: appInfo.name, //Replace Title here?
|
||||||
production: env.isProd(),
|
production: env.isProd(),
|
||||||
appId,
|
appId,
|
||||||
clientLibPath: objectStore.clientLibraryUrl(appId!, appInfo.version),
|
clientLibPath: objectStore.clientLibraryUrl(appId!, appInfo.version),
|
||||||
usedPlugins: plugins,
|
usedPlugins: plugins,
|
||||||
|
favicon: objectStore.getGlobalFileUrl("settings", "faviconUrl"),
|
||||||
})
|
})
|
||||||
|
|
||||||
const appHbs = loadHandlebarsFile(`${__dirname}/templates/app.hbs`)
|
const appHbs = loadHandlebarsFile(`${__dirname}/templates/app.hbs`)
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
<meta property="og:title" content="{title} - built with Budibase" />
|
<meta property="og:title" content="{title} - built with Budibase" />
|
||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
<meta property="og:image" content={metaImage} />
|
<meta property="og:image" content={metaImage} />
|
||||||
|
|
||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
<link rel="icon" type="image/png" href={favicon} />
|
<link rel="icon" type="image/png" href={favicon} />
|
||||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
|
<link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
|
||||||
|
|
|
@ -27,6 +27,20 @@ export interface SettingsInnerConfig {
|
||||||
company?: string
|
company?: string
|
||||||
logoUrl?: string // Populated on read
|
logoUrl?: string // Populated on read
|
||||||
logoUrlEtag?: string
|
logoUrlEtag?: string
|
||||||
|
|
||||||
|
faviconUrl?: string
|
||||||
|
faviconUrlEtag?: string
|
||||||
|
|
||||||
|
emailBrandingEnabled?: boolean
|
||||||
|
|
||||||
|
// Self host only
|
||||||
|
appFooterEnabled?: boolean
|
||||||
|
testimonialsEnabled?: boolean
|
||||||
|
licenceAgreementEnabled?: boolean
|
||||||
|
platformTitle?: string
|
||||||
|
loginHeading?: string
|
||||||
|
loginButton?: string
|
||||||
|
|
||||||
uniqueTenantId?: string
|
uniqueTenantId?: string
|
||||||
analyticsEnabled?: boolean
|
analyticsEnabled?: boolean
|
||||||
isSSOEnforced?: boolean
|
isSSOEnforced?: boolean
|
||||||
|
|
|
@ -285,6 +285,14 @@ export async function publicSettings(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.faviconUrl && config.faviconUrl !== "") {
|
||||||
|
config.faviconUrl = objectStore.getGlobalFileUrl(
|
||||||
|
"settings",
|
||||||
|
"faviconUrl",
|
||||||
|
config.faviconUrl
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// google
|
// google
|
||||||
const googleConfig = await configs.getGoogleConfig()
|
const googleConfig = await configs.getGoogleConfig()
|
||||||
const preActivated = googleConfig?.activated == null
|
const preActivated = googleConfig?.activated == null
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
{{#if enableEmailBranding}}
|
||||||
|
<tr>
|
||||||
|
<td cellpadding="0" cellspacing="0">
|
||||||
|
<table
|
||||||
|
align="center"
|
||||||
|
width="570"
|
||||||
|
cellpadding="0"
|
||||||
|
cellspacing="0"
|
||||||
|
role="presentation"
|
||||||
|
>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td
|
||||||
|
style="padding:0 35px;vertical-align: middle;"
|
||||||
|
cellpadding="0"
|
||||||
|
cellspacing="0"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
width="32px"
|
||||||
|
style="margin-right: 16px;"
|
||||||
|
alt="Budibase Logo"
|
||||||
|
src="https://i.imgur.com/Xhdt1YP.png"
|
||||||
|
/>
|
||||||
|
<h2 style="margin: 0px">Budibase</h2>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{/if}}
|
|
@ -21,6 +21,8 @@ export const EmailTemplates = {
|
||||||
join(__dirname, "welcome.hbs")
|
join(__dirname, "welcome.hbs")
|
||||||
),
|
),
|
||||||
[EmailTemplatePurpose.CUSTOM]: readStaticFile(join(__dirname, "custom.hbs")),
|
[EmailTemplatePurpose.CUSTOM]: readStaticFile(join(__dirname, "custom.hbs")),
|
||||||
|
//Core wrapper
|
||||||
|
["branding"]: readStaticFile(join(__dirname, "core.hbs")),
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addBaseTemplates(templates: Template[], type?: string) {
|
export function addBaseTemplates(templates: Template[], type?: string) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
import { EmailTemplatePurpose, TemplateType } from "../constants"
|
import { EmailTemplatePurpose, TemplateType } from "../constants"
|
||||||
import { getTemplateByPurpose } from "../constants/templates"
|
import { getTemplateByPurpose, EmailTemplates } from "../constants/templates"
|
||||||
import { getSettingsTemplateContext } from "./templates"
|
import { getSettingsTemplateContext } from "./templates"
|
||||||
import { processString } from "@budibase/string-templates"
|
import { processString } from "@budibase/string-templates"
|
||||||
import { getResetPasswordCode, getInviteCode } from "./redis"
|
import { getResetPasswordCode, getInviteCode } from "./redis"
|
||||||
|
@ -108,30 +108,53 @@ async function buildEmail(
|
||||||
let [base, body] = await Promise.all([
|
let [base, body] = await Promise.all([
|
||||||
getTemplateByPurpose(TYPE, EmailTemplatePurpose.BASE),
|
getTemplateByPurpose(TYPE, EmailTemplatePurpose.BASE),
|
||||||
getTemplateByPurpose(TYPE, purpose),
|
getTemplateByPurpose(TYPE, purpose),
|
||||||
|
//getTemplateByPurpose(TYPE, "branding"), //should generalise to 'branding'
|
||||||
])
|
])
|
||||||
if (!base || !body) {
|
|
||||||
|
let branding = EmailTemplates["branding"]
|
||||||
|
|
||||||
|
if (!base || !body || !branding) {
|
||||||
throw "Unable to build email, missing base components"
|
throw "Unable to build email, missing base components"
|
||||||
}
|
}
|
||||||
base = base.contents
|
base = base.contents
|
||||||
body = body.contents
|
body = body.contents
|
||||||
|
|
||||||
|
//branding = branding.contents
|
||||||
|
|
||||||
let name = user ? user.name : undefined
|
let name = user ? user.name : undefined
|
||||||
if (user && !name && user.firstName) {
|
if (user && !name && user.firstName) {
|
||||||
name = user.lastName ? `${user.firstName} ${user.lastName}` : user.firstName
|
name = user.lastName ? `${user.firstName} ${user.lastName}` : user.firstName
|
||||||
}
|
}
|
||||||
context = {
|
context = {
|
||||||
...context,
|
...context, //enableEmailBranding
|
||||||
contents,
|
contents,
|
||||||
email,
|
email,
|
||||||
name,
|
name,
|
||||||
user: user || {},
|
user: user || {},
|
||||||
}
|
}
|
||||||
|
|
||||||
body = await processString(body, context)
|
const core = branding + body
|
||||||
// this should now be the complete email HTML
|
|
||||||
|
body = await processString(core, context)
|
||||||
|
|
||||||
|
// Conditional elements
|
||||||
|
// branding = await processString(branding, {
|
||||||
|
// ...context,
|
||||||
|
// body,
|
||||||
|
// })
|
||||||
|
|
||||||
|
// this should now be the core email HTML
|
||||||
return processString(base, {
|
return processString(base, {
|
||||||
...context,
|
...context,
|
||||||
body,
|
body, //: branding, // pass as body as usual
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// body = await processString(body, context)
|
||||||
|
// // this should now be the coree email HTML
|
||||||
|
// return processString(base, {
|
||||||
|
// ...context,
|
||||||
|
// body,
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -25,6 +25,11 @@ export async function getSettingsTemplateContext(
|
||||||
[InternalTemplateBinding.CURRENT_DATE]: new Date().toISOString(),
|
[InternalTemplateBinding.CURRENT_DATE]: new Date().toISOString(),
|
||||||
[InternalTemplateBinding.CURRENT_YEAR]: new Date().getFullYear(),
|
[InternalTemplateBinding.CURRENT_YEAR]: new Date().getFullYear(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Need to be careful with the binding as it shouldn't be surfacable
|
||||||
|
// Also default to false if not explicit
|
||||||
|
context["enableEmailBranding"] = settings.emailBrandingEnabled
|
||||||
|
|
||||||
// attach purpose specific context
|
// attach purpose specific context
|
||||||
switch (purpose) {
|
switch (purpose) {
|
||||||
case EmailTemplatePurpose.PASSWORD_RECOVERY:
|
case EmailTemplatePurpose.PASSWORD_RECOVERY:
|
||||||
|
|
Loading…
Reference in New Issue