Méthode Swing documentation

Context Object Reference

The Context Object is a Javascript object available throughout the Swing Extensions. In every extension point, the context object can be accessed and its methods called.

The Context Object is composed of public methods (see Public methods), object-specific methods (see Object class methods), and user-specific methods (see User class methods). If the context object is referred to a specific object, an activeObject property is available (see activeObject). If the context object is inside a supported Editor, the activeDocument property is available (see activeDocument).

Public methods

List of methods

Table 1. Parameters

Name

Description

Obtains the application info.

Obtains the area info.

Obtains the communication status.

Obtains the component info.

Reads a file given its filename, and returns it in a particular format.

Executes a Methode query.

Reads a user data content given its filename.

Adds a new JSON object to the specified user data content.

Set a new JSON array to the specified user data content.

Obtains a content of a document, given its path

Obtains the current user

Obtains a Methode object, given its id

Obtains a Methode object, given its path

Obtains the Platform information

Obtains a user, given its name

Obtains the list of member names, given a group name

Open a Methode document, given its id

Open the new content dialog, with the selected options

Open a Methode document, given its id, in readonly mode

Opens a popup panel

getApplicationInfo

Retrieves the application info and returns a JSON object containing the context and the name of the application.

    {
        context: "/swing",
        name: "Swing"
    }
Syntax
ctx.getApplicationInfo()
Returns

Type

Description

{JSON Object}

A json object containing the context and the name of the application.

Example
var applicationInfo = ctx.getApplicationInfo();

getAreaInfo

Retrieves the area info and returns a JSON object containing the area type and the area name.

    {
        name: "story",
        type: "editor"
    }
Syntax
ctx.getAreaInfo()
Returns

Type

Description

{JSON Object}

A json object containing the area type and the area name.

Example
var areaInfo = ctx.getAreaInfo();

getCommunicationStatus

Retrieves the communication status of the application (useful when working in offline mode). The object returned contains the following fields:

  • applicationOnline: true if the application is in online mode, false otherwise.

  • isOnline: true if the application reaches the server and the application is in online mode, false otherwise.

  • offlineServer: enabled : true if the offline server is available, enabled: false otherwise.

  • serverReachable: true if the server is reachable, false otherwise.

    {
        applicationOnline: true,
        isOnline: true,
        offlineServer: {
            enabled: false
        },
        serverReachable: true
    }
Syntax
ctx.getCommunicationStatus()
Returns

Type

Description

{JSON Object}

A json object containing the communication status of the application.

Example
var communicationStatus = ctx.getCommunicationStatus();

getComponentInfo

Retrievest the conponent info and returns a JSON object containing the type and the subType of the current component. e.g.

    {
        type: "objectpanel",
        subType: ""
    }
Syntax
ctx.getComponentInfo()
Returns

Type

Description

{JSON Object}

A json object containing the type and the subType of the current component.

Example
var componentInfo = ctx.getComponentInfo();

readFile

Reads a file given its filename, and returns it in a particular format.

Syntax
ctx.readFile( options, callback )
Parameters
Table 2. Parameters

Name

Type

Required

Description

options

{Object}

No

An optional list of options. See below

callback

{Function}

No

A callback function. See Callback definition for further information.

The options object is a Javascript object with the following properties:

  • filename - The path to the file to read. (Physical path in Méthode, e.g. /SysConfig/Common/Data/Sections.xml).

  • format (default is json) - The format of the returned content, Available formats: json, xml.

  • resolveEntities (default is false) - Boolean value to choose whether to get raw or resolved content, valid only in case the format is xml.

  • asString (default is false) - Boolean value to choose whether to get XML object or String content, valid only in case the format is xml.

Returns

Type

Description

{JSON} or {XML}

The content of the object in the format specified in the options parameter.
From Swing 5.2019.10 and later, this method returns an XML object, to return a String please use the parameter asString.

Example
var file = ctx.readFile( { 'filename': '/SysConfig/Common/Data/Sections.xml',
 'format': 'xml',
 'resolveEntities': true
});

executeQuery

Executes a Methode query given the query content as String, and returns a query result as JSON.

Syntax
ctx.executeQuery( settings, callback )
Parameters
Table 3. Parameters

Name

Type

Required

Description

settings

{String or Object}

Yes

If a string is passed, it represents the query content to be executed. Else, represents a set of arguments.

callback

{Function}

No

A callback function. See Callback definition for further information.

settings can be either a string or a Javascript Object. If it is a string, it represents the query content to be executed. Elsewhere, if a Javascript object is passed, the following properties are supported:

  • query: the query string in Methode format to be executed.

  • limit: The max number of items.

  • view: Catalog view to applied to query result.

Returns

Type

Description

{JSON Object}

The query execution result.

Example
var settings = {
    "limit":70,
    "query":"<?xml version=\"1.0\" encoding=\"utf-8\"?><!DOCTYPE EOMSearch><EOMSearch version=\"1.0\" xmlns=\"http://EidosMedia.com/EOM/SearchEngine\" xmlns:se=\"http://EidosMedia.com/EOM/SearchEngine\" xmlns:q=\"http://EidosMedia.com/EOM/SearchEngine/query\" xmlns:qm=\"http://EidosMedia.com/EOM/SearchEngine/query/macro\" xmlns:qa=\"http://EidosMedia.com/EOM/SearchEngine/query/alias\" xmlns:qui=\"http://EidosMedia.com/EOM/SearchEngine/query/UI\" xmlns:i=\"http://EidosMedia.com/query/interpolate\"><q:Query type=\"INDEX\"><q:Properties><q:MaxResultItems value=\"70\"/><q:Sort on=\"ObjectInfo/created\" type=\"NDESCENDING\"/><q:Index name=\"@meth01_eomjse1\"/></q:Properties><q:Boolean><q:OR><se:Content q:op=\"INC\" xmlns=\"\">obama</se:Content><se:Attributes q:op=\"INC\">obama</se:Attributes></q:OR></q:Boolean></q:Query></EOMSearch>"
  }
var result = ctx.executeQuery(settings);

getUserData

Reads a user data content given its filename. If the file does not exist the system creates it in the following path "eomfs:/Configurations/Profiles/{currentUserName}/{filename}.json", and returns an empty JSON array.

Syntax
ctx.getUserData( filename, callback )
Parameters
Table 4. Parameters

Name

Type

Required

Description

filename

{String}

Yes

The user data file’s name without extension.
The following names are reserved for use by the Swing application. You cannot use these names in your custom code.
- latestqueries
- contacts
- uploads
- todolist
- recents
- favourites

callback

{Function}

No

A callback function. See Callback definition for further information.

Returns

Type

Description

{JSON Array}

The content of user’s data in JSON format or an empty JSON array if is a new file.

Example
var friends = ctx.getUserData('friends');

addUserData

Adds a new JSON object to the specified user data content, denoted by the filename input parameter.

Syntax
ctx.addUserData( filename, data, callback )
Parameters
Table 5. Parameters

Name

Type

Required

Description

filename

{String}

Yes

The user data file’s name without extension.

data

{JSON Object}

Yes

The data object to be added to the user data file.

callback

{Function}

No

A callback function. See Callback definition for further information.

Returns

Type

Description

{JSON Array}

The updated list with the new added data object.

Example
var friends = ctx.addUserData('friends',  {
                 'fullName': 'Carroll Walke'
             });

setUserData

Set a new JSON array to the specified user data content, denoted by the filename input parameter.

Syntax
ctx.setUserData( filename, list, callback )
Parameters
Table 6. Parameters

Name

Type

Required

Description

filename

{String}

Yes

The user data file’s name without extension.

list

{JSON Array}

Yes

The JSON array to replace the current user data content.

callback

{Function}

No

A callback function. See Callback definition for further information.

Returns

Type

Description

{JSON Array}

The updated list with the specified JSON array

Example
var friends = ctx.setUserData('friends', [
                {
                  "fullName": "Mitchell Henson"
                },
                {
                  "fullName": "Carroll Walker"
                },
                {
                  "fullName": "Carla Sargent"
                },
                {
                  "fullName": "Sara Savage"
                },
                {
                  "fullName": "Beach Workman"
                },
                {
                  "fullName": "Courtney Roberts"
                },
                {
                  "fullName": "Abby Hood"
                },
                {
                  "fullName": "Belinda Acosta"
                }
            ]);

getContentByPath

Obtains a content of a document, given its path.

Syntax
ctx.getContentByPath( path, options, callback )
Parameters
Table 7. Parameters

Name

Type

Required

Description

path

{String}

Yes

The path of the object. E.g. 199$/SysConfig/columnCatalogCfg.swing.xml
For paths of databases other than the primary, the database Id must be specified.

options

{Object}

No

An optional list of options. See below

callback

{Function}

No

A callback function. See Callback definition for further information.

The options object is a Javascript object with the following properties:

  • cached (default is false). If true, the document xml is cached and the following times will be retrived from the internal cache.

Returns

Type

Description

{string}

The content of the object as a string.

Example
ctx.getContentByPath('199$/SysConfig/columnCfgCatalogs.swing.xml', { cached: true }, function(err, content) {
    if (err) {
        ctx.showError(err);
        return;
    }
    console.log(content);
});
Note

The method can also be called synchronously, but this usage is not recommended.

getCurrentUser

Obtains the current user.

Syntax
ctx.getCurrentUser( callback )
Parameters
Table 8. Parameters

Name

Type

Required

Description

callback

{Function}

Yes

A callback function. See Callback definition for further information.

Returns

Type

Description

{User object}

An instance of the User Class. See User class methods for further information on the methods available.

Example
ctx.getCurrentUser(function(err,User) {
    if (err) {
        ctx.showError(err);
        return;
    }
    // Do something with the user...
    alert(User.getName());
});
Note

The method can also be called synchronously, but this usage is not recommended.

getObject

Obtains a Methode object, given its id

Syntax
ctx.getObject( id, callback );
Parameters
Table 9. Parameters

Name

Type

Required

Description

id

{String}

Yes

The document id. Format is [<databaseId>$]<loid>.

callback

{Function}

Yes

A callback function. See Callback definition for further information.

Returns

Type

Description

{Object class}

An instance of the Object Class. See Object class methods for further information on the methods available.

Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }
    // Do something with the object...
    Obj.openReadonly();
});
Note

The method can also be called synchronously, but this usage is not recommended.

getObjectByPath

Obtains a Methode object, given its path

Syntax
ctx.getObjectByPath( path, callback );
Parameters
Table 10. Parameters

Name

Type

Required

Description

path

{String}

Yes

The document path.

callback

{Function}

Yes

A callback function. See Callback definition for further information.

Returns

Type

Description

{Object class}

An instance of the Object Class. See Object class methods for further information on the methods available.

Example
ctx.getObjectByPath('/Globe/Stories/Test.xml', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }
    // Do something with the object...
    Obj.openReadonly();
});
Note

The method can also be called synchronously, but this usage is not recommended.

getPlatformInfo

Obtains the Platform information

Syntax
ctx.getPlatformInfo();
Parameters

No parameters required.

Returns

Type

Description

{Object}

A generic Javascript Object containing the platform information. application: The name of the Application ('Swing', 'Prime', 'SwingApp')
copyright: The copyright information

Example
var platformInfo = ctx.getPlatformInfo();

if (platformInfo.application.indexOf('Swing') > -1) {
    // Do something only if it's Swing/SwingApp
}

getUserByName

Obtains a user, given its name.

Syntax
ctx.getUserByName( name, callback )
Parameters
Table 11. Parameters

Name

Type

Required

Description

name

{String}

Yes

The user name to look for.

callback

{Function}

Yes

A callback function. See Callback definition for further information.

Returns

Type

Description

{User object}

An instance of the User Class. See User class methods for further information on the methods available.

Example
ctx.getUserByName( 'user.test1', function(err,User) {
    if (err) {
        ctx.showError(err);
        return;
    }
    // Do something with the user...
    alert(User.getName());
});
Note

The method can also be called synchronously, but this usage is not recommended.

getGroupMembers

Obtains the list of member names, given a group name

Syntax
ctx.getGroupMembers( groupName, callback )
Parameters
Table 12. Parameters

Name

Type

Required

Description

groupName

{String}

Yes

The group name to look for.

callback

{Function}

Yes

A callback function. See Callback definition for further information.

Returns

Type

Description

{JSON Array}

The callback function will be called with the list of users belonging to the requested group, as an array of user names.

Example
ctx.getGroupMembers( 'group.test1', function(err, members) {
    if (err) {
        ctx.showError(err);
        return;
    }
    // Do something with the user names...
    for (var i = 0; i < members.length; i++) {
        console.log(members[i]);
    }
});

openDocument

Open a Methode document, given its id

Syntax
ctx.openDocument( id, callback )
Parameters
Table 13. Parameters

Name

Type

Required

Description

id

{String}

Yes

The document id to open. Format is [<databaseId>$]<loid>.

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Returns

The methods does not return any information. The callback is called when the document is opened.

Example
ctx.openDocument( '199$1.0.893734064', function(err,User) {
    if (err) {
        ctx.showError(err);
        return;
    }

    // Document has opened correctly
});

openNewContentDialog

Open the new content dialog, with the selected options

Syntax
ctx.openNewContentDialog( options, callback )
Parameters
Table 14. Parameters

Name

Type

Required

Description

options

{Object}

Yes

The options with which the dialog is loaded.

callback

{Function}

Optional

A callback function. See Callback definition for further information.

The options parameter is a javascript object in which it is possible to define:

  • contentType (available options are 'story', 'topic', 'dwp', 'dwc', 'topicplan')

  • name

  • channel

  • edition

  • team

  • issueDate

  • template

  • workfolder

Returns

The methods does not return any information. The callback is called when the document is created.

Example
ctx.openNewContentDialog( {
    template: '/SysConfig/Globe/Templates/poll.xml',
    contentType: 'story'
}, function(err,data) {
    if (err) {
        ctx.showError(err);
        return;
    }
    // Document has been created correctly
    alert( data.name );
});

openReadonly

Open a Methode document, given its id, in Readonly mode

Syntax
ctx.openReadonly( id, callback )
Parameters
Table 15. Parameters

Name

Type

Required

Description

id

{String}

Yes

The document id to open. Format is [<databaseId>$]<loid>.

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Returns

The methods does not return any information. The callback is called when the document is opened.

Example
ctx.openReadonly( '199$1.0.893734064', function(err,data) {
    if (err) {
        ctx.showError(err);
        return;
    }

    // Document has opened correctly
});

openPopupPanel

Opens a custom HTML panel developed and specified by the user.

Syntax
ctx.openPopupPanel( panelName );
Table 16. Method information

Method

openPopupPanel

Parameter

{string} panelName - The string panel name.

Returns

undefined

Example
var panelName = 'myTestPanel';
ctx.openPopupPanel( panelName );
Registration of the custom panel

The custom panel must be registered via Javascript. The namespace of reference is the following:

eidosmedia.webclient.extensions.popups

A call to the register method must be done as follows:

eidosmedia.webclient.extensions.popups.register( name, settings );
  • {String} name: the name of the custom popup ( same as the one called from the ctx.openPopupPanel() method.

  • {Object} settings: a javascript object with the settings. It has the following parameters:

    • {String} icon: the icon css class.

    • {String} title: the title of the custom dialog.

    • {String} containerStyle: the style of the container. See info box below.

    • {String} template: the path to the HTML file. It is relative to the application context.

    • {Function} ready: a function which is called when the popup is loaded. The function is called with two parameters:

      • ctx - the Context Object ( with the activeDocument if you are in the Story Editor context )

      • $modal - the jQuery reference to identify the modal. The example below will show some possible uses.

eidosmedia.webclient.extensions.popups.register('testpopup', {
    icon: 'icon-plus',
    title: 'Custom panel title',
    containerStyle: 'background-color:red;',
    template: '/config/templates/customTemplates/testpopup.html',
    ready: function( ctx, $modal ) {
        // Popup is visible.
        console.log( ctx );

        // CUSTOM EXAMPLE
        /*
            In the footer example, we added a 'data-modal-action' to the OK button.
            We now intercept the click inside the modal.
        */

        // Add modal listeners.
        $modal.on('click', '[data-modal-action]', function( event ) {
            var action = $(this).attr('data-modal-action');
            switch(action) {
                case 'ok':
                    alert('Button clicked!');

                    // To close the modal, use $modal.modal('hide');
                    $modal.modal('hide');
                break;
            }
            event.preventDefault();
            event.stopPropagation();


        });

    }
});

The popup panel is built around a classical Bootstrap Modal. In particular, the HTML code in your template will be placed INSIDE the modal-body.

So, the modal will be basically created as follows:

<div class="modal-header">
  <!-- HEADER CREATED BY SWING -->
</div>
<div class="modal-body" style="<!-- containerStyle info here -->">
  <!-- CUSTOM HTML HERE -->
</div>

No predefined style is given for the footer, but it is recommended to be as follows:

<div class="modal-footer">
  <button class="btn btn-primary" data-modal-action="ok">OK</button>
    <button class="btn" data-dismiss="modal">Cancel</button>
</div>

So, considering that your code is wrapped inside the modal-body, if you want to use a footer, close a </div> and leave the footer html "unclosed".

Example:

My custom html here.
</div> <!--Here we close the modal-body -->
<div class="modal-footer">
    <button class="btn btn-primary" data-modal-action="ok">OK</button>
    <button class="btn" data-dismiss="modal">Cancel</button>
<!-- We don't close the footer since it is closed by the modal dialog. -->

IMPORTANT: as it is a bootstrap modal, by adding data-dismiss="modal" to a button, bootstrap will automatically close the modal.

Object class methods

List of methods

Table 17. Parameters

Name

Description

Add the collaborators to an object

Execute an EOMDB action for the current object

Returns the collaborators of an object

Returns the correlations of an object

Returns the object id

Returns the object info

In case of a report or a DWP, returns the documents linked.

Returns the object locker

Returns the metadata of the object

Returns the object name

Returns the object status information

Returns the system attributes of the object, in XML format

Returns the object type

Checks if the object is typeof the given type

Returns the usage tickets of the object

Returns the virtual attributes of the object, in XML format

Open the current object

Open the current object in Readonly mode

Register to all the EOMDB events of an item

Refresh the current object information

Remove the collaborators of an object

Set the metadata of an object

Update the status of the current object

Set the system attributes of an object

Gets the bundle information of the current object

Unset a metadata field of the current object

Gets the channel copies list of the current object - Restricted for EOM::CompoundStory objects

Create a channel copy of the current object - Restricted for EOM::CompoundStory objects

addCollaborators

Add the collaborators to an object

Parameters
Table 18. Parameters

Name

Type

Required

Description

collaborators

{String or Array}

Yes

A list made of user names.

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Syntax
Object.addCollaborators(collaborators, callback)
Returns

The method returns the an array of collaborators as string.

Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }
    Obj.addCollaborators(['user.3', 'user.4'], function(err, data){
        if (err) {
            ctx.showError(err);
            return;
        }
        // data is now ['user.1', 'user.2', 'user.3', 'user.4'];
        var User = ctx.getUserByName(data[0]).getInfo()
    });
});

The method can also be called synchronously, but this usage is not recommended.

executeAction

Execute an EOMDB action for the current object

Syntax
Object.executeAction( settings, callback )
Parameters
Table 19. Parameters

Name

Type

Required

Description

settings

{String or Object}

Yes

If a string is passed, it represents the name of the action to execute. Else, represents a set of arguments.

callback

{Function}

Yes

A callback function. See Callback definition for further information.

settings can be either a string or a Javascript Object. If it is a string, it will interpreted as the actionId, and the action will be executed with no other params. Elsewhere, if a Javascript object is passed, the following properties are supported:

  • actionId: the action name. REQUIRED.

  • args: the arguments as a String.

Returns

The method returns the action response.

Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }
    // Or... Obj.executeAction({ actionId: 'postToTwitter', args: 'arguments here');
    Obj.executeAction('postToTwitter', function(err, data) {
        //.. Do something here.
    });
});

The method can also be called synchronously, but this usage is not recommended.

getId

Returns the object id

Syntax
Object.getId()
Returns

The method returns the object id as a string.

Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }

    // Well, we already knew that...
    var objectId = Obj.getId();
});

getCollaborators

Get the collaborators of an object

Syntax
Object.getCollaborators(callback)
Returns

The method returns the an array of collaborators as string.

Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }
    Obj.getCollaborators(function(err, data){
        if (err) {
            ctx.showError(err);
            return;
        }
        // data is now ['user.1', 'user.2'];
        var User! = ctx.getUserByName(data[0]).getInfo()
    });
});

The method can also be called synchronously, but this usage is not recommended.

getCorrelations

Get the correlations of an object

Syntax
Object.getCorrelations(linkName, callback)
Parameters
Table 20. Parameters

Name

Type

Required

Description

linkName

{String}

Optional

The name of the linkName to show.

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Returns

The method returns the an array of correlations as Ctx Objects.

Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }
    // Without linkName
    Obj.getCorrelations(function(err, data){
    /*
    * With linkName,
    * Obj.getCorrelations('see_also', function(err, data){
    */
        if (err) {
            ctx.showError(err);
            return;
        }

        // data is an array of Objects
        for (var j = 0; j < data.length; j++) {
            console.log(data.getName());
        }
    });
});

The method can also be called synchronously, but this usage is not recommended.

getInfo

Returns the object info

Syntax
Object.getInfo()
Returns

The method returns the object information as JSON.

Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }

    var objInfo = Obj.getInfo();
    // Obj info is now a JSON containing all the information.
});

In case of a report or a DWP, returns the documents linked.

Syntax
Object.getLinks(callback)
Returns

The method returns the an array of links in the form of Object Class instances.

Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }
    Obj.getLinks(function(err, links){
        if (err) {
            ctx.showError(err);
            return;
        }
        // Each link is an Object Class instance.
        // So, it is possible to do so.
        for (var j = 0, ln = links.length; j < ln; j++) {
            console.log( links[j].getLocker() );
        }
    });
});

getLocker

Returns the object locker

Syntax
Object.getLocker()
Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }
    var lockerBy = Obj.getLocker();
});

getMetadata

Returns the metadata of the object

Syntax
Object.getMetadata()
Returns

The method returns the metadata information as String

Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }
    var objMetadata = Obj.getMetadata();
    var $objMetadata = $.parseXML(objMetadata);
});

getName

Returns the object name

Syntax
Object.getName()
Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }
    var name = Obj.getName();
});

getStatusInfo

Returns the object statusInformation

Syntax
Object.getStatusInfo()
Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }
    var statusInfo = Obj.getStatusInfo();
    // Now
    // statusInfo.name is the status name
    // statusInfo.comment
    // statusInfo.identifier is the RGB color
});

getSystemAttributes

Returns the system attributes of the object, in XML format (as string)

Syntax
Object.getSystemAttributes()
Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }
    var objSysAttr = Obj.getSystemAttributes();
    var $sysAttr = $.parseXML(objSysAttr);
});

Use the refresh method to get up to date object information before call this method.

getType

Returns the object type

Syntax
Object.getType()
Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }
    var type = Obj.getType();
});

isType

Checks if the object is typeof the given type

Syntax
Object.isType(testtype)
Parameters
Table 21. Parameters

Name

Type

Required

Description

testtype

{String}

Yes

The type to test against the object type.

Returns

The method returns true if and only if the object type is type of the testtype else the method returns false.

Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }
    var type = Obj.isType('EOM::Story');
});

getUsageTicket

Returns the usage tickets of the object, as an XML String

Syntax
Object.getUsageTicket()
Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }
    var usageTicket = Obj.getUsageTicket();
    var $usageTicket = $.parseXML(usageTicket);
});

Use the refresh method to get up to date object information before call this method.

getVirtualAttributes

Returns the virtual attributes of the object, as an XML String

Syntax
Object.getVirtualAttributes()
Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }
    var virtualAttributes = Obj.getVirtualAttributes();
    var $virtualAttributes = $.parseXML(virtualAttributes);
});

Use the refresh method to get up to date object information before call this method.

open

Open the current object in editing mode

Syntax
Obj.open( id, callback )
Parameters
Table 22. Parameters

Name

Type

Required

Description

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Returns

The methods does not return any information. The callback is called when the document is opened.

Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }
    Obj.open(function(err, data) {
        // Callback after the document is opened
    });
});

openReadonly

Open the current document in Readonly mode

Syntax
Obj.openReadonly( callback )
Parameters
Table 23. Parameters

Name

Type

Required

Description

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Returns

The methods does not return any information. The callback is called when the document is opened.

Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }
    Obj.openReadonly(function(err, data) {
        // Callback after the document is opened
    });
});

registerToEvents

Register to all the EOMDB events of an item

Syntax
Obj.registerToEvents(eventCallback)
Parameters
Table 24. Parameters

Name

Type

Required

Description

eventCallback

{Function}

Yes

A callback function which is called at any event. See the example for further information.

Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }

    // Register to all the events.
    Obj.registerToEvents(function(eventName, data) {
        // eventName contains the name of the event
        // "unlock", "lock", "check in"...
        // data contains the information regarding the event.

        // Eventually, if needed, call a refresh for the object.

        Obj.refresh().getLocker();

    });


});

refresh

Refresh the current object information and returns a new Object class.

Syntax
Obj.refresh()
Returns

Type

Description

{Object class}

An instance of the Object Class. See Object class methods for further information on the methods available.

Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }

    // Perform very complex operations
    // Such as saving the document or setting the document metadata

    // To obtain the new metadata... do this.
    Obj = Obj.refresh();
    Obj.getMetadata();
});

removeCollaborators

Remove the collaborators from an object

Parameters
Table 25. Parameters

Name

Type

Required

Description

collaborators

{String or Array}

Yes

A list made of user names.

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Syntax
Object.removeCollaborators(collaborators, callback)
Returns

The method returns the an array of collaborators as string.

Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }
    Obj.removeCollaborators(['user.1'], function(err, data){
        if (err) {
            ctx.showError(err);
            return;
        }
        // data is now ['user.1'];
        var User = ctx.getUserByName(data[0]).getInfo()
    });
});

The method can also be called synchronously, but this usage is not recommended.

setMetadata

Set the metadata of an object

Syntax
Obj.setMetadata(options, callback);
Parameters
Table 26. Parameters

Name

Type

Required

Description

options

{Object or Array}

Yes

An object made of xpath and content properties. Can be an array of those properties.

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }

    var request = { xpath: '/ObjectMetadata/General/Priority', content: 'High title'};
    // Or an array
    // var request = [ { xpath: '/ObjectMetadata/General/Priority', content: 'High title'},
                        { xpath: '/ObjectMetadata/General/Priority2', content: 'High title2'}
                        { xpath: '/ObjectMetadata/General/Priority3', content: 'High title3'} ];
    Obj.setMetadata(request, function(err, data) {
        if (err) {
            ctx.showError(err);
            return;
        }
        // Success...
    });
});

The method can also be called synchronously, but this usage is not recommended.

setStatus

Update the status of the current object

Syntax
Obj.setStatus('NewsFlow/Editing', callback);
Obj.setStatus({ name: 'NewsFlow/Editing', comment: 'My comment'}, callback)
Returns

Type

Description

{Object class}

An instance of the Object Class. See Object class methods for further information on the methods available.

Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }

    Obj.setStatus('NewsFlow/Editing', function(err, data) {
        if (err) {
            ctx.showError(err);
            return;
        }
        // Success...
    });
});

The method can also be called synchronously, but this usage is not recommended.

setSystemAttributes

Set the system attributes of an object

Syntax
Obj.setSystemAttributes(options, callback);
Parameters
Table 27. Parameters

Name

Type

Required

Description

options

{Object or Array}

Yes

An object made of xpath and content properties. Can be an array of those properties.

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }

    var request = { xpath: '/props/title/', content: 'High title'};
    // Or an array
    // var request = [ { xpath: '/props/title/', content: 'High title'},
                        { xpath: '/props/summary/', content: 'Summary'} ];
    Obj.setSystemAttributes(request, function(err, data) {
        if (err) {
            ctx.showError(err);
            return;
        }
        // Success...
    });
});

The method can also be called synchronously, but this usage is not recommended.

cleanMetadataField

Unset a metadata field of the current object

Syntax
Obj.cleanMetadataField(xpath, callback);
Parameters
Table 28. Parameters

Name

Type

Required

Description

option

{String}

Yes

A metadata field xpath to remove.

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Example
ctx.getObject('199$1.0.893734064', function(err, Obj) {
    if (err) {
        ctx.showError(err);
        return;
    }

    Obj.cleanMetadataField('/ObjectMetadata/General/Priority', function(err, data) {
        if (err) {
            ctx.showError(err);
            return;
        }
        // Success...
    });
});

The method can also be called synchronously, but this usage is not recommended.

getBundleInfo

Gets the bundle information of the current object

Syntax
Obj.getBundleInfo(callback);
Parameters
Table 29. Parameters

Name

Type

Required

Description

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Returns

The bundle information:

[
  {
    "id": "99$1.0.287059148",
    "name": "Obama.xml",
    "baseName": "Obama",
    "path": "/Globe/Stories/Globe/Stories/Politics/2019-03-18/Politics/Obama.xml",
    "channel": "Globe-Web",
    "edition": null,
    "section": null,
    "issueDate": "20190318",
    "subFolder": null,
    "workFolder": "/Globe/Politics",
    "channelIdentifier": "Globe-Web"
  },
  {
    "id": "99$1.0.287059376",
    "name": "Obama@Globe-Print-USA.xml",
    "baseName": "Obama@Globe-Print-USA",
    "path": "/Globe/Stories/Globe/Stories/Politics/2019-03-18/Politics/Obama.xml/Obama@Globe-Print-USA.xml",
    "channel": "Globe-Print",
    "edition": "USA",
    "section": null,
    "issueDate": "20190318",
    "subFolder": null,
    "workFolder": "/Globe/Politics",
    "channelIdentifier": "Globe-Print/USA"
  }
]
Example
ctx.activeObject.getBundleInfo(function(err, list) {
    if (err) {
        ctx.showError(err);
        return;
    }
    //Success
});

The method can also be called synchronously, but this usage is not recommended.

getChannelCopiesList

Gets the channel copies list of the current object - Restricted for EOM::CompoundStory objects

Syntax
Obj.getChannelCopiesList(callback);
Parameters
Table 30. Parameters

Name

Type

Required

Description

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Returns

The list of the created channel copies:

[
    {
        "id": "41$1.0.107332121",
        "channel": null,
        "info": {
            "id": "41$1.0.107332121",
            "name": "story.xml",
            "baseName": "story",
            "path": "/Globe/Stories/Art/story.xml",
            "channel": null,
            "edition": null,
            "section": null,
            "issueDate": "20190111",
            "subFolder": null,
            "workFolder": "/Globe/Art",
            "channelIdentifier": "none"
        },
        "master": true
    },
    {
        "id": "41$1.0.107314527",
        "channel": "Globe-Print",
        "info": {
            "id": "41$1.0.107314527",
            "name": "story@Globe-Print.xml",
            "baseName": "story@Globe-Print",
            "path": "/Globe/Stories/Art/story.xml/story@Globe-Print.xml",
            "channel": "Globe-Print",
            "edition": null,
            "section": null,
            "issueDate": "20190111",
            "subFolder": null,
            "workFolder": "/Globe/Art",
            "channelIdentifier": "Globe-Print"
        }
    }
]
Example
ctx.activeObject.getChannelCopiesList(function(err, list) {
    if (err) {
        ctx.showError(err);
        return;
    }
    //Success
});

The method can also be called synchronously, but this usage is not recommended.

createChannelCopy

Create a channel copy of the current object - Restricted for EOM::CompoundStory objects

Syntax
Obj.createChannelCopy(channel, callback);
Parameters
Table 31. Parameters

Name

Type

Required

Description

channel

{String}

Yes

A string containing the channel in form of "Product/Edition".

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Returns

The method returns the object information of the new channel copy as JSON.

Example
ctx.activeObject.createChannelCopy('Product/Edition', function(err) {
    if (err) {
        ctx.showError(err);
        return;
    }
    //Success
});

The method can also be called synchronously, but this usage is not recommended.

User class methods

List of methods

Table 32. Parameters

Name

Description

Returns the user groups

Returns the user info

Returns the usermetadata

Returns the username

Returns the system attributes of the user, in XML format

Returns the user teams.

Sets the system attributes of the user

Sets the metadata of the user

getGroups

Returns the groups which the user belongs to

The method returns an error for users other than the current one.

Syntax
User.getGroups( [callback] )
Parameters
Table 33. Parameters

Name

Type

Required

Description

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Example
ctx.getCurrentUser(function(err,User) {
    var groups = User.getGroups();
    console.log(teams);
});

The method can be called either synchronously or asynchronously. It is indifferent.

getInfo

Returns the user info

Syntax
User.getInfo( [callback] )
Parameters
Table 34. Parameters

Name

Type

Required

Description

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Returns

The user information in JSON.

