Skip navigation
All Places > Alfresco Content Services (ECM) > Blog > 2016 > May
2016

The content of this post is mostly based on the talk I gave at the amazing BeeCon 2016 conference, in Brussels.

Alfresco One 5.1, released in March 2016, introduced a separation in the packaging between Alfresco One Platform and Alfresco One Share. I want to explain a bit behind the motivations for this; how we did it and what's in it for you.

Why separate?


The main reason was to have different release lifecycles between Platform and Share. That means we can now, for instance, release versions of Share more often, without having to retest the whole Platform (which is very costly with all the databases, application servers, etc.)

The releases are also smaller and more incremental. We hope it makes it easier to upgrade the Platform while keeping a heavily modified Share: this seems to be a common use case!

From a more 'Alfresco Engineering' point-of-view, there are more reasons to split: the combined codebase was huge, and we couldn't really assign it to a single team. Now, we have reorganised our teams, and we have proper Platform and Share teams, each taking responsibility for their own codebase. Ownership is simpler, the issue backlog is smaller, and we are overall more agile.

Where to cut?


We needed to decide where to draw the line between Platform and Share code.

An initial idea was to create one codebase for each artifact (jar or war): alfresco-core.jar, alfresco-data-model.jar, etc. But that would have been a bit too many to handle.

The most obvious was to simply cut between the two war files: alfresco.war and share.war. However, that meant that we couldn't update the Share APIs (webscripts, etc.) when we update Share, since they are actually delivered in alfresco.war.

We got inspiration from this blog post from Netflix. The idea is explained in this picture, which I have shamelessly reused here:

Slide04

Netflix has to support a lot of very different devices: browsers, Wii, XBox, iPhones, ...

To add some flexibility to their server-side code, they created this notion of Client Adapter Code. It is delivered at the same time as the client, but actually sits on the server-side, allowing each client to evolve and manage their own API within their own lifecycle. This is exactly what we needed!

The Share codebase therefore produces a Platform-side component, called Share Services, which is packaged as an AMP. It contains the /slingshot webscripts, the sample site bootstrap, and the datalist and wiki services. The alfresco-share-services.amp is applied to alfresco.war: its source is in Share, but it runs in the Platform.

SeparatingShare-shareservices

Separating common pieces


Some components are used by both Platform and Share. We needed to separate each of them into their own codebase too, otherwise we couldn't release Share without releasing Platform. So we extracted these components, and now they have their own lifecycle:

       
    • alfresco-core  (which should really be called alfresco-common as it's a set of utility classes.)
       
    • JLan (our implementation of the CIFS protocol)

 


All these are kept in the same Subversion server. This is much more convenient when we need to merge fixes from old branches to and from 5.1. We may move these to Git in the future.

Of course, we also had to cut a few cyclic dependencies, but it wasn't too bad...

What do we package?

Community Edition


We had discussions on whether we should provide Platform and Share packages for the Community Edition. We decided not to, because Community is typically used on single node installations. Also, it's quite often an entry point for people researching Alfresco, so we wanted to keep things simple there. (I had people complaining about this at BeeCon, but I'm still looking for a valid use case...)

Since it will contain components with different versions, we had to find a different versioning convention. We settled on a version based on the year and month: 201605. We also add a -GA (General Availability) or -EA (Early Access) suffix, to make clear the level of testing it had. For instance, the latest version of Community is called 201605-GA, and it contains:

       
    • Alfresco Platform 5.1.g (a security issue fix from 5.1.f)

 

    • Alfresco Share 5.1.f

 

    • GoogleDocs integration 3.0.3

 

    • Alfresco Office Services 1.1


Share separation does benefit Community Edition though, because we can ship more often, and include only what has changed, as with 201605-GA

Enterprise Edition


For clusters and complex setups, as supported by Alfresco One, it is useful to install Platform and Share separately, so we have provided separate installers for each of them. The Share one installs a separate Tomcat instance and just deploys share.war in it. It also copies the Share Services AMP into the installation folder, but you will need to apply it yourself to the Platform installation it connects to - see the documentation.

We thought a 'full' installer, with both Platform and Share, was still needed. It is convenient for people who want to try Alfresco without knowing much about its architecture, or simply for standalone instances. The version of this all-in-one installer for Alfresco One still reflects the version number of the Platform, i.e. 5.1. We are waiting to see what people think of the Community Edition naming before we change the naming for Alfresco One.

