Customizing users and groups

  Written by The Jahia Team
 
Developers
   Estimated reading time:
8.0

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.

Users batch creation

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).

Prepare 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

Import 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 edit mode engines or content manager.

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. For the name of the properties to update we get it from the ID of the HTML object associated with this edition. When the post is submitted we get the updated node as json data and we update some 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

For rich text it works the same way, only the type of the editable will change :

<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 tag <jcr:propertyInitializers/> store the list of ChoiceListValue object associated with the property to the specified var. We iterate over the list to create a JavaScript map that will be 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 we associate it to the property we want. Here the code doing that :

<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>

Access 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 will 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 for storing some navigational history or heavy data sets requiring a huge continuous write activity, they must be reserved to store contents/preferences/properties related to a user.

To store some behavioral data about users you will have to implement some 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 are not coming from Jahia or from 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 the relevant administrator(s) 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.