{
  "name": "john.doe",
  "description": "Star Software",
  "ucount": 13159,
  "databaseId": 199,
  "id": "1.0.532488046",
  "fullName": "John Doe",
  "phoneNumber": "(01) 5748584456)",
  "mobileNumber": "(001) 5476987496798)",
  "twitter": "johndoe",
  "role": "Reporter",
  "homeEmail": "john.doe@google.com",
  "businessEmail": "john.doe@star.com",
  "statusMessage": "I am available",
  "workDir": "workFolder:///Globe/Art",
  "location": "London",
  "lastLoggedOn": 1494418598,
  "initials": "JD",
  "signature": "john doe",
  "homePath": "/Users/john.doe",
  "calendars": [
    {
      "color": "#32AE0C",
      "id": "U12001806763ERC",
      "name": "UK Holidays",
      "url": "https://www.gov.uk/england-and-wales.ics",
      "icon": "icon-calendar",
      "private": false
    }
  ],
  "status": "busy",
  "disabled": false
}
Example
ctx.getCurrentUser(function(err,User) {
    var userInfo = User.getInfo();
    // Or, if you prefer,
    User.getInfo(function(err, userInfo){
        console.log(userInfo.name);
    });
});

The method can be called either synchronously or asynchronously. It is indifferent.

getMetadata

Returns the user metadata, as a string.

The method returns an error for users other than the current one.

Syntax
User.getMetadata( [callback] )
Parameters
Table 35. Parameters

Name

Type

Required

Description

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Example
ctx.getCurrentUser(function(err,User) {
    var metadata = User.getMetadata();
    console.log(metadata);
});

The method can be called either synchronously or asynchronously. It is indifferent.

getName

Returns the user name

Syntax
User.getName( [callback] )
Parameters
Table 36. Parameters

Name

Type

Required

Description

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Returns

The user name as string.

Example
ctx.getCurrentUser(function(err,User) {
    var userName = User.getName();
    console.log(userName);
});

The method can be called either synchronously or asynchronously. It is indifferent.

getSystemAttributes

Returns the system attributes of the user, in XML format (as string)

The method returns an error for users other than the current one.

Syntax
User.getSystemAttributesXML( [callback] )
Parameters
Table 37. Parameters

Name

Type

Required

Description

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Returns

The system attributes of the User, as a XML string.

Example
ctx.getCurrentUser(function(err,User) {
    var systemAttributesXml = User.getSystemAttributes();
    var $sysAttr = $.parseXML(systemAttributesXml);
    console.log(systemAttributesXml);
});

The method can be called either synchronously or asynchronously. It is indifferent.

getTeams

Returns the teams which the user belongs to

The method returns an error for users other than the current one.

Syntax
User.getTeams( [callback] )
Parameters
Table 38. Parameters

Name

Type

Required

Description

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Example
ctx.getCurrentUser(function(err,User) {
    var teams = User.getTeams();
    console.log(teams);
});

The method can be called either synchronously or asynchronously. It is indifferent.

setSystemAttributes

Sets the system attributes of the user

The method returns an error for users other than the current one.

Syntax
User.setSystemAttributes( systemAttributes, [callback] )
Parameters
Table 39. Parameters

Name

Type

Required

Description

systemAttributes

{String or object}

Yes

A string or an object to represent the system attributes

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Returns

The system attributes of the User, as a XML string.

Example
ctx.getCurrentUser(function(err,User) {
    var systemAttributes = '<props><principalInfo><email type="business">johndoe@star.comw</email><initials>JD</initials><signature>john doe</signature><statusMessage>I am available</statusMessage><color type="revision" value="#008080"/><color type="annotation" value="#800000"/><location>London</location><email type="home">johndoe@google.com</email><twitter>johndoe</twitter><facebook>aaaa</facebook><mobileNumber>(01) 7834684587438</mobileNumber><phoneNumber>(001) 54753754765375</phoneNumber><status>busy</status><workDir>workFolder:///Globe/Art</workDir><calendar><id>U12001806763ERC</id><icon>icon-calendar</icon><color>#32AE0C</color><name>UK Holidays</name><url>https://www.gov.uk/bank-holidays/england-and-wales.ics</url><private>false</private></calendar></principalInfo><role>Reporter</role><OutputChannels><channel name="Globe-Print"/></OutputChannels></props>';

    User.setSystemAttributes(systemAttributes, function(err, data) {
        if (err) {
            // Do something...
            return;
        }
    );

});

setMetadata

Sets the metadata of the user

The method returns an error for users other than the current one.

Syntax
User.setMetadata( metadata, [callback] )
Parameters
Table 40. Parameters

Name

Type

Required

Description

metadata

{String or object}

Yes

A string or an object to represent the metadata

callback

{Function}

Optional

A callback function. See Callback definition for further information.

Returns

The system attributes of the User, as a XML string.

Example
ctx.getCurrentUser(function(err,User) {
    var attributes = '<test><secondtest>hello</secondtest></test>';

    User.setMetadata(attributes, function(err, data) {
        if (err) {
            // Do something...
            return;
        }
    );

});

activeObject

In various contexts, such as the toolbars, editors, Diagram workflow, etc., there is an additional property available, named activeObject.

The activeObject is actually an instance of the Object Class and contains all methods of this class. See Object class methods for further information.

The old activeObj property is being discontinued from Swing 4 and will not be available in Prime. Please use the new activeObject property instead.

activeDocument

Inside the editor context, it is possible to access the activeDocument. Please refer to Editor API methods reference.

Callback definition

The callback parameter, when specified, is always defined as follows:

// Callback definition
function( err, response ) {
    // This is the "Node.js" callback style.
    // It forces the developer to manage the errors.

    if (err) {
        // Do something...
        return;
    }

    // Real implementation here

}

Basic structure of the Context Object

The Context object is made of a specific list of properties, and a number of publicly available methods.

{
    application: {
        getId: function()
    },
    area: {
        getType: function(),
        getName: function()
    },
    component: {
        getType: function()
    },
    activeObject: {
        // Methods of the single object.
    },
    selection: [ ] // Array of objects. See below.
}
Table 41. Parameters list
Parameter Value type Description Values

application.getId()

String

Returns the application Id.

"swing", …​

area.getType()

String

Returns the area type.

"main" (for the main views), "editor"

area.getName()

String

Returns the area name.

"explorer", "dashboard", "myarea", "liveblogmanagement" (for "main" areas). "story", "gallery" (for editors).

component.getType()

String

Returns the component type.

"toolbar", "grid", "objectpanel"…​

activeObject

Object

Returns an object with a set of methods.

selection

Array of Object

Returns an array with the currently selected items. Each item has the same set of methods available.

Each context has a different value for these properties.

Custom menu and actions

In Methode Swing it is possible to configure custom menus and custom object actions.

Prepare the plugin to add menus and action

In general, all the extensions of Méthode Swing are places under

{SWING-APP}/plugins

So, all the Javascript described in the following sections should be placed under

{SWING-APP}/app/plugins/{EXTENSION-FOLDER}/{EXTENSION-NAME}.js

Do not use the word libs as an extension folder. The libs folder is reserved for loading external libs. See the proper documentation to obtain further info on the topic.

DEBUG MODE: by default, all plugins are aggregated into a single plugins.js file at the Tomcat startup. When creating a plugin, it may be frustrating to restart the Tomcat Server every time a change is made.

To avoid this, set the configuration property debugEnabled to true.

Then, that extension will be loaded on every refresh of the page.

Add a custom menu

In order to add a custom menu to Methode Swing, it is necessary to use the following namespace:

eidosmedia.webclient.extensions.header.newcontentmenu

New menus will be added in the next versions.

The syntax is the following:

eidosmedia.webclient.extensions.header.newcontentmenu.add(name, options);

Where:

  • name [string] is the name of the custom menu, alias a unique identifier.

  • options [optional, object] is a Javascript object containing the properties of the custom actions.

The options parameter can be omitted. In this case, Swing will use the previously registered command with the same name property. See example below.

If options is specified, it must follow the guidelines of a generic Swing command. See Commands documentation for further information.

Example

eidosmedia.webclient.commands.add({
    name: 'complex',
    icon: 'icon-compass',
    label: 'Perform some complex operation...',
    action: function( ctx, params, callback ) {
        if ( ctx.component.getType() === 'menu') {
            alert('hello menu');
            // perform same difficult operations...
            if (callbacks && callbacks.success) {
                callbacks.success( /*...*/ );
            }
        }

    }
});

eidosmedia.webclient.extensions.header.newcontentmenu.add('complex');

Add a custom action to an object

In a conceptually similar way, it is possible to add custom actions to objects. They will be shown in the search results, in the detailed preview of the object, and generally speaking everytime the object is available.

The syntax is the following:

eidosmedia.webclient.extensions.objectactions.add(name, options);

Where:

  • name [string] is the name of the custom menu, alias a unique identifier.

  • options [optional, object] is a Javascript object containing the properties of the custom actions.

The options parameter can be omitted. In this case, Swing will use the previously registered command with the same name property. See example below.

If options is specified, it must follow the guidelines of a generic Swing command. See Commands documentation for further information.

Example

eidosmedia.webclient.commands.add({
    name: 'complex',
    icon: 'icon-compass',
    label: 'Perform some complex operation...',
    isActive: function( ctx ) {
        // For some buttons ctx.activeObject is not available.
        return ctx.activeObject && ctx.activeObject.getType() === 'Image';
    },
    action: function( ctx, params, callback ) {
        if ( ctx.component.getType() === 'menu') {
            alert('hello menu');
            // perform same difficult operations...
            if (callbacks && callbacks.success) {
                callbacks.success( /*...*/ );
            }
        }

    }
});

eidosmedia.webclient.extensions.objectActions.add('complex');

Validators

Méthode Swing can be extended by providing the so called validators, that is a set of function to validate the input before it is sent to the Server to be saved.

Location of the Validators extensions

In general, all the extensions of Méthode Swing are places under

{SWING-APP}/plugins

So, all the Javascript described in the following sections should be placed under

{SWING-APP}/app/plugins/{EXTENSION-FOLDER}/{EXTENSION-NAME}.js

Do not use the word libs as an extension folder. The libs folder is reserved for loading external libs. See the proper documentation to obtain further info on the topic.

DEBUG MODE: by default, all plugins are aggregated into a single plugins.js file at the Tomcat startup. When creating a plugin, it may be frustrating to restart the Tomcat Server every time a change is made.

To avoid this, set the configuration property debugEnabled to true.

Then, that extension will be loaded on every refresh of the page.

Supported validators

With the evolution of Méthode Swing, more validators will be added. At the moment, the application supports the following validators.

renameFile validator

As the name suggests, the renameFile validator is called whenever the user tries to rename a file. This happens in an opened editor, or in the Explorer area, or during a Worflow/Release action.

To add a renameFile validator, it is necessary to register it inside Swing by calling the following Javascript function, inside your Javascript file:

eidosmedia.webclient.extensions.validators.addValidator('renameFile', function( ctx, onSuccess, onError ) { /* ... */ } );

The function requires three parameters:

  • ctx: The ctx is a JSON object which represents the object’s context. See Basic structure of the Context Object for further details. See the code for further details on the available properties.

  • onSuccess: function to be called when the validation has passed. It wants a single parameter, newName, which is optional. See the code for further details on the parameters.

  • onError: function to be called when the validation failed. It wants a single parameter, errorMessage. See the code for further details.

Example and explanation of parameters

eidosmedia.webclient.extensions.validators.addValidator('renameFile', function( ctx, onSuccess, onError ) {
    // The ctx object contains the following properties.
    // context: 'application'
    // activeObject->getInfo() : {
    //    id: the story id,
    //    newName: the name specified by the user
    //    originalName: the originalName of the file
    // }

    // We obtain the information
    var info = ctx.activeObject.getInfo(); // { id, newName, originalName }

    // SAMPLE IMPLEMENTATION.

    if ( info.newName === "FORBIDDEN.xml" ) {

        // onError = function (errorMessage). Call it with the error message to be shown.
        onError( "This name is absolutely forbidden!" );
        return;
    } else {
        // The parameter is optional. If not passed, the document will be saved with the name specified
        // by the user. Passing the parameter, it is possible to change the name in the validator.
        onSuccess(); // Same as: onSuccess( info.newName );
        // OR
        onSuccess( "VALIDATED_" + info.newName );
    }
});

Editor URL Redirect validator

As the name suggests, the Editor URL Redirect validator is called whenever the user tries to add or edit a url in the Editor Url Redirect.

To add a Editor URL Redirect validator, it is necessary to register it inside Swing by calling the following Javascript function, inside your Javascript file:

eidosmedia.webclient.extensions.validators.addValidator('url-redirect', function( ctx, urlInfo, callback ) { /* ... */ } );

The function requires three parameters:

  • ctx: The ctx is a JSON object which represents the object’s context. See Basic structure of the Context Object for further details. See the code for further details on the available properties.

  • urlInfo: a Javascript object containing all the information regarding the selected url. See the code for further details on the available properties.

  • callback: function to be called when the validation has been completed. See the code to know how to call it.

Example and explanation of parameters

eidosmedia.webclient.extensions.validators.addValidator('url-redirect', function( ctx, urlInfo, callback ) {
    // The ctx object contains the following properties.
    // context: 'editor'
    // area->getName: 'editor-url-redirect',
    // activeObject->getInfo() : the document information.

    // The urlInfo contains the following properties
    // urlInfo: {
    //  type: 'redirect' // or 'permanentredirect', or 'temporaryredirect' or 'forward'
    //  url: the starting url
    //  redirect: the target url
    //  isEditing: true / false ( if the url is a new one or not )
    //}

    // Perform the validation here...
    callback( true ); // Will tell the editor that the validation has passed.
    // callback() or callback( false ) or callback( any falsy value ) will tell the editor that the validation has failed.
}

Editor Extensions

Commands configuration

Méthode Swing allows to add custom commands. Such commands can be launched in different situations, such as external DAM searches, custom Object Panels and widgets. Furthermore, M´thode Swing allows to configure such commands in specific parts of the User Interface (UI) such as, for example, the Toolbar inside the Explorer tab or the Explorer Grid (via Column Catalog).

Prepare the plugin to add commands

In general, all the extensions of Méthode Swing are places under

{SWING-APP}/plugins

So, all the Javascript described in the following sections should be placed under

{SWING-APP}/app/plugins/{EXTENSION-FOLDER}/{EXTENSION-NAME}.js

Do not use the word libs as an extension folder. The libs folder is reserved for loading external libs. See the proper documentation to obtain further info on the topic.

DEBUG MODE: by default, all plugins are aggregated into a single plugins.js file at the Tomcat startup. When creating a plugin, it may be frustrating to restart the Tomcat Server every time a change is made.

To avoid this, set the configuration property debugEnabled to true.

Then, that extension will be loaded on every refresh of the page.

Format of a command

A command is represented by a JSON object which should contain at least the following properties:

// Command options
{
    name: "" // commandName
    isActive: function( ctx ) { /* ... */ },
    isEnabled: function( ctx ) { /* ... */ },
    action: function( ctx, params, callback ) { /* ... */ },
    icon: 'icon-compass',
    label: 'My label'
}

name

The unique command name.

The command name is mandatory.

isActive

The isActive method is used to determine whether a command must be added to the context of reference. That is, for example, whether a custom button should be added to a toolbar.

The isActive method has the Context Object as the only parameters. See Basic structure of the Context Object for further details on the Context Object.

This is the best place to check for particular privileges.
Each default Swing context calls the isActive method with different parameters. See Swing default contexts to see the different parameters used by the different Swing contexts.

isEnabled

The isEnabled method is used to determine whether a command must be enabled and disabled according to the current situation. That is, for enable, whether a custom button in a toolbar should be enabled according to the current selection.

The isEnabled method has the Context Object as the only parameters. See Basic structure of the Context Object for further details on the Context Object.

This is the best place to check for the current selection ( selected items, text selection, etc. according to the context)
Each default Swing context calls the isEnabled method with different parameters. See Swing default contexts to see the different parameters used by the different Swing contexts.

action( ctx, params, callback )

The action property is a Javascript function which is called when a command is executed.

The parameters are the following:

  • ctx: the Context. See Basic structure of the Context Object for further details on the Context Object.

  • params: a list of params to be passed to the action.

  • callbacks: if available, it is an object containing the success and the error functions.

Every context inside Méthode Swing calls the commands with different values for the params and the callback arguments. See Swing default contexts to see the different parameters used by the different Swing contexts.

icon

Describes the icon associated with the command.

The icon property is optional.

label

Describes the label associated with the command.

The label property is optional.

Register, obtain and execute commands

Commands are, basically, functions that can be called in particular cases. The commands can be associated with specific Swing contexts (such as the Toolbars, or the Grid rows), or can be executed freely from inside the user plugins.

While the basic command format is equal for any case, the command registration is different according to the context of reference. The following paragraphs describe this concept in details.

"Free" command registration and execution

In this case, the user registers a command without associating it with a particular context. For this, all the methods to add, get and execute commands are within the following namespace:

eidosmedia.webclient.commands

Available methods are add, get, exec, defined as follows:

// Add a new command
eidosmedia.webclient.commands.add( command );

// Get the command object
eidosmedia.webclient.commands.get( commandName );

// Execute a command
eidosmedia.webclient.commands.exec( commandName, ... );

add( command )

Adds a command to Méthode Swing. It needs one parameter:

It is recommended not to use standard names such as edit, save, etc. because there is the risk to override standard Swing commands.
Example
eidosmedia.webclient.commands.add({
    name: 'myCustomCommand',
    icon: 'icon-compass',
    label: 'Perform some complex operation...',
    isActive: function( ctx ) {
        // check privileges...
        return true;
    },
    isEnabled: function( ctx ) {
        // check current selected items...
        return true;
    },
    action: function( ctx, params, callbacks ) {
        if ( ctx.component.getType() === 'toolbar') {
            // perform same difficult operations...
            if (callbacks && callbacks.success) {
                callbacks.success( /*...*/ );
            }
        }

    }
});

get( commandName )

Return a Javascript Object containing all the properties of the command, as defined in the previous set method.

Example
var cmd = eidosmedia.webclient.commands.get( 'myCustomCommand' );

/*
Now cmd is: {
    icon: 'icon-compass',
    label: 'Perform some complex operation...',
    isActive: function( ctx ) {
        // check privileges...
        return true;
    },
    isEnabled: function( ctx ) {
        // check current selected items...
        return true;
    },
    action: function( ctx, params, callback ) {
        if ( ctx.component.getType() === 'toolbar') {
            // perform same difficult operations...
            if (callbacks && callbacks.success) {
                callbacks.success( /*...*/ );
            }
        }

    }
*/

exec( commandName, …​ )

It is the method to execute a particular command.

var result = eidosemdia.webclient.commands.exec('myCustomCommand', ctx, params, callbacks );

The only mandatory param is commandName, which is the name of the command to execute. All the other parameters are passed as-is to the command.

As said above, Méthode Swing calls the custom commands with three parameters: ctx, params and callback. The nature of these parameters changes according to which context is using the command. See Swing default contexts to see the different parameters used by the different Swing contexts.

Of course, if the custom commands are used only within your extensions ( and not by default Swing contexts ), it is possible to use any parameter of choice.

Sample
/** in a custom widget, for example */

var btn = document.getElementById('myButton');

$(btn).on('click', function() {
    eidosmedia.webclient.commands.exec('myCustomCommand', 'all', 'my', 'parameters', 'here', true, [ { everything: 'ok' } ]);
});

Registration of a command to a context

See Swing default contexts for further info on how to register and work with different Swing contexts.

Swing default contexts

Explorer Toolbar

Context Object properties for the Explorer Toolbar

The Object Panel context has the following values:

Table 42. Parameters list
Parameter Values Description

area.getType()

"main"

area.getName()

"explorer"

component.getType()

"toolbar"

activeObject.getId()

String

Return the ID of the current folder / query opened in the Explorer.

activeObject.getInfo()

JSON

Return the info of the current folder / query opened in the Explorer.

activeObject.getType()

JSON

Return the type of the current folder / query opened in the Explorer.

activeObject.invalidate() (additional)

(void)

Invalidates all the changes and refresh the content of the current folder / query.

selection

JSON Array

Returns an array with the currently selected items in the folder/query. Each item has the same set of methods available.

Associate commands to the toolbar

It is possible to register the command with the commands.add method ( see add( command ) for futher details ).

Then the commands must be associated to the toolbar as follows:

eidosmedia.webclient.actions.explorer.toolbar.addButton({
    action: "COMMAND NAME",
    label: "Label",
    icon: "Icon"
});

or, it is possible to pass directly the command object to the same function, e.g. :

eidosmedia.webclient.actions.explorer.toolbar.addButton({
    name: "cox.resetPriority",
    isActive: function( ctx ) { /* ... */ },
    isEnabled: function( ctx ) { /* ... */ },
    action: function( ctx, params, callbacks ) { /* ... */ },
    icon: 'icon-compass',
    label: 'My label'
});

The toolbar actions are called without a params and a callbacks parameter.

Grid Items

These values are valid for any grid. At the moment, only the Explorer grid is managed.

Context Object properties for the Grid Item

The Object Panel context has the following values:

Table 43. Parameters list
Parameter Values Description

component.getType()

"grid-item"

component.getId()

String

Not available

activeObject.getId()

String

Return the ID of the current item (grid row).

activeObject.getInfo()

JSON

Return the info of the current item (grid row).

activeObject.getType()

JSON

Return the type of the current item (grid row).

selection

JSON Array

Not available (empty array)

It is possible to register the command with the commands.add method ( see add( command ) for futher details ).

Then the commands must be associated to the grid item as follows:

eidosmedia.webclient.actions.explorer.grid.addEditor({
    action: "COMMAND NAME",
    label: "Label",
    icon: "Icon"
});

or, it is possible to pass directly the command object to the same function, e.g. :

eidosmedia.webclient.actions.explorer.grid.addEditor({
    name: "cox.resetPriority",
    isActive: function( ctx ) { /* ... */ },
    isEnabled: function( ctx ) { /* ... */ },
    action: function( ctx, params, callbacks ) { /* ... */ },
    icon: 'icon-compass',
    label: 'My label'
});

Object panel

Context Object properties for the ObjectPanel

The Object Panel context has the following values:

Table 44. Parameters list
Parameter Values Description

component.getType()

"objectpanel"

component.getId()

String

The id of the ObjectPanel DOM Element ( e.g. '#em-metadata-area' )

activeObject.getId()

String

Return the ID of the current object

activeObject.getInfo()

JSON

Return the info of the current object

activeObject.getType()

JSON

Return the type of the current object

selection

JSON Array

Not available (empty array)

Additional methods in the Context Object for Object Panel.
Table 45. Parameters list
Method Available in

activeObject.getUniqueMetadataId()

preFillForm - postFillForm - preSave - postSave - onClose

activeObject.setReadonly()

preFillForm - postFillForm - preSave - postSave - onClose

activeObject.showLoading()

preFillForm - postFillForm - preSave - postSave - onClose

activeObject.hideLoading()

preFillForm - postFillForm - preSave - postSave - onClose

activeObject.getMetadataXML()

preFillForm - postFillForm - preSave - validate - postSave - onClose

activeObject.setMetadataXML()

preFillForm - onClose

activeObject.refreshMetadata()

postFillForm - postSave - onClose

activeObject.saveMetadata()

preFillForm - postFillForm - onClose

activeObject.isMixedValue()

postFillForm (uploader)

getUniqueMetadataId()

Return the same unique id used in the HTML panel whenever a -<%=uniqueMetadataId%> has been used to generate a unique Id.

The syntax is the following:

var uniqueId = ctx.activeObject.getUniqueMetadataId(); // e.g "5";
var uniqueId = ctx.activeObject.getUniqueMetadataId(); // e.g "5";
// In this way we ensure that the only the current custom panel is referenced
$('#myCustomPanel-' + uniqueId) // ...
setReadonly()

Creates a readonly layer over the object Panel. The syntax is the following:

ctx.activeObject.setReadonly( [value] );

Value can be true (default) or false.

showLoading(), hideLoading()

Shows or hides a loading element. The syntax is the following:

ctx.activeObject.showLoading( );
ctx.activeObject.hideLoading( );

Value can be true (default) or false.

getMetadataXML()

Return the current metadata XML as a document object.

setMetadataXML( metadataXMO )

Sets the the new metadata XML ( only in the preFillForm and onClose method ).

refreshMetadata()

Refresh the current Metadata.

This will cause a reload of the objectPanel, and consequently a call to "preFillForm" and "postFillFrom" methods.

saveMetadata()

Saves the current Metadata. Before saving, the "preSave" method is called. After saving, the "postSave" method is called.

isMixedValue()

Check if the given value is the "Mixed" string.

Editor extensions

Méthode Swing Text editor can be extended in multiple parts by adding custom buttons and executing custom actions.

Location of the Editor extensions.

Editor extensions consist in a Javascript (*.js) file loaded with Swing.

In general, all the extensions of Méthode Swing are places under

{SWING-APP}/plugins

So, all the Javascript described in the following sections should be placed under

{SWING-APP}/app/plugins/{EXTENSION-FOLDER}/{EXTENSION-NAME}.js

Do not use the word libs as an extension folder. The libs folder is reserved for loading external libs. See the proper documentation to obtain further info on the topic.

DEBUG MODE: by default, all plugins are aggregated into a single plugins.js file at the Tomcat startup. When creating a plugin, it may be frustrating to restart the Tomcat Server every time a change is made.

To avoid this, set the configuration property debugEnabled to true.

Then, that extension will be loaded on every refresh of the page.

Namespaces of the editor extensions

Editor extensions are available under the following namespace:

eidosmedia.webclient.actions.editor

This namespace delves into more sub-namespaces, according to the part of the editor the user want to extend. At the moment, the following namespaces are supported:

eidosmedia.webclient.actions.editor.toolbar // To add buttons and actions in the editor toolbar

eidosmedia.webclient.actions.editor.components // To add buttons and actions in the swing components

The approach to use is the same throughout all the namespaces.

Configure editor actions

Throughout this document, the example will focus on the development of a simple custom command, named custom.PrependText, which will prepend a specific text to the current selection.

To add an editor action, then, it is necessary to follow the steps described below:

1. Add a custom command

The editor extensions follow the same approach of Swing commands.

See the Command configuration paragraph for further details.

Example

eidosmedia.webclient.commands.add({
    name: 'custom.PrependText',
    isActive: function( ctx ) {
        // check privileges...
        return true;
    },
    isEnabled: function( ctx ) {
        // check current selected items...
        return true;
    },
    action: function( ctx, params, callbacks ) {
        if ( ctx.component.getType() === 'toolbar') {
            // perform same difficult operations...
            if (callbacks && callbacks.success) {
                callbacks.success( /*...*/ );
            }
        }

    }
});

The details of the implementation of the isEnabled and isActive method will be explained later.

2. Register the commands in the different areas

As a second step, the newly created command must be associated to the specific areas of the editor.

2.a Editor toolbar and custom tabs

To add a button to the Editor toolbar, the following namespace must be used

eidosmedia.webclient.actions.editor.toolbar

The method to be used is addButton(settings). See the following example:

/**
 * We now register a new action in the editor toolbar using
 * the proper namespace. In this case we call the addButton
 * method for the toolbar visible in the editor instance. We
 * define a label and an icon for the button and more important
 * we tell the button which command has to trigger using the
 * action property
 */
eidosmedia.webclient.actions.editor.toolbar.addButton({
    action: "custom.PrependText",
    label: "Prepend Xml",
    icon: 'icon-edit',
    tabId: 'custom-tab-id'
});

The addButton method requires a single Javascript object ( settings ) as a parameter. This objects need to have the following properties:

  • action: the name of the previously registered command

  • label: the label associated with the command

  • icon: the icon associated with the command.

Buttons can be placed under custom tabs. See Editor custom tabs for more information.

By defining an order property, it is possible to change the order of the buttons in the toolbar (only for the Editor).

If you need to use the call the same command from different parts of the editor ( e.g. the toolbar and some components ), or you need to differentiate the components for which the command is available, you can ( and should ) verify the origin of the action by checking the Context Object Component Type. This is detailed in the Verify the component type paragraph.

Editor custom tabs

Swing story editor toolbar is divided in tabs. By default, all the custom buttons are added to a specific "Custom Actions" tab. It is possible, though, to define multiple (2 at maximum) custom tabs and to place the custom buttons accordingly. To add a custom tab, write the following:

/**
 * We now register a new tab in the editor toolbar using
 * the proper namespace. The tab name must be used in the <tabId> property of the custom button.
 */
eidosmedia.webclient.extensions.editor.tabs.add({
    name: 'em-tab-custom',
    label: 'Swing custom actions'
});

To add the button to a tab, use the following syntax:

eidosmedia.webclient.actions.editor.toolbar.addButton({
    action: "custom.PrependText",
    label: "Prepend Xml",
    icon: 'icon-edit',
    tabId: 'em-tab-custom'
});
  • If the registered tab has no associated buttons, it will not be shown.

  • All the custom buttons who do not have a "tabId" property will be added to the usual "Custom Actions" tab.

2.b Editor components

To add a button to the Editor components, the following namespace must be used

eidosmedia.webclient.actions.editor.components

The method to be used is addButton(settings). See the following example:

/**
 * We now register a new action in the editor component using
 * the proper namespace. In this case we call the addButton
 * method for the components available in the editor instance. We
 * define a label and an icon for the button and more important
 * we tell the button which command has to trigger using the
 * action property
 */
eidosmedia.webclient.actions.editor.components.addButton({
    action: "custom.PrependText",
    label: "Prepend Xml",
    icon: 'icon-edit',
    allowReadOnly: false,
    singleItem: true
});

The addButton method requires a single Javascript object ( settings ) as a parameter. This objects need to have the following properties:

  • action: the name of the previously registered command

  • label: the label associated with the command

  • icon: the icon associated with the command.

  • allowReadOnly: if true the button is visible in the component when the story is read only.

  • singleItem: if true the button is visible in a single element of a component with multiple items, for example Collection and Gallery component.

If you need to use the call the same command from different parts of the editor ( e.g. the toolbar and some components ), or you need to differentiate the components for which the command is available, you can ( and should ) verify the origin of the action by checking the Context Object Component Type. This is detailed in the Verify the component type paragraph.

singleItem attributte is available only for elements in a Collection and Gallery component. In the action method the second paramater contains the selectedItem object.

2.c Editor components Drop Down menu

It’s possible to add one or more buttons in a drop down menu. In this case the submenu property has to be added to the settings object:

  • submenu: the object containing the buttons to be added in the drop down menu

/**
 * We now register a new drop down menu action in the editor component using
 * the proper namespace. In this case we call the addButton
 * method for the components available in the editor instance. We
 * define a label and an icon for the drow down menu and more important
 * we set the submenu property that contains the button of the drop down menu
 */
eidosmedia.webclient.actions.editor.components.addButton({
    action: "example.customDropDown",
    label: "Custom popup",
    name: "submenu.customDropDown",
    icon: 'icon-copy',
    allowReadOnly: false,
    submenu: {
            videoPreview: {
                action: "example.openVideoPreview",
                label: "Open Video Preview",
                icon: 'fa fa-video-camera'
            },
            customEdit: {
                action: "example.customEditing",
                label: "Custom editing",
                icon: 'icon-edit',
            }
    }
});

Implementation of the editor extensions

The editor extensions are powerful because they make use of an editor-related version of the context object. See Basic structure of the Context Object for further details.

As of any command implementation, it is possible to specify the isActive, isEnabled and action methods.

  • isActive method is called in different points according to the editor part it refers to.

    • For the toolbar the method is called after the story is loaded to determine whether a custom action is available.

    • For the components the method is called when a new component is created ( or an existing one is loaded from the story )

  • isEnabled method is called in different points according to the editor part it refers to.

    • For the toolbar the method is called whenever the user changes the current selection or cursor position.

  • the action is called when the user clicks on the corresponding action button.

All the methods are called with the same Context Object ( ctx ), which is enhanced for the editor and allows to access the current document ( activeDocument ). See Basic structure of the Context Object foo further details.

In the Editor context, activeObj and selection are NOT available. The available methods are:

  • {ctx}.component.getType(), which returns one of the following values:

    • toolbar for the Editor toolbar

    • image for the Editor Component "Image"

    • video for the Editor Component "Video"

    • More to come…​

  • {ctx}.component.getSubType(), in case a component can embed, in addition to the main type, another type, which returns one of the following values:

    • video for the element "Video"

    • More to come…​

Example the image component can embed also a video element.

In addtion only in teh Editor context the following methods are available for the element info:

  • {ctx}.component.getXPath(), The xpath of the element info

  • {ctx}.component.getAttributes(), The attributes of the element info

The Editor Context Object is enriched by an additional object, named activeDocument, which contains a subset of methods to access the current document. These methods are described in the Dwp Editor API paragraph.

Editor API

Suggestions for the Editor extensions

Verify the component type

Since the Editor Context object contains a method

ctx.component.getType();

it is possible to use it in the isEnabled and isActive methods to check the kind of component which is calling the action.

    // EXCERPT FROM THE COMMAND CODE
    isActive: function( ctx ) {
        if ( ctx.component.getType() === 'toolbar') {
            return true;
        }
        return false;
    },
    isEnabled: function( ctx ) {
        // Enabling the action for the editable image only.
        if ( ctx.component.getType() === ctx.activeDocument.CONSTANTS.BLOCK_IMAGE) {
            return true;
        }
        return false;
    },

