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

//@include lib_Class.js
//@include lib_sol.common.ObjectUtils.js
//@include lib_sol.common.SordUtils.js
//@include lib_sol.common.AclUtils.js
//@include lib_sol.common.RepoUtils.js
//@include lib_sol.common.AsyncUtils.js
//@include lib_sol.common.UserUtils.js
//@include lib_sol.common.ObjectFormatter.js
//@include lib_sol.common.Template.js
//@include lib_sol.common.ix.FunctionBase.js

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

/**
 * Changes rights.
 *
 * Add rights based on a configuration. It can also perform a cleanup before setting the new rights.
 *
 * # Node configuration example:
 *
 *     {
 *       "users": ["user1"],      // user1 will get
 *       "rights": { "r": true }, // read access
 *       "mode": "SET"            // while all other rights will be removed
 *     }
 *
 *     {
 *       "users": ["group2"],     // group2 will get
 *       "rights": { "w": true }, // write access
 *       "mode": "ADD"            // in addition to the other (already existing) rights
 *     }
 *
 * Without configuration the current user will get full rights for the given
 * object and it's children.
 *
 * # Extended configuration:
 *
 *     {
 *       "users": [
 *         { "name": "Mustermann" },                                          // user 'Mustermann' gets the fallback rights
 *         { "name": "Haenschenklein", "rights": { "r": true, "w": true } },  // user 'Haenschenklein' gets read and write access
 *         { "type": "GRP", "key": "MY_USER" }                                // if there is a user configured in index field 'MY_USER', it gets the fallback rights
 *       ],
 *       "andGroups": [
 *         { "groups": ["GroupA", "GroupB", { "type": "GRP", "key": "CONTRACT_RESPONSIBLE" }] }, // and-group (containing three groups) with fallback rights
 *         { "groups": ["GroupX", "GroupY"], "rights": { "r": true, "w": true, "l": true } }         // and-group (containing two groups) with read, write and list access
 *       ],
 *       "rights": { "r": true }
 *     }
 *
 * # Cleanup
 * This function can also perform a cleanup (see {@link #cleanup}).
 * If new rights are configured, this cleanup step will be performed before the new rights will be set.
 *
 * The following example will remove the 'delete' permission for user 'kraft' and add 'read' permission for user 'renz':
 *
 *     {
 *       "users": [
 *         "renz"
 *       ],
 *       "cleanup": {
 *         "users": [
 *           { "name": "kraft", "rights": { "d": true } }
 *         ]
 *       },
 *       "rights": { "r": true }
 *     }
 *
 * @author MW, ELO Digital Office GmbH
 * @version 1.03.000
 *
 * @eloix
 *
 * @requires sol.common.SordUtils
 * @requires sol.common.AclUtils
 * @requires sol.common.JsonUtils
 * @requires sol.common.RepoUtils
 * @requires sol.common.Utils
 * @requires sol.common.ObjectFormatter
 * @requires sol.common.Template
 * @requires sol.common.WfUtils
 * @requires sol.common.ix.RfUtils
 * @requires sol.common.ix.FunctionBase
 */
