Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,24 @@ static List<FormField> copyFields(List<FormField> fields) {
return fields == null || fields.isEmpty() ? List.of() : List.copyOf(fields);
}

static List<FormTab> tabs(Object value) {
if (!(value instanceof Collection<?> tabs)) {
return List.of();
}

List<FormTab> result = new ArrayList<>();
for (Object tab : tabs) {
if (tab instanceof Map<?, ?> map) {
result.add(FormTab.fromMap(objectMap(map)));
}
}
return List.copyOf(result);
}

static List<FormTab> copyTabs(List<FormTab> tabs) {
return tabs == null || tabs.isEmpty() ? List.of() : List.copyOf(tabs);
}

private static FormField field(Map<String, Object> definition) {
String type = string(definition.get("type"), "");
String name = string(definition.get("name"), "");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,25 @@
* objects before they are stored in this definition.
*/
@Builder
public record FormDefinition(List<FormField> fields) {
public record FormDefinition(List<FormField> fields, List<FormTab> tabs) {

public FormDefinition {
fields = ContentTypeDefinitionMapper.copyFields(fields);
tabs = ContentTypeDefinitionMapper.copyTabs(tabs);
}

/** Keeps the original fields-only Java API source compatible. */
public FormDefinition(List<FormField> fields) {
this(fields, List.of());
}

static FormDefinition fromMap(Map<String, Object> form) {
return new FormDefinition(ContentTypeDefinitionMapper.fields(form.get("fields")));
return new FormDefinition(
ContentTypeDefinitionMapper.fields(form.get("fields")),
ContentTypeDefinitionMapper.tabs(form.get("tabs")));
}

public static FormDefinition empty() {
return new FormDefinition(List.of());
return new FormDefinition(List.of(), List.of());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.condation.cms.api.ui.elements;

/*-
* #%L
* CMS Api
* %%
* Copyright (C) 2023 - 2026 CondationCMS
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/

import com.condation.cms.api.ui.elements.fields.FormField;
import java.util.List;
import java.util.Map;
import lombok.Builder;

/** A named group of fields rendered as one tab of a manager form. */
@Builder
public record FormTab(String title, List<FormField> fields) {

public FormTab {
title = ContentTypeDefinitionMapper.string(title, "");
fields = ContentTypeDefinitionMapper.copyFields(fields);
}

static FormTab fromMap(Map<String, Object> tab) {
return new FormTab(
ContentTypeDefinitionMapper.string(tab.get("title"), ""),
ContentTypeDefinitionMapper.fields(tab.get("fields")));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,16 @@ void convertsDynamicRegistrationIntoDetachedTypedDefinition() {
"name", "count",
"options", options));
List<Map<String, Object>> fields = new ArrayList<>(List.of(field));
Map<String, Object> settings = new HashMap<>(Map.of("fields", fields));
Map<String, Object> tabField = new HashMap<>(Map.of(
"type", "text",
"name", "description"));
List<Map<String, Object>> tabFields = new ArrayList<>(List.of(tabField));
Map<String, Object> tab = new HashMap<>(Map.of(
"title", "Details",
"fields", tabFields));
Map<String, Object> settings = new HashMap<>(Map.of(
"fields", fields,
"tabs", new ArrayList<>(List.of(tab))));
Map<String, Object> input = new HashMap<>(Map.of(
"name", "StartPage",
"template", "start.html",
Expand All @@ -65,23 +74,37 @@ void convertsDynamicRegistrationIntoDetachedTypedDefinition() {
assertThat(numberField.getName()).isEqualTo("count");
assertThat(numberField.getOptions().min()).isEqualTo(1);
});
assertThat(pageTemplate.getForm("settings").tabs())
.singleElement()
.satisfies(storedTab -> {
assertThat(storedTab.title()).isEqualTo("Details");
assertThat(storedTab.fields()).singleElement()
.isInstanceOfSatisfying(StringField.class,
storedTabField -> assertThat(storedTabField.getName()).isEqualTo("description"));
});

input.put("template", "changed.html");
field.put("name", "changed");
options.put("min", 99);
tab.put("title", "Changed");
tabField.put("name", "changed");

assertThat(pageTemplate.template()).isEqualTo("start.html");
NumberField storedField = (NumberField) pageTemplate.getForm("settings").fields().getFirst();
assertThat(storedField.getName()).isEqualTo("count");
assertThat(storedField.getOptions().min()).isEqualTo(1);
assertThat(pageTemplate.getForm("settings").tabs().getFirst().title()).isEqualTo("Details");
assertThat(pageTemplate.getForm("settings").tabs().getFirst().fields().getFirst().getName())
.isEqualTo("description");
}

@Test
void supportsTypedJavaRegistration() {
FormDefinition form = new FormDefinition(List.of(
new StringField("title", "Title"),
new NumberField("count", "Count", 0, 100, 1),
new MarkdownField("description", "Description")));
FormDefinition form = new FormDefinition(
List.of(new StringField("title", "Title")),
List.of(new FormTab("Details", List.of(
new NumberField("count", "Count", 0, 100, 1),
new MarkdownField("description", "Description")))));
PageTemplate pageTemplate = PageTemplate.builder()
.name("Default")
.template("default.html")
Expand All @@ -106,5 +129,7 @@ void supportsTypedJavaRegistration() {
assertThat(contentTypes.getListItemTypes()).containsExactly(listItemType);
assertThat(pageTemplate.createButton()).isTrue();
assertThat(form.fields()).allMatch(FormField.class::isInstance);
assertThat(form.tabs()).singleElement()
.satisfies(tab -> assertThat(tab.fields()).allMatch(FormField.class::isInstance));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ void acceptsExistingJavaScriptObjectLiteralRegistrations() {
contentFolder: 'content',
forms: {
settings: {
fields: [{ type: 'text', name: 'title', required: true }]
fields: [{ type: 'text', name: 'title', required: true }],
tabs: [{
title: 'SEO',
fields: [{ type: 'text', name: 'meta.description' }]
}]
}
}
});
Expand All @@ -85,6 +89,14 @@ void acceptsExistingJavaScriptObjectLiteralRegistrations() {
assertThat(field.getName()).isEqualTo("title");
assertThat(field.isRequired()).isTrue();
});
assertThat(pageTemplate.getForm("settings").tabs())
.singleElement()
.satisfies(tab -> {
assertThat(tab.title()).isEqualTo("SEO");
assertThat(tab.fields()).singleElement()
.isInstanceOfSatisfying(StringField.class,
field -> assertThat(field.getName()).isEqualTo("meta.description"));
});
assertThat(contentTypes.getSectionEntryTemplates("main")).hasSize(1);
assertThat(contentTypes.getListItemTypes()).hasSize(1);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,13 @@ export async function runAction(params) {
var mediaForm = (await getMediaForm({
form: params.options.form || 'meta'
})).result;
const fields = [
...mediaForm?.form?.fields
];
const formDefinition = mediaForm?.form ?? {};
const values = {
...(await getMediaMetaData({ image: params.options.image })).result.meta
};
const form = createForm({
fields: fields,
fields: formDefinition.fields ?? [],
tabs: formDefinition.tabs ?? [],
values: values
});
openSidebar({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
import { createForm } from '@cms/modules/form/forms.js';
import { createForm, getFormFields } from '@cms/modules/form/forms.js';
import { showToast } from '@cms/modules/toast.js';
import { reloadPreview } from '@cms/modules/preview.utils.js';
import { buildValuesFromFields, getValueByPath } from '@cms/modules/node.js';
Expand All @@ -39,19 +39,21 @@ export async function runAction(params) {
templates = (await getPageTemplates()).result;
}
var selected = templates.filter(item => item.template === getContentResponse?.result?.meta?.template);
var attrForm = [];
var attrForm = { fields: [], tabs: [] };
if (selected.length === 1) {
attrForm = selected[0].forms?.[params.form]?.fields ?? [];
attrForm = selected[0].forms?.[params.form] ?? attrForm;
}
const attrFields = getFormFields(attrForm);
//const previewMetaForm = getMetaForm()
const fields = [
...attrForm
...(attrForm.fields ?? [])
];
const values = {
...buildValuesFromFields(attrForm, getContentResponse?.result?.meta)
...buildValuesFromFields(attrFields, getContentResponse?.result?.meta)
};
const form = createForm({
fields: fields,
tabs: attrForm.tabs ?? [],
values: values
});
openSidebar({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
* #L%
*/
import { openSidebar } from '@cms/modules/sidebar.js';
import { createForm } from '@cms/modules/form/forms.js';
import { createForm, getFormFields } from '@cms/modules/form/forms.js';
import { showToast } from '@cms/modules/toast.js';
import { setMeta, getContent } from '@cms/modules/rpc/rpc-content.js';
import { reloadPreview } from '@cms/modules/preview.utils.js';
Expand All @@ -40,24 +40,26 @@ export async function runAction(params) {
const getContentResponse = await getContent({});
var pageTemplates = (await getPageTemplates()).result;
var selected = pageTemplates.filter(pageTemplate => pageTemplate.template === getContentResponse?.result?.meta?.template);
var pageSettingsForm = [];
var pageSettingsForm = { fields: [], tabs: [] };
if (selected.length === 1) {
pageSettingsForm = selected[0].forms?.settings?.fields ?? [];
pageSettingsForm = selected[0].forms?.settings ?? pageSettingsForm;
}
const pageSettingsFields = getFormFields(pageSettingsForm);
//const previewMetaForm = getMetaForm()
const fields = [
...DEFAULT_FIELDS,
...pageSettingsForm
...(pageSettingsForm.fields ?? [])
];
const values = {
'title': getContentResponse?.result?.meta?.title,
'status': getContentResponse?.result?.meta?.status || 'draft',
'publish_date': getContentResponse?.result?.meta?.publish_date,
'unpublish_date': getContentResponse?.result?.meta?.unpublish_date,
...buildValuesFromFields(pageSettingsForm, getContentResponse?.result?.meta)
...buildValuesFromFields(pageSettingsFields, getContentResponse?.result?.meta)
};
const form = createForm({
fields: fields,
tabs: pageSettingsForm.tabs ?? [],
values: values
});
openSidebar({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
*/
import { createID } from "@cms/modules/form/utils.js";
import { i18n } from "@cms/modules/localization.js";
import { createForm } from "@cms/modules/form/forms.js";
import { createForm, getFormFields } from "@cms/modules/form/forms.js";
import { openModal } from "@cms/modules/modal.js";
import { buildValuesFromFields } from "@cms/modules/node.js";
import { getListItemTypes, getPageTemplates } from "@cms/modules/rpc/rpc-manager.js";
Expand Down Expand Up @@ -102,20 +102,20 @@ const getItemForm = async (el) => {
var selected = pageTemplates.filter((pageTemplate) => pageTemplate.template === getContentResponse?.result?.meta?.template);
const listContainer = el.closest("[data-cms-form-field-type='list']");
const fieldName = listContainer?.getAttribute('name');
var itemForm = [];
var itemForm = { fields: [], tabs: [] };
if (selected.length === 1) {
itemForm = fieldName ? selected[0].forms?.[fieldName]?.fields ?? [] : [];
itemForm = fieldName ? selected[0].forms?.[fieldName] ?? itemForm : itemForm;
}
if (!itemForm || itemForm.length === 0) {
if (getFormFields(itemForm).length === 0) {
let itemTypes = (await getListItemTypes({})).result;
var selectedItemType = itemTypes.filter((itemType) => itemType.name === fieldName);
itemForm = (selectedItemType.length === 1) ? selectedItemType[0].form?.fields ?? [] : [];
itemForm = (selectedItemType.length === 1) ? selectedItemType[0].form ?? itemForm : itemForm;
}
return itemForm;
}
catch (e) {
console.error("Error loading item form", e);
return [];
return { fields: [], tabs: [] };
}
};
const handleDoubleClick = async (event, context) => {
Expand All @@ -125,10 +125,12 @@ const handleDoubleClick = async (event, context) => {
if (itemDataString) {
const itemData = JSON.parse(itemDataString);
var itemForm = await getItemForm(el);
const itemFields = getFormFields(itemForm);
const form = createForm({
fields: itemForm,
fields: itemForm.fields ?? [],
tabs: itemForm.tabs ?? [],
values: {
...buildValuesFromFields(itemForm, itemData)
...buildValuesFromFields(itemFields, itemData)
}
});
openModal({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
declare const getFormFields: (definition: any) => any[];
declare const createForm: (options: any) => Form;
export { createForm };
export { createForm, getFormFields };
export interface FormContext {
formElement: HTMLFormElement | null;
fields: any[];
Expand Down
Loading
Loading