From b2da94c49035e18ed6c0f2795b96d64501fc2d8c Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 20 Nov 2020 17:47:13 +0000 Subject: [PATCH 01/79] initial work towards page refactor. --- packages/server/src/api/controllers/screen.js | 18 +------------ packages/server/src/api/routes/screen.js | 3 +-- packages/server/src/db/utils.js | 26 +++++++++---------- 3 files changed, 15 insertions(+), 32 deletions(-) diff --git a/packages/server/src/api/controllers/screen.js b/packages/server/src/api/controllers/screen.js index 694d171fff..acf6df22f0 100644 --- a/packages/server/src/api/controllers/screen.js +++ b/packages/server/src/api/controllers/screen.js @@ -20,29 +20,13 @@ exports.fetch = async ctx => { ) } -exports.find = async ctx => { - const appId = ctx.user.appId - const db = new CouchDB(appId) - - const screens = await db.allDocs( - getScreenParams(ctx.params.pageId, { - include_docs: true, - }) - ) - - ctx.body = await new AccessController(appId).checkScreensAccess( - screens, - ctx.user.accessLevel._id - ) -} - exports.save = async ctx => { const appId = ctx.user.appId const db = new CouchDB(appId) const screen = ctx.request.body if (!screen._id) { - screen._id = generateScreenID(ctx.params.pageId) + screen._id = generateScreenID() } delete screen._css const response = await db.put(screen) diff --git a/packages/server/src/api/routes/screen.js b/packages/server/src/api/routes/screen.js index ce49f66043..1b21a58452 100644 --- a/packages/server/src/api/routes/screen.js +++ b/packages/server/src/api/routes/screen.js @@ -30,9 +30,8 @@ function generateSaveValidation() { router .get("/api/screens", authorized(BUILDER), controller.fetch) - .get("/api/screens/:pageId", authorized(BUILDER), controller.find) .post( - "/api/screens/:pageId", + "/api/screens", authorized(BUILDER), generateSaveValidation(), controller.save diff --git a/packages/server/src/db/utils.js b/packages/server/src/db/utils.js index 4edb27a416..c3b5866a2a 100644 --- a/packages/server/src/db/utils.js +++ b/packages/server/src/db/utils.js @@ -179,14 +179,6 @@ exports.getAccessLevelParams = (accessLevelId = null, otherProps = {}) => { return getDocParams(DocumentTypes.ACCESS_LEVEL, accessLevelId, otherProps) } -/** - * Generates a new webhook ID. - * @returns {string} The new webhook ID which the webhook doc can be stored under. - */ -exports.generateWebhookID = () => { - return `${DocumentTypes.WEBHOOK}${SEPARATOR}${newid()}` -} - /** * Generates a new page ID. * @returns {string} The new page ID which the page doc can be stored under. @@ -206,15 +198,23 @@ exports.getPageParams = (pageId = null, otherProps = {}) => { * Generates a new screen ID. * @returns {string} The new screen ID which the screen doc can be stored under. */ -exports.generateScreenID = pageId => { - return `${DocumentTypes.SCREEN}${SEPARATOR}${pageId}${SEPARATOR}${newid()}` +exports.generateScreenID = () => { + return `${DocumentTypes.SCREEN}${SEPARATOR}${newid()}` } /** - * Gets parameters for retrieving screens for a particular page, this is a utility function for the getDocParams function. + * Gets parameters for retrieving screens, this is a utility function for the getDocParams function. */ -exports.getScreenParams = (pageId = null, otherProps = {}) => { - return getDocParams(DocumentTypes.SCREEN, pageId, otherProps) +exports.getScreenParams = (screenId = null, otherProps = {}) => { + return getDocParams(DocumentTypes.SCREEN, screenId, otherProps) +} + +/** + * Generates a new webhook ID. + * @returns {string} The new webhook ID which the webhook doc can be stored under. + */ +exports.generateWebhookID = () => { + return `${DocumentTypes.WEBHOOK}${SEPARATOR}${newid()}` } /** From 8ae24a4b30ac0fda19814b500053c8f000751779 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 23 Nov 2020 14:07:18 +0000 Subject: [PATCH 02/79] Storing some work in commit, currently broken, further work needed - application needs cleaned up. --- .../server/src/api/controllers/application.js | 28 ++--- .../server/src/api/controllers/deploy/aws.js | 28 +++-- packages/server/src/api/controllers/layout.js | 15 +++ packages/server/src/api/controllers/page.js | 19 ---- .../src/api/controllers/static/index.js | 4 - .../static/templates/BudibaseApp.svelte | 3 +- packages/server/src/api/routes/index.js | 4 +- .../src/api/routes/{pages.js => layout.js} | 4 +- .../src/api/routes/tests/automation.spec.js | 1 - .../src/constants/{pages.js => layouts.js} | 8 +- packages/server/src/db/utils.js | 16 +-- .../utilities/builder/compileStaticAssets.js | 83 ++++++++++++++ .../builder/compileStaticAssetsForPage.js | 102 ------------------ 13 files changed, 137 insertions(+), 178 deletions(-) create mode 100644 packages/server/src/api/controllers/layout.js delete mode 100644 packages/server/src/api/controllers/page.js rename packages/server/src/api/routes/{pages.js => layout.js} (62%) rename packages/server/src/constants/{pages.js => layouts.js} (97%) create mode 100644 packages/server/src/utilities/builder/compileStaticAssets.js delete mode 100644 packages/server/src/utilities/builder/compileStaticAssetsForPage.js diff --git a/packages/server/src/api/controllers/application.js b/packages/server/src/api/controllers/application.js index 512299c5c8..095cb3b831 100644 --- a/packages/server/src/api/controllers/application.js +++ b/packages/server/src/api/controllers/application.js @@ -1,5 +1,5 @@ const CouchDB = require("../../db") -const compileStaticAssetsForPage = require("../../utilities/builder/compileStaticAssetsForPage") +const compileStaticAssets = require("../../utilities/builder/compileStaticAssets") const env = require("../../environment") const { existsSync } = require("fs-extra") const { budibaseAppsDir } = require("../../utilities/budibaseDir") @@ -14,16 +14,16 @@ const { generateAppID, DocumentTypes, SEPARATOR, - getPageParams, + getLayoutParams, getScreenParams, - generatePageID, + generateLayoutID, generateScreenID, } = require("../../db/utils") const { BUILTIN_LEVEL_IDS } = require("../../utilities/security/accessLevels") const { downloadExtractComponentLibraries, } = require("../../utilities/createAppPackage") -const { MAIN, UNAUTHENTICATED, PageTypes } = require("../../constants/pages") +const { MAIN, UNAUTHENTICATED, LayoutTypes } = require("../../constants/layouts") const { HOME_SCREEN } = require("../../constants/screens") const { cloneDeep } = require("lodash/fp") @@ -32,14 +32,14 @@ const APP_PREFIX = DocumentTypes.APP + SEPARATOR // utility function, need to do away with this async function getMainAndUnauthPage(db) { let pages = await db.allDocs( - getPageParams(null, { + getLayoutParams(null, { include_docs: true, }) ) pages = pages.rows.map(row => row.doc) - const mainPage = pages.find(page => page.name === PageTypes.MAIN) - const unauthPage = pages.find(page => page.name === PageTypes.UNAUTHENTICATED) + const mainPage = pages.find(page => page.name === LayoutTypes.MAIN) + const unauthPage = pages.find(page => page.name === LayoutTypes.UNAUTHENTICATED) return { mainPage, unauthPage } } @@ -194,11 +194,11 @@ const createEmptyAppPackage = async (ctx, app) => { fs.mkdirpSync(newAppFolder) const mainPage = cloneDeep(MAIN) - mainPage._id = generatePageID() + mainPage._id = generateLayoutID() mainPage.title = app.name const unauthPage = cloneDeep(UNAUTHENTICATED) - unauthPage._id = generatePageID() + unauthPage._id = generateLayoutID() unauthPage.title = app.name unauthPage.props._children[0].title = `Log in to ${app.name}` @@ -206,14 +206,6 @@ const createEmptyAppPackage = async (ctx, app) => { homeScreen._id = generateScreenID(mainPage._id) await db.bulkDocs([mainPage, unauthPage, homeScreen]) - await compileStaticAssetsForPage(app._id, "main", { - page: mainPage, - screens: [homeScreen], - }) - await compileStaticAssetsForPage(app._id, "unauthenticated", { - page: unauthPage, - screens: [], - }) - + await compileStaticAssets(app._id) return newAppFolder } diff --git a/packages/server/src/api/controllers/deploy/aws.js b/packages/server/src/api/controllers/deploy/aws.js index d478c4efde..5c788a9dcc 100644 --- a/packages/server/src/api/controllers/deploy/aws.js +++ b/packages/server/src/api/controllers/deploy/aws.js @@ -136,25 +136,21 @@ exports.uploadAppAssets = async function({ appId, bucket, accountId }) { const appAssetsPath = join(budibaseAppsDir(), appId, "public") - const appPages = fs.readdirSync(appAssetsPath) - let uploads = [] - for (let page of appPages) { - // Upload HTML, CSS and JS for each page of the web app - walkDir(join(appAssetsPath, page), function(filePath) { - const appAssetUpload = prepareUploadForS3({ - file: { - path: filePath, - name: [...filePath.split("/")].pop(), - }, - s3Key: filePath.replace(appAssetsPath, `assets/${appId}`), - s3, - metadata: { accountId }, - }) - uploads.push(appAssetUpload) + // Upload HTML, CSS and JS of the web app + walkDir(appAssetsPath, function(filePath) { + const appAssetUpload = prepareUploadForS3({ + file: { + path: filePath, + name: [...filePath.split("/")].pop(), + }, + s3Key: filePath.replace(appAssetsPath, `assets/${appId}`), + s3, + metadata: { accountId }, }) - } + uploads.push(appAssetUpload) + }) // Upload file attachments const db = new PouchDB(appId) diff --git a/packages/server/src/api/controllers/layout.js b/packages/server/src/api/controllers/layout.js new file mode 100644 index 0000000000..eae7b04d49 --- /dev/null +++ b/packages/server/src/api/controllers/layout.js @@ -0,0 +1,15 @@ +const CouchDB = require("../../db/client") +const { generateLayoutID } = require("../../db/utils") +const compileStaticAssets = require("../../utilities/builder/compileStaticAssets") + +exports.save = async function(ctx) { + const db = new CouchDB(ctx.user.appId) + const appPackage = ctx.request.body + + // remove special doc props which couch will complain about + delete appPackage.layout._css + appPackage.layout._id = appPackage.layout._id || generateLayoutID() + ctx.body = await db.put(appPackage.layout) + await compileStaticAssets(ctx.user.appId) + ctx.status = 200 +} diff --git a/packages/server/src/api/controllers/page.js b/packages/server/src/api/controllers/page.js deleted file mode 100644 index 4f3989ae90..0000000000 --- a/packages/server/src/api/controllers/page.js +++ /dev/null @@ -1,19 +0,0 @@ -const CouchDB = require("../../db/client") -const { generatePageID } = require("../../db/utils") -const compileStaticAssetsForPage = require("../../utilities/builder/compileStaticAssetsForPage") - -exports.save = async function(ctx) { - const db = new CouchDB(ctx.user.appId) - - const appPackage = ctx.request.body - - const page = await db.get(ctx.params.pageId) - await compileStaticAssetsForPage(ctx.user.appId, page.name, ctx.request.body) - - // remove special doc props which couch will complain about - delete appPackage.page._css - delete appPackage.page._screens - appPackage.page._id = appPackage.page._id || generatePageID() - ctx.body = await db.put(appPackage.page) - ctx.status = 200 -} diff --git a/packages/server/src/api/controllers/static/index.js b/packages/server/src/api/controllers/static/index.js index b048bbd9fe..5676afb230 100644 --- a/packages/server/src/api/controllers/static/index.js +++ b/packages/server/src/api/controllers/static/index.js @@ -142,15 +142,11 @@ exports.performLocalFileProcessing = async function(ctx) { exports.serveApp = async function(ctx) { const App = require("./templates/BudibaseApp.svelte").default - const db = new CouchDB(ctx.params.appId) - const appInfo = await db.get(ctx.params.appId) const { head, html, css } = App.render({ title: appInfo.name, - pageName: - ctx.auth.authenticated === AuthTypes.APP ? "main" : "unauthenticated", production: env.CLOUD, appId: ctx.params.appId, }) diff --git a/packages/server/src/api/controllers/static/templates/BudibaseApp.svelte b/packages/server/src/api/controllers/static/templates/BudibaseApp.svelte index 7d6ad8141c..7668d7c7eb 100644 --- a/packages/server/src/api/controllers/static/templates/BudibaseApp.svelte +++ b/packages/server/src/api/controllers/static/templates/BudibaseApp.svelte @@ -3,14 +3,13 @@ export let favicon = "" export let appId - export let pageName = "" export let production export const PRODUCTION_ASSETS_URL = `https://${appId}.app.budi.live` function publicPath(path) { if (production) { - return `${PRODUCTION_ASSETS_URL}/assets/${appId}/${pageName}/${path}` + return `${PRODUCTION_ASSETS_URL}/assets/${appId}/${path}` } return `/assets/${path}` diff --git a/packages/server/src/api/routes/index.js b/packages/server/src/api/routes/index.js index 840f968eb3..43732298e1 100644 --- a/packages/server/src/api/routes/index.js +++ b/packages/server/src/api/routes/index.js @@ -1,5 +1,5 @@ const authRoutes = require("./auth") -const pageRoutes = require("./pages") +const layoutRoutes = require("./layout") const screenRoutes = require("./screen") const userRoutes = require("./user") const applicationRoutes = require("./application") @@ -19,7 +19,7 @@ const routingRoutes = require("./routing") exports.mainRoutes = [ deployRoutes, - pageRoutes, + layoutRoutes, screenRoutes, userRoutes, applicationRoutes, diff --git a/packages/server/src/api/routes/pages.js b/packages/server/src/api/routes/layout.js similarity index 62% rename from packages/server/src/api/routes/pages.js rename to packages/server/src/api/routes/layout.js index 43fb0e764c..b773dc7dae 100644 --- a/packages/server/src/api/routes/pages.js +++ b/packages/server/src/api/routes/layout.js @@ -1,10 +1,10 @@ const Router = require("@koa/router") const authorized = require("../../middleware/authorized") const { BUILDER } = require("../../utilities/security/permissions") -const controller = require("../controllers/page") +const controller = require("../controllers/layout") const router = Router() -router.post("/api/pages/:pageId", authorized(BUILDER), controller.save) +router.post("/api/layouts/:layoutId", authorized(BUILDER), controller.save) module.exports = router diff --git a/packages/server/src/api/routes/tests/automation.spec.js b/packages/server/src/api/routes/tests/automation.spec.js index 226bbad226..4c9bdb67cb 100644 --- a/packages/server/src/api/routes/tests/automation.spec.js +++ b/packages/server/src/api/routes/tests/automation.spec.js @@ -17,7 +17,6 @@ const AUTOMATION_ID = generateAutomationID() const TEST_AUTOMATION = { _id: AUTOMATION_ID, name: "My Automation", - pageId: "123123123", screenId: "kasdkfldsafkl", live: true, uiTree: { diff --git a/packages/server/src/constants/pages.js b/packages/server/src/constants/layouts.js similarity index 97% rename from packages/server/src/constants/pages.js rename to packages/server/src/constants/layouts.js index 5fe74d6123..065bcdf261 100644 --- a/packages/server/src/constants/pages.js +++ b/packages/server/src/constants/layouts.js @@ -1,4 +1,4 @@ -const PageTypes = { +const LayoutTypes = { MAIN: "main", UNAUTHENTICATED: "unauthenticated", } @@ -8,7 +8,7 @@ const MAIN = { title: "{{ name }}", favicon: "./_shared/favicon.png", stylesheets: [], - name: PageTypes.MAIN, + name: LayoutTypes.MAIN, props: { _id: "private-master-root", _component: "@budibase/standard-components/container", @@ -153,7 +153,7 @@ const UNAUTHENTICATED = { title: "{{ name }}", favicon: "./_shared/favicon.png", stylesheets: [], - name: PageTypes.UNAUTHENTICATED, + name: LayoutTypes.UNAUTHENTICATED, props: { _id: "public-master-root", _component: "@budibase/standard-components/container", @@ -218,4 +218,4 @@ const UNAUTHENTICATED = { }, } -module.exports = { MAIN, UNAUTHENTICATED, PageTypes } +module.exports = { MAIN, UNAUTHENTICATED, LayoutTypes } diff --git a/packages/server/src/db/utils.js b/packages/server/src/db/utils.js index c3b5866a2a..9d9e470ec1 100644 --- a/packages/server/src/db/utils.js +++ b/packages/server/src/db/utils.js @@ -13,7 +13,7 @@ const DocumentTypes = { ACCESS_LEVEL: "ac", WEBHOOK: "wh", INSTANCE: "inst", - PAGE: "page", + LAYOUT: "layout", SCREEN: "screen", } @@ -180,18 +180,18 @@ exports.getAccessLevelParams = (accessLevelId = null, otherProps = {}) => { } /** - * Generates a new page ID. - * @returns {string} The new page ID which the page doc can be stored under. + * Generates a new layout ID. + * @returns {string} The new layout ID which the layout doc can be stored under. */ -exports.generatePageID = () => { - return `${DocumentTypes.PAGE}${SEPARATOR}${newid()}` +exports.generateLayoutID = () => { + return `${DocumentTypes.LAYOUT}${SEPARATOR}${newid()}` } /** - * Gets parameters for retrieving pages, this is a utility function for the getDocParams function. + * Gets parameters for retrieving layout, this is a utility function for the getDocParams function. */ -exports.getPageParams = (pageId = null, otherProps = {}) => { - return getDocParams(DocumentTypes.PAGE, pageId, otherProps) +exports.getLayoutParams = (layoutId = null, otherProps = {}) => { + return getDocParams(DocumentTypes.LAYOUT, layoutId, otherProps) } /** diff --git a/packages/server/src/utilities/builder/compileStaticAssets.js b/packages/server/src/utilities/builder/compileStaticAssets.js new file mode 100644 index 0000000000..a64c2f0b98 --- /dev/null +++ b/packages/server/src/utilities/builder/compileStaticAssets.js @@ -0,0 +1,83 @@ +const { ensureDir, constants, copyFile, writeFile } = require("fs-extra") +const { join } = require("../centralPath") +const { budibaseAppsDir } = require("../budibaseDir") +const CouchDB = require("../../db") +const { getScreenParams, getLayoutParams } = require("../../db/utils") + +async function getAppPackage(appId) { + const db = new CouchDB(appId) + let params = { + include_docs: true, + } + let [screens, layouts] = await Promise.all([ + db.allDocs(getScreenParams(null, params)), + db.allDocs(getLayoutParams(null, params)), + ]) + screens = screens.rows.map(row => row.doc) + layouts = layouts.rows.map(row => row.doc) + if (!screens) { + screens = [] + } + if (!layouts) { + layouts = [] + } + return { screens, layouts } +} + +/** + * Compile all the non-db static web assets that are required for the running of + * a budibase application. This includes CSS, the JSON structure of the DOM and + * the client library, a script responsible for reading the JSON structure + * and rendering the application. + * @param {string} appId - id of the application we want to compile static assets for + */ +module.exports = async appId => { + const publicPath = join(budibaseAppsDir(), appId, "public") + const pkg = await getAppPackage(appId) + + await ensureDir(publicPath) + await buildCssBundle(publicPath, pkg) + await copyClientLib(publicPath) +} + +/** + * Reads the _css property of all screens and the screen layouts, and creates a singular CSS + * bundle for the app at /public/bundle.css + * @param {String} publicPath - path to the public assets directory of the budibase application + * @param {Object} pkg - app package information + */ +const buildCssBundle = async (publicPath, pkg) => { + let cssString = "" + + for (let screen of pkg.screens || []) { + if (!screen._css) continue + if (screen._css.trim().length === 0) { + delete screen._css + continue + } + cssString += screen._css + } + + if (pkg.layout._css) cssString += pkg.layout._css + + writeFile(join(publicPath, "bundle.css"), cssString) +} + +/** + * Copy the budibase client library and sourcemap from NPM to /public/. + * The client library is then served as a static asset when the budibase application + * is running in preview or prod + * @param {String} publicPath - path to write the client library to + */ +const copyClientLib = async publicPath => { + const sourcepath = require.resolve("@budibase/client") + const destPath = join(publicPath, "budibase-client.js") + + await copyFile(sourcepath, destPath, constants.COPYFILE_FICLONE) + + await copyFile( + sourcepath + ".map", + destPath + ".map", + constants.COPYFILE_FICLONE + ) +} diff --git a/packages/server/src/utilities/builder/compileStaticAssetsForPage.js b/packages/server/src/utilities/builder/compileStaticAssetsForPage.js deleted file mode 100644 index c91ba24bb3..0000000000 --- a/packages/server/src/utilities/builder/compileStaticAssetsForPage.js +++ /dev/null @@ -1,102 +0,0 @@ -const { ensureDir, constants, copyFile, writeFile } = require("fs-extra") -const { join } = require("../centralPath") -const { budibaseAppsDir } = require("../budibaseDir") - -/** - * Compile all the non-db static web assets that are required for the running of - * a budibase application. This includes CSS, the JSON structure of the DOM and - * the client library, a script responsible for reading the JSON structure - * and rendering the application. - * @param {} appId - id of the application we want to compile static assets for - * @param {*} pageName - name of the page that the assets will be served for - * @param {*} pkg - app package information/metadata - */ -module.exports = async (appId, pageName, pkg) => { - const pagePath = join(budibaseAppsDir(), appId, "public", pageName) - - pkg.screens = pkg.screens || [] - - await ensureDir(pagePath) - - await buildPageCssBundle(pagePath, pkg) - - await buildFrontendAppDefinition(pagePath, pkg) - - await copyClientLib(pagePath) -} - -/** - * Reads the _css property of a page and its screens, and creates a singular CSS - * bundle for the page at /public//bundle.css - * @param {String} publicPagePath - path to the public assets directory of the budibase application - * @param {Object} pkg - app package information - * @param {"main" | "unauthenticated"} pageName - the pagename of the page we are compiling CSS for. - */ -const buildPageCssBundle = async (publicPagePath, pkg) => { - let cssString = "" - - for (let screen of pkg.screens || []) { - if (!screen._css) continue - if (screen._css.trim().length === 0) { - delete screen._css - continue - } - cssString += screen._css - } - - if (pkg.page._css) cssString += pkg.page._css - - writeFile(join(publicPagePath, "bundle.css"), cssString) -} - -/** - * Copy the budibase client library and sourcemap from NPM to /public/. - * The client library is then served as a static asset when the budibase application - * is running in preview or prod - * @param {String} pagePath - path to write the client library to - */ -const copyClientLib = async pagePath => { - const sourcepath = require.resolve("@budibase/client") - const destPath = join(pagePath, "budibase-client.js") - - await copyFile(sourcepath, destPath, constants.COPYFILE_FICLONE) - - await copyFile( - sourcepath + ".map", - destPath + ".map", - constants.COPYFILE_FICLONE - ) -} - -/** - * Build the frontend definition for a budibase application. This includes all page and screen information, - * and is injected into the budibase client library to tell it how to start constructing - * the DOM from components defined in the frontendDefinition. - * @param {String} pagePath - path to the public folder of the page where the definition will be written - * @param {Object} pkg - app package information from which the frontendDefinition will be built. - */ -const buildFrontendAppDefinition = async (pagePath, pkg) => { - const filename = join(pagePath, "clientFrontendDefinition.js") - - // Delete CSS code from the page and screens so it's not injected - delete pkg.page._css - - for (let screen of pkg.screens) { - if (screen._css) { - delete pkg.page._css - } - } - - const clientUiDefinition = JSON.stringify({ - page: pkg.page, - screens: pkg.screens, - libraries: ["@budibase/standard-components"], - }) - - await writeFile( - filename, - ` - window['##BUDIBASE_FRONTEND_DEFINITION##'] = ${clientUiDefinition}; - ` - ) -} From 71ca88207d9902f6f94fd6958ce610974171c817 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 23 Nov 2020 15:46:26 +0000 Subject: [PATCH 03/79] Removing all reference to 'pages' in server source code, now to look at builder. --- .../server/src/api/controllers/application.js | 98 ++-- .../server/src/automations/automationUtils.js | 36 -- packages/server/src/automations/thread.js | 22 +- packages/server/src/constants/layouts.js | 420 +++++++++--------- packages/server/src/utilities/mustache.js | 73 +++ 5 files changed, 335 insertions(+), 314 deletions(-) create mode 100644 packages/server/src/utilities/mustache.js diff --git a/packages/server/src/api/controllers/application.js b/packages/server/src/api/controllers/application.js index 095cb3b831..6f5bf7f1e1 100644 --- a/packages/server/src/api/controllers/application.js +++ b/packages/server/src/api/controllers/application.js @@ -19,28 +19,45 @@ const { generateLayoutID, generateScreenID, } = require("../../db/utils") -const { BUILTIN_LEVEL_IDS } = require("../../utilities/security/accessLevels") +const { + BUILTIN_LEVEL_IDS, + AccessController, +} = require("../../utilities/security/accessLevels") const { downloadExtractComponentLibraries, } = require("../../utilities/createAppPackage") -const { MAIN, UNAUTHENTICATED, LayoutTypes } = require("../../constants/layouts") +const { BASE_LAYOUTS } = require("../../constants/layouts") const { HOME_SCREEN } = require("../../constants/screens") const { cloneDeep } = require("lodash/fp") +const { recurseMustache } = require("../../utilities/mustache") const APP_PREFIX = DocumentTypes.APP + SEPARATOR // utility function, need to do away with this -async function getMainAndUnauthPage(db) { - let pages = await db.allDocs( - getLayoutParams(null, { - include_docs: true, - }) - ) - pages = pages.rows.map(row => row.doc) +async function getLayouts(db) { + return ( + await db.allDocs( + getLayoutParams(null, { + include_docs: true, + }) + ) + ).rows.map(row => row.doc) +} - const mainPage = pages.find(page => page.name === LayoutTypes.MAIN) - const unauthPage = pages.find(page => page.name === LayoutTypes.UNAUTHENTICATED) - return { mainPage, unauthPage } +async function getScreens(db) { + return ( + await db.allDocs( + getScreenParams(null, { + include_docs: true, + }) + ) + ).rows.map(row => row.doc) +} + +function getUserAccessLevelId(ctx) { + return !ctx.user.accessLevel || !ctx.user.accessLevel._id + ? BUILTIN_LEVEL_IDS.PUBLIC + : ctx.user.accessLevel._id } async function createInstance(template) { @@ -85,25 +102,16 @@ exports.fetch = async function(ctx) { exports.fetchAppDefinition = async function(ctx) { const db = new CouchDB(ctx.params.appId) - // TODO: need to get rid of pages here, they shouldn't be needed anymore - const { mainPage, unauthPage } = await getMainAndUnauthPage(db) - const userAccessLevelId = - !ctx.user.accessLevel || !ctx.user.accessLevel._id - ? BUILTIN_LEVEL_IDS.PUBLIC - : ctx.user.accessLevel._id - const correctPage = - userAccessLevelId === BUILTIN_LEVEL_IDS.PUBLIC ? unauthPage : mainPage - const screens = ( - await db.allDocs( - getScreenParams(correctPage._id, { - include_docs: true, - }) - ) - ).rows.map(row => row.doc) - // TODO: need to handle access control here, limit screens to user access level + const layouts = await getLayouts(db) + const userAccessLevelId = getUserAccessLevelId(ctx) + const accessController = new AccessController(ctx.params.appId) + const screens = accessController.checkScreensAccess( + await getScreens(db), + userAccessLevelId + ) ctx.body = { - page: correctPage, - screens: screens, + layouts, + screens, libraries: ["@budibase/standard-components"], } } @@ -111,16 +119,14 @@ exports.fetchAppDefinition = async function(ctx) { exports.fetchAppPackage = async function(ctx) { const db = new CouchDB(ctx.params.appId) const application = await db.get(ctx.params.appId) + const layouts = await getLayouts(db) + const screens = await getScreens(db) - const { mainPage, unauthPage } = await getMainAndUnauthPage(db) ctx.body = { application, - pages: { - main: mainPage, - unauthenticated: unauthPage, - }, + screens, + layouts, } - await setBuilderToken(ctx, ctx.params.appId, application.version) } @@ -193,18 +199,18 @@ const createEmptyAppPackage = async (ctx, app) => { fs.mkdirpSync(newAppFolder) - const mainPage = cloneDeep(MAIN) - mainPage._id = generateLayoutID() - mainPage.title = app.name - - const unauthPage = cloneDeep(UNAUTHENTICATED) - unauthPage._id = generateLayoutID() - unauthPage.title = app.name - unauthPage.props._children[0].title = `Log in to ${app.name}` + const bulkDocs = [] + for (let layout of BASE_LAYOUTS) { + const cloned = cloneDeep(layout) + cloned._id = generateLayoutID() + cloned.title = app.name + bulkDocs.push(recurseMustache(cloned, app)) + } const homeScreen = cloneDeep(HOME_SCREEN) - homeScreen._id = generateScreenID(mainPage._id) - await db.bulkDocs([mainPage, unauthPage, homeScreen]) + homeScreen._id = generateScreenID() + bulkDocs.push(homeScreen) + await db.bulkDocs(bulkDocs) await compileStaticAssets(app._id) return newAppFolder diff --git a/packages/server/src/automations/automationUtils.js b/packages/server/src/automations/automationUtils.js index 3a66420dd0..ac0d9bf721 100644 --- a/packages/server/src/automations/automationUtils.js +++ b/packages/server/src/automations/automationUtils.js @@ -1,41 +1,5 @@ const CouchDB = require("../db") -/** - * When running mustache statements to execute on the context of the automation it possible user's may input mustache - * in a few different forms, some of which are invalid but are logically valid. An example of this would be the mustache - * statement "{{steps[0].revision}}" here it is obvious the user is attempting to access an array or object using array - * like operators. These are not supported by Mustache and therefore the statement will fail. This function will clean up - * the mustache statement so it instead reads as "{{steps.0.revision}}" which is valid and will work. It may also be expanded - * to include any other mustache statement cleanup that has been deemed necessary for the system. - * - * @param {string} string The string which *may* contain mustache statements, it is OK if it does not contain any. - * @returns {string} The string that was input with cleaned up mustache statements as required. - */ -module.exports.cleanMustache = string => { - let charToReplace = { - "[": ".", - "]": "", - } - let regex = new RegExp(/{{[^}}]*}}/g) - let matches = string.match(regex) - if (matches == null) { - return string - } - for (let match of matches) { - let baseIdx = string.indexOf(match) - for (let key of Object.keys(charToReplace)) { - let idxChar = match.indexOf(key) - if (idxChar !== -1) { - string = - string.slice(baseIdx, baseIdx + idxChar) + - charToReplace[key] + - string.slice(baseIdx + idxChar + 1) - } - } - } - return string -} - /** * When values are input to the system generally they will be of type string as this is required for mustache. This can * generate some odd scenarios as the Schema of the automation requires a number but the builder might supply a string diff --git a/packages/server/src/automations/thread.js b/packages/server/src/automations/thread.js index 26ca8f04a3..cc01ec2d4b 100644 --- a/packages/server/src/automations/thread.js +++ b/packages/server/src/automations/thread.js @@ -1,30 +1,10 @@ -const handlebars = require("handlebars") const actions = require("./actions") const logic = require("./logic") const automationUtils = require("./automationUtils") - -handlebars.registerHelper("object", value => { - return new handlebars.SafeString(JSON.stringify(value)) -}) +const { recurseMustache } = require("../utilities/mustache") const FILTER_STEP_ID = logic.BUILTIN_DEFINITIONS.FILTER.stepId -function recurseMustache(inputs, context) { - for (let key of Object.keys(inputs)) { - let val = inputs[key] - if (typeof val === "string") { - val = automationUtils.cleanMustache(inputs[key]) - const template = handlebars.compile(val) - inputs[key] = template(context) - } - // this covers objects and arrays - else if (typeof val === "object") { - inputs[key] = recurseMustache(inputs[key], context) - } - } - return inputs -} - /** * The automation orchestrator is a class responsible for executing automations. * It handles the context of the automation and makes sure each step gets the correct diff --git a/packages/server/src/constants/layouts.js b/packages/server/src/constants/layouts.js index 065bcdf261..85ab7ee69d 100644 --- a/packages/server/src/constants/layouts.js +++ b/packages/server/src/constants/layouts.js @@ -1,221 +1,219 @@ -const LayoutTypes = { - MAIN: "main", - UNAUTHENTICATED: "unauthenticated", -} - -const MAIN = { - componentLibraries: ["@budibase/standard-components"], - title: "{{ name }}", - favicon: "./_shared/favicon.png", - stylesheets: [], - name: LayoutTypes.MAIN, - props: { - _id: "private-master-root", - _component: "@budibase/standard-components/container", - _children: [ - { - _id: "c74f07266980c4b6eafc33e2a6caa783d", - _component: "@budibase/standard-components/container", - _styles: { - normal: { - display: "flex", - "flex-direction": "row", - "justify-content": "flex-start", - "align-items": "flex-start", - background: "#fff", - width: "100%", - "box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)", - }, - hover: {}, - active: {}, - selected: {}, - }, - _code: "", - className: "", - onLoad: [], - type: "div", - _appId: "inst_app_80b_f158d4057d2c4bedb0042d42fda8abaf", - _instanceName: "Header", - _children: [ - { - _id: "49e0e519-9e5e-4127-885a-ee6a0a49e2c1", - _component: "@budibase/standard-components/Navigation", - _styles: { - normal: { - "max-width": "1400px", - "margin-left": "auto", - "margin-right": "auto", - padding: "20px", - color: "#757575", - "font-weight": "400", - "font-size": "16px", - flex: "1 1 auto", - }, - hover: {}, - active: {}, - selected: {}, +const BASE_LAYOUTS = [ + { + componentLibraries: ["@budibase/standard-components"], + title: "{{ name }}", + favicon: "./_shared/favicon.png", + stylesheets: [], + name: "Main", + props: { + _id: "private-master-root", + _component: "@budibase/standard-components/container", + _children: [ + { + _id: "c74f07266980c4b6eafc33e2a6caa783d", + _component: "@budibase/standard-components/container", + _styles: { + normal: { + display: "flex", + "flex-direction": "row", + "justify-content": "flex-start", + "align-items": "flex-start", + background: "#fff", + width: "100%", + "box-shadow": "0 1px 2px 0 rgba(0, 0, 0, 0.05)", }, - _code: "", - logoUrl: - "https://d33wubrfki0l68.cloudfront.net/aac32159d7207b5085e74a7ef67afbb7027786c5/2b1fd/img/logo/bb-emblem.svg", - title: "", - backgroundColor: "", - color: "", - borderWidth: "", - borderColor: "", - borderStyle: "", - _appId: "inst_cf8ace4_69efc0d72e6f443db2d4c902c14d9394", - _instanceName: "Navigation", - _children: [ - { - _id: "48b35328-4c91-4343-a6a3-1a1fd77b3386", - _component: "@budibase/standard-components/link", - _styles: { - normal: { - "font-family": "Inter", - "font-weight": "500", - color: "#000000", - "text-decoration-line": "none", - "font-size": "16px", - }, - hover: { - color: "#4285f4", - }, - active: {}, - selected: {}, + hover: {}, + active: {}, + selected: {}, + }, + _code: "", + className: "", + onLoad: [], + type: "div", + _appId: "inst_app_80b_f158d4057d2c4bedb0042d42fda8abaf", + _instanceName: "Header", + _children: [ + { + _id: "49e0e519-9e5e-4127-885a-ee6a0a49e2c1", + _component: "@budibase/standard-components/Navigation", + _styles: { + normal: { + "max-width": "1400px", + "margin-left": "auto", + "margin-right": "auto", + padding: "20px", + color: "#757575", + "font-weight": "400", + "font-size": "16px", + flex: "1 1 auto", }, - _code: "", - url: "/", - openInNewTab: false, - text: "Home", - color: "", - hoverColor: "", - underline: false, - fontSize: "", - fontFamily: "initial", - _appId: "inst_cf8ace4_69efc0d72e6f443db2d4c902c14d9394", - _instanceName: "Home Link", - _children: [], + hover: {}, + active: {}, + selected: {}, }, - ], - }, - ], - }, - { - _id: "7fcf11e4-6f5b-4085-8e0d-9f3d44c98967", - _component: "##builtin/screenslot", - _styles: { - normal: { - flex: "1 1 auto", - display: "flex", - "flex-direction": "column", - "justify-content": "flex-start", - "align-items": "stretch", - "max-width": "100%", - "margin-left": "20px", - "margin-right": "20px", - width: "1400px", - padding: "20px", - }, - hover: {}, - active: {}, - selected: {}, + _code: "", + logoUrl: + "https://d33wubrfki0l68.cloudfront.net/aac32159d7207b5085e74a7ef67afbb7027786c5/2b1fd/img/logo/bb-emblem.svg", + title: "", + backgroundColor: "", + color: "", + borderWidth: "", + borderColor: "", + borderStyle: "", + _appId: "inst_cf8ace4_69efc0d72e6f443db2d4c902c14d9394", + _instanceName: "Navigation", + _children: [ + { + _id: "48b35328-4c91-4343-a6a3-1a1fd77b3386", + _component: "@budibase/standard-components/link", + _styles: { + normal: { + "font-family": "Inter", + "font-weight": "500", + color: "#000000", + "text-decoration-line": "none", + "font-size": "16px", + }, + hover: { + color: "#4285f4", + }, + active: {}, + selected: {}, + }, + _code: "", + url: "/", + openInNewTab: false, + text: "Home", + color: "", + hoverColor: "", + underline: false, + fontSize: "", + fontFamily: "initial", + _appId: "inst_cf8ace4_69efc0d72e6f443db2d4c902c14d9394", + _instanceName: "Home Link", + _children: [], + }, + ], + }, + ], }, - _code: "", - _children: [], - }, - ], - type: "div", - _styles: { - active: {}, - hover: {}, - normal: { - display: "flex", - "flex-direction": "column", - "align-items": "center", - "justify-content": "flex-start", - "margin-right": "auto", - "margin-left": "auto", - "min-height": "100%", - "background-image": - "linear-gradient(135deg, rgba(252,215,212,1) 20%, rgba(207,218,255,1) 100%);", - }, - selected: {}, - }, - _code: "", - className: "", - onLoad: [], - }, -} - -const UNAUTHENTICATED = { - componentLibraries: ["@budibase/standard-components"], - title: "{{ name }}", - favicon: "./_shared/favicon.png", - stylesheets: [], - name: LayoutTypes.UNAUTHENTICATED, - props: { - _id: "public-master-root", - _component: "@budibase/standard-components/container", - _children: [ - { - _id: "686c252d-dbf2-4e28-9078-414ba4719759", - _component: "@budibase/standard-components/login", - _styles: { - normal: { - padding: "64px", - background: "rgba(255, 255, 255, 0.4)", - "border-radius": "0.5rem", - "margin-top": "0px", - margin: "0px", - "line-height": "1", - "box-shadow": - "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)", - "font-size": "16px", - "font-family": "Inter", - flex: "0 1 auto", - transform: "0", + { + _id: "7fcf11e4-6f5b-4085-8e0d-9f3d44c98967", + _component: "##builtin/screenslot", + _styles: { + normal: { + flex: "1 1 auto", + display: "flex", + "flex-direction": "column", + "justify-content": "flex-start", + "align-items": "stretch", + "max-width": "100%", + "margin-left": "20px", + "margin-right": "20px", + width: "1400px", + padding: "20px", + }, + hover: {}, + active: {}, + selected: {}, }, - hover: {}, - active: {}, - selected: {}, + _code: "", + _children: [], }, - _code: "", - loginRedirect: "", - usernameLabel: "Username", - passwordLabel: "Password", - loginButtonLabel: "Login", - buttonClass: "", - _instanceName: "Login", - inputClass: "", - _children: [], - title: "Log in to {{ name }}", - buttonText: "Log In", - logo: - "https://d33wubrfki0l68.cloudfront.net/aac32159d7207b5085e74a7ef67afbb7027786c5/2b1fd/img/logo/bb-emblem.svg", + ], + type: "div", + _styles: { + active: {}, + hover: {}, + normal: { + display: "flex", + "flex-direction": "column", + "align-items": "center", + "justify-content": "flex-start", + "margin-right": "auto", + "margin-left": "auto", + "min-height": "100%", + "background-image": + "linear-gradient(135deg, rgba(252,215,212,1) 20%, rgba(207,218,255,1) 100%);", + }, + selected: {}, }, - ], - type: "div", - _styles: { - active: {}, - hover: {}, - normal: { - display: "flex", - "flex-direction": "column", - "align-items": "center", - "justify-content": "center", - "margin-right": "auto", - "margin-left": "auto", - "min-height": "100%", - "background-image": - "linear-gradient(135deg, rgba(252,215,212,1) 20%, rgba(207,218,255,1) 100%);", - }, - selected: {}, + _code: "", + className: "", + onLoad: [], }, - _code: "", - className: "", - onLoad: [], }, -} + { + componentLibraries: ["@budibase/standard-components"], + title: "{{ name }}", + favicon: "./_shared/favicon.png", + stylesheets: [], + name: "Unauthenticated", + props: { + _id: "public-master-root", + _component: "@budibase/standard-components/container", + _children: [ + { + _id: "686c252d-dbf2-4e28-9078-414ba4719759", + _component: "@budibase/standard-components/login", + _styles: { + normal: { + padding: "64px", + background: "rgba(255, 255, 255, 0.4)", + "border-radius": "0.5rem", + "margin-top": "0px", + margin: "0px", + "line-height": "1", + "box-shadow": + "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)", + "font-size": "16px", + "font-family": "Inter", + flex: "0 1 auto", + transform: "0", + }, + hover: {}, + active: {}, + selected: {}, + }, + _code: "", + loginRedirect: "", + usernameLabel: "Username", + passwordLabel: "Password", + loginButtonLabel: "Login", + buttonClass: "", + _instanceName: "Login", + inputClass: "", + _children: [], + title: "Log in to {{ name }}", + buttonText: "Log In", + logo: + "https://d33wubrfki0l68.cloudfront.net/aac32159d7207b5085e74a7ef67afbb7027786c5/2b1fd/img/logo/bb-emblem.svg", + }, + ], + type: "div", + _styles: { + active: {}, + hover: {}, + normal: { + display: "flex", + "flex-direction": "column", + "align-items": "center", + "justify-content": "center", + "margin-right": "auto", + "margin-left": "auto", + "min-height": "100%", + "background-image": + "linear-gradient(135deg, rgba(252,215,212,1) 20%, rgba(207,218,255,1) 100%);", + }, + selected: {}, + }, + _code: "", + className: "", + onLoad: [], + }, + }, +] -module.exports = { MAIN, UNAUTHENTICATED, LayoutTypes } +module.exports = { + BASE_LAYOUTS, +} diff --git a/packages/server/src/utilities/mustache.js b/packages/server/src/utilities/mustache.js new file mode 100644 index 0000000000..0428bdc03d --- /dev/null +++ b/packages/server/src/utilities/mustache.js @@ -0,0 +1,73 @@ +const handlebars = require("handlebars") + +handlebars.registerHelper("object", value => { + return new handlebars.SafeString(JSON.stringify(value)) +}) + +/** + * When running mustache statements to execute on the context of the automation it possible user's may input mustache + * in a few different forms, some of which are invalid but are logically valid. An example of this would be the mustache + * statement "{{steps[0].revision}}" here it is obvious the user is attempting to access an array or object using array + * like operators. These are not supported by Mustache and therefore the statement will fail. This function will clean up + * the mustache statement so it instead reads as "{{steps.0.revision}}" which is valid and will work. It may also be expanded + * to include any other mustache statement cleanup that has been deemed necessary for the system. + * + * @param {string} string The string which *may* contain mustache statements, it is OK if it does not contain any. + * @returns {string} The string that was input with cleaned up mustache statements as required. + */ +function cleanMustache(string) { + let charToReplace = { + "[": ".", + "]": "", + } + let regex = new RegExp(/{{[^}}]*}}/g) + let matches = string.match(regex) + if (matches == null) { + return string + } + for (let match of matches) { + let baseIdx = string.indexOf(match) + for (let key of Object.keys(charToReplace)) { + let idxChar = match.indexOf(key) + if (idxChar !== -1) { + string = + string.slice(baseIdx, baseIdx + idxChar) + + charToReplace[key] + + string.slice(baseIdx + idxChar + 1) + } + } + } + return string +} + +/** + * Given an input object this will recurse through all props to try and update + * any handlebars/mustache statements within. + * @param {object|array} inputs The input structure which is to be recursed, it is important to note that + * if the structure contains any cycles then this will fail. + * @param {object} context The context that handlebars should fill data from. + * @returns {object|array} The structure input, as fully updated as possible. + */ +function recurseMustache(inputs, context) { + // JSON stringify will fail if there are any cycles, stops infinite recursion + try { + JSON.stringify(inputs) + } catch (err) { + throw "Unable to process inputs to JSON, cannot recurse" + } + for (let key of Object.keys(inputs)) { + let val = inputs[key] + if (typeof val === "string") { + val = cleanMustache(inputs[key]) + const template = handlebars.compile(val) + inputs[key] = template(context) + } + // this covers objects and arrays + else if (typeof val === "object") { + inputs[key] = recurseMustache(inputs[key], context) + } + } + return inputs +} + +exports.recurseMustache = recurseMustache From ddca22245cdf1d007110c2cb08a31a3ecfc7bec7 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 23 Nov 2020 16:56:35 +0000 Subject: [PATCH 04/79] Changing up how the static assets are compiled, making a 'css' directory in which individual assets CSS is written and then bundled together meaning that not all assets need to be sent up at once for css bundle to be built. --- .../server/src/api/controllers/application.js | 10 +-- packages/server/src/api/controllers/layout.js | 10 +-- packages/server/src/api/controllers/screen.js | 5 +- .../utilities/builder/compileStaticAssets.js | 75 +++++++++---------- 4 files changed, 48 insertions(+), 52 deletions(-) diff --git a/packages/server/src/api/controllers/application.js b/packages/server/src/api/controllers/application.js index 6f5bf7f1e1..1bc1be09b7 100644 --- a/packages/server/src/api/controllers/application.js +++ b/packages/server/src/api/controllers/application.js @@ -199,19 +199,19 @@ const createEmptyAppPackage = async (ctx, app) => { fs.mkdirpSync(newAppFolder) - const bulkDocs = [] + let screensAndLayouts = [] for (let layout of BASE_LAYOUTS) { const cloned = cloneDeep(layout) cloned._id = generateLayoutID() cloned.title = app.name - bulkDocs.push(recurseMustache(cloned, app)) + screensAndLayouts.push(recurseMustache(cloned, app)) } const homeScreen = cloneDeep(HOME_SCREEN) homeScreen._id = generateScreenID() - bulkDocs.push(homeScreen) - await db.bulkDocs(bulkDocs) + screensAndLayouts.push(homeScreen) - await compileStaticAssets(app._id) + screensAndLayouts = await compileStaticAssets(app._id, screensAndLayouts) + await db.bulkDocs(screensAndLayouts) return newAppFolder } diff --git a/packages/server/src/api/controllers/layout.js b/packages/server/src/api/controllers/layout.js index eae7b04d49..9e22fe027d 100644 --- a/packages/server/src/api/controllers/layout.js +++ b/packages/server/src/api/controllers/layout.js @@ -4,12 +4,10 @@ const compileStaticAssets = require("../../utilities/builder/compileStaticAssets exports.save = async function(ctx) { const db = new CouchDB(ctx.user.appId) - const appPackage = ctx.request.body + let layout = ctx.request.body - // remove special doc props which couch will complain about - delete appPackage.layout._css - appPackage.layout._id = appPackage.layout._id || generateLayoutID() - ctx.body = await db.put(appPackage.layout) - await compileStaticAssets(ctx.user.appId) + layout._id = layout._id || generateLayoutID() + layout = await compileStaticAssets(ctx.user.appId, layout) + ctx.body = await db.put(layout) ctx.status = 200 } diff --git a/packages/server/src/api/controllers/screen.js b/packages/server/src/api/controllers/screen.js index acf6df22f0..588b642156 100644 --- a/packages/server/src/api/controllers/screen.js +++ b/packages/server/src/api/controllers/screen.js @@ -1,5 +1,6 @@ const CouchDB = require("../../db") const { getScreenParams, generateScreenID } = require("../../db/utils") +const compileStaticAssets = require("../../utilities/builder/compileStaticAssets") const { AccessController } = require("../../utilities/security/accessLevels") exports.fetch = async ctx => { @@ -23,12 +24,12 @@ exports.fetch = async ctx => { exports.save = async ctx => { const appId = ctx.user.appId const db = new CouchDB(appId) - const screen = ctx.request.body + let screen = ctx.request.body if (!screen._id) { screen._id = generateScreenID() } - delete screen._css + screen = await compileStaticAssets(ctx.user.appId, screen) const response = await db.put(screen) ctx.message = `Screen ${screen.name} saved.` diff --git a/packages/server/src/utilities/builder/compileStaticAssets.js b/packages/server/src/utilities/builder/compileStaticAssets.js index a64c2f0b98..43c7309f5e 100644 --- a/packages/server/src/utilities/builder/compileStaticAssets.js +++ b/packages/server/src/utilities/builder/compileStaticAssets.js @@ -1,66 +1,63 @@ -const { ensureDir, constants, copyFile, writeFile } = require("fs-extra") +const { + ensureDir, + constants, + copyFile, + writeFile, + readdir, + readFile, +} = require("fs-extra") const { join } = require("../centralPath") const { budibaseAppsDir } = require("../budibaseDir") -const CouchDB = require("../../db") -const { getScreenParams, getLayoutParams } = require("../../db/utils") -async function getAppPackage(appId) { - const db = new CouchDB(appId) - let params = { - include_docs: true, - } - let [screens, layouts] = await Promise.all([ - db.allDocs(getScreenParams(null, params)), - db.allDocs(getLayoutParams(null, params)), - ]) - screens = screens.rows.map(row => row.doc) - layouts = layouts.rows.map(row => row.doc) - if (!screens) { - screens = [] - } - if (!layouts) { - layouts = [] - } - return { screens, layouts } -} +const CSS_DIRECTORY = "css" /** * Compile all the non-db static web assets that are required for the running of * a budibase application. This includes CSS, the JSON structure of the DOM and * the client library, a script responsible for reading the JSON structure * and rendering the application. - * @param {string} appId - id of the application we want to compile static assets for + * @param {string} appId id of the application we want to compile static assets for + * @param {array|object} assets a list of screens or screen layouts for which the CSS should be extracted and stored. */ -module.exports = async appId => { +module.exports = async (appId, assets) => { const publicPath = join(budibaseAppsDir(), appId, "public") - const pkg = await getAppPackage(appId) - await ensureDir(publicPath) - await buildCssBundle(publicPath, pkg) - await copyClientLib(publicPath) + for (let asset of Array.isArray(assets) ? assets : [assets]) { + await buildCssBundle(publicPath, asset) + await copyClientLib(publicPath) + // remove props that shouldn't be present when written to DB + if (asset._css) { + delete asset._css + } + } + return assets } /** * Reads the _css property of all screens and the screen layouts, and creates a singular CSS * bundle for the app at /public/bundle.css * @param {String} publicPath - path to the public assets directory of the budibase application - * @param {Object} pkg - app package information + * @param {Object} asset a single screen or screen layout which is being updated */ -const buildCssBundle = async (publicPath, pkg) => { +const buildCssBundle = async (publicPath, asset) => { + const cssPath = join(publicPath, CSS_DIRECTORY) let cssString = "" - for (let screen of pkg.screens || []) { - if (!screen._css) continue - if (screen._css.trim().length === 0) { - delete screen._css - continue - } - cssString += screen._css + // create a singular CSS file for this asset + const assetCss = asset._css ? asset._css.trim() : "" + if (assetCss.length !== 0) { + await ensureDir(cssPath) + await writeFile(join(cssPath, asset._id), assetCss) } - if (pkg.layout._css) cssString += pkg.layout._css + // bundle up all the CSS in the directory into one top level CSS file + const cssFiles = await readdir(cssPath) + for (let filename of cssFiles) { + const css = await readFile(filename) + cssString += css + } - writeFile(join(publicPath, "bundle.css"), cssString) + await writeFile(join(publicPath, "bundle.css"), cssString) } /** From e26baa6faf35bd558ee0bd7e4fe4118542ed6e2c Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 24 Nov 2020 18:11:18 +0000 Subject: [PATCH 05/79] Creating CSS generation capabilities in the server. --- .../server/src/api/controllers/application.js | 10 +++- .../src/api/controllers/static/index.js | 6 +++ packages/server/src/api/routes/static.js | 1 + .../src/api/routes/tests/generateCss.spec.js | 46 +++++++++++++++++++ packages/server/src/constants/screens.js | 28 ++++++++++- .../src/utilities/builder/generateCss.js | 43 +++++++++++++++++ 6 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 packages/server/src/api/routes/tests/generateCss.spec.js create mode 100644 packages/server/src/utilities/builder/generateCss.js diff --git a/packages/server/src/api/controllers/application.js b/packages/server/src/api/controllers/application.js index 1bc1be09b7..6cbf3891d5 100644 --- a/packages/server/src/api/controllers/application.js +++ b/packages/server/src/api/controllers/application.js @@ -30,6 +30,7 @@ const { BASE_LAYOUTS } = require("../../constants/layouts") const { HOME_SCREEN } = require("../../constants/screens") const { cloneDeep } = require("lodash/fp") const { recurseMustache } = require("../../utilities/mustache") +const { generateAssetCss } = require("../../utilities/builder/generateCss") const APP_PREFIX = DocumentTypes.APP + SEPARATOR @@ -119,9 +120,14 @@ exports.fetchAppDefinition = async function(ctx) { exports.fetchAppPackage = async function(ctx) { const db = new CouchDB(ctx.params.appId) const application = await db.get(ctx.params.appId) - const layouts = await getLayouts(db) - const screens = await getScreens(db) + const [layouts, screens] = await Promise.all([getLayouts(db), getScreens(db)]) + for (let layout of layouts) { + layout._css = generateAssetCss([layout.props]) + } + for (let screen of screens) { + screen._css = generateAssetCss([screen.props]) + } ctx.body = { application, screens, diff --git a/packages/server/src/api/controllers/static/index.js b/packages/server/src/api/controllers/static/index.js index 5676afb230..51aef4cd34 100644 --- a/packages/server/src/api/controllers/static/index.js +++ b/packages/server/src/api/controllers/static/index.js @@ -17,10 +17,16 @@ const setBuilderToken = require("../../../utilities/builder/setBuilderToken") const fileProcessor = require("../../../utilities/fileProcessor") const { AuthTypes } = require("../../../constants") const env = require("../../../environment") +const { generateAssetCss } = require("../../../utilities/builder/generateCss") // this was the version before we started versioning the component library const COMP_LIB_BASE_APP_VERSION = "0.2.5" +exports.generateCss = async function(ctx) { + const structure = ctx.request.body + ctx.body = generateAssetCss(structure) +} + exports.serveBuilder = async function(ctx) { let builderPath = resolve(__dirname, "../../../../builder") if (ctx.file === "index.html") { diff --git a/packages/server/src/api/routes/static.js b/packages/server/src/api/routes/static.js index a519a63781..f8f399a4ea 100644 --- a/packages/server/src/api/routes/static.js +++ b/packages/server/src/api/routes/static.js @@ -24,6 +24,7 @@ if (env.NODE_ENV !== "production") { } router + .post("/api/css/generate", authorized(BUILDER), controller.generateCss) .post( "/api/attachments/process", authorized(BUILDER), diff --git a/packages/server/src/api/routes/tests/generateCss.spec.js b/packages/server/src/api/routes/tests/generateCss.spec.js new file mode 100644 index 0000000000..84ad2eafc6 --- /dev/null +++ b/packages/server/src/api/routes/tests/generateCss.spec.js @@ -0,0 +1,46 @@ +const { generateAssetCss, generateCss } = require("../../../utilities/builder/generateCss") + +describe("generate_css", () => { + it("Check how array styles are output", () => { + expect(generateCss({ margin: ["0", "10", "0", "15"] })).toBe("margin: 0 10 0 15;") + }) + + it("Check handling of an array with empty string values", () => { + expect(generateCss({ padding: ["", "", "", ""] })).toBe("") + }) + + it("Check handling of an empty array", () => { + expect(generateCss({ margin: [] })).toBe("") + }) + + it("Check handling of valid font property", () => { + expect(generateCss({ "font-size": "10px" })).toBe("font-size: 10px;") + }) +}) + + +describe("generate_screen_css", () => { + const normalComponent = { _id: "123-456", _component: "@standard-components/header", _children: [], _styles: { normal: { "font-size": "16px" }, hover: {}, active: {}, selected: {} } } + + it("Test generation of normal css styles", () => { + expect(generateAssetCss([normalComponent])).toBe(".header-123-456 {\nfont-size: 16px;\n}") + }) + + const hoverComponent = { _id: "123-456", _component: "@standard-components/header", _children: [], _styles: { normal: {}, hover: {"font-size": "16px"}, active: {}, selected: {} } } + + it("Test generation of hover css styles", () => { + expect(generateAssetCss([hoverComponent])).toBe(".header-123-456:hover {\nfont-size: 16px;\n}") + }) + + const selectedComponent = { _id: "123-456", _component: "@standard-components/header", _children: [], _styles: { normal: {}, hover: {}, active: {}, selected: { "font-size": "16px" } } } + + it("Test generation of selection css styles", () => { + expect(generateAssetCss([selectedComponent])).toBe(".header-123-456::selection {\nfont-size: 16px;\n}") + }) + + const emptyComponent = { _id: "123-456", _component: "@standard-components/header", _children: [], _styles: { normal: {}, hover: {}, active: {}, selected: {} } } + + it("Testing handling of empty component styles", () => { + expect(generateAssetCss([emptyComponent])).toBe("") + }) +}) \ No newline at end of file diff --git a/packages/server/src/constants/screens.js b/packages/server/src/constants/screens.js index 5c5a9dfd26..dbdaf2b975 100644 --- a/packages/server/src/constants/screens.js +++ b/packages/server/src/constants/screens.js @@ -104,5 +104,31 @@ exports.HOME_SCREEN = { route: "/", accessLevelId: BUILTIN_LEVEL_IDS.BASIC, }, - name: "d834fea2-1b3e-4320-ab34-f9009f5ecc59", + name: "home-screen", +} + +exports.LOGIN_SCREEN = { + description: "", + url: "", + props: { + _id: "781e497e-2e7c-11eb-adc1-0242ac120002", + _component: "@budibase/standard-components/login", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _code: "", + className: "", + onLoad: [], + type: "div", + _children: [], + _instanceName: "Login", + }, + routing: { + route: "/", + accessLevelId: BUILTIN_LEVEL_IDS.PUBLIC, + }, + name: "login-screen", } diff --git a/packages/server/src/utilities/builder/generateCss.js b/packages/server/src/utilities/builder/generateCss.js new file mode 100644 index 0000000000..c3d72c741f --- /dev/null +++ b/packages/server/src/utilities/builder/generateCss.js @@ -0,0 +1,43 @@ +exports.generateAssetCss = component_arr => { + let styles = "" + for (const { _styles, _id, _children, _component } of component_arr) { + let [componentName] = _component.match(/[a-z]*$/) + Object.keys(_styles).forEach(selector => { + const cssString = exports.generateCss(_styles[selector]) + if (cssString) { + styles += exports.applyClass(_id, componentName, cssString, selector) + } + }) + if (_children && _children.length) { + styles += exports.generateAssetCss(_children) + "\n" + } + } + return styles.trim() +} + +exports.generateCss = style => { + let cssString = Object.entries(style).reduce((str, [key, value]) => { + if (typeof value === "string") { + if (value) { + return (str += `${key}: ${value};\n`) + } + } else if (Array.isArray(value)) { + if (value.length > 0 && !value.every(v => v === "")) { + return (str += `${key}: ${value.join(" ")};\n`) + } + } + + return str + }, "") + + return (cssString || "").trim() +} + +exports.applyClass = (id, name = "element", styles, selector) => { + if (selector === "normal") { + return `.${name}-${id} {\n${styles}\n}` + } else { + let sel = selector === "selected" ? "::selection" : `:${selector}` + return `.${name}-${id}${sel} {\n${styles}\n}` + } +} From e1314b0d8873a90087a98ad2cb5a2d5be1bd852e Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 24 Nov 2020 18:11:34 +0000 Subject: [PATCH 06/79] Starting work on builder, very broken. --- .../builder/src/builderStore/generate_css.js | 43 --------- packages/builder/src/builderStore/index.js | 34 ++++--- .../src/builderStore/store/frontend.js | 96 ++++++------------- .../modals/CreateTableModal.svelte | 2 +- .../components/start/CreateAppModal.svelte | 3 +- .../AppPreview/CurrentItemPreview.svelte | 2 +- .../ScreenDropdownMenu.svelte | 2 +- .../ComponentSelectionList.svelte | 4 +- .../userInterface/FrontendNavigatePane.svelte | 4 +- .../{PagesList.svelte => LayoutsList.svelte} | 14 +-- .../userInterface/SettingsView.svelte | 12 +-- .../userInterface/temporaryPanelStructure.js | 3 +- .../automate/[automation]/_layout.svelte | 2 +- .../design/[page]/[screen]/_layout.svelte | 6 +- .../design/[page]/_layout.svelte | 2 +- packages/builder/tests/generate_css.spec.js | 51 ---------- 16 files changed, 76 insertions(+), 204 deletions(-) delete mode 100644 packages/builder/src/builderStore/generate_css.js rename packages/builder/src/components/userInterface/{PagesList.svelte => LayoutsList.svelte} (76%) delete mode 100644 packages/builder/tests/generate_css.spec.js diff --git a/packages/builder/src/builderStore/generate_css.js b/packages/builder/src/builderStore/generate_css.js deleted file mode 100644 index 2bb5a3bd2e..0000000000 --- a/packages/builder/src/builderStore/generate_css.js +++ /dev/null @@ -1,43 +0,0 @@ -export const generate_screen_css = component_arr => { - let styles = "" - for (const { _styles, _id, _children, _component } of component_arr) { - let [componentName] = _component.match(/[a-z]*$/) - Object.keys(_styles).forEach(selector => { - const cssString = generate_css(_styles[selector]) - if (cssString) { - styles += apply_class(_id, componentName, cssString, selector) - } - }) - if (_children && _children.length) { - styles += generate_screen_css(_children) + "\n" - } - } - return styles.trim() -} - -export const generate_css = style => { - let cssString = Object.entries(style).reduce((str, [key, value]) => { - if (typeof value === "string") { - if (value) { - return (str += `${key}: ${value};\n`) - } - } else if (Array.isArray(value)) { - if (value.length > 0 && !value.every(v => v === "")) { - return (str += `${key}: ${value.join(" ")};\n`) - } - } - - return str - }, "") - - return (cssString || "").trim() -} - -export const apply_class = (id, name = "element", styles, selector) => { - if (selector === "normal") { - return `.${name}-${id} {\n${styles}\n}` - } else { - let sel = selector === "selected" ? "::selection" : `:${selector}` - return `.${name}-${id}${sel} {\n${styles}\n}` - } -} diff --git a/packages/builder/src/builderStore/index.js b/packages/builder/src/builderStore/index.js index ae77889404..aac2437560 100644 --- a/packages/builder/src/builderStore/index.js +++ b/packages/builder/src/builderStore/index.js @@ -10,19 +10,29 @@ export const backendUiStore = getBackendUiStore() export const automationStore = getAutomationStore() export const themeStore = getThemeStore() +export const currentAsset = derived(store, $store => { + const layout = $store.layouts ? $store.layouts.find(layout => layout._id === $store.currentAssetId) : null + if (layout) { + return layout + } + const screen = $store.screens ? $store.screens.find(screen => screen._id === $store.currentAssetId) : null + if (screen) { + return screen + } + return null +}) + +export const currentAssetName = derived(store, () => { + return currentAsset.name +}) + +// leave this as before for consistency export const allScreens = derived(store, $store => { - let screens = [] - if ($store.pages == null) { - return screens - } - for (let page of Object.values($store.pages)) { - screens = screens.concat(page._screens) - } - return screens + return $store.screens }) export const currentScreens = derived(store, $store => { - const currentScreens = $store.pages[$store.currentPageName]?._screens + const currentScreens = $store.layouts[currentAssetName]?._screens if (currentScreens == null) { return [] } @@ -31,12 +41,6 @@ export const currentScreens = derived(store, $store => { : Object.values(currentScreens) }) -export const selectedPage = derived(store, $store => { - if (!$store.pages) return null - - return $store.pages[$store.currentPageName || "main"] -}) - export const initialise = async () => { try { await analytics.activate() diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index 778c7f7be5..0523b6693f 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -5,8 +5,7 @@ import { getBuiltin, makePropsSafe, } from "components/userInterface/pagesParsing/createProps" -import { allScreens, backendUiStore, selectedPage } from "builderStore" -import { generate_screen_css } from "../generate_css" +import { allScreens, backendUiStore, currentAsset } from "builderStore" import { fetchComponentLibDefinitions } from "../loadComponentLibraries" import api from "../api" import { DEFAULT_PAGES_OBJECT } from "../../constants" @@ -23,14 +22,15 @@ const INITIAL_FRONTEND_STATE = { apps: [], name: "", description: "", - pages: DEFAULT_PAGES_OBJECT, + layouts: DEFAULT_PAGES_OBJECT, + screens: [], mainUi: {}, unauthenticatedUi: {}, components: [], currentPreviewItem: null, currentComponentInfo: null, currentFrontEndType: "none", - currentPageName: "", + currentAssetId: "", currentComponentProps: null, errors: [], hasAppPackage: false, @@ -43,52 +43,12 @@ export const getFrontendStore = () => { const store = writable({ ...INITIAL_FRONTEND_STATE }) store.actions = { - // TODO: REFACTOR initialise: async pkg => { + const layouts = pkg.layouts, screens = pkg.screens, application = pkg.application store.update(state => { - state.appId = pkg.application._id + state.appId = application._id return state }) - const screens = await api.get("/api/screens").then(r => r.json()) - - const mainScreens = screens.filter(screen => - screen._id.includes(pkg.pages.main._id) - ), - unauthScreens = screens.filter(screen => - screen._id.includes(pkg.pages.unauthenticated._id) - ) - pkg.pages = { - main: { - ...pkg.pages.main, - _screens: mainScreens, - }, - unauthenticated: { - ...pkg.pages.unauthenticated, - _screens: unauthScreens, - }, - } - - // if the app has just been created - // we need to build the CSS and save - if (pkg.justCreated) { - for (let pageName of ["main", "unauthenticated"]) { - const page = pkg.pages[pageName] - store.actions.screens.regenerateCss(page) - for (let screen of page._screens) { - store.actions.screens.regenerateCss(screen) - } - - await api.post(`/api/pages/${page._id}`, { - page: { - componentLibraries: pkg.application.componentLibraries, - ...page, - }, - screens: page._screens, - }) - } - } - - pkg.justCreated = false const components = await fetchComponentLibDefinitions(pkg.application._id) @@ -99,7 +59,8 @@ export const getFrontendStore = () => { name: pkg.application.name, description: pkg.application.description, appId: pkg.application._id, - pages: pkg.pages, + layouts, + screens, hasAppPackage: true, builtins: [getBuiltin("##builtin/screenslot")], appInstance: pkg.application.instance, @@ -111,7 +72,7 @@ export const getFrontendStore = () => { store.update(state => { state.currentFrontEndType = type - const page = get(selectedPage) + const page = get(currentAsset) const pageOrScreen = type === "page" ? page : page._screens[0] @@ -168,7 +129,7 @@ export const getFrontendStore = () => { await savePromise }, save: async screen => { - const page = get(selectedPage) + const page = get(currentAsset) const currentPageScreens = page._screens const creatingNewScreen = screen._id === undefined @@ -197,14 +158,15 @@ export const getFrontendStore = () => { state.currentComponentInfo = safeProps screen.props = safeProps } - savePromise = store.actions.pages.save() + savePromise = store.actions.layouts.save() return state }) if (savePromise) await savePromise }, - regenerateCss: screen => { - screen._css = generate_screen_css([screen.props]) + regenerateCss: async asset => { + const response = await api.post("/api/css/generate", asset) + asset._css = await response.json() }, regenerateCssForCurrentScreen: () => { const { currentPreviewItem } = get(store) @@ -218,7 +180,7 @@ export const getFrontendStore = () => { const screensToDelete = Array.isArray(screens) ? screens : [screens] store.update(state => { - const currentPage = get(selectedPage) + const currentPage = get(currentAsset) for (let screenToDelete of screensToDelete) { // Remove screen from current page as well @@ -242,17 +204,17 @@ export const getFrontendStore = () => { if (state.currentFrontEndType !== "page") { await store.actions.screens.save(state.currentPreviewItem) } - await store.actions.pages.save() + await store.actions.layouts.save() }, }, - pages: { + layouts: { select: pageName => { store.update(state => { - const currentPage = state.pages[pageName] + const currentPage = state.layouts[pageName] state.currentFrontEndType = "page" state.currentView = "detail" - state.currentPageName = pageName + state.currentAssetId = pageName // This is the root of many problems. // Uncaught (in promise) TypeError: Cannot read property '_component' of undefined @@ -264,11 +226,11 @@ export const getFrontendStore = () => { ) state.currentComponentInfo = safeProps currentPage.props = safeProps - state.currentPreviewItem = state.pages[pageName] + state.currentPreviewItem = state.layouts[pageName] store.actions.screens.regenerateCssForCurrentScreen() for (let screen of get(allScreens)) { - screen._css = generate_screen_css([screen.props]) + screen._css = store.actions.screens.regenerateCss(screen) } return state @@ -276,7 +238,7 @@ export const getFrontendStore = () => { }, save: async page => { const storeContents = get(store) - const pageName = storeContents.currentPageName || "main" + const pageName = storeContents.currentAssetId || "main" const pageToSave = page || storeContents.pages[pageName] // TODO: revisit. This sends down a very weird payload @@ -293,7 +255,7 @@ export const getFrontendStore = () => { if (!json.ok) throw new Error("Error updating page") store.update(state => { - state.pages[pageName]._rev = json.rev + state.layouts[pageName]._rev = json.rev return state }) }, @@ -324,7 +286,7 @@ export const getFrontendStore = () => { if ( componentToAdd.startsWith("##") && - findSlot(state.pages[state.currentPageName].props._children) + findSlot(state.layouts[state.currentAssetId].props._children) ) { return state } @@ -480,7 +442,7 @@ export const getFrontendStore = () => { store.update(state => { // Try to extract a nav component from the master screen const nav = findChildComponentType( - state.pages.main, + state.layouts.main, "@budibase/standard-components/Navigation" ) if (nav) { @@ -515,12 +477,12 @@ export const getFrontendStore = () => { // Save page and regenerate all CSS because otherwise weird things happen nav._children = [...nav._children, newLink] - state.currentPageName = "main" - store.actions.screens.regenerateCss(state.pages.main) - for (let screen of state.pages.main._screens) { + state.currentAssetId = "main" + store.actions.screens.regenerateCss(state.layouts.main) + for (let screen of state.layouts.main._screens) { store.actions.screens.regenerateCss(screen) } - savePromise = store.actions.pages.save() + savePromise = store.actions.layouts.save() } return state }) diff --git a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte index f064ff923c..d111821fb3 100644 --- a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte +++ b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte @@ -51,7 +51,7 @@ const screens = screenTemplates($store, [table]) .filter(template => defaultScreens.includes(template.id)) .map(template => template.create()) - store.actions.pages.select("main") + store.actions.layouts.select("main") for (let screen of screens) { // Record the table that created this screen so we can link it later screen.autoTableId = table._id diff --git a/packages/builder/src/components/start/CreateAppModal.svelte b/packages/builder/src/components/start/CreateAppModal.svelte index 049b21c995..99539e965f 100644 --- a/packages/builder/src/components/start/CreateAppModal.svelte +++ b/packages/builder/src/components/start/CreateAppModal.svelte @@ -155,9 +155,8 @@ const pkg = await applicationPkg.json() if (applicationPkg.ok) { backendUiStore.actions.reset() - pkg.justCreated = true await store.actions.initialise(pkg) - automationStore.actions.fetch() + await automationStore.actions.fetch() } else { throw new Error(pkg) } diff --git a/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte b/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte index c9fe02e786..2e7d719084 100644 --- a/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte +++ b/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte @@ -56,7 +56,7 @@ screenPlaceholder.props._id = "screenslot-placeholder" // Extract data to pass to the iframe - $: page = $store.pages[$store.currentPageName] + $: page = $store.layouts[$store.currentPageName] $: screen = $store.currentFrontEndType === "page" ? screenPlaceholder diff --git a/packages/builder/src/components/userInterface/ComponentNavigationTree/ScreenDropdownMenu.svelte b/packages/builder/src/components/userInterface/ComponentNavigationTree/ScreenDropdownMenu.svelte index 3ecd8ce0dc..07f0f042a1 100644 --- a/packages/builder/src/components/userInterface/ComponentNavigationTree/ScreenDropdownMenu.svelte +++ b/packages/builder/src/components/userInterface/ComponentNavigationTree/ScreenDropdownMenu.svelte @@ -19,7 +19,7 @@ // update the page if required store.update(state => { if (state.currentPreviewItem._id === screen) { - store.actions.pages.select($store.currentPageName) + store.actions.layouts.select($store.currentPageName) notifier.success(`Screen ${screenToDelete.name} deleted successfully.`) $goto(`./:page/page-layout`) } diff --git a/packages/builder/src/components/userInterface/ComponentSelectionList.svelte b/packages/builder/src/components/userInterface/ComponentSelectionList.svelte index f099984e5a..edce828f41 100644 --- a/packages/builder/src/components/userInterface/ComponentSelectionList.svelte +++ b/packages/builder/src/components/userInterface/ComponentSelectionList.svelte @@ -1,6 +1,6 @@
- {#each pages as { title, id }} - {/each} diff --git a/packages/builder/src/components/userInterface/SettingsView.svelte b/packages/builder/src/components/userInterface/SettingsView.svelte index 8afdfce7ec..1ceb0f6c06 100644 --- a/packages/builder/src/components/userInterface/SettingsView.svelte +++ b/packages/builder/src/components/userInterface/SettingsView.svelte @@ -4,7 +4,7 @@ import Input from "./PropertyPanelControls/Input.svelte" import { goto } from "@sveltech/routify" import { excludeProps } from "./propertyCategories.js" - import { store, allScreens } from "builderStore" + import { store, allScreens, currentAsset } from "builderStore" import { walkProps } from "builderStore/storeUtils" export let panelDefinition = [] @@ -58,15 +58,15 @@ } }) } - // check page first - lookForDuplicate($store.pages[$store.currentPageName].props) - if (duplicate) return true - + // check against layouts + for (let layout of $store.layouts) { + lookForDuplicate(layout.props) + } // if viewing screen, check current screen for duplicate if ($store.currentFrontEndType === "screen") { lookForDuplicate($store.currentPreviewItem.props) } else { - // viewing master page - need to dedupe against all screens + // need to dedupe against all screens for (let screen of $allScreens) { lookForDuplicate(screen.props) } diff --git a/packages/builder/src/components/userInterface/temporaryPanelStructure.js b/packages/builder/src/components/userInterface/temporaryPanelStructure.js index e18ca6014f..5c18e012b0 100644 --- a/packages/builder/src/components/userInterface/temporaryPanelStructure.js +++ b/packages/builder/src/components/userInterface/temporaryPanelStructure.js @@ -1185,6 +1185,7 @@ export default { settings: [{ label: "Logo URL", key: "logoUrl", control: Input }], }, }, + // TODO: need to deal with this { name: "Login", _component: "@budibase/standard-components/login", @@ -1192,7 +1193,7 @@ export default { "A component that automatically generates a login screen for your app.", icon: "ri-login-box-line", children: [], - showOnPages: ["unauthenticated"], + showOnAsset: ["login-screen"], properties: { design: { ...all }, settings: [ diff --git a/packages/builder/src/pages/[application]/automate/[automation]/_layout.svelte b/packages/builder/src/pages/[application]/automate/[automation]/_layout.svelte index a61df114db..87d314e5bd 100644 --- a/packages/builder/src/pages/[application]/automate/[automation]/_layout.svelte +++ b/packages/builder/src/pages/[application]/automate/[automation]/_layout.svelte @@ -1,4 +1,4 @@ diff --git a/packages/builder/src/pages/[application]/design/[page]/[screen]/_layout.svelte b/packages/builder/src/pages/[application]/design/[page]/[screen]/_layout.svelte index 99434b7cd5..56884a4879 100644 --- a/packages/builder/src/pages/[application]/design/[page]/[screen]/_layout.svelte +++ b/packages/builder/src/pages/[application]/design/[page]/[screen]/_layout.svelte @@ -14,7 +14,7 @@ if (!validScreen) { // Go to main layout if URL set to invalid screen - store.actions.pages.select("main") + store.actions.layouts.select("main") $goto("../../main") } else { // Otherwise proceed to set screen @@ -23,7 +23,7 @@ // There are leftover stuff, like IDs, so navigate the components and find the ID and select it. if ($leftover) { // Get the correct screen children. - const screenChildren = $store.pages[$params.page]._screens.find( + const screenChildren = $store.layouts[$params.page]._screens.find( screen => screen._id === $params.screen || screen._id === decodeURIComponent($params.screen) @@ -37,7 +37,7 @@ // There are leftover stuff, like IDs, so navigate the components and find the ID and select it. if ($leftover) { - findComponent(componentIds, $store.pages[$params.page].props._children) + findComponent(componentIds, $store.layouts[$params.page].props._children) } } diff --git a/packages/builder/src/pages/[application]/design/[page]/_layout.svelte b/packages/builder/src/pages/[application]/design/[page]/_layout.svelte index d07a4c5695..45f577a0a8 100644 --- a/packages/builder/src/pages/[application]/design/[page]/_layout.svelte +++ b/packages/builder/src/pages/[application]/design/[page]/_layout.svelte @@ -2,7 +2,7 @@ import { params } from "@sveltech/routify" import { store } from "builderStore" - store.actions.pages.select($params.page) + store.actions.layouts.select($params.page) diff --git a/packages/builder/tests/generate_css.spec.js b/packages/builder/tests/generate_css.spec.js deleted file mode 100644 index 5fcdef25c1..0000000000 --- a/packages/builder/tests/generate_css.spec.js +++ /dev/null @@ -1,51 +0,0 @@ -import { - generate_css, - generate_screen_css, -} from "../src/builderStore/generate_css.js" - -describe("generate_css", () => { - - - test("Check how array styles are output", () => { - expect(generate_css({ margin: ["0", "10", "0", "15"] })).toBe("margin: 0px 10px 0px 15px;") - }) - - test("Check handling of an array with empty string values", () => { - expect(generate_css({ padding: ["", "", "", ""] })).toBe("") - }) - - test("Check handling of an empty array", () => { - expect(generate_css({ margin: [] })).toBe("") - }) - - test("Check handling of valid font property", () => { - expect(generate_css({ "font-size": "10px" })).toBe("font-size: 10px;") - }) -}) - - -describe("generate_screen_css", () => { - const normalComponent = { _id: "123-456", _component: "@standard-components/header", _children: [], _styles: { normal: { "font-size": "16px" }, hover: {}, active: {}, selected: {} } } - - test("Test generation of normal css styles", () => { - expect(generate_screen_css([normalComponent])).toBe(".header-123-456 {\nfont-size: 16px;\n}") - }) - - const hoverComponent = { _id: "123-456", _component: "@standard-components/header", _children: [], _styles: { normal: {}, hover: {"font-size": "16px"}, active: {}, selected: {} } } - - test("Test generation of hover css styles", () => { - expect(generate_screen_css([hoverComponent])).toBe(".header-123-456:hover {\nfont-size: 16px;\n}") - }) - - const selectedComponent = { _id: "123-456", _component: "@standard-components/header", _children: [], _styles: { normal: {}, hover: {}, active: {}, selected: { "font-size": "16px" } } } - - test("Test generation of selection css styles", () => { - expect(generate_screen_css([selectedComponent])).toBe(".header-123-456::selection {\nfont-size: 16px;\n}") - }) - - const emptyComponent = { _id: "123-456", _component: "@standard-components/header", _children: [], _styles: { normal: {}, hover: {}, active: {}, selected: {} } } - - test.only("Testing handling of empty component styles", () => { - expect(generate_screen_css([emptyComponent])).toBe("") - }) -}) \ No newline at end of file From 8a013c33c3c5c2dc03119b7fcdcae3c5f2c97832 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 25 Nov 2020 17:56:09 +0000 Subject: [PATCH 07/79] Major re-work, client library stills needs some work but it appears layouts and screens are no longer inter-dependent. --- .../src/builderStore/getNewComponentName.js | 8 +- packages/builder/src/builderStore/index.js | 10 +- .../src/builderStore/store/frontend.js | 188 +++++++++--------- .../builder/src/builderStore/storeUtils.js | 2 +- .../modals/CreateTableModal.svelte | 2 +- .../AppPreview/CurrentItemPreview.svelte | 8 +- .../AppPreview/iframeTemplate.js | 5 +- .../ComponentDropdownMenu.svelte | 2 +- .../ComponentTree.svelte | 2 +- .../ComponentNavigationTree/PathTree.svelte | 2 +- .../ScreenDropdownMenu.svelte | 9 - .../ComponentPropertiesPanel.svelte | 6 +- .../ComponentSelectionList.svelte | 2 +- .../ComponentsPaneSwitcher.svelte | 2 +- .../userInterface/FrontendNavigatePane.svelte | 13 +- .../{PageLayout.svelte => Layout.svelte} | 4 +- ...ponents.svelte => LayoutComponents.svelte} | 0 .../userInterface/LayoutsList.svelte | 7 +- .../userInterface/NewScreenModal.svelte | 2 +- .../userInterface/SettingsView.svelte | 21 +- .../createProps.js | 0 .../getRootComponent.js | 0 .../searchComponents.js | 0 .../splitRootComponentName.js | 0 .../{pagesParsing => assetParsing}/types.js | 0 .../pagesParsing/renameScreen.js | 60 ------ .../userInterface/temporaryPanelStructure.js | 2 +- packages/builder/src/constants/index.js | 4 +- .../automate/[automation]/_layout.svelte | 4 +- .../design/[page]/[screen]/_layout.svelte | 12 +- .../design/[page]/_layout.svelte | 2 +- .../[application]/design/[page]/index.svelte | 2 +- .../pages/[application]/design/_layout.svelte | 2 +- packages/builder/tests/createProps.spec.js | 2 +- .../tests/searchComponentsProps.spec.js | 2 +- packages/builder/yarn.lock | 170 +--------------- .../server/src/api/controllers/application.js | 6 +- packages/server/src/api/controllers/layout.js | 2 - packages/server/src/api/controllers/screen.js | 2 - .../src/api/controllers/static/index.js | 5 +- packages/server/src/api/routes/layout.js | 2 +- packages/server/src/constants/layouts.js | 5 +- .../utilities/builder/compileStaticAssets.js | 11 +- 43 files changed, 180 insertions(+), 410 deletions(-) rename packages/builder/src/components/userInterface/{PageLayout.svelte => Layout.svelte} (93%) rename packages/builder/src/components/userInterface/{PagesComponents.svelte => LayoutComponents.svelte} (100%) rename packages/builder/src/components/userInterface/{pagesParsing => assetParsing}/createProps.js (100%) rename packages/builder/src/components/userInterface/{pagesParsing => assetParsing}/getRootComponent.js (100%) rename packages/builder/src/components/userInterface/{pagesParsing => assetParsing}/searchComponents.js (100%) rename packages/builder/src/components/userInterface/{pagesParsing => assetParsing}/splitRootComponentName.js (100%) rename packages/builder/src/components/userInterface/{pagesParsing => assetParsing}/types.js (100%) delete mode 100644 packages/builder/src/components/userInterface/pagesParsing/renameScreen.js diff --git a/packages/builder/src/builderStore/getNewComponentName.js b/packages/builder/src/builderStore/getNewComponentName.js index a69bec21ad..1c156fd9f5 100644 --- a/packages/builder/src/builderStore/getNewComponentName.js +++ b/packages/builder/src/builderStore/getNewComponentName.js @@ -19,14 +19,16 @@ export default function(component, state) { }) } - // check page first - findMatches(state.pages[state.currentPageName].props) + // check layouts first + for (let layout of state.layouts) { + findMatches(layout.props) + } // if viewing screen, check current screen for duplicate if (state.currentFrontEndType === "screen") { findMatches(state.currentPreviewItem.props) } else { - // viewing master page - need to find against all screens + // viewing a layout - need to find against all screens for (let screen of get(allScreens)) { findMatches(screen.props) } diff --git a/packages/builder/src/builderStore/index.js b/packages/builder/src/builderStore/index.js index aac2437560..b29792fa94 100644 --- a/packages/builder/src/builderStore/index.js +++ b/packages/builder/src/builderStore/index.js @@ -31,14 +31,8 @@ export const allScreens = derived(store, $store => { return $store.screens }) -export const currentScreens = derived(store, $store => { - const currentScreens = $store.layouts[currentAssetName]?._screens - if (currentScreens == null) { - return [] - } - return Array.isArray(currentScreens) - ? currentScreens - : Object.values(currentScreens) +export const mainLayout = derived(store, $store => { + return $store.layouts?.find(layout => layout.props?._id === "private-master-layout") }) export const initialise = async () => { diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index 0523b6693f..37337cb98c 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -4,11 +4,11 @@ import { createProps, getBuiltin, makePropsSafe, -} from "components/userInterface/pagesParsing/createProps" -import { allScreens, backendUiStore, currentAsset } from "builderStore" +} from "components/userInterface/assetParsing/createProps" +import { allScreens, backendUiStore, currentAsset, mainLayout } from "builderStore" import { fetchComponentLibDefinitions } from "../loadComponentLibraries" import api from "../api" -import { DEFAULT_PAGES_OBJECT } from "../../constants" +import { DEFAULT_LAYOUTS } from "../../constants" import getNewComponentName from "../getNewComponentName" import analytics from "analytics" import { @@ -22,7 +22,7 @@ const INITIAL_FRONTEND_STATE = { apps: [], name: "", description: "", - layouts: DEFAULT_PAGES_OBJECT, + layouts: DEFAULT_LAYOUTS, screens: [], mainUi: {}, unauthenticatedUi: {}, @@ -68,16 +68,14 @@ export const getFrontendStore = () => { await backendUiStore.actions.database.select(pkg.application.instance) }, - selectPageOrScreen: type => { + selectAssetType: type => { store.update(state => { state.currentFrontEndType = type - const page = get(currentAsset) + const asset = get(currentAsset) - const pageOrScreen = type === "page" ? page : page._screens[0] - - state.currentComponentInfo = pageOrScreen ? pageOrScreen.props : null - state.currentPreviewItem = pageOrScreen + state.currentComponentInfo = asset && asset.props ? asset.props : null + state.currentPreviewItem = asset state.currentView = "detail" return state }) @@ -94,14 +92,16 @@ export const getFrontendStore = () => { }, }, screens: { - select: screenId => { + select: async screenId => { + let promise store.update(state => { const screen = get(allScreens).find(screen => screen._id === screenId) state.currentPreviewItem = screen state.currentFrontEndType = "screen" + state.currentAssetId = screenId state.currentView = "detail" - store.actions.screens.regenerateCssForCurrentScreen() + promise = store.actions.screens.regenerateCssForCurrentScreen() const safeProps = makePropsSafe( state.components[screen.props._component], screen.props @@ -110,44 +110,38 @@ export const getFrontendStore = () => { state.currentComponentInfo = safeProps return state }) + await promise }, create: async screen => { - let savePromise + let promises = [] store.update(state => { state.currentPreviewItem = screen state.currentComponentInfo = screen.props state.currentFrontEndType = "screen" if (state.currentPreviewItem) { - store.actions.screens.regenerateCss(state.currentPreviewItem) + promises.push(store.actions.screens.regenerateCss(state.currentPreviewItem)) } - savePromise = store.actions.screens.save(screen) + promises.push(store.actions.screens.save(screen)) return state }) - await savePromise + await Promise.all(promises) }, save: async screen => { - const page = get(currentAsset) - const currentPageScreens = page._screens - const creatingNewScreen = screen._id === undefined - - let savePromise - const response = await api.post(`/api/screens/${page._id}`, screen) + const response = await api.post(`/api/screens`, screen) const json = await response.json() screen._rev = json.rev screen._id = json.id - const foundScreen = page._screens.findIndex(el => el._id === screen._id) - if (foundScreen !== -1) { - page._screens.splice(foundScreen, 1) - } - page._screens.push(screen) - // TODO: should carry out all server updates to screen in a single call store.update(state => { - page._screens = currentPageScreens + const foundScreen = state.screens.findIndex(el => el._id === screen._id) + if (foundScreen !== -1) { + state.screens.splice(foundScreen, 1) + } + state.screens.push(screen) if (creatingNewScreen) { state.currentPreviewItem = screen @@ -158,107 +152,99 @@ export const getFrontendStore = () => { state.currentComponentInfo = safeProps screen.props = safeProps } - savePromise = store.actions.layouts.save() - return state }) - if (savePromise) await savePromise }, regenerateCss: async asset => { const response = await api.post("/api/css/generate", asset) - asset._css = await response.json() + asset._css = (await response.json())?.css }, - regenerateCssForCurrentScreen: () => { + regenerateCssForCurrentScreen: async () => { const { currentPreviewItem } = get(store) if (currentPreviewItem) { - store.actions.screens.regenerateCss(currentPreviewItem) + await store.actions.screens.regenerateCss(currentPreviewItem) } }, delete: async screens => { - let deletePromise - const screensToDelete = Array.isArray(screens) ? screens : [screens] + const screenDeletePromises = [] store.update(state => { - const currentPage = get(currentAsset) - for (let screenToDelete of screensToDelete) { - // Remove screen from current page as well - // TODO: Should be done server side - currentPage._screens = currentPage._screens.filter( - scr => scr._id !== screenToDelete._id - ) - - deletePromise = api.delete( + state.screens = state.screens.filter(screen => screen._id !== screenToDelete._id) + screenDeletePromises.push(api.delete( `/api/screens/${screenToDelete._id}/${screenToDelete._rev}` - ) + )) } return state }) - await deletePromise + await Promise.all(screenDeletePromises) }, }, preview: { saveSelected: async () => { const state = get(store) - if (state.currentFrontEndType !== "page") { - await store.actions.screens.save(state.currentPreviewItem) + if (state.currentFrontEndType !== "layout") { + await store.actions.screens.save(currentAsset) } - await store.actions.layouts.save() + await store.actions.layouts.save(currentAsset) }, }, layouts: { - select: pageName => { + select: async layoutName => { store.update(state => { - const currentPage = state.layouts[pageName] + const layout = store.actions.layouts.find(layoutName) - state.currentFrontEndType = "page" + state.currentFrontEndType = "layout" state.currentView = "detail" - state.currentAssetId = pageName + state.currentAssetId = layout._id // This is the root of many problems. // Uncaught (in promise) TypeError: Cannot read property '_component' of undefined - // it appears that the currentPage sometimes has _props instead of props + // it appears that the currentLayout sometimes has _props instead of props // why const safeProps = makePropsSafe( - state.components[currentPage.props._component], - currentPage.props + state.components[layout.props._component], + layout.props ) state.currentComponentInfo = safeProps - currentPage.props = safeProps - state.currentPreviewItem = state.layouts[pageName] - store.actions.screens.regenerateCssForCurrentScreen() - - for (let screen of get(allScreens)) { - screen._css = store.actions.screens.regenerateCss(screen) - } + layout.props = safeProps + state.currentPreviewItem = store.actions.layouts.find(layoutName) return state }) - }, - save: async page => { - const storeContents = get(store) - const pageName = storeContents.currentAssetId || "main" - const pageToSave = page || storeContents.pages[pageName] + let cssPromises = [] + cssPromises.push(store.actions.screens.regenerateCssForCurrentScreen()) - // TODO: revisit. This sends down a very weird payload - const response = await api.post(`/api/pages/${pageToSave._id}`, { - page: { - componentLibraries: storeContents.pages.componentLibraries, - ...pageToSave, - }, - screens: pageToSave._screens, + for (let screen of get(allScreens)) { + cssPromises.push(store.actions.screens.regenerateCss(screen)) + } + await Promise.all(cssPromises) + }, + save: async layout => { + const response = await api.post(`/api/layouts`, { + ...layout, }) const json = await response.json() - if (!json.ok) throw new Error("Error updating page") + if (!json.ok) throw new Error("Error updating layout") store.update(state => { - state.layouts[pageName]._rev = json.rev + const layoutToUpdate = state.layouts.find(stateLayouts => stateLayouts._id === layout._id) + if (layoutToUpdate) { + layoutToUpdate._rev = json.rev + } return state }) }, + find: layoutName => { + if (!layoutName) { + return get(mainLayout) + } + const storeContents = get(store) + return storeContents.layouts.find(layout => layout.name.toLowerCase() === layoutName.toLowerCase()) + }, }, components: { select: component => { @@ -274,6 +260,9 @@ export const getFrontendStore = () => { create: (componentToAdd, presetProps) => { store.update(state => { function findSlot(component_array) { + if (!component_array) { + return false + } for (let component of component_array) { if (component._component === "##builtin/screenslot") { return true @@ -286,7 +275,7 @@ export const getFrontendStore = () => { if ( componentToAdd.startsWith("##") && - findSlot(state.layouts[state.currentAssetId].props._children) + findSlot(get(currentAsset)?.props._children) ) { return state } @@ -349,7 +338,8 @@ export const getFrontendStore = () => { return state }) }, - paste: (targetComponent, mode) => { + paste: async (targetComponent, mode) => { + let promises = [] store.update(state => { if (!state.componentToPaste) return state @@ -377,26 +367,29 @@ export const getFrontendStore = () => { const index = mode === "above" ? targetIndex : targetIndex + 1 parent._children.splice(index, 0, cloneDeep(componentToPaste)) - store.actions.screens.regenerateCssForCurrentScreen() - store.actions.preview.saveSelected() + promises.push(store.actions.screens.regenerateCssForCurrentScreen()) + promises.push(store.actions.preview.saveSelected()) store.actions.components.select(componentToPaste) return state }) + await Promise.all(promises) }, - updateStyle: (type, name, value) => { + updateStyle: async (type, name, value) => { + let promises = [] store.update(state => { if (!state.currentComponentInfo._styles) { state.currentComponentInfo._styles = {} } state.currentComponentInfo._styles[type][name] = value - store.actions.screens.regenerateCssForCurrentScreen() + promises.push(store.actions.screens.regenerateCssForCurrentScreen()) // save without messing with the store - store.actions.preview.saveSelected() + promises.push(store.actions.preview.saveSelected()) return state }) + await Promise.all(promises) }, updateProp: (name, value) => { store.update(state => { @@ -423,7 +416,7 @@ export const getFrontendStore = () => { } } - // Remove root entry since it's the screen or page layout. + // Remove root entry since it's the screen or layout. // Reverse array since we need the correct order of the IDs const reversedComponents = pathComponents.reverse().slice(1) @@ -438,11 +431,12 @@ export const getFrontendStore = () => { }, links: { save: async (url, title) => { - let savePromise + let promises = [] + const layout = get(mainLayout) store.update(state => { - // Try to extract a nav component from the master screen + // Try to extract a nav component from the master layout const nav = findChildComponentType( - state.layouts.main, + layout, "@budibase/standard-components/Navigation" ) if (nav) { @@ -475,18 +469,18 @@ export const getFrontendStore = () => { }).props } - // Save page and regenerate all CSS because otherwise weird things happen + // Save layout and regenerate all CSS because otherwise weird things happen nav._children = [...nav._children, newLink] - state.currentAssetId = "main" - store.actions.screens.regenerateCss(state.layouts.main) - for (let screen of state.layouts.main._screens) { - store.actions.screens.regenerateCss(screen) + state.currentAssetId = layout._id + promises.push(store.actions.screens.regenerateCss(layout)) + for (let screen of get(allScreens)) { + promises.push(store.actions.screens.regenerateCss(screen)) } - savePromise = store.actions.layouts.save() + promises.push(store.actions.layouts.save(layout)) } return state }) - await savePromise + await Promise.all(promises) }, }, }, diff --git a/packages/builder/src/builderStore/storeUtils.js b/packages/builder/src/builderStore/storeUtils.js index 9c9d1ef940..7833477ff5 100644 --- a/packages/builder/src/builderStore/storeUtils.js +++ b/packages/builder/src/builderStore/storeUtils.js @@ -1,4 +1,4 @@ -import { getBuiltin } from "components/userInterface/pagesParsing/createProps" +import { getBuiltin } from "components/userInterface/assetParsing/createProps" import { uuid } from "./uuid" import getNewComponentName from "./getNewComponentName" diff --git a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte index d111821fb3..75b27f71d3 100644 --- a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte +++ b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte @@ -58,7 +58,7 @@ await store.actions.screens.create(screen) } - // Create autolink to newly created list page + // Create autolink to newly created list screen const listScreen = screens.find(screen => screen.props._instanceName.endsWith("List") ) diff --git a/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte b/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte index 292718c665..e659edf2a1 100644 --- a/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte +++ b/packages/builder/src/components/userInterface/AppPreview/CurrentItemPreview.svelte @@ -1,6 +1,6 @@ diff --git a/packages/builder/src/components/userInterface/ComponentNavigationTree/ScreenDropdownMenu.svelte b/packages/builder/src/components/userInterface/ComponentNavigationTree/ScreenDropdownMenu.svelte index 07f0f042a1..f716b94351 100644 --- a/packages/builder/src/components/userInterface/ComponentNavigationTree/ScreenDropdownMenu.svelte +++ b/packages/builder/src/components/userInterface/ComponentNavigationTree/ScreenDropdownMenu.svelte @@ -16,15 +16,6 @@ const screenToDelete = $allScreens.find(scr => scr._id === screen) store.actions.screens.delete(screenToDelete) store.actions.routing.fetch() - // update the page if required - store.update(state => { - if (state.currentPreviewItem._id === screen) { - store.actions.layouts.select($store.currentPageName) - notifier.success(`Screen ${screenToDelete.name} deleted successfully.`) - $goto(`./:page/page-layout`) - } - return state - }) } diff --git a/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte b/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte index 831864a89d..009596e0f3 100644 --- a/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte +++ b/packages/builder/src/components/userInterface/ComponentPropertiesPanel.svelte @@ -58,7 +58,7 @@ return components } - function setPageOrScreenProp(name, value) { + function setAssetProps(name, value) { store.update(state => { if (name === "_instanceName" && state.currentFrontEndType === "screen") { state.currentPreviewItem.props[name] = value @@ -94,8 +94,8 @@ {panelDefinition} displayNameField={displayName} onChange={store.actions.components.updateProp} - onScreenPropChange={setPageOrScreenProp} - screenOrPageInstance={$store.currentView !== 'component' && $store.currentPreviewItem} /> + onScreenPropChange={setAssetProps} + assetInstance={$store.currentView !== 'component' && $store.currentPreviewItem} /> {/if}
diff --git a/packages/builder/src/components/userInterface/ComponentSelectionList.svelte b/packages/builder/src/components/userInterface/ComponentSelectionList.svelte index edce828f41..a6f1d6fea5 100644 --- a/packages/builder/src/components/userInterface/ComponentSelectionList.svelte +++ b/packages/builder/src/components/userInterface/ComponentSelectionList.svelte @@ -27,7 +27,7 @@ const onComponentChosen = component => { store.actions.components.create(component._component, component.presetProps) const path = store.actions.components.findRoute($store.currentComponentInfo) - $goto(`./:page/:screen/${path}`) + $goto(`./:screen/${path}`) close() } diff --git a/packages/builder/src/components/userInterface/ComponentsPaneSwitcher.svelte b/packages/builder/src/components/userInterface/ComponentsPaneSwitcher.svelte index 4e865b4514..8615a6016e 100644 --- a/packages/builder/src/components/userInterface/ComponentsPaneSwitcher.svelte +++ b/packages/builder/src/components/userInterface/ComponentsPaneSwitcher.svelte @@ -18,7 +18,7 @@
- {#if $store.currentFrontEndType === 'page' || $allScreens.length} + {#if $store.currentFrontEndType === "layout" || $allScreens.length}
- + + +{#if $store.currentFrontEndType === "layout" && $currentAsset} +{/if} diff --git a/packages/standard-components/src/index.js b/packages/standard-components/src/index.js index 3dec13e27b..89189a287b 100644 --- a/packages/standard-components/src/index.js +++ b/packages/standard-components/src/index.js @@ -23,6 +23,7 @@ export { default as embed } from "./Embed.svelte" export { default as stackedlist } from "./StackedList.svelte" export { default as card } from "./Card.svelte" export { default as cardhorizontal } from "./CardHorizontal.svelte" +export { default as cardstat } from "./CardStat.svelte" export { default as rowdetail } from "./RowDetail.svelte" export { default as newrow } from "./NewRow.svelte" export { default as datepicker } from "./DatePicker.svelte" From 4b007bbe63a49bf9bcb0eb1cbf0f885bc7c9f3fb Mon Sep 17 00:00:00 2001 From: Joe <49767913+joebudi@users.noreply.github.com> Date: Sat, 28 Nov 2020 16:44:07 +0000 Subject: [PATCH 10/79] Formatting and linting --- .../src/components/userInterface/temporaryPanelStructure.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/builder/src/components/userInterface/temporaryPanelStructure.js b/packages/builder/src/components/userInterface/temporaryPanelStructure.js index 092f4eafdd..2465e938d3 100644 --- a/packages/builder/src/components/userInterface/temporaryPanelStructure.js +++ b/packages/builder/src/components/userInterface/temporaryPanelStructure.js @@ -430,8 +430,7 @@ export default { { _component: "@budibase/standard-components/cardstat", name: "Stat", - description: - "A card component for displaying numbers.", + description: "A card component for displaying numbers.", icon: "ri-dual-sim-2-line", children: [], properties: { From b29f06db1fd819211ca99f1687a6e853c9d5b5ec Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 30 Nov 2020 11:19:22 +0000 Subject: [PATCH 11/79] Adding to JOI the layoutId. --- packages/server/src/api/routes/screen.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/server/src/api/routes/screen.js b/packages/server/src/api/routes/screen.js index 1b21a58452..7723418b95 100644 --- a/packages/server/src/api/routes/screen.js +++ b/packages/server/src/api/routes/screen.js @@ -24,6 +24,7 @@ function generateSaveValidation() { _styles: Joi.object().required(), type: Joi.string().optional(), table: Joi.string().optional(), + layoutId: Joi.string().optional(), }).required().unknown(true), }).unknown(true)) } From 0fea7893ea16f185761fbac1635290a5d272f3a2 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 30 Nov 2020 16:12:06 +0000 Subject: [PATCH 12/79] Quick update to allow deleting layouts if they are not currently in use. --- packages/server/src/api/controllers/layout.js | 25 +++++++++++++++++-- packages/server/src/api/controllers/screen.js | 2 +- packages/server/src/api/routes/layout.js | 8 +++++- packages/server/src/api/routes/screen.js | 2 +- packages/server/src/constants/layouts.js | 10 ++++++-- 5 files changed, 40 insertions(+), 7 deletions(-) diff --git a/packages/server/src/api/controllers/layout.js b/packages/server/src/api/controllers/layout.js index e9d2b3b955..78425fbbdd 100644 --- a/packages/server/src/api/controllers/layout.js +++ b/packages/server/src/api/controllers/layout.js @@ -1,5 +1,5 @@ -const CouchDB = require("../../db/client") -const { generateLayoutID } = require("../../db/utils") +const CouchDB = require("../../db") +const { generateLayoutID, getScreenParams } = require("../../db/utils") exports.save = async function(ctx) { const db = new CouchDB(ctx.user.appId) @@ -9,3 +9,24 @@ exports.save = async function(ctx) { ctx.body = await db.put(layout) ctx.status = 200 } + +exports.destroy = async function(ctx) { + const db = new CouchDB(ctx.user.appId) + const layoutId = ctx.params.layoutId, + layoutRev = ctx.params.layoutRev + + const layoutsUsedByScreens = ( + await db.allDocs( + getScreenParams(null, { + include_docs: true, + }) + ) + ).rows.map(element => element.doc.props.layoutId) + if (layoutsUsedByScreens.indexOf(layoutId) !== -1) { + ctx.throw(400, "Cannot delete a base layout") + } + + await db.remove(layoutId, layoutRev) + ctx.message = "Layout deleted successfully" + ctx.status = 200 +} diff --git a/packages/server/src/api/controllers/screen.js b/packages/server/src/api/controllers/screen.js index 42a11dbc26..f18c1d1f30 100644 --- a/packages/server/src/api/controllers/screen.js +++ b/packages/server/src/api/controllers/screen.js @@ -36,7 +36,7 @@ exports.save = async ctx => { exports.destroy = async ctx => { const db = new CouchDB(ctx.user.appId) - await db.remove(ctx.params.screenId, ctx.params.revId) + await db.remove(ctx.params.screenId, ctx.params.screenRev) ctx.message = "Screen deleted successfully" ctx.status = 200 } diff --git a/packages/server/src/api/routes/layout.js b/packages/server/src/api/routes/layout.js index 208849ab46..a903aab959 100644 --- a/packages/server/src/api/routes/layout.js +++ b/packages/server/src/api/routes/layout.js @@ -5,6 +5,12 @@ const controller = require("../controllers/layout") const router = Router() -router.post("/api/layouts", authorized(BUILDER), controller.save) +router + .post("/api/layouts", authorized(BUILDER), controller.save) + .delete( + "/api/layouts/:layoutId/:layoutRev", + authorized(BUILDER), + controller.destroy + ) module.exports = router diff --git a/packages/server/src/api/routes/screen.js b/packages/server/src/api/routes/screen.js index 7723418b95..bece56fe67 100644 --- a/packages/server/src/api/routes/screen.js +++ b/packages/server/src/api/routes/screen.js @@ -38,7 +38,7 @@ router controller.save ) .delete( - "/api/screens/:screenId/:revId", + "/api/screens/:screenId/:screenRev", authorized(BUILDER), controller.destroy ) diff --git a/packages/server/src/constants/layouts.js b/packages/server/src/constants/layouts.js index 2f1bd06dc7..f07759754f 100644 --- a/packages/server/src/constants/layouts.js +++ b/packages/server/src/constants/layouts.js @@ -1,3 +1,8 @@ +const BASE_LAYOUT_PROP_IDS = { + PRIVATE: "private-master-layout", + PUBLIC: "public-master-layout", +} + const BASE_LAYOUTS = [ { componentLibraries: ["@budibase/standard-components"], @@ -6,7 +11,7 @@ const BASE_LAYOUTS = [ stylesheets: [], name: "Main", props: { - _id: "private-master-layout", + _id: BASE_LAYOUT_PROP_IDS.PRIVATE, _component: "@budibase/standard-components/container", _children: [ { @@ -151,7 +156,7 @@ const BASE_LAYOUTS = [ stylesheets: [], name: "Unauthenticated", props: { - _id: "public-master-layout", + _id: BASE_LAYOUT_PROP_IDS.PUBLIC, _component: "@budibase/standard-components/container", _children: [ { @@ -217,4 +222,5 @@ const BASE_LAYOUTS = [ module.exports = { BASE_LAYOUTS, + BASE_LAYOUT_PROP_IDS, } From 4319c4bb3f65d378bb80cba54c97ef542fc7540f Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 2 Dec 2020 13:27:20 +0000 Subject: [PATCH 13/79] Linting. --- .../components/userInterface/ComponentsPaneSwitcher.svelte | 2 +- .../components/userInterface/FrontendNavigatePane.svelte | 3 +-- .../[application]/design/screens/[screen]/_layout.svelte | 6 +++--- packages/server/src/integrations/Integration.js | 5 ++--- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/builder/src/components/userInterface/ComponentsPaneSwitcher.svelte b/packages/builder/src/components/userInterface/ComponentsPaneSwitcher.svelte index 8615a6016e..1ecadebeb1 100644 --- a/packages/builder/src/components/userInterface/ComponentsPaneSwitcher.svelte +++ b/packages/builder/src/components/userInterface/ComponentsPaneSwitcher.svelte @@ -18,7 +18,7 @@
- {#if $store.currentFrontEndType === "layout" || $allScreens.length} + {#if $store.currentFrontEndType === 'layout' || $allScreens.length}
@@ -61,8 +60,8 @@ - + diff --git a/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte b/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte new file mode 100644 index 0000000000..627112d87c --- /dev/null +++ b/packages/builder/src/components/backend/DataTable/modals/EditRoles.svelte @@ -0,0 +1,136 @@ + + + + {#if errors.length} + + {/if} + + {#if selectedRole} + + + + {/if} +
+ {#if !isCreating} + + {/if} +
+
From 922256790ce10c1f5ac173c1b932fd4188926455 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Dec 2020 08:27:59 +0000 Subject: [PATCH 32/79] Update errors box to work better in dark theme --- packages/builder/src/components/common/ErrorsBox.svelte | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/components/common/ErrorsBox.svelte b/packages/builder/src/components/common/ErrorsBox.svelte index 9e29784c66..f05e03f596 100644 --- a/packages/builder/src/components/common/ErrorsBox.svelte +++ b/packages/builder/src/components/common/ErrorsBox.svelte @@ -5,9 +5,9 @@ {#if hasErrors} -
+
{#each errors as error} -
{error.dataPath} {error.message}
+
{error.dataPath || ''} {error.message}
{/each}
{/if} @@ -17,6 +17,8 @@ border-radius: var(--border-radius-m); margin: 0; padding: var(--spacing-m); + background-color: rgba(241, 165, 165, 0.2); + color: var(--red); } .error { From a8a8b60a55120eac8f6eb367e6ea5cc584eb1b86 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Dec 2020 08:28:35 +0000 Subject: [PATCH 33/79] Fix variable name in returned message when deleting a role --- packages/server/src/api/controllers/role.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/role.js b/packages/server/src/api/controllers/role.js index 3ebda124b0..cbe55bc5d6 100644 --- a/packages/server/src/api/controllers/role.js +++ b/packages/server/src/api/controllers/role.js @@ -50,6 +50,6 @@ exports.save = async function(ctx) { exports.destroy = async function(ctx) { const db = new CouchDB(ctx.user.appId) await db.remove(ctx.params.roleId, ctx.params.rev) - ctx.message = `Role ${ctx.params.id} deleted successfully` + ctx.message = `Role ${ctx.params.roleId} deleted successfully` ctx.status = 200 } From 20c0cd810889b3e6f30911acf37b03033b50907f Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Dec 2020 08:28:44 +0000 Subject: [PATCH 34/79] Update lock file --- yarn.lock | 325 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 229 insertions(+), 96 deletions(-) diff --git a/yarn.lock b/yarn.lock index e9f9998edc..f4d4d2114f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,40 +2,64 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": +"@babel/code-frame@^7.0.0": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" dependencies: "@babel/highlight" "^7.8.3" -"@babel/generator@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.4.tgz#35bbc74486956fe4251829f9f6c48330e8d0985e" +"@babel/code-frame@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== dependencies: - "@babel/types" "^7.8.3" + "@babel/highlight" "^7.10.4" + +"@babel/generator@^7.12.5": + version "7.12.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.5.tgz#a2c50de5c8b6d708ab95be5e6053936c1884a4de" + integrity sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A== + dependencies: + "@babel/types" "^7.12.5" jsesc "^2.5.1" - lodash "^4.17.13" source-map "^0.5.0" -"@babel/helper-function-name@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz#eeeb665a01b1f11068e9fb86ad56a1cb1a824cca" +"@babel/helper-function-name@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a" + integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ== dependencies: - "@babel/helper-get-function-arity" "^7.8.3" - "@babel/template" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/helper-get-function-arity" "^7.10.4" + "@babel/template" "^7.10.4" + "@babel/types" "^7.10.4" -"@babel/helper-get-function-arity@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5" +"@babel/helper-get-function-arity@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2" + integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.10.4" -"@babel/helper-split-export-declaration@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9" +"@babel/helper-split-export-declaration@^7.11.0": + version "7.11.0" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f" + integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg== dependencies: - "@babel/types" "^7.8.3" + "@babel/types" "^7.11.0" + +"@babel/helper-validator-identifier@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" + integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== + +"@babel/highlight@^7.10.4": + version "7.10.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" + integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + dependencies: + "@babel/helper-validator-identifier" "^7.10.4" + chalk "^2.0.0" + js-tokens "^4.0.0" "@babel/highlight@^7.8.3": version "7.8.3" @@ -45,38 +69,42 @@ esutils "^2.0.2" js-tokens "^4.0.0" -"@babel/parser@^7.0.0", "@babel/parser@^7.8.3", "@babel/parser@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.4.tgz#d1dbe64691d60358a974295fa53da074dd2ce8e8" +"@babel/parser@^7.12.7", "@babel/parser@^7.7.0": + version "7.12.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.7.tgz#fee7b39fe809d0e73e5b25eecaf5780ef3d73056" + integrity sha512-oWR02Ubp4xTLCAqPRiNIuMVgNO5Aif/xpXtabhzW2HWUD47XJsAB4Zd/Rg30+XeQA3juXigV7hlquOTmwqLiwg== -"@babel/template@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.3.tgz#e02ad04fe262a657809327f578056ca15fd4d1b8" +"@babel/template@^7.10.4": + version "7.12.7" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.7.tgz#c817233696018e39fbb6c491d2fb684e05ed43bc" + integrity sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow== dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/parser" "^7.8.3" - "@babel/types" "^7.8.3" + "@babel/code-frame" "^7.10.4" + "@babel/parser" "^7.12.7" + "@babel/types" "^7.12.7" -"@babel/traverse@^7.0.0": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.4.tgz#f0845822365f9d5b0e312ed3959d3f827f869e3c" +"@babel/traverse@^7.7.0": + version "7.12.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.9.tgz#fad26c972eabbc11350e0b695978de6cc8e8596f" + integrity sha512-iX9ajqnLdoU1s1nHt36JDI9KG4k+vmI8WgjK5d+aDTwQbL2fUnzedNedssA645Ede3PM2ma1n8Q4h2ohwXgMXw== dependencies: - "@babel/code-frame" "^7.8.3" - "@babel/generator" "^7.8.4" - "@babel/helper-function-name" "^7.8.3" - "@babel/helper-split-export-declaration" "^7.8.3" - "@babel/parser" "^7.8.4" - "@babel/types" "^7.8.3" + "@babel/code-frame" "^7.10.4" + "@babel/generator" "^7.12.5" + "@babel/helper-function-name" "^7.10.4" + "@babel/helper-split-export-declaration" "^7.11.0" + "@babel/parser" "^7.12.7" + "@babel/types" "^7.12.7" debug "^4.1.0" globals "^11.1.0" - lodash "^4.17.13" + lodash "^4.17.19" -"@babel/types@^7.0.0", "@babel/types@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c" +"@babel/types@^7.10.4", "@babel/types@^7.11.0", "@babel/types@^7.12.5", "@babel/types@^7.12.7", "@babel/types@^7.7.0": + version "7.12.7" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.7.tgz#6039ff1e242640a29452c9ae572162ec9a8f5d13" + integrity sha512-MNyI92qZq6jrQkXvtIiykvl4WtoRrVV9MPn+ZfsoEENjiWcBQ3ZSHrkxnJWgWtLX3XXqX5hrSQ+X69wkmesXuQ== dependencies: - esutils "^2.0.2" - lodash "^4.17.13" + "@babel/helper-validator-identifier" "^7.10.4" + lodash "^4.17.19" to-fast-properties "^2.0.0" "@fortawesome/fontawesome-common-types@^0.1.7": @@ -825,13 +853,15 @@ abbrev@1: version "1.1.1" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" -acorn-jsx@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384" +acorn-jsx@^5.2.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" + integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== -acorn@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" +acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== agent-base@4, agent-base@^4.3.0: version "4.3.0" @@ -865,10 +895,11 @@ ansi-escapes@^3.2.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" ansi-escapes@^4.2.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.0.tgz#a4ce2b33d6b214b7950d8595c212f12ac9cc569d" + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== dependencies: - type-fest "^0.8.1" + type-fest "^0.11.0" ansi-regex@^2.0.0: version "2.1.1" @@ -892,6 +923,13 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -998,13 +1036,14 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e" babel-eslint@^10.0.3: - version "10.0.3" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.3.tgz#81a2c669be0f205e19462fed2482d33e4687a88a" + version "10.1.0" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" + integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg== dependencies: "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.0.0" - "@babel/traverse" "^7.0.0" - "@babel/types" "^7.0.0" + "@babel/parser" "^7.7.0" + "@babel/traverse" "^7.7.0" + "@babel/types" "^7.7.0" eslint-visitor-keys "^1.0.0" resolve "^1.12.0" @@ -1202,6 +1241,14 @@ chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" + integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -1232,6 +1279,7 @@ cli-cursor@^2.1.0: cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== dependencies: restore-cursor "^3.1.0" @@ -1239,6 +1287,11 @@ cli-width@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + cliui@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" @@ -1275,10 +1328,22 @@ color-convert@^1.9.0: dependencies: color-name "1.1.3" +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + columnify@^1.5.4: version "1.5.4" resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" @@ -1447,6 +1512,7 @@ cosmiconfig@^5.1.0: cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== dependencies: nice-try "^1.0.4" path-key "^2.0.1" @@ -1530,6 +1596,7 @@ dedent@^0.7.0: deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= defaults@^1.0.3: version "1.0.3" @@ -1637,6 +1704,7 @@ emoji-regex@^7.0.1: emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== encoding@^0.1.11: version "0.1.12" @@ -1699,14 +1767,16 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" eslint-plugin-cypress@^2.11.1: - version "2.11.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-cypress/-/eslint-plugin-cypress-2.11.1.tgz#a945e2774b88211e2c706a059d431e262b5c2862" + version "2.11.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-cypress/-/eslint-plugin-cypress-2.11.2.tgz#a8f3fe7ec840f55e4cea37671f93293e6c3e76a0" + integrity sha512-1SergF1sGbVhsf7MYfOLiBhdOg6wqyeV9pXUAIDIffYTGMN3dTBQS9nFAzhLsHhO+Bn0GaVM1Ecm71XUidQ7VA== dependencies: globals "^11.12.0" eslint-plugin-prettier@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz#432e5a667666ab84ce72f945c72f77d996a5c9ba" + version "3.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.2.0.tgz#af391b2226fa0e15c96f36c733f6e9035dbd952c" + integrity sha512-kOUSJnFjAUFKwVxuzy6sA5yyMx6+o9ino4gCdShzBNx4eyFRudWRYKCFolKjoM40PEiuU6Cn7wBLfq3WsGg7qg== dependencies: prettier-linter-helpers "^1.0.0" @@ -1715,15 +1785,17 @@ eslint-plugin-svelte3@^2.7.3: resolved "https://registry.yarnpkg.com/eslint-plugin-svelte3/-/eslint-plugin-svelte3-2.7.3.tgz#e793b646b848e717674fe668c21b909cfa025eb3" eslint-scope@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: - esrecurse "^4.1.0" + esrecurse "^4.3.0" estraverse "^4.1.1" eslint-utils@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f" + integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q== dependencies: eslint-visitor-keys "^1.1.0" @@ -1734,6 +1806,7 @@ eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: eslint@^6.8.0: version "6.8.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb" + integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig== dependencies: "@babel/code-frame" "^7.0.0" ajv "^6.10.0" @@ -1774,11 +1847,12 @@ eslint@^6.8.0: v8-compile-cache "^2.0.3" espree@^6.1.2: - version "6.1.2" - resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.2.tgz#6c272650932b4f91c3714e5e7b5f5e2ecf47262d" + version "6.2.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a" + integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== dependencies: - acorn "^7.1.0" - acorn-jsx "^5.1.0" + acorn "^7.1.1" + acorn-jsx "^5.2.0" eslint-visitor-keys "^1.1.0" esprima@^4.0.0: @@ -1786,21 +1860,28 @@ esprima@^4.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" esquery@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== dependencies: - estraverse "^4.0.0" + estraverse "^5.1.0" -esrecurse@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: - estraverse "^4.1.0" + estraverse "^5.2.0" -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: +estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + estree-walker@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" @@ -1913,6 +1994,7 @@ fast-json-stable-stringify@^2.0.0: fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: version "3.5.1" @@ -1925,8 +2007,9 @@ figures@^2.0.0: escape-string-regexp "^1.0.5" figures@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.1.0.tgz#4b198dd07d8d71530642864af2d45dd9e459c4ec" + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== dependencies: escape-string-regexp "^1.0.5" @@ -2235,6 +2318,11 @@ has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + has-symbols@^1.0.0, has-symbols@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" @@ -2422,21 +2510,22 @@ inquirer@^6.2.0: through "^2.3.6" inquirer@^7.0.0: - version "7.0.4" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.0.4.tgz#99af5bde47153abca23f5c7fc30db247f39da703" + version "7.3.3" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" + integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== dependencies: ansi-escapes "^4.2.1" - chalk "^2.4.2" + chalk "^4.1.0" cli-cursor "^3.1.0" - cli-width "^2.0.0" + cli-width "^3.0.0" external-editor "^3.0.3" figures "^3.0.0" - lodash "^4.17.15" + lodash "^4.17.19" mute-stream "0.0.8" - run-async "^2.2.0" - rxjs "^6.5.3" + run-async "^2.4.0" + rxjs "^6.6.0" string-width "^4.1.0" - strip-ansi "^5.1.0" + strip-ansi "^6.0.0" through "^2.3.6" invert-kv@^2.0.0: @@ -2546,6 +2635,7 @@ is-fullwidth-code-point@^2.0.0: is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-glob@^3.1.0: version "3.1.0" @@ -2776,6 +2866,7 @@ lerna@3.14.1: levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= dependencies: prelude-ls "~1.1.2" type-check "~0.3.2" @@ -2877,10 +2968,15 @@ lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" -lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5, lodash@^4.2.1: +lodash@^4.17.12, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5, lodash@^4.2.1: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" +lodash@^4.17.19: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + loud-rejection@^1.0.0: version "1.6.0" resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" @@ -3063,6 +3159,7 @@ mimic-fn@^1.0.0: mimic-fn@^2.0.0, mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== minimatch@^3.0.0, minimatch@^3.0.4: version "3.0.4" @@ -3169,6 +3266,7 @@ mute-stream@0.0.7: mute-stream@0.0.8, mute-stream@~0.0.4: version "0.0.8" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== nanomatch@^1.2.9: version "1.2.13" @@ -3404,8 +3502,9 @@ onetime@^2.0.0: mimic-fn "^1.0.0" onetime@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== dependencies: mimic-fn "^2.1.0" @@ -3419,6 +3518,7 @@ optimist@^0.6.1: optionator@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== dependencies: deep-is "~0.1.3" fast-levenshtein "~2.0.6" @@ -3696,6 +3796,7 @@ posix-character-classes@^0.1.0: prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= prettier-linter-helpers@^1.0.0: version "1.0.0" @@ -3704,13 +3805,14 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier-plugin-svelte@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-1.4.0.tgz#bb992759fb77ec2c3545d454a7c60f7a258cb745" - integrity sha512-KXO2He7Kql0Lz4DdlzVli1j2JTDUR9jPV/DqyfnJmY1pCeSV1qZkxgdsyYma35W6OLrCAr/G6yKdmzo+75u2Ng== + version "1.4.1" + resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-1.4.1.tgz#2f0f7a149190f476dc9b4ba9da8d482bd196f1e2" + integrity sha512-6y0m37Xw01GRf/WIHau+Kp3uXj2JB1agtEmNVKb9opMy34A6OMOYhfneVpNIlrghQSw/jIV+t3e5Ngt4up2CMA== prettier@^1.19.1: version "1.19.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== process-nextick-args@~2.0.0: version "2.0.1" @@ -3907,6 +4009,7 @@ regex-not@^1.0.0, regex-not@^1.0.2: regexpp@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" + integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== repeat-element@^1.1.2: version "1.1.3" @@ -3995,6 +4098,7 @@ restore-cursor@^2.0.0: restore-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== dependencies: onetime "^5.1.0" signal-exit "^3.0.2" @@ -4045,18 +4149,30 @@ run-async@^2.2.0: dependencies: is-promise "^2.1.0" +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" dependencies: aproba "^1.1.1" -rxjs@^6.4.0, rxjs@^6.5.3: +rxjs@^6.4.0: version "6.5.4" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.4.tgz#e0777fe0d184cec7872df147f303572d414e211c" dependencies: tslib "^1.9.0" +rxjs@^6.6.0: + version "6.6.3" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" + integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ== + dependencies: + tslib "^1.9.0" + safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" @@ -4082,6 +4198,7 @@ safe-regex@^1.1.0: semver@^6.0.0, semver@^6.1.2: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" + integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== semver@~5.3.0: version "5.3.0" @@ -4320,6 +4437,7 @@ string-width@^3.0.0: string-width@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== dependencies: emoji-regex "^8.0.0" is-fullwidth-code-point "^3.0.0" @@ -4400,8 +4518,9 @@ strip-indent@^2.0.0: resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" strip-json-comments@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== strong-log-transformer@^2.0.0: version "2.1.0" @@ -4417,6 +4536,13 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + svelte@^3.30.0: version "3.30.0" resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.30.0.tgz#cbde341e96bf34f4ac73c8f14f8a014e03bfb7d6" @@ -4563,9 +4689,15 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= dependencies: prelude-ls "~1.1.2" +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" @@ -4723,6 +4855,7 @@ windows-release@^3.1.0: word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== wordwrap@~0.0.2: version "0.0.3" From 3aaf0e644ad04437b0f5db25c809919d37420aa9 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Dec 2020 09:13:38 +0000 Subject: [PATCH 35/79] Update stat card to use new component SDK --- .../userInterface/temporaryPanelStructure.js | 12 ----------- packages/standard-components/components.json | 4 +--- .../standard-components/src/CardStat.svelte | 21 ++++++++----------- 3 files changed, 10 insertions(+), 27 deletions(-) diff --git a/packages/builder/src/components/userInterface/temporaryPanelStructure.js b/packages/builder/src/components/userInterface/temporaryPanelStructure.js index 9e9ede545d..81b73d3204 100644 --- a/packages/builder/src/components/userInterface/temporaryPanelStructure.js +++ b/packages/builder/src/components/userInterface/temporaryPanelStructure.js @@ -454,18 +454,6 @@ export default { control: Input, placeholder: "Stripe", }, - { - label: "Value Color", - key: "color", - control: Input, - placeholder: "Blue", - }, - { - label: "Border Color", - key: "bordercolor", - control: Input, - placeholder: "lightgrey", - }, ], }, }, diff --git a/packages/standard-components/components.json b/packages/standard-components/components.json index 179ec1acbe..7504c15b40 100644 --- a/packages/standard-components/components.json +++ b/packages/standard-components/components.json @@ -262,9 +262,7 @@ "props": { "title": "string", "value": "string", - "label": "string", - "color": "string", - "bordercolor": "string" + "label": "string" } }, "cardhorizontal": { diff --git a/packages/standard-components/src/CardStat.svelte b/packages/standard-components/src/CardStat.svelte index cb58bdfb5a..e5e40ad862 100644 --- a/packages/standard-components/src/CardStat.svelte +++ b/packages/standard-components/src/CardStat.svelte @@ -1,20 +1,16 @@ -
+

{title}

{value}

{label}

@@ -25,8 +21,9 @@ min-width: 260px; width: max-content; max-height: 170px; - border: 1px solid var(--bordercolor); + border: 1px solid var(--grey-3); border-radius: 0.3rem; + color: var(--blue); } .title { @@ -39,8 +36,8 @@ .value { font-size: 2rem; font-weight: 500; - color: var(--color); - margin: 0rem 1.5rem 1.5rem 1.5rem; + margin: 0 1.5rem 1.5rem 1.5rem; + color: inherit; } .label { From 6f3c4ba0ea4a402df494d80969e327cba30cf13c Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 4 Dec 2020 12:09:02 +0000 Subject: [PATCH 36/79] Hopefully a fix for the 4001 bug we have been experiencing. --- packages/server/src/app.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/server/src/app.js b/packages/server/src/app.js index 10eec1e66f..58526cdf4c 100644 --- a/packages/server/src/app.js +++ b/packages/server/src/app.js @@ -1,4 +1,5 @@ const Koa = require("koa") +const destroyable = require("server-destroy") const electron = require("electron") const koaBody = require("koa-body") const logger = require("koa-pino-logger") @@ -44,6 +45,7 @@ if (electron.app && electron.app.isPackaged) { } const server = http.createServer(app.callback()) +destroyable(server) server.on("close", () => console.log("Server Closed")) @@ -51,3 +53,14 @@ module.exports = server.listen(env.PORT || 4001, () => { console.log(`Budibase running on ${JSON.stringify(server.address())}`) automations.init() }) + +process.on("uncaughtException", err => { + console.error(err) + server.close() + server.destroy() +}) + +process.on("SIGTERM", () => { + server.close() + server.destroy() +}) From f05d696ef7e6d04889524749dce6463b8ae9bb04 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Fri, 4 Dec 2020 12:22:45 +0000 Subject: [PATCH 37/79] email as default user identifier --- packages/builder/cypress/support/commands.js | 8 ++--- .../SetupPanel/AutomationBlockSetup.svelte | 2 ++ .../components/start/CreateAppModal.svelte | 7 +++-- .../src/components/start/Steps/User.svelte | 14 ++++----- packages/builder/src/constants/index.js | 2 +- packages/client/src/api/auth.js | 8 ++--- packages/client/src/store/auth.js | 4 +-- packages/server/src/api/controllers/auth.js | 10 +++---- packages/server/src/api/controllers/user.js | 30 ++++++++----------- .../src/api/routes/tests/couchTestUtils.js | 13 ++++---- .../server/src/api/routes/tests/user.spec.js | 12 ++++---- packages/server/src/api/routes/user.js | 4 +-- packages/server/src/app.js | 13 ++++++++ .../src/automations/steps/createUser.js | 13 ++++---- packages/server/src/constants/index.js | 9 +++--- packages/server/src/db/utils.js | 4 +-- packages/standard-components/components.json | 2 +- packages/standard-components/src/Login.svelte | 16 +++++----- 18 files changed, 92 insertions(+), 79 deletions(-) diff --git a/packages/builder/cypress/support/commands.js b/packages/builder/cypress/support/commands.js index 76f3417eac..0c177e6177 100644 --- a/packages/builder/cypress/support/commands.js +++ b/packages/builder/cypress/support/commands.js @@ -44,9 +44,9 @@ Cypress.Commands.add("createApp", name => { cy.contains("Next").click() - cy.get("input[name=username]") + cy.get("input[name=email]") .click() - .type("test") + .type("test@test.com") cy.get("input[name=password]") .click() .type("test") @@ -111,7 +111,7 @@ Cypress.Commands.add("addRow", values => { }) }) -Cypress.Commands.add("createUser", (username, password, accessLevel) => { +Cypress.Commands.add("createUser", (email, password, accessLevel) => { // Create User cy.contains("Users").click() @@ -123,7 +123,7 @@ Cypress.Commands.add("createUser", (username, password, accessLevel) => { .type(password) cy.get("input") .eq(1) - .type(username) + .type(email) cy.get("select") .first() .select(accessLevel) diff --git a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte index 8bb4dc36dd..7e26b2155e 100644 --- a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte +++ b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte @@ -62,6 +62,8 @@ {:else if value.customType === 'password'} + {:else if value.customType === 'email'} + {:else if value.customType === 'table'} {:else if value.customType === 'row'} diff --git a/packages/builder/src/components/start/CreateAppModal.svelte b/packages/builder/src/components/start/CreateAppModal.svelte index 049b21c995..2eb644cdfb 100644 --- a/packages/builder/src/components/start/CreateAppModal.svelte +++ b/packages/builder/src/components/start/CreateAppModal.svelte @@ -52,7 +52,9 @@ applicationName: string().required("Your application must have a name."), }, { - username: string().required("Your application needs a first user."), + email: string() + .email() + .required("Your application needs a first user."), password: string().required( "Please enter a password for your first user." ), @@ -164,8 +166,7 @@ // Create user const user = { - name: $createAppStore.values.username, - username: $createAppStore.values.username, + email: $createAppStore.values.email, password: $createAppStore.values.password, accessLevelId: $createAppStore.values.accessLevelId, } diff --git a/packages/builder/src/components/start/Steps/User.svelte b/packages/builder/src/components/start/Steps/User.svelte index edc1fdf40b..e778d5f8e7 100644 --- a/packages/builder/src/components/start/Steps/User.svelte +++ b/packages/builder/src/components/start/Steps/User.svelte @@ -2,18 +2,18 @@ import { Input, Select } from "@budibase/bbui" export let validationErrors - let blurred = { username: false, password: false } + let blurred = { email: false, password: false }

Create your first User

(blurred.username = true)} - label="Username" - name="username" - placeholder="Username" - type="name" - error={blurred.username && validationErrors.username} /> + on:input={() => (blurred.email = true)} + label="Email" + name="email" + placeholder="Email" + type="email" + error={blurred.email && validationErrors.email} /> (blurred.password = true)} label="Password" diff --git a/packages/builder/src/constants/index.js b/packages/builder/src/constants/index.js index ae88d81bde..ff424bf0ef 100644 --- a/packages/builder/src/constants/index.js +++ b/packages/builder/src/constants/index.js @@ -3,7 +3,7 @@ export const TableNames = { } // fields on the user table that cannot be edited -export const UNEDITABLE_USER_FIELDS = ["username", "password", "accessLevelId"] +export const UNEDITABLE_USER_FIELDS = ["email", "password", "accessLevelId"] export const DEFAULT_PAGES_OBJECT = { main: { diff --git a/packages/client/src/api/auth.js b/packages/client/src/api/auth.js index 9273a00cc7..ddaaa7a491 100644 --- a/packages/client/src/api/auth.js +++ b/packages/client/src/api/auth.js @@ -3,15 +3,15 @@ import API from "./api" /** * Performs a log in request. */ -export const logIn = async ({ username, password }) => { - if (!username) { - return API.error("Please enter your username") +export const logIn = async ({ email, password }) => { + if (!email) { + return API.error("Please enter your email") } if (!password) { return API.error("Please enter your password") } return await API.post({ url: "/api/authenticate", - body: { username, password }, + body: { email, password }, }) } diff --git a/packages/client/src/store/auth.js b/packages/client/src/store/auth.js index a7c91a0972..d641113b4a 100644 --- a/packages/client/src/store/auth.js +++ b/packages/client/src/store/auth.js @@ -5,8 +5,8 @@ import { writable } from "svelte/store" const createAuthStore = () => { const store = writable("") - const logIn = async ({ username, password }) => { - const user = await API.logIn({ username, password }) + const logIn = async ({ email, password }) => { + const user = await API.logIn({ email, password }) if (!user.error) { store.set(user.token) location.reload() diff --git a/packages/server/src/api/controllers/auth.js b/packages/server/src/api/controllers/auth.js index 21136b0214..bac4ff6c2d 100644 --- a/packages/server/src/api/controllers/auth.js +++ b/packages/server/src/api/controllers/auth.js @@ -10,21 +10,21 @@ exports.authenticate = async ctx => { const appId = ctx.appId if (!appId) ctx.throw(400, "No appId") - const { username, password } = ctx.request.body + const { email, password } = ctx.request.body - if (!username) ctx.throw(400, "Username Required.") + if (!email) ctx.throw(400, "Email Required.") if (!password) ctx.throw(400, "Password Required.") - // Check the user exists in the instance DB by username + // Check the user exists in the instance DB by email const db = new CouchDB(appId) const app = await db.get(appId) let dbUser try { - dbUser = await db.get(generateUserID(username)) + dbUser = await db.get(generateUserID(email)) } catch (_) { // do not want to throw a 404 - as this could be - // used to determine valid usernames + // used to determine valid emails ctx.throw(401, "Invalid Credentials") } diff --git a/packages/server/src/api/controllers/user.js b/packages/server/src/api/controllers/user.js index f67ae72a89..83408d80f9 100644 --- a/packages/server/src/api/controllers/user.js +++ b/packages/server/src/api/controllers/user.js @@ -20,16 +20,10 @@ exports.fetch = async function(ctx) { exports.create = async function(ctx) { const db = new CouchDB(ctx.user.appId) - const { - username, - password, - name, - accessLevelId, - permissions, - } = ctx.request.body + const { email, password, name, accessLevelId, permissions } = ctx.request.body - if (!username || !password) { - ctx.throw(400, "Username and Password Required.") + if (!email || !password) { + ctx.throw(400, "email and Password Required.") } const accessLevel = await checkAccessLevel(db, accessLevelId) @@ -37,10 +31,10 @@ exports.create = async function(ctx) { if (!accessLevel) ctx.throw(400, "Invalid Access Level") const user = { - _id: generateUserID(username), - username, + _id: generateUserID(email), + email, password: await bcrypt.hash(password), - name: name || username, + name, type: "user", accessLevelId, permissions: permissions || [BUILTIN_PERMISSION_NAMES.POWER], @@ -54,7 +48,7 @@ exports.create = async function(ctx) { ctx.userId = response._id ctx.body = { _rev: response.rev, - username, + email, name, } } catch (err) { @@ -76,22 +70,22 @@ exports.update = async function(ctx) { user._rev = response.rev ctx.status = 200 - ctx.message = `User ${ctx.request.body.username} updated successfully.` + ctx.message = `User ${ctx.request.body.email} updated successfully.` ctx.body = response } exports.destroy = async function(ctx) { const database = new CouchDB(ctx.user.appId) - await database.destroy(generateUserID(ctx.params.username)) - ctx.message = `User ${ctx.params.username} deleted.` + await database.destroy(generateUserID(ctx.params.email)) + ctx.message = `User ${ctx.params.email} deleted.` ctx.status = 200 } exports.find = async function(ctx) { const database = new CouchDB(ctx.user.appId) - const user = await database.get(generateUserID(ctx.params.username)) + const user = await database.get(generateUserID(ctx.params.email)) ctx.body = { - username: user.username, + email: user.email, name: user.name, _rev: user._rev, } diff --git a/packages/server/src/api/routes/tests/couchTestUtils.js b/packages/server/src/api/routes/tests/couchTestUtils.js index ee12c1c6d8..783eca8920 100644 --- a/packages/server/src/api/routes/tests/couchTestUtils.js +++ b/packages/server/src/api/routes/tests/couchTestUtils.js @@ -118,7 +118,7 @@ exports.clearApplications = async request => { exports.createUser = async ( request, appId, - username = "babs", + email = "babs", password = "babs_password" ) => { const res = await request @@ -126,7 +126,7 @@ exports.createUser = async ( .set(exports.defaultHeaders(appId)) .send({ name: "Bill", - username, + email, password, accessLevelId: BUILTIN_LEVEL_IDS.POWER, }) @@ -174,15 +174,14 @@ const createUserWithPermissions = async ( request, appId, permissions, - username + email ) => { - const password = `password_${username}` + const password = `password_${email}` await request .post(`/api/users`) .set(exports.defaultHeaders(appId)) .send({ - name: username, - username, + email, password, accessLevelId: BUILTIN_LEVEL_IDS.POWER, permissions, @@ -203,7 +202,7 @@ const createUserWithPermissions = async ( Cookie: `budibase:${appId}:local=${anonToken}`, "x-budibase-app-id": appId, }) - .send({ username, password }) + .send({ email, password }) // returning necessary request headers return { diff --git a/packages/server/src/api/routes/tests/user.spec.js b/packages/server/src/api/routes/tests/user.spec.js index f9277039ea..94dcb6c63c 100644 --- a/packages/server/src/api/routes/tests/user.spec.js +++ b/packages/server/src/api/routes/tests/user.spec.js @@ -34,8 +34,8 @@ describe("/users", () => { describe("fetch", () => { it("returns a list of users from an instance db", async () => { - await createUser(request, appId, "brenda", "brendas_password") - await createUser(request, appId, "pam", "pam_password") + await createUser(request, appId, "brenda@brenda.com", "brendas_password") + await createUser(request, appId, "pam@pam.com", "pam_password") const res = await request .get(`/api/users`) .set(defaultHeaders(appId)) @@ -43,8 +43,8 @@ describe("/users", () => { .expect(200) expect(res.body.length).toBe(2) - expect(res.body.find(u => u.username === "brenda")).toBeDefined() - expect(res.body.find(u => u.username === "pam")).toBeDefined() + expect(res.body.find(u => u.email === "brenda@brenda.com")).toBeDefined() + expect(res.body.find(u => u.email === "pam@pam.com")).toBeDefined() }) it("should apply authorization to endpoint", async () => { @@ -67,7 +67,7 @@ describe("/users", () => { const res = await request .post(`/api/users`) .set(defaultHeaders(appId)) - .send({ name: "Bill", username: "bill", password: "bills_password", accessLevelId: BUILTIN_LEVEL_IDS.POWER }) + .send({ name: "Bill", email: "bill@bill.com", password: "bills_password", accessLevelId: BUILTIN_LEVEL_IDS.POWER }) .expect(200) .expect('Content-Type', /json/) @@ -79,7 +79,7 @@ describe("/users", () => { await testPermissionsForEndpoint({ request, method: "POST", - body: { name: "brandNewUser", username: "brandNewUser", password: "yeeooo", accessLevelId: BUILTIN_LEVEL_IDS.POWER }, + body: { name: "brandNewUser", email: "brandNewUser@user.com", password: "yeeooo", accessLevelId: BUILTIN_LEVEL_IDS.POWER }, url: `/api/users`, appId: appId, permName1: BUILTIN_PERMISSION_NAMES.ADMIN, diff --git a/packages/server/src/api/routes/user.js b/packages/server/src/api/routes/user.js index 9394d842bd..1ad1d2363e 100644 --- a/packages/server/src/api/routes/user.js +++ b/packages/server/src/api/routes/user.js @@ -16,7 +16,7 @@ router controller.fetch ) .get( - "/api/users/:username", + "/api/users/:email", authorized(PermissionTypes.USER, PermissionLevels.READ), controller.find ) @@ -32,7 +32,7 @@ router controller.create ) .delete( - "/api/users/:username", + "/api/users/:email", authorized(PermissionTypes.USER, PermissionLevels.WRITE), usage, controller.destroy diff --git a/packages/server/src/app.js b/packages/server/src/app.js index 10eec1e66f..58526cdf4c 100644 --- a/packages/server/src/app.js +++ b/packages/server/src/app.js @@ -1,4 +1,5 @@ const Koa = require("koa") +const destroyable = require("server-destroy") const electron = require("electron") const koaBody = require("koa-body") const logger = require("koa-pino-logger") @@ -44,6 +45,7 @@ if (electron.app && electron.app.isPackaged) { } const server = http.createServer(app.callback()) +destroyable(server) server.on("close", () => console.log("Server Closed")) @@ -51,3 +53,14 @@ module.exports = server.listen(env.PORT || 4001, () => { console.log(`Budibase running on ${JSON.stringify(server.address())}`) automations.init() }) + +process.on("uncaughtException", err => { + console.error(err) + server.close() + server.destroy() +}) + +process.on("SIGTERM", () => { + server.close() + server.destroy() +}) diff --git a/packages/server/src/automations/steps/createUser.js b/packages/server/src/automations/steps/createUser.js index b24c2cbe56..2d5dc2a4fe 100644 --- a/packages/server/src/automations/steps/createUser.js +++ b/packages/server/src/automations/steps/createUser.js @@ -5,7 +5,7 @@ const usage = require("../../utilities/usageQuota") module.exports.definition = { description: "Create a new user", - tagline: "Create user {{inputs.username}}", + tagline: "Create user {{inputs.email}}", icon: "ri-user-add-line", name: "Create User", type: "ACTION", @@ -16,9 +16,10 @@ module.exports.definition = { schema: { inputs: { properties: { - username: { + email: { type: "string", - title: "Username", + customType: "email", + title: "Email", }, password: { type: "string", @@ -32,7 +33,7 @@ module.exports.definition = { pretty: accessLevels.BUILTIN_LEVEL_NAME_ARRAY, }, }, - required: ["username", "password", "accessLevelId"], + required: ["email", "password", "accessLevelId"], }, outputs: { properties: { @@ -59,13 +60,13 @@ module.exports.definition = { } module.exports.run = async function({ inputs, appId, apiKey }) { - const { username, password, accessLevelId } = inputs + const { email, password, accessLevelId } = inputs const ctx = { user: { appId: appId, }, request: { - body: { username, password, accessLevelId }, + body: { email, password, accessLevelId }, }, } diff --git a/packages/server/src/constants/index.js b/packages/server/src/constants/index.js index 24fe45eb6b..a1f70c6c62 100644 --- a/packages/server/src/constants/index.js +++ b/packages/server/src/constants/index.js @@ -12,17 +12,18 @@ const USERS_TABLE_SCHEMA = { views: {}, name: "Users", schema: { - username: { + email: { type: "string", constraints: { type: "string", + email: true, length: { maximum: "", }, presence: true, }, - fieldName: "username", - name: "username", + fieldName: "email", + name: "email", }, accessLevelId: { fieldName: "accessLevelId", @@ -35,7 +36,7 @@ const USERS_TABLE_SCHEMA = { }, }, }, - primaryDisplay: "username", + primaryDisplay: "email", } exports.AuthTypes = AuthTypes diff --git a/packages/server/src/db/utils.js b/packages/server/src/db/utils.js index 96fd92218e..1f347fe7af 100644 --- a/packages/server/src/db/utils.js +++ b/packages/server/src/db/utils.js @@ -101,10 +101,10 @@ exports.generateRowID = tableId => { /** * Gets parameters for retrieving users, this is a utility function for the getDocParams function. */ -exports.getUserParams = (username = "", otherProps = {}) => { +exports.getUserParams = (email = "", otherProps = {}) => { return getDocParams( DocumentTypes.ROW, - `${ViewNames.USERS}${SEPARATOR}${DocumentTypes.USER}${SEPARATOR}${username}`, + `${ViewNames.USERS}${SEPARATOR}${DocumentTypes.USER}${SEPARATOR}${email}`, otherProps ) } diff --git a/packages/standard-components/components.json b/packages/standard-components/components.json index 7504c15b40..c55c04f119 100644 --- a/packages/standard-components/components.json +++ b/packages/standard-components/components.json @@ -56,7 +56,7 @@ }, "login": { "name": "Login Control", - "description": "A control that accepts username, password an also handles password resets", + "description": "A control that accepts email, password an also handles password resets", "props": { "logo": "string", "title": "string", diff --git a/packages/standard-components/src/Login.svelte b/packages/standard-components/src/Login.svelte index 4193f0a684..27c540abc7 100644 --- a/packages/standard-components/src/Login.svelte +++ b/packages/standard-components/src/Login.svelte @@ -10,7 +10,7 @@ export let buttonClass = "" export let inputClass = "" - let username = "" + let email = "" let password = "" let loading = false let error = false @@ -24,7 +24,7 @@ const login = async () => { loading = true - await authStore.actions.logIn({ username, password }) + await authStore.actions.logIn({ email, password }) loading = false } @@ -32,7 +32,9 @@
{#if logo} -
logo
+
+ logo +
{/if} {#if title} @@ -42,9 +44,9 @@
@@ -62,7 +64,7 @@
{#if error} -
Incorrect username or password
+
Incorrect email or password
{/if}
From ad4e4e46793a7abd8f026b7fa740031bf1318dad Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Fri, 4 Dec 2020 13:28:19 +0000 Subject: [PATCH 38/79] update user id generation --- packages/server/src/db/utils.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/server/src/db/utils.js b/packages/server/src/db/utils.js index 1f347fe7af..02ffd5019c 100644 --- a/packages/server/src/db/utils.js +++ b/packages/server/src/db/utils.js @@ -111,11 +111,11 @@ exports.getUserParams = (email = "", otherProps = {}) => { /** * Generates a new user ID based on the passed in username. - * @param {string} username The username which the ID is going to be built up of. + * @param {string} email The email which the ID is going to be built up of. * @returns {string} The new user ID which the user doc can be stored under. */ -exports.generateUserID = username => { - return `${DocumentTypes.ROW}${SEPARATOR}${ViewNames.USERS}${SEPARATOR}${DocumentTypes.USER}${SEPARATOR}${username}` +exports.generateUserID = email => { + return `${DocumentTypes.ROW}${SEPARATOR}${ViewNames.USERS}${SEPARATOR}${DocumentTypes.USER}${SEPARATOR}${email}` } /** From 1c372991070a03fee3c4ff2f29d3a3edbab54d75 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 4 Dec 2020 14:01:02 +0000 Subject: [PATCH 39/79] Fixing a caching bug found by cheeks. --- packages/server/src/utilities/security/roles.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/utilities/security/roles.js b/packages/server/src/utilities/security/roles.js index e971217d17..b503446823 100644 --- a/packages/server/src/utilities/security/roles.js +++ b/packages/server/src/utilities/security/roles.js @@ -148,7 +148,7 @@ class AccessController { let roleIds = this.userHierarchies[userRoleId] if (!roleIds) { roleIds = await exports.getUserRoleHierarchy(this.appId, userRoleId) - this.userHierarchies[userRoleId] = userRoleId + this.userHierarchies[userRoleId] = roleIds } return roleIds.indexOf(tryingRoleId) !== -1 From cecf4e9bca560d5a19b23f4e275d2797a7a7d25e Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Dec 2020 14:01:10 +0000 Subject: [PATCH 40/79] Fix bug with checking user access --- packages/server/src/utilities/security/roles.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/utilities/security/roles.js b/packages/server/src/utilities/security/roles.js index 09ca2c23fd..8a2b343c7a 100644 --- a/packages/server/src/utilities/security/roles.js +++ b/packages/server/src/utilities/security/roles.js @@ -108,7 +108,7 @@ class AccessController { let roleIds = this.userHierarchies[userRoleId] if (!roleIds) { roleIds = await exports.getUserRoleHierarchy(this.appId, userRoleId) - this.userHierarchies[userRoleId] = userRoleId + this.userHierarchies[userRoleId] = roleIds } return roleIds.indexOf(tryingRoleId) !== -1 From 0eb599ee168b26ffd3d4f21cf89b5fc3673cdf62 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Dec 2020 14:02:58 +0000 Subject: [PATCH 41/79] Fix bug with getting screen list in app definition --- packages/server/src/api/controllers/application.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/server/src/api/controllers/application.js b/packages/server/src/api/controllers/application.js index 5d001691d4..b06a7fa759 100644 --- a/packages/server/src/api/controllers/application.js +++ b/packages/server/src/api/controllers/application.js @@ -26,7 +26,10 @@ const { downloadExtractComponentLibraries, } = require("../../utilities/createAppPackage") const { BASE_LAYOUTS } = require("../../constants/layouts") -const { HOME_SCREEN, LOGIN_SCREEN } = require("../../constants/screens") +const { + createHomeScreen, + createLoginScreen, +} = require("../../constants/screens") const { cloneDeep } = require("lodash/fp") const { recurseMustache } = require("../../utilities/mustache") const { generateAssetCss } = require("../../utilities/builder/generateCss") @@ -112,7 +115,7 @@ exports.fetchAppDefinition = async function(ctx) { const layouts = await getLayouts(db) const userRoleId = getUserRoleId(ctx) const accessController = new AccessController(ctx.params.appId) - const screens = accessController.checkScreensAccess( + const screens = await accessController.checkScreensAccess( await getScreens(db), userRoleId ) @@ -218,11 +221,11 @@ const createEmptyAppPackage = async (ctx, app) => { screensAndLayouts.push(recurseMustache(cloned, app)) } - const homeScreen = cloneDeep(HOME_SCREEN) + const homeScreen = createHomeScreen(app) homeScreen._id = generateScreenID() screensAndLayouts.push(homeScreen) - const loginScreen = cloneDeep(LOGIN_SCREEN) + const loginScreen = createLoginScreen(app) loginScreen._id = generateScreenID() screensAndLayouts.push(loginScreen) From 75e4b4f8cdf767ad8500ed26b49d3826ac52ee86 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Dec 2020 14:03:40 +0000 Subject: [PATCH 42/79] Change screen templates to be functions which can be enriched by the application --- packages/server/src/constants/screens.js | 33 ++++++++++++++++-------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/packages/server/src/constants/screens.js b/packages/server/src/constants/screens.js index 3ff3a68590..1e65e7baa3 100644 --- a/packages/server/src/constants/screens.js +++ b/packages/server/src/constants/screens.js @@ -1,7 +1,7 @@ const { BUILTIN_ROLE_IDS } = require("../utilities/security/roles") const { BASE_LAYOUT_PROP_IDS } = require("./layouts") -exports.HOME_SCREEN = { +exports.createHomeScreen = app => ({ description: "", url: "", props: { @@ -99,9 +99,9 @@ exports.HOME_SCREEN = { roleId: BUILTIN_ROLE_IDS.BASIC, }, name: "home-screen", -} +}) -exports.LOGIN_SCREEN = { +exports.createLoginScreen = app => ({ description: "", url: "", props: { @@ -113,8 +113,8 @@ exports.LOGIN_SCREEN = { flex: "1 1 auto", display: "flex", "flex-direction": "column", - "justify-content": "flex-start", - "align-items": "stretch", + "justify-content": "center", + "align-items": "center", }, hover: {}, active: {}, @@ -127,15 +127,26 @@ exports.LOGIN_SCREEN = { _id: "781e497e-2e7c-11eb-adc1-0242ac120002", _component: "@budibase/standard-components/login", _styles: { - normal: {}, + normal: { + padding: "64px", + background: "rgba(255, 255, 255, 0.4)", + "border-radius": "0.5rem", + "margin-top": "0px", + "box-shadow": + "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)", + "font-size": "16px", + "font-family": "Inter", + flex: "0 1 auto", + }, hover: {}, active: {}, selected: {}, }, _code: "", - className: "", - onLoad: [], - type: "div", + logo: + "https://d33wubrfki0l68.cloudfront.net/aac32159d7207b5085e74a7ef67afbb7027786c5/2b1fd/img/logo/bb-emblem.svg", + title: `Log in to ${app.name}`, + buttonText: "Log In", _children: [], _instanceName: "Login", }, @@ -143,7 +154,7 @@ exports.LOGIN_SCREEN = { }, routing: { route: "/", - accessLevelId: BUILTIN_ROLE_IDS.PUBLIC, + roleId: BUILTIN_ROLE_IDS.PUBLIC, }, name: "login-screen", -} +}) From 155c375ada2cc36e27ecfa5627c7efeb97110261 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Dec 2020 14:04:07 +0000 Subject: [PATCH 43/79] Update client library to work with template screens in real preview --- packages/client/src/store/screens.js | 29 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/client/src/store/screens.js b/packages/client/src/store/screens.js index 6f5bcd1396..b1dbdfd28f 100644 --- a/packages/client/src/store/screens.js +++ b/packages/client/src/store/screens.js @@ -20,20 +20,21 @@ const createScreenStore = () => { activeScreen = $builderStore.screen } else { // Otherwise find the correct screen by matching the current route - // const { screens, layouts } = $config - // activeLayout = layouts[0] - // if (screens.length === 1) { - // activeScreen = screens[0] - // } else { - // activeScreen = screens.find( - // screen => screen.routing.route === $routeStore.activeRoute - // ) - // } - // if (activeScreen) { - // activeLayout = layouts.find( - // layout => layout._id === activeScreen.props.layoutId - // ) - // } + const { screens, layouts } = $config + console.log(screens) + activeLayout = layouts[0] + if (screens.length === 1) { + activeScreen = screens[0] + } else { + activeScreen = screens.find( + screen => screen.routing.route === $routeStore.activeRoute + ) + } + if (activeScreen) { + activeLayout = layouts.find( + layout => layout._id === activeScreen.props.layoutId + ) + } } return { activeLayout, activeScreen } } From f1d4b60b97376a95ec5a416e4d412de350c31392 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Dec 2020 14:06:55 +0000 Subject: [PATCH 44/79] Add basic role by default to new screens --- .../src/builderStore/store/screenTemplates/utils/Screen.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/builderStore/store/screenTemplates/utils/Screen.js b/packages/builder/src/builderStore/store/screenTemplates/utils/Screen.js index df727de9a1..aadf5b05eb 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/utils/Screen.js +++ b/packages/builder/src/builderStore/store/screenTemplates/utils/Screen.js @@ -19,7 +19,7 @@ export class Screen extends BaseStructure { }, routing: { route: "", - roleId: "", + roleId: "BASIC", }, name: "screen-id", } From 5f811b9852129da498abb25ff1c759f8dee6f147 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Dec 2020 14:07:23 +0000 Subject: [PATCH 45/79] Remove log statement --- packages/builder/src/builderStore/store/frontend.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index 6977d238dc..91ca68f916 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -153,7 +153,6 @@ export const getFrontendStore = () => { } return state }) - console.log(screen) return screen }, regenerateCss: async asset => { From dbe754dea4a63e3ec471414baf3d43ad0dacf893 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 4 Dec 2020 14:07:56 +0000 Subject: [PATCH 46/79] Fix screen list UI width and tidy up unused imports --- .../userInterface/FrontendNavigatePane.svelte | 13 +- packages/builder/yarn.lock | 179 +----------------- 2 files changed, 12 insertions(+), 180 deletions(-) diff --git a/packages/builder/src/components/userInterface/FrontendNavigatePane.svelte b/packages/builder/src/components/userInterface/FrontendNavigatePane.svelte index e8732cb8e0..1cd2049827 100644 --- a/packages/builder/src/components/userInterface/FrontendNavigatePane.svelte +++ b/packages/builder/src/components/userInterface/FrontendNavigatePane.svelte @@ -1,14 +1,11 @@ + +
+ +
diff --git a/packages/builder/src/components/userInterface/NewLayoutModal.svelte b/packages/builder/src/components/userInterface/NewLayoutModal.svelte new file mode 100644 index 0000000000..a96c2e24db --- /dev/null +++ b/packages/builder/src/components/userInterface/NewLayoutModal.svelte @@ -0,0 +1,21 @@ + + + + + diff --git a/packages/builder/src/components/userInterface/SettingsView.svelte b/packages/builder/src/components/userInterface/SettingsView.svelte index 0c881ad092..551d8044bd 100644 --- a/packages/builder/src/components/userInterface/SettingsView.svelte +++ b/packages/builder/src/components/userInterface/SettingsView.svelte @@ -1,6 +1,7 @@ - -
- -
diff --git a/packages/builder/src/components/userInterface/ComponentNavigationTree/LayoutDropdownMenu.svelte b/packages/builder/src/components/userInterface/ComponentNavigationTree/LayoutDropdownMenu.svelte new file mode 100644 index 0000000000..d5a0d411a9 --- /dev/null +++ b/packages/builder/src/components/userInterface/ComponentNavigationTree/LayoutDropdownMenu.svelte @@ -0,0 +1,51 @@ + + +
+
dropdown.show()}> + +
+ + + confirmDeleteDialog.show()} /> + + +
+ + + diff --git a/packages/builder/src/components/userInterface/ComponentNavigationTree/ScreenDropdownMenu.svelte b/packages/builder/src/components/userInterface/ComponentNavigationTree/ScreenDropdownMenu.svelte index 0f2c30ff88..0b0cb44fa7 100644 --- a/packages/builder/src/components/userInterface/ComponentNavigationTree/ScreenDropdownMenu.svelte +++ b/packages/builder/src/components/userInterface/ComponentNavigationTree/ScreenDropdownMenu.svelte @@ -3,7 +3,6 @@ import { store, allScreens } from "builderStore" import { notifier } from "builderStore/store/notifications" import ConfirmDialog from "components/common/ConfirmDialog.svelte" - import EditScreenLayoutModal from "./EditScreenLayoutModal.svelte" import { DropdownMenu, Modal, ModalContent } from "@budibase/bbui" import { DropdownContainer, DropdownItem } from "components/common/Dropdowns" @@ -19,14 +18,6 @@ store.actions.screens.delete(screen) store.actions.routing.fetch() } - - async function saveScreen() { - try { - await store.actions.screens.save(screen) - } catch (err) { - notifier.danger("Error saving page.") - } - }
diff --git a/packages/builder/src/components/userInterface/Layout.svelte b/packages/builder/src/components/userInterface/Layout.svelte index 7222ffd36a..7473e0bfec 100644 --- a/packages/builder/src/components/userInterface/Layout.svelte +++ b/packages/builder/src/components/userInterface/Layout.svelte @@ -2,6 +2,7 @@ import { goto } from "@sveltech/routify" import { FrontendTypes } from "constants" import ComponentTree from "./ComponentNavigationTree/ComponentTree.svelte" + import LayoutDropdownMenu from "./ComponentNavigationTree/LayoutDropdownMenu.svelte" import initDragDropStore from "./ComponentNavigationTree/dragDropStore" import NavItem from "components/common/NavItem.svelte" import { last } from "lodash/fp" @@ -28,7 +29,9 @@ withArrow selected={$store.currentComponentInfo?._id === layout.props._id} opened={$store.currentAssetId === layout._id} - on:click={selectLayout} /> + on:click={selectLayout}> + + {#if $store.currentAssetId === layout._id && layout.props._children} + + \ No newline at end of file From 40f609319847227fa2bb905d3e491cb1ec12feb4 Mon Sep 17 00:00:00 2001 From: Martin McKeaveney Date: Sat, 5 Dec 2020 09:43:00 +0000 Subject: [PATCH 52/79] allow renaming of layouts --- .../src/builderStore/store/frontend.js | 18 ++++++---- .../LayoutDropdownMenu.svelte | 34 ++++++++++++++++--- .../components/userInterface/Layout.svelte | 2 +- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index 8d432ae5d2..8ebfececc4 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -238,15 +238,21 @@ export const getFrontendStore = () => { if (!json.ok) throw new Error("Error updating layout") store.update(state => { - const layoutToUpdate = state.layouts.find( - stateLayout => stateLayout._id === layout._id + layoutToSave._rev = json.rev + layoutToSave._id = json.id + + const layoutIdx = state.layouts.findIndex( + stateLayout => stateLayout._id === layoutToSave._id ) - if (layoutToUpdate) { - layoutToUpdate._rev = json.rev + + if (layoutIdx >= 0) { + // update existing layout + state.layouts.splice(layoutIdx, 1, layoutToSave) } else { - // TODO: when a new layout is created - state.layouts.push({}) + // save new layout + state.layouts.push(layoutToSave) } + return state }) }, diff --git a/packages/builder/src/components/userInterface/ComponentNavigationTree/LayoutDropdownMenu.svelte b/packages/builder/src/components/userInterface/ComponentNavigationTree/LayoutDropdownMenu.svelte index d5a0d411a9..1767cae54c 100644 --- a/packages/builder/src/components/userInterface/ComponentNavigationTree/LayoutDropdownMenu.svelte +++ b/packages/builder/src/components/userInterface/ComponentNavigationTree/LayoutDropdownMenu.svelte @@ -3,16 +3,17 @@ import { store } from "builderStore" import { notifier } from "builderStore/store/notifications" import ConfirmDialog from "components/common/ConfirmDialog.svelte" - import { DropdownMenu, Modal, ModalContent } from "@budibase/bbui" + import { DropdownMenu, Modal, ModalContent, Input } from "@budibase/bbui" import { DropdownContainer, DropdownItem } from "components/common/Dropdowns" + import { cloneDeep } from "lodash/fp" - export let layoutId + export let layout let confirmDeleteDialog + let editLayoutNameModal let dropdown let anchor - - $: layout = $store.layouts.find(layout => layout._id === layoutId) + let name = layout.name const deleteLayout = async () => { try { @@ -22,6 +23,17 @@ notifier.danger(`Error deleting layout: ${err.message}`) } } + + const saveLayout = async () => { + try { + const layoutToSave = cloneDeep(layout) + layoutToSave.name = name + await store.actions.layouts.save(layoutToSave) + notifier.success(`Layout saved successfully.`) + } catch (err) { + notifier.danger(`Error saving layout: ${err.message}`) + } + }
@@ -34,6 +46,10 @@ icon="ri-delete-bin-line" title="Delete" on:click={() => confirmDeleteDialog.show()} /> + editLayoutNameModal.show()} />
@@ -44,6 +60,16 @@ okText="Delete Layout" onOk={deleteLayout} /> + + + + + +