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

    //@include lib_Class.js
    //@include lib_sol.common.IxUtils.js
    //@include lib_sol.common.Template.js
    //@include lib_sol.common.Injection.js
    //@include lib_sol.common.UserUtils.js
    //@include lib_sol.common.JsonUtils.js
    //@include lib_sol.common.SordUtils.js
    //@include lib_sol.common.ObjectUtils.js
    //@include lib_sol.common.ix.FunctionBase.js
    //@include lib_sol.common.ObjectFormatter.MapTableToArray.js

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

    /**
     * Creates a new ELO group and fills it with members by default.
     * Returns an object with the name of the group and the groupId.
     *
     * ### Output
     *  {
     *    "name": "groupname",
     *    "groupId": "4711"
     *  }
     *
     * If no predefined groupId is passed, one is automatically generated by ELO
     *
     *
     * It is possible to add some members to the created group directly
     * {@link sol.common.ix.functions.ChangeGroup}
     *
     *  {
     *     addMembers: [
     *       "renz",
     *       { "type": "GRP", "key": "ELOINDEX" },
     *       { "type", "MAP", "key": "USERNAME", table: true }
     *      ]
     *    }
     *
     * In addition when you want to add the created group is memberOf another group you can declare
     * the following configuration
     *
     *    {
     *      memberOf: [sol.common.roles.StandardUsers]
     *    }
     * This is really handy when you want to give some default rights to the new created groups.
     * For example the new created group should be have the role `StandardUsers`
     *
     * If the group already exists, no further operations will be performed with this
     * groups. Only if the group is newly created.
     *
     * ### saveToField
     *
     * Finally save the newly created group in a field so that it can be easily retrieved later
     *
     *   {
     *      saveGroupToField: { type: "GRP", key: "ELOINDEX" }
     *   }
     *
     * Internal is {@link sol.common.ix.function.Set} used, so the config is delegate to the
     * set function {@see sol.common.ix.function.Set#entries}.
     *
     *
     * # As workflow node
     *
     * ObjId is set based on the element that the workflow is attached to.
     * Following additional configuration can be applied to the comments field.
     *
     *     {
     *      "groupName": "New Group",
     *      "groupId": "new group id"
     *     }
     *
     * # As IX function call
     *
     * In addition to the workflow node configuration the objId must be passed.
     *
     *     sol.common.IxUtils.execute("RF_sol_function_CreateGroup", {
     *       objId: "4712",
     *       name: "New Group"
     *       memberOf: [ "sol.common.roles.StandardUsers" ],
     *       addMembers: ["renz", "kraft"]
     *     });
     *
     * @eloix
     * @requires sol.common.IxUtils
     * @requires sol.common.WfUtils
     * @requires sol.common.Template
     * @requires sol.common.Injection
     * @requires sol.common.ix.RfUtils
     * @requires sol.common.ix.JsonUtils
     * @requires sol.common.ix.ObjectUtils
     * @requires sol.common.ix.FunctionBase
     * @requires sol.common.ObjectFormatter.MapTableToArray
     *
     */
    sol.define("sol.common.ix.functions.CreateGroup", {
      extend: "sol.common.ix.FunctionBase",

      /**
       * @cfg {String} groupName
       * The name of the new group. Templating is possible if objId/flowId is passed to the function
       */

      /**
       * @cfg {String|number} groupId (optional)
       * If `groupId` is set the passed id will be used by creation otherwise the id will generate randomily
       */

      /**
       * @cfg {Array<String>} memberOf (optional)
       * Assign the nwe created group to each group which contains `memberOf`
       */

      /**
       * @cfg {Array} addUsers (optional)
       * Add each user to the new created group. usernames could be added directly or from sord fields
       */

      /**
       * @cfg {Boolean} [asAdmin=false] (optional)
       * When `asAdmin` is set to true the operation will be execute with an admin connection
       */

      mixins: ["sol.common.mixins.Inject"],

      inject: {
        name: { prop: "groupName", template: true },
        sord: { sordIdFromProp: "objId", flowIdFromProp: "flowId", optional: false }
      },

      /**
       * @cfg {String} name
       */

      /**
       * @param {String | Array<String>} memberOf
       */

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

        if (config.memberOf && !sol.common.ObjectUtils.isArray(config.memberOf)) {
          config.memberOf = [config.memberOf];
        }

        if (config.addMembers && !sol.common.ObjectUtils.isArray(config.addMembers)) {
          config.addMembers = [config.addMembers];
        }

        me.$super("sol.common.ix.FunctionBase", "initialize", [config]);
        me.connection = ((me.asAdmin === true) && (typeof ixConnectAdmin !== "undefined")) ? ixConnectAdmin : ixConnect;
      },

      /**
       *
       */
      process: function () {
        var me = this, createdGroupId, saveGroupToField;

        try {
          createdGroupId = me.createGroup(me.name, me.prepareGroupConfig());

          if (!createdGroupId) {
            me.logger.warn(["Group {0} already exists. Skip creation...", me.name]);
          }

          if (me.memberOf) {
            // add created group to specific group (for example roles `sol.meeting.roles.Organizer`)
            me.addMemberToGroup([me.name], me.memberOf);
          }

          if (me.addMembers) {
            sol.common.IxUtils.execute("RF_sol_function_ChangeGroup", {
              objId: me.objId,
              flowId: me.flowId,
              groupName: me.name,
              addMembers: me.addMembers,
              asAdmin: me.asAdmin || false
            });
          }

          if (me.shouldSaveGroupToField(createdGroupId)) {
            saveGroupToField = {
              type: me.saveGroupToField.type,
              key: me.saveGroupToField.key,
              value: me.name
            };

            sol.common.IxUtils.execute("RF_sol_function_Set", {
              objId: me.objId,
              entries: [saveGroupToField]
            });
          }

        } catch (ex) {
          me.logger.warn(["An error occured {0}", ex.message], ex);
          throw ex;
        }

        return {
          name: me.name,
          groupId: createdGroupId
        };
      },

      shouldSaveGroupToField: function (createdGroupId) {
        var me = this;
        me.logger.info(["should save objId={0}, createdGroupId={1}, {2}", me.objId, createdGroupId])
        return me.objId && createdGroupId && me.saveGroupToField;
      },

      createGroup: function (name, groupConfig) {
        var me = this, groupId;
        try {
          // Attention: groupConfig is not serializable
          groupId = String(sol.common.UserUtils.createUser(name, groupConfig));
        } catch (ex) {
          me.logger.error(["could not create group {0}", name], ex);
          throw ex;
        }

        // api function here is a little bit weird we can create groups here as well
        me.logger.info(["created elo group {0} groupId= {1}", name, groupId]);
        return groupId;
      },

      addMemberToGroup: function (names, targetGroups) {
        var me = this;
        me.logger.debug(["addUsersToGroups {0} {1}", names, targetGroups]);
        sol.common.UserUtils.addUsersToGroups(names, targetGroups, {
          connection: me.connection
        });
      },

      prepareGroupConfig: function () {
        var me = this, config = {
          type: "group",
          connection: me.connection
        };

        // set fix elo group id
        me.groupId && (config.id = me.groupId);
        return config;
      }
    });

    /**
     * @member sol.common.ix.functions.CreateGroup
     * @static
     * @inheritdoc sol.common.ix.FunctionBase#onEnterNode
     */
    function onEnterNode(clInfo, userId, wFDiagram, nodeId) {
      logger.enter("onEnterNode_CreateGroup", { flowId: wFDiagram.id, nodeId: nodeId });
      var params = sol.common.WfUtils.parseAndCheckParams(wFDiagram, nodeId),
          module;

      sol.common.WfUtils.checkMainAdminWf(wFDiagram);

      params.objId = String(wFDiagram.objId);
      params.flowId = String(wFDiagram.id);
      module = sol.create("sol.common.ix.functions.CreateGroup", params);

      module.process();

      logger.exit("onEnterNode_CreateGroup");
    }

    /**
     * @member sol.common.ix.functions.CreateGroup
     * @static
     * @inheritdoc sol.common.ix.FunctionBase#onExitNode
     */
    function onExitNode(clInfo, userId, wFDiagram, nodeId) {
      logger.enter("onExitNode_CreateGroup", { flowId: wFDiagram.id, nodeId: nodeId });
      var params = sol.common.WfUtils.parseAndCheckParams(wFDiagram, nodeId),
          module;

      sol.common.WfUtils.checkMainAdminWf(wFDiagram);

      params.objId = wFDiagram.objId;
      params.flowId = String(wFDiagram.id);
      module = sol.create("sol.common.ix.functions.CreateGroup", params);

      module.process();

      logger.exit("onExitNode_CreateGroup");
    }

    /**
     * @member sol.common.ix.functions.CreateGroup
     * @method RF_sol_function_CreateGroup
     * @static
     * @inheritdoc sol.common.ix.FunctionBase#RF_FunctionName
     */
    function RF_sol_function_CreateGroup(ec, args) {
      logger.enter("RF_sol_function_CreateGroup", args);
      var module, result, params = sol.common.ix.RfUtils.parseAndCheckParams(ec, arguments.callee.name, args);

      sol.common.ix.RfUtils.checkMainAdminRights(ec.user, params);
      module = sol.create("sol.common.ix.functions.CreateGroup", params);

      result = module.process();
      logger.exit("RF_sol_function_CreateGroup");
      return sol.common.JsonUtils.stringifyQuick(result);
    }