A Local package description

Last week I wrote the first part of a two blog post series on how to add a Forgotten Password Functionality to Nuxeo. So make sure you read the first one and here comes the second part :-)

Reset the password


The link we send to the user (see first blog post) will be manage by the enterNewPassword method. The key is part of the URL so it's easy to retrieve using JAX-RS annotations. Once I have the key, I use the SearchRegistrationByResetPassKeyUnrestricted runner to verify that it's still a valid key. If it is, I can render the submitNewPassword template. If not, I render the wrongResetKey template.

[java]
@GET
@Path("enterNewPassword/{key}")
@Produces("text/html")
public Object enterNewPassword(@PathParam("key") String key)
throws ClientException {
SearchRegistrationByResetPassKeyUnrestricted runner = new SearchRegistrationByResetPassKeyUnrestricted(
getDefaultRepositoryName(), key);
runner.runUnrestricted();

    String errorMessage = runner.getErrorMessage();
    if (errorMessage != null) {
        return getView("wrongResetKey");
    } else {
        Map<String, String> data = new HashMap<String, String>();
        return getView("submitNewPassword").arg("key", key).arg("data",
                data);
    }
}

@POST
@Path("submitNewPassword")
@Produces("text/html")
public Object submitNewPassword() throws ClientException,
        URISyntaxException {
    FormData formData = getContext().getForm();
    String passwordKey = formData.getString("PasswordKey");
    String password = formData.getString("Password");
    String passwordConfirmation = formData.getString("PasswordConfirmation");
    if (password == null || "".equals(password.trim())) {
        return redisplayFormWithErrorMessage("submitNewPassword",
                ctx.getMessage("label.registerForm.validation.password"),
                formData);
    }
    if (passwordConfirmation == null
            || "".equals(passwordConfirmation.trim())) {
        return redisplayFormWithErrorMessage(
                "submitNewPassword",
                ctx.getMessage("label.registerForm.validation.passwordconfirmation"),
                formData);
    }
    password = password.trim();
    passwordConfirmation = passwordConfirmation.trim();
    if (!password.equals(passwordConfirmation)) {
        return redisplayFormWithErrorMessage(
                "submitNewPassword",
                ctx.getMessage("label.registerForm.validation.passwordvalidation"),
                formData);
    }

    SetNewPasswordUnrestricted runner = new SetNewPasswordUnrestricted(
            getDefaultRepositoryName(), password, passwordKey);
    runner.runUnrestricted();
    Response response = runner.getResponse();
    if (response != null) {
        return response;
    }
    String errorMessage = runner.getErrorMessage();
    if (errorMessage != null) {
        return redisplayFormWithErrorMessage("submitNewPassword",
                ctx.getMessage(errorMessage), formData).arg("key",
                passwordKey);
    } else {
        return redisplayFormWithInfoMessage("submitNewPassword",
                ctx.getMessage("label.submitNewPassword.saved"), formData).arg(
                "key", passwordKey);
    }
}

[/java]

The form submitted from submitNewPassword is handled by the submitNewPassword method of my WebEngine module. As we did previously for the email, the form validation is run on the server side. I'm just ensuring that the passwords are not empty and identical. If the password is OK, we can jump to the SetNewPasswordUnrestricted runner. The goal of this runner is to check again if the key is still valid, and if true, change the password using the UserManager.

And that's it, now we have a working forgotten password module. There is, of course, still room for improvements, but I feel like I can use it. So I'm going to show you how to package it for the Nuxeo Marketplace.

Create the Marketplace package


My final package is a simple zip containing only three files.

  • /install/bundles/nuxeo-userpassword-reset-5.5.jar, that contains the code of my module.
  • /install.xml, containing the installation instructions
  • /package.xml, containing my package metadata.

Our install file will be really simple, since all we have to do is copy the jar in the nxserver/bundles folder. The following code copies the content of the folder install/bundles of my package into the bundles folder of the server. You can read more about those scripting commands in our wiki.