Checking the component type is mandatory in case of actions added to the Editor components namespace. Otherwise, the action will be available for all components. It’s possible to use some constants defined in the activeDocument:

ctx.activeDocument.CONSTANTS.BLOCK_IMAGE ctx.activeDocument.CONSTANTS.BLOCK_VIDEO

Use of Editor API and extensions in Object Panel

Only for the Object Panel of an opened story, the activeDocument object of the Dwp Editor API is available. So, it is possible, from the Object Panel, to call custom actions and manipulate the current document.

It is suggested, though, to verify if the {ctx}.activeDocument is available to show and hide editor-related parts in the Object Panel. See example below:

// From the object panel API
    ready: function( ctx ) {
        // some other code...
        if ( ctx.activeDocument ) {
            // We are in the Editor.
            $('.custom-editor-metadata-panel').show();

            // Use it to manipulate the document

            var sel = ctx.activeDocument.getSelection();
            // ...

        }
    }

To learn how to extend the Object Panel, see Object panel documentation.

Full example of Editor extension

eidosmedia.webclient.commands.add({
    name: "custom.PrependText",
    action: function(ctx, params, callback) {
        var editorApi = ctx.activeDocument;
        // Obtain the current selection
        var sel = editorApi.getSelection();
        // Supposedly check the current selection ( if it a Content Item, ... )
        sel.insertXml('<p>test</p>', 'insertBefore');
    },

    isActive: function( ctx ) {
        // Enables it only for toolbar or Editable Image
        var componentType = ctx.component.getType();
        if ( componentType === 'toolbar' || componentType === 'editableimage' )
            return true;
        else
            return false;
    },

    isEnabled: function( ctx ) {
        var selection = ctx.activeDocument.getSelection();
        var contentItem = sel.getContentItem();

        if ( contentItem ) {
            return true;
        }
        return false;
    }
});

/**
 * We now register a new action in the editor toolbar using
 * the proper namespace. In this case we call the addButton
 * method for the toolbar visible in the editor instance. We
 * define a label and an icon for the button and more important
 * we tell the button which command has to trigger using the
 * action property
 */
eidosmedia.webclient.actions.editor.toolbar.addButton({
    action: "custom.PrependText",
    label: "Prepend Xml",
    icon: 'icon-edit'
});

eidosmedia.webclient.actions.editor.components.addButton({
    action: "custom.PrependText",
    label: "Prepend Xml",
    icon: 'icon-gear'
});

// Example for custom action for a single element in a Collection or Gallery component
eidosmedia.webclient.commands.add({
    name: "example.openVideoPreview",
    action: function(ctx, params, callback) {

        // params contains the selectedItem
        var selectedItem = params.selectedItem;

        // checks selectedItem values

    },

    isActive: function( ctx ) {
        // Handle condition activation
        return true;
    },

    isEnabled: function( ctx ) {
        // Handle condition enable
        return true;
    }
});

/**
 * We now register a new action in the editor components using
 * the proper namespace. We tell the button which command has to trigger using the
 * action property
 */
eidosmedia.webclient.actions.editor.components.addButton({
    action: "example.openVideoPreview",
    label: "Open Video Preview",
    icon: 'icon-facetime-video',
    singleItem: true
});

Override editor’s default counter

Swing editor comes with a default counter for chars and words. On the one hand, it is possible to specify an additional counter under the Editor configuration ; on the other hand, it is possible to completely override the counter behaviour by defining the following function.

eidosmedia.webclient.editor.counter = function( counterInfo, storyInfo, userInfo ) { /* function here */ }
Table 46. Method information

Method

counter

Parameter

{object} counterInfo - An object containing the information about the counter.

  • {integer} chars - the number of chars in the relative contentItem ( the document, unless different configurations )

  • {integer} words - the number of words in the relative contentItem ( the document, unless different configurations )

  • {string} contentItemType - the name of the selected contentItem ( only in case of "contentItem" setting for Editor/Story/Count/Type ).

  • {string} xpath - the xpath of the selected item ( only in case of "contentItem" setting for Editor/Story/Count/Type and a list of conteintItems is configured Editor/Story/Count/contentItems).

  • {string} customLabel - the value for the custom label ( only if specified in the Story editor configuration )

  • {integer} customValue - the value for the custom value ( only if specified in the Story editor configuration )

  • {boolean} currentNodeEmpty - true if the current node is empty

  • {string} occupation - the task occupation value inherited by the document.

Parameter

{object} storyInfo - An object containing the information about the current document.

  • {string} channel - The story channel

  • {various info} - try to use a console.log(storyInfo) to see all the passed information. Please stick to the "classic" properties ( channel, id, name ) for the others may change in the future.

Parameter

{object} userInfo - An object containing the information about the current user.

  • {various info} - try to use a console.log(userInfo) to see all the passed information.

Returns

{String} - the function should return the HTML for the counter area.

So, if the user wants to override the counter behaviour, the eidosmedia.webclient.editor.counter method provides all the available information. The result of the function MUST be a valid HTML which is placed in the "counter area" of the Editor.

To create a separator, use an empty DIV with the class "em-info-separator" (see examples below).
If you need to use this features, you’ll probably need to change the Editor > Story > Count configuration to contentItem. See the Story Editor Configuration.

Full working example

The following example warns the user if the number of characters in a particular content item is reaching ( or above ) the maximum level. It only works with the "contentItem" configuration.

(function() {
    eidosmedia.webclient.editor.counter = function( countInfo, storyInfo, userInfo ) {
        var charsStyle = '';
        var html = '';

        var countLimits = {
            'headline': 50,
            'text': 1500
        };

        var countXpathLimits = {
            '/doc/story/grouphead/headline': 10,
        };
        var found = false;

        if (countInfo.xpath) {
            var MAX_CHARS_IN_XPATH = countXpathLimits[ countInfo.xpath ];
            found = MAX_CHARS_IN_XPATH != null;
            if ( MAX_CHARS_IN_XPATH &&
                 countInfo.chars > MAX_CHARS_IN_XPATH ) {
                 charsStyle = 'color: white; font-weight: bold; background-color: red;';
            } else if ( MAX_CHARS_IN_XPATH &&
                        countInfo.chars > (MAX_CHARS_IN_XPATH*0.75) ) {
                // We have gone over the 75% of the allowed number of characters
                charsStyle = 'color: black; font-weight: bold; background-color: orange;';
            }
        }

        if ( !found && countInfo.contentItemType ) {
            var MAX_CHARS_IN_CONTENTITEM = countLimits[ countInfo.contentItemType ];
            if ( MAX_CHARS_IN_CONTENTITEM &&
                 countInfo.chars > MAX_CHARS_IN_CONTENTITEM ) {
                charsStyle = 'color: white; font-weight: bold; background-color: red;';
            } else if ( MAX_CHARS_IN_CONTENTITEM &&
                        countInfo.chars > (MAX_CHARS_IN_CONTENTITEM*0.75) ) {
                // We have gone over the 75% of the allowed number of characters
                charsStyle = 'color: black; font-weight: bold; background-color: orange;';
            }
        }

        html += '<span class="em-info-container" style="' + charsStyle +
                '"><b>Chars:</b> <span id="chars_counter">' + (countInfo.chars || 0) +
                '</span></span>';
        html += '<span class="em-info-container" id="words_counter_box">' +
                '<b>Words:</b> <span id="words_counter">' + (countInfo.words || 0) +
                '</span></span>';

        if ( countInfo.customValue ) {
            html += '<span class="em-info-container"><b id="custom_counter_label">' +
                    countInfo.customLabel + '</b><span id="custom_counter">' +
                    countInfo.customValue + '</span></span>';
        }
        return html;
    }
})();

General information on the Editor API

Editor API allows the user to perform different operations on the currently opened document. They are available within the Context Object, when the user is editing a Story.

Editor API are to be considered as a feature in progress. Changes and additions may be made without warning.

In the source code, we will refer to the Context Object as a generic "ctx" object.

activeDocument

The activeDocument is the entry point of the Editor API. It contains a set of different methods to analyze and manage the currently opened document.

Methods of the activeDocument object

getXMLContent

Returns the complete XML content of the document.

It can be called as

ctx.activeDocument.getXMLContent();
Table 47. Method information

Method

getXMLContent

Parameter

None

Returns

{String} The XML document as a string.

Example
// Obtains the XML of the document.
var xml = ctx.activeDocument.getXmlContent();

// For example, we can parse the XML with jQuery and use it somehow.
var parsedXML = $.parseXML( xml );

// ...
getTextContent

Returns the complete text content of the document.

It can be called as

ctx.activeDocument.getTextContent(options);
Table 48. Method information

Method

getTextContent

Parameter

Optional {object} options - An object containing the some options. It can contains the following properties:

  • Optional {Boolean} includeWhitespaceNodes - true if you want to include the whitespace nodes.

Returns

{String} The text content of the document as a string.

Example
// Obtains the text content of the document.
var options = {};
// Uncomment this line if you want to include witespace nodes
options.includeWhitespaceNodes = true;

var text = ctx.activeDocument.getTextContent(options);

// ...
getMetadata

Returns the complete Metadata of the document as a string.

It can be called as

ctx.activeDocument.getMetadata( callbacks );
Table 49. Method information

Method

getMetadata

Parameter

{object} callbacks - An object containing the callbacks. It contains the following properties:

  • Required {Function} success - the function to be called when the Metadata is retrieved. It has a single parameter, the metadata as a string.

  • Optional {Function} error - the function to be called when an error occurred.

Returns

undefined

This call is asynchronous. The Metadata string is available only as the parameter of the success callback.

Example
// Obtains the Metadata of the document
ctx.activeDocument.getMetadata({
    success: function( metadata ) {
        // Do something with this...
    },
    error: function( err ) {
        // For example.
        ctx.showError( err.message );
    }
});
getSelection

Returns the current Selection in the active document.

The current selection is an object of type Selection. See Selection object for further information on available methods.

It can be called as

ctx.activeDocument.getSelection();
Table 50. Method information

Method

getSelection

Parameter

None

Returns

{Selection} The current selection object

Example
// Obtains the current selection.
var sel = ctx.activeDocument.getSelection();

// sel is now a Selection object.
// For example, we can verify that the current selection is a ContentItem

var selNode = sel.getNode();

if ( selNode.nodeType === ctx.activeDocument.CONSTANTS.CONTENTITEM_TYPE ) {
    // Perform a specific action...
}
insertXML

Insert an XML in a specified part of the document.

It can be called as

ctx.activeDocument.insertXML(xml, xpathOptions, insertOptions);
Table 51. Method information

Method

insertXML

Parameter

{String} xml - The xml to insert, as a string.

e.g. <p>Append me</p>

Parameter

{object} xpathOptions - Contains the information on the XPath to append the XML to. It contains the following properties:

  • Required {String / Array} xpath - the xpath to append the xml to, as a string (single xpath) or array (multiple xpaths).

  • Optional {Array} criteria - an array of objects containing the criteria to further detail the node to select. Each object contains the following properties, all required:

    • {String} param the attribute to test (e.g. xsm-preserve). The eom-name is not a valid attribute if specified it will be ignored.

    • {String} condition the operator to use in the selector. Can be equal or contains.

    • {String} value the value to test for.

See example below.

Parameter

{object / string} insertOptions the options to insert the XML in the selected node. If used as an object, it contains the following properties:

  • Optional {String} insertType - determines where to insert the xml with respect to the node. Defaults to append, can be prepend or insertBefore (which both prepend the content to the selected node).

Otherwise, it is possible to directly pass the insertType as a string (append, prepend or insertBefore)

Returns

{Boolean} true if the operation was executed correctly

At the moment, if the specified xpath in the xpathOptions corresponds to more than one xPath, only the first is taken.

// Prepares the xml.
var xml = '<p>Prepend me</p>';

ctx.activeDocument.insertXml( xml, {
    xpath: 'doc/story/text/superbyline',
    criteria: {
        [
            param: 'xsm-preserve',
            condition: 'equal',
            value: 'true'
        ]
    } },
    'prepend' );

// the xml will be inserted only in the first superbyline tag that satisfy xpath and the additional (optional) criteria
getDocumentInfo

Returns a subset of information on the current document. The information includes the following information : channel, workfolder, issueDate, templateName, language.

It can be called as

ctx.activeDocument.getDocumentInfo();
Table 52. Method information

Method

getDocumentInfo

Parameter

None

Returns

{JSON Object} Information on the current document

Example
// Obtains the document information.
var documentInfo = ctx.activeDocument.getDocumentInfo();

/*
    documentInfo is now:
    {
        "id": "id",
        "readonly": true,
        "channel": "CHANNEL",
        "workfolder": "WORKFOLDER",
        "issueDate": "ISSUE DATE",
        "templateName": "TEMPLATE NAME",
        "language": "en/uk"
    }

*/

if ( documentInfo.channel === 'Globe-Web' ) {
    // ... do something, for example it can remove a specific custom button.
}
isReadonly

Returns the readonly status of the story

It can be called as

ctx.activeDocument.isReadonly();
Table 53. Method information

Method

isReadonly

Parameter

None

Returns

{Boolean} true if the story is readonly

Example
// Obtains the document information.
var isReadonly = ctx.activeDocument.isReadonly();
saveDocument

Execute the save of the current document.

It can be called as

ctx.activeDocument.saveDocument();
Table 54. Method information

Method

saveDocument

Parameter

Optional {Function} callback - A function invoked if the the document is successfully saved

If the document is not dirty, the save is not executed, but the callback, if defined, is always invoked.

Example
// Save the current document.
ctx.activeDocument.saveDocument();
getNode

Returns a specific node, as a DocumentNode object, upon the information contained in the XPath

It can be called as

ctx.activeDocument.getNode(xpathOptions);
Table 55. Method information

Method

getNode

Parameter

{object} xpathOptions - Contains the information on the XPath to append the XML to. It contains the following properties:

  • Required {String / Array} xpath - the xpath to append the xml to, as a string (single xpath) or array (multiple xpaths).

  • Optional {object} absolute [default: false] - if true, the search is limited to the absolute XPath.

  • Optional {Array} criteria - an array of objects containing the criteria to further detail the node to select. Each object contains the following properties, all required:

    • {String} param the attribute to test (e.g. eom-name, xsm-preserve)

    • {String} condition the operator to use in the selector. Can be equal or contains.

    • {String} value the value to test for.

Return

{DocumentNode} A document node object containing. See DocumentNode interface object for details on the Document Node object.

At the moment, if the specified xpath in the xpathOptions corresponds to more than one node, only the first is taken.

Example
// prepare the xpath options
var xpathOptions = {
    xpath: 'doc/story/text',
    criteria: {
      [ param: 'channel',
        condition: 'equal',
        value: 'Globe-Print'
      ]
    }
};

// Obtains the node.
var node = ctx.activeDocument.getNode(xpathOptions);

// Perform actions on the found node
// ...
if (node) {
...

}
insertExternalUrl

Insert an external URL in the document, upon the information contained in the object to insert and in the provided options

It can be called as

ctx.activeDocument.insertExternalUrl(item, insertOptions);
Table 56. Method information

Method

insertExternalUrl

Parameter

{object} item - Contains the information about the object to insert. It contains the following properties:

  • Required {String} url - the url of the object to insert.

  • Required {String} type - the type of the object to insert. The suppoerted type are the following:

    • webimage insert the object in the EOMDB and try to insert the created object as image

    • externalwebpage insert the object as link in the opened document

    • embedurl insert the object as embed code bock if the provided url is a supported embed

Parameter

{object} insertOptions - Contains additional option to insert the object. Right now not used, for future purpose

Return

{Boolean} true if the object has been successfully inserted, false otherwise.

Example
// prepare the object to insert
var item = {
    'url': 'https://images-assets.nasa.gov/image/GSFC_20171208_Archive_e001240/GSFC_20171208_Archive_e001240~orig.jpg',
    'type': 'web::image'
};

// Perform the insert.
ctx.activeDocument.insertExternalUrl(item, {});
setDocumentToBeRefreshed

Some events in Swing are not handled. This API could be useful when some actions are fired and they change the document server side. This way, it’s possible to warn the user that the document should be refresh.

It can be called as

ctx.activeDocument.setDocumentToBeRefreshed(options);
Table 57. Method information

Method

setDocumentToBeRefreshed

Parameter

{object} options - Contains additional option. Right now not used, for future purpose

Return

{Boolean} true if the operation has been successfully executed, false otherwise.

Example
action: function(ctx, params, callback) {

    var editorApi = ctx.activeDocument;

    editorApi.setDocumentToBeRefreshed();
}
switchToChannel(channel)

Switch the document to the specified channel

It can be called as

ctx.activeDocument.getCollectionSelectedItems(attrOptions)
Table 58. Method information

Method

switchToChannel

Parameter

Required {String} channel - A string containing the channel in form of "Product/Edition".

// Switch to channel
ctx.activeDocument.switchToChannel(channel);

Constants of the activeDocument object

The activeDocument object comes with a set of constants to simplify the management of the return values of the different methods.

The constants are available under

activeDocument.CONSTANTS

Currently the following constants are visible:

ctx.activeDocument.CONSTANTS = {
    DUMMY_TYPE: 'DummyText', // Document Node type
    CONTENTITEM_TYPE: 'ContentItem', // Document Node type
    CONTENTBLOCK_TYPE: 'ContentBlock', // Document Node type
    CONTENTNODE_TYPE: 'ContentNode', // Document Node type,
    BLOCK_IMAGE: 'image', // ContentBlock of type Image
    BLOCK_VIDEO: 'video', // ContentBlock of type Video
};

Selection object

The Selection Object allows to manipulate the selection. It contains a set of properties and methods described below.

Methods

getNode

Returns the current selected node, as a DocumentNode object.

It can be called as

{SelectionObject}.getNode();
Table 59. Method information

Method

getNode

Parameter

Optional {Object} options Additional options to get the node.

- Set options.getWrappedSection = true if it’s needed to get the section wrapped node

Return

{DocumentNode} A document node object containing the current selection. See DocumentNode interface object for details on the Document Node object.

// Obtains the document selection
var sel = ctx.activeDocument.getSelection();

var selectedNode = sel.getNode();

// Perform actions on the selected node
// ...
getXmlContent

Returns the XML of the current Selection.

It can be called as

{SelectionObject}.getXmlContent();
Table 60. Method information

Method

getXmlContent

Parameter

None

Return

{String} The xml of the current selection as a String

// Obtains the document selection
var sel = ctx.activeDocument.getSelection();

var selectedXml = sel.getXmlContent();

// Perform actions on the selected node
// ...

Calling {Selection}.getXmlContent() when a ContentBlock node is selected is equal to call {ContentBlock}.getXmlContent() or {Selection}.getNode().getXmlContent().

insertXml

Insert an XML in a specified part of the current selection or replaces the current selection with the new XML.

It can be called as

{SelectionObject}.insertXML(xml, insertOptions);
Table 61. Method information

Method

insertXML

Parameter

{String} xml - The xml to insert, as a string.

e.g. <p>Append me</p>

Parameter

{object / string} insertOptions the options to insert the XML in the selected node. If used as an object, it contains the following properties:

  • Optional {String} insertType - determines where to insert the xml with respect to the node. Defaults to append, can be prepend or insertBefore (which both prepend the content to the selected node), replace (replace the content of the selected node with the provided xml).

Otherwise, it is possible to directly pass the insertType as a string (append, prepend or insertBefore, replace)

Returns

{Boolean} true if the operation was executed correctly

// Obtains the document information.
var xml = '<p>Prepend me</p>';
var sel = ctx.activeDocument.getSelection();

sel.insertXml( xml, 'prepend' );
prepend, append

Insert an XML in a specified part of the current selection. They are shortcuts method for insertXml.

It can be called as

{SelectionObject}.prepend(xml);
{SelectionObject}.append(xml);

See insertXml for further details.

getTextContent

Returns the text content of the current Selection.

It can be called as

{SelectionObject}.getTextContent();
Table 62. Method information

Method

getTextContent

Parameter

Optional {object} options - An object containing the some options. It can contains the following properties:

  • Optional {Boolean} includeWhitespaceNodes - true if you want to include the whitespace nodes.

Return

{String} The text content of the current selection as a String

// Obtains the document selection
var sel = ctx.activeDocument.getSelection();

var options = {};
// Uncomment this line if you want to include witespace nodes
// options.includeWhitespaceNodes = true;
var text = sel.getTextContent(options);

// ...

Calling {Selection}.getTextContent() when a ContentBlock node is selected an empty string will be returned.

DocumentNode interface

It is the lowest-level object available for manipulating the Active Document. It represents a node within the document. It contains different methods and properties to interact with the node.

The DocumentNode object is NOT a DOM node, so operation as $(<DocumentNode>) will not work.

Properties

nodeType

Return the node type. Can be one of the following:

  • ContentItem

  • ContentNode

  • ContentBlock

  • DummyText

Verify the value of this property against the Constants of the activeDocument object to perform the best check.

Each Document Node type has specific methods. See Types for details on each document node.

var sel = ctx.activeDocument.getSelection();
var selectedNode = sel.getNode();

if ( selectedNode.nodeType === ctx.activeDocument.CONSTANTS.DUMMY_TYPE ) {
    // It is a dummy text...
    sel.append('<p>New text here</p>');
}
contentItem

Returns the contentItem in which the element is contained. It can be one of two values:

  • null, if the element is not part of any contentItem, or it is a contentItem itself.

  • {DocumentNode} ContentItem the document node of type 'ContentItem'. See ContentItem for further details.

Methods

getXmlContent

Returns the XML of the node.

It can be called as

{DocumentNode}.getXmlContent();
Table 63. Method information

Method

getXmlContent

Parameter

None

Return

{String} The xml of the node as a String

// prepare the xpath options
var xpathOptions = {
    xpath: 'doc/story/text',
    criteria: {
       [
        param: 'channel',
        condition: 'equal',
        value: 'Globe-Print'
        ]
    }
};

// Obtains the node.
var node = ctx.activeDocument.getNode(xpathOptions);

// Perform actions on the found node
// ...
if (node) {
    var xmlContent = node.getXmlContent();

}
replaceXmlContent

Replaces the XML content of the current node.

It can be called as

{DocumentNode}.replaceXmlContent();
Table 64. Method information

Method

replaceXmlContent

Parameter

{String} xml - The xml content that replaces the current content node.

Return

{Boolean} True if replacement succeeded

// prepare the xpath options
var xpathOptions = {
    xpath: 'doc/story/summary',
    criteria: {
        [
            param: 'channel',
            condition: 'equal',
            value: 'Globe-Print'
        ]
    }
};

// Obtains the node.
var node = ctx.activeDocument.getNode(xpathOptions);

// Perform actions on the found node
// ...
if (node) {
    var repxml = '<summary id="U26664270086oOb"><p>New summary content</p></summary>';
    node.replaceXmlContent(repxml);
}
getTextContent

Returns the text content of the node.

It can be called as

{DocumentNode}.getTextContent();
Table 65. Method information

Method

getTextContent

Parameter

Optional {object} options - An object containing the some options. It can contains the following properties:

  • Optional {Boolean} includeWhitespaceNodes - true if you want to include the whitespace nodes.

Return

{String} The text content of the node as a String

// prepare the xpath options
var xpathOptions = {
    xpath: 'doc/story/text',
    criteria: {
       [
        param: 'channel',
        condition: 'equal',
        value: 'Globe-Print'
        ]
    }
};

// Obtains the node.
var node = ctx.activeDocument.getNode(xpathOptions);

// Perform actions on the found node
// ...
if (node) {

    var options = {};
    // Uncomment this line if you want to include witespace nodes
    // options.includeWhitespaceNodes = true;

    var text = node.getTextContent(options);

    // ....

}
replaceTextContent

Replace the text of the node with another text.

It can be called as

{DocumentNode}.replaceTextContent(text);
Table 66. Method information

Method

replaceTextContent

Parameter

{String} text - The text to replace with.

Parameter

{object} replaceOptions - Contains additional information about the replace operation.

  • Options {String} format - the format of the string used to replace the selected text (e.g. 'xml')

Returns

{Boolean} true if the operation was executed correctly

Right now to execute the replace the API use the information in the current selection.
If a text node is selected the replace is executed with success, otherwise an exception is thrown.
It the selection is not collapsed the current selection is removed and the new text is inserted.
If the selection is collapsed the content of the parent node is checked.
If the text node is not the only child of the parent node the new text wil l be inserted at the cursor position.
Otherwise the entire content of the parent node will be replaced with the new text.

// Obtains the current selection.
var sel = ctx.activeDocument.getSelection();
var selectedNode = sel.getNode();

// Replace the text of the current node with simple text
selectedNode.replaceTextContent( 'Simple text' );

// Replace the text of the current node with an xml format
selectedNode.replaceTextContent('<a href="http://www.eidosmedia.com" title="Eidosmedia">Eidosmedia</a>', {'format': 'xml'});
getContentInfo

Returns an object that contains a set of different methods to analyze and manage the current node.

This method is valid only for content nodes that refers to Methode Content: Images, External content items.
If the selected is not a valid one an exception is thrown.

API functions to get and set system attributes and metadata of an image linked to a story

It can be called as

{DocumentNode}.getContentInfo();
Table 67. Method information

Method

getContentInfo

Parameter

None

Return

{Object} Returns an object with a set of methods.

// Obtains the current selection.
var sel = ctx.activeDocument.getSelection();
var selectedNode = sel.getNode();

// Perform actions on the found node
// ...
if (selectedNode) {
    var ctx = selectedNode.getContentInfo();

    var id = ctx.getId();
    var type = ctx.getType();
    var sysAttributes = ctx.getSysAttributes();
    var usageTickets = ctx.getUsageTickets();
}
getAttributes

Get node attributes.

It can be called as

{DocumentNode}.getAttributes();
Table 68. Method information

Method

getAttributes

Parameter

None

Return

{Array} An array of objects {{name: String, value: String}} that represent all the attributes of the XML node that have a value.

// Obtains the current selection.
var sel = ctx.activeDocument.getSelection();
var selectedNode = sel.getNode();

// Perform actions on the found node
if (selectedNode) {
    var attributes = selectedNode.getAttributes();

    // ...
}
setAttributes

Set attributes to a node.

It can be called as

{DocumentNode}.setAttributes();
Table 69. Method information

Method

setAttributes

Parameter

{Object} options - The object containing information about attributes to set.

  • Required {Array} attributes - an array of objects pair name/value. If the value is empty the attribute will be removed.

Return

{Boolean} True if replacement succeeded

Don’t use this API to set the channel attribute.
To set the channel attribute it’s mandatory to use the specific API setChannels

// prepare the xpath options
var xpathOptions = {
    xpath: 'doc/story/summary',
    criteria: {
        [
            param: 'channel',
            condition: 'equal',
            value: 'Globe-Print'
        ]
    }
};

// Obtains the node by x path.
var node = ctx.activeDocument.getNode(xpathOptions);

// To obtain the node by selection uncomment the following lines
// var sel = editorApi.getSelection();
// var node = sel.getNode();

var attrOptions = {};
var attributes = [{'name': 'testattr', 'value': 'testvalue'}];
attrOptions.attributes = attributes;

// Perform actions on the found node
// ...
if (node) {
    node.setAttributes(attrOptions);
}
setChannels

Set channels to a node.

It can be called as

{DocumentNode}.setChannels();
Table 70. Method information

Method

setChannels

Parameter

Required {Array} channels - The array containing the channels to set.

Return

{Boolean} True if set succeeded

// prepare the xpath options
var xpathOptions = {
    xpath: 'doc/story/summary',
    criteria: {
        [
            param: 'highlight',
            condition: 'equal',
            value: 'yes'
        ]
    }
};

// Obtains the node by x path.
var node = ctx.activeDocument.getNode(xpathOptions);

// To obtain the node by selection uncomment the following lines
// var sel = editorApi.getSelection();
// var node = sel.getNode();

var channels = ['Globe-Web'];

// Perform actions on the found node
// ...
if (node) {
    node.setChannels(channels);
}

Types

There are four different DocumentNode types.

ContentItem

Its nodeType can be checked against ctx.activeDocument.CONSTANTS.CONTENTITEM_TYPE.

It has some specific methods:

duplicate

Duplicates a contentItem.

It can be called as

{ContentItem}.duplicate(channel);
Table 71. Method information

Method

duplicate

Parameter

{String} channel - The channel to duplicate the ContentItem to. To duplicate it in more channels, separate them with a comma.

e.g. Globe-Web,Globe-Print

Returns

{Boolean} true if the operation was executed correctly

// Obtains the current selection.
var sel = ctx.activeDocument.getSelection();
var selectedNode = sel.getNode();

if ( selectedNode.nodeType === ctx.activeDocument.CONSTANTS.CONTENTITEM_TYPE ) {
    // it is a ContentItem node. Duplicate itself.
    selectedNode.duplicate( 'Globe-Web,Globe-Print' );
} else {
    // Duplicates the container ContentItem.
    selectedNode.contentItem.duplicate( 'Globe-Web,Globe-Print' );
}
duplicateContent

Copy the content of the ContentItem node into another node.

It can be called as

{ContentItem}.duplicateContent(xpathOptions);
Table 72. Method information

Method

duplicateContent

Parameter

{object} xpathOptions - Contains the information on the XPath to copy the XML to. It contains the following properties:

  • Required {String / Array} xpath - the xpath to append the xml to, as a string (single xpath) or array (multiple xpaths).

  • Optional {Array} criteria - an array of objects containing the criteria to further detail the node to select. Each object contains the following properties, all required:

    • {String} param the attribute to test (e.g. eom-name, xsm-preserve)

    • {String} condition the operator to use in the selector. Can be equal or contains.

    • {String} value the value to test for.

See example below.

Returns

{Boolean} true if the operation was executed correctly

At the moment, if the specified xpath in the xpathOptions corresponds to more than one node, only the first is taken.

// Obtains the current selection.
var sel = ctx.activeDocument.getSelection();
var selectedNode = sel.getNode();

var xpathOptions = {
    xpath: 'doc/story/text',
    criteria: {
        [
            param: 'xsm-preserve',
            condition: 'equal',
            value: 'true'
        ]
    }
};

if ( selectedNode.nodeType === ctx.activeDocument.CONSTANTS.CONTENTITEM_TYPE ) {
    // it is a ContentItem node. Duplicate its own content.
    selectedNode.duplicateContent( xpathOptions );
} else {
    // Duplicates the content of the container ContentItem.
    selectedNode.contentItem.duplicateContent( xpathOptions );
}
ContentNode

Its nodeType can be checked against ctx.activeDocument.CONSTANTS.CONTENTNODE_TYPE.

It has no specific methods at the moment.

ContentBlock

Its nodeType can be checked against ctx.activeDocument.CONSTANTS.CONTENTBLOCK_TYPE.

Its blockType can be checked against ctx.activeDocument.CONSTANTS.BLOCK_*.

Methods common to all the ContentBlocks
getXmlContent

Returns the XML of the current node.

It can be called as

{ContentBlock}.getXmlContent();
Table 73. Method information

Method

getXmlContent

Parameter

None

Return

{String} The xml of the current selection as a String

// Obtains the document selection
var sel = ctx.activeDocument.getSelection();
var selectedNode = self.getNode();

if ( selectedNode.nodeType === ctx.activeDocument.CONSTANTS.CONTENTITEM_TYPE ) {
    var xml = selectedNode.getXmlContent();
    // ...
}
replaceXmlContent

Replaces the XML content of the current node.

It can be called as

{ContentBlock}.replaceXmlContent();
Table 74. Method information

Method

replaceXmlContent

Parameter

{String} xml - The xml content that replaces the current content node.

Return

{Boolean} True if replacement succeeded

If the specified xml doesn’t contain a special element an error occurred. A block content can be replaced only with another block content. Right now we can replace only imageElement, videoElement and objectElement

// Obtains the document selection
var sel = ctx.activeDocument.getSelection();
var selectedNode = self.getNode();

if ( selectedNode.nodeType === ctx.activeDocument.CONSTANTS.CONTENTBLOCK_TYPE ) {

    var xml = '<photo-web><fw-photo width="200" height="200"></fw-photo>' +
    '<photo-caption channel="Globe-Web"><?EM-dummyText Insert caption first here?><p><?EM-dummyText Insert caption here?></p></photo-caption></photo-web>';

    var result = selectedNode.replaceXmlContent(xml);
    // ...
}
Image Block

It represents an Image Block. Its blockType can be checked against ctx.activeDocument.CONSTANTS.BLOCK_IMAGE.

copyImage( xpathOptions, settings )

Copies the image into other image elements, maintaining the crop and the transformation information.

It can be called as

{ContentBlock}.copyImage(xpathOptions, settings)
Table 75. Method information

Method

copyImage

Parameter

{object} xpathOptions - Contains the information on the XPath to copy the image to. It contains the following properties:

  • Required {String / Array} xpath - the xpath to append the xml to, as a string (single xpath) or array (multiple xpaths).

  • Optional {Array} criteria - an array of objects containing the criteria to further detail the node to select. Each object contains the following properties, all required:

    • {String} param the attribute to test (e.g. eom-name, xsm-preserve)

    • {String} condition the operator to use in the selector. Can be equal or contains.

    • {String} value the value to test for.

See example below.

Parameter