In summary, here are the different packages we offer for the Enterprise Edition:

SeparatingShare-overview

What next?


We didn't go as far as we had planned. For 5.1.x, the source codebases are separated, but we are still going to release Platform and Share at the same time, with the same version number. We lacked time to test each component individually. Previously, Alfresco could be tested as a whole and it was fine. For instance, JLan was mostly tested by integration tests in the Platform. Now, we need to make sure JLan works on its own, before we release it, because we may have to release it just for a Share version, without running the Platform tests against it.

We also didn't think we could ensure that Platform 5.1.X would work with Share 5.1.Y - once again due to the lack of tests and of clear public APIs between the components. (Although this improved in 5.1 – see the BCK project for instance, or the documentation on extension points.)

In the near future, we are planning to separate more components from the Platform. The most likely candidates are currently:
 

    • Search, as part of the upgrade to Solr 6
       
    • More of Share: there are parts of the Platform that we think should belong to Share, such as calendars, blogs, ... And we still want to release it separately in the future!
       
    • Enterprise extensions, to upgrade from a Community Edition


I'm not making promises of course, but this is on the roadmap, so it looks good!

Introduction

In previous blog posts I’ve shown examples of how the Aikau version of the Document Library can be used both within Share and in standalone clients. In this blog post I thought it might be useful to provide a more in-depth guide on how to add the Aikau Document Library as a site page in Share, as well as to provide a general status update on progress.

 

Background

It’s important to understand that the Aikau team is a “Component Team” and not a “Feature Team”. This means that we provide the components (in this case Aikau widgets and services) for other teams to deliver features or products.

 

At the time of writing there is no feature team driving the replacement of the old YUI2 Document Library with the Aikau version - nor indeed has there ever been. Aikau has always predominantly been about re-use and the Document Library has progressed because other features have required Document Library related widgets.

 

There are two epics in JIRA that list the current tasks required to achieve feature parity with the YUI2 Document Library: one for general features and one for action support. Reviewing these epics should give an overview of what work remains.

 

In general the Document Library is quite functional and should be more than adequate for covering a large number of use cases. The following steps and code is available within the following GitHub repository and all of the files described before are linked to the specific files in that repository.

 

Create the Aikau Document Library Page

The first step is to create the WebScripts for the new page. Create the WebScript descriptor file…

DocLib.get.desc.xml

<webscript>
  <shortname>Document Library Example </shortname>
  <description>This provides an example of building the standard Document Library using the doclib.lib.js library file.</description>
  <family>Aikau</family>
  <url>/DocLib</url>
</webscript>

 

Now let’s create the template…

DocLib.get.html.ftl

<@processJsonModel/>

 

The properties file is slightly more involved…

DocLib.get.properties

surf.include.resources=org/alfresco/share/imports/share-header.lib,org/alfresco/aikau/{aikauVersion}/libs/doclib/doclib.lib


This file just contains an instruction to import two other properties files:

    • org/alfresco/share/imports/share-header.lib 
    • org/alfresco/aikau/{aikauVersion}/libs/doclib/doclib.lib


This process is described in more detail in this previous blog post.

 

The last WebScript file required is the JavaScript controller… this is where most of the code goes:

 

DocLib.get.js

The first thing is to import the required controller files…

<import resource='classpath:/alfresco/site-webscripts/org/alfresco/share/imports/share-header.lib.js'>
<import resource='classpath:/alfresco/site-webscripts/org/alfresco/share/imports/share-footer.lib.js'>
<import resource='classpath:alfresco/site-webscripts/org/alfresco/aikau/{aikauVersion}/libs/doclib/doclib.lib.js'>

 

Now you need to import the services and widgets that the header uses…

var services = getHeaderServices();
var widgets = getHeaderModel(msg.get('aikau.doclib.title'));

 

The header services need to be combined with the services required by the Document Library. These can be retrieved by calling getDocumentLibraryServices.

services = services.concat(getDocumentLibraryServices());

 

Now use the getDocLib function to create the model for the Document Library. The main data to provide is the site id which is available from a template argument and the name of the folder in which the Document Library files can be found within the site folder (typically “documentLibrary”).

var docLib = getDocLib({
  siteId: page.url.templateArgs.site,
  containerId: 'documentLibrary'
});

 

This model needs to be added to the header model....

widgets.push(docLib);

 

