Tuesday, June 19, 2012

Custom admin console groups and sorting

In continuation of previous post, anybody trying this, will notice, that the sorting is out-of-control. For a quick Alfresco Share customisation, you could be tempted to hard-code some sorting into the freemaker templates. Well we could of cause also take the long road (some times better) and make a more general solution. So here goes. I propose a mechanism controlled by a group index configured in the properties along side the group label etc. The index can be configured for each locale/language thus alphabetical sorting is possible. The index sorting shall allow for multiple groups on same index and holes in the index sequence, and still handle the default (no group) tools.

This is implemented by customising the page-template controller ('console.js' in 'templates/org/alfresco/'), applying a property key for each group. The key is formatted like tools.group.<group>.index

Change group key generating loop in console.js:
            var group = "",
                groupLabelId = null,
                groupIndexId = null,
                paths = tool.scriptPath.split('/');
            if (paths.length > 1 && paths[paths.length - 2] == "console")
            {
               // found webscript package grouping
               group = paths[paths.length - 1];
               groupLabelId = "tool.group." + group;
               groupIndexId = "tool.group." + group + ".index";
            }
           
            var info =
            {
               id: scriptName,
               url: toolUrl,
               label: labelId,
               group: group,
               groupLabel: groupLabelId,
               groupIndex: groupIndexId,
               description: descId,
               selected: (currentToolId == scriptName)
            };


Then the component controller ('consol-tools.js' in 'site-webscripts/org/alfresco/components/console') will read the index (if present) and attempt sorting. The sorting rules are simple, precedence to groups with configured index, rest are places if index is 'free'. Group zero(0) is still for default group (no group name).

The component controller (console-tools.get.js) has large parts added:

/**
 * Admin Console Tools list component
 */

function main()
{
   // get the tool info from the request context - as supplied by the console template script
   var toolInfo = context.properties["console-tools"];
  
   // resolve the message labels
   for (var g = 0, group; g < toolInfo.length; g++)
   {
      group = toolInfo[g];
      for (var i = 0, info; i < group.length; i++)
      {
         info = group[i];
         info.label = msg.get(info.label);
         info.description = msg.get(info.description);
         if (info.group != "")
         {
            info.groupLabel = msg.get(info.groupLabel);
            info.groupIndex = msg.get(info.groupIndex);
         } else {
            info.groupIndex = 0;
         }
        
      }
   }

   var index = 0;
   var addedAtIndex = false;
   var indexArray = new Array();
   // Sort
   while (index < toolInfo.length) {
       for (var g = 0, group; g < toolInfo.length; g++)
       {
          group = toolInfo[g];
          for (var i = 0, info; i < group.length; i++)
          {
             info = group[i];
            
             if (info.groupIndex == index)
             {
                indexArray[indexArray.length] = index;
                 addedAtIndex=true;
                 break;
             }
          }
          if (addedAtIndex==true) break;
       }
      
       if (!addedAtIndex) {
           //find one with
           for (var g = 0, group; g < toolInfo.length; g++)
           {
              group = toolInfo[g];
              for (var i = 0, info; i < group.length; i++)
              {
                 info = group[i];
                
                 if (isNaN(info.groupIndex))
                 {
                    if (indexArray.length == index) indexArray[indexArray.length] = index;
                    info.groupIndex=index;
                     addedAtIndex=true;
                    
                 }
              }
              if (addedAtIndex==true) break;
           }
       }
      
       addedAtIndex = false;
       index++;
   }
  
   model.tools = toolInfo;
   model.indeces = indexArray;
}

main();


The sorting might still result in holes in the indexes range, so the freemarker template could be messy trying to figure out the ordering (group are not sorted in the transferred data (model.tools)). So to make it easy for the template, a new array is transferred with the actual used indexes. So the freemaker template can just go through the indexes finding the group with each index.

The component template (console-tools.get.html.ftl) is changed to:

<div id="${args.htmlid?html}-body" class="tool tools-link">
   <h2>${msg("header.tools")}</h2>
   <ul class="toolLink">
  
    <#list indeces as index>
          
        <#list tools as group>
               <#list group as tool>
                 <#if (tool.groupIndex)?number == index>
                     <#if tool_index=0 && tool.group != ""></ul><h3>${tool.groupLabel}</h3><ul class="toolLink"></#if>
                    <li class="<#if tool_index=0>first-link</#if><#if tool.selected> selected</#if>"><span><a href="${tool.id}" class="tool-link" title="${tool.description?html}">${tool.label?html}</a></span></li>
                 </#if>
             </#list>
        </#list>
           
    </#list>
   </ul>
</div>