Monday, May 18, 2015

Content Management Lifecycles in Nuxeo


Lifecycle Versus Workflow

Content lifecycle refers to the states or phases that content transitions through over time. A lifecycle can be visually represented using UML or a state transition diagram.  Content states act as identifiers that can be used by search.  They're also often used to control content access permissions.  Additionally, the change of content state can cause actions to be triggered.

Content lifecycles shouldn't be confused with workflows.  Workflows are used to manage the processes that occur within and between lifecycles.  They model the flow of tasks associated with a process.  Very often content will be attached to a workflow and acted on as part of the completion of the tasks that make up the workflow.

It's a best practice to cleanly separate lifecycle and workflow when implementing content management.  Workflow typically is the thing that enables content to move through the different states of its lifecycle.  But because lifecycles and workflows work closely together, it's easy to focus on building workflows without explicitly modeling the states of the lifecycle.

Very often lifecycle is something that is implicitly modeled in content management systems.  In that case, metadata schemas or content models will be extended to include a state variable to track the state of the content.  Workflows are then built to update state information as content moves through a process.

Nuxeo Content Lifecycle

In Nuxeo the concept of content lifecycles comes prewired as part of the Studio web-based configuration tool.  Within Studio you can define the states in the lifecycle and the transition paths between them.

Here is a screenshot from Studio of the default lifecycle state transition diagram.  For the default lifecycle, possible states include project, obsolete, deleted and approved.


Using Studio, let's now define a new lifecycle called ReviewCycle with states called Draft, Review, Reject, Available, Obsolete as follows:


This new lifecycle can then be associated with a new document type definition called ReviewType using Studio configuration forms.  All ReviewType documents on creation will automatically be associated with this new lifecycle definition.  Within Studio, in the definition of the document type, we reference the ReviewCycle Lifecycle as follows:

In this case our new document type is defined as inheriting from the standard File type.  We'll make no other changes, other than to define a different label and icons associated with the new document type.

To briefly see how our new document type and lifecycle with states work, we can create a simple demonstration using an Automation Chain that transitions a ReviewType document that is in the initial state Draft to the state Review.  Note that the name of the transition between these two states is called to_Review.

Next let's create a button in the UI that the user can press that we can program to change the state of the document.  To do that we create a User Action as follows:

Here we've uploaded a new icon of a right-pointing arrow that will be visible as a button to click on that will then run our Automation Chain.  Note that we specify that the button should be visible only when a document is in the lifecycle state of Draft.  At the bottom of the definition form we specify the Automation Chain that the button is associated with: setReviewLifeCycle.

Now let's run our test.  We first create our new document type called ReviewType (label is Review):


On the summary page for the document we can see that the state of the document is initially Draft.  Next to the permanent link icon on the document Title header we can see a new icon that we've added that when clicked on will automatically transition the state of the lifecycle to Review.


When we click on this transition icon, the summary page refreshes and the document is then in the Review state.  Note that the transition icon button is no longer displayed in the Title.


One more thing we can demonstrate is how easy it is to then programatically locate all documents that are in a specific lifecycle state.  We can power up the Nuxeo shell and run an NXQL query against the repository to find all documents that are in the Review state.  In our case, it will be just the one we uploaded and transitioned.

We can see that in the next screenshot where we issue an NXQL query.  The query finds the document (called Application) that we've just uploaded and transitioned into the Review state.

Select * from Document where ecm:currentLifeCycleState = 'Review'.


Saturday, May 9, 2015

Nuxeo Automation Scripting with Nashorn

In March, Nuxeo Platform Fast Track 7.2 was released.  One new feature of that release is Automation Scripting enabled by Java 8 and Nashorn.

Nashorn replaces the Rhino Javascript scripting engine in Java 8.  Nashorn is based on JSR 262 and provides better compliance with the ECMA normalized Javascript specification.  Compared to Rhino, the performance and memory usage of Nashorn is significantly better.

Thierry Delprat wrote a blog that introduces Nuxeo Automation Scripting with Nashorn.

I wanted to test out the new feature, so I thought that I'd apply scripting to a "drop folder" use case.  In a "drop folder"scenario, an action is triggered that processes and then files documents as they are created and dropped into a folder.

This is typically how imports from a capture product like Ephesoft are handled.  For example, in the Ephesoft case, scanned images are written to a repository folder using CMIS, and then a rule or action associated with the folder pushed to from Ephesoft further processes document metadata and ultimately files the document into a target folder.

The Drop Folder Scenario

I wanted to see how easy it would be to use Nuxeo Automation Scripting to create a "drop folder" script.

For this test scenario, I created two Workspaces in Nuxeo called "Drop Folder" and "Target Folder".



Under Target Folder, I created the following three folders:


The idea for the test script is that documents will be uploaded into the "Drop Folder".  Based on the mimetype of the document, it will be moved into the "Target Folder" area and filed under "PDF Files" if the document is PDF, under "Word Files" if the mimetype is Word, or otherwise filed under "Other Files".

In addition, the description field for the document will be updated with information stating that the document was autofiled and the time the file was made.

Implementation

To implement this, in Nuxeo Studio, I first created an Automation Script called "OnImportScript" and an Event Handler called "OnDocImport".


The event handler tracks the "Document created" events and is triggered when items are created in the folder "/default-domain/workspaces/Drop Folder".  The operation that is run when the event is triggered is called OnImportScript.

Here is the screen for configuring the event handler.


Next I filled in the Javascript code that runs when the event triggers.  The Javascript code for the OnImportScript is shown below.  In that code, the run() method will be called to start the processing.

The run() method identifies the location of Target Folder and moves the document based on the mimetype.


var WORD_MIMETYPE = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
var PDF_MIMETYPE = "application/pdf";

var WORD_FOLDER = "Word Files";
var PDF_FOLDER = "PDF Files";
var OTHER_FOLDER = "Other Files";
var TOP_TARGET_FOLDER = "Target Folder";

//  Get the parent node for the current node
function getParentFolder(node)
{
    // Get the parent reference -- org.nuxeo.ecm.core.api.DocumentRef
  var parent = node.getParentRef();
  return Repository.GetDocument(node, { 'value': parent.toString() });  
}

// Return a child of a folder by name
function getChildByName(fldr, childName)
{
  if(! fldr.getDocumentType().isFolder() ) return null;
  
  var children = Document.GetChildren(fldr, {});

  if(children==null) return null;
  
  for each(var child in children)
  {
      if(child.getName().equals(childName))  return child;
  }

  return null;
}

