diff --git a/src/app/UserItemListPage.js b/src/app/UserItemListPage.js
index 311071f..402b7fc 100644
--- a/src/app/UserItemListPage.js
+++ b/src/app/UserItemListPage.js
@@ -349,7 +349,9 @@ export function ClosetList({
boxShadow="sm"
/>
) : (
- {closetList.description}
+
+ {closetList.description}
+
)}
)}
@@ -556,4 +558,56 @@ export function NeopetsStarIcon(props) {
);
}
+/**
+ * Rewrite old-style links from Classic DTI, for compatibility with our new
+ * URLs and anchor syntax.
+ */
+function rewriteLinkAnchors(node) {
+ try {
+ // Only rewrite `a` tags, and only if they point to DTI (either classic
+ // DTI, or a path relative to the current host.)
+ if (node.nodeName === "A") {
+ const url = new URL(node.href);
+ if (
+ url.host === "impress.openneo.net" ||
+ url.host === window.location.host
+ ) {
+ // Rewrite the `closet` path component to `lists`.
+ const closetPathMatch = url.pathname.match(/^\/user\/([^/]+)\/closet$/);
+ if (closetPathMatch) {
+ url.pathname = `/user/${closetPathMatch[1]}/lists`;
+ }
+
+ // Rewrite `#closet-list-123` to `#list-123`.
+ const closetListAnchorMatch = url.hash.match(/^#closet-list-(.+)$/);
+ if (closetListAnchorMatch) {
+ url.hash = "#list-" + closetListAnchorMatch[1];
+ }
+
+ // Rewrite `#closet-hangers-group-true` to `#owned-items`.
+ if (url.hash === "#closet-hangers-group-true") {
+ url.hash = "#owned-items";
+ }
+
+ // Rewrite `#closet-hangers-group-false` to `#wanted-items`.
+ if (url.hash === "#closet-hangers-group-false") {
+ url.hash = "#wanted-items";
+ }
+
+ // Set the href to be an absolute path to the new URL. (We do this
+ // rather than url.toString(), because that will return a full absolute
+ // URL, and DOMPurify might not have the current host in its allow list,
+ // e.g. in dev or in a preview deploy.)
+ node.href = url.pathname + url.search + url.hash;
+ }
+ }
+ } catch (e) {
+ // None of these operations are safety operations; they're just to make
+ // old-style links compatible. If processing fails, then they'll just be
+ // judged for safety by DOMPurify and deleted if unsafe, which is fine!
+ // So, we allow processing to succeed, and just log the error.
+ console.error("[rewriteLinkAnchors] Could not rewrite node", node, e);
+ }
+}
+
export default UserItemListPage;
diff --git a/src/app/components/MarkdownAndSafeHTML.js b/src/app/components/MarkdownAndSafeHTML.js
index b97bdb7..1093922 100644
--- a/src/app/components/MarkdownAndSafeHTML.js
+++ b/src/app/components/MarkdownAndSafeHTML.js
@@ -37,11 +37,14 @@ const unsafeMarkdownOutput = SimpleMarkdown.htmlFor(
* Rendering this component *should* be XSS-safe, it's designed to strip out
* bad things! Still, be careful when using it, and consider what you're doing!
*/
-function MarkdownAndSafeHTML({ children }) {
+function MarkdownAndSafeHTML({ children, onBeforeSanitizeNode }) {
const htmlAndMarkdown = children;
const unsafeHtml = unsafeMarkdownOutput(markdownParser(htmlAndMarkdown));
+ if (onBeforeSanitizeNode) {
+ DOMPurify.addHook("beforeSanitizeAttributes", onBeforeSanitizeNode);
+ }
const sanitizedHtml = DOMPurify.sanitize(unsafeHtml, {
ALLOWED_TAGS: [
"b",
@@ -62,6 +65,9 @@ function MarkdownAndSafeHTML({ children }) {
// slash or hash (internal link).
ALLOWED_URI_REGEXP: /^https?:\/\/(impress\.openneo\.net|impress-2020\.openneo\.net|www\.neopets\.com|neopets\.com|items\.jellyneo\.net)\/|^[/#]/,
});
+ if (onBeforeSanitizeNode) {
+ DOMPurify.removeHook("beforeSanitizeAttributes");
+ }
return (