Skip to content
This repository has been archived by the owner on Mar 21, 2024. It is now read-only.

Creating An Application Module

wskidmore edited this page Jan 16, 2012 · 10 revisions

What Does An Application Module Do?

An application module is a way to encapsulate additional application functionality and customizations without needing to modify the host applications code.

Creating The Project

It is recommended you have the following base folder structure:

+---mobile		
    +---argos-sdk
    +---products
        +---argos-saleslogix
        +---argos-sample
        \---<< other products and modules >>

You will want to create the base folder for your module inside of the products folder. The name of the folder should be prefixed with argos- as the primary build scripts currently require it to be. The folder structure inside of the base folder is entirely up to you, though we use, and recommend, the following:

+---products
    +---<< prefixed module name, i.e. argos-sample >>
        |    .gitignore    
        |    README.md    
        |    index-dev-<< module name >>.html
        |    module-fragment.html
        |    module-info.json
        |
        +---build
        |        release-template.jsb2
        |        release.cmd
        |        release.jsb2
        |        release.sh
        |
        +---configuration
        |    \---<< module name >>
        |            development.js
        |            production.js
        |
        +---content
        |    +---images
        |    \---css
        |            << prefixed module name >>.css
        |
        +---localization
            |    en.js
        |
        \---src
            |    ApplicationModule.js
            |
            \---Views

The rest of the guide will assume that you have used the folder structure above.

Setting Up A Development Environment

In order to test your module, you'll need to create a development shell to run it in. The best way to do this is to copy the index-dev.html from the product for which your module is target, place it in the base folder, and rename it to index-dev-<< module name>>.html, i.e. index-dev-sample.html.

Before we can go any futher, we need to decide on the namespace that we will use for all classes inside of the module. In our own modules, we use Mobile.<< name >>, i.e. Mobile.Sample or Mobile.SalesLogix. Once you have decided on a namespace, use it to setup our references - correlating to the define() file paths, e.g.:

<script type="text/javascript">
require({
    baseUrl: "./",
    packages: [
        { name: 'dojo', location: '../../argos-sdk/libraries/dojo/dojo' },
        { name: 'dijit', location: '../../argos-sdk/libraries/dojo/dijit' },
        { name: 'Sage/Platform/Mobile', location: '../../argos-sdk/src' },
        { name: 'Mobile/SalesLogix', location: 'src' },
        { name: 'Mobile/Sample', location: '../argos-sample/src' },
        { name: 'configuration/sample', location: '../argos-sample/configuration' },
        { name: 'localization/sample', location: '../argos-sample/localization' }
        ]
    });
</script>

Please make sure the relative folder paths are originating from the target product folder and not from the custom module.

Then to finish off the index-dev-sample.html file we need to point the initialization code to require (load) our custom module:

<script type="text/javascript">
(function() {
    var application = 'Mobile/SalesLogix/Application',
        configuration = [
            'configuration/sample/development'
        ];
    require([application].concat(configuration), function(application, configuration) {
        var localization = [
            'localization/en',
            'localization/saleslogix/en',
            'localization/sample/en'
        ];
        require(localization.concat('dojo/domReady!'), function() {
            var instance = new application(configuration);

            instance.activate();
            instance.init();
            instance.run();
        });
    });
})();
</script>

The configuration/sample/development file will be covered further in this document and localizaton/sample/en will be covered in the localization article.

Now that our dev file is finished it must be either symlinked to, or copied to, the product's base folder. The .gitignore file for the product should be aware of these development shells, and they should not be included in any commits for the product.

To setup a symlink (recommended), you can do the following (from the product's folder):

Windows:
mklink index-dev-sample.html ..\argos-sample\index-dev-sample.html

Unix:
ln -s ../argos-sample/index-dev-sample.html index-dev-sample.html		

If the product's development shell is updated, changes should be easily merged into your shell, or, if no merge is desired, the shell can be easily recreated.

Creating The ApplicationModule Class

Open up the ...\src\ApplicationModule.js file. First you will need to declare the ApplicationModule class, like so:

define('Mobile/Sample/ApplicationModule', [
    'Sage/Platform/Mobile/ApplicationModule'
], function() {
    return dojo.declare('Mobile.Sample.ApplicationModule', Sage.Platform.Mobile.ApplicationModule, {
        // customization code
    });
});

Those familiar with dojo, or any AMD type loading system, will find the above normal, but to expand upon it lightly: the define() call provides 1) a file path to the file; 2) dependencies and 3) a factory function.

  1. The file path base url is defined in the index-dev-<< module name>>.html file, then you add the appropriate folder levels then the file name. Breaking down this sample:
- `Mobile/Sample/ApplicationModule` = `../argos-sample/src/ApplicationModule.js`

- Just remember to reference your namespace setup in the `index` file and add the folder levels.
  1. Dependencies is an array of define() file paths that must be loaded before our attached function runs. In ApplicationModule we will need to have its base class loaded, as well as any additional javascript files we add - such as new Views.

  2. The factory function will run once all dependencies are loaded and will return an export object as the main object of this define() so that other files may use this object as one of their dependencies.

The dojo.declare() call defines a namespace, any base classes to inherit from, and lastly an object that will be mixed-in. Mix-in in this context means to copy (and overwrite) any properties from the passed object to the new object.

Now we can move onto implementation.

Implementing The ApplicationModule Class

In most general terms, when we are implementing a module, we are looking to do two things: create new views and customize existing ones. The Sage.Platform.Mobile.ApplicationModule class provides hooks for us to do both of these things. We'll want to override the loadViews and loadCustomizations methods of the ApplicationModule class, e.g.:

