Tag: Alfresco

How I successfully studied for the Alfresco Certified Engineer Exam

Back in March I blogged about why I took the Alfresco Certified Administrator exam (post). Today I passed the Alfresco Certified Engineer exam. I took it for the same reasons I took the ACA exam, as outlined in that post, so in this post, I thought I’d share how I studied for the test.

Let me start off with a complaint: There is nowhere I could find that describes which specific version of Alfresco the test covers. This wasn’t that big of a deal for the ACA exam, but for the ACE exam, I felt a little apprehensive not knowing.

I know Alfresco probably doesn’t want to lock the exam version to an Alfresco version. But the blueprint really needs to give people some idea. Ultimately, I decided 4.1 was a safe bet.

I can’t tell you what was on the test, but I can tell you how I studied.

First, review the blueprint

The exam blueprint is the only place that gives you hints as to what’s on the test. If you look at the blueprint, you’ll see that the test is divided into five areas: Architectural Core, Repository Customization, Web Scripting, UI Customization, and Alfresco API.

The blueprint breaks down each of those five areas into topics, but they are still pretty broad. Some of them helped me figure out what to review and some of them didn’t. For example, under Architectural Core, topics like “Repository”, “Subsystems”, and “Database” were too vague to be that helpful in guiding my study plans.

Next, identify your focus areas

Looking at the blueprint, most of those topics have been in the product since the early days and haven’t changed much. I figured I could take the test cold and pass those. But Share Configuration and Customization has changed here and there between releases. With a lot of different ways to do things, and ample opportunity for testing around minutiae, I figured this would be where I’d need to spend most of my study time. I also wanted to spend time reviewing the various API’s listed under Architectural Core because I typically just look those up rather than commit the details to memory.

To validate where I thought my focus areas should be I took the sample test on the blueprint page, which was helpful.

Now, study

For Architectural Core, I spent most of my time reviewing the list of public services in the Foundation API found in Appendix A of the Alfresco Developer Guide, the JavaScript API (also in Appendix A as well as the official documentation), and the Freemarker Templating API documentation.

For the Repository Customization I figured I had most of that down cold and just spent a little time reviewing Activiti BPM XML and associated workflow content models. The workflow tutorial on this site is one place with sample workflows to review and obviously the out-of-the-box workflows are also good examples.

According to the blueprint, the UI Customization section is now focused entirely on Alfresco Share, so I didn’t spend any time reviewing Alfresco Explorer customization. Instead, I read through the Share Configuration and Share Customization sections of the documentation. There are now tutorials on Share Customization in the Alfresco docs so I went through those again just to make sure everything was fresh. The Share configuration examples in my custom content types tutorial are another resource.

The Alfresco API section consists of questions about the Alfresco REST API and CMIS. This is only 5% of the test so I spent no time reviewing this. I also ignored Web Scripts, figuring my existing knowledge was good enough.

After studying the resources in my focus areas I took the sample test once more. It’s always the same set of questions, so taking it repeatedly isn’t a great way to prove your readiness, but at least you know you won’t miss those questions if they show up on the real test.

Feel ready? Go for it

If you get paid to work with Alfresco, you really ought to take this exam (and the ACA exam). Obviously, what I’ve reviewed here is a study plan for someone who has significant experience with the platform doing real world projects. If you are new to Alfresco you’ll have to adjust your plan and preparation time accordingly. Better yet, get a few projects under your belt first. I think it would be tough for someone with no practical experience to pass the test with any amount of study time, which is the whole point.

So there you go, that’s how I studied. Your mileage will vary based on what your focus areas need to be. Now go hit the books!

Cool things you can do in Alfresco with cmis:item support

allyoursysbaseI’ve been taking a look at the newly-added support for cmis:item in Alfresco. As a refresher for those who may not be familiar, cmis:item is a new object type added to the CMIS 1.1 specification. Alfresco added support for CMIS 1.1 in 4.2 but did not immediately add support for cmis:item, which is optional, according to the spec. Now cmis:item support is available in Alfresco in the cloud as well as the nightly builds of 4.3.a Community Edition.

So what is cmis:item?