sol.define("sol.common.ix.functions.ChangeRights", {
  extend: "sol.common.ix.FunctionBase",

  requiredConfig: ["objId"],

  /**
   * @cfg {String} objId (required)
   */

  /**
   * @cfg {String} objIds If the processing should be executed on more then one element. `objId` is still required, as base for the data.
   */

  /**
   * @cfg {String[]|Object[]} users
   * Specifies, which users rights will be altered (see {@link sol.common.AclUtils} and especially {@link sol.common.AclUtils#changeRightsInBackground changeRightsInBackground}).
   * Additionally the user object can have a `type` and a `key` property (see {@link sol.common.SordUtils#getValue}), which will be used to determine the user name.
   */

  /**
   * @cfg {Object[]} andGroups
   * An array with and-group definitions.
   * See {@link sol.common.AclUtils#changeRightsInBackground changeRightsInBackground}
   */

  /**
   * @cfg {Object} rights
   * Rights that will be set to the workflow object and it's children.
   * See {@link sol.common.AclUtils#changeRightsInBackground changeRightsInBackground}
   */

  /**
   * @cfg {String} mode See {@link sol.common.AclUtils#changeRightsInBackground changeRightsInBackground}
   */

  /**
   * @cfg {Boolean} dontWait See {@link sol.common.AclUtils#changeRightsInBackground changeRightsInBackground}
   */

  /**
   * @cfg {Boolean} recursive See {@link sol.common.AclUtils#changeRightsInBackground changeRightsInBackground}
   */

  /**
   * @cfg {String} wfOwner
   * This attribute will be used, if the cleanup specifies to remove rights of the `wfOwner`.
   * If this function is used in a workflow, this will be set automatically.
   */

  /**
   * @cfg {Object} cleanup Configuration of a cleanup, before the new rights will be applied
   * @cfg {String[]|Object[]} cleanup.users Specifies, which users rights will be removed (see {@link sol.common.AclUtils#changeRightsInBackground changeRightsInBackground}).
   * @cfg {Boolean} cleanup.revokeWfOwnerRights If `true` and the property `wfOwner` is set, `wfOwner` loses all access rights
   * @cfg {Object} cleanup.rights Specifies, which rights will be removed in case the user object does not state otherwise (see {@link sol.common.AclUtils#changeRightsInBackground changeRightsInBackground}). If nothing is set, all rights will be removed.
   */

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

  /**
   * Adds rights to an object or branch.
   */
  process: function () {
    var me = this,
        rightsConfig = {};

    me.performCleanup();

    if (me.inherit || me.users || me.andGroups) {
      rightsConfig.inherit = me.inherit;
      rightsConfig.users = me.users;
      rightsConfig.andGroups = me.andGroups;
      rightsConfig.rights = me.rights;
      rightsConfig.mode = me.mode;
      rightsConfig.dontWait = me.dontWait;
      rightsConfig.recursive = me.recursive;
      rightsConfig.flowId = me.flowId;

      me.performBackgroundAclJob(rightsConfig);
    }
  },

  /**
   * @private
   * Uses {@link sol.common.AclUtils#changeRightsInBackground changeRightsInBackground} to remove specific user rights.
   */
  performCleanup: function () {
    var me = this,
        removeRightsConfig = {},
        fullRemove = { r: true, w: true, d: true, e: true, l: true, p: true };

    if (me.cleanup && me.mode != "SET") { // cleanup is redundant, if mode is "SET", because all previous rights will be overriden anyway
      removeRightsConfig.users = (me.cleanup.users && (me.cleanup.users.length > 0)) ? me.cleanup.users : [];
      removeRightsConfig.rights = (me.cleanup.rights) || fullRemove;
      removeRightsConfig.mode = "REMOVE";
      removeRightsConfig.dontWait = me.dontWait;
      removeRightsConfig.recursive = me.recursive;

      if (me.wfOwner && (me.cleanup.revokeWfOwnerRights === true)) {
        removeRightsConfig.users.push({ name: me.wfOwner, rights: fullRemove });
      }

      if (removeRightsConfig.users.length > 0) {
        me.performBackgroundAclJob(removeRightsConfig);
      }
    }
  },

  /**
   * @private
   * Performs the background job to change the rights.
   * @param {Object} rightsConfig
   */
  performBackgroundAclJob: function (rightsConfig) {
    var me = this,
        objId;

    if (me.objIds) {
      objId = me.objIds;
      rightsConfig.srcObjId = rightsConfig.srcObjId || me.objId;
    } else {
      objId = me.objId;
    }

    sol.common.AclUtils.changeRightsInBackground(objId, rightsConfig);
  }

});

