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
12 changes: 6 additions & 6 deletions cms-api/src/main/java/com/condation/cms/api/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,19 +95,19 @@ public static class ContentTypes {

public static final Pattern TAXONOMY_VALUE = Pattern.compile("taxonomy\\.([a-zA-Z0-9-]+)\\.yaml");

public static final Pattern SLOT_ITEM_PATTERN = Pattern.compile("\\w+\\.(?<slot>[a-zA-Z0-9-]+)\\.md");
public static final Pattern SECTION_ENTRY_PATTERN = Pattern.compile("\\w+\\.(?<section>[a-zA-Z0-9-]+)\\.md");

public static final Function<String, Pattern> SLOT_ITEM_OF_PATTERN = (fileName) -> {
return Pattern.compile("%s\\.(?<slot>[a-zA-Z0-9-]+)\\.md".formatted(Pattern.quote(fileName)));
public static final Function<String, Pattern> SECTION_ENTRY_OF_PATTERN = (fileName) -> {
return Pattern.compile("%s\\.(?<section>[a-zA-Z0-9-]+)\\.md".formatted(Pattern.quote(fileName)));
};

public static final Pattern SLOT_ITEM_NAMED_PATTERN = Pattern.compile("[\\w-]+\\.(?<slot>[a-zA-Z0-9-]+)\\.(?<id>[\\w-]+)\\.md");
public static final Pattern SECTION_ENTRY_NAMED_PATTERN = Pattern.compile("[\\w-]+\\.(?<section>[a-zA-Z0-9-]+)\\.(?<id>[\\w-]+)\\.md");

public static final Function<String, Pattern> SLOT_ITEM_NAMED_OF_PATTERN = (fileName) -> {
public static final Function<String, Pattern> SECTION_ENTRY_NAMED_OF_PATTERN = (fileName) -> {
return Pattern.compile("%s\\.[a-zA-Z0-9-]+\\.[a-zA-Z0-9-]+\\.md".formatted(Pattern.quote(fileName)));
};

public static final int DEFAULT_SLOT_ITEM_LAYOUT_ORDER = 0;
public static final int DEFAULT_SECTION_ENTRY_LAYOUT_ORDER = 0;
public static final double DEFAULT_MENU_POSITION = 1000f;
public static final boolean DEFAULT_MENU_VISIBILITY = true;
public static final int DEFAULT_EXCERPT_LENGTH = 200;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.condation.cms.api.annotations;

/*-
* #%L
* UI Module
* CMS Api
* %%
* Copyright (C) 2023 - 2026 CondationCMS
* %%
Expand All @@ -18,12 +20,20 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
import { openFileBrowser } from '@cms/modules/filebrowser.js';
// hook.js
export async function runAction(params) {
openFileBrowser({
type: "content",
uri: params.folder,
template : params.template,
});

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Maps a hook method parameter to a named argument from the hook's argument map.
* Used on action hook methods that receive individual parameters instead of {@code ActionContext}.
*
* @author t.marx
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Param {
String value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public interface Content {

boolean isVisible (ContentNode node);

List<ContentNode> listSlotItems(final ReadOnlyFile contentFile);
List<ContentNode> listSectionEntries(final ReadOnlyFile contentFile);

List<ContentNode> listContent(final ReadOnlyFile base, final String start);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import com.condation.cms.api.request.RequestContextScope;
import com.condation.cms.api.utils.DateRange;
import com.condation.cms.api.utils.MapUtil;
import com.condation.cms.api.utils.SlotUtil;
import com.condation.cms.api.utils.SectionUtil;
import java.io.Serializable;
import java.time.Instant;
import java.time.LocalDate;
Expand Down Expand Up @@ -124,8 +124,8 @@ public boolean isVisible() {
return DateRange.isNowWithin(publish_date, unpublish_date);
}

public boolean isSlotItem() {
return SlotUtil.isSlotItem(name);
public boolean isSectionEntry() {
return SectionUtil.isSectionEntry(name);
}

public boolean isRedirect() {
Expand Down
18 changes: 7 additions & 11 deletions cms-api/src/main/java/com/condation/cms/api/hooks/HookSystem.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;

/**
*
Expand All @@ -41,18 +41,14 @@ public interface HookSystem {

public <T> void registerFilter(final String name, final FilterFunction<T> hookFunction, int priority);

public ActionContext<Object> doAction(final String name);
public <T> List<T> doAction(final String name);

public ActionContext<Object> doAction(final String name, final Map<String, Object> arguments);
public <T> List<T> doAction(final String name, final Map<String, Object> arguments);

/**
* calls all filters with the given parameters, if no filter is executed,
* the original parameters are returned
*
* @param <T>
* @param name
* @param parameters
* @return
* Calls all filters with the given value in priority order and returns the
* final transformed value. If no filter is registered, the original value
* is returned unchanged.
*/
public <T> FilterContext<T> doFilter(final String name, final T parameters);
public <T> T doFilter(final String name, final T value);
}
4 changes: 2 additions & 2 deletions cms-api/src/main/java/com/condation/cms/api/hooks/Hooks.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ public enum Hooks {
/* content */
CONTENT_TAGS("system/content/tags"),
CONTENT_FILTER("system/content/filter"),
CONTENT_SLOT_HEADER("system/content/slot/header"),
CONTENT_SLOT_FOOTER("system/content/slot/footer"),
LAYOUT_HEADER("system/layout/header"),
LAYOUT_FOOTER("system/layout/footer"),
/*query*/
DB_QUERY_OPERATIONS("system/db/query/operations"),
/* scheduler */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class ContentTypes {

public Set<PageTemplate> pageTemplates = new HashSet();

public Set<SlotItemTemplate> slotItemTemplates = new HashSet<>();
public Set<SectionEntryTemplate> sectionEntryTemplates = new HashSet<>();

public Set<ListItemType> listItemTypes = new HashSet<>();

Expand All @@ -55,21 +55,21 @@ public void registerPageTemplate(Map<String, Object> pageTemplate) {
pageTemplates.add(new PageTemplate(pageTemplate));
}

public void registerSlotItemTemplate(Map<String, Object> slotItemTemplate) {
slotItemTemplates.add(new SlotItemTemplate(slotItemTemplate));
public void registerSectionEntryTemplate(Map<String, Object> sectionEntryTemplate) {
sectionEntryTemplates.add(new SectionEntryTemplate(sectionEntryTemplate));
}

public Set<PageTemplate> getPageTemplates () {
return new HashSet<>(pageTemplates);
}

public Set<SlotItemTemplate> getSlotItemTemplates (String slot) {
return slotItemTemplates.stream()
.filter(template -> template.slot().equals(slot))
public Set<SectionEntryTemplate> getSectionEntryTemplates (String section) {
return sectionEntryTemplates.stream()
.filter(template -> template.section().equals(section))
.collect(Collectors.toSet());
}
public Set<SlotItemTemplate> getSlotItemTemplates () {
return new HashSet<>(slotItemTemplates);
public Set<SectionEntryTemplate> getSectionEntryTemplates () {
return new HashSet<>(sectionEntryTemplates);
}

public static record PageTemplate(String name, String template, Map<String, Object> data) {
Expand All @@ -94,17 +94,17 @@ public boolean addCreateButton () {
}
}

public static record SlotItemTemplate(String name, String template, Map<String, Object> data) {
public static record SectionEntryTemplate(String name, String template, Map<String, Object> data) {

public SlotItemTemplate (Map<String, Object> data) {
public SectionEntryTemplate (Map<String, Object> data) {
this(
(String) data.getOrDefault("name", "<no name>"),
(String) data.getOrDefault("template", "<no template>"),
data);
}

public String slot() {
return (String) data.getOrDefault("slot", "<no slot>");
public String section() {
return (String) data.getOrDefault("section", "<no section>");
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package com.condation.cms.api.utils;

/*-
* #%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.annotations.Param;
import com.google.common.base.Strings;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
* Shared reflection helpers for annotation-driven method registration
* ({@code @Param}, context-style, no-arg).
*
* @author t.marx
*/
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class ParamAnnotationUtil {

/**
* Extracts {@link Param} names from every parameter in {@code params}.
* <ul>
* <li>Returns an empty array when {@code params} is empty (no-arg style).</li>
* <li>Returns {@code null} when any parameter lacks {@code @Param} (unsupported signature).</li>
* </ul>
*/
public static String[] extractParamNames(Parameter[] params) {
if (params.length == 0) {
return new String[0];
}
String[] names = new String[params.length];
for (int i = 0; i < params.length; i++) {
Param p = params[i].getAnnotation(Param.class);
if (p == null) {
return null;
}
names[i] = p.value();
}
return names;
}

/**
* Resolves positional method arguments from {@code source} using {@code names} as keys.
* Missing keys resolve to {@code null}.
*/
public static Object[] resolveArgs(Map<String, Object> source, String[] names) {
Object[] args = new Object[names.length];
for (int i = 0; i < names.length; i++) {
args[i] = source.get(names[i]);
}
return args;
}

/**
* Returns {@code true} when {@code params} contains exactly one entry whose type
* is assignable from {@code contextType}.
*/
public static boolean isContextStyle(Parameter[] params, Class<?> contextType) {
return params.length == 1 && contextType.isAssignableFrom(params[0].getType());
}

/**
* Builds a {@code "namespace:name"} registration key, substituting
* {@code defaultNamespace} when the namespace is blank.
*/
public static String buildNamespaceKey(String namespace, String name, String defaultNamespace) {
String ns = Strings.isNullOrEmpty(namespace) ? defaultNamespace : namespace;
return "%s:%s".formatted(ns, name);
}

/**
* Invokes {@code method} on {@code target} with {@code args}.
* Logs the error and returns {@code null} on failure.
*/
public static Object invokeOrNull(Object target, Method method, Object... args) {
try {
return method.invoke(target, args);
} catch (IllegalAccessException | InvocationTargetException e) {
log.error("error invoking '{}' on '{}'", method.getName(), target.getClass().getSimpleName(), e);
return null;
}
}

/**
* Invokes {@code method} on {@code target} with {@code args}.
* Logs the error and rethrows as {@link RuntimeException} on failure.
*/
public static Object invokeOrThrow(Object target, Method method, String label, Object... args) {
try {
return method.invoke(target, args);
} catch (IllegalAccessException | InvocationTargetException e) {
log.error("error invoking '{}'", label, e);
throw new RuntimeException("Error invoking: " + label, e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,26 @@
*
* @author t.marx
*/
public class SlotUtil {
public class SectionUtil {

public static boolean isNamedSlotItem(final String name) {
return Constants.SLOT_ITEM_NAMED_PATTERN.matcher(name).matches();
public static boolean isNamedSectionEntry(final String name) {
return Constants.SECTION_ENTRY_NAMED_PATTERN.matcher(name).matches();
}

public static String getSlotItemName(final String name) {
if (isNamedSlotItem(name)) {
var matcher = Constants.SLOT_ITEM_NAMED_PATTERN.matcher(name);
public static String getSectionName(final String name) {
if (isNamedSectionEntry(name)) {
var matcher = Constants.SECTION_ENTRY_NAMED_PATTERN.matcher(name);
matcher.matches();
return matcher.group("slot");
return matcher.group("section");
} else {
var matcher = Constants.SLOT_ITEM_PATTERN.matcher(name);
var matcher = Constants.SECTION_ENTRY_PATTERN.matcher(name);
matcher.matches();
return matcher.group("slot");
return matcher.group("section");
}
}

public static boolean isSlotItem(final String name) {
return Constants.SLOT_ITEM_PATTERN.matcher(name).matches()
|| Constants.SLOT_ITEM_NAMED_PATTERN.matcher(name).matches();
public static boolean isSectionEntry(final String name) {
return Constants.SECTION_ENTRY_PATTERN.matcher(name).matches()
|| Constants.SECTION_ENTRY_NAMED_PATTERN.matcher(name).matches();
}
}
Loading