/**
* Copyright 2011 Alexandre Dutra
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
*
*/
package fr.xebia.confluence.gist;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
import java.util.Map;
import org.apache.commons.io.IOUtils;
import com.atlassian.cache.Cache;
import com.atlassian.cache.CacheManager;
import com.atlassian.confluence.core.ConfluenceActionSupport;
import com.atlassian.confluence.setup.settings.SettingsManager;
import com.atlassian.renderer.RenderContext;
import com.atlassian.renderer.TokenType;
import com.atlassian.renderer.v2.RenderMode;
import com.atlassian.renderer.v2.macro.BaseMacro;
import com.atlassian.renderer.v2.macro.MacroException;
import com.atlassian.renderer.v2.macro.WysiwygBodyType;
import com.google.common.io.Closeables;
/**
*
*/
public class GistMacro extends BaseMacro {
private static final String ID_DEFAULT_PARAM_KEY = "0";
private static final String ID_PARAM_KEY = "id";
private static final String FILE_PARAM_KEY = "file";
private static final String PREFETCH_PARAM_KEY = "prefetch";
private static final String PROXY_HOST_PARAM_KEY = "proxyHost";
private static final String PROXY_PORT_PARAM_KEY = "proxyPort";
private static final String CACHE_KEY = "fr.xebia.confluence.confluence-gist-macro";
private static final String COULD_NOT_FETCH_URL_CONTENTS_ERROR_KEY = "fr.xebia.confluence.confluence-gist-macro.errors.prefetch";
private static final String TRUE = "true";
private static final String SCRIPT_START = "<script src=\"";
private static final String SCRIPT_END = "\"></script>";
private CacheManager cacheManager;
private SettingsManager settingsManager;
public void setCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
}
public void setSettingsManager(SettingsManager settingsManager) {
this.settingsManager = settingsManager;
}
@Override
public RenderMode getBodyRenderMode() {
return RenderMode.NO_RENDER;
}
@Override
public TokenType getTokenType(@SuppressWarnings("rawtypes") Map parameters, String body, RenderContext context) {
return TokenType.BLOCK;
}
@Override
public WysiwygBodyType getWysiwygBodyType() {
return WysiwygBodyType.WIKI_MARKUP;
}
@Override
public boolean hasBody() {
return false;
}
@Override
public boolean isInline() {
return false;
}
@Override
public boolean suppressMacroRenderingDuringWysiwyg() {
return true;
}
@Override
public boolean suppressSurroundingTagDuringWysiwygRendering() {
return false;
}
@Override
public String execute(@SuppressWarnings("rawtypes") Map parameters, String body, RenderContext renderContext) throws MacroException {
String id;
if(parameters.containsKey(ID_PARAM_KEY)){
id = extractMacroParameter(parameters, ID_PARAM_KEY);
} else {
id = extractMacroParameter(parameters, ID_DEFAULT_PARAM_KEY);
}
String file = extractMacroParameter(parameters, FILE_PARAM_KEY);
StringBuilder gistUrl = createGistUrl(id, file);
if(parameters.containsKey(PREFETCH_PARAM_KEY) && TRUE.equals(parameters.get(PREFETCH_PARAM_KEY))){
Cache cache = cacheManager.getCache(CACHE_KEY);
String key = getCacheKey(id, file);
String contents = (String) cache.get(key);
if(contents == null) {
String proxyHost = extractMacroParameter(parameters, PROXY_HOST_PARAM_KEY);
String proxyPort = extractMacroParameter(parameters, PROXY_PORT_PARAM_KEY);
contents = fetchGistUrl(gistUrl.toString(), proxyHost, proxyPort);
cache.put(key, contents);
}
return contents;
} else {
StringBuilder sb = new StringBuilder();
sb.append(SCRIPT_START);
sb.append(gistUrl);
sb.append(SCRIPT_END);
return sb.toString();
}
}
private String extractMacroParameter(Map<?,?> parameters, String key) {
return (String) parameters.get(key);
}
private String fetchGistUrl(String gistUrl, String proxyHost, String proxyPort) throws MacroException {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
InputStream in = null;
try {
URL url = new URL(gistUrl);
URLConnection c;
if(proxyHost != null){
int port;
if(proxyPort == null){
port = 80;
} else {
port = Integer.parseInt(proxyPort);
}
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, port));
c = url.openConnection(proxy);
} else {
c = url.openConnection();
}
in = c.getInputStream();
IOUtils.copy(in, pw, settingsManager.getGlobalSettings().getDefaultEncoding());
pw.flush();
sw.flush();
return sw.toString();
} catch (IOException e) {
String message = ConfluenceActionSupport.getTextStatic(COULD_NOT_FETCH_URL_CONTENTS_ERROR_KEY, new String[]{gistUrl, e.getMessage()});
throw new MacroException(message, e);
} finally {
Closeables.closeQuietly(pw);
Closeables.closeQuietly(sw);
Closeables.closeQuietly(in);
}
}
private StringBuilder createGistUrl(String id, String file) {
StringBuilder sb = new StringBuilder();
sb.append("https://gist.github.com/");
sb.append(id);
sb.append(".js");
if(file != null){
sb.append("?file=");
sb.append(file);
}
return sb;
}
private String getCacheKey(String id, String file) {
StringBuilder sb = new StringBuilder();
sb.append(id);
if(file != null){
sb.append("-");
sb.append(file);
}
return sb.toString();
}
}