[xml]
<install>
<update file="${package.root}/install/bundles" todir="${env.bundles}" />
</install>
[/xml]

The package file has a lot more tags since it contains all its metadata. Most of them will be displayed in the update center. I let you read them, they're self-explanatory. And you can read all the details in our documentation.

[xml]
<package type="addon" name="nuxeo-userpassword-reset" version="5.5">
<title>Nuxeo Platform User Password Reset</title>
<description>
<p>Nuxeo Platform User Password Reset adds a forgotten password link to Nuxeo's home page. The user clicks on the link and fill in his email address. He will receive an email shortly after with a one-day valid link. This link gets him to a simple form to reset his forgotten password.</p>
</description>
<home-page>https://github.com/ldoguin/nuxeo-userpassword-reset</home-page&gt;
<vendor>Nuxeo</vendor>
<installer restart="true" />
<uninstaller restart="true" />
<hotreload-support>false</hotreload-support>
<nuxeo-validation>nuxeo_certified</nuxeo-validation>
<production-state>testing</production-state>
<supported>true</supported>
<platforms>
<platform>cap-5.5</platform>
</platforms>
<license>LGPL</license>
<license-url>http://www.gnu.org/licenses/lgpl.html</license-url&gt;
</package>
[/xml]

We can build this zip file easily using Maven and nuxeo-distribution assembly. All we have to do is zip three files so it should be easy :) This is basically a set of ant tasks. You can read the comments to understand what each task does:

[xml]
<project name="nuxeo-assembly"
default="build"
xmlns:nx="urn:nuxeo-build"
xmlns:artifact="urn:nuxeo-artifact">
<taskdef resource="org/nuxeo/build/antlib.xml" uri="urn:nuxeo-build" />
<taskdef resource="org/nuxeo/build/artifact/antlib.xml"
uri="urn:nuxeo-artifact" />

<target name="build" description="Build Nuxeo Platform User Password Reset Marketplace package">
<tstamp />
<!-- delete previous working directory -->
<delete failonerror="false"
dir="${maven.project.build.directory}/marketplace" />
<!-- create working directory -->
<mkdir dir="${maven.project.build.directory}/marketplace" />
<!-- copy install.xml and package.xml -->
<copy todir="${maven.project.build.directory}/marketplace">
<fileset dir="src/main/resources" />
<filterset>
<filter token="VERSION" value="${maven.project.version}" />
</filterset>
</copy>
<!-- copy nuxeo-userpassword-reset jar in /install/bundles folder.-->
<copy todir="${maven.project.build.directory}/marketplace/install/bundles">
<artifact:resolveFile key="org.nuxeo.ecm.platform:nuxeo-userpassword-reset::jar" />
</copy>
<!-- zip the working directory to ${maven.project.artifactId}-${maven.project.version}.zip -->
<zip destfile="${maven.project.build.directory}/${maven.project.artifactId}-${maven.project.version}.zip"
basedir="${maven.project.build.directory}/marketplace" />
<artifact:attach file="${maven.project.build.directory}/${maven.project.artifactId}-${maven.project.version}.zip"
target="${maven.project.groupId}:${maven.project.artifactId}"
type="zip" />
</target>

</project>
[/xml]

This assembly file is processed by a nuxeo-distribution-tools Maven plugin. You can find the related documentation here.

[xml]
<build>
<plugins>
<plugin>
<groupId>org.nuxeo.build</groupId>
<artifactId>nuxeo-distribution-tools</artifactId>
<configuration>
<buildFiles>
<buildFile>${basedir}/src/main/assemble/assembly.xml</buildFile>
</buildFiles>
</configuration>
</plugin>
</plugins>
</build>
[/xml]

So if you type mvn install, you will have your Marketplace package in the target folder. As you can see, creating a simple marketplace package doesn't require too much efforts :-)

I've added the source code on Github. Let me know if you have any questions through the comments or on answers.nuxeo.com. See ya' on Friday!