From f98ccdfd77b16fc4eb471bfb1621a3c23a51b032 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Pardou?= <jeremie@baserow.io>
Date: Tue, 5 Mar 2024 07:59:38 +0000
Subject: [PATCH] Minor Ab fixes before public

---
 .nvmrc                                        |   2 +-
 .../contrib/builder/domains/handler.py        |   8 ++
 .../baserow/contrib/builder/pages/handler.py  |   6 +-
 .../builder/pages/test_page_handler.py        |   2 +-
 .../integrations/appAuthProviderTypes.js      |   7 +-
 .../baserow_enterprise/locales/en.json        |   3 +-
 .../elements/components/HeadingElement.vue    |   7 +-
 .../collectionField/form/LinkFieldForm.vue    |   3 +
 .../components/page/PreviewNavigationBar.vue  |   2 +-
 .../components/theme/MainThemeConfigBlock.vue |  13 ++-
 web-frontend/modules/builder/elementTypes.js  | 100 ++++++++++--------
 web-frontend/modules/builder/locales/en.json  |   4 +-
 web-frontend/modules/builder/plugin.js        |  14 +--
 .../modules/builder/plugins/router.js         |  10 +-
 .../test/unit/builder/elementTypes.spec.js    |   2 +-
 15 files changed, 97 insertions(+), 86 deletions(-)

diff --git a/.nvmrc b/.nvmrc
index b6a7d89c6..3c032078a 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-16
+18
diff --git a/backend/src/baserow/contrib/builder/domains/handler.py b/backend/src/baserow/contrib/builder/domains/handler.py
index eb49c8dc8..e593d62fb 100644
--- a/backend/src/baserow/contrib/builder/domains/handler.py
+++ b/backend/src/baserow/contrib/builder/domains/handler.py
@@ -107,6 +107,10 @@ class DomainHandler:
 
         prepared_values = domain_type.prepare_values(allowed_values)
 
+        # Save only lower case domain
+        if "domain_name" in prepared_values:
+            prepared_values["domain_name"] = prepared_values["domain_name"].lower()
+
         domain = model_class(builder=builder, order=last_order, **prepared_values)
         domain.save()
 
@@ -138,6 +142,10 @@ class DomainHandler:
 
         prepared_values = domain_type.prepare_values(allowed_values)
 
+        # Save only lower case domain
+        if "domain_name" in prepared_values:
+            prepared_values["domain_name"] = prepared_values["domain_name"].lower()
+
         for key, value in prepared_values.items():
             setattr(domain, key, value)
 