{object} settings additional options to change the copy Image behaviour.It contains the following properties:

  • Optional {Boolean} cover - If true, applies an effect similar to css background-size:cover property. If omitted, the default value is false.

  • Optional {Boolean} hardCrop - If true, applies the hardCrop parameter to the copied image. If omitted, the default value is false.

  • Optional {Boolean} copy - If true, create a copy of the object before linking the image to the selected placeholder. If omitted, the default value is false.

Returns

{Boolean} true if the operation was executed correctly

At the moment, if the specified xpath in the xpathOptions corresponds to more than one xPath, only the first is taken.

// Obtains the current selection
var sel = ctx.activeDocument.getSelection();
var selNode = sel.getNode();

// Check if it is an image block

if ( selNode.blockType === ctx.activeDocument.CONSTANTS.BLOCK_IMAGE ) {

    var xpathOptions = {
      'xpath': 'doc/story/text/photo-web/fw-photo',
      'criteria': [
          {'param': 'eomattr',
          'condition': 'equal',
          'value': 'true'}
        ]
    };

    selNode.copyImage(xpathOptions, { 'cover': true, 'hardCrop': false, 'copy': true });
}
changeAlignment( align, settings )

Change the alignment of a block component.

It can be called as

{ContentBlock}.changeAlignment(align, settings)
Table 76. Method information

Method

changeAlignment

Parameter

Required {String} align - The align to set, allow values: left, right, center, fullwidth, background.

Parameter

Optional {object} settings Additional options right now not used for future purpose

See example below.

// Obtains the current selection
var sel = ctx.activeDocument.getSelection();
var selNode = sel.getNode();

// Check if it is an image block
if ( selNode.blockType === ctx.activeDocument.CONSTANTS.BLOCK_IMAGE ) {
    selNode.changeAlignment('center');
}
changeBlockDimensions( dimensions, settings )

Change width and height of a block.

It can be called as

{ContentBlock}.changeBlockDimensions(xpathOptions, settings)
Table 77. Method information

Method

changeBlockDimensions

Parameter

Required {object} dimensions - Contains the width and height to set.

Parameter

Optional {object} settings Additional options right now not used for future purpose

See example below.

// Obtains the current selection
var sel = ctx.activeDocument.getSelection();
var selNode = sel.getNode();

// Check if it is an image block
if ( selNode.blockType === ctx.activeDocument.CONSTANTS.BLOCK_IMAGE ) {
    var dimensions = {'width': 300, 'height': 300};
    selNode.changeBlockDimensions(dimensions);
}
Video Block

It has no specific methods at the moment.

Collection Block

It represents a Collection Block. It can be of three types: insert, embed or link. Its blockType can be checked against ctx.activeDocument.CONSTANTS.BLOCK_COLLECTION_INSERT, ctx.activeDocument.CONSTANTS.BLOCK_COLLECTION_EMBED, ctx.activeDocument.CONSTANTS.BLOCK_COLLECTION_LINK.

setItemAttributes(attrOptions)

Set attributes to an item of a collection .

It can be called as

{ContentBlock}.setItemAttributes(attrOptions)
Table 78. Method information

Method

setItemAttributes

Parameter

{object} attrOptions - Contains the information on the item and attributes to set. It contains the following properties:

  • Required {Array} attributes - an array of objects pair name/value. If the value is empty the attribute will be removed.

  • Optional {Boolean} selected - it true the attributes will be applied to the collection selected item.

  • Optional {Integer} index - the index of THE item to apply the attributes (the index is not zero-based):

Returns

{Boolean} true if the operation was executed correctly

At least selected or index must be specified, otherwise the attributes will be not applied.

// Obtains the current selection
var sel = ctx.activeDocument.getSelection();
var selNode = sel.getNode();

// Check if it is a collection block

if ( selNode.blockType === ctx.activeDocument.CONSTANTS.BLOCK_COLLECTION_INSERT ||
        selNode.blockType === ctx.activeDocument.CONSTANTS.BLOCK_COLLECTION_EMBED ||
        selNode.blockType === ctx.activeDocument.CONSTANTS.BLOCK_COLLECTION_LINK) {

    var options = {};
    var attributes = [{'name': 'eomtest', 'value': 'test'}, {'name': 'class', 'value': 'testclass'}];
    options.attributes = attributes;
    options.selected = true; // the attributes will be applied to the selected item

    selNode.setItemAttributes(options);
}
getCollectionSelectedItems(attrOptions)

Get the selected items of a collection .

It can be called as

{ContentBlock}.getCollectionSelectedItems(attrOptions)
Table 79. Method information

Method

getCollectionSelectedItems

Parameter

{object} attrOptions - Contains the information on the attributes to search. It contains the following properties:

  • Optional {Array} attributes - an array of objects pair name/value. If empty the selected items will be returned.

Returns

{Boolean} an index array of selected items

// Obtains the current selection
var sel = ctx.activeDocument.getSelection();
var selNode = sel.getNode();

// Check if it is a collection block

if ( selNode.blockType === ctx.activeDocument.CONSTANTS.BLOCK_COLLECTION_INSERT ||
        selNode.blockType === ctx.activeDocument.CONSTANTS.BLOCK_COLLECTION_EMBED ||
        selNode.blockType === ctx.activeDocument.CONSTANTS.BLOCK_COLLECTION_LINK) {

    var options = {};
    // Uncomment this lines if you want to get items with a specific attribute value
    /* var attributes = [{'name': 'lead', 'value': 'test'}];
    options.attributes = attributes; */

    var selectedItems = selNode.getCollectionSelectedItems(options);
}
DummyText

Its nodeType can be checked against ctx.activeDocument.CONSTANTS.DUMMY_TYPE.

It has no specific methods at the moment.

General information on the GalleryEditor API

GalleryEditor API allows the user to perform different operations on the currently opened document. They are available within the Context Object, when the user is editing a Gallery.

GalleryEditor API are to be considered as a feature in progress. Changes and additions may be made without warning.

In the source code, we will refer to the Context Object as a generic "ctx" object.

activeDocument

The activeDocument is the entry point of the GalleryEditor API. It contains a set of different methods to analyze the currently opened document.

Methods of the activeDocument object

getXMLContent

Returns the complete XML content of the document.

It can be called as

ctx.activeDocument.getXMLContent();
Table 80. Method information

Method

getXMLContent

Parameter

None

Returns

{String} The XML document as a string.

Example
// Obtains the XML of the document.
var xml = ctx.activeDocument.getXmlContent();

// For example, we can parse the XML with jQuery and use it somehow.
var parsedXML = $.parseXML( xml );

// ...
getObjectsData

Returns an array containing the list of the data of each object present in the active document.

It can be called as

ctx.activeDocument.getObjectsData();
Table 81. Method information

Method

getObjectsData

Parameter

None

Returns

{Array} The array of data infornation of the all the objects present in the active document

The objects contained in the array are intended as "read only" objects. Any change made on them will not be reflected in the active document.

Example
// Obtains the current selection.
var objectsData = ctx.activeDocument.getObjectsData();
// objectsData is now an array of objects.

Here below an extract of the data contained in each returned object:

{
    "id": "199$1.0.624088073",
    "type": "Image",
    "name": "Official_portrait_of_Barack_Obama-U47037702204ISU-266x200@Globe-Web.jpg",
    "description": "",
    "owner": "user",
    "creator": "user",
    "created": 1457105152,
    "last_modifier": "user",
    "size": 125008,
    "system_attributes": {
        "workfolder": "",
        "template": "",
        "templateName": "",
        "summary": "",
        "wordCount": "",
        "sugCategory": "",
        "channel": "",
        "title": "...",
        "storyType": "",
        "productInfo": {
            "name": "",
            "issueDate": ""
        },
        "imageInfo": {
            "width": 266,
            "height": 200,
            "ptWidth": 63.84000015258789,
            "ptHeight": 48,
            "colorType": "RGB",
            "processTypes": ["resample"]
        },
        "priority": ""
    },
    "system_attributes_xml": "<props><process><type>resample</type></process><title>...</title><summary/><imageInfo>\n<width>266</width>\n<height>200</height>\n<ptWidth>63.84</ptWidth>\n<ptHeight>48.0</ptHeight>\n<xDim>22.52</xDim>\n<yDim>16.93</yDim>\n<dim>2.252cm x 1.693cm</dim>\n<xres>300.0</xres>\n<yres>300.0</yres>\n<colorType>RGB</colorType>\n<fileType>PNG</fileType>\n</imageInfo></props>",
    "usage_ticket": "<?xml version='1.0' encoding='UTF-8'?><tl></tl>"
}
isCroppedImage

Return a boolean value to check if the image is hard cropped.

It can be called as

ctx.activeDocument.isCroppedImage(loid);
Table 82. Method information

Method

isCroppedImage

Parameter

{String} - The loid of the image in the format: {dbId}${loid} (e.g. 199$1.0.624088073).

Returns

{Boolean} true if the image is hard cropped.

// Obtain the cropped information.
var isCropped = ctx.activeDocument.isCroppedImage(loid);

//check the isCropped variable
saveDocument

Execute the save of the current document.

It can be called as

ctx.activeDocument.saveDocument();
Table 83. Method information

Method

saveDocument

Parameter

Optional {Function} callback - A function invoked if the the document is successfully saved

If the document is not dirty, the save is not executed, but the callback, if defined, is always invoked.

Dwp Editor extensions

Méthode Swing DWP editor can be extended in multiple parts by adding custom buttons and executing custom actions, or configuring custom templates to render DWP linked objects.

Location of the DWP Editor extensions.

DWP Editor extensions consist in a Javascript (*.js) file loaded with Swing.

In general, all the extensions of Méthode Swing are places under

{SWING-APP}/plugins

So, all the Javascript described in the following sections should be placed under

{SWING-APP}/app/plugins/{EXTENSION-FOLDER}/{EXTENSION-NAME}.js

Do not use the word libs as an extension folder. The libs folder is reserved for loading external libs. See the proper documentation to obtain further info on the topic.

DEBUG MODE: by default, all plugins are aggregated into a single plugins.js file at the Tomcat startup. When creating a plugin, it may be frustrating to restart the Tomcat Server every time a change is made.

To avoid this, set the configuration property debugEnabled to true.

Then, that extension will be loaded on every refresh of the page.

Namespaces of the DWP editor extensions

DWP Editor extensions are available under the following namespace:

eidosmedia.webclient.actions.dwpeditor

Configure actions

Throughout this document, the example will focus on the development of a simple custom command, named custom.EditDwplink, which will edit a dwp link custom properties.

To add an action, then, it is necessary to follow the steps described below:

1. Add a custom command

The editor extensions follow the same approach of Swing commands.

See the Command configuration paragraph for further details.

Example

eidosmedia.webclient.commands.add({
    name: 'change.dwpLink',
    isActive: function( ctx ) {
        return true;
    },
    isEnabled: function( ctx ) {
        return true;
    },
    action: function( ctx, params, callbacks ) {
       if (callbacks && callbacks.success) {
          callbacks.success( /*...*/ );
       }
   }
});

The details of the implementation of the isEnabled and isActive method will be explained later.

2. Associated the commands to item toolbar

As a second step, the newly created command must be associated to the item toolbar. To add a button to the item toolbar, the following namespace must be used

eidosmedia.webclient.actions.dwpeditor

The method to be used is addButton(settings). See the following example:

/**
 * We now register a new action in the item toolbar using
 * the proper namespace. In this case we call the addButton
 * method, we define a label and an icon for the button and more important
 * we tell the button which command has to trigger using the
 * action property
 */
eidosmedia.webclient.actions.dwpeditor.addButton({
    action: "change.dwpLink",
    label: "Chane Dwp Link",
    icon: 'icon-globe'
});

The addButton method requires a single Javascript object ( settings ) as a parameter. This objects need to have the following properties:

  • action: the name of the previously registered command

  • label: the label associated with the command

  • icon: the icon associated with the command.

Implementation of the DWP editor extensions

The editor extensions are powerful because they make use of an editor-related version of the context object. See Basic structure of the Context Object for further details.

As of any command implementation, it is possible to specify the isActive, isEnabled and action methods.

  • isActive method is called in different points according to the editor part it refers to.

    • For the toolbar the method is called after the story is loaded to determine whether a custom action is available.

    • For the components the method is called when a new component is created ( or an existing one is loaded from the story )

  • isEnabled method is called in different points according to the editor part it refers to.

    • For the toolbar the method is called whenever the user changes the current selection or cursor position.

  • the action is called when the user clicks on the corresponding action button.

All the methods are called with the same Context Object ( ctx ), which is enhanced for the editor and allows to access the active object ( activeObject ). See Basic structure of the Context Object for further details.

In the Editor context, activeDocument and selection are NOT available.

The Dwp Editor Context Object activeObject contains a subset of methods to access the current dwp link information. These methods are described in the Dwp Editor API paragraph.

Dwp Editor API

Returns dwp link information.

It can be called as

ctx.activeObject.getLink();
Table 84. Method information

Method

getLink

Parameter

None

Returns

{JSON Object} Dwp link information

Example
var info = ctx.activeObject.getLink();

// info is now:
{
    "name": "230-Short updates.dwc",
    "id": "1.0.36956807",
    "type": "EOM::WebContainer",
    "modified": {
        "type": "DATE",
        "format": "YYYYMMDDHHmmss",
        "value": "20160323133940"
    },
    "last_modifier": "Dario",
    "locked": {
        "type": "DATE",
        "format": "YYYYMMDDHHmmss",
        "value": "20160323133940"
    },
    "locker": "",
    "webType": "dwc-4stories-230",
    "styleSheet": "List",
    "templates": [
        "Grid",
        "List",
        "230-4rows1col"
    ],
    "status_info": {
        "name": "",
        "identifier": "",
        "comment": ""
    },
    "system_attributes": {
        "workfolder": "/Globe/Politics",
        "template": "",
        "templateName": "/SysConfig/Globe/Web/DwcTemplates/public/230/230-4rows1col.dwc",
        "summary": "",
        "wordCount": "",
        "sugCategory": "",
        "channel": "Globe-Web",
        "storyType": "",
        "productInfo": {
            "name": "Globe-Web",
            "issueDate": "20111005"
        },
        "priority": ""
    },
    "system_attributes_xml": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<props><productInfo><name>Globe-Web</name>\r\n<issueDate>20111005</issueDate>\r\n</productInfo>\r\n<workFolder>/Globe/Politics</workFolder>\r\n<templateName>/SysConfig/Globe/Web/DwcTemplates/public/230/230-4rows1col.dwc</templateName>\r\n</props>\r\n",
    "channel": "Globe-Web",
    "pstate": {
        "uuid": "f8c9bd50-ee54-11e0-b51c-542d5e32b6d1",
        "suid": "",
        "loid": "1.0.36956807",
        "retention_time": 0,
        "ucount": 44
    },
    "position": 1
}
getTemplates

Returns the list of dwp link available templates.

It can be called as

ctx.activeObject.getTemplates();
Table 85. Method information

Method

getTemplates

Parameter

None

Returns

{JSON Array} List of dwp link available templates

Example
var templates = ctx.activeObject.getTemplates();

// templates is now:
[{
        "value":"Grid",
        "selected":false
},{
        "value":"List",
        "selected":true
}, {
        "value":"230-4rows1col"
        ,"selected": false
}]
getCustomProperties

Returns the list of dwp link custom properties.

It can be called as

ctx.activeObject.getCustomProperties();
Table 86. Method information

Method

getCustomProperties

Parameter

None

Returns

{JSON Array} List of dwp link custom properties

Example
var properties = ctx.activeObject.getCustomProperties();

// properties is now:
[{
        "key": "customTitle",
        "value": "Alternate title"
}, {
        "key": "customDisplayLink",
        "value": "yes"
}, {
        "key": "customMaxResults",
        "value": "10"
}, {
        "key": "customTitleColor",
        "value": "gray"
}]
setCustomProperties

Set the list of dwp link custom properties.

It can be called as

ctx.activeObject.setCustomProperties(properties);
Table 87. Method information

Method

setCustomProperties

Parameter

{JSON Array} List of dwp link custom properties

Returns

None

Example
var properties = [{
        "key": "customTitle",
        "value": "Alternate title"
}, {
        "key": "customDisplayLink",
        "value": "yes"
}, {
        "key": "customMaxResults",
        "value": "10"
}, {
        "key": "customTitleColor",
        "value": "gray"
}];

ctx.activeObject.setCustomProperties(properties);

Custom DWP linked item templates

Méthode Swing DWP editor uses HTML templates to render linked objects. It is possible to configure custom templates to be used based on linked item type.

Editor configuration

In DWP editor configuration can be specified which HTML template should be used to render a specific item linked type.

<WebClientConfiguration>
    ...
    <editor>
        ...
        <webpage>
            ...
            <linkedItemTemplates>
                <linkedItemTemplate>
                    <type>EOM::CompoundStory</type>
                    <template>dwp-story-template.html</template>
                </linkedItemTemplate>
                <linkedItemTemplate>
                    <type>EOM::WebContainer</type>
                    <template>dwp-dwc-template.html</template>
                </linkedItemTemplate>
            </linkedItemTemplates>
        </webpage>
    </editor>
<WebClientConfiguration>

Configured template files will be retrieved from

{SWING-APP}/config/templates/dwp/{TEMPLATE-NAME}

and can be configured outside {SWING-APP}, see the paragraph How to configure DWP linked item templates in external folder.

Templating system

Underscore.JS templating system is used to define templates.

The template is rendered with a main object item, which contains all the information about the item.

// item:
{
    name: 'ART-stories_block.dwc',
    id: '1.0.895409210',
    databaseId: '41',
    type: 'EOM::WebContainer',
    modified: 1524043188,
    last_modifier: 'john.doe',
    locked: 1542709437,
    // empty string if the item is not locked, username of the locker otherwise
    locker: '',
    webType: 'dwc-element-4row3cols',
    styleSheet: null,
    // stylesheet configured for the zone and applied as default
    defaultStylesheet: 'first',
    // selected template
    template: 'stories_block',
    // available templates
    templates: [
        {
            name: 'stories_block',
            path:
                '/SysConfig/Globe/WebV2/DwcTemplates/public/1060/stories_block.dwc'
        },
        {
            name: 'one_story_2_heads',
            path:
                '/SysConfig/Globe/WebV2/DwcTemplates/public/1060/one_story_2_heads.dwc'
        }
    ],
    status_info: {
        name: '',
        identifier: ''
    },
    // thumbnail preview if available, null otherwise
    preview: null,
    channel: 'Globe-Web',
    owner: 'john.doe',
    creator: 'john.doe',
    created: 1500474152,
    size: 243,
    path: '/Globe/WebV2/Web Components/Art/ART-stories_block(1).dwc',
    issueDate: '20150903',
    system_attributes_xml: 'system attributes XML string',
    system_attributes_json: {
        // system attributes as json object
    },
    usage_ticket: 'usage ticket XML string',
    usage_ticket_json: {
        // usage ticket as json object
    },
    attributes: 'attributes XML string',
    attributes_json: {
        // attributes as json object
    },
    virtual_attributes: 'virtual attributes XML string',
    virtual_attributes_json: {
        // attributes as json object
    },
    summary: '',
    dwc: {
        struct: {
            // dwc container structure as json object, see next section
        },
        links: {
            // dwc linked items, see next section
        }
    },
    // Icon class for type
    iconType: 'emui-icon-dwp',
    // Display label for type
    labelType: 'Web Container'
}

DWC

If a linked item is also a container, e.g. a DWC, the object passed to the template will also have a dwc field, containing information about container structure and linked items.

{
    // ...
    dwc: {
        struct: {
            type: 'container',
            path: 'root',
            pageTemplate: 'stories_block',
            depth: 0,
            slots: 12,
            rowspan: 1,
            id: '41$1.0.895409210',
            channel: 'Globe-Web',
            webType: 'dwc-element-4row3cols',
            rows: [
                {
                    dimension: 1,
                    boxesCount: 3,
                    boxes: [
                        {
                            type: 'zone',
                            path: 'root[r0]-zone0',
                            depth: 1,
                            slots: 5,
                            rowspan: 1,
                            sequence: 1,
                            maxItems: 4,
                            styleSheet: '2col-photo-homepage',
                            name: 'box',
                            zoneIndex: 1
                        }
                        // ...
                    ]
                }
            ],
            dimension: 1
        },
        links: {
            box: [
                {
                    depth: 0,
                    slots: 0,
                    rowspan: 0,
                    sequence: 1,
                    maxItems: 1,
                    styleSheet: '1col-photo-tight-homepage',
                    links: [
                        {
                            id: '1.0.740572086',
                            name: 'Story 1',
                            databaseId: '41',
                            channel: 'Globe-Web',
                            webType: 'story',
                            position: 1,
                            type: 'EOM::CompoundStory',
                            modified: 1522759672,
                            last_modifier: 'john.doe',
                            locked: 1524465039,
                            locker: '',
                            status_info: {
                                name: 'NewsFlow/Editing',
                                identifier: 'RGB(255,0,0)'
                            },
                            system_attributes_json: {
                                // system attributes as json object
                            }
                        }
                    ],
                    zoneIndex: 0
                }
                // ...
            ]
        }
    }
}

Following, a very simple example of a custom template for DWC objects:

<%
    function writeLink(link) {
        %>
            <div><%=link.name%></div>
        <%
        // ...
    }

    function writeRow(row) {
        %>
        <div>DWC row</div>
        <ul>
            <%
                _.each(row.boxes, function(box) {
                    if (box.type === 'zone' || box.type === 'container') {
                        %><li>
                            <div>BOX</div>
                            <%
                                var seqObj = _.findWhere(
                                    item.dwc.links[box.name],
                                    { sequence: parseInt(box.sequence) }
                                );
                                _.each(seqObj && seqObj.links, writeLink);
                            %>
                        </li><%
                    }
                });
            %>
            </ul>
        <%
    }
%>
<div>
    <h1>DWC</h1>
    <p>Name <%=item.name%></p>
    <%
        _.each(item.dwc.struct.rows, writeRow);
    %>
</div>

How to configure DWP linked item templates in external folder

Custom DWP linked item templates may be made available to Swing application by defining one or more nested components in Swing web context in server.xml. (for further details on Tomcat 9.x Resources configuration, please refer to Resources configuration).

<Context docBase="com.eidosmedia.webclient.web-app"
    path="/swing" reloadable="false">
    <Resources className="org.apache.catalina.webresources.StandardRoot">
        <PreResources
            className="org.apache.catalina.webresources.DirResourceSet"
            base="/methode/meth01/extension/dwp-templates" readOnly="true"
            webAppMount="/config/templates/dwp" />
    </Resources>
</Context>

Custom editor panels

With Méthode Swing it’s possible to add custom lateral panels to the Editor. The behaviour and the registration is similar to the Object Panel extension, and allows the user to interact with the Editor and the opened document through the use of the Editor Apis.

Registration of a custom panel

To register a new custom panel, use the following namespace

eidosmedia.webclient.extensions.binder

and its function

add( name, options )
Table 88. Method information

Method

add

Parameter

Required {String} name - The name of the custom panel Required {object} options - An object containing the options and the behaviour of the panel. See Properties and methods of the custom insertinfo for details.

Returns

{undefined} undefined

The following is a very simple of a working custom panel.

eidosmedia.webclient.extensions.binder.add( "city-panel", {
    label: "City info",
    icon: 'fa fa-building-o',
    template: 'city-panel.html',
    isActive: function(ctx) {
        return true;
    },
    headerButtons: [{ name: 'refresh', icon: 'icon-refresh', title: 'toolbar.refresh' }],
    init: function(ctx, $container, panelMethods) {
        // Load external sources...
    },
    ready: function(ctx, $container, panelMethods) {
        alert("No man is an island, / Entire of itself. / Each is a piece of the continent, / A part of the main. ... / For whom the bell tolls, / It tolls for thee.");
    },
    closeOnHide: true,
    refreshOnShow: true,
    /**
     * Method called whenever the user clicks on the refresh button.
     */
    onRefresh: function(ctx, $container, panelMethods) {
        console.error('Clicked on refresh');
    },
    onClose: function(ctx, $container, panelMethods) {
        alert("Don't leave me, please!");
    },
    /**
     * Method called whenever the user changes selection. Available only inside
     * the story editor.
     */
    onChangedPath : function(ctx, $container, panelMethods, pathInfo) {
        console.log(pathInfo);
    }
});

Properties and methods of the custom panel

The following paragraphs describe each of the available properties or methods for the custom panel.

label

[Mandatory] {String} label defines the label visible on the top of the panel when it is selected.

Example
{
    // ...
    label: "For whom the bell tolls",
    // ...
}

icon

[Optional] {String} icon defines the icon of the custom panels. All the most recent Font Awesome icons are available.

Example
{
    // ...
    icon: "fa fa-bell",
    // ...
}

template

[Mandatory] {String} template defines the path of the custom panel HTML template. The templates must be put under the following folder:

{SWING-APP}/config/templates/panels/
Swing supports Tomcat 9.x, so custom panels can be configured outside {SWING-APP}. See the paragraph How to configure custom panels in external folder for some tips in how to take advantage of this configuration option.
the path specified in the template option is relative to the /panels/ folder.
Example
{
    // ...
    template: 'bell.html',
    // ...
}

offlineAvailable

[Optional] {Boolean} offlineAvailable determines whether the custom panel is visible or not when the application is offline.

Example
{
    // ...
    offlineAvailable: true,
    // ...
}

localStoryAvailable

[Optional] {Boolean} localStoryAvailable determines whether the custom panel is visible or not when editing a local story.

Example
{
    // ...
    localStoryAvailable: true,
    // ...
}

isActive( ctx )

[Optional] {Function} isActive determines whether the custom panel is visible or not. If not specified, the panel is always visible. This function must return a valid Boolean value to be used. The isActive method has the usual ctx parameter available.

Example
{
    // ...
    isActive: function( ctx ) {
        return ctx.activeObject.getType() === 'EOM::Story';
    },
    // ...
}

init( ctx, $container )

[Optional] {Function} init is called as soon as the panel is initialized, after the isActive method has passed. It has the following parameters:

Table 89. Method parameters
Name Description

ctx

The usual Context Object

$container

The jQuery reference to the container node.

The init method is called before the template has been loaded, so the $container content is actually empty. Do not use it to add event listeners to DOM objects.

Example
{
    // ...
    init: function( ctx, $container ) {
        // For example, load external sources...
    },
    // ...
}

ready( ctx, $container )

[Optional] {Function} ready is called after the panel has been initialized and the template loaded. It has the following parameters:

Table 90. Method parameters
Name Description

ctx

The usual Context Object

$container

The jQuery reference to the container node.

panelMethods

A small set of common methods to be used inside the panel. The only available method is setMessage( message, icon, element ).

This is the best place to add event listeners to DOM object and to actually implement the whole panel’s logic.

Example
{
    // ...
    ready: function( ctx, $container, panelMethods ) {
        var _ctx = ctx; // Remember to save the instance, otherwise inside the event it will not work
        $container.off('click', '.my-save-button').on('click', '.my-save-button', function() {
            _ctx.activeDocument.saveDocument();
        });
    },
    // ...
}

refreshOnShow

[Optional] If set to true, the custom panel will refresh ( and call all the subsequent methods, e.g. ready (not _init) ), any time the custom panel tab is selected by the user ( except for the first time ).

closeOnHide

[Optional] If set to true, the onClose method, if available, will be called any time the custom tab loses focus ( another tab is opened ).

onClose

[Optional] Method called when the custom panel is closed. This happens when a document is closed, when a different element is selected, or when the custom tab is hidden ( in case of closeOnHide set to true).

The syntax is the following:

onClose( ctx );
Table 91. Method information

Method

onClose

Parameter

{object} ctx - The context object. See Basic structure of the Context Object for further information.

Returns

undefined.

onApplicationStatusChange

[Optional] Method called when the application status change from online to offline or vice versa (triggered only when using the offline functionality).

The syntax is the following:

onApplicationStatusChange(ctx, $container, panelMethods, status);
Table 92. Method information

Method

onApplicationStatusChange

Parameter

{object} ctx - The context object. See Basic structure of the Context Object for further information.

$container

The jQuery reference to the container node.

panelMethods

A small set of common methods to be used inside the panel. The only available method is setMessage( message, icon, element ).

Parameter

{object} status - The application status. The status object is structured as below:

  • applicationOnline: true if the application is in online mode, false otherwise.

  • isOnline: true if the application reaches the server and the application is in online mode, false otherwise.

  • offlineServer: enabled : true if the offline server is available, enabled: false otherwise.

  • serverReachable: true if the server is reachable, false otherwise.

Returns

undefined.

Example
// options object...
{
    onApplicationStatusChange: function( ctx, $container, panelMethods, status ) {
       // For example, we can check the application status
       if (status.isOnline) {
        ....
       }
    }
}

How to configure custom panels in external folder

Additional objects may be made available to Swing application by defining one or more nested components in Swing web context in server.xml. (for further details on Tomcat 9.x Resources configuration, please refer to Resources configuration).

This is an example of the external custom panels configuration.

<Context docBase="com.eidosmedia.webclient.web-app"
    path="/swing" reloadable="false">
    <Resources
        className="org.apache.catalina.webresources.StandardRoot">
        <PreResources
            className="org.apache.catalina.webresources.DirResourceSet"
            base="/methode/meth01/extension/custompanels" readOnly="true"
            webAppMount="/config/templates/panels" />
    </Resources>
</Context>

Custom insertinfo panels

With Méthode Swing it’s possible to add custom insertinfos to the Editor. The behaviour and the registration is similar to the Object Panel extension.

Registration of a custom insertinfo template

To register a new custom insertinfo template, use the following namespace

eidosmedia.webclient.insertinfo

and its function

add( options )
Table 93. Method information

Method

add

Parameter

Required {object} options - An object containing the options and the behaviour of the custom insertinfo template.

Returns

{undefined} undefined

The following is a very simple of a working custom insertinfo template.

(function() {

                eidosmedia.webclient.insertinfo.add({

                /**
                * name of the custom insertinfo implementation
                */
                name: "CustomInsertInfo",

                /**
                * path of the html template relative to the insertinfo folder
                */
                template: "customInsertInfo.html",

                /**
                * An array of insertinfo template configured in the DTX which the custom implementation will be connected.
                */
                dtxTemplateName: ['Custom Image Size', 'Custom width-height'],
                /**
                *
                * @param ctx: The usual Context Object. See <<ctx>> for further details. It could be empty
                * if only placeholder is inserted.
                * @param htmlTemplate: a String containing the html of
                * the configured template
                *
                * @return a jQuery object with the html to show to the user
                */
                init: function(ctx, htmlTemplate){

                        /*
                        * Elaborate the html and return the
                        * html to show to the user
                        *
                        */

                        return $(htmlTemplate);
                },

                /**
                *
                * @param ctx: The usual Context Object. See <<ctx>> for further details. It could be empty
                * if only placeholder is inserted.
                * @param $html: the jQuery representation of the html
                * edited by the user
                *
                * @return an object with key values pairs:
                *    {
                *        "uVariable"  : "value",
                *         "uVariable2" : "value2"
                *    }
                */
                getValues: function(ctx, $html){
                        /**
                        * Elaborate $html object
                        **/

                        var width = $html.find('#custom_width').val();
                        var height = $html.find('#custom_height').val();

                        result = {
                                "uWidth"  : width,
                                "uHeight" : height
                        }

                        return result;
                }
    });

})();

Properties and methods of the custom insertinfo

The following paragraphs describe each of the available properties or methods for the custom insertinfo.

name

[Mandatory] {String} name defines the name of the custom inserinfo.

{
    // ...
    name: 'CustomInsertInfo',
    // ...
}

dtxTemplateName

[Mandatory] {Array,String} dtxTemplateName An array of insertinfo template configured in the DTX which the custom implementation will be connected.

String type is still supported only for backward compatibility.
Example
{
    // ...
    dtxTemplateName: ['Custom image size', 'Custom width height'],
    // ...
}

DTX configuration:

{
    <template name="Custom image size" element="fg-photo" mode="inline">
        <photo-group>
            <fg-photo width="{uWidth}" height="{uHeight}"></fg-photo>
            <photo-caption>
                <p xpAttributesSource="/*/iptc/caption/text()">
                        <source xpAttributesSource="/*/iptc/source/text()"><?EM-dummyText Insert caption source?></source>
                        <?EM-dummyText Insert caption here?>
                </p>
                <foto-links></foto-links>
            </photo-caption>
        </photo-group>
    </template>

    <template name="Custom width height" element="fg-photo" mode="inline">
        <photo-group>
            <fg-photo width="{uWidth}" height="{uHeight}"></fg-photo>
            <photo-caption>
                <p xpAttributesSource="/*/iptc/caption/text()">
                    <source xpAttributesSource="/*/iptc/source/text()"><?EM-dummyText Insert caption source?></source>
                    <?EM-dummyText Insert caption here?>
                </p>
                <foto-links></foto-links>
            </photo-caption>
        </photo-group>
    </template>
}
dtxTemplateName can contain the value of the attribute customInsertInfo of the link element of type insert. In this case the custom implementation will be connected to all the tempplate configured.
{
    <insertInfo type="Image" channel="Globe-Web" container="EOM::CompoundStory,EOM::MediaGallery">
        <link name="Insert" type="insert" customInsertInfo="customLinkInsert">

            <template name="Custom Image One" element="fg-photo" mode="inline">
                <photo-group>
                    <fg-photo width="{uWidth}" height="{uHeight}"></fg-photo>
                    <photo-caption>
                        <p xpAttributesSource="/*/iptc/caption/text()">
                            <source xpAttributesSource="/*/iptc/source/text()"><?EM-dummyText Insert caption source?></source>
                            <?EM-dummyText Insert caption here?>
                        </p>
                        <foto-links></foto-links>
                    </photo-caption>
                </photo-group>
            </template>

            <template name="Custom Image Two" element="fg-photo" mode="inline">
                <photo-group>
                    <fg-photo width="{uWidth}" height="{uHeight}"></fg-photo>
                    <photo-caption>
                        <p xpAttributesSource="/*/iptc/caption/text()">
                            <source xpAttributesSource="/*/iptc/source/text()"><?EM-dummyText Insert caption source?></source>
                            <?EM-dummyText Insert caption here?>
                        </p>
                        <foto-links></foto-links>
                    </photo-caption>
                </photo-group>
            </template

        </link>
    </insertInfo>
}
all the variables have to be wrapped with brackets (e.g. {uWidth}).