function getFolderChild(fldr, childName)
{
  var child = getChildByName(fldr, childName);
  if(child)  return child;
  
  // Create the child folder if it doesn't exist
  if(! fldr.getDocumentType().isFolder() ) return null;
  return Document.Create(fldr, { 'type': 'Folder', 'name': childName });

}

//  ctx --  java.util.HashMap
//  input -- org.nuxeo.ecm.core.api.impl.DocumentModelImpl
//  params --  java.util.HashMap
function run(ctx, input, params) {
  
  // Event is instance of org.nuxeo.ecm.core.event.impl.EventImpl
  // If the event isn't "Create Document", then return
   if(!ctx.Event.getName().equals("documentCreated")) return;
   
  //  If the input isn't a document, then return
  if( input===null || !input.getClass().getName().equals("org.nuxeo.ecm.core.api.impl.DocumentModelImpl") ) return;
  
  // If the input is a folder and not a document, ignore it
  if( input.getDocumentType().isFolder() ) return null;
  
  // Collect some information about the document
  var doc = input;
  var docBlob = Document.GetBlob(doc, {});
  var mimeType = docBlob.getMimeType();
  
  var dropFolderNode   = getParentFolder(doc);
  var dropFolderParent = getParentFolder(dropFolderNode);
  var targetFolder =  getFolderChild(dropFolderParent, TOP_TARGET_FOLDER); 
  
  // File the incoming document based on its mimetype
  var targetFolderSub;
  if(mimeType.equals(PDF_MIMETYPE))
  {
      targetFolderSub = getFolderChild(targetFolder, PDF_FOLDER);
  }
  else if(mimeType.equals(WORD_MIMETYPE))
  {
      targetFolderSub = getFolderChild(targetFolder, WORD_FOLDER);
  }
  else
  {
     targetFolderSub = getFolderChild(targetFolder, OTHER_FOLDER);
  }
  
  //  File the document by moving it to the target folder
  if(targetFolderSub != null)
  {
     // Move the document to the correct target folder
     Document.Move(doc, { 'target': targetFolderSub.getId()} );
     var now = new Date();
     Document.SetProperty(doc,{"xpath":'dc:description', "save":true, "value":"Autofiled from folder '" + dropFolderNode.getName() + "' at " + now.toLocaleString()  });
  }

} 

Results

To test, I created documents in the Drop Folder and saw that the Document created event successfully triggered and filed the documents to the correct target folders based on the mimetype.

I was happy with the results, although my testing was minimal.  The purpose of this was just to see how Nuxeo Automation Scripting works and to validate that the approach could be used for creating Javascript-based folder rules, although the approach isn't limited to just putting rules on folders.

Wednesday, November 26, 2014

Deploying Nuxeo IDE Customizations

In this article, I have a tip for deploying Nuxeo customizations developed  using the Nuxeo Eclipse IDE.

But first let me first mention a few things about how to go about customizing Nuxeo.

There are two main methods for customizing the Nuxeo web application:
  • Nuxeo Studio
  • Nuxeo Eclipse IDE plugin

Nuxeo Studio

Nuxeo Studio is a cloud-based configuration tool.  It's available by subscription, but it's not required to have in order to use Nuxeo.  Actually, without Studio you don't miss out on any of the cool end-user Nuxeo product features, but by using it, you can save yourself a tremendous amount of development and administration time, and you'll also know that your configurations made in Studio are guaranteed to be automatically upgradable to future product versions of Nuxeo.  

Nuxeo without Studio is much harder.  If you don't use Studio you'll need to hand-code and debug quite a few configuration files.  That can involve writing a lot of XML, XHTML and other code, easily hundreds of lines of code even for simple configurations.  And while manually creating those files isn't really that complex, writing those files can be tedious, and it's easy to introduce syntax errors while writing them that may end up later costing you many hours of time trying to track down and fix. 

You can do a lot with Studio, and if you're serious about Nuxeo, you really should use it.  We've found, for example, that most projects we work on start out by developing a custom content model and designing associated create, view and edit forms.  With Nuxeo Studio, an analyst could easily build and test the content model and all associated forms without needing assistance from a developer.  Studio also allows you to graphically design workflows, set up automation tasks, and a lot more.

Nuxeo IDE

For many installations, using Nuxeo Studio for configuring your application is sufficient, but if you need to do even more in-depth customizations than what Studio lets you do, you can use Nuxeo's Eclipse plugin.  Nuxeo has great on-line documentation showing you how to use it.  Unlike Studio, the Nuxeo IDE is a tool that targets Java developers.

Using the Nuxeo Eclipse IDE you can extend and override parts of the Nuxeo application.  From a Nuxeo perspective within Eclipse you can create a Nuxeo project and then add artifacts to it.  You can then deploy your project changes, launch Tomcat and run and debug the Nuxeo application, all within the Eclipse environment.

Deploying the Project Bundle

Now for the tip.  

It's easy to hot reload Nuxeo projects within Eclipse using the Nuxeo IDE plugin.  That feature really speeds up development.  But when deploying your IDE-developed customizations to a new Nuxeo instance, there's an additional deployment file that you need to have in your project.

Nuxeo has an option in the IDE to jar all the files of your project.  To deploy your changes, you just create the jar and then drop it into the nxserver/bundles directory of your new instance and restart.

The option to jar is available by first right-clicking on your project in the Eclipse Nuxeo perspective and then selecting Nuxeo and Export Jar.


That's easy enough.  But there's one more thing you need to do to prepare the jar file for deployment on another server, and if you don't do it, you're likely to run into problems.  This step isn't needed when you're developing and deploying from within Eclipse and is easy to overlook when reading Nuxeo's explanation for how to use the Nuxeo Eclipse IDE.

Every time Tomcat is restarted, the nuxeo.war directory under nxserver will get redeployed and expanded.  Because of that, any files you may have attempted to manually add to the nuxeo.war area after a deployment will be lost the next time the war is redeployed.  

A feature of the Nuxeo Eclipse IDE is that after the war is expanded, the project's web asset files from the src/main/resources/web directory of your project will be automatically copied into the war area, modifying the standard Nuxeo instance with your customizations.


But when deploying your project jar file to another Nuxeo instance, if you just copy over the jar to the new instance, the web asset files in your jar won't be visible to Tomcat.  Similar to what is done automatically for you with the Eclipse hot reload, the web asset files need to be placed within the expanded war.  This can be done using the deployment-fragment.xml file.  This file needs to be placed in your project at the top of the directory src/main/resources/OSGI-INF, for example:


