og.module

Tracking 7.x-2.x branch
  1. drupal
    1. 5 contributions/og/og.module
    2. 6 contributions/og/og.module
    3. 7 contributions/og/og.module

Enable users to create and manage groups with roles and permissions.

Functions & methods

NameDescription
og_action_infoImplements hook_action_info().
og_check_field_cardinalityReturn TRUE if a field can be used and has not reached maximum values.
og_create_fieldCreate an organic groups field in a bundle.
og_cron_queue_infoImplements hook_cron_queue_info().
og_ctools_plugin_directoryImplements hook_ctools_plugin_directory().
og_default_og_membership_typeImplements hook_default_og_membership_type().
og_delete_user_roles_by_groupDelete all roles belonging to a group.
og_entity_deleteImplements hook_entity_delete().
og_entity_getterGetter callback to load the 'entity' or 'group' property from OG membership.
og_entity_infoImplements hook_entity_info().
og_entity_insertImplements hook_entity_insert().
og_entity_property_infoImplements hook_entity_property_info().
og_entity_query_alterImplements hook_entity_query_alter().
og_entity_setterEntity property info setter callback to set the "entity" property for groups and memberships.
og_entity_updateImplements hook_entity_update().
og_features_apiImplements hook_features_api().
og_features_pipe_alterImplements hook_features_pipe_alter().
og_fields_infoGet all the modules fields that can be assigned to fieldable entities.
og_field_accessImplements hook_field_access().
og_field_attach_insertImplements hook_field_attach_insert().
og_field_attach_updateImplements hook_field_attach_update().
og_field_create_instanceImplements hook_field_create_instance().
og_field_delete_instanceImplements field_delete_instance().
og_flush_cachesImplements hook_flush_caches().
og_form_alterImplements hook_form_alter().
og_form_group_manager_validateValidate handler; Make sure a group can be created.
og_get_all_groupReturn all existing groups of an entity type.
og_get_all_group_bundleReturn all bundles that are a group type.
og_get_all_group_content_bundleReturn all the entities that are a group content.
og_get_all_group_content_entityReturn all the entities that are a group content.
og_get_all_group_entityReturn all the entities that are a group.
og_get_best_group_audience_fieldGet the first best matching group-audience field.
og_get_default_permissionsGet default permissions.
og_get_default_rolesGet array of default roles, keyed by their declaring module.
og_get_entity_groupsGet the groups an entity is associated with.
og_get_fieldable_entity_listReturn a list of fieldable entities.
og_get_field_og_membership_propertiesProperty getter callback for OG membership per field.
og_get_groups_by_userGet the group IDs of all the groups a user is an approved member of.
og_get_group_audience_fieldsGet the name of the group-audience type field.
og_get_group_members_propertiesProperty getter callback for group members.
og_get_group_typeReturn the group type (i.e. "group" or "group_content") of an entity.
og_get_membershipGet the group membership entity by user and group.
og_get_og_membership_propertiesProperty getter callback for OG membership.
og_get_permissionsGet all permissions defined by implementing modules.
og_get_user_rolesGet all roles of a user in a certain group.
og_get_user_roles_nameGet the role names of role IDs.
og_groupSet an association (e.g. subscribe) an entity to a group.
og_group_content_statesReturn the states a group content can be in.
og_group_statesReturn the states a group can be in.
og_helpImplements hook_help().
og_invalidate_cacheInvalidate cache.
og_is_groupReturn TRUE if the entity is acting as a group.
og_is_group_audience_fieldReturn TRUE if field is a group audience type.
og_is_group_content_typeReturn TRUE if the entity type is a "group content" type.
og_is_group_default_accessCheck if group should use default roles and permissions.
og_is_group_typeReturn TRUE if the entity type is a "group" type.
og_is_memberReturn TRUE if entity belongs to a group.
og_list_permissionsHelper function to generate standard node permission list for a given type.
og_membership_accessAccess callback for the group membership entity.
og_membership_createCreates a new OG membership.
og_membership_deleteDelete an existing OG membership.
og_membership_delete_by_groupRegister memberships for deletion.
og_membership_delete_multipleDelete multiple existing OG memberships.
og_membership_invalidate_cacheReset static cache related to group membership.
og_membership_labelLabel callback; Return the label of OG membership entity.
og_membership_loadOG membership loader.
og_membership_load_multipleLoad multiple OG membership entities based on certain conditions.
og_membership_orphans_workerQueue worker; Process a queue item.
og_membership_saveInserts or updates an OG membership entity into the database.
og_membership_type_accessAccess callback for the OG membership type entity.
og_membership_type_createCreates a new membership type.
og_membership_type_deleteDeletes an existing OG membership type.
og_membership_type_loadOG membership type loader.
og_membership_type_saveInserts or updates an OG membership type entity into the database.
og_menuImplements hook_menu().
og_migrate_apiImplements hook_migrate_api().
og_modules_uninstalledImplements hook_modules_uninstalled().
og_node_accessImplements hook_node_access().
og_node_create_linksReturn a form element with crafted links to create nodes for a group.
og_og_default_rolesImplements hook_og_default_roles().
og_og_fields_infoImplements hook_og_fields_info().
og_og_membership_deleteImplements hook_og_membership_delete().
og_og_membership_insertImplements hook_og_membership_insert().
og_og_membership_updateImplements hook_og_membership_update().
og_og_permissionImplements hook_og_permission().
og_operations_load_action_includesLoads the VBO actions placed in their own include files.
og_permissionImplements hook_permission().
og_permissions_delete_by_moduleDelete all permissions defined by a module.
og_query_og_membership_alterImplements hook_query_QUERY_TAG_alter().
og_rolesRetrieve an array of roles matching specified conditions.
og_roles_overrideCreate new roles, based on the default roles and permissions.
og_role_change_permissionsChange permissions for a user role.
og_role_createCreate a stub OG role object.
og_role_deleteDelete a user role from database.
og_role_grantGrant a group role to a user.
og_role_grant_permissionsGrant permissions to a user role.
og_role_loadFetch a user role from database.
og_role_permissionsDetermine the permissions for one or more roles.
og_role_revokeRevoke a group role from a user.
og_role_revoke_permissionsRevoke permissions from a user role.
og_role_saveSave a user role to the database.
og_set_breadcrumbSet breadcrumbs according to a given group.
og_ungroupDelete an association (e.g. unsubscribe) of an entity to a group.
og_user_accessDetermine whether a user has a given privilege.
og_user_access_entityCheck if a user has access to a permission on a certain entity context.
og_views_apiImplements hook_views_api().
_og_orphans_deleteHelper function to delete orphan group-content.
_og_orphans_moveHelper function to move orphan group-content to another group.
_og_query_og_membership_alter_conditionsRecursively replace the fields to their aliases in the query's conditions.
_og_update_entity_fieldsUpdate the field values in the entity, to reflect the membership.

Constants

NameDescription
OG_ADMINISTRATOR_ROLEThe role name of group administrator.
OG_ANONYMOUS_ROLEThe role name of group non-members.
OG_AUDIENCE_FIELDGroup audience field.
OG_AUTHENTICATED_ROLEThe role name of group member.
OG_DEFAULT_ACCESS_FIELDGroup default roles and permissions field.
OG_GROUP_FIELDGroup field.
OG_MEMBERSHIP_REQUEST_FIELDThe name of the user's request field in the default group membership type.
OG_MEMBERSHIP_TYPE_DEFAULTThe default group membership type that is the bundle of group membership.
OG_STATE_ACTIVEDefine active group content states.
OG_STATE_BLOCKEDDefine blocked group content states. The user is rejected from the group.
OG_STATE_PENDINGDefine pending group content states. The user is subscribed to the group but isn't an active member yet.

File