We’ve all written content-centric applications that have included objects that don’t have a content stream. You might use one to store some configuration information, for example, or maybe you have some other object that doesn’t naturally include a file that needs to be managed as part of it. There are a few approaches to this in Alfresco:

  1. Create your custom type as a child of sys:base (or cm:cmobject if you don’t mind your objects being subject to the file name constraint)
  2. Create your custom type as child of cm:content and simply do not set a content stream
  3. Ignore the content model altogether and use the attribute service

I’m not going to cover the third option in this post. If you want to learn more about the attribute service you should take a look at the Tech Talk Live we did in April.

The second option is fine, but then you’ve got objects with content properties that will never be set. Your objects look like they want to be documents, but they really aren’t because you don’t expect them to ever have a file as part of the object. Not the end of the world, but it’s kind of lazy and potentially confusing to developers that come after you.

The first option is what most people go with, but it has a drawback. Instances of types that do not extend from cm:content or cm:folder are invisible to CMIS 1.0. Not only are those objects invisible to CMIS 1.0 but relationships that point to such objects are also invisible. Depending on how much you use CMIS in your application this could be a fairly severe limitation.

Thankfully, CMIS 1.1 addresses the issue with a new object type called cmis:item. It’s made precisely for this purpose.

What can I do with it out-of-the-box?

Even if your custom content model doesn’t need a “content-less object” you can still benefit from cmis:item support. Let me walk you through my five favorite object types that you can now work with via cmis:item support in CMIS 1.1 that were not previously available: Category, Person, Group, Rating, and Rule. I tested everything I’m showing you here using Alfresco 4.3.a Community Edition, Nightly Build (4/27/2014, (r68092-b4954) schema 7003) and Apache Chemistry OpenCMIS Workbench 0.11.0.

Category

Man, if I had a bitcoin for every person I’ve seen asking how to get the categories of a document via CMIS, I’d have a garage full of Teslas. With cmis:item support, it’s easy. Here’s how you get a list of every category in the system with a CMIS Query Language (CQL) query:

SELECT * FROM cm:category

That returns a list of cm:category objects that represent each category in the system. Note that this is a flat list. In Alfresco, categories are hierarchical. I’m not sure what the plan to address that is, to be honest.

Now suppose you have an object and you want to know the categories that have been assigned. Here is some Groovy code that does that:

(Can’t see the code? Click here)

Categories live in a property called “cm:categories”. It’s a multi-value field that contains the Alfresco node references for each category that has been assigned to a document. Once you get that list you can iterate over them and fetch the cm:category object to figure out the category’s human-readable name. That’s pretty cool and wasn’t possible before CMIS 1.1 support.

How about assigning existing categories to documents? Sure, no problem. Here’s how.

(Can’t see the code? Click here)

This is a little Groovy function that takes a full path to a document and the name of a category. Obviously it depends on your categories being uniquely-named.

First, it finds the category by name using CQL and snags its Alfresco node reference.

Next, it checks to see if the cm:generalclassifiable aspect has already been added to the document and adds it if it has not.

Finally, it gets the current list of categories from the cm:categories property and adds the new category’s node reference to it.

I started to look at creating new categories with CMIS, but it isn’t immediately obvious how that would work due to the hierarchical nature of categories. The only parent-child association supported by CMIS is the one implied by folder-document relationship. I’ll ask around and see if there’s a trick.

That’s it for categories. Let’s look at Person objects next.

Person

Here’s another frequently-asked how-to: How do I query users through CMIS? Before cmis:item support you couldn’t do it, but now you can. For example, here is how you can use CQL to find a list of the Person objects in Alfresco:

SELECT * FROM cm:person

You can qualify it further based on properties of cm:person. For example, maybe I want all users who’s first name starts with “Te” and who work for an organization that starts with “Green”. That query looks like:

SELECT * FROM cm:person where cm:firstName like 'Te%' and cm:organization like 'Green%'

Suppose you wanted to find every Alfresco Person object that has a city starting with “Dallas” and you want to set their postal code to “75070”. Ignoring cities named “Dallas” that aren’t in Texas (like Dallas, Georgia) or the fact that there are multiple zip codes in Dallas, Texas, the code would look like this:

(Can’t see the code? Click here)

That’s it for Person objects. Let’s look at Group objects.