define('Mobile/Sample/ApplicationModule', [
    'Sage/Platform/Mobile/ApplicationModule'
], function() {
    return dojo.declare('Mobile.Sample.ApplicationModule', Sage.Platform.Mobile.ApplicationModule, {
        loadViews: function() {
            this.inherited(arguments);
        },
        loadCustomizations: function() {
            this.inherited(arguments);
        }
    });
});

Dojo handles proper inheritance chaining so that you can easily call the superclass method by calling this.inherited(). Using that call in the above two functions ensures that any code in its base Sage.Platform.Mobile.ApplicationModule will be called.

Creating and registering new views and customizations will be covered in other articles.

Setting Up The Build Script

In the ...\build folder, you'll see four files listed in the structure above: release-template.jsb2, release.cmd, release.jsb2 and release.sh. The release.cmd (Windows) and release.sh (Unix/Linux) files start the build process with all the correct options; it is best to copy this file from another module. The release.jsb2 is the actual build project and is what we we need to create for our module. A basic build project a sample module would look like:

{
    "projectName": "Sample Module",
    "licenseText": "",
    "deployDir": "deploy/",
    "pkgs": [{
        "name": "Sample Module",
        "file": "content/javascript/argos-sample.js",
        "isDebug": true,
        "fileIncludes": []
    }],
    "resources": [
    {
      "src": "../content",
      "dest": "content",
      "filters": ".*(\\.css|\\.jpg|\\.png|\\.gif)"
    },
    {
      "src": "../configuration",
      "dest": "configuration",
      "filters": ".*\\.js"
    }
  ]
}

Starting with that as our base, we need to add the ApplicationModule.js file that was created previously. You can do this by adding an item to the fileIncludes array property:

fileIncludes: [{
    "text": "ApplicationModule.js",
    "path": "../src/"
}]

The reason that we have to use a relative path for the path property is that all file includes are based off of the jsb2 file's path. As you add more Views or additional files make sure to continue adding to the fileIncludes[] array with the name and path.

Establishing The Configuration

In the ...\configuration folder, there are two files, development.js and production.js, which contain application configuration for development, and production, respectively. The format for both is the same, as they use the mergeConfiguration() function to merge in new modules and, optionally, new SData connections:

define('configuration/sample/development', ['configuration/development', 'Mobile/Sample/ApplicationModule'], function(baseConfiguration) {
    return mergeConfiguration(baseConfiguration, {
        modules: [
            new Mobile.Sample.ApplicationModule()
        ],
        connections: {
            'crm-sample': {
                isDefault: true,
                offline: true,
                url: 'http://50.16.242.109/sdata/slx/dynamic/-/',
                json: true
            }
        }
    });
});

Notice that we add a new instance of our ApplicationModule to the modules list; this is how the application knows to load our module. In the connections property we can define any and all SData connections we need. The key used here to define the SData connections is the same that the views will need to have as their serviceName property, unless they will be using the default, application provided connection.

Preparing The Module Fragment

Open the module-fragment.html file in the base folder and add your stylesheet and final build javascript file path, e.g.:

<!-- Sample -->
<link type="text/css" rel="stylesheet" href="content/css/sample.css" />
<script type="text/javascript" src="content/javascript/argos-sample.js"></script>

This fragment will be injected into all index files when the module is deployed via AA. If you are not deploying via AA you will then to manually copy and paste the above lines directly into index.aspx and index-nocache.aspx at the <!-- {{modules}} --> marker to get the following result:

<!-- Application -->
<script type="text/javascript" src="content/javascript/argos-saleslogix.js"></script>

<!-- Modules -->
<!-- Sample -->
<link type="text/css" rel="stylesheet" href="content/css/sample.css" />
<script type="text/javascript" src="content/javascript/argos-sample.js"></script>

If you plan to use the static html versions, index.html and index-nocache.html, then you forfeit the configuration and localization detection provided by the dynamic adapter. Follow the same instructions for the aspx versions (above) by injecting the fragment then proceed to specify your configuration and locale directly:

        var application = 'Mobile/SalesLogix/Application',
            configuration = [
                'configuration/production',
                'configuration/sample/production'
            ];
        require([application].concat(configuration), function(application, configuration) {
            var localization = [
                'localization/en',
                'localization/saleslogix/en',
                'localization/sample/en' 
            ];

Defining The Module

In the base folder there is a module-info.json file. This file reports information about your module to AA so that easier versioning, naming and targeting may be applied. A sample module-info.json file is as follows:

{
    "name": "sample",
    "displayName": "Sample Customizations",
    "description": "A sample module implementation that shows how to customize the Sage SalesLogix Mobile client.",
    "majorVersion": 1,
    "minorVersion": 1,
    "buildNumber": 0,
    "targetProduct": "saleslogix"
}

Please ensure that your file passes JSON validation (double quotes, properly escaped, etc).

Building The Module

First, save this (gist)[https://gist.github.com/815451] as build-module.cmd to the same folder where build-product.cmd and the argos-sdk reside. After this is done, you can build your module by executing the build-module.cmd script and passing in the module name as the argument, e.g.:

build-module sample

The output of the build will be in a folder named after the module inside of the deploy folder, i.e. ...\mobile\deploy\argos-sample.

Deploying The Module

In order to deploy your module, you'll need to copy the output of the build, to the folder where the product is deployed. The copy process may complain about existing folders; You'll want to allow it to copy into existing folders.

If the dynamic manifest, index.manifest.ashx is not being used, you must update the static manifest, index.manifest, and place the files into it.

A couple more things must be done if your module creates it's own SData connections. First, you'll need to add your SData endpoints to the NETWORK: section of the manifest files, index.manifest and template.manifest, e.g.:

NETWORK:
../sdata/
http://sample-server/sdata/

Next, if your SData host is on another server, you'll need to enable Cross-Origin-Request-Sharing (CORS). You can find a document on how to set it up for IIS at: Setting-Up-CORS.