Alfresco: Property Decorators — cont.

No Comments »

In my post on Property Decorators I pointed out that there was an issue adding a customer property decorator: you couldn’t create a custom bean to perform the mapping and had to overwrite the out of the box bean. Well Mike Hatfield has found a solution which is just awesome: Map Merge.

It is possible to merge maps of a child bean to a parent bean. Here is how it is done: Create a new bean, where the parent is the applicationScriptUtils bean. Add a single property of decoratedProperties and define a map in the property. When you define the map add the attribute merge="true". Finally, add your custom property to decorator bean mappings. Done!

Here is an example:

  <bean id="customApplicationScriptUtils" parent="applicationScriptUtils">
   <property name="decoratedProperties">
      <map merge="true">
         <entry key="alf:propertyHolder">
            <ref bean="customDecoratorBean"/>
         </entry>
      </map>
   </property>
  </bean>

Alfresco: Simple Workflow Web Scripts

No Comments »

Continuing on from my previous post on Simple Workflows let’s look at using the Simple Workflow model in a few web scripts.

The first web script will add a simple workflow to a node. The second will allow us to signal the transition of accept or reject on that node.

Add a Workflow

As discussed in the previous post, a simple workflow is made up of two paths: accept or reject with a copy or move to another folder. You are also able to set the name presented to the user for the accept or reject step. This is very useful as what we may be presenting to the user is to not accept or reject documents, but rather sending the document to two different group owned folders based on context/content of those documents. At least one step is required. Alfresco defaults “accept” for the required step and makes “reject” the optional step. These need to be passed to our web script. The payload is passed as json. A full example of this looks as follows:

{
    "simpleworkflow": {
        "accept": {
            "name": "acceptstep",
            "move": true,
            "folder": "workspace://SpacesStore/ebd7ff99-b423-4e45-ad4f-71929ce4c089"
        },
        "reject": {
            "name": "rejectstep",
            "move": true,
            "folder": "workspace://SpacesStore/0239b2ed-ebfa-4b9d-a7fa-f0404448cffc"
        }
    }
}

In the above example, the reject object is optional.

Both the accept and reject objects are made up of the three required values of the simple workflow aspect: name, move and folder.

The name field maps to either approveStep or rejectStep. It is the name presented in the Alfresco UIs. But it could also be used in a custom interface as they are returned as part of the list of properties for the node.

The move field maps to either approveMove or rejectMove. This boolean field indicates with a true value to move the node or, if false, to copy the node.

The final field, folder, maps to approveFolder or rejectFolder. This is the folder node where the document should be moved or copied to when the appropriate action is taken.

The web script can be call by passing the simpleworkflow json object to simpleworkflow/add/{store_type}/{store_id}/{id}. Upon completion a json object of { "success": true|false] }. True upon successful completion, false if there was an error adding the simpleworkflow aspect to the node.

Using a Simple Workflow from a Web Script

This web script was originally developed for a customer to return XML, but I’ve now updated it to return json by default. The XML is maintained here for backwards compatibility.

When a simple workflow aspect is added to a node, it just adds the properties of what should occur when an accept or reject action is taken. The actual actions of what happens is built into UIs. So the web scripts need to do the same: perform the copy or move to the targeted folder. The call is pretty simple: simpleworkflow/{accept|reject}/{store_type}/{store_id}/{id}. The json or XML that is returned let us know of either the success or the failure of this action. Failures can provide some information on why the failure occurred. A success messages, provides more detail: what action occured — copy or move, which step was taken — returning the name as set in the approveStep or rejectStep properties, the folder that document was moved or copied to. The destination that is returned is the human readable path. (If there is any interest we can update this to return the nodeRef as well as the path.)

The json returned looks like:

{
    "success": true,
    "action": "move",
    "step": "acceptStep",
    "destination": "/Company Home/Sites/workflow/docmentLibrary/workflow/accept"
}

These web scripts can be downloaded from its Google Code Project.


Alfresco: Simple Workflow

No Comments »

When I worked on the sales side at Alfresco, one of the easiest things to demo was a simple workflow. It was usually something that enabled the prospect to tick off a major requirement on their long list of things that the solution they were looking for needed. Alfresco has two kinds of workflow: simple and for lack of a better word advanced. Advanced workflows usually require some level of business logic, multiple steps, several actors, etc. Advanced workflows are implemented using Activiti (Alfresco 4.0 forward) or JBPM (pre Alfresco 4.0 and maintained for legacy in Alfresco 4.0) Simple workflows provide an accept, a reject, and then the content is either copied or moved to another folder.

What I really like about simple workflows is how they are implemented and how you can model your own customizations or applications using this pattern. As a follow up to this post I’m going to share a web script to add a simple workflow to a node and then another to progress the workflow by accepting or rejecting it.

Let’s dig in!

Simple workflows present, as stated above, the ability to accept and reject the document and then copy or move that document to another space. The key to a simple workflow is the model.

      <aspect name="app:simpleworkflow">
         <parent>app:workflow</parent>
         <properties>
            <property name="app:approveStep">
               <type>d:text</type>
               <protected>true</protected>
            </property>
            <property name="app:approveFolder">
               <type>d:noderef</type>
               <protected>true</protected>
            </property>
            <property name="app:approveMove">
               <type>d:boolean</type>
            </property>
            <property name="app:rejectStep">
               <type>d:text</type>
               <protected>true</protected>
            </property>
            <property name="app:rejectFolder">
               <type>d:noderef</type>
               <protected>true</protected>
            </property>
            <property name="app:rejectMove">
               <type>d:boolean</type>
            </property>
         </properties>
      </aspect>

Each node that has a simple workflow attached to it has this aspect applied to it, which specifies through properties what steps are available, what that step should be called (acceptStep and rejectStep) and then what should occur when that step is taken: should it be moved or copied (acceptMove and rejectMove)? and where it should go (acceptFolder and rejectFolder).

Knowing this opens many doors for us. Because the steps are properties on the node, we can modify them, if needs be, using either javascript or Java. We can also build on top of this. When I worked on the Consulting team at Alfresco a customer wanted to have a three step workflow: accept, reject and other. Modeling this was simple. All we really needed to do was add three simple properties: the step name (text), the destination node (noderef) and if it is a move or copy (boolean). A bit more complex was adding the logic for exposing this through the Explorer UI. (This customer had not yet moved to Share). Because we did not want to affect all of the simple workflows already in place, we opted to add an additional 3 step simple workflow, giving them the ability to have up to 5 different paths that could be taken for a single node.

