ezjscore: eZ Publish JavaScript and Ajax framework

 

ezjscore features

The main features of ezjscore are as follows:

  • Javascript / CSS file dependency handling, to enable developers to define files to load per code block. This supports loading only those JavaScript and CSS files that are needed for a particular page.
  • Packs JavaScript files and CSS files into single JavaScript or CSS files and minifies the result. This improves performance by reducing the number and sizes of requests.
  • A framework to communicate between client and server using the YUI and jQuery libraries; with a bit of extra code, any JavaScript library can be supported. This includes wrappers for the respective JavaScript IO calls, and a server-side router framework for the Ajax requests.
  • Functions (and template operators) to encode eZ Publish nodes and variables to JSON and XML.

Note that this article is current as of ezjscore 1.0.1. To get the most out of this article, you should have at least some basic knowledge about eZ Publish templates, configuration files, and its permission system.

What is Ajax?

Ajax (asynchronous JavaScript and XML) means different things to different people. Many people think of animations and rich interfaces when they think about Ajax, but this is traditionally and more properly termed: DHTML (Dynamic HTML). Today, DHTML involves unobtrusive, cross browser and ECMAScript compliant Javascript / DOM code.

DHTML only tells a small part of the Ajax concept. For the purpose of this article, we will refer to Ajax as follows:
“With Ajax web applications can retrieve data from the server asynchronously in the background without interfering with the display and behaviour of the existing page.” [source: wikipedia.org]

And while "XML" is part of the "Ajax" acronym, much Ajax code uses JSON as its data interchange format, so it might also have been called Ajaj – asynchronous JavaScript and JSON!

Some key uses of Ajax include: client-server applications which minimize page-reloads to improve user experience; enabling richer user experience in the context of heavily cached content; and mashing up web services to enable greater user functionalities.

Why not something like xajax?

Today, the client part of Ajax is far more than RPCs (Remote Produce Calls) to the server to insert pre-generated HTML into a document. It can include XML transformation using XSLT, JavaScript animation including drag and drop, dynamic canvas, scripted SVG, custom DOM code, geolocation, web/locale storage, web workers or others of the many emerging client-based technologies.

The point is, the browser is being called upon to do ever more. Most Ajax applications strive to achieve clear client-serve separation, where the client has the role of the presenter and the server more or less only does authentication and data retrieval. Although a complete separation is difficult to achieve, hard-coupling your client-side code with your server code is going to limit you sooner or later.

In contrast to the xajax PHP library, ezjscore has a loosely coupled approach to keep client-side code and server-side code clearly separated. This enables you to use any kind of JavaScript / Ajax library. Only a few lines of optional glue code is needed to make the coupling transparent to the developer. ezjscore includes this glue layer for both YUI 3.0 and jQuery.

So pick your JavaScript library, check the examples and API documentation, and you should be able to start using ezjscore in no time!

Introduction to the ezjscore packer

The ezjscore packer minifies and merges several JavaScript or stylesheet files into one file. For stylesheets it also changes relative URLs (background or @import) before it stores the result in the public cache folder (var/<site_name>/cache/public), leaving it to the web server to serve the file and take care of HTTP cache handling. On second run, the packer will only regenerate the cache if some of the source files have been modified after the cache file was created. The benefits of this include smaller files to reduce download size, and reduced HTTP connections done by the browser to download files or check whether a file has changed. This especially benefits complex websites that use lots of large JavaScript and stylesheet files.

Currently, for eZ Publish versions between 4.0 and 4.2, the packed files stored in the public cache folder are not cleared by the automated clearing tools (for example, ezcache.php). They must be manually removed from the file system. This will be fixed in future versions.

Technical overview of ezjscore

We will start with a technical overview of ezjscore's configuration settings, PHP classes and template operators, and modules. This should serve as essential reference documentation, and its structure should be familiar to eZ Publish developers. Be sure to also read the INSTALL document that comes with the ezjscore extension.

