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
17 changes: 17 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,23 @@
every AtomPub URI, permission, element and status code, with examples
that the test suite checks against the code.

### Bug fixes

- **AtomPub fixes found while running a 7.0.0 snapshot.**
- Uploaded media files get the usual extension for their type:
`image/svg+xml` gives `.svg`, parameters such as `charset` are ignored,
and a `Slug` that already has a matching extension keeps it.
- The `next` and `previous` links of a named media directory include the
slash before the offset, so paging through it works.
- Characters that XML 1.0 does not allow are left out of AtomPub
responses, so one stored control character no longer makes a whole page
unreadable.
- A file uploaded over AtomPub can be read back through its `edit-media`
URI; it answered 404.
- **The rich text editor's menus show their labels again.** Summernote is
upgraded to 0.9.1, and the panel chevron no longer appears on the
editor's menu items.

### Behaviour changes worth reading before upgrading

- **AtomPub no longer uses ROME Propono.** Roller now has its own AtomPub
Expand Down
6 changes: 4 additions & 2 deletions app/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -277,10 +277,12 @@ limitations under the License.
<version>1.14.2+1</version>
</dependency>

<!-- 0.9 renders its menu check icons as <i></i>; 0.8.12 wrote <i/>,
which jQuery 3.5+ no longer expands, hiding the menu labels -->
<dependency>
<groupId>org.webjars.bower</groupId>
<groupId>org.webjars.npm</groupId>
<artifactId>summernote</artifactId>
<version>0.8.12</version>
<version>0.9.1</version>
</dependency>