Finally we need to call getFooterModel passing in the header and Document Library services and widgets. This is required because the footer model wraps everything else on the page.

model.jsonModel = getFooterModel(services, widgets);

 

Make the Document Library a Site Page

In this previous post I describe the process for adding Aikau pages in general - so please refer to that for a slightly more in-depth description of the process.

 

Create a new Surf Page definition that will provide the title and description of the page as shown when customizing sites.

AikauDocLib.xml

<?xml version='1.0' encoding='UTF-8'?>
<page>
  <title>Aikau Document Library</title>
  <description>The Aikau Document Library</description>
</page>

 

Now create an extension module to ensure that the site page is an option when customizing sites.

Aikau-document-library.xml

<extension>
  <modules>
    <module>
      <id>Aikau Document Library Site Page</id>
      <auto-deploy>true</auto-deploy>
      <evaluator type='default.extensibility.evaluator'/>
      <configurations>
        <config evaluator='string-compare' condition='SitePages' replace='false'>
          <pages>
            <page id='AikauDocLib'>dp/ws/DocLib</page>
          </pages>
        </config>
      </configurations>
    </module>
  </modules>
</extension>

 

Now you need to pull these files together into a single JAR and copy them to the “share/WEB-INF/lib” folder.

AikauDocLib4

 

Restart Share and go to any site and customize it - you should see the “Aikau Document Library” in the list of available pages:

 

AikauDocLib1

 

Drag it into the “Current Site Pages” list and click “OK”

 

AikauDocLib2

 

Now you should have a new link for the “Aikau Document Library” (if you have a lot of site pages, try looking under the “More” drop-down menu!). Click on the link and you’ll be taken to your the Aikau Document Library:

 

AikauDocLib3

 

 

What Are The Benefits and Limitations?

One of the main reasons for creating an Aikau version of the Document Library was to make customization much simpler. Through the use of the Aikau model it is significantly easier to make fine-grained changes to the Document Library. Some things you could do for example would be:

    • Add custom views 
    • Edit existing views (add or remove the metadata that is displayed) 
    • Filter the displayed actions or add entirely new actions 
    • Render only specific sections of the Document Library (the tree, breadcrumb trail or list for example) 
    • Edit the menu options that are displayed


The Aikau version also has a few features that are not included with the original Document Library: 

    • Drag-and-drop upload new version 
    • Inline document preview 
    • Inline commenting 
    • Inline content creation


As of Aikau 1.0.68 the Document Library uses a new non-modal upload file design. See screenshot below:

AikauDocLib5

 

The main limitation is that there is not yet support for all the actions that you would find in the existing Document Library (you can review the missing actions in the previously linked epic).

 

The other potential limitation is the lack of integration with the forms runtime. This means that when editing properties for custom types the XML forms configuration will not be represented.

 

Despite these issues the Aikau Document Library should still be useful for many use cases - especially where serious customization of the existing Document Library is required.

We've been discussing REST APIs at Alfresco for a while now. If you haven't already seen Gavin Cornwell's Tech Talk Live session 'The Future of REST APIs'  I highly recommend you check it out now.

It's hard to sum up Gavin's presentation, but the gist is that we are working on a new set of REST APIs that all follow a common set of guidelines. They are consistent, well tested, versioned which makes them extremely easy to consume. We call these 'the v1 REST APIs', however this concept is not new. We introduced the foundation for these APIs back in 4.2 along with our Cloud offering. The idea back then was to fill the missing features of CMIS so a combination of these APIs + CMIS would give you everything you need. These APIs have been documented in our official docs, however they were a bit hard to navigate and hard to consume.

This raises the question of how do we do API documentation in an efficient way. OpenAPI Specification (formerly known as Swagger) seems to do the trick in terms of describing our APIs. The cherry on the top is Swagger UI - a cool project that generates interactive documentation from an OpenAPI Specification - that even allows you to test out the APIs directly from the API Explorer.

We've put a lot of work into documenting our existing API's, which is what we're releasing today. The new 'v1 APIs' are now marked up with OpenAPI Specification (you can find the source here). We do realise that these API's don't look like much - but as mentioned above, the scope of the existing APIs was to fill the gap.

As we're adopting an 'API First' approach, we're also moving to a 'contract first' approach. This means that before we start coding any APIs we mock them up in OpenAPI Specifications, then have a review. Once the review is complete and we have ensured it conforms to our standards and guidelines we will start the work. We hope to see proposals for new APIs following this approach in the future.

We've made much progress on additional APIs, and we're continuing to invest heavily in stable, rich and powerful REST APIs this year. We already have a great set of APIs that cover file and folder operations, trashcan, renditions, live search, ticket authentication and site creation. We're adding new APIs every week and we're hoping to release a great set of APIs to community edition over the next couple of months.

We are working on a 'bleeding edge' version of the REST API Explorer - this will be a snapshot of the latest development. We hope this will be a great tool for our developer ecosystem to test the API's and provide feedback. We don't currently have a fixed date for the 'bleeding edge' version, but we will make sure to make some noise when it's ready. In case you can’t wait, you can clone the REST API Explorer source on GitHub and check out the “develop” branch. Run “mvn tomcat7:run-war” to get it up and running, however you will not be able to interact with the APIs.

You can find the online version of the REST API Explorer here: https://api-explorer.alfresco.com. It also comes bundled with the Alfresco SDK 2.2, be sure to check it out!

To report a bug please open an issue in the ALF project over at http://issues.alfresco.com, please set the “REST API” component if possible.

By Joe Emett

In this post, I’d like to give some tips on some useful JIRA Query Language (JQL) queries.  By no means exhaustive, they are things I’ve come to use and love in Jira.  It will focus on getting started with JQL and is not meant as a Jira tutorial.  Syntax rules are outside the boundaries of this blog.  Rather than giving descriptions on JQL keywords I hope this acts as a quick-start guide for you to build your own queries.

These tips assume the user is able to navigate to a Jira project backlog and range from rookie to more seasoned advice.

1.  Create your own JQL with 2 character presses


In the top right of any Jira screen is the QuickSearch text box:

1QuickSearch

When you enter my, you will redirected to the Issue Navigator and be presented with Jira issues that are currently assigned to you.

You can then tweak your JQL to refine the set of results, for example if you want just the open issues you could modify the query by including AND status = 'Open' :

1CreateYourOwnJQL

You will find other smart query shortcuts here, at the very least QuickSearch offers fast access to the Issue Navigator and by entering free text into QuickSearch, this will search all Jira projects for instances of the text in these fields: Summary, Description and Comments.

This blog now assumes you are comfortable with accessing the Issue Navigator.

2.  Build on what is there already


Every backlog has an associated Jira query.  If you have access to a Jira project backlog, you are looking at the results of a JQL query.  On every Jira Scrum board, there is a ‘Board’ dropdown list:

BoardConfigure

On pressing ‘Configure’, on the ‘General’ tab there are the filter details the board is based on:2FilterQuery

Even though you may not have privileges to edit the query, you will be able to press View Filter Query, change the JQL query and Save as, as you wish.

Jira Reports and Gadgets on Dashboards, are other good places to look, as these are based on existing JQL queries you can dip into and change.

Look out for View in Issue Navigator to see more JQL.

3.  Just created an issue and you’ve forgotten the Key?


3ForgottenKey

You’ve created an issue, gone and made a cup of coffee and can’t remember the Key, and it’s not on your board.

In the Issue Navigator enter:

project = ACE and created >= startOfDay() ORDER BY Key DESC3OrderKeyDescending

4.  Use Empty to find forgotten issues

Find issues with no fix version.  In the Issue Navigator enter:

project in (ACE, MNT) AND status = Open AND fixVersion is EMPTY ORDER BY created DESC4Empty

5.  Commented by a colleague


You know the project and the colleague who commented on an issue, you’re just not sure which issue. You think the comment was made in the last 30 days, but you query 60 days back, just in case:  In the Issue Navigator enter:

project = ACE and issueFunction in commented('by cthompson after startOfDay(-60d)')5CommentedBy

Please note, you require the ‘Adaptavist SciptRunner for Jira’ plug-in to include issueFunction in your query. issueFunction is a search mechanism allowing you to perform searches that you would not be able to do with ‘out of the box’ JQL.

6.  Tracking Assignee


You need to locate a Jira Issue that was assigned to a member of your team, but you’re not sure who. In the Issue Navigator enter:

project = ACE AND fixVersion in versionMatch(5.1) AND assignee WAS IN (cthompson, tbagnall, vhemmert, jemett) ORDER BY Rank6AAssignee

You can subscribe to any filter, where Jira will run the search and email you the results.  This is particularly useful when your project is in a stabilization phase and you would like daily updates containing the latest bugs.  In the filter view, click on Details then New subscription:

