package com.revolsys.ui.web.config; import java.net.URI; import java.security.Principal; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.security.access.expression.ExpressionUtils; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.util.UriTemplate; import com.revolsys.spring.StringTemplate; import com.revolsys.spring.security.SpringExpressionUtil; import com.revolsys.ui.web.exception.PageNotFoundException; import com.revolsys.ui.web.utils.HttpServletUtils; import com.revolsys.util.CaseConverter; import com.revolsys.util.JexlUtil; import com.revolsys.util.UrlUtil; public class Page extends Component { private static final Logger LOG = Logger.getLogger(Page.class); private final List<Argument> arguments = new ArrayList<>(); private final Map<String, Argument> argumentsMap = new HashMap<>(); private final List<Attribute> attributes = new ArrayList<>(); private final Map<String, Attribute> attributesMap = new HashMap<>(); private Layout layout; private long menuId; private final Map<String, Menu> menus = new HashMap<>(); private final Map<String, Page> pages = new HashMap<>(); private Page parent; private String path = ""; private Map<String, Page> pathMap = new HashMap<>(); private String permission; private Expression permissionExpression; private final Map<String, String> properties = new HashMap<>(); private boolean secure; private String title = ""; private org.apache.commons.jexl.Expression titleExpression; private StringTemplate titleTemplate; private UriTemplate uriTemplate; public Page() { } public Page(final Page page) { super(page); this.menuId = page.menuId; setPath(page.path); setTitle(page.title); this.properties.putAll(page.properties); this.arguments.addAll(page.arguments); this.argumentsMap.putAll(page.argumentsMap); this.attributes.addAll(page.attributes); this.attributesMap.putAll(page.attributesMap); } public Page(final String name, final String path) { super(name); setPath(path); } public Page(final String name, final String title, final String path) { this(name, path); setTitle(title); } public Page(final String name, final String title, final String path, final boolean secure) { this(name, title, path); this.secure = secure; } public void addArgument(final Argument argument) { if (!hasArgument(argument.getName())) { this.arguments.add(argument); this.argumentsMap.put(argument.getName(), argument); } if (argument.isInheritable()) { for (final Page page : this.pages.values()) { page.addArgument(argument); } } } public void addField(final Attribute attribute) { if (!hasArgument(attribute.getName())) { this.attributes.add(attribute); this.attributesMap.put(attribute.getName(), attribute); } if (attribute.isInheritable()) { for (final Page page : this.pages.values()) { page.addField(attribute); } } } public void addMenu(final Menu menu) { this.menus.put(menu.getName(), menu); } public void addPage(final Page page) { this.pages.put(page.getName(), page); this.pathMap.put(page.getPath(), page); page.setParent(this); for (final Entry<String, Page> entry : page.getPathMap().entrySet()) { final String path = page.getPath() + entry.getKey(); final Page childPage = entry.getValue(); this.pathMap.put(path, childPage); } for (final Argument argument : this.arguments) { if (argument.isInheritable()) { page.addArgument(argument); } } for (final Attribute attribute : this.attributes) { if (attribute.isInheritable()) { page.addField(attribute); } } } public void addProperty(final String name, final String value) { this.properties.put(name, value); } public boolean canAccess(final Map<String, ? extends Object> parameters) { if (this.permissionExpression == null) { return true; } else { try { final EvaluationContext securityEvaluationContext = SpringExpressionUtil .newSecurityEvaluationContext(); for (final Entry<String, ? extends Object> entry : parameters.entrySet()) { final String name = entry.getKey(); final Object value = entry.getValue(); securityEvaluationContext.setVariable(name, value); } return ExpressionUtils.evaluateAsBoolean(this.permissionExpression, securityEvaluationContext); } catch (final Throwable t) { LOG.error("Unable to evaluate " + this.permission, t); return false; } } } @Override public Object clone() { return new Page(this); } @Override public boolean equals(final Object o) { if (o instanceof Page) { final Page p = (Page)o; if (super.equals(o) && p.menuId == this.menuId && p.path.equals(this.path) && (p.title == this.title || p.title != null && this.title != null && p.title.equals(this.title)) && p.properties.equals(this.properties)) { return true; } } return false; } public String getAbsolutePath() { if (this.parent != null) { return this.parent.getAbsolutePath() + this.path; } else { final WebUiContext uiContext = WebUiContext.get(); if (uiContext != null) { final Config config = uiContext.getConfig(); if (config != null) { final String basePath = config.getBasePath(); if (basePath != null) { return basePath + this.path; } } } } return this.path; } public List<Argument> getArguments() { return this.arguments; } public String getExpandedTitle() { final Map<String, Object> parameters = Collections.<String, Object> emptyMap(); return getTitle(parameters); } public List<Attribute> getFields() { return this.attributes; } public String getFullPath() { if (this.secure) { return getAbsolutePath() + ".wps"; } else { return getAbsolutePath(); } } public String getFullUrl() { final Map<String, Object> parameters = Collections.emptyMap(); return getFullUrl(parameters); } public String getFullUrl(final Map<String, ? extends Object> parameters) { final Map<String, Object> uriParameters = new HashMap<>(parameters); final HttpServletRequest request = HttpServletUtils.getRequest(); if (request != null) { for (final Argument argument : this.arguments) { final String name = argument.getName(); if (!uriParameters.containsKey(name)) { final String value = request.getParameter(name); if (value != null) { uriParameters.put(name, value); } } } } if (canAccess(uriParameters)) { try { final Map<String, Object> uriTemplateVariables = getUriTemplateVariables(uriParameters); final SecurityContext securityContext = SecurityContextHolder.getContext(); final Authentication authentication = securityContext.getAuthentication(); if (authentication != null) { uriTemplateVariables.put("remoteUser", authentication.getName()); } else if (request != null) { final Principal userPrincipal = request.getUserPrincipal(); if (userPrincipal != null) { uriTemplateVariables.put("remoteUser", userPrincipal.getName()); } } final URI path = this.uriTemplate.expand(uriTemplateVariables); final String url = UrlUtil.getUrl(path, uriParameters); return HttpServletUtils.getFullUrl(url); } catch (final IllegalArgumentException e) { LOG.debug("Unable to expand variables for " + this.uriTemplate, e); return null; } } else { return null; } } public Layout getLayout() { return this.layout; } public Menu getMenu(final String name) { return this.menus.get(name); } public long getMenuId() { return this.menuId; } public Page getPage(final String name) { if (name == null) { return null; } try { if (name.startsWith("/")) { return WebUiContext.get().getConfig().getPage(name); } Page page = this.pages.get(name); if (page == null) { final Page parent = getParent(); if (parent != null) { page = parent.getPage(name); } else { page = WebUiContext.get().getConfig().getPage("/" + name); } } return page; } catch (final PageNotFoundException e) { return null; } } public Page getParent() { return this.parent; } public String getPath() { return this.path; } public Map<String, Page> getPathMap() { return this.pathMap; } public String getPermission() { return this.permission; } public Expression getPermissionExpression() { return this.permissionExpression; } public String getProperty(final String name) { return this.properties.get(name); } public String getTitle() { if (this.titleExpression != null) { final WebUiContext context = WebUiContext.get(); return (String)context.evaluateExpression(this.titleExpression); } else { return this.title; } } public String getTitle(final Map<String, Object> parameters) { if (this.titleTemplate == null) { return this.title; } else { final Map<String, Object> uriTemplateVariables = getUriTemplateVariables(parameters); return this.titleTemplate.expand(uriTemplateVariables); } } public Map<String, Object> getUriTemplateVariables(final Map<String, Object> parameters) { final HttpServletRequest request = HttpServletUtils.getRequest(); final Map<String, String> pathVariables = HttpServletUtils.getPathVariables(); final Map<String, Object> uriTemplateVariables = new HashMap<>(); for (final String name : this.uriTemplate.getVariableNames()) { Object value = parameters.remove(name); if (value == null) { if (pathVariables != null) { value = pathVariables.get(name); } if (value == null) { value = request.getParameter(name); } } if (value != null) { uriTemplateVariables.put(name, value); } } return uriTemplateVariables; } public boolean hasArgument(final String name) { return this.argumentsMap.containsKey(name); } public boolean hasAttribute(final String name) { return this.attributesMap.containsKey(name); } /** * Generate the hash code for the object. * * @return The hashCode. */ @Override public int hashCode() { return super.hashCode() + (this.path.hashCode() << 2); } public final boolean isSecure() { return this.secure; } public void setLayout(final Layout layout) { this.layout = layout; layout.setPage(this); } public void setMenuId(final long menuId) { this.menuId = menuId; } public void setParent(final Page parent) { this.parent = parent; if (parent.isSecure()) { this.secure = true; } } public void setPath(final String path) { if (path != null) { this.path = path; } else { this.path = "/" + CaseConverter.toLowerCamelCase(getName()); } this.uriTemplate = new UriTemplate(this.path); } public void setPathMap(final Map<String, Page> pathMap) { this.pathMap = pathMap; } public void setPermission(final String permission) { this.permission = permission; if (com.revolsys.util.Property.hasValue(permission)) { final SpelExpressionParser parser = new SpelExpressionParser(); this.permissionExpression = parser.parseExpression(permission); } else { this.permissionExpression = null; } } public final void setSecure(final boolean secure) { this.secure = secure; } public void setTitle(final String title) { if (title != null) { this.title = title; this.titleTemplate = new StringTemplate(title); if (this.titleTemplate.getVariableNames().isEmpty()) { this.titleTemplate = null; } try { this.titleExpression = JexlUtil.newExpression(title); } catch (final Exception e) { LOG.error(e.getMessage(), e); } } else { this.title = CaseConverter.toCapitalizedWords(getName()); } } @Override public String toString() { return getName(); } }