Index: lams_central/src/java/org/lamsfoundation/lams/web/controller/EditorEmbedProxyController.java =================================================================== diff -u --- lams_central/src/java/org/lamsfoundation/lams/web/controller/EditorEmbedProxyController.java (revision 0) +++ lams_central/src/java/org/lamsfoundation/lams/web/controller/EditorEmbedProxyController.java (revision 162784e5aec3a88d7e9454a05502976b920d53a4) @@ -0,0 +1,141 @@ +package org.lamsfoundation.lams.web.controller; + +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang.StringUtils; +import org.apache.log4j.Logger; +import org.lamsfoundation.lams.util.Configuration; +import org.lamsfoundation.lams.util.ConfigurationKeys; +import org.lamsfoundation.lams.util.FileUtil; +import org.lamsfoundation.lams.util.WebUtil; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; + +/** + * Provides media embedding custom service for sites which are not supported by the default embed provider. + */ +@Controller +@RequestMapping("/embed") +public class EditorEmbedProxyController { + private static final Logger logger = Logger.getLogger(EditorEmbedProxyController.class); + + private static final String EMBED_PROVIDER_PROPERTY_NAME = "lams_embed_provider"; + private static final String CUSTOM_EMBED_PROPERTY_NAME = "lams_embed_custom_domains"; + private static final String EMBED_PROVIDER_DEFAULT_HTTP_PROTOCOL = "http:"; + private static final Pattern PROPERTY_VALUE_EXTRACTOR = Pattern.compile("'(.*?)'"); + + private final String targetEmbedUrl; + private final Set customEmbedDomains; + private final ObjectNode customEmbedJsonTemplate; + + public EditorEmbedProxyController() { + String targetEmbedUrl = null; + String customEmbedDomains = null; + + try { + // extract properties from ckconfig_custom.js + List ckeditorProperties = Files + .readAllLines(Paths.get(Configuration.get(ConfigurationKeys.LAMS_EAR_DIR), + FileUtil.LAMS_CENTRAL_WAR_DIR, "includes", "javascript", "ckconfig_custom.js")); + for (String property : ckeditorProperties) { + if (property == null) { + continue; + } + if (property.startsWith(EMBED_PROVIDER_PROPERTY_NAME)) { + Matcher matcher = PROPERTY_VALUE_EXTRACTOR.matcher(property); + if (matcher.find()) { + targetEmbedUrl = matcher.group(1); + } + } else if (property.startsWith(CUSTOM_EMBED_PROPERTY_NAME)) { + Matcher matcher = PROPERTY_VALUE_EXTRACTOR.matcher(property); + if (matcher.find()) { + customEmbedDomains = matcher.group(1); + } + } + } + } catch (Exception e) { + logger.error("Error while parsing CKEditor custom config file", e); + } + + if (StringUtils.isBlank(targetEmbedUrl)) { + this.targetEmbedUrl = null; + } else { + targetEmbedUrl = targetEmbedUrl.strip(); + if (targetEmbedUrl.startsWith("//")) { + targetEmbedUrl = EMBED_PROVIDER_DEFAULT_HTTP_PROTOCOL + targetEmbedUrl; + } + this.targetEmbedUrl = targetEmbedUrl; + } + + if (StringUtils.isBlank(customEmbedDomains)) { + this.customEmbedDomains = null; + this.customEmbedJsonTemplate = null; + } else { + this.customEmbedDomains = Arrays.stream(customEmbedDomains.split(",")).map(String::strip) + .collect(Collectors.toUnmodifiableSet()); + this.customEmbedJsonTemplate = JsonNodeFactory.instance.objectNode(); + // at the moment only video embeds work + this.customEmbedJsonTemplate.put("type", "video"); + this.customEmbedJsonTemplate.put("version", "1.0"); + // placeholder [URL] will be replaced for each request + this.customEmbedJsonTemplate.put("html", + "
" + + "
"); + + } + if (this.targetEmbedUrl == null || this.customEmbedDomains == null) { + logger.info("CKEditor embed custom properties are not set"); + } + } + + @GetMapping("") + public ResponseEntity proxyEmbed(HttpServletRequest request) { + if (targetEmbedUrl == null) { + return ResponseEntity.status(HttpStatus.NOT_FOUND) + .body(EMBED_PROVIDER_PROPERTY_NAME + " property is not set in ckeditor_custom.js"); + } + String embeddedUrl = request.getParameter("url"); + if (StringUtils.isBlank(embeddedUrl)) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("\"url\" parameter not found"); + } + + for (String customEmbedDomain : customEmbedDomains) { + if (embeddedUrl.contains(customEmbedDomain)) { + String callback = request.getParameter("callback"); + if (StringUtils.isBlank(callback)) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("\"callback\" parameter not found"); + } + + // copy JSON template and construct response + ObjectNode responseJSON = this.customEmbedJsonTemplate.deepCopy(); + responseJSON.put("url", embeddedUrl); + responseJSON.put("html", + this.customEmbedJsonTemplate.get("html").asText().replace("[URL]", embeddedUrl)); + StringBuilder responseString = new StringBuilder(callback).append(" && ").append(callback).append("(") + .append(responseJSON.toString()).append(");"); + return ResponseEntity.ok(responseString.toString()); + } + } + + // if the requested media is not in custom domain list, just redirect to the original embed provider + String callUrl = WebUtil.appendParameterDeliminator(targetEmbedUrl) + request.getQueryString(); + return ResponseEntity.status(HttpStatus.FOUND).location(URI.create(callUrl)).build(); + } +} \ No newline at end of file Index: lams_central/web/includes/javascript/ckconfig_custom.js =================================================================== diff -u -rd8fc956784e11bbceaf2ceb984ab1013d361e3de -r162784e5aec3a88d7e9454a05502976b920d53a4 --- lams_central/web/includes/javascript/ckconfig_custom.js (.../ckconfig_custom.js) (revision d8fc956784e11bbceaf2ceb984ab1013d361e3de) +++ lams_central/web/includes/javascript/ckconfig_custom.js (.../ckconfig_custom.js) (revision 162784e5aec3a88d7e9454a05502976b920d53a4) @@ -137,7 +137,6 @@ CKEDITOR.config.removePlugins = 'elementspath,about,specialchar'; CKEDITOR.config.allowedContent = true; CKEDITOR.config.toolbarCanCollapse = true; -CKEDITOR.config.embed_provider = '//ckeditor.iframe.ly/api/oembed?url={url}&callback={callback}'; //default URL for 'embed' plugin CKEDITOR.config.filebrowserUploadMethod = 'form'; //allow empty i and span tags (for font awesome) CKEDITOR.dtd.$removeEmpty['i'] = false; @@ -205,3 +204,20 @@ placeholderLessenHandler(event.editor); }); }); + +CKEDITOR.config.embed_provider = '//ckeditor.iframe.ly/api/oembed?url={url}&callback={callback}'; //default URL for 'embed' plugin +/* If you want to use LAMS embedding proxy: +Set +CKEDITOR.config.embed_provider = '/lams/embed.do?url={url}&callback={callback}'; +You can use absolute URL instead, for example +CKEDITOR.config.embed_provider = 'https://demo.lamsfoundation.org/lams/embed.do?url={url}&callback={callback}'; + +Then set the real embed provider URL into this property +lams_embed_provider = '//ckeditor.iframe.ly/api/oembed?consent=0'; +For iframely remember to use "consent=0" parameter, otherwise each user will be asked whether to trust this content. +You can also append your API key: +lams_embed_provider = '//ckeditor.iframe.ly/api/oembed?consent=0&api_key=YOUR_API_KEY_HERE'; + +Then put a comma-separated list of domains for which you want to use LAMS embedding, for example +lams_embed_custom_domains = 'video.someuni.ac.uk,media.anotheruni.sg'; +*/ \ No newline at end of file