<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,9 @@ public MediaFile getMediaFileByPath(Weblog weblog, String path)
} else {
mdir = getDefaultMediaFileDirectory(weblog);
}
if (mdir == null) {
return null;
}
if (slash != -1) {
fileName = fileName.substring(slash + 1);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ public void writeEntry(OutputStream out, AtomEntry entry) throws AtomException {
public void writeCategoriesDoc(OutputStream out, AtomCategories cats) throws AtomException {
writeDocument(out, APP_NS, "categories", "atom", ATOM_NS,
"Error serializing category document", w -> {
w.writeAttribute("fixed", cats.isFixed() ? "yes" : "no");
w.writeAttribute("fixed", xml(cats.isFixed() ? "yes" : "no"));
if (cats.getScheme() != null) {
w.writeAttribute("scheme", cats.getScheme());
w.writeAttribute("scheme", xml(cats.getScheme()));
}
for (AtomCategory cat : cats.getCategories()) {
writeCategory(w, cat);
Expand Down Expand Up @@ -151,24 +151,24 @@ private void writeCollection(XMLStreamWriter w, AtomCollection collection)
throws XMLStreamException {
w.writeStartElement(APP_NS, "collection");
if (collection.getHref() != null) {
w.writeAttribute("href", collection.getHref());
w.writeAttribute("href", xml(collection.getHref()));
}
writeAtomText(w, "title", collection.getTitle());
for (String accept : collection.getAccepts()) {
w.writeStartElement(APP_NS, "accept");
w.writeCharacters(accept);
w.writeCharacters(xml(accept));
w.writeEndElement();
}
for (AtomCategories cats : collection.getCategories()) {
if (cats.getHref() != null) {
w.writeEmptyElement(APP_NS, "categories");
w.writeAttribute("href", cats.getHref());
w.writeAttribute("href", xml(cats.getHref()));
continue;
}
w.writeStartElement(APP_NS, "categories");
w.writeAttribute("fixed", cats.isFixed() ? "yes" : "no");
w.writeAttribute("fixed", xml(cats.isFixed() ? "yes" : "no"));
if (cats.getScheme() != null) {
w.writeAttribute("scheme", cats.getScheme());
w.writeAttribute("scheme", xml(cats.getScheme()));
}
for (AtomCategory cat : cats.getCategories()) {
writeCategory(w, cat);
Expand Down Expand Up @@ -204,32 +204,32 @@ private void writeEntryBody(XMLStreamWriter w, AtomEntry entry) throws XMLStream
}
if (entry.getInReplyToRef() != null) {
w.writeEmptyElement(THREAD_NS, "in-reply-to");
w.writeAttribute("ref", entry.getInReplyToRef());
w.writeAttribute("ref", xml(entry.getInReplyToRef()));
if (entry.getInReplyToHref() != null) {
w.writeAttribute("href", entry.getInReplyToHref());
w.writeAttribute("href", xml(entry.getInReplyToHref()));
}
}
if (entry.getMobileRendition() != null) {
w.writeStartElement(ROLLER_NS, "rendition");
w.writeAttribute("type", "mobile");
w.writeCharacters(entry.getMobileRendition());
w.writeAttribute("type", xml("mobile"));
w.writeCharacters(xml(entry.getMobileRendition()));
w.writeEndElement();
}
for (Map.Entry<String, String> ext : entry.getExtensions().entrySet()) {
w.writeStartElement(ROLLER_NS, ext.getKey());
w.writeCharacters(ext.getValue());
w.writeCharacters(xml(ext.getValue()));
w.writeEndElement();
}
// APP extensions (RFC 5023): app:edited is a child of atom:entry
// (section 10.2); app:draft goes inside app:control (section 13.1)
if (entry.getEdited() != null) {
w.writeStartElement(APP_NS, "edited");
w.writeCharacters(formatDate(entry.getEdited()));
w.writeCharacters(xml(formatDate(entry.getEdited())));
w.writeEndElement();
}
w.writeStartElement(APP_NS, "control");
w.writeStartElement(APP_NS, "draft");
w.writeCharacters(entry.isDraft() ? "yes" : "no");
w.writeCharacters(xml(entry.isDraft() ? "yes" : "no"));
w.writeEndElement();
w.writeEndElement();
}
Expand All @@ -238,40 +238,40 @@ private void writeContent(XMLStreamWriter w, String name, AtomContent content)
throws XMLStreamException {
w.writeStartElement(ATOM_NS, name);
if (content.getType() != null) {
w.writeAttribute("type", content.getType());
w.writeAttribute("type", xml(content.getType()));
}
if (content.getSrc() != null) {
w.writeAttribute("src", content.getSrc());
w.writeAttribute("src", xml(content.getSrc()));
} else if (content.getValue() != null) {
w.writeCharacters(content.getValue());
w.writeCharacters(xml(content.getValue()));
}
w.writeEndElement();
}

private void writeLink(XMLStreamWriter w, AtomLink link) throws XMLStreamException {
w.writeStartElement(ATOM_NS, "link");
if (link.getRel() != null) {
w.writeAttribute("rel", link.getRel());
w.writeAttribute("rel", xml(link.getRel()));
}
if (link.getHref() != null) {
w.writeAttribute("href", link.getHref());
w.writeAttribute("href", xml(link.getHref()));
}
if (link.getType() != null) {
w.writeAttribute("type", link.getType());
w.writeAttribute("type", xml(link.getType()));
}
w.writeEndElement();
}

private void writeCategory(XMLStreamWriter w, AtomCategory cat) throws XMLStreamException {
w.writeStartElement(ATOM_NS, "category");
if (cat.getTerm() != null) {
w.writeAttribute("term", cat.getTerm());
w.writeAttribute("term", xml(cat.getTerm()));
}
if (cat.getScheme() != null) {
w.writeAttribute("scheme", cat.getScheme());
w.writeAttribute("scheme", xml(cat.getScheme()));
}
if (cat.getLabel() != null) {
w.writeAttribute("label", cat.getLabel());
w.writeAttribute("label", xml(cat.getLabel()));
}
w.writeEndElement();
}
Expand All @@ -282,7 +282,36 @@ private void writeAtomText(XMLStreamWriter w, String name, String text)
return;
}
w.writeStartElement(ATOM_NS, name);
w.writeCharacters(text);
w.writeCharacters(xml(text));
w.writeEndElement();
}

/**
* Drops the characters that XML 1.0 does not allow in a document (most
* control characters, U+FFFE, U+FFFF and unpaired surrogates). Stored
* text can contain them, and a single one makes the whole response
* unreadable for a client.
*/
static String xml(String text) {
if (text == null) {
return null;
}
StringBuilder out = null;
int i = 0;
while (i < text.length()) {
int cp = text.codePointAt(i);
int len = Character.charCount(cp);
boolean legal = cp == 0x9 || cp == 0xA || cp == 0xD
|| (cp >= 0x20 && cp <= 0xD7FF)
|| (cp >= 0xE000 && cp <= 0xFFFD)
|| (cp >= 0x10000 && cp <= 0x10FFFF);
if (!legal && out == null) {
out = new StringBuilder(text.length()).append(text, 0, i);
} else if (legal && out != null) {
out.appendCodePoint(cp);
}
i += len;
}
return out == null ? text : out.toString();
}
}
Loading
Loading