Rewrite old closet links to use new syntax
A lot of DTI lists use old URLs to anchor-link between lists! Here, we rewrite those URLs to match what DTI 2020 expects, so that they actually correctly jump you across the page and aren't filtered out!
This commit is contained in:
parent
dfcd9985d4
commit
cc9cd5ef28
2 changed files with 62 additions and 2 deletions
|
@ -349,7 +349,9 @@ export function ClosetList({
|
||||||
boxShadow="sm"
|
boxShadow="sm"
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<MarkdownAndSafeHTML>{closetList.description}</MarkdownAndSafeHTML>
|
<MarkdownAndSafeHTML onBeforeSanitizeNode={rewriteLinkAnchors}>
|
||||||
|
{closetList.description}
|
||||||
|
</MarkdownAndSafeHTML>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
@ -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;
|
export default UserItemListPage;
|
||||||
|
|
|
@ -37,11 +37,14 @@ const unsafeMarkdownOutput = SimpleMarkdown.htmlFor(
|
||||||
* Rendering this component *should* be XSS-safe, it's designed to strip out
|
* 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!
|
* 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 htmlAndMarkdown = children;
|
||||||
|
|
||||||
const unsafeHtml = unsafeMarkdownOutput(markdownParser(htmlAndMarkdown));
|
const unsafeHtml = unsafeMarkdownOutput(markdownParser(htmlAndMarkdown));
|
||||||
|
|
||||||
|
if (onBeforeSanitizeNode) {
|
||||||
|
DOMPurify.addHook("beforeSanitizeAttributes", onBeforeSanitizeNode);
|
||||||
|
}
|
||||||
const sanitizedHtml = DOMPurify.sanitize(unsafeHtml, {
|
const sanitizedHtml = DOMPurify.sanitize(unsafeHtml, {
|
||||||
ALLOWED_TAGS: [
|
ALLOWED_TAGS: [
|
||||||
"b",
|
"b",
|
||||||
|
@ -62,6 +65,9 @@ function MarkdownAndSafeHTML({ children }) {
|
||||||
// slash or hash (internal link).
|
// slash or hash (internal link).
|
||||||
ALLOWED_URI_REGEXP: /^https?:\/\/(impress\.openneo\.net|impress-2020\.openneo\.net|www\.neopets\.com|neopets\.com|items\.jellyneo\.net)\/|^[/#]/,
|
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 (
|
return (
|
||||||
<ClassNames>
|
<ClassNames>
|
||||||
|
|
Loading…
Reference in a new issue