diff --git a/public/assets/css/designsystem_formbuilder.css b/public/assets/css/designsystem_formbuilder.css
index 3c314038..8dca678a 100644
--- a/public/assets/css/designsystem_formbuilder.css
+++ b/public/assets/css/designsystem_formbuilder.css
@@ -6,6 +6,11 @@
line-height: 1em;
text-align: justify;
}
+.formbuilder a {
+ text-decoration: underline;
+ text-decoration-style: wavy;
+ text-decoration-color: rgba(0, 0, 0, 0.3);
+}
.formbuilder input::placeholder, .formbuilder textarea::placeholder {
opacity: 0.6;
}
@@ -46,3 +51,11 @@
border-bottom: 2px solid rgba(70, 99, 114, 0.1);
transition: border-color 0.2s ease-out;
}
+
+.formbuilder .banner {
+ background: var(--bg-color);
+ border: 2px solid rgba(0, 0, 0, 0.05);
+ border-radius: 3px;
+ margin-bottom: 20px;
+ padding: 10px;
+}
diff --git a/public/assets/lib/form.js b/public/assets/lib/form.js
index 868bd239..4f451999 100644
--- a/public/assets/lib/form.js
+++ b/public/assets/lib/form.js
@@ -49,7 +49,7 @@ async function createFormNodes(node, { renderNode, renderLeaf, renderInput, path
else {
const currentPath = path.concat(key);
const $leaf = renderLeaf({
- ...withMarkdown(node[key]),
+ ...node[key],
path: currentPath,
label: key,
});
@@ -122,15 +122,3 @@ export async function createForm(node, opts) {
});
return $container;
}
-
-function withMarkdown(obj) {
- if (!("description" in obj)) return obj;
- obj["description"] = toMarkdown(obj["description"]);
- return obj;
-}
-
-function toMarkdown(str = "") {
- str = str.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1");
- str = str.replaceAll("\n", "
");
- return str;
-}
diff --git a/public/assets/pages/adminpage/helper_form.js b/public/assets/pages/adminpage/helper_form.js
index 958d2f27..1dde7598 100644
--- a/public/assets/pages/adminpage/helper_form.js
+++ b/public/assets/pages/adminpage/helper_form.js
@@ -4,7 +4,7 @@ import rxjs from "../../lib/rx.js";
export function renderLeaf({ format, label, description, type }) {
if (label === "banner") return createElement(`
- ${description}
+ ${fromMarkdown(description)}
`);
const $el = createElement(`
@@ -29,6 +29,12 @@ export function renderLeaf({ format, label, description, type }) {
return $el;
}
+function fromMarkdown(str = "") {
+ str = str.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1");
+ str = str.replaceAll("\n", "
");
+ return str;
+}
+
export function useForm$($inputNodeList) {
return rxjs.pipe(
rxjs.mergeMap(() => $inputNodeList()),
diff --git a/public/assets/pages/adminpage/index.css b/public/assets/pages/adminpage/index.css
index db0941cc..4aa5e86f 100644
--- a/public/assets/pages/adminpage/index.css
+++ b/public/assets/pages/adminpage/index.css
@@ -98,9 +98,6 @@
}
.component_page_admin .page_container a {
color: var(--dark);
- text-decoration: underline;
- text-decoration-style: wavy;
- text-decoration-color: rgba(0, 0, 0, 0.3);
}
.component_page_admin .page_container a:hover {
opacity: 0.8;
@@ -181,13 +178,6 @@
.component_page_admin .formbuilder textarea::placeholder {
opacity: 0.6;
}
-.component_page_admin .formbuilder .banner {
- background: var(--bg-color);
- border: 2px solid rgba(0, 0, 0, 0.05);
- border-radius: 3px;
- margin-bottom: 15px;
- padding: 10px;
-}
.component_page_admin .formbuilder .banner pre {
background: inherit;
color: inherit;
diff --git a/public/assets/pages/viewerpage/application_form.css b/public/assets/pages/viewerpage/application_form.css
index c83c9a1a..7bd8b5b6 100644
--- a/public/assets/pages/viewerpage/application_form.css
+++ b/public/assets/pages/viewerpage/application_form.css
@@ -58,9 +58,6 @@
.component_formviewer > .formviewer_container .box .formbuilder label.no-select > div > span span.mandatory {
opacity: 0.6;
}
-.component_formviewer > .formviewer_container .box .formbuilder label.no-select > div div.description a {
- border-bottom: 1px dashed var(--color);
-}
.component_formviewer .formbuilder .component_input[disabled],
.component_formviewer .formbuilder .component_input[readonly],
diff --git a/public/assets/pages/viewerpage/application_form.js b/public/assets/pages/viewerpage/application_form.js
index 7b39f7f9..e0b13c15 100644
--- a/public/assets/pages/viewerpage/application_form.js
+++ b/public/assets/pages/viewerpage/application_form.js
@@ -64,7 +64,12 @@ export default function(render, { acl$, getFilename, getDownloadUrl }) {
}
return $el;
},
- renderLeaf: ({ label, format, description, required }) => createElement(`
+ renderLeaf: ({ label, format, description, required }) => label === "banner"
+ ? createElement(`
+
+ ${fromMarkdown(safe(description))}
+
+ `) : createElement(`
`),
@@ -170,3 +175,9 @@ function readOnlyForm(formSpec) {
}
return formSpec;
}
+
+function fromMarkdown(str = "") {
+ str = str.replace(/\[([^\]]+)\]\(([^)]+)\)/g, "$1");
+ str = str.replaceAll("\n", "
");
+ return str;
+}
diff --git a/server/plugin/plg_backend_psql/index_cat.go b/server/plugin/plg_backend_psql/index_cat.go
index 365c62aa..5466736f 100644
--- a/server/plugin/plg_backend_psql/index_cat.go
+++ b/server/plugin/plg_backend_psql/index_cat.go
@@ -53,21 +53,34 @@ func (this PSQL) Cat(path string) (io.ReadCloser, error) {
forms := make([]FormElement, len(c))
for i, _ := range columns {
forms[i] = createFormElement(col[i], columns[i])
+ if columnComment := _findCommentColumn(this.ctx, this.db, l.table, columns[i].Name); columnComment != "" {
+ forms[i].Description = columnComment
+ }
if slices.Contains(columns[i].Constraint, "PRIMARY KEY") && forms[i].Value != nil {
forms[i].ReadOnly = true
} else if slices.Contains(columns[i].Constraint, "FOREIGN KEY") {
if link, err := _findRelation(this.ctx, this.db, columns[i]); err == nil {
- forms[i].Description = _createDescription(columns[i], link)
if len(link.values) > 0 {
forms[i].Type = "select"
forms[i].Opts = link.values
}
+ if forms[i].Description == "" {
+ forms[i].Description = _createDescription(columns[i], link)
+ }
}
} else if values, err := _findEnumValues(this.ctx, this.db, columns[i]); err == nil && len(values) > 0 {
forms[i].Type = "select"
forms[i].Opts = values
}
-
+ }
+ if comment := _findCommentTable(this.ctx, this.db, l.table); comment != "" {
+ forms = append([]FormElement{
+ {
+ Name: "banner",
+ Type: "hidden",
+ Description: comment,
+ },
+ }, forms...)
}
b, err := Form{Elmnts: forms}.MarshalJSON()
if err != nil {
@@ -78,11 +91,36 @@ func (this PSQL) Cat(path string) (io.ReadCloser, error) {
func _createDescription(el Column, link LocationColumn) string {
if slices.Contains(el.Constraint, "FOREIGN KEY") {
- return fmt.Sprintf("points to <%s> → <%s>", link.table, link.column)
+ return fmt.Sprintf("points to [<%s> → <%s>](/files/%s/)", link.table, link.column, link.table)
}
return ""
}
+func _findCommentTable(ctx context.Context, db *sql.DB, tableName string) string {
+ var comment string
+ if err := db.QueryRowContext(ctx, `
+ SELECT obj_description(c.oid)
+ FROM pg_class c
+ WHERE c.relname = $1 AND c.relkind = 'r'
+ `, tableName).Scan(&comment); err != nil {
+ return ""
+ }
+ return comment
+}
+
+func _findCommentColumn(ctx context.Context, db *sql.DB, tableName, columnName string) string {
+ var comment string
+ if err := db.QueryRowContext(ctx, `
+ SELECT col_description(c.oid, a.attnum)
+ FROM pg_class c
+ JOIN pg_attribute a ON a.attrelid = c.oid
+ WHERE c.relname = $1 AND a.attname = $2 AND c.relkind = 'r'
+ `, tableName, columnName).Scan(&comment); err != nil {
+ return ""
+ }
+ return comment
+}
+
func _findRelation(ctx context.Context, db *sql.DB, el Column) (LocationColumn, error) {
l := LocationColumn{}
rows, err := db.QueryContext(ctx, `