Skip navigation
All Places > Alfresco Content Services (ECM) > Blog > Authors ddraper
1 2 3 4 Previous Next

Alfresco Content Services (ECM)

104 Posts authored by: ddraper

Last November (2015) I gave a presentation on "Rapid UI Development with Aikau" at the Alfresco Day in Stockholm on and realized that the recording we took never got published so I've now uploaded it for you to watch here.

 

I wanted to provide this link in a blog post so that I could mention some caveats around the content...

  • It's almost a year old - so we've made nearly 50 more releases since I gave the presentation (so there are more widgets, more services and more library files available)
  • The Alfresco company strategy has since been changed so that Aikau is now focused specifically on Share (although it can be used to rapidly develop new standalone clients as I demonstrate by building a new client with a fully-fledged Document Library from scratch in under 5 minutes - 18 mins into the recording)

 

I do think that it still contains some valuable information though as well as providing a good overview of Aikau - for example there's a live demo of customizing the search page 29 mins into the recording.

 

Finally... although we were in Sweden, my presentation immediately followed Ole Hejlskov's (who is Danish) hence my joke about not speaking in Danish - I did know which country I was in!

Background

There have been a few questions on the discussions pages and on StackOverflow about how to create Share Dashlets using Aikau. This blog post aims to take you through the process required using a use case mentioned in this previous post.

 

Each dashlet in Share is represented by a WebScript so we need to create the necessary files required to define a new WebScript. There are a number of places you could create this files but ultimately they need to be accessible from the "alfresco/site-webscripts" location on the Java classpath that Share is configured to use (e.g. "share/WEB-INF/classes/alfresco/site-webscripts" in the exploded WAR, or "alfresco/site-webscripts" in a JAR placed in the "share/WEB-INF/lib" directory).

 

PLEASE NOTE: You'll need at least version 1.0.52 of Aikau for the upload dashlet example to work and at least version 1.0.63 for the Document Library example to work

 

Create a Dashlet WebScript

There is lots of information available on how WebScripts work elsewhere in the official documentation on on the wiki pages so I won't dwell on that here. Instead lets just focus on the files that you need to create.

 

Descriptor (aikau-dashlet.get.desc.xml)
<webscript>
  <shortname>Aikau Dashlet</shortname>
  <description>A dashlet rendered with Aikau</description>
  <family>dashlet</family>
  <url>/aikau-dashlet</url>
</webscript>

The main thing to note about this file is the value assigned to the "family" element. You can use either "dashlet", "user-dashlet" or "site-dashlet" to register a WebScript as being applicable for use as a dashlet. Here we're using just "dashlet" meaning that it can be used on either the user or site dashboards.

 

Template (aikau-dashlet.get.html.ftl)
<@markup id="widgets">
  <@processJsonModel/>
</@>

<@markup id="html">
  <div id="${args.htmlid?html}"></div>
</@>

You should notice here that this is slightly different to the usual template that we'd define for full Aikau pages in that we're adding a new DIV element to the page. This is because we want to provide a custom element for the the Aikau content to be rendered into. The "args.htmlid" token will automatically be provided from the Surf Component that the dashlet WebScript gets bound to.

 

Controller (aikau-dashlet.get.js)
model.jsonModel = {
   rootNodeId: args.htmlid,
   pubSubScope: instance.object.id,
   services: [],
   widgets: [
      {
         name: "alfresco/dashlets/Dashlet",
         config: {
            title: "Upload Dashlet",
            bodyHeight: args.height || null,
            componentId: instance.object.id,
            widgetsForToolbar: [],
            widgetsForBody: []
         }
      }
   ]
};

This is the basic model that all Aikau dashlets should start with. At the moment it is not providing any services or widgets - the important things to note are that the "rootNodeId" is also being set to the "args.htmlid" (as was referenced in the template) and that we're providing a "pubSubScope" (optional, but recommended) as well as passing through "bodyHeight" and "componentId" attributes to the root Dashlet widget.

 

Add The Dashlet to the Dashboard

Once your dashlet WebScript is deployed you can add it to your dashboard via the "Customize Dashboard" page in Share.

Screenshot of the customize dashboard page

Once you save your page you should see your empty Aikau dashlet on the page (I've removed all the other dashlets from the dashboard shown).

Screenshot of empty Aikau dashlet on the dashboard

Add Some Widgets

So far, not very usable. Let's add some widgets to our model to provide some useful content... for example you could reuse the example from this blog post to create a dashlet that provides a simple way to upload content:

model.jsonModel = {
   rootNodeId: args.htmlid,
   pubSubScope: instance.object.id,
   services: [
     "alfresco/services/FileUploadService",
     "alfresco/services/ContentService",
     "alfresco/services/DialogService",
     "alfresco/services/DocumentService",
     "alfresco/services/SiteService",
     "alfresco/services/NotificationService"
   ],
   widgets: [
      {
         name: "alfresco/dashlets/Dashlet",
         config: {
            title: "Upload",
            bodyHeight: args.height || null,
            componentId: instance.object.id,
            widgetsForToolbar: [],
            widgetsForBody: [
               {
                  name: "alfresco/upload/UploadTarget"
               }
            ]
         }
      }
   ]
};

...which would result in this:

Screenshot of upload dashlet

Alternatively you might want to follow the example from this blog post to have a Document Library dashlet...

<import resource="classpath:alfresco/site-webscripts/org/alfresco/aikau/{aikauVersion}/libs/doclib/doclib.lib.js">

var docLib = getDocLib({
  rootLabel: "Documents",
  useHash: false,
  rootNode: "alfresco://company/home",
  siteId: null,
  containerId: null
});
docLib.config.pubSubScope = instance.object.id;

model.jsonModel = {
   rootNodeId: args.htmlid,
   services: getDocumentLibraryServices(),
   widgets: [
      {
         name: "alfresco/dashlets/Dashlet",
         config: {
            title: "Repository",
            bodyHeight: args.height || null,
            componentId: instance.object.id,
            widgetsForToolbar: [],
            widgetsForBody: [docLib]
         }
      }
   ]
};

Which would result in the following:

Screenshot of the Document Library as a dashlet

 

These are only a couple of examples of the endless possibilities available to you - however, the core setup remains the same.

 

 

If you've liked this blog post or found it helpful then please "Like" or "Comment" or "Share" it using the the controls at the top of the page.

Introduction

I'm trying to work through use cases that are raised as questions on our new Community Platform. In this post I'm going to be looking at this question on date rendering. Although it is possible to provide a custom workaround - the better solution would be to provide a pull request (PR) on the Aikau GitHub repository. This post will go through how to solve the problem and contribute the solution back.

 

Existing Date Renderer

The question refers to the Property renderer widget. This widget is the root module from which most other modules in the alfresco/renderers package descend. This widget provides numerous configuration options for selecting a property to render and how it should be rendered (such as size, style, prefix, suffix, label, missing value messages and whether or not to only show when hovered over). It provides some basic support for date rendering when the propertyToRender attribute is configured to be an object with an iso8601 attribute.

 

There is however a dedicated Date renderer. This widget was originally written to show dates in the context of showing when the date that something was created or modified on by a specific user, i.e.

Example of Date renderer

 

This widget was later modified to support the ability to render simple dates without use information through the use of the simple attribute, i.e.

 

Date renderer in simple mode

 

This is an example of how we are able to progressively update Aikau modules. It is possible to provide new capabilities through "opt-in" configuration attributes. So although at the time of writing we don't support date formatting, we make an update to optionally provide some formatting information providing that the default behavior when the new configuration is not provided remains the same. This is how Aikau has made incremental improvements for 87 releases while maintaining backwards compatibility.

 

Test Driven Development

In order to have a PR accepted you're going to need to provide a unit test. So the first thing to do would be to update the existing unit test page, or create a new one. We want to make a change to the Date renderer so the file we want to update is the Date.get.js file.

 

Add in a new instance of the widget with the configuration you would like to see work, like this:

{
   id: "FORMATTED",
   name: "alfresco/renderers/Date",
   config: {
      currentItem: {
         date: "2000-04-11T12:42:02+00:00"
      },
      simple: true,
      propertyToRender: "date",
      renderOnNewLine: true,
      format: "shortDate"
   }
},

 

We're provided a new format attribute and we want to output the date in the shortDate format. So now we need to update the unit test page to verify that this is going to work (which of course it won't yet).

 

Update the associated DateTest.js file to include a new test to verify the output...

"Date rendered in custom format": function() {
   return this.remote.findByCssSelector("#FORMATTED .value")
      .getVisibleText()
      .then(function(visibleText) {
         assert.equal(visibleText, "11/4/00");
      });
}

 

Update the Suites.js file to just run the Date unit test (you shouldn't include this change in the PR, but do this so you don't run all the unit tests)...

