46
Customizing the Document Library 1 Mike Hatfield Senior Software Engineer, Alfresco twitter: @mikehatfield

Customizing the Document Library

Embed Size (px)

DESCRIPTION

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.

Citation preview

Page 1: Customizing the Document Library

1

Customizing the Document Library

Mike HatfieldSenior Software Engineer, Alfresco

twitter: @mikehatfield

Page 2: Customizing the Document Library

2

Customizing the Document Library

What We’ll Cover

• Architecture• Challenges to customization and extension

• Plans for improvement

• Case Study: the DoD 5015.2 Extensions• Extension Example

• Status Indicators• Custom Actions• Custom Metadata• Filters

Page 3: Customizing the Document Library

3

Architecture

Page 4: Customizing the Document Library

4

Architecture

Heavy Use of YUI for “Web 2.0” Experience

• Many JavaScript frameworks evaluated• Only YUI had a full-time staff, full API documentation, supported

UI widgets

• Many YUI widgets and modules used• DataTable• Treeview• Buttons, Menus, Containers• History Manager for “AJAX back button” support• XHR wrappers, JSON parser

• Most HTML rendering performed client-side• yuilibrary.com is your friend!

Page 5: Customizing the Document Library

5

Architecture

Initial Page Load

• Create all YUI controls• Register event handlers• Extract URL arguments• Bubbling Event “changeFilter”• onChangeFilter: updateDocList()• Webscript URL (via proxy) YUI DataSource• Repository webscripts execute for given filter & params• Bubbling Event “filterChanged”• Render JSON response using DataTable cell renderers

Page 6: Customizing the Document Library

6

Architecture

Subsequent Navigations (e.g Folder)

• Bubbling Event “changeFilter” [new filter params]• onChangeFilter: Build data webscript URL• History Manager multiNavigate()• onHistoryManagerFilterChanged: updateDocList()• Webscript URL (via proxy) YUI DataSource• Repository webscripts execute for given filter & params• Bubbling Event “filterChanged”• Render JSON response using DataTable cell renderers

Page 7: Customizing the Document Library

7

Architecture

YUI DataTable Renderers

• One renderer per data column• Selected file / folder• Status indicator• Thumbnail / icon• Metadata description• Actions

• Render XSS-safe HTML from parsed JSON• elCell.innerHTML = “<div>…</div>”

• YUI wraps output in <table><tr><td> tags

Page 8: Customizing the Document Library

8

Architecture

Actions

• Currently defined in webscript config xml files• Separate configs for browse page, details pages

• Action Sets based on repository business logic• evaluator.lib.js• “document”, “folder”, “locked”, “workingCopyOwner”, etc.

• Two action types• “simple-link” URL via getActionUrls()

• {downloadUrl}, {viewUrl}, {folderDetailsUrl}• “action-link” JavaScript function “id” attribute

• ID attribute defines action icon and JavaScript function

Page 9: Customizing the Document Library

9

Architecture

Actions

• Permissions• Comma-separated list• User AND asset must have ALL permissions for action to appear• “Virtual” permissions as well as role-based

• create, edit, delete, permissions, cancel-checkout• inline-edit, simple-approve, googledocs-edit

• Negative permission via tilde (~)• ~filter-path, ~portlet, ~googledocs-edit

• Label attribute for I18N message

Page 10: Customizing the Document Library

10

Current Extension Points

New Actions

New Actions

New Actions

New Actions

Custom UI

Custom UI

Custom UI

New Filters

New Filters

New Filters

Page 11: Customizing the Document Library

11

Plans for Improvements

• Consolidate scattered action configuration• share-config-custom.xml instead of webscript config• Still possible to restrict & specialise actions on details pages

• New actions via configuration where practicable• jar file for client-side UI assets, I18N• CSS and JS dependencies via config (see Forms & Header)

• Leverage Repository Actions & scripts• Custom Views

• Web-tier rendering• Open CMIS

Page 12: Customizing the Document Library

12

Case Study: DoD 5015.2 Extensions

Page 13: Customizing the Document Library

13

Case Study: DoD 5015.2 Extensions

Custom Actions

Numerous new and overridden actions to support the DoD requirements specification.

Page 14: Customizing the Document Library

14

Case Study: DoD 5015.2 Extensions

Custom Toolbar

Sensitive to current folder type. New and overridden actions.

Custom Filters

Removed unsuitable filters (user filters, tags). One static, one dynamic (populated from list of saved searches on the Repository).

Page 15: Customizing the Document Library

15

Case Study: DoD 5015.2 Extensions

“documentLibrary” container type determines components

Page 16: Customizing the Document Library

16

Case Study: DoD 5015.2 Extensions

Page 17: Customizing the Document Library

17

Template: documentlibrary

documentlibrary.js

