Customizing users and groups

November 14, 2023

Users and groups are nodes like others on a Jahia platform, they follow definitions and can be manipulated through administration interfaces and programmatically, using APIs.

Creating users with a batch file

Jahia allows administrators to import users in batch to create local accounts (meaning you don't use an external repository of users like Active Directory or LDAP).

Creating a CSV file

Your CSV file must be structured as follow: The file must include a header line with at minimum the columns j:nodename (username) and j:password.

You can include any other supported user properties as values, in the desired order. Then, each user to be created must be declared on a different line, with associated values in the correct order.

Example:

j:nodename,j:password,j:firstName,j:lastName
steven,Sseagal666,Steven,Seagal
chucky,TexasRanger,Chuck,Norris

Importing users into Jahia

To import users, log yourself into the Jahia Administration Interface (default: http://localhost:8080/administration/) Click on 'Manage users' Click on the button "Batch create users" In the form newly displayed: Type the separator used in your CSV file (default is ',') Select the CSV file by clicking on 'browse' Click on the OK button at the bottom of the screen

Default properties available on users nodes

  • j:nodename (string) 
  • j:accountLocked (boolean)=false autocreated 
  • j:password (string) indexed=no 
  • j:external (boolean)=false autocreated 
  • j:externalSource (string) nofulltext 
  • j:firstName (string) 
  • j:lastName (string) 
  • j:gender (string,choicelist[resourceBundle]) < male,female,other 
  • j:title (string,choicelist[resourceBundle]) < mister,master,professor,doctor,miss,madam 
  • j:birthDate (date,datepicker) 
  • j:organization (string) 
  • j:function (string) 
  • j:about (string,richtext) 
  • j:email (string) < '^$|[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}' 
  • j:skypeID (string) 
  • j:twitterID (string) 
  • j:facebookID (string) 
  • j:linkedinID (string) 
  • j:picture (weakreference,picker[type='file']) < jnt:file 
  • j:publicProperties (string) multiple 
  • * (string)

User profile

When a user accesses their profile in live mode they can edit all their information directly on the page without going to Content Editor. This module (located in modules/default/) mainly use jQuery and Jeditable.

This is a good entry point to have example of how to use in-line edition for editing small properties in live mode.

This documentation shows how to make edition of text, richtext, date, file, dropdown properties.

For each of those type of data we use the same principle: a JavaScript function applied to a class that allows the edition of the content when the user clicks on it.

Simple Text

Here the JavaScript example of how to edit a simple text :

<script type="text/javascript">
$(document).ready(function() {
        $(".edit").editable(function (value, settings) {
            var submitId = $(this).attr('id').replace("_", ":");
            var data = {};
            data[submitId] = value;
            data['jcrMethodToCall'] = 'put';
            $.post("${url.base}${currentNode.path}", data, function(result) {
                $("#personDisplay").html(result.j_title + " " + result.j_firstName + " " + result.j_lastName);
            }, "json");
            return(value);
        }, {
            type    : 'text',
            onblur : 'ignore',
            submit : 'OK',
            cancel : 'Cancel',
            tooltip : 'Click to edit'
        });
    });
</script>

In this JavaScript function, we create a data map for handling our form data, we pass a 'jcrMethodToCall' parameter to Jahia to use a put method instead of a post. We get the name of the properties to update from the ID of the HTML object associated with the edit. When the post is submitted, we get the updated node as JSON data and update specific content on the page.

Now the HTML code associated:

<span class="label"><fmt:message key="jnt_user.j_email"/> : </span>
<span id="j_email" class="edit">${fields['j:email']}</span>

Rich Text

Rich text works in the same way as rich text. Only the type of the editable changes:

<script type="text/javascript">
$(document).ready(function() {
     $(".ckeditorEdit").editable(function (value, settings) {
            var submitId = $(this).attr('id').replace("_", ":");
            var data = {};
            data[submitId] = value;
            data['jcrMethodToCall'] = 'put';
            $.post("${url.base}${currentNode.path}", data, null, "json");
            return(value);
        }, {
            type : 'ckeditor',
            onblur : 'ignore',
            submit : 'OK',
            cancel : 'Cancel',
            tooltip : 'Click to edit'
        });
    });
</script>

Now the HTML for using this CKEditor inline edition:

<div class="ckeditorEdit" id="j_about">${fields['j:about']}</div>

Date Picker

For date, we will use a date picker plugin for jQuery. Here how we use it for Jahia.

<jcr:nodeProperty node="${currentNode}" name="j:birthDate" var="birthDate"/>
<c:if test="${not empty birthDate}">
    <fmt:formatDate value="${birthDate.date.time}" pattern="dd/MM/yyyy" var="editBirthDate"/>
</c:if>
<fmt:formatDate value="${now}" pattern="dd/MM/yyyy" var="editNowDate"/>
<script type="text/javascript">
$(document).ready(function() {
     $(".dateEdit").editable(function (value, settings) {
            var submitId = $(this).attr('id').replace("_", ":");
            var data = {};
            if (value.match("[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]")) {
                var split = value.split("/");
                var birth = new Date();
                birth.setFullYear(split[2], split[1], split[0]);
                var month = "";
                if (birth.getMonth() < 10) {
                    month = "0" + birth.getMonth();
                } else month = birth.getMonth();
                data[submitId] = birth.getFullYear() + '-' + month + '-' + birth.getDate() + 'T00:00:00';
                data['jcrMethodToCall'] = 'put';
                $.post("${url.base}${currentNode.path}", data, null, "json");
            }
            return(value);
        }, {
            type : 'datepicker',
            onblur : 'ignore',
            submit : 'OK',
            cancel : 'Cancel',
            tooltip : 'Click to edit',
            datepicker : {
                flat: true,
                date: '${not empty editBirthDate ? editBirthDate : editNowDate}',
                format: 'd/m/Y',
                view: 'years',
                current: '${not empty editBirthDate ? editBirthDate : editNowDate}',
                calendars: 1,
                starts: 1     }
        });
     });
</script>

In this case the internal format for date is day/month/year, when submitting we convert that to an ISO8601 for updating date in Jahia (this is the internal format of the JCR).

HTML usage example:

<jcr:nodeProperty node="${currentNode}" name="j:birthDate" var="birthDate"/>
<c:if test="${not empty birthDate}">
    <fmt:formatDate value="${birthDate.date.time}" pattern="dd, MMMM yyyy" var="displayBirthDate"/>
</c:if>
<c:if test="${empty birthDate}">
   <jsp:useBean id="now" class="java.util.Date"/>
   <fmt:formatDate value="${now}" pattern="dd, MMMM yyyy" var="displayBirthDate"/>
</c:if>
<div class="dateEdit" id="j_birthDate">${displayBirthDate}</div>

DropDown List

We will now see how to use the choicelist initializer mechanism to allow creation of custom static drop down list (as opposed to dynamic one that will load data from the server on demand).

<jcr:propertyInitializers node="${currentNode}" name="j:title" var="titleInit"/>
<script type="text/javascript">
    var titleMap = "{<c:forEach items="${titleInit}" varStatus="status" var="title"><c:if test="${status.index > 0}">,</c:if>'${title.value.string}':'${title.displayName}'</c:forEach>}";
$(document).ready(function() {
    $(".titleEdit").editable(function (value, settings) {
            var submitId = $(this).attr('id').replace("_", ":");
            var data = {};
            data[submitId] = value;
            data['jcrMethodToCall'] = 'put';
            $.post("${url.base}${currentNode.path}", data, null, "json");
            return eval("values="+titleMap)[value];
        }, {
            type    : 'select',
            data   : titleMap,
            onblur : 'ignore',
            submit : 'OK',
            cancel : 'Cancel',
            tooltip : 'Click to edit'
        });
    });
</script>

The <jcr:propertyInitializers/> tag stores the list of ChoiceListValue objects associated with the property to the specified var. We iterate over the list to create a JavaScript map that is used for initializing our data for the dropdown list.

HTML usage example:

<div class="titleEdit" id="j_title">
    <jcr:nodePropertyRenderer node="${currentNode}" name="j:title" renderer="resourceBundle"/>
</div>

Ajax Upload

Now we will see how to upload a file in live mode using AJAX requests. This is a two-phase submit, first we upload the selected file to the user files folder, then we get back the id of the file and associate it to the property we want. Here is the code responsible for this:

<script type="text/javascript">
$(document).ready(function() {
        $(".imageEdit").editable('${url.base}${currentNode.path}', {
            type : 'ajaxupload',
            onblur : 'ignore',
            submit : 'OK',
            cancel : 'Cancel',
            tooltip : 'Click to edit',
            callback : function (data, status) {
                uploadedImageCallback(data, status);
            }
        });

        function uploadedImageCallback(data, status) {
            var datas = {};
            datas['j:picture'] = data.uuids[0];
            datas['jcrMethodToCall'] = 'put';
            $.post('${url.base}${currentNode.path}', datas, function(result) {
                var input = $('<div class="itemImage itemImageRight"><img src="' + result.j_picture + '" width="60" height="60"/></div>');
                $("#portrait").html(input);
            }, "json");
        }
    });
</script>

We see that the ajaxupload editable allows you to define a callback with the data from the result of the post. We use that data (list of uuids) to associate the newly created file with our profile j:picture property. Once associated in the JSON for the j:picture property, we have the URL associated instead of the uuid of the reference.

HTML usage example:

<div class="image imageEdit" id="portrait">
    <div class="itemImage itemImageRight"><jcr:nodeProperty var="picture" node="${currentNode}" name="j:picture"/>
        <c:if test="${not empty picture}">
            <img src="${picture.node.url}" alt="${person}" width="60" height="60"/>
        </c:if>
    </div>
</div>

Accessing user properties from My Templates

In your templates, the user as a JahiaUser object is available directly under the renderContext object.

// Display username
${renderContext.user.username}
// Display user firstname
${renderContext.user.properties['j:firstName']}

Users extension

General info about users and users data

All users nodes are found under the /users folder. They are split following some customizable rules. By default, we split them into 3 subfolders to ensure scalability of the platform. Under a user node, you can store anything you want (any properties, any sub-nodes). 

All properties you add to users are not displayed in their profiles. If you want the users to manage those properties through their profiles pages, you will have to customize the profiles templates (through the Studio/JSP files). The user nodes are not meant for storing navigational history or heavy data sets requiring a huge continuous write activity. They must be reserved to store content, preferences, and properties related to a user.

To store behavioral data about users, you must implement Apache Camel log listeners like the Page Hit module (available on the Jahia Forge as a code sample) to store data in an asynchronous way in a DB/Hadoop (FS or DB) or other type of storage. See the Jahia/Apache Camel documentation. If you have a limited number of data (less than 1000 nodes) per user you want to use for personalization then you can store them under subnodes of the user.

Differences between Jahia Users / LDAP Users

From Jahia side there are not many differences between LDAP users and Jahia users, you can store your custom properties/nodes in both of them. All users of the system have their own sub node that comes from Jahia or an external system like LDAP.

The only difference is that in the case of the LDAP users, all properties do not come only from Jahia or LDAP, but are a merge of those two sources.

For more information on the Jahia LDAP Connector module, see Connecting to a LDAP server.

Customizing user dashboard

The user dashboard is defined as a wrapper for the user object jnt:user. This wrapper is editable in the Studio. By default, we use a tabular list where each tab displays a specific view of the jnt:user object. You can define a new view for the type jnt:user in your module and then add a new tab in the wrapper to render it.

For example, we will show you how the module docspace define a view for the user listing all the docspaces they can access. The path of the template is docspace/jnt_user/html/user.mydocspaces.jsp.

<template:addResources type="css" resources="docspace.css"/>
<template:addResources type="javascript" resources="jquery.min.js"/>
<div id="${currentNode.identifier}">
    <jcr:sql var="result"
             sql="select * from [jnt:folder] as file order by file.[jcr:lastModified] desc"/>
    <c:set var="currentList" value="${result.nodes}" scope="request"/>
    <c:set var="listTotalSize" value="${functions:length(result.nodes)}" scope="request"/>
    <c:choose>
        <c:when test="${empty param.pagesize}">
            <c:set var="pageSize" value="40"/>
        </c:when>
        <c:otherwise>
            <c:set var="pageSize" value="${param.pagesize}"/>
        </c:otherwise>
    </c:choose>
    <template:initPager totalSize="${listTotalSize}" pageSize="${pageSize}" id="${currentNode.identifier}"/>
    <ul class="docspacelist">
        <c:forEach items="${currentList}" var="subchild" varStatus="status" begin="${begin}" end="${end}">
            <c:if test="${jcr:hasPermission(subchild, 'jcr:write') and (not empty jcr:getParentOfType(subchild, 'jnt:page'))}">
                <li>
                    <a class="adocspace" href="${url.basePreview}${subchild.path}.html"
                       title="${subchild.name}">${functions:abbreviate(subchild.name,20,30,'...')}</a>

                    &nbsp;<span><fmt:message key="label.lastModif"/>:&nbsp;<fmt:formatDate
                        value="${subchild.properties['jcr:lastModified'].date.time}" dateStyle="short"
                        type="both"/></span>
                </li>
            </c:if>
        </c:forEach>
    </ul>
    <template:displayPagination nbItemsList="5,10,20,40,60,80,100,200"/>
    <template:removePager id="${currentNode.identifier}"/>
</div>

Users registration

Optional Module: user_registration

This module declares a component which, once inserted in a page, presents a form to users to allow self-registration into the system.

It is possible to notify relevant administrators that a new user has registered (by email). The landing page (where the user is redirected after the form is submitted) needs to be set.
By default, the following fields are displayed and marked as "mandatory" (this, of course, can be customized into the definition)

  • First Name
  • Last Name
  • Username
  • Password
  • Email

The form can be extended at will, with any additional user property.

<input type="text" name="property_name" id="property_name" value="" tabindex="xx"/>

Registering a user triggers an automatic email to the administrator of the platform, and a welcome email to the newly registered user.

On the server, the Jahia folder User registration folder/mail/templates/contains the template used for these automatic notifications. This template accepts HTML and can be customized easily.