/**
 * This is a wrapper class for {@link sol.common.ix.functions.ChangeRights ChangeRights}.
 *
 * It supports the following modes:
 *
 * - single element
 * - children (permission groups)
 *
 * # Single element
 * The configuration will be passed to {@link sol.common.ix.functions.ChangeRights ChangeRights}.
 *
 * ## Node configuration example:
 * See {@link sol.common.ix.functions.ChangeRights ChangeRights}.
 *
 * # Children (permission groups)
 * Changes rights on the children (only first level) of an element.
 *
 * The script will read a permission group (or a list) from an index field of each individual child element.
 * If there is a configuration for that permission group(s), it will adjust the rights of the child element by handing that configuration over to {@link sol.common.ix.functions.ChangeRights ChangeRights}.
 *
 * ## Node configuration example:
 *
 *     {
 *       "processChildrenPermissions": true,
 *       "permissionGroupField": "RIGHT_GROUP",
 *       "permissionGroups": [
 *         { "name": "aclGroup1", "config": { ... } },
 *         { "name": "aclGroup1", "config": { ... } },
 *         ...
 *       ]
 *     }
 *
 * For the configuration of the single permission groups see {@link sol.common.ix.functions.ChangeRights ChangeRights}.
 * The name is used to determine which set of permissions should be applied on which child.
 *
 * @author PZ, ELO Digital Office GmbH
 * @version 1.03.000
 *
 * @eloix
 *
 * @requires sol.common.SordUtils
 * @requires sol.common.AclUtils
 * @requires sol.common.JsonUtils
 * @requires sol.common.RepoUtils
 * @requires sol.common.Utils
 * @requires sol.common.ObjectFormatter
 * @requires sol.common.Template
 * @requires sol.common.WfUtils
 * @requires sol.common.ix.RfUtils
 * @requires sol.common.ix.FunctionBase
 */
