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
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
categories inline, as before, and also references that document.
- Comment moderation: list, change status, delete.

- **New AtomPub Guide** (`docs/roller-atompub-guide.adoc`). It documents
every AtomPub URI, permission, element and status code, with examples
that the test suite checks against the code.

### Behaviour changes worth reading before upgrading

- **AtomPub no longer uses ROME Propono.** Roller now has its own AtomPub
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,14 @@

import static org.junit.jupiter.api.Assertions.assertTrue;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import org.junit.jupiter.api.Test;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXParseException;

import com.thaiopensource.util.PropertyMapBuilder;
import com.thaiopensource.validate.ValidateProperty;
import com.thaiopensource.validate.ValidationDriver;
import com.thaiopensource.validate.rng.CompactSchemaReader;

/**
* Validates the XML produced by {@link AtomWriter} against the official RELAX NG
Expand All @@ -49,25 +39,8 @@ public class AtomSchemaValidationTest {
private static final Date PUBLISHED = Date.from(Instant.parse("2026-06-03T12:34:56Z"));
private static final Date UPDATED = Date.from(Instant.parse("2026-06-04T01:02:03Z"));

/** Validate xml against a classpath RELAX NG Compact schema; return errors (empty == valid). */
private List<String> validate(String schemaResource, byte[] xml) throws Exception {
List<String> errors = new ArrayList<>();
ErrorHandler handler = new ErrorHandler() {
@Override public void warning(SAXParseException e) { /* ignore warnings */ }
@Override public void error(SAXParseException e) { errors.add(e.getMessage()); }
@Override public void fatalError(SAXParseException e) { errors.add(e.getMessage()); }
};
PropertyMapBuilder props = new PropertyMapBuilder();
props.put(ValidateProperty.ERROR_HANDLER, handler);
ValidationDriver driver =
new ValidationDriver(props.toPropertyMap(), CompactSchemaReader.getInstance());

try (InputStream schema = getClass().getResourceAsStream(schemaResource)) {
assertTrue(driver.loadSchema(new InputSource(schema)),
"schema " + schemaResource + " failed to compile: " + errors);
}
driver.validate(new InputSource(new ByteArrayInputStream(xml)));
return errors;
return RelaxNgValidator.validateResource(schemaResource, xml);
}

private byte[] writeEntry(AtomEntry entry) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. The ASF licenses this file to You
* under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. For additional information regarding
* copyright in this work, please see the NOTICE file in the top level
* directory of this distribution.
*/
package org.apache.roller.weblogger.webservices.atomprotocol;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import com.thaiopensource.util.PropertyMapBuilder;
import com.thaiopensource.validate.ValidateProperty;
import com.thaiopensource.validate.ValidationDriver;
import com.thaiopensource.validate.rng.CompactSchemaReader;