Configuration (ezjscore.ini)

Note that the configuration of server-side Ajax functions is explained at the end of this article.

[eZJSCore]
Packer = enabled | disabled | 0 | 1 | 2 | 3

Force JavaScript and CSS packer level, useful for debugging Apache rewrite rules. This is toggled on a general basis by the DevelopmentMode setting in the [TemplateSettings] block of site.ini, but can also be specifically controlled by this setting. A value of 0 means disabled; 1 is just merging the files to one file; 2 is merging and simple white space removal (the default setting if "enabled" is chosen), and 3 is merging and more complex white space removal.

LoadFromCDN = enabled | disabled

Setting to control whether jQuery or YUI 2.x / 3.x should be loaded from the Google or Yahoo! CDN or from the local server. The URL for the locations of the libraries are configurable by the ExternalScripts (CDN) and LocaleScripts settings described below.

PreferredLibrary = yui3 | jquery

A setting to hint about your preferred library to extensions that build on ezjscore and choose to support several. An example of that is ezstarrating, and we'll explain how that is done a bit later. The value here can be anything, but if you are aiming to publish code on projects.ez.no, please support one of YUI 3.x or jQuery.

ExternalScripts[<script_identifier>] = <http_url>

List of external JavaScript libraries with their global CDN URL.

LocaleScripts[<script_identifier>] = <relative_path>

List of local JavaScript libraries. A relative path starting with "/" specifies that the file is in the root of the design folder rather than a path starting in the /javascript folder.

LocaleScriptBasePath[<script_identifier>] = <relative_path>

Local base script path, needed for YUI scripts.

[ezjscServer]
FunctionList[] = <ezjscore/call/limitation_name>

List of permission functions that will be used by the eZ Publish permission system. This, as well as the block [ezjscServer_<name>], will be explained later in this article in "Creating Ajax server calls".

Template operators

has_access_to_limitation:

has_access_to_limitation( <string $module> ,
                          <string $function> ,
                          <array $limitations> )

PHP:

ezjscAccessTemplateFunctions::hasAccessToLimitation()

This function lets you check if a user has access to a certain limitation. It returns "true" if the user has access to the ones specified.

Example use:

has_access_to_limitation( 'ezjscore', 'call', 
                    hash( 'FunctionList', 'ezstarrating_rate' ) 
                        )

Note: Future releases of ezjscore will support adding an array of values to check – that is, being able to change 'ezstarrating_rate' above to array( 'ezstarrating_view', 'ezstarrating_rate' )

 

ezscript:

ezscript( array|string $scripts
         [, string $type='text/javascript'
          [, string $language='javascript'
           [, string $charset='utf-8'
            [, int $pack_level=2]]]] )

PHP:

ezjscPacker::buildJavascriptTag()

Packs several JavaScript files together and generates the script tag with the "src" attribute set to the packed file. $pack_level is an integer from 0 to 3, where 0 is disabled; 1 is just merging the files to one file; 2 is merging and simple white space removal; and 3 is merging and more complex white space removal using JSMIN. If you use 3, then make sure your JavaScript files are valid according to JSLint. To override the $pack_level globally, use the ezjscore.ini[eZJSCore]Packer setting.

 

ezscript_require:

ezscript_require( array|string $scripts
                 [, string $type='text/javascript'
                  [, string $language='javascript'
                   [, string $charset='utf-8'
                    [, int $pack_level=2]]]] )

Like ezscript(), but stores script files to be loaded using the persistent_variable.js_files template variable* instead of generating the script tag unless ezscript_load() is already called.

