Showing posts with label Workflow. Show all posts
Showing posts with label Workflow. Show all posts

Friday, October 27, 2017

Alfresco Web Scripts: Limiting Picker Search for Users

Both the Alfresco repository and Alfresco Share products extensively use Alfresco Web Scripts. Web Scripts are an important concept to understand in order to be able to customize Alfresco.

In this post we'll look at a requested customization for Alfresco Share and how it can be implemented by overriding the controller of an existing Alfresco Web Script.

First let's look at the following functionality in Alfresco Share.

By default, any user in Alfresco can start a workflow on a document which they have access to. If a user chooses to create a New Task (ad hoc task) workflow on a document, the workflow creation form is displayed, and on that form, the user is able to select a user to assign the new task to.

In the "Select..." dialog that pops up, the user is able to search across all registered Alfresco users.

It looks like this:


Customization Requirement


The requirement for the customization is, for all users other than admin users, to limit the search result to include only users that belong to the same groups which they are members of.

Implementation

How can we achieve that?

After a little investigation, we find that the user assignment popup is a kind of picker object, and that the client JavaScript code for the picker searches for users by making a call to a repository Web Script.  The controller for that Web Script is the following file:

alfresco/templates/webscripts/org/alfresco/repository/forms/pickerchildren.get.js.

[Note that this file can be found bundled in the repo jar file: alfresco-remote-api-5.x.x.jar]

Within the Web Script, the function that gets called to search for users is findUsers().  We'll change this method to limit the search results from the Picker to include only those users that belong to same groups that the user belongs to.








function findUsers(searchTerm, maxResults, results)
{
   var personRefs = people.getPeople(searchTerm, maxResults, "lastName", true);
   
   // create person object for each result
   for each(var personRef in personRefs)
   {
      if (logger.isLoggingEnabled())
         logger.log("found personRefs = " + personRefs);
      
      //filter out  the disabled users
      var daname = (search.findNode(personRef)).properties.userName;
 
      if(people.isAccountEnabled(daname)){
          results.push(
          {
              item: createPersonResult(search.findNode(personRef)),
              selectable: true
          });
      }
      else{
          results.push(
       {
           item: createPersonResult(search.findNode(personRef)),
           selectable: false
       });   
          if (logger.isLoggingEnabled())
              logger.log("User not added to results not enabled" + daname);
      }
   }
}




We see here that searches are currently being made using the Alfresco JavaScript API using root object 'people':

 var personRefs = people.getPeople(searchTerm, maxResults, "lastName", true);

The requirement for the customization is to change the search by limiting the results to only those users that belong to the same group as the current user.  We can again use the 'people' root-scoped object to find which groups the user belongs to:

var userGroups = people.getContainerGroups(person);

But the problem with the method getContainerGroups() is that it does not include system groups with the results.  It would be great if this method took another parameter as a flag to specify that system groups should be included in the result, but it doesn't. System groups include the Share groups created for each site for managing the four default Site roles: site manager, collaborator, contributor, and consumer.



If a user belongs to a Share site, we also want to include in the results any users which belong to any of the four Share site groups for that site. To find the additional site groups corresponding to the sites that a user belongs to, we will write a new method.  That is shown in the following code which takes a list and adds the Share site groups to the list.




function addSiteGroups(g)
{
    var s = siteService.findSites("", null, 0);
    for(var j=0; j<s.length; j++)
      {
 var perms = s[j].getSitePermissionGroups();
 for(var k=0;k<perms.length;k++)
 {
     g.push( groups.getGroupForFullAuthorityName(perms[k]).getGroupNode() );
 }
      }
  }

Here we query the site service to find all sites that the current user is able to access.  Then we get the names of the roles for the site -- there typically will be just the four we mentioned earlier. Then we get the group corresponding to each of those permissions and push the group nodes onto the list of groups:



groups.getGroupForFullAuthorityName(perms[k]).getGroupNode()

Once we have the list of groups that the user belongs to, we can make a query to find only those people that match the search criteria and also are members of those groups.

First we a build a string which contains part of the condition for the query that lists the groups that the user belongs:

 grpQuery = " AND (";
  
 // make concatenated list of groups
 for(var i=0; i<userGroups.length; i++)
   {
      if(i>0) 
      {
  grpQuery = grpQuery + " OR ";
      }
      grpQuery = grpQuery + "PARENT:\"" + userGroups[i].nodeRef.toString() + "\" ";
    }
    grpQuery = grpQuery + ")";



The query is as follows.  Note that 'filterTerm' corresponds to the text entered by the user within the UI. 'grpQuery' corresponds to the criteria for the group restriction. Note that we search for objects that are type cm:person and which do not have the cm:personDisabled flag applied.