Here's an example of what you can put in that file:

<?xml version="1.0"?>
<fragment version="1">
  
  <extension target="application#MODULE"> 
    <module> 
      <java>${bundle.fileName}</java> 
    </module> 
  </extension>  
  
  <require>all</require>
  <install>
    <delete path="${bundle.fileName}.tmp"/>
    <unzip from="${bundle.fileName}" to="${bundle.fileName}.tmp"/>
    <copy from="${bundle.fileName}.tmp/web/nuxeo.war" to="/"/>
    <append from="${bundle.fileName}.tmp/OSGI-INF/I18n/com.formtek.nuxeo.xrefs.messages.properties" to="nuxeo.war/WEB-INF/classes/messages_en_US.properties" addNewLine="true"/>
    <append from="${bundle.fileName}.tmp/OSGI-INF/I18n/com.formtek.nuxeo.xrefs.messages.properties" to="nuxeo.war/WEB-INF/classes/messages_en.properties" addNewLine="true"/>
    <delete path="${bundle.fileName}.tmp"/>
  </install>  
</fragment>

You can see that the install section of the code unjars your bundle and copies over all assets that are under the web directory of your project to the corresponding area of the expanded nuxeo.war.

That's it.  With the deployment-fragment.xml file in place, your bundle will be correctly deployed into the target Nuxeo instance when Tomcat starts up.

The content of the deployment-fragment.xml file looks something like an ant build file.  It describes tasks that are run when the bundle file is loaded.  Some of the things that you can script in this file include:

    • unzip or unjar files 
    • create folders 
    • move files
    • delete files and folders
    • append files

[Note that there was a problem in the initial release of Nuxeo 6.0 for handling hot reloads.  Future releases are fixed.  For the 6.0 release, this JIRA explains a workaround.]

Monday, November 17, 2014

Nuxeo 6.0 and Elasticsearch

Lucidworks' Lucene and Solr have been the dominant open source search options for the last decade.  Solr is now widely used and is tightly integrated with many products, like those from Alfresco and PTC, and it's used by a wide variety of companies and organizations like Comcast, Disney, Goldmansachs and the FCC.  Here at Formtek, we've integrated it into Formtek Orion software too.

But Solr isn't the only viable open-source search option any more.  For example, it got my attention earlier this year when ECM vendor Nuxeo upgraded the search capabilities of their core product to use Elasticsearch in their 5.9.3 fast track release.  That Elasticsearch integration is officially available now in the Nuxeo long term support (LTS) 6.0 release and was just made available this week.

Solr Versus Elasticsearch

What makes Elasticsearch attractive as a technology?

There's actually a lot of similarities between Solr and Elasticsearch technologies. Both Elasticsearch and Solr are built on top of Lucene, and they're both Java-based Apache-licensed open source software. The feature sets for both of them are very comparable, partly because they're both built on top of Lucene.  Both technologies offer:
  • Java API and REST
  • Faceting
  • Highlighting
  • Replication
  • Distribution
But despite the similarities, or maybe because of them, Elasticsearch has seen tremendous growth in mindshare over the last two years. Google Trends shows that Elasticsearch interest surpassed interest in Solr in 2014.  So, at this point, while the Solr community is significantly bigger and Solr is more mature, Elasticsearch is growing quickly and is expected to grow even faster, especially now after Elasticsearch received $70 million of venture funding in June 2014.

Compared to Solr, opinions about ElasticSearch are often that it is simpler to configure and administer, it's use of REST and JSON is more intuitive, and it is built on an architecture that was designed from the ground up for distributed scaling.

Nuxeo Implementation of Elasticsearch

Some of the benefits of Elasticsearch derived by Nuxeo in their 6.0 release include:

  • Faster full text search
  • Query features like facets, geo location, and "more results like this"
  • Consistency with Nuxeo's NXQL query language
  • Ability to aggregate data for running reports and generating statistics
  • Highly scalability horizontally by adding Elasticsearch nodes
Eric Barroca, Nuxeo CEO, commented that "with Elasticsearch, we have separated the query engine from the database, which has major implications for architectural flexibility and performance.  Because Elasticsearch scales horizontally, the Nuxeo Platform now has virtually infinite scalability.”


Eventual Consistency


When working with Alfresco and Solr implementations I first ran into the problem of 'eventual consistency'.  I like Nuxeo's solution for this with their Elasticsearch implementation.

In short, the problem is that a repository which uses an external search engine often takes time to update the search indexes after any changes are made in the repository.  As a way to make client software seem more responsive, repositories like Alfresco and Nuxeo separate out the process of updating the search index from the database transaction. 

'Eventual consistency' or 'asynchronous indexing' refers to a small gap of time, often just seconds, between when a database operation occurs and when the request to update the search index to reflect the data changes is queued and then finally processed.  Ultimately both the database and search index will be consistent.

In Alfresco 4.0 you had to choose a search engine: either Lucene or Solr.  Lucene searches were 'in transaction' so that database and search indexes were always consistent, while Solr searches would use 'eventual consistency'.  Depending on your use case, it was possible to choose either the Solr or Lucene implementation, and that one engine would then be used for all queries.  But with Alfresco 5.0 Lucene is no longer available, so 'in transaction' consistency is no longer an option.

For most use cases, eventual consistency doesn't cause a problem.  But it means that if a query were to fire off immediately after a database update, the search results may not be totally consistent with what's actually in the database.

With Nuxeo 6.0 there are two ways to search data in the repository:
  • Elasticsearch index query, and
  • Direct Relational or No-SQL database query
Based on your use case, with Nuxeo, you can control which of these types of queries to run.  Elasticsearch queries will be fast but use 'eventual consistency'.  Queries made directly to the database will likely be slower, but provide assurance that the results are totally accurate.

Nuxeo 6.0 allows you to decide which of the two types of queries will be used, either database or Elasticsearch, and both of the query types can be used at different points in the same client application.


Wednesday, November 12, 2014

Nuxeo Platform 6.0 is Released

Version 6.0 of the Nuxeo platform was officially released today.

Nuxeo has been pretty busy over the last year and they've added some innovative features to their enterprise content management (ECM) platform that really set them apart from other ECM vendors.

While a number of the big features in the new Nuxeo release have been available via 'Fast Track' preview releases made periodically since last December, those features will now all officially roll up and become part of the fully-supported Nuxeo product feature set going forward.

Some of the major highlights of the Nuxeo platform 6.0 release include:

Elasticsearch - extremely scalable and distributed search engine.  Enables hierarchical faceted search.

Collections - a light-weight folder-like object for grouping documents.  Bulk operations like export and download can then be applied to to the collection

MongoDB - optional NoSQL-backend storage offering high flexibility, easy sharding and replication

Mule Connector - enables Nuxeo Automation operations to be inserted inside a Mule Flow, allowing easy integration with other software platforms like Salesforce, Marketo, SAP, and Magento

User Interface Enhancements - including a spreadsheet editor and lightbox support.

CMIS - supports CMIS 1.1 specification, like the new JSON browser binding

Mobile APIs - includes native client SDKs for iOS and Android, including offline sync

Javascript API - includes two implementations, one for node.js and another for jQuery

SAML2 and OAuth 2.0 - enables secure authentication for client applications

AES Encryption - encrypts content with an AES algorithm before moving into the store

A complete list that documents the changes and new features of the Nuxeo platform 6.0 release can be found in the product release notes here.

Nuxeo's Josh Fletcher will also be giving an overview next week on the Nuxeo 6.0 release in a webinar on November 18th.

You can also test drive the latest release here (login: Administrator/Administrator).

The next step in Nuxeo's open product roadmap is just two months away with the 7.1 Fast Track release planned for mid-January 2015.

Monday, November 10, 2014

CMIS Document Migration with Apache Chemistry and Camel


The Headache of Data Migration 

Migration of data between different content repositories can be difficult.  The primary goal of a migration project is to move as losslessly as possible the stored files, associated metadata and filing hierarchy from one system into another.  But data migration can be challenging.

Migrations typically require that an analyst first create a detailed map for how document types and properties will be transferred between the two systems, and then a developer implements that strategy by writing a migration script.  The actual migration process can be tedious and involve a sequence of imports and exports and things like parallel intermediate files or databases which hold normalized property data.

Something Easier: The Apache Camel camel-cmis Component

Recently while looking at how to migrate content stored in an Alfresco repository into a Nuxeo repository, I came across a blog article by Bilgin Ibryam about the Apache Camel project connector for CMIS, a component he contributed to the Camel project.  I was impressed by how he was able to define in just two lines of Java code a program that could move all the data from an Alfresco repository into Nuxeo by recursively iterating through the folder hierarchy starting at the repository root node, and preserving the hierarchy in the move.

While an indiscriminate migration of all content from one repository into another wasn't exactly what I was looking for, I did find that the camel-cmis component was a good starting point for creating a simple migration tool that could move content easily between CMIS compliant repositories.

Besides the repo-to-repo copy, the camel-cmis component also has the ability to identify groups of documents by using a CMIS query and can then pipe the document data from the result set into the next processing step of a Camel route.

Migrating Engineering Documents from Alfresco to Nuxeo

My goal was to be able to successfully migrate into Nuxeo engineering documents which were stored in Alfresco and defined by a content model and document type based on Alfresco aspects.

To do that, I tweaked the camel-cmis component to accept source and target folders, rather than migrate all documents from the repository starting at the repository root.

I modified the camel-cmis component to accept custom metadata properties, and by using CMIS 1.1 'secondary-types' Alfresco aspect data can also be handled.  Both Nuxeo and Alfresco understand CMIS 1.1.

And finally, I created a simple Camel Message Translator (Java bean) that maps the names of the document types and properties extracted from Alfresco to the names in the content model that are used by Nuxeo.  In this case, the property name translations were defined in a simple key-value property file which, when applied, maps the extracted property names before passing them into Nuxeo.



With that it's then possible to write a simple Camel route that defines a migration of data under an Alfresco folder to a Nuxeo folder:
    
from("cmis://http://54.198.64.173/alfresco/api/-default-/public/cmis/versions/1.1/atom?username=admin&password=admin&folderId=744385f3-27fd-4096-a29a-e6108d35cfa0")
    .to("bean:translate")
    .to("cmis://http://localhost:8080/nuxeo/atom/cmis?username=Administrator&password=Administrator&folderId=66d138e4-b0e6-41ee-91c2-aa6fc5991c5e");

This Camel route recursively copies the contents of a specified Alfresco folder and its children to a folder in the Nuxeo repository, maintaining the folder hierarchy.  The following screenshots show how documents and folder structure were moved from an Alfresco Share folder into Nuxeo.



Documents in Alfresco Share

Documents Migrated to Nuxeo

You can see that the documents moved from Alfresco were all engineering AutoCAD DWG files.  The files, custom metadata, and foldering hierarchy were copied into Nuxeo.  Then within Nuxeo we can see the migrated documents.  Also, through a configuration of Nuxeo, we are able to display the engineering metadata and render the AutoCAD file content as both thumbnails and preview images.

Using CMIS tools, and software plug-ins for engineering data management and AutoCAD document management, Formtek can assist organizations with ECM migration to the Nuxeo platform.

Footnotes on CMIS and Camel

The use of CMIS makes it easy to interact with compliant content repositories in a standard way.  It enables the easy sharing of content between repositories from different vendors  CMIS is based on a web services interface that accepts either REST or SOAP protocol.

The Apache Chemistry project provides open source implementation of the CMIS standard.  Both the Alfresco and Nuxeo implementations of CMIS  are based on the Chemistry libraries.  Chemistry offers CMIS server libraries only available for Java.  CMIS client libraries exist for Java, Python, PHP, .NET and ObjectiveC, but the Java libraries are the most complete and best tested.

Apache Camel is an open source framework for implementing Enterprise Integration Patterns (EIP).  It lets you use messaging and transport models like HTTP, ActiveMQ, JMS, JBI, SCA, and CXF to grab data, transform and move it to different end points.


Wednesday, November 21, 2012

Synchronization of File Properties with Alfresco Metadata Properties

At Formtek we recently had a request to customize Alfresco Share for synchronizing document header properties with corresponding metadata for documents stored in Share.

I'm not able to share the code from the project here, but I thought that outlining the basic concept of the project here would serve as an example of some of the things which are possible to implement within Share.

There were a number of requirements for this project, but two two of the core ones were:

  1. Synchronize on uploads and metadata updates the properties of Microsoft Office (Word/Excel/PowerPoint all versions) and PDF files with corresponding metadata for the document in Alfresco.
  2. Provide a method for 'publishing' a synchronized document into another location as a PDF.  The file header of the published document should bring along with it the values for the Alfresco metadata at the time the document was published.
When I talk about file content header properties, I'm referring to the types of properties that can be set in the header of Microsoft Office and PDF files.  For example, the next figure is a screenshot in Microsoft Word 2010 for setting the standard (Title, Author, keywords, and subject) properties and custom properties.


Properties and custom properties can be similarly defined in PDF files.

Property Extraction on File Upload
When one of these files with properties/custom properties is uploaded into Alfresco, the document that is created captures this additional data based on a mapping properties file that is configured.

Within Share, the metadata would get mapped to something similar to the following panel view of the property data in the Share document detail window.


In this case, the mapping file that specifies how mapping from the content file to the Alfresco metadata properties is as follows:


Property Updates on Alfresco Metadata Edits
This mapping of properties on upload resembles standard Alfresco property extraction, or a special version of it that also accepts and knows how to map the custom property values.  But what is different is that two way synchronization with the properties in the file also occurs.  Note that the mapping also correctly handles the datatypes in the mapping, like boolean, text, number and date.

The property mapping is bi-directional so that when properties are updated in Alfresco, the electronic file associated with the document will be rewritten.  That means that the next time the document is downloaded, the properties in the file will be consistent with the corresponding properties in Alfresco.

Publishing to PDF
When a synchronized file is rendered as a PDF file and 'published', the user can select the location of a folder in the current or different Share site.  Actually for our customization, we call the 'publish' action 'transfer' to avoid confusion with the 'Publish' action already available in Share.

The user clicks on the 'Transfer to...' action for the document to start the process.


After that the user selects the target location of the published PDF document using a re-engineered Copy/Move to dialog from Share:


The rendered PDF file is then available as a new document in the target location.


When we download the file associated with this document and open it into Adobe Reader, we can examine the settings of the file properties.

The standard properties in the newly created PDF file are shown as:


And custom properties are seen here:


Tracking Published Documents

Within the original document, we also keep track of when the document has been published.  A panel in the document details page in Share for the original document now shows how many times the document has been published/transferred and to where.


Monday, November 19, 2012

Book Review: "Intelligent Document Capture with Ephesoft" by Pat Myers and Ike Kavas


Intelligent Document Capture with Ephesoft is a new book from Packt Publishing.  The primary authors of the book are Pat Myers, executive vice president of Zia, and Ike Kavas, founder and CTO of Ephesoft and also former Kofax employee.  Myers and Kavas together developed the Ephesoft training program.

What is Ephesoft?  Ephesoft software is used to process and capture paper, email and fax documents for use within ECM, ERP and other enterprise software systems.  ECM systems supported by Ephesoft include Alfresco, FileNet, SharePoint, and generic CMIS repositories.  Ephesoft's capabilities include document classification, separation, and data extraction.

Ephesoft is Open Source software and similar in functionality to proprietary systems like IBM-DataCapEMC CaptivaKofax, and Athento.  It is built from Open Source components like Spring DM, Hibernate, Lucene, and jBPM.

At only 161 pages, this book on Ephesoft uses a format that's considerably shorter than many other technical books, and because of the large number of screenshots it contains, it is a relatively quick read.

The book provides a high-level overview of Ephesoft and describes a path that users can take to get an Ephesoft document capture system up and running quickly.  After finishing this book, the reader will have enough background to get started with building their own capture projects based on Ephesoft.   But that's not to say that this book is a definitive reference for Ephesoft.  Actually, there is much more detailed documentation available on-line that can be found in the Ephesoft wiki pages.  Free on-line training is also available from Ephesoft via the YouTube-based Ephesoft University.

The book consists of the following chapters:
  1. Introduction
    Discusses document capture history, benefits of capture, and a description of some typical high-ROI document capture use cases like mortgage loan processing, claims processing, and the handling of invoices and sales orders.
    At a high level, and in a way not specific to Ephesoft, the book describes different document classification methods like the use of barcodes, image layout classification, keywords, and content analysis.
    Similarly the book explains different types of extraction methods, like zonal OCR (optical character recognition), keywords, position information, and the look up of supplemental information from databases and other systems.
  2. A Quick Tour of Ephesoft
    This chapter describes each of the five tabs in the Ephesoft administrative user interface [see also the on-line Ephesoft Admin Manual]:
        - Batch Class Management
        - Batch Instance Management
        - Workflow Management
        - Folder Management
        - Reports
    It also describes the four tabs of the Operator User Interface [see also the on-line Ephesoft User Manual]:
        - Home/Batch List
        - Batch Details
        - Web Scanner
        - Batch Upload
    The description for each tab is based on a screenshot followed up with details about how to use the features available on the tab.
    This chapter is made available for free by Packt as a sample of the book and can be found online here.
  3. Creating a Batch Class
    This chapter gives an example of how to create a new batch class from the Ephesoft administrative user interface.
    The standard Ephesoft mailroom automation batch template is copied and modified to create a new custom batch class.  Then a new document type for that batch is added and configured.  With training, Ephesoft is able to recognize the document type for automatic classification and separation.
    With configuration, Ephesoft can extract content from scanned images and map the extracted data as key/value pairs to fields for the document type.  Field data can also be validated with validation rules using regular expressions.
  4. Processing a Batch
    This chapter uses the batch class created in chapter 3 and shows how incoming documents for this batch class can be processed.  Batch processing is performed from the Operator's interface.
    This is the shortest chapter in the book.  It shows how a batch is started, and from the Operator's interface, how the review and verification steps are performed.
  5. Core Ephesoft Features
    I found the book to become more interesting after this point, because starting in this chapter the examples are a bit more detailed.
    For example, there is information here about the different types of document classification and how to configure them: Search, Image, Barcode, Automatic, and Programmatic.
    Also discussed is how, once document and field data have been captured, how to export that information into a repository (primarily via CMIS) or database.
  6. Ephesoft Extended Features
    This chapter gets into more advanced features available in Ephesoft.  For example, it describes some features of classification based on image and barcode recognition that are a bit more advanced than the techniques described in chapter 5.
    The Enterprise version of Ephesoft includes an integration with OpenText's RecoStar OCR engine -- this chapter describes how to enable and configure the option.
    Discussed here are product extension points where the user can write Java 'scripts' which customize and change standard product behavior.
    The chapter also talks about how the base Ephesoft product can be extended with plugins and how to write new custom plugins.
  7. Tips
    The final chapter collects a variety of general tips and pieces of information to optimize your use of Ephesoft.  It contains troubleshooting hints like how to configure logging and how to monitor batch processes.  It also discusses how to configure Ephesoft to use authentication with LDAP and Active Directory.
