All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.morpheusdata.views.HandlebarsRenderer Maven / Gradle / Ivy

package com.morpheusdata.views;


import com.github.jknack.handlebars.Handlebars;
import com.github.jknack.handlebars.Helper;
import com.github.jknack.handlebars.Template;
import com.github.jknack.handlebars.io.TemplateLoader;
import com.morpheusdata.core.MorpheusContext;
import com.morpheusdata.core.Plugin;
import com.morpheusdata.core.web.MorpheusWebRequestService;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.LinkedHashMap;

/**
 * HandlebarsRenderer implements the Renderer interface.
 * It uses a Dynamic template loader and Handlebars engine to render templates.
 */
public class HandlebarsRenderer implements Renderer {
	private final Handlebars engine;
	private DynamicTemplateLoader loader;

	public HandlebarsRenderer() {
		HandlebarsPluginTemplateLoader customLoader = new HandlebarsPluginTemplateLoader(this.getClass().getClassLoader());
		loader = new DynamicTemplateLoader(customLoader);
		engine = new Handlebars(loader);
	}

	public HandlebarsRenderer(String prefix) {
		HandlebarsPluginTemplateLoader customLoader = new HandlebarsPluginTemplateLoader(prefix, this.getClass().getClassLoader());
		loader = new DynamicTemplateLoader(customLoader);
		engine = new Handlebars(loader);
	}

	public HandlebarsRenderer(ClassLoader classLoader) {
		HandlebarsPluginTemplateLoader customLoader = new HandlebarsPluginTemplateLoader(classLoader);
		loader = new DynamicTemplateLoader(customLoader);
		engine = new Handlebars(loader);
	}

	public HandlebarsRenderer(String prefix, ClassLoader classLoader) {
		HandlebarsPluginTemplateLoader customLoader = new HandlebarsPluginTemplateLoader(prefix, classLoader);
		loader = new DynamicTemplateLoader(customLoader);
		engine = new Handlebars(loader);
	}

	/**
	 * Add a child first class loader
	 * @param classLoader plugin class loader
	 */
	public void addTemplateLoader(ClassLoader classLoader) {
		HandlebarsPluginTemplateLoader loader = new HandlebarsPluginTemplateLoader(classLoader);
		this.loader.addTemplateLoader(loader);
	}

	/**
	 * Remove a class loader
	 * @param classLoader plugin class loader
	 */
	public void removeTemplateLoader(ClassLoader classLoader) {
		for(TemplateLoader templateLoader : this.loader.getTemplateLoaders()) {
			if (templateLoader.getClass().getClassLoader() == classLoader) {
				this.loader.removeTemplateLoader(loader);
				break;
			}
		}
	}

	/**
	 * Get a list of all template loaders
	 * @return list of template loaders
	 */
	public Iterable getTemplateLoaders() {
		return this.loader.getTemplateLoaders();
	}

	public HandlebarsRenderer(Handlebars overrideEngine) {
		engine = overrideEngine;
	}

	@Override
	public HTMLResponse render(String templateText, ViewModel model) {
		Template template = null;
		try {
			template = engine.compileInline(templateText);
		} catch (Exception e) {
			handleError(e);
		}
		return applyModel(template, model);
	}

	@Override
	public HTMLResponse renderTemplate(String location, ViewModel model) {
		Template template;
		try {
			template = engine.compile(location);
		} catch (IOException e) {
			return handleError(e);
		}
		return applyModel(template, model);
	}

	@Override
	public Handlebars getEngine() {
		return engine;
	}

	/**
	 * Creates an {{asset}} helper tag that outputs the plugin specific relative url for assets stored in the /src/assets
	 * e.g. <link href="{{asset "/custom.css"}}" />
	 * @see Handlebars Helpers
	 * @param pluginName name of the plugin
	 */
	public void registerAssetHelper(String pluginName) {
		engine.registerHelper("asset", (Helper) (context, options) -> "/assets/plugin/" + pluginName.toLowerCase().replace(" ", "-") + context);
	}

	public void registerNonceHelper(MorpheusWebRequestService requestService) {
		engine.registerHelper("nonce", (Helper) (context, options) -> requestService.getNonceToken() );
	}


	public void registerI18nHelper(Plugin plugin, MorpheusContext morpheus) {
		engine.registerHelper("i18n", (Helper) (context, options) -> {
			String defaultMessage = options.hash.get("default") != null ? options.hash.get("default").toString() : null;
			return morpheus.getWebRequest().getMessage(context,null,defaultMessage,morpheus.getWebRequest().getLocale());
		});

	}
	/**
	 * Returns an error response with the exception message in the response html
	 * @param e the exception
	 * @return 400 HTTP response
	 */
	private HTMLResponse handleError(Exception e) {
		e.printStackTrace();
		HTMLResponse response  = new HTMLResponse();
		if (FileNotFoundException.class.equals(e.getClass())) {
			response.html = "Template file not found: " + e.getMessage();
		} else {
			response.html = e.getMessage();
		}
		response.status = 400;
		return response;
	}

	/**
	 * Merge the template tree using the given context if model is provided, otherwise provide the raw text
	 *
	 * @param template the view template
	 * @param model view model
	 * @return the response
	 */
	private HTMLResponse applyModel(Template template, ViewModel model) {
		HTMLResponse response = new HTMLResponse();
		try {
			if(template == null) {
				throw new Exception("No template defined.");
			} else if (model != null && model.object != null) {
				response.html = template.apply(model.object);
			} else {
				response.html = template.apply(new LinkedHashMap());
			}

			response.status = model.status;
			return response;
		} catch (Exception e) {
			return handleError(e);
		}
	}
}