Chapter 17. Integrating with Other Applications

WHAT'S IN THIS CHAPTER?

  • Understanding why applications use content management over a file system or database

  • Examining options for integrating Alfresco with your application

  • Investigating patterns of integrating content management into an application

  • Integrating with Alfresco

The Alfresco system is both an application and a platform to build content applications or provide content capabilities to other applications. As an application, it can become a component of a much larger application or even become a framework for new applications. With Alfresco Share, you have seen how you can use and extend Share to create collaborative applications. Much of the work of creating menus, pages, and layout are already done and adding your components can become a matter of configuration.

However, just like database management systems and their use in many different types of applications for managing and manipulating data, a content management system like Alfresco can provide powerful application features for various applications and Web sites that would otherwise need to be built from scratch. Some examples of capabilities that can be added to otherwise–non-content applications include:

  • Presenting relevant content and images—An application that is just data or short strings can potentially be deadly dull. You can enhance data with explanatory text or images that provide context. Data enhanced with analysis in the form of content becomes a much more useful business tool.

  • Adding collaboration—Even a very data-intensive application can benefit from providing users the ability to discuss and resolve issues around data in a collaborative environment. By providing social capabilities around the data, such as blogs, users can provide updates and news around changes in data, and allow others to track these changes through news feeds.

  • Archiving reports and data—Enterprise Resource Planning (ERP) and Customer Relationship Management (CRM) systems like SAP and Oracle Applications have included integrations with Enterprise Content Management (ECM) systems for many years to archive reports and audit trails.

  • Content for portals—A portal without content, news, and information may still be an important source of data and reports; however, without content, a portal can be an uninteresting destination and can cause users to miss out on some important information. This is why you rarely see a portal without some sort of content management. Many portals may include a basic content management capability, but these often lack some of even the most basic capabilities of a purpose-built content management system.

  • Document-enabling a Web site—Documents, such as reports, plans, and policies, can enhance a Web site and the services it provides its users. Integrating browsing and search capabilities similar to those of Alfresco Share can allow users to access information that may not be easily presented in a purely HTML format.

  • Document scanning—Some applications, such as legal and accounting applications, require the capture of paper documents that may be related to the data in the system. Some examples include contracts, receipts, invoices, and purchase orders that are the source for the numbers in the system and may be required for auditing purposes.

  • Publishing and reports—Some applications may generate documents as part of their functionality, such as generating contracts or financial reports. The application may have the data, but managing the descriptive content and images that are part of these documents may require more effort than the core data functionality of the application.

  • Microsoft Office integration—Generating and then managing office documents, such as spreadsheets and Word reports, can be much easier if these are integrated with Microsoft Office. More importantly, if a user needs to change the default output, managing the document after it has been generated can be done much more easily through a content management system than on a file system that may not have security and version controls in place.

  • Social networking applications—You are likely familiar with the pictures and micro-blogs of social networking systems like Facebook, but documents and other content, such as more elaborate blogs, can be a useful and compelling point of discussion and social networking. Your site or internal social network can provide content, such as resumes or even presentations, as in the Web 2.0 sites SlideShare (www.slideshare.com) and Scribd (www.scribd.com), along with collaboration and social tagging.

WHY INTEGRATE CONTENT MANAGEMENT INTO APPLICATIONS?

Patterns of integrating content management systems have developed over the last 20 years based upon requirements of enterprise systems and applications to use, capture, and manage content. Generally, these have only been used in high-value applications where the investment in the application could justify the cost and complexity of integrating an ECM system, such as integrations with ERP systems. Often, smaller and lower-value applications built these patterns as an extension of the application, using a database system and file system for storing the content.

With the emergence of open source and both the commoditization and standardization of content management, applications and sophisticated Web sites no longer need to add this functionality themselves, but can use the capabilities of content management systems. Alfresco provides the following capabilities:

  • Capture content as the output of an application or transactional information that results from the application for retention, archival, and future use. This includes managing the lifecycle of this content as well as providing the process to turn this content into an official record.

  • Search and retrieve application-specific and contextually relevant content based upon metadata, categories, or full text within content in order to provide the application with content that can assist the user in their task or to provide context that may not be easily encoded in the application.

  • Organize, categorize, and browse application content, such as instructions, notes, shared tips, diagrams, photos, or maps, using a clear hierarchy of information, a standardized categorization scheme, user-contributed tags, or all of the above.

  • Generate context-specific lists and catalogs of content using sophisticated queries based upon metadata, category, or full-text content.

  • View and access content in its original application or transform it into Web-friendly formats, such as Flash, HTML, or text. Provide previews and thumbnail images of content to allow the user to decide whether they want to download content before doing so.

  • Control and version application-specific content along with workflow-based review and approval to ensure accuracy, currency, and timeliness of critical instructions, information, and updates.

  • Compose and publish Web pages, documents, or articles that include images, diagrams, or other content. Manage the linkages, associations, and dependencies between content components.

Why Not Just Use a File System or a Database?

So why wouldn't you just implement this functionality in a relational database or store this content on a file system and use normal file system tools and file interfaces to manage this content? This is probably the approach that most applications have taken and most often the reason is cost. ECM systems can be extremely expensive. Some large commercial systems have even used source code–control systems to manage their content. Still, even if the cost were zero, either a file system or a database would be many developers' default choice for managing any information, including content.

There are many reasons for using a content management system rather than a database or file system. File systems are generally the most primitive and least controlled ways to handle content. There is usually no context or metadata other than the file's name, application type, and location in a folder hierarchy. This often leads to context being encoded in the folder name and hierarchy. Content in this structure is not queryable; thus, paths need to be hardcoded, even if they are relative paths. In addition, large amounts of content and large numbers of files become as unmanageable as any departmental shared drive. Normally, the only control over content is through permissions or a read-only bit. There is no review and approval of the content, no versioning, no recovery, and no guarantee that the content has not been tampered with. Such a foundation for content would never survive an audit and can irritate users even more than bugs in an application.

Relational databases provide an easy default choice for application developers, particularly since most applications already have a database. Binary large objects, or blobs, were introduced into relational databases to store content for applications. Treated like any other data, they use the same programming interfaces as the data in an application and they can take advantage of services provided by the database, such as backup and replication. However, since content is generally not a core competence of the database vendors, some of the trade-offs made for fast and general purpose handling of data have meant that databases are often not a good choice for managing content.

One of the primary reasons is the same as why many content systems have chosen not to store content in the database. Database caches are designed to handle small pages of data rather than large, unstructured streams of data that can quickly overwhelm database caches. Also, the clear separation between the database interface and where the blob is stored means that content must be copied to perform reads, writes, and selective writing into random accessed portions of the content. Due to how the blobs are stored in the database and the need to locate them near the other columns in the row, there are usually limitations on the size and number of the blobs in order to control effective use of storage and performance. These limitations have meant that content applications built upon a pure database interface tend to be less scalable and poorer-performing than content management systems, except where the content is smaller and usage is more constrained.

Scalability and size limitations ignore the additional domain model and logic that have been implemented on top of the relational systems by content management systems. Virtually every content management system is built upon a relational database to manage metadata and associations, although they tend to store the actual body of the content as files for performance. The domain model of Alfresco and other content management systems have pre-defined models of frequently used objects such as content types, content properties, folders, versions, and associations, as well as tools to extend these models with application-specific domain models that reuse core model definitions.

In addition, content management systems like Alfresco provide a number of services in managing, controlling, searching, and retrieving content beyond the simple insert, update, and delete functions of a database. Content management systems provide:

  • Version control and locking

  • Lifecycle and retention management

  • Transformation

  • Metadata extraction

  • Hierarchical navigation

  • Classification and tagging

  • Query facilities that combine relational, full-text, and hierarchical searching

  • Business process services for human-based review and approval

  • Access to functions through file and mail services

  • Audit trails and compliance checks

  • Fine-grained permission controls

If any of these services is of use to the application or for the control and guarantees of the content used in an application, then these would have to be re-implemented on top of a relational database. In effect, these services are provided out of the box for an application to use and can use the same database as the application.

APPLICATION INTEGRATION OPTIONS

To support the previously mentioned use cases where applications need content services, Alfresco provides a number of different programmatic ways to access the content management capabilities of the system. In some cases, the application doesn't even need to be aware that it is accessing Alfresco. By supporting a number of standards-based protocols, applications and application development environments can use tools already available to it to access, update, and search content.

Although Alfresco is built using the Java programming language, the system can be used by most of the popular programming languages. Either through the direct embedding of the language environment, as you have already seen with JavaScript and FreeMarker, or through REST-based or SOAP-based protocols, many of the popular development languages can access Alfresco. Content Management Interoperability Services (CMIS) will probably broaden the number of languages significantly.

Some applications can integrate with Alfresco by using their normal means of accessing information without having to change at all. The file system emulation of Alfresco allows ordinary document production tools to access and store documents via their normal Open and Save dialogs. Mail-based systems can access Alfresco using widely adopted mail protocols. Feed readers can use the feed and search interfaces of Alfresco as a normal source of feeds. Automatic metadata extraction and folder-based rules can solve many of the problems of capturing metadata as content is entered into the system.

Alfresco enables you to use the tools that are easiest to get your application up and going as quickly as possible, where the repository takes care of handling metadata, workflow, lifecycles, location, and search access. The tool you use depends on the level of control your application needs over metadata, context, and business processes, as well as the tools your application or your development environment provide for handling content. The following sections offer some guidelines on how and where you might use these various integration points as part of your application.

CMIS

The OASIS Technical Committee developing the CMIS specification identified a set of use cases that CMIS would target. Aimed at avoiding scope creep and making it practical to get interoperability across a wide variety of systems, it nevertheless surfaces a wide range of capabilities in the Alfresco repository. Alfresco's support for CMIS has already been covered previously; however, using CMIS should be considered in general whenever your application needs programmatic access to the content repository. You should especially consider CMIS if you intend to port your application to other content management systems.

Web Scripts

There is a number of existing Web scripts, which have already been described, that provide a great deal of functionality in Alfresco. However, if you wish to perform an operation in Alfresco that is not covered by a Web script or you wish to perform several operations and minimize network communication, then you can create your own Web scripts. This can increase the performance of your application and push the logic down to the Alfresco repository, where it can be reused.

File-Based Access

If metadata is not important to your application or processing of content and metadata can be handled with rules and actions, then file-based access may be an easier route to integration with Alfresco. With the CIFS, WebDAV, NFS, and FTP file system emulation, storing and accessing content to and from Alfresco may already be supported by your application through standard file interfaces. Applications that create content can store content in Alfresco and it is now available for other applications and Web sites to access. Applications that access files through Open dialogs or through programmatic file interfaces can navigate folder hierarchies and the content seamlessly.

Although the content appears to be a file, the application is not directly accessing the content. Rather, it uses Alfresco APIs, so all the controls and services are in place. File access is simply an emulation of files and its access and storage would be no different than through APIs directly.

By storing content using file system emulation, logic for processing content (for example, archiving, applying workflows and retention policies, and extracting metadata) can be performed with rules. Applied at the folder or space level, these rules are initiated when the file is stored; therefore, the application integrating with Alfresco does not need to be modified to use Alfresco. All the business logic can be stored in the Alfresco repository.

OpenSearch and Feeds

Some applications, Web sites, and portals may use standard Web-based technology to access external information using feeds (such as RSS or Atom subscription protocols) or queries using OpenSearch. Alfresco provides out-of-the-box RSS feeds to observe the contents of an individual folder or space, as well as activities related to a user or Share site. The Alfresco system also implements OpenSearch, which is a standard query protocol supported by many Web sites, such as Yahoo!, Google, and Amazon.

If an application is designed to use either feeds or OpenSearch, then the application can access Alfresco using the feed or search address. The results return descriptive information about the content return and a URL pointing directly to the content.

If more specialized information is required or you would rather point to the properties page in Share or Explorer, then you can create new RSS feeds or OpenSearch APIs as fairly simple Web scripts. See the actual RSS feed and OpenSearch templates in the Data Dictionary as a reference for how to build your own.

Java Applications

If a repository is single-purpose and performance is most important, then you may want to access the Alfresco core API directly. This requires your application to reside on the same application server as the Alfresco system. A Java application can access all the base APIs and Web scripts with minimal context switch.

PHP Applications

PHP applications can access Alfresco through CMIS, through Web scripts as a RESTful interface, or directly from the same application server as Alfresco. Alfresco has an optional extension that includes the Quercus PHP interpreter in a Tomcat server. The Quercus PHP interpreter is capable of running popular PHP applications, such as Joomla!, Drupal, WordPress, and MediaWiki. With the Quercus integration with Alfresco, these applications are capable of accessing the Alfresco system, as are other PHP applications. The most popular way of accessing Alfresco is remotely through CMIS.

.NET Applications

Integrating Alfresco applications with .NET applications is very similar to integrations with Java applications through remote interfaces such as Web scripts or CMIS. With Microsoft's support of CMIS in SharePoint, there will likely be more tools to support using CMIS to integrate other CMSs, such as Alfresco. Either the SOAP or Atom Publishing interfaces can be used. All patterns of integration can be supported.

Surf Components

Surf components are designed to work with other Web frameworks, such as Spring Webflow or Spring MVC. Surf components can save a lot of work in reimplementing user interface portions of your application or Web site if they replicate functionality that you were intending to build in a different Web framework, such as a browser or viewer.

Authentication and Directory Services

In order to interoperate between an application and Alfresco, it is important to consistently authenticate users to ensure secure access to either system. In addition, both systems need a consistent understanding of the identity of the user, so sharing a common directory service is also important. Alfresco synchronizes and uses standard LDAP and Active Directory services for authentication and directory services. The security systems are open as well to adapt to other systems. Alfresco also integrates with a few single sign-on technologies to provide a seamless navigation between Alfresco and another Web application.

PATTERNS OF CONTENT MANAGEMENT INTEGRATION

When applications use the value of a content management system like Alfresco, they use common patterns in the way that they access this information. These patterns have developed as a result of the way users interact with systems that they use on a day-to-day basis. File systems and the standard Open/Save/Save As dialogs provide a common mental model for how they find and share information. Google has standardized the way users tend to look for information and how they express queries. Transactional systems, with their tabular presentation of information, set users' expectations of how they filter and sort information. Online commerce and Web 2.0 sites have influenced users in how to access and share large amounts of information and have set the bar in responsiveness of the applications. There are some common office practices that are modeled in applications as their real-world counterparts, such as review and approval.

Based upon users' expectations from other systems and developers' own usage of database systems, applications and Web sites have integrated content management in common patterns that are similar to file systems, physical paper handling and storage systems, online commerce and news sites, and popular search and retrieval tools. The following shows some of the most common patterns of content management integration.

Content Service Mappers

Not all integrations work exclusively with Alfresco APIs. Sometimes there are standardized interfaces, which are Service Provider Interfaces to which a content management system provides an implementation. These are often protocol-based services to provide language neutrality and to naturally balance the load between the application and the CMS. Services typically include authentication, query, folder navigation, CRUD (Create, Read, Update, Delete) operations, content transfer, and versioning. These services are abstract and mapped onto the Alfresco native APIs.

Some of the patterns you will see in this chapter are implemented using a Content Service Mapper to access Alfresco from other applications (see Figure 17-1). Most patterns are similar between Content Service Mappers and are generally determined by commonality between the ECM systems they are accessing. As a result, these Content Service Mappers often implement a lowest common denominator of functionality.

FIGURE 17-1

Figure 17.1. FIGURE 17-1

Using a Content Service Mapper

A content service accesses the content management system through either a remote protocol or through direct calls. In most cases, the access is remote to allow for reuse of the repository by other applications and to simplify the administration of applications without dependencies on the ECM system. The most common protocols are based on SOAP or RESTful Web services, such as AtomPub. However, some applications will mandate other Remote Procedure Call interfaces.

A Content Service Mapper then translates the calls into the appropriate mapping in the ECM system. The most common areas of mapping are Data Dictionary for determining what content types are available in the system, content access to map metadata and content streams, folder navigation to map Folder/Space hierarchies, and query interfaces to provide query and retrieval functionality. Query mapping can potentially be complex, such as XML query mapping against an SQL-based system. These mappings are implemented as Facades on the underlying ECM system.

Many systems provide kits for implementing a Content Service Mapper. Much of the framework may be already implemented in Java or C, leaving only the mapping functionality of the Facades to be implemented.

When to Use It

Content Service Mappers are used when an application connects to more than one CMS and the developer wishes to isolate the porting of the application to a separate layer. Some examples of Content Service Mapper interfaces that are specified by other applications include SAP ArchiveLink for archiving reports and accessing attached image files, IBM/Lotus's Quickr Services for ECM, and OASIS CMIS as a general-purpose connector for many different types of applications. The Alfresco IMAP protocol mapping is an unusual Content Service Mapper in that it maps content services to an email domain model.

You can also define and build a Content Service Mapper if something like CMIS is overkill for your needs or you need functionality or protocol not covered by CMIS. For example, some applications use JSON for remote access of objects and these applications may need the CMS to comply with this pattern. Some systems provide aspects and since CMIS does not support aspects, it may be necessary to provide a separate Content Service Mapper. If an application needs only to access well-structured content as simple objects, a simple Content Service Mapper may be a simpler, higher-performance alternative.

Example

The Alfresco ECM Services for IBM Lotus Quickr are an example of a Content Service Mapper that provides content services on top of Alfresco and maps them to a service interface that Lotus Quickr is expecting in the Quickr application. The Content Service Mapper allows Quickr to navigate the Alfresco repository, store and retrieve content from Quickr into Alfresco, and manipulate metadata in the content objects from Quickr.

In order to provide the mapping to Quickr, IBM has provided a guide of the services required to access an ECM system like Alfresco or IBM FileNet. This interface required implementing a combination of SOAP for content transfer and AtomPub services for metadata manipulation. Details can be found at www.ibm.com/developerworks/lotus/library/quickr-web-services/. IBM provides a WSDL for the SOAP interfaces and can generate the abstract implementations of the WSDL.

In the case of the SOAP interfaces, the Alfresco Content Mapper for Quickr used the same Web services infrastructure as the Alfresco CMIS Web services. The AtomPub interface used Web scripts implemented in Java for performance reasons. Both implementations used the Alfresco Java Foundation API.

The following example shows one portion of the implementation that lists children of a document as a feed. Notice that the Mapper is primarily handling the translation of terminology and the mechanics of accessing the objects. The concepts are roughly the same between Quickr and Alfresco. A collection is an artifact of AtomPub rather than Quickr, but the notions of folders, content, and children are the same in both systems.

publicclass AlfrescoAtomBasedFeedServiceImpl implements AtomBasedFeedService
{

  private NodeService nodeService;

  private PersonService personService;

  public Feed getListDocuments(String id)
  {
    NodeRef storeRef = newNodeRef(id);

    // <feed>
    Feed feed = newFOMFeed();

    feed.setBaseUri("/library/" + id + "/");

    // <generator>
    feed.setGenerator("", "1.0", "Teamspace Documents");

    // <id>
    feed.setId("urn:lsid:ibm.com:td:" + id);

    // <link>
    feed.addLink("feed", "self");
    feed.addLink("http://quickr.acme.com/wps/mypoc?uri=dm:" + id
             + "&verb=view", "alternate");
    feed.addLink("feed?pagesize=2&page=3", "next");
    feed.addLink("feed?pagesize=2&page=1", "previous");

    String contentName = (String) nodeService.getProperty(storeRef,
               ContentModel.PROP_NAME);

    // <collection>
    feed.setCollection(new FOMCollection(contentName, "feed",
               new String[] { "*/*" }));

    String authorName = (String) nodeService.getProperty(storeRef,
                            ContentModel.PROP_AUTHOR);
    String email = (String) nodeService.getProperty(
                              personService.getPerson(authorName),
                              ContentModel.PROP_EMAIL);
    String userName = (String) nodeService.getProperty(
                              personService.getPerson(authorName),
                              ContentModel.PROP_USERNAME);
// feed.addAuthor(createPerson(storeRef));

    // <author>
    feed.addAuthor(authorName, email, "uid=" + userName + ",o=acme");

    // <title>
    feed.setTitle(contentName);

    // <updated>
    feed.setUpdated((Date) nodeService.getProperty(storeRef,
                                    ContentModel.PROP_MODIFIED));

    // add<entry>
    for (ChildAssociationRef childAssoc : nodeService.getChildAssocs(storeRef))
    {
       NodeRef childRef = childAssoc.getChildRef();
       String childName = (String) nodeService.getProperty(childRef,
                                         ContentModel.PROP_NAME);

       Entry entry = newFOMEntry();

       // <id>
       entry.setId("urn:lsid:ibm.com:td:" + childRef.getId());

       // <link>
       entry.addLink("document/" + childRef.getId() + "/entry", "self");
       entry.addLink("http://quickr.acme.com/wps/mypoc?uri=dm:" + childRef.getId()
         + "&verb=view", "alternate");
       entry.addLink("document/" + childRef.getId() + "/entry", "edit");
       if (nodeService.getProperty(childRef, ContentModel.PROP_CONTENT) != null)
       {
         entry.addLink("document/" + childRef.getId() + "/entry", "edit-media");
         entry.addLink("document/" + childRef.getId() + "/entry", "enclosure",
               (String) nodeService.getProperty(childRef,
         ContentModel.PROP_CONTENT), childName, "en",
               (Long) nodeService.getProperty(childAssoc.getChildRef(),
         ContentModel.PROP_SIZE_CURRENT));

         // <category>
         entry.addCategory("tag:ibm.com,2006:td/type", "document", "document");
       }
       else
       {
         // <category>
         entry.addCategory("tag:ibm.com,2006:td/type", "folder", "folder");
       }

       authorName = (String) nodeService.getProperty(childRef,
         ContentModel.PROP_AUTHOR);
       email = (String) nodeService.getProperty(
         personService.getPerson(authorName),
         ContentModel.PROP_EMAIL);
       userName = (String) nodeService.getProperty(
         personService.getPerson(authorName),
ContentModel.PROP_USERNAME);

       // <author>
       entry.addAuthor(authorName, email, "uid=" + userName + ",o=acme");

       // <title>
       entry.setTitle(childName);

       // <published>
       entry.setPublished((Date) nodeService.getProperty(childRef,
         ContentModel.PROP_CREATED));

       // <updated>
       entry.setUpdated((Date) nodeService.getProperty(childRef,
         ContentModel.PROP_MODIFIED));

       return feed;
    }

This example implements a small but important portion of the Quickr API as a Content Mapper Service. This is the service to get a list of documents that may be used in a portlet or document browser. The service, implemented as an Atom-based feed, references a space node by ID and returns the metadata associated with the space. It then iterates through the children of the node through the child associations. If the child is a document, it provides a link to the content. If it is a folder, it provides a link to the space representing that folder. This is likely to be a common pattern in many Content Mappers.

Property View

A Property view (see Figure 17-2) is a building block of any Alfresco or any other content management application. A Property view presents information about a content object based on the content object's ID. Usually there will be additional actions that can be performed on the object, such as Download, Check Out/In, Edit using WebDAV, Invoke Workflow, and others. The actions will be determined based on the context of the content object in the application and whether the Property view is read-only or editable. An integrated viewer using a Flash viewer or an in-line editor can make for a more streamlined experience for the user.

Using a Property View

The Property view uses a Web Script Controller to fetch the content object using either CMIS or Web scripts. The Property view accesses the properties needed for the view. If the view is dynamic, then it may use the Data Dictionary or introspection to find out what properties are appropriate for the object fetched. A Property view can be read-only or bi-modal, allowing for editing of properties if appropriate for the application.

The view then constructs an HTML page with the properties and any actions that are appropriate for the object. If the view is read-only, then the Property view is generally very simple, adding only basic controls to download content. However, you can add other controls that are relevant to the application, such as e-commerce or process-related actions. On an OK or Cancel action, control is returned to the Browser or Query view that invoked the Property view and returned to the original context.

FIGURE 17-2

Figure 17.2. FIGURE 17-2

It is possible to add a Flash-based viewer, such as the one found in Alfresco Share. An example will be provided with the online reference.

If the view is editable, then a more sophisticated controller needs to be built, such as the one used in Alfresco Share. It may be best under those circumstances to direct control to the Property view in Share. If it is important for the Edit view, then the developer can create a special purpose fixed form, use the Data Dictionary to construct a dynamic form, or use the Forms Service from Alfresco Share. Details on the Forms Service can be found on the Alfresco wiki.

When to Use It

A Property view is generally used with either a Browser view or a Query view and invoked by an action on the line item of either. The Property view can be used to view basic properties on content or can be specialized for different types and aspects. Property views can also be specialized based on the nature of the content. For example, if a content object is used to present a product for sale, such as in a catalog, then the Property view can be used to display the content, such as a photo, and provide actions for purchasing the item. A Property view can also be used for very specialized content types, such as high-resolution photos, providing different thumbnail views and exposure information for digital assets in a Digital Asset Management catalog.

Example: Confluence

Confluence is an enterprise wiki from Atlassian used in many development shops. An integration of Confluence with Alfresco was created using CMIS by Alfresco and SourceSense. The project can be found at http://code.google.com/p/confluence-alfresco.

The following are some examples of a Property view created to work with the wiki built by Sourcesense. In this case, the view is constructed in Java using the wiki syntax for the wiki to render. It uses the Apache Abdera AtomPub toolkit and the Abdera extensions for CMIS to access the CMIS AtomPub protocol.

private String renderEntry(Entry entry) {
  StringBuilder out = new StringBuilder();
  out.append("||Property||Value||
");
ExtensibleElement cmisObject = entry.getExtension(CMISConstants.OBJECT);
  if (cmisObject != null) {
    ExtensibleElement cmisProperties =
      cmisObject.getExtension(CMISConstants.PROPERTIES);
    if (cmisProperties != null) {
      List<Element> cmisProps = cmisProperties.getElements();
      for (Element prop : cmisProps) {

        System.err.println(prop.getQName());

        if (!CMISConstants.CMIS_NS_URI.equals(prop.getQName().getNamespaceURI())){
          continue;
        }
        String name = prop.getAttributeValue(CMISConstants.NAME);
        if (name == null) {
          continue;
        }
        Element cmisValue =
          ((ExtensibleElement)prop).getExtension(CMISConstants.VALUE);
        if (cmisValue != null) {
          String value = cmisValue.getText();
          if (CMISConstants.PROPERTY_BOOLEAN.equals(prop.getQName()))
            value = "true".equalsIgnoreCase(value) ? "(/)" : "(x)";
          else if (CMISConstants.PROPERTY_URI.equals(prop.getQName()))
            value = "[LINK|" + value + "]";
          out.append("|");
          out.append(name);
          out.append("|");
          out.append(value);
          out.append("|
");
        }
      }
    }
  }
  return out.toString();
}

This code snippet is part of a macro extension added into the Confluence wiki engine to iterate to present the properties of a document. In this case, it uses CMIS to fetch the object. The string out is used to construct a wiki fragment using wiki syntax. The list of elements cmisProps is used to iterate through the properties and present them as a set of attribute value pairs in a wiki-formatted table. The vertical bar is used to separate columns in the table with the property name as one column and the property value as another. The string that is composed is then sent to the wiki formatting engine for presentation when the macro is called.

Article

An article pattern (see Figure 17-3) is a single content object used in relation to the context of the application or Web site to present relevant and rich information to the user. For example, the information presented may be a featured document, an explanatory piece of text, or a deeper description of a category of information. A physical article in a magazine is a good analog of a single content object or document used by the application or Web site to showcase new information or provide a deeper understanding of a piece of information.

FIGURE 17-3

Figure 17.3. FIGURE 17-3

User-generated content, such as a blog, is a good example of an article and a content repository is a good place to store this content. A photo article may consist of something as simple as an image and a caption, or a title, image, and caption. A photo article can also contain multiple images, which is usually driven by a top article that determines the order of the photos.

Compound documents are a special case of article, which may be assembled from other related objects to create the composed or compound document. Using associations to create compound documents in a related document management application is best for identifying the associated objects.

Using the Article Pattern

The article is retrieved from the repository with, usually, a singleton query. This allows the article to be identified logically based upon context and to change which content will change. For instance, a tip of the day can be identified as the latest tip of the day. Avoid hard-coding any identifiers.

Set up a query with a predicate that identifies the appropriate content object. It is not necessary to retrieve any properties other than the identifier since you will have the object available for any properties. CMIS provides all the required functionality to implement access to an article and the result will be portable. However, using Web scripts can be equally effective. Fetch the content object and any properties you wish to display. The title of the article may be derived from the title property.

Stream the article into your user interface, usually as text or HTML. If it is XML, then you will need to either load the payload as into a DOM object or render using an XSLT transformation. Alternatively, if there is a Flash-based preview available (such as those built into Share), derived from Office documents or PDF, you could embed the Flash content along with a Flash viewer as Share does.

If the article is a compound object, then the application walks the associations including sub-components and any in-line images. The simplest way to assemble the content is to walk the tree depth-first and make sure that embedded objects are presented as valid HTML.

When to Use It

Use the article pattern when a single piece of content is required in a well-known position of the application. The following examples indicate where an article may be useful:

  • Introduction—As you enter the application, view a new section of the application, or walk through a wizard, an introductory article can provide context for the user as to what will happen when they use that part of the application. A banner or introductory article can also present a more professional appearance for the user. In addition to being only text, these can include video in the form of an embedded Flash object.

  • Application assistant—Instructions or help can aid a complex part of an application. This content can be presented as in-line instructions, pop-up help, or an aid. These can also be presented where you anticipate the user will need help, such as with a tip of the day.

  • Related information—If the user wishes to go beyond this portion of the application, see related information, or be presented with news related to the context of the application, then an article is a better way to provide a richer set of description and guidance than with a simple list of URLs.

Example

The Home Page of this Surf site includes a static HTML file that is created using the article.xsd Web form in the Surf Framework. This creates the file latestNews.html within the repository to ROOT/content/news, which is then deployed to ROOT/content/news. The Surf Web application then includes latestNews.html at request time, as seen in Figure 17-4.

FIGURE 17-4

Figure 17.4. FIGURE 17-4

The HTML include import is done by the Surf component; however, before this will function, you need to configure Surf (using Spring) to look into the deploy directory for the static content and Web scripts. For example, you can create a Web extension configuration bean as follows:

<!-- Local Store Abstract  -->
<bean id="webframework.localstore"
      class="org.alfresco.web.scripts.LocalFileSystemStore"
      abstract="true" init-method="init">
  <property name="root">
    <value>/surf-sample/sample/deploy</value>
  </property>
</bean>
<!-- Web Scripts: Local Store -->
<bean id="webframework.localstore.webscripts"
      parent="webframework.localstore">
  <property name="path">
    <value>alfresco/site-webscripts</value>
  </property>
</bean>

Once this is configured, components can access static content. The component that is used on the home page is site-data/components/page.main.index.xml and looks like this:

<?xml version='1.0' encoding='UTF-8'?>
  <component>
    <scope>page</scope>
    <region-id>main</region-id>
    <source-id>index</source-id>
    <component-type-id>/component/common/include</component-type-id>
    <properties>
      <container>div</container>
    </properties>
    <resources>
      <resource id="source" type="webapp">/content/news/latestNews.html</resource>
    </resources>
  </component>

You can see that you are using one of the Web scripts that comes bundled with Surf, which is referenced by /component/common/include. The resource ID is then passed in (that is, the HTML fragment).

Query View

A Query view (see Figure 17-5) is a general-purpose pattern that generates very application-specific views for accessing sets of content and presenting a consistent set of metadata about that set. Like most ECM systems, Alfresco provides query facilities that are based on SQL. In particular, Alfresco has been careful to follow the CMIS-query language specification that is based on SQL 92 with extensions to support querying within folders and adding full-text expressions for searching within the text of the content. The view is constructed from the query model and presented to the user for further action, either within the application or by handing control to one of the Alfresco applications, such as Share.

FIGURE 17-5

Figure 17.5. FIGURE 17-5

Using a Query View

A Query view can be used to provide a flat set of content filtered on a specific folder, metadata (such as a project name or author), the text within the content (such as all documents containing the word "Aspirin"), or any combination preceding. The qualification can be as expressive as SQL. The target list can contain any property that is common to the object types or aspects listed in the FROM clause. The qualification can be either hard-coded in the application or captured from a form in the application. For instance, a form can capture a project to be searched.

The query in the Query view can be constructed using the CMIS query specification and sent through either CMIS or Web scripts. The query is executed in the repository, which then formats the query results as a flat tuple set with the properties requested by the query. Normally, the content ID is returned so that the content can be retrieved or some other action can be taken on the content. Alfresco can also provide specialized properties specifically for applications and user interfaces, such as icon types. CMIS allows rendition types to be added to queries to present thumbnails with the results. This enables the application to provide visual indicators as well as data indicators of what content information is being returned.

The view in the Query view then constructs a Web page with the information retrieved and constructs a set of links for further action. The view is normally built using a templating language geared toward constructing Web pages or Web page fragments, such as FreeMarker, PHP, or JSP. The view may be part of a wider Web framework, such as a portal, Share, or a Surf application. The view will normally present the results in a tabular view with standard metadata, such as name, title, author, and creation date. If a thumbnail rendition is selected, then the thumbnail will normally be of a size that fits neatly into a row associated with a content object. If there are a lot of nodes, it is the responsibility of the view to handle pagination of results sets.

The links constructed by the view are normally URLs linking to the content itself; another page of the application that provides more context or information; the property sheet of the content object in Share where the content preview, metadata, and actions can be found; or another part of the application that may show a more application-specific view of the content properties and actions. If a link to the content needs to be fast, then this may be a direct, read-only link to the content from the content store. If the content needs to be editable, then it is best to use a WebDAV link using the WebDAV protocol. CMIS will construct a direct, read-only link and is portable. An application can construct a link to the Share application by pointing to the right site path and appending the path to the content in the document library. Specialized content views can be constructed for simpler viewing, annotating for linking to other application objects.

When to Use It

A Query view is used when a logical set of content is needed for an application and the content is very commonly used. Use a Query view to assemble content related to a particular topic or subject by using keyword-based properties, tags, or categories provided by Alfresco. Also use this view to assemble process or project information. In this case, status information captured by Alfresco or the application can be used to filter the appropriate information. Workflow status can also be useful in the query. Content that might be of interest to the end user can be assembled by querying for content that has the user as an author or is waiting on that user for action in a workflow.

A Query view allows the content view to be very specific to the application by filtering out irrelevant content across the repository. Unlike a browser or folder view, only relevant information is displayed, making for a much easier user experience.

Example: Drupal and OpenSearch

In this example, Drupal already uses OpenSearch as a mechanism to search other content sources, and integrating Alfresco OpenSearch is an easier route to integrating search. This Drupal module successfully mixes CMIS AtomPub for deeper object inspection and creation of content, but is able to simply add Alfresco as another search source. To download the source code and view additional information, visit http://drupal.org/project/cmis_alfresco.

The following is an implementation in the Drupal Content Construction Kit (CCK): http://drupal.org/node/101723.

/**
 * Keyword based search
 *
 * @param $keyword
 * @param $p
 */
function cmis_alfresco_opensearch_view($keyword = NULL, $p = 1) {
  module_load_include('utils.inc', 'cmis_alfresco'),

  // Add opensearch form
  $contents = drupal_get_form('cmis_alfresco_opensearch_form', NULL);

  if ($keyword) {
    $result = cmis_alfresco_invoke_service('/api/search/keyword.atom?q='.
urlencode($keyword) .'&p='. $p);
  if (false != $result) {

    // Process the returned XML
    $xml = cmis_alfresco_utils_get_CMIS_xml($result);

    // Set up results list
    $contents .= theme('cmis_alfresco_opensearch_results',
         $xml->xpath('//D:entry'));

    // Set up pager
    $opensearch = $xml->children(cmis_alfresco_utils_ns('opensearch'));
    $total_items = (int) $opensearch->totalResults;
    $items_per_page = (int) $opensearch->itemsPerPage;

    if (fmod($total_items, $items_per_page) == 0) {
      $last_page_number = floor($total_items / $items_per_page);
    }
    else {
      $last_page_number = floor($total_items / $items_per_page) + 1;
    }

    // Add pagination bar
    $contents .= theme('cmis_alfresco_pager', 'cmis/opensearch/'. $keyword .'/',
          $last_page_number);
    }
    else {
      $contents .= 'Error';
    }
  }

  return $contents;
}

The actual Query view is implemented through the Drupal CCK, which ties the OpenSearch query view to a theme.

/**
 * Implementation of hook_theme()
 *
 */
function cmis_alfresco_theme() {
  return array(
    'cmis_alfresco_opensearch_results' => array('arguments' => array('entries')),
    'cmis_alfresco_pager' => array('arguments' => array('base_search_url',
    'last_page_number'))
  );
}

function theme_cmis_alfresco_opensearch_results($entries) {
  if (empty($entries)) {
    return '<div class="empty-search">There are no results for your search.</div>';
  }

  module_load_include('utils.inc', 'cmis_alfresco'),
foreach ($entries as $entry) {
    $summary = $entry->summary;
    $score = $entry->children(cmis_alfresco_utils_ns('relevance'))->score;
    $updated = date_create($entry->updated);
    $updatedStr = date_format($updated, 'n/j/Y g:i A'),
    $alfIcon = $entry->icon;
    $documentLink = l($entry->title, 'cmis/get',
           array('query' => array('id' => $entry->id)));

    $rows[] = array('<img src="'. $alfIcon .'" />'. $documentLink,
           $entry->author->name, $updatedStr, $score);
  }

  $contents .= theme('table', $header, $rows);

  return $contents;
}

function theme_cmis_alfresco_pager($base_search_url, $last_page_number) {
  $contents .= '<div class="pagination">';
  $contents .= l('first', $base_search_url .'1'),

  for ($counter = 1; $counter <= $last_page_number; $counter++) {
    if ($p!= $counter) {
        $contents .= ' '. l($counter, $base_search_url . $counter);
    }
    else {
      $contents .= ' '. $counter;
    }
  }

  $contents .=  ' '. l('last', $base_search_url . $last_page_number);
  $contents .= '</div>';

  return $contents;
}

Where needed, Drupal can also use the CMIS query interface.

/**
 * Implementation of cmisapi_query method
 *
 * @param $repositoryId
 * @param $statement
 * @param $searchAllVersions
 * @param $includeAllAllowableActions
 * @param $includeRelationships
 * @param $maxItems
 * @param $skipCount
 * @return array
 */
function cmis_alfresco_cmisapi_query($repositoryId, $statement,
              $searchAllVersions = FALSE, $includeAllAllowableActions = FALSE,
              $includeRelationships = NULL, $maxItems = 0, $skipCount = 0)
{
  module_load_include('utils.inc', 'cmis_alfresco'),
$postvars = '<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>'.
      '<query xmlns="'. cmis_alfresco_utils_ns('cmis') .'">'.
        '<statement>'. $statement .'</statement>'.
        '<searchAllVersions>'. ($searchAllVersions?'true':'false') .
        '</searchAllVersions>'.
        '<maxItems>'. $maxItems .'</maxItems>'.
        '<skipCount>'. $skipCount .'</skipCount>'.
        '<returnAllowableActions>'. ($includeAllAllowableActions?'true':'false') .
        '</returnAllowableActions>'.
      '</query>';

  $header[] = 'Content-type: application/cmisquery+xml';
  $header[] = 'Content-length: '. strlen($postvars);
  $header[] = 'MIME-Version: 1.0';

  $response = cmis_alfresco_invoke_service('/api/query', $header, 'CUSTOM-POST',
      $postvars);
  if (false != $response) {
    return _cmis_alfresco_getEntries(cmis_alfresco_utils_get_CMIS_xml($response,
              '//D:entry'));
  }

  return FALSE;
}

Here the function is constructing a CMIS query by composing the XML to wrap around the query passed as the variable $statement. The query is then posted to the CMIS query service in Alfresco.

Browser View

A Browser view generates a File System Explorer–like view on top of the repository (see Figure 17-6). The user drills down a hierarchical view of the repository folder structure. The view then guides the user to the next level of the hierarchy. Normally, this tree structure is exposed in a single view that expands as a tree. However, it may also be presented as a single level at a time with a breadcrumb trail showing the user's current location. Browsers are useful for general purpose browsing of a repository to allow the user to explore the structure of the repository.

Browsers can also take the form of dialogs similar to Open File dialogs. These can be used for finding and selecting content for articles within the application, or attachments to attach content to business objects in the application.

Using a Browser View

A Browser view typically begins with a starting folder, which can be the root folder, the user's home folder, or another folder that has a semantic context, such as a project or team folder. From this starting point, a model is constructed that traverses all the children in that folder. This can be done through either Web scripts or CMIS.

Alternatively, a Browser view can navigate a category structure. Since there is no standardized structure of categories within CMIS, it is best to use Web scripts to navigate the hierarchy. A parent-child hierarchy is very similar to a folder-child hierarchy.

FIGURE 17-6

Figure 17.6. FIGURE 17-6

A view constructs a page or page component with the Container object and its immediate children, usually starting with the children that are themselves Containers. Unlike the Query view, which presents a normalized view of the result set, a Browser view usually constructs a Property view that is specific to the type of the child. For instance, folders would be presented as leading to a new level, documents might have document-oriented information and actions, and Web pages may have their URLs listed. Normally all the information required is immediately available as part of the child object.

When a child is clicked, the view will recurse to the next level, either refreshing the view to the next level or expanding an indented or accordion view of the next level. The navigation tree of Container nodes may be displayed in a separate view. For a simpler view, particularly in more limited portal environments, a flat structure is more often used but should contain a breadcrumb trail above the list of children. This trail prevents the user from getting lost and enables a quick return to previous levels. The easiest way to maintain a consistent breadcrumb that is compatible with the Back button of a browser is to walk the primary ancestor path of a node.

As with the Query view, the Browser view constructs the actions for the user to take as a set of URL links behind the metadata that is presented by the view. As with the Query view, this can take the form of accessing the content directly or leading to a specialized property page. Browsers generally replace Share or Explorer, so are less likely to point to those applications for property sheets.

When to Use It

Browsers are useful for general-purpose content management and document management applications where Share and Explorer are not appropriate for portability or platform reasons. Browsers are particularly useful where folders are a logical organizer of information. They do not replace, but can supplement, Query views. Folder structures often mimic organization, team, project, and record hierarchies, so browsers are useful for navigating these structures. Collaboration sites may organize their content in such structures and provide a Browser view.

Some of the first general-purpose tools built using CMIS were browsers using the Browser view. These browsers were designed to exercise the CMIS interface and provide value-added viewing and exploration on top of these views.

Example: Drupal Repository Browser

In the following Drupal integration example, the CCK is used again and provides the presentation layer functionality. The Browser Controller uses CMIS to get the children of the current node based upon which level you are browsing. In the Drupal example, it starts with the Company Home Page. Following are some of the helper methods for accessing CMIS calls.

/**
 * Implementation of cmisapi_getRepositoryInfo method
 *
 */
function cmis_alfresco_cmisapi_getRepositoryInfo() {
  static $repository_info;

  if (empty($repository_info)) {
    module_load_include('utils.inc', 'cmis_alfresco'),

    $response = cmis_alfresco_invoke_service('/api/repository'),
    $xml = cmis_alfresco_utils_get_CMIS_xml($response);

    if ($xml) {
      $cmis = $xml->workspace->children(cmis_alfresco_utils_ns('cmis'));
      $repository_info = $cmis->repositoryInfo;
    }
  }
return $repository_info;
}

/**
 * Implementation of cmisapi_getChildren method
 *
 * @param $repositoryId
 * @param $folderId
 */
function cmis_alfresco_cmisapi_getChildren($repositoryId, $folderId) {
  module_load_include('utils.inc', 'cmis_alfresco'),

  $folderId = cmis_alfresco_objectId($folderId);
  if ($folderId['noderef_url']) {
    $url = '/api/node/'. $folderId['noderef_url'];
  }
  else {
    $url = $folderId['url'];
  }

  $response = cmis_alfresco_invoke_service($url .'/children'),
  if (false != $response) {
    return _cmis_alfresco_getEntries(cmis_alfresco_utils_get_CMIS_xml($response,
        '//D:entry'));
  }
return FALSE;
}

/**
 * Implementation of cmisapi_getObjectParents method
 *
 * @param $repositoryId
 * @param $folderId
 */
function cmis_alfresco_cmisapi_getObjectParents($repositoryId, $folderId) {
  module_load_include('utils.inc', 'cmis_alfresco'),

  $folderId = cmis_alfresco_objectId($folderId);
  if ($folderId['noderef_url']) {
    $url = '/api/node/'. $folderId['noderef_url'];
  }
  else {
    $url = $folderId['url'];
  }

  $response = cmis_alfresco_invoke_service($url .'/parent'),
  if (false != $response) {
    return _cmis_alfresco_getEntries(cmis_alfresco_utils_get_CMIS_xml($response,
    '//D:entry'));
  }

return FALSE;
}

/**
 * Implementation of cmisapi_getProperties method
 *
 * @param $repositoryId
 * @param $objectId
 */
function cmis_alfresco_cmisapi_getProperties($repositoryId, $objectId) {
  module_load_include('utils.inc', 'cmis_alfresco'),
  $objectId = cmis_alfresco_objectId($objectId);

  if ($response =cmis_alfresco_invoke_service(
          '/api/node/'.$objectId['noderef_url'])) {
    if ($entries = _cmis_alfresco_getEntries(
            cmis_alfresco_utils_get_CMIS_xml($response, '//D:entry'))) {
      return $entries[0];
    }
  }

  watchdog('cmis_alfresco_cmisapi_getProperties', 'Unknown objectId "@objectId"',
             array('@objectId' => $objectId));
return FALSE;
}

/**
 * Implemetation of cmisapi_getContentStream method
*
 * @param $repositoryId
 * @param $objectId
 */
function cmis_alfresco_cmisapi_getContentStream($repositoryId, $objectId) {
module_load_include('utils.inc', 'cmis_alfresco'),

  $objectId = cmis_alfresco_objectId($objectId);
return cmis_alfresco_invoke_service(
'/api/node/content/'. $objectId['noderef_url']);
}

The following Drupal module processes the XML entries that return from the CMIS AtomPub call. There is no standard Atom entry processing in Drupal.

/**
 * Process CMIS XML.
 *
 * @param $xml CMIS response XML.
 * @param $xpath xpath expression.
 */
function cmis_alfresco_utils_get_CMIS_xml($xml, $xpath = NULL) {
  try {
    $cmis_service = new SimpleXMLElement($xml);
  }
  catch (Exception $e) {
    cmis_error_handler('cmis_alfresco_utils_get_CMIS_xml', $e);
    throw new CMISException(t('Unable to process xml.'));
  }

  foreach (cmis_alfresco_utils_get_namespaces() as $ns => $namespace) {
    $cmis_service->registerXPathNamespace($ns, $namespace);
  }

  if ($xpath) {
    return $cmis_service->xpath($xpath);
  }

  return $cmis_service;
}

/**
 * Utility function for returning CMIS objects from cmis response
 * (ie. getChildren, query, getDescendants)
 *
 * @param $entries
 * @return array
 */
function _cmis_alfresco_getEntries($entries) {
  $result = array();
  foreach ($entries as $entry) {
    $cmis_object = _cmis_alfresco_utils_entry($entry);
    $cmis_object->properties = array();
    $cmis_element = $entry->children(cmis_alfresco_utils_ns('cmis'));
foreach (_cmis_alfresco_utils_known_property_types() as $type) {
      $property_tag = 'property'. $type;
      foreach ($cmis_element->object->properties->$property_tag as $property) {
        $attrs = $property->attributes(cmis_alfresco_utils_ns('cmis'));
        $cmis_object->properties[(string) $attrs->name] =
              cmis_alfresco_utils_cast($property->value, $type);
      }
    }

    $cmis_object->type = $cmis_object->properties['BaseType'];

    if ($cmis_object->type == 'document') {
      $cmis_object->size = $cmis_object->properties['ContentStreamLength'];
      $cmis_object->contentMimeType =
          $cmis_object->properties['ContentStreamMimeType'];
      $cmis_object->versionSeriesCheckedOutBy =
           $cmis_object->properties['VersionSeriesCheckedOutBy'];
    }

    $result[] = $cmis_object;
  }

return $result;
}

/**
 * Utility function for returning a common entry object from a feed entry
 *
 * @param $xml_element
 * @return stdClass
 */
function _cmis_alfresco_utils_entry($xml_element) {
  $entry = new stdClass();

  $tmp_objectId = cmis_alfresco_objectId((string) $xml_element->id);
  $entry->id = $tmp_objectId['noderef'];

  $entry->title = (string) $xml_element->title;
  $entry->summary = (string) $xml_element->summary;
  $entry->updated = date_create($xml_element->updated);
  $entry->author = (string) $xml_element->author->name;

return $entry;
}

/**
 * Utility function form casting cmis properties
 *
 * @param $value
 * @param $type
 * @return mixed
 */
function _cmis_alfresco_utils_cast($value, $type) {
  $return = NULL;
switch ($type) {
  case 'Integer':
    $return = (int) $value;
    break;
  case 'Boolean':
    $return = (bool) $value;
    break;
  case 'String':
  case 'Id':
    $return = (string) $value;
    break;
  case 'DateTime':
    $return = date_create((string) $value);
    break;
  default:
    $return = $value;
  }

return $return;
}

/**
 * Utility function for encoding cmis properties
 *
 * @param $propertyCollection
 * @return string
 */
function _cmis_alfresco_utils_properties_to_xml($propertyCollection = array()) {
  $properties_xml = '';

  foreach ($propertyCollection as $key => $value) {
    $property_type = gettype($value);

    switch ($property_type) {
    case 'integer':
    case 'boolean':
    case 'string':
      $properties_xml .= '<cmis:property'. ucfirst($property_type)
         .'cmis:name="'. $key .'">';
      $properties_xml .= '<cmis:value>'. $value .'</cmis:value>';
      $properties_xml .= '</cmis:property'. ucfirst($property_type) .'>';
      break;
    default:
      watchdog('_cmis_alfresco_utils_properties_to_xml',
          'Unable to map property "@property" of type "@property_type" '.
          'for destination object "@objectId"',
          array('@property' => $key, '@property_type' => $property_type,
              '@objectId' => $objectId), WATCHDOG_ERROR);

      drupal_set_message(t(
          'Unable to map property "@property" of type "@property_type" for ' .
          'destination object "@objectId"',
          array('@property' => $key, '@property_type' => $property_type,
             '@objectId' => $objectId)), 'error'),
}
  }

return $properties_xml;
}

These calls, in turn, are used to hook into the CCK, which has predefined templates for presenting forms and tabular information of similar types. Reusing work to integrate other hierarchical browsers, tabular results, and metadata forms, this Drupal integration provides the following hook points.

<?php
// $Id: cmis_alfresco_field.module,v 1.2 2009/09/09 16:51:41 cbalan Exp $

/**
 * Implementation of hook_field_settings
 *
 * @param $op - operation
 * @param $field - field begin operated on
 * @return - form or settings array dependent on operation
 */
function cmis_alfresco_field_field_settings($op, $field) {
  switch ($op) {
  case 'form':
    $form['root_directory'] = array(
        '#title' => t('Root Directory'),
        '#description' => t('Root Directory for Alfresco nodes'),
        '#type' => 'textfield',
        '#autocomplete_path' => 'cmis/autocomplete',
        '#default_value' => '/Company Home',
      );
    return $form;

  case 'save':
    $settings = array('root_directory'),
    return $settings;

  case 'database columns':
    $columns = array(
      'path' => array('type' => 'varchar', 'length' => 255, 'not null' => FALSE),
    );
    return $columns;
  }
}

/**
 *
 * @param $op - operation
 * @param $node - node
 * @param $field - field settings
 * @param $items - field value(s)
 * @param $teaser - boolean for whether or not we're displaying a teaser
 * @param $page - boolean for whether or not we're displaying a page
 * @return unknown_type
 */
function cmis_alfresco_field_field($op, &$node, $field, &$items, $teaser, $page) {
  switch ($op) {
  case 'validate':
    foreach ($items as $i) {
      if (empty($i)) {
        form_set_error('', 'The field cannot be empty'),
      }
    }
  return $items;
  break;
  }
}

/**
 * Implementation of hook_widget_info
 *
 * @return array defining the widget
 */
function cmis_alfresco_field_widget_info() {
  return array(
    'cmis_alfresco_field_widget' => array(
       'label' => t('Alfresco browser'),
       'field types' => array('cmis_alfresco_field'),
       'multiple values' => CONTENT_HANDLE_CORE,
       'callbacks' => array('default value' => CONTENT_CALLBACK_CUSTOM),
       'description' => t('Click to browse the Alfresco repository and ' .
            'select an Alfresco node to associate with this Drupal node.' )
    )
  );
}

/**
 * Implementation of hook_elements()
 *
 * @return array elements to be processed by FAPI
 */
function cmis_alfresco_field_elements() {
  return array(
    'cmis_alfresco_field_widget' => array(
      '#input' => TRUE,
      '#process' => array('cmis_alfresco_field_widget_process')
  )
  );
}

/**
 * Implementation of hook_process()
 *
 * @param $element - the form element array
 * @param $edit -
 * @param $form_state - form state array
 * @param $form - form array
 * @return array - form element
 */
function cmis_alfresco_field_widget_process($element, $edit, $form_state, $form) {
  $defaults = $element['#value'];
  if (!is_array($defaults)) {
    $defaults = unserialize($defaults);
  }

  $element['path'] = array(
    '#type' => 'textfield',
    '#default_value' => $defaults['path'],
    '#autocomplete_path' => 'cmis/autocomplete',
  );

return $element;
}

/**
 * Implementation of hook_widget()
 *
 * @param $form - form array
 * @param $form_state - form state array
 * @param $field - field array
 * @param $items - field values
 * @param $delta - id of the field (if there is more than one in the form)
 * @return form element array
 */
function cmis_alfresco_field_widget(& $form, & $form_state, $field, $items,
                $delta = 0) {
  $element = array(
    '#type' => $field['widget']['type'],
    '#default_value' => isset($items[$delta]) ? $items[$delta] : ''
  );

return $element;
}

/**
 * Implementation of hook_field_formatter_info
 *
 * @return array
 */
function cmis_alfresco_field_field_formatter_info() {
  return array(
    'default' => array(
       'label' => t('Alfresco browser'),
       'field types' => array('alfresco_cmis_field')
    )
  );
}

/**
 * Implementation of hook_theme()
 *
 * @return array of theme functions
 */
function cmis_alfresco_field_theme() {
  return array(
    'cmis_alfresco_field_widget' => array('arguments' => array('element')),
    'cmis_alfresco_field_formatter_default' =>
         array('arguments' => array('element' => NULL))
  );
}

/**
 * Function to theme the widget form
 * @param $element
 * @return string - themed output of the widget
 */
function theme_cmis_alfresco_field_widget(&$element) {
return theme('form_element', $element, $element['#children']);
}

function theme_cmis_alfresco_field_formatter_default($element = NULL) {
if (empty( $element['#item'])) {
return '';
  }

return print_r($element, TRUE);
}
?>

The most important part of the previous example is the first section, which gets the repository information, enumerates through children in a folder, and gets the properties of an entry. The sections that follow are common for browser-style interfaces that present a user interface. The second section handles the mechanics of manipulating the Atom feed generated by CMIS, as well as the feed to CMIS. In this case, neither Drupal nor PHP has a native library for handling Atom and its data types, but the functions presented can be reused for these purposes and are not specific to Alfresco. Finally, the hook functions in the last section are needed to integrate the browser with the CCK, but are similar to other Drupal theme extensions using CCK.

Librarian

A Librarian (see Figure 17-7) is a specialized browser that controls the content in a hierarchical view. Because the primary context in which content normally lives within an Alfresco repository is the folder/space hierarchy, the Browser view is the natural place to put library type controls. Library services describe the Check Out and Check In services originally designed for engineering document control, but has been extended to document management and Web content management. It provides the tools to edit the content with integration to the appropriate authoring application, which may or may not be available within the browser, depending upon the MIME type of the content. It also provides a view to explore the version history of a content object.

Using a Librarian

A Librarian provides the hierarchical browsing experience of a browser, while adding the ability to check out a content object for revision. It also provides a property sheet for metadata entry and update, an interface to add or replace the content file, and a form to check in the content object. In addition, there is a Query view for exploring and accessing the version history of a content object.

FIGURE 17-7

Figure 17.7. FIGURE 17-7

The Librarian view presents specific actions for the content Check Out and Check In. It must also present a property sheet to edit the content object properties with whichever properties should be editable, as well as those properties that are mandatory. This information is available from the Data Dictionary, which is accessible through Web scripts and CMIS if the Librarian dynamically generates the forms for different types.

The Librarian view provides the interfaces for adding or replacing content. If the MIME type of the content is text, HTML, or even some types of XML, it may be possible to provide an editing tool within the browser application. More complex AJAX applications can be used to manipulate even more complex document types, such as graphical drawings and Office documents. In most cases beyond basic text and HTML, the Librarian will be responsible for launching the correct application or will use the MIME type for the browser to launch the correct application. Using WebDAV to edit the document makes it possible for the application to replace the content in the repository; the repository then controls versioning and user permissions.

When to Use It

A Librarian is useful for applications that provide general or special purpose controls of large amounts of content. A general purpose document management tool that is independent of an underlying repository usually implements a Librarian. Image-scanning and -management tools are also Librarians. Applications that manage special purpose libraries, such as research libraries, reference materials, or policy libraries, are vertical examples of libraries. However, these are often best handled with something like the Alfresco Share application or a general-purpose CMIS library tool extended with domain-specific pages or forms.

Example

CMIS Spaces, from Integrated Semantics, is a CMIS-based Librarian that uses Adobe Flex for the user interface (see Figure 17-8). Flex provides a very rich browser experience with all the functionality of a thick client. CMIS Spaces can be distinguished from a browser in that it provides library services for uploading and updating content as well as manipulating metadata.

FIGURE 17-8

Figure 17.8. FIGURE 17-8

CMIS Spaces provides Rich Internet Application (RIA) clients for Flex and AIR (the Adobe client-based runtime) Flex and Internet browser (such as Microsoft IE or Mozilla Firefox). CMIS Spaces uses the UI-independent ActionScript (Adobe's JavaScript-like script-based process language) and APIs for both CMIS AtomPub REST and CMIS Web services (SOAP). It uses a number of other open source projects, such as Cairngorm, Presentation Model pattern, and Spring ActionScript.

CMIS Spaces' Flex+AIR configuration also can handle the headers and delete/put HTTP verbs for AtomPub updating. For Flex+Browser AtomPub updating, CMIS Spaces can be configured to use sockets (as 3httpclientlib).

Catalog View

A Catalog view (see Figure 17-9) is either a Query view or a specialized Browser view to present a list of items to act upon, such as purchasing the item being presented. A Catalog view may make extensive use of images. The number of items may determine whether to use a Query view, a Browser view, or both. The List view of the catalog will present information specific to what the content is describing rather than the content object itself. Likewise, a detail or Property Sheet view will present metadata and actions relevant to the subject of the content rather than the content. For instance, a catalog of products may have a set of images, but present information and metadata about the product rather than the actual photo. However, a catalog can still present photos and articles and be content-specific since that may be the subject matter of the catalog.

FIGURE 17-9

Figure 17.9. FIGURE 17-9

Using a Catalog View

A Catalog view uses a Query view either when there are relatively few items or when a categorization is not appropriate, often because there are too many categories. A catalog of articles is a relatively simple example of a catalog and doesn't really require much structure if there are a dozen or so articles. If there are tens of thousands of items that are not easily accessible through a category structure, then queries may be the easier way to access the catalog item. In such a circumstance, additional Query views may be used to show recently added items.

A Catalog view uses a Browser view when there is a moderate number of items and is easily browsed and accessed through a folder or category hierarchy. Using the native folder system in a subset of folders is usually the best way to manage the catalog for presenting catalog items through a Browser view. Even in this case, it is usually best to provide a Query view to find the catalog items.

The view generated for either the Query view or the Browser view may be presented differently than in the normal views. If the catalog is more visual because the content is an image or a thumbnail is more important than the metadata, then the view may generate more of a "Light Box" view that adds as many images as possible on a page or page component. Otherwise, a standard Browser view list would be more appropriate.

The property sheet should present the actions that are appropriate for the purpose of the catalog, such as download or purchase. Metadata should be kept to a minimum, such as name, description, and date added or available.

When to Use It

Catalogs are useful for presenting lists of articles or images that can be used for business purposes. Catalogs can enhance applications by presenting lists of content that might be helpful in using the application.

Example: Image Gallery in Share

An image gallery, in this case of photos or graphics, is a good example of a catalog. The image gallery available with Alfresco Share is an example of how you can build a similar catalog-type interface using FreeMarker with the Alfresco API. Since FreeMarker is a Web templating engine, this example shows the type of data, images, and presentation that are required.

<#macro detailsUrl image label>
  <a href="${url.context}/page/site/${page.url.templateArgs.site}
/document-details?nodeRef=${image.nodeRef}" class="theme-color1">${label}
  </a>
</#macro>
<script type="text/javascript">//<![CDATA[
  new Alfresco.ImageSummary("${args.htmlid}");
  new Alfresco.widget.DashletResizer("${args.htmlid}", "${instance.object.id}");
//]]></script>
<div class="dashlet">
  <div class="title">${msg("header.title")}</div>
  <div id="${args.htmlid}-list" class="body scrollableList"
        <#if args.height??>style="height: ${args.height}px;"</#if>>
    <#if images.message?exists>
      <div class="detail-list-item first-item last-item">
        <div class="error">${images.message}</div>
      </div>
    <#elseif images.items?size == 0>
      <div class="detail-list-item first-item last-item">
        <span>${msg("label.noitems")}</span>
      </div>
    <#else>
      <#assign detailsmsg = msg("label.viewdetails")>
      <#list images.items as image>
        <#assign nodeRefUrl=image.nodeRef?replace('://','/')>
        <div class="images">
          <div class="item">
            <div class="thumbnail">
              <a href="${url.context}/proxy/alfresco/api/node/content/
${nodeRefUrl}/${image.name?url}"
                   rel="lightbox"
                   title="${image.title?html} –
                   ${msg("text.modified-by", image.modifier)}
                   ${image.modifiedOn?datetime(
                     "dd MMM yyyy HH:mm:ss 'GMT'Z '('zzz')'")
                   ?string("dd MMM, yyyy HH:mm:ss")}">
                <img src="${url.context}/proxy/alfresco/api/node/
${nodeRefUrl}/content/thumbnails/doclib?c=force"/>
              </a>
            </div>
            <div class="details">
<@detailsUrl image detailsmsg />
            </div>
          </div>
          </div>
        </#list>
      </#if>
    </div>
</div>

In this example, the #list directive of FreeMarker is used to iterate through the items in a space that is dedicated to images. A thumbnail generated by the Thumbnail service is accessed from the repository and placed next to the metadata. Within a catalog, thumbnails are a good way to present images, products, or media for detailed access and actions, such as download or purchase.

Attachment

An attachment (see Figure 17-10) is a document logically attached to a business object in an application, such as an invoice, a contract, or a quality-control report. The attachment is important in the process in which the business object is involved, providing documentation such as transactional information or quality data. The attachment may be added outside the application, such as in Alfresco Share with a Share extension, or it may be added by the application, either by adding the content directly or using a browser to find existing content. The attachment is generally available when users access or browse the business object.

FIGURE 17-10

Figure 17.10. FIGURE 17-10

Using an Attachment

In the design of the attachment, you must first determine how the content will be associated with the business object. If the sole purpose of the document is to act as an attachment to one and only one business object, then it is best to have a foreign key (http://en.wikipedia.org/wiki/foreign_key) as part of an Alfresco type or aspect. If the application is responsible for managing the relationship, such as in SAP, then it is still a good idea to have a foreign key to provide a backward reference for access to the business object. If there is more than one business object associated with a document or the document is used for other purposes, you can either use an aspect with a repeating foreign key or use an association in Alfresco. If you are using CMIS, then you must use a foreign key in a type or association.

The attachment first needs to exist in the Alfresco repository. If the application either creates or captures the document that will become the attachment, then the application stores the document in the repository. Applications generally place the content in well-known locations or create the folders/spaces in which to place the content in order to simplify the user's interaction with the application.

If the attachment already exists in Alfresco, then you can add it either in Alfresco Share or using a browser in the application. In Share, you can create a special property sheet associated with an application-specific aspect that allows you to choose a foreign key to associate with the document by querying the applications database for a list of foreign keys. If the application adds the attachment, then the application can present a browser to choose the document to attach.

If the application provides an interface to add an attachment, then it must also provide an interface to remove an attachment.

When the user navigates to a Business Object in the application, the application provides links to the attachment. Normally this would open the attachment either in situ or in a separate window.

When to Use It

Many ERP and CRM applications already provide interfaces to attach content from content management systems or archives. Alfresco makes a good alternative to other systems for this purpose. If an application drives a business process with a requirement to document data (such as invoices or receipts) being entered into a business object, the application needs an object, like an attachment.

If there are reference materials that can help explain a set of data or some other context and the users are capable of adding this content, then an attachment is a good mechanism for allowing users to generate and attach this content.

Example

The SAP ArchiveLink integration created by CTAC, a system integrator in Germany, implements the Content Services Mapping protocol to allow access of SAP systems to external ECM systems. On top of the ArchiveLink protocol, SAP implements the Archive and Query pattern to store and find content in the underlying ECM system. The Attachment pattern is associated with individual business objects in SAP to access the content. Although the metadata is stored in SAP, a copy is made in Alfresco by the CTAC system, as seen in Figure 17-11. The content is accessed from the attachments through the ArchiveLink protocol.

Annotation

An annotation (see Figure 17-12) is a piece of user-generated content that annotates an object (usually a document, Web page, or article) with information or comments regarding that object. Annotations themselves can be annotated, leading to a threaded trail of discussion. Annotations provide the end user with the opportunity to comment on the quality, intent, or accuracy of the document, which improves the document. Thus, annotations are a good way to capture user-generated content for a Web site or Web 2.0 application.

FIGURE 17-11

Figure 17.11. FIGURE 17-11

Annotations can be put in the context of a specific location on a page or can trail the document at the end, appearing to be part of the metadata of the document. Threaded annotation discussions are best kept separate from the content. However, an annotation placed in situ in the document must be done in conjunction with either a desktop-based or rich client-based editor, such as those associated with PDF or Computer Aided Design (CAD) tools.

FIGURE 17-12

Figure 17.12. FIGURE 17-12

Using an Annotation

The application presents a simple form to capture the comment about the content. This is usually done in the Property view. This form is normally either plain text or restricted HTML limited to only basic formatting. The application creates the content as type Annotation and creates an association between the annotation and the document. If the annotation is threaded, an association is also made to the annotation to which this annotation is referring.

Once stored, the Property view regenerates the form view or uses AJAX to append the new annotation. The new annotation is placed at the end of the annotations if the preferred order is chronological or after the annotation if this is an annotation of an annotation.

When to Use It

Annotations are important for collaborative applications as they provide an important part of the feedback loop on the quality of content and presentation of a document or article. They are also useful for engaging the user, and become an important part of the usability of the application.

Example: Comments in a Share Blog

The following is an example of annotations with comments in a blog in Alfresco Share. In this case, these are the comments at the end of the blog and are handled by the FreeMarker template comments.get.html.ftl. This is included in the same page as the blogs using Surf regions. This lists the comments attached to a blog. Most of the setup is for layout within Surf, but the core of the functionality is at the end.

<script type="text/javascript">//
<![CDATA[
  new Alfresco.CommentList("${args.htmlid}").setOptions({
    siteId: "${page.url.templateArgs.site!""}",
    containerId: "${template.properties.container!"blog"}",
    height: ${args.editorHeight!180},
    width: ${args.editorWidth!700},
    editorConfig: {
      height: ${args.editorHeight!180},
      width: ${args.editorWidth!700},
      theme: 'advanced',
      theme_advanced_buttons1:
      "bold,italic,underline,|,bullist,numlist,|,
forecolor,backcolor,|,undo,redo,removeformat",
      theme_advanced_toolbar_location: "top",
      theme_advanced_toolbar_align: "left",
      theme_advanced_statusbar_location: "bottom",
      theme_advanced_resizing: true,
      theme_advanced_buttons2: null,
      theme_advanced_buttons3: null,
      theme_advanced_path: false,
      language: '${locale?substring(0, 2)}'
      }
    }).setMessages(${messages});
//]]>
</script>
<div id="${args.htmlid}-body" class="comment-list" style="display:none;">
  <div class="postlist-infobar">
    <div id="${args.htmlid}-title" class="commentsListTitle"></div>
    <div id="${args.htmlid}-paginator" class="paginator"></div>
  </div>
<div class="clear"></div>
  <div id="${args.htmlid}-comments"></div>
</div>

There is a link in the Comments section that invokes the Web script implemented by createcomment.get.html.ftl. This presents a special view to capture the comment and add it to the repository.

<script type="text/javascript">//<![CDATA[
  new Alfresco.CreateComment("${args.htmlid}").setOptions(
  {
    siteId: "${page.url.templateArgs.site!""}",
    containerId: "${template.properties.container!"blog"}",
    height: ${args.editorHeight!250},
    width: ${args.editorWidth!538},
    editorConfig : {
      height: ${args.editorHeight!250},
      width: ${args.editorWidth!538},
      theme: 'advanced',
      theme_advanced_buttons1:
      "bold,italic,underline,|,bullist,numlist,|,
forecolor,backcolor,|,undo,redo,removeformat",
      theme_advanced_toolbar_location: "top",
      theme_advanced_toolbar_align: "left",
      theme_advanced_statusbar_location: "bottom",
      theme_advanced_resizing: true,
      theme_advanced_buttons2: null,
      theme_advanced_buttons3: null,
      theme_advanced_path: false,
      language: '${locale?substring(0, 2)}'
    }
   }).setMessages(${messages});
//]]></script>

<div id="${args.htmlid}-form-container" class="addCommentForm hidden">
  <div class="commentFormTitle">
    <label for="${htmlid}-content">${msg("addComment")}:</label>
  </div>
  <div class="editComment">
    <form id="${htmlid}-form" method="post" action="">
      <div>
        <input type="hidden" id="${args.htmlid}-nodeRef" name="nodeRef" value="" />
        <input type="hidden" id="${args.htmlid}-site" name="site" value="" />
        <input type="hidden" id="${args.htmlid}-container" name="container"
             value="" />
        <input type="hidden" id="${args.htmlid}-itemTitle" name="itemTitle"
             value="" />
        <input type="hidden" id="${args.htmlid}-page" name="page" value="" />



        <input type="hidden" id="${args.htmlid}-pageParams"
             name="pageParams" value=""/>

        <textarea id="${htmlid}-content" rows="8" cols="80" name="content">
</textarea>
      </div>
      <div class="commentFormAction">
        <input type="submit" id="${htmlid}-submit" value="${msg('postComment')}"/>
      </div>
    </form>
  </div>
</div>

The code here is the presentation layer using Surf to present a list of comments and then to capture a comment from a user. There are calls to set up the theme for the forms, but the main part in both is to use div classes to set up the presentation of the list or form. In the first part, Surf is used to present the list of comments associated with htmlid of the object to which the comments are attached. In the second part, a comment is captured using an HTML form and posted to the object identified by htmlid.

Archive

An Archive (see Figure 17-13) is a repository for the long-term storage and control of information that must be retained for operational or regulatory reasons. Typical uses for an archive are to store reports, scanned documents, or electronic documents that are no longer used, but which the organization wishes to retain for possible future use. Records are a special case of Archive in which the documents and content stored in the archive are managed according to official rules of retention, lifecycle, and/or review process.

FIGURE 17-13

Figure 17.13. FIGURE 17-13

Using an Archive

An archive is usually a section of an Alfresco repository that has a separate folder/space structure specifically for the documents stored in the archive. An archive will specify permission controls on who can add, modify, or delete documents. An archive also provides lifecycles on change of state and location.

The Alfresco Records Management module provides even more sophisticated controls over security and retention. File plans are associated with folders, which can determine how long records will be retained and what happens to the record after retention: is it destroyed or transferred, how often must it be reviewed, and what events change the state of the record? If the application chooses to use the Records Management option, it should either be responsible for filing the records and adding all mandatory information, or it should direct the user or records manager to the Records Management application to process the record.

Most applications integrating an archive are using it to store captured or generated information from the application. The application may also use the archive to store attachments that are part of a business process. Therefore, an archive usually works in cooperation with attachments. There is usually enough information in the application to allow the application to decide where to store the information in the archive. In addition, the application should be responsible for generating the foreign key to allow retrieval and association of the document in the archive.

Since many applications use the file system to store their output, then using the CIFS, NFS, or WebDAV interface may be an alternative way to store and access the documents in the archive. Most email clients support IMAP, so Alfresco's IMAP interface is capable of acting as an archive for email as well. This can be a very simple way to integrate content into a repository, although it will not be able to capture as much metadata as a direct connection. For email, there may be enough information in the From, To, and Subject fields to extract and classify emails going into an archive. The application would be dependent upon rules, actions, and metadata extraction to fill in default information. By associating a workflow with a folder through a rule, Alfresco could forward the document for further metadata entry.

A separate Query view may be generated to present an application-specific view were the documents in the archive based upon context in the application. For instance, if an application was archiving reports based upon a specific portion of that application, then the application would provide a view of those reports that may be independent of where they are stored. It may also provide context, status, and process information that are specific to that application. Alternatively, an application could use a URL link to a specialized Share view or portlet.

Because an archive is used for long-term storage, a separate content store may be used for a Hierarchical Storage Management system or secondary storage. This can be controlled by the application by applying a special aspect that determines into which store the content will go.

When to Use It

Scanning applications commonly use archives. Paper documents are scanned to simplify their retrieval and use in electronic business processes. Scans are also stored to ensure the documents' long-term retention. Very simple scanning interfaces can use the file system emulation to store content in the archive. More sophisticated scanning applications, such as Kofax, use repository-specific release scripts that capture and load metadata into the archive.

Enterprise Resource Planning and Customer Relationship Management applications, such as SAP and Oracle, use Service Provider Interfaces (SPI) for storing reports and archiving data. SAP's ArchiveLink interface was one of the first of its kind to standardize and certify interfaces for archiving. Similar applications that are very process- and report-heavy are also good candidates for an archive.

General clearing and retention of documents and emails are other good uses for an archive. Email clients can use an archive with the IMAP protocol to drag and drop or use email rules to archive old emails. By presenting the archive as a shared drive, it is easy to move content and still make it accessible.

Records in either governmental or regulatory use can also use an archive with the Records Management module. Documents, reports, and scanned documents all represent potential records in an archive backed by the Records repository.

Example: Kofax Release Script

Micro Strategies, Inc. (www.microstrategies.com) has created a Kofax release script that is a standard way to integrate to the Kofax scanning system (www.kofax.com). Kofax is a high-end, high-volume image-scanning system that works with multiple content repositories, and a release script is the standard way to capture metadata and store the content and metadata into a content repository.

In this instance, the release script is written in C# and .NET to integrate with the Kofax client and uses Web scripts to access the Alfresco repository. This file, AlfrescoRelease.cs, uploads the images as documents and hooks into standard Kofax integration points.

/// <summary>
/// The Release URL for Alfresco: This is the URL used to post documents
/// to Alfresco.
/// It uses a ticket for authentication. For example:
/// <code>
/// /service/kofax/release?alf_ticket={0}
/// </code>
/// </summary>
public string ReleaseURL
{
  get { return releaseURL; }
  set { releaseURL = value; }
}

/// <summary>
/// Releases documents to Alfresco: Retrieves all document and metadata information
/// from Kofax and inserts it into Alfresco.
/// </summary>
/// <returns>A KfxReturnValue object detailing if the release was successful
/// </returns>
public KfxReturnValue release()
{
  try
  {
    int uniqueDocumentID = this.releaseData.UniqueDocumentID;
    CustomProperties properties = this.releaseData.CustomProperties;
    initializeCustomProperties(properties);
    initializeWorkingFolders(uniqueDocumentID);

    string response = uploadDocuments();

    //Process Result
    if (response.StartsWith("ERROR"))
    {
      logger.Error(response);
      this.releaseData.SendMessage(response, 9999,
          KfxInfoReturnValue.KFX_REL_DOC_ERROR);
      error.LogError(response, "Alfresco.Kofax.Release.WebScripts.Release",
          response, true, true, "");
return KfxReturnValue.KFX_REL_ERROR;
    }
    else
    {
      WebStatus status = new WebStatus(response);
      int code = status.Code;
      string description = status.Description;
      string message = status.Message;
      string statusName = status.Name;

      if (message.StartsWith("ERROR"))
      {
        logger.Error(response);
        this.releaseData.SendMessage(message, 9999,
            KfxInfoReturnValue.KFX_REL_DOC_ERROR);
        error.LogError(message, "Alfresco.Kofax.Release.WebScripts.Release",
            response, true, true, "");
        return KfxReturnValue.KFX_REL_ERROR;
      }
    }
  }
}

/// <summary>
/// Uploads the documents.
/// </summary>
/// <returns></returns>

private string uploadDocuments()
{
  //Upload Documents
  string kofaxPDFFileName = this.releaseData.KofaxPDFFileName;

  string ticket = AlfrescoTicketHelper.getTicket(url + String.Format(loginURL,
      customUsername, customPassword));

  WebUpload release = new WebUpload(url + String.Format(releaseURL, ticket));
  release.addField("contentType", contentType);
  release.addField("overwrite", overwrite);
  release.addField("uploaddirectory", defaultFolder);
  if ((dynamicFolder != null) && (!dynamicFolder.Equals("")))
    release.addField("dynamicuploaddirectory", dynamicFolder);
  foreach (Value oVal in this.releaseData.Values)
  {
    string destination = oVal.Destination;
    string source = oVal.SourceName;
    string value = oVal.Value;

    if (source.Equals("Image"))
    {
      releaseData.ImageFiles.Copy(imageDirectory, 0);

      //rename move all docs to the primary release dir
      FileInfo[] files = imageDirectoryInfo.GetFiles();
foreach (FileInfo file in files)
      {
        release.addFile(destination, file.FullName, "image/tiff");
      }
    }
  ]
]

This module posts the documents scanned in Kofax to Alfresco using a ticket for authentication. It collects the documents and metadata from Kofax and provides this information as part of the post. It then uploads the document as a stream.

To help the release script, Kofax has created the following C# module, WebUpload.cs, to access the Web scripts that provide the functionality in the Alfresco server needed to process the documents.

namespace Alfresco.Kofax.Release.WebScripts
{
  public class WebUpload
  {
    private string boundary;
    private HttpWebRequest httpWebRequest;
    private Stream requestStream;
    private FileStream fileStream;
    private Hashtable fields;
    private static readonly ILog logger = LogManager.GetLogger(typeof(WebUpload));

    public WebUpload(string url)
    {
      // Create a boundary
      boundary = "----------------------------" + DateTime.Now.Ticks.ToString("x");

      // Create the web request
      httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
      httpWebRequest.ContentType = "multipart/form-data; boundary=" + boundary;
      httpWebRequest.Method = "POST";
      httpWebRequest.KeepAlive = true;
      fields = new Hashtable();
    }

    public string upload()
    {
      string responseString = "";
      try
      {
        //Add Fields to Stream - Content should already be added.
        addFieldsToStream();

        // Get the boundary in bytes
        byte[] boundarybytes = System.Text.Encoding.ASCII
                .GetBytes("
--" + boundary + "--
");

        // Write out the trailing boundary
        requestStream.Write(boundarybytes, 0, boundarybytes.Length);
// Close the request and file stream
        requestStream.Close();
        fileStream.Close();

        WebResponse webResponse = httpWebRequest.GetResponse();
        Stream responseStream = webResponse.GetResponseStream();
        StreamReader responseReader = new StreamReader(responseStream);
        responseString = responseReader.ReadToEnd();

        // Close response object.
        webResponse.Close();
      }
      catch (WebException e)
      {
        if (e.Status == WebExceptionStatus.ProtocolError)
        {
          HttpWebResponse response = ((HttpWebResponse)e.Response);
          string text;
          try
          {
            using (Stream stream = response.GetResponseStream())
                        {
            using (StreamReader reader = new StreamReader(stream))
                            {
            text = reader.ReadToEnd();
            responseString = "ERROR: " + text;
            logger.Error(text);
          }
        }
      }
      catch (WebException ex)
      {
        logger.Error(ex.Message);
        responseString = "ERROR: " + ex.Message;
      }
    }

    return responseString;
  }

  public void addFile(string formFileName, string file, string mimeType)
  {
    // Get the boundary in bytes
    byte[] boundarybytes = System.Text.Encoding.ASCII.GetBytes("
--"
          + boundary + "
");

    // Get the header for the file upload
    string headerTemplate = "Content-Disposition: form-data;
name="{0}";filename="{1}"
 Content-Type: " + mimeType + "

";

    // Add the filename to the header
    string header = string.Format(headerTemplate, formFileName, file);

    //convert the header to a byte array
byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);

    // Get the output stream
    requestStream = httpWebRequest.GetRequestStream();

    // Write out the starting boundary
    requestStream.Write(boundarybytes, 0, boundarybytes.Length);

    // Write the header including the filename.
    requestStream.Write(headerbytes, 0, headerbytes.Length);

    // Open up a filestream
    fileStream = new FileStream(file, FileMode.Open, FileAccess.Read);

    // Use 4096 for the buffer
    byte[] buffer = new byte[4096];

    int bytesRead = 0;
    // Loop through whole file uploading parts in a stream.
    while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
    {
      requestStream.Write(buffer, 0, bytesRead);
      requestStream.Flush();
    }
  }

  private void addFieldsToStream()
  {
    // Loop through all items of a Hashtable
    IDictionaryEnumerator item = fields.GetEnumerator();
    while (item.MoveNext())
    {
      addFieldToStream(item.Key.ToString(), item.Value.ToString());
    }
  }

  public void addField(string formFieldName, string value)
  {
    fields.Add(formFieldName, value);
  }

  private void addFieldToStream(string formFieldName, string value)
  {
    // Get the boundary in bytes
    byte[] boundarybytes = System.Text.Encoding.ASCII
            .GetBytes("
--" + boundary + "
");

    // Get the header for the file upload
    string headerTemplate = "Content-Disposition: form-data; name="{0}"

";

    // Add the filename to the header
    string header = string.Format(headerTemplate, formFieldName);

    //convert the header to a byte array
byte[] headerbytes = System.Text.Encoding.UTF8.GetBytes(header);
    byte[] valuebytes = System.Text.Encoding.UTF8.GetBytes(value);

    // Get the output stream
    requestStream = httpWebRequest.GetRequestStream();

    // Write out the starting boundary
    requestStream.Write(boundarybytes, 0, boundarybytes.Length);

    // Write the header
    requestStream.Write(headerbytes, 0, headerbytes.Length);

    // Write the value
    requestStream.Write(valuebytes, 0, valuebytes.Length);
  }
}

These helper methods provide the tools for the .NET release script to add the metadata and file binaries to the HTTP request that interacts with the Web script. These are used in the preceding release script example and can be valuable for any .NET application accessing a Web script.

Project Space

A Project Space (see Figure 17-14) is a place for users of an application to share ideas, comments, documents, emails, and other information. If a business application is very data-centric, then it will generally not have a good place to store unstructured information as the users of the application develop changes to configuration or structured information that may go into the application. Given a certain context and structure, a Project Space can enhance a data-centric business application to take advantage of collaboration between users of the application. Share, in turn, can access information through a view presented by the business application.

FIGURE 17-14

Figure 17.14. FIGURE 17-14

Using a Project Space

This is really a cooperation of two applications: the business application and Alfresco Share. The business application interacts with Share through an interface similar to an archive to store and retrieve information. This can either be through a file emulation interface, such as CIFS, NFS, or FTP, or through an API. This is where the business application deposits information that might be useful for the collaboration, such as reports or analysis.

To capture the results of the project, it is best to provide a view on the business application to the users of the Project Space. This is best provided with a Page Component for Alfresco Share that may simply expose an iFrame, or through a Share dashlet, which again can be an iFrame. A button or interface on or near this view can be used to capture the action taken in the business system to record the results.

When to Use It

In Customer Relationship Management or Bid Management systems, large teams may collaborate in the creation of offerings and terms for customers. A Project Space provides a space for them to discuss and decide on approaches and iterate on the offerings or contracts. The document management system provides the tools for creation, management, reuse, and delivery of the output, while the CRM system takes the result as an attachment, thus enhancing the sales process.

Configuration of complex data or structures in a business application, such as an ERP or CRM system, can be difficult to track. By turning the process into a recipe upon which other people collaborate, it is possible to walk through the scenarios of what changes in the configuration would mean and then take action. If the change in configuration is not complete, then the team can collaborate on new configurations. Alfresco Share would allow them to document changes, discuss the possible outcomes, and act. The view from the business application would allow the configuration to take place.

Review and Approval Workflow

A Review and Approval workflow (see Figure 17-15) is similar to a Project Space but the purpose is more focused on tracking the review of critical documents in a review process. The workflow takes an artifact and delivers it to key responsible people for review, and then either approval or rejection. The workflow also provides an audit trail of the decisions made. As with the Project Space, the business application may lack a workflow capability and a repository to track the artifacts of the business process. A Review and Approval workflow demonstrates that key documents and deliverables have been signed off by those responsible for the end result, either for regulatory or operational efficiency reasons.

Using a Review and Approval Workflow

A workflow is built using jBPM either in XML or using the Eclipse-based workflow design tool for jBPM. One or more workflows can be made available for usage. The easiest way to assign the workflows is to use a rule in a space in an archive dedicated to processing the contents of the business application. Alternatively, the application can initiate the workflow using a Web script upon entry in the archive. Using the Attachment pattern, the content can be added to the business object and then the workflow is initiated on that document.

The workflow then manages the process of Review and Approval. The content is delivered to the end user via email. The user can then track items that need action through the Alfresco clients or via a portlet. The Alfresco system can also be configured to send reminders if necessary.

FIGURE 17-15

Figure 17.15. FIGURE 17-15

Status can be tracked using an aspect. This aspect should have the status and the identifier of the workflow to allow action on that workflow if necessary. This status can be inspected by the application to indicate whether the document is ready for use or not and whether it has been processed and edited properly. The attachment can then indicate the current status of its associated workflow through that aspect so that the status persists beyond the life of the workflow. Status can also be tracked using a Query view that queries the status of currently active documents for review.

When to Use It

When an application must provide documentation that procedures and policies are being followed, a Review and Approval workflow can demonstrate that the appropriate reviews have been taken by those responsible. This is often required for compliance with regulatory authorities, such as for Sarbanes-Oxley, and for industry certification such as ISO 9000.

Example: Kofax

In the Kofax example provided earlier, Micro Strategies used the rules and actions capabilities of Alfresco. As part of the repository setup, the spaces that the images are stored in are assigned a workflow for any new images coming in. As soon as the images are added, the rule associated with new items assigns a quality control workflow that puts the image through a workflow and assigns the image to the appropriate person. No coding was necessary.

Feed

Some applications can access or track content using RSS feeds (see Figure 17-16). Some examples are mail clients, feed readers, or portals that have feed readers built in. With these feed readers, the application can track changes or actions that a user needs to take in a workflow. In addition, feeds are a good way to track the changes in a repository that, in turn, can be a good way to track what is happening in the business if you are tracking business-critical documents or Web pages.

Using a Feed

A feed can be implemented as a simple Query view implemented as a Web script that is rendered as an RSS feed using an RSS template. There are default feeds defined in Alfresco Share and Explorer, so these do not need to be coded if the default feeds suit your purpose. The links from the feed can be directed either to the content directly or to the Properties page in Alfresco Share. The Web script can also render the Activities feed to track changes across multiple sites.

FIGURE 17-16

Figure 17.16. FIGURE 17-16

When to Use It

Use a feed to track process changes or changes in business-critical information in a repository. Workflow changes are tracked well if they are delivered to a favorite feed reader. Also, feeds can be an easy way to integrate into a portal.

Example: Activities Atom Feed

The following is an example of an Atom subscription feed implemented using the FreeMarker API. Specifically, this is getting the activities associated with a site and returning the latest updates as an Atom feed.

<#assign mode = args.mode!"">
<#if mode = "user">
  <#assign title=msg("atom.title.user", user.fullName?xml)>
<#else>
  <#assign title=msg("atom.title.site", args["site"]?xml)>
</#if>
<#assign genericTitle=msg("title.generic")>
<?xml version="1.0" encoding="UTF-8"?>
  <feed xmlns="http://www.w3.org/2005/Atom">
    <generator version="1.0">Alfresco (1.0)</generator>
    <link rel="self" href="${absurl(url.full)?xml}" />
    <id>${absurl(url.full)?xml}</id>
    <title>${title?xml}</title>
    <#if activities?exists && activities?size &gt; 0>
<updated>${activities[0].date.isoDate}</updated>
      <#list activities as activity>
        <#assign userLink="
          <a href="${absurl(activity.userProfile)}">
                ${activity.fullName?html}</a>">
        <#assign itemLink="
          <a href="${absurl(activity.itemPage)}">
                ${activity.title?html}</a>">
        <#assign siteLink="
          <a href="${absurl(activity.sitePage)}">
                ${activity.siteId?html}</a>">
        <entry xmlns='http://www.w3.org/2005/Atom'>
          <#assign detail = msg(activity.type, activity.title?xml,
                activity.fullName?xml,activity.custom0, activity.custom1)>
          <#if mode="user" && !activity.suppressSite>
            <#assign detail=msg("in.site", detail, activity.siteId?xml)></#if>
          <title><![CDATA[${detail?xml}]]></title>
          <link rel="alternate" type="text/html"
                      href="${absurl(activity.itemPage)}" />
          <id>${activity.id}</id>
          <updated>${activity.date.isoDate}</updated>
          <#assign detailHTML = msg(activity.type, itemLink, userLink,
                      activity.custom0, activity.custom1)>
          <#if mode = "user" && !activity.suppressSite>
            <#assign detailHTML = msg("in.site", detailHTML, siteLink)></#if>
          <summary type="html">
            <![CDATA[${msg(detailHTML)}]]>
          </summary>
          <author>
            <name>${activity.fullName?xml}</name>
            <uri>${absurl(activity.userProfile)?xml}</uri>
          </author>
        </entry>
      </#list>
    </#if>
  </feed>
</xml>

The #list directive in FreeMarker is used to iterate through the activities of the site. FreeMarker has access to the activities as a collection and this script accesses each activity and formats the information specified by the Atom subscription specification, including name, author, and date.

Portlet View

Many Alfresco installations use Alfresco in conjunction with portals, such as Liferay. Portals provide a page view composed of individual windows called portlets (see Figure 17-17), which aggregate and assemble information according to the user's preference. Content from content management systems is often some of the most important information that comes through a portal in the form of corporate news, key business decisions, changes in plans or products, or policies and procedures.

A portlet consists of specialized Query views or Browser views. The portlets are designed to work with JSR-168 or JSR-286–compatible portals. The presentation technology is either Web scripts or Surf. Depending on how they are rendered, they can also be used as Google Gadgets, which is a similar technology. The Portlet views can be used for tracking workflow tasks, currently checked-out documents, activities from Share sites, or documents of interest to the user based upon topic or keyword. A Portlet view can also present a Browser view for navigating a repository from the portal.

FIGURE 17-17

Figure 17.17. FIGURE 17-17

Using a Portlet View

A Portlet view wraps a Query view or a Browser view within a JSR-168 container. This container persists preferences and can provide configuration information for the portlet, as well as authentication and personalization. The portlet redirects a request to a Web script, which in turn renders a Query view as a JSR-168 portlet. The actions rendered by the Query view should be consistent with a portlet. For example, when a view accesses a content and details page, this should be handled in a portal-friendly way.

A Property view in a portal should be its own portlet, allowing the user to navigate correctly through the portal. In this case, the Property view should appear as a portlet, providing details of an object that would have been clicked through from a Query view or Browser view.

When to Use It

Portals are useful when integrating information from many sources, particularly across the enterprise. When you wish to present more than just content from Alfresco, such as news feeds, general information (for example, weather), information from other enterprise applications, or analytical dashboards, then a portal can be a good choice for accessing Alfresco as well. Liferay is the most common open source portal used with Alfresco. However, other portals have also been used, such as IBM WebSphere, Oracle/BEA WebLogic, and Microsoft SharePoint.

Example: Liferay and CMIS

The following is an example of a portlet designed to work in the Liferay portal using CMIS to access the repository. It uses a basic XML parser to handle the AtomPub protocol for accessing CMIS.

package training;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.PortletSecurityException;
import javax.portlet.PortletSession;
import javax.portlet.PortletURL;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class AlfrescoTrainingPortlet extends GenericPortlet {

  @Override
  protected void doView(RenderRequest request, RenderResponse response)
     throws PortletException, IOException
  {

    response.setContentType("text/html");

    HttpClient client = new HttpClient();
    client.getParams().setAuthenticationPreemptive(true);
    Credentials defaultcreds = new UsernamePasswordCredentials("admin", "admin");
    client.getState().setCredentials(AuthScope.ANY, defaultcreds);
    String url;

    String objectIdParam = (String)    request.getPortletSession()
            .getAttribute("objectId", PortletSession.PORTLET_SCOPE);
    if (objectIdParam == null) {
      url = http://localhost:8080/alfresco/s/api/path/workspace/SpacesStore/"+
              "Company%20Home/children";
    } else {
      url = "http://localhost:8080/alfresco/s/api/node/workspace/SpacesStore/" +
            objectIdParam + "/children";
    }
GetMethod method = new GetMethod(url);
    client.executeMethod(method);
    PortletURL actionURL = response.createActionURL();

    PrintWriter writer = response.getWriter();
    try {
      Document dom = DocumentBuilderFactory.newInstance().newDocumentBuilder()
            .parse(method.getResponseBodyAsStream());
      NodeList list = dom.getElementsByTagName("cmis:propertyId");
      int len = list.getLength();
      for (int i = 0; i < len; i++) {
        Element element = (Element) list.item(i);
        String propertyName = element.getAttribute("cmis:name");
        String objectId = null;
        if (propertyName.equals("ObjectId")) {
          objectId = element.getElementsByTagName("cmis:value")
                .item(0).getTextContent();
          objectId = objectId.replaceAll("workspace://SpacesStore/", "");
          writer.println("<p>" + objectId);
        }
        if (objectId == null) {
          continue;
        }
        NodeList stringList = ((Element) element.getParentNode())
              .getElementsByTagName("cmis:propertyString");
        int stringSize = stringList.getLength();
        for (int j = 0; j < stringSize; j++) {
          Element strElem = ((Element) stringList.item(j));
          String strName = strElem.getAttribute("cmis:name");
          if (strName.equals("Name")) {
            actionURL.setParameter("objectId", objectId);
            writer.println("<a href='" + actionURL.toString() + "'>"
                  + strElem.getTextContent() + "</a>");
            break;
          }
        }
      }
    } catch (Exception exc) {
     exc.printStackTrace();
    }
  }

  @Override
  public void processAction(ActionRequest request, ActionResponse response)
      throws PortletException, PortletSecurityException, IOException
  {
    String objectId = request.getParameter("objectId");
    if (objectId != null) {
      request.getPortletSession().setAttribute("objectId", objectId,
              PortletSession.PORTLET_SCOPE);
    }
  }
}

This class, specialized from GenericPortlet, uses the method doView() to actually create the Portlet view. It is using CMIS getChildren() to get the children of the folder browsed, initially the Company Home Page at the top of the repository. It constructs a Browser view specialized for a portlet and creates links for the next level of browsing. It also iterates through all the properties to display their values in the portlet.

EXAMPLE INTEGRATIONS

You have already seen a number of systems that have integrated with Alfresco. Many of these use CMIS because of the opportunity to make them work with other ECM systems. However, Alfresco is often the starting point for these integrations because of the open source implementation available as a community download and because of the growing community of CMIS developers around Alfresco.

Following are examples of some of the most popular or requested integrations.

Joomla and Drupal

Joomla and Drupal represent the most popular PHP-based content management systems and a significant percentage of LAMP (Linux Apache MySQL PHP) implementations. Neither system has focused on repository or document capabilities, so Alfresco as an open source content repository represents a popular way of managing documents or other controlled content. Also, because content is stored in a MySQL database, Alfresco becomes a scalable way of storing information.

Integrations are available from both communities (see Figures 17-18 and 17-19) and focus on the Browser and Search patterns.

FIGURE 17-18

Figure 17.18. FIGURE 17-18

FIGURE 17-19

Figure 17.19. FIGURE 17-19

Confluence

Confluence is a popular wiki from Atlassian often used in development and increasingly used in enterprise environments. Based upon Java, the Confluence integration (see Figure 17-20) fits neatly with Alfresco and uses REST and Web scripts to integrate, as well as CMIS. The integration with Alfresco consists primarily of macros that can be used in wiki pages to access documents or other content in the Alfresco repository. These macros implement either the browser pattern or the query pattern, allowing the wiki page author to put a CMIS query in the page.

FIGURE 17-20

Figure 17.20. FIGURE 17-20

Liferay Portal

Alfresco integrates with a number of portals, particularly JSR-168 and JSR-286–based portals, but Liferay is probably the most popular open source and possibly general portal integrated with Alfresco. Integration with Alfresco consists of single sign-on and a set of portlets. Many organizations write their own portlets with Alfresco; however, with Alfresco 3.3 it is possible to use Share portlets and the Share document library as portlets as well.

SAP

Alfresco is used in several SAP installations, as seen in Figure 17-21, using the archive pattern and the SAP ArchiveLink protocol. Thus the ArchiveLink and SAP implement the Content Service Mapping pattern for ArchiveLink and Archive patterns inside the SAP system. This allows the linkage of content to SAP business objects and to archive reports. In addition, ArchiveLink supports the Attachment pattern to attach scanned images, perhaps with Kofax, and attaching those images to a business object.

FIGURE 17-21

Figure 17.21. FIGURE 17-21

Kofax Image Scanning

The Kofax integration was built by Micro Strategies and integrates Alfresco through release scripts. These release scripts implement the archive pattern by storing content and metadata in Alfresco, and implement the workflow pattern by presenting quality control workflows inside the Alfresco system. Images are scanned locally and the user is presented with metadata to capture. Users can access and process images through the Web, as seen in Figure 17-22.

Microsoft SharePoint

CMIS-based Web parts can integrate Microsoft SharePoint with Alfresco. Some prototype Web parts were demonstrated during the CMIS Technical Committee face-to-face meetings. It is likely that CMIS-based Web parts will be available with the official release of SharePoint 2010 and the official version of CMIS 1.0 by OASIS. These Web parts would implement the Portlet, Browser, and Search patterns.

FIGURE 17-22

Figure 17.22. FIGURE 17-22

FIGURE 17-23

Figure 17.23. FIGURE 17-23

Lotus Quickr

IBM Lotus Quickr and Connections access content services through ECM Services for Quickr, an abstract interface to ECM systems. Alfresco ECM Services for Quickr provides this interface as an extension to the Alfresco system using a combination of AtomPub and Web services calls. Alfresco Services for Quickr implements the Content Service Mapping pattern to allow access to Alfresco; the Archive pattern for storing transient content in Quickr into Alfresco; the Attachment pattern by having a link in Quickr to access the content; and, finally, the Browser pattern to navigate the Alfresco repository. This provides to Quickr users lightweight ECM and archiving services that can run on the same machine as the Quickr system (see Figure 17-23).

Email Clients

Integrating email clients such as Microsoft Outlook, Lotus Notes, or Apple Mail can be done through the Alfresco IMAP protocol, as seen in Figure 17-24. The IMAP protocol itself implements the Content Services Mapping pattern by mapping browsing calls to mail folders and individual content items to mail with the content presented as attachments. In addition, this can implement the Archive pattern in that email may be archived by drag and drop or by archiving to what appear to be mail folders.

FIGURE 17-24

Figure 17.24. FIGURE 17-24

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.80.173