We can also tie simple workflows into more complex actions: dynamically adding simple workflows or scripting more complex actions that allow users to manually take the workflow steps but also allowing programatic lifecycle or state to take simple workflow actions. By jumping out of Explorer or Share and into a custom application you can expose simple workflows from the repository through a web script (an easier task than modifying JSF) and one that I’ve used for several different Alfresco implementations. We will cover that in my next post.


Alfresco: Scheduled Jobs

No Comments »

Note: @tommoz has pointed out to me a blog post that I’d never seen before from @Ixxus which is a complete example of writing a scheduled job (spring beans and all). You should really check out their excellent example if you need a full example of writing scheduled jobs for Alfresco.

One of the things that is easy to forget when adding/writing a new scheduled jobs is to wrap your code in a RetryingTransactionHelper _and_ a RunAs. When executing the scheduled job we need to make sure that we provide a transaction in which to perform your custom code and a user to perform the code under.

Example:

 public void execute()
    {
        AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>()
        {
            @Override
            public Object doWork()
                throws Exception
            {
                RetryingTransactionCallback<;Object>; txnWork = new RetryingTransactionCallback<Object>()
                {
                    public Object execute()
                        throws Exception
                    {
                        //Add logic here
                        return null;
                    }
                };

                transactionService.getRetryingTransactionHelper().doInTransaction(txnWork, false);
                return null;
            }
        }, user_authority);

    }

Hopefully this template will prove usefully to anyone writing a custom scheduled actions.


Alfresco: Property Decorators

No Comments »

Note: After fighting through some upgrade issues with MySQL…and it still not being 100% fixed…I’m finally at a point where I can comfortably write to the DB and bring to you two, thats right two!, new posts in quick succession.  I’m hoping to make this a trend to have two new posts a month (so look for 4 this month as these two should have gone live last month). So without further ado….

One of the exciting, at least in my opinion, new features of Alfresco 4 is the Share Document Library Extension’s (hat tip to Mike Hatfield).  In the above post Mike goes in to detail on how to use this new feature and I’m going to share an example on how I’m using it as part of integration with Dropbox I’m working on.

The Problem Set

As part of the integration we need to add a status indicator (see the above article for in depth details on status indicators) to, as the name states, indicate that the file/folder is in a specific state, ie synced to Dropbox.  Early on this was a simple tasks: does it have a specific aspect or not.  As the project has moved forward, we’ve raised the complexity: does it have a specific association and does that association have a child association of a specific value…a direct correlation between this child association and a specific user.  Now that we have moved beyond what the out of the box evaluators can do directly, what are our options?

Options?

The Share Document Library Extension Framework provides two options for retrieving additional metadata/values from the repository tier: Custom Responses and Property Decorators.

Custom Responses allow you to return information that is not specific to any node.  Alfresco 4 uses this to return information about our Sharepoint Protocol integration.

Property Decorators allow you to return node specific information in a more usable format to the web tier (ie Share).  For example rewriting a nodeRef to a filename, or a username as first and last name. Or as in our case, a new set of properties or key/value pairs.

Since we are looking for specific information about a node, a property decorator is what we will need.

Implemention

A property decorator needs to have the following:

A content model property to hold a map of properties. In our model we added a property similar to:

<property name="alf:propertyHolder">
    <type>d:boolean</type>
    <default>false</default>
    <index enabled="false"/>
</property>

This property will never be needed in the index since it will always be the same value until it is requested in Share.

Next we need to implement our logic.  This requires a new Java class that implements PropertyDecorator.

public class CustomProperty
    implements PropertyDecorator
{

  //Add any needed services

    @Override
    public Serializable decorate(NodeRef nodeRef, String propertyName, Serializable value)
    {
        Map<String, Serializable> map = new LinkedHashMap<String, Serializable>(4);

        //Add logic here

        //One to many
        map.put("key", value);

        return (Serializable)map;
    }

}

This also requires a new Spring bean definition.

  <bean id="customProperty" class="org.alfresco.extension.repo.jscript.app.CustomProperty">
   <!-- Any needed service -->
  </bean>

Lastly we need to add it to the list of propertyDecorators.  Update: I’ve add a new post that covers how the mapping should occur and added a new bean here. Currently this requires overwriting the out of the box definition for applicationScriptUtils.  This could cause issues if you have several different AMPs that need to overwrite this bean. (I’ve opened ALF-13038 for this issue).

<bean id="customApplicationScriptUtils" parent="applicationScriptUtils">
 <property name="decoratedProperties">
    <map merge="true">
       <entry key="alf:propertyHolder">
          <ref bean="customDecoratorBean"/>
       </entry>
    </map>
 </property>
</bean>

Now this completes the repo side of our extension.  Now we need to work with the values returned on the Share side.  We needr a status indicator so we’ll focus there.

First we’ll define the evaluator.  We need to add the evaluator to web-extension/custom-slingshot-something-context.xml

<bean id="evaluator.doclib.indicator.custom" parent="evaluator.doclib.metadata.value">
      <property name="accessor" value="properties.customProperty"/>
	  <property name="comparator">
         <bean class="org.alfresco.web.evaluator.StringEqualsComparator">
            <property name="value" value="true" />
         </bean>
      </property>
   </bean>

It is important to note that the value of the accessor is not the content model property name, but the key value used in our map from the CustomProperty class.

And finally, we use the evaluator in our indicator config. This is added to our share-config-custom.xml under the META-INF in our custom jar file.

<config evaluator="string-compare" condition="DocumentLibrary">
		<indicators>
			<indicator id="customDecorator" index="250" icon="custom-16.png">
				<evaluator>evaluator.doclib.indicator.custom</evaluator>
			</indicator>
		</indicators>
</config>

In conclusion, the Share Document Library Extension Framework is a powerful new tool in the developer toolbox.


Alfresco: Share Discussion Notification

No Comments »

A few months ago in the Alfresco Forums, a user asked about adding email notification to Share Site Discussions.  We went back and forth working out a rough way to accomplish this (some of this in private messages).  And then a couple of weeks ago the question came up internally.  I passed on what we had figured out and told them I would put together a blog post.

So what are we trying to accomplish?

The Share Site Discussion Components supports the following:

  • Threaded Discussions — Post topic and replies
  • Dynamic Filtering — Tags/New/Hot/All/My Topics
  • RSS Feed for latest discussions

Notifications of new posts can happen over RSS and to the Site Activities Dashlet both of which require access to the system, most likely behind a firewall.  Email notifications can provide updates the don’t require direct access to the system, unless you are looking to update the post.

