From 720e1ca9e79a00acf72e77a42de3f421ff661081 Mon Sep 17 00:00:00 2001 From: David Taylor Date: Mon, 28 Mar 2022 16:46:47 +0100 Subject: [PATCH] FEATURE: Support upload:// urls in img tags (#16277) Previously, our `upload://` protocol urls were only supported in markdown image tags. This meant that our PullHotlinkedImages job was forced to convert ` 0) { - let srcList = uploads.map(([token, srcIndex]) => token.attrs[srcIndex][1]); + let srcList = uploads.map((u) => u.origSrc); + + // In client-side cooking, this lookup returns nothing + // This means we set data-orig-src, and let decorateCooked + // lookup the image URLs asynchronously let lookup = state.md.options.discourse.lookupUploadUrls; let longUrls = (lookup && lookup(srcList)) || {}; - uploads.forEach(([token, srcIndex]) => { - let origSrc = token.attrs[srcIndex][1]; + uploads.forEach(({ token, srcIndex, origSrc }) => { let mapped = longUrls[origSrc]; - switch (token.tag) { - case "img": - if (mapped) { - token.attrs[srcIndex][1] = mapped.url; - token.attrs.push(["data-base62-sha1", mapped.base62_sha1]); - } else { - // no point putting a transparent .png for audio/video - if (token.content.match(/\|video|\|audio/)) { - token.attrs[srcIndex][1] = state.md.options.discourse.getURL( - "/404" - ); - } else { - token.attrs[srcIndex][1] = state.md.options.discourse.getURL( - "/images/transparent.png" - ); - } + if (HTML_TYPES.includes(token.type)) { + const locator = uploadLocatorString(origSrc); + let attrs = []; - token.attrs.push(["data-orig-src", origSrc]); - } - break; - case "a": - if (mapped) { - // when secure media is enabled we want the full /secure-media-uploads/ - // url to take advantage of access control security - if ( - state.md.options.discourse.limitedSiteSettings.secureMedia && - mapped.url.indexOf("secure-media-uploads") > -1 - ) { - token.attrs[srcIndex][1] = mapped.url; - } else { - token.attrs[srcIndex][1] = mapped.short_path; - } - } else { + if (mapped) { + attrs.push( + attr("src", mapped.url), + attr("data-base62-sha1", mapped.base62_sha1) + ); + } else { + attrs.push( + attr( + "src", + state.md.options.discourse.getURL("/images/transparent.png") + ), + attr("data-orig-src", origSrc) + ); + } + + token.content = token.content.replace(locator, attrs.join(" ")); + } else if (token.tag === "img") { + if (mapped) { + token.attrs[srcIndex][1] = mapped.url; + token.attrs.push(["data-base62-sha1", mapped.base62_sha1]); + } else { + // no point putting a transparent .png for audio/video + if (token.content.match(/\|video|\|audio/)) { token.attrs[srcIndex][1] = state.md.options.discourse.getURL( "/404" ); - - token.attrs.push(["data-orig-href", origSrc]); + } else { + token.attrs[srcIndex][1] = state.md.options.discourse.getURL( + "/images/transparent.png" + ); } - break; + token.attrs.push(["data-orig-src", origSrc]); + } + } else if (token.tag === "a") { + if (mapped) { + // when secure media is enabled we want the full /secure-media-uploads/ + // url to take advantage of access control security + if ( + state.md.options.discourse.limitedSiteSettings.secureMedia && + mapped.url.indexOf("secure-media-uploads") > -1 + ) { + token.attrs[srcIndex][1] = mapped.url; + } else { + token.attrs[srcIndex][1] = mapped.short_path; + } + } else { + token.attrs[srcIndex][1] = state.md.options.discourse.getURL("/404"); + + token.attrs.push(["data-orig-href", origSrc]); + } } }); } diff --git a/spec/lib/pretty_text_spec.rb b/spec/lib/pretty_text_spec.rb index e60117d664d..50b0f411029 100644 --- a/spec/lib/pretty_text_spec.rb +++ b/spec/lib/pretty_text_spec.rb @@ -1877,6 +1877,12 @@ HTML ![upload](#{upload.short_url.gsub(".png", "")}) + Inline img + +
+ Block img +
+ [some attachment](#{upload.short_url}) [some attachment|attachment](#{upload.short_url}) @@ -1901,6 +1907,10 @@ HTML

upload

+

Inline img

+
+ Block img +

some attachment

some attachment

some attachment|random