var def = 
{ 
  query: "+TYPE:\"cm:person\" AND (-ASPECT:\"cm:personDisabled\") AND(cm:lastName:\"*" + filterTerm + "*\" OR cm:userName:\"*" + filterTerm + "*\")" + grpQuery, 
  store: "workspace://SpacesStore", 
    language: "fts-alfresco",
    page: paging
  }; 

Putting it all together, the combined changes to the Web Script are as follows:



function addSiteGroups(g)
{
 var s = siteService.findSites("", null, 0);
 for(var j=0; j<s.length; j++)
 {
  var perms = s[j].getSitePermissionGroups();
  for(var k=0;k<perms.length;k++)
  {
      g.push( groups.getGroupForFullAuthorityName(perms[k]).getGroupNode() );
  }
 }
}

// Modified by Formtek to find only group to which the user belongs
function findUsers(filterTerm, maxResults, results)
{  
 var paging = 
 { 
     maxItems: maxResults, 
     skipCount: 0 
 }; 
   
 var grpQuery = "";
 
 
 // If the user is not an admin, restrict the search
 if(!people.isAdmin(people.getPerson(person.properties["cm:userName"])))
 {
     // Find all the groups that the user is in
     var userGroups = people.getContainerGroups(person);
  addSiteGroups(userGroups);
  grpQuery = " AND (";
  
     // make concatenated list of groups
     for(var i=0; i<userGroups.length; i++)
     {
      if(i>0) 
      {
       grpQuery = grpQuery + " OR ";
      }
      grpQuery = grpQuery + "PARENT:\"" + userGroups[i].nodeRef.toString() + "\" ";
     }
     grpQuery = grpQuery + ")";
    }
 
 var def = 
 { 
   query: "+TYPE:\"cm:person\" AND -ASPECT:\"cm:personDisabled\" AND (cm:firstName:\"*" + filterTerm + "*\" OR cm:lastName:\"*" + filterTerm + "*\" OR cm:userName:\"*" + filterTerm + "*\")" + grpQuery, 
   store: "workspace://SpacesStore", 
   language: "fts-alfresco",
   page: paging
 }; 
 
 var personRefs = search.query(def); 
   
 // create person object for each result
 for each(var personRef in personRefs)
 {
    // add to results
    results.push(
    {
       item: createPersonResult(search.findNode(personRef.nodeRef)),
       selectable: true
    });
 }
}

To override the existing Web Script file, copy the original file into the extensions area and make the change to the findUsers() method as shown above here:

tomcat/shared/WEB-INF/classes/alfresco/extension/templates/webscripts/org/alfresco/repository/forms/pickerchildren.get.js.

One additional thing to notice is that filtering by group is only applied when the user is not an admin:

people.isAdmin(people.getPerson(person.properties["cm:userName"]))
 

Workflow assignments of tasks to users will now use the override method defined here and users will be filtered by group.

Thursday, March 2, 2017

Auditing Document Transactions in Alfresco Share



A frequent need for many of our customers is the ability to audit activity on a document in the Alfresco repository.  An easy way to do this is via a subscription to the Formtek Auditing Extension for Alfresco.


The Auditing Extension is one of several extensions to Alfresco that Formtek offers to enhance your Alfresco implementation.

The Auditing Extension is Integrated with Alfresco Share


For every object accessible via Alfresco Share, the Auditing Extension records the action performed, the date and time the action occurred, the user who performed the action, and optional details on the action. Each document and folder maintains its own auditing information, and any user who can access the document or folder can also view its audit log.


The Auditing Extension records standard Alfresco transactions such as Create, Edit Properties, Update Properties, Checkout, Download, Upload New Version, Move to, Copy to, Manage Permissions, Manage Aspects, Delete Document, and more.  It will also record actions initiated by other installed Formtek extensions (i.e. File Linking, Peer Association, Version Browser, and the Engineering Data Management Module).

So, what does it look like?  Here’s a screenshot of an audit of a single document.

Audit History for a Single Object


Selecting “More” provides additional information about the action performed.


Additional Capabilities for Administrators
If you are an administrator, you may access the admin console and use the Audit History Search Panel to search for audit records for different types of content stored in the repository. Audit records include the following information:

  • Timestamp – When the action occurred
  • Action – The action performed
  • User – The user who performed the action
  • Type – The type of item on which the action was performed
  • Name – The name of the item on which the action was performed.

Here’s a screenshot.



Audit History Search Panel with Results


The admin console also provides an effective means for an administrator to search for active and/or completed workflows. Workflow records include the following information:

  • Description – The workflow message
  • Initiator – The user who started the workflow
  • Start Date – The workflow start date
  • Due Date – The workflow due date
  • End Date – The date the workflow completed
  • Priority – The workflow priority (1=High, 2=Medium, and 3=Low)
  • Status – The workflow status, either Active or Completed
  • Details – A link to the details of the workflow in a new window.



Workflow History Results List



Need more information?  You can watch this short demo video.