Where to start?

There are a few things that we need to know as we get started. First, when a site is created in Alfresco Share, the content nodes supporting discussions are not created.  Only after a user accesses the discussion component does it get created.

Second, discussions are created as fm:topic types.  Posts are added as children of fm:topic as a fm:post.

Using a Rule

The simplest way to add notifications is as a rule via the Alfresco Explorer client on the discussion space that is created for the site, under the Sites space.  Adding a rule to the disucssions space matching all content items will result on two emails (one for the creation of the topic and one for the first post).  So we  need to be more specific: We need to expose the Forum Post and  Topic types as Subtypes to the rules engine.  You add fm:topic as well if you want to enable delete notifications

To web-client-config-custom.xml add

<alfresco-config>
    <config evaluator="string-compare" condition="Action Wizards">
        <subtypes>
            <type name="fm:post"/>
            <type name="fm:topic"/>
        </subtypes>
    </config>
</alfresco-config>

Topics are the best choice for building rules on.  Notifications on fm:topic will work for create and delete but not update.  The title of a topic is stored on the first post of a discussion.  You can’t delete individual posts but you can delete a Topic so when specifying a delete notification it can be made directly against a topic,but again…it is easier to just do the same using fm:post.

The notification template would be added to email templates and would look like:

A new post is available in the '${document.parent.childAssocs["cm:contains"][0].properties.title}' dicussion in the '${document.parent.parent.parent.properties.name}' site, it was added by ${person.properties.firstName}<#if person.properties.lastName?exists> ${person.properties.lastName}</#if>.

You can view it through this link:
${url.serverPath}/share/page/site/${document.parent.parent.parent.properties.name}/discussions-topicview?topicId=${document.parent.children[0].name}&listViewLinkBack=true

Email Notification Rule

An email rule is simple to setup but the maintenance cost can be high.  You have to add each user/group manually and Share site groups are not visible to the Explorer client.

The next option is to script the notification.   A Share site group or dynamically build email list of those participating in the discussion.  (A nice to have would be to build in a watch option for those who may not participate in the discussion but want to be notified.)

Mail action sample for group or individual.

The to_many paramater currently only supports one group at a time….if you need to send to multiple people or groups, you’ll need to loop over the to_many or to paramaters and execute against each.

var siteGroup = "GROUP_site_" + document.parent.parent.parent.name;
var template = "Data Dictionary/Email Templates/Notify Email Templates/share_discussion_notification.ftl";

// create mail action
var mail = actions.create("mail");
mail.parameters.to_many = siteGroup;
//mail.parameters.from (with no from address provided the email address of the user triggering the action is used)
mail.parameters.subject="New Post in Discussion: "+document.parent.childAssocs["cm:contains"][0].properties.title;
mail.parameters.template = companyhome.childByNamePath(template);

//execute action against a document
mail.execute(document);

This option also will send email to all users in a group…even if their account has been disabled.

Another option when scripting is to dynamically build the notification list  – send only to those that are part of the discussion.  You can walk the posts through the Child Associations of the topic to build an array of users.  For smaller discussion threads there will not be that high of a cost. (Small here is a relative term, as I don’t have any metrics to say what is small).

var emailAddresses = [];

//for p expresion variable
var p, e, a;

//change to use your template
var template = "Data Dictionary/Email Templates/Notify Email Templates/share_discussion_notification.ftl";

function getEmail(person) {
	var personNode = people.getPerson(person);
	return personNode.properties.email;
}

// build emailAddresses
for (p = 0; p &lt; document.parent.childAssocs["cm:contains"].length; p += 1) {
	var user = document.parent.childAssocs["cm:contains"][p].properties.creator;
	var email = getEmail(user);

	//Is the emailAddress already in the array? If not, add it
	if (emailAddresses.length &gt; 0) {
		var match = false;
		for (e = 0; e &lt; emailAddresses.length; e += 1) {
			if (emailAddresses[e] === email) {
				match = true;
				break;
			}
		}

		if (!match) {
			emailAddresses.push(email);
		}
	} else {
		emailAddresses.push(email);
	}
}

// create mail action
var mail = actions.create("mail");
mail.parameters.subject = "New Post in Discussion: " + document.parent.childAssocs["cm:contains"][0].properties.title;
mail.parameters.template = companyhome.childByNamePath(template);

//send an email for each address
for (a = 0; a &lt; emailAddresses.length; a += 1) {
	mail.parameters.to = emailAddresses[a];
	//execute action against a document
	mail.execute(document);
}

Another option would be to add an aspect to the topic that stores a collection of all the users who have participated in the thread.  (You could also add a place to collect watchers, but you’d also need to extend the UI to allow you to capture the users for this list.)  The aspect would be added on creation on a new topic. (Use Usernames, so that you can avoid  any issues with email addresses changing — Usernames are unmutable).  When a user adds a post, their email address is added to the collection for notification.

var creator = document.properties.creator;

//for expresion variable
var u, a;

//change to use your template
var template = “Data Dictionary/Email Templates/Notify Email Templates/share_discussion_notification.ftl”;

function getEmail(person) {
	var personNode = people.getPerson(person);
	return personNode.properties.email;
}

//Look for match in n:users, if no match add them other wise, ignore the user
function updateUsers(user) {
	if (document.parent.properties["n:users"] === null) {
		document.parent.properties["n:users"] = [];
		document.parent.properties["n:users"].push(user);
		document.parent.save();
	} else {
		var match = false;

		for (u = 0; u < document.parent.properties["n:users"].length; u += 1) {
			if (document.parent.properties["n:users"][u] === user) {
				match = true;
				break;
			}
		}

		if (!match) {
			document.parent.properties["n:users"].push(user);
			document.parent.save();
		}
	}
}