sol.define("sol.common.ix.functions.ChangeRightsWrapper", {
  extend: "sol.common.ix.FunctionBase",

  /**
   * @cfg {Boolean} processChildrenPermissions
   */

  /**
   * @cfg {String} permissionGroupField The index field containing the permission group(s) for the child.
   */

  /**
   * @cfg {Object[]} permissionGroups
   * The configurations for the different permission groups. For configuration of each individual group see {@link sol.common.ix.functions.ChangeRights ChangeRights}.
   * `objId` (and `objIds`) do not have to be configured, the wrapper will take care of those.
   */

  MODES: {
    SINGLE: "SINGLE",
    CHILDREN: "CHILDREN"
  },

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

  /**
   * Performs the processing.
   */
  process: function () {
    var me = this,
        mode;

    me.conn = (typeof ixConnectAdmin !== "undefined") ? ixConnectAdmin : ixConnect;

    mode = me.determineMode();

    switch (mode) {
      case me.MODES.CHILDREN:
        me.performChildrenRun();
        break;
      default:
        me.performSingleRun();
    }
  },

  /**
   * @private
   * Determines in which mode the function was executed.
   * @return {String}
   */
  determineMode: function () {
    var me = this;
    if ((me.processChildrenPermissions === true) && me.permissionGroupField && me.permissionGroups) {
      return me.MODES.CHILDREN;
    }
    return me.MODES.SINGLE;
  },

  /**
   * @private
   * Performs a standard change rights run.
   */
  performSingleRun: function () {
    var me = this,
        module;

    module = sol.create("sol.common.ix.functions.ChangeRights", me.$config);
    module.process();
  },

  /**
   * @private
   * Performs a change rights run on all the children depending on the configured permission group per children.
   */
  performChildrenRun: function () {
    var me = this,
        execBuckets = {},
        permissionGroup, startIds, permissionGrpCfg, module;

    me.processChildren(execBuckets);

    for (permissionGroup in execBuckets) {
      if (execBuckets.hasOwnProperty(permissionGroup) && execBuckets[permissionGroup] && (execBuckets[permissionGroup].length > 0)) {
        startIds = execBuckets[permissionGroup].map(function (child) {
          return child.id;
        });
        permissionGrpCfg = me.getPermissionConfig(permissionGroup);

        if (permissionGrpCfg) {
          permissionGrpCfg.objId = me.objId;
          permissionGrpCfg.objIds = startIds;

          module = sol.create("sol.common.ix.functions.ChangeRights", permissionGrpCfg);
          module.process();

          permissionGrpCfg = null;
          module = null;
        } else {
          me.logger.warn(["no config for permission group '{0}' found", permissionGroup]);
        }
      }
    }
  },

  /**
   * @private
   * Processes all child elements on the first level.
   * @param {Object} execBuckets
   */
  processChildren: function (execBuckets) {
    var me = this,
        children;

    me.logger.debug(["processChildren: conn.user.name={0}", me.conn.loginResult.user.name]);

    children = sol.common.RepoUtils.findChildren(me.objId, {
      includeFolders: true,
      includeDocuments: false,
      includeReferences: false,
      sordZ: SordC.mbAllIndex
    }, me.conn);

    if (children && (children.length > 0)) {
      children.forEach(function (child) {
        me.processChild(child, execBuckets);
      });
    }
  },

  /**
   * @private
   * Determines the right groups for a child element and adds them to the execution buckets.
   * @param {de.elo.ix.client.Sord} child
   * @param {Object} execBuckets
   */
  processChild: function (child, execBuckets) {
    var me = this,
        permissionGroups;

    permissionGroups = sol.common.SordUtils.getObjKeyValues(child, me.permissionGroupField);

    if (permissionGroups && (permissionGroups.length > 0)) {
      permissionGroups.forEach(function (permissionGroup) {
        if (execBuckets[permissionGroup]) {
          execBuckets[permissionGroup].push(child);
        } else {
          execBuckets[permissionGroup] = [child];
        }
      });
    }
  },

  /**
   * @private
   * Loads the permission config for a specified group.
   * @param {String} permissionGroup
   * @return {Object}
   */
  getPermissionConfig: function (permissionGroup) {
    var me = this,
        permissionCfg = null;

    if (me.permissionGroups && (me.permissionGroups.length > 0)) {
      me.permissionGroups.some(function (pg) {
        if (pg.name == permissionGroup) {
          permissionCfg = pg.config;
          return true;
        }
      });
    }

    return permissionCfg;
  }

});


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

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

  sol.common.WfUtils.checkMainAdminWf(wFDiagram);
  params = sol.common.WfUtils.parseAndCheckParams(wFDiagram, nodeId);
  params.objId = wFDiagram.objId;
  params.flowId = wFDiagram.id;
  params.wfOwner = wFDiagram.ownerName;
  params.objIds = null;

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

  logger.exit("onEnterNode_ChangeRights");
}


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

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

  sol.common.WfUtils.checkMainAdminWf(wFDiagram);
  params = sol.common.WfUtils.parseAndCheckParams(wFDiagram, nodeId);
  params.objId = wFDiagram.objId;
  params.flowId = wFDiagram.id;
  params.wfOwner = wFDiagram.ownerName;
  params.objIds = null;

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

  logger.exit("onExitNode_ChangeRights");
}


/**
 * @member sol.common.ix.functions.ChangeRightsWrapper
 * @method RF_sol_function_ChangeRights
 * @static
 * @inheritdoc sol.common.ix.FunctionBase#RF_FunctionName
 */
function RF_sol_function_ChangeRights(ec, args) {
  var params, module;

  logger.enter("RF_sol_function_ChangeRights", args);

  params = sol.common.ix.RfUtils.parseAndCheckParams(ec, arguments.callee.name, args, "objId");
  sol.common.ix.RfUtils.checkMainAdminRights(ec.user, params);

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

  logger.exit("RF_sol_function_ChangeRights");
}