Here's a fun project to help you learn more about working with the Nuxeo Platform — display a random background image on the login page. And of course we'll be picking up the image into our Nuxeo instance. Part of the challenge is to access this image when there is no user session :) This an opportunity to learn more about OpenURL and UnrestrictedSessionRunner. Here's how we're going to do this.

Create a WebEngine Module


What we need is to return an image for a simple URL. A WebEngine module is the simplest and quickest solution to achieve that. So using Nuxeo IDE, I've created a new WebEngine module called RandomLoginPage. It doesn't do much for now. If you deploy it on your server and go to yourserver/nuxeo/site/RandomLoginPage/, you should be redirected to the login page. Once you login, you see a simple web page explaining hte module was created with Nuxeo IDE. Before searching for a random image, let's remove the authentication need for this specific URL.

Nuxeo OpenURL Extension Point


The Nuxeo OpenURl extension point is used to define URLs that can be accessed without authentication. Which is exactly what we need!

URLs are declared using a pattern. You can insert properties variables like the context path declared in nuxeo.conf. You can also use regex to avoid repeating yourself like this:

 <extension
target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService"
point="openUrl">
<openUrl name="userReg">
<grantPattern>/nuxeo/site/userRegistration/validate/.*</grantPattern>
</openUrl>
</extension>


Here all the URLs after /nuxeo/site/userRegistration/validate/ will be accessible without authentication.

This is my contribution for my WebEngine module:

 <!-- Following URLs don't need user authentication -->
<extension
target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService"
point="openUrl">
<openUrl name="RandomLoginScreen">
<grantPattern>${org.nuxeo.ecm.contextPath}/site/RandomLoginPage/</grantPattern>
</openUrl>
</extension>


Now you should be able to access your page yourserver/nuxeo/site/RandomLoginPage/ without having to login. We can start searching for an image.

Finding Picture Assets


To retrieve a document, we need a CoreSession object. It's the link to the content repository. When using WebEngine, you have access to it from the context object. Here's how I would do it:

package org.nuxeo.sample;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.nuxeo.ecm.core.api.ClientException;
import org.nuxeo.ecm.core.api.CoreSession;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.DocumentModelList;
import org.nuxeo.ecm.core.api.blobholder.BlobHolder;
import org.nuxeo.ecm.webengine.model.WebObject;
import org.nuxeo.ecm.webengine.model.impl.ModuleRoot;

@Path("/RandomLoginPage")
// the returned object will be an image, and yes I only have PNG images :D
@Produces("image/png")
@WebObject(type="RandomLoginPage")
public class RandomLoginPage extends ModuleRoot {

public static final Log log = LogFactory.getLog(RandomLoginPage.class);

@GET
public Object doGet() throws ClientException {
// retrieve the coreSession from the WebEngine context
CoreSession coreSession = ctx.getCoreSession();
// Query all the Picture documents
DocumentModelList pictures = coreSession.query("Select From Picture");
// Generate a random number
int size = pictures.size();
if (pictures.isEmpty()) {
return Response.status(404).build();
}
int nbPicture = (int) Math.floor(Math.random()
size);
// Retrieve the blob from the picture document with the BlobHolder Adapter
DocumentModel pictureDoc = pictures.get(nbPicture);
BlobHolder bh = pictureDoc.getAdapter(BlobHolder.class);
return bh.getBlob();
}

}


But the thing is, this won't work :) The CoreSession object will be null because we don't have any user logged in. And no user means no CoreSession.

Get a CoreSession when you don't have a user


Usually when you search for documents in Nuxeo, you use the logged in user's CoreSession. When you're writing code in a Seam bean, in an operation or a listener, there is always an easy way to get your hands on the user's CoreSession. Here we still have no user. So the trick is to use the UnrestrictedSessiontRunner. This class abstracts everything you need to get a CoreSession. So let's create a new class, FindRandomLoginScreenUnrestricted, that extends the UnrestrictedSessionRunner class. You'll have to implement the run method as follow:

package org.nuxeo.sample;

import org.nuxeo.ecm.core.api.Blob;
import org.nuxeo.ecm.core.api.ClientException;
import org.nuxeo.ecm.core.api.DocumentModel;
import org.nuxeo.ecm.core.api.DocumentModelList;
import org.nuxeo.ecm.core.api.UnrestrictedSessionRunner;
import org.nuxeo.ecm.core.api.blobholder.BlobHolder;

public class FindRandomLoginScreenUnrestricted extends
UnrestrictedSessionRunner {

protected FindRandomLoginScreenUnrestricted(String repositoryName) {
super(repositoryName);
}

private Blob b;

@Override
public void run() throws ClientException {
DocumentModelList pictures = session.query("Select From Picture");
// Generate a random number
int size = pictures.size();
if (pictures.isEmpty()) {
return;
}
int nbPicture = (int) Math.floor(Math.random()
size);
// Retrieve the blob from the picture document with the BlobHolder Adapter
DocumentModel pictureDoc = pictures.get(nbPicture);
BlobHolder bh = pictureDoc.getAdapter(BlobHolder.class);
b = bh.getBlob();
}

public Blob getBlob() {
return b;
}
}


To be instantiated, a runner needs a repository name or an existing CoreSession. Here's how to retrieve the default repository name:

private static String defaultRepositoryName;

@GET
public Object doGet() throws ClientException {
FindRandomLoginScreenUnrestricted runner = new FindRandomLoginScreenUnrestricted(
getDefaultRepositoryName());
runner.runUnrestricted();
Blob b = runner.getBlob();
if (b == null) {
return Response.status(404).build();
}
return b;
}

private String getDefaultRepositoryName() {
if (defaultRepositoryName == null) {
try {
defaultRepositoryName = Framework.getService(
RepositoryManager.class).getDefaultRepository().getName();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
return defaultRepositoryName;
}


Now if you go to yourserver/nuxeo/site/RandomLoginPage/, You should have your image. The next step is to display it on the login screen.

The loginScreen Extension Point


Our next goal is to modify the login screen do display our image instead of the default one. We could overwrite the login.jsp page and hard code our new URL. But we won't do that, because that would be BAD. You need to avoid as much as possible to overwrite templates in Nuxeo. Extension points are made to help us. In our case, we need to use the loginScreen extension point. It lets you define custom styles for the login box, the footer and header, a new logo, the newsBox content, bodyBackgroundStyle style, OpenId providers and much more... Right now we just need the bodyBackgroundStyle option:

<extension
target="org.nuxeo.ecm.platform.ui.web.auth.service.PluggableAuthenticationService"
point="loginScreen">
<loginScreenConfig>
<bodyBackgroundStyle>url("${org.nuxeo.ecm.contextPath}/site/RandomLoginPage/") repeat scroll bottom left #ffffff;</bodyBackgroundStyle>
</loginScreenConfig>
</extension>

Now you should see a random image taken from the content repository each time you load the login page :)


randomloginScreen You may not know it, but this is an image chosen randomly from my Nuxeo instance :)