    //@include lib_Class.js
    //@include lib_sol.common.StringUtils.js
    //@include lib_sol.common.ObjectUtils.js
    //@include lib_sol.common.JsonUtils.js
    //@include lib_sol.common.SordUtils.js
    //@include lib_sol.common.IxUtils.js
    //@include lib_sol.common.CounterUtils.js
    //@include lib_sol.common.AclUtils.js
    //@include lib_sol.common.TranslateTerms.js
    //@include lib_sol.common.Template.js
    //@include lib_sol.common.WfUtils.js
    //@include lib_sol.common.Map.js
    //@include lib_sol.common.ix.RfUtils.js

    /**
     * @abstract
     *
     * Base class for actions.
     *
     * Actions allow providing the same functionality in several clients. The primary goal is to handle core operations
     * (creating a folder and starting a workflow) in the process function of an action. Further logic should be implemented
     * by workflow function modules.
     *
     * Subclasses have to implement the two abstract methods 'getName' and 'process'. These are used by the 'execute' function.
     *
     * # Response
     *
     * Actions can return various types in form of a JSON String. The response is a standardized format and handled by this class.
     * There are three return types:
     *
     * - events
     * - messages (deprecated, because not used in standard action handling)
     * - arbitrary data (deprecated, because not used in standard action handling)
     *
     * ## Add event functions
     *
     * Events are used in order to define a postprocessing in the clients. This allows to trigger a goto to the created element in elo or display a dialog that contains a workflow form.
     *
     * Following example shows how to display a dialog by a given flow id.
     *
     *     me.addWfDialogEvent(flowId, { objId: objId });
     *
     * Currently following events are supported:
     *
     * - addWfDialogEvent
     * - addUrlDialogEvent
     * - addAppDialogEvent (utilizes url event)
     * - addActionEvent
     * - addRefreshEvent
     * - addGotoIdEvent
     * - addGotoWfTaskEvent
     * - addErrorEvent
     * - addInfoEvent
     * - addFeedbackEvent
     *
     * Please note that events are handled synchronously. If addWfDialogEvent and addRefreshEvent is called. The client will refresh the element after the users closes the dialog.
     *
     * # onSuccess and onFailure
     * These functions can be implemented by the subclass. They will be called either if the action was processed successfully or if there occured an error.
     *
     * # Conditional events
     *
     * It is possible to declare events as conditional. The client handler will evaluate if the event should be executed.
     * This is required since workflow processing can be influenced by the user. As an example: A WF-Form is displayed in a dialog. If the user clicks cancel refresh and goto events should'nt be excecuted.
     *
     * The events has to declare an `ON` property, which has to define the following properties.
     *
     * - type {String}: "WF_STATUS"|"SORD"|"GRP"|"MAP"
     * - key {String}: only for type="SORD"|"GRP"|"MAP", contains the sord property, the group key or map key
     * - objId {String}: only for type="SORD"|"GRP"|"MAP", the objId of the sord which should be checked
     * - flowId {String}: only for type="WF_STATUS", the workflow id to get the status from
     * - value {String}: the value which has to be fulfilled
     *
     * Following example shows a condition used by the refresh event.
     *
     *     me.addRefreshEvent(objId, {
     *       type: "WF_STATUS",
     *       value: "APPROVE",
     *       flowId: flowId
     *     });
     *
     * @author PZ, ELO Digital Office GmbH
     * @version 1.05.000
     *
     * @eloix
     * @eloas
     * @requires sol.common.StringUtils
     * @requires sol.common.ObjectUtils
     * @requires sol.common.JsonUtils
     * @requires sol.common.SordUtils
     * @requires sol.common.IxUtils
     * @requires sol.common.CounterUtils
     * @requires sol.common.AclUtils
     * @requires sol.common.TranslateTerms
     * @requires sol.common.WfUtils
     * @requires sol.common.WfMap
     */
    sol.define("sol.common.ActionBase", {

      _registeredEvents: undefined,

      _messages: undefined,

      _data: undefined,

      /**
       * @private
       * @property {String} _APP_URL_TEMPLATE Template for the app event
       */
      _APP_URL_TEMPLATE: "{{eloWfBaseUrl}}/apps/app/{{appName}}/?lang={{language}}&ticket={{ticket}}",

      /**
       * @private
       * @property {String} _APP_URL_TEMPLATE_12 Template for the app event since ELO12
       */
      _APP_URL_TEMPLATE_12: "{{eloWfBaseUrl}}/apps/app/{{appName}}/?lang={{language}}",

      actionId: undefined,

      initialize: function (config) {
        var me = this;

        if (me.$className === "sol.common.ActionBase") {
          throw "can not create instance of abstract class 'sol.common.ActionBase'";
        }
        RhinoManager.registerClass(me.$className);
        me.$super("sol.Base", "initialize", [config]);
        me._registeredEvents = [];
        me._messages = [];
        me._data = {};
        me.actionId = sol.common.CounterUtils.incCounter("SOL_ACTION_ID");
      },

      /**
       * @abstract
       * Name of the action. Has to be implemented by subclass.
       */
      getName: function () {
        throw "cannot call 'name' of abstract class 'sol.common.ActionBase'";
      },

      /**
       * @abstract
       * Implementation of the action. Has to be implemented by subclass.
       */
      process: function () {
        throw "cannot call 'process' of abstract class 'sol.common.ActionBase'";
      },

      /**
       * Will be called in case of a successfull action execution. The implementation in the subclass is optional.
       */
      onSuccess: function () { },

      /**
       * Will be called in case of an action failure. The implementation in the subclass is optional.
       * The default implementation will write an ERROR log entry and call {@link #addErrorEvent}.
       * @param {Exception} exception
       */
      onFailure: function (exception) {
        var me = this;
        me.logger.error(["Error processing '{0}'", me.$className], exception);
        me.addErrorEvent(exception.message || exception);
      },

      /**
       * @private
       * Marks an action (aka the workflow) as creation, or not.
       *
       * An action workflow will be marked as creation if there is a registered event with a `flowId` and either:
       *
       *  - the action defines a `$new: true` property
       *  - or it is a `StandardAction` with a `$new` parameter.
       *  - or the action name of the action start with 'create' (and `$new` is not set to `false` explicitly)
       *
       */
      markCreationAction: function () {
        var me = this,
            isNew, flowId, objId, wfmap;

        isNew = function () {
          var reason;
          if (me.$new === true) {
            reason = "Action defines '$new=true'";
          } else if (me.$className === "sol.common.ix.actions.Standard" && me.$new) {
            reason = "StandardAction with '$new' property";
          } else if ((me.getName().indexOf("Create") === 0) && (me.$new !== false)) {
            reason = "Action name starts with 'Create' and '$new=" + me.$new + "'";
          }
          if (reason) {
            me.logger.info("Mark as 'created' to suppress update registration in form. Reason: " + reason);
          }
          return !!reason;
        };

        me._registeredEvents.some(function (event) {
          if (event.obj && event.obj.flowId) {
            flowId = event.obj.flowId;
            return flowId;
          }
        });

        if (flowId && isNew()) {
          objId = sol.common.WfUtils.getWorkflow(flowId).objId;
          wfmap = sol.create("sol.common.WfMap", {
            flowId: flowId,
            objId: objId
          });
          wfmap.setValue("COMMON_SKIP_UPDATE_REGISTRATION", "true");
          wfmap.write();
        }
      },

      /**
       * Handles the execution of Actions and internally calls sol.common.ix.ActionHandler#process.
       * This function is called by sol.common.jc.ActionHandler.execute
       * @return {Object}
       */
      execute: function () {
        var me = this;

        me.logger.enter("execute_" + me.$className);
        me.logger.info(["executing action '{0}' => actionId={1}", me.getName(), me.actionId]);

        try {
          me.process();
          me.markCreationAction();
          me.onSuccess();
        } catch (ex) {
          me.onFailure(ex);
        }

        me.logger.exit("execute_" + me.$className);

        return me.buildResponse();
      },

      stringifyAll: function (obj) {
        return JSON.stringify(obj, function (key, value) {
          if (value instanceof java.lang.String) {
            value = String(value);
          }
          if (value && value.getClass) {
            value = String(value.toString());
          }
          return value;
        });
      },

      renderConfig: function (config) {
        var me = this, str;
        str = Handlebars.compile(me.stringifyAll(config))({
          objId: config.objId,
          type: (config.$templating ? config.$templating.$type : config.type),
          tree: (config.$templating ? config.$templating.$tree : config.tree),
          preconditions: (config.$templating ? config.$templating.$preconditions : config.preconditions)
        });
        str = str.replace(/&quot;/g, "\\&quot;");
        return JSON.parse(org.apache.commons.lang.StringEscapeUtils.unescapeHtml(str));
      },

      /**
       * Checks if a user has the required rights on an object.
       *
       * If some rights are missing this will throw an exception.
       *
       * @param {String|de.elo.ix.client.Sord} sord ObjId or sord to check the access rights on
       * @param {Object} params
       * @param {String|Object} params.rights Either a string (in form 'RWDEL' or 'RWDELP' since ELO12) or an object (see {@link sol.common.AclUtils rights}) specifying the rights the user requires
       * @param {String} params.message (optional) This can specify a message to override the default exception text from `sol.common.ix.actions.errorRequiredUserRights`. Has priority over `messageKey`.
       * @param {String} params.messageKey (optional) This can specify a translation key to override the default exception text from `sol.common.ix.actions.errorRequiredUserRights`
       * @param {String|de.elo.ix.client.ClientInfo} params.language (optional)
       * Either an ISO language String, or an de.elo.ix.client.ClientInfo Object. Default will be the ClientInfo from the connection. Not relevant for `message`.
       * @throws Throws an exception if the user has not the specified rights
       */
      requireUserRights: function (sord, params) {
        var me = this,
            hasRights = true,
            cfg, exception;

        if (params && params.rights) {
          cfg = {};
          if (!sol.common.ObjectUtils.isObject(params.rights)) {
            cfg.rights = {};
            if (sol.common.StringUtils.contains(params.rights, "R")) {
              cfg.rights.r = true;
            }
            if (sol.common.StringUtils.contains(params.rights, "W")) {
              cfg.rights.w = true;
            }
            if (sol.common.StringUtils.contains(params.rights, "D")) {
              cfg.rights.d = true;
            }
            if (sol.common.StringUtils.contains(params.rights, "E")) {
              cfg.rights.e = true;
            }
            if (sol.common.StringUtils.contains(params.rights, "L")) {
              cfg.rights.l = true;
            }
            if (sol.common.StringUtils.contains(params.rights, "P")) {
              cfg.rights.p = true;
            }
          } else {
            cfg.rights = params.rights;
          }
          hasRights = sol.common.AclUtils.hasEffectiveRights(sord, cfg);
        }

        if (!hasRights) {
          if (params && params.message) {
            exception = params.message;
          } else {
            exception = me.getLocalizedString(
              (params && params.language) ? params.language : ixConnect.loginResult.clientInfo,
              (params && params.messageKey) ? params.messageKey : "sol.common.ix.actions.errorRequiredUserRights"
            );
          }
          throw exception;
        }
      },

      /**
       * @deprecated 1.03.000 Not used for standard action handling and might be removed in future versions
       * Add a (localized) massage to the response.
       * @param {String|de.elo.ix.client.ClientInfo} language Either an ISO language String, or an de.elo.ix.client.ClientInfo Object
       * @param {String} msg Either a message string or a message key (if language is set)
       */
      addMessage: function (language, msg) {
        var me = this;

        if (language) {
          msg = this.getLocalizedString(language, msg);
        }

        me._messages.push(msg);
      },

      /**
       * @deprecated 1.03.000 Not used for standard action handling and might be removed in future versions
       * Add some payload data to the response
       * @param {String} key
       * @param {String} value
       */
      addPayload: function (key, value) {
        var me = this;
        me._data[key] = value;
      },

      /**
       * Add an event which tells the client to open a workflow form in a dialog.
       *
       * Please not that the flowId must be given in order to identify the current Workflow node for the user.
       *
       *     flowId = me.startWorkflow(contractObjId, contractConfig.workflows.approveContract.workflowTemplateName, wfName);
       *     me.addWfDialogEvent(flowId, { objId: objId });
       *
       * @param {Number} flowId (required)
       * @param {Object} params
       * @param {String} params.objId Either `nodeId` or `objId` is required
       * @param {Number} params.nodeId Either `nodeId` or `objId` is required
       * @param {String} params.title (optional) Dialog title
       * @param {String} params.dialogId (optional) Id so the client can save the size for the dialog
       * @param {Object} on (optional) Object with conditions for the event execution (see class documentation)
       */
      addWfDialogEvent: function (flowId, params, on) {
        var me = this,
            eventdata = { flowId: flowId },
            wfCollectNode;

        if (params && params.nodeId) {
          eventdata.nodeId = params.nodeId;
        } else if (params && params.objId) {
          wfCollectNode = sol.common.WfUtils.findFirstActiveNode(params.objId, flowId);
          eventdata.flowId = wfCollectNode ? wfCollectNode.flowId : flowId; // could be the subworkflow ID
          eventdata.nodeId = wfCollectNode ? wfCollectNode.nodeId : null;
          eventdata.formSpec = (wfCollectNode && wfCollectNode.formSpec) ? wfCollectNode.formSpec : null;
        }

        if (!eventdata.flowId || !eventdata.nodeId) {
          return;
        }

        me.addDialogEvent(eventdata, params, on);
      },

      /**
       * Add an event which tells the client to open a URL in a dialog.
       *
       *     me.addUrlDialogEvent("http://server/myCustomApp");
       *
       * @param {String} url
       * @param {Object} params (optional)
       * @param {String} params.title (optional) Dialog title
       * @param {String} params.dialogId (optional) Id so the client can save the size for the dialog
       * @param {Object} on (optional) Object with conditions for the event execution (see class documentation)
       */
      addUrlDialogEvent: function (url, params, on) {
        var me = this,
            eventdata = { url: url };

        me.addDialogEvent(eventdata, params, on);
      },

      /**
       * Add an event which tells the client to open a dialog with an app.
       *
       *     me.addAppDialogEvent();
       *
       * @param {String} appName The name of the app
       * @param {Object} params (optional)
       * @param {String} params.language (optional) Language shurtcut to call the app with. Default will be from IX connection.
       * @param {String} params.title (optional) Dialog title
       * @param {String} params.dialogId (optional) Id so the client can save the size for the dialog
       * @param {Object} on (optional) Object with conditions for the event execution (see class documentation)
       */
      addAppDialogEvent: function (appName, params, on) {
        var me = this,
            language, eloWfBaseUrl, urlTemplate, url, eventdata;

        language = (params && params.language) ? params.language : ixConnect.loginResult.clientInfo.language;
        eloWfBaseUrl = sol.common.WfUtils.getWfBaseUrl();

        urlTemplate = (sol.common.IxUtils.checkVersion(ixConnect.implVersion, "12.00.000.000")) ? me._APP_URL_TEMPLATE_12 : me._APP_URL_TEMPLATE;

        url = sol.create("sol.common.Template", { source: urlTemplate }).apply({
          eloWfBaseUrl: eloWfBaseUrl,
          appName: appName,
          language: language,
          ticket: ixConnect.loginResult.clientInfo.ticket
        });

        if (!url) {
          return;
        }

        eventdata = { url: url };

        me.addDialogEvent(eventdata, params, on);
      },

      /**
       * @private
       * Adds a dialog event. Used by {@link #addWfDialogEvent}, {@link #addUrlDialogEvent} and {@link #addAppDialogEvent}
       * @param {Object} eventdata Prefilled data object (e.g. `url` or `flowId` and `nodeId`)
       * @param {Object} params (optional)
       * @param {String} params.title (optional) Dialog title
       * @param {String} params.dialogId (optional) Id so the client can save the size for the dialog
       * @param {Object} on (optional) Object with conditions for the event execution (see class documentation)
       */
      addDialogEvent: function (eventdata, params, on) {
        var me = this,
            event;

        if (params && params.title) {
          eventdata.title = eventdata.title || params.title;
        }

        if (params && params.dialogId) {
          eventdata.dialogId = eventdata.dialogId || params.dialogId;
        }

        event = me.createEvent(sol.common.IxUtils.CONST.EVENT_TYPES.DIALOG, eventdata, on);

        me._registeredEvents.push(event);
      },

      /**
       * Adds an event which tells the client to start another action.
       *
       * @param {String} registeredFunction RF of the action which should be called next
       * @param {Object} params Params for the RF which should be called next
       * @param {Object} on (optional) Object with conditions for the event execution (see class documentation)
       */
      addActionEvent: function () {
        throw "cannot call 'addActionEvent' of class 'sol.common.ActionBase', function has to be implemented by subclass";
      },

      /**
       * Adds an event which tells the client to refresh the current view.
       *
       *     me.addRefreshEvent(objId);
       *
       * @param {String} objId
       * @param {Object} on (optional) Object with conditions for the event execution (see class documentation)
       */
      addRefreshEvent: function (objId, on) {
        var me = this;
        me._registeredEvents.push(me.createEvent(sol.common.IxUtils.CONST.EVENT_TYPES.REFRESH, { objId: objId }, on));
      },

      /**
       * Adds an event which tells the client to navigate to a given Sord id.
       *
       * Goto a new element.
       *
       *     me.addGotoIdEvent(objId);
       *
       * Goto a new element if the workflow status was set to "CREATE".
       *
       *     me.addGotoIdEvent(objId, undefined, {
       *       type: "WF_STATUS",
       *       value: "CREATE",
       *       flowId: flowId
       *     });
       *
       * Goto a new document and open the document for edit. (Checkout)
       *
       *     me.addGotoIdEvent(objId, true, {
       *       type: "WF_STATUS",
       *       value: "CREATE",
       *       flowId: flowId
       *     });
       *
       * @param {String} objId
       * @param {Boolean} checkout
       * @param {Object} on (optional) Object with conditions for the event execution (see class documentation)
       */
      addGotoIdEvent: function (objId, checkout, on) {
        var me = this,
            eventCfg = { objId: objId };

        eventCfg.checkout = (checkout === true) ? true : false;

        me._registeredEvents.push(me.createEvent(sol.common.IxUtils.CONST.EVENT_TYPES.GOTO, eventCfg, on));
      },

      /**
       * Adds an event which tells the client to navigate to a given task.
       *
       *     me.addGotoWfTaskEvent(flowId);
       *
       * @param {String} flowId (required)
       * @param {Object} params
       * @param {String} params.objId Either `nodeId` or `objId` is required
       * @param {String} params.nodeId Either `nodeId` or `objId` is required
       * @param {Object} on (optional) Object with conditions for the event execution (see class documentation)
       */
      addGotoWfTaskEvent: function (flowId, params, on) {
        var me = this,
            eventdata = { flowId: flowId },
            node;

        if (params && params.nodeId) {
          eventdata.nodeId = params.nodeId;
        } else if (params && params.objId) {
          node = sol.common.WfUtils.findFirstActiveNode(params.objId, flowId);
          eventdata.nodeId = (node) ? node.nodeId : null;
        }

        if (!eventdata.flowId || !eventdata.nodeId) {
          return;
        }

        me._registeredEvents.push(me.createEvent(sol.common.IxUtils.CONST.EVENT_TYPES.GOTO, eventdata, on));
      },

      /**
       * Adds an error event.
       *
       *     if (!me.templateId) {
       *       me.addErrorEvent("partner.msgs.errormessage", null, null, me.ci);
       *       return;
       *     }
       *
       * @param {String} message Either a message string or a message key (if language is set)
       * @param {String} errorcode (optional)
       * @param {String} exception (optional)
       * @param {String|de.elo.ix.client.ClientInfo} language (optional) Either an ISO language String, or an de.elo.ix.client.ClientInfo Object
       * @param {Object} on (optional) Object with conditions for the event execution (see class documentation)
       */
      addErrorEvent: function (message, errorcode, exception, language, on) {
        var me = this,
            errorCfg = {},
            errorEvent;

        if (language) {
          message = me.getLocalizedString(language, message);
        }

        if (message) {
          errorCfg.msg = message;
        }
        if (errorcode) {
          errorCfg.code = errorcode;
        }
        if (exception) {
          errorCfg.ex = exception;
        }

        errorEvent = me.createEvent(sol.common.IxUtils.CONST.EVENT_TYPES.ERROR, errorCfg, on);

        me._registeredEvents.push(errorEvent);
      },

      /**
       * Adds an info event.
       *
       *     addFeedbackEvent("sol.pubsec.MyString", "de")
       *
       * will return the string as defined the relevant properties language file of the indexserver translation
       *
       *     addFeedbackEvent("hello {{str1}}", null, { str1: "world" })  ==> "hello world"
       *
       * Assuming, that we have property language keys defined for German (`sol.pubsec.MyString=Herr {{name}} wir grüßen Sie`) and English (`sol.pubsec.MyString=Greetings Mr. {{name}}`):
       *
       *     addFeedbackEvent("sol.pubsec.MyString", "de", { name: "Mustermann" })  ==> "Herr Mustermann wir grüßen Sie"
       *     addFeedbackEvent("sol.pubsec.MyString", "en", { name: "Mustermann" })  ==> "Greetings Mr. Mustermann"
       *
       * @param {String} message Either a message string or a message key (if language is set)
       * @param {String|de.elo.ix.client.ClientInfo} language (optional) Either an ISO language String, or an de.elo.ix.client.ClientInfo Object
       * @param {Object} params (optional) if set, the message will be used as a handlebars string with the params applied (if there is also a `language`, the translation will be applied first)
       * @param {Object} on (optional) Object with conditions for the event execution (see class documentation)
       */
      addInfoEvent: function (message, language, params, on) {
        var me = this,
            feedbackCfg, feedbackEvent;

        feedbackCfg = me.createFeedbackEventCfg(message, language, params, on);
        if (feedbackCfg) {
          feedbackCfg.permanent = true;
          feedbackEvent = me.createEvent(sol.common.IxUtils.CONST.EVENT_TYPES.FEEDBACK, feedbackCfg, on);
          me._registeredEvents.push(feedbackEvent);
        }
      },

      /**
       * Adds a feedback event.
       *
       *     addFeedbackEvent("sol.pubsec.MyString", "de")
       *
       * will return the string as defined the relevant properties language file of the indexserver translation
       *
       *     addFeedbackEvent("hello {{str1}}", null, { str1: "world" })  ==> "hello world"
       *
       * Assuming, that we have property language keys defined for German (`sol.pubsec.MyString=Herr {{name}} wir grüßen Sie`) and English (`sol.pubsec.MyString=Greetings Mr. {{name}}`):
       *
       *     addFeedbackEvent("sol.pubsec.MyString", "de", { name: "Mustermann" })  ==> "Herr Mustermann wir grüßen Sie"
       *     addFeedbackEvent("sol.pubsec.MyString", "en", { name: "Mustermann" })  ==> "Greetings Mr. Mustermann"
       *
       * @param {String} message Either a message string or a message key (if language is set)
       * @param {String|de.elo.ix.client.ClientInfo} language (optional) Either an ISO language String, or an de.elo.ix.client.ClientInfo Object
       * @param {Object} params (optional) if set, the message will be used as a handlebars string with the params applied (if there is also a `language`, the translation will be applied first)
       * @param {Object} on (optional) Object with conditions for the event execution (see class documentation)
       */
      addFeedbackEvent: function (message, language, params, on) {
        var me = this,
            feedbackCfg, feedbackEvent;

        feedbackCfg = me.createFeedbackEventCfg(message, language, params, on);
        if (feedbackCfg) {
          feedbackCfg.permanent = false;
          feedbackEvent = me.createEvent(sol.common.IxUtils.CONST.EVENT_TYPES.FEEDBACK, feedbackCfg, on);
          me._registeredEvents.push(feedbackEvent);
        }
      },

      /**
       * @private
       * Creates an basic feedback event config. Used by {@link #addFeedbackEvent} and {@link addInfoEvent}.
       * @param {String} message
       * @param {String|de.elo.ix.client.ClientInfo} language
       * @param {Object} params
       * @param {Object} on
       * @return {Object}
       */
      createFeedbackEventCfg: function (message, language, params, on) {
        var me = this,
            feedbackCfg = {};

        if (!message) {
          return null;
        }
        if (language) {
          message = me.getLocalizedString(language, message);
        }

        if (params) {
          message = sol.create("sol.common.Template", { source: message }).apply(params);
        }

        feedbackCfg.msg = message;

        return feedbackCfg;
      },

      /**
       * Get a localized string for a key.
       * @param {String|de.elo.ix.client.ClientInfo} language Either an ISO language String, or an de.elo.ix.client.ClientInfo Object
       * @param {String} key The key in the resource files
       * @return {String}
       */
      getLocalizedString: function (language, key) {
        return sol.common.TranslateTerms.getTerm(language, key);
      },

      /**
       * Writes a feed event for an object.
       *
       * Uses {@link sol.common.ix.functions.FeedComment#RF_sol_function_FeedComment RF_sol_function_FeedComment}.
       *
       * @param {String} objId
       * @param {Object} params
       * @param {String} params.file The name of the language file (in `Administration/ELOwf Base/Feed/Script Locales`)
       * @param {String} params.key The key in the language file
       * @param {String[]} params.data (optional) Optional data, if the language key contains placeholders
       */
      writeFeedEvent: function (objId, params) {
        var me = this;
        if (!objId) {
          me.logger.error("IllegalArgumentException: can not write feed comment without an 'objId'");
          return;
        }
        if (!params || !params.file || !params.key) {
          me.logger.error("IllegalArgumentException: can not write feed comment without a 'file' or a 'key'");
          return;
        }
        sol.common.IxUtils.execute("RF_sol_function_FeedComment", params);
      },

      /**
       * Starts a workflow and returns the new workflow Id.
       * Uses {@link sol.common.WfUtils#startWorkflow WfUtils.startWorkflow}.
       *
       *
       *
       * @param {String} objId
       * @param {String} templateId
       * @param {String} name The workflow name
       * @return {String} The workflow ID
       */
      startWorkflow: function (objId, templateId, name) {
        return sol.common.WfUtils.startWorkflow(templateId, name, objId);
      },

      /**
       * Starts the workflow defined in the as standard workflow for the mask or in an index field.
       * If there is already an workflow, it will do nothing and return the first active one.
       * Uses {@link sol.common.WfUtils#startMaskStandardWorkflow WfUtils.startMaskStandardWorkflow}.
       *
       * @param {String} objId
       * @param {Object} params (optional) Default will be the sord name
       * @param {Object} params.name (optional) Default will be the sord name
       * @param {Object} params.field (optional) The field to read the workflow template from
       * @return {String} The workflow ID
       */
      startMaskStandardWorkflow: function (objId, params) {
        return sol.common.WfUtils.startMaskStandardWorkflow(objId, params);
      },

      /**
       * @private
       * Builds a json response including events, messages.
       * @return {String}
       */
      buildResponse: function () {
        var me = this,
            response = {};

        if (me.arrayIsNotEmpty(me._registeredEvents)) {
          response.events = me._registeredEvents;
        }

        if (me.arrayIsNotEmpty(me._messages)) {
          response.messages = me._messages;
        }

        response.data = me._data;

        return sol.common.JsonUtils.stringifyAll(response);
      },

      /**
       * @abstract
       * @private
       * @param {String} type The type of event which should be executed after function returns
       * @param {Object} params Object with key-value-pairs to configure the event
       * @param {Object} on (optional) Object with conditions for the event execution (see class documentation)
       */
      createEvent: function () {
        throw "cannot call 'createEvent' of class 'sol.common.ActionBase', function has to be implemented by subclass";
      },

      /**
       * @private
       * Checks if an Array is empty or not.
       * @param {Array} array
       * @returns {Boolean}
       */
      arrayIsNotEmpty: function (array) {
        return array && array.length > 0;
      }

    });