Group

Similar to querying for Person objects, cmis:item support gives you the ability to query for groups–all you have to know is that groups are implemented as instances of cm:authorityContainer. Here’s the query:

SELECT * FROM cm:authorityContainer

Unfortunately, it doesn’t seem possible to use CMIS to actually add or remove members to or from the group. Maybe that will change at some point.

Rating

It’s easy to query for ratings:

SELECT * FROM cm:rating

But it’s hard to do anything useful with those objects because, at least in this nightly release, you can’t follow the relationships from or two a rating. As a sidenote, you can get the count and total for a specific node’s ratings by getting the value of the cm:likesRatingSchemeCount and cm:likesRatingSchemeTotal properties respectively. But that’s not related to cmis:item support.

Rule

Rules are a powerful feature in Alfresco that help you automate document handling. Here’s how to use a query to get a list of rules in your repository:

SELECT * FROM rule:rule

Rules are so handy you might end up with a bunch of them. What if you wanted to find all of the rules that matched a certain criteria (title, for example) so that you could enable them and tell them to run on sub-folders? Here is a little Groovy that does that:

(Can’t see the code? Click here)

First the code uses a join to find the rule based on a title. The property that holds a rule’s title is defined in an aspect and that requires a join when writing CQL.

Then the code simply iterates over the result set and updates the rule:applyToChildren and rule:disabled properties. You could also set the rule’s type, description, and whether or not it runs asynchronously. It does not appear to be possible to change the actions or the filter criteria for a rule through CMIS at this time.

What about custom types?

Custom types? Sure, no problem. Suppose I have a type called sc:client that extends sys:base and has two properties, sc:clientName and sc:clientId. Alfresco automatically makes that type accessible via CMIS. It’s easy to create a new instance and set the value of those two properties. Here’s the groovy code to do it:

(Can’t see the code? Click here)

Did you notice I created the object in a folder? In Alfresco, everything lives in a folder, even things that aren’t documents. In CMIS parlance, you would say that Alfresco does not support “unfiled” objects.

The custom object can be queried as you would expect, including where clauses that use the custom property values, like this:

SELECT * FROM sc:client where sc:clientId = '56789'

In CMIS 1.0 you could not use CMIS to work with associations (“relationships”) between objects that did not inherit from either cm:content (cmis:document) or cm:folder (cmis:folder). In CMIS 1.1 that changed. You can create relationships between documents and folders and items. Unfortunately, in the latest nightly build this does not appear to be implemented. Hopefully that goes in soon!

Summary

The new cmis:item type in CMIS 1.1 is a nice addition to the specification that is useful when you are working with objects that are not documents and do not have a content stream. I showed you some out-of-the-box types you can work with via cmis:item but you can also leverage cmis:item with your custom types as well.

Support for cmis:item requires CMIS 1.1 and either Alfresco in the cloud or a recent nightly build of Alfresco 4.3.

Quick Hack: Creating default folder structures in Alfresco Share sites

Someone recently asked how to create Alfresco Share sites with a default folder structure. Currently, out-of-the-box, when you create an Alfresco Share site, the document library is empty. This person instead wanted to define a set of folders that would be created in the document library when a new Alfresco Share site is created.

Although this exact functionality is not available out-of-the-box, there is something similar. It’s called a “space template”. Space templates have been in the product since the early days but haven’t yet been exposed to Alfresco Share. In the old Alfrexsco Explorer client you could specify a space template when you created a new folder, and the resulting folder would have the same set of folders and documents that were present in the template.

With a little bit of work using the out-of-the-box extension points we can get Alfresco to use space templates when creating new sites in Share. I’ve created this as an add-on and the code lives on GitHub. I thought it might be instructive to review how it works here.

Approach

The first thing to realize is that a space template isn’t special. It’s just a folder that happens to live in Data Dictionary/Space Templates. In fact, there aren’t any API calls specific to space templates. If you go looking for a createFolderFromTemplate() method on a Folder object you’ll be disappointed. When the Alfresco Explorer client creates a folder from a template, it simply finds the template folder and copies its contents into the newly-created folder.

