Alfresco: Share Discussion Notification
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 < 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 > 0) {
var match = false;
for (e = 0; e < 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 < 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.
Perhaps only notify an individual if they have subscribed to that topic (like the Alfresco Forum works). This gives the user the ability to turn the subscription on/off as desired versus automatically sending to anyone that has participated in that discussion. I’d set the default to “subscribed” whenever someone participates in a topic.
On a related note, the Mail Action will need to be able to handle HTML. And if the Javascript interface to the Mail Action supported Asych, that would also be a nice improvement to improve responsiveness of the UI when sending to a large group.
Steve G.
2 Jun 11 at 5:20 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Similar idea to the watch list option mentioned in the post. It requires UI work which they didn’t want to do at the time. I might be able to tackle it as a follow up post.
I believe this was fixed
Rules can be set to run in the background. This is equivalent to running them asynchronously.
jared
2 Jun 11 at 8:28 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Regarding async, yes, I see your point regarding use within a rule. I was working with a javascript behaviour that needed to use the mail action, in that context, there is no parameter exposed for async.
Regarding mail action html, I tried it in 3.4b and html wasn’t supported, but have not gone back to see if works in more recent release.
Thanks for your reply.
Steve G.
2 Jun 11 at 9:33 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Awesome post! Would there be a dumbed down version of how I can implement this? Coming from a Windows only environment and having 0 knowledge of Java/API language(s) it’s hard to follow your posts at times. Don’t get me wrong, I love Alfresco out-of-the-box. It’s customizing and implementing processes (described in topic) that make it a bit cumbersome.
One day I’ll get the hang of this system. =)
Nicolas T
18 Aug 11 at 3:13 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
I don’t think there is an easier way at the moment. There is discussion/work around a messaging service in the next release of Alfresco.
jared
18 Aug 11 at 3:18 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Thanks very much for posting this, worked fine in Alfresco Community 4.0.b.
I think the email template is missing a couple of dollar signs in the second para.
Alistair Miles
3 Apr 12 at 6:00 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Thanks! I’ve updated it now.
jared
3 Apr 12 at 7:11 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
I also wanted to include the content of the post, with HTML tags stripped out, and based on this post on regular expressions in freemarker, I did this:
${document.content?replace(“]+(>|$)”, “”, “r”)}
Alistair Miles
3 Apr 12 at 2:06 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>