Tag: Alfresco

Alfresco Surf Code Camp comes to Boston

We’ve just announced a new Code Camp date and city. We’ll be doing a Code Camp on Alfresco Surf in Boston on December 16th (Code Camp Details). Our New York City event filled quickly so if you want to attend you should sign up now. Hope to see you there.

BTW, the Code Camp in Munich looks like it will be some time during the week of January 12th. Obviously it will be posted here, at Optaros.com, and at Alfresco.com when it finalizes.

Alfresco Surf 3.0 Code Camp NYC in the bag

What a great group of campers we had today at the Alfresco 3.0 Surf Camp in New York City. We were all quite impressed with how much the attendees were able to get accomplished.

It’s clear that experience with web scripts sets one up for success with Surf. Surf is a two-tier framework. The repository tier is exposed to the presentation or Surf tier through web scripts running on the repository. So, obviously experience writing repository tier web scripts is directly applicable. On the presentation tier, it is still MVC and web scripts, but Surf adds page definition and templating constructs. The biggest challenge for people already familiar with web scripts, then, is to learn the presentation tier lingo and to sort out the numerous XML files, what they do, where they go, etc.

The best way to learn Surf seems to be to start with a simple Share dashlet then work up to building a complete web site. The dashlet gives you a chance to practice with the Surf JavaScript API and to make remote calls to the Alfresco repository (or other HTTP end points). Once you get the hang of that, try to build a simple one page web site that maybe queries some data from the repository and formats the results. Then, broaden from there.

Anyway, thanks to everyone that attended or helped put this together.

Importing ACP files with the UUID binding

A common way to get files and metadata into and out of the Alfresco repository is to use ACP (Alfresco Content Package) files. People that work with ACP files quickly find out that the out-of-the-box ACP import action will only import a given object once–it won’t update an object if it is already in the repository. By default, the import action tries to create a new object on every import. If a like-named object already exists the import will fail.

There’s a simple fix for this. The underlying API actually supports updating objects by matching on UUID. All you need to do is create your own version of the import action that uses the UUID_BINDING rather than the default.

Here are the steps:

  1. Find the org/alfresco/repo/action/executer/ImporterActionExecuter.java class in the source.
  2. Make a copy of the class. I called mine com.optaros.action.executer.UpdatingImportExecuter.java.
  3. Find the line of code that reads: this.importerService.importView(importHandler, new Location(importDest), null, null);
  4. Change it to: this.importerService.importView(importHandler, new Location(importDest), REPLACE_BINDING, null);
  5. Now add the definition for REPLACE_BINDING which is a private static inner class:
private static ImporterBinding REPLACE_BINDING = new ImporterBinding()
{

    public UUID_BINDING getUUIDBinding()
    {
        return UUID_BINDING.UPDATE_EXISTING;
    }

    public String getValue(String key)
    {
        return null;
    }

    public boolean allowReferenceWithinTransaction()
    {
        return false;
    }

    public QName[] getExcludedClasses()
    {
        return null;
    }

};

As with any action, the last step is to add the Spring configuration:

<bean id="updating-import" class="com.optaros.action.executer.UpdatingImportExecuter" parent="action-executer">
    <property name="importerService">
        <ref bean="ImporterService"/>
    </property>
    <property name="nodeService">
        <ref bean="NodeService"></ref>
    </property>
    <property name="contentService">
        <ref bean="ContentService" />
    </property>
    <property name="mimetypeService">
        <ref bean="mimetypeService"/>
    </property>
    <property name="fileFolderService">
        <ref bean="FileFolderService"/>
    </property>
</bean>

After deploying your changes and restarting the application server you can test the new action. In my case, I wrote a workflow that used JavaScript to execute the export action to export the documents in the workflow. I then simulated an external system operating on the ACP file by writing a quick Perl script to unzip the ACP, inject some metadata into the ACP’s XML manifest, and then zip it back up. At this point the ACP file contains the exported objects (with their associated UUID) and some new metadata. I then trigger the next step in the workflow which, again, uses JavaScript to execute the newly-written Updating Import Action. Unlike the OOTB import, when this one runs Alfresco finds the existing objects based on the incoming UUID’s and instead of trying to create new objects, it updates the old objects with the new metadata in the ACP file. Problem solved.

You can learn more about writing custom Actions on the Alfresco Wiki and in Chapter 4 of the Alfresco Developer Guide available at Packt Publishing or your favorite online book seller.

Surfing in D.C. with Alfresco’s new web application framework

If you are in Washington, D.C. today for Alfresco’s North American Community Conference please be sure to stop me and say hello. I’ll be in the sessions, in the Optaros booth, and presenting during the technical track. For my European friends, I’m sad to say that I will not be in Munich for the European version of the conference next week, but I will be in Europe at the end of this year or the first part of January so we can meet up then.

During the technical track (in both D.C and Munich) we will be showing some Surf 3.0 components we’ve built. One is called “Status”. It is kind of like a Facebook status or a Basecamp Journal entry. It allows you to say what you’re working on right now and what your mood is. A dashlet aggregates the status entries for all of your teammates across the site and another one shows status across all sites to which you are a member. When you mark it “Done” that status is archived. When status changes are made the new Activity Service is called so that if people are following site activity by subscribing to the activity feed, the status changes are included.

We’ve also got a simple “bookmark” component that lets you share URLs with other team members. As it exists right this second it allows a team to manage a set of shared bookmarks. Before it is GA we plan to make sure bookmarks are taggable and rateable and marked as shared or private.

I’ll follow up soon with a deconstruction of these components so you can learn more about how they work (and even contribute code to them if you want to make improvements).

Finally, we plan to leverage the Rating Service that was used as an example in the forthcoming Alfresco Developer Guide as the back-end for a five star rating component that would allow any Share or Surf site to enable users to rate any node.

These components will be available for you to add to your Share sites or any site built with Alfresco Surf. Our goal is to have them generally available by the time 3.0 Enterprise ships.

We’ll also be hosting code camps in North America and Europe so that you can learn to build your own Surf components. I’ll provide more details on those as they are available.

Running Alfresco web scripts as Liferay portlets

I’ve seen a lot of Liferay and Alfresco forum posts from people having trouble getting Alfresco running within a Liferay portal. Once that’s done, people usually want to invoke Alfresco web scripts as portlets without requiring a separate single sign-on (SSO) infrastructure. Some people have pointed to the Alfresco wiki (Deploying 2.1 WAR Liferay 4.3). That is a helpful reference but it isn’t the full story. Here are some notes that may help.

1. Download the Liferay 4.3.6 + Tomcat 5.5 JDK5 bundle. I had mixed results with the latest release 4.4.2. You may be tempted to try to download the WAR-only distribution and configure it in your existing Tomcat instance. In this case, save yourself the time and headache and get the bundle. Fool with the WAR distribution later.

2. Unpack the Liferay distribution and fire it up. Make sure you can log in as the test@liferay.com (password: test) user to validate that all is well with the Liferay install.

2a. Create a test user. (“Create Account” on the Liferay login screen). Remember the email address. This will matter shortly. For this discussion I’ll assume Foo User with a screen name of fuser and an email address of fuser@foo.com. Make sure you create a home directory. In this example, we’ll call it “fuser”.

2b. Verify that you can log in as your test user.

3. Shut down the server.

4. Download Alfresco 2.1.2 Enterprise, WAR only. Alfresco 2.1.1 has a known issue (AWC-1686) with the way authentication is handled for web scripts in the context of Liferay so make sure you are using 2.1.2.

5. Expand the Alfresco 2.1.2 WAR into the Tomcat webapps/alfresco directory (which you’ll have to create the first time). If you are tweaking the install (such as pointing to a specific MySQL database, using something other than MySQL, pointing to a different data directory, etc.) make sure you have copied your good set of extensions into Tomcat’s shared/classes/alfresco/extension directory.

6. Copy the MySQL connector into Tomcat’s common/lib directory.

7. Start Tomcat. When it comes up, you’ll have Liferay running and you’ll have Alfresco running, but Liferay doesn’t yet know about Alfresco. Verify that you can log in to Alfresco as admin.

7a. While you are here, create a test user account. You need to create a user account that has an email address that matches the test user account you created in Liferay. In this example you created Foo User with a screen name of fuser and an email address of fuser@foo.com so you need to create an Alfresco user with the same settings. You’ll log in to Alfresco as fuser. You’ll log in to Liferay as fuser@foo.com.

7b. Verify that you can log in to Alfresco as fuser.

8. Shut down Tomcat.

9. Now you need to configure Alfresco as a Liferay plug-in. This involves adding four files to Alfresco’s WEB-INF directory: liferay-display.xml, liferay-plugin-package.xml, liferay-portlet.xml, and portlet.xml. Why aren’t these available in the Alfresco source or on the wiki? Apparently someone tried to address this at some point because there is a link on the wiki but it is broken. Until that’s addressed, I’ve put them here.

10. Remove the portlet-api-lib.jar file from Alfresco’s WEB-INF/lib directory.

11. Re-package alfresco.war. It is now ready to hand over to Liferay.

12. Start Tomcat.

13. Find your Liferay deploy directory. If you are running out-of-the-box on Linux, Liferay’s “deploy” directory is called liferay/deploy and it resides in the home directory of the user who started Tomcat. I’m running it as root so my Liferay deploy directory is /root/liferay/deploy.

14. Copy the alfresco.war you just created into the deploy directory. Watch the log. You should see Liferay working on the WAR. He’s finding the plug-in config files and essentially deploying the Alfresco portlets.

15. Now log in to Liferay using the Liferay admin account (test@liferay.com). Go to a page, then use the global navigation dropdown to select “Add Content”. The list of portlets should appear and you should see the “Alfresco” category. If you don’t, look at the log because something is amiss. Add the My Spaces portlet to the page. You may see an error at this point but ignore it. The problem is you probably don’t have a user in Alfresco that has an email address of “test@liferay.com”, which is the currently-logged in user.

16. Log out.

17. Log in as your test user that exists in both Alfresco and Liferay (fuser@foo.com).

18. Go to the page. You should see the “My Spaces” portlet. You should be able to upload content, create spaces, etc.

Exposing your own web scripts as portlets

All Alfresco web scripts are automatically exposed as JSR-168 portlets, including the ones you create. To add your web scripts as portlets, first make sure you have authentication set to “user” and transaction set to “required” in your web script’s descriptor. Then, update portlet.xml, liferay-portlet.xml, and liferay-display.xml. Follow the pattern that’s in those files already and you’ll be fine. For example, if you deploy the Hello World web script from my web script tutorial, you need to add a new portlet to portlet.xml with a “scriptUrl” like: /alfresco/168s/someco/helloworld?name=jeff. Then you update liferay-portlet.xml and liferay-display.xml with the new portlet name or portlet ID.

Single sign-on with no single sign-on?

The web script runtime has a JSR-168 authenticator. So when your web scripts get invoked by the portlet, the current credentials are passed in. That’s why your web script can run without requiring an additional sign in. Prior to this being put in place, people had to implement Yale CAS (or an equivalent) to get SSO between Liferay and Alfresco web scripts.

What’s not covered in these instructions is that you’ll probably want to (1) configure both Alfresco and Liferay to authenticate against LDAP and (2) change the configuration of either Alfresco or Liferay to use the same credential (either username or email address) for both systems so that if you do have users logging in to both, they don’t have to remember that one requires the full email address but the other doesn’t.

Troubleshooting

If you see one of the Alfresco portlets displaying “Data is not currently available” or somesuch, try hitting
Alfresco in another tab. Log in, then log out. Then go back to the
portal and open the page again. It should work now. I’m not sure what’s going on there. I think it may have to do with me switching back-and-forth between Liferay instances (4.3.2 versus 4.4.2) so maybe you won’t see it.

Open issues

You may see an error like this:

21:22:15,965 WARN [BaseDeployer:1038] Unable to format /usr/local/bin/liferay-4.3.6/temp/20080408212212978/WEB-INF/faces-config-jbpm.xml: Error on line 5 of document file:///usr/local/bin/liferay-4.3.6/temp/20080408212212978/WEB-INF/faces-config-jbpm.xml : A ‘)’ is required in the declaration of element type “application”. Nested exception: A ‘)’ is required in the declaration of element type “application”.

I haven’t chased that down yet. I’ll update this post with a comment when I find out. I’m sure fixing that will also fix the problem that you’ll see if you try to start an advanced workflow from a piece of content displayed in the My Spaces portlet.

