Tuesday, 21 January 2014

Magnolia, REST and angularjs. A proof of concept.

Lately I had to quickly prepare a PoC for a prospect showing how Magnolia can easily integrate with pure client-side technologies. We know that Magnolia 5 new shiny UI is built on Vaadin and that apps use Vaadin components to make a Java developer's life easier when it comes to build their UI. Still, if for whatever reason you need to work with pure Javascript, Magnolia won't let you down. 

The goal of the PoC was pretty simple - create an editor app which fetches the model (i.e. data from a JCR repository) and bind it to the view (a freemarker template in our case) by using an angularjs controller. The data can be edited inline, saved back to the JCR repository and finally published. As you may know, Magnolia recently released a pretty powerful REST API and, of course, in this scenario it was the best fit for the task of fetching and saving data. The REST API also allows to execute commands (once we explicitly allow this in the security app), so publishing wasn't a problem either. 

This is a screenshot showing the final result. Admittedly, it doesn't look fancy and it's far from being perfect but it will make our point.





So, on to the code! 


When the app presenter starts it will pass our view the workspace and the path of the currently selected page (a JCR node). Workspace and path are the two basic info we need to use the REST API (authentication here is not a problem as, when using our PoC app, we'll be already logged in). That info will be in turn passed as request parameters to the iframe source, so that eventually we will be able to use it in our editor freemarker template (of course we could have also used JSP there).


public void setParameters(String path, String workspace) {
        // Vaadin Iframe component
        BrowserFrame editor = new BrowserFrame(null, new ExternalResource("poc-editor?path=" + path + "&workspace=" + workspace));
        editor.setSizeFull();
        // clean previously set iframe
        root.removeAllComponents();
        root.addComponent(editor);
    }

The iframe source resolves to /.magnolia/poc-editor which is mapped to info.magnolia.poc.servlet.EditorServlet. This servlet will render our freemarker template. (As usual with Magnolia, the mapping is defined in the module descriptor at /src/main/resources/META-INF/magnolia/rest-ng-poc.xml [see the servlet tag]).


EditorServlet.java
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HashMap map = new HashMap();
        // expose the variables we need to use the REST API in the freemarker template.
        map.put("path", req.getParameter("path"));
        map.put("workspace", req.getParameter("workspace"));
        FreemarkerUtil.process("rest-ng-poc/pages/editor.ftl", map, resp.getWriter());
    }

editor.ftl injects the angularjs core lib and a controllers.js, that is a Javascript file holding all the ng controllers for our ng app. The ng app is called editorApp and in our case there's only one controller called PageCtrl. As we said above, an ng controller is the glue binding together the model and the view, so let's have a look at it.
(Here I assume you have a very basic understanding of angularjs. Their tutorial is a great place to get started and, on the other hand, I am no angularjs expert - I actually knew nothing about it before starting this PoC - so following should not be that hard.)


controllers.js
var editorApp = angular.module('editorApp', []);

editorApp.controller('PageCtrl', ['$scope', '$http', function ($scope, $http) {

  $scope.init = function(ctx, workspace, path) {
    $scope.path = path
    $scope.ctx = ctx
    $scope.workspace = workspace
    $scope.url = ctx + '/.rest/nodes/v1/'+ workspace + path

      $http.get($scope.url).
      success(function(data) {
        //console.log(data)
        $scope.page = data;
      }).
      error(function(data) {
        alert('ERROR: ' + data)
      });
    };

   $scope.save = function() {
     $http.post($scope.url, $scope.page).
     success(function(data) {
        alert('Page saved')
      }).
      error(function(data) {
        alert('ERROR: ' + data)
      });
   }

   $scope.preview = function() {
     window.open($scope.ctx + $scope.path)
   }

   $scope.publish = function() {
     // In order for this to work you need to grant "rest" role get&post for "/.rest/commands*"
     $http.post($scope.ctx + '/.rest/commands/v1/website/activate', {"path":$scope.page.path, "uuid": $scope.page.identifier, "repository":$scope.workspace}).
     success(function(data) {
       alert('Publication workflow started')
     }).
     error(function(data) {
       alert('ERROR: ' + data)
     });
   }

}]);

Our controller gets passed two services (in angularjs parlance) named $scope and $http. Those come of out-of-the-box with angularjs but you can write your own services if you need to. $http in particular will be used to call the REST API (for an alternative and "better" way of doing that, you can have a look at the $resource service).
Our PageCtrl is basically attaching its functions and variables to the $scope service. This will ensure that they will remain scoped to the page portion delimited by the special attribute ng-controller="PageCtrl". 

The interesting functions exposed here are

  • $scope.init 
    • Gets the model to initialise the view by calling the REST API and assigning the fetched data (the model) to the $scope.page variable. 
  • $scope.save
    • Saves the model, i.e. $scope.page via a REST call. That's one powerful feature of angularjs. We don't need to check ourselves for model changes, angular will do it for us through its internal observation mechanism. What we pass back to the rest API is actually only the REST url and $scope.page without worrying about anything else.
  • $scope.publish
    • Here we publish the current page via REST. The request body, i.e. the second argument to the $http.post function, will take an object with three properties named "path", "uuid" and "repository". Such parameters are needed by the activation command to perform its duty. Be aware that by default commands are disabled, therefore we will need to grant them to the rest role in the security app, else we'll get a 403 error.
And finally the freemarker template. Here we iterate over the model exposed in $scope.page (by filtering out everything but String properties) and assign the values to some html elements. The values we want to be able to change are bound to the model via the special attribute ng-model. This is doing the magic I was talking about a few lines above. It will basically observe the model for us and update it with any changes we'll make so that we won't need to worry about it when we'll want to persist it to JCR.
The complete Maven project is available here https://github.com/fgrilli/mgnl-rest-ng-poc for you to explore and try out.

To sum it up, Magnolia proved once again to be an exceptionally flexible CMS and in combination with its REST API making this PoC was indeed extremely easy, fast and fun.




10 comments:

online Angularjs training said...

We study from this weblog along with attending Angularjs Training In Hyderabad center. With this our knowledge in the subject increased in all aspects. Wish you the best and thank you for the way information is presented on this weblog.

Angularjs Training in Chennai said...

In recent days Angular plays vital role to loading your site content in a fastest way, so it’s a required skill for everyone, thanks for sharing this useful information to our vision keep blogging.

osvaldo anderson said...

AngularJS is an open-source JavaScript framework that assists with running SPA.
AngularJS Training

for IT the said...

Great Article on Angular.js

Angularjs Training in Chennai| Angularjs Training

Nikshitha S said...


Very informative content that guided me to know the details about the training offered in different technology. Angular JS is a structural framework for dynamic web application and angular's data binding eliminates much of the code that you would otherwise write it.
Angularjs Training in Chennai | angularjs course in chennai

Nikshitha S said...

Excellent post!!!. The strategy you have posted on this technology helped me to get into the next level and had lot of information in it.
Hadoop Training Chennai | PHP Training in Chennai

Adlearning Technologies said...

Thank You for sharing your article. I like it. We provide TIBCO Online Training in Hyderabad.

sowmyace said...

Hi,
Your blog is really nice, keep it up! I’ll go ahead and bookmark your website to come back later. Thanks for sharing this article.
Regards,
Best AngularJS2 Online Training in Hyderabad, India

Gopi Krishna said...

AngularJS is a toolset for building the framework most suited to your application development. It is fully extensible and works well with other libraries. Every feature can be modified or replaced to suit your unique development workflow and feature needs. Read on to find out how.

AngularJS Training in Chennai
AngularJS Training Institute in Chennai

Lotus.Inc said...

Damn, this is not angularjs vlog, this is about magnolia cms integration with ng. ][\;'
>:"?