6CSubscribeToFilter

7.  Multiple teams in one project


You are sharing a project with another team, but want your own board, sprints and version control. Create some JQL that will track your Epics, Stories and Sub-Tasks.  In the Issue Navigator enter:

project = SHA AND (key in (sha-847, sha-856, sha-860, sha-445) OR 'Epic Link' in (sha-847, sha-856, sha-860, sha-445)) OR issueFunction in subtasksOf('\'Epic Link\'=sha-847') OR issueFunction in subtasksOf('\'Epic Link\'=sha-856') OR issueFunction in subtasksOf('\'Epic Link\'=sha-436') OR issueFunction in subtasksOf('\'Epic Link\'=sha-445') ORDER BY Rank ASC7MultipleTeams

8.  Bugs fixed after a certain date


You would like to track the bugs where status has been changed to Review or Verify after a date and want to exclude issues that have been pinged back to the Developers.  In the Issue Navigator enter:

project = SHA AND fixVersion in versionMatch(5.1) AND (status changed to Review after '2015/11/29' OR status changed to Verify after '2015/11/29') AND Status != Open AND status != 'In Progress' ORDER BY priority DESC8BugsFixed

9.  More on Empty


For some fields, such as ‘Labels’ you need to explicitly query if the field is ‘Empty’ as part of your criteria, for example .. (labels !=CMM) .. will only return issues with the label field that has been populated, ignoring issues with no labels.  To fully capture the set of records you’re really after use: .. (labels is empty or labels != CMM) ..

Incorporating this logic into a larger query, in the Issue Navigator enter:

project = SHA and fixVersion='5.1' and (labels is empty or labels !=CMM) and type in (Story,Task,Bug) order by rank9MoreEmpty

10.  Aggregate Expressions

JQL is not great for aggregation, but I find totaling points of stories that fall into certain sprints useful. In the Issue Navigator enter:

project = SHA and sprint in('CMM Sprint 5', 'CMM Sprint 6', 'CMM Sprint 7') and issueFunction in aggregateExpression('Total points', 'storyPoints.sum()')10AggregateExpressions
As elsewhere in this post, you need the ‘Adaptavist SciptRunner for Jira’ plug-in to include issueFunction in your query.

Material Designed Aikau

Posted by ddraper May 5, 2016

Introduction

It was recently announced at BeeCon 2016 that Alfresco would be adopting Angular 2 and Google Material Design for all future applications that it develops (but that Aikau would continue to be developed to support Alfresco Share and Records Management).

 

Obviously Google Material Design is not compatible stylistically with Share currently, but Aikau can be used to build standalone Alfresco clients as well, so I thought it might be interesting to see if I could combine the two.

 

There are specific implementations of Material Design for Angular which were unlikely to be compatible with Aikau so I just used Material Design Lite (MDL) to quickly build some widgets and construct a page.

 

The purpose of this blog is to demonstrate that these types of integrations are possible and to provide further examples of how Aikau is able to easily integrate 3rd party libraries.

 

 

Base Material Design Lite Widget

MDL is provided in the form of a JavaScript file and a CSS file. The JavaScript file should be included to process the DOM once it has been loaded. MDL is primarily aimed at static web pages rather than dynamic ones which presented a minor issue for Aikau as the DOM is dynamically constructed after the page has been loaded.

 

MDL does support dynamic construction of some of its elements (unfortunately tabs was not one of them which was something of a disappointment) but it was simple enough to “upgrade” widget elements after they had been created.

 

I created a base widget “mdl/BaseMdlWidget” that handled all the dependencies and dynamic upgrading:

 

define(['dojo/_base/declare',
        'dijit/_WidgetBase',
        'dijit/_TemplatedMixin',
        'dojo/text!./templates/Header.html', 
        'alfresco/core/Core',
        'alfresco/core/CoreWidgetProcessing',
        'dojo/_base/array'],
        function(declare, _WidgetBase, _TemplatedMixin, template, AlfCore, CoreWidgetProcessing, array) {

  return declare([_WidgetBase, _TemplatedMixin, AlfCore, CoreWidgetProcessing], {

    cssRequirements: [{cssFile:'./css/material.css'}],

    templateString: '<div>No template provided</div>',

    nonAmdDependencies: ['./material.js'],

    postCreate: function mdl_BaseMdlWidget__postCreate() {
      if (this.widgets) {
        this.processWidgets(this.widgets, this.domNode);
      }
    },

    allWidgetsProcessed: function mdl_BaseMdlWidget__allWidgetsProcessed(widgets) {
      if (widgets) {
        array.forEach(widgets, function(widget) {
          if (widget.domNode) {
            componentHandler.upgradeElement(widget.domNode);
          }
        });
      }
    }
  });
});


This widget includes the references to both the “material.css” and “material.js” files that are stored relative to the widget (Surf ensures that they are only loaded once onto the page regardless of how many Aikau widgets declare a dependency on them). Note the use of the “nonAmdDependencies” attribute to load a JavaScript file that is not AMD compatible.

 

All the subsequent MDL based widgets I created extended this module.

 

The majority of the widgets I created were just simple representations of the various layout containers, for example:

 

header.js

define(['dojo/_base/declare',
        'mdl/BaseMdlWidget',
        'dojo/text!./templates/Header.html'],
        function(declare, BaseMdlWidget, template) {

    return declare([BaseMdlWidget], {
      templateString: template
    });
});

...with the template, header.html

<header class='mdl-layout__header '></header>

 

You might wonder what the point of doing this is?

 

Well, it’s quite simple really… although it would be simpler to just write out an HTML page, you would lose all the dynamic customization options that Aikau provides. If Alfresco were to ship an MDL based Aikau client it would be possible to add, remove and reconfigure the various elements on the page through an extension module.

 

Mixing in Aikau

Some of the other widgets were much more interesting though and demonstrate the power of the Aikau mixin modules.

 

For example the “mdl/MenuItem” used for the logout option in the header menu mixes in the “alfresco/renderers/_PublishPayloadMixin” to gain access to all the payload manipulation capabilities for publications.

define(['dojo/_base/declare',
        'mdl/BaseMdlWidget',
        'dojo/text!./templates/MenuItem.html',
        'dijit/_OnDijitClickMixin',
        'alfresco/renderers/_PublishPayloadMixin'],
        function(declare, BaseMdlWidget, template, _OnDijitClickMixin, _PublishPayloadMixin) {

  return declare([BaseMdlWidget, _OnDijitClickMixin, _PublishPayloadMixin], {

    templateString: template,

    title: 'Menu Item',

    onClick: function mdl_MenuItem__onClick(evt) {
      this.publishPayload = this.getGeneratedPayload();
      this.alfPublish(this.publishTopic, this.publishPayload, !!this.publishGlobal, !!this.publishToParent);
      evt.stopPropagation();
    }
  });
});

 

Another example was the “mdl/CreateContentFabButton” that mixed in the 'alfresco/documentlibrary/_AlfCreateContentMenuItemMixin' and 'alfresco/documentlibrary/_AlfCreateContentPermissionsMixin' modules to be able to generate content creation dialogs and automatically disable itself when a folder is viewed that the current user cannot create content in.

define(['dojo/_base/declare',
        'mdl/FabButton',
        'alfresco/documentlibrary/_AlfCreateContentMenuItemMixin',
        'alfresco/documentlibrary/_AlfCreateContentPermissionsMixin',
        'alfresco/documentlibrary/_AlfDocumentListTopicMixin',
        'dojo/_base/lang'],
        function(declare, AlfFilteringMenuItem, _AlfCreateContentMenuItemMixin, _AlfCreateContentPermissionsMixin, _AlfDocumentListTopicMixin, lang) {

  return declare([AlfFilteringMenuItem, _AlfCreateContentMenuItemMixin, _AlfCreateContentPermissionsMixin, _AlfDocumentListTopicMixin], {

    postCreate: function alfresco_documentlibrary_AlfCreateContentMenuBarPopup__postCreate() {
      this.alfSubscribe(this.hashChangeTopic, lang.hitch(this, this.onFilterChange));
      this.alfSubscribe(this.userAccessChangeTopic, lang.hitch(this, this.onUserAcess));
      this.alfSubscribe(this.metadataChangeTopic, lang.hitch(this, this.onCurrentNodeChange));
    },

    filter: function alfresco_documentlibrary_AlfCreateContentMenuItem__filter(payload) {
      if (this.hasPermission(this.permission, payload.userAccess)) {
        this.domNode.setAttribute('disabled');
      }
      else {
        this.domNode.removeAttribute('disabled');
      }
    }
  });
});

 

