In order to use the GCN JS Library you need to provide jQuery 1.5.1 or newer. If you need support for Internet Explorer 7 don’t use 1.7.1, as an error in this version prevents jQuery to work correctly.
1 Loading the library
There are two main use cases for the library: Frontend Editing and Scripting with Gentics Content.Node backend objects. In any case you will need to define the BACKEND_PATH with locates your Gentics Content.Node backend server:
GCN.settings.BACKEND_PATH="/[Proxy]/CNPortletapp";
When your Gentics Content.Node backend resides on a different server you will most likely need to use a Proxy Script to access it, as loading data using AJAX calls is only allowed on the same domain. In our example this script is referred to as [Proxy].
1.1 Frontend editing
When using the GCN JS Library for Frontend Editing you will also need Aloha Editor to be able to edit the contents of a page. As Aloha Editor’s Gentics Content.Node Integration Plugin also uses the GCN JS Library to communicate with the backend all you need to do is to load the most recent version of Aloha Editor from the Gentics Content.Node Backend.
<!-- Load Aloha Editor --> <script src="/[Proxy]/CNPortletapp/latest/alohaeditor-0.10/lib/aloha.js" data-aloha-plugins="extra/ribbon, common/format, common/highlighteditables, common/list, common/link, common/table, common/paste, common/contenthandler, common/commands, common/block, extra/browser, gcn/gcn-linkbrowser, gcn/gcn"></script> <!-- Load Aloha Editor CSS --> <link rel="stylesheet" href="/[Proxy]/CNPortletapp/latest/alohaeditor-0.10/css/aloha.css" />
1.2 Scripting
In other cases you just want to load the GCN JS Library without Aloha Editor. An example for this is if you intend to code an additional administrative interface that allows you to browse through folders and pages without being able to change contents. In this case you just have to include the library. You can either load it from the server or copy it to your frontend and load it from there.
<script src="/[Proxy]/CNPortletapp/latest/gcnjslib/bin/gcnlib.js"></script>
2 Usage
The general rule of this control abstraction is that the return value of GCN JS Library API methods are always continuation objects and not typical return values.
For example:
var page = GCN.page(42);
In the above code, the variable `myPage` is not assigned a page but rather a continuation object. This continuation object exposes the appropriate parts of the GCN library’s API for operating on page 42 and for querying for objects contained in that page.
Therefore, this would be an incorrect usage of the Library:
console.log(GCN.page(42).prop('name'));
The appropriate way to read the value of an object provided by the GCN Library requires that you use a callback like this:
GCN.page(42, function (page) { console.log(page.prop('name')); })
The simple rule to keep in mind in this regard is that whenever you want to read a value of an object retreived through the GCN JS Library, then you must do so through a callback. The callback causes the object’s data to be fetched from the server or from the in-memory cache and make it’s properties available in the object passed into the callback function as its only argument.
3 Simple Example
The following example illustrates how to use the Gentics Content.Node JS Library with Aloha Editor. Please change the timestamp according to your installation.
<!doctype html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <title>GCNLib + Aloha Editor</title> <link rel="stylesheet" href="http://aloha-editor.org/builds/development/latest/src/css/aloha.css" id="aloha-style-include" type="text/css"> <script src="http://aloha-editor.org/builds/development/latest/src/lib/aloha.js" data-aloha-plugins="common/format,common/table,common/list,common/link,common/highlighteditables,common/commands"></script> </head> <body style="padding: 100px"> <h1>My Demo Page</h1> <div id="content">Hi, there!</div> <script type="text/javascript"> <!-- (function () { 'use strict'; window.$ = window.jQuery = window.Aloha.jQuery; jQuery('head').append( '<script type="text/javascript" src="/CNPortletapp/YOURVERSION/gcnlib/bin/gcnlib.js"></script>' + ); jQuery(function () { GCN.login('node', 'node', function (success, data) { jQuery('#content') .html(success ? '<p>Hello, ' + data.user.firstName + '!</p>' : '<p>Login failed</p>'); }); }); }()); --> </script> </body> </html>
4 Library Versions
There are three version of the Gentics Content.Node Javascript Library.
- gcnlib/bin/gcnlib.js – Minified concatenated version
- gcnlib/dev/gcnlib.js – Unminified not concatenated version
- gcnlib/debug/gcnlib.js – Unminified concatenated version
5 Caching
Whenever the Gentics Content.Node Javascript Library is loading objects from the server it will automatically cache them for later actions. This resembles no problem as long as you are the only one working on that object. If you want to force the library to retrieve a fresh copy from the server you can reset the local object data by using the clear() method.
calling clear() on an object will also delete all the changes that have been applied but where not saved yet.
GCN.folder(42, function (folder) { // [some magic going on here for a while] ... // [wayyyyy later] // now we need a fresh copy of the folder since // someone might have changed it on the server // this will clear the cache and reset *ANY* changes // that have not been saved yet folder.clear(); // now fetch the folder once again to be good to go GCN.folder(42, function(folder) { // now we're good to go with our fresh copy folder.prop('name', $('#foldername').val()); folder.save(); }); });
6 Example Code Snippets
This should give you a basic idea of what using the GCN JS Library should look like:
// Print all folder in a node. GCN.node(1).folders(function(folders) { console.log(folders[0].prop('name')); }); // Change the url of an image in a page. GCN.page(7).tag('splash_image', function(tag) { var url = prompt('Please input a new image url'); if (url) { tag.part('url', url).save(); } });
A little more advanced control flow:
var myPage; // Render for editing the `content` tag in page 1 in a // HTML element whose id is "main-content." function loadPage() { myPage = GCN.page(1, function () {}); myPage.tag('content').edit( '#main-content' ); } // Modify the page's tag and save the page. function savePage() { myPage.save(function (page) { console.log("Saved page " + page.prop("name")); }); } // on DOM ready jQuery(loadPage); // onUnload jQuery(window).bind('unload', savePage);
Whenever the first callback is provided to any of the API methods, it is treated as a success handler. It will be invoked when the method succeeeds and will have passed to it, the requested object(s). A second callback, if provided is treaded a custom override to the global error handler, and will only be invoked if the operation for whatever reason fails.
When fetching a an object or a collection of objects a
7 Control Abstraction
The GCN JS Library favors the JavaScript promises pattern rather than the continous passing style, although it facilitates both. This is why:
The GCN Library abstracts Content.Node objects, like pages, folders, and tags, into JavaScript objects, and provides methods to conveniently operate over these objects without worrying about how they are represented or transported between the client and the server.
Because of the frequent transfering of object states between the client and the server, the control flow of an application using Content.Node as its remote data store will have to be very asynchronous if one is to avoid blocking the browser’s execution with synchronous communication to the server.
Using callbacks to work around this often forces programming to be done in the continuation passing style. Continous passing style, however, is error-prone and quickly becomes unweildy with non trivial application control flow.
We want to avoid code that looks like this:
// pseudocode: ajax(request_object, callback) ajax(a, function() { ajax(b(a.somedata), function() { ajax(c(b.somedata), function() { c.finish() }) }) })
To provide better control flow, the GCN Library provides a fluent API that facilitates chainable asynchronous methods. If you are familiar with the concept of JavaScript promises, you will understand this control abstraction as similary kind of pattern.
See these resources for an introduction to this approach:
- http://www.slideshare.net/domenicdenicola/callbacks-promises-and-coroutines-oh-my-the-evolution-of-asynchronicity-in-javascript)
- http://www.infoq.com/articles/surviving-asynchronous-programming-in-javascript