<?php

/**
 * @file
 * Rules integration for the Organic groups module.
 *
 * @addtogroup rules
 * @{
 */

/**
 * Implementation of hook_rules_event_info().
 *
 * Adds some additional event that make reacting on user subscriptions easier.
 */
function og_rules_event_info() {
  $defaults = array(
    'variables' => array(
      'og_membership' => array(
        'type' => 'og_membership',
        'label' => t("The user's OG membership"),
      ),
      'account' => array(
        'type' => 'user',
        'label' => t('User'),
      ),
    ),
    'group' => t('OG membership'),
  );
  return array(
    'og_user_insert' => $defaults + array(
      'label' => t('User has become a group member'),
      'help' => t("A user has become a group member, but might have been not yet approved."),
    ),
    'og_user_approved' => $defaults + array(
      'label' => t('User membership has been approved'),
    ),
    'og_user_blocked' => $defaults + array(
      'label' => t('User membership has been blocked'),
    ),
    'og_user_delete' => $defaults + array(
      'label' => t('User has been removed from group'),
      'help' => t("A user has been removed from group and is no longer a group member."),
   ),
 );
}

/**
 * Implements hook_rules_data_info_alter().
 */
function og_rules_data_info_alter(&$data_info) {
  $data_info['group']['ui class'] = 'OGRulesDataUIGroup';
}

/**
 * UI that adds in the group options list.
 */
class OGRulesDataUIGroup extends RulesDataUIEntity {

  public static function inputForm($name, $info, $settings, RulesPlugin $element) {
    // Add an options list of all groups if there is none yet.
    if (!isset($info['options list'])) {
      $info['options list'] = array('OGRulesDataUIGroup', 'optionsList');
    }
    return parent::inputForm($name, $info, $settings, $element);
  }

  public static function optionsList() {
    return og_label_multiple(og_get_all_group());
  }
}

/**
 * Implements hook_rules_action_info().
 */
function og_rules_action_info() {
  $items = array();
  $items['og_get_members'] = array(
    'label' => t('Get group members from group audience'),
    'group' => t('Organic groups'),
    'parameter' => array(
      'group_content' => array(
        'type' => 'entity',
        'label' => t('Group content'),
        'description' => t('The group content determining the group audience.'),
      ),
      // @todo: Add option to filter by member-state?
    ),
    'provides' => array(
      'group_members' => array('type' => 'list<user>', 'label' => t('List of group members')),
    ),
    'base' => 'og_rules_get_members',
    'access callback' => 'og_rules_integration_access',
  );

  $items['og_get_managers'] = array(
    'label' => t('Get group managers from group audience'),
    'group' => t('Organic groups'),
    'parameter' => array(
      'group_content' => array(
        'type' => 'entity',
        'label' => t('Group content'),
        'description' => t('The group content determining the group audience.'),
      ),
    ),
    'provides' => array(
      'group_managers' => array('type' => 'list<user>', 'label' => t('List of group managers')),
    ),
    'base' => 'og_rules_get_managers',
    'access callback' => 'og_rules_integration_access',
  );

  $items['og_get_group_content'] = array(
    'label' => t('Get group content from a group'),
    'group' => t('Organic groups'),
    'parameter' => array(
      'group' => array(
        'type' => 'group',
        'label' => t('Group'),
        'description' => t('The group for which to fetch content.'),
      ),
      'entity_type' => array(
        'type' => 'token',
        'label' => t('Entity type'),
        'description' => t('The entity type of the content which is to be fetched.'),
        'options list' => 'og_get_fieldable_entity_list',
      ),
    ),
    'provides' => array(
      'group_content' => array('type' => 'list<entity>', 'label' => t('Group content')),
    ),
    'base' => 'og_rules_get_group_content',
    'access callback' => 'og_rules_integration_access',
  );

  $items['og_group_content_add'] = array(
    'label' => t('Add entity to group'),
    'group' => t('Organic groups'),
    'parameter' => array(
      'entity' => array(
        // Do not use type 'entity' but restrict the type to group content type.
        'type' => array_keys(og_get_all_group_content_entity()),
        'label' => t('Entity'),
        'description' => t('The group content which is to be added to a group.'),
        'wrapped' => TRUE,
        'save' => TRUE,
      ),
      'group' => array(
        'type' => 'group',
        'label' => t('Group'),
      ),
    ),
    'base' => 'og_rules_add_entity_to_group',
    'access callback' => 'og_rules_integration_access',
  );

  $items['og_group_content_remove'] = array(
    'label' => t('Remove entity from group'),
    'group' => t('Organic groups'),
    'parameter' => array(
      'entity' => array(
        // Do not use type 'entity' but restrict the type to group content type.
        'type' => array_keys(og_get_all_group_content_entity()),
        'label' => t('Entity'),
        'description' => t('The entity which is to be removed from a group.'),
        'wrapped' => TRUE,
        'save' => TRUE,
      ),
      'group' => array(
        'type' => 'group',
        'label' => t('Group'),
      ),
    ),
    'base' => 'og_rules_remove_entity_from_group',
    'access callback' => 'og_rules_integration_access',
  );

  // For UX also provide separate actions for user subcriptions although it is
  // technically the same as adding entities to groups.

  $items['og_subcribe_user'] = array(
    'label' => t('Subscribe user to group'),
    'group' => t('Organic groups'),
    'parameter' => array(
      'user' => array(
        'type' => 'user',
        'label' => t('User'),
        'description' => t('The user who will be subscribed.'),
        'wrapped' => TRUE,
        'save' => TRUE,
      ),
      'group' => array(
        'type' => 'group',
        'label' => t('Group'),
      ),
      // @todo: Add membership-type setting + add in the membership-entity
      // fields via the info_alter callback + reload the form once the
      // membership-type has been chosen.
      // Then, we probably also want to provide the newly created membership
      // entity.
    ),
    'base' => 'og_rules_add_entity_to_group',
    'access callback' => 'og_rules_integration_access',
  );

  $items['og_unsubscribe_user'] = array(
    'label' => t('Unsubscribe user from group'),
    'group' => t('Organic groups'),
    'parameter' => array(
      'user' => array(
        'type' => 'user',
        'label' => t('User'),
        'description' => t('The user who will be unsubscribed.'),
        'wrapped' => TRUE,
        'save' => TRUE,
      ),
      'group' => array(
        'type' => 'group',
        'label' => t('Group'),
      ),
    ),
    'base' => 'og_rules_remove_entity_from_group',
    'access callback' => 'og_rules_integration_access',
  );

  return $items;
}