Building a Document Library

Having created some modules I then set about composing a page using them with the Aikau Document Library. In a previous blog post I described how the doclib.lib.js and doclib.lib.properties files could be imported into an Aikau page WebScript. I briefly mentioned that this library file provided functions that could be called to build specific parts of the Document Library.

 

I’ve used this approach to place the controls normally found in a sidebar (filters, tree, tags and categories) into the MDL drawer and placed a breadcrumb trail and document list into the main content section.

 

Other Steps

There a few other steps that were necessary…

 

The Java based LESS engine in Surf compressor was having an issue with the material.css file so I swapped out the “css.theme.handler” bean with a custom version that uses a Node based LESS processor by adding the following definition into the “web-application-config.xml” file (PLEASE NOTE: This is only available in Surf 6 and can’t be used in Alfresco 5.0 or 5.1 out-of-the-box).

<bean id='css.theme.handler' parent='css.theme.handler.abstract' class='org.springframework.extensions.surf.ExternalLessCssThemeHandler'>
  <property name='cmd'><value>lessc -</value></property>
</bean>

 

This required me to install LESS globally via NPM.

npm install less -g

I also updated the “theme_1-theme.xml” to set a LESS variable to disable legacy button design:

<less-variables>
  @use-legacy-buttons: false;
</less-variables>

It was also necessary to update the “page-template.ftl” file to include:

<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<link rel='stylesheet' href='https://fonts.googleapis.com/icon?family=Material+Icons'>

To get the necessary icon font from and set the viewport property for scaling to other devices.

 

Test it out

All the code is available on a GitHub repository for you to try out. Simply clone the repository and run:Test it out

mvn clean install jetty:run

 

Make sure you have an Alfresco Repository running locally on port 8080 and when the Jetty server has started go to http://localhost:8090/aikau-sample/page/ap/ws/home

 

Once you’ve logged in you should be taken to the main page which shows the content from the sample site. This video shows what you can expect to see.

 

Summary

This is just another example of how Aikau can make use of 3rd party libraries and should demonstrate that it is possible to easily take advantage of the capabilities that Aikau and Surf provides with your own custom widgets. It is by no means a statement of direction for Aikau but hopefully shows what is possible in a short amount of time and hopefully highlighted a few useful tricks along the way.

Introduction

One of the greatest misconceptions about Aikau is that it is somehow aiming to compete with other UI frameworks and libraries such as Angular and React. This really isn’t the case - Aikau is designed to address specific use cases that no other UI framework meets.

This isn’t trying to say that Aikau is in any way better - it’s just that it can do things that other frameworks can’t. We have integrated other frameworks such as Dojo, JQuery and YUI2 into Aikau - but Angular has been somewhat conspicuous by its absence.

There are a couple of key reasons for this:
 

    1. Angular 1 doesn’t really “play nicely” with other frameworks, although Angular 2 does a much better job of this
    2. Quite honestly there has never been a truly compelling reason to integrate it.


It’s worth remembering the granularity of Aikau widgets and how they are fully decoupled from both each other and the data that they use. Whilst this approach has been key to supporting the dynamic customization use cases, it has not lent itself particularly well to making use of the capabilities that Angular provides.

 

As you may have heard from recent announcements at BeeCon, Alfresco is looking to start making greater use of Angular 2 for the development of new applications but that it intends to continue to develop Aikau for use in Share.

 

 

Recent Announcements

That doesn’t mean that Share is not capable of making use of Angular 2 (or indeed any of the other modern UI frameworks such as React, Ember, etc). In my previous post I showed how modern web development practices could be integrated into Share and in this post I’m going to demonstrate how to make use of some new capabilities available in the 1.0.66 release of Aikau that will allow you to seamlessly insert Angular 2 code into an Aikau page in Share.

 

One important thing to be aware of… the widget that will be demonstrated resides under a new “alfresco/experimental” package in the Aikau library - anything under this package does not fall into the usual backwards compatibility rules of Aikau and as such maybe changed or removed at any time. If this widget appears useful then you should let us know and we’ll look to make it a fully fledged Aikau widget so that it gets all the backwards compatibility guarantees that this brings.

 

In this example we’re going to be integrating an example from the Angular 2 tutorial into the faceted search page in Share. Doing so holds no value other than to demonstrate that it can be done.

 

 

Step 1. Get Aikau release