diff --git a/backend/src/baserow/contrib/builder/pages/handler.py b/backend/src/baserow/contrib/builder/pages/handler.py
index 32bdc4dd7..64a556fbd 100644
--- a/backend/src/baserow/contrib/builder/pages/handler.py
+++ b/backend/src/baserow/contrib/builder/pages/handler.py
@@ -228,9 +228,13 @@ class PageHandler:
         :return: A unique path to use
         """
 
+        page_path = proposed_path
+        if page_path.endswith("/"):
+            page_path = page_path[:-1]
+
         existing_paths = list(builder.page_set.values_list("path", flat=True))
         return find_unused_name(
-            [proposed_path], existing_paths, max_length=255, suffix="{0}"
+            [page_path], existing_paths, max_length=255, suffix="/{0}"
         )
 
     def is_page_path_valid(
diff --git a/backend/tests/baserow/contrib/builder/pages/test_page_handler.py b/backend/tests/baserow/contrib/builder/pages/test_page_handler.py
index 07cba1b8f..ac4a98f79 100644
--- a/backend/tests/baserow/contrib/builder/pages/test_page_handler.py
+++ b/backend/tests/baserow/contrib/builder/pages/test_page_handler.py
@@ -213,7 +213,7 @@ def test_is_page_path_valid_raises():
 def test_find_unused_page_path(data_fixture):
     page = data_fixture.create_builder_page(path="/test")
 
-    assert PageHandler().find_unused_page_path(page.builder, "/test") == "/test2"
+    assert PageHandler().find_unused_page_path(page.builder, "/test") == "/test/2"
 
 
 @pytest.mark.django_db
diff --git a/enterprise/web-frontend/modules/baserow_enterprise/integrations/appAuthProviderTypes.js b/enterprise/web-frontend/modules/baserow_enterprise/integrations/appAuthProviderTypes.js
index 0d85b6503..19eabf8e3 100644
--- a/enterprise/web-frontend/modules/baserow_enterprise/integrations/appAuthProviderTypes.js
+++ b/enterprise/web-frontend/modules/baserow_enterprise/integrations/appAuthProviderTypes.js
@@ -1,9 +1,6 @@
 import { AppAuthProviderType } from '@baserow/modules/core/appAuthProviderTypes'
 import LocalBaserowUserSourceForm from '@baserow_enterprise/integrations/localBaserow/components/appAuthProviders/LocalBaserowPasswordAppAuthProviderForm'
-import {
-  TextFieldType,
-  LongTextFieldType,
-} from '@baserow/modules/database/fieldTypes'
+import { PasswordFieldType } from '@baserow/modules/database/fieldTypes'
 
 export class LocalBaserowPasswordAppAuthProviderType extends AppAuthProviderType {
   static getType() {
@@ -23,7 +20,7 @@ export class LocalBaserowPasswordAppAuthProviderType extends AppAuthProviderType
    * It's defined here so that it can be changed by a plugin.
    */
   get allowedPasswordFieldTypes() {
-    return [TextFieldType.getType(), LongTextFieldType.getType()]
+    return [PasswordFieldType.getType()]
   }
 
   getOrder() {
diff --git a/enterprise/web-frontend/modules/baserow_enterprise/locales/en.json b/enterprise/web-frontend/modules/baserow_enterprise/locales/en.json
index 36531931f..71faf6679 100644
--- a/enterprise/web-frontend/modules/baserow_enterprise/locales/en.json
+++ b/enterprise/web-frontend/modules/baserow_enterprise/locales/en.json
@@ -296,6 +296,7 @@
     "localBaserowPassword": "Email/Password"
   },
   "localBaserowPasswordAppAuthProviderForm": {
-    "passwordFieldLabel": "Select password field"
+    "passwordFieldLabel": "Select password field",
+    "noFields": "No compatible fields"
   }
 }
diff --git a/web-frontend/modules/builder/components/elements/components/HeadingElement.vue b/web-frontend/modules/builder/components/elements/components/HeadingElement.vue
index 3c65ac617..c3b76b8f9 100644
--- a/web-frontend/modules/builder/components/elements/components/HeadingElement.vue
+++ b/web-frontend/modules/builder/components/elements/components/HeadingElement.vue
@@ -8,9 +8,12 @@
     <component
       :is="`h${element.level}`"
       class="heading-element__heading"
-      :class="{ [`ab-heading--h${element.level}`]: true }"
+      :class="`ab-heading--h${element.level}`"
       :style="{
-        '--color': resolveColor(element.font_color, headingColorVariables),
+        [`--heading-h${element.level}--color`]: resolveColor(
+          element.font_color,
+          headingColorVariables
+        ),
       }"
     >
       {{ resolvedValue || $t('headingElement.noValue') }}
diff --git a/web-frontend/modules/builder/components/elements/components/collectionField/form/LinkFieldForm.vue b/web-frontend/modules/builder/components/elements/components/collectionField/form/LinkFieldForm.vue
index 83ab6ac7a..c63becb23 100644
--- a/web-frontend/modules/builder/components/elements/components/collectionField/form/LinkFieldForm.vue
+++ b/web-frontend/modules/builder/components/elements/components/collectionField/form/LinkFieldForm.vue
@@ -48,6 +48,9 @@
         :label="$t('linkNavigationSelection.url')"
         :placeholder="$t('linkNavigationSelection.urlPlaceholder')"
         :data-providers-allowed="DATA_PROVIDERS_ALLOWED_ELEMENTS"
+        :application-context-additions="{
+          element,
+        }"
       />
     </FormGroup>
 
diff --git a/web-frontend/modules/builder/components/page/PreviewNavigationBar.vue b/web-frontend/modules/builder/components/page/PreviewNavigationBar.vue
index 871d734e5..4eba12bcf 100644
--- a/web-frontend/modules/builder/components/page/PreviewNavigationBar.vue
+++ b/web-frontend/modules/builder/components/page/PreviewNavigationBar.vue
@@ -23,7 +23,7 @@
         />
         <div
           v-else
-          :key="pathPart.key"
+          :key="`else_${pathPart.key}`"
           class="preview-navigation-bar__address-bar-path"
         >
           {{ pathPart.value }}
diff --git a/web-frontend/modules/builder/components/theme/MainThemeConfigBlock.vue b/web-frontend/modules/builder/components/theme/MainThemeConfigBlock.vue
index d0c7e1e36..badc87ff0 100644
--- a/web-frontend/modules/builder/components/theme/MainThemeConfigBlock.vue
+++ b/web-frontend/modules/builder/components/theme/MainThemeConfigBlock.vue
@@ -106,15 +106,18 @@
           </div>
         </div>
         <div class="theme_settings__section-preview">
-          <h1
-            class="heading-element margin-bottom-2 theme_settings__section-ellipsis"
+          <component
+            :is="`h${i}`"
+            class="margin-bottom-2 theme_settings__section-ellipsis"
+            :class="`ab-heading--h${i}`"
             :style="{
-              '--color': builder.theme[`heading_${i}_color`],
-              '--font-size': builder.theme[`heading_${i}_font_size`] + 'px',
+              [`--heading-h${i}--color`]: builder.theme[`heading_${i}_color`],
+              [`--heading-h${i}--font-size`]:
+                builder.theme[`heading_${i}_font_size`] + 'px',
             }"
           >
             {{ $t('mainThemeConfigBlock.headingValue', { i }) }}
-          </h1>
+          </component>
         </div>
       </div>
     </div>
diff --git a/web-frontend/modules/builder/elementTypes.js b/web-frontend/modules/builder/elementTypes.js
index c81e45352..d0663c956 100644
--- a/web-frontend/modules/builder/elementTypes.js
+++ b/web-frontend/modules/builder/elementTypes.js
@@ -13,7 +13,10 @@ import TableElement from '@baserow/modules/builder/components/elements/component
 import TableElementForm from '@baserow/modules/builder/components/elements/components/forms/general/TableElementForm'
 
 import { ELEMENT_EVENTS } from '@baserow/modules/builder/enums'
-import { ensureBoolean } from '@baserow/modules/core/utils/validator'
+import {
+  ensureBoolean,
+  ensureString,
+} from '@baserow/modules/core/utils/validator'
 import ColumnElement from '@baserow/modules/builder/components/elements/components/ColumnElement'
 import ColumnElementForm from '@baserow/modules/builder/components/elements/components/forms/general/ColumnElementForm'
 import _ from 'lodash'
@@ -296,11 +299,10 @@ export class FormElementType extends ElementType {
    */
   getDisplayName(element, applicationContext) {
     if (element.label) {
-      const resolvedName = this.resolveFormula(
-        element.label,
-        applicationContext
-      )
-      return resolvedName.length ? resolvedName : this.name
+      const resolvedName = ensureString(
+        this.resolveFormula(element.label, applicationContext)
+      ).trim()
+      return resolvedName || this.name
     }
     return this.name
   }
@@ -359,9 +361,11 @@ export class InputTextElementType extends FormElementType {
     const displayValue =
       element.label || element.default_value || element.placeholder
 
-    if (displayValue && displayValue.length) {
-      const resolvedName = this.resolveFormula(displayValue, applicationContext)
-      return resolvedName.length ? resolvedName : this.name
+    if (displayValue?.trim()) {
+      const resolvedName = ensureString(
+        this.resolveFormula(displayValue, applicationContext)
+      ).trim()
+      return resolvedName || this.name
     }
     return this.name
   }
@@ -401,11 +405,10 @@ export class HeadingElementType extends ElementType {
 
   getDisplayName(element, applicationContext) {
     if (element.value && element.value.length) {
-      const resolvedName = this.resolveFormula(
-        element.value,
-        applicationContext
-      )
-      return resolvedName.length ? resolvedName : this.name
+      const resolvedName = ensureString(
+        this.resolveFormula(element.value, applicationContext)
+      ).trim()
+      return resolvedName || this.name
     }
     return this.name
   }
@@ -437,12 +440,11 @@ export class TextElementType extends ElementType {
   }
 
   getDisplayName(element, applicationContext) {
-    if (element.value && element.value.length) {
-      const resolvedName = this.resolveFormula(
-        element.value,
-        applicationContext
-      )
-      return resolvedName.length ? resolvedName : this.name
+    if (element.value) {
+      const resolvedName = ensureString(
+        this.resolveFormula(element.value, applicationContext)
+      ).trim()
+      return resolvedName || this.name
     }
     return this.name
   }
@@ -489,10 +491,9 @@ export class LinkElementType extends ElementType {
         destination = `${destinationPage.name}`
       }
     } else if (element.navigation_type === 'custom') {
-      destination = this.resolveFormula(
-        element.navigate_to_url,
-        applicationContext
-      )
+      destination = ensureString(
+        this.resolveFormula(element.navigate_to_url, applicationContext)
+      ).trim()
     }
 
     if (destination) {
@@ -500,7 +501,9 @@ export class LinkElementType extends ElementType {
     }
 
     if (element.value) {
-      displayValue = this.resolveFormula(element.value, applicationContext)
+      displayValue = ensureString(
+        this.resolveFormula(element.value, applicationContext)
+      ).trim()
     }
 
     return displayValue
@@ -535,12 +538,11 @@ export class ImageElementType extends ElementType {
   }
 
   getDisplayName(element, applicationContext) {
-    if (element.alt_text && element.alt_text.length) {
-      const resolvedName = this.resolveFormula(
-        element.alt_text,
-        applicationContext
-      )
-      return resolvedName.length ? resolvedName : this.name
+    if (element.alt_text) {
+      const resolvedName = ensureString(
+        this.resolveFormula(element.alt_text, applicationContext)
+      ).trim()
+      return resolvedName || this.name
     }
     return this.name
   }
@@ -576,12 +578,11 @@ export class ButtonElementType extends ElementType {
   }
 
   getDisplayName(element, applicationContext) {
-    if (element.value && element.value.length) {
-      const resolvedName = this.resolveFormula(
-        element.value,
-        applicationContext
-      )
-      return resolvedName.length ? resolvedName : this.name
+    if (element.value) {
+      const resolvedName = ensureString(
+        this.resolveFormula(element.value, applicationContext)
+      ).trim()
+      return resolvedName || this.name
     }
     return this.name
   }
@@ -654,16 +655,17 @@ export class TableElementType extends ElementType {
   }
 
   getDisplayName(element, { page }) {
-    let displayValue = ''
+    let suffix = ''
+
     if (element.data_source_id) {
       const dataSource = this.app.store.getters[
         'dataSource/getPageDataSourceById'
       ](page, element.data_source_id)
-      displayValue = dataSource
-        ? `${dataSource.name} ${this.name.toLowerCase()}`
-        : ''
+
+      suffix = dataSource ? ` - ${dataSource.name}` : ''
     }
-    return displayValue.length ? displayValue : this.name
+
+    return `${this.name}${suffix}`
   }
 }
 
@@ -707,9 +709,11 @@ export class DropdownElementType extends FormElementType {
     const displayValue =
       element.label || element.default_value || element.placeholder
 
-    if (displayValue && displayValue.length) {
-      const resolvedName = this.resolveFormula(displayValue, applicationContext)
-      return resolvedName.length ? resolvedName : this.name
+    if (displayValue) {
+      const resolvedName = ensureString(
+        this.resolveFormula(displayValue, applicationContext)
+      ).trim()
+      return resolvedName || this.name
     }
     return this.name
   }
@@ -823,8 +827,10 @@ export class IFrameElementType extends ElementType {
 
   getDisplayName(element, applicationContext) {
     if (element.url && element.url.length) {
-      const resolvedName = this.resolveFormula(element.url, applicationContext)
-      return resolvedName.length ? resolvedName : this.name
+      const resolvedName = ensureString(
+        this.resolveFormula(element.url, applicationContext)
+      )
+      return resolvedName || this.name
     }
     return this.name
   }
diff --git a/web-frontend/modules/builder/locales/en.json b/web-frontend/modules/builder/locales/en.json
index 40cd3c42e..f5ccd7466 100644
--- a/web-frontend/modules/builder/locales/en.json
+++ b/web-frontend/modules/builder/locales/en.json
@@ -70,7 +70,7 @@
     "heading": "Heading",
     "headingDescription": "Page heading title",
     "text": "Text",
-    "textDescription": "Single line text",
+    "textDescription": "Multi-line text",
     "link": "Link",
     "linkDescription": "A link to page/URL",
     "image": "Image",
@@ -286,7 +286,7 @@
   },
   "horizontalAlignmentSelector": {
     "alignment": "Horizontal alignment",
-    "alignmentLeft": "Link",
+    "alignmentLeft": "Left",
     "alignmentCenter": "Center",
     "alignmentRight": "Right"
   },
diff --git a/web-frontend/modules/builder/plugin.js b/web-frontend/modules/builder/plugin.js
index e820296bc..8f17356cb 100644
--- a/web-frontend/modules/builder/plugin.js
+++ b/web-frontend/modules/builder/plugin.js
@@ -96,10 +96,6 @@ import {
 export default (context) => {
   const { store, app, isDev } = context
 
-  if (!app.$featureFlagIsEnabled('builder')) {
-    return
-  }
-
   // Allow locale file hot reloading in dev
   if (isDev && app.i18n) {
     const { i18n } = app
@@ -152,12 +148,10 @@ export default (context) => {
     new DomainsBuilderSettingsType(context)
   )
 
-  if (app.$featureFlagIsEnabled('builder-user-source')) {
-    app.$registry.register(
-      'builderSettings',
-      new UserSourcesBuilderSettingsType(context)
-    )
-  }
+  app.$registry.register(
+    'builderSettings',
+    new UserSourcesBuilderSettingsType(context)
+  )
 
   app.$registry.register('errorPage', new PublicSiteErrorPageType(context))
 
diff --git a/web-frontend/modules/builder/plugins/router.js b/web-frontend/modules/builder/plugins/router.js
index 87cb9f3c2..c078a33b1 100644
--- a/web-frontend/modules/builder/plugins/router.js
+++ b/web-frontend/modules/builder/plugins/router.js
@@ -5,11 +5,6 @@ import {
   routerOptions as defaultRouterOptions,
 } from './defaultRouter'
 
-import {
-  featureFlagIsEnabled,
-  getFeatureFlags,
-} from '@baserow/modules/core/utils/env'
-
 /**
  * Replace the official Nuxt `createRouter` function. If the request hostname is equal
  * to the `PUBLIC_WEB_FRONTEND_URL` hostname, the router will contain only routes that
@@ -37,11 +32,8 @@ export function createRouter(ssrContext, config) {
     ).hostname
     const requestHostname = new URL(`http://${req.headers.host}`).hostname
 
-    const featureFlags = getFeatureFlags(runtimeConfig.public)
     // We allow published routes only if the builder feature flag is on
-    isWebFrontendHostname =
-      frontendHostname === requestHostname ||
-      !featureFlagIsEnabled(featureFlags, 'builder')
+    isWebFrontendHostname = frontendHostname === requestHostname
 
     // Send the variable to the frontend using the `__NUXT__` property
     ssrContext.nuxt.isWebFrontendHostname = isWebFrontendHostname
diff --git a/web-frontend/test/unit/builder/elementTypes.spec.js b/web-frontend/test/unit/builder/elementTypes.spec.js
index 13c5f75cc..b0d8cc3f1 100644
--- a/web-frontend/test/unit/builder/elementTypes.spec.js
+++ b/web-frontend/test/unit/builder/elementTypes.spec.js
@@ -197,7 +197,7 @@ describe('elementTypes tests', () => {
       }
       expect(
         elementType.getDisplayName({ data_source_id: 1 }, applicationContext)
-      ).toBe('Customers elementtype.table')
+      ).toBe('elementType.table - Customers')
 
       // In the event we don't find the data source.
       expect(