Вы находитесь на странице: 1из 10

ecmarchitect.

com

AlfrescoDeveloper:DevelopingCustomActions
January,2007
JeffPotts

ThisworkislicensedundertheCreativeCommonsAttributionShareAlike2.5License.Toviewacopyofthislicense, visithttp://creativecommons.org/licenses/bysa/2.5/orsendalettertoCreativeCommons,543HowardStreet,5thFloor, SanFrancisco,California,94105,USA.

ecmarchitect.com
AlfrescoDeveloper:DevelopingCustomActions
January,2007 JeffPotts Introduction Alfrescoisaflexibleplatformfordevelopingcontentmanagementapplications.Clientshaveseveral optionstoconsiderwhenselectingauserinterfaceapproach.Alfrescocomeswithawebclientthatcan beusedasisorcustomized.Alternatively,developerscancreatecustomapplicationsusingeitherthe webservicesAPIorthefoundationAPI. Manytimes,theoutoftheboxwebclientissufficient,evenifithastobecustomizedslightlytofit yourrequirements.Thisisparticularlytruewhenyourrequirementscloselyresembletheallpurpose documentmanagementusecase. Decidingwhethertogowiththeoutoftheboxwebclient,acustomizedwebclient,orbuildingyour ownuserinterfacefromscratchrequirescarefulthoughtandanalysis.We'llcoverthatsomeothertime. ThisarticleisthefirstinaseriesthatcoversAlfrescowebclientuserinterfacecustomizations.The focusinthisarticleisondevelopingcustomactions. WhatisanAction? Asyoumightsuspect,anactionissomethingausercandotoapieceofcontent.Theyarediscreteunits ofworkandcanoptionallybeconfiguredatruntimebytheuser.Someoftheoutoftheboxactions includeCheckout,Checkin,Update,Cut,Copy,Edit,andDelete. IntheAlfrescowebclientinterface,actionsareeverywhere.Herearesomeexamples... Withinaspacetherearedocument specificactions...

Clickimagetoenlarge

AlfrescoDeveloper:DevelopingCustomActions
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

Page2of10

ecmarchitect.com
...aswellasactionslistedunder "MoreActions".

Clickimagetoenlarge

Whenviewingcontentproperties actionsarelistedontherighthand sideofthepage.

Clickimagetoenlarge

Actionscanalsobeinvokedaspart ofAlfresco'ssimpleworkflow mechanism.

Clickimagetoenlarge

AnExample Usingactionsandworkflowtogethercanbeaneffectivewaytocustomizethewebclientfortheneeds ofthebusiness.Forexample,recentlyaclientimplementedAlfrescotomanagethepoliciesand proceduresfortheirentireorganization.Theyneededtobeabletotrackwhenapolicyorprocedure waspublishedthatsupersededanotherdocument. Therearethreeareastolookatwhenconsideringthisrequirement:thecontentmodel,thebusiness logic,andtheuserinterface.

AlfrescoDeveloper:DevelopingCustomActions
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

Page3of10

ecmarchitect.com
A"replaces"relationshipisaprettycommonrequirement.SomuchsothatAlfrescohasalreadyadded a"replaceable"aspecttoitstandardcontentmodel.Sowithoutwritinganycode,thebusinessspecific contentmodelcanbewrittentoaccommodatedocumentsthatreplaceotherdocuments. Next,let'slookatthebusinesslogic.Inthecaseofthisparticularclient,policiesandprocedures undergoasimpleworkflow.Theyaresubmittedforreviewandtheneitherapprovedorrejected. Rejecteddocumentsgobacktothesubmitter.Approveddocumentsarecopiedtoa"Published"folder. Ifanapproveddocumentreplacesapublisheddocument,thepublisheddocumentismovedtoan "Archived"folder. Soourbusinesslogicwillbethatwhenadocumentispublished,ifthedocumenthasa"replaces" relationship,thedocumentsbeingreplacedneedtomovetoanarchivedfolder. Theuserinterfacecanbebrokendownintoafewdifferentscenarios.First,documentmanagersneedto beabletoestablishthe"replaces"relationshipforagivendocument,theyneedtobeabletopointto anotherdocumentinarepositoryanddeclarethatthisdocumentreplacesthatone.Next,content consumersneedtobeabletoseealistofdocumentsthataparticulardocumentreplaces.Third,the destinationfolderforreplaceddocumentsshouldbeconfigurable,andthatwillneedauserinterface. Alfrescoalreadyhasacomponentrendererinplaceforthe"replaces"relationship.Thatmeansthatthe firstandsecondUIscenariosaretakencareofforus.Wecouldcreateourownrenderer,butforthis example,we'llusetheoutoftheboxcode.Lookingaheadtoimplementation,we'llbeusinganaction toimplementourbusinesslogic.Oneofthenicesideeffectsofthatdecisionisthatactionshavea configurationframeworkwecanleverage.Soforthisexample,wewon'thavetowriteanycodeto addressthethirdUIscenario.Thatleavesusneedingtoimplementonlythebusinesslogic. ImplementingtheAction Alfrescodoesn'thaveoutoftheboxcodetohandlethetypeoflogicweoutlinedintheprevious section.Onewaytoimplementitistowriteacustomactionandthencalltheactionfromaworkflow. Theactionwillcheckthedocumentitisrunningagainstfor"replaces"associationsandmoveany documentsitreplacestoafolderspecifiedwhentheactionisconfigured.Inthisexample,thefolder willbesettothe"Archived"folder. Implementingthelogicinanactionmakesalotofsensebecauseactionsareconfigurablecomponents thatcanbereusedacrosstheenterprise.Ifanotherdepartmentneedstomovereplaceddocumentsthey canusethecustomactionwithoutwritingcode. Howtodoit Atitsmostbasic,anactionconsistsofanActionExecuterclassanditsassociatedbeandeclaration.In Page4of10

AlfrescoDeveloper:DevelopingCustomActions
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
ourexample,weneedaUItosetthedestinationfoldersowe'llalsoneedabeanhandler,aJSPpage, andaresourcebundle. Beforestarting,though,let'sthinkaboutsimilarcodethatmightalreadyexistwithinAlfrescothatwe couldleverageforournewaction.Alfrescoisopensourceitwouldbeashametoignorethatvaluable resource.Plus,followingthesamepatternstoimplementourcustomizationwillmakeiteasierfor someonetosupportandmakesiteasiertoshareourcodewithothersoreventocontributethecode backtotheAlfrescocommunityproject. We'redoingamove,sotheMoveactionisagoodplacetostart.Infact,theonlydifferencebetweenthe Moveactionandoursisthatthenodebeingmovedisn'tthecurrentnodeit'sthenodeonthetargetend ofa"replaces"association. Actionexecuter Alfresco'sexecuterclassfortheMoveactioniscalled org.alfresco.repo.action.executer.MoveActionExecuter.YoucanfinditintheAlfrescoRepository project.Let'scopyitintoourownprojectandcallitMoveReplacedActionExecuter.(We'llcallour action"MoveReplaced"becauseitmovesreplaceddocuments).TheexecuteImplmethodiswhatwe're lookingfor.That'swherethemovelogicishandled.Itlookslikethis:
publicvoidexecuteImpl(ActionruleAction,NodeRefactionedUponNodeRef){ if(this.nodeService.exists(actionedUponNodeRef)==true){ NodeRefdestinationParent= (NodeRef)ruleAction.getParameterValue(PARAM_DESTINATION_FOLDER); QNamedestinationAssocTypeQName= (Qname)ruleAction.getParameterValue(PARAM_ASSOC_TYPE_QNAME); QNamedestinationAssocQName= (Qname)ruleAction.getParameterValue(PARAM_ASSOC_QNAME); this.nodeService.moveNode(actionedUponNodeRef,destinationParent, destinationAssocTypeQName,destinationAssocQName); } }

ThecodesimplygrabssomeparametervaluesandthencallstheNodeServicetodothemove.Allwe needtodoismodifyittofindthenodesrelatedtothecurrentnodebya"replaces"association,and thenforeachresult,setupandperformamove.


