Agenda
Introduction 
http://www.keensoft.es! 
!!!!!!! 
http://orderofthebee.org! 
Addons committee 
CE 
EE 
eSign! 
LTA! 
RM!
Preliminary thoughts 
From a newbie’s point of view
Alfresco is hard to learn
Some new words (for newbies) 
Aikau! Activiti! 
AMP! 
WebScript! Spring Surf! 
FreeMarker! 
CMIS! Content Model!
Is there another life? 
! 
"With even a few lines of code, you can 
easily add useful functionality to Alfresco." 
! 
(April 4, 2014 by Jeff Potts)!
Disclaimer 
! 
Source code based on Alfresco CE 4.2.f ! 
and available at GitHub ! 
! 
WARNING!! 
Code for teaching purposes. ! 
Don’t try to compile it on your head.!
Countdown 
Real questions from real persons
10. Add a download button 
Motivation. "When I get a shared 
document link by email and click on it, the 
result may not be accessible: sometimes 
the preview is not working, sometimes I 
can only see a simple picture, sometimes 
my computer has no Flash Player... How 
can I get the content of a document 
shared with me?"!
10. Add a download button 
Quick Share!
10. Add a download button 
1. 
2. 
<div class="document-download"> 
<a title="Download" class="simple-link" 
download="${displayName?html}" 
href="${url.context}/proxy/alfresco-noauth/api/internal/ 
shared/node/content/${args.shareId}?a=true">Download</a> 
</div> 
web-extension/site-webscripts/org/alfresco/components/quickshare/node-header.get.html.ftl
10. Add a download button 
2 lines! 
Not all browsers supported *! 
! 
Danger zone! 
Alfresco default FTL! 
should not be ! 
overwritten!! 
! 
web-extension 
FTL 
WebScript 
CE 
5.0.a
9. Site creation ability 
Motivation. "I don't want all the users 
creating sites without control, our 
organization is being directed by our own 
Process Map and no one should create 
content out of its limits."!
9. Site creation ability
9. Site creation ability (share) 
1. 
2. 
3. 
var sitesMenu = widgetUtils.findObject(model.jsonModel, "id", 
"HEADER_SITES_MENU"); 
if (sitesMenu) { 
sitesMenu.config.showCreateSite = user.isAdmin; 
} 
web-extension/site-webscripts/es/keensoft/share/header/share-header.get.js 
4. model.showCreateSite = user.isAdmin; 
web-extension/site-webscripts/es/keensoft/components/dashlets/my-sites.get.js
9. Site creation ability (share) 
<extension> 
<modules> 
<module> 
<id>Hide Create Site</id> 
<customizations> 
<customization> 
<targetPackageRoot>org.alfresco</targetPackageRoot> 
<sourcePackageRoot>es.keensoft</sourcePackageRoot> 
</customization> 
</customizations> 
</module> 
</modules> 
</extension> 
web-extension/site-data/extensions/hide-create-site-extensions.xml
9. Site creation ability (share) 
<!-- Required from 5.0.a --> 
<customization> 
<targetPackageRoot>org.alfresco.share.pages 
</targetPackageRoot> 
<sourcePackageRoot>es.keensoft.share.header 
</sourcePackageRoot> 
<alwaysApply> 
<webscript>share-header</webscript> 
</alwaysApply> 
</customization> 
web-extension/site-data/extensions/hide-create-site-extensions.xml 
C 
E 
>= 
5.0.a 
(aikau)!
9. Site creation ability (repo) 
function main(){ 
var user = people.getPerson(person.properties.userName); 
if (!people.isAdmin(user)) { 
status.setCode(status.STATUS_FORBIDDEN, "error.noPermissions"); 
return; 
} 
config/alfresco/extension/templates/webscripts/org/alfresco/repository/site/ 
sites.post.json.js 
5. 
6. 
7. 
8.
9. Site creation ability 
web-extension 
8 lines! 
No Alfresco repository! 
permissions modified *! 
but! 
repo webscript overridden! 
WebScript 
aikau 
Alfresco JS API 
* http://wiki.alfresco.com/wiki/Site_Service#Controlling_who_can_create_sites 
Other dashlets could be extended: dynamic-welcome, my-meeting-workspaces, my-workspaces
8. Changing document extension 
Motivation. "We are elaborating 
documents on Microsoft Word, once the 
document is closed we are uploading a 
final version in PDF. Surprisingly, final 
document has the same extension as 
original (.DOC) although mimetype is right. 
Users can't open PDF document by 
downloading or by editing online because 
local programs are associated by file 
extension."!
8. Changing document extension
8. Changing document extension 
1. 
2. 
3. 
4. 
5. 
6. 
7. 
public class ContentBehaviour implements 
ContentServicePolicies.OnContentPropertyUpdatePolicy { 
@Override 
public void onContentPropertyUpdate(NodeRef nodeRef, QName propertyQName, 
ContentData beforeValue, ContentData afterValue) { 
if (beforeValue != null && 
!beforeValue.getMimetype().equals(afterValue.getMimetype())) { 
String name = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); 
String nameNoExt = FilenameUtils.getBaseName(name); 
String newExt = mimetypeService.getExtension(afterValue.getMimetype()); 
nodeService.setProperty(nodeRef, ContentModel.PROP_NAME, 
nameNoExt + "." + newExt); 
} 
} 
} 
es.keensoft.alfresco.behaviour.ContentBehaviour.java
8. Changing document extension 
<beans> 
<bean id="${project.artifactId}-ContentBehavior" 
class="es.keensoft.alfresco.behaviour.ContentBehaviour" 
init-method="init"> 
<property name="policyComponent" ref="policyComponent" /> 
<property name="nodeService" ref="NodeService" /> 
<property name="mimetypeService" ref="MimetypeService" /> 
</bean> 
</beans> 
config/alfresco/module/context/service-context.xml
8. Changing document extension 
behavior 
7 lines! 
Alfresco Java 
API
7. Setting create missing person 
Motivation. "I have configured our LDAP 
for identification because I don't want to 
take care of passwords, but I don't want 
everyone on this LDAP to have access to 
Alfresco.!
7. Setting create missing person 
# Some authentication mechanisms may need to create people 
# in the repository on demand. This enables that feature. 
# If disabled an error will be generated for missing 
# people. If enabled then a person will be created and 
# persisted. 
create.missing.people=${server.transaction.allow-writes}! 
! 
From 4.2.d! 
! 
From 4.2! 
CE 
EE
7. Setting create missing person 
1. 
2. 
3. 
4. 
public class CustomSpringBeanPostProcessor implements 
BeanFactoryPostProcessor, ApplicationContextAware { 
@Override 
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 
throws BeansException { 
BeanDefinition bd = beanFactory.getBeanDefinition(beanName); 
bd.getPropertyValues().add(propertyName, propertyValue); 
} 
} 
es.keensoft.alfresco.behaviour.ContentBehaviour.java 
C 
E 
< 
4.2.d 
E 
E 
< 
4.2
7. Setting create missing person 
<beans> 
<bean id="customSpringBeanPostProcessor" 
class="es.keensoft.repo.CustomSpringBeanPostProcessor"> 
<property name="beanName" value="personService" /> 
<property name="propertyName” 
value="createMissingPeople" /> 
<property name="propertyValue" value="false" /> 
</bean> 
</beans> 
config/alfresco/module/context/service-context.xml
7. Setting create missing person 
Spring bean 
post-processor 
4 lines! 
! 
Updating Alfresco properties 
after Spring initialization 
phase!
6. Custom LDAP identification 
Motivation. "We have several LDAP 
branches, one for each mail subdomain, 
but mail attribute is not bindable. The only 
attribute bindable is an UID which can be 
repeated on every branch. How can I 
identify my users?"!
6. Custom LDAP identification 
o=sales! 
uid=peter 
peter@mkt.mail.com!o=mkt! 
uid=peter 
peter@sales.mail.com!
6. Custom LDAP identification 
1. 
2. 
3. 
4. 
5. 
6. 
7. 
8. 
9. 
public class CustomDNLDAPAuthenticationComponentImpl extends 
LDAPAuthenticationComponentImpl { 
protected void authenticateImpl(String userName, char[] password) 
throws AuthenticationException { 
if (userName.indexOf("@") != -1) { 
String mailDomain = userName.substring(userName.indexOf("@") + 1); 
String patchedUserName = userName.substring(0, userName.indexOf("@")); 
String dnPattern = (mailDomain == null ? null : 
mailDomainLdapMapping.get(mailDomain)); 
if (mailDomain != null && dnPattern != null && 
userNameFormat.indexOf(dnPattern) != -1) { 
userName = patchedUserName; 
} 
} 
super.authenticateImpl(userName, password); 
} 
} 
es.keensoft.repo.security.authentication.ldap. 
CustomDNLDAPAuthenticationComponentImpl
6. Custom LDAP identification 
<beans> 
<bean id="authenticationComponent" 
class="es.keensoft.repo.security.authentication.ldap. 
CustomDNLDAPAuthenticationComponentImpl" 
parent="authenticationComponentBase"> 
... 
<!-- mailDomain, targeted DN pattern --> 
<property name="mailDomainLdapMapping"> 
<map> 
<entry key="sales.mail.com" value="o=sales,o=isp"/> 
<entry key="mkt.mail.com" value="o=mkt,o=isp"/> 
</map> 
</property> 
</bean> 
</beans> 
config/alfresco/module/context/service-context.xml
6. Custom LDAP identification 
Alfresco bean 
override 
9 lines! 
! 
Danger zone! 
Alfresco default beans! 
should not be overridden!!
5. PDF/A transformation 
Motivation. "We are required by law to 
expose all our documents in PDF/A 
format"!
5. PDF/A transformation 
<document-formats> 
<document-format><name>Portable Document Format</name> 
<mime-type>application/pdf</mime-type> 
<file-extension>pdf</file-extension> 
<export-filters> 
<entry><family>Presentation</family> 
<string>impress_pdf_Export</string></entry> 
<entry><family>Spreadsheet</family><string>calc_pdf_Export</string></entry> 
<entry><family>Text</family><string>writer_pdf_Export</string></entry> 
</export-filters> 
<export-options> 
<entry><string>SelectPdfVersion</string><int>1</int></entry> 
</export-options> 
</document-format> 
... 
</document-formats> 
config/alfresco/mimetype/openoffice-document-formats.xml
5. PDF/A transformation 
Properties 
override 
0 lines! 
! 
Some additional development 
would be necessary to have 
both (PDF and PDFA) 
transformers available!
4. Importing original dates 
Motivation. "We have large history before 
Alfresco, how can I preserve the records of 
date for every imported document?”!
4. Importing original dates 
1. 
2. 
3. 
4. 
5. 
<model name="ks:custom-model" xmlns="http://www.alfresco.org/model/dictionary/1.0"> 
<imports> 
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d" /> 
</imports> 
<namespaces> 
<namespace uri="http://www.alfresco.com/model/custom-model/1.0" prefix="ks" /> 
</namespaces> 
<aspects> 
<aspect name="ks:importedDoc"> 
<properties> 
<property name="ks:originalCreationDate"> 
<type>d:date</type> 
</property> 
<property name="ks:originalModificationDate"> 
<type>d:date</type> 
</property> 
</properties> 
</aspect> 
</aspects> 
</model> 
config/alfresco/extension/model/custom-model.xml
4. Importing original dates 
<beans> 
<!-- Office 2007+ --> 
<bean id="extracter.Poi” 
class="org.alfresco.repo.content.metadata.PoiMetadataExtracter” 
parent="baseMetadataExtracter"> 
<property name="inheritDefaultMapping"> 
<value>true</value> 
</property> 
<property name="mappingProperties"> 
<props> 
<prop key="namespace.prefix.ks"> 
http://www.alfresco.com/model/custom-model/1.0</prop> 
<prop key="Creation-Date">ks:originalCreationDate</prop> 
<prop key="Last-Modified">ks:originalModificationDate</prop> 
</props> 
</property> 
</bean> 
</beans> 
config/alfresco/extension/custom-model-context.xml
4. Importing original dates 
Content Model 
5 lines! 
! 
Other formats could be 
included (PDF, ODF…)! 
Some other XML config is 
required for Alfresco Share! 
Extracter 
configuration
3. Custom EML node names 
Motivation. "I don't understand Alfresco 
imported mails from IMAP, the names have 
no sense for me."!
3. Custom EML node names 
content 
autoVersionOnUpdateProps, 
autoVersion, initialVersion, versionLabel 
title, author, modified, description 
1 Create node! 
2 Create content! 
3 Create version! 
4 Update props! 
name, node-dbid, store-identifier, 
node-uuid, modified, locale, created, 
store-protocol, creator, modifier 
Properties 
Properties 
Properties 
Properties
3. Custom EML node names 
public class ImapContentBehaviour implements 
NodeServicePolicies.OnUpdatePropertiesPolicy { 
public void onUpdateProperties(NodeRef nodeRef, 
Map<QName, Serializable> before, Map<QName, Serializable> after) { 
if (!before.get(ContentModel.PROP_TITLE).equals( 
after.get(ContentModel.PROP_TITLE))) { 
String intendedName = 
nodeService.getProperty(nodeRef, ContentModel.PROP_TITLE) + ".eml"; 
try { 
fileFolderService.rename(nodeRef, intendedName); 
} catch (Exception e) { // ELM node not renamed... } 
} 
} 
} 
1. 
2. 
3. 
4. 
5. 
6. 
es.keensoft.alfresco.behaviour.ImapContentBehaviour.java
3. Custom EML node names 
<beans> 
<bean id="${project.artifactId}-ContentBehavior" 
class="es.keensoft.alfresco.behaviour.ImapContentBehaviour" 
init-method="init"> 
<property name="policyComponent" ref="policyComponent" /> 
<property name="nodeService" ref="NodeService" /> 
<property name="fileFolderService" ref="FileFolderService"/> 
</bean> 
</beans> 
config/alfresco/module/context/service-context.xml
3. Custom EML node names 
behavior 
6 lines! 
! 
Custom RFC822 transformer 
for HTML could be included ! 
Alfresco Java 
API
2. Site custom properties 
Motivation. "Every site on our company 
belongs to one entity and it’s identified by 
an internal ID, it’s required to set this entity 
on every Alfresco site creation."!
2. Site custom properties (share) 
1. 
2. 
3. 
4. 
<@markup id="custom-properties" target="fields" action="after"> 
<div class="yui-gd"> 
<div class="yui-u first"><label for="${el}-idEntity">Entity:</label></div> 
<div class="yui-u"><input id="${el}-idEntity" type="text" name="idEntity”/></div> 
</div> 
</@markup> 
config/alfresco/web-extension/site-webscripts/es/keensoft/modules/ 
create-site.get.html.ftl
2. Site custom properties (share) 
<extension> 
<modules> 
<module> 
<id>Custom Site Props</id> 
<customizations> 
<customization> 
<targetPackageRoot>org.alfresco</targetPackageRoot> 
<sourcePackageRoot>es.keensoft</sourcePackageRoot> 
</customization> 
</customizations> 
</module> 
</modules> 
</extension> 
web-extension/site-data/extensions/custom-site-prop-extensions.xml
2. Site custom properties (repo) 
5. 
6. 
7. 
8. 
site = siteService.createSite(sitePreset, shortName, title, description, visibility, 
sitetype); 
if (json.has("idEntity")) { 
site.node.properties["stcp:idEntity"] = json.get("idEntity"); 
site.node.save(); 
site.save(); 
} 
model.site = site; 
config/alfresco/extension/templates/webscripts/org/alfresco/repository/site/ 
sites.post.json.js 
2 in a row!
2. Site custom properties 
web-extension 
8 lines! 
! 
Alfresco webscript repo 
extension not available! 
Easy to extend this ! 
sample for site edition! 
FTL 
Alfresco JS API
1. Site templates with folders 
Motivation. "Someone recently asked how 
to create Alfresco Share sites with a 
default folder structure. Currently, out-of-the- 
box, when you create an Alfresco 
Share site, the document library is empty. 
This person instead wanted to define a set 
of folders that would be created in the 
document library when a new Alfresco 
Share site is created."! 
http://ecmarchitect.com/archives/2014/04/04/3687
1. Site templates with folders 
public class ShareDocumentLib implements NodeServicePolicies.OnCreateNodePolicy { 
public void onCreateNode(ChildAssociationRef childAssocRef) { 
String sitePreset = 
nodeService.getProperty(childAssocRef.getChildRef(), PROP_SITE_PRESET); 
String query = "+PATH:"/app:company_home/app:dictionary/app:space_templates/*" + 
@cm:name:"" + sitePreset + """; 
ResultSet rs = 
searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, 
SearchService.LANGUAGE_LUCENE, query); 
NodeRef documentLibrary = 
fileFolderService.copy(rs.getNodeRef(0), childAssocRef.getChildRef(), 
"documentLibrary").getNodeRef(); 
Map<QName, Serializable> props = new HashMap<QName, Serializable>(); 
props.put(ContentModel.PROP_DESCRIPTION, "Document Library"); 
props.put(PROP_SITE_COMPONENT_ID, "documentLibrary"); 
nodeService.addAspect(documentLibrary, ASPECT_SITE_CONTAINER, props); 
} 
} 
1. 
2. 
3. 
4. 
5. 
6. 
7. 
8. 
9. 
10. 
com.ecmarchitect.share.behavior. 
ShareDocumentLibraryFromTemplate.java
1. Site templates with folders 
<beans> 
<bean id="${project.artifactId}_siteBehavior” 
class="com.ecmarchitect.share.behavior.ShareDocumentLib” 
init-method="init"> 
<property name="nodeService” ref="NodeService”/> 
<property name="policyComponent” ref="policyComponent”/> 
<property name="fileFolderService” ref="FileFolderService”/> 
<property name="searchService” ref="SearchService”/> 
</bean> 
</beans> 
config/alfresco/module/share-site-space-templates-repo/context/service-context.xml
1. Site templates with folders 
behavior 
10 lines! 
! 
Jeff Potts article and ! 
GitHub sample available! 
Alfresco Java 
API
What we have learned 
As Alfresco developers
Badges earned 
Content Model 
FTL 
Alfresco JS API 
behavior 
Alfresco Java 
API 
Alfresco source 
aikau 
web-extension 
WebScriptoverride
What should I learn? 
XML! 
JS! 
FTL! 
Java! 
Source code lines (percentage)
Keep it SSO 
Extend! 
Override! 
Overwrite! 
! 
Alfresco source code to the rescue! .! 
The less you write, the better you code . 
Alfresco is full of hooks! .! 
! 
[Open]
Resources 
And some contact data
Resources 
! 
http://github.com/keensoft/alfresco-summit-2014! 
[Enhancements 1-9]! 
!! 
! 
http://ecmarchitect.com/archives/2014/04/04/3687! 
[Enhancement 10]!
Contact 
http://www.keensoft.es! 
! 
http://github.com/keensoft! 
! 
@AngelBorroy! 
! 
http://orderofthebee.org! 
! 
http://angelborroy.wordpress.com!
Summit2014 topic 0066 - 10 enhancements that require 10 lines of code

Summit2014 topic 0066 - 10 enhancements that require 10 lines of code

  • 2.
  • 3.
    Introduction http://www.keensoft.es! !!!!!!! http://orderofthebee.org! Addons committee CE EE eSign! LTA! RM!
  • 4.
    Preliminary thoughts Froma newbie’s point of view
  • 5.
  • 6.
    Some new words(for newbies) Aikau! Activiti! AMP! WebScript! Spring Surf! FreeMarker! CMIS! Content Model!
  • 7.
    Is there anotherlife? ! "With even a few lines of code, you can easily add useful functionality to Alfresco." ! (April 4, 2014 by Jeff Potts)!
  • 8.
    Disclaimer ! Sourcecode based on Alfresco CE 4.2.f ! and available at GitHub ! ! WARNING!! Code for teaching purposes. ! Don’t try to compile it on your head.!
  • 9.
    Countdown Real questionsfrom real persons
  • 10.
    10. Add adownload button Motivation. "When I get a shared document link by email and click on it, the result may not be accessible: sometimes the preview is not working, sometimes I can only see a simple picture, sometimes my computer has no Flash Player... How can I get the content of a document shared with me?"!
  • 11.
    10. Add adownload button Quick Share!
  • 12.
    10. Add adownload button 1. 2. <div class="document-download"> <a title="Download" class="simple-link" download="${displayName?html}" href="${url.context}/proxy/alfresco-noauth/api/internal/ shared/node/content/${args.shareId}?a=true">Download</a> </div> web-extension/site-webscripts/org/alfresco/components/quickshare/node-header.get.html.ftl
  • 13.
    10. Add adownload button 2 lines! Not all browsers supported *! ! Danger zone! Alfresco default FTL! should not be ! overwritten!! ! web-extension FTL WebScript CE 5.0.a
  • 14.
    9. Site creationability Motivation. "I don't want all the users creating sites without control, our organization is being directed by our own Process Map and no one should create content out of its limits."!
  • 15.
  • 16.
    9. Site creationability (share) 1. 2. 3. var sitesMenu = widgetUtils.findObject(model.jsonModel, "id", "HEADER_SITES_MENU"); if (sitesMenu) { sitesMenu.config.showCreateSite = user.isAdmin; } web-extension/site-webscripts/es/keensoft/share/header/share-header.get.js 4. model.showCreateSite = user.isAdmin; web-extension/site-webscripts/es/keensoft/components/dashlets/my-sites.get.js
  • 17.
    9. Site creationability (share) <extension> <modules> <module> <id>Hide Create Site</id> <customizations> <customization> <targetPackageRoot>org.alfresco</targetPackageRoot> <sourcePackageRoot>es.keensoft</sourcePackageRoot> </customization> </customizations> </module> </modules> </extension> web-extension/site-data/extensions/hide-create-site-extensions.xml
  • 18.
    9. Site creationability (share) <!-- Required from 5.0.a --> <customization> <targetPackageRoot>org.alfresco.share.pages </targetPackageRoot> <sourcePackageRoot>es.keensoft.share.header </sourcePackageRoot> <alwaysApply> <webscript>share-header</webscript> </alwaysApply> </customization> web-extension/site-data/extensions/hide-create-site-extensions.xml C E >= 5.0.a (aikau)!
  • 19.
    9. Site creationability (repo) function main(){ var user = people.getPerson(person.properties.userName); if (!people.isAdmin(user)) { status.setCode(status.STATUS_FORBIDDEN, "error.noPermissions"); return; } config/alfresco/extension/templates/webscripts/org/alfresco/repository/site/ sites.post.json.js 5. 6. 7. 8.
  • 20.
    9. Site creationability web-extension 8 lines! No Alfresco repository! permissions modified *! but! repo webscript overridden! WebScript aikau Alfresco JS API * http://wiki.alfresco.com/wiki/Site_Service#Controlling_who_can_create_sites Other dashlets could be extended: dynamic-welcome, my-meeting-workspaces, my-workspaces
  • 21.
    8. Changing documentextension Motivation. "We are elaborating documents on Microsoft Word, once the document is closed we are uploading a final version in PDF. Surprisingly, final document has the same extension as original (.DOC) although mimetype is right. Users can't open PDF document by downloading or by editing online because local programs are associated by file extension."!
  • 22.
  • 23.
    8. Changing documentextension 1. 2. 3. 4. 5. 6. 7. public class ContentBehaviour implements ContentServicePolicies.OnContentPropertyUpdatePolicy { @Override public void onContentPropertyUpdate(NodeRef nodeRef, QName propertyQName, ContentData beforeValue, ContentData afterValue) { if (beforeValue != null && !beforeValue.getMimetype().equals(afterValue.getMimetype())) { String name = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); String nameNoExt = FilenameUtils.getBaseName(name); String newExt = mimetypeService.getExtension(afterValue.getMimetype()); nodeService.setProperty(nodeRef, ContentModel.PROP_NAME, nameNoExt + "." + newExt); } } } es.keensoft.alfresco.behaviour.ContentBehaviour.java
  • 24.
    8. Changing documentextension <beans> <bean id="${project.artifactId}-ContentBehavior" class="es.keensoft.alfresco.behaviour.ContentBehaviour" init-method="init"> <property name="policyComponent" ref="policyComponent" /> <property name="nodeService" ref="NodeService" /> <property name="mimetypeService" ref="MimetypeService" /> </bean> </beans> config/alfresco/module/context/service-context.xml
  • 25.
    8. Changing documentextension behavior 7 lines! Alfresco Java API
  • 26.
    7. Setting createmissing person Motivation. "I have configured our LDAP for identification because I don't want to take care of passwords, but I don't want everyone on this LDAP to have access to Alfresco.!
  • 27.
    7. Setting createmissing person # Some authentication mechanisms may need to create people # in the repository on demand. This enables that feature. # If disabled an error will be generated for missing # people. If enabled then a person will be created and # persisted. create.missing.people=${server.transaction.allow-writes}! ! From 4.2.d! ! From 4.2! CE EE
  • 28.
    7. Setting createmissing person 1. 2. 3. 4. public class CustomSpringBeanPostProcessor implements BeanFactoryPostProcessor, ApplicationContextAware { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { BeanDefinition bd = beanFactory.getBeanDefinition(beanName); bd.getPropertyValues().add(propertyName, propertyValue); } } es.keensoft.alfresco.behaviour.ContentBehaviour.java C E < 4.2.d E E < 4.2
  • 29.
    7. Setting createmissing person <beans> <bean id="customSpringBeanPostProcessor" class="es.keensoft.repo.CustomSpringBeanPostProcessor"> <property name="beanName" value="personService" /> <property name="propertyName” value="createMissingPeople" /> <property name="propertyValue" value="false" /> </bean> </beans> config/alfresco/module/context/service-context.xml
  • 30.
    7. Setting createmissing person Spring bean post-processor 4 lines! ! Updating Alfresco properties after Spring initialization phase!
  • 31.
    6. Custom LDAPidentification Motivation. "We have several LDAP branches, one for each mail subdomain, but mail attribute is not bindable. The only attribute bindable is an UID which can be repeated on every branch. How can I identify my users?"!
  • 32.
    6. Custom LDAPidentification o=sales! uid=peter peter@mkt.mail.com!o=mkt! uid=peter peter@sales.mail.com!
  • 33.
    6. Custom LDAPidentification 1. 2. 3. 4. 5. 6. 7. 8. 9. public class CustomDNLDAPAuthenticationComponentImpl extends LDAPAuthenticationComponentImpl { protected void authenticateImpl(String userName, char[] password) throws AuthenticationException { if (userName.indexOf("@") != -1) { String mailDomain = userName.substring(userName.indexOf("@") + 1); String patchedUserName = userName.substring(0, userName.indexOf("@")); String dnPattern = (mailDomain == null ? null : mailDomainLdapMapping.get(mailDomain)); if (mailDomain != null && dnPattern != null && userNameFormat.indexOf(dnPattern) != -1) { userName = patchedUserName; } } super.authenticateImpl(userName, password); } } es.keensoft.repo.security.authentication.ldap. CustomDNLDAPAuthenticationComponentImpl
  • 34.
    6. Custom LDAPidentification <beans> <bean id="authenticationComponent" class="es.keensoft.repo.security.authentication.ldap. CustomDNLDAPAuthenticationComponentImpl" parent="authenticationComponentBase"> ... <!-- mailDomain, targeted DN pattern --> <property name="mailDomainLdapMapping"> <map> <entry key="sales.mail.com" value="o=sales,o=isp"/> <entry key="mkt.mail.com" value="o=mkt,o=isp"/> </map> </property> </bean> </beans> config/alfresco/module/context/service-context.xml
  • 35.
    6. Custom LDAPidentification Alfresco bean override 9 lines! ! Danger zone! Alfresco default beans! should not be overridden!!
  • 36.
    5. PDF/A transformation Motivation. "We are required by law to expose all our documents in PDF/A format"!
  • 37.
    5. PDF/A transformation <document-formats> <document-format><name>Portable Document Format</name> <mime-type>application/pdf</mime-type> <file-extension>pdf</file-extension> <export-filters> <entry><family>Presentation</family> <string>impress_pdf_Export</string></entry> <entry><family>Spreadsheet</family><string>calc_pdf_Export</string></entry> <entry><family>Text</family><string>writer_pdf_Export</string></entry> </export-filters> <export-options> <entry><string>SelectPdfVersion</string><int>1</int></entry> </export-options> </document-format> ... </document-formats> config/alfresco/mimetype/openoffice-document-formats.xml
  • 38.
    5. PDF/A transformation Properties override 0 lines! ! Some additional development would be necessary to have both (PDF and PDFA) transformers available!
  • 39.
    4. Importing originaldates Motivation. "We have large history before Alfresco, how can I preserve the records of date for every imported document?”!
  • 40.
    4. Importing originaldates 1. 2. 3. 4. 5. <model name="ks:custom-model" xmlns="http://www.alfresco.org/model/dictionary/1.0"> <imports> <import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d" /> </imports> <namespaces> <namespace uri="http://www.alfresco.com/model/custom-model/1.0" prefix="ks" /> </namespaces> <aspects> <aspect name="ks:importedDoc"> <properties> <property name="ks:originalCreationDate"> <type>d:date</type> </property> <property name="ks:originalModificationDate"> <type>d:date</type> </property> </properties> </aspect> </aspects> </model> config/alfresco/extension/model/custom-model.xml
  • 41.
    4. Importing originaldates <beans> <!-- Office 2007+ --> <bean id="extracter.Poi” class="org.alfresco.repo.content.metadata.PoiMetadataExtracter” parent="baseMetadataExtracter"> <property name="inheritDefaultMapping"> <value>true</value> </property> <property name="mappingProperties"> <props> <prop key="namespace.prefix.ks"> http://www.alfresco.com/model/custom-model/1.0</prop> <prop key="Creation-Date">ks:originalCreationDate</prop> <prop key="Last-Modified">ks:originalModificationDate</prop> </props> </property> </bean> </beans> config/alfresco/extension/custom-model-context.xml
  • 42.
    4. Importing originaldates Content Model 5 lines! ! Other formats could be included (PDF, ODF…)! Some other XML config is required for Alfresco Share! Extracter configuration
  • 43.
    3. Custom EMLnode names Motivation. "I don't understand Alfresco imported mails from IMAP, the names have no sense for me."!
  • 44.
    3. Custom EMLnode names content autoVersionOnUpdateProps, autoVersion, initialVersion, versionLabel title, author, modified, description 1 Create node! 2 Create content! 3 Create version! 4 Update props! name, node-dbid, store-identifier, node-uuid, modified, locale, created, store-protocol, creator, modifier Properties Properties Properties Properties
  • 45.
    3. Custom EMLnode names public class ImapContentBehaviour implements NodeServicePolicies.OnUpdatePropertiesPolicy { public void onUpdateProperties(NodeRef nodeRef, Map<QName, Serializable> before, Map<QName, Serializable> after) { if (!before.get(ContentModel.PROP_TITLE).equals( after.get(ContentModel.PROP_TITLE))) { String intendedName = nodeService.getProperty(nodeRef, ContentModel.PROP_TITLE) + ".eml"; try { fileFolderService.rename(nodeRef, intendedName); } catch (Exception e) { // ELM node not renamed... } } } } 1. 2. 3. 4. 5. 6. es.keensoft.alfresco.behaviour.ImapContentBehaviour.java
  • 46.
    3. Custom EMLnode names <beans> <bean id="${project.artifactId}-ContentBehavior" class="es.keensoft.alfresco.behaviour.ImapContentBehaviour" init-method="init"> <property name="policyComponent" ref="policyComponent" /> <property name="nodeService" ref="NodeService" /> <property name="fileFolderService" ref="FileFolderService"/> </bean> </beans> config/alfresco/module/context/service-context.xml
  • 47.
    3. Custom EMLnode names behavior 6 lines! ! Custom RFC822 transformer for HTML could be included ! Alfresco Java API
  • 48.
    2. Site customproperties Motivation. "Every site on our company belongs to one entity and it’s identified by an internal ID, it’s required to set this entity on every Alfresco site creation."!
  • 49.
    2. Site customproperties (share) 1. 2. 3. 4. <@markup id="custom-properties" target="fields" action="after"> <div class="yui-gd"> <div class="yui-u first"><label for="${el}-idEntity">Entity:</label></div> <div class="yui-u"><input id="${el}-idEntity" type="text" name="idEntity”/></div> </div> </@markup> config/alfresco/web-extension/site-webscripts/es/keensoft/modules/ create-site.get.html.ftl
  • 50.
    2. Site customproperties (share) <extension> <modules> <module> <id>Custom Site Props</id> <customizations> <customization> <targetPackageRoot>org.alfresco</targetPackageRoot> <sourcePackageRoot>es.keensoft</sourcePackageRoot> </customization> </customizations> </module> </modules> </extension> web-extension/site-data/extensions/custom-site-prop-extensions.xml
  • 51.
    2. Site customproperties (repo) 5. 6. 7. 8. site = siteService.createSite(sitePreset, shortName, title, description, visibility, sitetype); if (json.has("idEntity")) { site.node.properties["stcp:idEntity"] = json.get("idEntity"); site.node.save(); site.save(); } model.site = site; config/alfresco/extension/templates/webscripts/org/alfresco/repository/site/ sites.post.json.js 2 in a row!
  • 52.
    2. Site customproperties web-extension 8 lines! ! Alfresco webscript repo extension not available! Easy to extend this ! sample for site edition! FTL Alfresco JS API
  • 53.
    1. Site templateswith folders Motivation. "Someone recently asked how to create Alfresco Share sites with a default folder structure. Currently, out-of-the- box, when you create an Alfresco Share site, the document library is empty. This person instead wanted to define a set of folders that would be created in the document library when a new Alfresco Share site is created."! http://ecmarchitect.com/archives/2014/04/04/3687
  • 54.
    1. Site templateswith folders public class ShareDocumentLib implements NodeServicePolicies.OnCreateNodePolicy { public void onCreateNode(ChildAssociationRef childAssocRef) { String sitePreset = nodeService.getProperty(childAssocRef.getChildRef(), PROP_SITE_PRESET); String query = "+PATH:"/app:company_home/app:dictionary/app:space_templates/*" + @cm:name:"" + sitePreset + """; ResultSet rs = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_LUCENE, query); NodeRef documentLibrary = fileFolderService.copy(rs.getNodeRef(0), childAssocRef.getChildRef(), "documentLibrary").getNodeRef(); Map<QName, Serializable> props = new HashMap<QName, Serializable>(); props.put(ContentModel.PROP_DESCRIPTION, "Document Library"); props.put(PROP_SITE_COMPONENT_ID, "documentLibrary"); nodeService.addAspect(documentLibrary, ASPECT_SITE_CONTAINER, props); } } 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. com.ecmarchitect.share.behavior. ShareDocumentLibraryFromTemplate.java
  • 55.
    1. Site templateswith folders <beans> <bean id="${project.artifactId}_siteBehavior” class="com.ecmarchitect.share.behavior.ShareDocumentLib” init-method="init"> <property name="nodeService” ref="NodeService”/> <property name="policyComponent” ref="policyComponent”/> <property name="fileFolderService” ref="FileFolderService”/> <property name="searchService” ref="SearchService”/> </bean> </beans> config/alfresco/module/share-site-space-templates-repo/context/service-context.xml
  • 56.
    1. Site templateswith folders behavior 10 lines! ! Jeff Potts article and ! GitHub sample available! Alfresco Java API
  • 57.
    What we havelearned As Alfresco developers
  • 58.
    Badges earned ContentModel FTL Alfresco JS API behavior Alfresco Java API Alfresco source aikau web-extension WebScriptoverride
  • 59.
    What should Ilearn? XML! JS! FTL! Java! Source code lines (percentage)
  • 60.
    Keep it SSO Extend! Override! Overwrite! ! Alfresco source code to the rescue! .! The less you write, the better you code . Alfresco is full of hooks! .! ! [Open]
  • 61.
    Resources And somecontact data
  • 62.
    Resources ! http://github.com/keensoft/alfresco-summit-2014! [Enhancements 1-9]! !! ! http://ecmarchitect.com/archives/2014/04/04/3687! [Enhancement 10]!
  • 63.
    Contact http://www.keensoft.es! ! http://github.com/keensoft! ! @AngelBorroy! ! http://orderofthebee.org! ! http://angelborroy.wordpress.com!