//Check for notifiable aspect
if (document.parent.hasAspect("n:notifiable") {

	//if the user is not already in the n:users property of the notifiable aspect, add them
	updateUsers(creator);

	//create mail action
	var mail = actions.create(“mail”);
	mail.parameters.subject = “New Post in Discussion: ” + document.parent.childAssocs["cm:contains"][0].properties.title;
	mail.parameters.template = companyhome.childByNamePath(template);

	//send an email for each user in n:users
	for (a = 0; a < document.parent.properties["n:users"].length; a += 1) {
		mail.parameters.to = getEmail(document.parent.properties["n:users"][a]);

		//execute action against a document
		mail.execute(document);
	}
} else {
	logger.log("The notifiable aspect has not been added to the topic: " + document.parent.name;
}

These options should also take into account disabled accounts: (person.isAccountEnabled(userName); The problem with this is that isAccountEnables requires admin privileges.  There is no runAs for this kind of javascript so implementing this would require implementing the action in Java, which is outside the scope of this exercise.

Hopefully, I’ve given you some ideas of ways to approach adding discussion notifications.  The key here in discovering how to approach this was knowing where to look at how discussions are stored in the repository.  In fact this is the key for most any extension to Alfresco:  Asking yourself how does this work for this feature, which is similar to what I need, work in Alfresco? Understanding how this is pulled together from looking at the content model, looking at actual content/nodes through the node browser, understanding the relationships between nodes: parent/child and peer and sometimes digging into the source code can help to expand your understanding when looking to extend Alfresco.


Alfresco: Share Discussion Notification

No Comments »

A few months ago in the Alfresco Forums, a user asked about adding email notification to Share Site Discussions.  We went back and forth working out a rough way to accomplish this (some of this in private messages).  And then a couple of weeks ago the question came up internally.  I passed on what we had figured out and told them I would put together a blog post.

So what are we trying to accomplish?

The Share Site Discussion Components supports the following:

  • Threaded Discussions — Post topic and replies
  • Dynamic Filtering — Tags/New/Hot/All/My Topics
  • RSS Feed for latest discussions

Notifications of new posts can happen over RSS and to the Site Activities Dashlet both of which require access to the system, most likely behind a firewall.  Email notifications can provide updates the don’t require direct access to the system, unless you are looking to update the post.

Where to start?

There are a few things that we need to know as we get started. First, when a site is created in Alfresco Share, the content nodes supporting discussions are not created.  Only after a user accesses the discussion component does it get created.

Second, discussions are created as fm:topic types.  Posts are added as children of fm:topic as a fm:post.

Using a Rule

The simplest way to add notifications is as a rule via the Alfresco Explorer client on the discussion space that is created for the site, under the Sites space.  Adding a rule to the disucssions space matching all content items will result on two emails (one for the creation of the topic and one for the first post).  So we  need to be more specific: We need to expose the Forum Post and  Topic types as Subtypes to the rules engine.  You add fm:topic as well if you want to enable delete notifications

To web-client-config-custom.xml add

<alfresco-config>
    <config evaluator="string-compare" condition="Action Wizards">
        <subtypes>
            <type name="fm:post"/>
            <type name="fm:topic"/>
        </subtypes>
    </config>
</alfresco-config>

Topics are the best choice for building rules on.  Notifications on fm:topic will work for create and delete but not update.  The title of a topic is stored on the first post of a discussion.  You can’t delete individual posts but you can delete a Topic so when specifying a delete notification it can be made directly against a topic,but again…it is easier to just do the same using fm:post.

The notification template would be added to email templates and would look like:

A new post is available in the '${document.parent.childAssocs["cm:contains"][0].properties.title}' dicussion in the '${document.parent.parent.parent.properties.name}' site, it was added by ${person.properties.firstName}<#if person.properties.lastName?exists> ${person.properties.lastName}</#if>.

You can view it through this link:
${url.serverPath}/share/page/site/${document.parent.parent.parent.properties.name}/discussions-topicview?topicId=${document.parent.children[0].name}&listViewLinkBack=true

Email Notification Rule

An email rule is simple to setup but the maintenance cost can be high.  You have to add each user/group manually and Share site groups are not visible to the Explorer client.

The next option is to script the notification.   A Share site group or dynamically build email list of those participating in the discussion.  (A nice to have would be to build in a watch option for those who may not participate in the discussion but want to be notified.)

Mail action sample for group or individual.

The to_many paramater currently only supports one group at a time….if you need to send to multiple people or groups, you’ll need to loop over the to_many or to paramaters and execute against each.

var siteGroup = "GROUP_site_" + document.parent.parent.parent.name;
var template = "Data Dictionary/Email Templates/Notify Email Templates/share_discussion_notification.ftl";

// create mail action
var mail = actions.create("mail");
mail.parameters.to_many = siteGroup;
//mail.parameters.from (with no from address provided the email address of the user triggering the action is used)
mail.parameters.subject="New Post in Discussion: "+document.parent.childAssocs["cm:contains"][0].properties.title;
mail.parameters.template = companyhome.childByNamePath(template);

//execute action against a document
mail.execute(document);

This option also will send email to all users in a group…even if their account has been disabled.

Another option when scripting is to dynamically build the notification list  – send only to those that are part of the discussion.  You can walk the posts through the Child Associations of the topic to build an array of users.  For smaller discussion threads there will not be that high of a cost. (Small here is a relative term, as I don’t have any metrics to say what is small).

var emailAddresses = [];

//for p expresion variable
var p, e, a;

//change to use your template
var template = "Data Dictionary/Email Templates/Notify Email Templates/share_discussion_notification.ftl";

function getEmail(person) {
	var personNode = people.getPerson(person);
	return personNode.properties.email;
}

// build emailAddresses
for (p = 0; p &lt; document.parent.childAssocs["cm:contains"].length; p += 1) {
	var user = document.parent.childAssocs["cm:contains"][p].properties.creator;
	var email = getEmail(user);

	//Is the emailAddress already in the array? If not, add it
	if (emailAddresses.length &gt; 0) {
		var match = false;
		for (e = 0; e &lt; emailAddresses.length; e += 1) {
			if (emailAddresses[e] === email) {
				match = true;
				break;
			}
		}

		if (!match) {
			emailAddresses.push(email);
		}
	} else {
		emailAddresses.push(email);
	}
}

// create mail action
var mail = actions.create("mail");
mail.parameters.subject = "New Post in Discussion: " + document.parent.childAssocs["cm:contains"][0].properties.title;
mail.parameters.template = companyhome.childByNamePath(template);

//send an email for each address
for (a = 0; a &lt; emailAddresses.length; a += 1) {
	mail.parameters.to = emailAddresses[a];
	//execute action against a document
	mail.execute(document);
}

Another option would be to add an aspect to the topic that stores a collection of all the users who have participated in the thread.  (You could also add a place to collect watchers, but you’d also need to extend the UI to allow you to capture the users for this list.)  The aspect would be added on creation on a new topic. (Use Usernames, so that you can avoid  any issues with email addresses changing — Usernames are unmutable).  When a user adds a post, their email address is added to the collection for notification.

var creator = document.properties.creator;

//for expresion variable
var u, a;

//change to use your template
var template = “Data Dictionary/Email Templates/Notify Email Templates/share_discussion_notification.ftl”;

function getEmail(person) {
	var personNode = people.getPerson(person);
	return personNode.properties.email;
}

//Look for match in n:users, if no match add them other wise, ignore the user
function updateUsers(user) {
	if (document.parent.properties["n:users"] === null) {
		document.parent.properties["n:users"] = [];
		document.parent.properties["n:users"].push(user);
		document.parent.save();
	} else {
		var match = false;

		for (u = 0; u < document.parent.properties["n:users"].length; u += 1) {
			if (document.parent.properties["n:users"][u] === user) {
				match = true;
				break;
			}
		}

		if (!match) {
			document.parent.properties["n:users"].push(user);
			document.parent.save();
		}
	}
}

//Check for notifiable aspect
if (document.parent.hasAspect("n:notifiable") {

	//if the user is not already in the n:users property of the notifiable aspect, add them
	updateUsers(creator);

	//create mail action
	var mail = actions.create(“mail”);
	mail.parameters.subject = “New Post in Discussion: ” + document.parent.childAssocs["cm:contains"][0].properties.title;
	mail.parameters.template = companyhome.childByNamePath(template);

	//send an email for each user in n:users
	for (a = 0; a < document.parent.properties["n:users"].length; a += 1) {
		mail.parameters.to = getEmail(document.parent.properties["n:users"][a]);

		//execute action against a document
		mail.execute(document);
	}
} else {
	logger.log("The notifiable aspect has not been added to the topic: " + document.parent.name;
}

These options should also take into account disabled accounts: (person.isAccountEnabled(userName); The problem with this is that isAccountEnables requires admin privileges.  There is no runAs for this kind of javascript so implementing this would require implementing the action in Java, which is outside the scope of this exercise.

Hopefully, I’ve given you some ideas of ways to approach adding discussion notifications.  The key here in discovering how to approach this was knowing where to look at how discussions are stored in the repository.  In fact this is the key for most any extension to Alfresco:  Asking yourself how does this work for this feature, which is similar to what I need, work in Alfresco? Understanding how this is pulled together from looking at the content model, looking at actual content/nodes through the node browser, understanding the relationships between nodes: parent/child and peer and sometimes digging into the source code can help to expand your understanding when looking to extend Alfresco.


Alfresco: Share Discussion Notification

No Comments »

A few months ago in the Alfresco Forums, a user asked about adding email notification to Share Site Discussions.  We went back and forth working out a rough way to accomplish this (some of this in private messages).  And then a couple of weeks ago the question came up internally.  I passed on what we had figured out and told them I would put together a blog post.

So what are we trying to accomplish?

The Share Site Discussion Components supports the following:

  • Threaded Discussions — Post topic and replies
  • Dynamic Filtering — Tags/New/Hot/All/My Topics
  • RSS Feed for latest discussions

Notifications of new posts can happen over RSS and to the Site Activities Dashlet both of which require access to the system, most likely behind a firewall.  Email notifications can provide updates the don’t require direct access to the system, unless you are looking to update the post.

Where to start?

There are a few things that we need to know as we get started. First, when a site is created in Alfresco Share, the content nodes supporting discussions are not created.  Only after a user accesses the discussion component does it get created.

Second, discussions are created as fm:topic types.  Posts are added as children of fm:topic as a fm:post.

Using a Rule

The simplest way to add notifications is as a rule via the Alfresco Explorer client on the discussion space that is created for the site, under the Sites space.  Adding a rule to the disucssions space matching all content items will result on two emails (one for the creation of the topic and one for the first post).  So we  need to be more specific: We need to expose the Forum Post and  Topic types as Subtypes to the rules engine.  You add fm:topic as well if you want to enable delete notifications

To web-client-config-custom.xml add

<alfresco-config>
    <config evaluator="string-compare" condition="Action Wizards">
        <subtypes>
            <type name="fm:post"/>
            <type name="fm:topic"/>
        </subtypes>
    </config>
</alfresco-config>

Topics are the best choice for building rules on.  Notifications on fm:topic will work for create and delete but not update.  The title of a topic is stored on the first post of a discussion.  You can’t delete individual posts but you can delete a Topic so when specifying a delete notification it can be made directly against a topic,but again…it is easier to just do the same using fm:post.

The notification template would be added to email templates and would look like:

A new post is available in the '${document.parent.childAssocs["cm:contains"][0].properties.title}' dicussion in the '${document.parent.parent.parent.properties.name}' site, it was added by ${person.properties.firstName}<#if person.properties.lastName?exists> ${person.properties.lastName}</#if>.

You can view it through this link:
${url.serverPath}/share/page/site/${document.parent.parent.parent.properties.name}/discussions-topicview?topicId=${document.parent.children[0].name}&listViewLinkBack=true

Email Notification Rule

An email rule is simple to setup but the maintenance cost can be high.  You have to add each user/group manually and Share site groups are not visible to the Explorer client.

The next option is to script the notification.   A Share site group or dynamically build email list of those participating in the discussion.  (A nice to have would be to build in a watch option for those who may not participate in the discussion but want to be notified.)

Mail action sample for group or individual.

The to_many paramater currently only supports one group at a time….if you need to send to multiple people or groups, you’ll need to loop over the to_many or to paramaters and execute against each.

var siteGroup = "GROUP_site_" + document.parent.parent.parent.name;
var template = "Data Dictionary/Email Templates/Notify Email Templates/share_discussion_notification.ftl";

// create mail action
var mail = actions.create("mail");
mail.parameters.to_many = siteGroup;
//mail.parameters.from (with no from address provided the email address of the user triggering the action is used)
mail.parameters.subject="New Post in Discussion: "+document.parent.childAssocs["cm:contains"][0].properties.title;
mail.parameters.template = companyhome.childByNamePath(template);

//execute action against a document
mail.execute(document);

This option also will send email to all users in a group…even if their account has been disabled.

Another option when scripting is to dynamically build the notification list  – send only to those that are part of the discussion.  You can walk the posts through the Child Associations of the topic to build an array of users.  For smaller discussion threads there will not be that high of a cost. (Small here is a relative term, as I don’t have any metrics to say what is small).

var emailAddresses = [];

//for p expresion variable
var p, e, a;

//change to use your template
var template = "Data Dictionary/Email Templates/Notify Email Templates/share_discussion_notification.ftl";

function getEmail(person) {
	var personNode = people.getPerson(person);
	return personNode.properties.email;
}

// build emailAddresses
for (p = 0; p &lt; document.parent.childAssocs["cm:contains"].length; p += 1) {
	var user = document.parent.childAssocs["cm:contains"][p].properties.creator;
	var email = getEmail(user);

	//Is the emailAddress already in the array? If not, add it
	if (emailAddresses.length &gt; 0) {
		var match = false;
		for (e = 0; e &lt; emailAddresses.length; e += 1) {
			if (emailAddresses[e] === email) {
				match = true;
				break;
			}
		}

		if (!match) {
			emailAddresses.push(email);
		}
	} else {
		emailAddresses.push(email);
	}
}

// create mail action
var mail = actions.create("mail");
mail.parameters.subject = "New Post in Discussion: " + document.parent.childAssocs["cm:contains"][0].properties.title;
mail.parameters.template = companyhome.childByNamePath(template);

//send an email for each address
for (a = 0; a &lt; emailAddresses.length; a += 1) {
	mail.parameters.to = emailAddresses[a];
	//execute action against a document
	mail.execute(document);
}

Another option would be to add an aspect to the topic that stores a collection of all the users who have participated in the thread.  (You could also add a place to collect watchers, but you’d also need to extend the UI to allow you to capture the users for this list.)  The aspect would be added on creation on a new topic. (Use Usernames, so that you can avoid  any issues with email addresses changing — Usernames are unmutable).  When a user adds a post, their email address is added to the collection for notification.

var creator = document.properties.creator;

//for expresion variable
var u, a;

//change to use your template
var template = “Data Dictionary/Email Templates/Notify Email Templates/share_discussion_notification.ftl”;

function getEmail(person) {
	var personNode = people.getPerson(person);
	return personNode.properties.email;
}

//Look for match in n:users, if no match add them other wise, ignore the user
function updateUsers(user) {
	if (document.parent.properties["n:users"] === null) {
		document.parent.properties["n:users"] = [];
		document.parent.properties["n:users"].push(user);
		document.parent.save();
	} else {
		var match = false;

		for (u = 0; u < document.parent.properties["n:users"].length; u += 1) {
			if (document.parent.properties["n:users"][u] === user) {
				match = true;
				break;
			}
		}

		if (!match) {
			document.parent.properties["n:users"].push(user);
			document.parent.save();
		}
	}
}

//Check for notifiable aspect
if (document.parent.hasAspect("n:notifiable") {

	//if the user is not already in the n:users property of the notifiable aspect, add them
	updateUsers(creator);

	//create mail action
	var mail = actions.create(“mail”);
	mail.parameters.subject = “New Post in Discussion: ” + document.parent.childAssocs["cm:contains"][0].properties.title;
	mail.parameters.template = companyhome.childByNamePath(template);

	//send an email for each user in n:users
	for (a = 0; a < document.parent.properties["n:users"].length; a += 1) {
		mail.parameters.to = getEmail(document.parent.properties["n:users"][a]);

		//execute action against a document
		mail.execute(document);
	}
} else {
	logger.log("The notifiable aspect has not been added to the topic: " + document.parent.name;
}

These options should also take into account disabled accounts: (person.isAccountEnabled(userName); The problem with this is that isAccountEnables requires admin privileges.  There is no runAs for this kind of javascript so implementing this would require implementing the action in Java, which is outside the scope of this exercise.

Hopefully, I’ve given you some ideas of ways to approach adding discussion notifications.  The key here in discovering how to approach this was knowing where to look at how discussions are stored in the repository.  In fact this is the key for most any extension to Alfresco:  Asking yourself how does this work for this feature, which is similar to what I need, work in Alfresco? Understanding how this is pulled together from looking at the content model, looking at actual content/nodes through the node browser, understanding the relationships between nodes: parent/child and peer and sometimes digging into the source code can help to expand your understanding when looking to extend Alfresco.


Alfresco: Share Discussion Notification

No Comments »

A few months ago in the Alfresco Forums, a user asked about adding email notification to Share Site Discussions.  We went back and forth working out a rough way to accomplish this (some of this in private messages).  And then a couple of weeks ago the question came up internally.  I passed on what we had figured out and told them I would put together a blog post.

So what are we trying to accomplish?

The Share Site Discussion Components supports the following:

  • Threaded Discussions — Post topic and replies
  • Dynamic Filtering — Tags/New/Hot/All/My Topics
  • RSS Feed for latest discussions

Notifications of new posts can happen over RSS and to the Site Activities Dashlet both of which require access to the system, most likely behind a firewall.  Email notifications can provide updates the don’t require direct access to the system, unless you are looking to update the post.

Where to start?

There are a few things that we need to know as we get started. First, when a site is created in Alfresco Share, the content nodes supporting discussions are not created.  Only after a user accesses the discussion component does it get created.

Second, discussions are created as fm:topic types.  Posts are added as children of fm:topic as a fm:post.

Using a Rule

The simplest way to add notifications is as a rule via the Alfresco Explorer client on the discussion space that is created for the site, under the Sites space.  Adding a rule to the disucssions space matching all content items will result on two emails (one for the creation of the topic and one for the first post).  So we  need to be more specific: We need to expose the Forum Post and  Topic types as Subtypes to the rules engine.  You add fm:topic as well if you want to enable delete notifications

To web-client-config-custom.xml add

<alfresco-config>
    <config evaluator="string-compare" condition="Action Wizards">
        <subtypes>
            <type name="fm:post"/>
            <type name="fm:topic"/>
        </subtypes>
    </config>
</alfresco-config>

Topics are the best choice for building rules on.  Notifications on fm:topic will work for create and delete but not update.  The title of a topic is stored on the first post of a discussion.  You can’t delete individual posts but you can delete a Topic so when specifying a delete notification it can be made directly against a topic,but again…it is easier to just do the same using fm:post.

The notification template would be added to email templates and would look like:

A new post is available in the '${document.parent.childAssocs["cm:contains"][0].properties.title}' dicussion in the '${document.parent.parent.parent.properties.name}' site, it was added by ${person.properties.firstName}<#if person.properties.lastName?exists> ${person.properties.lastName}</#if>.

You can view it through this link:
${url.serverPath}/share/page/site/${document.parent.parent.parent.properties.name}/discussions-topicview?topicId=${document.parent.children[0].name}&listViewLinkBack=true

Email Notification Rule

An email rule is simple to setup but the maintenance cost can be high.  You have to add each user/group manually and Share site groups are not visible to the Explorer client.

The next option is to script the notification.   A Share site group or dynamically build email list of those participating in the discussion.  (A nice to have would be to build in a watch option for those who may not participate in the discussion but want to be notified.)

Mail action sample for group or individual.

The to_many paramater currently only supports one group at a time….if you need to send to multiple people or groups, you’ll need to loop over the to_many or to paramaters and execute against each.

var siteGroup = "GROUP_site_" + document.parent.parent.parent.name;
var template = "Data Dictionary/Email Templates/Notify Email Templates/share_discussion_notification.ftl";

// create mail action
var mail = actions.create("mail");
mail.parameters.to_many = siteGroup;
//mail.parameters.from (with no from address provided the email address of the user triggering the action is used)
mail.parameters.subject="New Post in Discussion: "+document.parent.childAssocs["cm:contains"][0].properties.title;
mail.parameters.template = companyhome.childByNamePath(template);

//execute action against a document
mail.execute(document);

This option also will send email to all users in a group…even if their account has been disabled.

Another option when scripting is to dynamically build the notification list  – send only to those that are part of the discussion.  You can walk the posts through the Child Associations of the topic to build an array of users.  For smaller discussion threads there will not be that high of a cost. (Small here is a relative term, as I don’t have any metrics to say what is small).

var emailAddresses = [];

//for p expresion variable
var p, e, a;

//change to use your template
var template = "Data Dictionary/Email Templates/Notify Email Templates/share_discussion_notification.ftl";

function getEmail(person) {
	var personNode = people.getPerson(person);
	return personNode.properties.email;
}

// build emailAddresses
for (p = 0; p &lt; document.parent.childAssocs["cm:contains"].length; p += 1) {
	var user = document.parent.childAssocs["cm:contains"][p].properties.creator;
	var email = getEmail(user);

	//Is the emailAddress already in the array? If not, add it
	if (emailAddresses.length &gt; 0) {
		var match = false;
		for (e = 0; e &lt; emailAddresses.length; e += 1) {
			if (emailAddresses[e] === email) {
				match = true;
				break;
			}
		}

		if (!match) {
			emailAddresses.push(email);
		}
	} else {
		emailAddresses.push(email);
	}
}

// create mail action
var mail = actions.create("mail");
mail.parameters.subject = "New Post in Discussion: " + document.parent.childAssocs["cm:contains"][0].properties.title;
mail.parameters.template = companyhome.childByNamePath(template);

//send an email for each address
for (a = 0; a &lt; emailAddresses.length; a += 1) {
	mail.parameters.to = emailAddresses[a];
	//execute action against a document
	mail.execute(document);
}

Another option would be to add an aspect to the topic that stores a collection of all the users who have participated in the thread.  (You could also add a place to collect watchers, but you’d also need to extend the UI to allow you to capture the users for this list.)  The aspect would be added on creation on a new topic. (Use Usernames, so that you can avoid  any issues with email addresses changing — Usernames are unmutable).  When a user adds a post, their email address is added to the collection for notification.

var creator = document.properties.creator;

//for expresion variable
var u, a;

//change to use your template
var template = “Data Dictionary/Email Templates/Notify Email Templates/share_discussion_notification.ftl”;

function getEmail(person) {
	var personNode = people.getPerson(person);
	return personNode.properties.email;
}

//Look for match in n:users, if no match add them other wise, ignore the user
function updateUsers(user) {
	if (document.parent.properties["n:users"] === null) {
		document.parent.properties["n:users"] = [];
		document.parent.properties["n:users"].push(user);
		document.parent.save();
	} else {
		var match = false;

		for (u = 0; u < document.parent.properties["n:users"].length; u += 1) {
			if (document.parent.properties["n:users"][u] === user) {
				match = true;
				break;
			}
		}

		if (!match) {
			document.parent.properties["n:users"].push(user);
			document.parent.save();
		}
	}
}

//Check for notifiable aspect
if (document.parent.hasAspect("n:notifiable") {

	//if the user is not already in the n:users property of the notifiable aspect, add them
	updateUsers(creator);

	//create mail action
	var mail = actions.create(“mail”);
	mail.parameters.subject = “New Post in Discussion: ” + document.parent.childAssocs["cm:contains"][0].properties.title;
	mail.parameters.template = companyhome.childByNamePath(template);

	//send an email for each user in n:users
	for (a = 0; a < document.parent.properties["n:users"].length; a += 1) {
		mail.parameters.to = getEmail(document.parent.properties["n:users"][a]);

		//execute action against a document
		mail.execute(document);
	}
} else {
	logger.log("The notifiable aspect has not been added to the topic: " + document.parent.name;
}

These options should also take into account disabled accounts: (person.isAccountEnabled(userName); The problem with this is that isAccountEnables requires admin privileges.  There is no runAs for this kind of javascript so implementing this would require implementing the action in Java, which is outside the scope of this exercise.

Hopefully, I’ve given you some ideas of ways to approach adding discussion notifications.  The key here in discovering how to approach this was knowing where to look at how discussions are stored in the repository.  In fact this is the key for most any extension to Alfresco:  Asking yourself how does this work for this feature, which is similar to what I need, work in Alfresco? Understanding how this is pulled together from looking at the content model, looking at actual content/nodes through the node browser, understanding the relationships between nodes: parent/child and peer and sometimes digging into the source code can help to expand your understanding when looking to extend Alfresco.


Alfresco: Share Discussion Notification

No Comments »

A few months ago in the Alfresco Forums, a user asked about adding email notification to Share Site Discussions.  We went back and forth working out a rough way to accomplish this (some of this in private messages).  And then a couple of weeks ago the question came up internally.  I passed on what we had figured out and told them I would put together a blog post.

So what are we trying to accomplish?

The Share Site Discussion Components supports the following:

  • Threaded Discussions — Post topic and replies
  • Dynamic Filtering — Tags/New/Hot/All/My Topics
  • RSS Feed for latest discussions

Notifications of new posts can happen over RSS and to the Site Activities Dashlet both of which require access to the system, most likely behind a firewall.  Email notifications can provide updates the don’t require direct access to the system, unless you are looking to update the post.

Where to start?

There are a few things that we need to know as we get started. First, when a site is created in Alfresco Share, the content nodes supporting discussions are not created.  Only after a user accesses the discussion component does it get created.

Second, discussions are created as fm:topic types.  Posts are added as children of fm:topic as a fm:post.

Using a Rule

The simplest way to add notifications is as a rule via the Alfresco Explorer client on the discussion space that is created for the site, under the Sites space.  Adding a rule to the disucssions space matching all content items will result on two emails (one for the creation of the topic and one for the first post).  So we  need to be more specific: We need to expose the Forum Post and  Topic types as Subtypes to the rules engine.  You add fm:topic as well if you want to enable delete notifications

To web-client-config-custom.xml add

<alfresco-config>
    <config evaluator="string-compare" condition="Action Wizards">
        <subtypes>
            <type name="fm:post"/>
            <type name="fm:topic"/>
        </subtypes>
    </config>
</alfresco-config>

Topics are the best choice for building rules on.  Notifications on fm:topic will work for create and delete but not update.  The title of a topic is stored on the first post of a discussion.  You can’t delete individual posts but you can delete a Topic so when specifying a delete notification it can be made directly against a topic,but again…it is easier to just do the same using fm:post.

The notification template would be added to email templates and would look like:

A new post is available in the '${document.parent.childAssocs["cm:contains"][0].properties.title}' dicussion in the '${document.parent.parent.parent.properties.name}' site, it was added by ${person.properties.firstName}<#if person.properties.lastName?exists> ${person.properties.lastName}</#if>.

You can view it through this link:
${url.serverPath}/share/page/site/${document.parent.parent.parent.properties.name}/discussions-topicview?topicId=${document.parent.children[0].name}&listViewLinkBack=true

Email Notification Rule

An email rule is simple to setup but the maintenance cost can be high.  You have to add each user/group manually and Share site groups are not visible to the Explorer client.

The next option is to script the notification.   A Share site group or dynamically build email list of those participating in the discussion.  (A nice to have would be to build in a watch option for those who may not participate in the discussion but want to be notified.)

Mail action sample for group or individual.

The to_many paramater currently only supports one group at a time….if you need to send to multiple people or groups, you’ll need to loop over the to_many or to paramaters and execute against each.

var siteGroup = "GROUP_site_" + document.parent.parent.parent.name;
var template = "Data Dictionary/Email Templates/Notify Email Templates/share_discussion_notification.ftl";

// create mail action
var mail = actions.create("mail");
mail.parameters.to_many = siteGroup;
//mail.parameters.from (with no from address provided the email address of the user triggering the action is used)
mail.parameters.subject="New Post in Discussion: "+document.parent.childAssocs["cm:contains"][0].properties.title;
mail.parameters.template = companyhome.childByNamePath(template);

//execute action against a document
mail.execute(document);

This option also will send email to all users in a group…even if their account has been disabled.

Another option when scripting is to dynamically build the notification list  – send only to those that are part of the discussion.  You can walk the posts through the Child Associations of the topic to build an array of users.  For smaller discussion threads there will not be that high of a cost. (Small here is a relative term, as I don’t have any metrics to say what is small).

var emailAddresses = [];

//for p expresion variable
var p, e, a;

//change to use your template
var template = "Data Dictionary/Email Templates/Notify Email Templates/share_discussion_notification.ftl";

function getEmail(person) {
	var personNode = people.getPerson(person);
	return personNode.properties.email;
}

// build emailAddresses
for (p = 0; p &lt; document.parent.childAssocs["cm:contains"].length; p += 1) {
	var user = document.parent.childAssocs["cm:contains"][p].properties.creator;
	var email = getEmail(user);

	//Is the emailAddress already in the array? If not, add it
	if (emailAddresses.length &gt; 0) {
		var match = false;
		for (e = 0; e &lt; emailAddresses.length; e += 1) {
			if (emailAddresses[e] === email) {
				match = true;
				break;
			}
		}

		if (!match) {
			emailAddresses.push(email);
		}
	} else {
		emailAddresses.push(email);
	}
}

// create mail action
var mail = actions.create("mail");
mail.parameters.subject = "New Post in Discussion: " + document.parent.childAssocs["cm:contains"][0].properties.title;
mail.parameters.template = companyhome.childByNamePath(template);

//send an email for each address
for (a = 0; a &lt; emailAddresses.length; a += 1) {
	mail.parameters.to = emailAddresses[a];
	//execute action against a document
	mail.execute(document);
}

Another option would be to add an aspect to the topic that stores a collection of all the users who have participated in the thread.  (You could also add a place to collect watchers, but you’d also need to extend the UI to allow you to capture the users for this list.)  The aspect would be added on creation on a new topic. (Use Usernames, so that you can avoid  any issues with email addresses changing — Usernames are unmutable).  When a user adds a post, their email address is added to the collection for notification.

var creator = document.properties.creator;

//for expresion variable
var u, a;

//change to use your template
var template = “Data Dictionary/Email Templates/Notify Email Templates/share_discussion_notification.ftl”;

function getEmail(person) {
	var personNode = people.getPerson(person);
	return personNode.properties.email;
}

//Look for match in n:users, if no match add them other wise, ignore the user
function updateUsers(user) {
	if (document.parent.properties["n:users"] === null) {
		document.parent.properties["n:users"] = [];
		document.parent.properties["n:users"].push(user);
		document.parent.save();
	} else {
		var match = false;

		for (u = 0; u < document.parent.properties["n:users"].length; u += 1) {
			if (document.parent.properties["n:users"][u] === user) {
				match = true;
				break;
			}
		}

		if (!match) {
			document.parent.properties["n:users"].push(user);
			document.parent.save();
		}
	}
}

//Check for notifiable aspect
if (document.parent.hasAspect("n:notifiable") {

	//if the user is not already in the n:users property of the notifiable aspect, add them
	updateUsers(creator);

	//create mail action
	var mail = actions.create(“mail”);
	mail.parameters.subject = “New Post in Discussion: ” + document.parent.childAssocs["cm:contains"][0].properties.title;
	mail.parameters.template = companyhome.childByNamePath(template);

	//send an email for each user in n:users
	for (a = 0; a < document.parent.properties["n:users"].length; a += 1) {
		mail.parameters.to = getEmail(document.parent.properties["n:users"][a]);

		//execute action against a document
		mail.execute(document);
	}
} else {
	logger.log("The notifiable aspect has not been added to the topic: " + document.parent.name;
}

These options should also take into account disabled accounts: (person.isAccountEnabled(userName); The problem with this is that isAccountEnables requires admin privileges.  There is no runAs for this kind of javascript so implementing this would require implementing the action in Java, which is outside the scope of this exercise.

Hopefully, I’ve given you some ideas of ways to approach adding discussion notifications.  The key here in discovering how to approach this was knowing where to look at how discussions are stored in the repository.  In fact this is the key for most any extension to Alfresco:  Asking yourself how does this work for this feature, which is similar to what I need, work in Alfresco? Understanding how this is pulled together from looking at the content model, looking at actual content/nodes through the node browser, understanding the relationships between nodes: parent/child and peer and sometimes digging into the source code can help to expand your understanding when looking to extend Alfresco.