publicvoidexecuteImpl(ActionruleAction,NodeRefactionedUponNodeRef){ //getthereplacesassociationsforthisnode ListassocRefs=nodeService.getTargetAssocs(actionedUponNodeRef, ((QNamePattern)QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "replaces"))); //iftherearenone,return if(assocRefs.isEmpty()){ //noworktodo,return

AlfrescoDeveloper:DevelopingCustomActions
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

Page5of10

ecmarchitect.com
return; }else{ for(AssociationRefassocNode:assocRefs){ //createanoderefforthereplacesassociationNodeRefassocRef= assocNode.getTargetRef(); //ifthenodeexists if(this.nodeService.exists(assocRef)==true){ NodeRefdestinationParent= (NodeRef)ruleAction.getParameterValue(PARAM_DESTINATION_FOLDER); QNamedestinationAssocTypeQName= (Qname)ruleAction.getParameterValue(PARAM_ASSOC_TYPE_QNAME); StringcurrentNameString=(String) this.nodeService.getProperty(assocRef, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,"name")); this.nodeService.moveNode(assocRef,destinationParent, destinationAssocTypeQName,QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI ,currentNameString)); } }//nextassocNode }//endifisEmpty }

NotethatinAlfresco'smoveNodecall,theyusedestinationAssocQNameastheqnameofthenewly movednode.Thatvariableresolvestothestringmove.Ifyouevermovemorethanonedocument intoafolderusingthataction,you'llgetintegrityviolations.Thisseemslikeabugtome.Myversionof themoveNodecallusesthecurrentnameoftheobjectbeingmovedasthenewname,whichseems likeabetterwaytogo. TheonlyotherchangeweneedtomakeistochangethevalueoftheconstantNAMEfrom"move"to "movereplaced".FollowingthesamepatternAlfrescoused,theexecuterNAMEconstantneedsto matchtheJSPnamewhichisreferencedbythebeanhandlerclass. Beanhandler Alfresco'sbeanhandlerclassfortheMoveactioniscalled org.alfresco.web.bean.actions.handlers.MoveHandleranditresidesintheAlfrescoWebClientproject. Thebeanhandlerclasshandlesthe"view"fortheactionconfiguration.Becauseboththe"Move"action andour"MoveReplaced"actionbothhaveadestinationfolderandnootherconfigurableproperties, onlyminormodificationsareneeded. First,we'llbeusingourownJSPforthepresentationsoweneedtochangethegetJSPPath()method fromthis:
publicStringgetJSPPath(){ returngetJSPPath(MoveActionExecuter.NAME); }

AlfrescoDeveloper:DevelopingCustomActions
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

Page6of10

ecmarchitect.com
tosomethinglikethis:
publicfinalstaticStringCUSTOM_ACTION_JSP="/jsp/extension/actions/"+ MoveReplacedActionExecuter.NAME+".jsp"; publicStringgetJSPPath(){ returnCUSTOM_ACTION_JSP; }

Allwe'redoinghereistellingthebeanwheretofindourcustomizedJSP. Next,ifyou'veeverconfiguredanactionbeforeyouknowthattheuserinterfaceincludesashort summaryofwhattheactionisgoingtodo.ThegenerateSummary()methodisresponsibleforthat message.Theactualtextwillbeinaresourcebundle,sowejustneedtochangethepropertynamefrom "action_move"to"action_move_replaced". JSPpage TheJSPpagethathandlesthemoveconfigurationforourclassisidenticaltotheoutoftheboxmove. Butwedowanttohaveadifferenttitle,sowe'llneedourownpage.And,becausethepageisina differentfolderstructurethantheAlfrescoJSP,theexistingJSPincludeswillneedtobeupdated, becausetheyuserelativelinks. Copy"/source/web/jsp/actions/move.jsp"fromtheAlfrescoWebClientprojectintoourcustomproject. Iuse"web/jsp/extension"forallcustomizedAlfrescoJSPsbyconvention,sothefullpathtotheJSPin thecustomprojectis,"web/jsp/extension/actions/movereplaced.jsp".ThecustomizedJSPswillbe deployedtothesame"web/jsp"directoryastheoutoftheboxJSPssoanyexistingrelativelinksthat needtopointtooutoftheboxJSPsneedanextra"../"prependedtothem.Inthiscasetherearethree includesthatneedtobeupdated. Topointtoourcustomtitlepropertyinourresourcebundle,changethetitleIdattributeofther:pagetag from"title_action_move"to"title_action_move_replaced". That'sitfortheJavaandJSPcode.NowitistimetotieitalltogetherwiththeappropriateXML configurationandpropertiesfiles. Beandeclaration First,weneedtodeclaretheactionexecuterclassandpointtotheresourcebundle.Createafilein alfresco/extensioncalledmovereplacedactioncontext.xmlwiththefollowing:
<beans> <!MoveReplacedActionBean> <beanid="movereplaced"

AlfrescoDeveloper:DevelopingCustomActions
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

Page7of10

ecmarchitect.com
class="com.optaros.alfresco.repo.action.executer.MoveReplacedActionExecuter" parent="actionexecuter"> <propertyname="nodeService"> <refbean="NodeService"/> </property> </bean> <beanid="extension.actionResourceBundles"parent="actionResourceBundles"> <propertyname="resourceBundles"> <list> <value>alfresco.extension.movereplacedactionmessages</value> </list> </property> </bean> </beans>

Resourcebundle Next,weneedtocreatearesourcebundle.Createafileinalfresco/extensioncalledmovereplaced actionmessages.properties.Notethatthisfilenameandpathmatchestheresourcebundledeclaration fromthepreviousstep.


## ##MoveReplacedActionI18Nfile ## #Actiontitleanddescription #Seeactionconfig.properties movereplaced.title=Movereplaceddocumenttospace movereplaced.description=Thiswillmovethetargetnodeofareplaces associationtoaspecifiedspace.

Webclient.propertiesandwebclientconfigcustom.xml ThesummarytextandtheJSPtitlegetpulledfromalfresco/extension/webclient.properties.Addthe followingentriestothefile:


action_move_replaced=Movereplacedto''{0}'' title_action_move_replaced=MoveReplacedAction

Finally,weneedtohookintotheAlfrescoactionwizardUIsothatwhensomeoneconfiguresa workflow,ournewactionislistedasavalidchoice.Editthealfresco/extension/webclientconfig custom.xmlfileandadd:


<configevaluator="stringcompare"condition="ActionWizards"> <!addcustomactionhandlerfor"MoveReplaced"action> <actionhandlers> <handlername="movereplaced" class="com.optaros.alfresco.web.bean.actions.handlers.MoveReplacedHandler"/>

AlfrescoDeveloper:DevelopingCustomActions
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

Page8of10

ecmarchitect.com
</actionhandlers> </config>

Deploy,test,andenjoy Thefileswe'vewritteninthisexamplecanbedeployedoverthetopofanexistingAlfrescodeployment withtheexceptionofthewebclient.propertiesandwebclientconfigcustom.xmlfiles,whichmay alreadyexist.Itcanbeabitconfusingtodeterminewhatgoeswhere.Inthiscase,allXMLand propertiesfilesgointheAlfrescoextensiondirectory.AllJavacodegoesinaJARfilethatshouldbe copiedtoAlfresco'sWEBINF/libdirectory.Ifyouhaveproblems,refertotheAlfrescowiki.Links appearattheendofthisarticle. Tosetupatest,you'llneedtocreatecontentusingatypethathasthereplaceableaspect.Probablythe easiestthingtodoistosimplyrunanactiononanexistingdocumenttoaddthereplaceableaspect.In 1.3Enterprise,"replaceable"isnotlistedasanaspectthatcanbeaddedthroughanaction.Ifyour releasehasthesameproblem,addthefollowingtothe"ActionWizards"sectionofthewebclient config.xmlfile:
<!addmissingaspectstolistofaspectsinadd/removeaspectaction> <aspects> <aspectname="replaceable"/> </aspects>

Aftertheaspecthasbeenapplied,youshouldseea"replaces"sectioninthedocument'sproperties.Edit thepropertiesandselectadocumenttoreplace.Thisestablishesthe"replaces"association. Nowaddaruletothespaceinwhich"Published"documentsreside.Theruleshouldrunournew "MoveReplaced"action.Configuretheactiontomovedocumentstothe"Archived"space.Totestthe logic,pastethedocumentwiththe"replaces"associationintothe"Published"space.Thedocumentitis replacingshouldgetautomaticallymovedtothe"Archived"folder. Conclusion ThisarticlehasshownhowactionsandworkflowscanbeusedwithinAlfrescotoimplementflexible andreusablebusinesslogicbymakinglightcustomizationstotheoutoftheboxwebclient.Hopefully ithassparkedsomeideasabouthowyoucouldusecustomactionsinyournextAlfresco implementation. Wheretofindmoreinformation

TheAlfrescoSDKcomeswithaCustomActionexample. Fordeploymenthelp,seetheClientConfigurationGuideandPackagingandDeploying ExtensionsintheAlfrescowiki. AlfrescoDeveloper:DevelopingCustomActions


ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

Page9of10

ecmarchitect.com

Forgeneraldevelopmenthelp,seetheDeveloperGuide. Forhelpcustomizingthedatadictionary,seetheDataDictionarywikipage.

AbouttheAuthor JeffPottsisaPrincipleArchitectatOptaros,aleadingOpenSourceandNext GenerationInternetconsultancy.Jeffhasnearlyfifteenyearsofexperience implementingcontentmanagement,collaboration,andotherknowledge managementtechnologiesforavarietyofFortune500companies.Jefflivesin Dallas,Texaswithhiswifeandtwokids.Readmoreatecmarchitect.com.

AlfrescoDeveloper:DevelopingCustomActions
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

Page10of10

Вам также может понравиться