/** Validates XML against a RELAX NG Compact schema with Jing. */
final class RelaxNgValidator {

private RelaxNgValidator() {
}

/**
* Validates {@code xml} against the schema in {@code schema}; returns the
* errors, empty when the document is valid.
*/
static List<String> validate(InputStream schema, String schemaName, byte[] xml)
throws IOException, SAXException {
List<String> errors = new ArrayList<>();
ErrorHandler handler = new ErrorHandler() {
@Override public void warning(SAXParseException e) { /* ignore warnings */ }
@Override public void error(SAXParseException e) { errors.add(e.getMessage()); }
@Override public void fatalError(SAXParseException e) { errors.add(e.getMessage()); }
};
PropertyMapBuilder props = new PropertyMapBuilder();
props.put(ValidateProperty.ERROR_HANDLER, handler);
ValidationDriver driver =
new ValidationDriver(props.toPropertyMap(), CompactSchemaReader.getInstance());
if (!driver.loadSchema(new InputSource(schema))) {
throw new IllegalStateException("schema " + schemaName + " failed to compile: " + errors);
}
driver.validate(new InputSource(new ByteArrayInputStream(xml)));
return errors;
}

/** Validates against a classpath schema. */
static List<String> validateResource(String schemaResource, byte[] xml)
throws IOException, SAXException {
try (InputStream schema = RelaxNgValidator.class.getResourceAsStream(schemaResource)) {
return validate(schema, schemaResource, xml);
}
}
}
4 changes: 4 additions & 0 deletions assembly-release/src/main/assembly/binary.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@
<source>target/generated-docs/roller-template-guide.pdf</source>
<outputDirectory>${artifact.artifactId}-${artifact.version}/docs</outputDirectory>
</file>
<file>
<source>target/generated-docs/roller-atompub-guide.pdf</source>
<outputDirectory>${artifact.artifactId}-${artifact.version}/docs</outputDirectory>
</file>
<file>
<source>target/generated-docs/roller-user-guide.pdf</source>
<outputDirectory>${artifact.artifactId}-${artifact.version}/docs</outputDirectory>
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ In this directory you'll find Roller docs in AsciiDoc format, various examples a
* `roller-install-guide.adoc` - How to install Roller
* `roller-user-guide.adoc` - How to use Roller
* `roller-template-guide.adoc` - How to create Roller templates and themes
* `roller-atompub-guide.adoc` - Reference for Roller's AtomPub interface; its examples and extension schema are in `atompub/`
* `security-model.md` - Who Roller trusts, what each user may do, and what counts as a vulnerability
* `examples/` - example configuration and script files
* `testing/` - Various test scripts
Expand Down
4 changes: 4 additions & 0 deletions docs/atompub/examples/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Examples included by ../../roller-atompub-guide.adoc. Files ending in
-request.xml are written by hand; the others are generated by
AtomPubDocExamplesTest (mvn -pl app test -Dtest=AtomPubDocExamplesTest
-Datompub.docs.update=true). The test fails when they no longer match.
6 changes: 6 additions & 0 deletions docs/atompub/examples/categories.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<categories xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom" fixed="yes" scheme="https://example.com/roller/myblog/">
<atom:category term="General" label="General"/>
<atom:category term="Java" label="Java"/>
<atom:category term="Travel" label="Travel"/>
</categories>
14 changes: 14 additions & 0 deletions docs/atompub/examples/category-entry.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app" xmlns:roller="http://roller.apache.org/ns/atompub/1.0" xmlns:thr="http://purl.org/syndication/thread/1.0">
<id>https://example.com/roller/roller-services/app/myblog/category/7c3e9a10</id>
<title>Travel</title>
<updated>2026-10-10T12:30:00Z</updated>
<summary type="text">Trips and places</summary>
<link rel="edit" href="https://example.com/roller/roller-services/app/myblog/category/7c3e9a10"/>
<roller:image>https://example.com/images/travel.png</roller:image>
<roller:position>2</roller:position>
<roller:inUse>true</roller:inUse>
<app:control>
<app:draft>no</app:draft>
</app:control>
</entry>
11 changes: 11 additions & 0 deletions docs/atompub/examples/category-request.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:roller="http://roller.apache.org/ns/atompub/1.0">
<id>urn:uuid:5b1f7a1e-0b6a-4c0e-9a37-3f1d2c4e8b11</id>
<title>Travel</title>
<updated>2026-10-10T12:00:00Z</updated>
<author>
<name>dave</name>
</author>
<summary type="text">Trips and places</summary>
<roller:image>https://example.com/images/travel.png</roller:image>
</entry>
21 changes: 21 additions & 0 deletions docs/atompub/examples/comment-entry.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app" xmlns:roller="http://roller.apache.org/ns/atompub/1.0" xmlns:thr="http://purl.org/syndication/thread/1.0">
<id>https://example.com/roller/roller-services/app/myblog/comment/8f0e2a6c</id>
<title>Comment on Hello AtomPub</title>
<published>2026-10-10T12:00:00Z</published>
<updated>2026-10-10T12:00:00Z</updated>
<author>
<name>Pat Reader</name>
<uri>https://pat.example.org/</uri>
<email>pat@example.org</email>
</author>
<content type="text">Nice post!</content>
<link rel="edit" href="https://example.com/roller/roller-services/app/myblog/comment/8f0e2a6c"/>
<link rel="related" href="https://example.com/roller/myblog/entry/hello_atompub"/>
<thr:in-reply-to ref="https://example.com/roller/myblog/entry/hello_atompub" href="https://example.com/roller/roller-services/app/myblog/entry/4a7d1ed4"/>
<roller:status>pending</roller:status>
<roller:remoteHost>192.0.2.10</roller:remoteHost>
<app:control>
<app:draft>no</app:draft>
</app:control>
</entry>
10 changes: 10 additions & 0 deletions docs/atompub/examples/comment-status-request.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:roller="http://roller.apache.org/ns/atompub/1.0">
<id>https://example.com/roller/roller-services/app/myblog/comment/8f0e2a6c</id>
<title>Comment on Hello AtomPub</title>
<updated>2026-10-10T12:00:00Z</updated>
<author>
<name>dave</name>
</author>
<roller:status>approved</roller:status>
</entry>
27 changes: 27 additions & 0 deletions docs/atompub/examples/comments-feed.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app" xmlns:roller="http://roller.apache.org/ns/atompub/1.0" xmlns:thr="http://purl.org/syndication/thread/1.0">
<id>https://example.com/roller/roller-services/app/myblog/comments/0</id>
<title>My Weblog comments</title>
<updated>2026-10-10T12:30:00Z</updated>
<link rel="next" href="https://example.com/roller/roller-services/app/myblog/comments/20?status=pending"/>
<entry>
<id>https://example.com/roller/roller-services/app/myblog/comment/8f0e2a6c</id>
<title>Comment on Hello AtomPub</title>
<published>2026-10-10T12:00:00Z</published>
<updated>2026-10-10T12:00:00Z</updated>
<author>
<name>Pat Reader</name>
<uri>https://pat.example.org/</uri>
<email>pat@example.org</email>
</author>
<content type="text">Nice post!</content>
<link rel="edit" href="https://example.com/roller/roller-services/app/myblog/comment/8f0e2a6c"/>
<link rel="related" href="https://example.com/roller/myblog/entry/hello_atompub"/>
<thr:in-reply-to ref="https://example.com/roller/myblog/entry/hello_atompub" href="https://example.com/roller/roller-services/app/myblog/entry/4a7d1ed4"/>
<roller:status>pending</roller:status>
<roller:remoteHost>192.0.2.10</roller:remoteHost>
<app:control>
<app:draft>no</app:draft>
</app:control>
</entry>
</feed>
27 changes: 27 additions & 0 deletions docs/atompub/examples/entries-feed.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app" xmlns:roller="http://roller.apache.org/ns/atompub/1.0" xmlns:thr="http://purl.org/syndication/thread/1.0">
<id>https://example.com/roller/roller-services/app/myblog/entries/0</id>
<title>My Weblog</title>
<updated>2026-10-10T12:30:00Z</updated>
<link rel="alternate" href="https://example.com/roller/myblog"/>
<link rel="next" href="https://example.com/roller/roller-services/app/myblog/entries/20"/>
<entry>
<id>https://example.com/roller/myblog/entry/hello_atompub</id>
<title>Hello AtomPub</title>
<published>2026-10-10T12:00:00Z</published>
<updated>2026-10-10T12:30:00Z</updated>
<author>
<name>dave</name>
<email>dave@example.com</email>
</author>
<category term="Java" scheme="https://example.com/roller/myblog/"/>
<category term="atompub"/>
<content type="html">&lt;p&gt;Posted with &lt;b&gt;AtomPub&lt;/b&gt;.&lt;/p&gt;</content>
<link rel="alternate" href="https://example.com/roller/myblog/entry/hello_atompub"/>
<link rel="edit" href="https://example.com/roller/roller-services/app/myblog/entry/4a7d1ed4"/>
<app:edited>2026-10-10T12:30:00Z</app:edited>
<app:control>
<app:draft>no</app:draft>
</app:control>
</entry>
</feed>
15 changes: 15 additions & 0 deletions docs/atompub/examples/entry-request.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app">
<id>urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a</id>
<title>Hello AtomPub</title>
<updated>2026-10-10T12:00:00Z</updated>
<author>
<name>dave</name>
</author>
<category term="Java" scheme="https://example.com/roller/myblog/"/>
<category term="atompub"/>
<content type="html">&lt;p&gt;Posted with &lt;b&gt;AtomPub&lt;/b&gt;.&lt;/p&gt;</content>
<app:control>
<app:draft>no</app:draft>
</app:control>
</entry>
20 changes: 20 additions & 0 deletions docs/atompub/examples/entry-response.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app" xmlns:roller="http://roller.apache.org/ns/atompub/1.0" xmlns:thr="http://purl.org/syndication/thread/1.0">
<id>https://example.com/roller/myblog/entry/hello_atompub</id>
<title>Hello AtomPub</title>
<published>2026-10-10T12:00:00Z</published>
<updated>2026-10-10T12:30:00Z</updated>
<author>
<name>dave</name>
<email>dave@example.com</email>
</author>
<category term="Java" scheme="https://example.com/roller/myblog/"/>
<category term="atompub"/>
<content type="html">&lt;p&gt;Posted with &lt;b&gt;AtomPub&lt;/b&gt;.&lt;/p&gt;</content>
<link rel="alternate" href="https://example.com/roller/myblog/entry/hello_atompub"/>
<link rel="edit" href="https://example.com/roller/roller-services/app/myblog/entry/4a7d1ed4"/>
<app:edited>2026-10-10T12:30:00Z</app:edited>
<app:control>
<app:draft>no</app:draft>
</app:control>
</entry>
14 changes: 14 additions & 0 deletions docs/atompub/examples/media-link-entry.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app" xmlns:roller="http://roller.apache.org/ns/atompub/1.0" xmlns:thr="http://purl.org/syndication/thread/1.0">
<id>https://example.com/roller/roller-services/app/myblog/resource/default/sunset.jpg</id>
<title>sunset.jpg</title>
<updated>2026-10-10T12:30:00Z</updated>
<content type="image/jpeg" src="https://example.com/roller/myblog/mediaresource/2b9c4f0e"/>
<link rel="alternate" href="https://example.com/roller/myblog/mediaresource/2b9c4f0e"/>
<link rel="edit" href="https://example.com/roller/roller-services/app/myblog/resource/default/sunset.jpg.media-link"/>
<link rel="edit-media" href="https://example.com/roller/roller-services/app/myblog/resource/default/sunset.jpg"/>
<app:edited>2026-10-10T12:30:00Z</app:edited>
<app:control>
<app:draft>no</app:draft>
</app:control>
</entry>
34 changes: 34 additions & 0 deletions docs/atompub/examples/service.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<service xmlns="http://www.w3.org/2007/app" xmlns:atom="http://www.w3.org/2005/Atom">
<workspace>
<atom:title>My Weblog</atom:title>
<collection href="https://example.com/roller/roller-services/app/myblog/entries">
<atom:title>Weblog Entries</atom:title>
<accept>application/atom+xml;type=entry</accept>
<categories fixed="yes" scheme="https://example.com/roller/myblog/">
<atom:category term="General" label="General"/>
<atom:category term="Java" label="Java"/>
<atom:category term="Travel" label="Travel"/>
</categories>
<categories href="https://example.com/roller/roller-services/app/myblog/categories.atomcat"/>
<categories fixed="no"/>
</collection>
<collection href="https://example.com/roller/roller-services/app/myblog/resources/default">
<atom:title>Media Files: default</atom:title>
<accept>image/*</accept>
<accept>application/pdf</accept>
</collection>
<collection href="https://example.com/roller/roller-services/app/myblog/templates">
<atom:title>Templates</atom:title>
<accept>application/atom+xml;type=entry</accept>
</collection>
<collection href="https://example.com/roller/roller-services/app/myblog/categories">
<atom:title>Categories</atom:title>
<accept>application/atom+xml;type=entry</accept>
</collection>
<collection href="https://example.com/roller/roller-services/app/myblog/comments">
<atom:title>Comments</atom:title>
<accept/>
</collection>
</workspace>
</service>
19 changes: 19 additions & 0 deletions docs/atompub/examples/template-entry.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app" xmlns:roller="http://roller.apache.org/ns/atompub/1.0" xmlns:thr="http://purl.org/syndication/thread/1.0">
<id>https://example.com/roller/roller-services/app/myblog/template/0e5d8b62</id>
<title>sidebar</title>
<updated>2026-10-10T12:30:00Z</updated>
<summary type="text">The sidebar</summary>
<content type="text">&lt;div class="sidebar"&gt;$model.weblog.name&lt;/div&gt;</content>
<link rel="edit" href="https://example.com/roller/roller-services/app/myblog/template/0e5d8b62"/>
<roller:rendition type="mobile">&lt;div class="sidebar-mobile"&gt;$model.weblog.name&lt;/div&gt;</roller:rendition>
<roller:action>custom</roller:action>
<roller:link>sidebar</roller:link>
<roller:navbar>true</roller:navbar>
<roller:hidden>false</roller:hidden>
<roller:required>false</roller:required>
<app:edited>2026-10-10T12:30:00Z</app:edited>
<app:control>
<app:draft>no</app:draft>
</app:control>
</entry>
Loading