Skip to content
Open
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
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
[![Quality gate status](https://sonarcloud.io/api/project_badges/measure?project=CondationCMS_cms-server&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=CondationCMS_cms-server)

![Maven Central Version](https://img.shields.io/maven-central/v/com.condation.cms/cms-api)

![NPM Version](https://img.shields.io/npm/v/condation-cms-ui)


# CondationCMS


CondationCMS is a fast, flexible, and developer-friendly content management system built with Java.

Content is stored in Markdown files with YAML front matter instead of a database. The file and directory structure remains transparent, easy to version with Git, and directly represents the structure of the website.
Expand Down
1 change: 1 addition & 0 deletions cms-api/src/main/java/com/condation/cms/api/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public static class MetaFields {

public static class Folders {
public static final String CONTENT = "content/";
public static final String COLLECTIONS = "collections/";
public static final String TEMPLATES = "templates/";
public static final String ASSETS = "assets/";
public static final String EXTENSIONS = "extensions/";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.condation.cms.api.configuration.configs;

/*-
* #%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.configuration.Config;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
* Reloadable, site-scoped collection definitions.
*/
public class CollectionConfiguration implements Config {

private volatile Map<String, CollectionDefinition> collections;

public CollectionConfiguration(Map<String, CollectionDefinition> collections) {
replaceCollections(collections);
}

/** Atomically replaces the complete set of collection definitions. */
public void replaceCollections(Map<String, CollectionDefinition> collections) {
this.collections = Map.copyOf(Objects.requireNonNull(collections));
}

public Optional<CollectionDefinition> collection(String name) {
return Optional.ofNullable(collections.get(name));
}

public Map<String, CollectionDefinition> collections() {
return collections;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.condation.cms.api.configuration.configs;

/*-
* #%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 java.util.Objects;
import java.util.Optional;
import java.util.regex.Pattern;

/**
* Configuration of one named collection.
*/
public record CollectionDefinition(String name, String site, CollectionDetailConfiguration detail) {

private static final Pattern VALID_NAME_PATTERN = Pattern.compile("[a-zA-Z0-9][a-zA-Z0-9_-]*");

public CollectionDefinition {
Objects.requireNonNull(name, "collection name must not be null");
if (!VALID_NAME_PATTERN.matcher(name).matches()) {
throw new IllegalArgumentException("invalid collection name: " + name);
}
if (site != null) {
site = site.trim();
if (site.isEmpty()) {
throw new IllegalArgumentException("collection site must not be blank");
}
}
}

public CollectionDefinition(String name, CollectionDetailConfiguration detail) {
this(name, null, detail);
}

public Optional<CollectionDetailConfiguration> detailPage() {
return Optional.ofNullable(detail);
}

/** Site whose collection data should be used, or empty for a local collection. */
public Optional<String> sourceSite() {
return Optional.ofNullable(site);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.condation.cms.api.configuration.configs;

/*-
* #%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 java.util.Objects;
import java.util.regex.Pattern;

/**
* Route and template used for collection detail pages.
*/
public record CollectionDetailConfiguration(String route, String template) {

private static final Pattern PARAMETER = Pattern.compile("\\{([a-zA-Z][a-zA-Z0-9_.-]*)}");

public CollectionDetailConfiguration {
Objects.requireNonNull(route, "collection detail route must not be null");
Objects.requireNonNull(template, "collection detail template must not be null");

route = normalizeRoute(route);
template = template.trim();
if (template.isEmpty()) {
throw new IllegalArgumentException("collection detail template must not be blank");
}

var matcher = PARAMETER.matcher(route);
if (!matcher.find() || matcher.find()) {
throw new IllegalArgumentException("collection detail route must contain exactly one parameter");
}
}

public String parameter() {
var matcher = PARAMETER.matcher(route);
if (!matcher.find()) {
throw new IllegalStateException("collection detail route has no parameter");
}
return matcher.group(1);
}

private static String normalizeRoute(String value) {
var normalized = value.trim();
if (!normalized.startsWith("/")) {
normalized = "/" + normalized;
}
while (normalized.length() > 1 && normalized.endsWith("/")) {
normalized = normalized.substring(0, normalized.length() - 1);
}
return normalized;
}
}
12 changes: 12 additions & 0 deletions cms-api/src/main/java/com/condation/cms/api/db/ContentQuery.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
*/


import com.condation.cms.api.Constants;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -90,6 +91,17 @@ ContentQuery<T> within(

ContentQuery<T> expression(final String expressions);

/**
* Restricts the query to items whose title matches the input.
*
* Implementations with a full-text title index should override this method.
* The default keeps existing query implementations source compatible and uses
* their regular title-field matching.
*/
default ContentQuery<T> searchByTitle(final String input) {
return where(Constants.MetaFields.TITLE, input);
}

public static interface Sort<T> {
public ContentQuery<T> asc();

Expand Down
39 changes: 39 additions & 0 deletions cms-api/src/main/java/com/condation/cms/api/db/CursorPage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.condation.cms.api.db;

/*-
* #%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 java.util.List;

/**
* One forward-only cursor page. The cursor is opaque and expires when the
* underlying index changes.
*/
public record CursorPage<T>(List<T> items, String nextCursor) {

public CursorPage {
items = List.copyOf(items);
}

public boolean hasNext() {
return nextCursor != null && !nextCursor.isBlank();
}
}
3 changes: 3 additions & 0 deletions cms-api/src/main/java/com/condation/cms/api/db/DB.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
*/

import com.condation.cms.api.db.taxonomy.Taxonomies;
import com.condation.cms.api.db.collection.Collections;
import com.condation.cms.api.db.cms.ReadOnlyFileSystem;


Expand All @@ -40,6 +41,8 @@ public interface DB extends AutoCloseable{
public ReadOnlyFileSystem getReadOnlyFileSystem();

public Content getContent();

public Collections getCollections();

public Taxonomies getTaxonomies();
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public interface DBFileSystem {

ReadOnlyFile contentBase();

ReadOnlyFile collectionsBase();

ReadOnlyFile assetBase();

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.condation.cms.api.db.collection;

/*-
* #%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.db.ContentQuery;
import java.util.Optional;

/**
* A named, file-backed collection.
*/
public interface Collection {

String name();

Optional<CollectionItem> item(String id);

ContentQuery<CollectionItem> query();

/** Queries collection metadata without opening the Markdown body files. */
ContentQuery<CollectionItemMetadata> metadataQuery();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.condation.cms.api.db.collection;

/*-
* #%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.db.ContentQuery;
import com.condation.cms.api.db.CursorPage;
import java.util.function.Consumer;

/**
* Internal capability for infrastructure that needs cursor-based collection
* traversal. It is deliberately separate from {@link Collection} and
* {@link ContentQuery}, which are exposed to templates.
*/
public interface CollectionCursorSupport {

CursorPage<CollectionItemMetadata> metadataCursorPage(
String collection,
String cursor,
long size,
Consumer<ContentQuery<CollectionItemMetadata>> queryConfigurer);
}
Loading
Loading