/**
 * Action: Get group members from a group content.
 */
function og_rules_get_members($group_content) {
  if (!isset($group_content->og_membership)) {
    // Not a group content.
    return;
  }

  $members = array();
  $gids = array();
  foreach ($group_content->og_membership->value() as $og_membership) {
    // Get the groups the group content belongs to.
    $gids[] = $og_membership->gid;
  }

  if (!$gids) {
    return;
  }

  foreach ($gids as $gid) {
    $wrapper = entity_metadata_wrapper('group', $gid);
    $members = array_merge_recursive($members, $wrapper->members->value(array('identifier' => TRUE)));
  }

  return array('group_members' => $members);
}

/**
 * Action: Get group managers from a group content.
 */
function og_rules_get_managers($group_content) {
  if (!isset($group_content->og_membership)) {
    // Not a group content.
    return;
  }

  $return = array();
  $gids = array();
  foreach ($group_content->og_membership->value() as $og_membership) {
    $gids[] = $og_membership->gid;
  }

  if (!$gids) {
    return;
  }

  $groups = og_load_multiple($gids);
  foreach ($groups as $group) {
    if ($account = $group->user()) {
      $return[] = $account->uid;
    }
  }

  return array('group_managers' => $return);
}

/**
 * Action: Fetch group content of a certain entity type.
 */
function og_rules_get_group_content($group, $entity_type) {
  $memberships = og_membership_load_multiple(FALSE, array('gid' => $group->gid, 'entity_type' => $entity_type));
  $entities = array();
  foreach ($memberships as $membership) {
    $entities[] = $membership->etid;
  }
  $entities = entity_load($entity_type, $entities);
  return array('group_content' => $entities);
}

/**
 * "Fetch group content" action info alter callback.
 */
function og_rules_get_group_content_info_alter(&$element_info, $element) {
  if (isset($element->settings['entity_type']) && $entity_type = $element->settings['entity_type']) {
    $element_info['provides']['group_content']['type']  = "list<$entity_type>";
  }
}

/**
 * Action: Add entity to group.
 */
function og_rules_add_entity_to_group(EntityDrupalWrapper $entity, $group) {
  // Let saving the entity handle by Rules.
  og_group($group->gid, array(
    'entity type' => $entity->type(),
    'entity' => $entity->value(),
    'save' => FALSE,
    'force reload' => FALSE,
  ));
}

/**
 * Action: Remove entity from group.
 */
function og_rules_remove_entity_from_group(EntityDrupalWrapper $entity, $group) {
  // Let saving the entity handle by Rules.
  og_ungroup($group->gid, $entity->type(), $entity->value(), FALSE);
}

/**
 * OG Rules integration access callback.
 */
function og_rules_integration_access($type, $name) {
  // Grant everyone access to conditions.
  return $type == 'condition' || user_access('administer group');
}

/**
 * Implements hook_rules_condition_info().
 */