template

[Mandatory] {String} template defines the path of the custom panel HTML template. The templates must be put under the following folder:

{SWING-APP}/config/templates/insertinfo/
Swing supports Tomcat 9.x, so custom panels can be configured outside {SWING-APP}. See the paragraph How to configure custom insertinfo in external folder for some tips in how to take advantage of this configuration option.
the path specified in the template option is relative to the /insertinfo/ folder.
Example
{
    // ...
    template: 'customInsertInfo.html',
    // ...
}

init( ctx, htmlTemplate )

[Mandatory] {Function} init is called as soon as the panel is initialized. This function must return a valid jQuery object to be used.

Table 94. Method parameters
Name Description

ctx

The usual Context Object. See Basic structure of the Context Object for further details. It could be empty if only placeholder is inserted.

htmlTemplate

htmlTemplate: a String containing the html of the configured template

Returns

{object} results - An object containig key value pairs of all the variables present in the insertinfo template.

Example
{
    // ...
    init: function( ctx, html ) {
        // Elaborate the html document an return a jQuery html object
        var $html = $(html);

        return $html;
    },
    // ...
}

getValues( params, $html )

[Mandatory] {Function} getValues is called when the user applies the insertinfo. This function must return a valid Object containing all the variables present in the template.

Table 95. Method parameters
Name Description

ctx

The usual Context Object. See Basic structure of the Context Object for further details. It could be empty if only placeholder is inserted.

$html

The jQuery representation of the html edited by the user.

Returns

{object} results - An object containig key value pairs of all the variables present in the insertinfo template.

Example
{
    // ...
    getValues: function( ctx, $html ) {
        /*
                * Elaborate $html object
                */
                var width = $html.find('#custom_width').val();
                var height = $html.find('#custom_height').val();
                result = {
                        "uWidth"  : width,
                        "uHeight" : height
                }
                return result;
    },
    // ...
}

How to configure custom insertinfo in external folder

Additional objects may be made available to Swing application by defining one or more nested components in Swing web context in server.xml. (for further details on Tomcat 9.x Resources configuration, please refer to Resources configuration).

This is an example of the external custom insertinfo configuration.

<Context docBase="com.eidosmedia.webclient.web-app"
    path="/swing" reloadable="false">
    <Resources
        className="org.apache.catalina.webresources.StandardRoot">
        <PreResources
            className="org.apache.catalina.webresources.DirResourceSet"
            base="/methode/meth01/extension/insertinfo" readOnly="true"
            webAppMount="/config/templates/insertinfo" />
    </Resources>
</Context>

Channel copy

With Méthode Swing it’s possible to add custom manipulation to the content of a channel copy after his creation.

Registration of a custom manipulation

To register a custom manipulation, use the following namespace

eidosmedia.webclient.extensions.channelCopy

and its function

register( options )
Table 96. Method information

Method

register

Parameter

Required {object} options - An object containing a function that will be invoked to perform the custom manipulation.

Returns

{undefined} undefined

The following is a very simple registration for a custom manipulation.

(function() {


    eidosmedia.webclient.extensions.channelCopy.register(function( data, callback ) {

            // Save the current content of the channel copy
            var xmlElaborated = data.xmlContent;

            try {

                /**
                 * Save the current content of the channel copy. This variable will be used to make some changes to the current content
                 */
                var xmlContent = data.xmlContent;

                /**
                 * Save information about the current channel copy. For example the channel name
                 */
                var copyInfo = data;

                /**
                 * Perform some simple manipulation. In our case add only a new attribute
                 */
                var xml = $.parseXML(xmlContent);
                var $xml = $(xml);
                var $doc = $xml.find('doc');
                $doc.attr('newattr', 'newvalue');

                /**
                 * Serialize the new xml content
                 */
                var serializer = new XMLSerializer();
                var preValue = xmlElaborated.slice(0, xmlElaborated.indexOf('<doc'));
                var result = serializer.serializeToString($doc[0]);

                /**
                 * Save the new xml content
                 */
                xmlElaborated = preValue + result;

                /**
                 * notify a callback if specified
                 */
                if (callback) {
                    callback(xmlElaborated);
                }

            } catch (ex) {
                console.log("Channel copy custom manipulation exception caught", ex);

                if (callback) {
                    callback(xmlElaborated);
                }
            }
    });

})();

The function must be have two parameters. The first one contains info about xml content and the channel object info. The second one it’s a callback that must be invoked after the custom manipulation. Wrap all the custom manipulation with a try…​catch block otherwise the normal process of the channel copy creation will be not performed.

After the custom manipulation the system will perform a validation check of the new content. If the check goes wrong the channel copy will be created with the original content and the user will be notified

Dashboard

Widget Creation - Méthode Swing

Méthode Swing dashboards are extensible with custom widgets.

This document will introduce the use of Méthode Swing SDK for creating and testing the widgets, as well as describe the correct widget structure and provide a complete example of a custom widget creation.

Widget structure

All the widgets are put inside the following path:

{SWING-APP}/app/widgets/{WIDGET-NAME}

Widgets can be configured outside {SWING-APP}. See the paragraph How to configure widgets in external folder for some tips in how to take advantage of this new configuration option.

As you can see, all the widget provided by Méthode Swing are included in this folder.

Each widget folder must contain the following files:

Table 97. Files in {WIDGET-NAME} folder
File name Description

{WIDGET-NAME} .js

The JavaScript file defines the widget’s behaviour.

{WIDGET-NAME} .html

The file represents the visual template of the widget.

{WIDGET-NAME} .less

LESS is a CSS-like language. This file changes the aesthetic appearance of the widget. It is not mandatory.

It is necessary that the file all have the same name of the folder. It is recommended, although not mandatory, to use lower case characters and, in general, to avoid "unusual" characters.

DEBUG MODE: by default, all widgets are aggregated into a single widgets.js file at the Tomcat startup. When creating a widget, it may be frustrating to restart the Tomcat Server every time a change is made.

To avoid this, set the configuration property debugEnabled to true.

Then, that widget will be loaded on every refresh of the page.

Note: in debug mode, the widgets are loaded separately and asynchronously; this may occur in a widget not loaded correctly. In this case, simply change tab (e.g. move to "My Area") and back to the Dashboard to have the widget loaded correctly.

JS file

The widget’s Javascript file should be written as follows:

/*
 * Todo List Widget. Used to demonstrate the creation of a custom widget
 */
eidosmedia.webclient.dashboard.widgets.define( {WIDGET-NAME}, function() {

    var settings = {
        title: 'SOME TITLE',
        icon: 'icon-gear'
        // Default settings...
    };

    function onResize() {
        // Method called on the windows resize.
    };

    function loadData() {
        // Method called on the widget loading. Used to retrieve the user's data.
    };

    function updateData() {
        // Used to update the widget's template.
    };

    return {
        settings: settings,
        onResize: onResize,
        loadData: loadData,
        updateData: updateData
    };

});

Basically, is the widget should call the

eidosmedia.webclient.dashboard.widgets.define

function, with the following parameters:

  • {WIDGET-NAME} : the same name used everywhere else in the widget folder.

  • a function which returns a JavaScript object with a set of mandatory methods as described in the following paragraph.

Each widget has a set of default settings, which can be specified under the settings property of the widget.

When the Dashboard is displayed, the widgets specified in the configuration are initialized. The chain of events continues as follows:

  1. First, the dashboard renders the HTML template with status = "init". ( See HTML file for details on how the template is called )

  2. Second, the dashboard calls the widget’s loadData (required) method which should load the widget’s data. ( See loadData for details on how the method is called )

  3. Within the widget loadData method, the widget should call either the dashboard updateBaseData method ( See updateBaseData for further details ), to let the dashboard know that the loading has finished.

Optionally, it is possible to specify the behaviour when the window is resized. (For example, it can be used to retrive the number of visible objects and change the widget’s rendering accordingly).

See onResize for further details on how to manage the resize.

Required methods for the widget
loadData

loadData is the method called by the Dashboard in order to load the Widget data. It must be declared as follows:

