Working commit
This commit is contained in:
parent
c7e54947a8
commit
253ee413a2
|
@ -4,73 +4,35 @@
|
||||||
import Icon from "../../Icon/Icon.svelte"
|
import Icon from "../../Icon/Icon.svelte"
|
||||||
import { createEventDispatcher } from "svelte"
|
import { createEventDispatcher } from "svelte"
|
||||||
|
|
||||||
export let value = null //support multi?
|
export let value = null
|
||||||
export let title = "Upload file"
|
export let title = "Upload file"
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
export let extensions = "*"
|
export let extensions = null
|
||||||
export let handleFileTooLarge = null
|
export let handleFileTooLarge = null
|
||||||
export let handleTooManyFiles = null
|
export let fileSizeLimit = BYTES_IN_MB * 20
|
||||||
export let fileSizeLimit = BYTES_IN_MB * 20 // Centralise
|
|
||||||
export let id = null
|
export let id = null
|
||||||
|
export let previewUrl = null
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
|
||||||
|
|
||||||
//Is this necessary?
|
|
||||||
const fieldId = id || uuid()
|
const fieldId = id || uuid()
|
||||||
|
|
||||||
// Centralise all in new file section?
|
|
||||||
const BYTES_IN_KB = 1000
|
const BYTES_IN_KB = 1000
|
||||||
const BYTES_IN_MB = 1000000
|
const BYTES_IN_MB = 1000000
|
||||||
|
|
||||||
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
let fileInput
|
let fileInput
|
||||||
// $: file = value[0] || null
|
|
||||||
|
|
||||||
// const imageExtensions = [
|
$: inputAccept = Array.isArray(extensions) ? extensions.join(",") : "*"
|
||||||
// "png",
|
|
||||||
// "tiff",
|
|
||||||
// "gif",
|
|
||||||
// "raw",
|
|
||||||
// "jpg",
|
|
||||||
// "jpeg",
|
|
||||||
// "svg",
|
|
||||||
// "bmp",
|
|
||||||
// "jfif",
|
|
||||||
// ]
|
|
||||||
|
|
||||||
// Should support only 1 file for now.
|
async function processFile(targetFile) {
|
||||||
// Files[0]
|
if (handleFileTooLarge && targetFile?.size >= fileSizeLimit) {
|
||||||
|
handleFileTooLarge(targetFile)
|
||||||
//What is the limit? 50mb?
|
|
||||||
|
|
||||||
async function processFileList(fileList) {
|
|
||||||
if (
|
|
||||||
handleFileTooLarge &&
|
|
||||||
Array.from(fileList).some(file => file.size >= fileSizeLimit)
|
|
||||||
) {
|
|
||||||
handleFileTooLarge(fileSizeLimit, value)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
dispatch("change", targetFile)
|
||||||
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) {
|
function handleFile(evt) {
|
||||||
console.log("Hello ", evt.target.files[0])
|
processFile(evt.target.files[0])
|
||||||
dispatch("change", evt.target.files[0])
|
|
||||||
//processFileList(evt.target.files)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearFile() {
|
function clearFile() {
|
||||||
|
@ -82,7 +44,7 @@
|
||||||
id={fieldId}
|
id={fieldId}
|
||||||
{disabled}
|
{disabled}
|
||||||
type="file"
|
type="file"
|
||||||
accept={extensions}
|
accept={inputAccept}
|
||||||
bind:this={fileInput}
|
bind:this={fileInput}
|
||||||
on:change={handleFile}
|
on:change={handleFile}
|
||||||
/>
|
/>
|
||||||
|
@ -90,7 +52,9 @@
|
||||||
<div class="field">
|
<div class="field">
|
||||||
{#if value}
|
{#if value}
|
||||||
<div class="file-view">
|
<div class="file-view">
|
||||||
<!-- <img alt="" src={value.url} /> -->
|
{#if previewUrl}
|
||||||
|
<img class="preview" alt="" src={previewUrl} />
|
||||||
|
{/if}
|
||||||
<div class="filename">{value.name}</div>
|
<div class="filename">{value.name}</div>
|
||||||
{#if value.size}
|
{#if value.size}
|
||||||
<div class="filesize">
|
<div class="filesize">
|
||||||
|
@ -128,6 +92,23 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.delete-button {
|
.delete-button {
|
||||||
|
transition: all 0.3s;
|
||||||
|
margin-left: 10px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.delete-button:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
color: var(--red);
|
||||||
|
}
|
||||||
|
.filesize {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.filename {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.preview {
|
||||||
|
height: 1.5em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
export let label = null
|
export let label = null
|
||||||
export let labelPosition = "above"
|
export let labelPosition = "above"
|
||||||
export let disabled = false
|
export let disabled = false
|
||||||
|
export let handleFileTooLarge = () => {}
|
||||||
|
export let previewUrl = null
|
||||||
|
export let extensions = null
|
||||||
export let error = null
|
export let error = null
|
||||||
export let title = null
|
export let title = null
|
||||||
export let value = null
|
export let value = null
|
||||||
|
@ -19,5 +22,14 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Field {label} {labelPosition} {error} {tooltip}>
|
<Field {label} {labelPosition} {error} {tooltip}>
|
||||||
<CoreFile {error} {disabled} {title} {value} on:change={onChange} />
|
<CoreFile
|
||||||
|
{error}
|
||||||
|
{disabled}
|
||||||
|
{title}
|
||||||
|
{value}
|
||||||
|
{previewUrl}
|
||||||
|
{handleFileTooLarge}
|
||||||
|
{extensions}
|
||||||
|
on:change={onChange}
|
||||||
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
<script>
|
||||||
|
import { organisation, auth, admin } from "stores/portal"
|
||||||
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
|
let loaded = false
|
||||||
|
let platformTitle = null
|
||||||
|
|
||||||
|
$: platformTitleText = $organisation.platformTitle
|
||||||
|
$: platformTitleText,
|
||||||
|
(platformTitle =
|
||||||
|
!$admin.isCloud && !$auth.user ? platformTitleText : "Budibase")
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
await organisation.init()
|
||||||
|
loaded = true
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
In order to update the org elements, an update will have to be made to clear them.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>{platformTitle}</title>
|
||||||
|
|
||||||
|
{#if loaded && !$auth.user}
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
href={$organisation.faviconUrl || "https://i.imgur.com/Xhdt1YP.png"}
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
<!-- A default must be set or the browser defaults to favicon.ico behaviour -->
|
||||||
|
<link rel="icon" href={"https://i.imgur.com/Xhdt1YP.png"} />
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<!-- Primary Meta Tags -->
|
||||||
|
<!-- <meta name="title" content={metaTitle} /> -->
|
||||||
|
<!--
|
||||||
|
metaTitle should match the title...
|
||||||
|
should title override metaTitle, if set??
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- <meta name="description" content={metaDescription} /> -->
|
||||||
|
|
||||||
|
<!-- Opengraph Meta Tags -->
|
||||||
|
<!-- <meta property="og:site_name" content="Budibase" />
|
||||||
|
<meta property="og:title" content="{title} - built with Budibase" />
|
||||||
|
<meta property="og:description" content={metaDescription} />
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:image" content={metaImage} /> -->
|
||||||
|
|
||||||
|
<!-- Twitter -->
|
||||||
|
<!-- <meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:site" content="@budibase" />
|
||||||
|
<meta name="twitter:image" content={metaImage} /> -->
|
||||||
|
<!-- Consider adding this twitter:image:alt -->
|
||||||
|
<!-- <meta name="twitter:title" content="{title} - built with Budibase" />
|
||||||
|
<meta property="twitter:description" content={metaDescription} /> -->
|
||||||
|
</svelte:head>
|
|
@ -147,7 +147,7 @@
|
||||||
</Layout>
|
</Layout>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if cloud && $organisation.licenceAgreementEnabled}
|
{#if cloud && $organisation.licenseAgreementEnabled}
|
||||||
<Body size="xs" textAlign="center">
|
<Body size="xs" textAlign="center">
|
||||||
By using Budibase Cloud
|
By using Budibase Cloud
|
||||||
<br />
|
<br />
|
||||||
|
|
|
@ -12,15 +12,34 @@
|
||||||
Toggle,
|
Toggle,
|
||||||
Input,
|
Input,
|
||||||
Label,
|
Label,
|
||||||
|
TextArea,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { auth, organisation, licensing, admin } from "stores/portal"
|
import { auth, organisation, licensing, admin } from "stores/portal"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
|
|
||||||
|
const imageExtensions = [
|
||||||
|
".png",
|
||||||
|
".tiff",
|
||||||
|
".gif",
|
||||||
|
".raw",
|
||||||
|
".jpg",
|
||||||
|
".jpeg",
|
||||||
|
".svg",
|
||||||
|
".bmp",
|
||||||
|
".jfif",
|
||||||
|
]
|
||||||
|
|
||||||
|
const faviconExtensions = [".png", ".ico", ".gif"]
|
||||||
|
|
||||||
let loaded = false
|
let loaded = false
|
||||||
let saving = false
|
let saving = false
|
||||||
|
|
||||||
let logoFile = null
|
let logoFile = null
|
||||||
|
let logoPreview = null
|
||||||
|
|
||||||
let faviconFile = null
|
let faviconFile = null
|
||||||
|
let faviconPreview = null
|
||||||
|
|
||||||
let config = {}
|
let config = {}
|
||||||
let updated = false
|
let updated = false
|
||||||
|
@ -39,9 +58,45 @@
|
||||||
: null
|
: null
|
||||||
|
|
||||||
$: favicon = config.faviconUrl
|
$: favicon = config.faviconUrl
|
||||||
? { url: config.logoUrl, type: "image", name: "Favicon" }
|
? { url: config.faviconUrl, type: "image", name: "Favicon" }
|
||||||
: null
|
: null
|
||||||
|
|
||||||
|
//If type of file do this IN the picker
|
||||||
|
//If string use the string
|
||||||
|
//If object?.url us that
|
||||||
|
const previewUrl = async localFile => {
|
||||||
|
if (!localFile) {
|
||||||
|
return Promise.resolve(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
let reader = new FileReader()
|
||||||
|
try {
|
||||||
|
reader.onload = e => {
|
||||||
|
resolve({
|
||||||
|
result: e.target.result,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
reader.readAsDataURL(localFile)
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
resolve(null)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
$: previewUrl(logoFile).then(response => {
|
||||||
|
if (response) {
|
||||||
|
logoPreview = response.result
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$: previewUrl(faviconFile).then(response => {
|
||||||
|
if (response) {
|
||||||
|
faviconPreview = response.result
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
async function uploadLogo(file) {
|
async function uploadLogo(file) {
|
||||||
let response = {}
|
let response = {}
|
||||||
try {
|
try {
|
||||||
|
@ -54,8 +109,6 @@
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limit file types
|
|
||||||
// PNG, GIF, or ICO?
|
|
||||||
async function uploadFavicon(file) {
|
async function uploadFavicon(file) {
|
||||||
let response = {}
|
let response = {}
|
||||||
try {
|
try {
|
||||||
|
@ -70,9 +123,7 @@
|
||||||
|
|
||||||
async function saveConfig() {
|
async function saveConfig() {
|
||||||
saving = true
|
saving = true
|
||||||
|
console.log("SAVING CONFIG ")
|
||||||
console.log("Save Config")
|
|
||||||
|
|
||||||
if (logoFile) {
|
if (logoFile) {
|
||||||
const logoResp = await uploadLogo(logoFile)
|
const logoResp = await uploadLogo(logoFile)
|
||||||
if (logoResp.url) {
|
if (logoResp.url) {
|
||||||
|
@ -80,8 +131,8 @@
|
||||||
...config,
|
...config,
|
||||||
logoUrl: logoResp.url,
|
logoUrl: logoResp.url,
|
||||||
}
|
}
|
||||||
} else {
|
logoFile = null
|
||||||
//would have to delete
|
logoPreview = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,6 +143,8 @@
|
||||||
...config,
|
...config,
|
||||||
faviconUrl: faviconResp.url,
|
faviconUrl: faviconResp.url,
|
||||||
}
|
}
|
||||||
|
faviconFile = null
|
||||||
|
faviconPreview = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log("SAVE CONFIG ", config)
|
console.log("SAVE CONFIG ", config)
|
||||||
|
@ -111,28 +164,19 @@
|
||||||
onMount(async () => {
|
onMount(async () => {
|
||||||
await organisation.init()
|
await organisation.init()
|
||||||
|
|
||||||
const {
|
|
||||||
faviconUrl,
|
|
||||||
logoUrl,
|
|
||||||
platformTitle,
|
|
||||||
emailBrandingEnabled,
|
|
||||||
appFooterEnabled,
|
|
||||||
loginHeading,
|
|
||||||
loginButton,
|
|
||||||
licenceAgreementEnabled,
|
|
||||||
testimonialsEnabled,
|
|
||||||
} = $organisation
|
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
faviconUrl,
|
faviconUrl: $organisation.faviconUrl,
|
||||||
logoUrl,
|
logoUrl: $organisation.logoUrl,
|
||||||
platformTitle,
|
platformTitle: $organisation.platformTitle,
|
||||||
emailBrandingEnabled,
|
emailBrandingEnabled: $organisation.emailBrandingEnabled,
|
||||||
appFooterEnabled,
|
appFooterEnabled: $organisation.appFooterEnabled,
|
||||||
loginHeading,
|
loginHeading: $organisation.loginHeading,
|
||||||
loginButton,
|
loginButton: $organisation.loginButton,
|
||||||
licenceAgreementEnabled,
|
licenseAgreementEnabled: $organisation.licenseAgreementEnabled,
|
||||||
testimonialsEnabled,
|
testimonialsEnabled: $organisation.testimonialsEnabled,
|
||||||
|
metaDescription: $organisation.metaDescription,
|
||||||
|
metaImageUrl: $organisation.metaImageUrl,
|
||||||
|
metaTitle: $organisation.metaTitle,
|
||||||
}
|
}
|
||||||
|
|
||||||
loaded = true
|
loaded = true
|
||||||
|
@ -158,17 +202,24 @@
|
||||||
<Label size="L">Logo</Label>
|
<Label size="L">Logo</Label>
|
||||||
<File
|
<File
|
||||||
title="Upload image"
|
title="Upload image"
|
||||||
|
handleFileTooLarge={() => {
|
||||||
|
notifications.warn("File too large. 20mb limit")
|
||||||
|
}}
|
||||||
|
extensions={imageExtensions}
|
||||||
|
previewUrl={logoPreview || logo?.url}
|
||||||
on:change={e => {
|
on:change={e => {
|
||||||
console.log("Updated Logo")
|
console.log("Updated Logo")
|
||||||
let clone = { ...config }
|
let clone = { ...config }
|
||||||
if (e.detail) {
|
if (e.detail) {
|
||||||
logoFile = e.detail
|
logoFile = e.detail
|
||||||
|
logoPreview = null
|
||||||
} else {
|
} else {
|
||||||
|
logoFile = null
|
||||||
clone.logoUrl = ""
|
clone.logoUrl = ""
|
||||||
}
|
}
|
||||||
config = clone
|
config = clone
|
||||||
}}
|
}}
|
||||||
value={logo}
|
value={logoFile || logo}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -176,17 +227,22 @@
|
||||||
<Label size="L">Favicon</Label>
|
<Label size="L">Favicon</Label>
|
||||||
<File
|
<File
|
||||||
title="Upload image"
|
title="Upload image"
|
||||||
|
handleFileTooLarge={() => {
|
||||||
|
notifications.warn("File too large. 20mb limit")
|
||||||
|
}}
|
||||||
|
extensions={faviconExtensions}
|
||||||
|
previewUrl={faviconPreview || favicon?.url}
|
||||||
on:change={e => {
|
on:change={e => {
|
||||||
console.log("Updated Favicon")
|
|
||||||
let clone = { ...config }
|
let clone = { ...config }
|
||||||
if (e.detail) {
|
if (e.detail) {
|
||||||
faviconFile = e.detail
|
faviconFile = e.detail
|
||||||
|
faviconPreview = null
|
||||||
} else {
|
} else {
|
||||||
clone.faviconUrl = ""
|
clone.faviconUrl = ""
|
||||||
}
|
}
|
||||||
config = clone
|
config = clone
|
||||||
}}
|
}}
|
||||||
value={favicon}
|
value={faviconFile || favicon}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
|
@ -197,12 +253,12 @@
|
||||||
clone.platformTitle = e.detail ? e.detail : ""
|
clone.platformTitle = e.detail ? e.detail : ""
|
||||||
config = clone
|
config = clone
|
||||||
}}
|
}}
|
||||||
value={config.platformTitle || "Budibase"}
|
value={config.platformTitle || ""}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Toggle
|
<Toggle
|
||||||
text={"Remove Buidbase brand from emails"}
|
text={"Remove Budibase brand from emails"}
|
||||||
on:change={e => {
|
on:change={e => {
|
||||||
let clone = { ...config }
|
let clone = { ...config }
|
||||||
clone.emailBrandingEnabled = !e.detail
|
clone.emailBrandingEnabled = !e.detail
|
||||||
|
@ -222,7 +278,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Should this be displayed? -->
|
|
||||||
{#if !$admin.cloud}
|
{#if !$admin.cloud}
|
||||||
<Divider />
|
<Divider />
|
||||||
<Layout gap="XS" noPadding>
|
<Layout gap="XS" noPadding>
|
||||||
|
@ -239,7 +294,7 @@
|
||||||
clone.loginHeading = e.detail ? e.detail : ""
|
clone.loginHeading = e.detail ? e.detail : ""
|
||||||
config = clone
|
config = clone
|
||||||
}}
|
}}
|
||||||
value={config.loginHeading || "Log in to Budibase"}
|
value={config.loginHeading || ""}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -251,7 +306,7 @@
|
||||||
clone.loginButton = e.detail ? e.detail : ""
|
clone.loginButton = e.detail ? e.detail : ""
|
||||||
config = clone
|
config = clone
|
||||||
}}
|
}}
|
||||||
value={config.loginButton || "Log in to Budibase"}
|
value={config.loginButton || ""}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -265,13 +320,13 @@
|
||||||
value={!config.testimonialsEnabled}
|
value={!config.testimonialsEnabled}
|
||||||
/>
|
/>
|
||||||
<Toggle
|
<Toggle
|
||||||
text={"Remove licence agreement"}
|
text={"Remove license agreement"}
|
||||||
on:change={e => {
|
on:change={e => {
|
||||||
let clone = { ...config }
|
let clone = { ...config }
|
||||||
clone.licenceAgreementEnabled = !e.detail
|
clone.licenseAgreementEnabled = !e.detail
|
||||||
config = clone
|
config = clone
|
||||||
}}
|
}}
|
||||||
value={!config.licenceAgreementEnabled}
|
value={!config.licenseAgreementEnabled}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -285,15 +340,37 @@
|
||||||
<div class="app-previews">
|
<div class="app-previews">
|
||||||
<div class="fields">
|
<div class="fields">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<!-- <Label size="L">Header</Label>
|
<Label size="L">Image URL</Label>
|
||||||
<Input
|
<Input
|
||||||
on:change={e => {
|
on:change={e => {
|
||||||
let clone = { ...config }
|
let clone = { ...config }
|
||||||
clone.loginHeading = e.detail ? e.detail : ""
|
clone.metaImageUrl = e.detail ? e.detail : ""
|
||||||
config = clone
|
config = clone
|
||||||
}}
|
}}
|
||||||
value={config.loginHeading || "Log in to Budibase"}
|
value={config.metaImageUrl}
|
||||||
/> -->
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<Label size="L">Title</Label>
|
||||||
|
<Input
|
||||||
|
on:change={e => {
|
||||||
|
let clone = { ...config }
|
||||||
|
clone.metaTitle = e.detail ? e.detail : ""
|
||||||
|
config = clone
|
||||||
|
}}
|
||||||
|
value={config.metaTitle}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<Label size="L">Description</Label>
|
||||||
|
<TextArea
|
||||||
|
on:change={e => {
|
||||||
|
let clone = { ...config }
|
||||||
|
clone.metaDescription = e.detail ? e.detail : ""
|
||||||
|
config = clone
|
||||||
|
}}
|
||||||
|
value={config.metaDescription}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -316,7 +393,8 @@
|
||||||
|
|
||||||
.branding,
|
.branding,
|
||||||
.login {
|
.login {
|
||||||
width: 60%;
|
width: 70%;
|
||||||
|
max-width: 70%;
|
||||||
}
|
}
|
||||||
.fields {
|
.fields {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
@ -324,7 +402,7 @@
|
||||||
}
|
}
|
||||||
.field {
|
.field {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 80px 1fr;
|
grid-template-columns: 80px auto;
|
||||||
grid-gap: var(--spacing-l);
|
grid-gap: var(--spacing-l);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,10 @@
|
||||||
Divider,
|
Divider,
|
||||||
Label,
|
Label,
|
||||||
Input,
|
Input,
|
||||||
// Dropzone,
|
|
||||||
notifications,
|
notifications,
|
||||||
Toggle,
|
Toggle,
|
||||||
} from "@budibase/bbui"
|
} from "@budibase/bbui"
|
||||||
import { auth, organisation, admin } from "stores/portal"
|
import { auth, organisation, admin } from "stores/portal"
|
||||||
import { API } from "api"
|
|
||||||
import { writable } from "svelte/store"
|
import { writable } from "svelte/store"
|
||||||
import { redirect } from "@roxi/routify"
|
import { redirect } from "@roxi/routify"
|
||||||
|
|
||||||
|
@ -28,21 +26,9 @@
|
||||||
company: $organisation.company,
|
company: $organisation.company,
|
||||||
platformUrl: $organisation.platformUrl,
|
platformUrl: $organisation.platformUrl,
|
||||||
analyticsEnabled: $organisation.analyticsEnabled,
|
analyticsEnabled: $organisation.analyticsEnabled,
|
||||||
// logo: $organisation.logoUrl
|
|
||||||
// ? { url: $organisation.logoUrl, type: "image", name: "Logo" }
|
|
||||||
// : null,
|
|
||||||
})
|
})
|
||||||
let loading = false
|
|
||||||
|
|
||||||
// async function uploadLogo(file) {
|
let loading = false
|
||||||
// try {
|
|
||||||
// let data = new FormData()
|
|
||||||
// data.append("file", file)
|
|
||||||
// await API.uploadLogo(data)
|
|
||||||
// } catch (error) {
|
|
||||||
// notifications.error("Error uploading logo")
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
async function saveConfig() {
|
async function saveConfig() {
|
||||||
loading = true
|
loading = true
|
||||||
|
|
|
@ -7,17 +7,20 @@ const DEFAULT_CONFIG = {
|
||||||
platformUrl: "",
|
platformUrl: "",
|
||||||
|
|
||||||
logoUrl: undefined,
|
logoUrl: undefined,
|
||||||
faviconUrl: undefined,
|
|
||||||
|
|
||||||
|
faviconUrl: undefined,
|
||||||
emailBrandingEnabled: true,
|
emailBrandingEnabled: true,
|
||||||
appFooterEnabled: true,
|
appFooterEnabled: true,
|
||||||
// Self host only
|
|
||||||
testimonialsEnabled: true,
|
testimonialsEnabled: true,
|
||||||
licenceAgreementEnabled: true,
|
licenseAgreementEnabled: true,
|
||||||
platformTitle: "Budibase",
|
platformTitle: "Budibase",
|
||||||
loginHeading: undefined,
|
loginHeading: undefined,
|
||||||
loginButton: undefined,
|
loginButton: undefined,
|
||||||
|
|
||||||
|
metaDescription: undefined,
|
||||||
|
metaImageUrl: undefined,
|
||||||
|
metaTitle: undefined,
|
||||||
|
|
||||||
docsUrl: undefined,
|
docsUrl: undefined,
|
||||||
company: "Budibase",
|
company: "Budibase",
|
||||||
oidc: undefined,
|
oidc: undefined,
|
||||||
|
|
|
@ -11,10 +11,11 @@ import {
|
||||||
} from "../../../utilities/fileSystem"
|
} from "../../../utilities/fileSystem"
|
||||||
import env from "../../../environment"
|
import env from "../../../environment"
|
||||||
import { DocumentType } from "../../../db/utils"
|
import { DocumentType } from "../../../db/utils"
|
||||||
import { context, objectStore, utils } from "@budibase/backend-core"
|
import { context, objectStore, utils, configs } from "@budibase/backend-core"
|
||||||
import AWS from "aws-sdk"
|
import AWS from "aws-sdk"
|
||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
import sdk from "../../../sdk"
|
import sdk from "../../../sdk"
|
||||||
|
|
||||||
const send = require("koa-send")
|
const send = require("koa-send")
|
||||||
|
|
||||||
async function prepareUpload({ s3Key, bucket, metadata, file }: any) {
|
async function prepareUpload({ s3Key, bucket, metadata, file }: any) {
|
||||||
|
@ -98,6 +99,8 @@ export const deleteObjects = async function (ctx: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const serveApp = async function (ctx: any) {
|
export const serveApp = async function (ctx: any) {
|
||||||
|
const { config } = await configs.getSettingsConfigDoc()
|
||||||
|
|
||||||
const db = context.getAppDB({ skip_setup: true })
|
const db = context.getAppDB({ skip_setup: true })
|
||||||
const appInfo = await db.get(DocumentType.APP_METADATA)
|
const appInfo = await db.get(DocumentType.APP_METADATA)
|
||||||
let appId = context.getAppId()
|
let appId = context.getAppId()
|
||||||
|
@ -105,16 +108,19 @@ 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:
|
||||||
|
config?.metaImageUrl ||
|
||||||
"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, //Replace Title here?
|
metaDescription: config?.metaDescription || "",
|
||||||
|
metaTitle: `${appInfo.name} - built with Budibase` || config?.metaTitle,
|
||||||
|
title: appInfo.name,
|
||||||
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"),
|
favicon: objectStore.getGlobalFileUrl("settings", "faviconUrl"),
|
||||||
|
//logo: objectStore.getGlobalFileUrl("settings", "logoUrl"),
|
||||||
})
|
})
|
||||||
|
|
||||||
const appHbs = loadHandlebarsFile(`${__dirname}/templates/app.hbs`)
|
const appHbs = loadHandlebarsFile(`${__dirname}/templates/app.hbs`)
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
<script>
|
<script>
|
||||||
export let title = ""
|
export let title = ""
|
||||||
export let favicon = ""
|
export let favicon = ""
|
||||||
|
|
||||||
export let metaImage = ""
|
export let metaImage = ""
|
||||||
|
export let metaTitle = ""
|
||||||
|
export let metaDescription = ""
|
||||||
|
|
||||||
export let clientLibPath
|
export let clientLibPath
|
||||||
export let usedPlugins
|
export let usedPlugins
|
||||||
|
@ -13,15 +16,31 @@
|
||||||
name="viewport"
|
name="viewport"
|
||||||
content="width=device-width, initial-scale=1.0, viewport-fit=cover"
|
content="width=device-width, initial-scale=1.0, viewport-fit=cover"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- Primary Meta Tags -->
|
||||||
|
<meta name="title" content={metaTitle} />
|
||||||
|
<meta name="description" content={metaDescription} />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
metaTitle should match the title...
|
||||||
|
should title override metaTitle, if set??
|
||||||
|
-->
|
||||||
|
|
||||||
<!-- Opengraph Meta Tags -->
|
<!-- Opengraph Meta Tags -->
|
||||||
<meta name="twitter:card" content="summary_large_image" />
|
|
||||||
<meta name="twitter:site" content="@budibase" />
|
|
||||||
<meta name="twitter:image" content={metaImage} />
|
|
||||||
<meta name="twitter:title" content="{title} - built with Budibase" />
|
|
||||||
<meta property="og:site_name" content="Budibase" />
|
<meta property="og:site_name" content="Budibase" />
|
||||||
<meta property="og:title" content="{title} - built with Budibase" />
|
<meta property="og:title" content="{title} - built with Budibase" />
|
||||||
|
<meta property="og:description" content={metaDescription} />
|
||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
<meta property="og:image" content={metaImage} />
|
<meta property="og:image" content={metaImage} />
|
||||||
|
|
||||||
|
<!-- Twitter -->
|
||||||
|
<meta property="twitter:card" content="summary_large_image" />
|
||||||
|
<meta property="twitter:site" content="@budibase" />
|
||||||
|
<meta property="twitter:image" content={metaImage} />
|
||||||
|
<!-- Consider adding this twitter:image:alt -->
|
||||||
|
<meta property="twitter:title" content="{title} - built with Budibase" />
|
||||||
|
<meta property="twitter:description" content={metaDescription} />
|
||||||
|
|
||||||
<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" />
|
||||||
|
|
|
@ -32,15 +32,17 @@ export interface SettingsInnerConfig {
|
||||||
faviconUrlEtag?: string
|
faviconUrlEtag?: string
|
||||||
|
|
||||||
emailBrandingEnabled?: boolean
|
emailBrandingEnabled?: boolean
|
||||||
|
|
||||||
// Self host only
|
|
||||||
appFooterEnabled?: boolean
|
appFooterEnabled?: boolean
|
||||||
testimonialsEnabled?: boolean
|
testimonialsEnabled?: boolean
|
||||||
licenceAgreementEnabled?: boolean
|
licenseAgreementEnabled?: boolean
|
||||||
platformTitle?: string
|
platformTitle?: string
|
||||||
loginHeading?: string
|
loginHeading?: string
|
||||||
loginButton?: string
|
loginButton?: string
|
||||||
|
|
||||||
|
metaDescription?: string
|
||||||
|
metaImageUrl?: string
|
||||||
|
metaTitle?: string
|
||||||
|
|
||||||
uniqueTenantId?: string
|
uniqueTenantId?: string
|
||||||
analyticsEnabled?: boolean
|
analyticsEnabled?: boolean
|
||||||
isSSOEnforced?: boolean
|
isSSOEnforced?: boolean
|
||||||
|
|
|
@ -20,6 +20,7 @@ export enum TemplateType {
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum EmailTemplatePurpose {
|
export enum EmailTemplatePurpose {
|
||||||
|
CORE = "core",
|
||||||
BASE = "base",
|
BASE = "base",
|
||||||
PASSWORD_RECOVERY = "password_recovery",
|
PASSWORD_RECOVERY = "password_recovery",
|
||||||
INVITATION = "invitation",
|
INVITATION = "invitation",
|
||||||
|
|
|
@ -10,18 +10,18 @@
|
||||||
>
|
>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td
|
<td style="padding:0 35px;" cellpadding="0" cellspacing="0">
|
||||||
style="padding:0 35px;vertical-align: middle;"
|
|
||||||
cellpadding="0"
|
|
||||||
cellspacing="0"
|
|
||||||
>
|
|
||||||
<img
|
<img
|
||||||
width="32px"
|
width="32px"
|
||||||
style="margin-right: 16px;"
|
style="margin-right:16px; vertical-align: middle;"
|
||||||
alt="Budibase Logo"
|
alt="Budibase Logo"
|
||||||
src="https://i.imgur.com/Xhdt1YP.png"
|
src="https://i.imgur.com/Xhdt1YP.png"
|
||||||
/>
|
/>
|
||||||
<h2 style="margin: 0px">Budibase</h2>
|
<strong
|
||||||
|
style="color:#333333; vertical-align: middle; font-size: 1.1em"
|
||||||
|
>
|
||||||
|
Budibase
|
||||||
|
</strong>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
@ -21,8 +21,7 @@ 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
|
[EmailTemplatePurpose.CORE]: readStaticFile(join(__dirname, "core.hbs")),
|
||||||
["branding"]: readStaticFile(join(__dirname, "core.hbs")),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addBaseTemplates(templates: Template[], type?: string) {
|
export function addBaseTemplates(templates: Template[], type?: string) {
|
||||||
|
|
|
@ -108,53 +108,39 @@ 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'
|
|
||||||
])
|
])
|
||||||
|
|
||||||
let branding = EmailTemplates["branding"]
|
// Change from branding to core
|
||||||
|
let core = EmailTemplates.core
|
||||||
|
|
||||||
if (!base || !body || !branding) {
|
if (!base || !body || !core) {
|
||||||
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, //enableEmailBranding
|
...context,
|
||||||
contents,
|
contents,
|
||||||
email,
|
email,
|
||||||
name,
|
name,
|
||||||
user: user || {},
|
user: user || {},
|
||||||
}
|
}
|
||||||
|
|
||||||
const core = branding + body
|
// Prepend the core template
|
||||||
|
const fullBody = core + body
|
||||||
|
|
||||||
body = await processString(core, context)
|
body = await processString(fullBody, context)
|
||||||
|
|
||||||
// Conditional elements
|
|
||||||
// branding = await processString(branding, {
|
|
||||||
// ...context,
|
|
||||||
// body,
|
|
||||||
// })
|
|
||||||
|
|
||||||
// this should now be the core email HTML
|
// this should now be the core email HTML
|
||||||
return processString(base, {
|
return processString(base, {
|
||||||
...context,
|
...context,
|
||||||
body, //: branding, // pass as body as usual
|
body,
|
||||||
})
|
})
|
||||||
|
|
||||||
// body = await processString(body, context)
|
|
||||||
// // this should now be the coree email HTML
|
|
||||||
// return processString(base, {
|
|
||||||
// ...context,
|
|
||||||
// body,
|
|
||||||
// })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue