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

ecmarchitect.

com

AlfrescoDeveloper:WorkingwithCustomContentTypes
June,2007
JeffPotts

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

ecmarchitect.com
AlfrescoDeveloper:WorkingwithCustomContentTypes
June,2007 JeffPotts

Introduction
Alfrescoisaflexibleplatformfordevelopingcontentmanagementapplications.Thefirststepinthe processofdesigningacustomcontentmanagementapplicationiscreatingthecontentmodel. ThecontentmodelAlfrescoprovidesoutoftheboxisfairlycomprehensive.Infact,forbasic documentmanagementneeds,youcouldprobablygetbywiththeoutoftheboxmodel.Ofcourse, you'dbemissingoutonalotofthepowerandfunctionalitythathavingamodelcustomizedforyour businessneedsprovides. Thisarticlediscusseshowtocreateyourowncustomcontentmodel,butitdoesn'tstopthere.What goodwouldacustomcontentmodelbeifyoudidnothingexcitingwiththecontent?Oncewegetour examplemodelinplace,we'lltweakthewebclientuserinterfacetoexposeourcustommodel,then we'llwritesomecodetocreate,searchfor,anddeletecontent. YoushouldalreadybefamiliarwithgeneraldocumentmanagementandAlfrescowebclientconcepts. Ifyouwanttofollowalong,youshouldalsoknowhowtowritebasicJavacode.SeeWheretofind moreinformationattheendofthisdocumentforalinktothecodesamplesthataccompanythis article.

Modelingbasics
Acontentmodeldescribesthedatabeingstoredintherepository.Thecontentmodeliscritical withoutit,Alfrescowouldbelittlemorethanafilesystem.Hereisalistofkeyinformationthecontent modelprovidesAlfresco:

Fundamentaldatatypesandhowthosedatatypesshouldbepersistedtothedatabase.For example,withoutacontentmodel,Alfrescowouldn'tknowthedifferencebetweenaStringand aDate. Higherorderdatatypeslikecontentandfolderaswellascustomcontenttypeslike StandardOperatingProcedureorContract. Outoftheboxaspectslikeauditableandclassifiableaswellascustomaspectslikerate ableorcommentable. AlfrescoDeveloper:WorkingwithCustomContentTypes Page2of32


ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com

Properties(ormetadata)specifictoeachcontenttype. Constraintsplacedonproperties(suchaspropertyvaluesthatmustmatchacertainpatternor propertyvaluesthatmustcomefromaspecificlistofpossiblevalues). Howtoindexcontentforsearching. Relationshipsbetweencontenttypes.

Alfrescocontentmodelsarebuiltusingasmallsetofbuildingblocks:Types,Properties,Property types,Constraints,Associations,andAspects.

Types
Typesareliketypesorclassesintheobjectorientedworld.Theycanbeusedtomodelbusinessobjects, theyhaveproperties,andtheycaninheritfromaparenttype.Content,Person,andFolderare threeimportanttypesdefinedoutofthebox.Customtypesarelimitedonlybyyourimaginationand businessrequirements.ExamplesincludethingslikeExpenseReport,MedicalRecord,Movie, Song,andComment. Notethattypes,properties,constraints,associations,andaspectshavenames.Namesaremadeunique acrosstherepositorybyusinganamespacespecifictothemodel.Thenamespacehasanabbreviation. So,forexample,atOptaroswemightdefineacustommodelwhichdeclaresanamespacewiththeURI ofhttp://www.optaros.com/model/content/1.0andaprefixofopt.Anytypedefinedaspartofthat modelwouldhaveanameprefixedwithopt:.We'lltalkmoreabouthowmodelsareactuallydefined usingXML,butIwantedtointroducetheconceptofnamespacesandprefixessoyouwouldknowwhat theyarewhenyouseethem.Usingnamespacesinthiswayhelpspreventnamecollisionswhencontent modelsaresharedacrossrepositories.

Properties
Propertiesarepiecesofmetadataassociatedwithaparticulartype.Forexample,thepropertiesofan ExpenseReportmightincludethingslikeEmployeeName,Datesubmitted,Project,Client, ExpenseReportNumber,Totalamount,andCurrency.TheExpenseReportmightalsoincludea contentpropertytoholdtheactualexpensereportfile(maybeitisaPDForanExcelspreadsheet,for example).

Propertytypes
Propertytypes(ordatatypes)describethefundamentaltypesofdatatherepositorywillusetostore AlfrescoDeveloper:WorkingwithCustomContentTypes Page3of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
properties.Examplesincludethingslikestrings,dates,floats,andbooleans.Becausethesedatatypes literallyarefundamental,theyareprettymuchthesameforeveryonesotheyaredefinedforusoutof thebox.(IfyouwantedtochangethefactthattheAlfrescodatatypetextmapstoyourowncustom classratherthanjava.lang.String,youcould,butlet'snotgetaheadofourselves).

Constraints
ConstraintscanoptionallybeusedtorestrictthevaluethatAlfrescowillstoreinaproperty.Thereare fourtypesofconstraintsavailable:REGEX,LIST,MINMAX,andLENGTH.REGEXisusedtomake surethatapropertyvaluematchesaregularexpressionpattern.LISTisusedtodefinealistofpossible valuesforaproperty.MINMAXprovidesanumericrangeforapropertyvalue.LENGTHsetsa restrictiononthelengthofastring. Constraintscanbedefinedonceandreusedacrossamodel.Forexample,outofthebox,Alfresco makesavailableaconstraintnamedcm:filenamethatdefinesaregularexpressionconstraintforfile names.Ifapropertyinacustomtypeneedstorestrictvaluestothosematchingthefilenamepattern, thecustommodeldoesn'thavetodefinetheconstraintagain,itsimplyreferstothecm:filename constraint.

Associations
Associationsdefinerelationshipsbetweentypes.Withoutassociations,modelswouldbefulloftypes withpropertiesthatstorepointerstootherpiecesofcontent.Goingbacktotheexpensereport example,wemightwanttostoreeachexpenseasanindividualobject.InadditiontoanExpenseReport typewe'dalsohaveanExpensetype.UsingassociationswecantellAlfrescoabouttherelationship betweenanExpenseReportandoneormoreExpenses. Associationscomeintwoflavors:PeerAssociationsandChildAssociations.(NotethatAlfrescorefers toPeerAssociationssimplyasAssociationsbutIthinkthat'sconfusingsoI'llrefertothemwiththe Peerdistinction).PeerAssociationsarejustthattheydefinearelationshipbetweentwoobjectsbut neitherissubordinatetotheother.ChildAssociations,ontheotherhand,areusedwhenthetargetof theassociation(orchild)shouldnotexistwhenthesource(orparent)goesaway.Thisworkslikea cascadeddeleteinarelationaldatabase:Deletetheparentandthechildgoesaway. Anoutoftheboxassociationthat'seasytorelatetoiscm:contains.Thecm:containsassociation definesaChildAssociationbetweenfolders(cm:folder)andallotherobjects(instancesofsys:base oritschildtypes).So,forexample,afoldernamedHumanResources(aninstanceofcm:folder) wouldhaveacm:containsassociationbetweenitselfandallofitsimmediatechildren.Thechildren couldbeinstancesofcustomtypeslikeResume,Policy,orPerformanceReview.

AlfrescoDeveloper:WorkingwithCustomContentTypes Page4of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
AnotherexamplemightbeaWhitepaperanditsRelatedDocuments.Supposethatacompany publisheswhitepapersontheirwebsite.Thewhitepapermightberelatedtootherdocumentssuchas productmarketingmaterialsorotherresearch.Iftherelationshipbetweenthewhitepaperanditsrelated documentsisformalizeditcanbeshownintheuserinterface.Toimplementthis,aspartofthe Whitepapercontenttype,you'ddefineaPeerAssociation.Youcouldusesys:baseasthetargettype toallowanypieceofcontentintherepositorytobeassociatedwithaWhitepaperoryoucouldrestrict theassociationtoaspecifictype.

Aspects
BeforewetalkaboutAspects,let'sfirstconsiderhowinheritanceworksandtheimplicationsonour contentmodel.SupposewearegoingtouseAlfrescotomanagecontenttobedisplayedinaportal (quiteacommonrequirement,bytheway).Supposefurtherthatonlyasubsetofthecontentinour repositoryiscontentwewanttoshowintheportal.And,whencontentistobedisplayedintheportal, therearesomeadditionalpiecesofmetadataweneedtocapture.Asimpleexamplemightbethatwe wanttoknowthedateandtimeapieceofcontentwasapprovedtobeshownintheportal. Usingthecontentmodelingconceptsdiscussedsofar,wewouldhaveonlytwooptions.First,wecould definearootcontenttypewiththepublishdateproperty.Allsubsequentcontenttypeswouldinherit fromthisroottypethusmakingthepublishdateavailableeverywhere.Second,wecouldindividually definethepublishdatepropertyonlyinthecontenttypesweknewweregoingtobepublishedtothe portal. Neitherofthesearegreatoptions.Inthefirstoption,wewouldwindupwithapropertyineachand everypieceofcontentintherepositorythatmayormaynotultimatelybeusedwhichcanleadto performanceandmaintenanceproblems.Thesecondoptionisn'tmuchbetterforafewreasons.First,it assumesweknowwhichcontenttypeswewanttopublishintheportalaheadoftime.Second,itopens upthepossibilitythatthesametypeofmetadatamightgetdefineddifferentlyacrosscontenttypes. Last,itdoesn'tgiveusaneasywaytoencapsulatebehaviororbusinesslogicwemightwanttotietothe publishdate. Asyouhaveprobablyfiguredoutbynow,thereisathirdoptionthataddressestheseissues:Aspects. Aspectsallowustocrosscutourcontentmodelwithpropertiesandassociationsbyattachingthemto contenttypes(orevenspecificinstancesofcontent)whenandwhereweneedthem. Goingbacktotheportalexample,wewoulddefineaPortalDisplayableaspectwithapublishdate property.Theaspectwouldthenbeaddedtoanypieceofcontent,regardlessoftype,thatneededtobe displayedintheportal.

AlfrescoDeveloper:WorkingwithCustomContentTypes Page5of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
CustomBehavior
Youmayfindthatyourcustomaspectorcustomtypeneedstohavebehaviororbusinesslogic associatedwithit.Forexample,everytimeanExpenseReportischeckedinyouwanttorecalculatethe totalbyiteratingthroughtheassociatedExpenses.Oneoptionwouldbetoincorporatethislogicinto rulesoractionsintheAlfrescowebclientoryourcustomwebapplication.Butsomebehaviorisso fundamentaltotheaspectortypethatitshouldreallybeboundtotheaspectortypeandinvokedany timeAlfrescoworkswiththoseobjects.I'llshowhowtodothisinafuturearticle,butyoushouldknow thatassociatingbusinesslogicwithyourcustomaspectsandtypes(oroverridingoutofthebox behavior)ispossible.

ContentModelingBestPractices
Nowthatyouknowthebuildingblocksofacontentmodel,itmakessensetoconsidersomebest practices.Herearethetopten: 1. Don'tchangeAlfresco'soutoftheboxcontentmodel.Ifyoucanpossiblyavoidit,donot changeAlfresco'soutoftheboxcontentmodel.Instead,extenditwithyourowncustom contentmodel.Ifrequirementscallforseveraldifferenttypesofcontenttobestoredinthe repository,createacontenttypeforeachonethatextendsfromcm:contentorfroman enterprisewiderootcontenttype. 2. Considerimplementinganenterprisewideroottype.Althoughtheneedforacommonancestor typeislessenedthroughtheuseofaspects,itstillmightbeagoodideatodefineanenterprise widerootcontenttypefromwhichallothercontenttypesintherepositoryinheritiffornoother reasonthanitgivescontentmanagersacatchalltypetousewhennoothertypewilldo. 3. Beconservativeearlyonbyaddingonlywhatyouknowyouneed.Acorollarytothatisprepare yourselftoblowawaytherepositorymultipletimesuntilthecontentmodelstabilizes.Onceyou getcontentintherepositorythatimplementsthetypesinyourmodel,makingmodeladditions iseasy,butsubtractionsaren't.Alfrescowillcomplainaboutintegrityerrorsandmaymake contentinaccessiblewhenthecontent'stypeorpropertiesdon'tmatchthecontentmodel definition.Whenthishappenstoyou(anditwillhappen)youroptionsaretoeither(1)leavethe oldmodelinplace,(2)attempttoexportthecontent,modifytheACPXMLfile,andreimport, or(3)droptheAlfrescotables,clearthedatadirectory,andstartfresh.Aslongaseveryoneon theteamisawareofthis,optionthreeisnotabigdealindevelopment,butmakesure expectationsaresetappropriatelyandhaveaplanforhandlingmodelchangesonceyougetto production.ThismightbeanareawhereAlfrescowillimproveinfuturereleases,butfornowit issomethingyouhavetowatchoutfor.

AlfrescoDeveloper:WorkingwithCustomContentTypes Page6of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
4. Avoidunnecessarycontentmodeldepth.IamnotawareofanyAlfrescoContentModeling Commandmentsthatsay,ThoushallnotexceedXlevelsofdepthinthinecontentmodellest thousufferthewrathofpoorperformancebutitseemslogicalthatdegradationwouldoccurat somepoint.Ifyourmodelhasseverallevelsofdepthbeyondcm:content,youshouldatleastdo aproofofconceptwitharealisticamountofdata,software,andhardwaretomakesureyou aren'tcreatingaproblemforyourselfthatmightbeverydifficulttoreversedowntheroad. 5. Takeadvantageofaspects.Inadditiontothepotentialperformanceandoverheadsavings throughtheuseofaspects,aspectspromotereuseacrossthemodel,thebusinesslogic,andthe presentationlayer.Whenworkingonyourmodelyoufindthattwoormorecontenttypeshave propertiesincommon,askyourselfifthosepropertiesarebeingusedtodescribesomehigher levelcharacteristiccommonacrossthetypesthatmightbetterbemodeledasanaspect. 6. Itmaymakesensetodefinetypesthathavenopropertiesorassociations.Youmayfind yourselfdefiningatypethatgetseverythingitneedsthrougheitherinheritancefromaparent typeorfromanaspect(orboth).Inthosecasesyoumightaskyourselfiftheemptytypeis reallynecessary.Inmyopinion,itshouldatleastbeconsidered.Itmightbeworthitjustto distinguishthecontentfromothertypesofcontentforsearchpurposes,forexample.Or,while youmightnothaveanyspecializedpropertiesorassociationsforthecontenttypeyoucould havespecializedbehaviorthat'sonlyapplicabletoinstancesofthecontenttype. 7. Rememberthatfoldersaretypestoo.Likeeverythingelseinthemodel,foldersaretypeswhich meanstheycanbeextended.Contentthatcontainsothercontentiscommon.Intheearlier expensereportexample,onewaytokeeptrackoftheexpensesassociatedwithanexpense reportwouldbetomodeltheexpensereportasasubtypeofcm:folder. 8. Don'tbeafraidtohavemorethanonecontentmodelXMLfile.Wehaven'ttalkedaboutexactly howcontentmodelsaredefinedyet,butwhenitistimetoimplementyourmodel,keepthisin mind:Itmightmakesensetosegmentyourmodelsintomultiplenamespacesandmultiple XMLfiles.Namesshouldbedescriptive.Don'tdeployamodelfilecalledcustomModel.xml ormyModel.xml. 9. ImplementaJavaclassthatcorrespondstoeachcustomcontentmodelyoudefine.Withineach contentmodelJavaclass,defineconstantsthatcorrespondtomodelnamespaces,typenames, propertynames,aspectnames,etc.You'llfindyourselfreferringtotheQnameoftypes, properties,andaspectsquiteoftensoithelpstohaveconstantsdefinedinanintuitiveway. 10. Usethesource!Theoutoftheboxcontentmodelisagreatexampleofwhat'spossible.The forumModelandrecordsModelhavesomeparticularlyusefulexamples.InthenextsectionI'll tellyouwherethemodelfilesliveandwhat'sineachsoyou'llknowwheretolooklaterwhen yousaytoyourself,Surely,thefolksatAlfrescohavedonethisbefore.

AlfrescoDeveloper:WorkingwithCustomContentTypes Page7of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
Outoftheboxmodels
TheAlfrescosourcecodeisanindispensablereferencetoolwhichyoushouldalwayshaveattheready, alongwiththedocumentation,wiki,forums,andJira.Withthatsaid,ifyouarefollowingalongwith thisarticlebuthavenotyetdownloadedthesource,youareinluck.Theoutoftheboxcontentmodel filesarewritteninXMLandgetdeployedwiththewebclient.Theycanbefoundinthealfresco.war filein/WEBINF/classes/alfresco/model.Thetablebelowdescribesseveralofthemodelfilesthatcan befoundinthedirectory. File Namespaces* d sys reg module cm d sys Typesandaspects extendedmostoftenby yourmodelslikeContent, Folder,Versionable,and Auditable. Advancedworkflowtypes. Extendthesewhenwriting yourowncustom advancedworkflows. Typesandaspectsrelated toaddingdiscussion threadstoobjects. Prefix Imports None d Description Fundamentaldatatypes usedinallothermodels. Systemlevelobjectslike base,storeroot,and reference.

dictionaryModel.xml model/dictionary/1.0 systemModel.xml model/system/1.0 system/registry/1.0 system/modules/1.0 contentModel.xml model/content/1.0

bpmModel.xml

model/bpm/1.0

bpm

d sys cm

forumModel.xml

model/forum/1.0

fm

d cm

Intheinterestofbrevity,I'veleftoffthetwoWCMrelatedmodelfiles,theJCRmodel,andtheweb clientapplicationmodel.Dependingonwhatyouaretryingtodowithyourmodel,orjusttoseefurther examples,youmightwanttotakealookatthoseatsomepoint. InadditiontothemodelfilesthemodelSchema.xsdfilecanbeagoodreference.Asthenamesuggests, AlfrescoDeveloper:WorkingwithCustomContentTypes Page8of32


ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
itdefinestheXMLvocabularyAlfrescocontentmodelXMLfilesmustadhereto.

CustomModelExample
Timeforadetailedexample.SupposeweworkforacompanycalledSomeCo.SomeCoisa commercialopensourcecompanythat'sbehindtheeverpopularopensourceproject,SomeSoftware. SomeCohasdecidedtorevampitswebpresencebyaddingnewtypesofcontentandcommunity functionalitytotheirwebsite.Forthisexample,we'llfocusonthewhitepapersSomeCowantstomake available. SomeCohasselectedAlfrescoastheirEnterpriseContentManagementsolution.Inadditionto managingthecontentonthenewsite,SomeCowantstouseAlfrescotomanageallofitsrichcontent. Let'sassumethatafteralotofdiscussion,SomeCohaselectedtogowithstraightAlfrescoforthis projectratherthanleveragingAlfresco'sWCMfeatures. Thefirststepistoconsiderthetypesandpropertieswearedealingwith.Therearesomepiecesof metadataSomeCowantstotrackaboutallcontent,regardlessofwhetherornotitwillbeshownonthe website.Alldocumentswillhaveanaudiencepropertythatidentifieswhowillbemostinterestedin thecontent.DocumentsrelatedtoSomeCo'ssoftwarewillhavepropertiesidentifyingtheSoftware ProductandSoftwareVersion. Contentthatneedstobeshownonthewebsiteneedstohaveaflagthatindicatesthecontentis activeandadatewhenthecontentwassettoactive. Nowlet'sthinkaboutassociations.Forsomedocuments,SomeCowouldliketoexplicitlydefineoneor morerelateddocuments.Onthewebsite,SomeComightchoosetoshowalistofrelateddocuments atthebottomofawhitepaper,forexample. Takingtheserequirementsintoconsideration,theteamcomesupwiththecontentmodeldepictedin Drawing1:SomeCo'sinitialcontentmodel.Asthedrawingshows,wehavedefinedacommonroot typecalledsc:docwithonechild,sc:whitepaper.Neithertypecurrentlyhasanypropertiesoftheirown. It'snotshownonthemodeldiagram,butwewilldefineaPeerAssociationaspartofsc:doctokeep trackofrelateddocuments.Thetargetclassoftheassociationwillbesc:docbecausewewanttobeable toassociateanyinstanceofsc:docoritschildrenwithoneormoreinstancesofsc:docoritschildren. Inaddition,therearetwoaspects.One,thewebableaspect,isusedforcontentthatistobeshownon thewebsiteitcontainstheactiveflagandactivedate.Theproductrelatedaspectisusedonlyfor contentthatrelatestoaSomeCoproduct.Itcapturesthespecificproductnamethecontentisrelatedto aswellastheproductversion.

AlfrescoDeveloper:WorkingwithCustomContentTypes Page9of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com

Drawing1:SomeCo'sinitialcontentmodel Itshouldbeeasytoseehowthemodelmightbeextendedovertime.Therequirementsmentioned communityfeaturesbeingneededatsomepoint.Arateableaspectcouldbeaddedalongwitharating type.Commentscouldworkthesameway,orcommentscouldleveragetheexistingforumscontent model. Asnewcontenttypesareidentifiedtheywillbeaddedundersc:doc. Usingtheaspecttodeterminewhetherornottoshowthecontentontheportalishandy,particularlyin lightoftheSomeCodecisiontouseAlfrescoforallofitscontentmanagementneeds.Therepository willcontaincontentthatmayormaynotbeontheportal.Portalcontentwillbeeasilydistinguishable fromnonportalcontentbythepresenceofthewebableaspect.

AlfrescoDeveloper:WorkingwithCustomContentTypes Page10of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
Implementinganddeployingthemodel
Beforewestart,acoupleofnotesaboutmysetup:

UbuntuDapperDrake MySQL4.1 Tomcat5.5.x Alfresco2.0.1Enterprise,WARonlydistribution

Obviously,otheroperatingsystems,databases,applicationservers,andAlfrescoversionswillworkas well. Okay,let'sconfigureAlfrescotouseournewcontentmodel.Herearethestepswearegoingtofollow: 1. Createanextensiondirectory 2. Createacustommodelcontextfile 3. Createamodelfile 4. RestartTomcat Thefirststepistocreateanextensiondirectoryifyoudonotalreadyhaveone.Anextension directorykeepsyourcustomizationsseparatefromAlfresco'scode.Thismakesiteasiertoupgrade Alfrescolateron. Theextensiondirectorycanbeanywhereonthewebapplication'sclasspath.Irecommendusingtwo. Onesshouldbeforserverspecificsettingssuchastherepositoryproperties(specifiesthedatabase username,password,andconnectionstring)andLDAPauthenticationcontext.ForTomcat installations,thisextensiondirectorywouldbe$TOMCAT_HOME/shared/classes/alfresco/extension. TheotherextensiondirectoryisforyourAlfrescowebclientcustomizationsandyourcontentmodel. ForthatIusetheextensiondirectorythatisincludedintheAlfrescowebapplicationwhichisunder $TOMCAT_HOME/webapps/alfresco/WEBINF/classes/alfresco/extension. It'sfineifyouwanttokeepitsimplefornowandjustuseoneextensiondirectory. Thesecondstepistocreateacustommodelcontextfile.Acontextfileisafilethatcontainsoneor moreSpringbeanconfigurations.ThereareseveralcontextfilesusedtoconfigureAlfrescoSpring beans.DependingontheAlfrescodistributionyoudownloadedyoumayhaveasetofsamplecontext filesinyourextensiondirectory. Alfrescoloadsanyfileontheclasspaththatendswithcontext.xml.Allwehavetodoiscreateafile thatendswiththatsuffix,andinit,overridethebeanthatreferstoourcontentmodel.Howdoweknow whichbeantoextend?Recallfromearlierthatthereisanoutoftheboxcontentmodelfilecalled AlfrescoDeveloper:WorkingwithCustomContentTypes Page11of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
contentModel.xml.Ifyouhavethesourcehandy,gototherepositoryprojectanddoarecursivegrep forthestringcontentModel.xml.You'llfindthatitisreferencedinafilecalledconfig/alfresco/core servicescontext.xmlasshownbelow.
<beanid="dictionaryBootstrap"parent="dictionaryModelBootstrap"depends on="resourceBundles"> <propertyname="models"> <list> <!Systemmodels> <value>alfresco/model/dictionaryModel.xml</value> <value>alfresco/model/systemModel.xml</value> <value>org/alfresco/repo/security/authentication/userModel.xml</va lue> <!Contentmodels> <value>alfresco/model/contentModel.xml</value> <value>alfresco/model/bpmModel.xml</value> ...

Listing1:coreservicescontext.xml Wewantourmodeltoberegisteredwiththedictionaryaswellsowe'lljustextendthisbeaninourown contextfileandaddourmodeltothelist.Createafilecalledintheextensiondirectorycalledsomeco modelcontext.xmlandaddthefollowing:


<?xmlversion='1.0'encoding='UTF8'?> <!DOCTYPEbeansPUBLIC'//SPRING//DTDBEAN//EN' 'http://www.springframework.org/dtd/springbeans.dtd'> <beans> <!Registrationofnewmodels> <beanid="extension.dictionaryBootstrap"parent="dictionaryModelBootstrap" dependson="dictionaryBootstrap"> <propertyname="models"> <list> <value>alfresco/extension/scModel.xml</value> </list> </property> </bean> </beans>

Listing2:somecomodelcontext.xml Again,itdoesn'tmatterwhatthefileiscalledaslongasitendswithcontext.xml. Next,itistimetocreateamodelfilethatimplementsthecontentmodelwedefinedearlier.Createa newXMLfileintheextensiondirectory.Makesurethenamematcheswhatyouspecifiedinthe somecomodelcontext.xmlfile.Inourexample,thefileshouldbenamedscModel.xml. ReferringbacktoDrawing1:SomeCo'sinitialcontentmodel,itlookslikewe'llneedtwoaspects,each withtwoproperties,andtwocontenttypes.TakealookatthecontentModel.xmlwefoundearlierand

AlfrescoDeveloper:WorkingwithCustomContentTypes Page12of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
useitasareferencetobuildourscModel.xmlfile.Theresultshouldlooklikethis:
<?xmlversion="1.0"encoding="UTF8"?> <!DefinitionofnewModel> <modelname="sc:somecomodel"xmlns="http://www.alfresco.org/model/dictionary/1.0"> <!Optionalmetadataaboutthemodel> <description>SomecoModel</description> <author>Optaros</author> <version>1.0</version> > <!Importsarerequiredtoallowreferencestodefinitionsinothermodels <imports> <!ImportAlfrescoDictionaryDefinitions> <importuri="http://www.alfresco.org/model/dictionary/1.0"prefix="d"/> <!ImportAlfrescoContentDomainModelDefinitions> <importuri="http://www.alfresco.org/model/content/1.0"prefix="cm"/> </imports> <!Introductionofnewnamespacesdefinedbythismodel> <namespaces> <namespaceuri="http://www.someco.com/model/content/1.0"prefix="sc"/> </namespaces> <types> <!Enterprisewidegenericdocumenttype> <typename="sc:doc"> <title>SomecoDocument</title> <parent>cm:content</parent> <associations> <associationname="sc:relatedDocuments"> <title>RelatedDocuments</title> <source> <mandatory>false</mandatory> <many>true</many> </source> <target> <class>sc:doc</class> <mandatory>false</mandatory> <many>true</many> </target> </association> </associations> <mandatoryaspects> <aspect>cm:generalclassifiable</aspect> </mandatoryaspects> </type> <typename="sc:whitepaper"> <title>SomecoWhitepaper</title>

AlfrescoDeveloper:WorkingwithCustomContentTypes Page13of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
<parent>sc:doc</parent> </type> </types> <aspects> <aspectname="sc:webable"> <title>SomecoWebable</title> <properties> <propertyname="sc:published"> <type>d:date</type> </property> <propertyname="sc:isActive"> <type>d:boolean</type> <default>false</default> </property> </properties> </aspect> <aspectname="sc:productRelated"> <title>SomecoProductMetadata</title> <properties> <propertyname="sc:product"> <type>d:text</type> <mandatory>true</mandatory> </property> <propertyname="sc:version"> <type>d:text</type> <mandatory>true</mandatory> </property> </properties> </aspect> </aspects> </model>

Listing3:scModel.xml Here'sanimportantnoteaboutthecontentmodelschemathatmaysaveyousometime:Ordermatters. Forexample,ifyoumovetheassociationselementaftermandatoryaspectsAlfrescowon'tbeable toparseyourmodel.RefertothemodelSchema.xsdreferencedearliertodeterminetheexpectedorder. ThefinalstepistorestartTomcatsothatAlfrescowillloadourcustommodel.Watchthelogduring therestart.Youshouldseenoerrorsrelatedtoloadingthecustommodel.Ifthereisaproblem,the messageusuallylookssomethinglike,Couldnotimportbootstrapmodel.

Exposingthecustommodelinthewebclientuserinterface
Nowthatthemodelisdefined,youcouldbeginusingitrightawaybywritingcodeagainstoneof Alfresco'sAPI'sthatcreatesinstancesofyourcustomtypes,addsaspects,etc.Inpracticeitisusuallya AlfrescoDeveloper:WorkingwithCustomContentTypes Page14of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
goodideatodojustthattomakesurethemodelbehaveslikeyouexpect.Beforewegettothat,though, let'stalkaboutwhatweneedtodoinordertobeabletoworkwithournewmodelintheAlfrescoweb clientuserinterface. First,thinkaboutthewebclientandalloftheplacesthecontentmodelcustomizationsneedtoshow up:

PropertySheet.Whenauserlooksatapropertysheetforapieceofcontentstoredasoneof thecustomtypesorwithoneofthecustomaspectsattached,thepropertysheetshouldshowthe customproperties. Createcontent/addcontent.WhenauserclicksCreateorAddContent,thecustomtypes shouldbeachoiceinthelistofcontenttypes. Issubtypecriteria.Whenauserconfiguresaruleonaspaceandusescontenttypesasa criteria,thecustomtypesshouldbeachoiceinthelistofpossiblecontenttypes. Specializetypeaction.Whenauserrunsthespecializetypeaction,thecustomtypesshould beavailable. Addaspect.Whenauserrunstheaddaspectaction,thecustomaspectsshouldbeavailable. Advancedsearch.Whenauserrunsanadvancedsearch,theyshouldbeabletorestrictsearch resultstoinstancesofourcustomtypesand/orcontentwithspecificvaluesforthepropertiesof ourcustomtypes.

Allofthesearehandledthroughafilecalledwebclientconfig.Lookingatthewebclientprojectinthe Alfrescosource,youcanseetheoutoftheboxwebclientconfig.xmlfileunderconfig/alfresco.The webclientconfig.xmlfileisaproprietaryfilecomposedofnumerousconfigelements.Eachconfig elementhasanevaluatorandacondition.Extendingthewebclientconfig.xmlfileisamatterof knowingwhichevaluator/conditiontouse. Tocustomizetheuserinterface,createawebclientconfigcustom.xmlfileinyourextensiondirectory witharootelementcalledalfrescoconfig.Addtheappropriateconfigelementsaschildrenof alfrescoconfig. Let'slookateachoftheareasmentionedaboveinordertounderstandhowthecustomcontentmodel canbeexposedintheuserinterface. Propertysheet Whenauserlooksatapropertysheetforapieceofcontentstoredasoneofthecustomtypesorwith oneofthecustomaspectsattached,thepropertysheetshouldshowthecustompropertiesasshown below:

AlfrescoDeveloper:WorkingwithCustomContentTypes Page15of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com

Drawing2:Custompropertiesonthe propertiessheet Toaddpropertiestopropertysheetsusetheaspectnameevaluatorforaspectsandnodetypefor contenttypes.Thesnippetbelowshowstheconfigforthesc:webableaspect.Thesc:productRelated aspectwouldbesimilar.


<!addwebableaspectpropertiestopropertysheet> <configevaluator="aspectname"condition="sc:webable"> <propertysheet> <showpropertyname="sc:published"displaylabelid="published"

/>

<showpropertyname="sc:isActive"displaylabelid="isActive" readonly="true"/> <showassociationname=sc:relatedDocuments/> </propertysheet> </config>

Listing4:Snippetfromwebclientconfigcustom.xml Notethedisplaylabelidattribute.Youcouldspecifythelabelinthisfilebyusingthelabelattribute, butabetterpracticeistoexternalizethestringsotheinterfacecouldbelocalizedifneeded.Attheend ofthissectionwe'llseewherethelocalizedstringsreside. Createcontent/Addcontent WhenauserclicksCreateorAddContent,thecustomtypesshouldbeachoiceinthelistofcontent typesasshownbelow:

AlfrescoDeveloper:WorkingwithCustomContentTypes Page16of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com

Drawing3:Customtypeintheaddcontent dialog Toaddcontenttypestothelistofavailabletypesinthecreatecontentandaddcontentdialogs,usethe stringcompareevaluatorandtheContentWizardscondition.


<!addsomecotypestoaddcontentlist> <configevaluator="stringcompare"condition="ContentWizards"> <contenttypes> <typename="sc:doc"/> <typename="sc:whitepaper"/> </contenttypes> </config>

Listing5:Snippetfromwebclientconfigcustom.xml Issubtypecriteria,specializetypeaction,addaspectaction Whenauserconfiguresaruleonaspaceandusescontenttypesoraspectsasacriteria,orrunsan actionrelatedtotypesoraspects,theappropriatelistoftypesandaspectsshouldbedisplayedasshown below:

Drawing5:Customaspectin"add aspect"dialog

Drawing4:Customaspectin"has aspect"dialog

Thesecustomizationsareallpartofthesameconfigelement.TheActionWizardsconfighasseveral AlfrescoDeveloper:WorkingwithCustomContentTypes Page17of32


ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
childelementsthatcanbeused.Theaspectselementdefinesthelistofaspectsshownwhentheadd aspectactionisconfigured.Thesubtypeselementliststypesthatshowupinthedropdownwhen configuringthecontenttypecriteriaforarule.Thespecialisetypeselement(notetheUKspelling) liststhetypesavailabletothespecializetypeaction.
<configevaluator="stringcompare"condition="ActionWizards"> <!Thelistofaspectstoshowintheadd/removefeaturesaction> <!andthehasaspectcondition> <aspects> <aspectname="sc:webable"/> <aspectname="sc:productRelated"/> </aspects> <!Thelistoftypesshownintheissubtypecondition> <subtypes> <typename="sc:doc"/> <typename="sc:whitepaper"/> </subtypes> <!Thelistofcontentand/orfoldertypesshowninthespecialise typeaction> <specialisetypes> <typename="sc:doc"/> <typename="sc:whitepaper"/> </specialisetypes> </config>

Listing6:Snippetfromwebclientconfigcustom.xml Advancedsearch Whenauserrunsanadvancedsearch,theyshouldbeabletorestrictsearchresultstoinstancesofour customtypesand/orcontentwithspecificvaluesforthepropertiesofourcustomtypesasshownbelow:

AlfrescoDeveloper:WorkingwithCustomContentTypes Page18of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com

Drawing6:Customtypesandpropertiesinadvancedsearch TheAdvancedSearchconfigspecifieswhichcontenttypesandpropertiescanbeusedtorefinean advancedsearchresultset.


<configevaluator="stringcompare"condition="AdvancedSearch"> <advancedsearch> <contenttypes> <typename="sc:doc"/> <typename="sc:whitepaper"/> </contenttypes> <customproperties> <metadataaspect="sc:webable"property="sc:published"display labelid="published"/> <metadataaspect="sc:webable"property="sc:isActive"display labelid="isActive"/> <metadataaspect="sc:productRelated"property="sc:product" displaylabelid="product"/> <metadataaspect="sc:productRelated"property="sc:version" displaylabelid="version"/> </customproperties> </advancedsearch> </config>

AlfrescoDeveloper:WorkingwithCustomContentTypes Page19of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
Listing7:Snippetfromwebclientconfigcustom.xml

Stringexternalization
Thelaststepinexposingourcustomcontentmodeltotheuserinterfaceisexternalizingthestrings usedforlabelsintheinterface.Thisisaccomplishedbycreatingawebclient.propertiesfile.The propertiesfileisastandardresourcebundle.Itholdskeyvaluepairs.Thekeysmatchthedisplay labelidattributevaluesinthewebclientconfigcustom.xmlfile. Inourexample,wehavefourpropertiesweneedlabelsfor.Theentirecontentsofour webclient.propertiesfilewouldbeasfollows:
#sc:webable published=Published isActive=Active? #sc:productRelated product=Product version=Version

Listing8:webclient.properties

Testthewebclientuserinterfacecustomizations
Nowthatyourwebclientcustomconfig.xmlandwebclient.propertiesfileshavebeenmodifiedand placedinyourextensiondirectory,youshouldbeabletorestartTomcatandseeyourchanges.Ifyou don't,checktheTomcatlogforerrormessages,thenrecheckyourmodelandwebclientfiles.

Workingwithcontentprogrammatically
Sofarwe'vecreatedacustommodelandwe'veexposedthemodelinAlfrescowebclient.Forsimple documentmanagementsolutions,thismaybeenough.Often,codewillalsorequired.Itcouldbecode inawebapplicationthatneedstoworkwiththerepository,codethatimplementscustombehaviorfor customcontenttypes,orcodethatimplementsAlfrescowebclientcustomizations. ThereareseveralAPI'savailabledependingonwhatyouwanttodo.Thetablebelowoutlinesthe choices: Solutiontype Alfrescowebclientuserinterfacecustomizations Language AlfrescoAPI

FreemarkerTemplating AlfrescoFoundation

AlfrescoDeveloper:WorkingwithCustomContentTypes Page20of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
Solutiontype Language Language Java/JSP CustomapplicationswithanembeddedAlfresco Java repository(Repositoryrunsinthesameprocessas theapplication) CustomapplicationsusingaseparateAlfresco repository Java PHP COM/.Net Anylanguagethat supportsWebServices ServersideScripts JavaScript AlfrescoJavaScriptAPI AlfrescoFoundation API JCRAPI AlfrescoWebServices API JCRAPIoverJNDI API AlfrescoAPI

Inthistutorial,we'llfocusonusingtheAlfrescoWebServicesAPIwithJavaandPHP.TheAlfresco WikiandtheAlfrescoSDKsamplesaregoodexamplesofprogrammingagainsttheAlfrescoAPI. SomeoftheexamplesinthisdocumentwilllooksimilartotheSDKsamples,buthopefullyprovidea morecompleteendtoendexample.

CreatingcontentprogrammaticallywithJava
First,let'screatesomecontentandaddsomeaspects.Thegeneralgististhatwe'regoingto:

Authenticate Getareferencetothefolderwherewewantthecontenttoreside CreateaseriesofCMLobjectsthatencapsulatetheoperationswewanttoexecute ExecutetheCML Dumptheresults

Let'slookattheJavacodethatdoesthisfirstandthenthesamethingwrittenwithPHP. Thecodewe'regoingtouseforcreatingcontentisalmostexactlythesamecodethatcomeswiththe AlfrescoSDKSamples.ButIthinkitishelpfultobreakitdownhere. AlfrescoDeveloper:WorkingwithCustomContentTypes Page21of32


ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
Alsonotethatallofthiscodeiscontainedwithinatrycatchfinallyblock(weendthesessionin finally)butI'veleftitouthereforbrevity.Theclassisrunnablewithargumentsbeingpassedinforthe username,password,folderinwhichtocreatethecontent,typeofcontentwe'recreating,andaname forthenewcontent.Again,I'veleftthatoutbutyoucanseethefullclassinitsentiretyifyoudownload thecodethataccompaniesthisdocument(SeeWheretoFindMoreInformation). Thefirstthingthecodedoesisstartasession.Next,itgetsareferencetothefolderwherethecontent willbecreated.Thetimestampisincorporatedintothecontentnamesothatifthecodeisrunmultiple times,theobjectnameswillbeunique.
AuthenticationUtils.startSession(getUser(),getPassword()); StorestoreRef=newStore(Constants.WORKSPACE_STORE,"SpacesStore"); StringfolderPath="/app:company_home/cm:"+getRootFolder(); StringtimeStamp=newLong(System.currentTimeMillis()).toString(); ParentReferencedocParent=newParentReference( storeRef, null, folderPath, Constants.ASSOC_CONTAINS, Constants.createQNameString(SomeCoModel.NAMESPACE_SOMECO_CONTENT_MODEL, getContentName()+timeStamp));

Listing9:SnippetfromSomeCoDataCreator.java Next,createanarrayofNamedValueobjectsforthepropertiesandtheirvalues.Takespecialnoteof thatdatetimeformat.


NamedValuenameValue=Utils.createNamedValue(Constants.PROP_NAME, getContentName()+"("+timeStamp+")");; NamedValueactiveValue=Utils.createNamedValue( Constants.createQNameString(SomeCoModel.NAMESPACE_SOMECO_CONTENT_MODEL, SomeCoModel.PROP_IS_ACTIVE), "true"); NamedValuepublishDateValue=Utils.createNamedValue( Constants.createQNameString(SomeCoModel.NAMESPACE_SOMECO_CONTENT_MODEL, SomeCoModel.PROP_PUBLISHED), "20070401T00:00:00.00005:00"); NamedValue[]contentProps=newNamedValue[]{nameValue,activeValue, publishDateValue};

Listing10:SnippetfromSomeCoDataCreator.java NowCMLcomesintoplay.I'mnotsurewhatitstandsfor,butCMLobjectscanbeusedtoexecute variouscontentoperations.Inthiscase,we'lluseCMLCreateandCMLAddAspectobjectsthatcreate thenodeandaddaspectstoit.Notetheref1string.That'sanarbitraryreferencethatAlfrescousesto relatetheCMLstatements.Withoutit,Alfrescowouldn'tknowwhichcontentobjecttoaddtheaspects AlfrescoDeveloper:WorkingwithCustomContentTypes Page22of32


ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
to.
CMLCreatecreateDoc=newCMLCreate( "ref1", docParent, null, null, null, Constants.createQNameString(SomeCoModel.NAMESPACE_SOMECO_CONTENT_MODEL, SomeCoModel.TYPE_SC_DOC), contentProps); CMLAddAspectaddWebableAspectToDoc=newCMLAddAspect( Constants.createQNameString(SomeCoModel.NAMESPACE_SOMECO_CONTENT_MODEL, SomeCoModel.ASPECT_SC_WEBABLE), null, null, "ref1"); CMLAddAspectaddProductRelatedAspectToDoc=newCMLAddAspect( Constants.createQNameString(SomeCoModel.NAMESPACE_SOMECO_CONTENT_MODEL, SomeCoModel.ASPECT_SC_PRODUCT_RELATED), null, null, "ref1");

Listing11:SnippetfromSomeCoDataCreator.java ToexecutetheCMLweinstantiateanewCMLobjectandpassthecreateandaddaspectarraystothe CMLobject'ssettermethods.TheCMLobjectispassedtotheupdatemethodoftheRepository ServicewhichreturnsanUpdateResultarray.ThedumpUpdateResultsmethodjustiteratesthroughthe UpdateResultarrayandwritessomeinformationtosysout.


//ConstructCMLBlock CMLcml=newCML(); cml.setCreate(newCMLCreate[]{createDoc}); cml.setAddAspect(newCMLAddAspect[]{addWebableAspectToDoc, addProductRelatedAspectToDoc}); //ExecuteCMLBlock UpdateResult[]results=WebServiceFactory.getRepositoryService().update(cml); ReferencedocRef=results[0].getDestination(); dumpUpdateResults(results);

Listing12:SnippetfromSomeCoDataCreator.java Thelaststepwritessometextcontenttothenewlycreatedcontentnode.ThisexampleusesaStringfor thecontentbutitcouldjustaseasilywritethebytesfromafileonthelocalfilesystem.


//Nodesarecreated,nowwritesomecontent ContentServiceSoapBindingStubcontentService= WebServiceFactory.getContentService();

AlfrescoDeveloper:WorkingwithCustomContentTypes Page23of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
ContentFormatcontentFormat=newContentFormat("text/plain","UTF8"); StringdocText="Thisisasample"+getContentType()+"documentcalled"+ getContentName(); ContentdocContentRef=contentService.write(docRef,Constants.PROP_CONTENT, docText.getBytes(),contentFormat); System.out.println("ContentLength:"+docContentRef.getLength());

Listing13:SnippetfromSomeCoDataCreator.java RunningtheJavasnippetproduces:
Command=create;Source=none;Destination=b901941e12d311dc9bf3 e998e07a8da1 Command=addAspect;Source=b901941e12d311dc9bf3e998e07a8da1;Destination= b901941e12d311dc9bf3e998e07a8da1 Command=addAspect;Source=b901941e12d311dc9bf3e998e07a8da1;Destination= b901941e12d311dc9bf3e998e07a8da1 ContentLength:26

Listing14:CommandlineresultsafterrunningSomeCoDataCreator.java

CreatingcontentprogrammaticallywithPHP
Javaisnotarequirementyoucoulduseanylanguagethatunderstandswebservicestoworkwiththe repository.Forexample,AlfrescoincludesaPHPAPI.SettingupPHPandgettingittoworkwith Alfrescoisbeyondthescopeofthisarticle(SeeWheretoFindMoreInformation),butforthe curious,here'showtodothesamethingwejustdidinJava,inPHP.
<?php require_once"Alfresco/Service/Session.php"; require_once"Alfresco/Service/SpacesStore.php"; require_once"Alfresco/Service/Node.php"; ...snip... functioncreateContent($username,$password,$rootFolder,$contentType, $contentName){ //Startandcreatethesession $repository=newRepository("http://localhost:8080/alfresco/api"); $ticket=$repository>authenticate($username,$password); $session=$repository>createSession($ticket); $store=newStore($session,"SpacesStore"); $folderPath="/app:company_home/cm:".$rootFolder; //GrabareferencetotheSomeCofolder $results=$session>query($store,'PATH:"'.$folderPath.'"'); $rootFolderNode=$results[0];

AlfrescoDeveloper:WorkingwithCustomContentTypes Page24of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
if($rootFolderNode==null){ echo"Rootfoldernode(".$folderPath.")isnull<br>"; exit; } $timestamp=time(); $newNode=$rootFolderNode >createChild("{http://www.someco.com/model/content/1.0}".$contentType, "cm_contains","{http://www.someco.com/model/content/1.0}".$contentType."_". $timestamp); if($newNode==null){ echo"Newnodeisnull<br>"; exit; } //Addthetwoaspects $newNode>addAspect("{http://www.someco.com/model/content/1.0}webable"); $newNode>addAspect("{http://www.someco.com/model/content/1.0}productRelated"); echo"Aspectsadded<br>"; //Settheproperties $properties=$newNode>getProperties(); $properties["{http://www.alfresco.org/model/content/1.0}name"]=$contentName. "(".$timestamp.")"; $properties["{http://www.someco.com/model/content/1.0}isActive"]="true"; $properties["{http://www.someco.com/model/content/1.0}published"]="200704 01T00:00:00.00005:00"; $newNode>setProperties($properties); echo"Propsset<br>"; $newNode>setContent("cm_content","text/plain","UTF8","Thisisasample". $contentType."documentnamed".$contentName); echo"Contentset<br>"; $session>save(); echo"Savedchangesto".$newNode>getId()."<br>"; } ?>

Listing15:Contentsofindex.php RunningthePHPscriptinawebbrowserproduces: AlfrescoDeveloper:WorkingwithCustomContentTypes Page25of32


ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
Aspectsadded Propsset Contentset Savedchangesto5a8dac5e131411dcab933b56af79ba48

Listing16:Resultsofrunningindex.php

Creatingassociations
Nowlet'sswitchbacktoJavaandwriteaclasstocreatetherelateddocumentsassociationbetween twodocuments. Themechanicsareessentiallythesame.We'regoingtosetupandexecutesomeCML.Ourclasswill acceptasourceUUIDandatargetUUIDasargumentswhicharepassedintothe CMLCreateAssociationconstructor,executetheCML,andthendumptheresults.
ReferencedocRefSource=newReference(storeRef,getSourceUuid(),null); ReferencedocRefTarget=newReference(storeRef,getTargetUuid(),null); CMLCreateAssociationrelatedDocAssoc=newCMLCreateAssociation(new Predicate(newReference[]{docRefSource},null,null), null, newPredicate(newReference[]{docRefTarget},null,null), null, Constants.createQNameString(SomeCoModel.NAMESPACE_SOMECO_CONTENT_MODEL, SomeCoModel.ASSN_RELATED_DOCUMENTS)); //SetupCMLblock CMLcml=newCML(); cml.setCreateAssociation(newCMLCreateAssociation[]{relatedDocAssoc}); //ExecuteCMLBlock UpdateResult[]results=WebServiceFactory.getRepositoryService().update(cml); dumpUpdateResults(results); System.out.println("AssociationsofsourceUuid:"+getSourceUuid()); dumpAssociations(docRefSource, Constants.createQNameString(SomeCoModel.NAMESPACE_SOMECO_CONTENT_MODEL, SomeCoModel.ASSN_RELATED_DOCUMENTS));

Listing17:SnippetfromSomeCoDataRelater.java Thelastlinecallsamethodthatqueriestheassociationsforagivenreference.Thisshoulddumpthe associationwejustcreatedplusanyotherassociationsthathavebeencreatedforthisobject. RunningtheJavasnippetproduces:


Command=createAssociation;Source=1355e60e160b11dca66fbb03ffd77ac6; Destination=bd0bd57d160c11dca66fbb03ffd77ac6 AssociationsofsourceUuid:1355e60e160b11dca66fbb03ffd77ac6

AlfrescoDeveloper:WorkingwithCustomContentTypes Page26of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
bd0bd57d160c11dca66fbb03ffd77ac6 {http://www.alfresco.org/model/content/1.0}name:TestDocument2(1181340487582)

Listing18:ResultsofrunningSomeCoDataRelater.java NowyoucanusetheAlfrescoWebClienttoviewtheassociations.Rememberthewebclientconfig custom.xmlfile?Wetolditthatanytimeweviewthepropertysheetforsc:docorsc:whitepaper objects,thesc:relatedDocumentsassociationsshouldbeshown.Alternatively,theNodeBrowser, availableintheAdministrationConsole,isahandywaytoviewassociations.

Searchingforcontentprogrammatically
NowthatwehavesomecontentintherepositorywecantestoutAlfresco'sfulltextsearchengine, Lucene.ContentintherepositoryisautomaticallyindexedbyLuceneandquerystringsusetheLucene querysyntaxtofindcontentbasedonfulltextcontents,propertyvalues,andcontenttype.(XPathis alsoasupportedsearchdialectbutwewon'tcoverthathere). Let'swritesomecodethatwillshowseveraldifferentexamplesofAlfrescoqueriesusingLucene.Our codewill:

Authenticate Getareferencetothenodewherewewanttostartthesearch EstablishaqueryobjectusingtheLucenequerystring Executethequery Dumptheresults

Justlikethecontentcreationcode,theclasswillbearunnableJavaapplicationthatacceptsthe username,password,andfoldernameasarguments.Thecodelivesinatrycatchfinallyblock.We'll writeagenericmethodtoexecutethequerysowecancallitrepeatedlywithmultipleexamples. Ifyouarefollowingalong,youshouldeitherrunthecontentcreationcodeafewtimesorcreatesome contentmanuallysoyoucantestoutthequeriesmorethoroughly. Let'stakealookatthegenericqueryexecutionmethodfirst,thenwe'llseethemethodthatcallsitfor eachexamplequerystring.First,wesetuptheQueryobjectandexecutetheQueryusingthequery() methodoftheRepositoryService.


publicList<ContentResult>getQueryResults(StringqueryString)throwsException{ List<ContentResult>results=newArrayList<ContentResult>(); Queryquery=newQuery(Constants.QUERY_LANG_LUCENE,queryString); //Executethequery QueryResultqueryResult=getRepositoryService().query(getStoreRef(),query,

AlfrescoDeveloper:WorkingwithCustomContentTypes Page27of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
false); //Displaytheresults ResultSetresultSet=queryResult.getResultSet(); ResultSetRow[]rows=resultSet.getRows();

Listing19:GenericmethodforexecutingqueriesfromSomeCoDataQueries.java Next,weiteratethroughtheresults,extractingpropertyvaluesfromthesearchresultsandstoringthem inahelperobjectcalledcontentResult.


if(rows!=null){ //Gettheinfomationfromtheresultset for(ResultSetRowrow:rows){ StringnodeId=row.getNode().getId(); ContentResultcontentResult=newContentResult(nodeId); //iteratethroughthecolumnsoftheresultsettoextract //specificnamedvalues for(NamedValuenamedValue:row.getColumns()){ if(namedValue.getName().endsWith(Constants.PROP_CREATED)==true) contentResult.setCreateDate(namedValue.getValue()); }elseif(namedValue.getName().endsWith(Constants.PROP_NAME)== } contentResult.setName(namedValue.getValue());

{ true){

} results.add(contentResult); }//nextrow }//endif returnresults;

Listing20:GenericmethodforexecutingqueriesfromSomeCoDataQueries.java Here'sthecodethatcallsthismethodonceforeachqueryexample.
System.out.println(RESULT_SEP); System.out.println("Findingcontentoftype:"+ SomeCoModel.TYPE_SC_DOC.toString()); queryString="+TYPE:\""+ Constants.createQNameString(SomeCoModel.NAMESPACE_SOMECO_CONTENT_MODEL, SomeCoModel.TYPE_SC_DOC)+"\""; dumpQueryResults(getQueryResults(queryString)); System.out.println(RESULT_SEP); System.out.println("Findcontentintherootfolderwithtextlike'sample'"); queryString="+PARENT:\"workspace://SpacesStore/"+ getRootFolderUuid()+"\"+TEXT:\"sample\"";

AlfrescoDeveloper:WorkingwithCustomContentTypes Page28of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
dumpQueryResults(getQueryResults(queryString)); System.out.println(RESULT_SEP); System.out.println("Findactivecontent"); queryString="+@sc\\:isActive:true"; dumpQueryResults(getQueryResults(queryString)); System.out.println(RESULT_SEP); System.out.println("Findactivecontentwithaproductequalto'SomePortal'"); queryString="+@sc\\:isActive:true+@sc\\:product:SomePortal"; dumpQueryResults(getQueryResults(queryString)); System.out.println(RESULT_SEP); System.out.println("Findcontentoftypesc:whitepaperpublishedbetween1/1/2006 and6/1/2007"); queryString="+TYPE:\""+ Constants.createQNameString(SomeCoModel.NAMESPACE_SOMECO_CONTENT_MODEL, SomeCoModel.TYPE_SC_WHITEPAPER)+ "\""+ "+@sc\\:published:[2006\\01\\01T00:00:00TO2007\\06\\01T00:00:00]"; dumpQueryResults(getQueryResults(queryString));

Listing21:SnippetfromSomeCoDataQueries.javashowingcallstogetQueryResults Yourresultswillvarybasedonhowmuchcontentyou'vecreatedandthevaluesyou'vesetinthe contentproperties,butwhenIranmytest,theresultswereasshownbelow.


====================== Findingcontentoftype:doc Result1: id=1355e60e160b11dca66fbb03ffd77ac6 name=TestWhitepaper(1181339773331) created=20070608T16:56:13.93205:00 Result2: id=bd0bd57d160c11dca66fbb03ffd77ac6 name=TestDocument2(1181340487582) created=20070608T17:08:08.15005:00 Result3: id=1fe9cf04160b11dca66fbb03ffd77ac6 name=TestDocument(1181339794431) created=20070608T16:56:35.02805:00 ====================== Findcontentintherootfolderwithtextlike'sample' Result1: id=bd0bd57d160c11dca66fbb03ffd77ac6 name=TestDocument2(1181340487582) created=20070608T17:08:08.15005:00

AlfrescoDeveloper:WorkingwithCustomContentTypes Page29of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
Result2: id=1fe9cf04160b11dca66fbb03ffd77ac6 name=TestDocument(1181339794431) created=20070608T16:56:35.02805:00 Result3: id=1355e60e160b11dca66fbb03ffd77ac6 name=TestWhitepaper(1181339773331) created=20070608T16:56:13.93205:00 ====================== Findactivecontent Result1: id=bd0bd57d160c11dca66fbb03ffd77ac6 name=TestDocument2(1181340487582) created=20070608T17:08:08.15005:00 Result2: id=1fe9cf04160b11dca66fbb03ffd77ac6 name=TestDocument(1181339794431) created=20070608T16:56:35.02805:00 Result3: id=1355e60e160b11dca66fbb03ffd77ac6 name=TestWhitepaper(1181339773331) created=20070608T16:56:13.93205:00 ====================== Findactivecontentwithaproductequalto'SomePortal' ====================== Findcontentoftypesc:whitepaperpublishedbetween1/1/2006and6/1/2007 Result1: id=1355e60e160b11dca66fbb03ffd77ac6 name=TestWhitepaper(1181339773331) created=20070608T16:56:13.93205:00

Listing22:CommandlineresultsforSomeCoDataQueries.java ThereareacoupleofotherusefultidbitsinthisclassthatI'veleftoutofthisdocumentsuchashowto usetheContentServicetogettheURLforthecontentandhowtheUUIDfortherootfolderis retrieved.Iencourageyoutoexplorethecodethataccompaniesthisguidetoseetheclassinitsentirety.

Deletingcontentprogrammatically
Nowitistimetocleanupafterourselvesbydeletingcontentfromtherepository.Deletingislike searchingexceptthatinsteadofdumpingtheresults,we'llcreateCMLDeleteobjectsforeachresult, andthenwe'llexecutetheCMLtoperformthedelete. AlfrescoDeveloper:WorkingwithCustomContentTypes Page30of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
//Createaqueryobject,lookingforallitemsofaparticulartype Queryquery=newQuery(Constants.QUERY_LANG_LUCENE,"TYPE:\""+ Constants.createQNameString(SomeCoModel.NAMESPACE_SOMECO_CONTENT_MODEL, SomeCoModel.TYPE_SC_DOC)"\""); //Executethequery QueryResultqueryResult=repositoryService.query(storeRef,query,false); //Gettheresultset ResultSetresultSet=queryResult.getResultSet(); ResultSetRow[]rows=resultSet.getRows(); //ifwefoundsomerows,createanarrayofDeleteCMLobjects if(rows!=null){ System.out.println("Found"+rows.length+"objectstodelete."); CMLDelete[]deleteCMLArray=newCMLDelete[rows.length]; for(intindex=0;index<rows.length;index++){ ResultSetRowrow=rows[index]; deleteCMLArray[index]=newCMLDelete(newPredicate(newReference[]{new Reference(storeRef,row.getNode().getId(),null)},null,null)); } //ConstructCMLBlock CMLcml=newCML(); cml.setDelete(deleteCMLArray); //ExecuteCMLBlock UpdateResult[]results= WebServiceFactory.getRepositoryService().update(cml); dumpUpdateResults(results); }//endif

Listing23:CodesnippetfromSomeCoDataCleaner.java Notethatthiscodedeleteseveryobjectintherepositoryoftypesc:docanditschildren.Youwould definitelywanttothinktwiceandcutonceifyouwererunningthiscodeonaproductionrepository, particularlyifyouwereusingabroadcontenttypelikecm:content. Again,yourresultswillvarybasedonthecontentyou'vecreatedbutinmyrepository,runningthecode resultsinthefollowing:


Found2objectstodelete. Command=delete;Source=b6c3f8b012fb11dcab933b56af79ba48;Destination= none Command=delete;Source=d932365a12fb11dcab933b56af79ba48;Destination= none

Listing24:CommandlineresultsforSomeCoDataCleaner.java

AlfrescoDeveloper:WorkingwithCustomContentTypes Page31of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

ecmarchitect.com
Conclusion
ThisarticlehasshownhowtoextendAlfresco'soutoftheboxcontentmodelwithyourownbusiness specificcontenttypes,howtoexposethosetypes,aspects,andpropertiesintheAlfrescowebclient userinterface,andhowtoworkwithcontentviathewebservicesAPIusingbothJavaandPHP.I've throwninafewrecommendationsthatwillhopefullysaveyousometimeoratleastsparksome discussion. There'splentyofadditionaldatamodelrelatedcustomizationstocoverinfuturearticlesincluding custombehavior,custommetadataextractors,customtransformers,andcustomdatarenderers.

Wheretofindmoreinformation

Thecompletesourcecodefortheseexamplesisavailableherefromecmarchitect.com. TheAlfrescoSDKcomeswithasamplesforworkingwithwebservices,theJCR,andthe foundationAPI. TheSearchDocumentationontheAlfrescowikiprovidesqueryexamplesusingbothLucene andXPath. SeeUsingAlfresco'sPHPAPIonecmarchitect.comforpointersongettingAlfrescoworking fromPHP. Fordeploymenthelp,seetheClientConfigurationGuideandPackagingandDeploying ExtensionsintheAlfrescowiki. Forgeneraldevelopmenthelp,seetheDeveloperGuide. Forhelpcustomizingthedatadictionary,seetheDataDictionarywikipage. Ifyouarereadytocovernewground,seetheecmarchitect.comtutorialonCustomActions.

AbouttheAuthor
JeffPottsistheEnterpriseContentManagementPracticeLeadatOptaros,a leadingOpenSourceandNextGenerationInternetconsultancy.Jeffhasnearly fifteenyearsofexperienceimplementingcontentmanagement,collaboration,and otherknowledgemanagementtechnologiesforavarietyofFortune500companies. JefflivesinDallas,Texaswithhiswifeandtwokids.Readmoreat ecmarchitect.com.

AlfrescoDeveloper:WorkingwithCustomContentTypes Page32of32
ThisworkislicensedundertheCreativeCommonsAttributionShare Alike2.5License

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