loadData: function( settings, ctx ) { ...

The function is called with one parameter:

  • settings - contains the settings specified in the widget configuration. It is a Javascript object.

  • ctx - the Swing context object. See Context object documentation.

If you specified the settings property in the widget declaration, you can be sure that at least those settings are available. If the user specifies customs settings in Swing configuration, the properties with the same name are overridden by the user configuration.

Within the loadData function, the Javascript context (also known as the this object) is the widgetContext object ( see widgetContext: list of available methods within the widget to know the complete list of methods available ).

It is mandatory that the widget calls the updateBaseData method of the widgetContext object (the this object), otherwise the dashboard as no way of knowing that the widget has finished loading. See updateBaseData for further details.

In general, the method updateBaseData must be called with two parameters:

  • settings: the settings described above.

  • data : the loaded data. It can be whatever you prefer (an array or JSON object). The data is passed to the widget without further elaboration.

This is an example of the TODOLIST widget loadData function.

function loadData( settings, ctx ) {
    var widgetContext = this; // save the widget variable.
    // Get the user todo list
    try {
        /* getUserData is one of the available methods,
           see the proper documentation below for further details */
        widgetContext.getUserData( 'todolist',{
            success: function( data ) {
                // Process List items.

                /* NOTE: IT IS NECESSARY TO CALL THE FOLLOWING METHOD TO LET THE DASHBOARD
                   KNOW THAT THE WIDGET HAS COMPLETED THE DATA LOADING. */
                widgetContext.updateBaseData( settings, data );
            },
            error: function(xhr, textStatus, errorThrown) {
                // Process the error response and show the Widget Error
                var error = widgetContext.processErrorResponse(xhr, textStatus, errorThrown);
                widgetContext.showError(error);
            }
        });
    } catch (ex) {
        widgetContext.showError( { message: ex.message } );
    }
};
Optional methods for the widget
updateData

updateData is the method called by the Dashboard after the widget has been loaded. It is generally used to add listeners for events, button clicks, and anything related.

Do not confuse updateData (the widget method) with updateBaseData (the widgetContext method).

The widgetContext.updateBaseData() checks if the widget updateData method is available and calls it.

updateData: function( settings, items, ctx ) { ...

The function is called with some parameters:

  • settings - contains the settings specified in the widget configuration. It is a Javascript object.

  • data - the loaded data. It can be whatever you prefer (an array or JSON object). The data is passed to the widget without further elaboration.

  • ctx - the Swing context object. See Context object documentation.

Within the updateData function, the Javascript context (also known as the this object) is the widgetContext object ( see widgetContext: list of available methods within the widget to know the complete list of methods available ).

This is an example of the updateData function.

function updateData( settings, data, ctx ) {
    var widgetContext = this; // save the widget variable.

    // add Listeners to the object.

    var $widget = widgetContext.getWidgetContainer(); //Obtains the jQuery reference to the widget.

    $widget.find('#saveSomething').off('click').on('click', function() {
        // do some elaboration of the data...
        alert('you clicked me');
    });

};
onResize

onResize is the method called by the Dashboard whenever the Browser window is resized. It can be used, for example, to change the number of rows shown.

onResize: function( ) { ...

The method is called without parameters.

Within the onResize function, the Javascript context (also known as the this object) is the widgetContext object ( see widgetContext: list of available methods within the widget to know the complete list of methods available ).

This is an example of the diggfeeds widget onResize function.

function onResize() {
    var widgetContext = this;
    var ROW_PIXEL_HEIGHT = 65;
    var HEADER_PIXEL_HEIGHT = 30;

    var calcElements = function() {
        var widgetHeight = widgetContext.getHeight();
        var totalEl = Math.floor( (widgetHeight - HEADER_PIXEL_HEIGHT) / ROW_PIXEL_HEIGHT);
        return totalEl;
    };

    var totalRow = widgetContext.settings.count;
    var totalNewEl = calcElements.call(this);

    if ( totalNewEl === totalRow ){
        return;
    } else {
        // New quantity of items.
        // Refresh the widget with new items.
        this.settings.count = totalNewEl;
        loadData.call( widgetContext, widgetContext.settings );
    }
};
widgetContext: list of available methods within the widget

The widgetContext object is very important and represents the context ( the this object ) with which the loadData, updateData and onResize methods are called.

It provides a set of common methods which can ( and should ) be used within the widget.

The most important is updateBaseData, which must be called within the widget loadData method as widely described before.

Any of these methods can be accessed with the keyword this within the threed common methods ( loadData, updateData and onResize ).

It is recommended to save the reference into a variable, to be used within the callback functions. For example:

var widgetContext = this;
// ....
widgetContext.updateBaseData( settings, data );
updateBaseData

The method must be called within the loadData method, to let the dashboard know that the widget has finished loading data. It must be called as follows:

widgetContext.updateBaseData( settings, data );
  • settings are the widget’s settings ( provided within the loadData method ), or any setting that you want to pass to the widget.

  • data is the loaded information to be passed to the widget.

This internal method automatically calls the widget updateData method with the same settings, as described above.
getHeight

Returns the current widget height. Useful in obtaining the widget dimensions for the onResize method.

function onResize() {
    var widgetHeight = this.getHeight();
    // Some elaboration...
}
getWidth

Returns the current widget width. Useful in obtaining the widget dimensions for the onResize method.

function onResize() {
    var widgetWidth = this.getWidth();
    // Some elaboration...
}
getWidgetIcon

Returns the current widget icon as specified in the settings. It is often used inside the widget HTML template, as follows.

The result of this.getWidgetIcon() and of this.settings.icon are the same.
<h1>
    <%=settings.title%>
    <i class="<%=(widget.getWidgetIcon() || settings.icon)%>"></i>
</h1>
getIcon

getIcon returns the configured icon according to the type.

It is called as follows:

this.getIcon( type );

It returns a string containing the corresponding icon.

    var icon = this.getIcon( 'EOM::Story' ); // Returns 'emui-icon-file-text';
    // Some elaboration...
addDummy

addDummy fills an array with empty elements.

It is called as follows:

addDummy(data, max)
  • data is the array of items

  • max is the array length.

    // data has 30 elements.
    this.addDummy( data, 50 );
    /**
    *  Now data has 50 elements. From Index 30 to index 49, the element is
    * { empty: true }
    */
execute

execute executes specific supported actions.

Supported actions are:

  • newcontent : opens the "New Content" dialog

  • newmessage : opens the "New Message" dialog

  • newstory : creates a new story document with the default configuration

  • newgallery : creates a new gallery document with the default configuration

It is called as follows:

execute( action, options )

The method does not return anything.

    // Open the "new content" dialog.
    this.execute( "newcontent" );
getWidgetContainer

The method returns the jQuery reference to the current widget’s container

Use this method to quickly look for DOM elements of your widget.
    var $container = this.getWidgetContainer();

    // Search all anchors inside the widget...

    var $links = $container.find('a');
getCurrentUser

The method returns a JSON object with the logged user’s properties. See the example to see the returned properties.

    var currentUser = this.getCurrentUser();

    /** Returns

    {
        "picture": "/WebClient/user/avatar/user_name?token=1fb301ce-1dc3-4f78-9e2c-0b9c2750ff1b",
        "name": "user_name",
        "description": "Eidosmedia top class R&D",
        "config_folder": "eomfs:/Configurations/Profiles/user_name/Config",
        "id": "1.0.417997841",
        "fullName": "Ted Mosby",
        "team": "Globe_Web",
        "phoneNumber": "456",
        "mobileNumber": "123",
        "twitter": "user_name",
        "facebook": "john.doe@r.com",
        "role": "Reporter",
        "homeEmail": "john.doe.home@r.com",
        "businessEmail": "john.doe@r.com2",
        "statusMessage": "Status msgsdsadsadas",
        "workDir": "workfolder:///Globe/Sport",
        "location": "Roma",
        "profileDir": "eomfs:/Configurations/Profiles/user_name",
        "lastLoggedOn": 1423501287,
        "initials": "AP",
        "signature": "John",
        "system_attributes": "SYSTEM ATTRIBUTES",
        "metadata": "",
        "homePath": "/Users/user_name",
        "admin": true,
        "groups": [
            {
                "name": "Administrators",
                "description": "",
                "config_folder": "eomfs:/Configurations/Profiles/Administrators/Config",
                "profile_folder": "eomfs:/Configurations/Profiles/Administrators",
                "categories": [
                    "EOM::Group",
                    "EOM::Configuration",
                    "EOM::Privileges"
                ],
                "isConfiguration": true
            }
        ],
        "calendars": [
            {
                "color": "#0C96D7",
                "id": "U00033812843qQO",
                "name": "World Cup 2014",
                "url": "https://www.google.com/calendar/ical/qlbi1b0rp50vb07rmssnebq694%40group.calendar.google.com/public/basic.ics",
                "icon": "icon-bookmark",
                "private": false
            }
        ],
        "status": "available",
        "teams": [
            "Globe_Team",
            "Globe_Web"
        ]
    }

    **/
getUserLocation

Returns the Backbone Model of the user location.

    var userLocation = this.getUserLocation();
    var latitude = userLocation.get('latitude'); // longitude...
getCurrentLocation

Returns, in a callback, the current location obtained with the browser geolocation API and with Google Maps API.

    this.getCurrentLocation( function(location) {

        // Do something with location...
        // It is an object
        /*
        {
            "latitude": 45.3231,
            "longitude": 9.1321
        }
        */

    });
getConfiguration

Returns an object with specific parts of the configuration. It is called as follows:

 var config = this.getConfiguration();

The result is a JSON object with the following properties:

  • config.isCacheEnabled() : returns true if the cache is enabled.

  • config.language : the current user language

  • config.location : the "location" object configured in the config.json file.

getPersistence

Returns the persistence services that enables to perform cached AJAX call.

 var persistence = this.getPersistence();

An example from the Digg feeds widget:

// Executes an AJAX call to obtain the DIGG feeds.
    persistence.ajax({
        emCache: {
            disabled: !this.getConfiguration().isCacheEnabled(),
            prefix: 'eidosmedia.widget|diggfeeds|',
            ttl: 600,
            safe: true
        },
        url : widgetContext.buildUrl( '/ws/widgets/digg/mostDuggFeed', widgetContext.getAppContext() ),
        data : request,
        success : function(respData) {
            if ( count < respData.length ) {
                respData = respData.splice(0, count);
            }
            response = widgetContext.addDummy(respData, count);

            // Update the widget data with the standard method, as we don't override it.
            widgetContext.updateBaseData( widgetContext.settings, response);
            // Add custom listeners.
            addListeners.call( widgetContext, response );
        },
        error : function(xhr, textStatus, errorThrown) {
            var error = widgetContext
                            .processErrorResponse(xhr, textStatus, errorThrown);
            widgetContext.showError(error);
        }
    });
processErrorResponse, showError

The processErrorResponse method is called to create a useful Javascript object, to be used (often) with the showError method.

The showError method is used to display an error message inside the widget container.

processErrorResponse is called as follows:

var errorObj = this.processErrorResponse( xhr, textStatus, errorThrown );

showError is called as follows:

this.showError( errorObj );

The correct usage for processErrorResponse is inside a jQuery AJAX error callback, as seen in the example:

var widgetContext = this;
// Executes an AJAX call to obtain the DIGG feeds.
$.ajax({
    url: '',
    data: [],
    success : function(respData) {
        // ...
    },
    error : function(xhr, textStatus, errorThrown) {
        var errorObj = widgetContext
                        .processErrorResponse(xhr, textStatus, errorThrown);
        widgetContext.showError( errorObj );
    }
});

showError can be called also without using processErrorResponse, as the example shows:

    this.showError( { message: 'Some error details here' } );
translateWidget

translateWidget translates all the HTML inside the widget container. It uses data-i18n attributes to perform the translation.

this.translateWidget();
getUserData, addUserData, removeUserData, setUserData

This methods allow to get, add, remove, and set specific data inside the user’s profile folder.

Syntax is as follows:

    this.getUserData( name, callbackSettings ); // Obtains the user data.

    this.addUserData( data, name, callbackSettings ); // Add some data to the user's data.

    this.removeUserData( data, name, callbackSettings ); // Remove some data from the user's data.

    this.setUserData( data, name, callbackSettings ); // Set the user data to some value.

In general, the parameters are:

  • data: the data to add, remove, set from the file.

  • name: the name of the file to be stored in the User profile folder, without the extension (e.g. 'recents', 'todolist', …​; it creates a 'recents.json' file into the User profile folder)

  • callbackSettings: a Javascript object with two parameters (success and error, which are both functions), to call the methods asynchronously. If omitted, the call is synchronous.

Examples:

    // assuming that, before, we had a 'var widgetContext = this;'...

    // GET USER DATA

    // Get recents asynchronously (recommended).
    this.getUserData('recents', {
        success: function( recents ) {
            //...
        },
        error: function(xhr, textStatus, errorThrown) {
            var errorObj = widgetContext
                        .processErrorResponse(xhr, textStatus, errorThrown);
            widgetContext.showError( errorObj );
        }
    });

    var recents = this.getUserData('recents'); // Get recents synchronously (not recommended)

    // ADD USER DATA (if the file does not exist, it is created).

    // add a recent to the list (sync).
    this.addUserData( { /* item */ }, 'recents', {
        success: function( newRecents ) {
            // returns the update list of recents
            //...
        },
        error: function(xhr, textStatus, errorThrown) {
            var errorObj = widgetContext
                        .processErrorResponse(xhr, textStatus, errorThrown);
            widgetContext.showError( errorObj );
        }
    });

    // add a recent to the list (async).
    var newRecents = this.addUserData( { /* item */ }, 'recents' );

    // REMOVE USER DATA

    // remove a recent from the list (sync).
    this.removeUserData( { /* item identifier */ }, 'recents', {
        success: function( newRecents ) {
            // returns the update list of recents
            //...
        },
        error: function(xhr, textStatus, errorThrown) {
            var errorObj = widgetContext
                        .processErrorResponse(xhr, textStatus, errorThrown);
            widgetContext.showError( errorObj );
        }
    });

    // add a recent to the list (async).
    var newRecents = this.removeUserData( { /* item */ }, 'recents' );

    // SET USER DATA (overrides the current content with the new one).

    // completely sets the new todo list (sync)
    this.setUserData( [ {}, {}, }{} ], 'todolist', {
        success: function( newTodoList ) {
            // returns the update Todo List
            //...
        },
        error: function(xhr, textStatus, errorThrown) {
            var errorObj = widgetContext
                        .processErrorResponse(xhr, textStatus, errorThrown);
            widgetContext.showError( errorObj );
        }
    });

    // completely sets the new todo list (async).
    var newTodoList = this.setUserData( [ {}, {}, }{} ], 'todolist' );
getPreview

Returns the URL of the server-side generated preview. Useful for Méthode Objects.

var preview = this.getPreview( id ); // e.g. this.getPreview( '0$1.231321421' );
// It returns a valid URL
openPreview

Opens the M&ecute;thode Swing Preview dialog.

this.openPreview( items, options );

The method accepts 2 parameters:

  • items: (object or array), mandatory - a Javascript Object representing the item information returd by query or a folder nativation/ Array including info on the file to load

  • options: (object), not mandatory - a Javascript Object including the preview options.

The options object is a Javascript object with the following properties:

  • loadObject (default is false) - Reloads the item information.

Example:

    this.openPreview( items, { loadObject: true });

HTML file

The HTML template can be as free as desired. The only contraint is that the whole HTML code should be wrapped into a div, such as the following example:

<div>
</div>

The widget can be have a fixed height, a minimum height or can be responsive. This enables the Swing dashboard to adapt according to the screen dimensions.

To have a fixed height widget, specify the height in the style attribute of the div, as follows:

<div style="height:300px;">
<!-- ... -->
</div>

To have a widget with minimum height, specify the min-height in the style attribute of the div, as follows:

<div style="min-height:300px;">
<!-- ... -->
</div>

To have a responsive widget (a widget that occupies the available height), add the class emui-widget-vfill to the div.

<div class="emui-widget-vfill">
<!-- ... -->
</div>

Méthode Swing uses Underscore.JS templating system to enhance the user experience.

See Underscore.JS for further information, or the box below for some useful tips.

Inside the template is then possible to use the Underscore.JS syntax, and use the methods and objects described in the following section.

Available information inside the HTML template

As described above, the HTML template is called with the Underscore.JS template syntax. Inside the HTML template, it is possible to use the following objects:

  • widget: it contains the same methods described in the widgetContext section above. See widgetContext: list of available methods within the widget section for further details on the available methods.

  • settings: it is a Javascript object containing the widget settings as specified in the configuration.

  • data: contains all the data available to the widget( the ones specified in the updateData and loadData method described above ).

  • status: either 'init' or 'loaded', for the first rendering ( before loadData ) and the last one ( after updateData ).

Sample HTML template
<%
    var title = data.title || "";
    title =  '<a href="#explore?query='+ data.id +'" class="emui-link">' + title +'</a>';
%>

<div class="emui-table-gallery emui-widget-vfill">
  <h1>
    <i class="<%=(settings.icon || 'icon-picture')%>"></i><%=title%></h1>
    <div class="list">
    <ul>
        <% _.each(data.items, function( item, index ) {
            var publishedDate = '',
                image = '';
            if (!item.empty) {
              publishedDate = item.publishedDate;
              image = ' style="cursor:pointer;background-image:url(' + widget.getPreview( item.id ) +'); background-size: cover;"  data-preview="image" data-preview-index="' + index + '"';
            }

            var preview = '<div class="image"' + image + '>';
            preview += '<div class="info">' + publishedDate +'</div>';
            preview +='</div>';
         %>
        <li><%=preview%></li>
    <% }); %>
  </ul>
  </div>
</div>

LESS file

Less is a CSS pre-processor, meaning that it extends the CSS language, adding features that allow variables, mixins, functions and many other techniques that allow you to make CSS that is more maintainable, themable and extendable.

LESS finally becomes CSS. Please refer to LESS website for further details.

Variable reference

Méthode Swing provides a set of common variables, as well as a set of predefined colors to maintain the coherence with Méthode Swing color palette.

To include the variable file inside your LESS, insert the following line at the beginning:

@import (reference) "../less/widgets-header.less";

Here follows an excerpt of the available variables:

// -------------------------
@black:                 #000;
@grayDarker:            #080808;
@grayDark:              #999;
@gray:                  #bcbcbc;    // view background
@grayLight:             #d8d8d8;    // body background
@grayLighter:           #e8e8e8;
@grayLighter2:          #f8f8f8;
@grayLightest:          #f8f8f8;
@white:                 #fff;

// Accent colors
// -------------------------
@blue:                  #a5c0dc;
@blueDark:              #4e84bb;
@blueLighter:           lighten(@blueDark, 10%);
@green:                 #77d89a;
@red:                   #9d261d;
@lightRed:              #E24242;
@darkRed:               darken(@lightRed, 10%);
@yellow:                #ffdb80;
@orange:                #ff9f6b;
@pink:                  #c3325f;
@purple:                #7a43b6;

Which can be used as follows:

.selector {
    background-color: @blueDark;
}

Again, please refer to LESS website for further details on how to write LESS files.

How to configure widgets in external folder

Additional widgets may be made available to Swing application by defining one or more nested components in Swing web context in server.xml. (for further details on Tomcat 9.x Resources configuration, please refer to Resources configuration).

This is an example of the external widgets configuration.

<Context docBase="com.eidosmedia.webclient.web-app"
    path="/swing" reloadable="false">
    <Resources
        className="org.apache.catalina.webresources.StandardRoot">
        <PreResources
            className="org.apache.catalina.webresources.DirResourceSet"
            base="/methode/meth01/extension/widgets" readOnly="true"
            webAppMount="/app/widgets" />
    </Resources>
</Context>

Customization

Addition of 3rd parties libs to Swing

In Méthode Swing it is possible to add 3rd-party libraries. Such libraries must be placed under

{SWING-APP}/plugins/libs

All the libraries contained in this folder are loaded separately into Méthode Swing.

The libraries are loaded asynchronously.

It is suggested to place each library under a specific folder, e.g:

{SWING-APP}/plugins/libs/exif/exif.js

Méthode Swing loads ONLY the Javascript (*.js) files in the folder.

Méthode Swing has no control over the 3rd parties lib. So, an external library could break the application.

Please refer to the log to see which libraries have been loaded.

New content creation dialog

With Méthode Swing it is possible to override the default creation dialog.

Registration of a new content creation dialog

To register a new content creation dialog, use the following namespace

eidosmedia.webclient.extensions.newcontent

and its function

register( contentTypes, options )
Table 98. Method information

Method

register

Parameter

Required {Array} contentTypes - The list of contentTypes for which the specified dialog must be used. See Available content types for details. Required {object} options - An object containing the options and the behaviour of the dialog. See Properties of the creation dialog for details.

Returns

{undefined} undefined

Available content types

When registering the dialog, the content type can be chosen among these options:

  • story

  • topic

Properties of the creation dialog

The following paragraphs describe each of the available properties or methods for the custom panel.

title

[Optional] {String} title defines the title of the dialog

Example
{
    // ...
    title: "For whom the bell tolls",
    // ...
}

layout

The layout property is optional and allows to have some control on the dialog dimensions. Available values are :

  • fullWidth - the dialog occupies all the screen width.

  • fullHeight - the dialog occupies all the screen height.

  • fullPage - the dialog occupies all the screen width and height.

If necessary, fullWidth and fullHeight properties could be used in addition with width and height properties.

Example
{
    // ...
    layout: "fullPage"
    // ...
}

width, height

If the layout property is not sufficient for your necessities, it is possible to customize the width and the height of the dialog by specifying the respective property.

Example
{
    // ...
    layout: 'fullWidth',
    height: "425px"
    // In this case we have a wide dialog with fixed height.
    // ...
}

css

If the layout property is not sufficient for your necessities, it is possible to customize all the css properties. It works exactly like jQuery.css method. Please refer to jQuery documentation for further information.

Example
{
    // ...
    css: {
        'fontSize': '12px',
        'width': '423px'
    }
}

dialogType

Dialog Type can be one of the following values.

  • full - the dialog is completely custom. It should be used when referencing an external url for the content creation. See Full custom dialog configuration documentation.

  • contained - the dialog is custom, but interacts with Methode Swing. The result is a modal that contains your custom HTML, but has the "Create" button as part of Swing. See [containeddialog] documentation.

  • template - the dialog is the standard Methode Swing dialog, but the template choice part is custom. See Template custom dialog configuration documentation.

Example
{
    // ...
    dialogType: "full"
    // ...
}

Methods of the creation dialog

postFillForm

Method called when the dialog and its custom html has been loaded. It can be used to add custom listeners to buttons, make some elaborations, etc..

The syntax is the following:

postFillForm( ctx, dialog, extraInfo );
Table 99. Method information

Method

postFillForm

Parameter

{object} ctx - The context object. See Context Object (ctx parameter) for further information.

Parameter

{DOM object} dialog - The DOM reference to the dialog

Parameter

{Object} extraInfo - Further information passed to the dialog. See below for further info.

Returns

undefined

extraInfo parameter is only valued within the "template" dialogType. It is made like this:

{
    templates: [] // list of templates,
    shapes: [] // list of shapes,
    currentInfo: {} // Current choices of the user in the first dialog
    /* An example of current Info is
    {
        "type": "story",
        "team": "Globe_Team",
        "channel": "Globe-Web",
        "edition": null,
        "insert": null,
        "workfolder": "/Globe/Art",
        "issueDate": "20170726",
        "subWorkfolder": null,
        "hasBaseFolders": true,
        "createTopicFrom": false,
        "dataInfo": {
            "type": "EOM::CompoundStory",
            "contentType": "story"
        }
    }
    */
}
Example
// options object...
{
    postFillForm: function( ctx, dialog ) {
        var $dialog = $(dialog);

        var _teams = ctx.getTeams();
        var teamHtml = _.map( _teams, function(t) {
            return '<option>' + t.name + '</option>';
        });
        $dialog.find('[data-field-type="team"]').html( _.map() );
        $('.my-custom-button', dialog ).on('click', function() {
            // do something here
        });
    }
}

getCreationInfo

Method called when the user clicks on the "Create" button. In this method, the dialog should return a JSON object with all the information necessary to call the REST api /object/create.

This will be called in the following circumstances:

  • showCreateButton set to true and dialogType either full or contained and user clicks on the create Button

  • dialogType is set to template

only if the showCreateButton property is true and the dialogType is either full or contained.

The syntax is the following:

getCreationInfo( ctx, dialog, callback );
Table 100. Method information

Method

postFillForm

Parameter

{object} ctx - The context object. See Context Object (ctx parameter) for further information.

Parameter

{DOM object} dialog - The DOM reference to the dialog

Parameter

{Function} callback - The function to be called with the JSON object for the /object/create REST API

Returns

undefined

Example
// options object...
{
    getCreationInfo: function( ctx, dialog, callback ) {
        var $dialog = $(dialog);
        var team = $dialog.find('[data-field-type="team"]').val();
        // ...
        callback({
            team: team,
            channel: channel,
            // ...
        })
    }
}

Full custom dialog configuration

A "full" custom dialog should be used when the customer has an indipendent Object Creation tool available on another website. The "full" dialog has minimal interaction with Swing, and merely opens an external URL in an iframe.

To activate the "full" custom dialog, dialogType property must be valued at full.

Configuration of a proxy

To allow the use of your external url inside Swing, it is necessary to use a Proxy.

Méthode Swing uses proxies to communicate to external services. This is necessary due to the Same-Origin Policy used by modern browsers.

The same-origin policy restricts how a document or script loaded from one origin can interact with a resource from another origin. Same-origin Policy is used as a means to prevent some of the Cross-site Request Forgery attacks.

— Mozilla Developer Network

Proxies can be configured in Méthode Swing.

<!-- other configuration -->
<proxies>
    <proxy>
        <name>[PROXY-NAME]</name> <!-- e.g. resourcespace -->
        <targetUri>http://192.168.203.129/resourcespace/plugins/api_search/</targetUri> <!-- e.g. destination target Uri -->
        <useTemplate>false</useTemplate>
    </proxy>
</proxies>

The useTemplate param allows to use templates to build the URL. The following is an example of url which uses templates.

<proxies>
    <proxy>
        <name>[PROXY-NAME]</name> <!-- e.g. resourcespace -->
        <targetUri>http://192.168.203.129:{_port}</targetUri> <!-- e.g. destination target Uri -->
        <useTemplate>true</useTemplate>
    </proxy>
</proxies>

The new proxed URL must be called with ALL the parameters in its querystring, e.g.

http://swing/[PROXY-NAME]/users/get?_port=8080&id=userID

Note: if the parameter is missing, the URL will return an HTTP 500 error.

Note: due to the nature of the Template Servlet, in this mode it is not possible to pass arrays as querystrings ( i.e. more than one querystring parameter with the same name ).

Configuration of the url

To point to the external url, it is mandatory to configure the url option. Please notice that, considering that you have just configured the correct proxy for your application, the url will be actually a subset of the Swing url.

{
    // ...
    url: 'http://localhost:9797/swing/newcontentproxy/index.html0
    // ...
}

Closing the dialog

The "full" custom dialog actually includes your custom external url inside a Swing modal dialog. Since there is no interaction between it and your code, the only way for the customer to close the Swing dialog is to put the following code inside their application.

// Creation code of your application..
// At the end of it, put the following code
if (window.top.eidosmedia) {
    window.top.eidosmedia.closeCurrentDialog();
}

Accessing the Context Object

The "full" custom dialog actually includes your custom external url inside a Swing modal dialog. Since there is no interaction between it and your code, the only way for the customer to access the Swing context (with all the limitation of being in another iframe), is the following.

// Creation code of your application..
// At the end of it, put the following code
if (window.top.eidosmedia) {
    var CTX = window.top.eidosmedia.getCTX();
    // Get current user...
    var currentUserName = CTX.getCurrentUser().getName();
    // All methods of the CTX are available (such as getChannels, getWorkfolders, getTemplates)
}

Full example

eidosmedia.webclient.extensions.newcontent.register( ['story'], {
    dialogType: 'full',
    title: 'Custom story creation dialog',
    url: 'http://localhost:8080/swing/testcustomcontent/index.html',
    layout: 'fullPage'
});

The code above will let Swing understand that the operation is done.

This will only work if the external url is Proxied inside Swing using the Proxy configuration.

Contained custom dialog configuration

The contained dialog is used in other to insert custom HTML in the New Content Creation dialog of Swing. The contained dialog is wrapped inside Swing, and uses a custom template. Inside the dialog, it is possible to use the Context Object to retrieve important information. See Context Object (ctx parameter) for further information.

The dialog can be configured with additional properties.

template

[Mandatory if dialogType is 'template' or 'contained', useless if dialogType is 'full'] {String} template defines the template of the dialog. The template is placed under the following path:

{SWING}/config/templates/newcontent/
Swing supports Tomcat 9.x, so custom panels can be configured outside {SWING-APP}. See the paragraph How to configure custom dialog in external folder for some tips in how to take advantage of this configuration option.

The template is an HTML file, and there is maximum liberty in its content. Please refer to documentation for further information.

Example

{
    // ...
    template: "mycustomnewcontent.html",
    // ...
}

showCreateButton

{Boolean} showCreateButton if true, the Create button is shown in the Modal footer, and the creation process is managed by Swing by calling the getCreationInfo method when the user clicks on the button.

Example

{
    // ...
    showCreateButton: true,
    // ...
}

Full example

eidosmedia.webclient.extensions.newcontent.register( ['story'], {
    template: 'newcustom.html',
    dialogType: 'contained',
    title: 'Custom story creation dialog',
    layout: 'fullPage',
    showCreateButton: true,
    postFillForm: function(ctx, $dialog) {
        // There are some APIs available to list channels, teams,
        // access the metadata of the user, and so on...
    },
    getCreationInfo: function( ctx, dialog, callback ) {
        var $dialog = $(dialog);
        var team = $dialog.find('[data-field-type="team"]').val();
        // ...
        callback({
            team: team,
            channel: channel,
            // ...
        })
    }
});

Template custom dialog configuration

The template custom dialog overrides the template / shape choice panel inside the standard "New Content" dialog of Swing. Inside the dialog, it is possible to use the Context Object to retrieve important information. See Context Object (ctx parameter) for further information.

The dialog can be configured with additional properties.

template

[Mandatory if dialogType is 'template' or 'contained', useless if dialogType is 'full'] {String} template defines the template of the dialog. The template is placed under the following path:

{SWING}/config/templates/newcontent/
Swing supports Tomcat 9.x, so custom panels can be configured outside {SWING-APP}. See the paragraph How to configure custom dialog in external folder for some tips in how to take advantage of this configuration option.

The template is an HTML file, and there is maximum liberty in its content. Please refer to documentation for further information.

Please notice that, in the template dialog, neither title nor any of the CSS properties (layout, width, height, css) will work.

Please notice that, in the template dialog, the extraInfo parameter in the postFillForm method contains the list of templates given the selected values.

Full example

eidosmedia.webclient.extensions.newcontent.register( ['story'], {
    template: 'customtemplatechoice.html',
    dialogType: 'template',
    postFillForm: function(ctx, $dialog, extraInfo) {
        // There are some APIs available to list channels, teams,
        // access the metadata of the user, and so on...
        var shapes = extraInfo.shapes;
        var templates = extraInfo.templates;
        // Populate elements with that information.
    },
    getCreationInfo: function( ctx, dialog, callback ) {
        var $dialog = $(dialog);
        var template = $dialog.find('[data-field-type="template"]').val();
        // ...
        callback({
            template: template,
            shape: shape,
            // ...
        })
    }
});

HTML Template

Swing provides a sample template inside the following folder.

{SWING}/config/templates/newcontent/

Please use it as a reference. In general, no restriction is given to the HTML code.

Context Object (ctx parameter)

Inside the different methods in the New Content Dialog configuration, the Context Object is available. See Context object documentation for reference.

Furthermore, the New Content Dialog context object has several specific methods available.

getTemplates

Return the list of templates, given some options. See the example for all the options available.

Syntax

ctx.getTemplates( options, callback );

Parameters

Table 101. Parameters

Name

Type

Required

Description

options

{Object}

Required

A set of options to retrieve the templates. See the example for all the options available.

callback

{Function}

Required

A callback function. See Callback definition for further information.

Returns

Table 102. Parameters

Name

Type

Description

data

{Array}

An array with a list of templates

Example

{
    // ...
    postFillForm: function(ctx, dialog, extraInfo) {

        var options = {
            workfolder: '/Globe/Art', // MANDATORY
            type: 'topic' // optional, the template type for filtering.
            // Could be "story", "gallery", "prs", "pkg"
        }

        // Synchronous way...
        try {
            var templates = ctx.getTemplates(options);
        } catch(ex) {
            // manage error here
        }

        // Better way...
        ctx.getTemplates(options, function(err, data) {
            var $dialog = $(dialog);
            if (err) {
                $dialog.find('.error-message').html(err.message).show();
                return;
            }
            // data is an array of templates...
            /* Like this:

            [
              {
                "id": "199$1.0.21025653",
                "name": "bio",
                "type": "EOM::Story",
                "contentType": "story",
                "path": "/SysConfig/Globe/Templates/bio.xml",
                "isDefault": false,
                "dir": "/SysConfig/Globe/Templates",
                "system_attributes": {
                  "workfolder": "",
                  "templateName": "",
                  "summary": "",
                  "wordCount": "",
                  "sugCategory": "",
                  "channel": "",
                  "edition": "",
                  "storyType": "",
                  "productInfo": {
                    "name": "",
                    "issueDate": ""
                  }
                }
              }, ...
            ]
            */
        });
    }
    // ...
}

Note

The method can also be called synchronously, but this usage is not recommended.

getChannels

Return the list of channels, given some options. See the example for all the options available.

Syntax

ctx.getChannels( options, callback );

Parameters

Table 103. Parameters

Name

Type

Required

Description

options

{Object}

Required

A set of options to retrieve the channels. See the example for all the options available.

callback

{Function}

Required

A callback function. See Callback definition for further information.

Returns

Table 104. Parameters

Name

Type

Description

data

{Array}

An array with a list of channels

Example

{
    // ...
    postFillForm: function(ctx, dialog, extraInfo) {

        var options = {
            showTemplates: truem
            showInserts: true,
            showCalendar: true
        }

        // Synchronous way...
        try {
            var channels = ctx.getChannels(options);
        } catch(ex) {
            // manage error here
        }

        // Better way...
        ctx.getChannels(options, function(err, data) {
            var $dialog = $(dialog);
            if (err) {
                $dialog.find('.error-message').html(err.message).show();
                return;
            }
            // data is an array of channels...
            /* Like this:

            [
                  {
                    "id": "199$0",
                    "name": "Globe-Print",
                    "displayName": "Globe-Print",
                    "type": "print",
                    "isDefault": false,
                    "editions": [
                      {
                        "name": "EUROPE",
                        "_default": false
                      },
                      {
                        "name": "USA",
                        "_default": false
                      },
                      {
                        "name": "ASIA",
                        "_default": false
                      }
                    ],
                    "inserts": [
                      "Insert One",
                      "Insert Two"
                    ],
                    "calendar": {
                      //...
                    },
                    "cobaltSite": false
                  }
                ]
            */
        });
    }
    // ...
}

Note

The method can also be called synchronously, but this usage is not recommended.

getTeams

Return the list of teams, given some options. See the example for all the options available.

Syntax

ctx.getTeams( options, callback );

Parameters

Table 105. Parameters

Name

Type

Required

Description

options

{Object}

Required

A set of options to retrieve the teams. See the example for all the options available.

callback

{Function}

Required

A callback function. See Callback definition for further information.

Returns

Table 106. Parameters

Name

Type

Description

data

{Array}

An array with a list of basefolders

Example

{
    // ...
    postFillForm: function(ctx, dialog, extraInfo) {

        var options = {
            showChannels: truem
        }

        // Synchronous way...
        try {
            var teams = ctx.getTeams(options);
        } catch(ex) {
            // manage error here
        }

        // Better way...
        ctx.getTeams(options, function(err, data) {
            var $dialog = $(dialog);
            if (err) {
                $dialog.find('.error-message').html(err.message).show();
                return;
            }
            // data is an array of teams...
        });
    }
    // ...
}

Note

The method can also be called synchronously, but this usage is not recommended.

getWorkfolders

Return the list of workfolders, given some options. See the example for all the options available.

Syntax

ctx.getWorkfolders( options, callback );

Parameters

Table 107. Parameters

Name

Type

Required

Description

options

{Object}

Required

A set of options to retrieve the workfolders. See the example for all the options available.

callback

{Function}

Required

A callback function. See Callback definition for further information.

Returns

Table 108. Parameters

Name

Type

Description

data

{Array}

An array with a list of workfolders

Example

{
    // ...
    postFillForm: function(ctx, dialog, extraInfo) {

        var options = {
            team: 'Globe_Team', // one of them is necessary, according to your requirements
            contentType: 'story'
        }

        // Synchronous way...
        try {
            var workfolders = ctx.getWorkfolders(options);
        } catch(ex) {
            // manage error here
        }

        // getBaseFolder way...
        ctx.getWorkfolders(options, function(err, data) {
            var $dialog = $(dialog);
            if (err) {
                $dialog.find('.error-message').html(err.message).show();
                return;
            }
            // data is an array of workfolders...
        });
    }
    // ...
}

Note

The method can also be called synchronously, but this usage is not recommended.

createDocument

Create a new document with the options passed. The newly created document is automatically opened.

Syntax

ctx.createDocument( options, callback );

Parameters

Table 109. Parameters

Name

Type

Required

Description

options

{Object}

Required

A set of options to create the document. See the details in the box belox.

callback

{Function}

Required

A callback function. See Callback definition for further information.

Returns

Table 110. Parameters

Name

Type

Description

Object

{Object class}

An instance of the Object Class. See Object class methods for further information on the methods available.

Options

The request to the createDocument method is a JSON with the following properties:

  • name: the file name. It must end with .xml for stories or .pkg for topics.

  • template: the path of the source template. It is mandatory

  • team: the base team

  • channel: the document channel

  • insert: the information on inserts

  • edition: the edition

  • issueDate: if available, the issueDate in the format YYYYMMDD.

  • workfolder: the workfolder of the document

  • path: the document path

  • workStatus: optional to specify the status of the document. The workStatus must be a valid status, reachable from the initial status in which the document is created.

  • attributes: optional to specify the metadata of the document

  • newObjOption: a number describing the behaviour in case of files with the same name. Values are the following:

    • 0 - An error is thrown

    • 1 - The new file is automatically renamed

    • 2 - The file is replaced

    • 3 - A new version is created

Example

{
    // ...
    postFillForm: function(ctx, dialog, extraInfo) {
        var _ctx = ctx;
        // With your custom button...

        $(dialog).on('click', '.create-button', function(event) {
            var request = {
                "name": "TestCreateCustom2.xml",
                "template": "/SysConfig/Globe/Templates/story-external.xml",
                "team": "Globe_Team",
                "channel": "Globe-Print",
                "insert": "Insert One",
                "attributes": "<test>true</test>",
                "workStatus": "NewsFlow/Editing",
                "edition": "EUROPE",
                "issueDate": "20170713",
                "workfolder": "/Globe/Art",
                "newObjOption": 2,
                "path": "workfolder:///Globe/Art"
            };
            ctx.createDocument(request, function(err, Obj) {
                if (err) {
                    ctx.showError(err);
                    return;
                }

                // The document is automatically opened
                // But I can still retrieve its info
                var newId = Obj.getId()

            });
        });
    // ...
}

Note

The method cannot be called synchronously.

createTopic

Create a new topic with the structure passed. Please refer to the REST API documentation for further info: Topic Creation documentation. Please notice that in this case the document is NOT opened.

Syntax

ctx.createTopic( options, callback );

Parameters

Table 111. Parameters

Name

Type

Required

Description

options

{Object}

Required

A set of options to create the document. See the details in the box belox.

callback

{Function}

Required

A callback function. See Callback definition for further information.

Returns

Table 112. Parameters

Name

Type

Description

Object

{Object class}

An instance of the Object Class. See Object class methods for further information on the methods available.

Example

{
    // ...
    postFillForm: function(ctx, dialog, extraInfo) {
        var _ctx = ctx;
        // With your custom button...

        $(dialog).on('click', '.create-button', function(event) {
            var request = {
            "containerType":"TOPIC",
            "source":"",
            "attributes": "<test>true</test>",
            "workStatus": "NewsFlow/Editing",
            "path":"/TestTopicPlan/Topic",
            "name":"I giochi a Rio",
            "description":"I Giochi della XXXI...",
            "correlates" : [ { "containerType": "CORRELATES", "source" : "loid:9$2.0.798314409", "correlationType": "RELATED_IMAGE" }],
            "children":[
                {
                    "containerType":"TOPICITEM",
                    "name":"La città di Rio De Janero",
                    "dueDate":"20160501",
                    "size":"2 colonne",
                    "channel":"Globe-Web",
                    "author":"Mario Rossi",
                    "comment":"This is a topic item comment",
                    "priority":"TASK_PRIORITY_NORMAL",
                    "content":"picture",
                    "workFolder":"/Globe/Sport",
                    "assignment":"test,prova1",
                    "asset":"",
                    "correlates" : [ { "containerType": "CORRELATES", "source" : "loid:9$2.0.798137329", "correlationType": "RELATED_IMAGE" }],
                    "children":[
                        {
                            "containerType":"TOPICSUBITEM",
                            "name":"Rio - Foto",
                            "dueDate":"20160501",
                            "size":"2 colonne",
                            "channel":"Globe-Web",
                            "author":"Mario Rossi",
                            "comment":"This is a sub topic item comment",
                            "priority":"TASK_PRIORITY_LOW",
                            "content":"PICTURE",
                            "workFolder":"/Globe/Sport",
                            "assignment":"test,prova1",
                            "asset":{
                                "source":"loid:9$2.0.795001757"
                            }
                        },
                        {
                            "containerType":"DIVIDER",
                            "source":"",
                            "name":"SUBITEM DIVIDER",
                            "font":{
                                "bold":true,
                                "color":"2558080",
                                "faceName":"Tahoma",
                                "italic":"false",
                                "lineThrough":"false",
                                "size":"14",
                                "underline":"true"
                            }
                        },
                        {
                            "containerType":"TOPICSUBITEM",
                            "name":"Rio - Foto",
                            "dueDate":"20160501",
                            "size":"2 colonne",
                            "channel":"Globe-Web",
                            "author":"Mario Rossi",
                            "comment":"This is a sub topic item comment",
                            "priority":"TASK_PRIORITY_LOW",
                            "content":"PICTURE",
                            "workFolder":"/Globe/Sport",
                            "assignment":"test,prova1",
                            "asset":{
                                "source":"loid:9$2.0.795001794"
                            }
                        }
                    ]
                },
                {
                    "containerType":"DIVIDER",
                    "source":"",
                    "name":"SUBTOPIC DIVIDER",
                    "font":{
                        "bold":true,
                        "color":"000000",
                        "faceName":"Tahoma",
                        "italic":"false",
                        "lineThrough":"false",
                        "size":"14",
                        "underline":"true"
                    }
                },
                {
                    "containerType":"TOPICITEM",
                    "name":"Le zone dei giochi",
                    "dueDate":"20160501",
                    "size":"2 colonne",
                    "channel":"Globe-Web",
                    "author":"Mario Rossi",
                    "comment":"This is a topic item comment",
                    "priority":"TASK_PRIORITY_NORMAL",
                    "content":"picture",
                    "workFolder":"/Globe/Sport",
                    "assignment":"test,prova1",
                    "asset":"",
                    "children": []
                }
            ]
        };
            ctx.createTopic(request, function(err, Obj) {
                if (err) {
                    ctx.showError(err);
                    return;
                }

                // The document is automatically opened
                // But I can still retrieve its info
                var newId = Obj.getId()

            });
        });
    // ...
}

Note

The method cannot be called synchronously.

Callback definition

The callback parameter, when specified, is always defined as follows:

// Callback definition
function( err, response ) {
    // This is the "Node.js" callback style.
    // It forces the developer to manage the errors.

    if (err) {
        // Do something...
        return;
    }

    // Real implementation here

}

Obtain a usable Context Object

For several reason, it could be necessary to use the Swing Context Object. In an iframed custom template, the only way to obtain it is to call

window.top.eidosmedia.webclient.app.getCurrentDocumentContext()

which returns an available CTX. Please refer to Context object documentation for further reference.

How to configure custom dialog in external folder

Additional objects may be made available to Swing application by defining one or more nested components in Swing web context in server.xml. (for further details on Tomcat 9.x Resources configuration, please refer to Resources configuration).

This is an example of the external custom panels configuration.

<Context docBase="com.eidosmedia.webclient.web-app"
    path="/swing" reloadable="false">
    <Resources
        className="org.apache.catalina.webresources.StandardRoot">
        <PreResources
            className="org.apache.catalina.webresources.DirResourceSet"
            base="/methode/meth01/extension/newcontent" readOnly="true"
            webAppMount="/config/templates/newcontent" />
    </Resources>
</Context>

Preview extensions

Preview API can be found under the following namespace:

eidosmedia.webclient.extensions.preview

In such namespaces, three methods are available: openPreview, addContentLoader. They are explained in detail in the following paragraphs.

Location of the custom Preview extensions.

In general, all the extensions of Méthode Swing are places under

{SWING-APP}/plugins

So, all the Javascript described in the following sections should be placed under

{SWING-APP}/app/plugins/{EXTENSION-FOLDER}/{EXTENSION-NAME}.js

Do not use the word libs as an extension folder. The libs folder is reserved for loading external libs. See the proper documentation to obtain further info on the topic.

DEBUG MODE: by default, all plugins are aggregated into a single plugins.js file at the Tomcat startup. When creating a plugin, it may be frustrating to restart the Tomcat Server every time a change is made.

To avoid this, set the configuration property debugEnabled to true.

Then, that extension will be loaded on every refresh of the page.

Open preview

To open the preview of an object, you must call the following method:

eidosmedia.webclient.extensions.preview.openPreview( item, options );

The method accepts 2 parameters:

  • item: (object), mandatory - a Javascript Object with item information.

  • options: (object), not mandatory - a Javascript Object including the preview options.

The options object is a Javascript object with the following properties:

  • loadObject (default is false) - Reloads the item information.

Example:

eidosmedia.webclient.extensions.preview.openPreview({ id: '0$1.0.236095983' });

Add custom content

Custom content can be loaded in the lazyLoad part of the preview. To add a custom content, simply call the addCustomContent method as follows:

eidosmedia.webclient.extensions.preview.addCustomContent( name, action );

For example:

eidosmedia.webclient.extensions.preview.addCustomContent( 'mycustomextension', function( ctx, callback ) {
    ctx.activeObject.getMetadata(function(metadata) {
        var videoUrl = $(metadata).find('WireURL').text();
        // We are calling the preview back with the value we want.
        callback( videoUrl );
    });
});

The method has two (mandatory) parameters:

  • name : the name of the custom content

  • action : the function to be called.

The action function has two parameters, ctx, which is the Context Object, and callback, which is the function to call back the preview with the desired value. See Basic structure of the Context Object for further details.

NOTE: to load your custom extension within the preview, simply add the following tag at the beginning of the preview template:

<load id="mycustomextension"/> // Replace mycustomextension with your extension's name

and use the following syntax to call your customextension within the preview:

Result: <%=data.mycustomextension%> // Prints "Result: custom extension! Preview item id: 0$123452324"

Add custom actions

It is possible to add custom actions in preview. To register an action, see how to add a custom command in Swing.

The action will be passed the Context Object as the first parameter. See Basic structure of the Context Object for further details.

To add your custom action within the preview, you need to used the data-emaction parameter as follows:

<button data-emaction="mycustomaction">Click me, I'm useful!</button> // Prompts " You clicked on this custom action! ... "

You can use the loaded information when calling an action. For example, if you want to show and use the userInfo within a custom action, you can do as follows:

<load id="userInfo"/> <!-- Load the user info -->
... <!-- Some awesome preview template -->

<button data-emaction="showcreatorinfo">Click me, I know the document creator!</button>

and, in the plugin folder,

eidosmedia.webclient.commands.add({
    name: 'showcreatorinfo',
    action: function (ctx, params, callbacks) {
        alert( ctx.activeObject.getInfo().userInfo.name + ' is the document creator!' );
    }
});

Uploader extensions

Méthode Swing uploader can be extended with custom behaviour.

Location of the Uploader extensions

In general, all the extensions of Méthode Swing are places under

{SWING-APP}/plugins

So, all the Javascript described in the following sections should be placed under

{SWING-APP}/app/plugins/{EXTENSION-FOLDER}/{EXTENSION-NAME}.js

Do not use the word libs as an extension folder. The libs folder is reserved for loading external libs. See the proper documentation to obtain further info on the topic.

DEBUG MODE: by default, all plugins are aggregated into a single plugins.js file at the Tomcat startup. When creating a plugin, it may be frustrating to restart the Tomcat Server every time a change is made.

To avoid this, set the configuration property debugEnabled to true.

Then, that extension will be loaded on every refresh of the page.

Image data extractor

When uploading files to Swing it’s possible to define a image data extractor function which will be called before the load the default metadata from server.

Registration

To add the image data extractor implementation, it is necessary to register it inside Swing by calling the following Javascript function, inside your Javascript file:

eidosmedia.webclient.extensions.objectpanel.addImageDataExtractor( options );

This register a image data extractor inside the Swing. It accepts one argument as object with these mandatory parameters:

  • "supportedTypes": List of supported types this image extractor is able to process. Can be an array of strings or single string, a default keyword *ALL* can be used if this image extractor support all the image types.

  • "process": The function called to process the image and extract data.

Process function

The process function is called when the image drop action is completed.

function(ctx, file, image, callback) {
    // Extract EXIF information

    // Extract IPTC information

    // Merge both information and return the metadata as XML
    callback( metadata );
}

Parameters

  • "ctx": The ctx is a JSON object which represents the object’s context. Available only in init method. See Basic structure of the Context Object for further details.

  • "file": The file parameter provides information about file, like name, size, ecc.

  • "image": The image parameter represents the image content. and must return the image metadata information (a string in XML format, or a jQuery document) that will be displayed in the object panel.

  • callback: The callback to be called with the updated metadata.

Final Implementation Example

// NOTE. It is necessary to add the EXIF.js library (https://github.com/exif-js/exif-js) under /app/plugins/libs
eidosmedia.webclient.extensions.objectpanel.addImageDataExtractor({
        "supportedTypes": ['JPG', 'JPEG','TIFF','PNG'],
        "process": function(ctx, file, image, callback ) {
            // Current XML
            var metadata = ctx.activeObject.getMetadataXML();
            // Extract EXIF information
            // Extract IPTC information
            EXIF.getData( image, function( info ) {
                var make = EXIF.getTag(this, "Make");
                var model = EXIF.getTag(this, "Model");
                // For example...
                $(metadata).find('Language').html( make + ' ' + model );
                // Merge both information and return the metadata as XML
                callback( metadata );
            })
    }
});

Filename Validation

When uploading files to Swing it’s possible to define a filename validation function which will be called before the actual files are transfered to the server. To add a filename validator, it is necessary to register it inside Swing by calling the following Javascript function, inside your Javascript file:

eidosmedia.webclient.extensions.uploader.validateFilename = function( filename ){}

Parameters

  • "filename": The filename to validate.

The function must the return the new filename.

Implementation Example

eidosmedia.webclient.extensions.uploader.validateFilename = function( filename ) {
    return "VALIDATED_" + filename;
};

Metadata Validation

When uploading files to Swing it’s possible to define a validation function which will be called before the actual files are transfered to the server. To activate this function it’s important to first define the correct validation namespace.

Namespace

Create a javascript file inside the plugin folder defining extending the objectpanel object with a new function called validateMetadata:

eidosmedia.webclient.extensions.objectpanel.validateMetadata = function( ctx, items, callbacks ){}

Parameters

  • "ctx": The ctx is a JSON object which represents the object’s context. Available only in init method. See Basic structure of the Context Object for further details.

  • "items": An array with the objects to be uploaded.

  • "callbacks": An object with success and error functions.

Validate data

Once the validateMetadata function is called it’s possible to cycle through the array of items and for each item work with its XML metadata document.

If all the checks on the XML documents are valid, call the callbacks.success function to proceed with the upload process; otherwise call the callbacks.error.

Callbacks and parameters

When calling the callbacks.error function it’s mandatory to pass two arguments.

callbacks.error(
    { msgError: 'General Error Message'},
    [{id: 'picId', msgError:'Specific Error Message'}]
 );

The first parameter is an object with the general error message to display after the validation. The second is the items array we received as input in the validateMetadata function but modified with a msgError property for those items (and only for those items) which failed validation.

Final Implementation Example

eidosmedia.webclient.extensions.objectpanel.validateMetadata = function(ctx, items, callbacks){

    var generalValidationOK = true;
    var itemsWithError = [];

    $(items).each(function(i, el){

        var xmlMetadataDoc = el.data.metadata;
        // call external validation method
        var validationOK = _callMyValidationMethod(xmlMetadataDoc);

        if ( !validationOK ){
            generalValidationOK = false;
            el.msgError = 'This item metadata is not valid!'
            itemsWithError.push( el );
        }
    });

    if ( generalValidationOK ){
        callbacks.success();

    } else {

        callbacks.error(
                {msgError: 'Error Message'},
                itemsWithError
        );
        generalValidationOK = true;
    }
};

Import URL: preview and import information.

With Swing it is possible to drag external URLd. Since there can’t be any control on the content of the URL, Swing tries to propose a generic preview, consisting in an iframe pointing to the specific URL.

Then, when trying to import the content, three standard cases may apply:

  • the content is recognized as an image. This will be imported as an image into the EOMDB.

  • the content is recognized as an HTML file. This will be imported as a URL into the EOMDB.

  • the content is not recognized as one of the above. This will be imported but its well-functioning is not guaranteed.

So, Swing allows to specify custom information to provide a more precise response according to the URL passed. It is possible to provide the preview information and the import information. A common use for this possibility is to perform regular expressions and provide additional information according to the url.

Specify preview information

Following the same guidelines of the other extensions, it is possible to specify a custom getPreviewInfoURL behaviour, registering a new uploader extension.

eidosmedia.webclient.extensions.uploader.add('getPreviewInfoURL', function( url, token, getConnectionURL ) { /* ... */ }

The parameters passed are:

  • the url.

  • the Swing application token.

  • the checkConnectionURL, an URL to be called to verify whether the token is valid.

The function must return a oEmbed-like json response, as the following example:

{
    //"version": "1.0",
    "type": "video",
    "importType": "video",
    //"provider_name": "YouTube",
    //"provider_url": "http://youtube.com/",
    //"width": 425,
    //"height": 344,
    //"title": "Amazing Nintendo Facts",
    //"author_name": "ZackScott",
    //"author_url": "http://www.youtube.com/user/ZackScott",
    "html":
        "<object width=\"425\" height=\"344\">
            <param name=\"movie\" value=\"http://www.youtube.com/v/M3r2XDceM6A&fs=1\"></param>
            <param name=\"allowFullScreen\" value=\"true\"></param>
            <param name=\"allowscriptaccess\" value=\"always\"></param>
            <embed src=\"http://www.youtube.com/v/M3r2XDceM6A&fs=1\"
                type=\"application/x-shockwave-flash\" width=\"425\" height=\"344\"
                allowscriptaccess=\"always\" allowfullscreen=\"true\"></embed>
        </object>"
}

What really matters here is the html attribute. This will be used by the Swing preview to show the content of the imported URL. Other parameters are, at the moment, ignored.

Another important attribute is the importType. It can specified either here or in the getImportInfoURL method. Please check the method in the following paragraph.

Specify import information

Following the same guidelines of the other extensions, it is possible to specify a custom getImportInfoURL behaviour, registering a new uploader extension.

eidosmedia.webclient.extensions.uploader.add('getImportInfoURL', function( url, token, getConnectionURL ) { /* ... */ }

The parameters passed are:

  • the url.

  • the Swing application token.

  • the checkConnectionURL, an URL to be called to verify whether the token is valid.

The function must return a Javascript, as the following example

{
    "importType": "picture",
    "filename": "someSpecificFilename"
}

If importType is specified, Swing will know better how to save the content. Otherwise, it will try to check the type attribute and, if no information is provided, the contentType of the url.

It is also possible to "force" a filename. This is especially useful in case of url without extension (e.g. services that return images). In that case, it would be better to specify a valid filename.

If the type is valued as text/html ( the basic contentType of an HTML page ), the content will be imported as URL. Available importTypes are: picture,story,gallery,dwp,dwc,video,audio. This will determine the corresponding workfolder in which the content will be saved.

getPreviewInfoURL and getImportInfoURL methods are optional. If not available, Swing will try to use the oEmbed protocol if available, and an internal preview system if the oEmbed protocol is missing. In that case, the importType will be guessed by the contentType of the URL response, and the preview will likely be an iframe pointing to the URL.

Final Implementation Example

eidosmedia.webclient.extensions.uploader.add('getPreviewInfoURL', function( url, token, checkConnectionURL ) {
    if ( url.match(/mycustomdamextension/ ) {
        return {
            "type": "image",
            "importType": "picture",
            "html":
            "<object width=\"425\" height=\"344\">
                <param name=\"movie\" value=\"http://www.youtube.com/v/M3r2XDceM6A&fs=1\"></param>
                <param name=\"allowFullScreen\" value=\"true\"></param>
                <param name=\"allowscriptaccess\" value=\"always\"></param>
                <embed src=\"http://www.youtube.com/v/M3r2XDceM6A&fs=1\"
                    type=\"application/x-shockwave-flash\" width=\"425\" height=\"344\"
                    allowscriptaccess=\"always\" allowfullscreen=\"true\"></embed>
            </object>"
        }
    } else {
        /* RETURNING NULL OR A FALSY VALUE OR AN OBJECT WITHOUT THE 'HTML' PROPERTY
           WILL RESULT IN SWING USING ITS STANDARD PREVIEW MECHANISM */
        return null;
    }
});

/* Please notice that, having specified the importType inside the
   getPreviewInfoURL method, the following method implementation is redundant */
eidosmedia.webclient.extensions.uploader.add('getImportInfoURL', function( url, token, checkConnectionURL ) {
    if ( url.match(/mycustomdamextension/ ) {
        return {
            "importType": "picture"
        }
    } else {
        /* RETURNING NULL OR A FALSY VALUE OR AN OBJECT WITHOUT THE 'HTML' PROPERTY
           WILL RESULT IN SWING USING ITS STANDARD MECHANISM */
        return null;
    }
});

Custom URL handling

With Méthode Swing it’s possible to add manipulation to a custom url.

Registration of a custom manipulation

To register a custom manipulation, use the following namespace

eidosmedia.webclient.extensions.manageCustomUrl

and its function

register( options )
Table 113. Method information

Method

register

Parameter

Required {object} options - An object containing more than one function that will be invoked to perform the custom manipulation.

Supported functions:

  • "handleUrl": Handle the custom url manipulation according to the area type

  • "getPreviewInfo": Handle the custom url to get preview info according to the area type

Returns

{undefined} undefined

The following is a very simple registration for a custom manipulation.

(function(){

    eidosmedia.webclient.extensions.manageCustomUrl.register({'handleUrl': function( data, ctx, callback ) {

            /**
             * Save the current url
             */
            var uri = data.uri;

            /**
             * Save the handle drop flag
             */
            var handleDrop = false;

            try {

                /**
                 * The area object it's useful to verify if the call come form the editor of the story
                 */
                var area = ctx.area;
                if (area.getType() == 'editor' && area.getName() == 'story') {

                    /**
                     * In our example we decide to handle only the url of eidosmedia site
                     * Change the if statement according to your needs
                     */
                    if (uri == 'https://www.eidosmedia.com/') {
                        var contentTitle = 'Eidosmedia link site';
                        handleDrop = true;

                        /**
                         * Prepare the xml to insert in the current active document context
                         */
                        var xml = '<a href="' + uri + '">' + contentTitle + '</a>';
                        ctx.activeDocument.insertXml(xml);
                    }
                }

                /**
                 * notify a callback, if specified, only if the drop has not been handled so Swing can manage
                 * the url in the standard way
                 */
                if (!handleDrop && callback) {
                    callback(data);
                }

            } catch (ex) {
                console.log("manageCustomUrl-handleUrl custom manipulation exception caught", ex);

                /**
                 * notify a callback, if specified, only if the drop has not been handled so Swing can manage
                 * the url in the standard way
                 */
                if (!handleDrop && callback) {
                    callback(data);
                }
            }
    }, 'getPreviewInfo': function( data, ctx ) {

        /**
         * Save the current url
         */
        var uri = data.uri;
        var currentXml = data.xml;
        var previewInfo;

        try {

            /**
             * The area object it's useful to verify if the call come form the editor of the story
             */
            var area = ctx.area;
            if (area.getType() == 'editor' && area.getName() == 'story') {

                /**
                 * In our example we decide to handle only the url of eidosmedia site
                 * Change the if statement according to your needs
                 */
                if (uri == 'https://www.eidosmedia.com/') {

                    var path = ''; // path of an image in the eomdb;
                    var customIcon = 'fa fa-exclamation-circle'; // the custom icon to use
                    previewInfo = {'path': path, 'icon': customIcon}
                }
            }

        } catch (ex) {
            console.log("manageCustomUrl-getPreviewInfo custom manipulation exception caught", ex);

        }

        return previewInfo;

    }});
})();
  • "handleUrl" Must have three parameters. The first one contains info about the url to handle. The second one is the context where the url has to be handle. The third one it’s a callback that must be invoked if the url doesn’t need custom manipulation, in this case Swing can proceed to handle the url in the standard way.

  • "getPreviewInfo" Must have TWO parameters. The first one contains info about the url to handle. The second one is the context where the url has to be handle. The method must return the preview info.

Wrap all the custom manipulation with a try…​catch block to allow Swing to handle the url if something goes wrong.

Popovers

In Methode Swing it is possible to customize some popovers available in the application

Usage Ticket Popover

Usage Ticket popover is located under the following path:

/{swing-app-folder}/app/templatesUI/popinfo/usageticket-popinfo.html

Méthode Swing uses Underscore.JS templating system to enhance the user experience.

See Underscore.JS for further information, or the box below for some useful tips.

The template is rendered with a main object:

  • data, which is an object with a single property, usageTickets, which contains all the usage tickets.

Custom OEmbed Providers

In Methode Swing it is possible to configure custom oembed providers.

Prepare the plugin to add custom oembed providers

In general, all the extensions of Méthode Swing are places under

{SWING-APP}/plugins

So, all the Javascript described in the following sections should be placed under

{SWING-APP}/app/plugins/{EXTENSION-FOLDER}/{EXTENSION-NAME}.js

Do not use the word libs as an extension folder. The libs folder is reserved for loading external libs. See the proper documentation to obtain further info on the topic.

DEBUG MODE: by default, all plugins are aggregated into a single plugins.js file at the Tomcat startup. When creating a plugin, it may be frustrating to restart the Tomcat Server every time a change is made.

To avoid this, set the configuration property debugEnabled to true.

Then, that extension will be loaded on every refresh of the page.

Add a custom oembed provider

In order to add a custom oembed provider to Methode Swing, it is necessary to use the following namespace:

eidosmedia.webclient.extensions.oembedProviders

The syntax is the following:

eidosmedia.webclient.extensions.oembedProviders.add((customOmbedProviders);

Where:

  • customOmbedProviders [array] is a list of custom oembed providers (oembed provider object) to add to the already available ones.

EmbedProviderObject are defined in this way:

  • name [string] is the name of the custom oembed provider.

  • type [string] [photo | video | rich ] - is the type of the oembed provider.

  • urlschemearray [array] a list of regular expression to matxh the custom url oembed provider.

  • apiendpoint [string] an endpoint url if requetsed by the custom oembed provider. It can be null.

  • extraSettings [object] extra setting needed by the custom oembed provider. It can be null.

It’s possible to configure an oembed provider to use a swing proxy. In this case the apiendpoint value must be the same of the proxy name and the attribute useSwingProxy of the extraSettings object must be set to true, the attribute dataType must be set according to the return value of the embed service that who made the extension has decided to integrate. In general it is set to the json value.

Example

eidosmedia.webclient.extensions.oembedProviders.add(
    [
        {
            name: 'scmptv',
            type: 'video',
            urlschemearray: ['widgets\\.scmp\\.com/video/.+'],
            apiendpoint: null,
            extraSettings: {
                templateRegex: /.*id=(\w+).*/,
                template: '<iframe src="http://widgets.scmp.com/video/video_iframe.php?id=$1" width="100%" allowfullscreen="true" allowscriptaccess="always"' +
                'scrolling="no" frameborder="0" style="max-height: none !important; height: 540px;"></iframe>',
                nocache: 1
            }
        },

        // custom oembed provider that use swing proxy
        {
            name: 'myvideoplace',
            type: 'video',
            urlschemearray: ["myvideoplace\\.tv/.+"],
            apiendpoint: 'mvp', // same of the proxy name
            extraSettings: {
                dataType: 'json',
                useSwingProxy: true
            }

        }
    ]
);

Custom Version Compare handling

With Méthode Swing it’s possible to add manipulation to version compare.

Registration of a custom version compare handling

To register a custom version compare manipulation, use the following namespace

eidosmedia.webclient.extensions.versionCompare

and its function

register( options )
Table 114. Method information

Method

register

Parameter

Required {object} options - An object containing one function that will be invoked to perform the version compare manipulation.

Supported functions:

  • "selectVersion": Select a version number among the avilable ones

Returns

{undefined} undefined

The following is a very simple registration for a version compare manipulation.

(function(){

    eidosmedia.webclient.extensions.versionCompare.register({'selectVersion': ( versions ) => {

            let versionNumber;

            try {

                /**
                 * Select the version number to compare
                 */
                versionNumber = versions[1].version_number;

            } catch (ex) {
                console.log("custom select version exception caught", ex);
            }

            return {versionNumber};
    }});
})();
  • "selectVersion" Must have one parameters. The parameter is mandatory and contains the available versions.

Wrap all the custom manipulation with a try…​catch block to allow Swing to work correctly if something goes wrong.

Styling

Addition of external CSS to Swing

In Méthode Swing it is possible to add external CSS files. Such files must be placed under

{SWING-APP}/plugins/assets

All the files contained in this folder are loaded asynchronously into Méthode Swing.

It is suggested to place each library under a specific folder, e.g:

{SWING-APP}/plugins/assets/font-test/font-test.css

By default, these CSS are loaded only at application level as "assets.css". To load and apply the CSS inside the editor context you must put the following line

  • /* @editor */ - for Story Editor

  • /* @dwpeditor */ - for DWP Editor

as the first line of the CSS. Only the CSS files with editors directive (@editor or @dwpeditor) are applied inside the editors, all CSS files with application directive (@app) or without directive are excluded.

Méthode Swing loads ONLY the CSS (*.css) files in the folder. Of course, if the CSS file refer to other files (such as fonts or background images) these will be working properly.

Méthode Swing has no control over the CSS. So, an external CSS could break the application layout.

Please refer to the log to see which libraries have been loaded.

Context Menu

In Methode Swing it is possible to configure context menus in the Explorer area

Prepare the plugin to add menus and action

In general, all the extensions of Méthode Swing are places under

{SWING-APP}/plugins

So, all the Javascript described in the following sections should be placed under

{SWING-APP}/app/plugins/{EXTENSION-FOLDER}/{EXTENSION-NAME}.js

Do not use the word libs as an extension folder. The libs folder is reserved for loading external libs. See the proper documentation to obtain further info on the topic.

DEBUG MODE: by default, all plugins are aggregated into a single plugins.js file at the Tomcat startup. When creating a plugin, it may be frustrating to restart the Tomcat Server every time a change is made.

To avoid this, set the configuration property debugEnabled to true.

Then, that extension will be loaded on every refresh of the page.

Add a context menu

In order to add a custom context menu to Methode Swing, it is necessary to use the following namespace:

eidosmedia.webclient.extensions.contextMenu

The syntax is the following:

eidosmedia.webclient.extensions.contextMenu.add(name, options);

Where:

  • name [string] is the name of the custom menu, alias a unique identifier.

  • options [optional, object] is a Javascript object containing the properties of the custom actions.

The options parameter can be omitted. In this case, Swing will use the previously registered command with the same name property. See example below.

If options is specified, it must follow the guidelines of a generic Swing command. See Commands documentation for further information.

Example

eidosmedia.webclient.commands.add({
    name: 'complex',
    label: 'Perform some complex operation...',
    isActive: function(ctx) {
        return ctx.activeObject.getType() === 'Image';
    },
    action: function( ctx, params, callback ) {
        alert('Performing action on ' + ctx.activeObject.getName());
    }
});

eidosmedia.webclient.extensions.contextMenu.add('complex');

Topics Calendar Filters

Methode Swing allows to customize the filter used inside the Topics Calendar view

Configure a custom filter

Filters are made of an HTML template and a related JS file.

Default templates are put in the base folder

{SWING-APP}/config/templates/topiclist

Filters can be configured outside {SWING-APP}. See the paragraph How to configure topic filters in external folder for some tips in how to take advantage of this new configuration option.

Topic List Templates are based on the same algorithm of Swing Custom Search Filter, with few minor differences, listed below. Please refer to the Custom search filter documentation for a detailed explanation of the filter inner workings.

Configure the JS file

To add a filter, the JS should be structured as follows.

eidosmedia.webclient.topicList.addFilter( options );

In detail, the options are:

eidosmedia.webclient.topicList.addFilter({
    // @property "template": {string} - the path to the filter template. Path is relative to the 'config/topiclist/' folder.
    // @required
    template: 'topiclistfilter.html',

    /**
     * Function called after loading the template, but BEFORE loading the filter
     * use it to add listeners.
     * @param {ContextObject}
     * @param {Object} options - the a list of options.
     * Please see below for the list of options.
     */
    onLoad: function( ctx, options ) {

    },

    /**
     * Function called after having executed the query.
     * @param {ContextObject}
     * @param {Object} - filterConditions: an object containing all the conditions obtained from the query
     * @param {Array} results - the results of the query
     * @param {Function} callback - the option to call after processing the result.
     * Please see below for the list of options.
     */
    processResults: function( ctx, filterConditions, results, callback ) {
       /*
        Here it is possible to process results,
        elaborate the custom conditions, etc.
        */
    }
});

Each parameter is detailed below.

template

This property defines the template for the filter.

{
    // ...
    template: "test.html",
    // ...
}

The path of the template starts from the topiclist/ folder.

onLoad

onLoad is a function called when the filter is loaded for the first time. This function is called AFTER the DOM has been filled, so that all the DOM elements of the filter are available. It can be used to add custom listeners for events.

It is called with 2 parameters:

  • ctx: the Context Object. See Context Object documentation for further details. In this case, though, the context object has neither activeObject nor selection, and only contains information on the Swing area.

  • options. It is a Javascript Object with a few properties. See code below for further details.

{
    options: {
        panel: DOMObject, // the DOM reference to the filter container
        data: {
            groups: [], // Array of groups
            topicListCfg: {}, // Topic list configuration as set in Swing main configuration,
            userChannels: [], // Array of channels for the user
            channels: [], // List of all the available channels
            workflows: [], // List of all the workflow steps available
        }
    }
}
{
    onLoad: function( ctx, options ) {
        var $panel = $(options.panel);
        // Put your code here...
    }
}

Return value of the function is not important.

processResults

The processResults function is called whenever the filter is executed. It is possible to elaborate the results as preferred.

It is called with a few parameters:

( ctx, filterConditions, results, callback )

  • ctx: the Context Object. See Context Object documentation for further details. In this case, though, the context object has neither activeObj nor selection, and only contains information on the Swing area.

  • filterConditions: a list of all the conditions set in the filter. It is an array of all the data-xvalue and data-custom-xvalue available in the filter.

  • results: the list of results of the query

  • callback: the callback to be called with the processed results

{
    // An example used to filter for "Standing Objects"
    processResults: function( ctx, filterConditions, results, callback ) {
        // console.log( filterConditions );
        var ownerTeamFilter = _.findWhere(filterConditions, { conditionKeyword: 'ownerteam' });
        if (ownerTeamFilter && ownerTeamFilter.conditionValue) {
            ownerTeamFilter = ownerTeamFilter.conditionValue.split(',');
            if (ownerTeamFilter.length) {
                var _groups = _.filter(ownerTeamFilter, function( tp ) { return tp.indexOf('-GROUP') > -1; });
                _groups = _.map( _groups, function( gp ) { return gp.replace('-GROUP', ''); } )
                results = _.filter( results, function(tp) {
                    if ( tp.virtual_attributes_json && tp.virtual_attributes_json.va && tp.virtual_attributes_json.va.oteam ) {
                        return _groups.indexOf( tp.virtual_attributes_json.va.oteam ) > -1;
                    }
                    return false;
                });
            }
        }
        callback( results );
    }
}

Configure the HTML File

While creating the HTML, the same mechanism as the Search Filter creation can be used. Both filters share the same keywords, same attributes and operators.

Please refer to the HTML and Keywords reference in the Filter documentation for further information.

How to configure topic filters in external folder

Additional topic filters may be made available to Swing application by defining one or more nested components in Swing web context in server.xml. (for further details on Tomcat 9.x Resources configuration, please refer to Resources configuration).

This is an example of the external filters configuration.

<Context docBase="com.eidosmedia.webclient.web-app"
    path="/swing" reloadable="false">
    <Resources
        className="org.apache.catalina.webresources.StandardRoot">
        <PreResources
            className="org.apache.catalina.webresources.DirResourceSet"
            base="/methode/meth01/extension/topicfilters" readOnly="true"
            webAppMount="/config/templates/topiclist" />
    </Resources>
</Context>

Searching

Search Configuration

Méthode Swing Search is an important component of Swing. It allows to perform searches in different domains (EOMDB, Web…​) and it is highly extendable.

To completely disable the search, simply add "disabled": true to the configuration.

<search>
    <disabled>true</disabled>
</search>

To disable the search preview results (so that all query are viewed directly in the Explorer area, if it is available), simply add "disableResultsPreview": true to the configuration.

NOTE: please notice that it works only for EOMDB searches. Other searches will be previewed as always.

<search>
    <disableResultsPreview>true</disableResultsPreview>
</search>

It is possible to define the click action performed on resultset items by configuring the itemClickAction property.

Allowed values are:

  • preview - click on item displays the preview of the item. (Default)

  • select - click on item selects item. Multi-selection is available. It enables drag’n’drop of single or multiple items to any editor, where allowed. Double click performs item preview.

<search>
    <itemClickAction>select</itemClickAction>
</search>

It is possible to define a fixed set of "custom queries" available in the top-left dropdown. This offer a practical shortcut to the most common used queries.

It is also possible to choose whether to execute the custom queries automatically or not, with the executeQueryAutomatically parameter.

<search>
    <customQueries>
        <customQuery>
            <id>199$0.1321231231</id>
            <name>First query</name>
        </customQuery>
        <customQuery>
            <id>/Globe/Query/MY_QUERY</id>
            <name>Second query (with path)</name>
        </customQuery>
    </customQueries>
    <executeQueryAutomatically>false</executeQueryAutomatically>
</search>

It is possible to insert the query path. Use the id property even in case of path.

Column catalog configuration

Inside the search configuration, for EOMDB domains, it is possible to configure multiple column catalogs for the results shown in the Swing Explorer View.

<search>
    <!-- ... -->
    <allowMultipleColumnCfg>true</allowMultipleColumnCfg>
    <defaultColumnCfg>methodeResultSetList</defaultColumnCfg>
    <catalogs>
        <catalog>methodeResultSetList</catalog>
        <catalog>methodeResultSetThumbnails</catalog>
        <catalog>SwingResultSet</catalog>
    </catalogs>
</search>
  • allowMultipleColumnCfg (true / false) - If true, the "Explore" button in the search is a dropdown and lists all the configured column catalogs.

  • defaultColumnCfg ("methodeResultSetList") - This is the column catalog used when the user clicks on the "Explore" button. Beware that each domain can override the default by providing a different defaultColumnCfg

  • catalogs a list of available columnCatalog names that will appear in the dropdown. Beware that each domain can override the default by providing a different set of catalogs.

  • databaseIndexes - The databaseIndexes property gives the user the possibility to specify the default index in which the default-folder filter. See Custom filters documentation for further info on the default-folder filter. Configuration of databaseIndexes property should be as follows:

<search>
    <!-- ... -->
    <databaseIndexes>
        <databaseIndex>
            <name>Editorial</name>
            <index>Production@meth01_eomjse1;Wires@meth01_eomjse1</index>
        </databaseIndex>
        <databaseIndex>
            <name>Archive</name>
            <index>Archive@meth02_eomjse1</index>
        </databaseIndex>
    </databaseIndexes>
</search>

Domain configuration

As far as the Search configuration is concerned, for the parameters found under the <domain> key, no merge will be applied and the hierarchy to be respected is the usual one:
User > Group > SysConfig

Search configuration is included within a single search object:

<search>
    <domains>
        <domain>...</domain>
        <domain>...</domain>
        <domain>...</domain>
    </domains>

It is possible to search within different search domains. Supported domains are:

  • EOMDB - internal database search

  • Web - search on different services ( their availability depends on the APIs. Support for the API may drop without warning )

  • Contacts - search within Méthode users.

  • Pinboard - search within the Pinboard.

  • Social (if available) - search in social networks.

  • Custom - custom search domains specified by the user.

Only EOMDB and Web domains are customizable.

Disable specific domains - It is possible to disable the different searches by setting the specific privileges ( "_PadmaPriv_enableSearchExternalSource", "_PadmaPriv_enableSearchContacts", "_PadmaPriv_enableTemporaryPinboard" )

General domain properties

Each domain has the following properties:

Table 115. Domain configuration: properties
Name Description Values

name

Domain name. Must be unique and without spaces.

type

Domain type. NOTE: there can be multiple EOMDB domains, while it is suggested to have a unique Web domain.

eomdb / web

connectionName

Used only by EOMDB domains, indicates the connection name configured in the MRAS configuration. For backward compatibility the previous <repositoryId> and <repositoryName> is correctly read anyway.

defaultColumnCfg

Used to override the default search defaultColumnCfg property.

showMetadata

Used only by EOMDB domains, if true the results will also contain the "attributes" property. Be aware that this will slow down the search.

false

hidden

If true, domain is hidden from the search

true / false

defaultFor

If specified, in case of specific searches ( e.g. search from image or video placeholder ), the domain with the corresponding searchType will be selected.

image / video

groupResults

For EOMDB domains, if true the results are grouped and sorted according to the type

true / false

label

The domain label

icon

Default icon for the domain

view

For EOMDB databases, it is possible to specify a custom ColumnCatalog. By default, the column catalog is "SwingResultSet" (internal to Swing). See 5. Usage and creation of result templates to understand how the information is used.

itemsPerPage

Number of item per page (quicksearch)

8

disablePagination

If true, no pages are created

true / false

quicksearchLimit

Max number of results in quicksearch mode.

100

openFilter

If a filter is available and the value is true, the filter is opened by default.

false

conditions

It is an array of additional conditions (filters) to be applied to the search. See Conditions configuration for further details.

Default conditions (filter by type)

archives

It is an array of indexes, through which the user can choose. See Indexes configuration (archives) for further details.

Indexes configuration (archives)

Each of them can be configured as follows:

Table 116. Index configuration: properties
Name Description

label

Name used in the search to visually identify the index

archive

The index name. It can contain multiple indexes, separated by a ";" (in this case, all indexes MUST be on the same repository)

repName

The EOMDB repository name. Mandatory for all archives

prefix

If specified, every search with that archive selected will be prepended with this prefix. E.g. in web searches, it is possible to specify "site:www.yoursite.com" as a prefix in order to force the search only on that website.

isDefault

Is true, the index is selected by default

Sample archive configuration
<archives>
    <archive>
        <label>Editorial</label>
        <archive>production@meth01_eomse1</archive>
        <repName>Editorial</repName>
        <isDefault>true</isDefault>
        <visible>true</visible>
        <enabled>true</enabled>
    </archive>
    <archive>
        <label>Wires</label>
        <archive>ap@eomjse3_meth01;nyt@eomjse3_meth01</archive>
        <repName>Wires</repName>
        <isDefault>false</isDefault>
        <visible>true</visible>
        <enabled>true</enabled>
    </archive>
    <archive>
        <label>Archive</label>
        <archive>archive@meth01_eomjse1</archive>
        <repName>Archive</repName>
        <isDefault>true</isDefault>
        <visible>true</visible>
        <enabled>true</enabled>
    </archive>
</archives>

In case of custom domains (created with a Plugin), it is possible to specify the archives as a function written in the following way:

{
    // ...
    archives: function( ctx, callback ) {
        // Perform same operations...
        callback( [
             { "name": "production", "label": "Editorial",
               "archive": "production@meth01_eomse1",
               "isDefault": true, "visible": true, "enabled": true },
             { "name": "globecms", "label": "GlobeCMS",
               "archive": "globecms@meth01_eomse1",
               "isDefault": false, "visible": true, "enabled": true },
             { "name": "wires", "label": "Wires",
               "archive": "wires@meth01_eomse1",
               "isDefault": false, "visible": true, "enabled": true },
             { "name": "archive", "label": "Archive",
               "archive": "archive@meth01_eomse1",
               "isDefault": false, "visible": true, "enabled": true },
             { "name": "configuration", "label": "Configuration",
               "archive": "configuration@meth01_eomse1",
               "isDefault": false, "visible": true, "enabled": true }
        ]);
    }
}

Conditions configuration

Conditions represent additional specifications added to a query. That is, if the user choose a condition whose value is type:EOM::Story, the results are limited to the EOMDB stories. (In a way, they can be considered as filters).

There are two different types of conditions:

  • default conditions - Such conditions are added in any search that the user performs. They can be seen as permanent filters, or are conditions that always limit or specify the searches that the user makes.

  • alternative conditions - Such conditions, instead, appear in the top right part of the quicksearch form (see picture below). The user can choose among them, to filter the results of a search.

Default conditions

Default conditions should be configured as follows:

<conditions>
    <condition>
        <isDefault>true</isDefault>
        <value>owner:myself</value>
    </condition>
    <condition>
        <isDefault>true</isDefault>
        <value>modified_lastdays:10</value>
    </condition>
    <condition>
        <isDefault>true</isDefault>
        <value>sort:ObjectInfo/modified;D</value>
    </condition>
    <condition>
        <isDefault>true</isDefault>
        <value>metadata:"My/Path=my value"</value>
    </condition>
</conditions>

This sample configuration limits all EOMDB quick-searches to the files whose owner is the current user, and that have been modified in the last 10 days. So, default conditions must have isDefault parameter set to true, and the value parameter set to the condition, as visible in availableKeywords paragraph.

All the conditions are put with an AND operator, but within a condition it is possible to specify OR operators. In this case, please put the condition within parenthesis.

Example:

<conditions>
    <condition>
        <!-- other properties here -->
        <value>(owner:myself OR owner:johndoe)</value>
    </condition>
</conditions>
Conditions are domain-specific. The described conditions are available only for EOMDB domains. For custom domains, it is you that have to code the behaviour.
Alternative conditions

They are configured as follows:

<conditions>
    <condition>
        <label>Story</label>
        <icon>emui-icon-newspaper</icon>
        <value>type:eom::story</value>
    </condition>
    <!-- Other conditions -->
</conditions>

The same keyword reference table ( availableKeywords ) can be used also for the Alternative conditions.

Domains: sample configuration

<search>
    <domains>
        <domain>
            <name>eomdblocal</name>
            <type>eomdb</type>
            <label>Local archive</label>
            <icon>emui-icon-database</icon>
            <defaultFor>image</defaultFor>
            <itemsPerPage>8</itemsPerPage>
            <groupResults>false</groupResults>
            <quicksearchLimit>48</quicksearchLimit>
            <useExternalSuggestion>true</useExternalSuggestion>
            <externalSuggestionURL>http://suggestqueries.google.com/complete/search?q={data.value}&client=firefox&callback=?</externalSuggestionURL>
            <conditions>
                <!-- this is a default condition -->
                <condition>
                    <isDefault>true</isDefault>
                    <value>modified_lastdays:10</value>
                </condition>
                <!-- these are the alternative (variable) condition -->
                <condition>
                    <label>Free search</label>
                    <labelkey>search.types.freesearch</labelkey>
                    <icon>icon-search</icon>
                    <value>type:</value>
                </condition>
                <condition>
                    <label>Story</label>
                    <labelkey>search.types.story</labelkey>
                    <icon>emui-icon-newspaper</icon>
                    <value>type:eom::story</value>
                </condition>
                <condition>
                    <label>WireStory</label>
                    <labelkey>search.types.wirestory</labelkey>
                    <icon>icon-file-text</icon>
                    <value>type:wirestory</value>
                </condition>
                <condition>
                    <label>Images</label>
                    <labelkey>search.types.images</labelkey>
                    <icon>icon-picture</icon>
                    <value>type:image</value>
                </condition>
                <condition>
                    <label>Gallery</label>
                    <labelkey>search.types.gallery</labelkey>
                    <icon>emui-icon-pictures</icon>
                    <value>type:eom::mediagallery</value>
                </condition>
                <condition>
                    <label>Audio</label>
                    <labelkey>search.types.audio</labelkey>
                    <icon>icon-music</icon>
                    <value>type:audio</value>
                </condition>
                <condition>
                    <label>Topic</label>
                    <labelkey>search.types.topic</labelkey>
                    <icon>emui-icon-topic</icon>
                    <value>type:eom::topic</value>
                </condition>
                <condition>
                    <label>Search by name</label>
                    <labelkey>search.types.byname</labelkey>
                    <icon>icon-file</icon>
                    <value>param:name:</value>
                </condition>
            </conditions>
            <archives>
                <archive>
                    <name>production</name>
                    <label>Editorial</label>
                    <archive>production@meth01_eomse1</archive>
                    <isDefault>true</isDefault>
                    <visible>true</visible>
                    <enabled>true</enabled>
                </archive>
                <archive>
                    <name>globecms</name>
                    <label>GlobeCMS</label>
                    <archive>globecms@meth01_eomse1</archive>
                    <isDefault>false</isDefault>
                    <visible>true</visible>
                    <enabled>true</enabled>
                </archive>
                <archive>
                    <name>wires</name>
                    <label>Wires</label>
                    <archive>wires@meth01_eomse1</archive>
                    <isDefault>false</isDefault>
                    <visible>true</visible>
                    <enabled>true</enabled>
                </archive>
                <archive>
                    <name>archive</name>
                    <label>Archive</label>
                    <archive>archive@meth01_eomse1</archive>
                    <isDefault>false</isDefault>
                    <visible>true</visible>
                    <enabled>true</enabled>
                </archive>
                <archive>
                    <name>configuration</name>
                    <label>Configuration</label>
                    <archive>configuration@meth01_eomse1</archive>
                    <isDefault>false</isDefault>
                    <visible>true</visible>
                    <enabled>true</enabled>
                </archive>
            </archives>
        </domain>
        <domain>
            <name>web</name>
            <type>web</type>
            <label>Web search</label>
            <icon>icon-globe</icon>
            <itemsPerPage>8</itemsPerPage>
            <useExternalSuggestion>true</useExternalSuggestion>
            <conditions>
                <condition>
                    <label>Free search</label>
                    <icon>icon-circle-blank</icon>
                    <value>type:web</value>
                </condition>
                <condition>
                    <label>News</label>
                    <icon>emui-icon-newspaper</icon>
                    <value>type:news</value>
                </condition>
            </conditions>
            <archives>
                <archive>
                    <name>google</name>
                    <label>Google</label>
                    <icon>icon-google</icon>
                    <archive>google</archive>
                    <isDefault>true</isDefault>
                    <visible>true</visible>
                    <enabled>true</enabled>
                    <exclusive>true</exclusive>
                </archive>
            </archives>
        </domain>
    </domains>
</search>

Swing search keywords documentation

Swing searches can be filtered by using keywords. Such keywords are then elaborated and transformed to create the adequate XML query for the EOMDB.

These keywords can be used within the Swing search, the Swing Explorer Folder filter, and the Swing search filters.

The supported keywords are meaningful for EOMDB searches only. If you want to use filters for custom domains, feel free to use any keyword you prefer. The filters will be passed in the conditions array of the Search Controller (options parameter). See Search controller documentation for further details.

Available keywords

channel

Returns only the objects of a given channel.

Syntax
channel:(CHANNEL)
userchannel:(CHANNEL)
Values
Table 117. Supported values
Keyword Example Description

CHANNEL

channel:"Globe-Print"

Any string value (with quotes if there is a space).

userchannel differs from channel in the fact that only the channels relative to the current user are available..

content

Search a specific text in the content of an object.

Syntax
content:"CONTENT"
Values
Table 118. Supported values
Keyword Example Description

CONTENT

content:Test

Any string value (with quotes if there is a space).

contentmetadata

Search for a text in both content and metadata.

Syntax
contentmetadata:"CONTENT"
Values
Table 119. Supported values
Keyword Example Description

CONTENT

contentmetadata:Test

Any string value (with quotes if there is a space).

created_lastdays

Allow to obtain the documents created in the last N days

Syntax
created_lastdays:(VALUE)
Values
Table 120. Supported values
Keyword Example Description

VALUE

created_lastdays:10

Any numeric value.

creator

freexml

Allow to insert a specific xml in the query. Please use it with moderation.

Syntax
freexml:"CONTENT"
Values
Table 121. Supported values
Keyword Example Description

CONTENT

freexml:"<SysAttributes><props><productInfo><issueDate q:op='EXIST'/></productInfo></props></SysAttributes>"

Any string value (with quotes if there is a space). Note: please use single quotes when a double quote is used inside the value.

lastmodifier

Returns the objects last modified by a specific user.

Syntax
lastmodifier:(USER|myself)
Values
Table 122. Supported values
Keyword Example Description

USER

lastmodifier:johndoe

Any string value (with quotes if there is a space). Return the objects modified by the specified user.

myself

lastmodifier:myself

Return the objects modified by the current user

limit

Limits the results to a specific number.

Syntax
limit:(VALUE)
Values
Table 123. Supported values
Keyword Example Description

VALUE

Any numeric value

limit:100

locker, lockedby

Search for objects locked by a given user

Syntax
locker:(USER|myself)
Values
Table 124. Supported values
Keyword Example Description

USER

locker:johndoe

Any string value (with quotes if there is a space). Return the objects locked by the specified user.

myself

locker:myself

Return the objects locked by the current user

locker, lockedby keywords are totally equivalent.

searchmetadata

Search a specific value in all the metadata.

Syntax
searchmetadata:"CONTENT"
Values
Table 125. Supported values
Keyword Example Description

CONTENT

searchmetadata:Test

Any string value (with quotes if there is a space).

metadata

Search a specific value in a given xpath.

Syntax
metadata:"{X-PATH}={VALUE}"
Values
Table 126. Supported values
Keyword Example Description

CONTENT

metadata:"/Metadata/Editorial/ContentType=RANGE"

See previous example. Note: Quotes are mandatory. If the value contains double quotes, use single quotes instead.

metadatarange

Search a specific value in a given xpath, with the "range" operator ( inside the se:attributes tag )

Syntax
metadatarange:"{X-PATH}={VALUE}"
Values
Table 127. Supported values
Keyword Example Description

"{X-PATH}={VALUE}"

metadatarange:"/Metadata/Editorial/ContentType=RANGE"

See previous example. Note: Quotes are mandatory. If the value contains double quotes, use single quotes instead.

The RANGE value must be in the following format: YYYYMMDDhhmmss;YYYYMMDDhhmmss.

modified_lastdays

Allow to obtain the documents modified in the last N days

Syntax
modified_lastdays:(VALUE)
Values
Table 128. Supported values
Keyword Example Description

VALUE

modified_lastdays:10

Any numeric value.

name

Search an object with a specific filename

Syntax
name:"CONTENT"
Values
Table 129. Supported values
Keyword Example Description

NAME

name:Test

Any string value

range

restrictto

Returns the objects inside a path.

Syntax
restrictto:(VALUE)
Values
Table 130. Supported values
Keyword Example Description

VALUE

Any string value (with quotes if there is a space).

restrictto:"/Globe/Images"

sort

Complete sorting mechanism.

Syntax
sort:"{X-PATH};{VALUE}"
Values
Table 131. Supported values
Keyword Example Description

"{X-PATH};{VALUE}"

sort:"ObjectInfo/modified;ND"

See previous example. Note: Quotes are mandatory. If the value contains double quotes, use single quotes instead.

In {x-path};{Value}, value can be:

Table 132. Values
Value Description

A

Ascending

D

Descending

NA

Numeric Ascending

ND

Ascending

sortby

Simple descending sort according to a parameter in the ObjectInfo.

Syntax
sortby:(VALUE)
Values
Table 133. Supported values
Keyword Example Description

created

sortby:created

Return the objects sorted by creation date, descending.

modified

sortby:modified

Return the objects sorted by modified date, descending.

status, workflow

Search objects with a given status ( workflow ).

Syntax
status:(VALUE)
Values
Table 134. Supported values
Keyword Example Description

VALUE

status:NewsFlow/Editing

Any string value (with quotes if there is a space).

type

Returns only the objects of a given type.

Syntax
type:(TYPE)
Values
Table 135. Supported values
Keyword Example Description

TYPE

type:EOM::Story

Any string value (with quotes if there is a space). Return the objects of specified type.

usageticket

Allow to search within the usage tickets.

Syntax
usageticket:"tp:{type};;c:{creator};;rng:{range}"
Values
Table 136. Supported values
Keyword Example Description

CONTENT

tp:{type};;c:{creator};;rng:{range}

usageticket:"tp:WebPub;c:alessandropiana;rng:20160420220000__20160421215900"

  • Type (tp) is the only mandatory field for the usageticket filter. creator (c) and range (rng) are optional.

  • Range (rng) is in the format YYYYMMDDhhmmss__YYYYMMDDhhmmss

  • Separate the fields with a double ; ( ;; ).

  • If the field values have spaces in it, use a double underscore instead of spaces ( __ )

user, creator, owner

Search for objects created by a given user

Syntax
user:(USER|myself)
Values
Table 137. Supported values
Keyword Example Description

USER

user:johndoe

Any string value (with quotes if there is a space). Return the objects created by the specified user.

myself

user:myself

Return the objects created by the current user

user, creator, owner keywords are totally equivalent.

userchannel

See channel.

workflow

workfolder

Returns the objects inside a workfolder.

Syntax
workfolder:(VALUE)
Values
Table 138. Supported values
Keyword Example Description

VALUE

Any string value (with quotes if there is a space).

workfolder:"/Globe/Images"

Search templates configuration, usage and creation

Méthode Swing uses templates to render the search results.

Configuration of result templates

To use a template, it is necessary to configure the searchTemplate options in the Domain section. See Domain setup for further details.

It is HIGHLY discouraged to change the default search themes for Methode types. Their layout can be customized by operating on the specific column catalog (SwingResultSet).

In the ResourceSpace example, we used the following settings:

<searchTemplate>
    <type>rsimage</type>
    <template>search-preview-resourcespace.html</template>
</searchTemplate>
<searchTemplate>
    <type>rstext</type>
    <template>search-preview-resourcespace-text.html</template>
</searchTemplate>
"searchTemplate" : [
    { type: "rsimage", template: "search-preview-resourcespace.html" },
    { type: "rstext", template: "search-preview-resourcespace-text.html" }
]

This settings specifies that we are using two templates for two different types.

We could have used the same template for different types.

Usage and creation of a template

Each template is an HTML file which can be customized according to the user’s necessity.

All the widgets are put inside the following path:

{SWING-APP}/config/templates/search/{TEMPLATE-NAME}

Preview files can be configured outside {SWING-APP}. See the paragraph How to configure search templates in external folder for some tips in how to take advantage of this new configuration option.

Default templates are:

  • src-preview-eom.html for Methode types (used in search, candidate and correlates)

  • src-preview-user.html for type: EOM::User

  • src-preview-webcontent.html for type: web::Content

Customize the templates

Méthode Swing uses Underscore.JS templating system to enhance the user experience.

See Underscore.JS for further information, or the box below for some useful tips.

The template is rendered with a main object:

  • item, which contains all the information about the item.

item: the item info

The item object inside template contains all the information retrived from the search source. That is, in case of external sources, the one you decide to pass back to the search. In general though, all Méthode objects should have the following properties:

// item: common properties { EXAMPLE }
{
    "id": "1.0.113665564",
    "columns": "SEE BOX BELOW"
    "name": "Obama, Hollande agree on much -- but not Afghanistan.xml",
    "description": "",
    "type": "EOM::Story",
    "owner": "johndoe",
    "creator": "johndoe",
    "created": 1337764225,
    "last_modifier": "johndoe",
    "modified": 1409816672,
    "locker": "",
    "locked": 1423560030,
    "status_info": {
        "name": "NewsFlow/Editing",
        "identifier": "RGB(255,0,0)",
        "comment": ""
    },
    "size": 6898,
    "system_attributes": {
        "workfolder": "/Globe/Politics",
        "templateName": "/SysConfig/Globe/Templates/story.xml",
        "summary": "New French President Francois Hollande told President Barack Obama on Friday that he will stick by his pledge .",
        "wordCount": "998",
        "sugCategory": "",
        "channel": "Globe-Web",
        "storyType": "",
        "productInfo": {
            "name": "Globe-Web",
            "issueDate": "20120524"
        }
    },
    "system_attributes_xml": "XML HERE",
    "createdStr": "20120523091025",
    "modifiedStr": "20140904074432",
    "channel": "Globe-Web",
    "issueDate": "20120524",
    "preview": "/WebClient/api/rest/object/preview?token=12dbbac2-6d8d-445d-8a26-8f79ff0aca84&id=1.0.113665564&fa=lowres"
}

Beyond these properties, ALL the results will at least have the following properties:

// item: other properties which will be always available for ANY object ( also external )
{
   title: 'Object title if available, otherwise equals to filename',
   preview: 'the preview URL which uses the Methode Preview server',
   previewThumbnail: 'the preview URL which uses the Methode Preview Thumbnail server'
   content: 'if not already available, the summary from the system attributes'
}

There is also the eomActions property, which has the HTML of the actions available for the object. If you want to have some actions available for the item, be sure to include this property inside a DOM element whose class is actions somewhere in the template.

item.columns: columnCatalog properties

The EOMDB results come with an additional property, named columns. The property is a JSON array containing the values specified in the ColumnCatalog associated with the search.

The default column catalog applyed by Swing is as follows (this can been overwritten in the columnCfgCatalogs.swing.xml file):

<!-- Swing ResultSet views -->
<catalog name="SwingResultSet" dir="/" type="methodeResultSetList" listable="true">
    <EOMCfgView>
        <props sort="name+type&amp;ascending" activeViewer="off" autoScroll="none" />
        <column fieldId="1" width="300" name="Title" priority="5">
            <choice op="notEmpty" minLength="5">
                <data source="info/system_attributes/props/title" />
                <data source="info/attributes/ObjectMetadata/General/Headline" />
                <data source="info/attributes/Metadata/DocTitle" />
                <data source="info/attributes/ObjectMetadata/iptc/headline" />
                <data source="info/attributes/ObjectMetadata/iptc/caption" />
                <data source="info/attributes/ObjectMetadata/iptc/image_name" />
                <data source="info/attributes/Metadata/Title" />
                <data source="info/attributes/meta/wire/title" />
                <data source="info/attributes/ObjectMetadata/GeneralMetadata/Headline" />
                <data source="info/attributes/meta/wire/title" />
                <data source="info/attributes/ObjectMetadata/iptc/caption" />
                <data source="info/attributes/Metadata/Keywords" />
                <data source="info/attributes/Metadata/DocKeywords" />
                <data source="info/name" />
            </choice>
        </column>
        <column fieldId="2" width="300" name="Summary" priority="5">
            <choice op="notEmpty" minLength="1">
                <data source="info/system_attributes/props/summary" />
                <data source="info/attributes/Metadata/iptc/caption" />
            </choice>
        </column>
        <column fieldId="3" name="Date" source="info/created" width="130" />
        <column fieldId="4" name="Type" width="90" source="info/type" priority="1" />
        <!-- Wires -->
        <column fieldId="5" name="Agency" width="90" priority="4">
            <choice op="notEmpty" minLength="1">
                <data source="info/attributes/ObjectMetadata/iptc/credit" />
                <data source="info/attributes/ObjectMetadata/Source"/>
                <data source="info/attributes/Metadata/Source"/>
            </choice>
            </column>
            <column fieldId="6" name="Category" width="90" priority="4">
            <choice op="notEmpty" minLength="1">
                <data source="info/attributes/ObjectMetadata/iptc/category" />
                <data source="info/attributes/ObjectMetadata/Category"/>
                <data source="info/attributes/Metadata/Category"/>
            </choice>
            </column>
            <!-- Production -->
        <column fieldId="7" name="WorkFolder" source="info/system_attributes/props/workFolder" width="90" priority="4"/>
        <column fieldId="8" name="Channel" source="info/system_attributes/props/productInfo/name" width="90" priority="4"/>

        <column fieldId="9" width="90" priority="4" name="Status">
            <data source="info/status_info/identifier" format="si:colorRect"/>
            <data source="info/status_info/name" format="tk:/$2" priority="3"/>
        </column>
        <column fieldId="10" width="70" priority="3" name="Locker" source="info/locker" format="lk:icon_name"/>
    </EOMCfgView>
</catalog>

Here an example of a record that represents the previous columns property:

[
    {
        "name": "Summary",
        "value": "President Barack Obama is photographed during a presidential portrait sitting for an official photo ...",
        "id": "2"
    },
    {
        "name": "Type",
        "value": " Image",
        "id": "4",
        "priority": "1"
    },
    {
        "name": "Locker",
        "value": "johndoe",
        "id": "10"
    }
]

If desired, it is possible to specify your own column catalog in the search domain configuration of the EOMDB. See Domain setup for further information.

How to configure the Web Search Domain

To configure the Web Search domain (Google), it is necessary to add the custom apikey in the MRAS configuration file, such as:

<config>
    <restApi>
        <!-- ... -->
        <searchEngines>
            <searchEngine id="google" dataSourceClass="com.eidosmedia.restserver.websearch.google.GoogleCustomSearchDataSource">
                <generic apiKey="YOUR_API_KEY" searchEngineCx="015464143803231643050:kgucpqylmq4"/>
            </searchEngine>
        </searchEngines>
    </restApi>
</config>

How to configure search templates in external folder

Additional search templates may be made available to Swing application by defining one or more nested components in Swing web context in server.xml. (for further details on Tomcat 9.x Resources configuration, please refer to Resources configuration).

This is an example of the external search templates configuration.

<Context docBase="com.eidosmedia.webclient.web-app"
    path="/swing" reloadable="false">
    <Resources className="org.apache.catalina.webresources.StandardRoot">
        <PreResources
            className="org.apache.catalina.webresources.DirResourceSet"
            base="/methode/meth01/extension/search" readOnly="true"
            webAppMount="/config/templates/search" />
    </Resources>
</Context>

Extensions: manipulate the search free text and XML

It is possible to manipulate the free text input BEFORE it is sent to validation and, in case of EOMDB searches, transformed into XML. For EOMDB searches, too, it is possible to add an additional validator to change the query XML just before the execution. The methods are described below.

addTextValidator

eidosmedia.webclient.search.addTextValidator(options);

In detail, the options are:

/**
 * CUSTOM TEXT VALIDATOR
 */
eidosmedia.webclient.search.addTextValidator({
    // @property "domain": {string} - a specific search domain to apply the filter to
    // @required - domain or domainType
    "domain": "eomdblocal",

    // @property "domainType": {string} - if specified, filter is applied to all the domains of the same type
    // @required - domain or domainType
    // "domainType": "eomdb"
}, function( searchInfo ) {
    // Search info contains the following properties:
    // - "textInput" {string} - the current search text.
    // - "conditions": {array of objects} - the conditions currently selected by the user, filters included.

    // Please beware that changes in the conditions made here will NOT be reflected in the search.
    // This methods only allows simple alterations to the search free text.

    var _textInput = searchInfo.textInput;
    // Simple elaboration.
    if ( _textInput === 'obama' ) {
        _textInput = '"Barack Obama"';
    }

    // This method must return the new search text, and only this.
    return _textInput;
});

addXMLValidator

eidosmedia.webclient.search.addXMLValidator(options, callback);

In detail, the options are:

/**
 * CUSTOM XML VALIDATOR
 */
eidosmedia.webclient.search.addXMLValidator({
    // @property "domain": {string} - a specific search domain to apply the filter to
    // @required - domain or domainType
    "domain": "eomdblocal",

    // @property "domainType": {string} - if specified, filter is applied to all the domains of the same type
    // @required - domain or domainType
    // "domainType": "eomdb"
}, function( searchInfo, callback ) {
    // Search info contains the following properties:
    // - "textInput" {string} - the current search text.
    // - "conditions": {array of objects} - the conditions currently selected by the user, filters included.
    // - "queryXML": {string} the final XML of the query

    // Please beware that changes in the conditions made here will NOT be reflected in the search.

    /* IMPORTANT: Please notice the difference with "addTextValidator". This function uses a callback
       to return the value */
    var _queryXML = searchInfo.queryXML;
    // Some elaboration here...
    // This method must call the "callback" with the new XML
    callback( _queryXML );
});

Search: integration of external DAM services

Méthode Swing allows the integration of external Digital Asset Management (DAM) services.

These services can be used within the Swing Quicksearch. It is also possible to elaborate the results received from such services and use them inside Méthode documents.

This document is a step-by-step tutorial on how to integrate an external DAM service. To make this process easer, we are also integrating the "ResourceSpace" service as a complete example.

1. Set up the search proxy

Méthode Swing uses proxies to communicate to external services. This is necessary due to the Same-Origin Policy used by modern browsers.

The same-origin policy restricts how a document or script loaded from one origin can interact with a resource from another origin. Same-origin Policy is used as a means to prevent some of the Cross-site Request Forgery attacks.

— Mozilla Developer Network

Proxies can be configured in Méthode Swing.

<!-- other configuration -->
<proxies>
    <proxy>
        <name>[PROXY-NAME]</name> <!-- e.g. resourcespace -->
        <targetUri>http://192.168.203.129/resourcespace/plugins/api_search/</targetUri> <!-- e.g. destination target Uri -->
        <useTemplate>false</useTemplate>
    </proxy>
</proxies>

The useTemplate param allows to use templates to build the URL. The following is an example of url which uses templates.

<proxies>
    <proxy>
        <name>[PROXY-NAME]</name> <!-- e.g. resourcespace -->
        <targetUri>http://192.168.203.129:{_port}</targetUri> <!-- e.g. destination target Uri -->
        <useTemplate>true</useTemplate>
    </proxy>
</proxies>

The new proxed URL must be called with ALL the parameters in its querystring, e.g.

http://swing/[PROXY-NAME]/users/get?_port=8080&id=userID

Note: if the parameter is missing, the URL will return an HTTP 500 error.

Note: due to the nature of the Template Servlet, in this mode it is not possible to pass arrays as querystrings ( i.e. more than one querystring parameter with the same name ).

[PROXY-NAME] must be equal to [DOMAIN-TYPE] (described in Domain setup).

2. Preparing the External DAM integration file

In general, all the extensions of Méthode Swing are places under

{SWING-APP}/plugins

So, all the Javascript described in the following sections should be placed under

{SWING-APP}/app/plugins/{EXTENSION-FOLDER}/{EXTENSION-NAME}.js

Do not use the word libs as an extension folder. The libs folder is reserved for loading external libs. See the proper documentation to obtain further info on the topic.

DEBUG MODE: by default, all plugins are aggregated into a single plugins.js file at the Tomcat startup. When creating a plugin, it may be frustrating to restart the Tomcat Server every time a change is made.

To avoid this, set the configuration property debugEnabled to true.

Then, that extension will be loaded on every refresh of the page.

In our example, it will be placed under {SWING-APP}/app/plugins/resourcespace/resourcespace.js.

Swing Search system is modular. The following paragraph describes in details each module.

Search structure

Swing Search system is modular and consists in three main modules:

  • Search input (domains): the module covering the user input, the search domain, the search archives and the filters available.

  • Search compiler: a compiling system that validates and translates the input into a language which is understandable by the controller. (For example, the EOMDB compiler validates the input and translates a user search, such as "U.S. politics", into a XML for the Méthode Query).

  • Search controller: the module that, effectively, executes the search and retrieves the result.

When the user searches for something, the compiler validates the content. If content is not valid, the search input is warned and the search is not executed (i.e. the search controller is not even called). If the content is valid, the search controller tries to call the external search services and retrives the results.

When the results are available, they are processed by the preview engine that shows a small preview of each result. From that point, according to the current context (e.g. Editor area, …​) and the actions which have been configured, the user can perform some specific operations.

Domain setup

Domain configuration follows the same properties as shown in the Search Configuration. However, here it must be specified as a Javascript object.

Example

{
    "name": "customtest",
    "type": "customtest",
    "label": "Local archive",
    "icon": "emui-icon-database",
    "itemsPerPage": 8,
    "quicksearchLimit": 48,
    "useExternalSuggestion": true,
    "externalSuggestionURL": "http://suggestqueries.google.com/complete/search?q={data.value}&client=firefox&callback=?",
    "conditions": [
          {
                "isDefault": true,
                "value": "modified_lastdays:10"
          },
          { "label": "Free search", "labelkey": "search.types.freesearch",
            "icon": "icon-search", "value": "type:",
            "type": "" },
          { "label": "Story", "labelkey": "search.types.story",
            "icon": "emui-icon-newspaper", "value": "type:eom::story",
            "type": "story" },
          { "label": "Images", "labelkey": "search.types.images",
            "icon": "icon-picture", "value": "type:image",
            "type": "image" }
    ],
    "archives": [
         { "name": "production", "label": "Editorial",
           "archive": "production@meth01_eomse1",
           "isDefault": true, "visible": true, "enabled": true },
         { "name": "globecms", "label": "GlobeCMS",
           "archive": "globecms@meth01_eomse1",
           "isDefault": false, "visible": true, "enabled": true }
    ]
}

Please refer to the Search Configuration for a reference of the meaning of each property. Even though the examples are all in XML, the concepts are equal.

To dinamically add a domain to the configuration, it is necessary to call the following Javascript function, inside your Javascript file:

eidosmedia.webclient.extensions.search.addDomain( /** domain settings **/ );

The domain behaviour must be defined in a JavaScript file and put inside the following path:

{SWING-APP}/app/plugins/{PROXY-NAME}

Domain controller can be configured outside {SWING-APP}. See the paragraph How to configure a domain controller in external folder for some tips in how to take advantage of this new configuration option.

Example: ResourceSpace integration

the key searchTemplate will be explained in 5. Usage and creation of result templates.

For our example we don’t need neither conditions nor archives, but the example contains them (commented) to simplify the code reuse.

// Add a new domain in the search dialog.
eidosmedia.webclient.extensions.search.addDomain({
    "name": "resourcespace",
    "type": "resourcespace",
    "label": "ResourceSpace Search",
    "icon": "icon-foursquare",
    "itemsPerPage": 8,
    "useExternalSuggestion": false,
    "searchTemplate" : [
        { type: "rsimage", template: "search-preview-resourcespace.html" },
        { type: "rstext", template: "search-preview-resourcespace.html" }
    ],
    "compiler": function( options, callbacks ) { /* ... SEE BELOW ... */ },
    "controller": function( options, callbacks, ctx ) { /* ... SEE BELOW ... */ },
    "allowImport": false, // Or it can be a function, see below
    "correlateOnImport": true,
    "getImportInfo": function( ctx ) { /* ... SEE BELOW ... */ }

    /* It is possible to add Archives and conditions. Please refer to Domains configuration. */

});

Compiler setup

The Search Compiler validates the user input and translates into the language which is understood by the search controller.

As seen in the Search structure section, the compiler is a middle layer between the search component (a.k.a. the input form) and the search controller (a.k.a. the search engine). The Compiler can be used to perform any transformation of the search input to be passed into the controller, as well as to provide some sort of validation that, in case of not valid arguments, interrupts the search before it starts, so not even calling the server.

Such validation logic can easily be moved inside the search controller, nevertheless the user may find useful to separate the Compiler and the Controller logic.

The Search compiler is a Javascript function called as follows:

// ... other configuration
"compiler": function( options, callbacks ) {
    /* ... CODE HERE ... */
},
// ... other configuration

The compiler is a Javascript function called with two main parameters:

  • options [JSON object] - information about the current search

  • callbacks [JSON object] - function to be called ( success and error)

options

  1. conditions: user specified conditions

  2. archives: subset of domain the user wants to limit the search to

  3. searchType: the specific type of search

The compiler is called with the following options:

{
    "domain": DOMAIN_NAME,
    "input": "my search input",
    "conditions": [ /* Array of conditions */ ],
    "archives": [
        "archive3"
    ],
    "searchType": "image"
}
  • domain contains the domain name

  • input contains the search input

  • conditions contains all the conditions specified by the user (also archive and type, which are just a specific condition.

  • archives lists only the archives selected

  • searchType lists the type selected.

With this information, it is possible to call the service with the specific parameters.

callback

This is the function that is called when the validation is completed.

It MUST be called with the following parameters.

callback( result );

Where result should include at least all the properties of options plus, if an error occurred, the following two properties:

  • error: set to true

  • errorMessage: the error message.

If you want, you can now add any other property that will be directly passed to the Search Compiler.

So, for example, a compiler that does absolutely nothing would be:

"compiler": function( options, callbacks ) {
    var result = options;
    // Some validation here...
    // Return the result as is to the compiler
    callback( result );
},

and, in case of error:

"compiler": function( options, callbacks ) {
    var result = options;
    // Some validation here...

    // EXAMPLE

    if (options.input === "FORBIDDEN") {
        result.error = true;
        result.errorMessage = "You cannot search for this term";
    }

    callback( result );
},

If you don’t call the callback function, the search will never be executed!

If you don’t pass the required parameters to the Controller, this will not be able to perform the search!

Example: ResourceSpace integration

Resource space does not need any specific validation, so there is nothing to configure and Méthode; Swing will simply go directly to the search Controller.

Controller setup

Controller is the most importart part of the search. Without a controller, the search cannot be executed and the results cannot be retrieved.

To add a controller, it is add a controller property to the Domain setup:

// ... other configuration
"controller" : function( options, callbacks, ctx ) {
    /* ... SEE BELOW ... */
},
// ... other configuration
Different domains of the same type have the same controller. So, it is the user choice to create different types for similar functionalities, or to have a single controller to behave differently according to the options.

The controller is a Javascript function called with three main parameters:

  • options [JSON object] - information about the current search

  • callbacks [JSON object] - function to be called ( success and error)

  • ctx [JSON object] - the classic context object.

options

Same as the compiler options.

callbacks

Callbacks are Javascript function internally used to determine if the search has been successful or not.

The user is not required to know how the callbacks work, but they should be called as follows:

callbacks.success( [PARAMETERS] ); // in case of success.
callbacks.error( [PARAMETERS] ); // in case of error.

where:

  • [PARAMETERS] are either the items (in case of success) or the exception (in case of error) [ in any case, it is a JSON object ].

Success example:

callbacks.success( { items: items } );
Please maintain the structure of the items are shown in the example (a JSON object with items which is an array of objects).

Error example:

callbacks.error( { message: 'Error during the search' } );

How to build a controller

The following code shows how a controller can be built. The code is well commented and should be easily understandable.

// Add a new controller for the search.
"controller": function( options, callbacks ) {

        var API_KEY = 'V0aRlntBV163dNJK5prQv5E1IXNUwGKudTIGZcq8xissufD8lBpQ2I1TgkRn_Xlvm8RvL_ezBOzTOS4s4IV40g';

        // If the user did not put any input, show an error.
        // In this specific case (resourceSpace), search without terms is allowed,
        // so the following code is commented
        /*
        if (!options.input) {
            callbacks.error( { message: 'No input provided' } );
            return;
        }
        */

        /*
            Archive management
            resourceSpace example did NOT use any archive, so this part is omitted.
            Another example (such as wikipedia), used the archive to build the link, as shown
            in the following (commented) example.
        */

        /*
            // Takes the first archive, or 'en' as default
            var archive = (options.archives && options.archives.length) ? options.archives[0] : 'en';

            // Build the url necessary for wikipedia.
            var url1 = 'http://' + (archive || 'en') +
                       '.wikipedia.org/w/api.php?action=query&list=search&callback=&srsearch=' +
                       encodeURI(options.input) + '&format=json&rawcontinue&srprop=snippet';
        */

        // Prepares the url
        var url = eidosmedia.webclient.app.context + "/resourcespacesearch/";

        // With the try..catch no error will escape!
        try {
            $.ajax({
              url: url,
              data: {
                "key": API_KEY,
                "search": options.input,
                "prettyfieldnames": true,
                "original": true
              },
              success: function( items ) {

                  var response = {};
                  response.items = items || [];
                  // Do something with the response...

                  // Prepare JSON ( see the next paragraph for that ).
                  // If we used a default template, we would have to adapt the response JSON
                  // to Swing Templates, as shown here.
                  /*
                  for( var j in response.items ) {
                      response.items[j].id = response.items[j]["original_link"];
                      response.items[j].type = "rsImage";
                      response.items[j].methodeType = "image";
                      response.items[j].title = response[j].Title;
                      response.items[j].hideStatus = true;
                      response.items[j].preview = response.items[j].content = response.items[j]["original_link"];
                  }*/
                  // OR
                  // If we used a custom template, we could just use the JSON 'as-is'

                  // REMEMBER TO SET AT LEAST ID AND TYPE FOR EACH ITEM.
                  for( var j in response ) {
                      response.items[j].id = response.items[j]["original_link"];
                      response.items[j].type = "rsImage";
                      response.items[j].methodeType = "image";
                  }

                  // Call the successful callback.
                  callbacks.success( response );
              },
              error: function( ex ) {
                  // Call the error callback.
                  callbacks.error( ex );
              }
            });
        } catch( ex ) {
            // Call the error callback.
            callbacks.error( ex );
        }
    }

Controller setup: dynamic page load.

As of Swing 3.0.0, it is possible for custom domain to load only a subset of results. Everytime the user requires additional results, the controller is called again with specific paramers in order to allow the search of the required items only.

To do so, it is necessary to structure the response object adding two additional properties:

  • totalCount : this is the total number of results of the query if there were no limits.

  • resultCount: this is the number of results provided by the current query (e.g. items.length )

This allows Swing to understand that the search is passing a subset of a larger amount of results. The calculation of pages is done automatically.

When the user requires a new page, the controller is called again with the same parameters. However, the options parameter will have an additional property:

  • pageInfo.start : the number of the starting required result ( e.g. the "from" object )

  • pageInfo.end : the number of the end required result ( e.g. the "to" object )

pageInfo is not passed in case of the first page.

Here follows a pseudo-code example:

    // Add a new controller for the search.
    "controller": function( options, callbacks ) {

        // other infos...

        // PAGE MANAGEMENT
        var hasPage = !!options.pageInfo;
        // Prepares the url
        var url = eidosmedia.webclient.app.context + "/resourcespacesearch/";

        var request = {
            "key": API_KEY,
            "search": options.input,
            "prettyfieldnames": true,
            "original": true
        }
        if ( hasPage ) {
            request.from = options.pageInfo.start;
            request.to = options.pageInfo.end;
        }

        // With the try..catch no error will escape!
        try {
            $.ajax({
              url: url,
              data: request,
              success: function( resp ) {
                  var response = {};
                  response.items = resp.items || [];
                  // Do something with the response...

                  if (!hasPage) {
                    // We pass the total amount of results to force dynamic pagination
                    response.totalCount = resp.totalResults;
                    response.resultCount = response.items.length;
                  }

                  // Call the successful callback.
                  callbacks.success( response );
              },
              error: function( ex ) {
                  // Call the error callback.
                  callbacks.error( ex );
              }
            });
        } catch( ex ) {
            // Call the error callback.
            callbacks.error( ex );
        }
    }

4. How to structure results in JSON for Méthode Swing

JSON results must be structured accordingly to be correctly interpreted by Méthode Swing Search. In fact, each item must have an id and a type property. (And a methodeType property if the import is allowed).

It is certainly possible to specify one of the default Méthode types. However, this is strongly discouraged in order to avoid any possibility of overriding the original behaviour.

Since the id and type properties are required, it is strongly advised (read: MUST) that:

  • id is unique.

  • type uses a custom prefix (e.g. rs) to avoid name conflicts with Méthode types.

Also, if the allowImport is enabled, the item should specify the correspondent methodeType.

There are several different MethodeType supported values:

  • image: allows import, download and insert.

  • externalwebpage: allows the text analysis / create story from the external webpage

  • htmlblock: allows to insert a Codeblock inside a story. (The HTML code to insert must be in the htmlCode property).

Having said that, the user has two possibilities:

5. Usage and creation of result templates

Documentation for this section is part of the general search documentation and is available here.

6. Import objects in a Méthode document

By specifying the allowImport property to the proper value, Méthode Swing automatically adds the possibility to download the object in the configured workfolder (according to the context: in Explorer Area, the current folder; in Editor Area, the configured workfolder of the current document).

allowImport can be a boolean, or a function which returns true if in that case the import is allowed, false otherwise. It is called with a parameter containing all the information about the item.

A common use for the function is to verify if to allow the import only for specific types.

    // ... other configuration
    "allowImport": function( ctx ) {
        // Allow import only for images.
        if (ctx.activeObject.getInfo().type.toLowerCase() === 'rsimage') {
            return true;
        }
        return false;
    },
    // ... other configuration

    // OR

    "allowImport": true / false

Furthermore, it is possible to specify the getImportInfo function, which is used to return the Import information. Consider the example:

{
    // ... other configuration
    "allowImport": function( ctx ) {
        // Allow import only for images.
        if (ctx.activeObject.getInfo().type.toLowerCase() === 'rsimage') {
            return true;
        }
        return false;
    },
    // ... other configuration
    "getImportInfo": function( ctx ) {
        var objInfo = ctx.activeObject.getInfo();

        // IMPORTANT. If the "url" is relative, this won't work.
        // To obtain the absolute url, use the ctx.getAbsoluteUrl( url ) method.

        return {
            "url": ctx.getAbsoluteUrl( objInfo.original_url ),
            "filename": objInfo.id + '.png', // See below
            "attributes": "Here you can specify the XML metadata",
            "uniqueId": "Specify the uniqueness_string here",
            "info": {
                "otherCustomProperties": "Some value"
            }
        }
    }
}

The function is not necessary, but if it is specified, it must return a JSON object with two parameters:

  • url : the URL of the downloaded object ( if not present, Méthode Swing will look for the item’s url property, or the id ).

  • info: a JSON object containing some additional metadata about the item. By default, all the item properties are added.

If specified, uniqueId