define(function() {

   // Whether to run all tests or just a few
   var runAllTests = true;
   runAllTests = false; // Comment/uncomment this line to toggle

   // This is the collection to change when only some tests are required
   var someTests = [
      "alfresco/renderers/DateTest"
...

 

Run the test (grunt test) and it should fail (because we've not added the update yet!). Information on getting the unit test application up and running is available for Windows, OSX and Linux.

===================
===== SUMMARY =====
===================

FAILED
alfresco/renderers/DateTest: "Date Tests"
  -  [Chrome52_LINUX, FF44_LINUX] Date rendered in custom format


==================
===== FAILED =====
==================

Chrome v52.0.2743.116 on LINUX

alfresco/renderers/DateTest: "Date Tests"
"Date rendered in custom format"
expected 'Tue 11 Apr 2000 12:42:02' to equal '11/4/00'

Firefox v44.0.2 on LINUX

alfresco/renderers/DateTest: "Date Tests"
"Date rendered in custom format"
expected 'Tue 11 Apr 2000 12:42:02' to equal '11/4/00'

 

Now we need to make the test pass... add the new attribute...

/**
* An optional format to apply to the date. This is only used when
* [simple]{@link module:alfresco/renderers/Date#simple} is configured to be true.
*
* @instance
* @type {string}
* @default
* @since 1.0.88
*/

format: null,

It's important to make sure you include some useful JSDoc - in particular make sure to include a since annotation so that developers know which versions of Aikau have the capability.

 

Now make use of the capability, in this case its as simple as passing the new format attribute...

this.originalRenderedValue = this.renderedValue = this.renderDate(dateProperty, this.format);

 

Now when you update the test app (grunt updateTest) to see the updates to the test page...

Screenshot from test page with custom format

Then run the test again (grunt test) and it should pass...

===================
===== SUMMARY =====
===================

No problems!

 

Create the PR

Now that you have working code you can create a pull request, like this. It will be reviewed and have a full regression test run against it and if all is well then it will be merged in preparation for the next release (which will usually be within a week).

 

The benefits for contributing enhancements like this back to the project are that it then becomes the responsibility of the Aikau team to maintain the capabilities to ensure that they continue to work. This means that you can upgrade to newer versions of Aikau safe in the knowledge that your customizations and custom pages that use the feature you provided will continue to work.

In my last post I described some work I'd done to provide a service within Aikau to support the XML based forms runtime in Share. In this post I attached a video demonstrating a page where the service could be tested by requesting a form from Share and seeing it mapped into a Aikau widgets.

 

Whilst fine in principal it doesn't actually provide much "real-world" use. Therefore I wanted to plug the service into a page to test it out in a more user focused way. The lack of support for the forms runtime has (up until now) been a barrier for converting more of Share to be implemented with Aikau.

 

I've started to explore how easy it is to recreate existing Share pages using Aikau and an obvious candidate for testing out the FormsRuntimeService was the "My Tasks" page. This video showing this page in action to demonstrate how we can start to leverage this new service and port more of Share to Aikau.

 

The service is still a work in progress and (at the time of writing) is not yet ready for production use. I'll keep posting updates as it improves - but as this isn't my primary work activity at the moment, the development rate may not be rapid. However, any feedback you have on this effort would be gratefully received - even if it's just to say that it's something that you'd be interested in using when it's ready!

 

Update 5th Sept 2016 :

I've created another video, this time using Data Lists:

The latest release of Aikau (at the time of writing 1.0.87) contains a new library file that provides functions that can be called to easily build user related content. This file can be imported into WebScripts much like the Data Lists and Document Library examples. This video demonstrates some example pages that you can build (the WebScripts for the pages are included in a JAR that can be downloaded from this GitHub repository).

 

Building the new people finder page is as simple as building a WebScript with the following JavaScript controller:

<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">
<import resource="classpath:alfresco/site-webscripts/org/alfresco/aikau/{aikauVersion}/libs/pages/people.lib.js">

var headerServices = getHeaderServices();
var headerWidgets = getHeaderModel("People Finder");

var docLibServices = getDocumentLibraryServices();

var peopleServices = getUserProfileServices();
var services = headerServices.concat(peopleServices).concat(docLibServices);

var widgets = getUserProfilesWidgets();
headerWidgets.push(widgets);

model.jsonModel = getFooterModel(services, headerWidgets);
model.jsonModel.groupMemberships = user.properties["alfUserGroups"];

 

Note that we're also pulling in the Document Library lib file to get the services used for document action handling!

 

As with the other library files the people.lib.js file is broken up into easily re-usable functions so that you can build as much or as little as you require. At the moment it isn't localized for languages other than English and we could do a better job of providing "id" attributes for each widget in the model - but if people show an interest in making use of this library then I'll be happy to make some updates.

 

Updating the people finder page is not on the road-map for the next release of Share but this library could be very useful for customizations and custom pages.

 

Screenshot of the Aikau people page

Custom Uploads with Aikau

Posted by ddraper Sep 20, 2016

Introduction

This question came up on the forums which highlights a couple of issues worth exploring. First of all is that people occasionally don't know how to approach Aikau development because of the different approach it takes towards UI development. If you're new to Aikau then I would always recommend working through the tutorial on GitHub and read this blog post which gives a good overview of the framework.

 

The main thing to understand about Aikau is that it is a framework geared up towards customization (although it can be used just as effectively for creating new applications). The UI for a page is defined in a JSON model because it's much easier to manipulate a JSON model through code than HTML. Widgets are intentionally and written to be easily configured, reused and extended. When building a page you won't be writing any HTML or CSS - but you might be if you're writing your own widgets.

 

Building a Custom Solution

The use case in the question it to provide a way in which the user can select a location to upload a file and then select the file to be uploaded. There is also another requirement relating to "check some config file and split xml" which I don't quite follow so I will gloss over for the time being - hopefully the question author will be able to provide some more information on this requirement.

 

My first thought when I looked at the question was about the existing widgets that might be used to build this solution. Aikau already provides:

  • A form control for selecting folders on the Alfresco Repository
  • A form control for selecting files from the operating system
  • A form widget for placing form controls in
  • service that can be used to show form controls in a dialog
  • button widget to launch actions
  • A service for handling file uploads

 

I figured that the approach to take would be to add a button that publishes a request to the DialogService to show a form containing a ContainerPicker and FileSelect that on confirmation will publish a request to the FileUploadService to upload the selected file to the selected destination.

 

The JavaScript controller for the WebScript to represent a new page would like this:

model.jsonModel = {
  services: [
    "alfresco/services/FileUploadService",
    "alfresco/services/ContentService",
    "alfresco/services/DialogService",
    "alfresco/services/DocumentService",
    "alfresco/services/SiteService",
    "alfresco/services/NotificationService"
  ],
  widgets: [
    {
      name: "alfresco/buttons/AlfButton",
      config: {
        label: "Upload",
        publishTopic: "ALF_CREATE_FORM_DIALOG_REQUEST",
        publishPayload: {
        dialogId: "UPLOAD_DIALOG",
        dialogTitle: "Upload a file",
        formSubmissionTopic: "ALF_UPLOAD_REQUEST",
        formSubmissionPayloadMixin: {
          targetData: {
            siteId: null,
            containerId: null
          }
        },
        widgets: [
          {
            name: "alfresco/forms/controls/ContainerPicker",
            config: {
              name: "targetData.destination",
              label: "Upload location"
            }
          },
          {
            name: "alfresco/forms/controls/FileSelect",
            config: {
              name: "files",
              label: "Select a file to upload"
            }
          }
        ]
      }
    }
   }
]
};

We'd need to provide a descriptor file...

<webscript>
  <shortname>Upload</shortname>
  <description>An example of uploading</description>
  <family>blog-sample-pages</family>
  <url>/upload</url>
</webscript>

...and a template file...

<@processJsonModel/>

...which when accessed via /share/page/dp/ws/upload would result in the following...

 

This video shows the resulting page in action

 

Using an Existing Solution

Then I remembered that I'd already previously put together an upload widget for this purpose. This widget not only provides the ability to select a file and a target upload location, but it provides a target zone for files to just be dropped onto that triggers a location selection dialog.

 

So the JavaScript controller for the page could look like this:

model.jsonModel = {
  services: [
    "alfresco/services/FileUploadService",
    "alfresco/services/ContentService",
    "alfresco/services/DialogService",
    "alfresco/services/DocumentService",
    "alfresco/services/SiteService",
    "alfresco/services/NotificationService"
  ],
  widgets: [
    {
      name: "alfresco/upload/UploadTarget"
    }
  ]
};

This video shows the page in action.


These models could easily be applied to customizations to existing pages - for example you could create a new dashlet for the UploadTarget or add a new menu item to the header in Share so that you can upload new content from any page in the application.

 

If you've found this post useful or interesting then it would be great if you could like or share it from the top of the page!

Clearing Aikau Form Fields

Posted by ddraper Sep 19, 2016

Introduction

This blog post is part of my commitment to work through interesting Aikau development and customization use cases. In this post I'm going to be addressing this question that was raised in relation to clearing form fields. One of the reasons why this question interested me was that it gives me the opportunity to point out a recently added (albeit minor) capability that was added on request from the Community.  An issue was raised on the Aikau GitHub repository pointing out that it was not possible to add additional buttons to a Form that don't include the form value in the published payload when clicked (see the "widgetsAdditionalButtons" configuration attribute).  

In one project I was recently working on we included a "reset" type button into an alfresco/forms/Form instance that contained a pre-defined set of values for each form field and published to the "setValueTopic" of the form. Unfortunately the form module always overrides the configured payload of any button in the "widgetsAdditionalButtons" property without providing any option to disable the override in general / per-button. Such an option is necessary.

Interestingly the request wasn't to just provide a "reset" option (which would have been a perfectly valid request to make) but simply to provide a way in which form buttons could be configured to not include the form value payload. So a JIRA issue was raised, included in a sprint and the solution was delivered as part of release 1.0.81.

 

It's important to understand that we want to support your use cases and are committed to delivering solutions for them - the main thing is for you to let us know what they are!

 

Some Forms Background

The forms solution provided by Aikau is one of its key strengths. As well as providing a wide variety of of form controls out of the box it also provides some very powerful capabilities that can be leveraged through a simple declarative model - this Sandbox page will take you through a handful of those capabilities and the Aikau tutorial also provides some excellent examples.

 

It is possible to set the values of form controls either individually on each control:

{
  name: "alfresco/forms/controls/TextBox",
  config: {
    fieldId: "FIELD1",
    label: "A TextBox",
    name: "text",
    value: "Some Value"
  }
}

...or on the form itself:

{
  name: "alfresco/forms/Form",
  config: {
    okButtonPublishTopic: "MY_TOPIC",
    widgets: [
      {
        name: "alfresco/forms/controls/TextBox",
        config: {
          fieldId: "FIELD1",
          label: "A TextBox",
          name: "text"
      }
    ],
    value: {
      text: "Some value"
    }
  }
}

The value configured for the Form will always override the value configured for the form control.

 

Additional Buttons

You can configure whether or not you want the "OK" and "Cancel" buttons to be displayed via the "showOkButton" and "showCancelButton" configuration attributes respectively. But if you want to provide additional buttons then you will need to provide them via  "widgetsAdditionalButtons".

{
  name: "alfresco/forms/Form",
  config: {
    okButtonPublishTopic: "MY_TOPIC",
    widgets: [
      {
        name: "alfresco/forms/controls/TextBox",
        config: {
          fieldId: "FIELD1",
          label: "A TextBox",
          name: "text"
      }
    ],
    widgetsAdditionalButtons: [
      {
        name: "alfresco/buttons/AlfButton",
        config: {
          label: "Extra Button",
          publishTopic: "MY_OTHER_BUTTON"
        }
      }
    ],
    value: {
      text: "Some value"
    }
  }
}

By default this new button will publish the form value as its payload, but if you add the "updatePayload" attribute and configure it to be false then it will just publish whatever payload it is configured with, 

...
widgetsAdditionalButtons: [
  {
    name: "alfresco/buttons/AlfButton",
    config: {
      updatePayload: false,
      label: "Extra Button",
      publishTopic: "MY_OTHER_BUTTON"
      publishPayload: {
        something: "else"
      }
    }
  }
],
...

 

Providing a Reset Button

In most cases you're likely to know the value that you want to assign your form... the most likely scenario that I can imagine is that you are retrieving the value from calling a REST API on the Alfresco Repository. If you have this value then you can set it as both the value of the form and the publishPayload of your reset button.

 

In order for the form to take notice of our reset button we need to ensure that it is configured with a "setValueTopic" attribute that matches the "publishTopic" of the reset button.

var formValue = {
  text: "Some value"
};

var myForm = {
  name: "alfresco/forms/Form",
  config: {
    setValueTopic: "RESET",
    setValueTopicGlobalScope: true,
    okButtonPublishTopic: "MY_TOPIC",
    widgets: [
      {
        name: "alfresco/forms/controls/TextBox",
        config: {
          fieldId: "FIELD1",
          label: "A TextBox",
          name: "text"
        }
      }
    ],
    widgetsAdditionalButtons: [
      {
        name: "alfresco/buttons/AlfButton",
        config: {
          updatePayload: false,
          label: "Reset",
          publishTopic: "RESET",
          publishGlobal: true,
          publishPayload: formValue
        }
      }
    ],
    value: formValue
  }
};

 

Bugs, bugs, bugs...

The great thing about doing an exercise like this is that it can help to flush out bugs. Those that are more familiar with Aikau may have noticed that the form is subscribing to the "RESET" topic globally (configured via the "setValueTopicGlobalScope" attribute) and that the button is publishing the value globally (configured via the "publishGlobal" attribute).

 

This is because in the course of working through this use case I discovered that the "setValueTopic" is subscribed to before the pubSubScope is generated meaning that it will always be subscribed to globally. So I raised a bug report and will fix the problem in the current sprint and the fix will be available from the 1.0.87 release.

 

Finally...

Having an easy config option for a form to render a reset button is still a perfectly valid request... someone just needs to ask for it !

 

 

If you've found this post useful or interesting then it would be great if you could like or share it from the top of the page!

Customizing Search Queries

Posted by ddraper Sep 16, 2016

Update 14/11/2016

From version 1.0.96 of Aikau this post is out-of-date in that the issue was raised as a feature enhancement and a solution was implemented to support adding additional query parameters through configuration of the additionalQueryParameters on the AlfSearchList widget.

 

Introduction

Now that we have this great new Community platform it should be easier for me to stay on top of Aikau and Share customization related questions. In particular I'm going to try and set aside some time each day to work through any interesting use cases that are raised in the ECM space.

 

This is the first question that I'm going to provide an example for. The requirement is to be able to customize the search results page so that only documents are shown. There are actually a number of different ways in which this problem can be solved using Aikau and I wanted to work through a few of them.

 

The various solutions fall into two different categories:

  1. Update the query before it is sent to the Repository
  2. Filter out any unwanted items from the response

 

Use renderFilter?

The second option could be achieved by creating an extension module to change the configuration of the search result widgets to only display when the "type" attribute of the result is "document". This would be done using the renderFilter configuration and would mostly work with a couple of caveats:

 

  1. If items were filtered out then the displayed search result count would not match the number of results displayed
  2. If only non-documents were found and all of them were filtered out then the user would not see the "No results found" message.
  3. If only non-documents made up the first page of results, then it would not be possible to trigger the infinite scrolling events to load the next page of data.
  4. It would be necessary to apply the configuration to widgets in each view (the search page has two views out-of-the-box).

 

So it looks like the renderFilter configuration approach is not the best solution in this case, but definitely one to be aware of when solving other problems. However, the configuration would look something like this:

 

...
"renderFilter": [
  {
    "property": "type",
    "values": ["document"]
  }
]
...

 

Some Background on Search

So in order to avoid the problems that would be introduced with the renderFilter approach we want to customize the search query before it is made. At this point it would probably be helpful to explain a little bit about how the search page (and Aikau in general) works.

 

The AlfSearchList widget is responsible for managing search state. It tracks search requests and manages the scope and facet filters that the user wishes to apply. It also manages what page of data is being displayed as well as the required sorting information and views that can display the data.

 

However it does not actually make the REST API call to perform the search, nor is it responsible for rendering the results. The REST API call is actually made by the SearchService which subscribes to a search request topic that the AlfSearchList publishes.

 

Therefore we have two choices - we can customize the AlfSearchList so that the request payload is updated before it is published, or we can customize the SearchService so that the payload is updated when it is received.

 

Configure AlfSearchList?

The ideal solution here would be if we were able to re-configure the AlfSearchList to force it to include additional data in the search payload. Unfortunately at the time of writing (when the latest Aikau release is 1.0.85) this capability does not exist. If you encounter this situation when trying to customize Share then ideally we would like you to raise an issue on the Aikau GitHub project, like this.....  or better yet - provide an update via a pull request

 

Customize AlfSearchList?

Since there isn't a configuration option available to us at present, then next option is to consider extending AlfSearchList with out own custom widget. The AlfList (which is an ancestor of AlfSearchList) provides the updateLoadDataPayload function for exactly this purpose - but sadly (due to the history of the development of AlfSearchList) this function is not called. If you were to encounter a problem like this then it would also be a good candidate for an issue!

 

Customize SearchService

So this leaves (at the time of writing at least) the best option as being to extend the SearchService. You can define an extending module to do this like this:

define(["dojo/_base/declare",
        "alfresco/services/SearchService"],
       function(declare,SearchService) {

  return declare([SearchService], {
    onSearchRequest: function(payload) {
      payload.term = payload.term + ""
      payload.datatype = "cm:content";
      this.inherited(arguments); // This calls the superclass function
    }
  });
});

 

This shows the onSearchRequest function being extended to update the payload before the superclass function is called. In the example shown above we are showing a couple of options:

 

  1. Appending to the search term
  2. Adding a query parameter to set the datatype to be "cm:content"

 

(Note: Any payload attribute unknown to the SearchService is treated as a query parameter!)

 

Creating An Extension Module

The thing we need to do is to create an extension module that can be deployed into Share to apply our changes. This module needs to do 3 things:

 

  1. Define an AMD package for our custom service
  2. Customize the search model to swap out the original service for our custom service
  3. Provide the custom service

 

Creating an extension module for a "full" Aikau page is incredibly simple using the "Developer View" and is described in detail in this previous blog post.

 

We will need to update the extension module to add in a new AMD package and this is described in this previous blog post.

 

In order to customize the search page model we will need to edit the "faceted-search.get.js" file that is generated for us to look like this:

var services = model.jsonModel.services;
for (var i=0; i<services.length; i++)
{
   if (services[i] === "alfresco/services/SearchService")
   {
      services[i] = "blogs/CustomSearchService";
   }
   else if (services[i].name === "alfresco/services/SearchService")
   {
      services[i].name = "blogs/CustomSearchService";
   }
}

 

We iterate over the services (which can be defined either as a simple string or as an object) and when we find the default search service we swap it out for our custom service.

 

The End Result

Once you've re-packaged your extension module as a JAR file you can drop it into the "share/WEB-INF/lib" folder (or include it in an AMP file, or port the code into an SDK project to build an AMP) and restart Share. All subsequent searches on the search page will only return items that are "cm:content".

 

The problem is that a lot more things are "cm:content" than you might realise. The question author only wanted to show documents, but "cm:content" does not mean "document". If you have a special type defined that you're using then this would work, actually though what you're probably going to want to do is to append some extra stuff the search term. This sort of thing in fact...

 

AND -TYPE:"cm:thumbnail" AND -TYPE:"cm:failedThumbnail" AND -TYPE:"cm:rating" AND -TYPE:"fm:post" AND -ASPECT:"sys:hidden" AND -cm:creator:system'

 

Note: In fact this query still doesn't completely filter everything out, but you get the general idea of what is required

 

Download and try it out

I've made the code available on this GitHub repository for people to review in more detail and try out. There is even a ready made JAR file that you can download and test with.

 

 

If you've found this post useful or interesting then it would be great if you could like or share it from the top of the page!

Aikau 1.0.85 - Data Lists

Posted by ddraper Sep 14, 2016

I was asked after I published my last blog post (on the progress being made with the FormsRuntimeService in Aikau) whether or not the example Data Lists page I showed would be available in the next release of Alfresco Share. At the time of writing it is not planned on the road map, however that does not stop you from making use of it.

 

The Aikau JAR contains a number of lib files that can be imported into WebScripts to quickly build standard pages. This previous blog post describes how to provide the Aikau version of the Document Library as a site page.

 

The steps outlined in that post can be loosely followed, but the main difference will be in the JavaScript controller for the WebScript, which should look like this:

<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/pages/datalists.lib.js">

var siteId = (page.url.templateArgs.site != null) ? page.url.templateArgs.site : "";

var headerServices = getHeaderServices();
var headerWidgets = getHeaderModel(getPageTitle());

var dataListServices = getDataListServices();
var dataListWidgets = getDataListWidgets({
siteId: siteId
});

var services = headerServices.concat(dataListServices);
headerWidgets.push(dataListWidgets);

model.jsonModel = getFooterModel(services, headerWidgets);
model.jsonModel.groupMemberships = user.properties["alfUserGroups"];

However, for convenience I've added a sample extension to a GitHub repository so that you can just download and try it out. Simply download the JAR file and place it into the share/WEB-INF/lib file and restart Share and you'll be able to add this page to existing sites. Don't forget that you'll also need the aikau-forms-runtime-support JAR as well as at least version 1.0.85 of the aikau JAR.

 

It is important to understand that at the time of writing this page is unsupported by Alfresco and is provided purely for educational purposes. The FormsRuntimeService and DataListService are both still under development and are not ready for production use. Having said that, it would be exceptionally valuable to get feedback and bugs reported so that we can continue to improve these until they are ready for production use.

 

This video shows the page in action.

One of the comments I had on my last blog post point out that one of the main problems with using Aikau is that it doesn’t support the XML-based Forms Runtime that Share uses to render forms using the old YUI2 controls.

 

I decided to take a look into this issue and see what could be done about it. The major issue with the existing implementation is that it the REST APIs return HTML fragments containing references to the YUI2 controls - it’s just not possible to extract the pure data from the API as it is not.

 

Fortunately one of the many great things about the WebScripts implementation is their ability to be extended and have multiple output formats defined for them. This meant that I was able to extend the FormUIGet Java controller for the WebScript to generate a pure JSON output for the forms data and then define a new WebScript descriptor to access that data.

 

Because the Forms Runtime only applies to Share and not to to standalone clients, it was necessary to package the Java class and Spring bean configuration into a separate JAR file. This means that to leverage this new capability of Aikau you now need to include an additional dependency in your AMP files (or simply download the JAR from our Maven repository and copy it into the “share/WEB-INF/lib” folder as I do in my demo video).

 

Now that we can access the forms runtime data we need to be able to convert the form definition into an Aikau forms model. This is capability is provided by the “alfresco/services/FormsRuntimeService” module. At the moment this module is not fully complete - but I wanted to get something working out to the Community as soon as possible in order to get feedback and hopefully some collaborative development.

 

At the moment only a couple of YUI2 controls are mapped to Aikau controls, but both edit and view modes are supported and the ability to configure in custom mappings is something that will be added in later. Constraints are also only partially handled, but the remaining constraints will be added soon.

 

The main objective for this first release was to get something out to the Community that demonstrated the end-to-end capability. A test page has been bundled with the Aikau that you can try out. Everything has been done to support multiple versions of both the Aikau and Forms-Runtime-Support JARs.

 

Take a look at the demo video and let me know what you think… please feel free to add comments to the JIRA epic or raise issues on GitHub to discuss where to take this next.

 

EDIT: Update for 1.0.77

We've made some more improvements that are shown in this video.

Improving Aikau Education

Posted by ddraper Jul 4, 2016

Introduction

In my last blog post I talked about the perspective I’d gained by working in another team at Alfresco that has been using Aikau. For the last 18 months the Aikau team has been so head down in development trying to provide features and fixes for internal teams, customers and the community that we’ve not had much opportunity to focus on reviewing and improving the educational material that is available for Aikau.

 

Interestingly with the advent of the new Angular based UI framework and the conclusion of a number of major Alfresco projects that use Aikau the heat is now off us and I’ve had some time to start looking at what resources are available and how we can improve the situation.

 

At this point I think it’s important to state that as far as I’m aware Aikau development is going to continue and that if you want to develop a user interface for Alfresco - particularly if you want to customize or add new pages to Share - then Aikau is still the recommended framework.

 

Alfresco is still actively using Aikau (most notably for Records Management in Share) which is hugely important for its business. The use of Aikau is never going to be the headline feature in an Alfresco release, but it’s usually there in the background getting the job done.

 

 

Eating Our Own Dog Food

I decided to have a go at reproducing some existing YUI2 based features of Share using Aikau to try and showcase how rapid development can be - but more importantly to try to understand where the gaps might be in available education.

 

I’ve started with the “People” page and you can watch me creating this page from scratch in a brand new client in this video. It’s a bit rough around the edges, for which I make no apologies - but hopefully it will be informative.

 

Feedback Wanted !

The full disclosure is that the first time I went through the process I found that some of the JSDoc could be better, we could provide some additional widgets and that the UserService needed updating - and I took care of these issues before recording the demo.

 

However, I would urge that anyone attempting to do any development that has similar problems to report them as a GitHub issue so that they can be addressed.

 

I’d also love your feedback on whether or not this medium is effective or not, whether I should record more demos and what they should be focused on. I’ve already got an idea for a follow up in developing some custom widgets to go on the page - but it might be better to work through an entirely different use case - Data Lists perhaps?

 

I'd also welcome any suggestions on other approaches we could take to help people get up to speed with Alfresco development using Aikau.

 

Accessing the Code

I've created a GitHub repository containing the application that I built in the video. I've tagged the commit with the code (and will continue to tag each commit if I do more examples) so that it's easy to try out the code and experiment with it yourself.

I’ve recently been moved out of the Aikau team in order to provide some assistance to a feature team that are using Aikau. This change in role has given me a very useful insight into the problems that developers have when using Aikau. When working on Aikau I only ever get to see a fragment of the overall implementation - I get very little context so I never know if the overall implementation is being done in a sensible way.

It’s important to remember that we never think of Aikau as being “done”. Our widgets are always open to further iteration to either add more capabilities, refactor them for better performance, improve the design and fix any bugs. We also don’t consider them to be an exhaustive set by any means - we know that more widgets will be needed.

What we don’t know is what bugs exist, what features could be added and what widgets are missing. We rely on our feature teams, customers, partners and community to tell us what is required.

When I started working on the feature I was given in the new team I almost immediately identified one bug and 3 features (1,2,3) that were required. This wasn’t a brand new requirement for my new feature team but these requirements hadn’t been identified.

Trying to act like a good community citizen I raised these as issues on JIRA and then being a model community citizen I created pull requests to fix the problem and add the features following the well defined acceptance criteria.

Of course it's much easier to do this when you have an awareness of what is available and how things are meant to work in a framework when you have been involved in the development of that framework. Having said that I really want to encourage you to just ask the question and start the dialog with us.

It should hopefully be clear from the closed issues that we’re more than happy to answer questions when they’re asked.

The flip side of the coin is to not start a dialog but to simply complain about something without trying to dig into the details like with this blog post published last week. Whilst I understand that something might not be immediately obvious when trying to use Aikau (we’re well aware that documentation could be improved, we just haven’t been allocated the resources to do enough about it) we will try and help you if we can - even if that help is simply explaining why something is the way it is.

We aren’t promising to implement all your feature requests (far from it in fact!) but we are committed to genuine bugs in our coding (meaning a defect in the intended behaviour of an individual widget or service).

We’re starting to see increased Community interaction - more issues raised, more traffic, more GitHub stars. What we’d really like to see is more pull requests - and if something is blocking you from making a pull request then we’d like to know what that impediment is so that we can remove it.

This is going to be especially important during my reassignment as it means that we might not be able to implement community requested features as we have in the past - but we will endeavour to try to stay on top of the bugs that you raise.

Thanks again for your ongoing support !

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.

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: