Alfresco Share 5, using “authority.ftl” form control

Last week I was designing an Alfresco Content Model including a property to gather a set of Alfresco users to be sent to an external service.

I remembered about authority.ftl form control, which is a picker that could fit fine with my requirements.

I searched about the control at official documentation

… and also in the Community.

After some research inspecting Alfresco source code, I realised that this control is auto-configured depending on the definition of the property to be rendered.


   picker.setOptions(
   {
      itemType: "${field.endpointType}",
      multipleSelectMode: ${field.endpointMany?string},
      itemFamily: "authority"
   });

So to include a form control to select MANY USERS following content model should be declared in Repository part.

<aspect name="ks:defaultSigners">
	<associations>
		<association name="ks:alfrescoUser">
			<source>
				<mandatory>false</mandatory>
				<many>true</many>
			</source>
			<target>
				<class>cm:person</class>
				<mandatory>false</mandatory>
				<many>true</many>
			</target>
		</association>
	</associations>
</aspect>

And a simple form control association in Share part makes the work.

<field id="ks:alfrescoUser">
    <control template="/org/alfresco/components/form/controls/authority.ftl"/>
</field>

Also relevant for this use case the addon provided by Douglas C. R. Paes at Alfresco Colleagues Picker Form.

Sometimes a small investigation before developing your tasks will save you many time and will produce an easier solution.

Anuncios

How to develop a SOAP client from Alfresco Repository

Due to integration requirements with legacy old systems, it happens that Alfresco Repository must invoke an external SOAP endpoint. As Alfresco Repository web application includes a changing JAR Library catalog, using an out-of-the-box implementation to build SOAP clients is advisable.

The target scenario

I selected a sample Global Weather SOAP Web Service endpoint available at

http://www.webservicex.net/globalweather.asmx?WSDL 

Starting from WSDL is always a must when build a SOAP Web Service for both client and server parts.

Alfresco repository will invoke this endpoint from a Web Script to proxy parameters and results in a REST invocation.

Creating an standard JAXWS client project

Source code available at https://github.com/angelborroy/alfresco-soap-client/tree/master/global-weather-wsdl-client

Once the project is compiled with Maven, client Java classes are packaged into the target JAR. In order to use this client, simple invocations can be done.

GlobalWeather service = new GlobalWeather(new URL("http://www.webservicex.com/globalweather.asmx?WSDL"));
String cities = service.getGlobalWeatherSoap().getCitiesByCountry("Spain");

In order to make the JAR available to Maven for next step, an installation is required.

$ mvn clean install

Creating an Alfresco Repository Module for Web Scripts

Source code available at https://github.com/angelborroy/alfresco-soap-client/tree/master/soap-client-repo

As JAXWS client does not include any external dependency, the library can be included directly in pom.xml file.

Web Scripts are defined in a standard way:

SOAP Endpoint URL has been declared as property in alfresco-global.properties to make easier changes in the configuration.

Inside Java controller implementation, SOAP JAXWS standard client is invoked:

String country = req.getServiceMatch().getTemplateVars().get("country");

GlobalWeather service = new GlobalWeather(new URL(url));
String cities = service.getGlobalWeatherSoap().getCitiesByCountry(country);

Map<String, Object> model = new HashMap<String, Object>();
model.put("cities", cities);
return model;

Testing

Once the project is deployed or even using run.sh script from Maven project, SOAP services can be accessed by using Alfresco REST API.

http://localhost:8080/alfresco/s/globalweather/Spain/getcities

http://localhost:8080/alfresco/s/globalweather/Spain/Zaragoza/getweather

Including new features inside Alfresco Repository is not a complex project, but using the simplest way is always worthy: upgrading and updating processes will be as safe as the are!

 

How to create a Site for Alfresco using Java API

Some time ago, Site creation in Alfresco was driven by Share web application. So using a simple SiteService.createSite from repository Java API is not enough to provide access to the site from Share.

Below some useful snippets are listed.

Creating the Site