First of all, you need to make sure you have downloaded the 1.0.66 release of Aikau and have placed it in the 'share/WEB-INF/lib' folder.

 

 

Step 2. Create extension

Now you need to create an extension module for the faceted search page. The quickest way to do this is to follow the steps in this blog post to download an extension JAR file for the faceted search page.

 

 

Step 3. Create a package definition

Unpack the JAR file somewhere and edit the the “alfresco/site-data/extensions/extension.xml” file to add in a new AMD package declaration for the Angular 2 tutorial code (background information on defining new AMD packages via extensions can be found in the Alfresco Documentation here.

<extension>
  <modules>
    <module>
      <id>Angular 2 Tutorial Extension</id>
      <auto-deploy>false</auto-deploy>
      <evaluator type='default.extensibility.evaluator'/>
      <configurations>
        <config evaluator='string-compare' condition='WebFramework' replace='false'>
          <web-framework>
            <dojo-pages>
              <packages>
                <package name='blog' location='js/blog'/>
              </packages>
            </dojo-pages>
          </web-framework>
        </config>
      </configurations>
      <customizations>
        <customization>
          <targetPackageRoot>org.alfresco.share.pages.faceted-search</targetPackageRoot>
          <sourcePackageRoot>org.alfresco.share.pages.faceted-search.customization</sourcePackageRoot>
        </customization>
      </customizations>
    </module>
  </modules>
</extension>

 

Next you want to copy the “main.ts” and “app.component.ts” (these can be copied from here) into the “META-INF/js/blog” folder.

 

 

Step 4. Edit the controller extension

Now you want to update the generated JavaScript controller extension ('alfresco/site-webscripts/org/alfresco/share/pages/faceted-search/customization/faceted-search.get.js') to make a dynamic request to add the new Angular 2 widget into the page.

 

Add the following code into the file:

var verticalStack = widgetUtils.findObject(model.jsonModel.widgets, 'id', 'FCTSRCH_MAIN_VERTICAL_STACK');
if (verticalStack && verticalStack.config && verticalStack.config.widgets)
{
  verticalStack.config.widgets.unshift({
    name: 'alfresco/experimental/ng2/Bootstrap',
    config: {
      main: 'blog/main.ts',
      templateString: '<my-app>Loading...</my-app>'
    }
  });
}

 

The Bootstrap Widget

The widget being added is the “alfresco/experimental/ng2/Bootstrap” widget. The purpose of this widget is to bootstrap the example from the Angular 2 tutorial. Here we are choosing to insert the example above the main search controls.

There are two configuration attributes:
 

    • “main” is the root Angular 2 component to load that should include the call to bootstrap Angular 2 
    • “templateString” is the DOM fragment that contains the custom elements that the Angular 2 component will be looking to parse during bootstrapping.


Once you’ve made these changes you can re-pack the JAR file and copy it to the 'share/WEB-INF/lib' folder.

 

Restart Share and perform a search. Initially you won’t see anything different - this is because the module has not been applied. More importantly, a second extension module provided by the Aikau JAR is also required.

 

Step 5. Deploy the modules

Navigate to the Module Deployment page ('/share/page/modules/deploy') and add the “Angular 2 Tutorial Extension” and the “Angular 2 Support (version 1.0.66) module and click the “Apply Changes” button.

 

Screenshot from 2016-05-03 09:35:54

 

The “Angular 2 Support” module adds all of the required Angular 2 JavaScript dependencies that are required - most importantly it loads System.js that is used to transpile the TypeScript and handle the ES6 import calls.

 

Once you have applied these modules you can reload the search page and you’ll see the tutorial example displayed like so:

 

Screenshot from 2016-04-28 21:23:02

 

Summary

Obviously this example has no practical purpose whatsoever. However the technique could be used to for much more sensible use cases.

 

It’s worth noting that, from the moment you bootstrap into an Angular 2 component, you are completely leaving Aikau behind and all future imports should be done via the ES6 import approach as shown in the Angular 2 tutorials.

 

It’s also important to recognize that this is not suitable for production purposes, because in reality you would want to transpile and compress the Angular 2 code before using it.

 

If you think the “alfresco/experimental/ng2/Bootstrap” widget would be useful to be made a first class Aikau widget then please let us know - if enough people are interested then we’ll move it to an appropriate package and make it product ready.

 

Download the Source

You can download the extension module source from this folder in a new GitHub repository that I've setup.

Filter Blog

By date: By tag: