package org.fenixedu.bennu.portal.servlet; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.servlet.ServletContext; import org.fenixedu.bennu.core.i18n.BundleUtil; import com.google.common.hash.Funnels; import com.google.common.hash.Hasher; import com.google.common.hash.Hashing; import com.google.common.io.BaseEncoding; import com.google.common.io.ByteStreams; import com.mitchellbosecke.pebble.extension.AbstractExtension; import com.mitchellbosecke.pebble.extension.Filter; import com.mitchellbosecke.pebble.extension.Function; import com.mitchellbosecke.pebble.extension.Test; import com.mitchellbosecke.pebble.tokenParser.TokenParser; class PortalExtension extends AbstractExtension { private final ServletContext context; public PortalExtension(ServletContext context) { this.context = context; } @Override public Map<String, Filter> getFilters() { Map<String, Filter> filters = new HashMap<>(); filters.put("base64", new Base64Filter()); return filters; } private static class Base64Filter implements Filter { @Override public List<String> getArgumentNames() { return null; } @Override public Object apply(Object input, Map<String, Object> args) { if (input instanceof byte[]) { byte[] bytes = (byte[]) input; return BaseEncoding.base64().encode(bytes); } return input; } } @Override public Map<String, Function> getFunctions() { Map<String, Function> functions = new HashMap<>(); functions.put("i18n", new I18NFunction()); functions.put("asset", new AssetFunction()); return functions; } @Override public Map<String, Test> getTests() { Map<String, Test> tests = new HashMap<>(); tests.put("in", new InTest()); return tests; } private static class I18NFunction implements Function { @Override public List<String> getArgumentNames() { List<String> names = new ArrayList<>(); names.add("bundle"); names.add("key"); return names; } @Override public Object execute(Map<String, Object> args) { String bundle = (String) args.get("bundle"); String key = args.get("key").toString(); return BundleUtil.getString(bundle, key); } } private class AssetFunction implements Function { private final Map<String, String> pathMapping = new ConcurrentHashMap<>(); @Override public List<String> getArgumentNames() { return Collections.singletonList("path"); } @Override public Object execute(Map<String, Object> args) { String path = (String) args.get("path"); return pathMapping.computeIfAbsent(path, this::computePath); } private String computePath(String path) { String checksum = "_"; try (InputStream stream = context.getResourceAsStream(path)) { if (stream != null) { Hasher hasher = Hashing.sha1().newHasher(); try (OutputStream out = Funnels.asOutputStream(hasher)) { ByteStreams.copy(stream, out); checksum = hasher.hash().toString().substring(0, 12); } } } catch (IOException e) { // Ignored, we'll simply use the default value } return context.getContextPath() + path + "?v=" + checksum; } } private static class InTest implements Test { @Override public List<String> getArgumentNames() { return Collections.singletonList("collection"); } @Override public boolean apply(Object input, Map<String, Object> args) { Collection<?> collection = (Collection<?>) args.get("collection"); return collection.contains(input); } } @Override public List<TokenParser> getTokenParsers() { return Collections.singletonList(new LazyForTokenParser()); } }