// Default site preset
String sitePreset = "site-dashboard";
SiteInfo siteInfo = siteService.createSite(sitePreset,
    "test-site", 
    "Test Site",
    "Test Site description", 
    SiteVisibility.PRIVATE);

Adding site members

Including at least a Manager is advisable.

siteService.setMembership("test-site", 
    "admin",
    SiteRole.SiteManager.toString());

Creating dashboard

This step is required to provide access from Share.

private void createDefaultDashboard(SiteInfo siteInfo) {
    
    NodeService nodeService = serviceRegistry.getNodeService();
    FileFolderService fileFolderService = serviceRegistry.getFileFolderService();
    ContentService contentService = serviceRegistry.getContentService();
    
    FileInfo surfConfig = fileFolderService.create(siteInfo.getNodeRef(), "surf-config", ContentModel.TYPE_FOLDER);
    Map<QName, Serializable> properties = new HashMap<QName, Serializable>();
    properties.put(ContentModel.PROP_CASCADE_HIDDEN, Boolean.TRUE);
    properties.put(ContentModel.PROP_CASCADE_INDEX_CONTROL, Boolean.TRUE);
    nodeService.addAspect(surfConfig.getNodeRef(), ContentModel.ASPECT_HIDDEN, properties);
    // Hint from Bertrand Forest
    properties = new HashMap<QName, Serializable>();
    properties.put(ContentModel.PROP_IS_INDEXED, Boolean.FALSE);
    properties.put(ContentModel.PROP_IS_CONTENT_INDEXED, Boolean.FALSE);
    nodeService.addAspect(surfConfig.getNodeRef(), ContentModel.ASPECT_INDEX_CONTROL, properties);
    
    FileInfo pages = fileFolderService.create(surfConfig.getNodeRef(), "pages", ContentModel.TYPE_FOLDER);
    FileInfo site = fileFolderService.create(pages.getNodeRef(), "site", ContentModel.TYPE_FOLDER);
    FileInfo siteName = fileFolderService.create(site.getNodeRef(), siteInfo.getShortName(), ContentModel.TYPE_FOLDER);
    
    Map<QName, Serializable> props = new HashMap<QName, Serializable>(1);
    props.put(ContentModel.PROP_NAME, "dashboard.xml");  

    NodeRef node = nodeService.createNode(
                            siteName.getNodeRef(), 
                        ContentModel.ASSOC_CONTAINS, 
                        QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "dashboard.xml"),
                        ContentModel.TYPE_CONTENT, 
                        props).getChildRef();
                        
    ContentWriter writer = contentService.getWriter(node, ContentModel.PROP_CONTENT, true);
    writer.setMimetype(MimetypeMap.MIMETYPE_XML);
    writer.setEncoding("UTF-8");
    // TODO Create dashboard.xml file by using an external file resource instead of a hand-coded String
    writer.putContent("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + 
            "<page>\n" + 
            "      <title>Collaboration Site Dashboard</title>\n" + 
            "      <title-id>page.siteDashboard.title</title-id>\n" + 
            "      <description>Collaboration site's dashboard page</description>\n" + 
            "      <description-id>page.siteDashboard.description</description-id>\n" + 
            "      <authentication>user</authentication>\n" + 
            "      <template-instance>dashboard-2-columns-wide-left</template-instance>\n" + 
            "      <properties>\n" + 
            "        <sitePages>[{\"pageId\": \"documentlibrary\"}]</sitePages>\n" + 
            "      <theme/><dashboardSitePage>true</dashboardSitePage></properties>\n" + 
            "    <page-type-id>generic</page-type-id></page>");
    
}

Creating Site containers

There are different containers that should be created (wiki, forum,…) but having a Document Library is required for many use cases.

NodeRef documentLibraryNodeRef =  siteService.createContainer(siteInfo.getShortName(), SiteService.DOCUMENT_LIBRARY, null, null);

Some operations are not described in Alfresco documentation, but they can be guessed inspecting internal structure of the repository.