diff --git a/cms-api/src/main/java/com/condation/cms/api/db/cms/NIOReadOnlyFile.java b/cms-api/src/main/java/com/condation/cms/api/db/cms/NIOReadOnlyFile.java index ebc97ea7..7ac2e682 100644 --- a/cms-api/src/main/java/com/condation/cms/api/db/cms/NIOReadOnlyFile.java +++ b/cms-api/src/main/java/com/condation/cms/api/db/cms/NIOReadOnlyFile.java @@ -116,7 +116,7 @@ public ReadOnlyFile getParent() { @Override public List children() throws IOException { - return Files.list(file).map(path -> new NIOReadOnlyFile(file, basePath)).map(ReadOnlyFile.class::cast).toList(); + return Files.list(file).map(child -> new NIOReadOnlyFile(child, basePath)).map(ReadOnlyFile.class::cast).toList(); } @Override diff --git a/cms-api/src/main/java/com/condation/cms/api/extensions/HttpRouteExtensionPoint.java b/cms-api/src/main/java/com/condation/cms/api/extensions/HttpRouteExtensionPoint.java index fb588b90..48c8095c 100644 --- a/cms-api/src/main/java/com/condation/cms/api/extensions/HttpRouteExtensionPoint.java +++ b/cms-api/src/main/java/com/condation/cms/api/extensions/HttpRouteExtensionPoint.java @@ -31,6 +31,7 @@ * * @author t.marx */ +@Deprecated(since = "7.3.0", forRemoval = true) public abstract class HttpRouteExtensionPoint extends AbstractExtensionPoint { abstract public String getRoute (); diff --git a/cms-api/src/main/java/com/condation/cms/api/extensions/http/APIHandlerExtensionPoint.java b/cms-api/src/main/java/com/condation/cms/api/extensions/http/APIHandlerExtensionPoint.java new file mode 100644 index 00000000..4c94490a --- /dev/null +++ b/cms-api/src/main/java/com/condation/cms/api/extensions/http/APIHandlerExtensionPoint.java @@ -0,0 +1,29 @@ +package com.condation.cms.api.extensions.http; + +/*- + * #%L + * cms-api + * %% + * Copyright (C) 2023 - 2024 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.condation.cms.api.extensions.AbstractExtensionPoint; + +public abstract class APIHandlerExtensionPoint extends AbstractExtensionPoint { + abstract public PathMapping getMapping(); +} diff --git a/cms-api/src/main/java/com/condation/cms/api/extensions/http/HttpHandler.java b/cms-api/src/main/java/com/condation/cms/api/extensions/http/HttpHandler.java new file mode 100644 index 00000000..8c495ed4 --- /dev/null +++ b/cms-api/src/main/java/com/condation/cms/api/extensions/http/HttpHandler.java @@ -0,0 +1,36 @@ +package com.condation.cms.api.extensions.http; + +/*- + * #%L + * cms-api + * %% + * Copyright (C) 2023 - 2024 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public + * License along with this program. If not, see + * . + * #L% + */ + +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.Callback; + +/** + * + * @author t.marx + */ +public interface HttpHandler { + + boolean handle (Request request, Response response, Callback callback) throws Exception; +} diff --git a/cms-api/src/main/java/com/condation/cms/api/extensions/http/PathMapping.java b/cms-api/src/main/java/com/condation/cms/api/extensions/http/PathMapping.java new file mode 100644 index 00000000..05afa914 --- /dev/null +++ b/cms-api/src/main/java/com/condation/cms/api/extensions/http/PathMapping.java @@ -0,0 +1,51 @@ +package com.condation.cms.api.extensions.http; + +/*- + * #%L + * cms-api + * %% + * Copyright (C) 2023 - 2024 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import org.eclipse.jetty.http.pathmap.PathSpec; + +public class PathMapping { + + private List mappings; + + public PathMapping () { + mappings = new ArrayList<>(); + } + + public void add (PathSpec pathSpec, String method, HttpHandler handler) { + mappings.add(new Mapping(pathSpec, method, handler)); + } + + public Optional getMatchingHandler (String uri, String method) { + return mappings.stream().filter(entry -> + entry.pathSpec.matches(uri) && entry.method.equalsIgnoreCase(method) + ).map(entry -> entry.handler).findFirst(); + } + + + private static record Mapping (PathSpec pathSpec, String method, HttpHandler handler){}; +} diff --git a/cms-api/src/main/java/com/condation/cms/api/hooks/Hooks.java b/cms-api/src/main/java/com/condation/cms/api/hooks/Hooks.java index 325773f3..61a5ce04 100644 --- a/cms-api/src/main/java/com/condation/cms/api/hooks/Hooks.java +++ b/cms-api/src/main/java/com/condation/cms/api/hooks/Hooks.java @@ -38,6 +38,7 @@ public enum Hooks { SCHEDULER_REMOVE("system/scheduler/remove"), HTTP_EXTENSION("system/server/http/extension"), HTTP_ROUTE("system/server/http/route"), + API_ROUTE("system/server/api/route"), TEMPLATE_SUPPLIER("system/template/supplier"), TEMPLATE_FUNCTION("system/template/function") ; diff --git a/cms-content/src/main/java/com/condation/cms/content/ContentResolver.java b/cms-content/src/main/java/com/condation/cms/content/ContentResolver.java index 3aa52b03..09a28db8 100644 --- a/cms-content/src/main/java/com/condation/cms/content/ContentResolver.java +++ b/cms-content/src/main/java/com/condation/cms/content/ContentResolver.java @@ -52,6 +52,9 @@ public class ContentResolver { private final DB db; public Optional getStaticContent (String uri) { + if (uri.endsWith(".md")) { + return Optional.empty(); + } if (uri.startsWith("/")) { uri = uri.substring(1); } diff --git a/cms-core/src/main/java/com/condation/cms/core/scheduler/SingleCronJobRunner.java b/cms-core/src/main/java/com/condation/cms/core/scheduler/SingleCronJobRunner.java index 00c19fb2..932d22cc 100644 --- a/cms-core/src/main/java/com/condation/cms/core/scheduler/SingleCronJobRunner.java +++ b/cms-core/src/main/java/com/condation/cms/core/scheduler/SingleCronJobRunner.java @@ -21,11 +21,12 @@ * . * #L% */ - import com.condation.cms.api.scheduler.CronJob; import com.condation.cms.api.scheduler.CronJobContext; +import java.util.concurrent.Semaphore; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +import lombok.extern.slf4j.Slf4j; import org.quartz.DisallowConcurrentExecution; import org.quartz.Job; @@ -36,19 +37,29 @@ * * @author t.marx */ +@Slf4j @DisallowConcurrentExecution public class SingleCronJobRunner implements Job { public static final String DATA_CRONJOB = "cronjob"; public static final String DATA_CONTEXT = "context"; + public static final Semaphore LOCK = new Semaphore(1, true); + @Override public void execute(JobExecutionContext context) throws JobExecutionException { - CronJobContext jobContext = (CronJobContext) context.getJobDetail().getJobDataMap().get(DATA_CONTEXT); - CronJob cronJob = (CronJob) context.getJobDetail().getJobDataMap().get(DATA_CRONJOB); + try { + LOCK.acquire(); + CronJobContext jobContext = (CronJobContext) context.getJobDetail().getJobDataMap().get(DATA_CONTEXT); + CronJob cronJob = (CronJob) context.getJobDetail().getJobDataMap().get(DATA_CRONJOB); - if (cronJob != null && jobContext != null) { - cronJob.accept(jobContext); + if (cronJob != null && jobContext != null) { + cronJob.accept(jobContext); + } + } catch (Exception e) { + log.error("", e); + } finally { + LOCK.release(); } } diff --git a/cms-extensions/src/main/java/com/condation/cms/extensions/hooks/ServerHooks.java b/cms-extensions/src/main/java/com/condation/cms/extensions/hooks/ServerHooks.java index 4387f8f4..f4663fb5 100644 --- a/cms-extensions/src/main/java/com/condation/cms/extensions/hooks/ServerHooks.java +++ b/cms-extensions/src/main/java/com/condation/cms/extensions/hooks/ServerHooks.java @@ -59,4 +59,12 @@ public HttpHandlerWrapper getHttpRoutes () { return httpExtensions; } + + public HttpHandlerWrapper getAPIRoutes () { + var httpExtensions = new HttpHandlerWrapper(); + requestContext.get(HookSystemFeature.class).hookSystem() + .execute(Hooks.API_ROUTE.hook(), Map.of("apiRoutes", httpExtensions)); + + return httpExtensions; + } } diff --git a/cms-media/src/test/java/com/condation/cms/media/MediaServiceTest.java b/cms-media/src/test/java/com/condation/cms/media/MediaServiceTest.java index e31b746c..6d15f462 100644 --- a/cms-media/src/test/java/com/condation/cms/media/MediaServiceTest.java +++ b/cms-media/src/test/java/com/condation/cms/media/MediaServiceTest.java @@ -23,7 +23,6 @@ */ -import com.condation.cms.media.FileMediaService; import java.io.IOException; import java.nio.file.Path; import org.assertj.core.api.Assertions; diff --git a/cms-server/pom.xml b/cms-server/pom.xml index 3f9b86b9..c19b3c4b 100644 --- a/cms-server/pom.xml +++ b/cms-server/pom.xml @@ -50,6 +50,11 @@ cms-auth + + com.condation.cms.modules + cms-system-modules + + com.condation.cms cms-test diff --git a/cms-server/src/main/java/com/condation/cms/server/VHost.java b/cms-server/src/main/java/com/condation/cms/server/VHost.java index 8b792c2a..ac220d3a 100644 --- a/cms-server/src/main/java/com/condation/cms/server/VHost.java +++ b/cms-server/src/main/java/com/condation/cms/server/VHost.java @@ -49,13 +49,11 @@ import com.condation.cms.api.eventbus.events.InvalidateTemplateCacheEvent; import com.condation.cms.api.eventbus.events.lifecycle.HostReloadedEvent; import com.condation.cms.api.eventbus.events.lifecycle.HostStoppedEvent; -import com.condation.cms.api.feature.features.ContentRenderFeature; import com.condation.cms.api.feature.features.ThemeFeature; import com.condation.cms.api.module.CMSModuleContext; import com.condation.cms.api.template.TemplateEngine; import com.condation.cms.api.theme.Theme; import com.condation.cms.api.utils.SiteUtil; -import com.condation.cms.content.ContentResolver; import com.condation.cms.core.configuration.ConfigManagement; import com.condation.cms.extensions.GlobalExtensions; import com.condation.cms.extensions.hooks.GlobalHooks; @@ -63,7 +61,6 @@ import com.condation.cms.media.MediaManager; import com.condation.cms.media.SiteMediaManager; import com.condation.cms.media.ThemeMediaManager; -import com.condation.cms.module.DefaultRenderContentFunction; import com.condation.cms.request.RequestContextFactory; import com.condation.cms.server.configs.ModulesModule; import com.condation.cms.server.configs.SiteConfigInitializer; @@ -80,12 +77,12 @@ import com.condation.cms.server.handler.content.JettyContentHandler; import com.condation.cms.server.handler.content.JettyTaxonomyHandler; import com.condation.cms.server.handler.content.JettyViewHandler; -import com.condation.cms.server.handler.extensions.JettyExtensionRouteHandler; import com.condation.cms.server.handler.extensions.JettyHttpHandlerExtensionHandler; +import com.condation.cms.server.handler.http.APIHandler; +import com.condation.cms.server.handler.http.RoutesHandler; import com.condation.cms.server.handler.media.JettyMediaHandler; import com.condation.cms.server.handler.module.JettyModuleHandler; import com.condation.cms.server.handler.module.JettyRouteHandler; -import com.condation.cms.server.handler.module.JettyRoutesHandler; import com.condation.modules.api.ModuleManager; import com.google.inject.Injector; import com.google.inject.Key; @@ -226,8 +223,7 @@ public Handler buildHttpHandler() { var taxonomyHandler = injector.getInstance(JettyTaxonomyHandler.class); var viewHandler = injector.getInstance(JettyViewHandler.class); var routeHandler = injector.getInstance(JettyRouteHandler.class); - var routesHandler = injector.getInstance(JettyRoutesHandler.class); - var extensionRouteHandler = injector.getInstance(JettyExtensionRouteHandler.class); + var routesHandler = injector.getInstance(RoutesHandler.class); var authHandler = injector.getInstance(JettyAuthenticationHandler.class); var initContextHandler = injector.getInstance(InitRequestContextFilter.class); @@ -236,7 +232,6 @@ public Handler buildHttpHandler() { initContextHandler, routeHandler, routesHandler, - extensionRouteHandler, viewHandler, taxonomyHandler, contentHandler @@ -271,6 +266,10 @@ public Handler buildHttpHandler() { createExtensionHandler() ); + pathMappingsHandler.addMapping(PathSpec.from("/" + APIHandler.PATH + "/*"), + createAPIHandler() + ); + ContextHandler defaultContextHandler = new ContextHandler( pathMappingsHandler, injector.getInstance(SiteProperties.class).contextPath() @@ -301,6 +300,18 @@ public Handler buildHttpHandler() { return hostHandler; } + private Handler.Wrapper createAPIHandler() { + var authHandler = injector.getInstance(JettyAuthenticationHandler.class); + var initContextHandler = injector.getInstance(InitRequestContextFilter.class); + var apiHandler = injector.getInstance(APIHandler.class); + var handlerSequence = new Handler.Sequence( + authHandler, + initContextHandler, + apiHandler + ); + return requestContextFilter(handlerSequence, injector); + } + private Handler.Wrapper createExtensionHandler() { var authHandler = injector.getInstance(JettyAuthenticationHandler.class); var initContextHandler = injector.getInstance(InitRequestContextFilter.class); diff --git a/cms-server/src/main/java/com/condation/cms/server/configs/ModulesModule.java b/cms-server/src/main/java/com/condation/cms/server/configs/ModulesModule.java index ab88cc35..b21444f3 100644 --- a/cms-server/src/main/java/com/condation/cms/server/configs/ModulesModule.java +++ b/cms-server/src/main/java/com/condation/cms/server/configs/ModulesModule.java @@ -21,32 +21,19 @@ * . * #L% */ -import com.condation.cms.api.ServerProperties; import com.condation.cms.api.SiteProperties; -import com.condation.cms.api.configuration.Configuration; -import com.condation.cms.api.eventbus.EventBus; import com.condation.cms.api.extensions.MarkdownRendererProviderExtensionPoint; import com.condation.cms.api.extensions.TemplateEngineProviderExtensionPoint; import com.condation.cms.api.extensions.TemplateEngineProviderExtentionPoint; -import com.condation.cms.api.feature.features.ConfigurationFeature; -import com.condation.cms.api.feature.features.CronJobSchedulerFeature; -import com.condation.cms.api.feature.features.DBFeature; -import com.condation.cms.api.feature.features.EventBusFeature; -import com.condation.cms.api.feature.features.MessagingFeature; import com.condation.cms.api.feature.features.ModuleManagerFeature; -import com.condation.cms.api.feature.features.ServerPropertiesFeature; -import com.condation.cms.api.feature.features.SitePropertiesFeature; -import com.condation.cms.api.feature.features.ThemeFeature; import com.condation.cms.api.hooks.HookSystem; import com.condation.cms.api.markdown.MarkdownRenderer; -import com.condation.cms.api.messaging.Messaging; import com.condation.cms.api.module.CMSModuleContext; import com.condation.cms.api.module.CMSRequestContext; import com.condation.cms.api.request.ThreadLocalRequestContext; import com.condation.cms.api.template.TemplateEngine; import com.condation.cms.api.theme.Theme; import com.condation.cms.content.markdown.module.CMSMarkdownRenderer; -import com.condation.cms.core.scheduler.SiteCronJobScheduler; import com.condation.cms.filesystem.FileDB; import com.condation.modules.api.ModuleManager; import com.condation.modules.api.ModuleRequestContextFactory; diff --git a/cms-server/src/main/java/com/condation/cms/server/configs/SiteHandlerModule.java b/cms-server/src/main/java/com/condation/cms/server/configs/SiteHandlerModule.java index ed91e3d4..e89a9900 100644 --- a/cms-server/src/main/java/com/condation/cms/server/configs/SiteHandlerModule.java +++ b/cms-server/src/main/java/com/condation/cms/server/configs/SiteHandlerModule.java @@ -1,5 +1,13 @@ package com.condation.cms.server.configs; +import java.io.IOException; +import java.nio.file.Path; +import java.time.Duration; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.eclipse.jetty.server.handler.ResourceHandler; + /*- * #%L * cms-server @@ -32,30 +40,25 @@ import com.condation.cms.auth.services.AuthService; import com.condation.cms.auth.services.UserService; import com.condation.cms.media.SiteMediaManager; -import com.condation.cms.server.handler.auth.JettyAuthenticationHandler; +import com.condation.cms.server.FileFolderPathResource; import com.condation.cms.server.filter.InitRequestContextFilter; +import com.condation.cms.server.handler.auth.JettyAuthenticationHandler; import com.condation.cms.server.handler.content.JettyContentHandler; import com.condation.cms.server.handler.content.JettyTaxonomyHandler; import com.condation.cms.server.handler.content.JettyViewHandler; import com.condation.cms.server.handler.extensions.JettyHttpHandlerExtensionHandler; -import com.condation.cms.server.handler.extensions.JettyExtensionRouteHandler; +import com.condation.cms.server.handler.http.APIHandler; +import com.condation.cms.server.handler.http.RoutesHandler; import com.condation.cms.server.handler.media.JettyMediaHandler; import com.condation.cms.server.handler.module.JettyModuleHandler; import com.condation.cms.server.handler.module.JettyRouteHandler; -import com.condation.cms.server.handler.module.JettyRoutesHandler; -import com.condation.cms.server.FileFolderPathResource; import com.condation.modules.api.ModuleManager; import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.Singleton; import com.google.inject.name.Named; -import java.io.IOException; -import java.nio.file.Path; -import java.time.Duration; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; + import lombok.RequiredArgsConstructor; -import org.eclipse.jetty.server.handler.ResourceHandler; /** * @@ -71,10 +74,11 @@ protected void configure() { bind(JettyContentHandler.class).in(Singleton.class); bind(JettyTaxonomyHandler.class).in(Singleton.class); bind(JettyRouteHandler.class).in(Singleton.class); - bind(JettyRoutesHandler.class).in(Singleton.class); + bind(RoutesHandler.class).in(Singleton.class); bind(JettyHttpHandlerExtensionHandler.class).in(Singleton.class); - bind(JettyExtensionRouteHandler.class).in(Singleton.class); bind(InitRequestContextFilter.class).in(Singleton.class); + + bind(APIHandler.class).in(Singleton.class); //bind(JettyAuthenticationHandler.class).in(Singleton.class); } diff --git a/cms-server/src/main/java/com/condation/cms/server/handler/extensions/JettyHttpHandlerExtensionHandler.java b/cms-server/src/main/java/com/condation/cms/server/handler/extensions/JettyHttpHandlerExtensionHandler.java index f81e55a5..a8ed7ec8 100644 --- a/cms-server/src/main/java/com/condation/cms/server/handler/extensions/JettyHttpHandlerExtensionHandler.java +++ b/cms-server/src/main/java/com/condation/cms/server/handler/extensions/JettyHttpHandlerExtensionHandler.java @@ -27,7 +27,6 @@ import com.condation.cms.extensions.HttpHandlerExtension; import com.condation.cms.extensions.hooks.ServerHooks; import com.condation.cms.extensions.http.JettyHttpHandlerWrapper; -import com.condation.cms.extensions.request.RequestExtensions; import com.condation.cms.server.filter.CreateRequestContextFilter; import java.util.Optional; import lombok.RequiredArgsConstructor; @@ -77,7 +76,7 @@ private String getExtensionName(Request request) { } contextPath = contextPath + PATH + "/"; - path = path.replace(contextPath, ""); + path = path.replaceFirst(contextPath, ""); if (!path.startsWith("/")) { path = "/" + path; } diff --git a/cms-server/src/main/java/com/condation/cms/server/handler/http/APIHandler.java b/cms-server/src/main/java/com/condation/cms/server/handler/http/APIHandler.java new file mode 100644 index 00000000..f205548d --- /dev/null +++ b/cms-server/src/main/java/com/condation/cms/server/handler/http/APIHandler.java @@ -0,0 +1,146 @@ +package com.condation.cms.server.handler.http; + +import com.condation.cms.api.configuration.configs.SiteConfiguration; +import com.condation.cms.api.extensions.HttpRoutesExtensionPoint; +import com.condation.cms.api.extensions.Mapping; +import com.condation.cms.api.extensions.http.APIHandlerExtensionPoint; +import com.condation.cms.api.extensions.http.PathMapping; +import com.condation.cms.api.feature.features.ConfigurationFeature; + +/*- + * #%L + * cms-server + * %% + * Copyright (C) 2023 - 2024 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.condation.cms.api.request.RequestContext; +import com.condation.cms.extensions.HttpHandlerExtension; +import com.condation.cms.extensions.hooks.ServerHooks; +import com.condation.cms.extensions.http.JettyHttpHandlerWrapper; +import com.condation.cms.server.filter.CreateRequestContextFilter; +import com.condation.modules.api.ModuleManager; +import com.google.inject.Inject; + +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.Callback; + +/** + * + * @author t.marx + */ +@RequiredArgsConstructor(onConstructor = @__({ + @Inject })) +@Slf4j +public class APIHandler extends Handler.Abstract { + + public static final String PATH = "api"; + + private final ModuleManager moduleManager; + + private boolean isApiActivated (Request request) { + var requestContext = (RequestContext) request.getAttribute(CreateRequestContextFilter.REQUEST_CONTEXT); + var siteProperties = requestContext.get(ConfigurationFeature.class).configuration().get(SiteConfiguration.class).siteProperties(); + + return siteProperties.getOrDefault("api.enabled", Boolean.FALSE); + } + + @Override + public boolean handle(Request request, Response response, Callback callback) throws Exception { + + if (!isApiActivated(request)) { + return false; + } + + try { + if (handleExtensionRoute(request, response, callback)) { + return true; + } + + if (handleModuleRoute(request, response, callback)) { + return true; + } + + Response.writeError(request, response, callback, 404); + return true; + } catch (Exception e) { + log.error(null, e); + callback.failed(e); + return true; + } + } + + private boolean handleModuleRoute(Request request, Response response, Callback callback) throws Exception { + + String apiRoute = getApiRoute(request); + var method = request.getMethod(); + + Optional firstMatch = moduleManager.extensions(APIHandlerExtensionPoint.class) + .stream() + .filter(extension -> extension.getMapping().getMatchingHandler(apiRoute, method).isPresent()) + .map(extension -> extension.getMapping()) + .findFirst(); + + if (firstMatch.isPresent()) { + var mapping = firstMatch.get(); + var handler = mapping.getMatchingHandler(apiRoute, method).get(); + return handler.handle(request, response, callback); + } + + return false; + } + + private boolean handleExtensionRoute(Request request, Response response, Callback callback) throws Exception { + var requestContext = (RequestContext) request.getAttribute(CreateRequestContextFilter.REQUEST_CONTEXT); + + String extension = getApiRoute(request); + var method = request.getMethod(); + + var httpExtensions = requestContext.get(ServerHooks.class).getAPIRoutes(); + Optional findHttpHandler = httpExtensions.findHttpHandler(method, extension); + + if (findHttpHandler.isPresent()) { + return new JettyHttpHandlerWrapper(findHttpHandler.get().handler()).handle(request, response, callback); + } + + return false; + } + + private String getApiRoute(Request request) { + var path = request.getHttpURI().getPath(); + var contextPath = request.getContext().getContextPath(); + + if (!contextPath.endsWith("/")) { + contextPath += "/"; + } + + contextPath = contextPath + PATH + "/"; + + path = path.replaceFirst(contextPath, ""); + if (!path.startsWith("/")) { + path = "/" + path; + } + return path; + } + +} \ No newline at end of file diff --git a/cms-server/src/main/java/com/condation/cms/server/handler/extensions/JettyExtensionRouteHandler.java b/cms-server/src/main/java/com/condation/cms/server/handler/http/RoutesHandler.java similarity index 55% rename from cms-server/src/main/java/com/condation/cms/server/handler/extensions/JettyExtensionRouteHandler.java rename to cms-server/src/main/java/com/condation/cms/server/handler/http/RoutesHandler.java index 8db965cd..2171ded6 100644 --- a/cms-server/src/main/java/com/condation/cms/server/handler/extensions/JettyExtensionRouteHandler.java +++ b/cms-server/src/main/java/com/condation/cms/server/handler/http/RoutesHandler.java @@ -1,4 +1,4 @@ -package com.condation.cms.server.handler.extensions; +package com.condation.cms.server.handler.http; /*- * #%L @@ -22,13 +22,16 @@ * #L% */ - +import com.condation.cms.api.extensions.HttpRoutesExtensionPoint; +import com.condation.cms.api.extensions.Mapping; import com.condation.cms.api.request.RequestContext; +import com.condation.cms.api.utils.RequestUtil; import com.condation.cms.extensions.HttpHandlerExtension; import com.condation.cms.extensions.hooks.ServerHooks; import com.condation.cms.extensions.http.JettyHttpHandlerWrapper; -import com.condation.cms.extensions.request.RequestExtensions; import com.condation.cms.server.filter.CreateRequestContextFilter; +import com.condation.modules.api.ModuleManager; +import com.google.inject.Inject; import java.util.Optional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -41,39 +44,63 @@ * * @author t.marx */ -@RequiredArgsConstructor +@RequiredArgsConstructor(onConstructor = @__({ + @Inject })) @Slf4j -public class JettyExtensionRouteHandler extends Handler.Abstract { +public class RoutesHandler extends Handler.Abstract { + + private final ModuleManager moduleManager; @Override public boolean handle(Request request, Response response, Callback callback) throws Exception { - var requestContext = (RequestContext) request.getAttribute(CreateRequestContextFilter.REQUEST_CONTEXT); - String extension = getExtensionName(request); - var method = request.getMethod(); - - var httpExtensions = requestContext.get(ServerHooks.class).getHttpRoutes(); - Optional findHttpHandler = httpExtensions.findHttpHandler(method, extension); - - if (findHttpHandler.isPresent()) { - return new JettyHttpHandlerWrapper(findHttpHandler.get().handler()).handle(request, response, callback); + try { + if (tryExtensionRoutes(request, response, callback)) { + return true; + } + + if (tryModuleRoutes(request, response, callback)) { + return true; + } + + return false; + } catch (Exception e) { + log.error(null, e); + callback.failed(e); + return true; } - return false; } - private String getExtensionName(Request request) { - var path = request.getHttpURI().getPath(); - var contextPath = request.getContext().getContextPath(); + private boolean tryModuleRoutes(Request request, Response response, Callback callback) throws Exception { + String route = "/" + RequestUtil.getContentPath(request); - if (!contextPath.endsWith("/")) { - contextPath += "/"; - } + Optional firstMatch = moduleManager.extensions(HttpRoutesExtensionPoint.class) + .stream() + .filter(extension -> extension.getMapping().getMatchingHandler(route).isPresent()) + .map(extension -> extension.getMapping()) + .findFirst(); - path = path.replace(contextPath, ""); - if (!path.startsWith("/")) { - path = "/" + path; + if (firstMatch.isPresent()) { + var mapping = firstMatch.get(); + var handler = mapping.getMatchingHandler(route).get(); + return handler.handle(request, response, callback); } - return path; + + return false; } + private boolean tryExtensionRoutes(Request request, Response response, Callback callback) throws Exception { + var requestContext = (RequestContext) request.getAttribute(CreateRequestContextFilter.REQUEST_CONTEXT); + + String route = "/" + RequestUtil.getContentPath(request); + var method = request.getMethod(); + + var httpExtensions = requestContext.get(ServerHooks.class).getHttpRoutes(); + Optional findHttpHandler = httpExtensions.findHttpHandler(method, route); + + if (findHttpHandler.isPresent()) { + return new JettyHttpHandlerWrapper(findHttpHandler.get().handler()).handle(request, response, callback); + } + return false; + } } diff --git a/cms-server/src/main/java/com/condation/cms/server/handler/module/JettyRouteHandler.java b/cms-server/src/main/java/com/condation/cms/server/handler/module/JettyRouteHandler.java index a5c44a20..97a6cb28 100644 --- a/cms-server/src/main/java/com/condation/cms/server/handler/module/JettyRouteHandler.java +++ b/cms-server/src/main/java/com/condation/cms/server/handler/module/JettyRouteHandler.java @@ -42,6 +42,7 @@ @RequiredArgsConstructor(onConstructor = @__({ @Inject})) @Slf4j +@Deprecated(since = "7.3.0", forRemoval = true) public class JettyRouteHandler extends Handler.Abstract { private final ModuleManager moduleManager; diff --git a/cms-server/src/main/java/com/condation/cms/server/handler/module/JettyRoutesHandler.java b/cms-server/src/main/java/com/condation/cms/server/handler/module/JettyRoutesHandler.java deleted file mode 100644 index 93ae373e..00000000 --- a/cms-server/src/main/java/com/condation/cms/server/handler/module/JettyRoutesHandler.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.condation.cms.server.handler.module; - -/*- - * #%L - * cms-server - * %% - * Copyright (C) 2023 - 2024 CondationCMS - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU 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 General Public - * License along with this program. If not, see - * . - * #L% - */ - - -import com.condation.cms.api.extensions.HttpRoutesExtensionPoint; -import com.condation.cms.api.extensions.Mapping; -import com.condation.cms.api.utils.RequestUtil; -import com.condation.modules.api.ModuleManager; -import com.google.inject.Inject; -import java.util.Optional; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.eclipse.jetty.server.Handler; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.Response; -import org.eclipse.jetty.util.Callback; - -/** - * - * @author t.marx - */ -@RequiredArgsConstructor(onConstructor = @__({ - @Inject})) -@Slf4j -public class JettyRoutesHandler extends Handler.Abstract { - - private final ModuleManager moduleManager; - - @Override - public boolean handle(Request request, Response response, Callback callback) throws Exception { - - try { - String route = "/" + RequestUtil.getContentPath(request); - - Optional firstMatch = moduleManager.extensions(HttpRoutesExtensionPoint.class) - .stream() - .filter(extension -> extension.getMapping().getMatchingHandler(route).isPresent()) - .map(extension -> extension.getMapping()) - .findFirst(); - - if (firstMatch.isPresent()) { - var mapping = firstMatch.get(); - var handler = mapping.getMatchingHandler(route).get(); - return handler.handle(request, response, callback); - } - - - return false; - } catch (Exception e) { - log.error(null, e); - callback.failed(e); - return true; - } - } -} diff --git a/modules/example-module/src/main/java/com/condation/cms/modules/example/ExampleApiHandlerExtension.java b/modules/example-module/src/main/java/com/condation/cms/modules/example/ExampleApiHandlerExtension.java new file mode 100644 index 00000000..e4c66f6d --- /dev/null +++ b/modules/example-module/src/main/java/com/condation/cms/modules/example/ExampleApiHandlerExtension.java @@ -0,0 +1,64 @@ +package com.condation.cms.modules.example; + +/*- + * #%L + * example-module + * %% + * Copyright (C) 2023 - 2024 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public + * License along with this program. If not, see + * . + * #L% + */ + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +import org.eclipse.jetty.http.pathmap.PathSpec; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.Callback; + +import com.condation.cms.api.extensions.http.APIHandlerExtensionPoint; +import com.condation.cms.api.extensions.http.HttpHandler; +import com.condation.cms.api.extensions.http.PathMapping; +import com.condation.modules.api.annotation.Extension; + +import lombok.RequiredArgsConstructor; + +@Extension(APIHandlerExtensionPoint.class) +public class ExampleApiHandlerExtension extends APIHandlerExtensionPoint { + + @Override + public PathMapping getMapping() { + PathMapping mapping = new PathMapping(); + + mapping.add(PathSpec.from("/test-api"), "GET", new ExampleHandler("CondationCMS test api")); + + return mapping; + } + + @RequiredArgsConstructor + private static class ExampleHandler implements HttpHandler { + + private final String message; + + @Override + public boolean handle(Request request, Response response, Callback callback) throws Exception { + response.write(true, ByteBuffer.wrap(message.getBytes(StandardCharsets.UTF_8)), callback); + return true; + } + + } +} diff --git a/modules/pom.xml b/modules/pom.xml index b76b5063..e95241be 100644 --- a/modules/pom.xml +++ b/modules/pom.xml @@ -12,5 +12,6 @@ example-module + system-modules diff --git a/modules/system-modules/pom.xml b/modules/system-modules/pom.xml new file mode 100644 index 00000000..e2565d1b --- /dev/null +++ b/modules/system-modules/pom.xml @@ -0,0 +1,22 @@ + + + 4.0.0 + + com.condation.cms.modules + cms-modules + 7.2.0 + + cms-system-modules + jar + + + + com.condation.cms + cms-api + + + com.condation.cms + cms-filesystem + + + \ No newline at end of file diff --git a/cms-auth/src/main/java/com/condation/cms/auth/AuthShortCodes.java b/modules/system-modules/src/main/java/com/condation/cms/modules/system/AuthShortCodes.java similarity index 97% rename from cms-auth/src/main/java/com/condation/cms/auth/AuthShortCodes.java rename to modules/system-modules/src/main/java/com/condation/cms/modules/system/AuthShortCodes.java index 7f14f8e1..b15a624b 100644 --- a/cms-auth/src/main/java/com/condation/cms/auth/AuthShortCodes.java +++ b/modules/system-modules/src/main/java/com/condation/cms/modules/system/AuthShortCodes.java @@ -1,4 +1,4 @@ -package com.condation.cms.auth; +package com.condation.cms.modules.system; /*- * #%L diff --git a/modules/system-modules/src/main/java/com/condation/cms/modules/system/handlers/ApiEndpoints.java b/modules/system-modules/src/main/java/com/condation/cms/modules/system/handlers/ApiEndpoints.java new file mode 100644 index 00000000..816f2cf4 --- /dev/null +++ b/modules/system-modules/src/main/java/com/condation/cms/modules/system/handlers/ApiEndpoints.java @@ -0,0 +1,56 @@ +package com.condation.cms.modules.system.handlers; + +/*- + * #%L + * cms-system-modules + * %% + * Copyright (C) 2023 - 2024 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.condation.cms.modules.system.handlers.v1.ContentHandler; +import com.condation.cms.api.extensions.http.APIHandlerExtensionPoint; +import com.condation.cms.api.extensions.http.PathMapping; +import com.condation.cms.api.feature.features.DBFeature; +import com.condation.cms.modules.system.handlers.v1.NavigationHandler; +import org.eclipse.jetty.http.pathmap.PathSpec; + +/** + * + * @author thmar + */ +//@Extension(APIHandlerExtensionPoint.class) +public class ApiEndpoints extends APIHandlerExtensionPoint { + + @Override + public PathMapping getMapping() { + var mapping = new PathMapping(); + + mapping.add(PathSpec.from("/v1/content/*"), + "GET", + new ContentHandler(getContext().get(DBFeature.class).db()) + ); + + mapping.add(PathSpec.from("/v1/navigation/*"), + "GET", + new NavigationHandler(getContext().get(DBFeature.class).db()) + ); + + return mapping; + } + +} diff --git a/modules/system-modules/src/main/java/com/condation/cms/modules/system/handlers/v1/ContentHandler.java b/modules/system-modules/src/main/java/com/condation/cms/modules/system/handlers/v1/ContentHandler.java new file mode 100644 index 00000000..c82d98bc --- /dev/null +++ b/modules/system-modules/src/main/java/com/condation/cms/modules/system/handlers/v1/ContentHandler.java @@ -0,0 +1,112 @@ +package com.condation.cms.modules.system.handlers.v1; + +/*- + * #%L + * cms-system-modules + * %% + * Copyright (C) 2023 - 2024 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.condation.cms.api.db.ContentNode; +import com.condation.cms.api.db.DB; +import com.condation.cms.api.db.cms.ReadOnlyFile; +import com.condation.cms.api.extensions.http.HttpHandler; +import com.condation.cms.api.utils.PathUtil; +import com.condation.cms.api.utils.RequestUtil; +import com.condation.cms.modules.system.helpers.NodeHelper; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.io.Content; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.Callback; + +/** + * + * @author thmar + */ +public class ContentHandler implements HttpHandler { + + private final DB db; + public static final Gson GSON = new GsonBuilder() + .enableComplexMapKeySerialization() + .create(); + + public ContentHandler(DB db) { + this.db = db; + } + + @Override + public boolean handle(Request request, Response response, Callback callback) throws Exception { + var uri = RequestUtil.getContentPath(request); + uri = uri.replaceFirst("api/v1/content", ""); + if (uri.startsWith("/")) { + uri = uri.substring(1); + } + + var resolved = resolveContentNode(uri); + if (resolved.isEmpty()) { + Response.writeError(request, response, callback, 404); + return true; + } + + final ContentNode node = resolved.get(); + final Map data = new HashMap<>(node.data()); + data.put("_links", NodeHelper.getLinks(node, request)); + + response.getHeaders().add(HttpHeader.CONTENT_TYPE, "application/json; charset=utf-8"); + Content.Sink.write(response, true, GSON.toJson(data), callback); + + return true; + } + + private Optional resolveContentNode(String uri) { + var contentBase = db.getReadOnlyFileSystem().contentBase(); + var contentPath = contentBase.resolve(uri); + ReadOnlyFile contentFile = null; + if (contentPath.exists() && contentPath.isDirectory()) { + // use index.md + var tempFile = contentPath.resolve("index.md"); + if (tempFile.exists()) { + contentFile = tempFile; + } else { + return Optional.empty(); + } + } else { + var temp = contentBase.resolve(uri + ".md"); + if (temp.exists()) { + contentFile = temp; + } else { + return Optional.empty(); + } + } + + var filePath = PathUtil.toRelativeFile(contentFile, contentBase); + if (!db.getContent().isVisible(filePath)) { + return Optional.empty(); + } + + final ContentNode contentNode = db.getContent().byUri(filePath).get(); + + return Optional.ofNullable(contentNode); + } +} diff --git a/modules/system-modules/src/main/java/com/condation/cms/modules/system/handlers/v1/NavigationHandler.java b/modules/system-modules/src/main/java/com/condation/cms/modules/system/handlers/v1/NavigationHandler.java new file mode 100644 index 00000000..5b241ac9 --- /dev/null +++ b/modules/system-modules/src/main/java/com/condation/cms/modules/system/handlers/v1/NavigationHandler.java @@ -0,0 +1,134 @@ +package com.condation.cms.modules.system.handlers.v1; + +/*- + * #%L + * cms-system-modules + * %% + * Copyright (C) 2023 - 2024 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.condation.cms.api.db.ContentNode; +import com.condation.cms.api.db.DB; +import com.condation.cms.api.db.cms.ReadOnlyFile; +import com.condation.cms.api.extensions.http.HttpHandler; +import com.condation.cms.api.utils.PathUtil; +import com.condation.cms.api.utils.RequestUtil; +import com.condation.cms.filesystem.metadata.AbstractMetaData; +import com.condation.cms.modules.system.helpers.NodeHelper; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.io.Content; +import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.server.Response; +import org.eclipse.jetty.util.Callback; + +/** + * + * @author thmar + */ +public class NavigationHandler implements HttpHandler { + + private final DB db; + public static final Gson GSON = new GsonBuilder() + .enableComplexMapKeySerialization() + .create(); + + public NavigationHandler(DB db) { + this.db = db; + } + + @Override + public boolean handle(Request request, Response response, Callback callback) throws Exception { + var uri = RequestUtil.getContentPath(request); + uri = uri.replaceFirst("api/v1/navigation", ""); + if (uri.startsWith("/")) { + uri = uri.substring(1); + } + final ReadOnlyFile contentBase = db.getReadOnlyFileSystem().contentBase(); + + var file = contentBase.resolve(uri); + + if (!file.exists()) { + Response.writeError(request, response, callback, 404); + return true; + } + + var filePath = PathUtil.toRelativeFile(file, contentBase); + + /*if (!db.getContent().isVisible(filePath)) { + Response.writeError(request, response, callback, 403); + return true; + }*/ + + final Optional contentNode = db.getContent().byUri(filePath); + + List children = new ArrayList<>(); + db.getContent().listDirectories(file, "").stream() + .filter(child -> AbstractMetaData.isVisible(child)) + .map(child -> new NavNode( + NodeHelper.getPath(child), + NodeHelper.getLinks(child, request)) + ).forEach(children::add); + + final String nodeUri = NodeHelper.getPath(uri); + db.getContent().listContent(file, "").stream() + .filter(child -> AbstractMetaData.isVisible(child)) + .filter(child -> + !NodeHelper.getPath(child).equals(nodeUri) + ) + .map(child -> new NavNode( + NodeHelper.getPath(child), + NodeHelper.getLinks(child, request)) + ).forEach(children::add); + + children.sort((node1, node2) -> node1.path.compareTo(node2.path)); + + NavNode node; + if (contentNode.isPresent()) { + node = new NavNode( + NodeHelper.getPath(contentNode.get()), + NodeHelper.getLinks(contentNode.get(), request), + children + ); + } else { + node = new NavNode( + "/" + uri, + Collections.emptyMap(), + children + ); + } + + + response.getHeaders().add(HttpHeader.CONTENT_TYPE, "application/json; charset=utf-8"); + Content.Sink.write(response, true, GSON.toJson(node), callback); + + return true; + } + + private static record NavNode (String path, Map _links, List children) { + public NavNode (String path, Map _links) { + this(path, _links, Collections.emptyList()); + } + }; +} diff --git a/modules/system-modules/src/main/java/com/condation/cms/modules/system/helpers/NodeHelper.java b/modules/system-modules/src/main/java/com/condation/cms/modules/system/helpers/NodeHelper.java new file mode 100644 index 00000000..db464b6a --- /dev/null +++ b/modules/system-modules/src/main/java/com/condation/cms/modules/system/helpers/NodeHelper.java @@ -0,0 +1,84 @@ +package com.condation.cms.modules.system.helpers; + +/*- + * #%L + * cms-system-modules + * %% + * Copyright (C) 2023 - 2024 CondationCMS + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public + * License along with this program. If not, see + * . + * #L% + */ + +import com.condation.cms.api.configuration.configs.SiteConfiguration; +import com.condation.cms.api.db.ContentNode; +import com.condation.cms.api.feature.features.ConfigurationFeature; +import com.condation.cms.api.request.RequestContext; +import java.util.Map; +import org.eclipse.jetty.server.Request; + +/** + * + * @author t.marx + */ +public final class NodeHelper { + + private NodeHelper() {} + + public static Map getLinks (ContentNode node, Request request) { + return getLinks(node.uri(), request); + } + + public static Map getLinks (String nodeUri, Request request) { + + var requestContext = (RequestContext) request.getAttribute("_requestContext"); + var siteProperties = requestContext.get(ConfigurationFeature.class).configuration().get(SiteConfiguration.class).siteProperties(); + + var contextPath = siteProperties.contextPath(); + if (!contextPath.endsWith("/")) { + contextPath += "/"; + } + + if (nodeUri.endsWith("index.md")) { + nodeUri = nodeUri.replaceFirst("index.md", ""); + } + + if (nodeUri.endsWith(".md")) { + nodeUri = nodeUri.substring(0, nodeUri.length() - 3); + } + + return Map.of( + "_self", "%s%s".formatted(contextPath, nodeUri), + "_content", "%sapi/v1/content/%s".formatted(contextPath, nodeUri) + ); + } + + public static String getPath (ContentNode node) { + return getPath(node.uri()); + } + + public static String getPath (String uri) { + if (uri.endsWith("index.md")) { + uri = uri.replaceFirst("index.md", ""); + } + if (!uri.startsWith("/")) { + uri = "/" + uri; + } + if (uri.endsWith(".md")) { + uri = uri.substring(0, uri.length() - 3); + } + return uri; + } +} diff --git a/pom.xml b/pom.xml index ebec4cb7..9944cfe1 100644 --- a/pom.xml +++ b/pom.xml @@ -116,6 +116,11 @@ modules-api ${project.version} + + com.condation.cms.modules + cms-system-modules + ${project.version} + org.apache.commons diff --git a/test-server/hosts/features/extensions/http.extension.js b/test-server/hosts/features/extensions/http.extension.js index 24edf6f8..bb47765e 100644 --- a/test-server/hosts/features/extensions/http.extension.js +++ b/test-server/hosts/features/extensions/http.extension.js @@ -24,3 +24,20 @@ $hooks.registerAction("system/server/http/route", (context) => { ) return null; }) + +$hooks.registerAction("system/server/api/route", (context) => { + context.arguments().get("apiRoutes").add( + "GET", + "/hello", + (request, response) => { + + let data = { + "name": "CondationCMS" + } + + response.addHeader("Content-Type", "application/json; charset=utf-8") + response.write(JSON.stringify(data), UTF_8) + } + ) + return null; +}) diff --git a/test-server/hosts/features/site.toml b/test-server/hosts/features/site.toml index 61882d1d..12455976 100644 --- a/test-server/hosts/features/site.toml +++ b/test-server/hosts/features/site.toml @@ -13,3 +13,6 @@ mode = "PERSISTENT" [modules] active = [ "forms-module" ] + +[api] +enabled = true \ No newline at end of file diff --git a/test-server/modules/example-module/libs/example-module-7.0.0.jar b/test-server/modules/example-module/libs/example-module-7.0.0.jar deleted file mode 100644 index 1e393994..00000000 Binary files a/test-server/modules/example-module/libs/example-module-7.0.0.jar and /dev/null differ diff --git a/test-server/modules/example-module/libs/example-module-7.2.0.jar b/test-server/modules/example-module/libs/example-module-7.2.0.jar new file mode 100644 index 00000000..46944789 Binary files /dev/null and b/test-server/modules/example-module/libs/example-module-7.2.0.jar differ diff --git a/test-server/modules/example-module/module.properties b/test-server/modules/example-module/module.properties index 2f1300c1..3ac324e0 100644 --- a/test-server/modules/example-module/module.properties +++ b/test-server/modules/example-module/module.properties @@ -1,4 +1,4 @@ id=example-module name=example module -version=7.0.0 +version=7.2.0 priority=HIGH \ No newline at end of file