6.3.4. Overriding user profile design

Warning

You are looking at documentation for an older release. Not what you want? See the current release documentation.

eXo Platform provides you with an extensible user profile design. With this extensibility, you can override portlets on the user profile page with your own templates.

To do this, there are 2 ways as follows:

This guide will walk you through both by overriding Profile Portlet, Experience Profile Portlet, Connections User Portlet and Recent Activities Portlet on the user profile page. You can download the source code used in this guide here.

Overriding user profile

  1. Create a webapp user-profile-extension.war as follows:

    • The user folder contains your new profile portlet templates.

  2. Add these configurations to web.xml:

    
    <?xml version="1.0" encoding="ISO-8859-1" ?>
    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">
    <web-app>
        <display-name>user-profile-extension</display-name>
        <!-- Resource filter to cache merged javascript and css -->
        <filter>
            <filter-name>ResourceRequestFilter</filter-name>
            <filter-class>org.exoplatform.portal.application.ResourceRequestFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>ResourceRequestFilter</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
        <!-- Listener -->
        <listener>
            <listener-class>org.exoplatform.container.web.PortalContainerConfigOwner</listener-class>
        </listener>
    </web-app>
  3. Override UIBasicProfilePortlet.gtmpl with the following code:

    <%
    import org.exoplatform.social.user.portlet.UserProfileHelper;
    import org.exoplatform.social.webui.Utils;
    
    //Retrieve the basic information of the user
    def profile = uicomponent.getProfileInfo();
    def keys = profile.keySet();
    %>
    
    <!-- showing and hiding control buttons -->
    <button onclick="showFullContact()" id="btn_show_contact">Show full contact information</button>
    <button onclick="hideFullContact()" id="btn_hide_contact" style="display: none">Hide full contact information</button>
    
    <!-- javascript to show and hide full contact information -->
    <script type="text/javascript">
    function showFullContact(){
    	document.getElementById("$uicomponent.id").style.display = "block";
    	document.getElementById("btn_show_contact").style.display = "none";
    	document.getElementById("btn_hide_contact").style.display = "block";
    }
    function hideFullContact(){
    	document.getElementById("$uicomponent.id").style.display = "none";
    	document.getElementById("btn_show_contact").style.display = "block";
    	document.getElementById("btn_hide_contact").style.display = "none";
    }
    </script>
    
    <div class="uiSocApplication uiBasicProfilePortlet" id="$uicomponent.id" style="display: none">
      <h4 class="head-container"><%=_ctx.appRes("UIBasicProfile.label.ContactInformation")%></h4>
      <div class="uiBasicInfoSection">
      
        <%
    	//Loop through to print out all information
          for(key in keys) {
            def values = profile.get(key);
            String clzz = key.substring(0, 1).toUpperCase() + key.substring(1);
            System.out.println(key);
    		
    		//If the user is not the owner, do not print out email
            if (!Utils.isOwner() && key.toString().equals("email")) continue;
        %>
        <div class="group-user-info">
          <div class="label-user-info"><strong><%=_ctx.appRes("UIBasicProfile.label." + key)%>:</strong></div>
          <div class="value-user-info">
          <%
            if(UserProfileHelper.isString(values)) {
          %>
            <div class="ui<%=clzz%> ellipsis" rel="tooltip" data-placement="top" title="" data-original-title="<%=values%>"><%=values%></div>
          <%} else  {
              for(subKey in values.keySet()) {
                def isIms = UserProfileHelper.isIMs(key);
                def typeIconClzz = "";
                if (isIms) {
                  typeIconClzz = UserProfileHelper.getIconCss(subKey); 
                }
                
                def listVal = values.get(subKey); 
                int valueNum = 0;
                if (UserProfileHelper.isURL(key)) {
                  for (url in listVal) { %>
                  <div class="ui<%=clzz%> ellipsis"><a href="<%=UserProfileHelper.toAbsoluteURL(url)%>" target="_blank"
                       rel="tooltip" data-placement="top" title="" data-original-title="<%=url%>"><%=url%></a></div>
                <%}
                } else {
                  if (typeIconClzz.length() > 0) {
                    typeIconClzz = typeIconClzz + " uiIconSocLightGray";
                  }
                  for (val in listVal) { 
                  %>
                  <div class="listContent">
                  <%
                    if (valueNum == 0) {
                  %>
                     <%if(isIms) {%>
                    <div><i class="<%=typeIconClzz%>"></i>&nbsp;&nbsp;<%=_ctx.appRes("UIBasicProfile.label." + subKey)%>:&nbsp;</div>
                     <%} else { %>
                    <div><%=_ctx.appRes("UIBasicProfile.label." + subKey)%>:&nbsp;</div>  
                     <%}%>
                  <%} else { %>
                    <div></div>
                  <%}
                    valueNum++;
                  %>
                    <div class="ellipsis" rel="tooltip" data-placement="top" title="" data-original-title="<%=val%>"><%=val%></div>
                  </div>
                  <% 
                  }
                }
              }
            }
          %>
          </div>
        </div>
        <%}%>
        <div class="line-bottom"><span></span></div>
      </div>
    </div>

    This template overrides the Profile Portlet by adding:

    • btn_show_contact: a button to show the portlet's content.

    • btn_hide_contact: a button to hide the portlet's content.

    • showFullContact(): a Javascript function to handle when user clicks on the btn_show_contact button.

    • hideFullContact(): a Javascript function to handle when user clicks on the btn_hide_contact button.

    • the code:

      if (!Utils.isOwner() && key.toString().equals("email")) continue;

      to check if the viewer is not the profile page owner, then the email information will not be displayed.

  4. Override UIMiniConnectionsPortlet.gtmpl as follows:

    <%
      import org.exoplatform.social.core.service.LinkProvider;
      import org.exoplatform.portal.webui.util.Util;
      import org.exoplatform.social.webui.Utils;
      import org.exoplatform.social.user.portlet.UserProfileHelper;
      
      //Load current connections of the user
      List profiles = uicomponent.loadPeoples();
      int size = uicomponent.getAllSize();
      uicomponent.initProfilePopup();
    %>
    
    <!-- showing and hiding control buttons -->
    <button onclick="showConnection()" id="btn_show_connection">Show connections</button>
    <button onclick="hideConnection()" id="btn_hide_connection" style="display: none">Hide connections</button>
    
    <!-- javascript to show and hide user's connections -->
    <script type="text/javascript">
    function showConnection(){
    	document.getElementById("$uicomponent.id").style.display = "block";
    	document.getElementById("btn_show_connection").style.display = "none";
    	document.getElementById("btn_hide_connection").style.display = "block";
    }
    function hideConnection(){
    	document.getElementById("$uicomponent.id").style.display = "none";
    	document.getElementById("btn_show_connection").style.display = "block";
    	document.getElementById("btn_hide_connection").style.display = "none";
    }
    </script>
    
    <div class="uiSocApplication uiMiniConnectionsPortlet" id="$uicomponent.id" style="display: none">
      <h4 class="head-container"><%=_ctx.appRes("UIBasicProfile.label.Connections")%></h4>
      <% if(size > 0) { %>
      
    	<!-- if having connections, loop through to print out -->
      	<div class="borderContainer" id="borderMiniConnectionsPortlet">
        <% for(profile in profiles) { %>
             <a href="<%=profile.getProfileURL()%>" class="avatarXSmall">
               <img alt="<%=profile.getDisplayName()%>" src="<%=profile.getAvatarURL()%>">         
             </a>
        <% } %>
    	
    		<!-- Provide View all connections feature -->
           <div class="viewAllConnection"><a href="<%=LinkProvider.getBaseUri(null, null)%>/connections/network/<%=uicomponent.getCurrentRemoteId()%>"><%=_ctx.appRes("UIBasicProfile.label.ViewAll")%>&nbsp;(<%=size%>)</a></div>
         </div>
      <% } else {
      
    	  //if no connection and the user is the owner, provide Find new connection feature
    	  //if the user is not the owner, just print out the message
          String keyNoConnection = Utils.isOwner() ? "YouHaveNotConnections" : "UserHaveNotConnections";
          String noConnectionCSS = Utils.isOwner() ? "noConnection" : "";
      %>
          <div class="borderContainer $noConnectionCSS center">
            <%=_ctx.appRes("UIBasicProfile.info." + keyNoConnection)%>
            <%if (Utils.isOwner()) { %>
            <div class="findConnection"><a href="<%=LinkProvider.getBaseUri(null, null)%>/connections/all-people/"><%=_ctx.appRes("UIBasicProfile.label.FindConnections")%></a></div>
            <%} %>
          </div>
      <% } %>
    </div>

    This template overrides the Connections User Portlet by adding:

    • btn_show_connection: a button to show the portlet's content.

    • btn_hide_connection: a button to hide the portlet's content.

    • showConnection(): a Javascript function to handle when user clicks on the btn_show_connection button.

    • hideConnection(): a Javascript function to handle when user clicks on the btn_hide_connection button.

  5. Override UIExperienceProfilePortlet.gtmpl with:

    <%
      import org.exoplatform.social.core.service.LinkProvider;
      
      //Retrieve the user's information and check whether the user is the owner or not
      String aboutMe = uicomponent.getAboutMe();
      boolean isOwner = uicomponent.isOwner();
      List experienceData = uicomponent.getExperience();
      def uiSocApplicationClzz = !isOwner && (experienceData.size() == 0) ? "" : "uiSocApplication";
    %>
    <div class="<%=uiSocApplicationClzz%> uiExperienceProfilePortlet" id="$uicomponent.id">
    <%
    	//if the About me information of the user is not empty, print out
    	if(aboutMe.length() > 0) { %>
    	  <h4 class="head-container"><%=_ctx.appRes("UIBasicProfile.label.AboutMe")%></h4>
    	  
    	  <!-- Add more description here -->
    	  <p>Quick description of the user</p>
    	  <div class="simpleBox aboutMe"><%=aboutMe%></div>
    	
    	<!-- if empty and the user is the owner, print out a message and provide Edit profile feature -->
    	<% } else if(isOwner) { %>
    	  <div class="no-content center">
    		<div><%=_ctx.appRes("UIBasicProfile.info.HaveNotAbout")%></div>
    		<button class="btn btn-primary" onclick="window.location.href=window.location.origin + '<%=LinkProvider.getBaseUri(null, null)%>/edit-profile/'">
    		  <i class="uiIconEdit uiIconLightGray"></i> <%=_ctx.appRes("UIBasicProfile.action.EditProfile")%></button>
    	  </div>
    	<% } 
    	
    	//if having experience information, loop through to print out
        if(experienceData.size() > 0) {
          print("<h4 class=\"head-container\">" + _ctx.appRes("UIBasicProfile.label.Experience") + "</h4>");
          print("<div class=\"simpleBox\"> ");
          for(experience in experienceData) {
            print("<div class=\"experience-container\"> ");
            String utilNow = experience.get(uicomponent.EXPERIENCES_IS_CURRENT);
            for(key in experience.keySet()) {
              if(uicomponent.EXPERIENCES_IS_CURRENT.equals(key)) {
                continue;
              }
              String label = _ctx.appRes("UIBasicProfile.label." + key);
    %>
            <div class="<%=key%> clearfix"><div class="labelName pull-left"><%=label%>:</div>
              <div rel="tooltip" data-placement="top" title="" data-original-title="<%=experience.get(key)%>"
                class="pull-left ellipsis"><%=experience.get(key)%>
    			<%=(utilNow != null && "startDate".equals(key)) ? (" "+_ctx.appRes("UIBasicProfile.label.untilNow")) : "" %></div>
             </div> 
    <%
            }
            print("</div>");
          }
          print("</div>");
        }
    %>
    </div>
    • This template overrides the Experience Profile Portlet with more description in the code:

      <p>Quick description of the user</p>
  6. Override UIRecentActivitiesPortlet.gtmpl as follows:

    <%
    import org.exoplatform.social.webui.Utils;
    import org.exoplatform.portal.webui.util.Util;
    import org.exoplatform.social.core.service.LinkProvider;
    import org.exoplatform.social.user.portlet.UserProfileHelper;
    import org.exoplatform.social.user.portlet.RecentActivitiesHelper;
    
    //Retrieve the most recent activities of the user
    List activities = uicomponent.getRecentActivities();
    %>
    <div class="uiSocApplication uiRecentActivitiesPortlet" id="$uicomponent.id">
      <h4 class="head-container"><%=_ctx.appRes("UIBasicProfile.label.RecentActivities")%></h4>
      
      <!-- Additional description -->
      <p>The most recent activities of the user</p>
      
      <!-- Main content of the recent activities -->
      <div class="activityCont">
      <%
      
    	//no activity
        if(activities.size() == 0) {
         String keyNoActivities = Utils.isOwner() ? "YouHaveNotActivities" : "UserHaveNotActivities";
      %>
          <div class="simpleBox noActivity center"><%=_ctx.appRes("UIBasicProfile.info." + keyNoActivities)%></div>
      <%
      
    	//if having activities, loop through to print out
        } else {
          String activityURL = LinkProvider.getBaseUri(null, null) + "/activity?id=";
          for (activity in activities) {
            def profile = RecentActivitiesHelper.getOwnerActivityProfile(activity);
            String avatarURL = profile.getAvatarUrl();
            String profileURL = profile.getUrl();
            String displayName = profile.getFullName();
            String activityTypeIcon =  RecentActivitiesHelper.getActivityTypeIcon(activity);
            String link = RecentActivitiesHelper.getLink(activity);
            String linkTitle = RecentActivitiesHelper.getLinkTitle(activity);
      %>
      
    	  <!-- Build an activity stream for each activity-->
          <div class="activityStream uiDefaultActivity clearfix" id="Activity<%=activity.id%>">
            <div class="activityTimeLine pull-left">
              <div class="activityAvatar avatarCircle">
                <a href="<%=profileURL%>">
                  <img alt="<%=displayName%>" src="
    			  <%=((avatarURL == null || avatarURL.length() == 0) ? LinkProvider.PROFILE_DEFAULT_AVATAR_URL : avatarURL)%>">
                </a>
              </div>
              <% if (activityTypeIcon != null && activityTypeIcon.length() > 0) { %>
              <div class="activityType"><span><i class="<%=activityTypeIcon%> uiIconSocWhite"></i></span></div>
              <% } %>
            </div>
    		<!--end activityTimeLine-->
    		
            <div class="boxContainer" id="boxContainer" onclick="window.open('<%=(activityURL + activity.id)%>', '_self')">
              <div id="Content<%=activity.id%>" class="content">
              <%if (link != null) { 
                  if (linkTitle != null) {
              %> 
                    <div class="status"><%=linkTitle%></div>
                    <div class="link"><a href="javascript:void(0);" onclick="(function(evt){ evt.stopPropagation(); window.open('<%=link%>', '_blank');})(event)"><%=activity.getTitle()%></a></div>
              <%
                  } else {
              %>
                    <div><a href="javascript:void(0);" onclick="(function(evt){ evt.stopPropagation(); window.open('<%=link%>', '_self');})(event)">
    				<%=activity.getTitle()%></a></div>
              <%  }
                } else {%>
                    <div class="status"><%=activity.getTitle()%></div>
              <%} %>
              </div>
            </div>
    		<!-- end boxContainer-->
          </div>
    	  <!-- end activityStream -->
      <%
          }
    	  
          //Provide view all activities feature
          String activityStreamURL = LinkProvider.getUserActivityUri(Utils.getOwnerIdentity(false).getRemoteId());
          print("<div style=\"display: block;\" class=\"boxLoadMore\">"+
    	  "<button class=\"btn\" style=\"width:100%;\" onclick=\"window.location.href='" + activityStreamURL + "'\">" +
          _ctx.appRes("UIBasicProfile.action.ViewAll") +
          "</button></div>");
          uicomponent.initProfilePopup();
        }
      %>
        </div>    
          <%
          if (uicomponent.hasActivityBottomIcon && activities.size() != 0) {
      %>
          <div class="activityBottom" style="display: block;"><span></span></div>
      <%      
          }
          %>
    </div>
    • This template overrides the Recent Activities Portlet with more description in the code:

      <p>The most recent activities of the user</p>
  7. Create a jar file to register this user-profile-extension.war to portal container as in Portal extension. Then, edit the configuration.xml file as follows:

    
    <?xml version="1.0" encoding="UTF-8"?>
    <configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd http://www.exoplatform.org/xml/ns/kernel_1_2.xsd"
    xmlns="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd">
        
        <external-component-plugins>
            <!-- The full qualified name of the PortalContainerConfig -->
            <target-component>org.exoplatform.container.definition.PortalContainerConfig</target-component>
            <component-plugin>
                <!-- The name of the plugin -->
                <name>Change PortalContainer Definitions</name>
                <!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
                <set-method>registerChangePlugin</set-method>
                <!-- The full qualified name of the PortalContainerDefinitionChangePlugin -->
                <type>org.exoplatform.container.definition.PortalContainerDefinitionChangePlugin</type>
                <priority>102</priority>
                <init-params>
                    <value-param>
                        <name>apply.default</name>
                        <value>true</value>
                    </value-param>
                    <object-param>
                        <name>change</name>
                        <object type="org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependenciesAfter">
                            <!-- The list of name of the dependencies to add -->
                            <field name="dependencies">
                                <collection type="java.util.ArrayList">
                                    <value>
                                        <!--The context name of the portal extension-->
                                        <string>user-profile-extension</string>
                                    </value>
                                </collection>
                            </field>
                        </object>
                    </object-param>     
                </init-params>
            </component-plugin>
        </external-component-plugins>   
    </configuration>
  8. Copy these jar and war files into the corresponding deployment folders where you unpacked the eXo Platform installation.

Testing what you have customized

Start eXo Platform and you will see your new profile appear as follows:

Clicking on Show full contact information or Show connections button will expand the corresponding information panel. Note that if you are not this user, the email will not be displayed:

Copyright ©. All rights reserved. eXo Platform SAS
blog comments powered byDisqus