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

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. Since I'd started using Data Lists as a case study for demonstrating the 'alfresco/services/CrudService' I realised that I may as well continue on the path of re-creating the page using Aikau - especially since it demonstrates more techniques that might be required by Alfresco developers, partners and community members.

Extending AlfList



We've already recreated the part of the Data List page sidebar and we now want to create the main section of the page that will show the currently selected Data List. It would be nice if we could just use another 'alfresco/lists/AlfList' widget for this main section but the problem is that each Data List uses different properties for their items. However, it is possible to re-use the same list but switch views for each Data List type.



Therefore we're going to create a custom widget that extends 'alfresco/lists/AlfList' and to do so we should add in our 'blog' package for our custom widget (see this previous post for details).



The main requirements of our custom list is to handle Data List selection and switch views to match the appropriate Data List type. Our 'blog/DataList' widget is declared as follows:

define(['dojo/_base/declare',

        'alfresco/lists/AlfList',

        'dojo/_base/lang'],

        function(declare, AlfList, lang) {



  return declare([AlfList], {


Now we need to override and add functions to satisfy our requirements:

postMixInProperties: function blog_DataList__postMixInProperties() {

  this.inherited(arguments);

  this.alfSubscribe('BLOG_LOAD_DATA_LIST', lang.hitch(this, this.loadDataList))

},


Note that we call 'this.inherited(arguments)' to execute the extended modules function of the same name. This can be omitted if you want to override rather than extend the function. We then call the 'alfSubscribe' function and bind the 'BLOG_LOAD_DATA_LIST' topic to the 'loadDataList' function using Dojo's lang.hitch utility function (so that when 'BLOG_LOAD_DATA_LIST' is published on, the 'loadDataList' function will be called).

loadDataList: function blog_DataList__loadDataList(payload) {

  this.dataListToLoad = payload;

  this.onViewSelected({

    value: payload.itemType

  });

  this.loadData();

},


The 'loadDataList' function assigns the published payload to the 'dataListToLoad' instance variable (so that it can be referred to later) and switches to the appropriate view by calling the 'onViewSelected' function inherited from the 'alfresco/lists/AlfList' widget. The the 'loadData' function (also inherited) is called to request the data.

loadData: function alfresco_lists_AlfList__loadData() {

  if (this.dataListToLoad != null)

  {

    this.inherited(arguments);

  }

},


This function needs to be extended to prevent attempts to load data when no Data List has been set. Note that we are able to conditionally decide whether or not call the inherited function.

updateLoadDataPayload: function blog_DataList__updateLoadDataPayload(payload) {

  if (this.dataListToLoad != null)

  {

    payload.url = 'slingshot/datalists/data/node/' + this.dataListToLoad.nodeRef.replace('://', '/');

  }

}


Finally we override the 'updateLoadDataPayload' extension point function to set the URL to load. This is a good example of how Aikau intentionally provides functions purely for developers to extend so that they don't need to copy and paste existing code.

Pub/Sub Scoping



Since we're going to be having two instances of the AlfList on the page we need to ensure that their publication/subscription (pub/sub) communications are scoped to prevent cross-communication. Therefore when we define our 'blog/DataList' in the JSON model we will set a specific 'pubSubScope'.

var pubSubScope = 'DATA_LIST_SCOPE';



var dataList = {

  name: 'blog/DataList',

  config: {

    pubSubScope: pubSubScope,

    loadDataPublishTopic: 'ALF_CRUD_CREATE',

    itemsProperty: 'items',

    widgets: buildViews()

  }

};


Configuring Views



Note that we're also calling a 'buildViews' function that you can review in the full example (see download link at end of post) but I won't list here. In summary we need to build a view for each Data List type that we know about. Each view is given an ID of the Data List type (e.g. 'dl:eventAgenda'). Views were described in this earlier post so I won't describe them here, other than to say that we are adding an 'alfresco/renderers/Property' for each property in the Data List type.



One addition to the view construction previously described is that we are also setting the 'widgetsForHeader' attribute for each widget (to create column headers) and setting the 'additionalCssClasses' attribute on each view to make it more tabular in app%MCEPASTEBIN%earance.

Updating the Link



Lastly we need to update our previously created list to make our 'alfresco/renderers/InlineEdityProperty' an 'alfresco/renderers/InlineEdityPropertyLink' so that when it is clicked it publishes the request to load the Data List it represents.

dataListTitle.name = 'alfresco/renderers/InlineEditPropertyLink';

dataListTitle.config.linkPublishTopic = pubSubScope + 'BLOG_LOAD_DATA_LIST';

dataListTitle.config.linkPublishPayloadType = 'CURRENT_ITEM';


Note that we're re-using the 'pubSubScope' variable so that we're publishing into the scope that our 'blog/DataList' is using.

Example in action



This is defined in a JavaScript controller for a WebScript mapped to the /datalist 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/datalist



These screenshots shows the page in action:



DataList1



 



DataList2



 



DataList3



Download the complete example here.

Next Steps



In the next post we'll make further updates to improve the layout and add support for creating Data List items.

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



Sometimes you want to be able to create things. And sometimes you want to be able to do this via a form dialog without going to a new page.

Example



This post picks up where the last one left off. I'm going to assume that you've already worked your way through the post or have at least downloaded the example JAR and taken a look at the code. We've already built a basic list of our Data Lists with the ability to delete individual items and inline edit the title of each Data List. We now want to provide the ability to create new Data Lists to add to our list.

Getting the Data List Container



Data Lists in Share are created within sites and each Data List is a new node created within a 'container' folder within the site. When creating a new Data List we need to know the nodeRef of that container to include in our POST request. Since the REST API doesn't support creation using site and container IDs we'll need to obtain the container nodeRef in our WebScript. This doesn't actually relate to Aikau in any way other than to show you that you can build data on the server before sending it to the client:

var alfDestination = null;

var result =

  remote.call('/slingshot/datalists/lists/site/datalistexample/dataLists');

if (result.status.code == status.STATUS_OK)

{

  alfDestination = JSON.parse(result).container;

}


If I was doing this for real, I'd probably want to use more defensive code but the snippet above is sufficient for the purposes of this example.

Create a form



We now want to create the form controls to capture the data required for our POST request:

var formControls = [

  {

    name: 'alfresco/forms/controls/DojoValidationTextBox',

    config: {

      name: 'alf_destination',

      value: alfDestination,

      visibilityConfig: {

        initialValue: false

      }

    }

  },

  {

    name: 'alfresco/forms/controls/DojoValidationTextBox',

    config: {

      label: 'Title',

      name: 'prop_cm_title',

      requirementConfig: {

        initialValue: true

      }

    }

  },

  {

    name: 'alfresco/forms/controls/DojoTextarea',

    config: {

      label: 'Description',

      name: 'prop_cm_description'

    }

  },

  {

    name: 'alfresco/forms/controls/DojoSelect',

    config: {

      label: 'List Type',

      name: 'prop_dl_dataListItemType',

      value: 'dl:event',

      optionsConfig: {

        publishTopic: 'ALF_GET_FORM_CONTROL_OPTIONS',

        publishPayload: {

          url: url.context + '/proxy/alfresco/api/classes/dl_dataListItem/subclasses',

          itemsAttribute: '',

          labelAttribute: 'title',

          valueAttribute: 'name'

        }

      }

    }

  }

];


Since i've already covered the basics of defining forms I'll focus on just the last control which is the 'alfresco/forms/controls/DojoSelect'.  The key thing to note about this is the 'optionsConfig' section which allows us to define how to retrieve and render the options to make available to the user.



We're going to use the 'alfresco/services/OptionsService' (which will be added into the page's JSON model later) to handle requests for options. This service is designed to return data in the structure that form controls expect and allows you to specify the:



  • itemsAttribute - a dot-notation property to look-up in the JSON response body that identifies an array of options (setting as the empty string indicates that the entire response is an array)


  • labelAttribute - the dot-notation property to use in each option as the label


  • valueAttribute - the dot-notation property to use in each option as the value


Use a dialog



We want to add a button to the page that will popup a dialog containing our form. Dialogs can be created using the 'alfresco/dialogs/AlfDialogService' and the is a topic just for form specific dialogs:

var button = {

  name: 'alfresco/buttons/AlfButton',

  config: {

    label: 'New List',

    additionalCssClasses: 'call-to-action',

    publishTopic: 'ALF_CREATE_FORM_DIALOG_REQUEST',

    publishPayloadType: 'PROCESS',

    publishPayloadModifiers: ['processCurrentItemTokens'],

    publishPayload: {

      dialogTitle: 'New List',

      dialogConfirmationButtonTitle: 'Save',

      dialogCancellationButtonTitle: 'Cancel',

      formSubmissionTopic: 'ALF_CRUD_CREATE',

      formSubmissionPayloadMixin: {

        url: 'api/type/dl%3AdataList/formprocessor'

      },

      fixedWidth: true,

      widgets: formControls

    }

  }

};


When clicked the 'alfresco/buttons/AlfButton' will publish the configured payload on the specified topic. Hopefully you now recognise many of the attributes from the previous blogs posts (e.g. 'publishTopic', 'publishPayload', 'publishPayloadType', 'publishPayloadModifiers') so I'll focus on the dialog specific attributes.



The 'dialogTitle', 'dialogConfirmationButtonTitle' and 'dialogCancellationButtonTitle' should hopefully be self-explanatory. The 'widgets' are just the form controls that we have previously defined (it's not necessary to create the 'alfresco/forms/Form' since the DialogService handles this for us).



Whenever the confirmation button on the dialog is clicked the value of the form will be published on the 'formSubmissionTopic' (which in this case will be handled by our trusty 'alfresco/services/CrudService') and additional data can be 'mixed in' via the 'formSubmissionPayloadMixin' (in this case the actual URL that the CrudService will need to POST to).

Put it all together



Now we just need to add the new definitions and services into our page:

model.jsonModel.services.push('alfresco/dialogs/AlfDialogService',

                              'alfresco/services/OptionsService');

model.jsonModel.widgets.splice(0, 0, button);


Example in action



This is defined in a JavaScript controller for a WebScript mapped to the /crudServiceCreate 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/crudServiceCreate



These screenshots shows the page in action:



DataListCreate1



DataListCreate2



DataListCreate5



DataListCreate4



You can download the entire example here.



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



Sometimes you want to be able to update things. And sometimes you want to be able to update things directly from where you see them listed.

Example



This post picks up where the last one left off. I'm going to assume that you've already worked your way through the post or have at least downloaded the example JAR and taken a look at the code. We've already built a basic list of our Data Lists and provided the ability to delete Data Lists by clicking an icon associated with each entry. We now want to switch the existing 'alfresco/renderers/Property' widget for an 'alfresco/renderers/InlineEditProperty' widget to allow inline editing, this is done as follows:

var dataListTitle = widgetUtils.findObject(model.jsonModel.widgets, 'id', 'DATA_LIST_TITLE');

dataListTitle.name = 'alfresco/renderers/InlineEditProperty';

dataListTitle.config = {

  propertyToRender: 'title',

  postParam: 'prop_cm_title',

  refreshCurrentItem: true,

  requirementConfig: {

    initialValue: true

  },

  publishTopic: 'ALF_CRUD_CREATE',

  publishPayloadType: 'PROCESS',

  publishPayloadModifiers: ['processCurrentItemTokens', 'convertNodeRefToUrl'],

  publishPayloadItemMixin: false,

  publishPayload: {

    url: 'api/node/{nodeRef}/formprocessor',

    noRefresh: true,

    successMessage: 'Update success'

  }

};


Let's break down the configuration as we did before:

Publication Updates



The changes here are largely similar to those that were made in the DELETE example. One curiosity here is that we have to use the 'ALF_CRUD_CREATE' topic to perform a POST request rather than an 'ALF_CRUD_UPDATE' topic because the FormsProcessor doesn't support PUT ! The other configuration attributes of note are that we set 'publishPayloadItemMixin' to false to change the default behaviour of including the 'currentItem' object in the publication (because it would confuse the FormsProcessor) and that we set 'noRefresh' to true in the payload to override the default behaviour of publishing a reload topic to refresh the list.

InlineEditProperty



As it's name suggests this widget allows inline editing of properties. This widget (like all Aikau widgets) has been written to be extendable (the 'alfresco/renderers/InlineEditSelect' is an example of extending it). Essentially it just uses an 'alfresco/forms/Form' widget (again - an example of how Aikau aims to re-use as much code as possible) so that we leverage all the power of the forms handling widgets. The main configuration settings to note are that we set 'postParam' when the 'propertyToRender' doesn't match the expected request parameter on the REST API (if 'postParam' is omitted then the request parameter name will be the configured 'propertyToRender' value. Setting 'refreshCurrentItem' to true we ensure that any changes are applied to any other widgets dependant upon the 'currentItem' object. The 'requirementConfig' is the same as is used in Aikau form widgets and this piece of configuration is just delegated onto the underlying form control (in this case an 'alfresco/forms/controls/DojoValidationTextBox').

Example In Action



This is defined in a JavaScript controller for a WebScript mapped to the /crudServiceUpdate 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/crudServiceUpdate This screenshots shows the page in action: DataListsUpdate DataListsUpdateInline DataListsUpdated   You can download the complete example here.

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, show case how to use existing widgets or how to create new ones.

Real World Use Case



Sometimes you want to be able to delete things. And sometimes you want to be able to delete things directly from where you see them listed.

Example



This post picks up where the last one left off. I'm going to assume that you've already worked your way through the post or have at least downloaded the example JAR and taken a look at the code.



We've already built a basic list of our Data Lists and now we want to provide an action icon that when clicked will allow us to delete a Data List. This is achieved by using the 'alfresco/renderers/PublishAction' widget. We're going to update the JSON model that was defined in the previous blog post to add a new cell to each row of data in our view:

var deleteCell = {

  name: 'alfresco/documentlibrary/views/layouts/Cell',

  config: {

    widgets: [

      {

        name: 'alfresco/renderers/PublishAction',

        config: {

          iconClass: 'delete-16',

          propertyToRender: 'title',

          altText: 'Delete {0}',

          publishTopic: 'ALF_CRUD_DELETE',

          publishPayloadType: 'PROCESS',

          publishPayload: {

            requiresConfirmation: true,

            url: 'slingshot/datalists/list/node/{nodeRef}',

            confirmationTitle: 'Delete Data List',

            confirmationPrompt: 'Are you sure you want to delete '{title}'?',

            successMessage: 'Successfully deleted '{title}''

          },

          publishPayloadModifiers: ['processCurrentItemTokens', 'convertNodeRefToUrl']

        }

      }

    ]

  }

};

var viewRow = widgetUtils.findObject(model.jsonModel.widgets, 'id', 'VIEW_ROW');

viewRow.config.widgets.push(deleteCell);


There's quite a lot of configuration in this example, so let's break it down into small chunks.

Configuring the DELETE Payload



Firstly we want to define the action itself. We're going to publish on the 'ALF_CRUD_DELETE' topic to which the 'alfresco/services/CrudService' subscribes and we want to identify the specific item that we want to delete.



When defining a publication payload you can optionally select a number of different payload types and here we are setting the 'publishPayloadType' attribute to be 'PROCESS' to indicate that we want to perform some processing on the payload.



A number of processors have been initially included in Aikau and we specify two of them in the 'publishPayloadModifiers' array:



  • 'processCurrentItemTokens' looks for all string data wrapped in curly braces (e.g. the {nodeRef} in 'slingshot/datalists/list/node/{nodeRef}') and converts it to the matching entry in the 'currentItem'.


  • 'convertNodeRefToUrl' replaces any occurence of '://' with '/' to make NodeRef data URL friendly.


Confirmation Prompting



The CrudService recognizes that DELETE actions may require a confirmation so we include a 'requiresConfirmation' attribute in the payload as well as the messages to use in the confirmation dialog title, confirmation dialog body and the subsequent success message (note that we're able to include 'currentItem' tokens in the message strings because the 'currentItem' is always included in the publication to the service).

Icon and Accessibility



The last part of the configuration to describe is that we specify the icon using the 'iconClass' attribute and the 'altText' for the icon. The 'propertyToRender' attribute is available for use as a message token in the alt text so we can provide meaningful text on the action.

Example In Action



This is defined in a JavaScript controller for a WebScript mapped to the /crudServiceDelete 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/crudServiceDelete



This screenshots shows the page in action:



DataListDelete



DataListDeletePromptYou can download the complete example here.



 



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



At some stage you're going to need to work with some data and Aikau is specially created to work with Alfresco repositories. The widgets are de-coupled from data by using client-side services as a go-between for getting and setting data on over the Alfresco Repository REST API. Most web applications are ultimately just lists and forms and Share is no different - one area that hasn't had a lot of work recently is the Data Lists in Sites. This area has lots of examples of getting and setting data in a number of different ways. This post and following posts will show how we can use Aikau to work with Data List data.

Example



In this example we're going to do nothing more complicated than just retrieving the list of Data Lists from the Repository. The purpose is just to introduce the 'alfresco/services/CrudService' module as well as basic list building.

Prerequisites



We could have started by actually using the CrudService to create some data lists, but it makes more sense to actually create some data to start of with so that you can compare the existing implementation with the Aikau version, so please do the following:



  1. Log into Alfresco Share


  2. Create a site


  3. Customize the site and add Data Lists to it


  4. Create some data lists (doesn't matter what they are).


Share Data Lists

Pick an API, any API...



Aikau provides a number of dedicated services for working with data (e.g. 'alfresco/services/DocumentService', 'alfresco/services/SearchService', etc.) which typically perform some additional work or error checking to the basic data manipulation. The 'alfresco/services/CrudService' is different in that it provides the raw capability to work with a REST API. Currently there is no dedicated service for working with Data Lists so we're going to use the CrudService for accessing the Data List APIs.

Service Dependencies



First we need to make sure that the CrudService is available on the page. This is done by including in in the list of services in the JSON model for the page.

model.jsonModel = {

  services: [

    'alfresco/services/CrudService'

  ]

};


The majority of services don't require any configuration so can be added to the list as a string. However, it's also possible to add services into the array as objects with name/config attributes as is done with widgets.

Define a List



The most simple form of list in Aikau is the 'alfresco/lists/AlfList' widget that provides basic list capabilities. This is extended by the 'alfresco/lists/AlfHashList' (that allows requests to be manipulated by the URL hash fragment) and the 'alfresco/lists/AlfSortablePaginatedList' (that provides support for sorting and pagination) which is in turn extended by specialist lists such as the 'alfresco/documentlibrary/AlfDocumentList' and 'alfresco/documentlibrary/AlfSearchList', etc. For this example we just need simple capabilities so the basic 'AlfList' widget is sufficient.

var list = {

  name: 'alfresco/lists/AlfList',

  config: {

    loadDataPublishTopic: 'ALF_CRUD_GET_ALL',

    loadDataPublishPayload: {

      url: 'slingshot/datalists/lists/site/datalistexample/dataLists'

    },

    itemsProperty: 'datalists'

  }

};


In this example we are essentially defining the data that we want to work with. The 'loadDataPublishTopic' defines the topic to publish to request data, and the 'loadDataPublishPayload' defines the payload to send when requesting data. We are using a topic defined by the CrudService and which is designed to work with the Alfresco Repository REST API so we do not need to include the full address, just the WebScript declared URL fragment.



It would be nice if all the Alfresco REST APIs used a consistent attribute for the arrays of data that they return but since this is not the case, we can instruct the AlfList to use a specific property in the response body to get the array from using the 'itemsProperty' configuration attribute (note: this will also accept dot-notation values).

Define a View



An 'alfresco/lists/AlfList' widget (and all it's descendants) can render multiple views of data. These are set as the 'widgets' config attribute and in order to actually display any data an AlfList needs to have a least one view configured.



It's possible to build almost any view and you can either re-use the existing view and data rendering widgets or build your own. In this case we're just going to build our view of data as a list of rows, each containing a single cell with a single data renderer.

var views = [

  {

    name: 'alfresco/documentlibrary/views/AlfDocumentListView',

      config: {

      widgets: [

        {

          id: 'VIEW_ROW',

          name: 'alfresco/documentlibrary/views/layouts/Row',

          config: {

            widgets: [

              {

                name: 'alfresco/documentlibrary/views/layouts/Cell',

                config: {

                widgets: [

                  {

                    name: 'alfresco/renderers/Property',

                    config: {

                      propertyToRender: 'title'

                    }

                  }

                ]

              }

            }

          ]

        }

      }

    ]

  }

}];


View definitions can get quite long and if you define a view that you want to re-use then it makes sense to extend 'alfresco/documentlibrary/views/AlfDocumentListView' and declare the widget model in your own subclass (see 'alfresco/documentlibrary/views/AlfSimpleView' and 'alfresco/documentlibrary/views/AlfDetailedView' as examples of this).



The reason that a lot of the list and view modules are defined in the 'alfresco/documentlibrary/' package is purely historical since most of the list and view code has been abstracted from Document Library specific use cases.



Finally we just need to pull all the individual bits together:

list.config.widgets = views;

model.jsonModel.widgets = [list]


Example In Action



This is defined in a JavaScript controller for a WebScript mapped to the /crudService 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/crudService



This screenshot shows the result:



Aikau Data Lists



Obviously this isn't especially exiting or indeed useful... the purpose is just to demonstrate how to access data. You can check out the page models for the document and search views for more complex examples of what is possible.



Download the complete example here.



 



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, show case how to use existing widgets or how to create new ones.

 

 

Real World Use Case

Ultimately most web applications boil down to just being lists and forms (given the REST based nature of HTTP) so it stands to reason that at some point you’re going to need to define a form. It’s quite likely that you want to build in some dynamic behaviour into your forms from simply not allow the user to POST invalid data to changing the state form fields as data is changed.

 

As well as providing many different form controls (and the means to easily implement complimentary additional controls) Aikau allows you to define complex form behaviour in a declarative way. This example will show a couple of form controls and a fraction of the capabilities to highlight the benefits of the publication/subscription model that Aikau utilises.

 

 

Example

In this example we’re going to defined a relatively simple form containing a text box for capturing an e-mail address and a checkbox that controls whether or not that text box is visible.

 

 

Define a form

Let’s start by defining the form widget:

var form = {
  name: 'alfresco/forms/Form',
  config: {
    showOkButton: true,
    okButtonLabel: 'Save',
    showCancelButton: false,
    cancelButtonLabel: 'Doesn't Matter',
    okButtonPublishTopic: 'PUBLISH_TOPIC',
    okButtonPublishGlobal: true,
    widgets: []
  }
};


In actual fact you could omit all of these attributes and Aikau would provide you with some sensible defaults, but for educational purposes I’ve included (almost) all the attributes available.

 

Define a text box

Now let’s add a basic text box and add it into the form:

 

var textBox = {
  name: 'alfresco/forms/controls/TextBox',
  config: {
    fieldId: 'EMAIL',
    name: 'email',
    label: 'Contact',
    description: 'Your e-mail address',
    placeHolder: 'e-mail',
    validationConfig: [
      {
        validation: 'regex',
        regex: '^([0-9a-zA-Z]([-.w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-w]*[0-9a-zA-Z].)+[a-zA-Z]{2,9})$',
        errorMessage: 'Valid E-mail Address Required'
      }
    ]
  }
};

form.config.widgets.push(textBox);

 

With the single exception of the “placeHolder” attribute all of the attributes are applicable to any Aikau form control. All form controls inherit from the “alfresco/forms/controls/BaseFormControl” module which defines the structure of form controls (e.g. labels, options, validation, behaviour, etc) it means that form widgets can be easily swapped without needing to re-write the configuration.

 

It should be relatively obvious what each attribute is doing. There is a short-hand version of defining validation configuration, but this style allows us to define multiple validators (although we’re only using the “regex” validator to check that the data maps to a valid e-mail address pattern).

 

Define a check box

Now define a checkbox that will control the visibility of the text box:

var checkbox = {
  name: 'alfresco/forms/controls/CheckBox',
  config: {
    fieldId: 'SHOW',
    name: 'showEmail',
    label: 'Show E-mail',
    description: 'Uncheck to hide the e-mail field',
    value: true
  }
};

form.config.widgets.push(checkbox);

 

Note that the attributes are the same as before but assigned different values. This time we’re also providing an initial value of true to ensure that the checkbox is initially in the checked state.

 

Define a visibility rule

Whenever a form control changes value it will publish the change within the scope of the form and other widgets can define rules that trigger updates based on those changes. Let’s update the text box with a rule that controls it’s visibility:

textBox.config.visibilityConfig = {
  initialValue: true,
  rules: [
    {
      targetId: 'SHOW',
      is: [true]
    }
  ]
};

It’s possible to set multiple rules, multiple “is” values (and indeed “isNot” values) to compare against. It is also possible to use the same rule structure for “requirementConfig” (whether or not the field is required) and “disablementConfig” whether or not the field is disabled.

 

Here we are simple defining a rule that says if the form control with the “fieldId” of “SHOW” is set with the value “true” then this form control should become visible (and should be hidden if it is set to any other value).

 

Create the page

 

Finally we need to add our widget definitions into the main page model:

model.jsonModel = {
  widgets: [
    form
  ]
};

 

Example In Action

This is defined in a JavaScript controller for a WebScript mapped to the /formExample 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/formExample

 

 

Here are some screenshots of the page. Note how when the e-mail address in invalid the 'Save' button is automatically disabled. When the checkbox is not checked the e-mail address field is hidden.

 

AikauEx5_1

 

AikauEx5_2

 

AikauEx5_3

 

Download the entire example here.

Download a PDF version of this post here.

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, show case how to use existing widgets or how to create new ones.

 

 

Real World Use Case

One of the most frequently asked questions we seem to be getting lately is whether or not we can render a different UI depending upon what groups the logged in user is a member of (also known as “role based rendering”).

 

This can range from simply displaying a warning instead of the page contents if the current user doesn’t have the privileges to be there, right up to fine grained differences between what different users see on the page.

 

 

Example

All widgets can be configured with a set of “renderFilter” rules that determine whether or not the widget is rendered (note: rendered not visible - if the renderFilter does not pass the widget will not be created). The rules are not constrained to just groups, it’s possible to define rules for any property that the widget has access to. However, two key pieces of information are always cascaded through the widget hierarchy and these are “groupMemberships” and “currentItem”.

 

This will be quite a convoluted example (aren’t they all?) but should at least show the options available for configuring renderFilters.

 

 

Pre-Requisites

First of all, log in as the Administrator and create a new group with the identifier “EXAMPLE” (under the covers Alfresco will actually call it “GROUP_EXAMPLE”). Then create two new users and add one (not both) to the group.

 

 

Create Warning Banner

In the JavaScript controller for the page define a warning widget that should be displayed if the logged in user is neither an Administrator or a member of the new example group.

 

 

var warning = {
  name: 'alfresco/header/Warning',
  config: {
    renderFilterMethod: 'ALL',
    renderFilter: [
      {
        target: 'groupMemberships',
        property: 'GROUP_ALFRESCO_ADMINISTRATORS',
        renderOnAbsentProperty: true,
        values: [false]
      },
      {
        target: 'groupMemberships',
        property: 'GROUP_EXAMPLE',
        renderOnAbsentProperty: true,
        values: [false]
      }
    ],
    warnings: [
      {
        message: 'You aren't in right group',
        level: 3
      }
    ]
  }
};


The “renderFilterMethod” defines whether to use AND or OR logic for the rules (e.g. ALL rules must pass in order for the widget to be rendered or ANY one of the rules must pass). There are two rules defined, one for the main Administrators group and one for the our newly created group. The “target” is set to be “groupMemberships” (one of the previously mentioned key attributes) and we also declare set “renderOnAbsentProperty” to be true so that the rule is passed if the group doesn’t appear in the “groupMemberships” object. Finally we set an array of values that the target property must be in order for the rule to be satisfied.

 

Create Menu Bar

Now define a menu bar that will be displayed if the user is an Administrator or a member of the new group.

var menuBar = {
  name: 'alfresco/menus/AlfMenuBar',
  config: {
    renderFilterMethod: 'ANY',
    renderFilter: [
      {
        target: 'groupMemberships',
        property: 'GROUP_ALFRESCO_ADMINISTRATORS',
        values: [true]
      },
      {
        target: 'groupMemberships',
        property: 'GROUP_EXAMPLE',
        values: [true]
      }
    ],
    widgets: []
  }
};

 

This time we are inverting the rules. The “renderFilterMethod” is set to ANY as they user just needs to be a member of one of the groups (although being a member of both groups is also fine). The “renderOnAbsentProperty” attribute is omitted because we want to be sure the user is a member of the group and the target value is switched from false to true.

 

Current Item Data

We’ll now define a custom “currentItem” object for our menu bar:

 

menuBar.config.currentItem = {
  test: {
    value: 'show'
  }
};

 

This data can then be used to determine whether or not to render a couple of menu bar items:

 

var menuBarItems = [
  {
    name: 'alfresco/menus/AlfMenuBarItem',
    config: {
      label: 'Should Appear',
        renderFilter: [
          {
            property: 'test.value',
            values: ['show']
          }
        ]
      }
    },
    {
      name: 'alfresco/menus/AlfMenuBarItem',
      config: {
        label: 'Should NOT appear',
        renderFilter: [
          {
            property: 'test.value',
            values: ['visible']
          }
        ]
      }
    }
];
menuBar.config.widgets = menuBarItems;

 

We’re able to omit the “target” attribute because the default behaviour is to check the “currentItem” object. This also shows how we can use dot-notation to address nested attributes in the “currentItem” object.

 

The first menu bar item should be displayed (because test.value is “show”) but the second should not be rendered (because test.value is not “visible”).

 

Build the Page

Finally let’s combine all the definitions into the JSON model for the page:

 

model.jsonModel = {
  widgets: [
    {
      name: 'alfresco/layout/VerticalWidgets',
      config: {
        widgets: [ warning, menuBar ]
      }
    }
  ]
};

model.jsonModel.groupMemberships = user.properties['alfUserGroups'];

 

The last line is the most important - we need to ensure that the “alfUserGroups” property is retrieved from the current user data (available in the user object since Alfresco 5.0).

 

Example In Action

This is defined in a JavaScript controller for a WebScript mapped to the /renderFilter 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/renderFilter

 

These screen shots show the two states depending upon which user has logged in:

 

AikauEx4_groupfail

 

AIkauEx4_menu_bar_item

 

Download the entire example here.

Download a PDF version of this document here.

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, show case how to use existing widgets or how to create new ones.



PLEASE NOTE: This requires at least Alfresco 5.0.b Community (or Alfresco 5.0 Enterprise when it is released).

Real World Use Case



Aikau heavy use of Dojo for its AMD module loading and many of the Aikau widgets use the Dojo widget templating and utility functions. That doesn’t mean that you have to use Dojo for your own widgets if you’d prefer to use other JavaScript libraries.

Simple Example



In this example we’re going to define an extension module that includes some JQuery libraries and then create a new Aikau widget that wraps an existing JQuery widget.

Build an Extension



First we need to download the JQuery and JQueryUI resources we want to make use of. In this example I have downloaded JQuery 1.11.1 and JQuery UI 1.11.1 and will wrap the Accordion widget. We’re going to need to define 3 new AMD packages for:



  • JQuery


  • JQuery UI


  • Our blog widgets


The extension looks like this and is included in the downloadable example JAR:

<extension>

  <modules>

    <module>

      <id>Add JQuery</id>

      <auto-deploy>true</auto-deploy>

      <evaluator type='default.extensibility.evaluator'/>

      <configurations>

        <config evaluator='string-compare' condition='WebFramework' replace='false'>

          <web-framework>

            <dojo-pages>

              <packages>

                <package name='blog' location='js/lib/blog'/>

                <package name='jquery' location='js/lib/jquery-1.11.1' main='jquery-1.11.1.min'/>

                <package name='jqueryui' location='js/lib/jquery-ui-1.11.1' main='jquery-ui'/>

              </packages>

            </dojo-pages>

          </web-framework>

        </config>

      </configurations>

    </module>

  </modules>

</extension>


I won’t go into any depth on what this configuration does as it has been discussed in this previous blog post.

Create Our Custom Widget



The JQuery UI website provides excellent examples of how to use the Accordion widget, but to make this a more realistic example we want to be able to include other Aikau widgets in each Accordion section.



Our widget comprises a JavaScript file and an HTML template file (although we could also include CSS and i18n files as well - see this previous post for details). The HTML template is just about as simple as possible containing nothing more than a single DIV element with a class that we could anchor specific selectors to.



The JavaScript file first declares our dependencies:

define(['dojo/_base/declare',

        'dijit/_WidgetBase',

        'dijit/_TemplatedMixin',

        'dojo/text!./templates/JQueryAccordion.html',

        'alfresco/core/Core',

        'alfresco/core/CoreWidgetProcessing',

        'jquery',

        'jqueryui'],

       function(declare, _WidgetBase, _TemplatedMixin, template, AlfCore, CoreWidgetProcessing, $) {


We’re going to construct the widget using the base Dojo templating capabilities provided by “dijit/_WidgetBase” and “dijit/_TemplatedMixin” and will then mixin in the Aikau modules “alfresco/core/Core” (which provides i18n handling, pub/sub, logging and data binding capabilites) and “alfresco/core/CoreWidgetProcessing” which provides capabilities for processing child widgets. Lastly we declare our JQuery dependencies, mapping the return value of the jquery module to $.

Dojo Widget Lifecycle



We then need to override two Dojo widget lifecycle attributes/functions.

templateString: template,



postCreate: function blog_JQueryAccordion__postCreate() {

  this.processWidgets(this.widgets, this.domNode);

  $(this.domNode).accordion();

},


“templateString” is set to be the template mapped from our HTML file (see the Dojo documentation for more information on this, but its essentially boilerplate code).



“postCreate” is the main function we need to override to actually do some work. This function is called once the widget DOM is available. We first call the “processWidgets” function (inherited from “alfresco/core/CoreWidgetProcessing”) to process any child widgets in the JSON model and then we use JQuery to create the Accordion against the root widget DOM node.

Aikau Override



In between those two lines executing the “createWidgetDomNode” function from “alfresco/core/CoreWidgetProcessing” will be called for each widget being processed. All Aikau widget functions are designed to be overridden for simple customizations and in this case we need to change the DOM that is constructed for each child widget to match the expectations of the JQuery widget. This is achieved by overriding the inherited function:

createWidgetDomNode: function(widget, rootNode, rootClassName) {

  $(rootNode).append($('<h3>' + widget.title + '</h3>'));

  return $('<div>').appendTo($('<div>').appendTo(rootNode))[0];

}


The normal behaviour of the function would just be to create a single DIV element, but our overridden function creates an H3 and and a DIV within a DIV. This last DIV element is returned to the next stage of wiget processing and will be the element that the child widget is added to.

WebScript Time



Now we just need to define an Aikau page that includes our new widget:

model.jsonModel = {

  widgets: [

    {

      name: 'blog/JQueryAccordion',

      config: {

        widgets: [

          {

            name: 'alfresco/logo/Logo',

            title: 'Alfresco Logo',

            config: {

              logoClasses: 'alfresco-logo-large'

            }

          },

          {

            name: 'alfresco/logo/Logo',

            title: 'Surf Logo',

            config: {

              logoClasses: 'surf-logo-large'

            }

          }

        ]

      }

    }

  ]

};


In this page adding two “alfresco/logo/Logo” widgets as children of an instance of our new “blog/JQueryAccordion” widget.

Example In Action



This is defined in a JavaScript controller for a WebScript mapped to the /jqueryWidget 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/jqueryWidget



Here is a screenshot of the resulting page:



AikauEx3_JQuery



Download the entire example here.



Download a PDF version of this blog here.

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, show case how to use existing widgets or how to create new ones.

 

 

Real World Use Case

There are many occasions where you might want to have a row of user interface components where you want each component to either retain the same width or dynamically adjust its dimensions as the browser window is resized. A typical example might be where you want a fixed with navigation sidebar and a dynamically resizing view port.

 

This isn't easily achievable with CSS and normally requires some JavaScript intervention. The Aikau alfresco/layout/HorizontalWidgets module make it easy to achieve these types of layout in a declarative and reliable fashion.

 

 

Simple Example

In this example we’re going to create a row of titled windows where each is configured with a slightly different width behaviour.

 

 

Fixed Widths

First lets define a widget with a fixed width:

var fixedWidth = {
  name: 'alfresco/layout/ClassicWindow',
  widthPx: '300',
  config: {
    title: 'Always 300px'
  }
};

 

The important thing to note here is that the width is defined outside of the “config” object. This is because it is an attribute relevant to the parent widget (the alfresco/layout/HorizontalWidgets instance) and not the widget itself). By specifying a “widthPx” attribute we are setting a fixed width that should not change as the window is resized.

 

Percentage Widths

Now let’s define a widget with a dynamic width:

var dynamicWidth = {
  name: 'alfresco/layout/ClassicWindow',
  widthPc: '50',
  config: {
    title: '50% after fixed deductions'
  }
};

 

This time we use the “widgetPc” attribute to set a percentage as the width. This will be a percentage of the available horizontal space after all fixed width widgets and widget margins have been deducted - it is not a percentage of the full window or parent width.

 

Automatic Widths

Now let’s define a couple more widgets that will automatically be assigned a width:

var auto1 = {
  name: 'alfresco/layout/ClassicWindow',
  config: {
    title: 'Share remainder'
  }
};

var auto2 = {
  name: 'alfresco/layout/ClassicWindow',
  config: {
    title: 'Share remainder'
  }
};


Neither of these widgets use the “widthPx” or “widthPc” attribute so will automatically be given an even share of whatever space is remaining after all fixed widget widths, widget margins and calculated percentage widget widths are deducted.

 

In this case because we have defined a width with a percentage width of 50% each of these widgets should get 25% of the remaining space after the other deductions are made.

 

Widget Margins

Finally, lets add all of the widget definitions into an “alfresco/layout/HorizontalWidgets” instance and give each widget a left and and right margin of 5 pixels:

model.jsonModel = {
  widgets: [
    {
      name: 'alfresco/layout/HorizontalWidgets',
      config: {
        widgetMarginLeft: '5',
        widgetMarginRight: '5',
        widgets: [ fixedWidth, dynamicWidth, auto1, auto2 ]
      }
    }
  ]
};

 

Example In Action

This is defined in a JavaScript controller for a WebScript mapped to the /horizontalLayout 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/horizontalLayout

 

This video shows it in action.

 

This entire example can be downloaded here for you to try out. Simply drop the JAR into your 'share/WEB-INF/lib' directory and restart the server.

 

Download a PDF version of this here.

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, show case how to use existing widgets or how to create new ones.

 

 

Real World Use Case

Whilst creating an example extension to embed advanced search forms into the standard filtered search page I wanted to be able to switch between simple and advanced search using a link. In order to do this I leveraged the “visibilityConfig” attribute that all widgets can be configured with (when nested in a parent widget). This is a simplified version of that use case.

 

 

Simplified Example

In this example we're going to create two logos that can be toggled between using two links, however only one link and one logo are ever displayed on the page at the same time. This is a trivial example but the technique can be easily expanded to cover much more complex scenarios.

 

 

Visibility Config

In my JavaScript controller file create a variable to representing the visibility topic that will be published on:

var toggleTopic = 'TOPIC';

 

Then create two JavaScript objects to represent the two different visibility configurations for each toggle state:

var showSurfLogoRules = {
  initialValue: false,
  rules: [
    {
      topic: toggleTopic,
      attribute: 'show',
      is: [true],
      isNot: [false]
    }
  ]
};

var showAlfrescoLogoRules = {
  initialValue: true,
  rules: [
    {
      topic: toggleTopic,
      attribute: 'show',
      is: [false],
      isNot: [true]
    }
  ]
};

 

Note that the “showSurfLogoRules” has an “initialValue” attribute of true and the “showAlfrescoLogoRules” has an “initialValue” attribute of false. This ensures that widgets are initialised in the correct visibility state depending upon whether they were to be shown or hidden.

 

Both configurations contain a single rule that subscribe to the same topic (defined earlier the eariler variable) and both identify the same attribute (“show”) in any payload published on that topic. The difference is that the “showSurfLogoRules” rule was looks for the attribute to be “true” and not false, and the “showAlfrescoLogoRules” rule looks for the attribute to be “false” and not “true”.

 

PropertyLinks

Then define a JSON model for two “alfresco/renderers/PropertyLink” widgets:

var showAlfrescoLogo = {
  name: 'alfresco/renderers/PropertyLink',
  config: {
    visibilityConfig: showAlfrescoLogoRules,
    currentItem: {
      label: 'Show Alfresco Logo'
    },
    propertyToRender: 'label',
    useCurrentItemAsPayload: false,
    publishTopic: toggleTopic,
    publishPayloadType: 'CONFIGURED',
    publishPayload: {
      show: true
    }
  }
};

var showSurfLogo = {
  name: 'alfresco/renderers/PropertyLink',
  config: {
    visibilityConfig: showSurfLogoRules,
    currentItem: {
      label: 'Show Surf Logo'
    },
    propertyToRender: 'label',
    useCurrentItemAsPayload: false,
    publishTopic: toggleTopic,
    publishPayloadType: 'CONFIGURED',
    publishPayload: {
      show: false
    }
  }
};

 

A PropertyLink widget is normally used within a list to provide links to perform actions (such as navigating to a page or opening a dialog) and would automatically be assigned a “currentItem” attribute for each item iterated over.

 

In this case we simply define a custom “currentItem” object containing a “label” attribute and then configure that attribute to be rendered by mapping it in the “propertyToRender” attribute. The “showAlfrescoLogo” link is assigned the “showAlfrescoLogoRules” visibility configuration and the “showSurfLogo” link is assigned the “showSurfLogoRules” visibility configuration so that they effectively hide themselves, but reveal the other PropertyLink when clicked.

 

Typically a PropertyLink will publish its “currentItem” object when clicked so this is overridden by setting the “useCurrentItemAsPayload” attribute to false and a new publication is configured to publish a payload containing a “show” attribute with the appropriate boolean value for the link.

 

Logo Widgets

Next define the “alfresco/logo/Logo” widgets to toggle between when using the links:

var alfrescoLogo = {
  name: 'alfresco/logo/Logo',
  config: {
    logoClasses: 'alfresco-logo-large',
    visibilityConfig: showSurfLogoRules
  }
};

var surfLogo = {
  name: 'alfresco/logo/Logo',
  config: {
    logoClasses: 'surf-logo-large',
    visibilityConfig: showAlfrescoLogoRules
  }
};

 

Finally add all the widgets into the model to be rendered as so:

model.jsonModel = {
  widgets: [
    {
      name: 'alfresco/layout/VerticalWidgets',
      config: {
        widgets: [
          showAlfrescoLogo,
          showSurfLogo,
          alfrescoLogo,
          surfLogo
        ]
      }
    }
  ]
};

 

Example In Action

This is defined in a JavaScript controller for a WebScript mapped to the /toggleLogo 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/toggleLogo

 

The following screenshots show the two toggle states.

 

AikauEx1_SurfLogo

 

AikauEx1_AlfrescoLogo

 

This entire example can be downloaded here for you to try out. Simply drop the JAR into your 'share/WEB-INF/lib' directory and restart the server.

 

Download a PDF version of this here.

Filter Blog

By date: By tag: