    importPackage(Packages.de.elo.ix.client);

    //@include lib_Class.js
    //@include lib_sol.common.AclUtils.js
    //@include lib_sol.common.UserUtils.js
    //@include lib_sol.common.WfUtils.js
    //@include lib_sol.common.ix.FunctionBase.js

    var logger = sol.create("sol.Logger", { scope: "sol.common.ix.functions.UserDispatcher" });

    /**
     * Decides, which direction the workflow should be dispatched, regarding the configured requirements.
     *
     * Can be used as node exit script. It updates the ELO_WF_STATUS field as configured. The status can be used in a decision node.
     *
     * # As workflow node
     *
     * Following configuration can be applied to the comments field.
     *
     *     {
     *       "wfStatus": { "onSuccess": "IS_IN_GROUP", "onFailure": "NOT_IN_GROUP" },
     *       "requirements": [
     *         { "type": "inGroup", "value": "sol.pubsec.admin.Record" }
     *       ]
     *     }
     *
     * @author PZ, ELO Digital Office GmbH
     * @version 1.03.000
     *
     * @eloix
     * @requires  sol.common.UserUtils
     * @requires  sol.common.WfUtils
     * @requires  sol.common.ix.FunctionBase
     *
     */
    sol.define("sol.common.ix.functions.UserDispatcher", {
      extend: "sol.common.ix.FunctionBase",

      requiredConfig: ["wfDiagram", "userId", "objId"],

      /**
       * @cfg {de.elo.ix.client.WFDiagram} wfDiagram (required)
       * The workflow which should be checked.
       */

      /**
       * @cfg {String} userId (required)
       * The ID of the user, who should be checked.
       */

      /**
       * @cfg {String} objId (required)
       * ID of the element which should be checked
       */

      /**
       * @cfg {Object} wfStatus
       *
       *     "wfStatus": { "onSuccess": "left", "onFailure": "right" }
       *
       * This object can override the default workflow states which will be set after the check, if ths was used in a workflow node.
       * If the is `undefined`, the function will throw an exception in case the checke fails.
       *
       * - `onSuccess`: set as ELO_WF_STATUS after a successfull check
       * - `onFailure`: set as ELO_WF_STATUS after a check failure
       */

      /**
       * @cfg {Object[]} requirements
       * This is an array with configurations which requirements a user has to fullfill. If empty, the check always succeeds.
       *
       *     "requirements": [
       *        { "type": "inGroup", "value": "sol.pubsec.admin.Record" },
       *        { "type": "inGroup", "value": "sol.pubsec.sysadmin.Record" },
       *        { "type": "hasEffectiveRights", "rights": { "rights": { "d": "true" } } }
       *     ]
       *
       * With this configuration the check would only succeed, if the user is in both groups ('sol.pubsec.admin.Record' and 'sol.pubsec.sysadmin.Record').
       * The value can also be the group ID.
       */

      /**
       * @cfg {Object} nextNodes
       *
       *     "nextNodes": { "onSuccess": ["OK", "Approve"], "onFailure": ["Cancel", "Reject"] }
       *
       *     or
       *
       *     "nextNodes": { "onSuccess": "OK", "onFailure": "Cancel" }
       *
       * This object determines the next nodes to activate in dependence of the workflow status.
       *
       * - `onSuccess`: array of next nodes or string of one next node to activate after a successfull check
       * - `onFailure`: array of next nodes or string of one next node to activate after a check failure
       */

      /**
       * @private
       * @property {String} [DEFAULT_PASSED_STATUS="SUCCESS"] Default workflow status in case of a successful check
       */
      DEFAULT_PASSED_STATUS: "SUCCESS",

      /**
       * @private
       * @property {String} [DEFAULT_FAILED_STATUS="FAILURE"] Default workflow status in case of a check failure
       */
      DEFAULT_FAILED_STATUS: "FAILURE",

      initialize: function (config) {
        var me = this;
        me.$super("sol.common.ix.FunctionBase", "initialize", [config]);
      },

      /**
       * Performs the check.
       */
      process: function () {
        var me = this,
            status, nextNodes, i, node, succNodes;

        if (me.checkRequirements()) {
          status = (me.wfStatus && me.wfStatus.onSuccess) ? me.wfStatus.onSuccess : me.DEFAULT_PASSED_STATUS;
          nextNodes = (me.nextNodes && me.nextNodes.onSuccess) ? me.nextNodes.onSuccess : undefined;
        } else {
          status = (me.wfStatus && me.wfStatus.onFailure) ? me.wfStatus.onFailure : me.DEFAULT_FAILED_STATUS;
          nextNodes = (me.nextNodes && me.nextNodes.onFailure) ? me.nextNodes.onFailure : undefined;
        }

        sol.common.WfUtils.setWorkflowStatus(me.wfDiagram, status);
        if (nextNodes) {
          if (sol.common.ObjectUtils.isString(nextNodes)) {
            nextNodes = [nextNodes];
          } else if (!sol.common.ObjectUtils.isArray(nextNodes)) {
            throw "nextNodes has incorrect datatype";
          }
          succNodes = sol.common.WfUtils.getSuccessorNodes(me.wfDiagram, me.nodeId);
          for (i = 0; i < succNodes.length; i++) {
            node = succNodes[i];
            if (nextNodes.indexOf(String(node.name)) < 0) {
              node.allowActivate = false;
            } else {
              node.isNext = 1;
            }
          }
        }
      },

      /**
       * @private
       * Checks, if the configured requirements are meet.
       * @return {Boolean}
       */
      checkRequirements: function () {
        var me = this,
            checkResult = false;

        if (me.requirements && (me.requirements.length > 0)) {
          checkResult = me.requirements.every(function (requirement) {
            return me.checkRequirement(requirement);
          });
        } else {
          checkResult = true;
        }
        return checkResult;
      },

      /**
       * @private
       * Checks an inividual requirement.
       * @param {Object} requirement
       * @return {Boolean}
       */
      checkRequirement: function (requirement) {
        var me = this;
        switch (requirement.type) {
          case "inGroup":
            return sol.common.UserUtils.isInGroup(requirement.value, { userId: me.userId });
          case "hasEffectiveRights":
            return sol.common.AclUtils.hasEffectiveRights(me.objId, { rights: requirement.rights });
          default:
            return false;
        }
      }

    });

    /**
     * @member sol.common.ix.functions.UserDispatcher
     * @static
     * @inheritdoc sol.common.ix.FunctionBase#onExitNode
     */
    function onExitNode(clInfo, userId, wFDiagram, nodeId) {
      var params, module;

      logger.enter("onExitNode_UserDispatcher", { flowId: wFDiagram.id, nodeId: nodeId });

      params = sol.common.WfUtils.parseAndCheckParams(wFDiagram, nodeId);
      params.wfDiagram = wFDiagram;
      params.userId = userId;
      params.objId = wFDiagram.objId;
      params.nodeId = nodeId;

      module = sol.create("sol.common.ix.functions.UserDispatcher", params);
      module.process();

      logger.exit("onExitNode_UserDispatcher");
    }