connector.get("/slingshot/doclib/container/" + siteId + "/" + containerId);"dod:filePlan"

model.doclibType = fromRepoType("dod:filePlan");“dod5015”

documentlibrary.ftl

<@region id=doclibType + "documentlist" scope="template" protected=true />

Surf Component Binding

template.dod5015-documentlist.documentlibrary.xml

<url>/components/documentlibrary/dod5015/documentlist</url>

Page 18: Customizing the Document Library

18

Creating the Container

DoD 5015.2 Method

documentlibrary.js

page = sitedata.getPage("site/" + siteId + "/dashboard");pageMeta = eval('(' + p.properties.pageMetadata + ')');contentType = doclibMeta.type;

connector.get("/slingshot/doclib/container/" + siteId + "/" + containerId + “?type=“ + toRepoType(contentType));

”dod:filePlan”

presets.xml

<preset id="rm-site-dashboard"> <page id="site/${siteid}/dashboard"> <properties> <pageMetadata>{"documentlibrary":{…, "type":"dod5015"}}</pageMetadata>

Page 19: Customizing the Document Library

19

Creating the Container

Web QuickStart Method

LoadWebSiteDataGet.java

NodeRef docLib = siteService.getContainer(siteId, COMPONENT_DOCUMENT_LIBRARY);

siteService.createContainer(siteId, COMPONENT_DOCUMENT_LIBRARY, WebSiteModel.TYPE_WEBSITE_CONTAINER, null);

ornodeService.setType(docLib, WebSiteModel.TYPE_WEBSITE_CONTAINER);

dashlet

connector.get("/api/loadwebsitedata?site=" + siteId);

Page 20: Customizing the Document Library

20

Case Study: DoD 5015.2 Extensions

YUI Helps

YUI developers added a number of helper functions to allow OO-style JavaScript modules.

• Notice:• constructor• superclass• extend• augment• etc…

Page 21: Customizing the Document Library

21

Component Replacement Approach

• Full override / replacement control on all tiers.

• Your code can be almost completely independent of Alfresco’s.

Pros

• Mandatory component mapping , even for “native” components.

• Still have to copy/paste where <include> cannot be used, e.g. I18N.

• Repository folder type to component prefix issue.

• Not a 100% “clean” override mechanism.

Cons

Page 22: Customizing the Document Library

22

Mandatory Component Mapping

Big Development Overhead

template.dod5015-actions-common.documentlibrary.xml

template.dod5015-documentlist.documentlibrary.xml

template.dod5015-file-upload.documentlibrary.xml

template.dod5015-fileplan.documentlibrary.xml

template.dod5015-flash-upload.documentlibrary.xml

template.dod5015-html-upload.documentlibrary.xml

template.dod5015-navigation.documentlibrary.xml

template.dod5015-savedsearch.documentlibrary.xml

template.dod5015-title.documentlibrary.xml

template.dod5015-toolbar.documentlibrary.xml

template.dod5015-tree.documentlibrary.xml

And that’s just the browse page!

Page 23: Customizing the Document Library

23

Extension Example

• Overview • Customisations

• Status Indicators• Custom Metadata• New Filters• Custom Action

• Based on Alfresco Community 3.4.b• Need to use AMP on the Repository until refactoring work is complete• Share extensions via .jar and web-extension folder

Page 24: Customizing the Document Library

24

Extension Example: Photography

Overview

Page 25: Customizing the Document Library

25

Demo

Out-of-the-box Share

Page 26: Customizing the Document Library

26

Extension: Status Indicators

Provide the user a quick indication of the current status of a folder or document, e.g. aspects applied.

Calculated in evaluator.lib.js

• Repository• evaluator.lib.js

• Share• I18N messages• Indicator images

Page 27: Customizing the Document Library

27

Repository: Override evaluator.lib.js

/* Exif metadata */if (node.hasAspect("exif:exif")){ status["exif"] = true;}

/* Geographic */if (node.hasAspect("cm:geographic")){ status["geographic"] = true;}

Page 28: Customizing the Document Library

28

Share: Indicator images

status[”exif"] = true;status["geographic"] = true;

share.jar!/META-INF/components/documentlibrary/images1. exif-indicator-16.png2. geographic-indicator-16.png

Page 29: Customizing the Document Library

29

Share: Add I18N messages

1. share.jar!/org/springframework/extensions/surf/custom-slingshot-geographic-context.xml<bean id="geographic.custom.resources" class="org.springframework.extensions.surf.util.ResourceBundleBootstrapComponent">

<property name="resourceBundles"> <list> <value>alfresco.messages.geographic</value> </list> </property></bean>

2. share.jar!/alfresco/messages/geographic.propertiestip.geographic=Geo Locationtip.exif=EXIF Metadata

Page 30: Customizing the Document Library

30

Extension: Custom Metadata

Rendered entirely by the web browser from JSON data.

• Repository• item.lib.ftl• Maybe also JavaScript logic

• Share• I18N messages• Override cell renderer

Page 31: Customizing the Document Library

31

Repository: Override item.lib.ftl

1. alfresco.amp!/WEB-INF/classes/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/item.lib.ftl

<#if node.hasAspect("cm:geographic")>“geolocation”:{ "latitude": ${(node.properties["cm:latitude"]!0)?c}, "longitude": ${(node.properties["cm:longitude"]!0)?c}},</#if>

<#if node.hasAspect(”exif:exif")>“exif”:{ …},</#if>

Page 32: Customizing the Document Library

32

Share: Reference extension JavaScript

1. shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/documentlibrary/actions-common.get.head.ftl

<@script type="text/javascript" src="${page.url.context}/res/components/geographic/geographic-extension.js"></@script>

Page 33: Customizing the Document Library

33

Share: Override cell renderer

1. Override fnRenderCellDescription

share.jar!/META-INF/components/geographic/geographic-extension.js

YAHOO.util.Event.onContentReady("alf-hd", function(){ if (Alfresco.DocumentList) { Alfresco.DocumentList.prototype.fnRenderCellDescription = function

DL_fnRenderCellDescription() { … if (record.exif) { desc += scope.msg(“detail.exposure”) + record.exif.exposureTime;

} … } }}

Page 34: Customizing the Document Library

34

Extension: New Filter

Allow easy filtering by any Repository logic, most commonly a Lucene or Alfresco FTS search query.

• Repository• filters.lib.js

• Share• I18N messages• Filter webscript config

Page 35: Customizing the Document Library

35

Share: Filter webscript config

1. shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/documentlibrary/filter.get.config.xml

<filters> … <filter id="geo" label="link.geo-located" /> <filter id="exif" label="link.exif" /> …</filters>

Page 36: Customizing the Document Library

36

Repository: Filter webscript override

1. alfresco.amp!/WEB-INF/classes/alfresco/templates/webscripts/org/alfresco/slingshot/documentlibrary/filters.lib.js

case "geo": filterQuery = "+PATH:\"" + parsedArgs.rootNode.qnamePath + "//*\""; filterQuery += "+ASPECT:\"cm:geographic\""; filterParams.query = filterQuery break;

case "exif": filterQuery = "+PATH:\"" + parsedArgs.rootNode.qnamePath + "//*\""; filterQuery += "+ASPECT:\"exif:exif\""; filterParams.query = filterQuery break;

Page 37: Customizing the Document Library

37

Extension: Custom Action

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.

• Repository• evaluator.lib.js• Action processing (optional)

• Share• I18N messages• Action configuration• Client-side logic & images

Page 38: Customizing the Document Library

38

Repository: Override evaluator.lib.js

/* Geographic */if (node.hasAspect("cm:geographic")){ status["geographic"] = true; permissions["geographic"] = true;}

Page 39: Customizing the Document Library

39

Share: Action configuration

1. shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/documentlibrary/documentlist.get.config.xml

2. shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/document-details/document-actions.get.config.xml

3. etc…

<actionSet id="document"> … <action type="simple-link"

id="onActionGeographic" permission="geographic" href="{geographicUrl}" label="actions.document.geographic" />

</actionSet>

Page 40: Customizing the Document Library

40

Share: CSS and JS dependencies

1. shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/documentlibrary/actions-common.get.head.ftl

<@link rel="stylesheet" type="text/css" href="${page.url.context}/res/components/geographic/geographic-extension.css" />

2. share.jar!/META-INF/components/geographic/geographic-extension.css

.doclist .onActionGeographic a{ background-image: url(pin.png) !important;}

Page 41: Customizing the Document Library

41

Share: Proxy JavaScript function

var override = Alfresco.DocumentList || Alfresco.DocumentActions;

// Store reference to getActionUrls() function to allow extension.var getActionUrls_geo = override.prototype.getActionUrls;

override.prototype.getActionUrls = function(recordData){ var actionUrls = getActionUrls_geo.apply(this, arguments);

actionUrls["geographicUrl"] = Alfresco.util.siteURL( "geographic-map?nodeRef=" + recordData.nodeRef);

return actionUrls;};

Page 42: Customizing the Document Library

42

Demo

New Extensions

Page 43: Customizing the Document Library

43

The End Result

Page 44: Customizing the Document Library

44

Roadmap

• Consolidate scattered action configuration• New actions via configuration where practicable• Remove references to non-core Share code• Leverage Repository Actions & scripts• Custom Views

Page 45: Customizing the Document Library

45

Q & A

• Feedback

Page 46: Customizing the Document Library

46

Learn Morewiki.alfresco.com/wiki/Shareblogs.alfresco.com/wp/mikeh/