Would I recommend this book?  I'd highly recommend it to someone that is not currently familiar with Ephesoft and who wants to jump start their use of the product.  But existing users of Ephesoft probably won't find too much new information here.

Again, while almost all the information presented in the book can be found elsewhere on-line, the advantage of the book is that the information is presented here in a directed and easy-to-consume format.  What's missing from the book though are more in-depth examples and perhaps more information about reporting and working with scanners.


Support for the Ephesoft Enterprise edition is available via an annual subscription. [Assistance with Ephesoft is also available from partners.  Formtek is an Ephesoft Platinum partner and we have a number of successful Ephesoft implementations.]



Friday, May 18, 2012

Book Review: 'Alfresco Share' by Amita Bhandari

Alfresco Share - Enterprise Collaboration and Efficient Social Content Management is a new book from Packt Publishing which became available in March 2012.  The book's authors are Amita Bhandari, Vinita Choudhary, and Pallika Majmudar, all consultants at Cignex.

This is the first third-party publication devoted entirely to a general discussion of Alfresco Share.  It provides a detailed feature summary of the Share product.  And surprisingly, the book already describes version 4 of Share, which is the latest major version of the Share Enterprise software and which was only just released in February.
The book targets readers that are new to Alfresco.  While much of the book's content doesn't really go beyond what's available in the Alfresco on-line documentation and wiki, the difference here is that it includes numerous screenshots that clearly illustrate the features and steps for configuring and using Share.

The authors took considerable care in creating the book's artwork.  Many of the images are annotated screenshots or are compact composite screenshots that describe the multiple steps needed to perform an operation.  I'd recommend viewing the electronic version of the book which contains color artwork, rather than the print version which has only grayscale images.

The book runs more than 300 pages and is split into 10 chapters.

The first chapter provides a brief overview of collaboration capabilities in Share and includes a brief description of a marketing site case study based on Share.

The second chapter dives into installation of the Alfresco repository and Share.  It is a bit long and includes in-depth details about installing many of the components and features that Alfresco offers. Most new users of Share will probably not need this level of detail.  They can simply use the standard Alfresco wizard install and be up and running with most of the needed Share features automatically installed and configured.

Developers will find some introductory technical material about the architecture of Share in Chapter 3. Readers primarily interested in how to use Share could skip that chapter.

It's not really until page 90 that the discussion about Share as an application begins.

Chapter 4 discusses Administration: Security, Creating/Deleting/Disabling users, Groups, Dashboards, Themes
Chapter 5 discusses how to set up a site, send invites, set site roles, and configure page features
Chapter 6 discusses collaboration tools within Share: the wiki, blog, data lists, calendar and links
Chapter 7 discusses the document library: the document list and details pages, document actions, versioning, thumbnails and web previews.  (Click here to read Chapter 7 as a sample chapter.)
Chapter 8 discusses rules-based simple workflows and out-of-the-box 'advanced' workflows based on Activiti
Chapter 9 discusses Share configurations, like creating a custom content model, configuring advanced workflow, configuring data lists, and making configurations in the alfresco-global.properties file.  While this information is good, it serves more as a starting point for how to do these types of customizations.  For most of these topics, you'll probably need to supplement this reading with documentation from somewhere else.

Chapter 10 goes back to a developer perspective and describes setting up a development environment and describes different options (JAR vs AMP) for how to deploy customizations to Share.

So, should you buy this book?  If you're new to Alfresco and just getting started with Share, this book will probably save you time that you would have otherwise spent navigating the Alfresco documentation, wiki and forums.  So for new users, I think that this book could be worth getting.

But if you've already been using Share for some time, you won't find too much new here.  And if you're looking for developer-specific information on Share, you probably won't find enough information here to warrant the purchase.


Friday, September 9, 2011

Resetting a Forgotten Alfresco admin password

It may never happen to you, but if you ever lose or forget the password for the administration user in Alfresco, it is possible to reset that password within the database.

While the reset process is easy to do, it involves fiddling directly with the database, something which you should be careful with, especially if that's not something you typically work with.  If you're having problems with a production system, by all means, experiment first on a test system.  You don't want to make a bad situation worse.

First run the following SQL to find out the identifying parameters for how the admin password is stored:

SELECT anp1.node_id,
       anp1.qname_id,
       anp1.string_value as hash_pass,
       anp2.string_value as user_string
FROM alf_node_properties anp1
        INNER JOIN alf_qname aq1
           ON aq1.id       = anp1.qname_id
        INNER JOIN alf_node_properties anp2 
           ON anp2.node_id = anp1.node_id
        INNER JOIN alf_qname aq2            
           ON aq2.id       = anp2.qname_id
WHERE aq1.local_name    = 'password'
AND aq2.local_name    = 'username';


After doing that, you'll see something like the following:

In this example, we can see that the password for admin is set to the default MD5 hash value for 'admin'.  You can set it back to some other MD5 hash value, but then of course, you'd need to calculate the MD5 value for whatever your desired password is.  It's easier to set it back to the default value corresponding to 'admin':  '209c6174da490caeb422f3fa5a7ae634'.  Then, once you can log back into Alfresco again you would be able to change the password to something else.

To update the password, the following SQL works:

UPDATE alf_node_properties 
 SET string_value='209c6174da490caeb422f3fa5a7ae634'
 WHERE 
 node_id=THEADMINNODEID
 and
 qname_id=THEADMINQNAME

Or, from the example shown in the screenshot above:

UPDATE alf_node_properties 
 SET string_value='209c6174da490caeb422f3fa5a7ae634'
 WHERE 
 node_id=4
 and
 qname_id=10

Saturday, August 13, 2011

Alfresco 3 Cookbook: Quick Answers to Common Problems by Snig Bhaumik

Alfresco 3 Cookbook: Quick Answers to Common Problems by Snig Bhaumik is the latest book from PACKT Publishing about the Alfresco Open Source Enterprise Content Management System.(CMS).  The book is now complete and is available in both print and electronic format.  Prior to the book being completed, parts of it had been  made available earlier by PACKT in a pre-release RAW format.

When I heard about this book, I was immediately interested.  The concept of having a cookbook-style reference book filled with easy-to-follow self-contained recipes for how to perform common tasks in Alfresco is very appealing.

