package play.templates; import groovy.lang.Closure; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang.StringUtils; import play.cache.Cache; import play.data.validation.Error; import play.data.validation.Validation; import play.exceptions.TagInternalException; import play.exceptions.TemplateExecutionException; import play.exceptions.TemplateNotFoundException; import play.libs.Codec; import play.mvc.Http; import play.mvc.Router.ActionDefinition; import play.mvc.Scope.Flash; import play.mvc.Scope.Session; import play.templates.BaseTemplate.RawData; import play.templates.GroovyTemplate.ExecutableTemplate; import play.utils.HTML; /** * Fast tags implementation */ public class FastTags { public static void _cache(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { String key = args.get("arg").toString(); String duration = null; if (args.containsKey("for")) { duration = args.get("for").toString(); } Object cached = Cache.get(key); if (cached != null) { out.print(cached); return; } String result = JavaExtensions.toString(body); Cache.set(key, result, duration); out.print(result); } public static void _verbatim(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { out.println(JavaExtensions.toString(body)); } public static void _jsAction(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { out.println("function(options) {var pattern = '" + args.get("arg").toString().replace("&", "&") + "'; for(key in options) { pattern = pattern.replace(':'+key, options[key]); } return pattern }"); } public static void _jsRoute(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { final Object arg = args.get("arg"); if (!(arg instanceof ActionDefinition)) { throw new TemplateExecutionException(template.template, fromLine, "Wrong parameter type, try #{jsRoute @Application.index() /}", new TagInternalException("Wrong parameter type")); } final ActionDefinition action = (ActionDefinition)arg; out.print("{"); if (action.args.isEmpty()) { out.print("url: function() { return '" + action.url.replace("&", "&") + "'; },"); } else { out.print("url: function(args) { var pattern = '" + action.url.replace("&", "&") + "'; for (var key in args) { pattern = pattern.replace(':'+key, args[key]); } return pattern; },"); } out.print("method: '" + action.method + "'"); out.print("}"); } public static void _authenticityToken(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { out.println("<input type=\"hidden\" name=\"authenticityToken\" value=\"" + Session.current().getAuthenticityToken() + "\">"); } public static void _option(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { Object value = args.get("arg"); Object selectedValue = TagContext.parent("select").data.get("selected"); boolean selected = selectedValue != null && value != null && (selectedValue.toString()).equals(value.toString()); out.print("<option value=\"" + (value == null ? "" : value) + "\" " + (selected ? "selected=\"selected\"" : "") + " " + serialize(args, "selected", "value") + ">"); out.println(JavaExtensions.toString(body)); out.print("</option>"); } /** * Generates a html form element linked to a controller action * @param args tag attributes * @param body tag inner body * @param out the output writer * @param template enclosing template * @param fromLine template line number where the tag is defined */ public static void _form(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { ActionDefinition actionDef = (ActionDefinition) args.get("arg"); if (actionDef == null) { actionDef = (ActionDefinition) args.get("action"); } String enctype = (String) args.get("enctype"); if (enctype == null) { enctype = "application/x-www-form-urlencoded"; } if (actionDef.star) { actionDef.method = "POST"; // prefer POST for form .... } if (args.containsKey("method")) { actionDef.method = args.get("method").toString(); } if (!("GET".equals(actionDef.method) || "POST".equals(actionDef.method))) { String separator = actionDef.url.indexOf('?') != -1 ? "&" : "?"; actionDef.url += separator + "x-http-method-override=" + actionDef.method.toUpperCase(); actionDef.method = "POST"; } String encoding = Http.Response.current().encoding; out.print("<form action=\"" + actionDef.url + "\" method=\"" + actionDef.method.toLowerCase() + "\" accept-charset=\""+encoding+"\" enctype=\"" + enctype + "\" " + serialize(args, "action", "method", "accept-charset", "enctype") + ">"); if (!("GET".equals(actionDef.method))) { _authenticityToken(args, body, out, template, fromLine); } out.println(JavaExtensions.toString(body)); out.print("</form>"); } /** * The field tag is a helper, based on the spirit of Don't Repeat Yourself. * @param args tag attributes * @param body tag inner body * @param out the output writer * @param template enclosing template * @param fromLine template line number where the tag is defined */ public static void _field(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { Map<String,Object> field = new HashMap<String,Object>(); String _arg = args.get("arg").toString(); field.put("name", _arg); field.put("id", _arg.replace('.','_')); field.put("flash", Flash.current().get(_arg)); field.put("flashArray", field.get("flash") != null && !StringUtils.isEmpty(field.get("flash").toString()) ? field.get("flash").toString().split(",") : new String[0]); field.put("error", Validation.error(_arg)); field.put("errorClass", field.get("error") != null ? "hasError" : ""); String[] pieces = _arg.split("\\."); Object obj = body.getProperty(pieces[0]); if(obj != null){ if(pieces.length > 1){ for(int i = 1; i < pieces.length; i++){ try{ Field f = obj.getClass().getField(pieces[i]); if(i == (pieces.length-1)){ try{ Method getter = obj.getClass().getMethod("get"+JavaExtensions.capFirst(f.getName())); field.put("value", getter.invoke(obj, new Object[0])); }catch(NoSuchMethodException e){ field.put("value",f.get(obj).toString()); } }else{ obj = f.get(obj); } }catch(Exception e){ // if there is a problem reading the field we dont set any value } } }else{ field.put("value", obj); } } body.setProperty("field", field); body.call(); } /** * Generates a html link to a controller action * @param args tag attributes * @param body tag inner body * @param out the output writer * @param template enclosing template * @param fromLine template line number where the tag is defined */ public static void _a(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { ActionDefinition actionDef = (ActionDefinition) args.get("arg"); if (actionDef == null) { actionDef = (ActionDefinition) args.get("action"); } if (!("GET".equals(actionDef.method))) { if (!("POST".equals(actionDef.method))) { String separator = actionDef.url.indexOf('?') != -1 ? "&" : "?"; actionDef.url += separator + "x-http-method-override=" + actionDef.method; actionDef.method = "POST"; } String id = Codec.UUID(); out.print("<form method=\"POST\" id=\"" + id + "\" " +(args.containsKey("target") ? "target=\"" + args.get("target") + "\"" : "")+ " style=\"display:none\" action=\"" + actionDef.url + "\">"); _authenticityToken(args, body, out, template, fromLine); out.print("</form>"); out.print("<a href=\"javascript:document.getElementById('" + id + "').submit();\" " + serialize(args, "href") + ">"); out.print(JavaExtensions.toString(body)); out.print("</a>"); } else { out.print("<a href=\"" + actionDef.url + "\" " + serialize(args, "href") + ">"); out.print(JavaExtensions.toString(body)); out.print("</a>"); } } public static void _ifErrors(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { if (Validation.hasErrors()) { body.call(); TagContext.parent().data.put("_executeNextElse", false); } else { TagContext.parent().data.put("_executeNextElse", true); } } public static void _ifError(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { if (args.get("arg") == null) { throw new TemplateExecutionException(template.template, fromLine, "Please specify the error key", new TagInternalException("Please specify the error key")); } if (Validation.hasError(args.get("arg").toString())) { body.call(); TagContext.parent().data.put("_executeNextElse", false); } else { TagContext.parent().data.put("_executeNextElse", true); } } public static void _errorClass(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { if (args.get("arg") == null) { throw new TemplateExecutionException(template.template, fromLine, "Please specify the error key", new TagInternalException("Please specify the error key")); } if (Validation.hasError(args.get("arg").toString())) { out.print("hasError"); } } public static void _error(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { if (args.get("arg") == null && args.get("key") == null) { throw new TemplateExecutionException(template.template, fromLine, "Please specify the error key", new TagInternalException("Please specify the error key")); } String key = args.get("arg") == null ? args.get("key") + "" : args.get("arg") + ""; Error error = Validation.error(key); if (error != null) { if (args.get("field") == null) { out.print(error.message()); } else { out.print(error.message(args.get("field") + "")); } } } static boolean _evaluateCondition(Object test) { if (test != null) { if (test instanceof Boolean) { return ((Boolean) test).booleanValue(); } else if (test instanceof String) { return ((String) test).length() > 0; } else if (test instanceof Number) { return ((Number) test).intValue() != 0; } else if (test instanceof Collection) { return !((Collection) test).isEmpty(); } else { return true; } } return false; } static String __safe(Template template, Object val) { if (val instanceof RawData) { return ((RawData) val).data; } if (!template.name.endsWith(".html") || TagContext.hasParentTag("verbatim")) { return val.toString(); } return HTML.htmlEscape(val.toString()); } public static void _doLayout(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { out.print("____%LAYOUT%____"); } public static void _get(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { Object name = args.get("arg"); if (name == null) { throw new TemplateExecutionException(template.template, fromLine, "Specify a variable name", new TagInternalException("Specify a variable name")); } Object value = BaseTemplate.layoutData.get().get(name); if (value != null) { out.print(value); } else { if (body != null) { out.print(JavaExtensions.toString(body)); } } } public static void _set(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { // Simple case : #{set title:'Yop' /} for (Map.Entry<?, ?> entry : args.entrySet()) { Object key = entry.getKey(); if (!key.toString().equals("arg")) { BaseTemplate.layoutData.get().put(key, (entry.getValue() != null && entry.getValue() instanceof String) ? __safe(template.template, entry.getValue()) : entry.getValue()); return; } } // Body case Object name = args.get("arg"); if (name != null && body != null) { Object oldOut = body.getProperty("out"); StringWriter sw = new StringWriter(); body.setProperty("out", new PrintWriter(sw)); body.call(); BaseTemplate.layoutData.get().put(name, sw.toString()); body.setProperty("out", oldOut); } } public static void _extends(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { try { if (!args.containsKey("arg") || args.get("arg") == null) { throw new TemplateExecutionException(template.template, fromLine, "Specify a template name", new TagInternalException("Specify a template name")); } String name = args.get("arg").toString(); if (name.startsWith("./")) { String ct = BaseTemplate.currentTemplate.get().name; if (ct.matches("^/lib/[^/]+/app/views/.*")) { ct = ct.substring(ct.indexOf("/", 5)); } ct = ct.substring(0, ct.lastIndexOf("/")); name = ct + name.substring(1); } BaseTemplate.layout.set((BaseTemplate) TemplateLoader.load(name)); } catch (TemplateNotFoundException e) { throw new TemplateNotFoundException(e.getPath(), template.template, fromLine); } } @SuppressWarnings("unchecked") public static void _include(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { try { if (!args.containsKey("arg") || args.get("arg") == null) { throw new TemplateExecutionException(template.template, fromLine, "Specify a template name", new TagInternalException("Specify a template name")); } String name = args.get("arg").toString(); if (name.startsWith("./")) { String ct = BaseTemplate.currentTemplate.get().name; if (ct.matches("^/lib/[^/]+/app/views/.*")) { ct = ct.substring(ct.indexOf("/", 5)); } ct = ct.substring(0, ct.lastIndexOf("/")); name = ct + name.substring(1); } BaseTemplate t = (BaseTemplate) TemplateLoader.load(name); Map<String, Object> newArgs = new HashMap<String, Object>(); newArgs.putAll(template.getBinding().getVariables()); newArgs.put("_isInclude", true); t.internalRender(newArgs); } catch (TemplateNotFoundException e) { throw new TemplateNotFoundException(e.getPath(), template.template, fromLine); } } @SuppressWarnings("unchecked") public static void _render(Map<?, ?> args, Closure body, PrintWriter out, ExecutableTemplate template, int fromLine) { try { if (!args.containsKey("arg") || args.get("arg") == null) { throw new TemplateExecutionException(template.template, fromLine, "Specify a template name", new TagInternalException("Specify a template name")); } String name = args.get("arg").toString(); if (name.startsWith("./")) { String ct = BaseTemplate.currentTemplate.get().name; if (ct.matches("^/lib/[^/]+/app/views/.*")) { ct = ct.substring(ct.indexOf("/", 5)); } ct = ct.substring(0, ct.lastIndexOf("/")); name = ct + name.substring(1); } args.remove("arg"); BaseTemplate t = (BaseTemplate) TemplateLoader.load(name); Map<String, Object> newArgs = new HashMap<String, Object>(); newArgs.putAll((Map<? extends String, ? extends Object>) args); newArgs.put("_isInclude", true); newArgs.put("out", out); t.internalRender(newArgs); } catch (TemplateNotFoundException e) { throw new TemplateNotFoundException(e.getPath(), template.template, fromLine); } } public static String serialize(Map<?, ?> args, String... unless) { StringBuilder attrs = new StringBuilder(); Arrays.sort(unless); for (Object o : args.keySet()) { String attr = o.toString(); String value = args.get(o) == null ? "" : args.get(o).toString(); if (Arrays.binarySearch(unless, attr) < 0 && !attr.equals("arg")) { attrs.append(attr); attrs.append("=\""); attrs.append(value); attrs.append("\" "); } } return attrs.toString(); } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public static @interface Namespace { String value() default ""; } }