Customizing the Document Library


Published on

The Share Document Library provides a number of out-of-the-box default actions and displays basic, essential metadata for documents and folders. This session will show you how to add custom metadata and status indicators, modify the available actions and wire-up new filters. We'll also look at how the Document Library was extended for the DoD 5015.2 Records Management Fileplan browser. You will need to be familiar with basic Surf concepts as well as JavaScript and Freemarker to follow the webscript customization. Familiarity with YUI 2.x and CSS will aid understanding during this session.

Published in: Technology
  • Customizing Alfresco Share is such a mess.. Worst practices are taken from PHP.
    Are you sure you want to  Yes  No
    Your message goes here

Customizing the Document Library

  1. 1. Customizing the Document Library<br />1<br />Mike Hatfield<br />Senior Software Engineer, Alfresco<br />twitter: @mikehatfield<br />
  2. 2. Customizing the Document Library<br />2<br />What We’ll Cover<br /><ul><li> Architecture
  3. 3. Challenges to customization and extension</li></ul>Plans for improvement<br /><ul><li> Case Study: the DoD 5015.2 Extensions
  4. 4. Extension Example</li></ul> Status Indicators<br /> Custom Actions<br /> Custom Metadata<br /> Filters<br />
  5. 5. Architecture<br />3<br />
  6. 6. Architecture<br />4<br />Heavy Use of YUI for “Web 2.0” Experience<br /><ul><li>Many JavaScript frameworks evaluated</li></ul>Only YUI had a full-time staff, full API documentation, supported UI widgets<br /><ul><li>Many YUI widgets and modules used</li></ul>DataTable<br />Treeview<br />Buttons, Menus, Containers<br />History Manager for “AJAX back button” support<br />XHR wrappers, JSON parser<br /><ul><li>Most HTML rendering performed client-side
  7. 7. is your friend!</li></li></ul><li>Architecture<br />5<br />Initial Page Load<br /><ul><li>Create all YUI controls
  8. 8. Register event handlers
  9. 9. Extract URL arguments
  10. 10. Bubbling Event  “changeFilter”
  11. 11. onChangeFilter: updateDocList()
  12. 12. Webscript URL (via proxy)  YUI DataSource
  13. 13. Repository webscripts execute for given filter & params
  14. 14. Bubbling Event  “filterChanged”
  15. 15. Render JSON response using DataTable cell renderers</li></li></ul><li>Architecture<br />6<br />Subsequent Navigations (e.g Folder)<br /><ul><li>Bubbling Event  “changeFilter” [new filter params]
  16. 16. onChangeFilter: Build data webscript URL
  17. 17. History Manager  multiNavigate()
  18. 18. onHistoryManagerFilterChanged: updateDocList()
  19. 19. Webscript URL (via proxy)  YUI DataSource
  20. 20. Repository webscripts execute for given filter & params
  21. 21. Bubbling Event  “filterChanged”
  22. 22. Render JSON response using DataTable cell renderers</li></li></ul><li>Architecture<br />7<br />YUI DataTable Renderers<br /><ul><li>One renderer per data column</li></ul>Selected file / folder<br />Status indicator<br />Thumbnail / icon<br />Metadata description<br />Actions<br /><ul><li>Render XSS-safe HTML from parsed JSON</li></ul>elCell.innerHTML = “<div>…</div>”<br /><ul><li>YUI wraps output in <table><tr><td> tags</li></li></ul><li>Architecture<br />8<br />Actions<br /><ul><li>Currently defined in webscript config xml files</li></ul>Separate configs for browse page, details pages<br /><ul><li>Action Sets based on repository business logic</li></ul>evaluator.lib.js<br />“document”, “folder”, “locked”, “workingCopyOwner”, etc.<br /><ul><li>Two action types</li></ul>“simple-link” URL via getActionUrls()<br />{downloadUrl}, {viewUrl}, {folderDetailsUrl}<br />“action-link” JavaScript function  “id” attribute<br /><ul><li>ID attribute defines action icon and JavaScript function</li></li></ul><li>Architecture<br />9<br />Actions<br /><ul><li>Permissions</li></ul>Comma-separated list<br />User AND asset must have ALL permissions for action to appear<br />“Virtual” permissions as well as role-based<br />create, edit, delete, permissions, cancel-checkout<br />inline-edit, simple-approve, googledocs-edit<br />Negative permission via tilde (~)<br />~filter-path, ~portlet, ~googledocs-edit<br /><ul><li>Label attribute for I18N message</li></li></ul><li>Current Extension Points<br />10<br />Custom UI<br />New Filters<br />New Actions<br />Custom UI<br />New Filters<br />New Actions<br />Custom UI<br />New Actions<br />New Actions<br />New Filters<br />
  23. 23. Plans for Improvements<br />11<br /><ul><li> Consolidate scattered action configuration</li></ul>share-config-custom.xml instead of webscriptconfig<br />Still possible to restrict & specialise actions on details pages<br /><ul><li> New actions via configuration where practicable</li></ul>jar file for client-side UI assets, I18N<br />CSS and JS dependencies via config (see Forms & Header)<br /><ul><li> Leverage Repository Actions & scripts
  24. 24. Custom Views</li></ul>Web-tier rendering<br />Open CMIS<br />
  25. 25. Case Study: DoD 5015.2 Extensions<br />12<br />
  26. 26. Case Study: DoD 5015.2 Extensions<br />13<br />Custom Actions<br />Numerous new and overridden actions to support the DoD requirements specification.<br />
  27. 27. Case Study: DoD 5015.2 Extensions<br />14<br />Custom Toolbar<br />Sensitive to current folder type. New and overridden actions.<br />Custom Filters<br />Removed unsuitable filters (user filters, tags). One static, one dynamic (populated from list of saved searches on the Repository).<br />
  28. 28. Case Study: DoD 5015.2 Extensions<br />15<br />“documentLibrary” container type determines components<br />
  29. 29. Case Study: DoD 5015.2 Extensions<br />16<br />
  30. 30. Template: documentlibrary<br />17<br />documentlibrary.js<br />connector.get("/slingshot/doclib/container/" + siteId + "/" + containerId);<br />"dod:filePlan"<br />model.doclibType = fromRepoType("dod:filePlan");<br />“dod5015”<br />documentlibrary.ftl<br /><@region id=doclibType+ "documentlist" scope="template" protected=true /><br />Surf Component Binding<br />template.dod5015-documentlist.documentlibrary.xml<br /><url>/components/documentlibrary/dod5015/documentlist</url><br />
  31. 31. Creating the Container<br />18<br />DoD 5015.2 Method<br />presets.xml<br /><preset id="rm-site-dashboard"><br /> <page id="site/${siteid}/dashboard"><br /> <properties><br /> <pageMetadata>{"documentlibrary":{…, "type":"dod5015"}}</pageMetadata> <br />documentlibrary.js<br />page = sitedata.getPage("site/" + siteId + "/dashboard");<br />pageMeta = eval('(' + + ')');<br />contentType = doclibMeta.type;<br />connector.get("/slingshot/doclib/container/" + siteId + "/" + containerId<br />+ “?type=“ + toRepoType(contentType));<br />”dod:filePlan”<br />
  32. 32. Creating the Container<br />19<br />Web QuickStart Method<br />dashlet<br />connector.get("/api/loadwebsitedata?site=" + siteId);<br /><br />NodeRefdocLib = siteService.getContainer(siteId, COMPONENT_DOCUMENT_LIBRARY);<br />siteService.createContainer(siteId, COMPONENT_DOCUMENT_LIBRARY, <br />WebSiteModel.TYPE_WEBSITE_CONTAINER, null);<br />or<br />nodeService.setType(docLib, WebSiteModel.TYPE_WEBSITE_CONTAINER);<br />
  33. 33. Case Study: DoD 5015.2 Extensions<br />20<br />YUI Helps<br />YUI developers added a number of helper functions to allow OO-style JavaScript modules.<br /><ul><li> Notice:</li></ul>constructor<br />superclass<br />extend<br />augment<br />etc…<br />
  34. 34. Component Replacement Approach<br />21<br />Full override / replacement control on all tiers.<br />Your code can be almost completely independent of Alfresco’s.<br />Pros<br />Mandatory component mapping , even for “native” components.<br />Still have to copy/paste where <include> cannot be used, e.g. I18N.<br />Repository folder type to component prefix issue.<br />Not a 100% “clean” override mechanism.<br />Cons<br />
  35. 35. Mandatory Component Mapping<br />22<br />Big Development Overhead<br />template.dod5015-actions-common.documentlibrary.xml<br />template.dod5015-documentlist.documentlibrary.xml<br />template.dod5015-file-upload.documentlibrary.xml<br />template.dod5015-fileplan.documentlibrary.xml<br />template.dod5015-flash-upload.documentlibrary.xml<br />template.dod5015-html-upload.documentlibrary.xml<br />template.dod5015-navigation.documentlibrary.xml<br />template.dod5015-savedsearch.documentlibrary.xml<br />template.dod5015-title.documentlibrary.xml<br />template.dod5015-toolbar.documentlibrary.xml<br />template.dod5015-tree.documentlibrary.xml<br />…<br />And that’s just the browse page!<br />
  36. 36. Extension Example<br />23<br /><ul><li> Overview
  37. 37. Customisations </li></ul>Status Indicators<br />Custom Metadata<br />New Filters<br />Custom Action<br /><ul><li> Based on Alfresco Community 3.4.b
  38. 38. Need to use AMP on the Repository until refactoring work is complete
  39. 39. Share extensions via .jar and web-extension folder</li></li></ul><li>Extension Example: Photography<br />24<br />Overview<br />
  40. 40. DemoOut-of-the-box Share<br />25<br />
  41. 41. Extension: Status Indicators <br />26<br />Provide the user a quick indication of the current status of a folder or document, e.g. aspects applied.<br />Calculated in evaluator.lib.js<br /><ul><li>Repository
  42. 42. evaluator.lib.js
  43. 43. Share
  44. 44. I18N messages
  45. 45. Indicator images</li></li></ul><li>Repository: Override evaluator.lib.js<br />27<br />/* Exif metadata */<br />if (node.hasAspect("exif:exif"))<br />{<br />status["exif"] = true;<br />}<br />/* Geographic */<br />if (node.hasAspect("cm:geographic"))<br />{<br />status["geographic"] = true;<br />}<br />
  46. 46. Share: Indicator images<br />28<br />status[”exif"] = true;<br />status["geographic"] = true;<br />share.jar!/META-INF/components/documentlibrary/images<br />exif-indicator-16.png<br />geographic-indicator-16.png<br />
  47. 47. Share: Add I18N messages<br />29<br />share.jar!/org/springframework/extensions/surf/custom-slingshot-geographic-context.xml<br /><bean id="geographic.custom.resources" class=""><br /> <property name="resourceBundles"><br /> <list><br /> <value>alfresco.messages.geographic</value><br /> </list><br /> </property><br /></bean><br />share.jar!/alfresco/messages/<br />tip.geographic=Geo Location<br />tip.exif=EXIF Metadata<br />
  48. 48. Extension: Custom Metadata<br />30<br />Rendered entirely by the web browser from JSON data.<br /><ul><li>Repository
  49. 49. item.lib.ftl
  50. 50. Maybe also JavaScript logic
  51. 51. Share
  52. 52. I18N messages
  53. 53. Override cell renderer</li></li></ul><li>Repository: Override item.lib.ftl<br />31<br />alfresco.amp!/WEB-INF/classes/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/item.lib.ftl<br /><#if node.hasAspect("cm:geographic")><br />“geolocation”:<br />{<br /> "latitude": ${(["cm:latitude"]!0)?c},<br /> "longitude": ${(["cm:longitude"]!0)?c}<br />},<br /></#if><br /><#if node.hasAspect(”exif:exif")><br />“exif”:<br />{<br /> …<br />},<br /></#if><br />
  54. 54. Share: Reference extension JavaScript<br />32<br />shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/documentlibrary/actions-common.get.head.ftl<br /><@script type="text/javascript" src="${page.url.context}/res/components/geographic/geographic-extension.js"></@script><br />
  55. 55. Share: Override cell renderer<br />33<br />Override fnRenderCellDescription<br />share.jar!/META-INF/components/geographic/geographic-extension.js<br />YAHOO.util.Event.onContentReady("alf-hd", function()<br />{<br /> if (Alfresco.DocumentList)<br /> {<br />Alfresco.DocumentList.prototype.fnRenderCellDescription = function DL_fnRenderCellDescription()<br /> {<br /> …<br /> if (record.exif)<br /> {<br />desc += scope.msg(“detail.exposure”) + record.exif.exposureTime; <br /> }<br /> …<br /> }<br /> }<br />}<br />
  56. 56. Extension: New Filter<br />34<br />Allow easy filtering by any Repository logic, most commonly a Lucene or Alfresco FTS search query.<br /><ul><li>Repository
  57. 57. filters.lib.js
  58. 58. Share
  59. 59. I18N messages
  60. 60. Filter webscriptconfig</li></li></ul><li>Share: Filter webscriptconfig<br />35<br />shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/documentlibrary/filter.get.config.xml<br /><filters><br /> …<br /> <filter id="geo" label="link.geo-located" /><br /> <filter id="exif" label="link.exif" /><br /> …<br /></filters><br />
  61. 61. Repository: Filter webscript override<br />36<br />alfresco.amp!/WEB-INF/classes/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/filters.lib.js<br />case "geo":<br />filterQuery = "+PATH:"" +<br />parsedArgs.rootNode.qnamePath + "//*"";<br />filterQuery += "+ASPECT:"cm:geographic"";<br />filterParams.query = filterQuery<br /> break;<br />case "exif":<br />filterQuery = "+PATH:"" +<br />parsedArgs.rootNode.qnamePath + "//*"";<br />filterQuery += "+ASPECT:"exif:exif"";<br />filterParams.query = filterQuery<br /> break;<br />
  62. 62. Extension: Custom Action<br />37<br />Can be configured to only appear if a folder or document is in a particular state and/or the user has the correct permission(s) and/or the page is within a Site context and/or the action is on the browse or details page.<br /><ul><li>Repository
  63. 63. evaluator.lib.js
  64. 64. Action processing (optional)
  65. 65. Share
  66. 66. I18N messages
  67. 67. Action configuration
  68. 68. Client-side logic & images</li></li></ul><li>Repository: Override evaluator.lib.js<br />38<br />/* Geographic */<br />if (node.hasAspect("cm:geographic"))<br />{<br />status["geographic"] = true;<br />permissions["geographic"] = true;<br />}<br />
  69. 69. Share: Action configuration<br />39<br />shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/documentlibrary/documentlist.get.config.xml<br />shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/document-details/document-actions.get.config.xml<br />etc…<br /><actionSet id="document"><br /> …<br /> <action type="simple-link" id="onActionGeographic" permission="geographic" href="{geographicUrl}" label="actions.document.geographic" /><br /></actionSet><br />
  70. 70. Share: CSS and JS dependencies<br />40<br />shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/documentlibrary/actions-common.get.head.ftl<br /><@link rel="stylesheet" type="text/css" href="${page.url.context}/res/components/geographic/geographic-extension.css" /><br />share.jar!/META-INF/components/geographic/geographic-extension.css<br />.doclist .onActionGeographica<br />{<br /> background-image: url(pin.png) !important;<br />}<br />
  71. 71. Share: Proxy JavaScript function<br />41<br />var override = Alfresco.DocumentList || Alfresco.DocumentActions;<br />// Store reference to getActionUrls() function to allow extension.<br />vargetActionUrls_geo = override.prototype.getActionUrls;<br />override.prototype.getActionUrls = function(recordData)<br />{<br />varactionUrls = getActionUrls_geo.apply(this, arguments);<br />actionUrls["geographicUrl"] = Alfresco.util.siteURL(<br /> "geographic-map?nodeRef=" + recordData.nodeRef);<br /> return actionUrls;<br />};<br />
  72. 72. DemoNew Extensions<br />42<br />
  73. 73. The End Result<br />43<br />
  74. 74. Roadmap<br />44<br /><ul><li> Consolidate scattered action configuration
  75. 75. New actions via configuration where practicable
  76. 76. Remove references to non-core Share code
  77. 77. Leverage Repository Actions & scripts
  78. 78. Custom Views</li></li></ul><li>Q & A<br />45<br /><ul><li>Feedback</li></li></ul><li>Learn More<br />46<br /><br /><br />