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

    //@include lib_Class.js
    //@include lib_sol.common.Template.js
    //@include lib_sol.common.WfUtils.js
    //@include lib_sol.common.JsonUtils.js
    //@include lib_sol.common.RepoUtils.js
    //@include lib_sol.common.AclUtils.js
    //@include lib_sol.common.AsyncUtils.js
    //@include lib_sol.common.SordUtils.js
    //@include lib_sol.common.SordTypeUtils.js
    //@include lib_sol.common.IxUtils.js
    //@include lib_sol.common.ix.RfUtils.js
    //@include lib_sol.common.ix.FunctionBase.js

    /**
     * Creates a sord in the chaos cabinet (0) based on a template or from scratch. Executes specified actions on the created element.
     * Returns the objId (and flowId, if a workflow has been started) of the created element if called as an RF.
     *
     * Usually, this function will be used in conjunction with {@link sol.common.ix.functions.FillSord FillSord}, which
     * can be used to fill metadata-fields of the created sord. (CreateSord can be called from within FillSord)
     *
     * ### Creating a sord using a template
     *
     * A template can be copied, resulting in a new sord.
     *
     *     {
     *       sourceElement: { objId: "ARCPATH:/path/to/my/template" }
     *     }
     *
     * Optionally, the templates permissions (acl) can be applied to the created sord:
     *
     *     {
     *       sourceElement: { objId: "ARCPATH:/path/to/my/template" },
     *       options: { copySourceAcl: true }
     *     }
     *
     * ### Creating a sord from scratch
     *
     * If no template is available, the following can be used to create a new sord from scratch
     *
     *     {
     *       sourceElement: { fromScratch: { mask: "Basic Entry", type: "sol.mytype" } }
     *     }
     *
     * ### Retrieving an objId from a service
     *
     * If the sord creation requires a more sophisticated process than a simple template-copy or `fromScratch`,
     * one can define an IX-RF-service/function which e.g. speaks to external systems and returns the objId
     * of the created sord.
     *
     *     {
     *       sourceElement: {
     *         fromService: { name: "RF_CreateSordByDate", params: { a: "x", b: "z"} }
     *       }
     *     }
     *
     *     // this service must return one of the following objects:
     *     { objId: "123456" }
     *     { fromScratch: { mask: "Basic Entry", type: undefined } } // type is optional
     *
     * ### Moving the created sord into a folder
     *
     * As a default, the sord is created in the chaos cabinet (objId 0), however, one must define
     * an objId/ARCPATH/GUID. The created sord will then be moved into this folder after creation.
     * It is possible to define "0" here, if one wants to leave the created sord in the chaos cabinet.
     *
     *     { targetFolder: { objId: "ARCPATH:/where/all/my/sords/go" } }
     *
     * ### Determining the targetFolder objId from a service
     *
     * A Service can be defined instead of specifying a fixed path/objId
     *
     *     {
     *       targetFolder: {
     *         fromService: { name: "RF_PrepareTargetFolder", params: { a: "x", b: "z"} }
     *       }
     *     }
     *
     *     // this service must return an object having an objId or id property:
     *     { objId: "123456" }
     *     { id: "123456" }
     *
     * ### Setting Permissions
     *
     * {@link sol.common.AclUtils.changeRightsInBackground Permissions} are set while copying the template or after creating the sord from scratch.
     *
     *     {
     *       onCreatedElement: {
     *         setPermissions: {
     *           users: [ { name: "MY_GROUP", rights: { r: true, w: true } } ],
     *           recursive: true,
     *           mode: "SET"
     *         }
     *       }
     *     }
     *
     * ### Starting a workflow
     *
     * A workflow can be started by defining the workflow template name and optionally a title
     *
     *     {
     *       onCreatedElement: {
     *         startWorkflow: { name: "sol.mymodule.workflow", title: "this is a workflow title" }
     *       }
     *     }
     *
     * ### Examples
     *
     * ### Sord from scratch, fixed targetFolder and a workflow
     *
     * #### Arguments
     *
     *     {
     *       sourceElement: {
     *         fromScratch: { mask: "Basic Entry", type: "sol.mytype" }
     *       },
     *       targetFolder: { objId: "ARCPATH:/my/target/folder" },
     *       onCreatedElement: {
     *         setName: "My Sord Short Description",
     *         startWorkflow: { name: "sol.mymodule.myworkflow", title: "A workflow title" }
     *       }
     *     }
     *
     * #### Return value
     *
     *     {
     *       objId: "3982",
     *       flowId: "89"
     *     }
     *
     * @author ESt, ELO Digital Office GmbH
     * @version 1.0
     *
     * @eloix
     *
     * @requires sol.common.Template
     * @requires sol.common.WfUtils
     * @requires sol.common.JsonUtils
     * @requires sol.common.RepoUtils
     * @requires sol.common.SordUtils
     * @requires sol.common.AclUtils
     * @requires sol.common.AsyncUtils
     * @requires sol.common.SordTypeUtils
     * @requires sol.common.IxUtils
     * @requires sol.common.ix.RfUtils
     * @requires sol.common.ix.FunctionBase
     *
     */
    sol.define("sol.common.ix.functions.CreateSord", {
      extend: "sol.common.ix.FunctionBase",

      /**
       * @cfg {Object} sourceElement (optional) Defines, how the new sord will be created (copy template by `objId` or create new `fromScratch`)
       * @cfg {String} sourceElement.objId objId/ARCPATH/GUID of sord to use as template (optional if `fromScratch` is defined)
       * @cfg {Object} sourceElement.fromScratch (optional) options for creating the sord from Scratch
       * @cfg {String} [sourceElement.fromScratch.mask = "Basic Entry"] (optional) mask name
       * @cfg {String} sourceElement.fromScratch.type (optional) sord type name
       * @cfg {Object} sourceElement.fromService (optional) defines a service which must return an Object containing an `objId` or `fromScratch` definition
       * @cfg {String} sourceElement.fromService.name IX-RF name
       * @cfg {Object} [sourceElement.fromService.params = {}] (optional) argument which will be passed to the defined service
       * @cfg {Object} sourceElement.options (optional) additional options used during creation of the new element
       * @cfg {Boolean} [sourceElement.options.copySourceAcl = false] (optional) copy permissions of source element (only when `sourceElement.objId` is defined)
       */

      /**
       * @cfg {Object} targetFolder (optional) Defines, where the created sord should be put
       * @cfg {String} targetFolder.objId objId/ARCPATH/GUID of folder to put created sord in
       * @cfg {String} targetFolder.path (optional) path to folder (which will may created on the fly) NOT IMPLEMENTED YET!
       * @cfg {Object} targetFolder.fromService (optional) defines a service which must return an Object containing an `objId` (or `path` not implemented yet, see above) definition
       * @cfg {String} targetFolder.fromService.name IX-RF name
       * @cfg {Object} [targetFolder.fromService.params = {}] (optional) argument which will be passed to the defined service
       */

      /**
       * @cfg {Object} onCreatedElement (optional) Defines, which actions will pe performed on the created sord
       * @cfg {String} onCreatedElement.setName (optional) sets the sords short-description
       * @cfg {Object} onCreatedElement.setOwner (optional) tries to set `sord.userId` (owner) ...
       * @cfg {Boolean} [onCreatedElement.setOwner.fromConnection = false] (optional) ... to the current connection's user
       * @cfg {String} onCreatedElement.setOwner.toUser (optional) ... to the specified user NOT IMPLEMENTED YET!
       * @cfg {Object} [onCreatedElement.setPermissions = {}] (optional) sets {@link sol.common.AclUtils.changeRightsInBackground permissions} on the created sord
       * @cfg {Object} onCreatedElement.startWorkflow (optional) starts a workflow on the created sord
       * @cfg {String} onCreatedElement.startWorkflow.name workflow template name
       * @cfg {String} [onCreatedElement.startWorkflow.title = "Workflow"] (optional) specifies the workflow-title
       * @cfg {String} onCreatedElement.startWorkflow.startMaskStandardWorkflow (optional) start workflow defined in the specified field NOT IMPLEMENTED YET!
       */

      /**
       * @private
       * @property
       *
       * References objId and flowId
       */

      _moveFct: "RF_sol_function_Move",

      createSordFromScratch: function (cfg) {
        var me = this,
            sord,
            mask = cfg.mask || "Basic Entry",
            user = (me.user && me.user.id) || me.user,
            targetObjId;

        try {
          targetObjId = me.getTargetForCreatedSord().objId;

          me.logger.debug("Trying to create sord from scratch using configuration ", [cfg, targetObjId]);

          sord = ixConnect.ix().createSord(targetObjId, mask, EditInfoC.mbSord).sord;
        } catch (e) {
          me.logger.debug("could not create sord", e);
          throw new Error("CreateSord: could not create sord in chaos cabinet. Mask not available or insufficient permissions? mask:`" + mask + "`");
        }

        sord.name = (me.onCreatedElement && me.onCreatedElement.setName) || "temp";

        if (cfg.type) {
          try {
            sord.type = sol.common.SordTypeUtils.getSordTypeId(cfg.type);
          } catch (e) {
            me.logger.debug("could not retrieve sordtype via id", e);
            throw new Error("CreateSord: could not retrieve sordtype via id `" + cfg.type + "`");
          }
        }

        if (me.onCreatedElement && me.onCreatedElement.setOwner && ((me.onCreatedElement.setOwner.fromConnection && me.user) || (me.onCreatedElement.setOwner.toUser))) {
          if (me.onCreatedElement.setOwner.toUser) {
            throw new Error("CreateSord: `onCreatedElement.setOwner.toUser` is not implemented yet. Please define `onCreatedElement.setOwner.fromConnection` instead");
          }
          if (user) {
            sord.userId = user;
          }
        }

        try {
          return String(ixConnect.ix().checkinSord(sord, SordC.mbAllIndex, LockC.NO));
        } catch (e) {
          me.logger.debug("creating the sord failed during checkin", e);
          throw new Error("CreateSord: creating the sord failed during checkin. Invalid user?` user:" + cfg.user + "`");
        }
      },

      copyElement: function (cfg) {
        var me = this;
        me.logger.debug("Copy Template using configuration ", cfg);
        try {
          return sol.common.IxUtils.execute("RF_sol_function_CopyFolderContents", {
            objId: me.getTargetForCreatedSord().objId,
            source: String(cfg.objId),
            copySourceAcl: !!(cfg.options && cfg.options.copySourceAcl),
            inheritDestinationAcl: false,
            name: (me.onCreatedElement && me.onCreatedElement.setName) || "temp"
          });
        } catch (e) {
          me.logger.debug("could not copy template sord", e);
          throw new Error("CreateSord: could not copy template sord. Insufficient permissions for objId `" + cfg.objId + "`?");
        }
      },

      getTargetForCreatedSord: function () {
        var me = this,
            path = String(me.targetFolder.path || me.targetFolder.objId || "0"),
            preparePathConfig = {
              mask: me.targetFolder.mask,
              skipIfNotExists: false,
              returnDetails: true
            };

        return sol.common.RepoUtils.isObjId(path)
          ? { objId: path }
          : sol.common.RepoUtils.preparePath(path, preparePathConfig);
      },

      prepareSourceElement: function (src) {
        var me = this;
        if (!src) {
          throw new Error("CreateSord: `sourceElement` parameter not defined.");
        }
        if (src.objId) {
          me._createdElement.objId = String(me.copyElement(src));
        } else if (src.fromScratch) {
          me._createdElement.objId = me.createSordFromScratch(src.fromScratch, src.options);
        } else if (src.fromService && src.fromService.name) {
          try {
            me.logger.debug("sord creation fromService:", src.fromService);
            me.prepareSourceElement(sol.common.IxUtils.execute(src.fromService.name, src.fromService.params || {}));
          } catch (e) {
            me.logger.debug("sord creation fromService failed", e);
            throw new Error("CreateSord: sord creation fromService failed. Used RF:`" + src.fromService.name);
          }
        } else {
          throw new Error("Could not determine source in CreateSord. Source config must contain a property `objId`, `fromScratch` or `fromService` which defines a service returning said properties.");
        }
      },

      prepareTargetFolder: function (tgt) {
        var me = this;
        if (!tgt) {
          throw new Error("CreateSord: `targetFolder` parameter not defined.");
        }
        if (tgt.objId || tgt.id) {
          me._createdElement.targetPathId = String(tgt.objId || tgt.id);
        } else if (tgt.path) {
          if (!me.preparePath) {
            throw new Error("CreateSord: `targetFolder.path` is not implemented yet. Please define a `targetFolder.objId` instead");
          }
          me._createdElement.targetPathId = me.preparePath(String(tgt.path));
        } else if (tgt.fromService && tgt.fromService.name) {
          try {
            me.logger.debug("sord creation fromService:", tgt.fromService);
            me.prepareTargetFolder(sol.common.IxUtils.execute(tgt.fromService.name, tgt.fromService.params || {}));
          } catch (e) {
            me.logger.debug("target creation fromService failed", e);
            throw new Error("CreateSord: target creation fromService failed. Used RF:`" + tgt.fromService.name);
          }
        } else {
          throw new Error("Could not determine target folder in CreateSord. targetFolder config must contain a property `objId`, `path`, or `fromService` which defines a service returning said properties.");
        }
      },

      moveElement: function (rights) {
        var me = this,
            moveConfig = {
              objId: me._createdElement.objId,
              path: me._createdElement.targetPathId
            };

        if (rights) {
          moveConfig.rightsConfig = rights;
        }
        me.logger.debug("Moving created sord using config", moveConfig);

        try {
          sol.common.IxUtils.execute(me._moveFct, moveConfig);
        } catch (e) {
          me.logger.debug("moving the created sord failed", e);
          throw new Error("CreateSord: moving the created sord failed. Used RF:`" + me._moveFct);
        }
      },

      setElementPermissions: function (cfg) {
        var me = this;
        me.logger.debug("Setting sord permissions using config", cfg);
        sol.common.AclUtils.changeRightsInBackground(me._createdElement.objId, cfg || {});
      },

      startWorkflowOnElement: function (cfg) {
        var me = this, title = cfg.title || "Workflow";
        if (cfg.startMaskStandardWorkflow) {
          throw new Error("CreateSord: `startWorkflow.startMaskStandardWorkflow` is not implemented yet. Please define a `startWorkflow.name` instead");
        }
        me.logger.debug("Starting workflow using config", cfg);
        try {
          me._createdElement.flowId = sol.common.WfUtils.startWorkflow(cfg.name, title, me._createdElement.objId);
        } catch (e) {
          me.logger.debug("workflow start failed", e);
          throw new Error("CreateSord: starting workflow `" + cfg.name + "` on sord with objId `" + me._createdElement.objId + "` failed.`");
        }
      },

      finalizeCreatedElement: function (cfg) {
        var me = this;
        cfg = cfg || {};
        me.logger.debug("Finalizing created sord using config", cfg);
        if (me._createdElement.targetPathId !== "0") {
          me.moveElement(cfg.setPermissions);
        } else if (cfg.setPermissions) {
          me.setElementPermissions(cfg.setPermissions);
        }
        if (cfg.removePermissions) {
          cfg.removePermissions.mode = "REMOVE";
          me.setElementPermissions(cfg.removePermissions);
        }
        if (cfg.startWorkflow) {
          me.startWorkflowOnElement(cfg.startWorkflow);
        }
      },

      getServiceResult: function (element) {
        var me = this, result = { objId: element.objId };
        if (element.flowId) {
          result.flowId = String(element.flowId);
        }
        me.logger.debug("Sord has been created:", result);
        return result;
      },

      /**
       * @return {Object}
       * @return {String} return.objId objId of created sord
       * @return {String} return.flowId (optional) flowId, if a workflow has been started on the created sord
       */
      process: function () {
        var me = this;
        me._createdElement = {};

        me.logger.debug("Preparing `sourceElement`", me.sourceElement);
        me.prepareSourceElement(me.sourceElement);
        me.logger.debug("Created element so far", me._createdElement);
        me.logger.debug("Preparing `targetFolder`", me.targetFolder);
        me.prepareTargetFolder(me.targetFolder);
        me.logger.debug("Created element so far", me._createdElement);
        me.finalizeCreatedElement(me.onCreatedElement);

        return me.getServiceResult(me._createdElement);
      }
    });

    /**
     * @member sol.common.ix.functions.CreateSord
     * @static
     * @inheritdoc sol.common.ix.FunctionBase#onEnterNode
     */
    function onEnterNode(clInfo, userId, wfDiagram, nodeId) {
      var args, fun;

      args = sol.common.WfUtils.parseAndCheckParams(wfDiagram, nodeId);

      args.sourceElement = args.sourceElement || { objId: wfDiagram.objId, flowId: wfDiagram.id };

      fun = sol.create("sol.common.ix.functions.CreateSord", args);

      fun.process();
    }

    /**
     * @member sol.common.ix.functions.CreateSord
     * @static
     * @inheritdoc sol.common.ix.FunctionBase#onExitNode
     */
    function onExitNode(clInfo, userId, wfDiagram, nodeId) {
      var args, fun;

      args = sol.common.WfUtils.parseAndCheckParams(wfDiagram, nodeId);

      args.sourceElement = args.sourceElement || { objId: wfDiagram.objId, flowId: wfDiagram.id };

      fun = sol.create("sol.common.ix.functions.CreateSord", args);

      fun.process();
    }

    /**
     * @member sol.common.ix.functions.CreateSord
     * @method RF_sol_function_CreateSord
     * @static
     * @inheritdoc sol.common.ix.FunctionBase#RF_FunctionName
     */
    function RF_sol_function_CreateSord(iXSEContext, args) {
      var rfArgs, fun, logger = sol.create("sol.Logger", { scope: "sol.common.ix.functions.CreateSord" });
      logger.enter("RF_sol_function_CreateSord");

      rfArgs = sol.common.ix.RfUtils.parseAndCheckParams(iXSEContext, arguments.callee.name, args);

      fun = sol.create("sol.common.ix.functions.CreateSord", rfArgs);

      logger.exit("RF_sol_function_CreateSord");
      return JSON.stringify(fun.process());
    }