On the Alfresco Share side, a site isn’t that special either. It’s just a special type of folder. The document library that sits within a Share site is also just a folder, albeit a specially-named folder with an aspect. Normally, the document library folder does not get created until the first user actually opens the site and navigates to the document library.

So all we really need to do is write some code that gets called when a site is created, looks up the space template, and then creates the document library folder with the contents of the space template.

What’s the best way for the code to know which space template to use? One way would be to use a specially-named space template for all Share sites. But using a single space template for all Share sites seems limiting. Alfresco Share already has a mechanism for selecting the “type” of site to create–it’s called a preset. So a better approach is to use the preset’s ID to determine which space template to use.

Code

We need to run some code when a site is created. One way to do this is with a behavior. The behavior is Java code that will be bound to the onCreateNode policy for nodes that are instances of st:site. The init() method does that:

// Create behaviors
this.onCreateNode = new JavaBehaviour(this, "onCreateNode", NotificationFrequency.TRANSACTION_COMMIT);

// Bind behaviors to node policies
this.policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"), TYPE_SITE, this.onCreateNode);

So any time a node is create that is an instance of st:site, the onCreateNode() method in this class will get called.

The first thing the onCreateNode() method needs to do is find the space template. To keep things simple, I’m going to assume that the space template is named the same thing as the site preset ID, so all I need to do is grab that preset ID and do a Lucene search to find the template:

NodeRef siteFolder = childAssocRef.getChildRef();

if (!nodeService.exists(siteFolder)) {
logger.debug("Site folder doesn't exist yet");
return;
}

//grab the site preset value
String sitePreset = (String) nodeService.getProperty(siteFolder, PROP_SITE_PRESET);

//see if there is a folder in the Space Templates folder of the same name
String query = "+PATH:\"/app:company_home/app:dictionary/app:space_templates/*\" +@cm\\:name:\"" + sitePreset + "\"";
ResultSet rs = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_LUCENE, query);

If there aren’t any space templates the method can return, otherwise, the space template should be copied into the site folder as the new document library folder:

documentLibrary = fileFolderService.copy(spaceTemplate, siteFolder, "documentLibrary").getNodeRef();

//add the site container aspect, set the descriptions, set the component ID
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
props.put(ContentModel.PROP_DESCRIPTION, "Document Library");
props.put(PROP_SITE_COMPONENT_ID, "documentLibrary");
nodeService.addAspect(documentLibrary, ASPECT_SITE_CONTAINER, props);

I used the fileFolderService to perform the copy, then I set the properties and aspect on the new document library folder with the values that Alfresco Share expects a document library to have.

I’ve left out some insignificant code bits here and there–you can look at the source if you need to. The project was bootstrapped using the Alfresco Maven SDK and includes a unit test that makes sure the behavior works as expected.

Result

The project creates an AMP file. If you’ve checked out the source you can build the AMP using mvn install. Then you can install the AMP into the Alfresco WAR by placing the AMP in the amps directory and run apply_amps.sh. Alternatively, you can use mvn alfresco:install. After installing the AMP into the Alfresco WAR you can test it out.

Out-of-the-box Alfresco Share has a single site type called “Collaboration Site”. The preset ID for that type of site is “site-dashboard”. You might have additional site types configured in your installation. To set up the template for the default collaboration site, navigate to Data Dictionary/Space Templates and create a folder called “site-dashboard”. Then add whatever folders and documents you want to that folder. Now, anytime a “Collaboration Site” gets created in Alresco Share, the document library will automatically be created with the same folders and documents you’ve set up in your space template.

This was not a mind-blowing, deeply-technical extension to Alfresco. Hopefully, it shows you that, with even a few lines of code, you can easily add useful functionality to Alfresco.

Updated tutorial: Creating custom advanced workflows in Alfresco

I have published a major revision of my “Creating custom advanced workflows in Alfresco” tutorial. Major changes include:

  • The tutorial now uses the Alfresco Maven SDK to instantiate the projects and to produce and install AMPs.
  • The tutorial no longer refers to jBPM, except to give brief historical context as to why the platforms includes both jBPM and Activiti.
  • The tutorial no longer includes any references to the old Alfresco Explorer client, except where it pertains to using the Alfresco Workflow Console, which is only available as part of the Alfresco Explorer Client.
  • Significant wordsmithing and re-organization to improve style and clarity.

I have tested the steps and the code against Alfresco 4.2.e Community Edition and version 5.14 of the Activiti Process Designer for Eclipse.

By the end of the tutorial, you will know how to:

  • Create, deploy, and run business processes using Activiti embedded within Alfresco.
  • Configure Alfresco Share to display custom forms when starting Activiti workflows or managing workflow tasks.
  • Use the Alfresco Workflow Console to deploy process definitions, start workflows, and delete workflows.
  • Add business logic to your process definitions using JavaScript and Java.
  • Assign workflow tasks to users and groups.
  • Add timers to your process definitions to take an action automatically after a specific time period.

The tutorial assumes you already know how to use the Alfresco Maven SDK. If you have never used it, take a look at this tutorial. The workflow tutorial also assumes you have worked through the Custom Content Types, Custom Actions, and Intro to Web Scripts tutorials.

The source code and the tutorial itself reside in GitHub. If you find problems or want to make improvements, please fork the project, make the change, and send me a pull request.

Updated tutorial: Introduction to Alfresco Web Scripts

I have published a new version of my Introduction to Web Scripts tutorial. This is a major revision that refactors the tutorial to leverage the Alfresco Maven SDK and AMPs. In addition, I have done a little bit of reorganization to improve clarity and a lot of wordsmithing to make the tutorial more consistent with the others in the Alfresco Developer Series.

By the end of this tutorial, you will know how to:

  • Extend Alfresco with your own custom RESTful API.
  • Write web scripts that respond to GET, POST, and DELETE requests over HTTP/S and return data in both HTML and JSON.
  • Use the web scripts console to display documentation and debug info on your custom and out-of-the-box web scripts.
  • Make AJAX calls to your custom web scripts.

The tutorial assumes you already know how to use the Alfresco Maven SDK. If you don’t, take a look this tutorial.

The tutorial text and all of the source code related to it are on GitHub. If you see problems or opportunities for improvement, please fork the project and send me a pull request.

Combine the qualitative and the quantitative with Alfresco and Pentaho

Yesterday we had our monthly Tech Talk Live session. The topic was “Business Intelligence for ECM Practitioners” and it was all about how BI can be applied to the data that lives in your Alfresco repository. We were joined on the panel by Francesco Corti, who is experienced in both ECM and BI, as well as John Iball, the product manager for Alfresco One.

Here is the broadcast in case you missed it:

During the broadcast we saw Francesco’s AAAR solution which extracts data from Alfresco and puts in a data warehouse. Pentaho, an open source Business Intelligence platform, is then used to create a dynamic dashboard that end users can use to interactively answer business questions about that data. Francesco showed a live demo of reporting using Alfresco auditing data, but with his CMIS connector for Pentaho, you could report on anything stored in the repository.

John Iball shared with us that reporting and analytics was one of the top features requested during a recent discussion with 70 or so Alfresco customers. Rather than very basic reporting features, John said that customers want the ability to do deeper analytics on the data stored in Alfresco. Some will already have BI platforms in place. Others will benefit from a complete solution. Either way, John says that meeting this need is high on Alfresco’s roadmap right now.

About Tech Talk Live

Tech Talk Live occurs on the first Wednesday of every month barring a holiday or some other conflict. During each episode, the Alfresco community team focuses on a specific topic and invites panelists from Alfresco, partners, and the broader community to take part in the conversation. The session is broadcast to the public live on Google Hangouts on Air with Q&A taking place simultaneously in #alfresco on freenode IRC. Check the wiki for links to prior and future episodes. The Alfresco events page also has entries for Tech Talk Live.

Our next episode will be on March 5 when Nathan McMinn joins us to recap some of the killer projects that were created during the Alfresco Summit 2013 hack-a-thon.

Updated tutorial: Implementing Custom Behaviors in Alfresco

I have published a major revision to my Implementing Custom Behaviors in Alfresco tutorial. I hadn’t really touched it since 2007–behaviors, the ability to bind programming logic to types, aspects, and events in Alfresco, haven’t changed at all since then.

The changes are mainly around using the Alfresco Maven SDK to produce AMPs and the addition of unit tests to the project. I also gave it a bit of a style scrub to make it more consistent with other tutorials.

The tutorial continues with the SomeCo example. In this tutorial you will create the content model and behavior needed to implement the back-end for SomeCo’s five star rating functionality. By the end of this tutorial you will know:

  • What a behavior is
  • How to bind a behavior to specific policies such as onCreateNode and onDeleteNode
  • How to write behaviors in Java as well as server-side JavaScript
  • How to write a unit test that tests your behavior

This tutorial, the source code that accompanies it, and the rest of the tutorials in the Alfresco Developer Series reside on GitHub. If you want to help with improvements, fork the project and send me a pull request.

Next week I hope to publish a major revision of the Introduction to Web Scripts tutorial.

Updated tutorial: Creating Custom Actions in Alfresco

I have published an updated version of the Creating Custom Actions in Alfresco tutorial. Similar to the recently updated Working With Custom Content Types in Alfresco tutorial, this version has been updated to match the refactored code which now assumes you are using the Alfresco Maven SDK to produce AMPs and that you are using Alfresco Share as the user interface. I’ve removed all references to Alfresco Explorer.

The Custom Actions tutorial covers:

  • What is an action
  • How to write your own custom action in Java
  • How to invoke the custom action from a rule or from the Alfresco Share UI
  • Configuring an evaluator to hide the UI action when certain conditions are true
  • Configuring an indicator to show an icon in the document library when documents meet certain conditions
  • Writing and executing unit tests with the Alfresco Maven SDK

If you aren’t familiar with the Alfresco Maven SDK and you need help diving in, take a look at this tutorial.

All of the tutorial source code and text for the Alfresco Developer Series of tutorials is on GitHub. Please fork the project, make improvements, and send me pull requests.

Next on the to-be-updated list is the Custom Behaviors tutorial. I expect that to go live sometime next week.

Updated tutorial: Working with Custom Content Types in Alfresco

The Working with Custom Content Types tutorial has just been given a major revision. I’ve updated it to match the refactored code. Here is a summary of the high-level changes:

  • Instructions now assume you are using the Alfresco Maven SDK. If you haven’t played with the Alfresco Maven SDK yet, check out my recently published tutorial on the subject.
  • Removed all mention of Alfresco Explorer. The tutorial is now exclusively focused on Alfresco Share for the user interface part.
  • Removed all mention of the Alfresco Web Services API. The tutorial is now exclusively focused on CMIS as the preferred API for performing CRUD functions against the Alfresco repository.

The code and the tutorial text reside in GitHub. If you find issues or make improvements, please fork the repository and send me a pull request.

Let’s stop writing books about Alfresco Explorer

I used to be of the opinion that when it came to books about Alfresco, the more, the better. But with about a dozen on the market at this point, I think it is probably past time to start focusing on quality over quantity.

A big driver of quality is relevance. We’re at a point where the old web client, Alfresco Explorer, is no longer relevant to any new project and most existing implementations. And yet Alfresco Explorer keeps showing up in new books. Come on, people. Alfresco Share made its debut in Alfresco Labs 3a way back in September of 2008. Granted, it needed a few releases before it became the preferred web client, and there are still a few minor things you cannot do in Share, but Alfresco Explorer has been virtually unchanged since then.

Alfresco Share is the preferred web client and has been for quite some time. Yes, there are people who still run old versions of Alfresco. Yes, there are people who like JavaServer Faces. But I’m pretty sure the existing catalog has those folks well-covered. I’d rather see authors spending their energy (and the readers’ time) elsewhere.

I say it all of the time and it seems like it ought to be common knowledge, but I’ll repeat it: No new customizations should be happening with Alfresco Explorer. Talking about Alfresco Explorer customizations is almost a disservice to the community, so let’s stop.

From time-to-time, publishers ask me to review book proposals. I know many of you get the same emails. Let’s all make a stand: No more green lights for books that feature significant coverage of Alfresco Explorer from here on out. Sound good?

Alfresco Explorer was a great web client in its day. It’s not that I dislike it at all. I’m just saying it’s time to say goodbye. So let’s all bid a fond farewell and let it go gently into the good night. We can remember it fondly over drinks at meetups, but for goodness sake, let’s stop writing about it.