View source
  1. <?php
  2. /**
  3. * @file
  4. * Enable users to create and manage groups with roles and permissions.
  5. */
  6. // Add field widget related code.
  7. require DRUPAL_ROOT . '/' . drupal_get_path('module', 'og') . '/includes/og.field.inc';
  8. /**
  9. * Define active group content states.
  10. *
  11. * When a user has this membership state they are considered to be of
  12. * "member" role.
  13. */
  14. define('OG_STATE_ACTIVE', 1);
  15. /**
  16. * Define pending group content states. The user is subscribed to the group
  17. * but isn't an active member yet.
  18. *
  19. * When a user has this membership state they are considered to be of
  20. * "non-member" role.
  21. */
  22. define('OG_STATE_PENDING', 2);
  23. /**
  24. * Define blocked group content states. The user is rejected from the group.
  25. *
  26. * When a user has this membership state they are denided access to any
  27. * group related action. This state, however, does not prevent user to
  28. * access a group or group content node.
  29. */
  30. define('OG_STATE_BLOCKED', 3);
  31. /**
  32. * Group audience field.
  33. */
  34. define('OG_AUDIENCE_FIELD', 'og_group_ref');
  35. /**
  36. * Group field.
  37. */
  38. define('OG_GROUP_FIELD', 'group_group');
  39. /**
  40. * Group default roles and permissions field.
  41. */
  42. define('OG_DEFAULT_ACCESS_FIELD', 'og_roles_permissions');
  43. /**
  44. * The role name of group non-members.
  45. */
  46. define('OG_ANONYMOUS_ROLE', 'non-member');
  47. /**
  48. * The role name of group member.
  49. */
  50. define('OG_AUTHENTICATED_ROLE', 'member');
  51. /**
  52. * The role name of group administrator.
  53. */
  54. define('OG_ADMINISTRATOR_ROLE', 'administrator member');
  55. /**
  56. * The default group membership type that is the bundle of group membership.
  57. */
  58. define('OG_MEMBERSHIP_TYPE_DEFAULT', 'og_membership_type_default');
  59. /**
  60. * The name of the user's request field in the default group membership type.
  61. */
  62. define('OG_MEMBERSHIP_REQUEST_FIELD', 'og_membership_request');
  63. /**
  64. * Implements hook_help().
  65. */
  66. function og_help($path, $arg) {
  67. switch ($path) {
  68. case 'admin/help#og':
  69. $path = drupal_get_path('module', 'og');
  70. $output = '<p>' . t("Read the <a href='@url'>README.txt</a> file in the Organic groups module directory.", array('@url' => "/$path/README.txt")) . '</p>';
  71. $output .= '<p>' . t("Information about Organic Groups can also be found on the module's<a href='@og'>documentation page</a>.", array('@og' => 'http://drupal.org/documentation/modules/og')) . '</p>';
  72. return $output;
  73. }
  74. }
  75. /**
  76. * Implements hook_menu().
  77. */
  78. function og_menu() {
  79. $items = array();
  80. // Add our own autocomplete callback to pass also the group and
  81. // vocabulary info.
  82. $items['og/autocomplete/single/%/%/%/%'] = array(
  83. 'title' => 'Entity Reference Autocomplete',
  84. 'page callback' => 'og_entityreference_autocomplete_callback',
  85. 'page arguments' => array(2, 3, 4, 5, 6),
  86. 'access callback' => TRUE,
  87. 'type' => MENU_CALLBACK,
  88. );
  89. $items['og/autocomplete/tags/%/%/%/%'] = array(
  90. 'title' => 'Entity Reference Autocomplete',
  91. 'page callback' => 'og_entityreference_autocomplete_callback',
  92. 'page arguments' => array(2, 3, 4, 5, 6),
  93. 'access callback' => TRUE,
  94. 'type' => MENU_CALLBACK,
  95. );
  96. return $items;
  97. }
  98. /**
  99. * Implements hook_entity_info().
  100. */
  101. function og_entity_info() {
  102. $items['og_membership_type'] = array(
  103. 'label' => t('OG membership type'),
  104. 'controller class' => 'EntityAPIControllerExportable',
  105. 'entity class' => 'OgMembershipType',
  106. 'base table' => 'og_membership_type',
  107. 'fieldable' => TRUE,
  108. 'entity keys' => array(
  109. 'id' => 'id',
  110. 'label' => 'description',
  111. 'name' => 'name',
  112. ),
  113. 'exportable' => TRUE,
  114. 'export' => array(
  115. 'default hook' => 'default_og_membership_type',
  116. ),
  117. 'bundle of' => 'og_membership',
  118. 'module' => 'og',
  119. 'metadata controller class' => 'EntityDefaultMetadataController',
  120. 'views controller class' => 'EntityDefaultViewsController',
  121. 'access callback' => 'og_membership_type_access',
  122. 'entity cache' => module_exists('entitycache'),
  123. );
  124. if (class_exists('OgMembershipTypeUIController')) {
  125. $items['og_membership_type'] += array(
  126. // Enable the entity API's admin UI.
  127. 'admin ui' => array(
  128. // TODO: This path doesn't exist before OG-ui.
  129. 'path' => 'admin/config/group/group-membership',
  130. 'file' => 'includes/og.admin.inc',
  131. 'controller class' => 'OgMembershipTypeUIController',
  132. ),
  133. );
  134. }
  135. $items['og_membership'] = array(
  136. 'label' => t('OG membership'),
  137. 'entity class' => 'OgMembership',
  138. 'controller class' => 'EntityAPIController',
  139. 'base table' => 'og_membership',
  140. 'fieldable' => TRUE,
  141. 'entity keys' => array(
  142. 'id' => 'id',
  143. // The message has no label.
  144. 'label' => FALSE,
  145. 'bundle' => 'type',
  146. ),
  147. 'label callback' => 'og_membership_label',
  148. 'bundles' => array(),
  149. 'bundle keys' => array(
  150. 'bundle' => 'name',
  151. ),
  152. 'module' => 'og',
  153. 'metadata controller class' => 'OgMembershipMetadataController',
  154. 'views controller class' => 'OgMembershipViewsController',
  155. 'access callback' => 'og_membership_access',
  156. 'entity cache' => module_exists('entitycache'),
  157. );
  158. // Add bundle info but bypass entity_load() as we cannot use it here.
  159. if (db_table_exists('og_membership_type')) {
  160. $memberships = db_select('og_membership_type', 'g')
  161. ->fields('g')
  162. ->execute()
  163. ->fetchAllAssoc('name');
  164. foreach ($memberships as $type_name => $type) {
  165. $items['og_membership']['bundles'][$type_name] = array(
  166. 'label' => $type->name,
  167. 'admin' => array(
  168. 'path' => 'admin/config/group/group-membership/manage/%og_membership_type',
  169. 'real path' => 'admin/config/group/group-membership/manage/' . $type->name,
  170. 'bundle argument' => 5,
  171. 'access arguments' => array('administer group'),
  172. ),
  173. );
  174. }
  175. }
  176. return $items;
  177. }
  178. /**
  179. * Implements hook_entity_property_info().
  180. */
  181. function og_entity_property_info() {
  182. $info = array();
  183. // Add OG membership metadata for every bundle that is a group content.
  184. foreach (og_get_all_group_content_bundle() as $entity_type => $bundles) {
  185. foreach ($bundles as $bundle => $bundle_value) {
  186. $info[$entity_type]['bundles'][$bundle]['properties']['og_membership'] = array(
  187. 'label' => t("OG memberships"),
  188. 'type' => 'list<og_membership>',
  189. 'description' => t("A list of all OG memberships of the @name entity.", array('@name' => $entity_type)),
  190. 'getter callback' => 'og_get_og_membership_properties',
  191. );
  192. // Add per-state properties.
  193. $general = $info[$entity_type]['bundles'][$bundle]['properties']['og_membership'];
  194. foreach (og_group_content_states() as $state => $state_label) {
  195. $params = array('@state' => $state_label, '@name' => $entity_type);
  196. $info[$entity_type]['bundles'][$bundle]['properties']['og_membership__' . $state] = $general;
  197. $info[$entity_type]['bundles'][$bundle]['properties']['og_membership__' . $state]['label'] = t('@state OG membership', $params);
  198. $info[$entity_type]['bundles'][$bundle]['properties']['og_membership__' . $state]['description'] = t("A list of all OG memberships of the @name entity with @state state.", $params);
  199. }
  200. // Add OG membership per field in a bundle.
  201. foreach (og_get_group_audience_fields($entity_type, $bundle) as $field_name => $label) {
  202. $params = array('@label' => $label);
  203. $field_info = field_info_field($field_name);
  204. $group_type = $field_info['settings']['target_type'];
  205. $info[$entity_type]['bundles'][$bundle]['properties'][$field_name . '__og_membership'] = array(
  206. 'label' => t('OG membership from field @label', $params),
  207. 'type' => 'list<og_membership>',
  208. // The bundle in this context means the OG membership type.
  209. 'bundle' => $field_info['settings']['handler_settings']['membership_type'],
  210. 'description' => t('A list of all OG memberships registered in field @label.', $params),
  211. 'getter callback' => 'og_get_field_og_membership_properties',
  212. );
  213. // Add per-state properties.
  214. $general = $info[$entity_type]['bundles'][$bundle]['properties'][$field_name . '__og_membership'];
  215. foreach (og_group_content_states() as $state => $state_label) {
  216. $params = array(
  217. '@label' => $label,
  218. '@label' => $label,
  219. '@state' => $state_label,
  220. );
  221. $info[$entity_type]['bundles'][$bundle]['properties'][$field_name . '__og_membership__' . $state] = $general;
  222. $info[$entity_type]['bundles'][$bundle]['properties'][$field_name . '__og_membership__' . $state]['label'] = t('@state OG memberships from field @label', $params);
  223. $info[$entity_type]['bundles'][$bundle]['properties'][$field_name . '__og_membership__' . $state]['description'] = t('A list of all OG memberships with @state registered in field @label.', $params);
  224. }
  225. }
  226. }
  227. }
  228. foreach (og_get_all_group_bundle() as $entity_type => $bundles) {
  229. foreach ($bundles as $bundle => $bundle_value) {
  230. $info[$entity_type]['bundles'][$bundle]['properties']['members'] = array(
  231. 'label' => t("Group members"),
  232. 'type' => 'list<user>',
  233. 'description' => t("A list group members of the @name entity.", array('@name' => $entity_type)),
  234. 'getter callback' => 'og_get_group_members_properties',
  235. );
  236. // Add per-state properties.
  237. $general = $info[$entity_type]['bundles'][$bundle]['properties']['members'];
  238. foreach (og_group_content_states() as $state => $state_label) {
  239. $params = array('@state' => $state_label, '@name' => $entity_type);
  240. $info[$entity_type]['bundles'][$bundle]['properties']['members__' . $state] = $general;
  241. $info[$entity_type]['bundles'][$bundle]['properties']['members__' . $state]['label'] = t('@state group members', $params);
  242. $info[$entity_type]['bundles'][$bundle]['properties']['members__' . $state]['description'] = t("A list of all users of the @name entity with @state state.", $params);
  243. }
  244. }
  245. }
  246. return $info;
  247. }
  248. /**
  249. * Property getter callback for group members.
  250. *
  251. * @see og_entity_property_info()
  252. */
  253. function og_get_group_members_properties($entity, array $options, $name, $type) {
  254. $args = explode('__', $name);
  255. $state = !empty($args[1]) ? $args[1] : FALSE;
  256. list($id) = entity_extract_ids($type, $entity);
  257. $cache = &drupal_static(__FUNCTION__, array());
  258. if (isset($cache[$type][$id][$state])) {
  259. // Return the cached result.
  260. return $cache[$type][$id][$state];
  261. }
  262. $cache[$type][$id][$state] = array();
  263. $query = new EntityFieldQuery();
  264. $query
  265. ->entityCondition('entity_type', 'og_membership')
  266. ->propertyCondition('group_type', $type, '=')
  267. ->propertyCondition('gid', $id, '=')
  268. ->propertyCondition('entity_type', 'user', '=');
  269. if ($state) {
  270. $query->propertyCondition('state', $state, '=');
  271. }
  272. $result = $query->execute();
  273. if (!empty($result['og_membership'])) {
  274. $og_memberships = og_membership_load_multiple(array_keys($result['og_membership']));
  275. foreach ($og_memberships as $og_membership) {
  276. $cache[$type][$id][$state][] = $og_membership->etid;
  277. }
  278. }
  279. return $cache[$type][$id][$state];
  280. }
  281. /**
  282. * Property getter callback for OG membership.
  283. *
  284. * @see og_entity_property_info()
  285. */
  286. function og_get_og_membership_properties($entity, array $options, $name, $type) {
  287. // Get the state from name, if exists.
  288. if ($name == 'og_membership') {
  289. $state = array();
  290. }
  291. else {
  292. $args = explode('__', $name);
  293. $state = array($args[1]);
  294. }
  295. $ids = array();
  296. if ($gids = og_get_entity_groups($type, $entity, $state)) {
  297. $ids = array();
  298. foreach ($gids as $group_type => $values) {
  299. $ids = array_merge($ids, array_keys($values));
  300. }
  301. }
  302. return $ids;
  303. }
  304. /**
  305. * Property getter callback for OG membership per field.
  306. *
  307. * @see og_entity_property_info()
  308. */
  309. function og_get_field_og_membership_properties($entity, array $options, $name, $type) {
  310. $args = explode('__', $name);
  311. // Field name might have double underscore as-well, so we need to make
  312. // sure we get it right.
  313. $last_char = substr($name, -1);
  314. $state = is_numeric($last_char) ? $last_char : FALSE;
  315. // The number of characters to ignore in the name (i.e. remove the
  316. // "__og_membership" or "__og_membership__0").
  317. $remove_char = $state ? -18 : -15;
  318. $field_name = substr($name, 0, $remove_char);
  319. $field_name = $args[0];
  320. $state = count($args) == 2 ? FALSE : $args[2];
  321. list($id) = entity_extract_ids($type, $entity);
  322. $identifier = $type . ':' . $id . ':' . $field_name . ':' . $state;
  323. $cache = &drupal_static(__FUNCTION__, array());
  324. if (isset($cache[$identifier])) {
  325. // Return the cached result.
  326. return $cache[$identifier];
  327. }
  328. $query = new EntityFieldQuery();
  329. $query
  330. ->entityCondition('entity_type', 'og_membership')
  331. ->propertyCondition('entity_type', $type, '=')
  332. ->propertyCondition('etid', $id, '=')
  333. ->propertyCondition('field_name', $field_name, '=');
  334. if ($state) {
  335. $query->propertyCondition('state', $state, '=');
  336. }
  337. $result = $query->execute();
  338. $cache[$identifier] = !empty($result['og_membership']) ? array_keys($result['og_membership']) : array();
  339. return $cache[$identifier];
  340. }
  341. /**
  342. * Getter callback to load the 'entity' or 'group' property from OG membership.
  343. *
  344. * We have to return the entity wrapped.
  345. */
  346. function og_entity_getter($object, array $options, $property_name) {
  347. switch ($property_name) {
  348. case 'entity':
  349. return entity_metadata_wrapper($object->entity_type, $object->etid);
  350. case 'group':
  351. return entity_metadata_wrapper($object->group_type, $object->gid);
  352. }
  353. }
  354. /**
  355. * Entity property info setter callback to set the "entity" property for groups
  356. * and memberships.
  357. *
  358. * As the property is of type entity, the value will be passed as a wrapped
  359. * entity.
  360. */
  361. function og_entity_setter($object, $property_name, $wrapper) {
  362. switch ($property_name) {
  363. case 'entity':
  364. $object->entity_type = $wrapper->type();
  365. $object->etid = $wrapper->getIdentifier();
  366. break;
  367. case 'group':
  368. $object->group_type = $wrapper->type();
  369. $object->gid = $wrapper->getIdentifier();
  370. break;
  371. }
  372. }
  373. /**
  374. * Implements hook_default_og_membership_type().
  375. */
  376. function og_default_og_membership_type() {
  377. $items = array();
  378. $items['og_membership_type_default'] = entity_import('og_membership_type', '{
  379. "name" : "og_membership_type_default",
  380. "description" : "Default",
  381. "rdf_mapping" : []
  382. }');
  383. return $items;
  384. }
  385. /**
  386. * Implements hook_modules_uninstalled().
  387. */
  388. function og_modules_uninstalled($modules) {
  389. // Delete module's permissions.
  390. og_permissions_delete_by_module($modules);
  391. }
  392. /**
  393. * Implements hook_ctools_plugin_directory().
  394. */
  395. function og_ctools_plugin_directory($module, $plugin) {
  396. if ($module == 'ctools') {
  397. return 'plugins/' . $plugin;
  398. }
  399. elseif ($module == 'entityreference') {
  400. return "plugins/entityreference/$plugin";
  401. }
  402. }
  403. /**
  404. * Implements hook_permission().
  405. */
  406. function og_permission() {
  407. return array(
  408. 'administer group' => array(
  409. 'title' => t('Administer Organic groups permissions'),
  410. 'description' => t('Administer all groups and permissions.'),
  411. ),
  412. );
  413. }
  414. /**
  415. * Implements hook_og_permission().
  416. */
  417. function og_og_permission() {
  418. // Generate standard node permissions for all applicable node types.
  419. $perms = array();
  420. $perms['update group'] = array(
  421. 'title' => t('Edit group'),
  422. 'description' => t('Edit the group. Note: This permission controls only node entity type groups.'),
  423. 'default role' => array(OG_ADMINISTRATOR_ROLE),
  424. );
  425. $perms['administer group'] = array(
  426. 'title' => t('Administer group'),
  427. 'description' => t('Manage group members and content in the group.'),
  428. 'default role' => array(OG_ADMINISTRATOR_ROLE),
  429. 'restrict access' => TRUE,
  430. );
  431. foreach (node_permissions_get_configured_types() as $type) {
  432. $perms = array_merge($perms, og_list_permissions($type));
  433. }
  434. return $perms;
  435. }
  436. /**
  437. * Implements hook_og_default_roles().
  438. */
  439. function og_og_default_roles() {
  440. return array(OG_ADMINISTRATOR_ROLE);
  441. }
  442. /**
  443. * Implements hook_node_access().
  444. */
  445. function og_node_access($node, $op, $account) {
  446. $type = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type);
  447. if ($op == 'create' && og_is_group_content_type('node', $type)) {
  448. // Save some legwork if the user has the core permission and strict node
  449. // access is not set.
  450. if (!variable_get('og_node_access_strict', TRUE) && user_access("create $type content", $account)) {
  451. // We just ignore: core access will take care of it.
  452. return NODE_ACCESS_IGNORE;
  453. }
  454. if (user_access('administer group', $account)) {
  455. return NODE_ACCESS_ALLOW;
  456. }
  457. // We can't check if user has create permissions using og_user_access(), as
  458. // there is no group context. However, we can check if there are any groups
  459. // the user will be able to select, and if not, we don't allow access.
  460. // @see OgSelectionHandler::getReferencableEntities()
  461. $required = FALSE;
  462. foreach (og_get_group_audience_fields('node', $type) as $field_name => $label) {
  463. $field = field_info_field($field_name);
  464. $instance = field_info_instance('node', $field_name, $type);
  465. // Set the "field mode" to default, before passing it to the
  466. // selection handler.
  467. $instance['field_mode'] = 'default';
  468. if (entityreference_get_selection_handler($field, $instance)->getReferencableEntities(NULL, 'CONTAINS', 1)) {
  469. return NODE_ACCESS_ALLOW;
  470. }
  471. // Allow users to create content outside of groups, if none of the
  472. // audience fields is required.
  473. if ($instance['required']) {
  474. $required = TRUE;
  475. }
  476. }
  477. // If no group audience field is required, we ignore.
  478. if (!$required) {
  479. return NODE_ACCESS_IGNORE;
  480. }
  481. // Otherwise, ignore or deny based on whether strict node access is set.
  482. return variable_get('og_node_access_strict', TRUE) ? NODE_ACCESS_DENY : NODE_ACCESS_IGNORE;
  483. }
  484. elseif (in_array($op, array('update', 'delete'))) {
  485. $access = og_user_access_entity('administer group', 'node', $node, $account);
  486. if (is_null($access)) {
  487. // The node isn't in an OG context, so no need to keep testing.
  488. return NODE_ACCESS_IGNORE;
  489. }
  490. else {
  491. $access = $access ||
  492. // Any content.
  493. og_user_access_entity("$op any $type content", 'node', $node, $account) ||
  494. // Own content.
  495. ($account->uid == $node->uid && og_user_access_entity("$op own $type content", 'node', $node, $account));
  496. }
  497. if (!$access && $op == 'update' && og_is_group('node', $node)) {
  498. // The node is a group, so check "update group" permission.
  499. $access = og_user_access_entity('update group', 'node', $node, $account);
  500. }
  501. if ($access) {
  502. return NODE_ACCESS_ALLOW;
  503. }
  504. // Check if OG should explicitly deny access or not.
  505. return variable_get('og_node_access_strict', TRUE) ? NODE_ACCESS_DENY : NODE_ACCESS_IGNORE;
  506. }
  507. return NODE_ACCESS_IGNORE;
  508. }
  509. /**
  510. * Implements hook_field_access().
  511. *
  512. * Hide group-audience fields from user's edit profile for non-privileged users.
  513. */
  514. function og_field_access($op, $field, $entity_type, $entity, $account) {
  515. global $user;
  516. if (empty($entity)) {
  517. // We are in field settings page.
  518. return;
  519. }
  520. if (!$user->uid) {
  521. // User is anonymous, and user register might try to add the
  522. // group-audience field.
  523. return;
  524. }
  525. if ($op != 'edit') {
  526. return;
  527. }
  528. $field_name = $field['field_name'];
  529. list($id, $vid, $bundle_name) = entity_extract_ids($entity_type, $entity);
  530. if ($field_name == OG_GROUP_FIELD) {
  531. $wrapper = entity_metadata_wrapper($entity_type, $entity);
  532. if ($wrapper->getIdentifier() && !$wrapper->{OG_GROUP_FIELD}->value()) {
  533. // Entity isn't an active group.
  534. return;
  535. }
  536. $instance = field_info_instance($entity_type, $field_name, $bundle_name);
  537. if (!empty($instance['widget']['settings']['og_hide'])) {
  538. return FALSE;
  539. }
  540. return;
  541. }
  542. if (!og_is_group_audience_field($field_name)) {
  543. return;
  544. }
  545. $field = field_info_field($field_name);
  546. $settings = $field['settings']['handler_settings'];
  547. // Check if we are editing the user entity.
  548. if ($entity_type == 'user') {
  549. return user_access('administer group', $account);
  550. }
  551. }
  552. /**
  553. * Implements hook_views_api().
  554. */
  555. function og_views_api() {
  556. return array(
  557. 'api' => 3,
  558. 'path' => drupal_get_path('module', 'og') . '/includes/views',
  559. );
  560. }
  561. /**
  562. * Implements hook_field_create_instance().
  563. *
  564. * - Create default OG roles per entity-type and bundle.
  565. * - Create a group audience field on the user's entity, referencing the first
  566. * group defined.
  567. */
  568. function og_field_create_instance($instance) {
  569. if ($instance['field_name'] != OG_GROUP_FIELD) {
  570. return;
  571. }
  572. // Create default roles per entity-type per bundle.
  573. og_roles_override($instance['entity_type'], $instance['bundle'], 0);
  574. // Check if we need to add a group audience on the user's entity.
  575. // We add a different field, so each field can be set differently.
  576. $entity_type = $instance['entity_type'];
  577. $bundle = $instance['bundle'];
  578. foreach (array_keys(og_get_group_audience_fields()) as $field_name) {
  579. $field = field_info_field($field_name);
  580. if ($field['settings']['target_type'] == $entity_type && empty($field['settings']['handler_settings']['target_bundles'])) {
  581. return;
  582. }
  583. if ($field['settings']['target_type'] == $entity_type && in_array($bundle, $field['settings']['handler_settings']['target_bundles'])) {
  584. return;
  585. }
  586. }
  587. // If we reached here, it means we need to create a field.
  588. // Pick an unused name.
  589. $field_name = substr("og_user_$entity_type", 0, 32);
  590. $i = 1;
  591. while (field_info_field($field_name)) {
  592. $field_name = substr("og_user_$entity_type", 0, 32 - strlen($i)) . $i;
  593. ++$i;
  594. }
  595. $og_field = og_fields_info(OG_AUDIENCE_FIELD);
  596. $og_field['field']['settings']['target_type'] = $entity_type;
  597. $og_field['instance']['label'] = t('Group membership');
  598. // If the user entity type has multiple bundles, make sure to attach a field
  599. // instance to all of them.
  600. $entity_info = entity_get_info('user');
  601. foreach (array_keys($entity_info['bundles']) as $user_bundle) {
  602. og_create_field($field_name, 'user', $user_bundle, $og_field);
  603. }
  604. }
  605. /**
  606. * Implements field_delete_instance().
  607. *
  608. * - Invalidate OG's static cache if a group-audience field is deleted.
  609. * - Delete the default OG roles per entity-type and bundle.
  610. */
  611. function og_field_delete_instance($instance) {
  612. if (og_is_group_audience_field($instance['field_name'])) {
  613. og_invalidate_cache();
  614. }
  615. if ($instance['field_name'] != OG_GROUP_FIELD) {
  616. return;
  617. }
  618. // Get the per-bundle roles.
  619. $roles = og_roles($instance['entity_type'], $instance['bundle']);
  620. foreach ($roles as $rid => $name) {
  621. og_role_delete($rid);
  622. }
  623. }
  624. /**
  625. * Implements hook_form_alter().
  626. */
  627. function og_form_alter(&$form, $form_state, $form_id) {
  628. if (empty($form['#entity_type']) || empty($form['#bundle'])) {
  629. return;
  630. }
  631. $entity_type = $form['#entity_type'];
  632. $bundle = $form['#bundle'];
  633. if ($entity_type == 'user' || !og_is_group_type($entity_type, $bundle)) {
  634. return;
  635. }
  636. $form['#validate'][] = 'og_form_group_manager_validate';
  637. }
  638. /**
  639. * Validate handler; Make sure a group can be created.
  640. *
  641. * We check if the group manager has a matching group-audience field for the
  642. * OG membership to be created in.
  643. */
  644. function og_form_group_manager_validate($form, &$form_state) {
  645. $entity_type = $form['#entity_type'];
  646. $bundle = $form['#bundle'];
  647. if (empty($form_state[$entity_type])) {
  648. // We are inside field settings page.
  649. return;
  650. }
  651. $entity = $form_state[$entity_type];
  652. $langcode = $form_state['values']['language'];
  653. if (!isset($form_state['values']['uid']) || !isset($entity->uid)) {
  654. // There is no user ID property on the entity.
  655. return;
  656. }
  657. if (isset($form_state['values'][OG_GROUP_FIELD]) && empty($form_state['values'][OG_GROUP_FIELD][$langcode][0]['value'])) {
  658. // Not a group.
  659. return;
  660. }
  661. if (!isset($form_state['values'][OG_GROUP_FIELD])) {
  662. // Field doesn't appear in the form, so it is probably hidden by
  663. // hook_field_access(). So check the default value of the field.
  664. $field = field_info_field(OG_GROUP_FIELD);
  665. $instance = field_info_instance($entity_type, OG_GROUP_FIELD, $bundle);
  666. $items = field_get_default_value($entity_type, $entity, $field, $instance, $langcode);
  667. if (empty($items[0]['value'])) {
  668. // Default value is not a group.
  669. return;
  670. }
  671. }
  672. if ($entity_type == 'node') {
  673. // A user might assign the node author by entering a user name in the
  674. // node form, which we then need to translate to a user ID.
  675. // However, this happens later on, in node_submit(), so we do a special
  676. // check for the node entity.
  677. if (!$account = user_load_by_name($form_state['values']['name'])) {
  678. // Invalid username.
  679. return;
  680. }
  681. }
  682. else {
  683. $account = user_load($form_state['values']['uid']);
  684. }
  685. list($id) = entity_extract_ids($entity_type, $entity);
  686. if ($id && $entity->uid == $account->uid) {
  687. // The entity's user ID hasn't changed.
  688. return;
  689. }
  690. if ($access = og_get_best_group_audience_field('user', $account, $entity_type, $bundle)) {
  691. // Matching group audience field found.
  692. return;
  693. }
  694. form_error($form, t("Can't save entity as group, because user @name can't be subscribed to group and become a manager.", array('@name' => format_username($account))));
  695. }
  696. /**
  697. * Implements hook_entity_insert().
  698. */
  699. function og_entity_insert($entity, $entity_type) {
  700. if (!og_is_group($entity_type, $entity)) {
  701. return;
  702. }
  703. list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
  704. if (!empty($entity->uid)) {
  705. // Subscribe the group manager.
  706. og_group($entity_type, $id, array('entity' => $entity->uid));
  707. // Assign roles to group manager.
  708. $name = 'og_group_manager_default_rids_' . $entity_type . '_' . $bundle;
  709. if ($rids = variable_get($name)) {
  710. foreach ($rids as $rid) {
  711. og_role_grant($entity_type, $id, $entity->uid, $rid);
  712. }
  713. }
  714. }
  715. if (!og_is_group_default_access($entity_type, $entity)) {
  716. // Override default roles.
  717. og_roles_override($entity_type, $bundle, $id);
  718. }
  719. }
  720. /**
  721. * Implements hook_entity_update().
  722. */
  723. function og_entity_update($entity, $entity_type) {
  724. if (!og_is_group($entity_type, $entity)) {
  725. return;
  726. }
  727. list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
  728. if (!empty($entity->uid) && !og_is_member($entity_type, $entity, 'user', $entity->uid)) {
  729. // Subscribe the group manager, in case the owner changed.
  730. og_group($entity_type, $id, array('entity' => $entity->uid));
  731. // Assign roles to group manager.
  732. $name = 'og_group_manager_default_rids_' . $entity_type . '_' . $bundle;
  733. if ($rids = variable_get($name)) {
  734. foreach ($rids as $rid) {
  735. og_role_grant($entity_type, $id, $entity->uid, $rid);
  736. }
  737. }
  738. }
  739. $origianl_entity = $entity->original;
  740. $property = OG_DEFAULT_ACCESS_FIELD;
  741. if (!empty($entity->{$property}) && $entity->{$property} != $origianl_entity->{$property}) {
  742. if (!og_is_group_default_access($entity_type, $entity)) {
  743. // Override default roles.
  744. og_roles_override($entity_type, $bundle, $id);
  745. }
  746. else {
  747. // Delete overridden roles.
  748. og_delete_user_roles_by_group($entity_type, $entity);
  749. }
  750. }
  751. }
  752. /**
  753. * Implements hook_field_attach_insert().
  754. */
  755. function og_field_attach_insert($entity_type, $entity) {
  756. _og_update_entity_fields($entity_type, $entity);
  757. }
  758. /**
  759. * Implements hook_field_attach_update().
  760. */
  761. function og_field_attach_update($entity_type, $entity) {
  762. _og_update_entity_fields($entity_type, $entity);
  763. }
  764. /**
  765. * Update the field values in the entity, to reflect the membership.
  766. *
  767. * This is used to allow other modules that save a new/ existing entity
  768. * to act on the field values, even before hook_field_load() is called.
  769. */
  770. function _og_update_entity_fields($entity_type, $entity) {
  771. list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
  772. if (!og_is_group_content_type($entity_type, $bundle)) {
  773. return;
  774. }
  775. $wrapper = entity_metadata_wrapper($entity_type, $entity);
  776. foreach (og_get_group_audience_fields($entity_type, $bundle) as $field_name => $label) {
  777. $field = field_info_field($field_name);
  778. $gids = array();
  779. if ($field['cardinality'] == 1) {
  780. if ($og_membership = $wrapper->{$field_name . '__og_membership'}->value()) {
  781. // Wrapper return an array.
  782. $gids = $og_membership[0]->gid;
  783. }
  784. }
  785. else {
  786. foreach ($wrapper->{$field_name . '__og_membership'}->value() as $og_membership) {
  787. $gids[] = $og_membership->gid;
  788. }
  789. }
  790. if ($gids) {
  791. $wrapper->{$field_name}->set($gids);
  792. }
  793. }
  794. }
  795. /**
  796. * Implements hook_entity_delete().
  797. */
  798. function og_entity_delete($entity, $entity_type) {
  799. list($id, , $bundle) = entity_extract_ids($entity_type, $entity);
  800. if (og_is_group($entity_type, $entity)) {
  801. og_delete_user_roles_by_group($entity_type, $entity);
  802. og_membership_delete_by_group($entity_type, $entity);
  803. }
  804. if (og_is_group_content_type($entity_type, $bundle)) {
  805. // As the field attachers are called after hook_entity_presave() we
  806. // can't delete the OG memberships here. So we just mark the entity
  807. // as being deleted, and we will do the actual delete in
  808. // OgBehaviorHandler::delete().
  809. $entity->delete_og_membership = TRUE;
  810. }
  811. }
  812. /**
  813. * Implements hook_og_membership_insert().
  814. */
  815. function og_og_membership_insert($og_membership) {
  816. if ($og_membership->entity_type == 'user' && module_exists('rules')) {
  817. rules_invoke_event('og_user_insert', $og_membership, entity_metadata_wrapper('user', $og_membership->etid));
  818. }
  819. }
  820. /**
  821. * Implements hook_og_membership_update().
  822. */
  823. function og_og_membership_update($og_membership) {
  824. if ($og_membership->entity_type == 'user' && module_exists('rules')) {
  825. if ($og_membership->original->state != OG_STATE_ACTIVE && $og_membership->state == OG_STATE_ACTIVE) {
  826. rules_invoke_event('og_user_approved', $og_membership, entity_metadata_wrapper('user', $og_membership->etid));
  827. }
  828. if ($og_membership->original->state != OG_STATE_BLOCKED && $og_membership->state == OG_STATE_BLOCKED) {
  829. rules_invoke_event('og_user_blocked', $og_membership, entity_metadata_wrapper('user', $og_membership->etid));
  830. }
  831. }
  832. }
  833. /**
  834. * Implements hook_og_membership_delete().
  835. */
  836. function og_og_membership_delete($og_membership) {
  837. if ($og_membership->entity_type != 'user') {
  838. return;
  839. }
  840. // Remove possible records in the {og_users_roles} table.
  841. db_delete('og_users_roles')
  842. ->condition('uid', $og_membership->etid)
  843. ->condition('gid', $og_membership->gid)
  844. ->condition('group_type', $og_membership->group_type)
  845. ->execute();
  846. if (module_exists('rules')) {
  847. rules_invoke_event('og_user_delete', $og_membership, entity_metadata_wrapper('user', $og_membership->etid));
  848. }
  849. }
  850. /**
  851. * Implements hook_og_fields_info().
  852. */
  853. function og_og_fields_info() {
  854. $items[OG_GROUP_FIELD] = array(
  855. 'type' => array('group'),
  856. 'description' => t('Determine if this should be a group.'),
  857. 'field' => array(
  858. 'field_name' => OG_GROUP_FIELD,
  859. 'type' => 'list_boolean',
  860. 'cardinality' => 1,
  861. 'settings' => array(
  862. 'allowed_values' => array(0 => 'Not a group', 1 => 'Group'),
  863. 'allowed_values_function' => '',
  864. ),
  865. ),
  866. 'instance' => array(
  867. 'label' => t('Group'),
  868. 'description' => t('Determine if this is an OG group.'),
  869. 'display_label' => 1,
  870. 'widget' => array(
  871. 'module' => 'options',
  872. 'settings' => array(
  873. 'og_hide' => TRUE,
  874. ),
  875. 'type' => 'options_onoff',
  876. 'weight' => 0,
  877. ),
  878. 'default_value' => array(0 => array('value' => 1)),
  879. 'view modes' => array(
  880. 'full' => array(
  881. 'label' => t('Full'),
  882. 'type' => 'og_group_subscribe',
  883. 'custom settings' => FALSE,
  884. ),
  885. 'teaser' => array(
  886. 'label' => t('Teaser'),
  887. 'type' => 'og_group_subscribe',
  888. 'custom settings' => FALSE,
  889. ),
  890. ),
  891. ),
  892. );
  893. $items[OG_DEFAULT_ACCESS_FIELD] = array(
  894. 'type' => array('group'),
  895. 'description' => t('Determine if group should use default roles and permissions.'),
  896. 'field' => array(
  897. 'field_name' => OG_DEFAULT_ACCESS_FIELD,
  898. 'type' => 'list_boolean',
  899. 'cardinality' => 1,
  900. 'settings' => array('allowed_values' => array(0 => 'Use default roles and permissions', 1 => 'Override default roles and permissions'), 'allowed_values_function' => ''),
  901. ),
  902. 'instance' => array(
  903. 'label' => t('Group roles and permissions'),
  904. 'widget' => array(
  905. 'module' => 'options',
  906. 'settings' => array(),
  907. 'type' => 'options_select',
  908. ),
  909. 'required' => TRUE,
  910. // Use default role and permissions as default value.
  911. 'default_value' => array(0 => array('value' => 0)),
  912. 'view modes' => array(
  913. 'full' => array(
  914. 'label' => t('Full'),
  915. 'type' => 'list_default',
  916. 'custom settings' => FALSE,
  917. ),
  918. 'teaser' => array(
  919. 'label' => t('Teaser'),
  920. 'type' => 'list_default',
  921. 'custom settings' => FALSE,
  922. ),
  923. ),
  924. ),
  925. );
  926. $items[OG_AUDIENCE_FIELD] = array(
  927. 'multiple' => TRUE,
  928. 'type' => array('group content'),
  929. 'description' => t('Determine to which groups this group content is assigned to.'),
  930. 'field' => array(
  931. 'field_name' => OG_AUDIENCE_FIELD,
  932. 'type' => 'entityreference',
  933. 'cardinality' => FIELD_CARDINALITY_UNLIMITED,
  934. 'settings' => array(
  935. 'handler' => 'og',
  936. 'handler_submit' => 'Change handler',
  937. 'handler_settings' => array(
  938. 'behaviors' => array(
  939. 'og_behavior' => array(
  940. 'status' => TRUE,
  941. ),
  942. ),
  943. 'target_bundles' => array(),
  944. 'membership_type' => OG_MEMBERSHIP_TYPE_DEFAULT,
  945. ),
  946. 'target_type' => 'node',
  947. ),
  948. ),
  949. 'instance' => array(
  950. 'label' => t('Groups audience'),
  951. 'widget' => array(
  952. 'type' => 'og_complex',
  953. 'module' => 'og',
  954. 'settings' => array(),
  955. ),
  956. 'settings' => array(
  957. 'behaviors' => array(
  958. 'og_widget' => array(
  959. 'status' => TRUE,
  960. 'default' => array(
  961. 'widget_type' => 'options_select',
  962. ),
  963. 'admin' => array(
  964. 'widget_type' => 'entityreference_autocomplete',
  965. ),
  966. ),
  967. ),
  968. ),
  969. 'view modes' => array(
  970. 'full' => array(
  971. 'label' => t('Full'),
  972. 'type' => 'og_list_default',
  973. 'custom settings' => FALSE,
  974. ),
  975. 'teaser' => array(
  976. 'label' => t('Teaser'),
  977. 'type' => 'og_list_default',
  978. 'custom settings' => FALSE,
  979. ),
  980. ),
  981. ),
  982. );
  983. return $items;
  984. }
  985. /**
  986. * Creates a new membership type.
  987. *
  988. * If a message type already exists, an exception will be thrown.
  989. *
  990. * @return OgMembershipType
  991. * Returns a new OG membership type object.
  992. */
  993. function og_membership_type_create($name, $values = array()) {
  994. global $language;
  995. // Make sure the message type doesn't already exist, to prevent duplicate key
  996. // error.
  997. if (og_membership_type_load($name)) {
  998. throw new OgException('Group membership type ' . check_plain($name) . ' already exists.');
  999. }
  1000. $values['name'] = $name;
  1001. $values += array(
  1002. 'language' => $language->language,
  1003. );
  1004. $wrapper = entity_property_values_create_entity('og_membership_type', $values);
  1005. return $wrapper->value();
  1006. }
  1007. /**
  1008. * OG membership type loader.
  1009. *
  1010. * @param $type_name
  1011. * (optional) The name for this message type. If no type is given all existing
  1012. * types are returned.
  1013. *
  1014. * @return MessageType
  1015. * Returns a fully-loaded message type definition if a type name is passed.
  1016. * Else an array containing all types is returned.
  1017. */
  1018. function og_membership_type_load($name = NULL) {
  1019. // Replace dashes with underscores so this can be used as menu argument
  1020. // loader too.
  1021. $types = entity_load_multiple_by_name('og_membership_type', isset($name) ? array(strtr($name, array('-' => '_'))) : FALSE);
  1022. if (isset($name)) {
  1023. return isset($types[$name]) ? $types[$name] : FALSE;
  1024. }
  1025. return $types;
  1026. }
  1027. /**
  1028. * Inserts or updates an OG membership type entity into the database.
  1029. *
  1030. * @param $og_membership
  1031. * The OG membership type entiyt to be saved.
  1032. *
  1033. * @return
  1034. * Failure to write a record will return FALSE. Otherwise SAVED_NEW or
  1035. * SAVED_UPDATED is returned depending on the operation performed.
  1036. */
  1037. function og_membership_type_save($og_membership) {
  1038. return entity_save('og_membership_type', $og_membership);
  1039. }
  1040. /**
  1041. * Deletes an existing OG membership type.
  1042. *
  1043. * @param $og_membership
  1044. * The OG membership type entity to be deleted.
  1045. */
  1046. function og_membership_type_delete($og_membership) {
  1047. return entity_delete('og_membership_type', $og_membership);
  1048. }
  1049. /**
  1050. * Access callback for the OG membership type entity.
  1051. */
  1052. function og_membership_type_access($op, $entity, $account = NULL, $entity_type = 'og_membership') {
  1053. // No-end user needs access to this entity, so restrict it to admins.
  1054. return user_access('administer group');
  1055. }
  1056. /**
  1057. * Reset static cache related to group membership.
  1058. */
  1059. function og_membership_invalidate_cache() {
  1060. $caches = array(
  1061. 'og_get_entity_groups',
  1062. 'og_get_membership',
  1063. 'og_get_field_og_membership_properties',
  1064. );
  1065. foreach ($caches as $cache) {
  1066. drupal_static_reset($cache);
  1067. }
  1068. }
  1069. /**
  1070. * Creates a new OG membership.
  1071. *
  1072. * If a group membership already exists, an exception will be thrown.
  1073. *
  1074. * @param $group_type
  1075. * The entity type of the group.
  1076. * @param $gid
  1077. * The group ID.
  1078. * @param $entity_type
  1079. * The entity type of the group content.
  1080. * @param $etid
  1081. * The entity ID of the group content.
  1082. * @param $field_name
  1083. * The group audience field name.
  1084. * @param $values
  1085. * (optional) Array of fields values to be attached to the OG membership, that
  1086. * will be processed using entity-metadata wrapper.
  1087. *
  1088. * @return OgMembership
  1089. * Returns a new OG membership object.
  1090. *
  1091. * @see entity_property_values_create_entity()
  1092. */
  1093. function og_membership_create($group_type, $gid, $entity_type, $etid, $field_name, $values = array()) {
  1094. global $language;
  1095. $values += array(
  1096. 'group_type' => $group_type,
  1097. 'gid' => $gid,
  1098. 'entity_type' => $entity_type,
  1099. 'etid' => $etid,
  1100. 'state' => OG_STATE_ACTIVE,
  1101. 'created' => time(),
  1102. 'field_name' => $field_name,
  1103. 'language' => $language->language,
  1104. );
  1105. if (!og_is_group_audience_field($field_name)) {
  1106. throw new OgException(format_string('%field-name is not a valid group-audience field.', array('%field-name' => $field_name)));
  1107. }
  1108. // Get the type from the field.
  1109. $field = field_info_field($field_name);
  1110. $values['type'] = $field['settings']['handler_settings']['membership_type'];
  1111. $wrapper = entity_property_values_create_entity('og_membership', $values);
  1112. return $wrapper->value();
  1113. }
  1114. /**
  1115. * OG membership loader.
  1116. *
  1117. * @param $name
  1118. * (optional) The name for this group membership. If no type is given all existing
  1119. * types are returned.
  1120. *
  1121. * @return OgMembership
  1122. * Returns a fully-loaded group membership definition if a type name is passed.
  1123. * Else an array containing all types is returned.
  1124. */
  1125. function og_membership_load($id) {
  1126. return entity_load_single('og_membership', $id);
  1127. }
  1128. /**
  1129. * Load multiple OG membership entities based on certain conditions.
  1130. *
  1131. * @param $gids
  1132. * An array of group membership IDs.
  1133. * @param $conditions
  1134. * An array of conditions to match against the {entity} table.
  1135. * @param $reset
  1136. * A boolean indicating that the internal cache should be reset.
  1137. *
  1138. * @return
  1139. * An array of group entities, indexed by group ID.
  1140. */
  1141. function og_membership_load_multiple($ids = array(), $conditions = array(), $reset = FALSE) {
  1142. return entity_load('og_membership', $ids, $conditions, $reset);
  1143. }
  1144. /**
  1145. * Get the group membership entity by user and group.
  1146. *
  1147. * @return
  1148. * The OgMembership object if found, or FALSE.
  1149. */
  1150. function og_get_membership($group_type, $gid, $entity_type, $etid) {
  1151. $return = &drupal_static(__FUNCTION__, array());
  1152. $identifier = $group_type . ':' . $gid . ':' . $entity_type . ':' . $etid;
  1153. if (!isset($return[$identifier])) {
  1154. $return[$identifier] = FALSE;
  1155. $query = new EntityFieldQuery();
  1156. $result = $query
  1157. ->entityCondition('entity_type', 'og_membership')
  1158. ->propertyCondition('gid', $gid, '=')
  1159. ->propertyCondition('group_type', $group_type, '=')
  1160. ->propertyCondition('etid', $etid, '=')
  1161. ->propertyCondition('entity_type', $entity_type, '=')
  1162. ->execute();
  1163. if (!empty($result['og_membership'])) {
  1164. $key = key($result['og_membership']);
  1165. $return[$identifier] = $key;
  1166. }
  1167. }
  1168. if (!empty($return[$identifier])) {
  1169. $og_membership = og_membership_load($return[$identifier]);
  1170. return $og_membership;
  1171. }
  1172. return FALSE;
  1173. }
  1174. /**
  1175. * Implements hook_entity_query_alter().
  1176. *
  1177. * Add "og_membership" tag if there's a group audience field in the query.
  1178. *
  1179. * @see og_query_og_membership_alter().
  1180. */
  1181. function og_entity_query_alter(EntityFieldQuery $query) {
  1182. foreach ($query->fieldConditions as $values) {
  1183. if (og_is_group_audience_field($values['field']['field_name'])) {
  1184. $query->addTag('og_membership');
  1185. return;
  1186. }
  1187. }
  1188. }
  1189. /**
  1190. * Implements hook_query_QUERY_TAG_alter().
  1191. *
  1192. * Join the {og_membership} table and alter the query.
  1193. */
  1194. function og_query_og_membership_alter(QueryAlterableInterface $query) {
  1195. $tables = &$query->getTables();
  1196. $fields = &$query->getFields();
  1197. $conditions = &$query->conditions();
  1198. // Find the group-audience fields.
  1199. $field_names = array();
  1200. foreach ($query->alterMetaData['entity_field_query']->fieldConditions as $values) {
  1201. $field_name = $values['field']['field_name'];
  1202. if (og_is_group_audience_field($field_name)) {
  1203. $field_names[] = $field_name;
  1204. }
  1205. }
  1206. $aliases = array();
  1207. $base_table = FALSE;
  1208. $base_table_alias = '';
  1209. foreach ($tables as $alias => $values) {
  1210. if (!$base_table_alias && empty($values['join type'])) {
  1211. $base_table_alias = $alias;
  1212. }
  1213. if (strpos($alias, 'field_data') !== 0) {
  1214. continue;
  1215. }
  1216. $field_name = substr($values['table'], 11);
  1217. if (!in_array($field_name, $field_names)) {
  1218. continue;
  1219. }
  1220. if (empty($values['join type'])) {
  1221. // This is the base table, so remove it in favor of OG membership.
  1222. $base_table = TRUE;
  1223. }
  1224. unset($tables[$alias]);
  1225. $aliases[$alias] = $field_name;
  1226. }
  1227. foreach ($aliases as $alias => $field_name) {
  1228. foreach ($tables as $key => $values) {
  1229. $condition = str_replace("$alias.entity_type", 'ogm.entity_type', $values['condition']);
  1230. $condition = str_replace("$alias.entity_id", 'ogm.etid', $condition);
  1231. $tables[$key]['condition'] = $condition;
  1232. }
  1233. }
  1234. $entity_type = $query->alterMetaData['entity_field_query']->entityConditions['entity_type']['value'];
  1235. $entity_type = is_array($entity_type) ? $entity_type[0] : $entity_type;
  1236. $entity_info = entity_get_info($entity_type);
  1237. $id = $entity_info['entity keys']['id'];
  1238. if ($base_table) {
  1239. // If the table of the base entity does not exist (e.g. there is no
  1240. // property condition), we need to add it, as we don't have the
  1241. // revision ID and bundle in {og_membership} table.
  1242. $base_table = $entity_info['base table'];
  1243. if (strpos($base_table_alias, 'field_data') === 0) {
  1244. // Check if the entity base table already exists.
  1245. $base_table_alias = FALSE;
  1246. foreach ($tables as $table) {
  1247. if ($table['table'] == $base_table) {
  1248. $base_table_alias = $table['alias'];
  1249. break;
  1250. }
  1251. }
  1252. if (!$base_table_alias) {
  1253. $base_table_alias = $query->innerJoin($base_table, NULL, "$base_table.$id = ogm.etid");
  1254. }
  1255. }
  1256. // Point the revision ID and bundle to the base entity.
  1257. $fields['revision_id']['table'] = $base_table;
  1258. // If there is no revision table, use the bundle.
  1259. if (!empty($entity_info['entity keys']['revision'])) {
  1260. // Entity doesn't support revisions.
  1261. $fields['revision_id']['field'] = $entity_info['entity keys']['revision'];
  1262. }
  1263. elseif (!empty($entity_info['entity keys']['bundle'])) {
  1264. $fields['revision_id']['field'] = $entity_info['entity keys']['bundle'];
  1265. }
  1266. else {
  1267. // Entity doesn't have bundles (e.g. user).
  1268. $fields['revision_id']['field'] = $id;
  1269. }
  1270. $fields['bundle']['table'] = $base_table;
  1271. $fields['bundle']['field'] = !empty($entity_info['entity keys']['bundle']) ? $entity_info['entity keys']['bundle'] : $id;
  1272. $fields['entity_type']['table'] = 'ogm';
  1273. $fields['entity_id']['table'] = 'ogm';
  1274. $fields['entity_id']['field'] = 'etid';
  1275. $ogm = array(
  1276. 'join type' => NULL,
  1277. 'table' => 'og_membership',
  1278. 'alias' => 'ogm',
  1279. 'condition' => '',
  1280. 'arguments' => array(),
  1281. );
  1282. $tables = array_merge(array('ogm' => $ogm), $tables);
  1283. }
  1284. else {
  1285. $query->join('og_membership', 'ogm', "ogm.etid = $base_table_alias.entity_id");
  1286. }
  1287. _og_query_og_membership_alter_conditions($conditions, $aliases, $base_table_alias, $entity_info);
  1288. }
  1289. /**
  1290. * Recursively replace the fields to their aliases in the query's conditions.
  1291. *
  1292. * See og_query_og_membership_alter().
  1293. */
  1294. function _og_query_og_membership_alter_conditions(&$conditions, $aliases, $base_table_alias, $entity_info) {
  1295. foreach ($conditions as $delta => $values) {
  1296. if (!is_array($values)) {
  1297. continue;
  1298. }
  1299. // Handle conditions in a sub-query.
  1300. if (is_object($values['value'])) {
  1301. _og_query_og_membership_alter_conditions($values['value']->conditions(), $aliases, $base_table_alias, $entity_info);
  1302. }
  1303. // Handle sub-conditions.
  1304. if (is_object($values['field'])) {
  1305. _og_query_og_membership_alter_conditions($values['field']->conditions(), $aliases, $base_table_alias, $entity_info);
  1306. continue;
  1307. }
  1308. if (strpos($values['field'], 'field_data_') !== 0) {
  1309. continue;
  1310. }
  1311. // Explode spaces on the fiels, for handling only the first part in values
  1312. // such as "foo.nid = bar.nid".
  1313. $field_parts = explode(' ', $values['field'], 2);
  1314. list($table, $column) = explode('.', $field_parts[0]);
  1315. if (empty($aliases[$table])) {
  1316. continue;
  1317. }
  1318. $table = 'ogm';
  1319. // Replace entity_id or any other primary id (e.g. nid for the node
  1320. // entity).
  1321. $id_columns = array('entity_id', $entity_info['entity keys']['id']);
  1322. if (in_array($column, $id_columns)) {
  1323. $column = 'etid';
  1324. }
  1325. if ($column == 'deleted') {
  1326. unset($conditions[$delta]);
  1327. continue;
  1328. }
  1329. elseif (strpos($column, 'target_id')) {
  1330. $column = 'gid';
  1331. }
  1332. elseif ($column == 'bundle') {
  1333. // Add the bundle of the base entity type.
  1334. $table = $base_table_alias;
  1335. $column = $entity_info['entity keys']['bundle'];
  1336. }
  1337. $conditions[$delta]['field'] = "$table.$column";
  1338. // Add the second part if it exists.
  1339. if (!empty($field_parts[1])) {
  1340. $conditions[$delta]['field'] .= ' ' . $field_parts[1];
  1341. }
  1342. }
  1343. }
  1344. /**
  1345. * Inserts or updates an OG membership entity into the database.
  1346. *
  1347. * @param $og_membership
  1348. * The OG membership entity to be inserted.
  1349. *
  1350. * @return
  1351. * Failure to write a record will return FALSE. Otherwise SAVED_NEW or
  1352. * SAVED_UPDATED is returned depending on the operation performed.
  1353. */
  1354. function og_membership_save($og_membership) {
  1355. return entity_save('og_membership', $og_membership);
  1356. }
  1357. /**
  1358. * Delete an existing OG membership.
  1359. *
  1360. * @param $id
  1361. * The OG membership entity ID to be deleted.
  1362. */
  1363. function og_membership_delete($id) {
  1364. return og_membership_delete_multiple(array($id));
  1365. }
  1366. /**
  1367. * Delete multiple existing OG memberships.
  1368. *
  1369. * We can't use entity_delete_multiple(), as we need to make sure the field
  1370. * cache is invalidated.
  1371. *
  1372. * @param $ids
  1373. * Array with OG membership entity IDs to be deleted.
  1374. */
  1375. function og_membership_delete_multiple($ids = array()) {
  1376. entity_delete_multiple('og_membership', $ids);
  1377. og_membership_invalidate_cache();
  1378. }
  1379. /**
  1380. * Implements hook_cron_queue_info().
  1381. */
  1382. function og_cron_queue_info() {
  1383. $items['og_membership_orphans'] = array(
  1384. 'title' => t('OG orphans'),
  1385. 'worker callback' => 'og_membership_orphans_worker',
  1386. 'time' => 60,
  1387. );
  1388. return $items;
  1389. }
  1390. /**
  1391. * Queue worker; Process a queue item.
  1392. *
  1393. * Delete memberships, and if needed all related group-content.
  1394. */
  1395. function og_membership_orphans_worker($data) {
  1396. $group_type = $data['group_type'];
  1397. $gid = $data['gid'];
  1398. $query = new EntityFieldQuery();
  1399. $result = $query
  1400. ->entityCondition('entity_type', 'og_membership')
  1401. ->propertyCondition('group_type', $group_type, '=')
  1402. ->propertyCondition('gid', $gid, '=')
  1403. ->propertyOrderBy('id')
  1404. ->range(0, 50)
  1405. ->execute();
  1406. if (empty($result['og_membership'])) {
  1407. return;
  1408. }
  1409. $ids = array_keys($result['og_membership']);
  1410. if ($data['orphans']['move']) {
  1411. _og_orphans_move($ids, $data['orphans']['move']['group_type'], $data['orphans']['move']['gid']);
  1412. $queue = DrupalQueue::get('og_membership_orphans');
  1413. return $queue->createItem($data);
  1414. }
  1415. elseif ($data['orphans']['delete']) {
  1416. _og_orphans_delete($ids);
  1417. // Create a new item.
  1418. $queue = DrupalQueue::get('og_membership_orphans');
  1419. return $queue->createItem($data);
  1420. }
  1421. }
  1422. /**
  1423. * Helper function to delete orphan group-content.
  1424. *
  1425. * @param $ids
  1426. * Array of OG membership IDs.
  1427. *
  1428. * @see og_membership_delete_by_group_worker()
  1429. */
  1430. function _og_orphans_delete($ids) {
  1431. // Get all the group-content that is now orphan.
  1432. $orphans = array();
  1433. $og_memberships = og_membership_load_multiple($ids);
  1434. foreach ($og_memberships as $og_membership) {
  1435. $entity_type = $og_membership->entity_type;
  1436. $id = $og_membership->etid;
  1437. // Don't delete users.
  1438. if ($entity_type == 'user') {
  1439. continue;
  1440. }
  1441. $entity_groups = og_get_entity_groups($entity_type, $id);
  1442. // Orphan node can be relate to only one type of entity group.
  1443. if (count($entity_groups) == 1) {
  1444. $gids = reset($entity_groups);
  1445. // Orphan node can be relate to only one node.
  1446. if (count($gids) > 1) {
  1447. continue;
  1448. }
  1449. }
  1450. $orphans[$entity_type][] = $id;
  1451. }
  1452. if ($orphans) {
  1453. foreach ($orphans as $entity_type => $orphan_ids) {
  1454. entity_delete_multiple($entity_type, $orphan_ids);
  1455. }
  1456. }
  1457. // Delete the OG memberships.
  1458. og_membership_delete_multiple($ids);
  1459. }
  1460. /**
  1461. * Helper function to move orphan group-content to another group.
  1462. *
  1463. * @param $ids
  1464. * Array of OG membership IDs.
  1465. *
  1466. * @see og_membership_delete_by_group_worker()
  1467. */
  1468. function _og_orphans_move($ids, $group_type, $gid) {
  1469. if (!og_is_group($group_type, $gid)) {
  1470. $params = array(
  1471. '@group-type' => $group_type,
  1472. '@gid' => $gid,
  1473. );
  1474. throw new OgException(format_string('Cannot move orphan group-content to @group-type - @gid, as it is not a valid group.', $params));
  1475. }
  1476. $og_memberships = og_membership_load_multiple($ids);
  1477. foreach ($og_memberships as $og_membership) {
  1478. $entity_type = $og_membership->entity_type;
  1479. $id = $og_membership->etid;
  1480. if (count(og_get_entity_groups($entity_type, $id)) > 1) {
  1481. continue;
  1482. }
  1483. $og_membership->group_type = $group_type;
  1484. $og_membership->gid = $gid;
  1485. $og_membership->save();
  1486. }
  1487. }
  1488. /**
  1489. * Register memberships for deletion.
  1490. *
  1491. * if the property "skip_og_membership_delete_by_group" exists on the
  1492. * entity, this function will return early, and allow other implementing
  1493. * modules to deal with the deletion logic.
  1494. *
  1495. * @param $entity_type
  1496. * The group type.
  1497. * @param $entity
  1498. * The group entity object.
  1499. */
  1500. function og_membership_delete_by_group($entity_type, $entity) {
  1501. if (!empty($entity->skip_og_membership_delete_by_group)) {
  1502. return;
  1503. }
  1504. list($gid) = entity_extract_ids($entity_type, $entity);
  1505. $query = new EntityFieldQuery();
  1506. $result = $query
  1507. ->entityCondition('entity_type', 'og_membership')
  1508. ->propertyCondition('group_type', $entity_type, '=')
  1509. ->propertyCondition('gid', $gid, '=')
  1510. ->execute();
  1511. if (empty($result['og_membership'])) {
  1512. return;
  1513. }
  1514. if (variable_get('og_use_queue', FALSE)) {
  1515. $queue = DrupalQueue::get('og_membership_orphans');
  1516. // Add item to the queue.
  1517. $data = array(
  1518. 'group_type' => $entity_type,
  1519. 'gid' => $gid,
  1520. // Allow implementing modules to determine the disposition (e.g. delete
  1521. // orphan group content).
  1522. 'orphans' => array(
  1523. 'delete' => isset($entity->og_orphans['delete']) ? $entity->og_orphans['delete'] : variable_get('og_orphans_delete', FALSE),
  1524. 'move' => isset($entity->og_orphans['move']) ? $entity->og_orphans['move'] : array(),
  1525. ),
  1526. );
  1527. // Exit now, as the task will be processed via queue.
  1528. return $queue->createItem($data);
  1529. }
  1530. // No scalable solution was chosen, so just delete OG memberships.
  1531. og_membership_delete_multiple(array_keys($result['og_membership']));
  1532. }
  1533. /**
  1534. * Label callback; Return the label of OG membership entity.
  1535. */
  1536. function og_membership_label($og_membership) {
  1537. $wrapper = entity_metadata_wrapper('og_membership', $og_membership);
  1538. $params = array(
  1539. '@entity' => $wrapper->entity->label(),
  1540. '@group' => $wrapper->group->label(),
  1541. );
  1542. return t('@entity in group @group', $params);
  1543. }
  1544. /**
  1545. * Access callback for the group membership entity.
  1546. */
  1547. function og_membership_access($op, $entity, $account = NULL, $entity_type = 'og_membership') {
  1548. // No-end user needs access to this entity, so restrict it to admins.
  1549. return user_access('administer group');
  1550. }
  1551. /**
  1552. * Return TRUE if the entity is acting as a group.
  1553. *
  1554. * @param $entity_type
  1555. * The entity type.
  1556. * @param $entity
  1557. * The entity object, or the entity ID.
  1558. */
  1559. function og_is_group($entity_type, $entity) {
  1560. $wrapper = entity_metadata_wrapper($entity_type, $entity);
  1561. $bundle = $wrapper->getBundle();
  1562. if (!field_info_instance($entity_type, OG_GROUP_FIELD, $bundle)) {
  1563. return variable_get("og_is_group__{$entity_type}__{$bundle}", FALSE);
  1564. }
  1565. return !empty($wrapper->{OG_GROUP_FIELD}) && $wrapper->{OG_GROUP_FIELD}->value();
  1566. }
  1567. /**
  1568. * Invalidate cache.
  1569. *
  1570. * @param $gids
  1571. * Array with group IDs that their cache should be invalidated.
  1572. */
  1573. function og_invalidate_cache($gids = array()) {
  1574. // Reset static cache.
  1575. $caches = array(
  1576. 'og_user_access',
  1577. 'og_user_access_alter',
  1578. 'og_role_permissions',
  1579. 'og_get_user_roles',
  1580. 'og_get_permissions',
  1581. 'og_get_group_audience_fields',
  1582. );
  1583. foreach ($caches as $cache) {
  1584. drupal_static_reset($cache);
  1585. }
  1586. // Invalidate group membersihp cache.
  1587. og_membership_invalidate_cache();
  1588. // Let other OG modules know we invalidate cache.
  1589. module_invoke_all('og_invalidate_cache', $gids);
  1590. }
  1591. /**
  1592. * Return all existing groups of an entity type.
  1593. */
  1594. function og_get_all_group($group_type = 'node') {
  1595. if (!field_info_field(OG_GROUP_FIELD)) {
  1596. return array();
  1597. }
  1598. $query = new EntityFieldQuery();
  1599. $return = $query
  1600. ->entityCondition('entity_type', $group_type)
  1601. ->fieldCondition(OG_GROUP_FIELD, 'value', 1, '=')
  1602. ->execute();
  1603. return !empty($return[$group_type]) ? array_keys($return[$group_type]) : array();
  1604. }
  1605. /**
  1606. * Get the first best matching group-audience field.
  1607. *
  1608. * @param $entity_type
  1609. * The entity type.
  1610. * @param $entity
  1611. * The entity object.
  1612. * @param $group_type
  1613. * The group type.
  1614. * @param $group_bundle
  1615. * The group bundle.
  1616. * @param $skip_access
  1617. * TRUE, if current user access to the field, should be skipped.
  1618. * Defaults to FALSE.
  1619. */
  1620. function og_get_best_group_audience_field($entity_type, $entity, $group_type, $group_bundle, $skip_access = FALSE) {
  1621. list(,, $bundle) = entity_extract_ids($entity_type, $entity);
  1622. $field_names = og_get_group_audience_fields($entity_type, $bundle);
  1623. if (!$field_names) {
  1624. return;
  1625. }
  1626. foreach ($field_names as $field_name => $label) {
  1627. $field = field_info_field($field_name);
  1628. $settings = $field['settings'];
  1629. if ($settings['target_type'] != $group_type) {
  1630. // Group type doesn't match.
  1631. continue;
  1632. }
  1633. if (!empty($settings['handler_settings']['target_bundles']) && !in_array($group_bundle, $settings['handler_settings']['target_bundles'])) {
  1634. // Bundles don't match.
  1635. continue;
  1636. }
  1637. if (!og_check_field_cardinality($entity_type, $entity, $field_name)) {
  1638. // Field reached maximum.
  1639. continue;
  1640. }
  1641. if (!$skip_access && !field_access('view', $field, $entity_type, $entity)) {
  1642. // User can't access field.
  1643. continue;
  1644. }
  1645. return $field_name;
  1646. }
  1647. }
  1648. /**
  1649. * Return TRUE if a field can be used and has not reached maximum values.
  1650. *
  1651. * @param $entity_type
  1652. * The entity type.
  1653. * @param $entity
  1654. * The entity object or entity ID.
  1655. * @param $field_name
  1656. * The group audience field name.
  1657. */
  1658. function og_check_field_cardinality($entity_type, $entity, $field_name) {
  1659. $field = field_info_field($field_name);
  1660. if ($field['cardinality'] == FIELD_CARDINALITY_UNLIMITED) {
  1661. return TRUE;
  1662. }
  1663. $wrapper = entity_metadata_wrapper($entity_type, $entity);
  1664. return count($wrapper->{$field_name . '__og_membership'}->value(array('identifier' => TRUE))) < $field['cardinality'];
  1665. }
  1666. /**
  1667. * Set an association (e.g. subscribe) an entity to a group.
  1668. *
  1669. * @param $group_type
  1670. * The entity type of the group.
  1671. * @param $gid
  1672. * The group entity or ID.
  1673. * @param $values
  1674. * Array with the information to pass along, until it is processed in the
  1675. * field handlers.
  1676. * - "entity_type": (optional) The entity type (e.g. "node" or "user").
  1677. * Defaults to 'user'
  1678. * - "entity": (optional) The entity object or entity Id to set the
  1679. * association. Defaults to the current user if the $entity_type property is
  1680. * set to 'user'.
  1681. * - "field_name": The name of the field, the membership should be registered
  1682. * in. If no value given, a first field with the correct membership type
  1683. * will be used. If no field found, an execpetion will be thrown.
  1684. * @param $save_created
  1685. * (optional) If the OG membership is new, it determines whether the
  1686. * membership will be saved. Defaults to TRUE.
  1687. *
  1688. * @return
  1689. * The OG membership entity.
  1690. */
  1691. function og_group($group_type, $gid, $values = array(), $save_created = TRUE) {
  1692. global $user;
  1693. // Set default values.
  1694. $values += array(
  1695. 'entity_type' => 'user',
  1696. 'entity' => FALSE,
  1697. 'field_name' => FALSE,
  1698. 'state' => OG_STATE_ACTIVE,
  1699. );
  1700. $entity_type = $values['entity_type'];
  1701. $entity = $values['entity'];
  1702. $field_name = $values['field_name'];
  1703. $state = $values['state'];
  1704. if ($entity_type == 'user' && empty($entity)) {
  1705. // We don't pass the object, as we want entity_metadata_wrapper() to reload
  1706. // the user object.
  1707. $entity = $user->uid;
  1708. }
  1709. $wrapper = entity_metadata_wrapper($entity_type, $entity);
  1710. // If entity was an ID, get the object.
  1711. $entity = $wrapper->value();
  1712. $bundle = $wrapper->getBundle();
  1713. $id = $wrapper->getIdentifier();
  1714. if (is_object($gid)) {
  1715. $group = $gid;
  1716. }
  1717. else {
  1718. $group = entity_load_single($group_type, $gid);
  1719. }
  1720. // the group ID might be the entity, so re-popualte it.
  1721. list($gid,, $group_bundle) = entity_extract_ids($group_type, $group);
  1722. // Get membership if exists.
  1723. $og_membership = og_get_membership($group_type, $gid, $entity_type, $id);
  1724. if (!$og_membership && empty($field_name)) {
  1725. $params = array(
  1726. '%entity-type' => $entity_type,
  1727. '%bundle' => $bundle,
  1728. '%group-type' => $group_type,
  1729. '%group-bundle' => $group_bundle,
  1730. );
  1731. // Allow getting fields not accessible by the user.
  1732. $field_name = og_get_best_group_audience_field($entity_type, $entity, $group_type, $group_bundle, TRUE);
  1733. if (!$field_name) {
  1734. throw new OgException(format_string('There are no OG fields in entity %entity-type and bundle %bundle referencing %group-type - %group-bundle.', $params));
  1735. }
  1736. }
  1737. if ($og_membership) {
  1738. if (empty($og_membership->is_new) && $og_membership->field_name == $field_name && $og_membership->state == $state) {
  1739. // Entity is already associated with group.
  1740. return $og_membership;
  1741. }
  1742. elseif (!empty($field_name) && $og_membership->field_name != $field_name) {
  1743. // Ungroup the current association, as it needs to change field.
  1744. og_ungroup($group_type, $gid, $entity_type, $id);
  1745. $og_membership = FALSE;
  1746. }
  1747. elseif ($og_membership->state != $state){
  1748. // Change the state.
  1749. $og_membership->state = $state;
  1750. }
  1751. else {
  1752. // Nothing changed.
  1753. return $og_membership;
  1754. }
  1755. }
  1756. if (!$og_membership) {
  1757. // Unset the values, so we don't try to process them.
  1758. unset($values['entity_type'], $values['entity'], $values['field_name']);
  1759. // Create a new OG membership.
  1760. $og_membership = og_membership_create($group_type, $gid, $entity_type, $id, $field_name, $values);
  1761. }
  1762. if (empty($og_membership->is_new) || $save_created) {
  1763. // Save the membership for update, or if the OG membership is new when
  1764. // "save-created" is TRUE.
  1765. $og_membership->save();
  1766. }
  1767. return $og_membership;
  1768. }
  1769. /**
  1770. * Delete an association (e.g. unsubscribe) of an entity to a group.
  1771. *
  1772. * @param $group_type
  1773. * The entity type (e.g. "node").
  1774. * @param $gid
  1775. * The group entity object or ID, to ungroup.
  1776. * @param $entity_type
  1777. * (optional) The entity type (e.g. "node" or "user").
  1778. * @param $etid
  1779. * (optional) The entity object or ID, to ungroup.
  1780. *
  1781. * @return
  1782. * The entity with the fields updated.
  1783. */
  1784. function og_ungroup($group_type, $gid, $entity_type = 'user', $etid = NULL) {
  1785. if (is_object($gid)) {
  1786. list($gid) = entity_extract_ids($group_type, $gid);
  1787. }
  1788. if ($entity_type == 'user' && empty($etid)) {
  1789. global $user;
  1790. $etid = $user->uid;
  1791. }
  1792. elseif (is_object($etid)) {
  1793. list($etid) = entity_extract_ids($entity_type, $etid);
  1794. }
  1795. if ($og_membership = og_get_membership($group_type, $gid, $entity_type, $etid)) {
  1796. $og_membership->delete();
  1797. }
  1798. }
  1799. /**
  1800. * Determine whether a user has a given privilege.
  1801. *
  1802. * All permission checks in OG should go through this function. This
  1803. * way, we guarantee consistent behavior, and ensure that the superuser
  1804. * and group administrators can perform all actions.
  1805. *
  1806. * @param $group_type
  1807. * The entity type of the group.
  1808. * @param $gid
  1809. * The entity ID of the group.
  1810. * @param $string
  1811. * The permission, such as "administer group", being checked for.
  1812. * @param $account
  1813. * (optional) The account to check. Defaults to the current user.
  1814. * @param $skip_alter
  1815. * (optional) If TRUE then user access will not be sent to other modules
  1816. * using drupal_alter(). This can be used by modules implementing
  1817. * hook_og_user_access_alter() that still want to use og_user_access(), but
  1818. * without causing a recursion. Defaults to FALSE.
  1819. * @param $ignore_admin
  1820. * (optional) When TRUE the specific permission is checked, ignoring the
  1821. * "administer group" permission if the user has it. When FALSE, a user
  1822. * with "administer group" will be granted all permissions.
  1823. * Defaults to FALSE.
  1824. *
  1825. * @return
  1826. * TRUE or FALSE if the current user has the requested permission.
  1827. * NULL, if the given group isn't a valid group.
  1828. */
  1829. function og_user_access($group_type, $gid, $string, $account = NULL, $skip_alter = FALSE, $ignore_admin = FALSE) {
  1830. global $user;
  1831. $perm = &drupal_static(__FUNCTION__, array());
  1832. // Mark the group ID and permissions that invoked an alter.
  1833. $perm_alter = &drupal_static(__FUNCTION__ . '_alter', array());
  1834. if (!og_is_group($group_type, $gid)) {
  1835. // Not a group.
  1836. return NULL;
  1837. }
  1838. if (empty($account)) {
  1839. $account = clone $user;
  1840. }
  1841. // User #1 has all privileges.
  1842. if ($account->uid == 1) {
  1843. return TRUE;
  1844. }
  1845. // Administer group permission.
  1846. if (user_access('administer group', $account) && !$ignore_admin) {
  1847. return TRUE;
  1848. }
  1849. // Group manager has all privileges (if variable is TRUE).
  1850. if (!empty($account->uid) && variable_get('og_group_manager_full_access', TRUE)) {
  1851. $group = entity_load_single($group_type, $gid);
  1852. if (!empty($group->uid) && $group->uid == $account->uid) {
  1853. return TRUE;
  1854. }
  1855. }
  1856. $identifier = $group_type . ':' . $gid;
  1857. // To reduce the number of SQL queries, we cache the user's permissions
  1858. // in a static variable.
  1859. if (!isset($perm[$identifier][$account->uid])) {
  1860. $roles = og_get_user_roles($group_type, $gid, $account->uid);
  1861. $role_permissions = og_role_permissions($roles);
  1862. $perms = array();
  1863. foreach ($role_permissions as $one_role) {
  1864. $perms += $one_role;
  1865. }
  1866. $perm[$identifier][$account->uid] = $perms;
  1867. }
  1868. if (!$skip_alter && empty($perm_alter[$identifier][$account->uid][$string])) {
  1869. // Let modules alter the permissions. since $perm is static we create
  1870. // a clone of it.
  1871. $group = !empty($group) ? $group : entity_load_single($group_type, $gid);
  1872. $temp_perm = $perm[$identifier][$account->uid];
  1873. $context = array(
  1874. 'string' => $string,
  1875. 'group_type' => $group_type,
  1876. 'group' => $group,
  1877. 'account' => $account,
  1878. );
  1879. drupal_alter('og_user_access', $temp_perm, $context);
  1880. // Re-assing the altered permissions.
  1881. $perm[$identifier][$account->uid] = $temp_perm;
  1882. // Make sure alter isn't called for the same permissions.
  1883. $perm_alter[$identifier][$account->uid][$string] = TRUE;
  1884. }
  1885. return !empty($perm[$identifier][$account->uid][$string]) || (!empty($perm[$identifier][$account->uid]['administer group']) && !$ignore_admin);
  1886. }
  1887. /**
  1888. * Check if a user has access to a permission on a certain entity context.
  1889. *
  1890. * @param $perm
  1891. * The organic groups permission.
  1892. * @param $entity_type
  1893. * The entity type.
  1894. * @param $entity
  1895. * The entity object, or the entity ID.
  1896. * @param $account
  1897. * (optional) The user object. If empty the current user will be used.
  1898. *
  1899. * @return
  1900. * Returns TRUE if the user has access to the permission, otherwise FALSE, or
  1901. * if the entity is not in OG context, function will return NULL. This allows
  1902. * a distinction between FALSE - no access, and NULL - no access as no OG
  1903. * context found.
  1904. */
  1905. function og_user_access_entity($perm, $entity_type, $entity, $account = NULL) {
  1906. if (empty($account)) {
  1907. global $user;
  1908. $account = clone $user;
  1909. }
  1910. // Set the default for the case there is not a group or a group content.
  1911. $result = NULL;
  1912. if (empty($entity)) {
  1913. // $entity might be NULL, so return early.
  1914. // @see field_access().
  1915. return $result;
  1916. }
  1917. elseif (is_numeric($entity)) {
  1918. $entity = entity_load_single($entity_type, $entity);
  1919. }
  1920. list($id, $vid, $bundle_name) = entity_extract_ids($entity_type, $entity);
  1921. if (empty($id)) {
  1922. // Entity isn't saved yet.
  1923. return $result;
  1924. }
  1925. $is_group = og_is_group($entity_type, $entity);
  1926. $is_group_content = og_is_group_content_type($entity_type, $bundle_name);
  1927. if ($is_group) {
  1928. if (og_user_access($entity_type, $id, $perm, $account)) {
  1929. return TRUE;
  1930. }
  1931. else {
  1932. // An entity can be a group and group content in the same time. The group
  1933. // didn't return TRUE, but the user still might have access to the
  1934. // permission in group content context.
  1935. $result = FALSE;
  1936. }
  1937. }
  1938. if ($is_group_content && $groups = og_get_entity_groups($entity_type, $entity)) {
  1939. foreach ($groups as $group_type => $gids) {
  1940. foreach ($gids as $gid) {
  1941. if (og_user_access($group_type, $gid, $perm, $account)) {
  1942. return TRUE;
  1943. }
  1944. }
  1945. }
  1946. return FALSE;
  1947. }
  1948. // Either the user didn't have permission, or the entity might be a
  1949. // disabled group or an orphaned group content.
  1950. return $result;
  1951. }
  1952. /**
  1953. * Get the groups an entity is associated with.
  1954. *
  1955. * @param $entity_type
  1956. * The entity type. Defaults to 'user'
  1957. * @param $entity
  1958. * (optional) The entity object or entity ID. If empty, and $entity_type is
  1959. * "user", the current user will be used.
  1960. * @param $states
  1961. * (optional) Array with the state to return. Defaults to active.
  1962. *
  1963. * @return
  1964. * An array with the group's entity type as the key, and array - keyed by
  1965. * the OG membership ID and the group ID as the value. If nothing found,
  1966. * then an empty array.
  1967. */
  1968. function og_get_entity_groups($entity_type = 'user', $entity = NULL, $states = array(OG_STATE_ACTIVE)) {
  1969. $cache = &drupal_static(__FUNCTION__, array());
  1970. if ($entity_type == 'user' && empty($entity)) {
  1971. global $user;
  1972. $entity = clone $user;
  1973. }
  1974. if (is_object($entity)) {
  1975. // Get the entity ID.
  1976. list($id) = entity_extract_ids($entity_type, $entity);
  1977. }
  1978. else {
  1979. $id = $entity;
  1980. }
  1981. // Get a string identifier of the states, so we can retrieve it from cache.
  1982. if ($states) {
  1983. sort($states);
  1984. $state_identifier = implode(':', $states);
  1985. }
  1986. else {
  1987. $state_identifier = 0;
  1988. }
  1989. if (isset($cache[$entity_type][$id][$state_identifier])) {
  1990. // Return cached values.
  1991. return $cache[$entity_type][$id][$state_identifier];
  1992. }
  1993. $cache[$entity_type][$id][$state_identifier] = array();
  1994. $query = new EntityFieldQuery();
  1995. $query
  1996. ->entityCondition('entity_type', 'og_membership')
  1997. ->propertyCondition('entity_type', $entity_type, '=')
  1998. ->propertyCondition('etid', $id, '=');
  1999. if ($states) {
  2000. $query->propertyCondition('state', $states, 'IN');
  2001. }
  2002. $result = $query->execute();
  2003. if (!empty($result['og_membership'])) {
  2004. // Get the group ID from the group membership.
  2005. $og_memberships = og_membership_load_multiple(array_keys($result['og_membership']));
  2006. foreach ($og_memberships as $og_membership) {
  2007. $cache[$entity_type][$id][$state_identifier][$og_membership->group_type][$og_membership->id] = $og_membership->gid;
  2008. }
  2009. }
  2010. return $cache[$entity_type][$id][$state_identifier];
  2011. }
  2012. /**
  2013. * Return TRUE if field is a group audience type.
  2014. *
  2015. * @param $field_name
  2016. * The field name.
  2017. */
  2018. function og_is_group_audience_field($field_name) {
  2019. $field = field_info_field($field_name);
  2020. return $field['type'] == 'entityreference' && ($field['settings']['handler'] == 'og' || strpos($field['settings']['handler'], 'og_') === 0);
  2021. }
  2022. /**
  2023. * Get the name of the group-audience type field.
  2024. *
  2025. * @param $entity_type
  2026. * The entity type.
  2027. * @param $bundle_name
  2028. * The bundle name to be checked.
  2029. * @param $group_type
  2030. * Filter list to only include fields referencing a specific group type.
  2031. * @param $group_bundle
  2032. * Filter list to only include fields referencing a specific group bundle.
  2033. * Fields that do not specify any bundle restrictions at all are also
  2034. * included.
  2035. *
  2036. * @return
  2037. * Array keyed with the field name and the field label as the value.
  2038. */
  2039. function og_get_group_audience_fields($entity_type = 'user', $bundle_name = 'user', $group_type = NULL, $group_bundle = NULL) {
  2040. $return = &drupal_static(__FUNCTION__, array());
  2041. $identifier = $entity_type . ':' . $bundle_name . ':' . $group_type;
  2042. if (isset($return[$identifier])) {
  2043. return $return[$identifier];
  2044. }
  2045. $return[$identifier] = array();
  2046. foreach (field_info_instances($entity_type, $bundle_name) as $field_name => $instance) {
  2047. if (!og_is_group_audience_field($field_name)) {
  2048. continue;
  2049. }
  2050. $field_info = field_info_field($instance['field_name']);
  2051. if (isset($group_type) && $field_info['settings']['target_type'] != $group_type) {
  2052. continue;
  2053. }
  2054. if ($group_bundle && !empty($field_info['settings']['handler_settings']['target_bundles']) && !in_array($group_bundle, $field_info['settings']['handler_settings']['target_bundles'])) {
  2055. continue;
  2056. }
  2057. $return[$identifier][$field_name] = $instance['label'];
  2058. }
  2059. return $return[$identifier];
  2060. }
  2061. /**
  2062. * Return the group type (i.e. "group" or "group_content") of an entity.
  2063. *
  2064. * @param $entity_type
  2065. * The entity type.
  2066. * @param $bundle_name
  2067. * The bundle name to be checked.
  2068. * @param $type
  2069. * The group usage type. Must be "group" or "group content".
  2070. *
  2071. * @return
  2072. * The group type or an "omitted" if node type doesn't participate in
  2073. * Group.
  2074. */
  2075. function og_get_group_type($entity_type, $bundle_name, $type = 'group') {
  2076. if ($type == 'group') {
  2077. return (bool)field_info_instance($entity_type, OG_GROUP_FIELD, $bundle_name);
  2078. }
  2079. elseif ($type == 'group content') {
  2080. return (bool)og_get_group_audience_fields($entity_type, $bundle_name);
  2081. }
  2082. }
  2083. /**
  2084. * Return TRUE if the entity type is a "group" type.
  2085. *
  2086. * This is a wrapper function around og_get_group_type().
  2087. *
  2088. * @param $node_type
  2089. * The node type to be checked.
  2090. */
  2091. function og_is_group_type($entity_type, $bundle_name) {
  2092. return og_get_group_type($entity_type, $bundle_name);
  2093. }
  2094. /**
  2095. * Return TRUE if the entity type is a "group content" type.
  2096. *
  2097. * This is a wrapper function around og_get_group_type().
  2098. *
  2099. * @param $entity_type
  2100. * The entity type to be checked.
  2101. */
  2102. function og_is_group_content_type($entity_type, $bundle_name) {
  2103. return og_get_group_type($entity_type, $bundle_name, 'group content');
  2104. }
  2105. /**
  2106. * Return all the entities that are a group.
  2107. *
  2108. * @return
  2109. * Array keyed with the entity type machine name and the entity human readable
  2110. * name as the value, or an empty array if no entities are defined as group.
  2111. */
  2112. function og_get_all_group_entity() {
  2113. $return = array();
  2114. foreach (entity_get_info() as $entity_type => $entity_value) {
  2115. foreach ($entity_value['bundles'] as $bundle => $bundle_value) {
  2116. if (og_is_group_type($entity_type, $bundle)) {
  2117. $return[$entity_type] = check_plain($entity_value['label']);
  2118. // At least one bundle of the entity can be a group, so break.
  2119. break;
  2120. }
  2121. }
  2122. }
  2123. return $return;
  2124. }
  2125. /**
  2126. * Return all bundles that are a group type.
  2127. *
  2128. * @return
  2129. * An associative array whose keys are entity types, and whose values are
  2130. * arrays of bundles for that entity type. The array of bundles is keyed by
  2131. * bundle machine name, and the values are bundle labels.
  2132. */
  2133. function og_get_all_group_bundle() {
  2134. $return = array();
  2135. foreach (entity_get_info() as $entity_type => $entity_value) {
  2136. foreach ($entity_value['bundles'] as $bundle => $bundle_value) {
  2137. if (og_is_group_type($entity_type, $bundle)) {
  2138. $return[$entity_type][$bundle] = check_plain($bundle_value['label']);
  2139. }
  2140. }
  2141. }
  2142. return $return;
  2143. }
  2144. /**
  2145. * Return all the entities that are a group content.
  2146. *
  2147. * @return
  2148. * Array keyed with the entity type machine name and the entity human readable
  2149. * name as the value, or an empty array if no entities are defined as group
  2150. * content.
  2151. */
  2152. function og_get_all_group_content_entity() {
  2153. $return = array();
  2154. foreach (entity_get_info() as $entity_type => $entity_value) {
  2155. foreach ($entity_value['bundles'] as $bundle => $bundle_value) {
  2156. if (og_is_group_content_type($entity_type, $bundle)) {
  2157. $return[$entity_type] = check_plain($entity_value['label']);
  2158. // At least one bundle of the entity can be a group, so break.
  2159. break;
  2160. }
  2161. }
  2162. }
  2163. return $return;
  2164. }
  2165. /**
  2166. * Return all the entities that are a group content.
  2167. *
  2168. * @return
  2169. * Array keyed with the entity type machine name and the entity human readable
  2170. * name as the value, or an empty array if no entities are defined as group
  2171. * content.
  2172. */
  2173. function og_get_all_group_content_bundle() {
  2174. $return = array();
  2175. foreach (entity_get_info() as $entity_type => $entity_value) {
  2176. foreach ($entity_value['bundles'] as $bundle => $bundle_value) {
  2177. if (og_is_group_content_type($entity_type, $bundle)) {
  2178. $return[$entity_type][$bundle] = check_plain($bundle_value['label']);
  2179. }
  2180. }
  2181. }
  2182. return $return;
  2183. }
  2184. /**
  2185. * Return TRUE if entity belongs to a group.
  2186. *
  2187. * @param $gid
  2188. * The group ID.
  2189. * @param $entity_type
  2190. * The entity type.
  2191. * @param $entity
  2192. * The entity object. If empty the current user will be used.
  2193. * @param $states
  2194. * (optional) Array with the state to return. If empty groups of all state will
  2195. * return.
  2196. *
  2197. * @return
  2198. * TRUE if the entity (e.g. the user) belongs to a group and is not pending or
  2199. * blocked.
  2200. */
  2201. function og_is_member($group_type, $gid, $entity_type = 'user', $entity = NULL, $states = array(OG_STATE_ACTIVE)) {
  2202. $groups = og_get_entity_groups($entity_type, $entity, $states);
  2203. return !empty($groups[$group_type]) && in_array($gid, $groups[$group_type]);
  2204. }
  2205. /**
  2206. * Check if group should use default roles and permissions.
  2207. *
  2208. * @param $group_type
  2209. * The entity type of the group.
  2210. * @param $gid
  2211. * The group ID or the group entity.
  2212. *
  2213. * @return
  2214. * TRUE if group should use default roles and permissions.
  2215. */
  2216. function og_is_group_default_access($group_type, $gid) {
  2217. $wrapper = entity_metadata_wrapper($group_type, $gid);
  2218. $bundle = $wrapper->getBundle();
  2219. if (!field_info_instance($group_type, OG_DEFAULT_ACCESS_FIELD, $bundle)) {
  2220. return variable_get("og_is_group_default_access__{$group_type}__{$bundle}", TRUE);
  2221. }
  2222. if (empty($wrapper->{OG_DEFAULT_ACCESS_FIELD})) {
  2223. return TRUE;
  2224. }
  2225. return !$wrapper->{OG_DEFAULT_ACCESS_FIELD}->value();
  2226. }
  2227. /**
  2228. * Determine the permissions for one or more roles.
  2229. *
  2230. * @param $roles
  2231. * An array whose keys are the role IDs of interest.
  2232. *
  2233. * @return
  2234. * An array indexed by role ID. Each value is an array whose keys are the
  2235. * permission strings for the given role ID.
  2236. */
  2237. function og_role_permissions($roles = array()) {
  2238. $cache = &drupal_static(__FUNCTION__, array());
  2239. $role_permissions = $fetch = array();
  2240. if ($roles) {
  2241. foreach ($roles as $rid => $name) {
  2242. if (isset($cache[$rid])) {
  2243. $role_permissions[$rid] = $cache[$rid];
  2244. }
  2245. else {
  2246. // Add this rid to the list of those needing to be fetched.
  2247. $fetch[] = $rid;
  2248. // Prepare in case no permissions are returned.
  2249. $cache[$rid] = array();
  2250. }
  2251. }
  2252. if ($fetch) {
  2253. // Get from the database permissions that were not in the static variable.
  2254. // Only role IDs with at least one permission assigned will return rows.
  2255. $result = db_query("SELECT rid, permission FROM {og_role_permission} WHERE rid IN (:fetch)", array(':fetch' => $fetch));
  2256. foreach ($result as $row) {
  2257. $cache[$row->rid][$row->permission] = TRUE;
  2258. }
  2259. foreach ($fetch as $rid) {
  2260. // For every rid, we know we at least assigned an empty array.
  2261. $role_permissions[$rid] = $cache[$rid];
  2262. }
  2263. }
  2264. }
  2265. return $role_permissions;
  2266. }
  2267. /**
  2268. * Retrieve an array of roles matching specified conditions.
  2269. *
  2270. * @param $group_type
  2271. * The group type.
  2272. * @param $bundle
  2273. * The bundle type.
  2274. * @param $gid
  2275. * The group ID.
  2276. * @param $force_group
  2277. * (optional) If TRUE then the roles of the group will be retrieved by the
  2278. * group ID, even if the group is set to have default roles and permissions.
  2279. * The group might be set to "Default access" but infact there are inactive
  2280. * group roles. Thus, we are forcing the function to return the overriden
  2281. * roles. see og_delete_user_roles_by_group().
  2282. * @param $include_all
  2283. * (optional) If TRUE then the anonymous and authenticated default roles will
  2284. * be included.
  2285. *
  2286. * @return
  2287. * An associative array with the role id as the key and the role name as
  2288. * value. The anonymous and authenticated default roles are on the top of the
  2289. * array.
  2290. */
  2291. function og_roles($group_type, $bundle, $gid = 0, $force_group = FALSE, $include_all = TRUE) {
  2292. if ($gid && !$bundle) {
  2293. $wrapper = entity_metadata_wrapper($group_type, $gid);
  2294. $bundle = $wrapper->getBundle();
  2295. }
  2296. // Check if overriden access exists.
  2297. if ($gid && !$force_group) {
  2298. $query_gid = og_is_group_default_access($group_type, $gid) ? 0 : $gid;
  2299. }
  2300. else {
  2301. $query_gid = $gid;
  2302. }
  2303. $query = db_select('og_role', 'ogr')
  2304. ->fields('ogr', array('rid', 'name'))
  2305. ->condition('group_type', $group_type, '=')
  2306. ->condition('group_bundle', $bundle, '=')
  2307. ->condition('gid', $query_gid, '=')
  2308. ->orderBy('rid', 'ASC');
  2309. if (!$include_all) {
  2310. $query->condition('name', array(OG_ANONYMOUS_ROLE, OG_AUTHENTICATED_ROLE), 'NOT IN');
  2311. }
  2312. $rids = $query
  2313. ->execute()
  2314. ->fetchAllkeyed();
  2315. return $rids;
  2316. }
  2317. /**
  2318. * Get array of default roles, keyed by their declaring module.
  2319. *
  2320. * @param $include
  2321. * (optional) If TRUE also anonymous and authenticated roles will be returned.
  2322. * Defaults to TRUE.
  2323. *
  2324. * @return
  2325. * Array of default roles, grouped by module name.
  2326. */
  2327. function og_get_default_roles($include = TRUE) {
  2328. $roles = array();
  2329. foreach (module_implements('og_default_roles') as $module) {
  2330. $roles = array_merge($roles, module_invoke($module, 'og_default_roles'));
  2331. }
  2332. // Allow other modules to alter the defult roles, excpet of the anonymous and
  2333. // authenticated.
  2334. drupal_alter('og_default_roles', $roles);
  2335. if ($include) {
  2336. array_unshift($roles, OG_AUTHENTICATED_ROLE);
  2337. array_unshift($roles, OG_ANONYMOUS_ROLE);
  2338. }
  2339. return $roles;
  2340. }
  2341. /**
  2342. * Get all roles of a user in a certain group.
  2343. *
  2344. * @param $group_type
  2345. * The entity type of the group.
  2346. * @param $gid
  2347. * The group ID.
  2348. * @param $uid
  2349. * (optional) Integer specifying the user ID. By default an ID of current
  2350. * logged in user will be used.
  2351. * @param $include
  2352. * (optional) If TRUE also anonymous or authenticated role ID will be
  2353. * returned. Defaults to TRUE.
  2354. *
  2355. * @return
  2356. * Array with the role IDs of the user as the key, and the role name as
  2357. * the value.
  2358. */
  2359. function og_get_user_roles($group_type, $gid, $uid = NULL, $include = TRUE) {
  2360. $roles = &drupal_static(__FUNCTION__, array());
  2361. if (empty($uid)) {
  2362. global $user;
  2363. $uid = $user->uid;
  2364. }
  2365. $identifier = implode(':', array($group_type, $gid, $uid, $include));
  2366. if (isset($roles[$identifier])) {
  2367. return $roles[$identifier];
  2368. }
  2369. $group = entity_load_single($group_type, $gid);
  2370. // Get the bundle of the group.
  2371. list($id, $vid, $bundle) = entity_extract_ids($group_type, $group);
  2372. // Check if roles are overriden for the group.
  2373. $query_gid = og_is_group_default_access($group_type, $gid) ? 0 : $gid;
  2374. $query = db_select('og_users_roles', 'ogur');
  2375. $query->innerJoin('og_role', 'ogr', 'ogur.rid = ogr.rid');
  2376. $rids = $query
  2377. ->fields('ogur', array('rid'))
  2378. ->fields('ogr', array('name'))
  2379. ->condition('ogr.group_type', $group_type, '=')
  2380. ->condition('ogr.group_bundle', $bundle, '=')
  2381. ->condition('ogr.gid', $query_gid, '=')
  2382. ->condition('ogur.uid', $uid, '=')
  2383. ->condition('ogur.gid', $gid, '=')
  2384. ->orderBy('rid')
  2385. ->execute()
  2386. ->fetchAllkeyed();
  2387. if ($include) {
  2388. $account = user_load($uid);
  2389. $role_name = og_is_member($group_type, $gid, 'user', $account) ? OG_AUTHENTICATED_ROLE : OG_ANONYMOUS_ROLE;
  2390. $rids = db_select('og_role', 'ogr')
  2391. ->fields('ogr', array('rid', 'name'))
  2392. ->condition('group_type', $group_type, '=')
  2393. ->condition('group_bundle', $bundle, '=')
  2394. ->condition('gid', $query_gid, '=')
  2395. ->condition('name', $role_name, '=')
  2396. ->execute()
  2397. ->fetchAllkeyed() + $rids;
  2398. }
  2399. $roles[$identifier] = $rids;
  2400. return $rids;
  2401. }
  2402. /**
  2403. * Create a stub OG role object.
  2404. *
  2405. * @param $name
  2406. * A name of the role.
  2407. * @param $group_type
  2408. * (optional) The entity type of the group.
  2409. * @param $gid
  2410. * (optional) The group ID.
  2411. * @param $group_bundle
  2412. * (optional) The bundle of the group.
  2413. *
  2414. * @return
  2415. * A stub OG role object.
  2416. */
  2417. function og_role_create($name, $group_type = '', $gid = 0, $group_bundle = '') {
  2418. $role = new stdClass;
  2419. $role->name = $name;
  2420. $role->gid = $gid;
  2421. $role->group_type = $group_type;
  2422. $role->group_bundle = $group_bundle;
  2423. return $role;
  2424. }
  2425. /**
  2426. * Fetch a user role from database.
  2427. *
  2428. * @param $rid
  2429. * An integer with the role ID.
  2430. *
  2431. * @return
  2432. * A fully-loaded role object if a role with the given ID exists,
  2433. * FALSE otherwise.
  2434. */
  2435. function og_role_load($rid) {
  2436. return db_select('og_role', 'r')
  2437. ->fields('r')
  2438. ->condition('rid', $rid)
  2439. ->execute()
  2440. ->fetchObject();
  2441. }
  2442. /**
  2443. * Save a user role to the database.
  2444. *
  2445. * @param $role
  2446. * A role object to modify or add. If $role->rid is not specified, a new
  2447. * role will be created.
  2448. *
  2449. * @return
  2450. * Status constant indicating if role was created or updated.
  2451. * Failure to write the user role record will return FALSE. Otherwise.
  2452. * SAVED_NEW or SAVED_UPDATED is returned depending on the operation
  2453. * performed.
  2454. */
  2455. function og_role_save($role) {
  2456. if ($role->name) {
  2457. // Prevent leading and trailing spaces in role names.
  2458. $role->name = trim($role->name);
  2459. }
  2460. if (!empty($role->rid) && $role->name) {
  2461. $status = drupal_write_record('og_role', $role, 'rid');
  2462. module_invoke_all('og_role_update', $role);
  2463. }
  2464. else {
  2465. $status = drupal_write_record('og_role', $role);
  2466. module_invoke_all('og_role_insert', $role);
  2467. }
  2468. og_invalidate_cache();
  2469. return $status;
  2470. }
  2471. /**
  2472. * Delete a user role from database.
  2473. *
  2474. * @param $rid
  2475. * An integer with the role ID.
  2476. */
  2477. function og_role_delete($rid) {
  2478. $role = og_role_load($rid);
  2479. db_delete('og_role')
  2480. ->condition('rid', $rid)
  2481. ->execute();
  2482. db_delete('og_role_permission')
  2483. ->condition('rid', $rid)
  2484. ->execute();
  2485. // Update the users who have this role set.
  2486. db_delete('og_users_roles')
  2487. ->condition('rid', $rid)
  2488. ->execute();
  2489. module_invoke_all('og_role_delete', $role);
  2490. og_invalidate_cache();
  2491. }
  2492. /**
  2493. * Delete all roles belonging to a group.
  2494. *
  2495. * @param $group_type
  2496. * The group type.
  2497. * @param $gid
  2498. * The group ID.
  2499. */
  2500. function og_delete_user_roles_by_group($group_type, $group) {
  2501. // Check if group has overriden roles defined.
  2502. list($gid, $vid,$bundle) = entity_extract_ids($group_type, $group);
  2503. if ($roles = og_roles($group_type, $bundle, $gid, TRUE)) {
  2504. foreach ($roles as $rid => $name) {
  2505. og_role_delete($rid);
  2506. }
  2507. }
  2508. }
  2509. /**
  2510. * Get the role names of role IDs.
  2511. *
  2512. * @param $rids
  2513. * Array with role IDs.
  2514. * @return
  2515. * Array keyed by the role ID, and the role name as the value.
  2516. */
  2517. function og_get_user_roles_name($rids = array()) {
  2518. if ($rids) {
  2519. $query = db_query("SELECT rid, name FROM {og_role} WHERE rid IN (:rids)", array(':rids' => $rids));
  2520. }
  2521. else {
  2522. $query = db_query("SELECT rid, name FROM {og_role}");
  2523. }
  2524. return $query->fetchAllKeyed();
  2525. }
  2526. /**
  2527. * Delete all permissions defined by a module.
  2528. *
  2529. * @see og_modules_uninstalled()
  2530. *
  2531. * @param $modules
  2532. * Array with the module names.
  2533. */
  2534. function og_permissions_delete_by_module($modules = array()) {
  2535. db_delete('og_role_permission')
  2536. ->condition('module', $modules, 'IN')
  2537. ->execute();
  2538. }
  2539. /**
  2540. * Create new roles, based on the default roles and permissions.
  2541. *
  2542. * @param $group_type
  2543. * The group type.
  2544. * @param $bundle
  2545. * The bundle type.
  2546. * @param $gid
  2547. * The group ID.
  2548. *
  2549. * @return
  2550. * The newly created roles keyed by role ID and role name as the value. Or
  2551. * FALSE if no roles were created.
  2552. */
  2553. function og_roles_override($group_type, $bundle, $gid) {
  2554. // Check if roles aren't already overridden. We can't use
  2555. // og_is_group_default_access() as the field is already set, so we
  2556. // check to see if there are new roles in the database by setting
  2557. // "force group" parameter to TRUE.
  2558. if (og_roles($group_type, $bundle, $gid, TRUE)) {
  2559. return;
  2560. }
  2561. $rids = array();
  2562. if ($gid) {
  2563. // Copy roles from a specific group
  2564. $og_roles = og_roles($group_type, $bundle);
  2565. $perms = og_role_permissions($og_roles);
  2566. }
  2567. else {
  2568. // Copy the global default roles
  2569. $og_roles = og_get_default_roles();
  2570. $perms = og_get_default_permissions();
  2571. }
  2572. foreach ($og_roles as $rid => $name) {
  2573. $role = og_role_create($name, $group_type, $gid, $bundle);
  2574. og_role_save($role);
  2575. $rids[$role->rid] = $role->name;
  2576. og_role_change_permissions($role->rid, $perms[$rid]);
  2577. // Remap the default roles, to the newely created ones.
  2578. db_update('og_users_roles')
  2579. ->fields(array('rid' => $role->rid))
  2580. ->condition('rid', $rid)
  2581. ->condition('group_type', $group_type)
  2582. ->condition('gid', $gid)
  2583. ->execute();
  2584. }
  2585. return $rids;
  2586. }
  2587. /**
  2588. * Grant a group role to a user.
  2589. *
  2590. * @param $group_type
  2591. * The entity type of the group.
  2592. * @param $gid
  2593. * The group ID.
  2594. * @param $uid
  2595. * The user ID.
  2596. * @param $rid
  2597. * The role ID.
  2598. */
  2599. function og_role_grant($group_type, $gid, $uid, $rid) {
  2600. // Make sure the role is valid.
  2601. $group = entity_load_single($group_type, $gid);
  2602. list(,, $bundle) = entity_extract_ids($group_type, $group);
  2603. $og_roles = og_roles($group_type, $bundle, $gid, FALSE, FALSE);
  2604. if (empty($og_roles[$rid])) {
  2605. // Role isn't valid.
  2606. return;
  2607. }
  2608. // Get the existiong user roles.
  2609. $user_roles = og_get_user_roles($group_type, $gid, $uid);
  2610. if (empty($user_roles[$rid])) {
  2611. $role = new stdClass();
  2612. $role->uid = $uid;
  2613. $role->rid = $rid;
  2614. $role->group_type = $group_type;
  2615. $role->gid = $gid;
  2616. drupal_write_record('og_users_roles', $role);
  2617. og_invalidate_cache();
  2618. module_invoke_all('og_role_grant', $group_type, $gid, $uid, $rid);
  2619. }
  2620. }
  2621. /**
  2622. * Revoke a group role from a user.
  2623. *
  2624. * @param $group_type
  2625. * The entity type of the group.
  2626. * @param $gid
  2627. * The group ID.
  2628. * @param $uid
  2629. * The user ID.
  2630. * @param $rid
  2631. * The role ID.
  2632. */
  2633. function og_role_revoke($group_type, $gid, $uid, $rid) {
  2634. $og_roles = og_get_user_roles($group_type, $gid, $uid);
  2635. if (!empty($og_roles[$rid])) {
  2636. db_delete('og_users_roles')
  2637. ->condition('uid', $uid)
  2638. ->condition('rid', $rid)
  2639. ->condition('group_type', $group_type)
  2640. ->condition('gid', $gid)
  2641. ->execute();
  2642. module_invoke_all('og_role_revoke', $group_type, $gid, $uid, $rid);
  2643. }
  2644. }
  2645. /**
  2646. * Change permissions for a user role.
  2647. *
  2648. * This function may be used to grant and revoke multiple permissions at once.
  2649. * For example, when a form exposes checkboxes to configure permissions for a
  2650. * role, the submitted values may be directly passed on in a form submit
  2651. * handler.
  2652. *
  2653. * @param $rid
  2654. * The ID of a group user role to alter.
  2655. * @param $permissions
  2656. * An array of permissions, where the key holds the permission name and the
  2657. * value is an integer or boolean that determines whether to grant or revoke
  2658. * the permission:
  2659. * @code
  2660. * array(
  2661. * 'edit group' => 0,
  2662. * 'administer group' => 1,
  2663. * )
  2664. * @endcode
  2665. * Existing permissions are not changed, unless specified in $permissions.
  2666. *
  2667. * @see og_role_grant_permissions()
  2668. * @see og_role_revoke_permissions()
  2669. */
  2670. function og_role_change_permissions($rid, array $permissions = array()) {
  2671. // Grant new permissions for the role.
  2672. $grant = array_filter($permissions);
  2673. if (!empty($grant)) {
  2674. og_role_grant_permissions($rid, array_keys($grant));
  2675. }
  2676. // Revoke permissions for the role.
  2677. $revoke = array_diff_assoc($permissions, $grant);
  2678. if (!empty($revoke)) {
  2679. og_role_revoke_permissions($rid, array_keys($revoke));
  2680. }
  2681. if (!empty($grant) || !empty($revoke)) {
  2682. // Allow modules to be notified on permission changes.
  2683. $role = og_role_load($rid);
  2684. module_invoke_all('og_role_change_permissions', $role, $grant, $revoke);
  2685. }
  2686. }
  2687. /**
  2688. * Grant permissions to a user role.
  2689. *
  2690. * @param $rid
  2691. * The ID of a user role to alter.
  2692. * @param $permissions
  2693. * A list of permission names to grant.
  2694. *
  2695. * @see user_role_change_permissions()
  2696. * @see user_role_revoke_permissions()
  2697. */
  2698. function og_role_grant_permissions($rid, array $permissions = array()) {
  2699. $modules = array();
  2700. foreach (og_get_permissions() as $name => $value) {
  2701. $modules[$name] = $value['module'];
  2702. }
  2703. // Grant new permissions for the role.
  2704. foreach ($permissions as $name) {
  2705. // Prevent WSOD, if the permission name is wrong, and we can't find its
  2706. // module.
  2707. if (!empty($modules[$name])) {
  2708. db_merge('og_role_permission')
  2709. ->key(array(
  2710. 'rid' => $rid,
  2711. 'permission' => $name,
  2712. 'module' => $modules[$name],
  2713. ))
  2714. ->execute();
  2715. }
  2716. }
  2717. og_invalidate_cache();
  2718. }
  2719. /**
  2720. * Revoke permissions from a user role.
  2721. *
  2722. * @param $rid
  2723. * The ID of a user role to alter.
  2724. * @param $permissions
  2725. * A list of permission names to revoke.
  2726. *
  2727. * @see user_role_change_permissions()
  2728. * @see user_role_grant_permissions()
  2729. */
  2730. function og_role_revoke_permissions($rid, array $permissions = array()) {
  2731. // Revoke permissions for the role.
  2732. db_delete('og_role_permission')
  2733. ->condition('rid', $rid)
  2734. ->condition('permission', $permissions, 'IN')
  2735. ->execute();
  2736. og_invalidate_cache();
  2737. }
  2738. /**
  2739. * Get all permissions defined by implementing modules.
  2740. *
  2741. * @return
  2742. * Array keyed with the permissions name and the value of the permissions.
  2743. * TODO: Write the values.
  2744. */
  2745. function og_get_permissions() {
  2746. $perms = &drupal_static(__FUNCTION__, array());
  2747. if (!empty($perms)) {
  2748. return $perms;
  2749. }
  2750. foreach (module_implements('og_permission') as $module) {
  2751. if ($permissions = module_invoke($module, 'og_permission')) {
  2752. foreach ($permissions as $key => $perm) {
  2753. $permissions[$key] += array(
  2754. // Initialize the roles key, if other modules haven't set it
  2755. // explicetly. This means the permissions can apply to anonymous and
  2756. // authenticated members as-well.
  2757. 'roles' => array(OG_ANONYMOUS_ROLE, OG_AUTHENTICATED_ROLE),
  2758. 'default role' => array(),
  2759. 'module' => $module,
  2760. );
  2761. }
  2762. $perms = array_merge($perms, $permissions);
  2763. }
  2764. }
  2765. // Allow other modules to alter the permissions.
  2766. drupal_alter('og_permission', $perms);
  2767. return $perms;
  2768. }
  2769. /**
  2770. * Get default permissions.
  2771. *
  2772. * @return
  2773. * Array keyed with the anonymous, authenticated and administror and the
  2774. * permissions that should be enabled by default.
  2775. */
  2776. function og_get_default_permissions() {
  2777. $roles = og_get_default_roles();
  2778. $default_perms = og_get_permissions();
  2779. $perms = array();
  2780. foreach ($roles as $rid => $role_name) {
  2781. $perms[$rid] = array();
  2782. // For each default role, iterate default permissions and mark the
  2783. // permissions that set the role as default.
  2784. foreach ($default_perms as $perm_name => $perm) {
  2785. if (in_array($role_name, $perm['default role'])) {
  2786. $perms[$rid][$perm_name] = TRUE;
  2787. }
  2788. }
  2789. }
  2790. return $perms;
  2791. }
  2792. /**
  2793. * Get all the modules fields that can be assigned to fieldable entities.
  2794. *
  2795. * @param $field_name
  2796. * The field name that was registered for the definition.
  2797. *
  2798. * @return
  2799. * An array with the field and instance definitions, or FALSE if not
  2800. * found.
  2801. */
  2802. function og_fields_info($field_name = NULL) {
  2803. $return = &drupal_static(__FUNCTION__, array());
  2804. if (empty($return)) {
  2805. foreach (module_implements('og_fields_info') as $module) {
  2806. if ($fields = module_invoke($module, 'og_fields_info')) {
  2807. foreach ($fields as $key => $field) {
  2808. // Add default values.
  2809. $field += array(
  2810. 'entity type' => array(),
  2811. 'multiple' => FALSE,
  2812. 'description' => '',
  2813. );
  2814. // Add the module information.
  2815. $return[$key] = array_merge($field, array('module' => $module));
  2816. }
  2817. }
  2818. }
  2819. // Allow other modules to alter the field info.
  2820. drupal_alter('og_fields_info', $return);
  2821. }
  2822. if (!empty($field_name)) {
  2823. return !empty($return[$field_name]) ? $return[$field_name] : FALSE;
  2824. }
  2825. return $return;
  2826. }
  2827. /**
  2828. * Set breadcrumbs according to a given group.
  2829. *
  2830. * @param $entity_type
  2831. * The entity type.
  2832. * @param $etid
  2833. * The entity ID.
  2834. * @param $path
  2835. * (optional) The path to append to the breadcrumb.
  2836. */
  2837. function og_set_breadcrumb($entity_type, $etid, $path = array()) {
  2838. $entity = entity_load_single($entity_type, $etid);
  2839. $label = entity_label($entity_type, $entity);
  2840. $uri = entity_uri($entity_type, $entity);
  2841. drupal_set_breadcrumb(array_merge(array(l(t('Home'), '<front>')), array(l($label, $uri['path'])), $path));
  2842. }
  2843. /**
  2844. * Create an organic groups field in a bundle.
  2845. *
  2846. * @param $field_name
  2847. * The field name
  2848. * @param $entity_type
  2849. * The entity type
  2850. * @param $bundle
  2851. * The bundle name.
  2852. * @param $og_field
  2853. * (optional) Array with field definitions, to allow easier overriding by the
  2854. * caller. If empty, function will get the field info by calling
  2855. * og_fields_info() with the field name.
  2856. */
  2857. function og_create_field($field_name, $entity_type, $bundle, $og_field = array()) {
  2858. if (empty($og_field)) {
  2859. $og_field = og_fields_info($field_name);
  2860. }
  2861. $field = field_info_field($field_name);
  2862. // Allow overriding the field name.
  2863. $og_field['field']['field_name'] = $field_name;
  2864. if (empty($field)) {
  2865. $field = field_create_field($og_field['field']);
  2866. }
  2867. $instance = field_info_instance($entity_type, $field_name, $bundle);
  2868. if (empty($instance)) {
  2869. $instance = $og_field['instance'];
  2870. $instance += array(
  2871. 'field_name' => $field_name,
  2872. 'bundle' => $bundle,
  2873. 'entity_type' => $entity_type,
  2874. );
  2875. field_create_instance($instance);
  2876. // Clear the entity property info cache, as OG fields might add different
  2877. // entity property info.
  2878. og_invalidate_cache();
  2879. entity_property_info_cache_clear();
  2880. }
  2881. }
  2882. /**
  2883. * Return the states a group can be in.
  2884. */
  2885. function og_group_states() {
  2886. return array(
  2887. OG_STATE_ACTIVE => t('Active'),
  2888. OG_STATE_PENDING => t('Pending'),
  2889. );
  2890. }
  2891. /**
  2892. * Return the states a group content can be in.
  2893. */
  2894. function og_group_content_states() {
  2895. return array(
  2896. OG_STATE_ACTIVE => t('Active'),
  2897. OG_STATE_PENDING => t('Pending'),
  2898. OG_STATE_BLOCKED => t('Blocked'),
  2899. );
  2900. }
  2901. /**
  2902. * Return a list of fieldable entities.
  2903. *
  2904. * @return
  2905. * Array keyed with the entity machine name and the saniztized human name as
  2906. * the value.
  2907. */
  2908. function og_get_fieldable_entity_list() {
  2909. $return = array();
  2910. foreach (entity_get_info() as $name => $info) {
  2911. if (!empty($info['fieldable'])) {
  2912. $return[$name] = check_plain($info['label']);
  2913. }
  2914. }
  2915. return $return;
  2916. }
  2917. /**
  2918. * Helper function to generate standard node permission list for a given type.
  2919. *
  2920. * @param $type
  2921. * The machine-readable name of the node type.
  2922. *
  2923. * @return array
  2924. * An array of permission names and descriptions.
  2925. */
  2926. function og_list_permissions($type) {
  2927. $info = node_type_get_type($type);
  2928. $type = check_plain($info->type);
  2929. $perms = array();
  2930. // Check type is of group content.
  2931. if (og_is_group_content_type('node', $type)) {
  2932. // Build standard list of node permissions for this type.
  2933. $perms += array(
  2934. "create $type content" => array(
  2935. 'title' => t('Create %type_name content', array('%type_name' => $info->name)),
  2936. ),
  2937. "update own $type content" => array(
  2938. 'title' => t('Edit own %type_name content', array('%type_name' => $info->name)),
  2939. ),
  2940. "update any $type content" => array(
  2941. 'title' => t('Edit any %type_name content', array('%type_name' => $info->name)),
  2942. ),
  2943. "delete own $type content" => array(
  2944. 'title' => t('Delete own %type_name content', array('%type_name' => $info->name)),
  2945. ),
  2946. "delete any $type content" => array(
  2947. 'title' => t('Delete any %type_name content', array('%type_name' => $info->name)),
  2948. ),
  2949. );
  2950. if (!module_exists('entityreference_prepopulate')) {
  2951. // We allow the create permission only on members, as otherwise we would
  2952. // have to iterate over every single group to decide if the user has
  2953. // permissions for it.
  2954. $perms["create $type content"]['roles'] = array(OG_AUTHENTICATED_ROLE);
  2955. }
  2956. // Add default permissions.
  2957. foreach ($perms as $key => $value) {
  2958. $perms[$key]['default role'] = array(OG_AUTHENTICATED_ROLE);
  2959. }
  2960. }
  2961. return $perms;
  2962. }
  2963. /**
  2964. * Return a form element with crafted links to create nodes for a group.
  2965. *
  2966. * @param $group_type
  2967. * The entity type of the group.
  2968. * @param $gid
  2969. * The group ID.
  2970. * @param $field_name
  2971. * The group audience field name.
  2972. * @param $destination
  2973. * (optional) The destiantion after a node is created. Defaults to the
  2974. * destination passed in the URL if exists, otherwise back to the current
  2975. * page.
  2976. * @param $types
  2977. * (optional) An array of type names. Restrict the created links to the given
  2978. * types.
  2979. */
  2980. function og_node_create_links($group_type, $gid, $field_name, $destination = NULL, $types = NULL) {
  2981. if (!og_is_group($group_type, $gid)) {
  2982. return;
  2983. }
  2984. $types = isset($types) ? $types : array_keys(node_type_get_types());
  2985. foreach ($types as $type_name) {
  2986. if (!og_is_group_content_type('node', $type_name) || !og_user_access($group_type, $gid, "create $type_name content")) {
  2987. continue;
  2988. }
  2989. $instance = field_info_instance('node', $field_name, $type_name);
  2990. if (empty($instance['settings']['behaviors']['prepopulate']['status'])) {
  2991. // Instance doesn't allow prepopulating.
  2992. continue;
  2993. }
  2994. $names[$type_name] = node_type_get_name($type_name);
  2995. }
  2996. if (empty($names)) {
  2997. return;
  2998. }
  2999. // Sort names.
  3000. asort($names);
  3001. // Build links.
  3002. $options = array(
  3003. 'query' => array($field_name => $gid),
  3004. );
  3005. if ($destination) {
  3006. $options['query']['destination'] = $destination;
  3007. }
  3008. else {
  3009. $options['query'] += drupal_get_destination();
  3010. }
  3011. $items = array();
  3012. foreach ($names as $type => $name) {
  3013. // theme_item_list's 'data' items isn't a render element, so use l().
  3014. // http://drupal.org/node/891112
  3015. $items[] = array('data' => l($name, 'node/add/' . str_replace('_', '-', $type), $options));
  3016. }
  3017. $element = array();
  3018. $element['og_node_create_links'] = array(
  3019. '#theme' => 'item_list',
  3020. '#items' => $items,
  3021. );
  3022. return $element;
  3023. }
  3024. /**
  3025. * Get the group IDs of all the groups a user is an approved member of.
  3026. *
  3027. * @param $account
  3028. * (optional) The user object to fetch group memberships for. Defaults to the
  3029. * acting user.
  3030. * @param $group_type
  3031. * (optional) The entity type of the groups to fetch. By default all group
  3032. * types will be fetched.
  3033. *
  3034. * @return
  3035. * An array with the group IDs or an empty array.
  3036. */
  3037. function og_get_groups_by_user($account = NULL, $group_type = NULL) {
  3038. if (empty($account)) {
  3039. global $user;
  3040. $account = $user;
  3041. }
  3042. if (!og_get_group_audience_fields()) {
  3043. // User entity doesn't have group audience fields.
  3044. return;
  3045. }
  3046. $gids = array();
  3047. // Get all active OG membership that belong to the user.
  3048. $wrapper = entity_metadata_wrapper('user', $account->uid);
  3049. $og_memberships = $wrapper->{'og_membership__' . OG_STATE_ACTIVE}->value();
  3050. if (!$og_memberships) {
  3051. return;
  3052. }
  3053. foreach ($og_memberships as $og_membership) {
  3054. $gids[$og_membership->group_type][$og_membership->gid] = $og_membership->gid;
  3055. }
  3056. if (empty($group_type)) {
  3057. return $gids;
  3058. }
  3059. elseif (!empty($gids[$group_type])) {
  3060. return $gids[$group_type];
  3061. }
  3062. }
  3063. /**
  3064. * Implements hook_action_info().
  3065. *
  3066. * @see views_bulk_operations_action_info()
  3067. */
  3068. function og_action_info() {
  3069. $actions = array();
  3070. $files = og_operations_load_action_includes();
  3071. foreach ($files as $filename) {
  3072. $action_info_fn = 'og_'. str_replace('.', '_', basename($filename, '.inc')).'_info';
  3073. $action_info = call_user_func($action_info_fn);
  3074. if (is_array($action_info)) {
  3075. $actions += $action_info;
  3076. }
  3077. }
  3078. return $actions;
  3079. }
  3080. /**
  3081. * Loads the VBO actions placed in their own include files.
  3082. *
  3083. * @return
  3084. * An array of containing filenames of the included actions.
  3085. *
  3086. * @see views_bulk_operations_load_action_includes()
  3087. */
  3088. function og_operations_load_action_includes() {
  3089. static $loaded = FALSE;
  3090. $path = drupal_get_path('module', 'og') . '/includes/actions/';
  3091. $files = array(
  3092. 'user_roles.action.inc',
  3093. 'set_state.action.inc',
  3094. 'membership_delete.action.inc',
  3095. );
  3096. if (!$loaded) {
  3097. foreach ($files as $file) {
  3098. include_once $path . $file;
  3099. }
  3100. $loaded = TRUE;
  3101. }
  3102. return $files;
  3103. }
  3104. /**
  3105. * Implements hook_features_api().
  3106. */
  3107. function og_features_api() {
  3108. return array(
  3109. 'og_features_role' => array(
  3110. 'name' => t('OG Role'),
  3111. 'feature source' => TRUE,
  3112. 'default_hook' => 'og_features_default_roles',
  3113. 'default_file' => FEATURES_DEFAULTS_INCLUDED,
  3114. 'file' => drupal_get_path('module', 'og') . '/includes/og_features_role.features.inc',
  3115. ),
  3116. 'og_features_permission' => array(
  3117. 'name' => t('OG Permissions'),
  3118. 'feature_source' => TRUE,
  3119. 'default_hook' => 'og_features_default_permissions',
  3120. 'default_file' => FEATURES_DEFAULTS_INCLUDED,
  3121. 'file' => drupal_get_path('module', 'og') . '/includes/og_features_permission.features.inc',
  3122. ),
  3123. );
  3124. }
  3125. /**
  3126. * Implements hook_features_pipe_alter().
  3127. *
  3128. * Prevent OG related fields from being piped in features, when a content
  3129. * type that has them is selected.
  3130. */
  3131. function og_features_pipe_alter(&$pipe, $data, $export) {
  3132. if (!variable_get('og_features_ignore_og_fields', FALSE) || empty($pipe['field'])) {
  3133. return;
  3134. }
  3135. foreach ($pipe['field'] as $delta => $value) {
  3136. $args = explode('-', $value);
  3137. $field_name = $args[2];
  3138. if (og_fields_info($field_name) || og_is_group_audience_field($field_name)) {
  3139. unset($pipe['field'][$delta]);
  3140. }
  3141. }
  3142. }
  3143. /**
  3144. * Implements hook_migrate_api().
  3145. */
  3146. function og_migrate_api() {
  3147. $migrations = array();
  3148. if (db_table_exists('d6_og')) {
  3149. $migrations['OgMigrateAddFields'] = array('class_name' => 'OgMigrateAddFields');
  3150. $migrations['OgMigrateContent'] = array('class_name' => 'OgMigrateContent');
  3151. $migrations['OgMigrateUser'] = array('class_name' => 'OgMigrateUser');
  3152. foreach (node_type_get_names() as $bundle => $value) {
  3153. $machine_name = 'OgMigrateGroup' . ucfirst($bundle);
  3154. $migrations[$machine_name] = array(
  3155. 'class_name' => 'OgMigrateGroup',
  3156. 'bundle' => $bundle,
  3157. );
  3158. }
  3159. }
  3160. elseif (db_field_exists('og_membership', 'group_type') && db_table_exists('og') && !db_table_exists('d6_og')) {
  3161. $migrations['OgMigrateMembership'] = array('class_name' => 'OgMigrateMembership');
  3162. $migrations['OgMigrateRoles'] = array('class_name' => 'OgMigrateRoles');
  3163. $migrations['OgMigrateUserRoles'] = array('class_name' => 'OgMigrateUserRoles');
  3164. }
  3165. $api = array(
  3166. 'api' => 2,
  3167. 'migrations' => $migrations,
  3168. );
  3169. return $api;
  3170. }
  3171. /**
  3172. * Implements hook_flush_caches().
  3173. */
  3174. function og_flush_caches() {
  3175. $bins = array(
  3176. 'cache_entity_og_membership',
  3177. 'cache_entity_og_membership_type',
  3178. );
  3179. return $bins;
  3180. }