Skip navigation
All Places > Alfresco Content Services (ECM) > Blog > 2014 > October
2014

Aikau - Simple Picker Updates

Posted by ddraper Oct 31, 2014

Introduction

In my last blog post I described how I had updated the dynamic visibility configuration options in order to support a new picker widget that I was working on. In this post I'm going to describe how to use this new picker.

 

The Simple Picker Widget

This new picker widget is defined in the “alfresco/forms/controls/SimplePicker” module and is intended to be used in forms where the user might need to select one or more items from a list. This widget re-uses much of the same code implemented for the more complex form pickers (e.g. for selecting documents, folders or properties) but distils the underlying JSON model into a small configuration set that is intended to make it as simple as possible get a picker onto your page.

 

The this list of available items is unsurprisingly a re-use of the existing 'alfresco/lists/AlfList' widget and the items can be populated either by directly configuring a “currentData” attribute or by configuring a “loadDataPublishTopic” (with an optional “loadDataPublishPayload”).

 

Let's start with a really simple configuration and build up towards a more realistic use case. All these examples are included in this JAR file for you to try out for yourself.

 

Basic Hard-coded Data

Let's start with some a really simple picker containing some hard-coded data that will be set using the 'currentData' attribute:

...
{
  name: 'alfresco/forms/controls/SimplePicker',
  config: {
    currentData: {
      items: [
        {
          name: 'One'
        },
        {
          name: 'Two'
        },
        {
          name: 'Three'
        }
      ]
    }
  }
}
...

 

This configuration will result in the following picker:

This is a simple picker constructed with hard-coded data. 

 

Just by providing some data we're able to build a picker, however it is worth nothing that our data has been tailored to fit the SimplePicker defaults (using 'items' to identify the list and 'name' to identify both the unique item key and displayable property) but I'll show how these can be configured later.

 

Simple Picker is a Form Control

Let's not forget that the Simple Picker is a form control and as such we can use any of the standard form configuration attributes... so let's update our picker to include a label, description and the request parameter name that would be posted on form submission.

 

We're also going to make the selected items re-orderable by setting the 'reorderable' attribute to true:

...
{
  name: 'alfresco/forms/controls/SimplePicker',
  config: {
    label: 'Pick Some items',
    description: 'This is a simple picker with some hard-coded data',
    name: 'hardcoded',
    reorderable: true,
    currentData: {
      items: [
        {
          name: 'One'
        },
        {
          name: 'Two'
        },
        {
          name: 'Three'
        }
      ]
    }
  }
}
...

This results in the following picker (with all the items selected to show the re-ordering controls):

The simple picker with label, description and re-ordering enabled 

 

Nothing to Display?

You have control over all of the messages that are displayed in the picker. One of which is the message shown when there is no data. This is done by setting the 'noItemsMessage' attribute.

 

PLEASE NOTE: In my examples I'm using hard-coded strings, but these would typically be replaced with localization keys for multi-language support in a real-world example.

...
{
  name: 'alfresco/forms/controls/SimplePicker',
  config: {
    label: 'No Items Picker',
    description: 'This examples shows a custom message to show when no items are available for picking.',
    name: 'nothingAvailable',
    noItemsMessage: 'Nothing to pick!',
    currentData: {
      items: []
    }
  }
},
...

 

This will result in the following picker:

 

A Simple Picker with a custom message when no items are available for selection 

 

One at a Time Please!