Optional parameters will not have any effect unless ezscript_load() is already called; then, the files are packed and the script tag is directly generated, typically when used in pagelayout and included templates.
* If the current view template does not support this (everything other than the content/view view, which loads node/view/*.tpl), then ezjscPackerTemplateFunctions::$persistentVariable is used instead.

 

ezscript_load:

ezscript_load( [ array|string $scripts
                [, string $type='text/javascript'
                 [, string $language='javascript'
                  [, string $charset='utf-8'
                   [, int $pack_level=2]]]]] )

Works like ezscript(), except that the optional $script parameter is prepended to the list of script files already saved with ezscript_require() before they are packed and the script tag is generated.

 

ezscriptfiles:

ezscriptfiles( array|string $scripts
              [, int $pack_level=2
               [, bool $ignore_loaded=false]] )

PHP:

ezjscPacker::buildJavascriptFiles()

Works like ezscript(), except instead of generating script tag(s), it will return an array of URLs and scripts to use. You can detect if an array element is a script URL or a JavaScript string by checking if it ends with '.js'. When $ignore_loaded is set to true(), it will ignore files already required with ezscript_require().

 

ezcss:

ezcss( array|string $css_files
      [, string $media='all'
       [, string $type='text/css'
        [, string $rel='stylesheet'
         [, string $charset='utf-8'
          [, int $pack_level=3]]]]] )

PHP:

ezjscPacker::buildStylesheetTag()

Packs several stylesheet files together, fixes relative URLs and links, and generates the link tag with the "href" attribute set to the packed file. $pack_level is an integer from 0 to 3, where 0 is disabled, 1 is just merging the files to one file, 2 is merging and simple white space removal and 3 is merging and more complex white space removal. To override $pack_level globally, use the ezjscore.ini[eZJSCore]Packer setting.

 

ezcss_require:

ezcss_require( array|string $css_files
              [, string $media='all'
               [, string $type='text/css'
                [, string $rel='stylesheet'
                 [, string $charset='utf-8'
                  [, int $pack_level=3]]]]] )

Like ezcss(), but stores stylesheet files to be loaded using the persistent_variable.css_files template variable* instead of generating a link tag unless ezcss_load() is already called. Optional parameters will not have any effect unless ezcss_load() is already called; then, the files are packed and the link tag is directly generated, typically when used in pagelayout and included templates.

* If the current view template does not support this (everything other than the content/view view, which loads node/view/*.tpl), then ezjscPackerTemplateFunctions::$persistentVariable is used instead.

 

ezcss_load:

ezcss_load( [ array|string $css_files
             [, string $media='all'
              [, string $type='text/css'
               [, string $rel='stylesheet'
                [, string $charset='utf-8'
                 [, int $pack_level=3]]]]]] )  

Works like ezcss(), except that the optional $css_files parameter is prepended to the list of stylesheet files already saved with ezcss_require() before they are packed and the link tag is generated.

 

ezcssfiles:

ezcssfiles( array|string $css_files
           [, int $pack_level=3
            [, bool $ignore_loaded=false]] ) 

PHP:

ezjscPacker::buildStylesheetFiles()

Works like ezcss(), except instead of generating link tag(s), it will return array of URLs and CSS rules. You can detect if each array element is a stylesheet URL or a CSS rule by checking if it ends with '.css'. Setting $ignore_loaded to true() will make it ignore files already required with ezcss_require().

 

json_encode:

json_encode( <mixed $data> )

PHP:

json_encode()

Encodes simple variables to a JSON string. Use node_encode() if you want to encode node(s) or content object(s).

 

xml_encode:

xml_encode( <mixed $data>) 

PHP:

ezjscAjaxContent::xmlEncode()

Encodes simple variables to an XML string. Use node_encode() if you want to encode node(s) or content object(s).

 

node_encode:

node_encode( <object|array $nodes>
            [, <hash $params>
             [, <string $type=json>]])

PHP:

ezjscAjaxContent::nodeEncode()

Encode node(s) or content object(s) to either JSON, XML, or to a simplified array structure (type=false()) .

$params (optional):

  • dataMap <array>: List of datamap attributes to load and encode.
  • fetchPath <bool>: Fetches the nodes in the path of the actual node
  • fetchChildrenCount <bool> : Returns .children_count for the node
  • dataMapType <array>: List of datamap types to load and encode.
  • ImagePreGenerateSizes <array>: Image sizes to pre-generate in case you
    specify an image attribute in dataMap or dataMapType.
    Default is array('small').

Modules and views

ezjscore/call

This is the view handling Ajax requests for the server function calls. You can use it indirectly as explained in the examples for YUI 3.0 and jQuery or directly using the URL:
ezjscore/call/<customClass>::<customFunction>[::<argument1>[::<argument2>....]]

Example:
With ezjscore installed, operational, and with correct permissions, you can always test http://<root>/ezjscore/call/ezjsc::time

ezjscore/run

This view is purely to be able to use other eZ Publish views more easily from Ajax calls. If an error occurs, it will give an error response in the requested format (JSON, XML, or HTML), and not execute the full eZ Publish error page (which can be quite slow for Ajax use). In addition, it is easy to set up rewrite rules to make it use index_ajax.php for faster execution. The basic syntax is:
ezjscore/run/<system url>

Example:
http://<root>/ezjscore/run/content/view/full/2

 

Using the ezjscore framework

For the admin, ezwebin 1.4, and ezflow 1.1 designs and higher, the page_head_style.tpl and page_head_script.tpl templates make use of the ezcss_load() and ezscript_load() template operators. These calls are required to make on-demand loading with ezcss_require() and/or ezscript_require() work.

For example, see: extension/ezjscore/design/ezwebin/templates/page_head_style.tpl

If you are using the standard or plain designs, older versions of ezwebin and ezflow, or need to merge your custom changes to these two templates, read on.

Scripts

First, take a look at the ezscript_load() description above. As stated, ezscript_load() loads all files that have been passed to ezscript_require() earlier in the request and optionally prepends file(s) you pass to it. A good example of a change you would make to your existing code is to change your template code that uses [JavaScriptSettings] JavaScriptList from something resembling:

{foreach ezini( 'JavaScriptSettings', 
                'JavaScriptList', 
                'design.ini' ) as $script}
  <script language="javascript" type="text/javascript" 
          src={concat( 'javascript/', $script )|ezdesign}>
  </script>
{/foreach}

to:

{ezscript_load( ezini( 'JavaScriptSettings', 
                       'JavaScriptList', 
                       'design.ini' ) )}

If you don't use JavaScriptList, then just add a simple {ezscript_load()} call somewhere in the <head> section of your pagelayout.

CSS

First, take a look at the ezcss_load() description above. As stated, ezcss_load() loads all files that have been passed to ezcss_require() earlier in the request and optionally prepends file(s) you pass to it. With CSS, the example is a bit bigger, but it follows the same pattern by changing:

<style type="text/css">
  @import url({"stylesheets/core.css"|ezdesign(no)}); 
  @import url({"stylesheets/debug.css"|ezdesign(no)});
  @import url({"stylesheets/pagelayout.css"|ezdesign(no)});
  @import url({"stylesheets/content.css"|ezdesign(no)});
  @import url({"stylesheets/websitetoolbar.css"|ezdesign(no)});
  {foreach ezini( 'StylesheetSettings', 
                  'CSSFileList', 
                  'design.ini' ) as $css_file}
    @import url({concat( 'stylesheets/', $css_file )|ezdesign});
  {/foreach}
  @import url({ezini('StylesheetSettings',
                     'ClassesCSS',
                     'design.ini')|ezroot(no)});
  @import url({ezini('StylesheetSettings',
                     'SiteCSS',
                     'design.ini')|ezroot(no)});
</style>

to:

{ezcss_load( array( 'core.css', 
                    'debug.css',
                    'pagelayout.css',
                    'content.css', 
                    'websitetoolbar.css',
                    ezini( 'StylesheetSettings', 
                           'CSSFileList', 
                           'design.ini' ) ))}  
{* 
   These are not loaded by ezcss_load 
   since they might exist in ~<user path> 
*} 
<style type="text/css">   
  @import url({ezini('StylesheetSettings',
                    'ClassesCSS',
                    'design.ini')|ezroot(no)}); 
  @import url({ezini('StylesheetSettings',
                     'SiteCSS',
                     'design.ini')|ezroot(no)}); 
</style>

If you don't use CSSFileList, then just remove that last line in the ezcss_load() call.

Using JavaScript libraries

As mentioned earlier, the Yahoo! User Interface and jQuery JavaScript libraries can be delivered either by content delivery networks (CDNs) or locally (the ezjscore extension bundles both libraries for 'offline' usage). YUI offline files are placed under design/standard/lib/yui/ while jQuery offline files are placed in the design/standard/javascript/ directory.

Most of the examples below are taken from the ezjscore_demo extension. The code used in the YUI and jQuery examples is included in the demo extension in three templates. These can be used in your development install by placing the following in the desired view template (be sure to customize the file name based on the library you'd like to test):

{include uri='design:ezjscore_jquery_demo.tpl'}

The ezjscore_demo extension is downloadable here <http://projects.ez.no/ezjscore/downloads/archive> or in SVN here <http://svn.projects.ez.no/ezjscore/trunk/packages/ezjscore_extension/documents/example/>.

The examples show a basic Ajax scenario where the server returns a string that is passed to it, and the client-side shows the result from the server.

YUI

The YUI Library is a set of utilities and controls, written with JavaScript and CSS, for building richly interactive web applications using techniques such as DOM scripting, DHTML and Ajax.
At the time this article was written, two major versions – YUI 2.x and YUI 3.x – are actively developed and supported. ezjscore comes with 2.7.0 and 3.0.0 in case you don't want to use the Yahoo! CDN.

YUI 2.x

YUI 2 is a JavaScript and CSS library with more than 30 unique components, including low-level DOM utilities and high-level user-interface widgets. ezjscore provides easy access to the YUI Loader utility, which is a client-side JavaScript component that enables you to load specific YUI components and their dependencies into your page via a script. Let's take a look at an example.

First, we need to load the YUI Loader utility. In order to do so, put the following piece of code in an eZ Publish template:

{* On demand require some custom css *} 
{ezcss_require( 'ezjscdemo.css' )}  

{* 
 Some simple html where we want to load 
 some content from server 
*} 
<div id="hello-world-id" class="my-custom-css-class">    
  Waiting for ezjscore ajax call... (See FAQ if you get script error and / or nothing happens! hint: permissions ) 
</div>  

{* On demand require YUI 2.x loader file *} 
{ezscript_require( 'ezjsc::yui2' )}

ezscript_require() will load the YUI Loader utility, which provides access to a variable called YUILoader. You can use YUILoader for loading your custom JavaScript and CSS files as well as YUI 2.x components.

This example (taken from the ezjscore_demo extension) shows how to use YUILoader to load custom JavaScript and CSS files as well as some YUI 2 components such as "dom" and "connection":

<script type="text/javascript"> 
<!-- {literal} 
  // This function is executed when all YUI 2.x components are available 
  YUILoader.onSuccess = function()  
  {  
  {/literal}     
    // A ezjscore server call   
    var serverCall = "{'/ezjscore/call/ezjscdemo::search'|ezurl( 'no' )}"; 
  {literal}     
    // POST parameters send along with XHR request     
    var data = "arg1=hi!";   
    YAHOO.util.Connect.asyncRequest( "POST", serverCall,  
          {success: function (o)          
                    {  
                      if ( o.responseText !== undefined ) 
                      {
                        YAHOO.util.Dom.get("hello-world-id").innerHTML = o.responseText;             
                      }
                    }
          }, 
          data ); 
  }
  /* 
    NOTE: Custom scripts which are depending on the YUI 2.x 
    components should be loaded via YUILoader       
    to avoid issues with dependency loading. 
    Scripts which does not require the YUI 2.x components 
    should be loaded via ez[script|css]_require       
    template operator which also takes care about packing (minifying). 
    YUILoader.addModule({     
        name: 'customscript',     
        type: 'js',   
        fullpath: 'full/path/to/customscript.js' 
        // Can be used with ezdesign() 
                       }); 
  */ 

  // Load DOM and Connection components 
  YUILoader.require(["dom","connection"]);  

  // Insert YUI 2.x components on page 
  YUILoader.insert(); 
  {/literal} 

//--> 
</script>

Code that depends on the successful loading of all necessary components can be implemented in the YUILoader.onSuccess function.
The YUI Loader utility is well-described in the official YUI 2 documentation. [http://developer.yahoo.com/yui/yuiloader/]

YUI 3.x

YUI 3 is Yahoo!'s next-generation JavaScript and CSS library. It was a complete re-design from YUI 2 to be lighter, faster, and easier to use. The new code style enables you to accomplish more with less code. In the new sandboxed approach, all loaded YUI modules are bound to the YUI instance when called with use(); this protects against changes that might happen later in the page’s life cycle. ezjscore provides easy access to the YUI 3 instance within templates. One difference in the ezjscore integration as compared to YUI 2 is that ezjscore includes a wrapper for a more abstracted server call from YUI 3. Let's take a look at a practical example.

First, we need to load the YUI 3 seed:

{ezscript_require( 'ezjsc::yui3' )}

This will provide a basic YUI 3 loader, which is used to load any module in the library on the fly. It also generates a global YUI3_config variable that tells YUI where to load the scripts from based on settings in ezjscore.ini.

Now that the script is available, we can start using YUI 3 immediately. We use YUI's "node" utility for DOM manipulation:

<script type="text/javascript"> 
<!-- {literal} 
  YUI( YUI3_config ).use('node', 
                         function( Y ) 
                         {
                           Y.on( "contentready", 
                                 function( e )     
                                 {         
                                   // What to do, what to do..   
                                 }); 
                         });
//--> 
</script>

YUI3_config can be used to load custom YUI scripts or CSS files that are required for the current instance. For full configuration information, see the YUI 3 loader documentation [http://developer.yahoo.com/yui/3/yui/#loader]

For IO operations such as Ajax calls, ezjscore provides a handy wrapper for the YUI 3 IO component [http://developer.yahoo.com/yui/3/io]. The wrapper has a pre-configured path to the eZ Publish router that receives calls and returns the output; the default output is a parsed JSON object since the default request is for the JSON format.

The following template code, taken from the ezjscore_demo extension, demonstrates the use of the IO wrapper in performing a simple search call:

{* On demand require some custom css *} 
{ezcss_require( 'ezjscdemo.css' )}  

{* 
  Some simple html where we want to 
  load some content from server 
*}
<div id="hello-world-id" class="my-custom-css-class"> 
  Waiting for ezjscore ajax call... 
  (See FAQ if you get script error and / or nothing happens! hint: permissions ) 
</div>

{* 
  On demand require YUI 3.0 loader file 
  and ezjscore's YUI.io.ez 
*}
{ezscript_require( array( 'ezjsc::yui3', 'ezjsc::yui3io' ) )}

Notice the ezjsc::yui3io item in the array. This is the io-ez YUI 3 module provider, which we must also load. This is done by adding the io-ez parameter in the use() function.

{*
  Use more or less standard YUI 3.0 code, only custom ezjscore bits are:
  YUI3_config : global yui configure object as dynamically created "ezjsc::yui3"
                based on cdn and script location settings in ezjscore.ini
  YUI.io.ez   : Simple wrapper for YUI.io created dynamically by "ezjsc::yui3io"
                so you don't have to deal with the url to the server.
*}
<script type="text/javascript">
{literal}
YUI( YUI3_config ).use('node', 'io-ez', function( Y )
{
    Y.on( "contentready", function( e )
    {
        // Parameters can be sent as post data ('arg1=hi!') or as 
        // call parameter (eg: "ezjscdemo::search::hello" )
        // Use post for string values to not end up reaching url path limit..
        Y.io.ez( 'ezjscdemo::search', {
            data: 'arg1=hi!',
            on: {success: function( id,r )
                { 
                    if ( r.responseJSON.error_text )
                        Y.get( '#hello-world-id' ).setContent(
                            r.responseJSON.error_text
                        );
                    else
                        Y.get( '#hello-world-id' ).setContent(
                            r.responseJSON.content
                        );
                }
            }
        });
    }, '#hello-world-id' );
});
{/literal}
</script>

jQuery

jQuery is a fast and concise JavaScript library that simplifies HTML document traversing, event handling, animating, and Ajax interactions for rapid web development. In order to use jQuery with ezjscore, simply put the following line in an eZ Publish template:

{ezscript_require( array( 'ezjsc::jquery' ) )}

This will provide a minified version of the jQuery library served from the Google CDN as mentioned earlier.

<script type="text/javascript" 
        src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js">
</script>

jQuery syntax is well documented in the official jQuery documentation [http://docs.jquery.com/Main_Page]. As with YUI 3, ezjscore provides a jQuery IO wrapper. The following example code is taken from the ezjscore_demo extension:

{* On demand require some custom css *}
{ezcss_require( 'ezjscdemo.css' )}

{* Some simple html where we want to load some content from server *}
<div id="hello-world-id" class="my-custom-css-class">
   Waiting for ezjscore ajax call... (See FAQ if you get script error and / or nothing happens!)
</div>

{* On demand require jQuery and ezjscore's jQuery.ez *}
{ezscript_require( array( 'ezjsc::jquery', 'ezjsc::jqueryio' ) )}

Notice the jqueryio item, which provides a wrapper for the ezjscore server call router. To make an XML HTTP request call to the router, use the jQuery.ez() function, where the first parameter is a string containing the name of the method to call, the second is for the post parameters, and the third parameter is the callback function.

{*
  Use more or less standard jQuery code, only custom 
  ezjscore bit is: jQuery.ez : 
  A jQuery.post wrapper generated by "ezjsc::jqueryio" 
  that figures out the server url for you.  
*}
<script type="text/javascript">
{literal}
// $(document).ready() alias without $ globally 
// (used in several library's)
jQuery(function( $ )
{
    // Parameters can be sent as post data ({arg1: 'hi!'}) or as 
    // call parameter (eg: "ezjscdemo::search::hello" )
    // Use post for string values to not end up reaching url path limit..
    $.ez( 'ezjscdemo::search', {arg1: 'hi!'}, function( data )
    { 
        if ( data.error_text )
            $( '#hello-world-id' ).html( data.error_text );
        else
            $( '#hello-world-id' ).html( data.content );
    });
});
{/literal}
</script>

Creating Ajax server calls

To create Ajax server-side calls, you will need a PHP class and the appropriate ezjscore.ini settings.

Server call class

The following is some boilerplate code to create a server-side Ajax function. All the function does is return "Hello" appended by the name you supply it. Assuming the extension name "ezjscore_demo", place your server call class in extension/ezjscore_demo/classes/ezjscoredemoservercallfunctions.php with the following content:

<?php
/**
 * File containing the ezjscoreDemoServerCallFunctions class.
 *
 * @package ezjscore_demo
 * @version //autogentag//
 * @copyright Copyright (C) 2005-2009 eZ Systems AS. All rights reserved.
 * @license http://ez.no/licenses/new_bsd New BSD License
 */

class ezjscoreDemoServerCallFunctions extends ezjscServerFunctions
{
    public static function search( $args )
    {
        if ( isset( $args[0] ) )
        {
            return 'Hello World, you sent me 
                    parameter : ' . $args[0];
        }
        else
        {
            $http = eZHTTPTool::instance();
            if ( $http->hasPostVariable( 'arg1' ) )
            {
                return 'Hello World, you sent 
                        me post : ' . $http->postVariable( 'arg1' );
            }
        }

        return "Request to server completed, 
                but you did not send any 
                post / function parameters!";
    }
}
?>

The functions in this class will be reachable as configured in ezjscore.ini, as explained next.

Setting up ezjscore.ini

To be able to reach the example function, you must first declare the function and make it available to the permission system. The file extension/ezjscore_demo/settings/ezjscore.ini.append.php contains:

[ezjscServer] FunctionList[]=ezjscdemo

It also contains the following settings block. It maps the server call "ezjscdemo" to the PHP class "ezjscoreDemoServerCallFunctions" and specifies the file in which the class is located. To be able to test permissions, we also have “Functions[]=ezjscdemo”.

[ezjscServer_ezjscdemo]
Class=ezjscoreDemoServerCallFunctions
# Define File to avoid relying on autoload 
# system for this simple example
File=extension/ezjscore_demo/classes/ezjscoredemoservercallfunctions.php
Functions[]=ezjscdemo

Testing

You can do a simple test that the server-side function is accessible by loading the following URLs:

http://<root>/ezjscore/call/ezjscdemo::search
http://<root>/ezjscore/call/ezjscdemo::search::Strawberry Fields

Keep in mind that this test is basic. Depending on the characters for the supplied name and the browser you use, putting the name in the URL might not work. It is best to use post parameters, as shown in extension/ezjscore_demo/classes/ezjscoredemoservercallfunctions.php and the examples in the "Bundled JavaScript libraries" section above or the example template code in the ezjscore_demo extension.

If you run the same simple test from above when you are an anonymous user, you should get the response “Not a valid ezjscServerRouter argument: test::name”. The relevant user(s) should be given access to ezjscore/call/ezjscdemo via the regular eZ Publish permission system.

Debugging

Keep the following in mind when debugging your ezjscore setups:

  • To test that a network connection to the JavaScript libraries is not a problem, disable the ezjscore.ini[eZJSCore]LoadFromCDN setting.
  • Make sure you have set up the proper rewrite rules for CSS and JavaScript packing. These are noted in the INSTALL document that comes with ezjscore. You can also disable ezjscore.ini[eZJSCore]Packer to temporarily avoid the reliance on Apache rewrite rules.
  • For debugging calls to the server, up-to-date versions of Firefox and Firebug make up the best environment. If your server call is not properly defined in ezjscore.ini, you will get a "500 Internal server error". If it is defined but you do not have access to the function or it is not callable, you will get a 200 OK request, but with the error "Not a valid ezjscServerRouter argument: <input_arguments>". To make sure it is not a permissions issue, log in as an administrator user to the siteaccesses you are testing.
  • As noted earlier, the packed JavaScript and CSS files stored in the public cache folder are not cleared by the automated clearing tools (for example, ezcache.php). They must be manually removed from the file system. This will be fixed in future versions.

Conclusion

In this article, we have introduced the eZ Publish JavaScript and Ajax framework extension ezjscore. We have covered the basic concepts behind the extension and gone through its configuration options, how to integrate it with the popular jQuery and YUI libraries, and how to create Ajax server calls.

We encourage all eZ Publish developers to use the ezjscore extension in their projects. We look forward to hearing your feedback!

We would like to thank Doug Plant and Peter Keung from Mugo Web [link], an eZ Systems business partner, for their valuable feedback and editorial work.

For more information on ezjscore, see:

Powered by eZ Publish™ CMS Open Source Web Content Management. Copyright © 1999-2014 eZ Systems AS (except where otherwise noted). All rights reserved.