I was also seeing an error when trying to use the “Add Content” link in the straight Alfresco client. I think it is JSF-related. Again, I’ll update this post with a comment when it is resolved (or when I find a Jira ticket).

Know the way to San Jose?

I’ll be at the Alfresco Community Conference in San Jose next week. I’ll speak for a bit on the Endeca solution at the BarCamp the night before. The day of the conference I’ll be moderating the web scripts discussion. If you’re attending as well be sure to say hello.

Speaking of web scripts, hopefully you are busy hacking together an entry for Alfresco’s Web Scripts Developer Challenge. I’m one of the judges so if you stick a link to ecmarchitect.com somewhere in your code maybe I’ll give you bonus points.

Two worlds collide in Boston

Okay, so maybe it wasn’t so much a collision as it was a harmless grazing. Even though there wasn’t a lot of crossover between the two, I’d still like to offer, as Mel Brooks put it, a “firm embrace” to whomever had the bright idea to co-locate DrupalCon with AIIM this week in Boston because I think (I hope) it gave the old guard of ECM some exposure to and appreciation for the wave of innovation taking place in the space.

Unfortunately, I don’t think there was as much crossover from the AIIM attendee side as there was from the Drupal side. There were a few curious DrupalCon’ers that ventured in to the AIIM exhibition hall. One person shouted out, “Hell yeah, Open Source!” as she walked by the Alfresco/Optaros booth, giving me a sort of “fight the power” gesture as she passed (at least that’s how I interpreted the gesture). Others stopped to chat at our little oasis of open source in the closed source desert. The serious under-representation of open source at the AIIM conference was a major topic of conversation.

I doubt we’ll ever see full integration between the two conferences–90% of the DrupalCon attendees were technical developers and integrators while AIIM attendees are mostly IT and business people evaluating or looking to purchase ECM solutions.

Another big difference is scope. AIIM, as an organization and as a conference, has gotten way too broad, at least for my own interests. A scanner that knows how to open sealed envelopes before imaging the contents is really cool, it’s just not a typical component of the solutions I implement.

Forgive the tangent, but this problem goes beyond AIIM to “ECM” itself. As Alan Pelz-Sharpe of CMSWatch pointed out in one of his sessions (the CMSWatch sessions were far and away the best sessions), almost everything under the sun calls itself “ECM” from source code control to imaging to records management in addition to my focus areas, WCM, Portal, Search, Collaboration, and Document Management. Indeed, the term was invented, largely by vendors, as a way to group all of those types of solutions together in an attempt to convince buyers that having one vendor that could do all of those things is better than buying from individual niche vendors.

I don’t deny a need for an association or a conference that can help people solve problems in these areas but how much overlap can there really be between people interested in microfiche and those looking to implement Web 2.0? One of our clients in the media industry stopped by the booth and asked me, “Are you sure this is the biggest content management conference of the year?”. He was having trouble finding the relevant content management information lost in the noise of vendors hawking copiers, scanners, and printers.

Anyway, back to the “two worlds” topic, the idealist in me hopes a sort of ping pong diplomacy took place. Perhaps the DrupalCon attendees were the New York Philharmonic to AIIM’s North Korea. Maybe a few of the suits learned something from the insightful questions being asked by the messenger bag crowd. Rather than be annoyed, I was actually encouraged by the legacy ECM vendor who came to our booth and grilled me on Alfresco and how it compared to their product. I tried to get her to renounce her faith right then and there but she was tough.

AIIM could do more to encourage that kind of cultural exchange and maybe even foster the innovation that old school ECM so sorely lacks. It sounds like the Rocky Mountain Chapter is planning on offering some open source topics soon. That’s great and I hope to see that happen in other chapters. And while we’re at it, maybe AIIM could offer free floor space to non-commercial open source projects. Just a thought.

At the very least, I hope the physical presence of 800 – 900 people passionate about open source content management was a jarring reality check to AIIM, legacy ECM vendors, and the larger community.

On a tactical note, a nice side-benefit of the co-located conferences was that the Optaros folks attending DrupalCon were able to put in some booth time on the AIIM show floor. If you want to read more about some of the DrupalCon sessions, check out John Eckman’s blog.