If we want to configure our picker to only allow a single item to be selected then we can set the 'singleItemMode' to be true. This also has the effect of overriding the 'reorderable' attribute (because what's the point in having re-ordering controls when you can only ever have one item selected).

 

The effect of this is that if you have an item already selected, then selecting another will remove the previously selected item.

 

Accessing Server Side Data

For a slightly more real-world example let's configure our Simple Picker to allow us to select from the User Groups that are configured in the Alfresco Repository. This means that we replace the hard-coded 'currentData' object with a 'loadDataPublishTopic'. We're going to be using the trusty 'alfresco/services/CrudService' for this so you'll need to remember to include it in the list of services.

 

As well as configuring the data source we also need to configure how we handle the resulting data:

...
{
  name: 'alfresco/forms/controls/SimplePicker',
  config: {
    label: 'Pick User Groups',
    description: 'This is a simple picker publishes a request to get data to display',
    name: 'groups',
    loadDataPublishTopic: 'ALF_CRUD_GET_ALL',
    loadDataPublishPayload: {
      url: 'api/groups?zone=APP.DEFAULT'
    },
    itemsProperty: 'data',
    itemKey: 'shortName',
    propertyToRender: 'displayName',
    availableItemsLabel: 'Available Groups',
    pickedItemsLabel: 'Currently Selected Groups'
  }
}
...


Here is a break-down of the attributes used:

    • 'itemsProperty' identifies the attribute within the JSON response from the server that contains the list of items to display. This could be a dot-notation address in the JSON object if necessary.
    • 'itemKey' should be set to an attribute in each item that is a unique identifier. This is important to get right because it could result in multiple items getting picked if there are duplicate keys
    • 'propertyToRender' is the attribute in each item to use as the display value. Note that we use 'shortName' as the 'itemKey' (because it is unique) but 'displayName' as the 'propertyToRender' because it is the user-friendly group name


For added fun we're also adding labels to the available and picked item columns using the 'availableItemsLabel' and 'pickedItemsLabel' attribute.

 

The resulting picker looks like this:

 

SImple Picker configured to access group data from the Alfresco Repository 

 

Still Not Enough?

Finally, if you're still not happy with how the available items and picked items are displayed then you can set 'widgetsForAvailableItemsView' and 'widgetsForPickedItemsView' which allow you to declare JSON models of views for both columns.

 

Although configuring these attributes will override some of the previously mentioned configuration options it does give you complete flexibility for rendering the data as you see fit. Once again this is demonstrating the key-strength of Aikau which is re-usabiliy. The views you configure for items are defined in exactly the same way as all views of data - this means that you can easily re-use previously defined views or reference your own custom widgets.

 

Summary

Hopefully this gives another indication of where we're trying to take Aikau. Maximising code re-use, attempting to distil complex widget models into simple to configure widgets but retaining the ability to fully customize them as required.

 

 

 

 

Introduction



With the Aikau framework for Share construction we have created a large number of widgets with some underlying code from Dojo’s UI Library, Dijit. It makes sense to automatically test the Aikau widgets in isolation and therefore we have created a test framework using Intern (http://theintern.io/) to do just that. The tests run quite fast and with predictable results. It is now easy to see if a recent addition or update to an existing widget has caused a bug or regression.



To perform tests we have written a number of test web scripts that contain widget models. The widget models are usually quite simple but occasionally have a more complex structure to expose edge case behaviours. Some test web scripts also contain intentionally broken models to test the catching of errors. For testing purposes the test web scripts are run using Jetty as we do not need any of the more complex overhead from Share.

Process Overview



To run a suite of tests we do the following:



  1. Launch an instance of an Ubuntu virtual machine using Vagrant

    The virtual machine provides a set of test browsers, drivers for those browsers and a running instance of Selenium that Intern can target


  2. Run up the test web scripts using Jetty

    We do not need all of Share so a basic Jetty server is fine here


  3. Run tests against the test web scripts using Intern

    Each of the tests will be run according to the Intern configuration and against the browsers defined and requested therein


Note: One of the tools we install when setting up testing is Grunt (http://gruntjs.com/). Much of the process of running tests is handled for us in commands we have already added using Grunt. In fact, once we have set up the testing framework for the first time, we really only need two commands to start a test run - firstly launch the virtual machine and then secondly run the tests.

Step 1 - Prerequisites



The testing framework for Aikau makes use of a number of technologies and you will need the following free downloads to proceed. Please install with the default settings:

I am assuming that you can use a command line and have a vague idea of what a virtual machine and Vagrant are. If not, please read about it first: http://www.vagrantup.com/

Step 2 - Installation



Having downloaded and installed a current version of the Alfresco code base, open a command prompt and navigate to the following Alfresco directory:

{alfresco}/code/root/projects/slingshot


Run the following command to install Node dependencies:

>> npm install


If you’re interested to know what is being installed you can look at file package.json in the directory above which contains a list of the dependencies. Once you have installed all of the components required for the test framework, you should be able to launch a virtual machine with vagrant for the first time. The first time you run this process it may be slow as it has a lot to download:

>> g vup


Note: This command is ‘g’ for grunt and ‘vup’ for vagrant up.



When the ‘g vup’ command has completed, if it has been successful you should be able to observe the Selenium instance running here:

http://192.168.56.4:4444/wd/hub/static/resource/hub.html



The selenium console should look something like this:



selenium

Step 3 - Running the suite of tests



With an instance of Selenium available on the virtual machine, you should now be able to run Intern by issuing the following command:

>> g test


Note: This command is ‘g’ for grunt and ‘test’ for run intern test suite (virtual machine).



This command first checks if the test application server is running and launches it if not. Once it is happy that the server has started completely it will proceed to launch the Intern test runner. You should see the test suite run through a large number of tests (about 110 tests run twice for two different browsers at time of writing) and log them to the console. Hopefully they will all pass.



This is the sort of output you should expect to see once the initialisation steps have been performed:

...

>> Starting 'AccessibilityMenuTest' on chrome

>> Test page for 'AccessibilityMenuTest' loaded successfully

>> AccessibilityMenuTest: Find the menu element

>> AccessibilityMenuTest: Find the heading text

>> AccessibilityMenuTest: Find the menu items

>> AccessibilityMenuTest: Find the first target

>> AccessibilityMenuTest: Find the second target

>> AccessibilityMenuTest: Find the first menu link - which links the first target

>> AccessibilityMenuTest: Hit the browser with a sequence of different accesskey combinations and the letter 's' for a nav skip

>> Starting 'SemanticWrapperMixinTest' on chrome

>> Test page for 'SemanticWrapperMixinTest' loaded successfully

>> SemanticWrapperMixinTest: Check NO_WRAPPER dom is correct

>> SemanticWrapperMixinTest: Check GOOD_WRAPPER dom is correct

>> SemanticWrapperMixinTest: Check BAD_WRAPPER dom is correct

>> SemanticWrapperMixinTest: Check LEFT_AND_RIGHT_WRAPPER dom is correct

>> Starting 'Pie Chart Test' on chrome

>> Test page for 'Pie Chart Test' loaded successfully

>> Starting 'PublishPayloadMixinTest' on chrome

>> Test page for 'PublishPayloadMixinTest' loaded successfully

...



Note: When a test run is complete the Jetty server is left running. If there are any failures it is possible to immediately investigate if something catastrophic has occurred by simply observing the test web script in a browser.

Useful commands



There are several grunt commands that can be used with Vagrant, the test application server and Intern. Here are the ones we've already seen and a few more, all of which should be run from the directory shown above:



















































































CommandFunction
g vupShort for ‘vagrant up’ this will launch a virtual machine instance with Vagrant
g vdownShort for ‘vagrant down’ this will stop a running instance of a Vagrant virtual machine
g vcleanShort for ‘vagrant clean’ this will delete an existing instance of a Vagrant virtual machine
g testRun up an instance of the test application server and run the Intern test suite against it
g shell:startTestAppStart the test application server
g shell:stopTestAppStop the test application server if it is running
g ntShort for ‘new test’ this command will restart the Vagrant virtual machine, restart the test application server and finally run the Intern test suite against them
g utdShort for ‘update test deployment’ this command will bring down the test application server, rebuild slingshot and then relaunch the test application server with any file modifications that have been made


Adding a test



If you wanted to add a test of your own there are three steps to the process:



  1. Create a test web script


  2. Create an Intern test


  3. Add the test to the Intern Suites.js file


Let's investigate those steps individually:

Create a test web script



Test web scripts all live in this location or a sub-directory of it:

{alfresco}/code/root/projects/slingshot/tests/testApp/WEB-INF/classes/alfresco/site-webscripts/alfresco


Each web script requires three files - a JavaScript file, a Freemarker template file and an xml description file. There are examples in the directory that can be looked at and copied. As with web scripts in the main applications the format of the file names is important. With a correctly written test web script you should be able to view the test model in action at a URL such as:

http://localhost:8089/aikau/page/tp/ws/AccessibilityMenu


This example test web script has in it's model an AccessibilityMenu widget. It isn't very pretty as rendered here but it isn't supposed to be.

Create an Intern test



The actual test files are created here or in a sub-directory of it:

{alfresco}/code/root/projects/slingshot/tests/alfresco


Intern tests are written in JavaScript using a promise-based dependency called Leadfoot which is provided by SitePen, the company who wrote Intern itself. You can read the Leadfoot documentation here. Strategies for writing Selenium tests are complex and I'm not going to investigate them here. Needless-to-say, one emulates the behaviour of an individual using a browser to interrogate the test web script as rendered.



The specific way in which an Intern test addresses a test web script can be seen if any of the existing tests is viewed. Pay close attention to this part of most tests which is the point at which the test web script is loaded:

...

var browser = this.remote;

var testname = 'AccessibilityMenuTest';

return TestCommon.loadTestWebScript(this.remote, '/AccessibilityMenu', testname)

...



Add the test to the Suites.js file



Tests that should be run are all listed in this file in the array 'baseFunctionalSuites':

{alfresco}/code/root/projects/slingshot/tests/config/Suites.js


Next steps



The testing framework supports testing against a local instance of Selenium rather than a VM instance as described above. It is also possible to run a coverage report on the test framework to indicate the amount of the code base being interrogated. The details of these alternative scenarios will follow in another post.

Introduction

In this previous blog post I introduced the concept of dynamic visibility rules in Aikau page models. In the course of working on some prototype pages for features pencilled in for Alfresco 5.1 I realised that there were a few useful capabilities missing. This post is going to cover the new options that are available for declaratively configuring visibility rules.

 

 

Strict Mode

I was doing some more work on pickers (see this post for more info) and was trying to re-use the code we had for defining the classic picker style of 2 vertical lists with add, remove and re-order controls.

 

In the old YUI2 style pickers we had (e.g. for selecting tags) we don't hide the selected item (just the control for adding it) and I wanted to improve on this so that a picked item would be removed from the list of available items (but would be revealed when removed).

 

The current visibilityConfig options prevented this because the rules were defined such that if a rule was not evaluated to be true then the widget would be hidden. In actual fact what we want is a more flexible mode where only successfully evaluated rules will reveal a widget, but failed evaluation will have no effect.

 

I've now added in an additional attribute called 'strict' that is a boolean value (that defaults to true if omitted for backwards compatibility) that implements this feature, when a widget is configured with the following visibilityConfig:

...
visibilityConfig: {
  initialValue: true,
  rules: [
    {
      topic: 'ALF_REVEAL_WIDGET',
      attribute: 'reveal',
      is: [true],
      strict: false
    }
  ]
}
...

 

...then publishing the following payload on the 'ALF_REVEAL_WIDGET' topic:

{
  reveal: false
}

 

Will not hide the widget, because the 'strict' attribute is set to false. If the 'strict' attribute was set to true then the publication would result in the widget being hidden.

 

Invisibility Configuration

When trying to use the original visibilityConfig I realised that it was sometimes difficult (if not impossible) to configure a rule that just hid a widget.

 

When setting up the rules for the picker widget I wanted to define one rule that made an item appear (when removed from the picked item list) and another rule to make the item disappear (when added to the picked item list).

 

The problem was that I only knew what an attribute in the payload should be and not what it shouldn't be. Therefore I added the ability to configure 'invisibilityConfig' as well as 'visibilityConfig'.

 

This takes exactly the same attributes but when the rule is satisfied the widget will be hidden instead of revealed, e.g:

...
invisibilityConfig: {
  rules: [
    {
      topic: 'ALF_HIDE_WIDGET',
      attribute: 'hide',
      is: [true]
    }
  ]
},
...

 

...will result in a widget being hidden when the following payload is published on the 'ALF_HIDE_WIDGET' topic:

{
  hide: true
}

 

NOTE: Because we've omitted the 'strict' attribute publishing any other payload on the topic will result in the widget being revealed.

 

Using Current Item Data

In this previous blog I described how we could use the 'currentItem' attribute for controlling when widgets are rendered. The 'currentItem' attribute is typically assigned when to widgets that are created when iterating over items in a list (although any widget can be defined with a 'currentItem' attribute).

 

in the picker example I wanted to make use of the 'currentItem' attribute when defining 'visibilityConfig' and 'invisibilityConfig' because the rules would need to be specific to each rendered row in the list of pick-able items.

 

This was solved by adding in support for a new 'useCurrentItem' attribute (that defaults to false) that treats each value in the 'is' and 'isNot' arrays as a dot-notation property address in the 'currentItem'.

 

Putting It All Together

The end result of these 3 new attributes was the following configuration:

...
visibilityConfig: {
  rules: [
    {
      topic: 'ALF_ITEM_REMOVED',
      attribute: 'name',
      is: ['name'],
      useCurrentItem: true,
      strict: false
    }
  ]
},
invisibilityConfig: {
  rules: [
    {
      topic: 'ALF_ITEM_SELECTED',
      attribute: 'name',
      is: ['name'],
      useCurrentItem: true,
      strict: false
    }
  ]
},
...

 

This defines the behaviour that when an item that has the same name as the current item is selected (published on the 'ALF_ITEM_SELECTED' topic) it will be hidden, and when an item that has the same name as the current item is removed (published on the 'ALF_ITEM_REMOVED' topic) it will be displayed again.

Introduction

I've previously provided a few examples and techniques of how to implement a variety of use cases using Aikau, but now I'd like to provide an example of the process I follow to implement a solution. Whilst this process might not be suitable for everyone it should at least be informative and show the ways in which you can leverage existing widgets to achieve your use case.

 

As a background exercise I've been working on re-implementing the Document Library using Aikau and my aim is not simply to achieve feature parity but to improve what is currently available. Something that I believe could be a potentially useful addition would be the ability to view and add comments on a document without needing to access the details page for that document.

 

 

Step 1 - Break Everything Down

Rather than trying to tackle everything in one go (e.g. as a single widget) it's important to determine whether elements of your target solution already exist and whether or not it is worth abstracting other elements into widgets that could be re-used in other use cases.

Almost everything in a web application boils down to lists, forms and layout and this use case is another example of that. We know that we want to be able to...

    1. See a list of existing comments (obviously a list)
    2. Add a new comment (a form)
    3. Edit an existing comment (another form)
    4. Delete an existing comment (an action on a list)
    5. Only reveal (and load) comments on demand (layout)


Aikau provides a good support for list rendering as shown in the previous Data List blog post - and in fact steps 1, 2 and 4 are almost identical to uses cases covered in that blog (it's only the data that is changing).

We can therefore re-use one the Aikau list widgets (e.g. 'alfresco/lists/AlfList') and the 'alfresco/services/CrudService' as a means to render the list, the 'alfresco/dialogs/AlfDialogService' to pop-up a form that can be used to add a new comment and an 'alfresco/renderers/PublishAction' widget to issue the delete request.

At this stage it's worth noting a couple of important points...

    1. There could very well be more comments than can easily be accommodated
    2. We don't yet have all the widgets we need to render the comments view as we'd like (in this case we don't have a widget for rendering user avatar thumbnails).


The nice thing about Aikau is that you don't need to get bogged down in such details because it's easy to come back and iterate over your page models without needing to throw anything away.

    • It's easy to update a list view to add in additional renderers.


We want to simply get our commenting use case implemented as quickly as possible. Focus on the key goals, get your solution working and then iterate to polish it up to the required level.

 

Step 2 - Use What You've Got

Rather immediately diving into writing widgets at this point I started with a new Aikau page WebScript to get as far as possible with the previously mentioned widgets and services at my disposal. You should probably use the Alfresco SDK for this, but I prefer to create and work with files directly in my Tomcat structure for maximum speed.

 

Rather than worrying about integrating this comments list into my detailed view, I just want to get some data to work with so I create a new document in my Alfresco repository using Share and then add some comments to it. I then take a note of the NodeRef and use that directly in my page model because I know that I can swap it out for contextual data at a later point.

 

Use a browsers developing tooling it's easy to capture the XHR requests that are made when working with existing comments and I can build this simple page model for listing the comments of my target node.

{
  name: 'alfresco/lists/AlfList',
  config: {
    loadDataPublishTopic: 'ALF_CRUD_GET_ALL',
    loadDataPublishPayload: {
      url: 'components/node/workspace/SpacesStore/eb98d7af-c311-4ab8-9596-6fcda675fe6e/comments?reverse=true&startIndex=0&pageSize=10',
      urlType: 'SHARE'
    },
    widgets: [
    ...
    ]
  }
}

 

I've not included the view definition here (which would appear in as the 'widgets' array) because views have previously been covered in other blog posts.

 

We now able to render a list of comments for a given NodeRef.

 

Step 3 - Identify What's Missing

From our breakdown above and with some knowledge of the Aikau widget library (e.g. by reviewing the JSDocs) we know that there a couple of key widgets missing...

    1. A widget for revealing and dynamically building new Aikau components (this is different from simply toggling visibility of previously rendered widgets described in this previous blog post which would be inefficient).
    2. The ability to toggle between reading and editing comments in an HTML editor.


Well, as it happens I'd already been working on editor widgets for another use case (inline content creation) and have a TinyMCE based HTML editor form control ready to go, but with no means to toggle between read and edit mode. We also need a widget for revealing and building new AIkau widgets.

 

Step 4 - Create Missing Widgets

The widgets I ended up building for this example are now committed to the Alfresco source code so I won't be including them here. I also don't want to go into too much detail on how they were developed as I want to cover widget development in another blog post, however I do want to call out a view key points in their development process.

The first widget I created was the 'alfresco/renderers/EditableComment' widget which allows your to switch between a read-only view and an editable view of data which can then be persisted.

This widget is essentially a wrapper around a form that contains the 'alfresco/forms/controls/TinyMCE' form control. When developing widgets we try to recognize that they may want to be either extended or re-configured in the future so we try to write them so that they can be used as they are with a minimum amount of configuration but make define the default behaviour within configurable variables. So for example...

    • The topic the widget subscribes to that will trigger the edit mode is defined by the 'subscriptionTopic' but has a default of 'ALF_EDIT_COMMENT'
    • The form and form controls as defined in the 'widgets' variable so that they can be easily be changed either in the page definition, by dynamic extension module or an extending widget
    • The labels for the save button is further abstracted so that it's not necessary to re-define the entire form to simply change their labels.
    • The property of the editable content is defined in the 'propertyToRender' variable so that the widget can be used with different data models
    • The property name that the updated comment will be POST back to the server as is defined in the 'postParam' variable so that the widget can be used with different services.


Essentially our widget becomes a configurable container around previously created widgets as we try to maximise re-use out of the existing widget library capabilities.

 

Similarly the new 'alfresco/layout/VerticalReveal' widget extends an existing widget and heavily uses existing widget processing capabilities for on reveal processing the JSON model that it can be configured with.

 

This widget was also written with respect to the fact that is is undoubtedly not the only use case that it would be suitable for. It contains no references to comments or indeed anything else relating to the use case it was written with. It's important for us to recognise when we're creating something that has multiple uses and not prevent ourselves from re-using it or extending it in the future.

 

Step 5 - Integrate New Widgets

Once the missing widgets have been developed they can be integrated into the our page WebScript to be tested out. The first issue that presents itself is that all the EditableComment widgets are publishing and subscribing on common topics. This means that editing one comment puts all comments into edit mode.

 

The solution to this is to scope each row of the in the list of comments. This is done by setting 'generatePubSubScope' to true on the Row widget in the view as this means that the widgets for each item in the list will have their own 'pubSubScope' attribute value and prevent cross-talk between widgets.

...
{
  name: 'alfresco/documentlibrary/views/AlfDocumentListView',
  config: {
    widgets: [
    {
      name: 'alfresco/documentlibrary/views/layouts/Row',
      config: {
        generatePubSubScope: true,
        widgets: [
...

 

Comment context is largely addressed in the 'url' attributes in the 'publishPayload' objects created for the EditableComment, PublishAction widgets along with the button for creating a dialog to create a new comment. It's worth noting that at some stage we'll want to introduce a dedicated CommentService module which will make the JSON model much simpler as it would only require the 'currentItem' to be published rather than there being a need to process payload configuration.

 

Step 6 - Create a Composite Widget

We don't want to have to re-declare the JSON model that has been created every time we want to render a list of comments. Instead we want to create a new widget that is little more than a alias to our model - this will be the widget that we reference in our Document Library view and means that we can pervasively change how comments lists are rendered in a single widget.

 

The 'alfresco/documentlibrary/views/AlfDetailedView' widget (another example of a composite widget) is then updated to include our CommentsList widget.

 

...
{
  name: 'alfresco/documentlibrary/views/layouts/Cell',
  config: {
    widgets: [
      {
        name: 'alfresco/layout/VerticalReveal',
        config: {
          subscriptionTopic: 'ALF_REVEAL_COMMENTS',
          widgets: [
            {
              name: 'alfresco/renderers/CommentsList'
            }
          ]
        }
      }
    ]
  }
}
...

 

Comments were previously shown as a simple count that when clicked take the user to the comments section of the detailed page. Since we wanted to retain the existing code that renders the comments count but change the behaviour to allow a click action to reveal the comments list I made an additive change so that if a specific 'publishTopic' attribute is configured then the behaviour changes so that the link becomes a simple comment toggle.

 

Changing the widget in this way means that all existing uses of the Comment widget behave exactly as they did before but the AlfDetailedView is able to reconfigure it to use it in a new way.

 

...
{
  name: 'alfresco/renderers/Comments',
  config: {
    publishTopic: 'ALF_REVEAL_COMMENTS'
  }
},
...

 

Changing the widget in this way means that all existing uses of the Comment widget behave exactly as they did before but the AlfDetailedView is able to reconfigure it to use it in a new way.

 

Step 7 - Addressing Further Context and Scoping Issues

Now that our CommentsList widget is being repeated multiple times within a single page we need to ensure that there is no cross-talk between the widgets when revealing comments. The same approach as before was taken to generate a new 'pubSubScope' for each item in the list.

 

We're also able to update our previous JSON model for the list to use the NodeRef of the document to retrieve comments for.

 

...
{
  name: 'alfresco/lists/AlfList',
  config: {
    waitForPageWidgets: false,
    loadDataPublishTopic: 'ALF_CRUD_GET_ALL',
    loadDataPublishPayload: {
      url: 'components/node/{nodeRef}/comments?reverse=true&startIndex=0&pageSize=10',
      urlType: 'SHARE'
    },
    widgets: [
...

 

Again, adding in a dedicated CommentService module will make this section of the model much simpler.

 

The Current (Not Final) Result

Rendering the Aikau based Document Library page now allows us to view, add, delete and edit comments for a Document without needing to access it's details page. It's quite clearly not the finished article but it's been relatively straightforward to get something working that we can being to polish into the finished article.

 

Aikau Document Library showing comment counts

 

 

Having revealed the current comments 

Prompt when deleting a comment Inline editing a comment using TinyMCE Creating a new comment 

 

Next Steps

Hopefully an Aikau based Document Library will be a key feature of an Alfresco 5.1 release (whenever that might be) and this is just a starting point for making comment handling much simpler.

The next steps to be implemented would be:

    1. Create a dedicated 'alfresco/services/CommentService' module and simplify the existing JSON model
    2. Switch to using the 'alfresco/lists/AlfSortablePaginatedList' and add pagination controls
    3. Add a user avatar thumbnail widget and include it in the comments list view
    4. Improve the styling of the VerticalReveal widget.
    5. Update the Comment widget further to subscribe to commenting topics to allow it to refresh the count as comments are added and deleted.

    Introduction

    As a fun side project I had a go at getting part of the Alfresco 5 platform - Alfresco Share 5 in the Apache Tomcat based web-tier - running on Raspberry Pi.

     

    I'm using a Raspberry Pi Model B which has a huge 512MB RAM and crazy fast 700Mhz single-core ARM CPU!! Wow. OK. If you hadn't already guessed, that is not huge nor it is at all fast - in fact it is a lot slower than my HTC One Android phone CPU (quad-core 1.7Ghz ARM CPU with 2GB RAM). To give you comparison - the low-end Intel Atom processor is approx 3-4x faster than this ARM CPU - so it's pretty amazing that it's possible at all.

     

     

    Install

    The Raspberry Pi is running the standard 32bit Debian Wheezy Linux distribution that is compiled specially for the Raspberry Pi but is not anything special from a Linux perspective. I had previously installed Oracle JDK 7 so that will save some time - but here's how:

     

    sudo apt-get update && sudo apt-get install oracle-java7-jdk

     

    I expect you can also use JDK 8 but I have not tried it!

     

    OK, next I gzipped up my local copy of Apache Tomcat which contains my share.war webapp. IMPORTANT: The Tomcat instance does not contain alfresco.war or solr.war webapps! To be clear, we are only trying the Share web-app on the Pi because the minimum CPU/RAM requirements of the complete Alfresco repository are too much for this tiny $30 computer. Most Alfresco developers run separate Share/Alfresco Tomcat instances as this makes it much easier when doing Share development so that the Alfresco+Solr server instance can be left running - it only takes a few seconds to restart a Tomcat instance containing just Share.

     

    Use 'scp' or FTP to get your tomcat zip bundle onto the Pi and unpack it somewhere.

     

     

    Configure

    1. Edit the tomcat/bin/catalina.sh file to apply JVM memory settings. Add this line near the top of the file:

     

    JAVA_OPTS='$JAVA_OPTS -Xms128m -Xmx200m'

     

    I set mine to 200MB which is more than enough for Share. Also ensure there are no other JVM options applied that may not be suitable - e.g. for my instance I removed settings for the CMS GC and a few server related flags that were not appropriate for a tiny device with a single-core CPU.

     

    2. Edit tomcat/shared/classes/alfresco/web-extension/share-config-custom.xml to point to your remote Alfresco 5.0 repository instance. This is a common Remote config change for an Alfresco Share installation. I won't go into the details here as I have posted about it before on few occasions - but here is what mine looks like:

    <alfresco-config>

    <config evaluator='string-compare' condition='Remote'>

    <remote>

    <endpoint>

    <id>alfresco-noauth</id>

    <name>Alfresco - unauthenticated access</name>

    <description>Access to Alfresco Repository WebScripts that do not require authentication</description>

    <connector-id>alfresco</connector-id>

    <endpoint-url>http://KEVLINUX:8080/alfresco/s</endpoint-url>

    <identity>none</identity>

    </endpoint>

    <endpoint>

    <id>alfresco</id>

    <name>Alfresco - user access</name>

    <description>Access to Alfresco Repository WebScripts that require user authentication</description>

    <connector-id>alfresco</connector-id>

    <endpoint-url>http://KEVLINUX:8080/alfresco/s</endpoint-url>

    <identity>user</identity>

    </endpoint>

    <endpoint>

    <id>alfresco-feed</id>

    <name>Alfresco Feed</name>

    <description>Alfresco Feed - supports basic HTTP authentication via the EndPointProxyServlet</description>

    <connector-id>http</connector-id>

    <endpoint-url>http://KEVLINUX:8080/alfresco/s</endpoint-url>

    <basic-auth>true</basic-auth>

    <identity>user</identity>

    </endpoint>

    </remote>

    </config>

    </alfresco-config>

     

    The important part is the <endpoint-url> address. You may need to use a direct IP address in your config depending on your Alfresco repository machine OS and network.

     

    3. In tomcat/bin folder execute ./catalina.sh run as usual to start Tomcat. Because I was using ssh to remote the Pi, I actually used nohup ./catalina.sh run & so I could then tail -f the log and leave it running in the background when logged out - but that's up to you.

     

    The server takes a little under 3 minutes to start (rather than around 20 seconds on a modern laptop!)

    INFO: Deploying web application directory /home/pi/dev/tomcat/webapps/share

    INFO: Starting Servlet Engine: Apache Tomcat/7.0.40

    INFO: Server startup in 156202 ms

    Point a web browser at the Raspberry Pi Share start page URL e.g. http://raspberrypi:8081/share - again this will vary with your set-up as you might need a direct IP and your port may be different depending on your original Share Tomcat config.

     

    The server takes another couple of minutes to warm up (Share is in production mode; warms caches, compiles JS/FTL/Less, retrieves License/DD from Alfresco etc. - lots to do for a single core 700Mhz CPU!)

    2014-10-14 21:04:51,434  INFO  [web.site.EditionInterceptor] [http-bio-8081-exec-4] Successfully retrieved license information from Alfresco.

    Login to Share!

     

    The server takes another 30 seconds to login the first time - then each new page takes ~10sec to warm up - but then response time is very reasonable! Pages appears in <2 seconds.

     

    Memory usage of Share heap is ~120MB during page generation, ~75MB at idle.

     

    A fun Raspberry Pi experiment and an example of how lean the Alfresco Share 5.0 web-tier is!

    Introduction



    In Alfresco 5 we have added two new types of events to the activity stream - for document Download and Preview events. Some concerns have been raised about the number of activities that may be added to the site or user streams and also concerns around privacy - seeing what documents other users have been downloading or previewing.



    Firstly, the ability of the server to process activities has been improved considerably since Alfresco 4. The activity processing is now multi-threaded and also handles more activities per stream.



    Secondly, the same ACL/permission enforcement that has always been present in Alfresco is still present in 5.0 - that has not changed! So the ability to see a public document is still the same for all users and if a document is private, it will remain private. For example if you add/modify/download a document that is private to you then activity events for the action will not appear on the public activity stream for other users in the site.

    Configuration



    The activities which are processed by the system can be configured in the Alfresco repository. A config file change is needed to disable certain activities from the various summary processing options. For example if you are still concerned about the Download and Preview activities and want to disable them, then this is how you would do it.

    RSS Summary Feeds



    To disable them from the RSS summary feeds used by the Document Library components we need to override the bean that deals with processing that list.



    In the Alfresco repository tomcat instance, in the tomcat/shared/classes/alfresco/extension folder create a spring context XML file called custom-web-context.xml. If you already have this extension file, then add the bean shown below to the existing file.



    The beans that process activities for RSS feeds work against a list of known activity types - the types that are generated by applications such as Alfresco Share. To remove the Download and Preview activities we identify the IDs of those activities which are org.alfresco.documentlibrary.file-previewed and org.alfresco.documentlibrary.file-downloaded and remove them from the list for the documentLibraryActivitySummaryProcessor bean definition.

    <?xml version='1.0' encoding='UTF-8'?>

    <beans xmlns='http://www.springframework.org/schema/beans'

    xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'

    xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd'>

    <bean id='documentLibraryActivitySummaryProcessor' class='org.alfresco.rest.api.impl.activities.BaseActivitySummaryProcessor'>

    <property name='registry' ref='activitySummaryParser' />

    <property name='eventTypes'>

    <list>

    <value>org.alfresco.documentlibrary.files-added</value>

    <value>org.alfresco.documentlibrary.files-updated</value>

    <value>org.alfresco.documentlibrary.files-deleted</value>

    <value>org.alfresco.documentlibrary.file-added</value>

    <value>org.alfresco.documentlibrary.file-created</value>

    <value>org.alfresco.documentlibrary.file-deleted</value>

    <value>org.alfresco.documentlibrary.file-liked</value>

    <value>org.alfresco.documentlibrary.inline-edit</value>

    <value>org.alfresco.documentlibrary.folder-liked</value>

    <value>org.alfresco.documentlibrary.folder-added</value>

    <value>org.alfresco.documentlibrary.folder-deleted</value>

    <value>org.alfresco.documentlibrary.folders-added</value>

    <value>org.alfresco.documentlibrary.folders-deleted</value>

    </list>

    </property>

    </bean>

    </beans>



    Restart Alfresco then no more Download and Preview activities in RSS feeds. You can modify the bean config above to remove any other activity types you don't like from the list.

    Removing from Activity Email Summary



    Of course the Activity Email Summary is generated by something completely different and uses different configuration - anything else just wouldn't be Alfresco. To deal with that, we need to override the bean that handles the generation of the list of Activities for Email templates.



    Fortunately that is not too hard either. Create the following folder path tomcat/shared/classes/alfresco/extension/subsystems/ActivitiesFeed/default/default and create a file called custom-activities-feed-context.xml. This specifically overrides configuration for the ActivitiesFeed sub-system. A sub-system in Alfresco is a powerful and highly configurable component that can be enabled/disabled and tweaked at run-time - however because of this flexibility comes some complexity with the configuration - which is why we have the strange looking folder path - see this wiki page for more info.



    The file should contain the following bean override:

    <?xml version='1.0' encoding='UTF-8'?>

    <beans xmlns='http://www.springframework.org/schema/beans'

    xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'

    xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd'>

    <bean id='feedModelBuilderPrototype' class='org.alfresco.repo.activities.feed.DefaultActivitiesFeedModelBuilder' scope='prototype'>

    <property name='ignoredActivityTypes'>

    <set>

    <value>org.alfresco.documentlibrary.file-previewed</value>

    <value>org.alfresco.documentlibrary.file-downloaded</value>

    </set>

    </property>

    </bean>

    </beans>


    Again you can see the activity types specified in a list to be ignored during Activity processing.

    Removing from Alfresco Share



    To remove the activities from those made available to Alfresco Share dashlets add the following extension config files to your Alfresco Share Tomcat instance (which will be the same Tomcat instance if you are using a default installed package).



    tomcat/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/dashlets/site-activities.get.config.xml



    and



    tomcat/shared/classes/alfresco/web-extension/site-webscripts/org/alfresco/components/dashlets/my-activities.get.config.xml



    Both files are identical, so create and edit them to contain:

    <config>

    <filter-range>

    <filter type='today' label='today' />

    <filter type='7' label='7days' />

    <filter type='14' label='14days' />

    <filter type='28' label='28days' />

    </filter-range>

    <filter-type>

    <filter type='mine' label='mine' />

    <filter type='others' label='others' />

    <filter type='all' label='all' />

    <filter type='following' label='following' />

    </filter-type>

    <filter-activities>

    <filter type='org.alfresco.profile.status-changed,org.alfresco.comments.comment-created,org.alfresco.comments.comment-updated,org.alfresco.documentlibrary.file-updated,org.alfresco.documentlibrary.file-added,org.alfresco.documentlibrary.files-added,org.alfresco.documentlibrary.files-deleted,org.alfresco.documentlibrary.files-updated,org.alfresco.documentlibrary.file-deleted,org.alfresco.documentlibrary.inline-edit,org.alfresco.wiki.page-created,org.alfresco.wiki.page-edited,org.alfresco.wiki.page-renamed,org.alfresco.wiki.page-deleted,org.alfresco.blog.post-created,org.alfresco.blog.post-updated,org.alfresco.blog.post-deleted,org.alfresco.site.user-joined,org.alfresco.site.user-left,org.alfresco.site.user-role-changed' label='allItems' />

    <filter type='org.alfresco.profile.status-changed' label='statusItems' />

    <filter type='org.alfresco.comments.comment-created,org.alfresco.comments.comment-updated' label='commentItems' />

    <filter type='org.alfresco.documentlibrary.file-updated,org.alfresco.documentlibrary.file-added,org.alfresco.documentlibrary.files-added,org.alfresco.documentlibrary.files-deleted,org.alfresco.documentlibrary.files-updated,org.alfresco.documentlibrary.file-deleted,org.alfresco.documentlibrary.inline-edit,org.alfresco.wiki.page-created,org.alfresco.wiki.page-edited,org.alfresco.wiki.page-renamed,org.alfresco.wiki.page-deleted,org.alfresco.blog.post-created,org.alfresco.blog.post-updated,org.alfresco.blog.post-deleted' label='contentItems' />

    <filter type='org.alfresco.site.user-joined,org.alfresco.site.user-left,org.alfresco.site.user-role-changed' label='membershipItems' />

    </filter-activities>

    </config>


    The important bit is we are removing the same file-previewed and file-downloaded activities mentioned above from the filter-activities configuration.



    This may also be a good time to review the above configuration - as it allows you to configure what filters are shown in the Activities summary dashlets, across what date ranges and what other Activities they will show. For instance if you don't want to see 'status' updates from users, then you could completely remove the org.alfresco.profile.status-changed references and filter section.

    Introduction

    At Alfresco Summit 2014 in London this week I was presented with many different use cases that various Business Partners and Community members would like to implement on Share using Aikau and I was mostly able to either point them to existing blogs or examples in 5.0.

     

    As always though there were a few use cases that made me curious to investigate and one of those was about the ability to pick different versions of a document. We are often asked about easily re-usable pickers and had already implemented a Document Picker widget with the intention of it being configurable and extendable and I wanted to check that the use case was possible. 

     

    The rule of thumb that I recommend to anyone using Aikau is that if a widget isn't used in Share out-of-the-box then it can't be guaranteed to be production quality. The 'alfresco/form/control/DocumentPicker' is not used out-of-the-box but I am pleased to report that investigation enabled me to identify a few issues and fix them to make it much more suitable for use in 5.0 customizations. However, as a result you're going to a need a nightly build, 5.0.c Community or 5.0 Enterprise (when released) for this example to work.

     

     

    Picking Documents

    There is a common requirement when using Share to be able to select a document to work with (e.g. when creating a workflow for example) and as such we need to ensure that Aikau provides widgets to satisfy this requirement. For 5.0 there was no specific feature that needed a document picker but I wanted to ensure that we had at least explored how we could implement a configurable picker.

     

    Typically you're going to want to capture the Document nodeRef information within a form, so the simplest way to achieve this would be with the following JSON model:

     

    services: [
      'alfresco/dialogs/AlfDialogService',
      'alfresco/services/DocumentService',
      'alfresco/services/SiteService'
    ],
    widgets: [
      {
        name: 'alfresco/forms/Form',
        config: {
          okButtonPublishTopic: 'FORM_CONTENTS',
          widgets: [
            {
              name: 'alfresco/forms/controls/DocumentPicker',
              config: {
                label: 'Choose a document',
                name: 'document'
              }
            }
          ]
        }
      }
    ]

     

    Here were defining a form that contains a single form control for selecting documents: 'alfresco/forms/controls/DocumentPicker'. This form control works with dialogs, documents and sites so the appropriate services need to be included in the page.

     

    This will result in the following screenshots during use:

    Document Picker in Form 

    Document Picker Dialog Opened with Document Selected Document Picker showing the selected document after closing the dialog

     

    So hopefully this provides a really easy way in which to select documents within a form. But what about extending the default implementation to make it possible to select a specific version of a document?

     

    Customizing the Picker

    As always in AIkau we try to make it possible to get a long way through JSON configuration and this example is no different. However, this is by no means necessarily the most simple configuration to follow compared with other examples.

    The 'alfresco/forms/controls/DocumentPicker' extends the more abstract 'alfresco/forms/controls/Picker' widget which provides the following configuration points that we're going to make use of:

      • configForPickedItems - this is the JSON model that declares how you see picked items when the dialog is not displayed
      • configForPicker.widgetsForPickedItems - this is the JSON model the declares how you see picked items within the picker dialog
      • configForPicker.widgetsForRootPicker - this is the JSON model that defines how the dialog picker is constructed.

     

    configForPickedItems

    Given how much we like code re-use in Aikau you probably won't be surprised to learn that since the picked documents is just a list of items we've re-used the 'alfresco/lists/AlfList' module and that the JSON model required here is just for the 'alfresco/documentlibrary/views/AlfDocumentListView' that is renders the selected documents. Since I've covered lists and views in previous blog posts [1, 2] I won't go over it again, except to say that we're defining a view where each item is represented by a row containing 3 cells that show the name, label (version) and an action for removing the item:

     

    configForPickedItems: {
      widgets: [
        {
          name: 'alfresco/documentlibrary/views/layouts/Row',
          config: {
            widgets: [
              {
                name: 'alfresco/documentlibrary/views/layouts/Cell',
                config: {
                  widgets: [
                    {
                      name: 'alfresco/renderers/Property',
                      config: {
                        propertyToRender: 'name'
                      }
                    }
                  ]
                }
              },
              {
                name: 'alfresco/documentlibrary/views/layouts/Cell',
                config: {
                  widgets: [
                    {
                      name: 'alfresco/renderers/Property',
                      config: {
                        propertyToRender: 'label'
                      }
                    }
                  ]
                }
              },
              {
                name: 'alfresco/documentlibrary/views/layouts/Cell',
                config: {
                  width: '20px',
                  widgets: [
                    {
                      name: 'alfresco/renderers/PublishAction',
                      config: {
                        iconClass: 'delete-16',
                        publishTopic: 'ALF_ITEM_REMOVED',
                        publishPayloadType: 'CURRENT_ITEM'
                      }
                    }
                  ]
                }
              }
            ]
          }
        }
      ]
    },

     

    widgetsForPickedItems

    It is possible that you might want to display different information within the dialog than in the form (e.g. you might choose to add more document information in the form such as a title and description or render the view as a table) in this case we use exactly the same JSON model to show the name, version and an action to remove the selected item.

     

    widgetsForRootPicker

    This is where it gets a bit more complicated...

     

    The idea is that we can define a starting point for our picker and then add additional exploratory panes into the dialog until we reach a point where we have items to select. For example in the default picker our starting point is the ability to select from the current user's recently visited sites, their favourite sites, all sites to which they have access, the repository root, the share files folder and their user home folder. Depending upon the selection the next pane will either contain a list of sites or a document list view of a selected location.

     

    The change that we want to make is to add an additional exploratory pane so that selecting a document allows further selection of version rather than just selecting that document as a picked item.

     

    To do this we currently need to copy and paste some of the default picker JSON model - this is not ideal and is something I'd like to improve in future versions. The JSON model is to large to easily fit into this blog but can be found in a JAR that you can download to try out this example.

     

    The JAR contains the file 'alfresco/site-webscripts/docversionpicker.get.js' which is the JavaScript controller for the WebScript that defines our sample page. The key lines in this file are as follows:

     

    Line 154:

    Here is where start changing the default 'alfresco/pickers/Picker' JSON model to override the default configuration for the 'alfresco/pickers/DocumentListPicker' that is displayed in the second picker pane.

     

    Lines 157 - 160:

    Here the publication data that defines picker is modified to change picked items from publishing the item data to making a request for a new picker to be added.

     

    Line 161:

    The 'currentPickerDepth' attribute is used to ensure that picker panes don't simply keep getting appended into the dialog. By specifying an incrementally greater depth we ensure that the version picker is added to a new pane rather than replacing the 'alfresco/pickers/DocumentListPicker'

     

    Line 162:

    This defines the picker that we want to add to use to select a document version.  The picker is just an 'alfresco/lists/AlfList' that is configured to publish requests for an 'alfresco/services/CrudService' to retrieve version information for the document selected in the previous pane.

     

    By changing the publish payload type to 'PROCESS' and specifying the 'processCurrentItemTokens' publish payload modifier we are able to define a URL that will include the correct nodeRef to retrieve version information for.

     

    Line 201:

    The 'alfresco/renderers/PublishAction' is configured to publish on the 'ALF_ITEM_SELECTED' topic to which the 'alfresco/pickers/PickedItems' widget subscribes. This is selected version appears in the picked items pane.

     

    Example In Action

    If you try out the example you should see the updated picker appear as shown in the following screenshots:

     

    The initially displayed dialog with just a single root option

     

    Having selected the Repository...

     

    Having selected a folder containing the target document

     

    Having selected the target document you see the available versions

     

    Having selected the required version

     

    Having confirmed selection of the required version

     

    Summary

    This is probably the most complicated example I've attempted to blog about in Aikau so far - certainly the most complicated to try to describe anyway. Hopefully though it demonstrates that we will provide a default document picker that can be easily re-used without a lot of effort and that we're trying to take a more open-ended approach towards pickers that will make it easier to both create and customize pickers through declarative JSON modelling rather than needing to write code.

     

    I still think that this is an area that we can further improve on going forwards, but it's always challenging to provide Aikau widgets without having a specific Alfresco product feature that they're required for.

     

    Download the example code to review or try out here.

    Alfresco Community 5.0.b

    Posted by kevinr1 Oct 2, 2014
    I'm pleased to announce that the Alfresco Community 5.0.b release is planned within the next few days. This will be the second Alfresco Community 5.0 release and is based on very latest public community code-line.



    Alfresco 5 is built with maven. All artifacts are available on the Alfresco nexus.



    Here are some of the exciting new features and improvements that you will find in 5.0.b. Many of these features have improved since 5.0.a, so it is recommended you read the Alfresco Community 5.0.a post first if you have not already done so!

    Search Improvements



    Search is an area we have improved greatly since Alfresco 4 and even more improvements are present in 5.0.b. Firstly a big step forward - Solr 4 is now the default search and indexing sub-system in Alfresco. I know this has been a long time coming but it is worth the wait! There is some great new search functionality we have been able to expose because of Solr 4, and there will be more in future releases.

    Live Search



    Since 5.0a the instant search results feature (Live Search) has been improved again and is now more reliable and able to show Wiki and Blog items as well as Documents, People and Sites in the list.



    [caption id='attachment_245' align='alignnone' width='226']Instant results in Live Search window. Instant results as you type in Live Search.[/caption]

    Filtered Search Results



    We first showed an early version of Filtered Search Results in Community 5.0.a built using Solr 1.4, but now with Solr 4 this is faster and more configurable. An administrator can use the interface to add/remove/configure facets, including size and date ranges, Site, Tag and your own custom property filters.



    [caption id='attachment_241' align='alignnone' width='632']Select and configure the filters to display in the search screen. Select and configure the filters to display in the search screen.[/caption]

    Suggest, Spell Check and Did you Mean?



    The search experience continues to improve in 5.0b with the addition of Solr 4 features 'suggest' and 'spell check/did you mean'. The 'suggest' feature performs real-time matching of the current phrase against text phrases extracted from the name, title, description and content of documents.



    [caption id='attachment_246' align='alignnone' width='640']Suggestions based on the search text. Suggestions based on the search text.[/caption]



    Suggest in combination with Live Search mean that exploratory searching is now genuinely useful and fun in Alfresco! The 'spell check' and 'did you mean' features will help the user correct simple spelling mistakes and suggest possible results from the index if no results for the specified query were found.



    [caption id='attachment_240' align='alignnone' width='532']My spelling is not good, but Alfresco is going to help me here! My spelling is not good and didn't match a thing, but Alfresco is going to help me out here![/caption]



    [caption id='attachment_243' align='alignnone' width='537']Did you mean this? Did you mean this?[/caption]



    If you can't find what you want now in Alfresco, it probably isn't there! As well as inline actions which appeared in 5.0a, document content can now be previewed directly in the search results page without navigating away. It's worth mentioning that even if you do leave the results page, the various filters, settings and sorting features you have applied are fully bookmarkable in your browser and you will be able to navigate back as needed.



    [caption id='attachment_244' align='alignnone' width='640']html5 preview Inline HTML5 document previews - without leaving the search results page.[/caption]

    Alfresco Surf 5.0 and Aikau framework



    Alfresco Surf is now part of the main Alfresco HEAD codeline and built as part of core Alfresco maven build. Surf has had a number of performance and memory usage improvements. All of which are easier to manage going forward now it is part of the main Alfresco build. The Aikau framework continues to improve - the new search results page and components are built using it. Developer docs are starting to appear and there are plenty of blog post tutorials also.

    Activities



    Activities are now generated by Share when a user previews or explicitly performs a Download action. This generates quite a lot more activities - so the number that can be processed by the repository has been improved and in the Share Activities dashlet a roll-up of similar activities now occurs. Also similar activities are rolled up in the notification emails that are generated.



    [caption id='attachment_242' align='alignnone' width='632']Read and Preview activities - roll-up in Activities dashlet. Read and Preview activities - roll-up in Activities dashlet.[/caption]

    Analytics



    A taster of the forthcoming analytics features in Alfresco can be found in this version with two new Share dashlets; Site File Type Breakdown and Site Contributor Breakdown. A lot more will be coming around analytics in future Alfresco releases.



    [caption id='attachment_239' align='alignnone' width='414']Pretty graphs, you love pretty graphs right? Pretty graphs, you love pretty graphs right?[/caption]

    Internals



    In Alfresco 5 we have made a big push on upgrading dependent libraries - a lot of developers have been asking for this. As well as moving to Solr 4.9.1, we have upgraded to Spring 3.2.10, myBatis 3.2.7, OpenCMIS 0.11.0, Activiti 5.16.2 and many more. The bundled JDK is 7.0.67 and office document transformation quality and performance is improved using LibreOffice 4.2.5.

    Miscellaneous



    Various UI tweaks and improvements, including Share header area clean-up.

    Installed 3rd party components



    Java 1.7.0_67, Tomcat 7.0.53, PostgreSQL 9.3.5, LibreOffice 4.2.5, ImageMagick 6.8.6

    Translations



    As usual the Share translations have been improved and updated. Translations include: French, German, Italian, Spanish, Japanese, Dutch, Russian, Norwegian, Simplified Chinese, Brazilian Portuguese.

    Bugs



    Lots of bugs have been fixed as usual. See the list of fixed bugs since the 5.0a release.

    In Summary



    We hope you enjoy this Alfresco Community release! Please leave feedback on the forums as usual and raise bugs in JIRA.

    Introduction



    This is one post in a series of short examples of things that can be done using the Aikau framework. The series is not intended to provide complete documentation but simply to show how to solve frequently encountered problems, implement repeating UI patterns, showcase how to use existing widgets or how to create new ones.

    Real World Use Case



    There are already pages in Share for handling Data Lists, but they haven't been updated for many releases.  They are therefore a good candidate to demonstrate how it's possible to re-use Aikau widgets to rebuild (and enhance) existing Share features. It may also be necessary to extend the existing Aikau widgets to achieve specific use cases.

    Example



    This post picks up where the last one left off. We've now got the basic Data List functionality for reading Data Lists and all that remains is to tweak the layout and handle creation and deletion of Data List items.

    Using Library Files



    We want our page to look like it belongs in Share and this means adding in the header and footer (unless of course you want to use the 'hybrid' template that automatically adds these for you - to do this, render the page using 'share/page/hdp/...' rather than 'share/page/dp/...'). To include the header and footer we need to import the library files provided and then call the functions they provide to set up the initial page model:

    <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'>



    var services = getHeaderServices(),

        widgets = getHeaderModel('Data Lists');

    model.jsonModel = getFooterModel(services, widgets);



    It's essential that imports as the first lines of your WebScript controller (not even comments can come before or between them) otherwise an error will be generated. When calling 'getHeaderModel' we can optionally provide a page title as we are doing here.

    Layout



    We're going to tweak the layout by using a combination of 'alfresco/layout/HorizontalWidgets' and 'alfresco/layout/VerticalWidgets' to place our widgets as we want them on the screen. There are other widgets in the 'alfresco/layout' package that we could use as well - one option would have been to use the 'alfresco/layout/AlfSideBarContainer' widget to add in a resizeable sidebar - but in this case I'm just fixing a sidebar using applying a fixed width to a child widget in a HorizontalWidgets instance as described in this previous post. Since it's already been covered I'm not going to include the code here, but please download the example code to review it.

    Adding a New Item Button



    This was actually the most interesting part of this example because we need to vary the published payload of a 'New Item' button for all of the different Data List types and instances. This is because we needed to include the NodeRef of the Data List to add an item to as well as the specific form controls for the Data List type that was currently being shown.



    I considered a variety of alternatives to solve this and each had their merits, but I decided to extend 'alfresco/button/AlfButton' to support the use case but eventually decided that the new widget was such a valuable addition that I added it straight into the main Alfresco code base. This in itself demonstrates another benefit of Aikau - because all the code is so decoupled it is so much easier to accept contributions of individual widgets without causing any major disruptions.



    Anyway... the new widget is 'alfresco/buttons/AlfDynamicPayloadButton' and allows you to create subscriptions to topics that update the payload on publication. In this case we are able to subscribe to the 'BLOG_LOAD_DATA_LIST' topic that was setup in the previous blog post to set the 'alf_destination' request parameter as follows:

    ...

    publishPayloadSubscriptions: [

      {

        topic: pubSubScope + 'BLOG_LOAD_DATA_LIST',

        dataMapping: {

          nodeRef: 'formSubmissionPayloadMixin.alf_destination'

        }

      }

    ],...


    In this example we listen for publications on the (scoped) 'BLOG_LOAD_DATA_LIST' topic and then update the publication payload of the button when the topic is published on. The 'nodeRef' attribute of the published payload is mapped to the 'formSubmissionPayloadMixin.alf_destination' attribute of the buttons 'publishPayload' attribute (note that we can use dot-notation syntax to set nested attributes).

    Dynamic Button Visibility



    We need to create a new button for every Data List type (because each Data List type expects different properties and therefore we need to specify different form controls) so we end up with more than one button. So that only one button is displayed at a time we use the 'visibilityConfig' (described in this previous blog post) to only show the appropriate button for the currently selected Data List:

    ...

    visibilityConfig: {

      initialValue: false,

      rules: [

        {

          topic: pubSubScope + 'BLOG_LOAD_DATA_LIST',

          attribute: 'itemType',

          is: [dataListTypes[i].name]

        }

      ]

    }

    ...


    Note that we're triggered off the same topic that is published whenever a user clicks on a specific Data List and that our rule is based on the name of the current data list (see the full code, but 'dataListTypes' is the array of Data Lists being iterated over to build the buttons).



    We also set an initial visibility of false so that it's not possible for a user to try and create a new Data List item until a Data List has been selected.

    Finishing Touches



    I've also added an 'alfresco/renderers/PublishAction' widget to each rendered item as was done for the Data List list. However, since that has already been described in this previous blog post I won't go into it again. However it is worth mentioning that I could have added another instance for opening an edit dialog.



    This obviously isn't the finished article and doesn't provide all the capabilities that the existing Data List page in Share provides. The idea is to illustrate that it's relatively easy to build functional pages for an Alfresco Repository using Aikau by simply re-using existing REST APIs and widgets. Hopefully we'll be able to use Aikau to re-write Data Lists (as well as the other site pages) in a future Alfresco release.

    Example In Action



    This is defined in a JavaScript controller for a WebScript mapped to the /datalist2 URL. When deployed to a Share (or any Surf based Aikau application) this can be accessed by the URL: http://localhost:8081/share/page/dp/ws/datalist2



    This screenshots shows the page in action:



    DataList2_1



    DataList2_2



    DataList2_3



    Download the complete example code here.



     



    Filter Blog

    By date: By tag: