Moving Alfresco build to Maven

Well, this was a long journey, but we are finally getting there! As was already announced, Alfresco Community is now built with Maven, and so will the next version of Alfresco One. We thought we would tell you a bit more about it.

Achievements unlocked 

Alfresco is a BIG piece of software, and building it is not simple. There are several editions (Community, Enterprise, Cloud), several code generation steps, a huge number of external dependencies, etc. Also, a lot of quirks had accumulated in the Ant build over the years, as they do…

We couldn’t just stop releasing for the few months while we did the migration. The difficult part was thus to make it progressive! Here are the steps we took

  • June 2012: Deploying Alfresco artifacts to a Maven Repository
    This was a popular request by customers and partners, who were already using Maven or Gradle. Gabriele Columbro had already started the job. We extracted the jars and wars of all past releases and uploaded them to http://artifacts.alfresco.com/
  • November 2012: Adding dependency declarations
    Initially, the pom.xml files for these artifacts were empty. With Alfresco 4.1.2, we started writing and providing POM files that defined the dependencies, both between Alfresco artifacts and with 3rd party libraries. This was also the start of the parallel build: the same code was built in both Ant and Maven in our continuous integration, to make sure the POM files were correct.
  • July 2013: Separating test classes
    A big problem was that the main and test classes were mixed in the same source folders. Mao had find a nice hack to overcome that, by recreating a Maven standard layout on the fly in the target folder! But a complete separation was overdue, and we finally did it for Alfresco 4.2
  • December 2013: MyAlfresco built with Maven
    This happened under the hood. We had decided that the build of the Cloud version my.alfresco.com would be easier to switch to Maven: it only needs to support one platform, and the artifacts to build are much simpler. At the end of the year, the Maven build was deemed good enough to be put in production.
  • February 2014: Separating unit tests from integration tests
    This was mostly the work  of Gethin James, who was tired of typing -DskipTests… The default Maven build now fires only unit tests, so there is no need to worry about configuring the database or waiting for hours: Just type mvn clean install :-)
  • June 2014: Alfresco Community 5.0.a is now built with Maven!
Changes on your side

If you depend on Alfresco artifacts coming from the Maven Repository, then you will have to change a few things to your builds. Hopefully not too much…

First, the infamous classifier=config artifacts, which contained the resources of each jar file, have now gone. The resources are contained in the jar, as is standard in most places. If you depend on such artifacts, just remove them from your dependencies.

Second, the packaging of solr has changed. Instead of providing a zip that contains everything, we are again now conforming to the standard, and so alfresco-solr is simply a war file, which comes with its jar of classes (classifier=classes). Yes, and another artifact that contains the configuration (classifier=config), which should be unzipped on top of it if you need to run Solr.

At last, alfresco-mmt is now a standalone executable jar, rather than a small jar with dependencies on third-party libraries, for convenience

A new version of the Maven SDK is on its way, which will support Alfresco 5.0, along with a few added goodies. Thank you Ole Hejlskov for your help with it!

What now?

The Enterprise version of Alfresco still requires a bit of work. This will be the last step before we can remove the Ant build completely – including all these annoying committed libraries!

Finally, there are most certainly a few remaining issues: missing artifacts, packaging mistakes, regressions, etc. Please tell us if you find anything!

You can now download Alfresco Community 5.0.a. Its artifacts are of course in our Maven Repository. The version of Alfresco is now 5.0.a, and 5.0.b-SNAPSHOT for the nightly builds. We will keep the Ant-built 4.3.a-SNAPSHOT artifacts in the Maven repository for a while, to help the transition.

Thank you for your support in making it happen! We hope you are just as pleased as we are :-)

Updates to Page Creator Tool

by Dave Draper

Introduction

I’ve recently committed some updates to the Alfresco Community SVN repository that enable some new Aikau page creation capabilities in Share. These changes are only currently available (at the time of writing) in nightly builds of Community and you will need to download the updated PageCreator extension (you’ll need to remove the previous JAR!).

There are two new main features – the first is the ability to edit previously created page definitions (created using the drag-and-drop editor) and the second is the ability to export a page definition as a WebScript JavaScript controller.

Some Caveats

There are a couple of caveats to be aware of:

  1. The edit function can end up pushing around some fairly sizeable JavaScript objects and can take down Firefox if you happen to have Firebug running (some future optimizations are required to the code – but this is not supported production quality code yet!)
  2. The WebScript controller export capability only currently works with Chrome.

…so what I’m really saying is that you should probably try these features out with Chrome!

To edit a previously created page simply add a “page” request parameter onto the end of the URL where the value is the name of a previously created page, e.g:

  • http://<server>:<port>/share/page/hdp/ws/page-creator?page=my-previous-page

Where “my-previous-page” is the name of the page you want to edit. If that page doesn’t exist then the creator will fall back to creating a new page.

When exporting a page model as a WebScript controller it will take the page name and append “.get.js” to it as the name of the download that is generated (obviously you’ll need to create the WebScript descriptor and template files – although this should be relatively straightforward to do).

Brief Introduction to QuADDS

You might also notice that some new widgets have appears in the palette – and in particular you might be wondering what “QuADDS” is. I’m going to describe this in a future blog post but just to whet your appetite it stands for “Quick And Dirty Data Structures” and is a mechanism that enables you to persist and retrieve data objects that are derived entirely from the UI that you build. This means that it is possible to quickly prototype out Aikau pages without needing to build a custom model and the associated REST API and services.

If you want to try it out (before I get around to describing it in more detail) then you’ll need to create a new folder in the “Data Dictionary” called “QuADDS” and create and view pages as “admin”.

Summary

The Page Creator Tool is still very much a work in progress (and not necessarily something that will ever get into a production release) however the lack of an “edit” capability was obviously something that was required.

I’m grateful for any feedback on these updates and the Page Creator Tool in general – so please feel free to comment anything below (bugs, questions, ideas, constructive criticism, etc).

Unit Testing Aikau

by Dave Draper

Introduction

In my last blog post I revealed that “Aikau” was the project name for the updates we’ve been making to our approach to developing Alfresco Share and I briefly mentioned that we had been working with Intern.io to develop a unit test framework. That unit testing framework is now available in the latest Community source on SVN and I thought that it would be worth explaining how it works.

It’s still early days so it’s a little rough around the edges and we’ve yet to hit 100% code coverage (more on that later) but hopefully this will provide an insight into our commitment to improving the overall quality of our code and to reduce the number of future bugs that get released into the wild.

Evaluating JavaScript Testing Frameworks

We spent quite a bit of time looking at the available JavaScript unit testing frameworks that are available and I’m confident that we won’t have used everyone’s particular favourite. However, one thing we did notice that there is quite a considerable amount of overlap between the underlying technologies that they use (e.g. WebDriverJS, Chai, etc).

We settled on Intern.io simply because it appeared to be the best fit for our needs – that isn’t to say that we think it’s the best testing framework available or that it’s the right choice for everyone, however it does seem to be serving our needs nicely at the moment and we have had some excellent support via StackOverflow from it’s developers.

Our main goals were to be able to write functional tests for the Aikau widgets in the context of pages rendered by Surf in exactly the same way as they would be rendered in an application. We didn’t want to just test the pure JavaScript functionality as that would only be half the story.

We also wanted to be able to perform cross-browser testing – and whilst we’re still some way from achieving that completely, we are well placed to get there in the future – especially if we decide to make use of a service such as SauceLabs for our testing (in fact we’ve already had some degree of success in using SauceLabs already).

Getting Our Hipster On

Back in October 2013 the Alfresco UI team went to the Future of Web Apps conference and got an insight into some cool new technologies that we subsequently decided to incorporate into our development practices.

We’re making use of NPM for managing our packages and Grunt for controlling our development and test environment. The team develop using Windows, OS/X and various distributions of Linux so we use Vagrant to provide a common VM to test against. We’ve got CSS and JS linting ready to go and we use “node-coverage” to ensure that our unit tests are fully testing our modules.

How It Works

Each unit test is run against a specific page defined by a JSON model containing the widget(s) to be tested. The JSON model is defined as a static resource that is loaded from the file system by Node.js. We’ve created a new test bootstrap page that requires no authentication. This page contains a simple form and we use the Intern functional test capabilities to manually enter the JSON model into the form and then POST it to a WebScript. The JSON model is stored as an HTTP session attribute and the page redirects to a test page that retrieves the JSON model from the session attribute and passes it through the Surf dynamic dependency analysis to render a page containing the Aikau widgets to be tested. The unit test is then able to go about the business of interacting with the widgets to test their expected behaviour.

As widgets are de-coupled over a publication/subscription communication framework it is very easy to both drive widgets and capture their resulting behaviour through that same framework. Buttons can be clicked that publish on topics that a widget listens to and we make use of a special logging widget (“alfresco/testing/SubscriptionLog“) to test that it is publishing the correct data on the correct topics.

The de-coupling also allows us to test data rendering widgets without needing an Alfresco Repository to be present. Aikau widgets never make REST calls themselves – instead they use the publication/subscription model to communicate with client-side “services” that handle XHR requests and normalize the data. This means that a test page JSON model can include services that mock XHR requests and provide reliable and consistent test data. This makes setup and teardown incredibly straight forward as their is no need for a “clean” repository for each test.

Code Coverage

We’re making use of the “node-coverage” project to capture how well our unit tests are driving the Aikau code and we’ll admit that at the moment we’re not doing a great job (our goal is to ensure that any Aikau widget that gets included in a Share feature gets a unit test that provides 100% code coverage.

We’re able to get accurate coverage results by “instrumenting” all of the AIkau code and then loading a special test page that builds a layer containing all of that instrumented code. This is a very elegant way of resolving the issue of only getting coverage results for the widgets that are actually tested and a very useful benefit of using AMD for Aikau.

We’ve written Grunt tasks for instrumenting the Aikau code, running the test suites and gathering the results to make it really easy for us to collect the data – and the coverage results enable us to write truly effective and comprehensive tests (as well as identifying any “dead” code that we can trim).

Local, VM and Cross-Browser Testing

When writing the tests we typically run the tests against a local Selenium instance so that we can actually see the tests running as it’s incredibly useful to be able to see the mouse move and text being typed, etc. However, we make use of a Vagrant VM to run background tests during development so that we are able to continue to use our keyboards and mice – it’s also incredibly useful to have a consistent test environment across all the team members.

The Vagrant VM is Linux and only allows us to test Chrome and Firefox but in order to test multiple versions Internet Explorer we need to use a Selenium grid. We’re still working through whether or not to go with an internal Selenium grid or outsource to SauceLabs but when everything is in place we will be able to reliably test the whole of Aikau against all supported browsers.

Summary

One of the primary goals of Aikau is for it to be a truly robust framework and a reliable unit testing framework is key to that. Hopefully this blog post will have illustrated our commitment to that goal and also given you something to experiment with. In a future blog post I’ll provide a guide on how to get the tests running. In the meantime we’d appreciate any feedback you have on the approach that we’ve taken so far.

Introducing Aikau

by Dave Draper

Introduction

For some time now I’ve been writing blog posts that refer to the “updated UI framework” describing a new approach that we’ve been working on for further developing Alfresco Share. This is a fairly ambiguous (as well as lengthy) term to use to describe what we’ve been up to and we thought it would be sensible to come up with a project name that encapsulates the work that we’ve been doing. The framework is completely reliant on Surf so we wanted it to be somehow Surf related so after throwing a few ideas around settled on the name “Aikau“. This post is going to attempt to describe exactly what Aikau is and why we’ve been working on it.

What is Aikau?

Aikau refers to a method of creating new Surf Pages or Components comprised of small atomic widgets. The widgets are referenced in a JSON model that can either be defined in the JavaScript controller of a WebScript or as a document stored on the Alfresco Repository. Each widget is an AMD module that references it’s own CSS, HTML and i18n resources Surf will ensure that all of those resources are imported into the page loaded by the browser when that widget is referenced in the JSON model that defines that page.

The widgets are intentionally de-coupled over publication/subscription communication and event frameworks so that widgets can be easily added/removed/changed within a JSON model without causing any missing reference errors. Each widget is intentionally designed to implement a single piece of function, but can themselves define JSON models of child widgets so that it is easy to define re-usable composite widgets.

Each widget should be written with customization in mind – methods are kept short and variables abstracted to allow configuration and allow customization through targeted method overrides in extending widgets.

Aikau also provides an Intern.io based unit testing framework and each widget should have it’s own unit test (although we don’t yet have 100% code coverage) to ensure that functionality is not broken during development. We aim to make use of tools such as Grunt and Vagrant to allow continuous testing to run in parallel with development to catch breaking code immediately (although this effort has not yet been completed).

What Are The Goals?

The primary goal of of Aikau are to:

  • maximize code re-use
  • make UI development faster
  • make it incredibly simple to customize pages
  • allow continuous iteration of existing pages.

Aikau leverages the existing Surf extensibility capabilities to allow a default JSON model to be manipulated by 3rd party extensions. This allows pages to be heavily customized without needing to access or work with the source code of the page – it is possible to isolate the customization work to just the specific target areas.

Performance improvements should also be achieved by only loading the minimum amount of JavaScript and CSS data into the browser and by reducing the number of HTTP requests to an absolute minimum.

One of the main benefits of Aikau is that it allows you to quickly prototype pages with existing or simple widgets and then refine them over time. Functional pages can be easily constructed in minutes (and hopefully in the future entirely via a drag-and-drop page creation tool within an application itself).

Surf now supports Theme based LESS CSS pre-processing and widgets are written to be completely theme-able. This allows functional pages to be have their design modified repeatedly over time without needing to throw away existing work and start over from scratch.

How Does it Work?

There have been numerous blogs over the past year that have described the work we’ve been doing – although none of them specifically refer to Aikau by name, the knowledge is already out there!

Essentially Aikau is powered by Surf and uses the Dojo AMD loader and widget templating capabilities to provide a basis for dynamic dependency analysis. Each JSON model is dynamically analysed for AMD modules and those modules themselves are then analysed for their dependent modules (and so on, and so forth) until a complete dependency “layer” has been constructed for that page.

Surf caches module dependency relationships as well as page dependencies to ensure that only the minimum amount of analysis is done to maximise performance.

JavaScript Framework Ambivalence

Although Aikau makes heavy use of Dojo for the AMD loader and widget templating, it is by no means forces the exclusive use of Dojo. it extends the AMD paradigm to allow non-AMD dependencies to be easily referenced by widgets and is completely ambivalent to the JavaScript framework being used.

We have already made use of numerous other JavaScript frameworks and projects within the current suite of Aikau widgets including:

  • “Legacy” Alfresco JavaScript code
  • YUI2
  • JQuery
  • ACE
  • JSON Editor Online

Summary

It’s still early days, but we now have a framework and an initial set of widgets that make page development using Aikau a reality.  It’s unlikely that Aikau will ever completely replace the existing Share implementation but it is intended to work in harmony with it as well as providing the infrastructure to quickly develop alternative Surf based Alfresco clients.

The goal of this post is simply to allow us to talk about Aikau and for you to know what we’re referring to!

 

WebScript Localization Import Support Added to Surf

by Dave Draper

Introduction

This blog post is going to describe a simple but useful new feature that has been added to Surf. It is available in the latest Alfresco Community nightly builds and in the SVN repository. It is now possible for WebScript iocalization .properties files to include the contents of another file and have it’s content merged into the resulting ResourceBundle.

For a long time it has been possible to include “library” files in WebScript JavaScript controllers and FreeMarker templates but it was not possible to include “library” localization content. This meant that any localizations performed by the library files would either have to be duplicated in the including WebScripts properties files or added into a “global” properties files (such as “slingshot.properties“).

By enabling library files to be a combination of FreeMarker, JavaScript and localization files it means that you can create much more rounded library files.

Example Use Case

Currently we render the Share header bar as a Surf Component and are able to “inject” additional widget models into the page using a hybrid-template approach (where any content is rendered between a standard header and footer). However, there are issues with this in that the Dojo AMD loader ends up importing dynamically generated resources with duplicate paths (this is only a problem in terms of debugging and the number of bytes loaded – the pages will still function). A much more useful approach would be to have a header library file (as indeed we already have: “share-header.lib.js” that provides methods for creating the header widgets and services which can be included in page specific WebScripts (e.g. “share-header.get.js“).

The problem is that this relies on common properties being defined in the slingshot.properties files. For the header this isn’t a major problem as it rarely varies, however in other situations (e.g. the Document Library) we want to import a library but vary the localization keys. This means that we either have to duplicate keys across each page WebScripts (e.g. “Document Library”, “Shared Files”, “My Files”, “Repository”) or put all the keys in the core “slingshot.properties” file (which will then be loaded on pages even when not required).

A better solution is for the Document Library JavaScript library file to have a partner .properties file that can be imported into each specific WebScript….. and that’s exactly what this new capability provides.

Example

To declare an import simply add the key “surf.include.resources“, e.g.

surf.include.resources=org/alfresco/share/header/share-header.lib

…and you define multiple imports by delimiting each file with a comma, e.g.

surf.include.resources=org/alfresco/share/header/share-header.lib,org/alfresco/share/documentlibrary/doc-lib.lib

Note that you should not attempt to define any locale information. The correct locale specific file will be imported based on the locale of the browser (and will gracefully fall-back as you would expect). So in the above example if your browser is set to use the locale “en_GB” then Surf will first attempt to import the file “share-header.lib_en_GB.properties” and if it fails to find that file it will fall back first to ”share-header.lib_en.properties” and finally to “share-header.lib.properties“.

Caveats

There are some important caveats:

  1. Imports cannot import (e.g. if you import a properties file that contains the key “surf.include.resources” then those resources won’t be imported) – this is a good thing as it prevents circular dependencies
  2. Imports are processed and cached BEFORE extension modules are applied (this means that an extension can’t alter imported properties – however it can still override them)

Summary

It’s now possible to include library style properties files in WebScripts. This should hopefully make it easier to create more complete collections of library files that don’t rely on global or duplicated localization keys.

Surf Support for LESS CSS Pre-Processing

by Dave Draper

Introduction

At Summit 2013 I briefly mentioned that Surf provided support for simple CSS token substitution via the Surf Theme XML files. We’ve since integrated LESS for Java directly into Surf to allow dynamic LESS pre-processing to be performed. This capability is currently available in the Alfresco Community nightly builds and in the Alfresco Community SVN trunk. This isn’t going to be a blog post on LESS… if you don’t know what it is, or how to use it (and to be perfectly honest – I’m no expert myself!) then you can find plenty of information elsewhere on the internet… this post is simply going to explain how you can now make use of the LESS support that is available in Surf.

Surf Themes

Hopefully you’re aware that Share provides a number of different themes out-of-the-box and that many Community members have created their own. If you look in the “webapps/share/themes” folder of your installation you will find a number of sub-folders (“default“, “greenTheme“, “greyTheme“, “lightTheme“, etc) all of which hold the CSS and associated image files for rendering that theme. In the “webapps/share/WEB-INF/classes/alfresco/site-data/themes” you will find a number of XML files whose names correspond to those theme folders (e.g. “default.xml“, “greenTheme.xml“, etc. etc.). When a Share page is loaded it will load the “presentation.css” file for the current theme which will customize the appearance of the page (mostly this just effects the colour scheme).

Basic CSS Tokenization

In the XML files now support the element “<css-tokens>” in which you can define any number of custom elements the value of which will be substituted into any CSS file containing a token matching that element. For example, if you current theme XML file contains:

<css-tokens>
  <theme-font-family-1>Open Sans Condensed,Arial,sans-serif</theme-font-family-1>
</css-tokens>

…and you have a CSS source file containing…

.alfresco-example-CssSelector {
  font-family: $theme-font-family-1;
}

…then the CSS file loaded by the browser would actually contain:

.alfresco-example-CssSelector {
  font-family: Open Sans Condensed,Arial,sans-serif;
}

LESS Pre-Processing

There is one special token that is reserved for LESS pre-processing – “<less-variables>”. The value of this element is injected into a CSS file and dynamically “compiled” by LESS for Java when that CSS file is required (Surf caches the compiled CSS file so that this only occurs once for each file).

This means that if the theme XML file contains:

<css-tokens>
  <less-variables>
    @toolTipBorderColour: #e5e5e5;
  </less-variables>
</css-tokens>

…and the CSS source file contains:

.alfresco-example-CssSelector {
border: 1px solid @toolTipBorderColour;
}

…then the CSS file loaded by the browser would actually contain:

.alfresco-example-CssSelector {
  border: 1px solid #e5e5e5;
}

This is obviously a very simplistic example – you can actually do much, much more with LESS than simple substitution.

Summary

As well as the obvious benefits of being able to take advantage of CSS pre-processing we have implemented this so that our new AMD widget based approach for developing Share can be written in a modular way but still support externally defined themes.

At the time of writing we have not yet adapted all the new widgets that have been written to take advantage of the LESS pre-processing but are starting to make the transition so you will start to see code appearing in SVN soon.

We have yet to completely identify and define the LESS variables that will be defined by the themes but we will be sure to make the information available as and when we have them so that the community can update their own themes to ensure that the new widgets are rendered as they would wish.

 

Share Page Creation Code

by Dave Draper

Introduction

It’s taken a little while due the convergence of Cloud and Enterprise code lines that has been ongoing for some time, but I can finally now release the source code for the drag-and-drop page creation tool that I demonstrated at Summit 2013. The recent updates to Alfresco in the Cloud marked the final step of the convergence process and allowed some code that had been on a private branch for many months to make its way to the main SVN trunk.

The code I’m providing is simply a couple of WebScripts that define pages made up of widgets that are now available in SVN. The code is packaged up as a JAR file that can be dropped into the “webapps/share/WEB-INF/lib” folder of the web server that the Share application is running on and the pages will be available on the next server restart. You can download the JAR from here.

Pre-Requisites

NOTE: You need to have either built the latest Community code or have downloaded a nightly build – this will NOT work against any current Enterprise or Community release. The pages that you create will be stored on the Alfresco Repository and you will need to create a specific location in the Data Dictionary for them to be saved.

  1. As “admin” log into Share and click on the “Repository” link in the header menu
  2. Click on the “Data Dictionary” folder
  3. Select “Folder” from the “Create…” menu
  4. Enter “ShareResources” as the folder name (NOTE: no space, capital “R”) and click “OK”
  5. Click on the newly created “ShareResources” folder and repeat steps 3-4 but create a folder called “Pages” (NOTE: capital “P“)

Screenshot showing the Repository page in Share

The New Pages

The two pages provided are the JSON editor (http://<server>:<port>/share/page/hdp/ws/page-editor) and the Drag-and-drop Creator (http://<server>:<port>/share/page/hdp/ws/page-creator).  The JSON editor allows you to create pages by typing out the page model directly and the Drag-and-drop creator allows you to create pages using a simple GUI.

The JSON models for the pages themselves are defined in the following WebScript JavaScript controller files found in the JAR:

  • alfresco/site-webscripts/org/alfresco/share/page-creation/dnd-creator.get.js
  • alfresco/site-webscripts/org/alfresco/share/page-creation/json-page-editor.get.js

The JSON Editor

The actual editor used by the JSON Editor page is provided using the JSON Editor Online third party library. The “alfresco/site-data/extensions/extension.xml” file in the JAR file shows an example of adding in a new AMD package into Share which was described in more detail in this previous blog post. The JSON Editor AMD modules are referenced from the “alfresco/forms/controls/JsonEditor” widget that already exists in Share which is referenced by the “json-page-editor.get.js” file.

Try the following simple example to get you started:

{
  "widgets": [
    {
      "name": "alfresco/layout/ClassicWindow",
      "config": {
        "title": "My Window",
        "widgets": [
          {
            "name": "alfresco/logo/Logo",
            "config": {
              "logoClasses": "surf-logo-large"
            }
          }
        ]
      }
    }
  ]
}
  1. Copy and paste the JSON into the “Page Definitions” field
  2. Click on the “Preview” button (the preview should be drawn at the top of the page)
  3. Enter a name into the “Page Name” field (e.g. “FirstPage“)
  4. Click the “Save” button
  5. Go back to the “Repository” page and check that the page definition has been created.
  6. Open the following URL to view the page: http://<server>:<port>/share/page/hrp/p/FirstPage

Screenshot showing the JSON editor with page definition and preview

You should see the following:

Screen shot showing remotely loaded page

The DND Page Creator

The drag-and-drop page creator effectively does exactly the same as the JSON editor under the covers (e.g. it uses exactly the same Repository based REST API for saving the pages) but the key difference is that each widget that you drag from the “palette” to the “canvas” contains it’s own configurable snippet of JSON.

I’d recommend watching both the Share Page Creation Live and Share Widget Library sessions from Summit 2013 to get a better understanding of how to use the page creator because it will be much simpler than trying to explain it in writing!

The Underlying Code

I’m not going to initially explain the underlying code but will rather rely on any questions in the comments section to provide assistance. You should be able to review the JSON page definitions and match the widget references to the corresponding source files in “webapps/share/js/alfresco” (on the web server) or “slingshot/source/web/js/alfresco” (in SVN).

What should be apparent though is that the pages that are used to create pages are defined using the exact same JSON structure as they themselves render. One file that is worth reviewing is the “slingshot/config/alfresco/site-webscripts/org/alfresco/share/imports/widget-palette.lib.js” as this is contains all the JSON definitions for the items shown on the palette in the drag-and-drop page creator. The palette only contains a subset of the available widgets but hopefully should outline how it would be possible to define and include additional widgets for selection.

Summary

As I promised at Summit 2013 I’ve made the page creation code available as soon as possible. I appreciate that this blog post does not provide an in-depth discussion of the underlying code, but hopefully it is enough to whet people’s appetite to prompt me with questions for further posts.

 

WebScript JavaScript Controller Extensions Recap

by Dave Draper

Introduction

Following discussions with various Alfresco Community members over recent weeks I’ve realised that there is possibly a lack of understanding about the relationship between the controller and template in a WebScript related to when JavaScript controller extensions are processed. This post will attempt to clarify the relationship and explain how and why to use a JavaScript controller extension as well as providing another concrete example of how to extend the new header bar in Alfresco Share 4.2

Extensibility Recap

The JavaScript controller (or the Java bean controller if you’re writing a Java backed WebScript) has one primary purpose; to create a model object that is then passed to the template renderer (typically FreeMarker).

Even if your WebScript has an additional configuration file (e.g. one that ends “*.config.xml“) then it the controller is still responsible for processing that configuration and adding it to the model … a configuration file achieves nothing by itself!

In Alfresco 4.0 we introduced the ability to apply extension modules that could dynamically manipulate the controller, localization properties and FreeMarker templates. Customizing the FreeMarker templates was by far the most limited of these approaches as it relied on additional <@markup> directives being included in the templates. Unfortunately most of WebScript logic and client-side JavaScript widget instantiation code was defined in the FreeMarker templates which often made it difficult to achieve the desired results through pure extensions.

We completed refactored the Share WebScripts in Alfresco Community 4.2c to move all of the logic and client-side widget instantiation configuration into the controller which made it possible to have greater control over customizations via extension modules. In particular it meant that for the first time you could either reconfigure or replace the client-side JavaScript widgets with an extension module.

In Alfresco 4.2.e Community and Alfresco 4.2 Enterprise we have introduced a new approach to page creation where a JSON model of widgets to render is defined entirely in the JavaScript controller and the FreeMarker template is reduced to a single custom FreeMarker directive. This allows extensions much finer control over the page that make it possible to achieve a wider range of customizations through model manipulation.

The JavaScript Controller Extension

When writing a JavaScript controller you have access to a variable called “model“. Any attributes added to this object are available in the associated FreeMarker template, e.g.

model.myVariable="This is a variable"

…in a controller can be referenced in a FreeMarker template by:

${myVariable}

The controller can add any variables it likes to the “model” object but the refactored WebScripts make use of the “widgets” attribute (e.g. “model.widgets“) and the new page creation approach relies on the “jsonModel” attribute (e.g. “model.jsonModel“).

Surf will check for the existence of any extension controllers mapped to the WebScript before the FreeMarker template is renderer is called.

This means that one or more controller extensions can change the contents of the “model.widgets” or “model.jsonModel” attributes before they are processed by the custom directives in the template. The core controller will always be run to setup the default model and all applicable extension controllers will have their turn at updating the model before it is passed to the template.

If you’re writing an extension module that you want to distribute around the Alfresco Community then you need to be aware that other extensions might have changed the model in an unexpected way before your extension has it’s turn.

Real World Example

Let’s see this in action by adding a new menu item to the header in Alfresco 4.2 Enterprise. Information on how to create and deploy your extension module have been covered in detail before so I’m not going to go over those now, instead I’ll just focus on the controller extension itself.

If we want to add a new menu item (or indeed any new widget) we need to identify a parent widget to add it to. At the moment this still requires some knowledge of the Alfresco source code (although it is easy enough to identify the source files using SurfBug).

In this case we’re customizing the “share-header.get” WebScript whose controller imports the “share-header.lib.js” file which provides functions that return a model fragment of the header menu. It’s important to note that you don’t want to (and indeed can’t) extend the lib file directly – but instead much extend the controller that imports the lib file.

All of the widgets are given unique IDs in the JSON model so that they can be easily referenced. In this case we want to add a new “alfresco/menus/AlfMenuBarItem” widget to the “alfresco/header/AlfMenuBar” widget identified as “HEADER_APP_MENU_BAR“. For the purposes of this example it doesn’t really matter where the menu item takes the user, but in this case we’re going to use it to navigate to the user’s trashcan.

The controller extension should contain the following code:

var menuBar = 
 widgetUtils.findObject(model.jsonModel, "id", "HEADER_APP_MENU_BAR");
if (menuBar != null)
{
  menuBar.config.widgets.push({
    name: "alfresco/menus/AlfMenuBarItem",
    config: {
      label: "My Trashcan",
      targetUrl: "user/" + encodeURIComponent(user.name) + "/user-trashcan"
    }
  });
}

…and when the module is deployed you will see the following added to the Share menu:

Alfresco Share With Added Menu Item

Summary

The key thing to take away from this post is how JavaScript controller extensions fit into the the processing order of WebScript files and that the sole purpose of a JavaScript controller is to set up a default model that extending controllers can then manipulate.

 

 

What’s new in RM 2.1? (Part 3)

Unfiled Records

RM 2.1 makes record capture very easy, but we haven’t discussed where these captured records go or how the records management team deals with them.

To understand this lets take a look at the file plan.

File Plan

 

Those of you who are already acquainted with previous versions of the Alfresco Records Management module will recognize this familiar sight, with the more observant of you spotting the new “Unfiled Records” filter on the left hand side.

Unfiled Record Filter

An unfiled, or unclassified, record is one that not yet been filed, or placed, into it’s final location within the file plan.  The “Unfiled Records” filter shows the records management team all these unfiled records in one place, called the unfiled record container.  This area that can be thought of as a staging area for new records.

When a record is declared within a collaboration site behind the scenes it ends up here and awaits the records managements team attention.

The records management team has a few options when dealing with unfiled records:

  • File into the file plan – This moves a record from the unfiled record container into a record folder within the file plan where it will be classified by the appropriate disposition schedule.
  • Reject - This removes the unfiled record from the file plan.  In the case of an in-place record it will be returned it’s original location with information about why it was rejected.
  • Request more information - This allows the records management team to request more information about a new record via a workflow.  This workflow request is passed to the content authoring team and makes it easy for the records management team to gather missing information or understand the context of the record.

Unfiled Record Actions

So now we can see how the records management team deals with newly captured records,  but how do we automate these tasks to reduce their work load?

In part 4 we will take a look at automatic filling and how it can be used to dynamically build areas of the file plan based on record context.

 

Sample JS Docs for Share Widget Library

by Dave Draper

Introduction

Any new framework is going to live and die by the strength of it’s documentation, so it’s important that we try to ensure that we make as much information on the new Alfresco Share widget library available as possible. In an ideal world we’d have have a big team working on the widgets (and the supporting framework) that would include people dedicated to writing blogs, tutorials and documentation for it.  Sadly that’s not the case at the moment but we’re going to do the best we can to support the Alfresco Community in getting up to speed with this new approach for Alfresco Share development

Source Documentation

From the outset we wanted to ensure that we provided useful documentation within the source through commenting and JSDoc. Hopefully we’ve created well-commented code that is easy to read and understand but sometimes it’s better to read and navigate comments via a browser through linked files rather than in the source itself. Therefore we’ve added comments using a JSDoc3 supported syntax that allows us to build a HTML representations of the source documentation.

We haven’t included or published this documentation with the release of Alfresco Enterprise 4.2 for a few reasons:

  • We haven’t had the time to put together an Alfresco specific template for the HTML output
  • Many of the widgets are still of beta quality (as explained at Alfresco Summit and mentioned briefly in my last post)
  • We’re not completely satisfied with the current quality of the existing documentation

I also discovered whilst writing this post that there are still some syntax errors in the comments that prevents the released code from being used to generate the documentation. However, in the spirit of getting information out to the Community early I wanted to provide a sample of what to expect in future releases.

Writing Widgets Supporting JSDoc3

We encountered a few issues when initially trying to generate the JSDoc due to the AMD-style template that we were using for our source files. This means that we have had to adopt a specific use of the available tags to generate output that meets our needs.

You can browse the source files to get an idea of the tags that we’ve used, but if you are writing your own widgets and want to document them in a similar style then you should follow these broad guidelines:

  1. Use @module, @extends, @mixes and @author in the root comment block
  2. Use @instance for each function and variable
  3. Use @type and @default for each variable
  4. Use @param and @returns as appropriate for each function

Sample JS Doc

You can download a current snap-shot of the widget library documentation here. This was generated using the code currently on the Community SVN trunk (although I need to to fix a few tagging syntax errors that I will commit as soon as possible). Please bear in mind that this is very much a work in progress but as always we welcome your feedback. If you find any issues such as typos, missing links or specific areas where a better explanation is required then please let us know.