Using an Association to a data (contact) list

The aim of this post is to describe how to use associations in Alfresco to link to a data list.

The use case I’m addressing here is to be able to associate a folder with people held in a contact list.

The first thing to do is to create the association in the model.

Here I’m creating an aspect that allows for one primary contact and n other contacts.

You’ll notice that there are properties as well as associations – more about that later.

        <aspect name="ael:contacts">
            <title>Contacts</title>
            <properties>
                <property name="ael:primaryContact">
                    <title>Primary Contact</title>
                    <type>d:text</type>
                    <mandatory>false</mandatory>
                    <multiple>false</multiple>
                </property>
                <property name="ael:otherContact">
                    <title>Other Contact</title>
                    <type>d:text</type>
                    <mandatory>false</mandatory>
                    <multiple>true</multiple>
                </property>

            </properties>
            <associations>
                <association name="ael:mainContact">
                    <source>
                        <mandatory>false</mandatory>
                        <many>true</many>
                    </source>
                    <target>
                        <class>dl:contact</class>
                        <mandatory>false</mandatory>
                        <many>false</many>
                    </target>
                </association>
                <association name="ael:otherContacts">
                    <source>
                        <mandatory>false</mandatory>
                        <many>true</many>
                    </source>
                    <target>
                        <class>dl:contact</class>
                        <mandatory>false</mandatory>
                        <many>true</many>
                    </target>
                </association>
            </associations>
        </aspect>
    </aspects>

You’ll need to create a data list to link to so let’s create a contact list called External Contacts.

Now it’s time to set up the edit form in the share-config-custom.xml so add the following to the appearance section of the relevant form

<field id="ael:mainContact" label-id="ael.metadata.mainContact"
                        set="aelMetadata">
  <control template="/org/alfresco/components/form/controls/association.ftl">
     <control-param name="startLocation">//cm:External_Contacts</control-param>
  </control>
</field>
<field id="ael:otherContacts" label-id="ael.metadata.otherContact"
                        set="aelMetadata">
  <control 
     template="/org/alfresco/components/form/controls/association.ftl">
     <control-param name="startLocation">//cm:External_Contacts</control-param>
  </control>
</field>

Now this is where it starts to get interesting….

You’ll notice that the start location is an xpath string to //cm:External_Contacts whereas our contact list is called ‘External Contacts’. If you run this then the xpath won’t be found so you’ll taken to the root – so navigate down to the site and into data lists and you’ll see that all the nodes are represented by their uid – not very helpful so let’s go in and change the name of the contact list using a script (the excellent javascript console is useful here)

document.name = 'External_Contacts';
document.save();

Note that it’s document.name not document.properties.name

(You won’t see the underscore and it makes the xpath easier)

So now if you run it you’ll be taken to the right place but any contacts will show up as uids again – so another script this time tied into the create and update actions on the folder.

 var first = document.properties['dl:contactFirstName'];
var last = document.properties['dl:contactLastName'];
var displayName = "";
if (last != null && last.length > 0) {
  displayName = last;
} else if (first != null && first.length > 0) {
  displayName = first;
}
if (first != null && first.length > 0 && last != null && last.length > 0) {
 displayName = last + ", " + first;
}
document.properties.name = displayName;
document.save();

Now we’re at the point where we can edit the properties and usefully create an association with a member of the contact list.

Next adding a custom advanced search – simple just use the association control in the search form and we’re away – NO – it looks like it works, you get the selection dialogs coming up but no results are returned. Unfortunately the Alfresco search doesn’t work across associations so now it’s time to get a bit creative and use those extra properties we set up earlier.

So on your folder with the aspect applied it’s time to add another action using the script below

for each (var assoc in document.assocs["ael:mainContact"]) {
  document.properties["ael:primaryContact"] = assoc.properties["cm:name"];
}

var contacts = new Array();
for each (var assoc in document.assocs["ael:otherContacts"]) {
  contacts.push(assoc.properties["cm:name"]);
}
document.properties["ael:otherContact"] = contacts;

document.save();

Once you’ve done this then you can use the otherContact and primaryContact fields in your search form instead of the mainContact and otherContacts fields. This means that you’ll have text searches and won’t get the option to select your contacts but at least it works and does allow the use of wildcard searching.

Another advantage of having these fields available is that you can use them in DocumentList renderer.

Prior to having these fields I created a custom renderer to follow the associations and get the properties to display. This worked but was a bit overcomplex (it would have helped if the propertyName was available to the renderer function so that the same function could be used for different properties – I used a hack with the label to achieve the same effect)

I discovered a draw back to this approach – the cm:name has to be a valid filename so something like Bloggs, Joe M.I. isn’t valid

 

CMIS using perl

I’ve had a requirement to get some document details out of Alfresco using perl.

CMIS seems to be the obvious answer and a little bit of googling gives us the WebService::Cmis package.

This seems to do the job nicely but the documentation isn’t great (hint: use the pod) so here is a trival example to recurse through a folder and print out the details of the contents.

#!/usr/bin/perl -w
use Data::Dumper;
use WebService::Cmis;
use Cache::FileCache;

print "Content-type:text/htmlrnrn";

my $client = WebService::Cmis::getClient(
url => "https://myhost/alfresco/cmisatom",
user => '',
password => "",
cache => new Cache::FileCache({
cache_root => "/tmp/cmis_client"
}
)
);

my $repo = $client->getRepository;
# print Dumper($repo);

my $projectFolder = "/Sites/mySite/documentLibrary/myFolder";

my $folder = $repo->getObjectByPath($projectFolder);
# print Dumper($folder);
showFolderContents($folder);

 

 

sub showFolderDetails {
 my $fold = shift;
 print "<h2>".$fold->getTitle()."</h2>n";
# my $props = $fold->getProperties;
#print Dumper($props);
}

sub showDocumentDetails {
 my $doc = shift;
# print Dumper($doc);
 my $props = $doc->getProperties;
 if ($props->{'cmis:isLatestVersion'}->getValue eq 1) {
  #Show mail messages
  if (defined $props->{'imap:messageFrom'}) {
   print $props->{'imap:messageFrom'}->getValue;
   print $props->{'imap:messageTo'}->getValue;
   print $props->{'imap:messageSubject'}->getValue;
   print $props->{'imap:flagAnswered'}->getValue;
  } else {
   print $doc->getTitle()."n";
  }
# print Dumper($props);
 }
}

sub showFolderContents {
 my $fold = shift;

 showFolderDetails($fold);
 my $projects = $fold->getChildren();
 while (($entry = $projects->getNext())){

   if ($entry->isa("WebService::Cmis::Folder")) {
      showFolderContents($entry);
   } else {
      showDocumentDetails($entry);
   }
 }
}