PhotoCMS in Alfresco Share

By Chris Dixon

Photo Management is a key component to Web Content Management. However, managing photos for multiple platforms can become a tangled mess of photo sizes,  duplications, and renditions.

PhotoCMS, built upon Alfresco Share 4, using ImageMagick, and Jquery plugins simplifies Photo Management for the Enterprise.

Alfresco Share [v4+] makes for a great platform with the Mozilla rhino JS engine built-in, JQuery and its numerous plugins can be integrated for advanced tools to supplement ImageMagick, which is also an Alfresco bundled application.

To manage the generation of numerous photo renditions, we make use of ImageMagick’s integration with Alfresco. Using a Spring context files we define each rendition:

<bean id=”Rendition40x40″ class=”com.src.repo.rendition.PhotoRendition” parent=”basePhotoRendition”>
<property name=”identifier” value=”40×40″/>
<property name=”sizeValue” value=”40×40″/>
<property name=”applicableSites”>
<list>
<value>showname</value>
</list>
</property>
</bean>

At one of our clients (a large Media Company) we have implemented Alfresco Share 4 with our WCM enhancements and PhotoCMS solution to manage their array of television shows.

They are currently using the PhotoCMS for 80 renditions per show. 80 renditions per image may seem like a lot, but thanks to ImageMagick, creation of renditions is quickly completed and ready for user review.

Using ImageMagick and the photo’s EXIF properties, we can identify the photo’s orientation before making a crop, to minimize heads being cutoff.

Once all renditions are generated a user needs a way to review and adjust the renditions; a banner ad style rendition is very different from portrait cut rendition.

The Photo Rendition page provides the user the opportunity to review the generated rendition and launch the ReCropping tool or the Override Thumbnail tool, if needed.

Screen Shot 2013-01-08 at 11.28.13 AM

Clicking on the given size updates the rendition image shown for review. If necessary the rendition can be recropped by launching the recrop tool. Alternatively, the Override Thumbnail page can be launched, allowing the user to replace the rendition with a custom photo of the same size.

Screen Shot 2013-01-08 at 11.33.10 AM

Clicking a given size launches a photo picker where a separate image can be selected or a new image can be uploaded.

Adding the ability to recrop a photo within Alfresco shortens the business process necessary to generate a rendition. The recrop tool integrates the Jquery Resize and Crop plugin from Cedric Gampert (https://github.com/trepmag/jrac) providing users the ability to create an updated rendition directly within the CMS to capture the exact rendition wanted.

Recrop Photo

The thumbnails drop down is populated by the previously mentioned context file where renditions are defined by site, minimizing the rendition sizes. The draggable yellow box is locked to the thumbnail (rendition) size selected. The bottom bar allows the user to zoom in and out.

Zoom Example

Upon Submit the zoom level, X and Y coordinates and thumbnail size are submitted to ImageMagick. The updated rendition is generated and saved as an associated rendition on the original image.

Once the image and its rendition have been sorted and approved, they may need additional details associated with them, ie. metadata. All photos have additional metadata fields (specific to the show or purpose) including the ability to be tagged and categorized into Photo Albums.

PhotoCMS Albums are actually specialized Alfresco categories.

Screen Shot 2013-01-08 at 7.43.55 PM

Specialized Category

This specialized category is just text data consisting of a collection of node IDs and additional metadata (tags, custom fields, ordering etc.) without the need to create additional copies of each image and its renditions.

Sample Album Metadata

This allows a single photo to exist only once in the PhotoCMS, but can be in an unlimited number of albums.

Album management needed to be a simple task, using JavaScript we are able to provide drag and drop capabilities to add photos to albums and manage the ordering of photos within an album.

Add to Album

While the focus of this blog post is on Photo Management within the CMS, we also have developed Deployment for photos to netstorage to support web browsing and web apps on multiple platforms. Metadata is deployed, in our media customer’s case, to a custom API in JSON format. This JSON houses all of the Photo’s (or Album’s) metadata and rendition sizes and netstorage URLs. Refer to the WCM on Share blog posts for details on Preview vs Production capabilities.

 

WCM on Share Part II

By Chris Dixon.

All Arise!

WCM management within Alfresco Share brings the best of two worlds together in a simple and easy to use interface, making users feel welcomed and empowered rather than lost. We have been dutifully working on bringing the AVM features to WCM on Share and enhancing the experience with easier to use Alpaca (http://alpacajs.org/) forms, strong merge capabilities and numerous deployment options.

  • Alpaca forms integration brings a new and fresh look to content creation with calendars, restricted pickers, ordering, and so much more using the existing WCM XSDs to build the new forms, storing the data in JSON and XML format!

 

We have also invested time in providing Deployment capabilities to numerous options

  • Alfresco FSTR

  • Custom APIs 

or even directly to MongoDB in JSON format.

We have extended our custom dashlet library to include a few dashlets to provide additional control over your Content, Snapshots, and System.

  • Landing Pages for Custom Content Types

  • Inactive Content Publishing

  • Repo Auditing

We continue to build and grow the WCM on Share interface with new and exciting features to bring Web Content controls to the ECM world. Our clients are thrilled with the progress of WCM on Share and the ease of use it brings to content creation and control.

WCM in Share: Snapshots/Rollback/versions

By Livern Chin

Snapshots

With the lack of snapshoting supports in alfresco share, we have implemented a snapshot approach to support change tracking to the share site as whole. This approach relies heavily on the versioning aspect of share and must be enabled in order for snapshoting to work successfully.

Snapshots are created as part of a business workflow process.  They can be deployed to various file transfer receivers once successfully created.

Snapshots are implemented as a collection of specializedType content nodes called snapshotItem under a specializedType folder called snapshot in our custom site model. A snapshot folder will have custom properties to describe the snapshot itself such as id, label, snapshot state, its creator and others. A snapshot item is a content node that stores the nodeRef to the content node it represents and its current version in the source container.

We have also implemented a custom WebProject type which we represented as a specializedType folder called workarea.  Our custom share site can have one or more workareas, which will have its own snapshots collections.

The process for creating a snapshot and snapshot revert are implemented as a java service in Alfresco.  The createSnapshot method is expecting a site noderef and a workarea (“WebProject”) name the snapshot is to be created in, a label that describes the snapshot, and the state of the snapshot. In our implementation, the state is used to indicate if a snapshot is an approved snapshot for production deployment use or if it has been successfully deployed to production.  With these provided parameters, a snapshot folder will be created in the specified site’s workarea with the proper label and state.  Each noderef and version of the specified site’s workarea descendant will be used to create a snapshot item representing its source node and the version of its source node at the time this snapshot is created.

Here are snippets of the create snapshot method as mentioned above.

public NodeRef createSnapshot(NodeRef siteNodeRef, String sourceName, String label, String status) {
//determine the snapshots container, and the source container for the snapshot, based on the sourceName
final NodeRef snapshotContainerNodeRef = getSnapshotContainerNodeRef(siteNodeRef, sourceName);
NodeRef snapshotSourceNodeRef = null;
//lookup the snapshotSourceNodeRef by siteNodeRef and sourceName
snapshotSourceNodeRef = getSourceNodeRef(siteNodeRef, sourceName);

// collect all the nodes under source as a flat list.
final Snapshot snapshot = new Snapshot();
snapshot.setSnapshotLabel(label);
snapshot.setSnapshotStatus(status);
snapshot.setSnapshotId(String.valueOf(getNextSnapshotId(snapshotContainerNodeRef, true)));

String rootPath = Util.getRootPath(snapshotSourceNodeRef, nodeService, permissionService);

final List  snapshotItems = collectItemsForSnapshot(snapshotSourceNodeRef, rootPath);

long snapshotCreateStart = 0L;
….
// save the snapshot to the repository.
Map  snapshotFolderProps = snapshot.toNodeProperties();
String folderName = (String) snapshotFolderProps.get(ContentModel.PROP_NAME);
FileInfo snapshotFolder = fileFolderService.create(snapshotContainerNodeRef, folderName,
      WebContentModel.TYPE_SNAPSHOT);
NodeRef snapshotNodeRef = snapshotFolder.getNodeRef();
nodeService.addProperties(snapshotNodeRef, snapshotFolderProps);

// the items in this snapshot are simply the items in the snapshotSourceNodeRef
  for (SnapshotItem item : snapshotItems) {
  // snapshotItem is a subtype of cm:content.
    Map  itemProps = item.toNodeProperties();
    String itemName = (String) itemProps.get(ContentModel.PROP_NAME);
    FileInfo repoItem = fileFolderService.create(snapshotNodeRef, itemName,
      WebContentModel.TYPE_SNAPSHOT_ITEM);
    nodeService.addProperties(repoItem.getNodeRef(), itemProps);
  }
return snapshotNodeRef;
}

Snapshot Revert

The revert method will revert an entire target workarea specified in the method paramater targetContainerNodeRef to the state of the snapshot specified.  In our implementation, we chose to first remove the target workarea, then restore each node from the version store by looping through the snapshot items registered in the snapshot and revert each node to its stored version property value.

// for each item:
for (SnapshotItem item : snapshotItems) { 
   String[] pathElements = StringUtils.split(item.getOrigPath(), '/');
   NodeRef itemParent = ensureRelativeFolderPath(targetContainerNodeRef, 
Arrays.asList(pathElements));
 
   VersionHistory history = versionService.getVersionHistory(item.getItemNodeRef());
   if (history == null) {
        continue;
   }

   Version version = null;
   try {
         version = history.getVersion(item.getVersion());
    } catch (VersionDoesNotExistException e) {
          continue;
}
 
    NodeRef versionStoreNodeRef = version.getFrozenStateNodeRef();
    String fileName = item.getOrigName();
    if (fileName == null) {
       fileName = (String) nodeService.getProperty(versionStoreNodeRef, ContentModel.PROP_NAME);
    }

    QName assocQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, fileName);
    if (nodeService.exists(item.getItemNodeRef())) {
        logger.warn(item.getItemNodeRef() + " unexpectedly already exists.");
    } else {
        NodeRef restoredNode = versionService.restore(item.getItemNodeRef(), itemParent,
        ContentModel.ASSOC_CONTAINS, assocQName, false);
        revertNodeToSnapshotVersion(restoredNode, item);
        nodeService.setProperty(restoredNode, ContentModel.PROP_NAME, fileName);

     }
}

Deploying a snapshot

A snapshot can be used as a source for deployment. In order for us to deploy a snapshot, we have staging spaces allocated for each site workarea as need be to prepare content for deployment based on the snapshot items.   Content nodes in the staging space are prepared by creating or updating the existing staging space node from the version store based on the snapshot item registered noderef and its stored version property.  A deployed snapshot will be tracked in the target staging space to improve performance for future deployments.

private NodeRef createOrUpdateInStagingSpace(final NodeRef folderNodeRef, final SnapshotItem snapshotItem) {
  NodeRef sourceNodeRef = snapshotItem.getItemNodeRef();
  String desiredVersion = snapshotItem.getVersion();
  String relativePath = snapshotItem.getOrigPath();
  long versionLookupStartTime = 0L;

  VersionHistory history = versionService.getVersionHistory(sourceNodeRef);
  Version version = history.getVersion(desiredVersion);

  NodeRef versionStoreNodeRef = version.getFrozenStateNodeRef();

  ContentReader versionReader = contentService.getReader(versionStoreNodeRef, ContentModel.PROP_CONTENT);
  if (versionReader == null || !versionReader.exists() || versionReader.getSize() == 0L) {
    return null;
  }

  ContentData versionContentData = versionReader.getContentData();

  return putContentInStagingSpace(folderNodeRef, relativePath, versionContentData, fileName);
}

Once the staging space is updated with the nodes represented in the snapshot items, it is ready for a file system transfer receiver deployment.

Website Content Management on Alfresco Share

By Chris Dixon with lots of thanks to our developers.

WCM is dead. Long live WCM on Share.

Rothbury has successfully implemented Alfresco’s WCM at numerous clients who are happily managing their web site content within the AVM context. With the grandfathering of the AVM, these clients are on a dead end road in terms of upgrades and the ability to make use of the feature rich Alfresco Share. Where do they want to go? Alfresco Share brings a great user experience to ECM, why not to WCM?

We have been dutifully working on bringing the AVM features to WCM on Share, providing an upgrade path for our clients.

• Managing multiple releases within a single site

• Deployment to Development, QA and Scheduled synchronous deployment to Production

• A long requested enhancement to WCM was the ability to merge from a sandbox
and see the differences, We are providing that ability with Releases and Merging with Enhanced Diff Comparison with text file editing and image preview:

We have created a few custom dashlets to provide additional control over your Releases, Snapshots, and Deployment endpoints and bring the ease of use straight to the Site Dashboard.

The Deployment endpoints (FTRs) for development provide for testing of releases, we have provided a dashlet to provide a status of the endpoints and who is controlling it. While Development and QA are limited to single controlled deployments, Production File Transfer Receivers (using Alfresco’s FSTR server) allow for scheduling the website updates in a single synchronous deployment, providing control of

• Workflow control from QA to Production.
• Expedited workflow for Hotfixes
• Deployment and Reverting of Snapshots

The feature set of WCM on Share continues to improve as we iterate through milestones and testing by QA.

What feature do you want to see in WCM on Share?