t('Group'), 'entity class' => 'EntityOg', 'controller class' => 'EntityAPIController', 'base table' => 'og', 'entity keys' => array( 'id' => 'gid', ), ); return $return; } /** * Implements hook_menu(). */ function og_menu() { $items = array(); $items['group/autocomplete'] = array( 'title' => 'group autocomplete', 'page callback' => 'og_field_audience_autocomplete', 'access arguments' => array('access content'), 'type' => MENU_CALLBACK ); return $items; } /** * Implements hook_entity_property_info(). * * Entity metadata hook. */ function og_entity_property_info() { $info = array(); $properties = &$info['group']['properties']; $properties['gid'] = array( 'label' => t("Group ID"), 'type' => 'integer', 'description' => t("The unique ID of the group."), 'required' => TRUE, ); $properties['etid'] = array( 'label' => t("Object ID"), 'type' => 'integer', 'description' => t("The ID of the entity."), 'required' => TRUE, ); $properties['entity_type'] = array( 'label' => t("Object type"), 'type' => 'text', 'description' => t("The entity type."), 'required' => TRUE, ); $properties['state'] = array( 'label' => t("State"), 'type' => 'text', 'description' => t("The state of the group."), 'setter callback' => 'entity_metadata_verbatim_set', 'options list' => 'og_group_states', ); $properties['label'] = array( 'label' => t("Label"), 'type' => 'text', 'description' => t("The label of the entity."), 'required' => TRUE, ); $properties['created'] = array( 'label' => t("Date created"), 'type' => 'date', 'description' => t("The date the group was created."), 'setter callback' => 'entity_metadata_verbatim_set', ); return $info; } /** * Implements hook_modules_enabled(). */ function og_modules_enabled($modules) { // Make sure we already have the Drupal 7 tables. if (module_exists('og') && db_table_exists('og') && db_field_exists('og', 'gid')) { foreach ($modules as $module) { // Add default roles and permissions, if existing and not set yet. og_set_global_access_module($module); } } } /** * Implements hook_modules_uninstalled(). */ function og_modules_uninstalled($modules) { // Delete module's permissions. og_permissions_delete_by_module($modules); } /** * Implementation of hook_ctools_plugin_directory(). */ function og_ctools_plugin_directory($module, $plugin) { // Safety: go away if CTools is not at an appropriate version. if (!module_invoke('ctools', 'api_version', OG_REQUIRED_CTOOLS_API)) { return; } if ($module == 'ctools') { return 'plugins/' . $plugin; } } /** * Implements hook_og_views_relationship(). * * Add relationship information for all core's enteties. */ function og_og_views_relationship() { $items = array(); $items['node'] = array( 'entity' => 'node', 'views table' => 'node', 'join field' => 'nid', ); $items['comment'] = array( 'entity' => 'comment', 'views table' => 'comment', 'join field' => 'cid', ); $items['user'] = array( 'entity' => 'user', 'views table' => 'users', 'join field' => 'uid', ); //TODO: Add all enteties. return $items; } /** * Implements hook_permission(). */ function og_permission() { return array( 'administer group' => array( 'title' => t('Administer Group permissions'), 'description' => t('Administer all groups and permissions.'), ), ); } /** * Implement hook_og_permission(). */ function og_og_permission() { // Generate standard node permissions for all applicable node types. $perms = array(); $perms['update group'] = array( 'title' => t('Edit group'), 'description' => t('Edit the group. Note: This permission controls only node entity type groups.'), 'default role' => array(OG_ADMINISTRATOR_ROLE), ); $perms['administer group'] = array( 'title' => t('Administer group'), 'description' => t('Manage or block users, and manage their role assignments in the group.'), 'default role' => array(OG_ADMINISTRATOR_ROLE), ); foreach (node_permissions_get_configured_types() as $type) { $perms = array_merge($perms, og_list_permissions($type)); } return $perms; } /** * Implement hook_og_default_roles() */ function og_og_default_roles() { return array(OG_ADMINISTRATOR_ROLE); } /** * Implement hook_node_access() */ function og_node_access($node, $op, $account) { // If not a group type or the operation is node creation which still has no // groups so we can't check it yet, we ignore the access. $return = NODE_ACCESS_IGNORE; $type = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type); if (in_array($op, array('update', 'delete'))) { if (og_is_group_type('node', $type) && $group = og_get_group('node', $node->nid)) { $return = NODE_ACCESS_DENY; // The node is a group. if (og_user_access($group->gid, $op . ' group', $account)) { $return = NODE_ACCESS_ALLOW; } } // A single content type can act as a group and as group content, so make // sure that even if the user doesn't have access on the group itself, // further access checks are performed. // However if we already have access, then skip the following access checks. if ($return != NODE_ACCESS_ALLOW && og_is_group_content_type('node', $type) && $gids = og_get_entity_groups('node', $node)) { $return = NODE_ACCESS_DENY; // We don't have a context, so we need to get all the permissions // of all the groups. We don't intersect with the user's group, as // groups might allow anonymous members access. foreach ($gids as $gid) { if (og_user_access($gid, "administer group", $account) || // Any content. og_user_access($gid, "$op any $type content", $account) || // Users own content. (og_user_access($gid, "$op own $type content", $account) && $node->uid == $account->uid)) { $return = NODE_ACCESS_ALLOW; break; } } } } return $return; } /** * Implementation of hook_views_api(). */ function og_views_api() { return array( 'api' => 2, 'path' => drupal_get_path('module', 'og') . '/includes', ); } /** * Implements hook_og_fields_info(). */ function og_og_fields_info() { $items[OG_GROUP_FIELD] = array( 'type' => array('group'), 'description' => t('Determine if this should be a group.'), 'field' => array( 'field_name' => OG_GROUP_FIELD, 'no_ui' => TRUE, 'type' => 'list_boolean', 'cardinality' => 1, 'settings' => array( 'allowed_values' => array(0 => 'Not a group type', 1 => 'Group type'), 'allowed_values_function' => '', ), ), 'instance' => array( 'label' => t('Group type'), 'widget_type' => 'options_select', 'required' => TRUE, // Make the group type default. 'default_value' => array(0 => array('value' => 1)), 'view modes' => array( 'full' => array( 'label' => t('Full'), 'type' => 'og_group_subscribe', 'custom settings' => FALSE, ), 'teaser' => array( 'label' => t('Teaser'), 'type' => 'og_group_subscribe', 'custom settings' => FALSE, ), ), ), ); $items[OG_DEFAULT_ACCESS_FIELD] = array( 'type' => array('group'), 'description' => t('Determine if group should use default roles and permissions.'), 'field' => array( 'field_name' => OG_DEFAULT_ACCESS_FIELD, 'no_ui' => TRUE, 'type' => 'list_boolean', 'cardinality' => 1, 'settings' => array('allowed_values' => array(0 => 'Use default roles and permissions', 1 => 'Override default roles and permissions'), 'allowed_values_function' => ''), ), 'instance' => array( 'label' => t('Group roles and permissions'), 'widget_type' => 'options_select', 'required' => TRUE, // Use default role and permissions as default value. 'default_value' => array(0 => array('value' => 0)), 'view modes' => array( 'full' => array( 'label' => t('Full'), 'type' => 'list_default', 'custom settings' => FALSE, ), 'teaser' => array( 'label' => t('Teaser'), 'type' => 'list_default', 'custom settings' => FALSE, ), ), ), ); $items[OG_AUDIENCE_FIELD] = array( 'type' => array('group content'), 'description' => t('Determine to which groups this group content is assigned to.'), 'field' => array( 'field_name' => OG_AUDIENCE_FIELD, 'no_ui' => TRUE, 'type' => 'group', 'cardinality' => FIELD_CARDINALITY_UNLIMITED, ), 'instance' => array( 'label' => t('Groups audience'), 'widget_type' => OG_AUDIENCE_FIELD, 'view modes' => array( 'full' => array( 'label' => t('Full'), 'type' => 'og_list_default', 'custom settings' => FALSE, ), 'teaser' => array( 'label' => t('Teaser'), 'type' => 'og_list_default', 'custom settings' => FALSE, ), ), ), ); return $items; } /** * Implement hook_node_type_delete(). * * We immediately delete those variables as they are only used to indicate a * content type should be a group or a group content. However, the actual * indication for it is in the field API. This is just a workaround, specifically * for the node entity, to allow users to define groups via the "content type" * page. */ function og_node_type_delete($info) { variable_del('og_group_type_' . $info->type); variable_del('og_group_content_type_' . $info->type); } /** * Implement hook_node_type_insert(). */ function og_node_type_insert($info) { og_node_type_save($info->type); } /** * Implement hook_node_type_update(). */ function og_node_type_update($info) { og_node_type_save($info->type); } /** * Return a loaded group entity if exists or create a new one. * * This is a wrapper function around og_load() that allows passing the group's * entity type and entity ID, and the correct group will be loaded according. * If no group exists and $create option is set to TRUE a new group entity will * be created. * * @param $etid * The group content ID. * @param $entity_type * The group entity type. "node" is the default value. Pass "group" if * content ID is equal to the group unique ID. * @param $create * Optional; If no existing group is found, create a new one. Defaults to * FALSE. * @param $reset * A boolean indicating that the internal cache should be reset. * * @return * The group entity if found, or an empty array. */ function og_get_group($entity_type, $etid, $create = FALSE, $states = array(OG_STATE_ACTIVE), $reset = FALSE) { $group = FALSE; if ($gids = og_get_group_ids($entity_type, array($etid), $states, $reset)) { // We don't use the entity ID directly, as it might change. For example, if // a node is a translation of another node that is a group, we need to load // the other node. og_get_group_ids() returns the correct entity ID as the // key, so we will use that. $correct_etid = key($gids); $group = og_load($gids[$correct_etid], $reset); } elseif ($create) { $group = og_create_group(array('entity_type' => $entity_type, 'etid' => $etid)); } return $group; } /** * Callback to create a new group. */ function og_create_group($values = array()) { if ($entity = entity_load($values['entity_type'], array($values['etid']))) { $entity = current($entity); // Add default values. $values += array( 'state' => OG_STATE_ACTIVE, 'created' => time(), 'label' => og_entity_label($values['entity_type'], $entity), ); return entity_create('group', $values); } // Entity couldn't be loaded. return FALSE; } /** * Main class for Group entities provided by Entity API. */ class EntityOg extends Entity { public function __construct(array $values = array(), $entityType = NULL) { parent::__construct($values, 'group'); } public function save() { parent::save(); og_invalidate_cache(); } public function delete() { $gid = $this->gid; parent::delete(); og_invalidate_cache(array($gid)); // Delete roles and permissions. og_delete_user_roles_by_group($gid, NULL, TRUE); } } /** * Load multiple Group entities based on certain conditions. * * @param $gids * An array of group entity IDs. * @param $conditions * An array of conditions to match against the {entity} table. * @param $reset * A boolean indicating that the internal cache should be reset. * * @return * An array of group entities, indexed by group ID. */ function og_load_multiple($gids = array(), $conditions = array(), $reset = FALSE) { return entity_load('group', $gids, $conditions, $reset); } /** * Load an Group entity from the database. * * @param $gid * The group ID. * @param $reset * Whether to reset the node_load_multiple cache. * * @return * A fully-populated group entity, or FALSE if none found. */ function og_load($gid, $reset = FALSE) { $group = og_load_multiple(array($gid), array(), $reset); return $group? reset($group) : FALSE; } /** * Invalidate cache. * * @param $gids * Array with group IDs that their cache should be invalidated. */ function og_invalidate_cache($gids = array()) { // Reset static cache. $caches = array( 'og_user_access', 'og_get_group_ids', 'og_get_entity_groups', 'og_field_audience_options', 'og_user_role_permissions', ); foreach ($caches as $cache) { drupal_static_reset($cache); } // Let other Group modules know we invalidate cache. module_invoke_all('og_invalidate_cache', $gids); } /** * Get group IDs by the entity type and entity IDs. * * @param $entity_type * The group entity type. Defaults to "group", that will return the group ID. * @param $etids * Array with the entity IDs that should be loaded. If FALSE, all groups IDs * that belong to the entity will be returned. * @param $states * Array of states the group must be in. Values can be OG_STATE_ACTIVE or * OG_STATE_PENDING. Defaults to OG_STATE_ACTIVE. * @param $soft_reset * A boolean indicating that the internal cache should be "soft" reset (i.e. * only the cached values of the specific entity type). For a "hard" reset use * @code * drupal_static_reset('og_get_group_ids'); * @endcode * * @return * Array keyed with the entity ID and the group ID as the value. */ function og_get_group_ids($entity_type = 'group', $etids = FALSE, $states = array(OG_STATE_ACTIVE), $soft_reset = FALSE) { $gids = &drupal_static(__FUNCTION__, array()); if ($soft_reset || empty($gids[$entity_type]) || (!empty($gids['__info'][$entity_type]['states'])) && array_diff($gids['__info'][$entity_type]['states'], $states)) { $gids[$entity_type] = array(); // Make sure the cached values are according to the states we are looking // for. $gids['__info'][$entity_type]['states'] = $states; $gids['__info'][$entity_type]['query all'] = FALSE; } $query_etids = $etids; // Check we don't already have the group IDs, and if we have them, return them // for the cache. if (!empty($gids[$entity_type])) { if ($query_etids !== FALSE) { $query_etids = array_diff($query_etids, $gids[$entity_type]); if (!$query_etids) { return array_intersect_key($gids[$entity_type], drupal_map_assoc($etids)); } } } // Check if we need to query all group, and if this was already cached. if ($query_etids === FALSE && $gids['__info'][$entity_type]['query all']) { return $gids[$entity_type]; } // Don't query if we already have already queried all. if (empty($gids['__info'][$entity_type]['query all'])) { if (!empty($query_etids) && $entity_type == 'node' && module_exists('translation')) { // If a node is a translation of another node that is a group, we should // mark it as a group as-well. $query = db_select('node', 'node'); $query->fields('node', array('tnid', 'tnid')) ->condition('nid', $query_etids, 'IN'); // Add the real group node IDs to the entity IDs that will be queried // against the {og} table. if ($result = $query->execute()->fetchAllKeyed()) { $query_etids = array_merge($query_etids, $result); // Add it to the original IDs, as they are needed to later on intersect // with the results from the cache. $etids = array_merge($etids, $result); } } // We can't use EntityFieldQuery as it return only the group ID, but in // order to cache the results we need to maintain a relation between the // entity ID and the group ID. $query = db_select('og', 'og'); if ($entity_type == 'group') { $query->fields('og', array('gid', 'gid')); if (!empty($query_etids)) { $query->condition('gid', $query_etids, 'IN'); } } else { $query->fields('og', array('etid', 'gid')); $query->condition('entity_type', $entity_type); if (!empty($query_etids)) { $query->condition('etid', $query_etids, 'IN'); } } if (!empty($states)) { $query->condition('state', $states, 'IN'); } $gids[$entity_type] += $query->execute()->fetchAllKeyed(); } // Make sure we return only the ids we were asked for, or if no specific IDs // were asked, then return all of them. if ($query_etids !== FALSE) { $return = array_intersect_key($gids[$entity_type], drupal_map_assoc($etids)); } else { $return = $gids[$entity_type]; // Let the cache know we queried all IDs of this entity type. $gids['__info'][$entity_type]['query all'] = TRUE; } return $return; } /** * Return all existing groups with a certain state. * * @param $states * Array of states the group must be in. * @param $options * - return query: If TRUE the return value will be the $query object. * Defaults to FALSE. * - count: If TRUE the function will return the total numer of groups in the desired * states. Defaults to FALSE. */ function og_get_all_group($states = array(OG_STATE_ACTIVE), $options = array()) { // Initialize values. $options += array('count' => FALSE, 'return query' => FALSE); $return = array(); $query = db_select('og', 'og'); $query->addMetaData('id', 'og_get_all_group'); $query->fields('og', array('gid')); if (!empty($states)) { // Get only the groups with the correct state. $query->condition('state', $states, 'IN'); } if ($options['return query']) { // Return the query itself. $return = $query; } elseif ($options['count']) { // Return the total number of groups found. $return = $query->countQuery()->execute()->fetchField(); } else { // Return the group IDs. $result = $query->execute()->fetchAll(); foreach ($result as $value) { $return[$value->gid] = $value->gid; } } return $return; } /** * Set an association (e.g. subscribe) an entity to a group. * * @param $entity_type * The entity type (e.g. "node" or "user"). * @param $entity * The entity to set the association. * @param $state * The state of the association. Can be: * - OG_STATE_ACTIVE * - OG_STATE_PENDING * - OG_STATE_BLOCKED * @param $save * Optional; TRUE if fields value should be saved. Defaults to TRUE. * * @return * The entity with the fields updated. */ function og_group($gid, $entity_type, $entity, $state = OG_STATE_ACTIVE, $save = TRUE) { $entity = og_load_entity($entity_type, $entity); $property = OG_AUDIENCE_FIELD; // Check the audience field exists in the entity. if (isset($entity->{$property})) { $wrapper = &$entity->{$property}[LANGUAGE_NONE]; $op = !empty($wrapper[0]['gid']) ? 'update' : 'insert'; list($id) = entity_extract_ids($entity_type, $entity); // Check if the entity is new or an existing one. // TODO: make sure is_new is on all entity types. $op = empty($entity->is_new) ? 'update' : 'insert'; if ($op == 'insert') { $values = array( 'gid' => $gid, 'state' => $state, 'created' => time(), ); $wrapper[] = $values; } else { $existing_key = FALSE; if (!empty($wrapper)) { foreach ($wrapper as $key => $value) { if ($gid == $value['gid']) { $existing_key = $key; break; } } } if ($existing_key === FALSE) { $values = array( 'gid' => $gid, 'state' => $state, 'created' => time(), ); $entity->{$property}[LANGUAGE_NONE][] = $values; } else { if ($wrapper[$existing_key]['state'] != $state) { $wrapper[$existing_key]['state'] = $state; } else { // Nothing changed, so don't try to save. $save = FALSE; } } } if ($save) { // Make sure a group isn't created for this entity. This is used for cases // that a user object can be a group, however we don't want // og_field_crud_group() to actually make it a group when the field // attachers are invoked. $entity->og_skip_group_create = TRUE; entity_save($entity_type, $entity); og_invalidate_cache(); // Unset our temporary property. unset($entity->og_skip_group_create); } } return $entity; } /** * Delete an an association (e.g. unsubscribe) of an entity to a group. * * @param $entity_type * The entity type (e.g. "node" or "user"). * @param $entity * The entity to set the association. * @param $save * Optioanl; TRUE if fields value shoudl be saved. Defaults to TRUE. * * @return * The entity with the fields updated. */ function og_ungroup($gid, $entity_type, $entity, $save = TRUE) { $entity = og_load_entity($entity_type, $entity); $property = OG_AUDIENCE_FIELD; if (!empty($entity->{$property})) { $wrapper = &$entity->{$property}[LANGUAGE_NONE]; $existing_key = FALSE; if (!empty($wrapper)) { foreach ($wrapper as $key => $value) { if ($gid == $value['gid']) { $existing_key = $key; break; } } } if ($existing_key !== FALSE) { unset($wrapper[$existing_key]); if ($save) { entity_save($entity_type, $entity); og_invalidate_cache(); if ($entity_type == 'user') { foreach (og_get_user_roles($gid, $entity->uid) as $rid) { og_users_roles_revoke($gid, $entity->uid, $rid); } } } } } return $entity; } /** * Determine whether a user has a given privilege. * * @param $gid * The group ID. * @param $string * The permission, such as "administer nodes", being checked for. * @param $account * (optional) The account to check, if not given use currently lgroupged in user. * * @return * Boolean TRUE if the current user has the requested permission. * * All permission checks in OG should go through this function. This * way, we guarantee consistent behavior, and ensure that the superuser * can perform all actions. */ function og_user_access($gid, $string, $account = NULL) { if (variable_get('og_skip_access', FALSE)) { // User access should always return TRUE, as Group is requested to // skip any access check. return TRUE; } global $user; $perm = &drupal_static(__FUNCTION__, array()); if (empty($account)) { $account = clone $user; } // User #1 has all privileges. if ($account->uid == 1) { return TRUE; } // Administer Group permission. if (user_access('administer group', $account)) { return TRUE; } if (!($group = og_load($gid))) { // Not a group. return FALSE; } // Group manager has all privileges (if variable is TRUE). if (variable_get('og_group_manager_full_access', TRUE)) { $entity = current(entity_load($group->entity_type, array($group->etid))); if (!empty($entity->uid) && $entity->uid == $account->uid) { return TRUE; } } // To reduce the number of SQL queries, we cache the user's permissions // in a static variable. if (!isset($perm[$gid][$account->uid])) { $roles = og_get_user_roles($gid, $account->uid); $role_permissions = og_user_role_permissions($roles); $perms = array(); foreach ($role_permissions as $one_role) { $perms += $one_role; $perm[$gid][$account->uid] = $perms; } } return isset($perm[$gid][$account->uid][$string]); } /** * Add group and group content fields to new content types. * * @param $bundle_name * The content type name. */ function og_node_type_save($bundle_name) { if (variable_get('og_group_type_' . $bundle_name, 'omitted') == 'group') { og_create_field(OG_GROUP_FIELD,'node', $bundle_name); // Delete the variable, as we will rely on the presence of th field. variable_del('og_group_type_' . $bundle_name); } if (variable_get('og_group_content_type_' . $bundle_name, 'omitted') == 'og_content') { og_create_field(OG_AUDIENCE_FIELD,'node', $bundle_name); // Delete the variable, as we will rely on the presence of th field. variable_del('og_group_content_type_' . $bundle_name); } } /******************************************************************************* * API functions ******************************************************************************/ /** * Get the groups a content is associated with. * * @param $entity_type * The entity type (e.g. "node" or "user"). * @param $entity * The entity can be a user, node or any fieldable entity. * @param $states * Optional; Array with the state to return. If empty groups of all state will * return. * @return * An array with the group IDs, or an empty array. */ function og_get_entity_groups($entity_type, $entity, $states = array(OG_STATE_ACTIVE)) { $groups = &drupal_static(__FUNCTION__, array()); // Get the entity ID. list($id) = entity_extract_ids($entity_type, $entity); if (!empty($groups[$entity_type][$id])) { return $groups[$entity_type][$id]; } $entity = og_load_entity($entity_type, $entity); $property = OG_AUDIENCE_FIELD; $wrapper = &$entity->{$property}[LANGUAGE_NONE]; $gids = array(); if (!empty($wrapper)) { foreach ($wrapper as $group) { if (!empty($states) && !in_array($group['state'], $states)) { // Don't register the group if it's state isn't the one we look for. continue; } $gids += drupal_map_assoc(array($group['gid'])); } } $groups[$entity_type][$id] = $gids; return $groups[$entity_type][$id]; } /** * Return the group type (i.e. "group" or "group_content") of an entity. * * @param $bundle_name * The bundle name to be checked. * @param $entity_type * The entity type. * @param $type * The group usage type. Must be "group" or "group content". * * @return * The group type or an "omitted" if node type doesn't participate in * Group. */ function og_get_group_type($entity_type, $bundle_name, $type = 'group') { if ($type == 'group') { return (bool)field_info_instance($entity_type, OG_GROUP_FIELD, $bundle_name); } elseif ($type == 'group content') { return (bool)field_info_instance($entity_type, OG_AUDIENCE_FIELD, $bundle_name); } } /** * Return TRUE if the entity type is a "group" type. * * This is a wrapper function around og_get_group_type(). * * @param $node_type * The node type to be checked. */ function og_is_group_type($entity_type, $bundle_name) { return og_get_group_type($entity_type, $bundle_name); } /** * Return TRUE if the entity type is a "group content" type. * * This is a wrapper function around og_get_group_type(). * * @param $node_type * The node type to be checked. */ function og_is_group_content_type($entity_type, $bundle_name) { return og_get_group_type($entity_type, $bundle_name, 'group content'); } /** * Return all the enteties that are a group. * * @return * Array keyed with the entity type machine name and the entity human readable * name as the value, or an empty array if no enteties are defined as group. */ function og_get_all_group_entity() { $return = array(); foreach (entity_get_info() as $entity_type => $entity_value) { if (!empty($entity_value['fieldable'])) { foreach ($entity_value['bundles'] as $bundle => $bundle_value) { if (og_is_group_type($entity_type, $bundle)) { $return[$entity_type] = check_plain($entity_value['label']); // At least one bundle of the entity can be a group, so break. break; } } } } return $return; } /** * Return TRUE if entity belongs to a group. * * @param $gid * The group ID. * @param $entity_type * The entity type. * @param $entity * The entity object. If empty the current user will be used. * * @return * TRUE if the entity (e.g. the user) belongs to a group and is not pending or * blocked. */ function og_is_member($gid, $entity_type = 'user', $entity = NULL, $states = array(OG_STATE_ACTIVE)) { if ($entity_type == 'user' && empty($entity)) { global $user; $entity = clone $user; } $entity = og_load_entity($entity_type, $entity); $groups = og_get_entity_groups($entity_type, $entity, $states); return in_array($gid, $groups); } /** * Wrapper of og_user_access(); Gets entity ID instead of group ID. * * Can be used as a menu callback. * * @param $perm * The permissions name. * @param $entity_type * The entity type. * @param $entity * The entity object. * @param $account * Optioanl; The user related to the action. For example if the operation is * "subscribe" then the account will be the subscribing user. * * @return * TRUE if access is allowed, otherise FALSE. */ function og_user_access_by_entity($perm, $entity_type = NULL, $etid = NULL, $account = NULL) { if (empty($account)) { global $user; $account = clone $user; } if ($group = og_get_group($entity_type, $etid)) { return og_user_access($group->gid, $perm, $account); } return FALSE; } /** * Get the state of an entity in a group. * * @param $entity_type * The entity type. * @param $entity * The entity object. * @param $gid * The group ID. * @return * The state value, or FALSE is entity is not associated with group. */ function og_get_entity_state($gid, $entity_type, $entity) { $state = FALSE; $entity = og_load_entity($entity_type, $entity); $property = OG_AUDIENCE_FIELD; $wrapper = &$entity->{$property}[LANGUAGE_NONE]; if (!empty($wrapper)) { foreach ($wrapper as $key => $value) { if ($value['gid'] == $gid) { $state = $value['state']; break; } } } return $state; } /** * Check if group should use default roles and permissions. * * @param $gid * The group ID. * @return * TRUE if group should use default roles and permissions. */ function og_is_group_default_access($gid) { $return = TRUE; if ($entity = og_load_entity_from_group($gid)) { $property = OG_DEFAULT_ACCESS_FIELD; if (!empty($entity->{$property}[LANGUAGE_NONE]) && $wrapper = $entity->{$property}[LANGUAGE_NONE]) { $return = empty($wrapper[0]['value']); } } return $return; } /** * Select groups if they were passed in the URL. * * You can pass a URL in in the form of * node/add/post?gids_group[]=1,2,3&gids_node[]=4,5,6 * Note that gids_ is the prefix followed by the entity type (e.g. "node", * "user") or "group" to indicate the passed values are group ID. */ function og_get_context_by_url() { $return = array(); foreach (array_keys(entity_get_info()) as $entity_type) { $etids = !empty($_GET['gids_' . $entity_type][0]) ? explode(',', $_GET['gids_' . $entity_type][0]) : array(); if ($etids) { $return = array_merge($return, og_get_group_ids($entity_type, $etids)); } } return $return; } /** * Get labels out of a list of group IDs. * * @param $gids * The group IDs. * * @return * Array keyed with the group ID, and the entity label as the value, or else * the group ID with the entity type and entity ID, sanitized. */ function og_label_multiple($gids = array()) { $labels = array(); $groups = og_load_multiple($gids); foreach ($groups as $group) { if (!empty($group->label)) { $labels[$group->gid] = check_plain($group->label); } else { $entity = entity_get_info($group->entity_type); $param = array( '@gid' => $group->gid, '@entity' => $entity['label'], '@etid' => $group->etid, ); $labels[$group->gid] = t('Group @gid - @entity ID @etid', $param); } } return $labels; } /** * Wrapper function; Get the label of a single group. * * @param $gid * The group ID. * * @return * The label of the group if found, or else the group ID with the entity type * and entity ID, sanitized. */ function og_label($gid) { $labels = og_label_multiple(array($gid)); return $labels[$gid]; } /** * Determine the permissions for one or more roles. * * @param $roles * An array whose keys are the role IDs of interest. * * @return * An array indexed by role ID. Each value is an array whose keys are the * permission strings for the given role ID. */ function og_user_role_permissions($roles = array()) { $cache = &drupal_static(__FUNCTION__, array()); $role_permissions = $fetch = array(); if ($roles) { foreach ($roles as $rid => $name) { if (isset($cache[$rid])) { $role_permissions[$rid] = $cache[$rid]; } else { // Add this rid to the list of those needing to be fetched. $fetch[] = $rid; // Prepare in case no permissions are returned. $cache[$rid] = array(); } } if ($fetch) { // Get from the database permissions that were not in the static variable. // Only role IDs with at least one permission assigned will return rows. $result = db_query("SELECT rid, permission FROM {og_role_permission} WHERE rid IN (:fetch)", array(':fetch' => $fetch)); foreach ($result as $row) { $cache[$row->rid][$row->permission] = TRUE; } foreach ($fetch as $rid) { // For every rid, we know we at least assigned an empty array. $role_permissions[$rid] = $cache[$rid]; } } } return $role_permissions; } /** * Retrieve an array of roles matching specified conditions. * * @param $gid * The group node ID. * @param $permission * Optional; A string containing a permission. If set, only roles containing * that permission are returned. * @param $force_group * Optioanl; If TRUE then the roles of the group will be retrieved by the * group ID, even if the group is set to have default roles and permissions. * The group might be set to "Default access" but infact there are inactive * group roles. Thus, we are forcing the function to return the overriden * roles. see og_delete_user_roles_by_group(). * * @return * An associative array with the role id as the key and the role name as * value. The anonymous and authenticated deault roles are on the top of the * array. */ function og_user_roles($gid = 0, $permission = NULL, $force_group = FALSE) { $roles = array(); // Check if overriden access exists. if (!$force_group) { $gid = og_is_group_default_access($gid) ? 0 : $gid; } if (!empty($permission)) { $roles = db_query("SELECT r.rid, r.name FROM {og_role} r INNER JOIN {og_role_permission} p ON r.rid = p.rid WHERE p.permission = :permission AND r.gid = :gid ORDER BY r.name", array(':permission' => $permission, ':gid' => $gid))->fetchAllKeyed(); } else { $roles = db_query("SELECT rid, name FROM {og_role} WHERE gid = :gid ORDER BY rid", array(':gid' => $gid))->fetchAllKeyed(); } return $roles; } /** * Get global roles - roles that belong to non-existent group ID 0. * * @return * A keyed array with role Id as key and role name as value. */ function og_get_global_roles() { return og_user_roles(); } /** * Get ar if default roles, keyed by their declaring module. */ function og_get_default_roles($include = TRUE) { $roles = array(); foreach (module_implements('og_default_roles') as $module) { $roles[$module] = module_invoke($module, 'og_default_roles'); } // Allow other modules to alter the defult roles, excpet of the anonymous and // authenticated. drupal_alter('og_default_roles', $roles); if ($include) { $roles += array('og' => array()); array_unshift($roles['og'], OG_ANONYMOUS_ROLE, OG_AUTHENTICATED_ROLE); } return $roles; } /** * Add default roles and permissions of a module to the global permissions. * * This function is called whenever a module is enabled. Calling this function * directly will re-assign permissions to thier default roles. * * @param $module * The module name. * @return * Array with the global roles, as new records might have been added. */ function og_set_global_access_module($module) { $default_roles = og_get_default_roles(); $global_roles = og_get_global_roles(); $permissions = og_get_permissions(); // The roles that should be added. $roles_to_add = array(); if (empty($global_roles)) { // Add all the roles, there are no roles defined yet. This is probably // becuase OG is only being installed. $roles_to_add = reset($default_roles); } elseif (!empty($default_roles[$module])) { // Diff the roles that should be added with the ones already defined as // global roles. $roles_to_add = array_diff($default_roles[$module], $global_roles); } // Add a new global role. if (!empty($roles_to_add)) { foreach ($roles_to_add as $name) { $role = og_create_global_role($name); $global_roles[$role->rid] = $name; } } // If there are permissions defined, make sure they were not applied already, // as it might happen if a module was disabled and re-enabled. $perms_to_add = array(); $perms_to_add_by_rid = array(); foreach ($permissions as $key => $value) { if ($value['module'] == $module) { $perms_to_add[$key] = $value; } } if ($perms_to_add) { // Get the assigned permissions of the global roles. $global_roles_perms = og_user_role_permissions($global_roles); // Get the roles keyed by thier name. $global_roles_flip = array_flip($global_roles); foreach ($perms_to_add as $key => $value) { if (!empty($value['default role'])) { // Don't try to assign permissions that are already assigned. foreach ($value['default role'] as $role) { $rid = $global_roles_flip[$role]; if (empty($global_roles_perms[$rid][$key])) { // Get the permissions to be added in the form: // array( // '1' => array( // '1' is the role ID. // 'perm_foo' => 'perm_foo', // 'perm_bar' => 'perm_bar', // ), // ); $perms_to_add_by_rid[$rid][$key] = $key; } } } } } if ($perms_to_add_by_rid) { foreach ($perms_to_add_by_rid as $rid => $perms) { // Assign the permissions to the roles. og_user_role_change_permissions($rid, $perms); } } return $global_roles; } /** * Add a new global role - a role associated to group ID 0. * * @param $name * The role name. * @return * The role object populated iwth the role ID. */ function og_create_global_role($name) { $role = new stdClass; $role->name = $name; $role->gid = 0; og_user_role_save($role); return $role; } /** * Get all roles of a user in a certain group. * * @param $gid * The group ID. * @param $uid * The user ID. * @param $include * Optional; If TRUE also anonymous or authenticated role ID will be returned. * Defaults to TRUE. * @return * Array with the role IDs of the user. */ function og_get_user_roles($gid, $uid = NULL, $include = TRUE) { $roles = array(); if (empty($uid)) { global $user; $uid = $user->uid; } if ($include) { // Check if overriden access exists. $query_gid = og_is_group_default_access($gid) ? 0 : $gid; $group_roles = og_user_roles($query_gid); $account = user_load($uid); $name = og_is_member($gid, 'user', $account) ? OG_AUTHENTICATED_ROLE : OG_ANONYMOUS_ROLE; $rid = array_search($name, $group_roles); $roles[$rid] = $rid; } $roles = $roles + db_query("SELECT rid, rid FROM {og_users_roles} WHERE uid = :uid AND gid = :gid", array(':uid' => $uid, ':gid' => $gid))->fetchAllKeyed(); return $roles; } /** * Get all the users with certain roles in a group. * * @param $gid * The group unique ID. * @param $roles * Array with the role IDs to query. */ function og_get_users_by_roles($gid, $rids = array()) { $query = db_select('og_users_roles', 'og_users_roles'); return $query->fields('og_users_roles', array('uid')) ->condition('gid', $gid) ->condition('rid', $rids, 'IN') ->execute() ->fetchAll(); } /** * Fetch a user role from database. * * @param $role * An integer with the role ID. * @return * A fully-loaded role object if a role with the given name or ID * exists, FALSE otherwise. */ function og_user_role_load($rid) { return db_select('og_role', 'r') ->fields('r') ->condition('rid', $rid) ->execute() ->fetchObject(); } /** * Save a user role to the database. * * @param $role * A role object to modify or add. If $role->rid is not specified, a new * role will be created. * @return * Status constant indicating if role was created or updated. * Failure to write the user role record will return FALSE. Otherwise. * SAVED_NEW or SAVED_UPDATED is returned depending on the operation * performed. */ function og_user_role_save($role) { if ($role->name) { // Prevent leading and trailing spaces in role names. $role->name = trim($role->name); } if (!empty($role->rid) && $role->name) { $status = drupal_write_record('og_role', $role, 'rid'); module_invoke_all('og_user_role_update', $role); } else { $status = drupal_write_record('og_role', $role); module_invoke_all('og_user_role_insert', $role); } og_invalidate_cache(); return $status; } /** * Delete a user role from database. * * @param $role * An integer with the role ID. */ function og_user_role_delete($rid) { $role = og_user_role_load($rid); db_delete('og_role') ->condition('rid', $rid) ->execute(); db_delete('og_role_permission') ->condition('rid', $rid) ->execute(); // Update the users who have this role set. db_delete('og_users_roles') ->condition('rid', $rid) ->execute(); module_invoke_all('og_user_role_delete', $role); og_invalidate_cache(); } /** * Delete all roles belonging to a group. * * @param $gid * The group ID. */ function og_delete_user_roles_by_group($gid) { // Check if group has overriden roles defined. if ($roles = og_user_roles($gid, NULL, TRUE)) { foreach ($roles as $rid => $name) { og_user_role_delete($rid); } } } /** * Get the role names of role IDs. * * @param $rids * Array with role IDs. * @return * Array keyed by the role ID, and the role name as the value. */ function og_get_user_roles_name($rids = array()) { return db_query("SELECT rid, name FROM {og_role} WHERE rid IN (:rids)", array(':rids' => $rids))->fetchAllKeyed(); } /** * Delete all permissions defined by a module. * * @see og_modules_uninstalled() * * @param $module * Array with the modules name. */ function og_permissions_delete_by_module($modules = array()) { db_delete('og_role_permission') ->condition('module', $modules, 'IN') ->execute(); } /** * Create new roles, based on the default roles and permissions. * * @param $gid * The group ID. * @return * The newly created roles keyed by role ID and role name as the value. Or * FALSE if no roles were created. */ function og_user_roles_override($gid) { // Check if roles aren't already overriden. We can't use // og_is_group_default_access() as the field is already set, so we // check to see if there are new roles in the database and compare // them with the default roles. // TODO: We can add a key to the $group object that will indicate this // if performance will be poor. if ($roles = og_user_roles($gid, NULL, TRUE)) { return; } $rids = array(); // Make sure roles doesn't exist already by looking for a row with the group // ID in {og_role} table. $perms = og_get_global_permissions(); foreach (og_get_global_roles() as $rid => $name) { $role = new stdClass; $role->name = $name; $role->gid = $gid; og_user_role_save($role); $rids[$role->rid] = $role->name; og_user_role_change_permissions($role->rid, $perms[$rid]); } return $rids; } /** * Grant a group role to a user. * * @param $uid * The user ID. * @param $rid * The role ID. */ function og_users_roles_grant($gid, $uid, $rid) { // Get the existiong user roles. $user_roles = og_get_user_roles($gid, $uid); if (!in_array($rid, $user_roles)) { $role = new stdClass(); $role->uid = $uid; $role->rid = $rid; $role->gid = $gid; drupal_write_record('og_users_roles', $role); module_invoke_all('og_users_roles_grant', $gid, $uid, $rid); } } /** * Revoke a group role from a user. * * @param $uid * The user ID. * @param $rid * The role ID. */ function og_users_roles_revoke($gid, $uid, $rid) { // Get the existiong user roles. $user_roles = og_get_user_roles($gid, $uid); if (in_array($rid, $user_roles)) { db_delete('og_users_roles') ->condition('uid', $uid) ->condition('rid', $rid) ->execute(); module_invoke_all('og_users_roles_revoke', $gid, $uid, $rid); } } /** * Change permissions for a user role. * * This function may be used to grant and revoke multiple permissions at once. * For example, when a form exposes checkboxes to configure permissions for a * role, the submitted values may be directly passed on in a form submit * handler. * * @param $rid * The ID of a group user role to alter. * @param $permissions * An array of permissions, where the key holds the permission name and the * value is an integer or boolean that determines whether to grant or revoke * the permission: * @code * array( * 'edit group' => 0, * 'administer group' => 1, * ) * @endcode * Existing permissions are not changed, unless specified in $permissions. * * @see og_user_role_grant_permissions() * @see og_user_role_revoke_permissions() */ function og_user_role_change_permissions($rid, array $permissions = array()) { // Grant new permissions for the role. $grant = array_filter($permissions); if (!empty($grant)) { og_user_role_grant_permissions($rid, array_keys($grant)); } // Revoke permissions for the role. $revoke = array_diff_assoc($permissions, $grant); if (!empty($revoke)) { og_user_role_revoke_permissions($rid, array_keys($revoke)); } } /** * Grant permissions to a user role. * * @param $rid * The ID of a user role to alter. * @param $permissions * A list of permission names to grant. * * @see user_role_change_permissions() * @see user_role_revoke_permissions() */ function og_user_role_grant_permissions($rid, array $permissions = array()) { $modules = array(); foreach (og_get_permissions() as $name => $value) { $modules[$name] = $value['module']; } // Grant new permissions for the role. foreach ($permissions as $name) { // Prevent WSOD, if the permission name is wrong, and we can't find its // module. if (!empty($modules[$name])) { db_merge('og_role_permission') ->key(array( 'rid' => $rid, 'permission' => $name, 'module' => $modules[$name], )) ->execute(); } } og_invalidate_cache(); } /** * Revoke permissions from a user role. * * @param $rid * The ID of a user role to alter. * @param $permissions * A list of permission names to revoke. * * @see user_role_change_permissions() * @see user_role_grant_permissions() */ function og_user_role_revoke_permissions($rid, array $permissions = array()) { // Revoke permissions for the role. db_delete('og_role_permission') ->condition('rid', $rid) ->condition('permission', $permissions, 'IN') ->execute(); og_invalidate_cache(); } /** * Get all permissions defined by implementing modules. * * @return * Array keyed with the permissions name and the value of the permissions. * TODO: Write the values. */ function og_get_permissions() { $perms = &drupal_static(__FUNCTION__, array()); if (!empty($perms)) { return $perms; } foreach (module_implements('og_permission') as $module) { if ($permissions = module_invoke($module, 'og_permission')) { foreach ($permissions as $key => $perm) { $permissions[$key] += array( // Initialize the roles key, if other modules haven't set it // explicetly. This means the permissions can apply to anonymous and // authenticated members as-well. 'roles' => array(OG_ANONYMOUS_ROLE, OG_AUTHENTICATED_ROLE), 'default role' => array(), 'module' => $module, ); } $perms = array_merge($perms, $permissions); } } // Allow other modules to alter the permissions. drupal_alter('og_permission', $perms); return $perms; } /** * Get global permissions. * * @return * Array keyed with the anonymous, authenticated and administror and the * permissions that should be enabled by default. */ function og_get_global_permissions() { $roles = og_get_global_roles(); $perms = og_user_role_permissions($roles); return $perms; } /** * Get all the modules fields that can be assigned to fieldable enteties. */ function og_fields_info($field_name = NULL) { $return = &drupal_static(__FUNCTION__, array()); if (empty($return)) { foreach (module_implements('og_fields_info') as $module) { if ($fields = module_invoke($module, 'og_fields_info')) { foreach ($fields as $key => $field) { // Add default values. $field += array( 'entity type' => array(), ); // Add the module information. $return[$key] = array_merge($field, array('module' => $module)); } } } // Allow other modules to alter the field info. drupal_alter('og_fields_info', $return); } return empty($field_name) ? $return : $return[$field_name]; } /** * Check to see if a token value matches the specified node. */ function og_check_token($token, $seed) { return drupal_get_token($seed) == $token; } /** * Set breadcrumbs according to a given group. * * @param $entity_type * The entity type. * @param $etid * The entity ID. * @param $path * Optional; The path to append to the breadcrumb. */ function og_set_breadcrumb($entity_type, $etid, $path = array()) { if ($entity = entity_load($entity_type, array($etid))) { $entity = reset($entity); $label = og_entity_label($entity_type, $entity); $uri = entity_uri($entity_type, $entity); drupal_set_breadcrumb(array_merge(array(l(t('Home'), '')), array(l($label, $uri['path'])), $path)) ; } } /** * Create an organic groups field in a bundle. * * @param $field_name * The field name * @param $entity_type * The entity type * @param $bundle * The bundle name. */ function og_create_field($field_name, $entity_type, $bundle) { if ($group_field = og_fields_info($field_name)) { $field = field_info_field($field_name); if (empty($field)) { $field = field_create_field($group_field['field']); } $instance = field_info_instance($entity_type, $field_name, $bundle); if (empty($instance)) { $instance = $group_field['instance']; $instance += array( 'field_name' => $field_name, 'bundle' => $bundle, 'entity_type' => $entity_type, ); field_create_instance($instance); } } } /** * Return a re-loaded entity with its fields. * * This is needed for example if a user account is passed, as global $user is * only a partial user entity, or only a partial entity object was sent. * * @param $entity_type * The entity type. * @param $entity * The entity. */ function og_load_entity($entity_type, $entity) { list($id) = entity_extract_ids($entity_type, $entity); $entity = entity_load($entity_type, array($id)); return reset($entity); } /** * Return a loaded entity from group. * * @param $gid * The group ID. */ function og_load_entity_from_group($gid) { $entity = FALSE; if ($group = og_load($gid)) { $entity = entity_load($group->entity_type, array($group->etid)); $entity = reset($entity); } return $entity; } /** * Return the states a group can be in. */ function og_group_states() { return array( OG_STATE_ACTIVE => t('Active'), OG_STATE_PENDING => t('Pending'), ); } /** * Return the states a group can be in. */ function og_group_content_states() { return array( OG_STATE_ACTIVE => t('Active'), OG_STATE_PENDING => t('Pending'), OG_STATE_BLOCKED => t('Blocked'), ); } /** * Wrapper function for entity_label() to return some text if label isn't found. * * @param $entity_type * The entity type. * @param $entity * The entity object. */ function og_entity_label($entity_type, $entity) { $label = ''; if (!empty($entity)) { $label = entity_label($entity_type, $entity); if (!$label) { list($id) = entity_extract_ids($entity_type, $entity); $label = t('Entity @entity_type ID @id', array('@entity_type' => $entity_type, '@id' => $id)); } } return $label; } /** * Helper function to generate standard node permission list for a given type. * * @param $type * The machine-readable name of the node type. * @return array * An array of permission names and descriptions. */ function og_list_permissions($type) { $perms = array(); // Check type is of group content. if (og_is_group_content_type('node', $type)) { $info = node_type_get_type($type); $type = check_plain($info->type); // Build standard list of node permissions for this type. $perms = array( "update own $type content" => array( 'title' => t('Edit own %type_name content', array('%type_name' => $info->name)), ), "update any $type content" => array( 'title' => t('Edit any %type_name content', array('%type_name' => $info->name)), ), "delete own $type content" => array( 'title' => t('Delete own %type_name content', array('%type_name' => $info->name)), ), "delete any $type content" => array( 'title' => t('Delete any %type_name content', array('%type_name' => $info->name)), ), ); // Add default permissions. foreach ($perms as $key => $value) { $perms[$key]['default role'] = array(OG_AUTHENTICATED_ROLE); } } return $perms; }