WebEngine (JAX-RS)

Managing Links

Updated: July 31, 2019

Module definition

module.xml

<?xml version="1.0"?>
<module name="sample6" root-type="sample6" path="/sample6" extends="base">
  <nature>groovy</nature>

  <links>
  <!-- link IDs are normally used as the keys of i18n messages - but in this example we are displaying them directly without using i18n mechanism-->
  <!-- link to a info view available for all Documents (i.e. WebObjects that are derived from Document type) -->
  <link id="Info" path="/@views/info">
    <category>TOOLS</category>
    <category>INFO</category>
    <type>Document</type>
  </link>

  <!-- Link enabled only for folderish documents -->
  <link id="Children" path="/@views/children">
    <facet>Folderish</facet>
    <type>Document</type>
    <category>TOOLS</category>
  </link>

  <!-- this is only demonstrating link conditions - this is not a real link...
       This link will be enabled only for WebObject derived from Workspace
  -->
  <link id="I am a workspace" path="">
    <type>Workspace</type>
    <category>TOOLS</category>
  </link>
  </links>

</module>

JAX-RS resources

Sample6.groovy

import javax.ws.rs.*;
import javax.ws.rs.core.*;
import org.nuxeo.ecm.core.rest.*;
import org.nuxeo.ecm.webengine.model.impl.*;
import org.nuxeo.ecm.webengine.model.*;
import org.nuxeo.ecm.webengine.model.exceptions.*;

/**
 * Managing links.
 *
<p>
 * Almost any template page will contain links to other pages in your application.
 * These links are usually absolute paths to other WebObjects or WebAdapters (including parameters if any).
 * Maintaining these links when application object changes is painful when you are using modular applications
 * (that may contribute new views or templates).
 *
<p>
 * WebEngine is providing a flexible way to ease link management.
 * First, you should define all of your links in <i>module.xml</i> configuration file.
 * A Link is described by a target URL, an enablement condition, and one or more categories that can be used to organize links.
 *
<ul>
 * Here are the possible conditions that you can use on links:
 *
<li> type - represent the target Web Object type. If present the link will be enabled only in the context of such an object.
 *
<li> adapter - represent the target Web Adapter name. If present the link will be enabled only if the active adapter is the same as this one.
 *
<li> facet - a set of facets that the target web object must have in order to enable the link.
 *
<li> guard - a guard to be tested in order to enable the link. This is using the guard mechanism of WebEngine.
 * </ul>
 * If several conditions are specified an <code>AND</code> will be used between them.
 *
<p>
 * Apart conditions you can <i>group</i> links in categories.
 * Using categories and conditions you can quickly find in a template which are all enabled links that are part of a category.
 * This way, you can control which links are written in the template without needing to do conditional code to check the context if links are enabled.
 *
<p>
 * Conditions and categories manage thus where and when your links are displayed in a page. Apart this you also want to have a target URL for each link.
 *
<ul>
 * You have two choices in specifying such a target URL:
 *
<li> define a custom link handler using the <code>handler</handler> link attribute.
 * The handler will be invoked each time the link code need to be written in the output stream so that it can programatically generate the link code.
 *
<li> use the builtin link handler. The builtin link handler will append the <code>path</code> attribute you specified in link definition
 * to the current WebObject path on the request. This behavior is good enough for most of the use cases.
 *
<li>
 * </ul>
 *
<p>
 *
<p>
 * This example will demonstrate how links work. Look into <code>module.xml</code> for link definitions
 * and then in <code>skin/views/Document/index.ftl</code> on how they are used in the template.
 *
 * @author <a href="mailto:[email protected]">Bogdan Stefanescu</a>
 */
@WebObject(type="sample6")
@Produces(["text/html"])
public class Sample6 extends ModuleRoot {

  @GET
  public Object doGet() {
    return getView("index");
  }

  /**
   * Get a repository view rooted under "/default-domain".
   */
  @Path("repository")
  public Object getRepositoryView() {
    return DocumentFactory.newDocumentRoot(ctx, "/default-domain");
  }