The book is about 380 pages long and is organized into 14 chapters, each chapter covering one category of Alfresco usage, like the Administration Console, the Web Client, and the Content Model.  Then within each chapter, there are a number of recipes that describe how to perform specific tasks.  In total, the book covers more than 70 such recipes.  For example, Chapter 6 covers how to customize the Alfresco Web Client and includes recipes like the following:

  • Changing languages in the Login page
  • Changing textbox length and text area size
  • Controlling the Date Picker
  • Controlling the sidebar display

Each of the sections or 'recipes' is then broken down into a brief description of the task followed by a sub-section titled "Getting Ready" and then another one titled "How to do it...".  There's also an occasional "There's more..." section that contains a more in-depth explanation for why or how something works.  The "How to do it..." section is the heart of the recipe and it breaks down each task into step-by-step instructions for how to complete it.

The text descriptions are very clear and there are many illustrations, mostly of UI screenshots.

I like the cookbook style the book uses -- this book is part of PACKT's cookbook series.  But to stay true to the cookbook format, I would have preferred a larger number of recipes with shorter discussions about the background mechanics for how any one particular recipe works.  Some of the recipes are a bit long and stretch to as many as twenty pages.


The book starts out by describing the Alfresco installation process and then discusses topics useful to end users and administrators.  In this part of the book and even in some of the later chapters, while I liked the overall style of presentation and format, it felt like there was a lot of overlap with material already covered in other places like Munwar Shariff's book Alfresco 3 Enterprise Content Management Implementation or even with what can be found in the standard Alfresco documentation.  I would have liked to have seen more 'tips, tricks and gotchas' that go beyond just that material.

Later chapters discuss topics for those who want to customize and develop in the Alfresco environment. Topics include the Content Model, the Alfresco Javascript API, FreeMarker and Workflow. The final chapter describes how to download Alfresco source and set up a build environment.  I did like the discussion in Chapter 10 on Web Scripts.

Chapter 12 had me a bit puzzled.  That chapter discusses integration of Alfresco with Microsoft applications and has a lengthy discussion of Alfresco's MS-Office 2003 plug-in instead of discussing SharePoint protocol integration.  I may be wrong, but I had been under the impression that the Office plugin for Alfresco has been problematic and doesn't support Office 2010.

The main caveat that I have about the book though is its focus on the use of Alfresco's older Explorer client.  Alfresco Share is only mentioned on a couple of pages early on in the chapter that describes installation.  I would have liked to have seen much more about Share.  I think that almost all new deployments of Alfresco today will want to use Share as the web client, not the older Explorer client.

So, in general, I thought this book was well written and I think that you'll find a lot of useful facts about Alfresco here.  I also find the cookbook format used by the book very appealing.  But you'll likely be disappointed if you're looking for a book that covers Alfresco Share. I expect that PACKT has some books in the works on Alfresco Share coming up.  In the near term though, there is a good overview discussion on Share in the latter part of Munwar's book that I reference above.

** I'd like to thank PACKT Publishing for making available to me a complementary copy of this book for review.

Monday, April 4, 2011

Alfresco Share Permissions/Roles -- Part II

Creating Custom Alfresco Permissions/Roles

In the previous blog we saw how we were able to fairly easily replace the Share Manage Permissions dialog with the Manage Permissions page used for the Repository button browser.  This allowed us to be able to assign at a much more granular level permissions to the folders and items that are stored within a Share site Document Library.

Now consider the scenario where we would like to be able to invite users to our site, and these users should be able to see and modify only a selected set of documents within the Document Library.  We want access to the site for the majority of users to be unrestricted.  This scenario doesn't work well with the standard Share site roles of Manager, Collaborator, Contributor and Consumer.

To invite a restricted user to the site, we still need to give them a role.  Even if we give this user the role of Site Consumer, they will be able to see much more content in the site than what we want them to see.

What I propose here to help solve this problem is to add new custom Share site roles.  Alfresco has a wiki page here that provides a good start for what needs to be done in creating a custom Share role.  After following the instructions there, which were targeted for version 3.2r, I ran into some issues, and I noted a number of other people in the Alfresco forums also had some issues with it.

What I describe here should work with a fresh Alfresco install.  Trying to add new roles after Share sites have already been created will likely result in errors being thrown.  The reason why this happens is that the appropriate group authorities will not exist and Share will report that as an error.  Once these new roles are created, Share expects them to exist for all sites.  The absence of these authorities for existing sites likely is  something that can be corrected by manually creating the correct authority objects in Alfresco, but we don't attempt to do that here.

Here we will create three new permissions sets/roles called External Consumer, External Contributor, and External Collaborator. The permissions for each of these roles are identical to those of the corresponding Site Consumer, Site Contributor and Site Collaborator that come standard with Share.  What will be different is how these permissions are applied to content in the Document Library.

To do that, we edit the file sitePermissionDefinitions.xml and replicate the lines for SiteContributor, SiteConsumer, and SiteCollaborator permissionGroups.  This file is then placed into the following directory:
tomcat/shared/classes/alfresco/extension/model/
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE permissions >

<permissions>
    
    <!-- Namespaces used in type references -->
    
   <namespaces>
      <namespace uri="http://www.alfresco.org/model/system/1.0" prefix="sys"/>
      <namespace uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
      <namespace uri="http://www.alfresco.org/model/site/1.0" prefix="st"/>
   </namespaces>
   
   <!-- ============================================ -->
   <!-- Permissions specific to the wiki integration -->
   <!-- ============================================ -->
   
   <permissionSet type="st:site" expose="selected">
   
      <permissionGroup name="SiteManager" allowFullControl="true" expose="true" />
      
      <permissionGroup name="SiteCollaborator" allowFullControl="false" expose="true">
         <includePermissionGroup permissionGroup="Collaborator" type="cm:cmobject" />
      </permissionGroup>
      
      <permissionGroup name="SiteContributor" allowFullControl="false" expose="true">
         <includePermissionGroup permissionGroup="Contributor" type="cm:cmobject" />
      </permissionGroup>
      
      <permissionGroup name="SiteConsumer" allowFullControl="false" expose="true">
         <includePermissionGroup permissionGroup="Consumer" type="cm:cmobject" />
      </permissionGroup>

      <permissionGroup name="ExternalCollaborator" allowFullControl="false" expose="true">
         <includePermissionGroup permissionGroup="Collaborator" type="cm:cmobject" />
      </permissionGroup>
      
      <permissionGroup name="ExternalContributor" allowFullControl="false" expose="true">
         <includePermissionGroup permissionGroup="Contributor" type="cm:cmobject" />
      </permissionGroup>
      
      <permissionGroup name="ExternalConsumer" allowFullControl="false" expose="true">
         <includePermissionGroup permissionGroup="Consumer" type="cm:cmobject" />
      </permissionGroup>
      
   </permissionSet>