function og_rules_condition_info() {
  $items = array();
  $items['og_user_has_permission'] = array(
    'label' => t('User has group permission'),
    'group' => t('Organic groups'),
    'parameter' => array(
      'permission' => array(
        'type' => 'text',
        'label' => t('Permission'),
        'description' => t('The permission to check for.'),
        'options list' => 'og_rules_user_has_permission_options_list',
        'restriction' => 'input',
      ),
      'group' => array(
        'type' => 'group',
        'label' => t('Group'),
        'description' => t('The group for which permission should be checked.'),
      ),
      'account' => array(
        'type' => 'user',
        'label' => t('User'),
      ),
    ),
    'base' => 'og_rules_user_has_permission',
    'access callback' => 'og_rules_integration_access',
  );
  $items['og_user_in_group'] = array(
    'label' => t('User is group member'),
    'group' => t('Organic groups'),
    'parameter' => array(
      'account' => array(
        'type' => 'user',
        'label' => t('User'),
        'wrapped' => TRUE,
      ),
      'group' => array(
        'type' => 'group',
        'label' => t('Group'),
      ),
      'states' => array(
        'type' => 'list<integer>',
        'label' => t('Membership states'),
        'restriction' => 'input',
        'options list' => 'og_group_content_states',
        'optional' => TRUE,
        'default value' => array(OG_STATE_ACTIVE),
      ),
    ),
    'base' => 'og_rules_condition_user_in_group',
    'access callback' => 'og_rules_integration_access',
  );
  $items['og_entity_in_group'] = array(
    'label' => t('Entity is in group'),
    'group' => t('Organic groups'),
    'parameter' => array(
      'entity' => array(
        // Do not use type 'entity' but restrict the type to group content type.
        'type' => array_keys(og_get_all_group_content_entity()),
        'label' => t('Group content entity'),
        'description' => t('The group content which will be checked.'),
        'wrapped' => TRUE,
      ),
      'group' => array(
        'type' => 'group',
        'label' => t('Group'),
      ),
    ),
    'base' => 'og_rules_condition_entity_in_group',
    'access callback' => 'og_rules_integration_access',
  );
  $items['og_entity_is_group'] = array(
    'label' => t('Entity is group'),
    'group' => t('Organic groups'),
    'parameter' => array(
      'entity' => array(
        // Do not use type 'entity' but restrict the type to group type.
        'type' => array_keys(og_get_all_group_entity()),
        'label' => t('Entity'),
        'wrapped' => TRUE,
      ),
    ),
    'base' => 'og_rules_entity_is_group',
    'access callback' => 'og_rules_integration_access',
  );
  $items['og_entity_is_group_content'] = array(
    'label' => t('Entity is group content'),
    'group' => t('Organic groups'),
    'parameter' => array(
      'entity' => array(
        // Do not use type 'entity' but restrict the type to group content type.
        'type' => array_keys(og_get_all_group_content_entity()),
        'label' => t('Entity'),
        'wrapped' => TRUE,
      ),
    ),
    'base' => 'og_rules_entity_is_group_content',
    'access callback' => 'og_rules_integration_access',
  );
  return $items;
}

/**
 * Condition: User has group permisison.
 */
function og_rules_user_has_permission($permission, $group, $account) {
  return og_user_access($group->gid, $permission, $account);
}

/**
 * Condition user has group permission options list callback for permissions.
 */
function og_rules_user_has_permission_options_list() {
  $perms = array();
  foreach (og_get_permissions() as $perm => $value) {
    // By keeping them keyed by module we can use optgroups with the
    // 'select' type.
    $perms[$value['module']][$perm] = strip_tags($value['title']);
  }
  return $perms;
}

/**
 * Condition: Entity is in group.
 */
function og_rules_condition_entity_in_group(EntityDrupalWrapper $entity, $group) {
  return og_is_member($group->gid, $entity->type(), $entity->value());
}

/**
 * Condition: User is in group.
 */
function og_rules_condition_user_in_group(EntityDrupalWrapper $entity, $group, $states = array(OG_STATE_ACTIVE)) {
  return og_is_member($group->gid, $entity->type(), $entity->value(), $states);
}

/**
 * Condition "User is in group" help.
 */
function og_rules_condition_user_in_group_help() {
  return t('Evaluates to TRUE if the user is a member of the group and the membership meets the configured valid membership state.');
}

/**
 * Condition: Entity is group.
 */
function og_rules_entity_is_group(EntityDrupalWrapper $entity) {
  return (bool) og_get_group($entity->type(), $entity->getIdentifier());
}

/**
 * Condition: "Entity is group" help.
 */
function og_rules_entity_is_group_help() {
  return t('Determines whether the entity is an active group.');
}

/**
 * Condition: Entity is group content.
 */
function og_rules_entity_is_group_content(EntityDrupalWrapper $entity) {
  return (bool) og_is_group_content_type($entity->type(), $entity->getBundle());
}

/**
 * Condition: "Entity is group content" help.
 */
function og_rules_entity_is_group_content_help() {
  return t('Determines whether the entity is group content, i.e. it can be or is used as group content.');
}

/**
 * @}
 */