  /**
   * Example on how to handle errors
   */
  public Response handleError(WebApplicationException e) {
    if (e instanceof WebSecurityException) {
      // display a login page
      return Response.status(401).entity(getTemplate("error/error_401.ftl")).build();
    } else if (e instanceof WebResourceNotFoundException) {
      return Response.status(404).entity(getTemplate("error/error_404.ftl")).build();
    } else {
      // not interested in that exception - use default handling
      return super.handleError(e);
    }
  }

}

Object views

skin/base.ftl

<html>
  <head>
    <title>Sample 6: Working with links</title>
  </head>
  <body>
    <@block name="content"/>
  </body>
</html>

skin/views/sample6/index.ftl

<@extends src="base.ftl">

  <@block name="content">
    Browse Repository: <a href="${This.path}/repository">repository</a>
  </@block>

</@extends>

skin/views/Document/index.ftl

<@extends src="base.ftl">

<@block name="content">

<h2>${Document.title}</h2>

<table width="100%" border="1">

<tr>

<td>

<div>Document ID: ${Document.id}

<div>Document path: ${Document.path}

<div>Document name: ${Document.name}

<div>Document type: ${Document.type}

<hr>
    <#if Document.isFolder>

<div>
    Document children:

<ul>
    <#list Document.children as doc>

<li> <a href="${This.path}/${doc.name}">${doc.name}</a> </li>
    </#list>
    </ul>
    </div>
    </#if>
    </td>

<td>
      <#-- display here the links available in the current context in category INFO -->

<ul>
      <b>Tools</b>
      <#list This.getLinks("INFO") as link>

<li> <a href="${link.getCode(This)}">${link.id}</a> </li>
      </#list>
      </ul>
      <#-- display here the links available in the current context in category TOOLS -->

<ul>
      <b>Adminitsration</b>
      <#list This.getLinks("TOOLS") as link>

<li> <a href="${link.getCode(This)}">${link.id}</a> </li>
      </#list>
      </ul>
    </td>
  </tr>
</table>

</@block>
</@extends>

skin/views/Document/children.ftl

<@extends src="base.ftl">

<@block name="content">
  <#if Document.isFolder>

<div>
    Document children:

<ul>
    <#list Document.children as doc>

<li> <a href="${This.path}/${doc.name}">${doc.name}</a> </li>
    </#list>
    </ul>
    </div>
  </#if>
</@block>

</@extends>

skin/views/Document/info.ftl

<@extends src="base.ftl">

<@block name="content">

<h2>More info on document ${Document.title}</h2>

<h3>Last modified: ${Document["dc:modified"]}</h3>

<div>
    Document schemas:

<ul>
    <#list Document.schemas as schema>

<li> ${schema} </li>
    </#list>
    </ul>
  </div>

<div>
    Document facets:

<ul>
    <#list Document.facets as facet>

<li> ${facet} </li>
    </#list>
    </ul>
  </div>

</@block>

</@extends>

Templates

skin/error/error_401.ftl

<@extends src="base.ftl">
<@block name="header">
<h1><a href="${appPath}">Error</a></h1></@block>
<@block name="content">

<h1>401 - Unauthorized</h1>

<p>
You don't have privileges to access this page
</p>

<p>
<br/>
</p>
<#include "error/login.ftl">

</@block>
</@extends>

skin/error/error_404.ftl

<@extends src="base.ftl">
<@block name="header">
<h1><a href="${appPath}">Error</a></h1></@block>
<@block name="content">

<h1>404 - Resource Not Found</h1>

The page you requested doesn't exists

</@block>
</@extends>

skin/error/login.ftl

<!-- Login Form -->
<form action="${Context.loginPath}" method="POST">

<table cellpadding="4" cellspacing="1">

<tr>

<td>Username:</td>

<td><input name="username" type="text"></td>
  </tr>

<tr>

<td>Password:</td>

<td><input name="password" type="password"></td>
  </tr>

<tr align="right">

<td colspan="2">
      <input type="submit" value="Sign In"/>
    </td>
  </tr>
  <#if Context.getProperty("failed") == "true">

<tr align="center">

<td colspan="2"><font color="red">Authentication Failed!</font></td>
  </tr>
  </#if>
</table>
</form>

We'd love to hear your thoughts!

All fields required