</permissions>
We need to alert Alfresco that this override file should be loaded on startup.  To do that, we create a new file with the path
tomcat/shared/classes/alfresco/extension/restricted-role-context.xml.
The contents of that file are as follows:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>

<!-- This file enables Alfresco Custom Site Roles.  It should be placed in shared/classes/extension -->

<beans>

    <bean id="siteService_permissionBootstrap" parent="permissionModelBootstrap">
     <property name="model" value="alfresco/extension/model/sitePermissionDefinitions.xml"/>
    </bean>

</beans>
Finally, there are a number of files where we add string properties that can be picked up so that the new role names display correctly within the Share UI.
First we create the file
tomcat/shared/classes/alfresco/web-extension/invitation-service-context.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
     
    <bean id="invitationResourceBundles" class="org.alfresco.i18n.ResourceBundleBootstrapComponent">
     <property name="resourceBundles">
      <list>
       <value>alfresco.web-extension.messages.invitation-service</value>
      </list>
     </property>
   </bean>

</beans>
And the associated property file:
tomcat/shared/classes/alfresco/web-extension/messages/invitation-service.properties:
invitation.invitesender.email.role.ExternalCollaborator=External Collaborator
invitation.invitesender.email.role.ExternalContributor=External Contributor
invitation.invitesender.email.role.ExternalConsumer=External Consumer
We add the following lines to the file:
tomcat/shared/classes/alfresco/web-extension/custom-slingshot-application-context.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>

<beans>
   <bean id="webscripts.resources" class="org.springframework.extensions.surf.util.ResourceBundleBootstrapComponent">
      <property name="resourceBundles">
         <list>
             <value>alfresco.messages.common</value>
             <value>alfresco.messages.slingshot</value>
            <value>alfresco.web-extension.messages.slingshot</value>
         </list>
      </property>
   </bean>
</beans>
And the referenced new properties are in the file:
tomcat/shared/classes/alfresco/web-extension/messages/slingshot.properties
## Custom Site External Reviewer Roles
role.ExternalCollaborator=External Collaborator
role.ExternalContributor=External Contributor
role.ExternalConsumer=External Consumer
Next we copy the file
tomcat/webapps/share/WEB-INF/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/folder-details/folder-info.get.properties 
to
tomcat/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/folder-details
and include these lines at the end of the file:
folder-info.role.ExternalCollaborator=External Collaborator
folder-info.role.ExternalConsumer=External Consumer
folder-info.role.ExternalContributor=External Contributor
Similarly copy the file
tomcat/webapps/share/WEB-INF/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/document-details/document-info.get.properties 
to
tomcat/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/document-details/document-info.get.properties
and include these lines at the end:
## Customer External Review Role
document-info.role.ExternalCollaborator=External Collaborator
document-info.role.ExternalConsumer=External Consumer
document-info.role.ExternalContributor=External Contributor
Copy the file
tomcat/webapps/share/WEB-INF/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/invite/invitationlist.get.properties 
to
tomcat/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/invite/invitationlist.get.properties
and include these lines at the end:
## External Groups and Roles for Site
group.ExternalCollaborator=External Collaborators
role.ExternalCollaborator=External Collaborators

group.ExternalConsumer=External Consumers
role.ExternalConsumer=External Consumers

group.ExternalCotributor=External Contributors
role.ExternalContributor=External Contributors

And finally, copy the file
tomcat/webapps/share/WEB-INF/classes/alfresco/web-extension/site-webscripts/org/alfresco/modules/documentlibrary/permissions.get.properties 
to
tomcat/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/modules/documentlibrary/permissions.get.properties
and include these lines at the end:
## External Groups and Roles for Site
group.ExternalCollaborator=External Collaborators
role.ExternalCollaborator=External Collaborator privileges

group.ExternalConsumer=External Consumers
role.ExternalConsumer=External Consumer privileges

group.ExternalCotributor=External Contributors
role.ExternalContributor=External Contributor privileges

External Site Roles in Action
Whew...
After doing that, we stop and restart the Alfresco server.  We can then log in and create a new Share site.

Immediately after creating the site, we can navigate to the root node for the site by using the Repository button in Share.  When we click on the Manage Permissions button for the new site, we can see that our new permissions sets (ExternalConsumer, ExternalCollaborator, and External Consumer) are included automatically and applied to this node.


At the Document Library level, we can create a folder structure where two top level folders are to be accessible only by the standard SiteConsumer, SiteCollaborator and SiteContributor.  And a third folder is available to standard users and is also open for viewing to External Reviewers.


The internal folder permissions look as follows:
With these settings, only internal reviewers will be able to see the content of this folder.

In the External Reviewer folder, we set the permissions as follows:
In this case, we can see that both external and standard internal reviewers are able to access this folder.
And if we navigate one Folder down in the hierarchy of the External Reviewer Folder, we can see that the inheritance of these permissions flow down.  One folder down, we see the permissions are set in the same way:
One last note.  In order for the user/group search capability to work correctly on this form, I found that the "Add User/Group" button on this page does not find Alfresco Share groups.  By default, search is performed for groups within the ALF.DEFAULT zone which does not include the Share zone groups.  In order to find our groups, the following Javascript file was changed (Note the changes in bold made to that file):
tomcat/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/people-finder/authority-query.get.js
var getMappings = function()
{
   var mappings = [],
      authorityType = args.authorityType === null ? "all" : String(args.authorityType).toLowerCase();
   
   if (authorityType === "all" || authorityType == "user")
   {
      mappings.push(
      {
         type: MAPPING_TYPE.API,
         url: "/api/people?filter=" + encodeURIComponent(args.filter),
         rootObject: "people",
         fn: mapUser
      });
   }

   if (authorityType === "all" || authorityType === "group")
   {
      var url = "/api/groups?shortNameFilter=" + encodeURIComponent(args.filter);
//    if (args.zone !== "all")
// All authorities are to be found
      if (args.zone !== "all" && args.zone !== null)
      {
         url += "&zone=" + encodeURIComponent(args.zone === null ? "APP.DEFAULT" : args.zone);
      }
...