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

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.

For the last few months, the Information Governance team at Alfresco has been heads-down getting Records Management 2.5 released. Since then we've been tidying things up on our community code base.

 

New Community Release

Alongside our Enterprise RM 2.5 release, we’ve got a shiny new RM Community release too: RM 2.5.a. This version has been tested against the latest 5.1.g (201605-EA) and 5.2.a (201609-EA) Alfresco One Community releases, so although the pom specifies 5.1 as a dependency, we don’t expect any issues when running against 5.2. Please let us know if you find anything.

 

Download the zip: https://download.alfresco.com/release/community/201609-EA-build-00012/alfresco-rm-community-2.5.a.zip

Check out the docs: http://docs.alfresco.com/rm-community/concepts/welcome-rm.html

and see below for details of the code.

 

Community code now back on Github!

As I said in a previous post on my blog, we moved the RM code from SVN to our internal Git server recently (actually, it was about a year ago!) and since then, the SVN -> GitHub community mirror hasn’t been doing much.

 

We had a few issues getting the mirror set up due to the way we’d structured the new project, rm-community and rm-enterprise are both in the same repo which makes it much easier for us internally when we’re working on code that crosses projects, but with some creative python code from Tom Page, we were able to extract only the commits that make sense for the community and push them to our GitHub repo: https://github.com/Alfresco/records-management - please take a look at the latest code. We’re keen to receive pull requests, so if there are any bugs you find and want to submit a PR for, please do (and raise a JIRA: https://issues.alfresco.com).

 

Community Versions tagged

If you’re looking for the code for a specific RM Community version, then Tom's hackathon project will please you no end. He’s fixed the tags in GitHub, so you can go to the releases page for our new repo to see community tags for 2.3, 2.4 and 2.5 releases. If you really want to see versions older than that, you’ll need to go to our old repo and look at the tags there, but try to stick with the new stuff if at all possible - it’s much more fun to work with!

 

What’s next for RM Community?

Following news that version 3.0 of the Alfresco SDK is on its way, we’ve got an on-going development task to upgrade the RM project to use it. It’s still a work in-progress and we’re keeping Ole Hejlskov and Martin Bergljung in the loop as we have feature requests/bugs. The theory is that we (Alfresco Engineers) should be using the SDK for our own projects, so it makes sense to ensure that the RM module works with version 3.0. I’ll post back here once we’ve got more news on that.

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

In Alfresco, on our platform we support a large number of code versions, somewhere over 30. The platform is also big, made up of many moving parts. Naturally all of these can produce bugs. 

 

We also have considerable work on our enhancements, our small increments to the features and architecture of the software.

We bring quite a mix across from these sources into our scrum teams. We have to balance across:

  • Items the team discover they need to make improvements on how they work - things that come out of retrospectives
  • Feature items - new exciting features or assess, such as the new ReST API we are working on
  • Architecture items - to make it easier to support and add features
  • Maintenance work - issues that our customer, community and internal users find on previous versions - the 29+ branches we are supporting.

 

Relatively the first 3 are easy to size. We can use story points based effort using complexity as the most significant indicator, but realising some things are not complex, just hard work.

 

We wanted to ensure we really were reserving capacity for maintenance and across the three others. We were also faced with a challenge: How do we know we can forecast the number of maintenance items we can get done?

 

Everyone know it is very hard to size a bug! We are lucky that we have an excellent support part of our organisation, that triage the issues, reproduce them and capture loads of information. Still it is hard to estimate.

 

As a team we also do not want to include in our velocity fixing issues that we (or previous teams / members) let out the door. It does not seem right.

 

What we have done for a few sprints is use T-Shirt sizes for these issues. We use labels in our jira tickets. Then as we progress we discover more about the issue. At that stage we resize - in an agile way we re-estimate constantly, and revise our plans. So if we find a ticket is taking 3 plus weeks to get done, and we originally thought it was a T-Shirt size of Extra Small, we revise it to reflect the actual effort we spent.

 

We have been able to take the T-Shirt sizes and equate them to story points. This allows us to relatively get a ratio of the amount of work we are doing across the 4 sources of work and ensure we are taking care of our support obligation, while continuing to innovate the Open ECM market place with new architecture and features.

 

At the moment this way works well for us, however we will continue to improve and look to other ways of doing it.

 

How have you gone about it?

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.

Filter Blog

By date: By tag: