oa_core.util.inc in Open Atrium Core 7.2
Code for Utility functions for OpenAtrium spaces
Also contains various node hooks (update,delete,insert) for cache handling
File
includes/oa_core.util.incView source
<?php
/**
* @file
* Code for Utility functions for OpenAtrium spaces
*
* Also contains various node hooks (update,delete,insert) for cache handling
*/
/**
* Name of various caches.
*/
define('OA_CACHE_GROUPS_BY_USER', 'oa_groups_by_user');
define('OA_CACHE_GROUPS_BY_USER_ACCESS', 'oa_groups_by_user_access');
define('OA_CACHE_GET_PARENTS', 'oa_get_parents');
define('OA_CACHE_GROUPS_BY_PARENT', 'oa_groups_by_parent');
define('OA_CACHE_TOP_PARENTS', 'oa_top_parents');
/**
* Get the group IDs of all the groups a user is an approved member of.
*
* REPLACES og_get_groups_by_user() in OG and returns a list of groups
* including subspaces including via INHERITED membership
*
* @param $account
* (optional) The user object to fetch group memberships for. Defaults to the
* acting user.
* @param $group_type
* (optional) The entity type of the groups to fetch. By default all group
* types will be fetched. (e.g. 'node', 'user')
* @param bool $include_archived
* (optional) Whether to include archived nodes or not. By default, archived
* items aren't included.
*
* @return array
* An array with the group IDs or an empty array, or returns NULL if there was an error
*/
function oa_core_get_groups_by_user($account = NULL, $group_type = NULL, $include_archived = FALSE) {
if (!isset($account)) {
global $user;
$account = $user;
}
$cid = oa_core_get_cache_id(OA_CACHE_GROUPS_BY_USER, $account->uid, $include_archived, array(
$group_type,
));
if (oa_core_get_cache(OA_CACHE_GROUPS_BY_USER, $cid, $result)) {
return $result;
}
// Do NOT call og_get_groups_by_user as it loads all entities
// Here we do a faster db query
$query = db_select('node', 'n');
$query
->join('og_membership', 'ogm', 'ogm.gid = n.nid');
$query
->fields('n', array(
'nid',
))
->condition('ogm.etid', $account->uid)
->condition('ogm.entity_type', 'user')
->condition('ogm.state', OG_STATE_ACTIVE)
->condition('n.type', array(
OA_SPACE_TYPE,
OA_GROUP_TYPE,
), 'IN');
if (module_exists('flag') && !$include_archived) {
if ($flag = flag_get_flag('trash')) {
$query
->leftJoin('flagging', 'a', "a.fid = :fid AND a.entity_id = n.nid", array(
':fid' => $flag->fid,
));
// This makes sure that archived content isn't included, because 'uid'
// will be NULL if the join didn't connect anything.
$query
->isNull('a.uid');
}
}
$user_groups = drupal_map_assoc($query
->execute()
->fetchCol(0));
if (module_exists('og_subgroups') && $user_groups) {
$user_groups = oa_core_get_groups_by_parent($user_groups, array(
OA_SPACE_TYPE,
OA_GROUP_TYPE,
), NULL, $include_archived, NULL, TRUE);
}
oa_core_set_cache(OA_CACHE_GROUPS_BY_USER, $cid, $user_groups);
return $user_groups;
}
/**
* Get the group IDs and Titles of all the groups a user is an approved member of.
*
* SIMILAR to oa_core_get_groups_by_user above but respects the NODE ACCESS
* of the spaces and allows an optional space status (published)
* Only returns spaces a user is ACTIVE in (not BLOCKED or PENDING)
* Also, returns both nids and titles in result
*
* DOES NOT return INHERITED subspaces
*
* @param $account
* (optional) The user object to fetch group memberships for. Defaults to the
* acting user. Can be a full user node or just a user uid
* @param bool $include_archived
* (optional) Whether to include archived nodes or not. By default, archived
* items aren't included.
* @param int $status
* (optional) The space node status to include. If NULL, all spaces are
* included. If NODE_PUBLISHED, then only include published spaces
* @param array $page
* (optional) Adds a pager to the query for the given page number
* @param string $filter
* (optional) Filters the space titles that are returned
* @param string $type
* (optional) Type of group. Defaults to OA_SPACE_TYPE
* @param string $sort
* (optional) Field to sort on. Use 'history' to sort by most recently accessed. Start with - to reverse sort direction
* @param int $limit
* (optional) Limit number of results
*
* @return array
* An array with the group IDs or an empty array, or returns NULL if there was an error
*/
function oa_core_get_groups_by_user_access($account = NULL, $include_archived = FALSE, $status = NULL, $page = NULL, $filter = NULL, $type = NULL, $sort = NULL, $limit = NULL) {
global $user;
if (!isset($account)) {
$uid = $user->uid;
}
elseif (is_numeric($account)) {
$uid = $account;
}
else {
$uid = $account->uid;
}
$sort = !empty($sort) ? $sort : 'title';
$limit = !empty($limit) ? $limit : 0;
$type = !empty($type) ? $type : OA_SPACE_TYPE;
// Only cache invocations without paging or filtering or sorting on history.
// $user->uid is added to the cache key because the result also depends on the permissions of the viewing user.
$cid = NULL;
if (!isset($page) && empty($filter) && $sort !== 'history') {
$cid = oa_core_get_cache_id(OA_CACHE_GROUPS_BY_USER_ACCESS, $uid, $include_archived, array(
$status,
$type,
$sort,
$limit,
$user->uid,
));
if (oa_core_get_cache(OA_CACHE_GROUPS_BY_USER_ACCESS, $cid, $result)) {
return $result;
}
}
$query = db_select('node', 'n');
if (isset($page)) {
if (!isset($_GET['page'])) {
$_GET['page'] = $page;
}
$query = $query
->extend('PagerDefault')
->limit(20);
}
$query
->join('og_membership', 'ogm', 'ogm.gid = n.nid');
if ($sort == 'history') {
$query
->join('history', 'h', 'h.nid = n.nid');
$query
->condition('h.uid', $uid);
$query
->orderBy('h.timestamp', 'DESC');
}
$query
->fields('n', array(
'nid',
'title',
))
->condition('ogm.etid', $uid)
->condition('ogm.entity_type', 'user')
->condition('ogm.state', OG_STATE_ACTIVE)
->addTag('node_access');
if (!empty($sort) && $sort != 'history') {
$dir = 'ASC';
if (substr($sort, 0, 1) == '-') {
$dir = 'DESC';
$sort = substr($sort, 1, strlen($sort) - 1);
}
$query
->orderBy('n.' . $sort, $dir);
// secondary sort on title
$query
->orderBy('n.title');
}
if (isset($type)) {
$query
->condition('n.type', $type);
}
if (isset($status)) {
$query
->condition('n.status', $status);
}
if (!empty($filter)) {
$query
->condition('n.title', '%' . db_like($filter) . '%', 'LIKE');
}
if ($limit > 0) {
$query
->range(0, $limit);
}
if (module_exists('flag') && !$include_archived) {
if ($flag = flag_get_flag('trash')) {
$query
->leftJoin('flagging', 'a', "a.fid = :fid AND a.entity_id = n.nid", array(
':fid' => $flag->fid,
));
// This makes sure that archived content isn't included, because 'uid'
// will be NULL if the join didn't connect anything.
$query
->isNull('a.uid');
}
}
$result = $query
->execute()
->fetchAll();
if ($cid !== NULL) {
oa_core_set_cache(OA_CACHE_GROUPS_BY_USER_ACCESS, $cid, $result);
}
return $result;
}
/**
* Returns the current space id context
* @param boolean $from_session only use the context stored in $SESSION
* set this to TRUE if calling from a access hook to avoid infinite loops
* @return int Space ID
*/
function oa_core_get_space_context($from_session = FALSE) {
if (variable_get('install_task') !== 'done') {
// don't run this during the installer
return 0;
}
if ($from_session) {
if (defined('OG_SESSION_CONTEXT_ID') && isset($_SESSION[OG_SESSION_CONTEXT_ID])) {
return $_SESSION[OG_SESSION_CONTEXT_ID];
}
return 0;
}
$context = og_context('node');
if (!empty($context['gid'])) {
og_session_context_set_context($context['gid']);
return $context['gid'];
}
else {
return og_session_context_get_context();
}
}
/**
* Returns the current space id context
* Returns zero if on the site home page
* @param boolean $from_session only use the context stored in $SESSION
* @return int Space ID
*/
function oa_core_get_space_home_context() {
if (variable_get('install_task') !== 'done') {
// don't run this during the installer
return 0;
}
$space_id = oa_core_get_space_context();
$node = menu_get_object();
if (drupal_is_front_page() && !(isset($node) && $node->type == OA_SPACE_TYPE)) {
return 0;
// front page, not a space page
}
return $space_id;
}
/**
* Returns the current section id context
* @param $node optional current page node
* @return int Section ID
*/
function oa_core_get_section_context($node = NULL) {
if (isset($node) && $node->type == OA_SECTION_TYPE) {
return $node->nid;
}
elseif (defined('OA_SESSION_SECTION') && isset($_SESSION[OA_SESSION_SECTION])) {
return $_SESSION[OA_SESSION_SECTION];
}
else {
return oa_section_get_section_context();
}
}
/**
* Get parent Spaces and Groups.
*
* @param int $nid
* The NID of the Space or Group to check.
* @param string|NULL $bundle
* (optional) The node type (default: OA_SPACE_TYPE). If NULL is passed, it
* will include all node types.
* @param int|NULL $status
* (optional) If specified, the node status (ex. NODE_PUBLISHED or
* NODE_NOT_PUBLISHED) to look for. If not specified, it return
* nodes of either status.
* @param bool $include_archived
* (optional) Whether to include archived nodes or not. By default, archived
* items aren't included.
* @param book $fetch_all
* (optional) Set to true to return all parents, rather than just direct parents
*
* @return array
* Array of parent NIDs to titles.
*/
function oa_core_get_parents_with_titles($nid, $bundle = OA_SPACE_TYPE, $status = NULL, $include_archived = FALSE, $fetch_all = FALSE, $account = NULL) {
if (!isset($account)) {
global $user;
$uid = $user->uid;
}
elseif (is_numeric($account)) {
$uid = $account;
}
else {
$uid = $account->uid;
}
$cid = oa_core_get_cache_id(OA_CACHE_GET_PARENTS, $nid . '_' . $uid . '_' . ($fetch_all ? '1' : '0'), $include_archived, array(
$bundle,
$status,
));
if (oa_core_get_cache(OA_CACHE_GET_PARENTS, $cid, $result)) {
return $result;
}
$result = og_subgroups_parents_load('node', $nid, FALSE, $fetch_all);
$result = !empty($result['node']) ? oa_core_get_nids_title_matching($result['node'], $bundle, $status, $include_archived, $account) : array();
oa_core_set_cache(OA_CACHE_GET_PARENTS, $cid, $result);
return $result;
}
/**
* Get parent Spaces and Groups.
*
* @param int $nid
* The NID of the Space or Group to check.
* @param string|NULL $bundle
* (optional) The node type (default: OA_SPACE_TYPE). If NULL is passed, it
* will include all node types. Can also be an array of types.
* @param int|NULL $status
* (optional) If specified, the node status (ex. NODE_PUBLISHED or
* NODE_NOT_PUBLISHED) to look for. If not specified, it return
* nodes of either status.
* @param bool $include_archived
* (optional) Whether to include archived nodes or not. By default, archived
* items aren't included.
*
* @return array
* Array of parent NIDs to titles.
*/
function oa_core_get_parents($nid, $bundle = OA_SPACE_TYPE, $status = NULL, $include_archived = FALSE, $fetch_all = FALSE, $account = NULL) {
$parents = oa_core_get_parents_with_titles($nid, $bundle, $status, $include_archived, $fetch_all, $account);
return array_keys($parents);
}
/**
* Get the nids => title matching criteria from a set of nids.
*
* @param int[] $nids
* The NIDs of the nodes to check.
* @param string|NULL $bundle
* (optional) The node type (default: OA_SPACE_TYPE). If NULL is passed, it
* will include all node types. Can also be an array of types.
* @param int|NULL $status
* (optional) If specified, the node status (ex. NODE_PUBLISHED or
* NODE_NOT_PUBLISHED) to look for. If not specified, it return
* nodes of either status.
* @param bool $include_archived
* (optional) Whether to include archived nodes or not. By default, archived
* items aren't included.
* @param stdClass|NULL $account
* (optional) the account used to check access to the results. Defaults to
* the current user.
*
* @return array
* Array of parent NIDs to title.
*/
function oa_core_get_nids_title_matching($nids, $bundle = OA_SPACE_TYPE, $status = NULL, $include_archived = FALSE, $account = NULL) {
if (is_numeric($account)) {
$account = user_load($account);
}
elseif (!$account) {
global $user;
$account = $user;
}
sort($nids);
$cid = implode(':', $nids);
if (is_array($bundle)) {
$cid .= implode(':', $bundle);
}
elseif (!empty($bundle)) {
$cid .= ':' . $bundle;
}
$cid .= ':' . $status . ':' . $include_archived . ':' . $account->uid;
$cache =& drupal_static(__FUNCTION__, array());
if (!isset($cache[$cid])) {
$query = db_select('node', 'n');
if ($bundle) {
$query
->condition('n.type', $bundle);
}
if (isset($status)) {
$query
->condition('n.status', $status);
}
$query
->condition('n.nid', $nids);
$query
->fields('n', array(
'nid',
'title',
))
->addTag('node_access')
->addMetaData('account', $account);
if (module_exists('oa_archive') && !$include_archived) {
if ($flag = flag_get_flag('trash')) {
$query
->leftJoin('flagging', 'a', "a.fid = :fid AND a.entity_id = n.nid", array(
':fid' => $flag->fid,
));
// This makes sure that archived content isn't included, because 'uid'
// will be NULL if the join didn't connect anything.
$query
->isNull('a.uid');
}
}
$cache[$cid] = $query
->execute()
->fetchAllKeyed();
}
return $cache[$cid];
}
/**
* Get child Spaces or Groups.
*
* @param int $nid
* The NID of the Space or Group to check, or an array of nids
* @param string|NULL $bundle
* (optional) The node type (default: OA_SPACE_TYPE). If NULL is passed, it
* will include all node types.
* @param int|NULL $status
* (optional) If specified, the node status (ex. NODE_PUBLISHED or
* NODE_NOT_PUBLISHED) to look for. If not specified, it return
* nodes of either status.
* @param bool $include_archived
* (optional) Whether to include archived nodes or not. By default, archived
* items aren't included.
* @param int|NULL $account
* (optional) user to control node acess. If Null, use current user
* @param bool $subspaces
* (optional) determine if subspaces are also fetched, or if just top level children are fetched
* @param bool $inheritance
* (optional) if TRUE, only return subspaces that have inheritance enabled
*
* @return array
* Array of children NIDs.
*/
function oa_core_get_groups_by_parent($nids, $bundle = OA_SPACE_TYPE, $status = NULL, $include_archived = FALSE, $account = NULL, $subspaces = FALSE, $inheritance = TRUE) {
if ($nids == 0) {
return oa_core_get_top_parents($bundle, $status, $include_archived, $account);
}
if (!isset($account)) {
global $user;
$uid = $user->uid;
}
elseif (is_numeric($account)) {
$uid = $account;
}
else {
$uid = $account->uid;
}
if (is_array($nids)) {
$hash = md5(serialize($nids));
}
else {
$hash = $nids;
}
$cid = oa_core_get_cache_id(OA_CACHE_GROUPS_BY_PARENT, $hash . '_' . $uid, $include_archived, array(
serialize($bundle),
$status,
$subspaces,
));
if (oa_core_get_cache(OA_CACHE_GROUPS_BY_PARENT, $cid, $result)) {
return $result;
}
if (!is_array($nids)) {
$nids = array(
$nids,
);
}
$query = db_select('node', 'n');
$query
->join('og_membership', 'f', "n.nid = f.etid AND f.entity_type = 'node'");
$query
->fields('n', array(
'nid',
))
->orderBy('n.title');
// to find all subspaces, we query *all* spaces with parents
// otherwise, if just getting top level children we can filter it here.
if (!$subspaces) {
$query
->condition('f.gid', $nids, 'IN');
$query
->addTag('node_access');
}
if (isset($bundle)) {
if (is_array($bundle)) {
$query
->condition('n.type', $bundle, 'IN');
}
else {
$query
->condition('n.type', $bundle);
}
}
if (isset($status)) {
$query
->condition('n.status', $status);
}
if (module_exists('flag') && !$include_archived) {
if ($flag = flag_get_flag('trash')) {
$query
->leftJoin('flagging', 'a', "a.fid = :fid AND a.entity_id = n.nid", array(
':fid' => $flag->fid,
));
// This makes sure that archived content isn't included, because 'uid'
// will be NULL if the join didn't connect anything.
$query
->isNull('a.uid');
}
}
if ($subspaces) {
$query
->fields('f', array(
'gid',
));
$query
->condition('f.etid', $nids, 'NOT IN');
$query
->join('field_data_og_user_inheritance', 'inh', "inh.entity_id = f.gid && f.group_type = 'node'");
$query
->fields('inh', array(
'og_user_inheritance_value',
));
$result = $query
->execute()
->fetchAll();
// to get a list of all subspaces, we can now loop through the list of all parents recursively
// to build our children list
$all_parents = array();
foreach ($result as $parent) {
if (!$inheritance || $parent->og_user_inheritance_value == 1) {
$all_parents[$parent->gid][] = $parent->nid;
}
}
$result = _oa_core_get_all_children($all_parents, $nids);
}
else {
$result = $query
->execute()
->fetchCol(0);
}
$result = drupal_map_assoc($result);
oa_core_set_cache(OA_CACHE_GROUPS_BY_PARENT, $cid, $result);
return $result;
}
/**
* Helper function to return children of a list of parents.
*
* @param $all_parents array of sub-space nids keyed by space id for all spaces
* @param $nids array an array of parents we want children for
*/
function _oa_core_get_all_children($all_parents, $nids, &$processed = array()) {
$processed = array_merge($nids, $processed);
$result = array();
$children = array();
foreach (array_intersect_key($all_parents, array_flip($nids)) as $parent_children) {
$children = array_merge($children, $parent_children);
}
if ($new_children = array_diff($children, $processed)) {
$result = _oa_core_get_all_children($all_parents, $new_children, $processed);
}
$result = array_unique(array_merge($nids, $result), SORT_NUMERIC);
return $result;
}
/**
* Get top-level spaces/groups (no parents of same bundle type)
*
* @param string $bundle
* The node type (default: OA_SPACE_TYPE).
* @param int|NULL $status
* (optional) If specified, the node status (ex. NODE_PUBLISHED or
* NODE_NOT_PUBLISHED) to look for. If not specified, it return
* nodes of either status.
* @param bool $include_archived
* (optional) Whether to include archived nodes or not. By default, archived
* items aren't included.
*
* @return array
* Array of children NIDs.
*/
function oa_core_get_top_parents($bundle, $status = NULL, $include_archived = FALSE, $account = NULL) {
if (!isset($account)) {
global $user;
$uid = $user->uid;
}
elseif (is_numeric($account)) {
$uid = $account;
}
else {
$uid = $account->uid;
}
$cid = oa_core_get_cache_id(OA_CACHE_TOP_PARENTS, $uid, $include_archived, array(
$bundle,
$status,
));
if (oa_core_get_cache(OA_CACHE_TOP_PARENTS, $cid, $result)) {
return $result;
}
// need to also add the spaces whose parents are not the correct bundle
$query_parents = db_select('node', 'n');
$query_parents
->join('og_membership', 'f', "n.nid = f.etid");
$query_parents
->join('node', 'n2', "n2.nid = f.gid");
$query_parents
->fields('n', array(
'nid',
))
->condition('f.entity_type', 'node')
->condition('n.type', $bundle)
->condition('n2.type', $bundle);
$query = db_select('node', 'n');
$query
->fields('n', array(
'nid',
))
->orderBy('n.title')
->addTag('node_access')
->condition('n.type', $bundle)
->condition('n.nid', $query_parents, 'NOT IN');
if (isset($status)) {
$query
->condition('n.status', $status);
}
if (module_exists('flag') && !$include_archived) {
if ($flag = flag_get_flag('trash')) {
$query
->leftJoin('flagging', 'a', "a.fid = :fid AND a.entity_id = n.nid", array(
':fid' => $flag->fid,
));
// This makes sure that archived content isn't included, because 'uid'
// will be NULL if the join didn't connect anything.
$query
->isNull('a.uid');
}
}
$result = $query
->execute()
->fetchCol(0);
oa_core_set_cache(OA_CACHE_TOP_PARENTS, $cid, $result);
return $result;
}
/**
* Get all the content within a particular Section.
*
* @param int $nid
* The node ID of the Section.
*
* @return array
* An array of node IDs.
*/
function oa_core_get_section_content($nid) {
$query = new EntityFieldQuery();
$query
->entityCondition('entity_type', 'node')
->propertyCondition('status', 1)
->fieldCondition('oa_section_ref', 'target_id', $nid);
$result = $query
->execute();
if (isset($result['node'])) {
return array_keys($result['node']);
}
return array();
}
/**
* Determine if a user is a member of a team
* @param int $team_id
* @param int $user_id
* @return boolean TRUE if user is in team
*/
function oa_core_member_of_team($team_id, $user_id) {
$cache =& drupal_static(__FUNCTION__);
if (!isset($cache[$team_id][$user_id])) {
$result = db_select('field_data_field_oa_team_users', 'f')
->fields('f', array(
'field_oa_team_users_target_id',
))
->condition('field_oa_team_users_target_id', $user_id)
->condition('entity_type', 'node')
->condition('entity_id', $team_id)
->condition('deleted', 0)
->execute();
if ($result
->rowCount() > 0) {
$access = TRUE;
}
else {
// not explicitly in team, but check ownership of team node
// do NOT use node_load as this is called from hook_node_grants()
$result = db_select('node', 'n')
->fields('n', array(
'uid',
))
->condition('nid', $team_id)
->execute()
->fetchAssoc();
$access = $result['uid'] == $user_id ? TRUE : FALSE;
}
$cache[$team_id][$user_id] = $access;
}
return $cache[$team_id][$user_id];
}
/**
* Returns TRUE if the section $node has open access to public
*/
function oa_core_section_is_public($node) {
return empty($node->field_oa_group_ref[LANGUAGE_NONE]) && empty($node->field_oa_team_ref[LANGUAGE_NONE]) && empty($node->field_oa_user_ref[LANGUAGE_NONE]);
}
/**
* Returns a list of group-content types throughout the system. List
* leaves out content types excluded by other modules/apps.
*
* @return array of strings denoting content types marked for omission
*/
function oa_core_list_content_types($space_content = FALSE, $include_core = TRUE) {
$nodes = node_type_get_types();
if ($space_content) {
// remove types that are not space content
foreach ($nodes as $key => $node) {
if (!og_is_group_content_type('node', $node->type)) {
unset($nodes[$key]);
}
}
}
// Queries hook_oa_omit_content_types to determine what content types
// modules wants to hide.
$deny = module_invoke_all('oa_omit_content_types');
if (!$include_core) {
$deny = array_merge($deny, array(
OA_SPACE_TYPE,
OA_GROUP_TYPE,
OA_SECTION_TYPE,
OA_TEAM_TYPE,
));
}
if (!empty($deny)) {
foreach ($deny as $remove) {
unset($nodes[$remove]);
}
}
return $nodes;
}
/**
* Create a new content node within a space/section
* @param string $bundle name of node bundle to create
* @param object $context an optional code to copy space/section from
* if not specified, current Session space/section context is used
* @return object $wrapper entity metadata wrapper around node
*
* NOTE: The created node is NOT SAVED. You need to use:
* $wrapper = oa_core_create_node(...);
* $wrapper->save();
* to actually save the created node. This allows you to set other
* wrapper fields before saving
*/
function oa_core_create_node($bundle, $title = '', $context = NULL) {
global $user;
$values = array(
'type' => $bundle,
'uid' => $user->uid,
'status' => 1,
'comment' => 0,
'promote' => 0,
);
$entity = entity_create('node', $values);
$wrapper = entity_metadata_wrapper('node', $entity);
$wrapper->title = $title;
$space_id = oa_core_get_space_context();
$section_id = oa_core_get_section_context();
if (isset($context)) {
// copy space and section fields from context node
$context_wrapper = entity_metadata_wrapper('node', $context);
if (isset($context_wrapper->{OA_SPACE_FIELD})) {
$space_id = $context_wrapper->{OA_SPACE_FIELD}
->value();
if (is_array($space_id)) {
// if multi-value space field, just use first space for message
$space_id = array_shift($space_id);
}
}
if (isset($context_wrapper->{OA_SECTION_FIELD})) {
$section_id = $context_wrapper->{OA_SECTION_FIELD}
->value();
}
}
if (isset($wrapper->{OA_SPACE_FIELD})) {
$wrapper->{OA_SPACE_FIELD} = $space_id;
}
if (isset($wrapper->{OA_SECTION_FIELD})) {
$wrapper->{OA_SECTION_FIELD} = $section_id;
}
return $wrapper;
}
/**
* Helper function to retrieve an array of node titles and links given
* a list of node ids
* @param array $ids array of node ids to fetch
* @param string $type optional node type to filter
* @param array $fields a list of fields to fetch (link is a special fieldname)
* @param integer $limit max number of return results
* @return array associative array:
* 'titles' is a list of node titles (clean)
* 'links' is a list of node links
* 'ids' is a list of the node ids
*/
function oa_core_get_titles($ids = array(), $type = '', $sort_field = 'title', $fields = array(
'title',
'link',
'id',
'type',
), $sanitize = TRUE, $limit = 100) {
$query_fields = array(
'nid',
'title',
);
$return = array();
foreach ($fields as $field) {
// for backwards compatibility the array keys of the $return are
// ids, titles, links, types. So we append an 's' to the field name
$return[$field . 's'] = array();
// the 'link' and 'id' field names are handled specially and are not
// direct query field names
if (!in_array($field, array(
'link',
'id',
)) && !in_array($field, $query_fields)) {
$query_fields[] = $field;
}
}
if (!empty($ids)) {
$query = db_select('node', 'n');
if (!empty($sort_field) && $sort_field != 'title') {
$query
->leftJoin('field_data_' . $sort_field, 's', "n.nid = s.entity_id AND s.entity_type = 'node'");
}
$query
->fields('n', $query_fields)
->condition('n.nid', $ids, 'IN');
if ($sort_field == 'title') {
$query
->orderBy('title');
}
elseif (!empty($sort_field)) {
$query
->orderBy('s.' . $sort_field . '_value');
}
if (!empty($type)) {
$query
->condition('n.type', $type);
}
if ($limit) {
$query
->range(0, $limit);
}
$result = $query
->execute();
// if $sort_field is empty, maintain the order of the original $ids
// so create a lookup table.
$nid_row = array();
$index = 0;
while ($row = $result
->fetchAssoc()) {
$id = empty($sort_field) ? $row['nid'] : $index;
$nid_row[$id] = $row;
$index++;
}
$index = 0;
foreach ($ids as $nid) {
$id = empty($sort_field) ? $nid : $index;
if (!empty($nid_row[$id])) {
foreach ($fields as $field) {
$field_name = $field == 'id' ? 'nid' : $field;
$value = isset($nid_row[$id][$field_name]) ? $nid_row[$id][$field_name] : NULL;
if ($field == 'title' && $sanitize) {
$value = check_plain($value);
}
if ($field == 'link') {
$value = l($nid_row[$id]['title'], 'node/' . $nid_row[$id]['nid']);
}
$return[$field . 's'][$nid_row[$id]['nid']] = $value;
}
}
$index++;
}
}
return $return;
}
/**
* truncate a list to a given number of items with optional More link
* @param array $list array to be truncated
* @param int $count number of items desired
* @param string $more_link optional "More" link added to end of array
* @param boolean $always_link TRUE to always display more link
* @return array new list of items
*/
function oa_core_truncate_list($list, $count, $more_link = '', $always_link = FALSE) {
$new_list = array_slice($list, 0, $count);
if (!empty($more_link) && ($always_link || count($list) != count($new_list))) {
$new_list[] = $more_link;
}
return $new_list;
}
/**
* Get a list of public spaces.
*
* Necessary since og_get_entity_groups() doesn't return anything for anonymous users
*
* @param array $group_types
* (optional) An associative array of node types.
* (default: array(OA_SPACE_TYPE => OA_SPACE_TYPE)
* @param int $status
* (optional) If specified, the node status (ex. NODE_PUBLISHED or
* NODE_NOT_PUBLISHED) to look for. If not specified, it return
* nodes of either status.
* @param bool $include_archived
* (optional) Whether to include archived nodes or not. By default, archived
* items aren't included.
* @param bool $check_access
* Check node access, defaults to TRUE.
* @param bool $only_top restrict to top-level spaces
*
* @return array
* An array of Space NIDs.
*/
function oa_core_get_public_spaces($group_types = array(
OA_SPACE_TYPE => OA_SPACE_TYPE,
), $status = NULL, $include_archived = FALSE, $check_access = TRUE, $only_top = FALSE) {
$cache =& drupal_static(__FUNCTION__);
$group_types_cid = implode(',', $group_types);
if (!isset($cache[$group_types_cid][$status][$include_archived])) {
$query = db_select('node', 'n');
$query
->join('field_data_group_access', 'g', "n.nid = g.entity_id AND g.entity_type = 'node'");
$query
->fields('n', array(
'nid',
));
if ($only_top) {
// need to restrict to space with either no parent, or just oa_group parents
$query
->leftJoin('og_membership', 'og', "og.etid = n.nid AND og.entity_type = 'node'");
$query
->leftJoin('node', 'pn', 'og.gid = pn.nid');
$query
->fields('pn', array(
'type',
));
// trick is to group the results and count the number of different parent types
$query
->addExpression('COUNT(DISTINCT pn.type)', 'num');
$query
->groupBy('n.nid');
// so we either return spaces with 0 parents, or 1 parent type that is a group
$query
->having("num = 0 OR (num = 1 AND pn.type = '" . OA_GROUP_TYPE . "')");
}
$query
->condition('n.type', $group_types, 'IN')
->condition('g.group_access_value', 0);
if (isset($status)) {
$query
->condition('n.status', $status);
}
if ($check_access) {
$query
->addTag('node_access');
}
if (module_exists('flag') && !$include_archived) {
if ($flag = flag_get_flag('trash')) {
$query
->leftJoin('flagging', 'a', "a.fid = :fid AND a.entity_id = n.nid", array(
':fid' => $flag->fid,
));
// This makes sure that archived content isn't included, because 'uid'
// will be NULL if the join didn't connect anything.
$query
->isNull('a.uid');
}
}
$cache[$group_types_cid][$status][$include_archived] = $query
->execute()
->fetchCol(0);
}
return $cache[$group_types_cid][$status][$include_archived];
}
/**
* Return a list of sections within a space
* Uses access control, so only sections with access are returned
* @param int $gid space ID
* @param int $status NULL for any status, otherwise specify status to return
* @param bool $bypass_access_check, TRUE to bypass access control
* @param array $fields, array of additional fields to return, otherwise just title
* fields array has format fieldname|column. e.g., field_oa_space|tid
* @param bool $include_archived Whether to include archived nodes or not
* @return array of section data: $array[$nid] = $title. If $fields is specified
* the return array is the full associative array of nid, title, fields
*/
function oa_core_space_sections($gid, $status = NULL, $bypass_access_check = FALSE, $fields = array(), $include_archived = FALSE) {
$query = db_select('node', 'n');
$query
->rightJoin('og_membership', 'og', 'n.nid = og.etid');
$query
->leftJoin('field_data_field_oa_section_weight', 'w', "n.nid = w.entity_id AND w.entity_type = 'node'");
$extra_fields = array();
foreach ($fields as $field) {
if (strpos($field, 'field_') === 0 || strpos($field, '|') !== FALSE) {
$field_list = explode('|', $field);
$field_name = $field_list[0];
$column = !empty($field_list[1]) ? $field_list[1] : 'value';
$query
->leftJoin('field_data_' . $field_name, $field_name, 'n.nid = ' . $field_name . '.entity_id');
$query
->fields($field_name, array(
$field_name . '_' . $column,
));
}
else {
$extra_fields[] = $field;
}
}
if (isset($status)) {
$query
->condition('n.status', $status);
}
$query
->fields('n', array(
'nid',
'title',
) + $extra_fields)
->condition('n.type', OA_SECTION_TYPE)
->condition('og.entity_type', 'node')
->condition('og.field_name', OA_SPACE_FIELD)
->condition('og.gid', $gid)
->orderBy('w.field_oa_section_weight_value')
->orderBy('n.title');
if (!$bypass_access_check) {
$query
->addTag('node_access');
}
if (module_exists('flag') && !$include_archived) {
if ($flag = flag_get_flag('trash')) {
$query
->leftJoin('flagging', 'a', "a.fid = :fid AND a.entity_id = n.nid", array(
':fid' => $flag->fid,
));
// This makes sure that archived content isn't included, because 'uid'
// will be NULL if the join didn't connect anything.
$query
->isNull('a.uid');
}
}
$result = $query
->execute();
if (empty($fields)) {
return $result
->fetchAllKeyed(0, 1);
}
else {
return $result
->fetchAllAssoc('nid');
}
}
/**
* Return a list of all Groups
*
* @param $match string for autocomplete lookup
* @param $match_operator string for autocomplete lookup
* @param $limit int number to limit, or zero for no limit
* @return
* An array of group nid, title, keyed by the group nid.
*/
function oa_core_get_all_groups($match = NULL, $match_operator = 'CONTAINS', $limit = 0) {
$query = db_select('node', 'n');
$query
->fields('n', array(
'nid',
'title',
))
->condition('n.type', OA_GROUP_TYPE)
->addTag('node_access');
if (!empty($match)) {
$match = $match_operator == 'CONTAINS' ? '%' . $match . '%' : $match;
$match = $match_operator == 'STARTS_WITH' ? '%' . $match : $match;
$query
->condition('n.title', $match, 'LIKE');
}
if (!empty($limit)) {
$query
->range(0, $limit);
}
return $query
->execute()
->fetchAllAssoc('nid');
}
/**
* Get the users that are in a space (excluding inherited users).
*
* @param int $space_id
* The NID of the Space.
* @param int $state
* (Optional) The state of the OG membership, for example:
* - OG_STATE_ACTIVE (the default)
* - OG_STATE_PENDING
* - OG_STATE_BLOCKED
*
* @return array
* An associative array of fully loaded user objects, keyed by user id.
*/
function oa_core_get_users_for_space($space_id, $state = OG_STATE_ACTIVE, $sort = FALSE) {
$query = db_select('og_membership', 'og')
->fields('og', array(
'etid',
))
->condition('og.entity_type', 'user')
->condition('og.state', $state)
->condition('og.group_type', 'node')
->condition('og.gid', $space_id);
$query
->join('users', 'u', 'u.uid = og.etid');
$query
->condition('u.status', 1);
if (!empty($sort)) {
$query
->join('realname', 'n', 'n.uid = og.etid');
$query
->orderBy('n.realname');
}
$uids = $query
->execute()
->fetchCol();
return user_load_multiple($uids);
}
/**
* Helper to get membership nids for a group.
*
* @param int $nid
* The nid of the group to find og_membership nodes.
* @param bool $loaded
* Whether to return just the nids or fully loaded nodes.
*
* @return array
* The og_membership nids or fully loaded node objects.
*/
function oa_core_get_membership_nodes($nid, $loaded = FALSE) {
$nids = db_select('og_membership', 'og')
->fields('og', array(
'etid',
))
->condition('og.entity_type', 'node')
->condition('og.gid', $nid)
->condition('og.field_name', OG_AUDIENCE_FIELD)
->execute()
->fetchCol(0);
if ($loaded) {
return node_load_multiple($nids);
}
else {
return $nids;
}
}
/**
* Get the users that are in a space, including inherited users.
*
* @param int $space_id
* The NID of the Space.
* @param int $state
* (Optional) The state of the OG membership, for example:
* - OG_STATE_ACTIVE (the default)
* - OG_STATE_PENDING
* - OG_STATE_BLOCKED
*
* @return array
* An associative array of fully loaded user objects, keyed by user id.
*/
function oa_core_get_inherited_users_for_space($space_id, $state = OG_STATE_ACTIVE) {
$users = oa_core_get_users_for_space($space_id, $state);
if (module_exists('og_subgroups') && function_exists('_og_subgroups_get_inherited_users')) {
$inherited_users = _og_subgroups_get_inherited_users('node', $space_id);
foreach ($inherited_users as $uid => $data) {
foreach ($data as $item) {
if ($item['membership']->state == $state) {
$users[$uid] = $item['user'];
}
}
}
}
return $users;
}
/**
* Return the users that are the intersection of Group and Space membership.
*
* @param int $space_id
* The Space ID of the Open Atrium site
* @param $group_id
* The Group ID
* @return
* An array of Users keyed by uid
*/
function oa_core_get_group_users_for_space($space_id, $group_id) {
$query = db_select('users', 'u');
$query
->innerJoin('og_membership', 'og1', 'u.uid = og1.etid');
$query
->innerJoin('og_membership', 'og2', 'u.uid = og2.etid');
$query
->fields('u', array(
'uid',
))
->condition('og1.entity_type', 'user')
->condition('og1.gid', $space_id)
->condition('og1.group_type', 'node')
->condition('og2.entity_type', 'user')
->condition('og2.gid', $group_id)
->condition('og1.group_type', 'node');
$results = $query
->execute()
->fetchAllAssoc('uid');
return user_load_multiple(array_keys($results));
}
/**
* Return a list of space ids that a user belongs to.
*
* @deprecated This function is redundant - use oa_core_get_groups_by_user().
*
* @see oa_core_get_groups_by_user()
*/
function oa_core_get_user_spaces($uid) {
$groups = oa_core_get_groups_by_user(user_load($uid), 'node');
return !empty($groups) ? $groups : array();
}
/**
* Helper function to return the id of the space/group that contains the nid node
*
* This function is called a lot, so it needs to be fast
* Do not use node_load here!
*
* @param $node
* A numeric NID or a complete $node object.
* @param $allowed_types
* an array of content types that this node can be part of.
*
* @return
* A numeric NID of the group that this content is part of.
* If $node is a group or not member of a group, its NID is returned.
*/
function oa_core_get_group_from_node($node, $allowed_types = array(
OA_SPACE_TYPE,
OA_GROUP_TYPE,
)) {
$cache =& drupal_static('oa_core_groups', array());
// Find the NID in case a $node object was passed in..
if (is_object($node) && !empty($node->nid)) {
$nid = $node->nid;
if (!empty($allowed_types) && in_array($node->type, $allowed_types)) {
$cache[$nid] = $nid;
}
}
else {
$nid = $node;
}
if (isset($cache[$nid])) {
return $cache[$nid];
}
$query = db_select('og_membership', 'f');
$query
->leftJoin('node', 'n', 'n.nid = f.gid');
$result = $query
->fields('f', array(
'gid',
))
->condition('f.etid', $nid)
->condition('f.group_type', 'node')
->condition('f.entity_type', 'node')
->condition('f.field_name', OA_SPACE_FIELD)
->condition('n.type', $allowed_types)
->execute()
->fetchAssoc();
if (!empty($result)) {
$cache[$nid] = $result['gid'];
}
else {
$cache[$nid] = $nid;
}
return $cache[$nid];
}
/**
* Sort function to sort users by name.
*/
function oa_core_sort_users_by_name($u1, $u2) {
// Get last name for sorting.
$name1 = !empty($u1->realname) ? oa_core_get_last_word($u1->realname) : $u1->name;
$name2 = !empty($u2->realname) ? oa_core_get_last_word($u2->realname) : $u2->name;
return strcasecmp($name1, $name2);
}
/**
* Helper to return last word of a string
*/
function oa_core_get_last_word($string) {
// Strip any text in parens or brackets
// Also strip any title ending in period, like PhD.
// Also strip roman numerals I, II, IV, etc
$string = preg_replace('/([\\(\\[].*[\\)\\]]|\\s[^\\s]+\\.$|\\s[IV]+)/', '', $string);
$string = trim($string);
return ($pos = strrpos($string, ' ')) ? substr($string, $pos + 1) : $string;
}
/**
* Return the name of a user
*/
function oa_core_realname($user) {
return !empty($user->realname) ? $user->realname : (!empty($user->name) ? $user->name : '');
}
/**
* Convert known entities in to a simple array of title and picture.
*/
function oa_core_entity_build_display($entity, $id, $space, $subpage = '') {
if (!empty($entity->nid)) {
if (node_access('view', $entity)) {
$display['title'] = l($entity->title, 'node/' . $entity->nid . $subpage);
}
else {
$display['title'] = t('A @type', array(
'@type' => node_type_get_name($entity->type),
));
}
$display['picture'] = '';
$display['uid'] = 0;
$display['options'] = array();
}
else {
$display['title'] = l(format_username($entity), 'user/' . $entity->uid . $subpage);
$display['uid'] = $entity->uid;
$display['picture'] = oa_users_picture($entity);
$display['options'] = array(
'attributes' => array(
'class' => array(
'use-ajax',
),
),
'query' => drupal_get_destination() + array(
'token' => drupal_get_token('node_' . $space->nid . '_' . $entity->uid),
),
);
}
return $display;
}
/**
* Return vocabularies assigned to a specific content type
*/
function oa_core_get_entity_vocabs($entity_type, $bundle) {
$vocabs = array();
$fields = field_info_instances($entity_type, $bundle);
foreach ($fields as $field_name => $field) {
$info = field_info_field($field_name);
// handle normal taxonomy_term_reference fields
if ($info['type'] == 'taxonomy_term_reference') {
foreach ($info['settings']['allowed_values'] as $value) {
if (!empty($value['vocabulary'])) {
$vocabs[$value['vocabulary']] = $value['vocabulary'];
}
}
}
elseif ($info['type'] == 'entityreference' && $info['settings']['target_type'] == 'taxonomy_term') {
if (!empty($info['settings']['handler_settings']['target_bundles'])) {
foreach ($info['settings']['handler_settings']['target_bundles'] as $value) {
$vocabs[$value] = $value;
}
}
}
}
return $vocabs;
}
/**
* Returns the named vocabulary
* Cannot use core taxonomy_vocabulary_machine_name_load because og_vocab
* alters the query to only return vocabs associated with the current space
* see issue https://drupal.org/node/2270529
*/
function oa_core_taxonomy_vocabulary($machine_name) {
// EntityFieldQuery is not subject to the
// og_vocab_query_taxonomy_vocabulary_load_multiple_alter() function
$efq = new EntityFieldQuery();
$result = $efq
->entityCondition('entity_type', 'taxonomy_vocabulary')
->propertyCondition('machine_name', $machine_name)
->execute();
return !empty($result['taxonomy_vocabulary']) ? current($result['taxonomy_vocabulary']) : NULL;
}
/**
* Return terms for a list of vocabularies
*/
function oa_core_get_vocab_terms($vocabs) {
$options = array();
foreach ($vocabs as $vocab) {
if ($vocabulary = oa_core_taxonomy_vocabulary($vocab)) {
if ($terms = taxonomy_get_tree($vocabulary->vid)) {
foreach ($terms as $term) {
$options[$term->tid] = str_repeat('-', $term->depth) . $term->name;
}
}
}
}
return $options;
}
/**
* Returns the version of Bootstrap (via Radix) being used
* Returns 2 or 3 for Bootstrap, returns 0 if no Bootstrap is used
*/
function oa_core_get_bootstrap_version() {
static $radix_version = NULL;
if (isset($radix_version)) {
return $radix_version;
}
// set default to 3 so if using a git clone without version info
// we assume it's the latest -dev version
$radix_version = 3;
// parse the Radix info file to determine version
$path = drupal_get_path('theme', 'radix') . '/' . 'radix.info';
$info = drupal_parse_info_file($path);
if (empty($info)) {
// no Radix theme found, so return zero.
$radix_version = 0;
}
elseif (!empty($info['version']) && strpos($info['version'], '7.x-') === 0) {
$radix_version = intval(substr($info['version'], 4, 1));
}
return $radix_version;
}
/**
* Helper function to create a new Section or Space Type taxonomy term
* @param string $vocab
* Machine name of vocabulary to add term to
* @param string $name
* Human readable label of the term
* @param array $params
* expected to have fields for:
* 'taxonomy' - vocabulary machine name.
* 'name' - The human readable label of the term.
* 'description' - The human readable description of the term.
* 'node_options' - An array of node types which should be allowed within the section.
* 'layout' - The panelizer layout key to use for this section.
* 'icon' - The icon class to use in the sitemap.
* @param bool $update
* Whether an existing term is allowed to be updated.
*/
function oa_core_create_term($vocab_name, $name, $params, $update = TRUE) {
$vocab = oa_core_taxonomy_vocabulary($vocab_name);
$term = NULL;
// This function can get called from install hooks.
// Make sure the Taxonomy is available.
if (!$vocab) {
features_revert(array(
'oa_core' => array(
'taxonomy',
),
'oa_sections' => array(
'taxonomy',
),
));
drupal_static('taxonomy_vocabulary_get_names');
$vocab = oa_core_taxonomy_vocabulary($vocab_name);
}
if (!empty($vocab)) {
$conditions = array(
'name' => trim($name),
'vid' => $vocab->vid,
);
$term = current(entity_load('taxonomy_term', array(), $conditions));
if (!$term || $update) {
// Make sure the fields are available.
$field_info = field_info_instances('taxonomy_term', $vocab_name);
$revert = array();
if (empty($field_info['field_oa_node_types'])) {
$revert['oa_buttons'] = array(
'field_base',
'field_instance',
);
}
if (empty($field_info['field_oa_section_layout'])) {
$revert['oa_core'] = array(
'field_base',
'field_instance',
);
}
if (empty($field_info['field_oa_icon_class'])) {
$revert['oa_sections'] = array(
'field_base',
'field_instance',
);
}
if (!empty($revert)) {
features_revert($revert);
field_info_cache_clear();
}
$description = $params['description'];
$node_options = $params['node_options'];
$layout = $params['layout'];
$icon = !empty($params['icon']) ? $params['icon'] : '';
if ($term && $update) {
// Update existing term.
if (isset($term->field_oa_icon_class)) {
$term->field_oa_icon_class[LANGUAGE_NONE][0]['value'] = $icon;
}
$term->field_oa_section_layout[LANGUAGE_NONE][0]['value'] = $layout;
$term->field_oa_node_types[LANGUAGE_NONE] = array();
}
else {
// Create new term.
$term = (object) array(
'vid' => $vocab->vid,
'name' => $name,
'description' => $description,
'format' => 'panopoly_wysiwyg_text',
'field_oa_icon_class' => array(
LANGUAGE_NONE => array(
array(
'value' => $icon,
),
),
),
'field_oa_section_layout' => array(
LANGUAGE_NONE => array(
array(
'value' => $layout,
),
),
),
'field_oa_node_types' => array(
LANGUAGE_NONE => array(),
),
'path' => array(
'alias' => '',
'pathauto' => '',
),
'oa_button' => TRUE,
);
}
if (!empty($node_options)) {
foreach ($node_options as $type) {
$term->field_oa_node_types[LANGUAGE_NONE][] = array(
'value' => $type,
);
}
}
taxonomy_term_save($term);
}
}
return $term;
}
/**
* Helper function to replace HTML tags in $str.
* @param $str - string to process
* @param $tags - array of oldtag=>newtag items
* @return string
*/
function oa_core_replace_tags($str, $tags) {
foreach ($tags as $old => $new) {
$str = preg_replace("~<(/)?{$old}>~", "<\\1{$new}>", $str);
}
return $str;
}
/**
* Trim text to a specified length
* Similar to views_trim_text or the Smart Trim module
* but includes consistent options for removing tags needed by OA2
*/
function oa_core_trim_text($value, $more_link = '', $max_length = NULL) {
$trimmed = FALSE;
// Remove and replace some HTML tags
$value = oa_core_replace_tags($value, array(
'h1' => 'h4',
'h2' => 'h4',
'h3' => 'h4',
));
$value = strip_tags($value, '<br><p><h1><h2><h3><h4><h5><h6><ol><ul><li><dl><dd><dt><a><b><i><strong><em><table><tbody><th><tr><td>');
$max_length = isset($max_length) ? $max_length : variable_get('teaser_length', 600);
if (drupal_strlen($value) > $max_length) {
$value = drupal_substr($value, 0, $max_length);
// only trim on word boundries
$regex = "(.*)\\b.+";
if (function_exists('mb_ereg')) {
mb_regex_encoding('UTF-8');
$matches = array();
$found = mb_ereg($regex, $value, $matches);
}
else {
$found = preg_match("/{$regex}/us", $value, $matches);
}
if ($found) {
$value = $matches[1];
}
// Remove scraps of HTML entities from the end of a strings
$value = rtrim(preg_replace('/(?:<(?!.+>)|&(?!.+;)).*$/us', '', $value));
$value .= '…';
$trimmed = TRUE;
}
// add any needed closing tags.
$value = _filter_htmlcorrector($value);
if ($trimmed) {
if (!empty($more_link)) {
$value .= '<span class="more-link">' . l(t('more…'), $more_link) . '</span>';
}
}
return $value;
}
/**
* Helper function to return the summary of a node or the trimmed body text
* @param $node
*/
function oa_core_get_summary($node) {
$body_field = field_view_field('node', $node, 'body');
$body = !empty($body_field['#items'][0]['summary']) ? $body_field['#items'][0]['summary'] : (!empty($body_field[0]['#markup']) ? oa_core_trim_text($body_field[0]['#markup'], 'node/' . $node->nid . '/view') : '');
return $body;
}
/**
* Determine whether a user has a given privilege for groups.
*
* This modules the logic of og_user_access but for multiple node groups.
* IMPORTANT, any changes to og_user_access reflect here.
*
* We cannot offer any speed improvements if og_user_access_alter is implemented
* as it requires a entity_load.
*
* @param $group_type
* This will fall through to og_user_access access if not 'node'
* @param $nids
* Array of node nids that are groups.
* @param $string
* String can be a string or array of strings (which diverges from
* og_user_access) of permissions to check. If an array is passed,
* it'll be TRUE for that group if user has ANY of the permissions.
*
* @return
* An array of nids to access.
*/
function oa_user_access_nids($group_type, $nids, $string, $account = NULL, $skip_alter = FALSE, $ignore_admin = FALSE) {
global $user;
$strings = is_array($string) ? $string : array(
$string,
);
$access =& drupal_static(__FUNCTION__, array());
if (empty($account)) {
$account = $user;
}
$nids = drupal_map_assoc($nids);
// Administer group permission. We don't cache it cause ignore_admin can change.
if ($account->uid == 1 || !$ignore_admin && user_access('administer group', $account)) {
$return = array();
foreach ($nids as $nid) {
$return[$nid] = TRUE;
}
return $return;
}
// We don't handle not-nodes (cause hard to look up owner), the alter hook
// (it needs full node_load).
$alter = module_implements('og_user_access_alter');
foreach (array(
'og_subgroups',
'og_ui',
) as $remove) {
$pos = array_search($remove, $alter);
if ($pos !== FALSE) {
unset($alter[$pos]);
}
}
// We cannot replicate unknown alters or unsubscribe logic, so fallback.
// @see og_ui_og_user_access_alter().
if ($group_type != 'node' || !$skip_alter && $alter || in_array('unsubscribe', $strings)) {
foreach ($nids as $nid) {
foreach ($strings as $string) {
if (!isset($access[$account->uid][$string][$nid])) {
$access[$account->uid][$string][$nid] = og_user_access($group_type, $nid, $string, $account, $skip_alter, $ignore_admin);
}
}
}
}
$lookup = array();
$all_lookup = array();
// Find what nodes/permissions still need to be looked up.
foreach ($strings as $string) {
if (!empty($access[$account->uid][$string])) {
$lookup[$string] = array_diff_key($nids, $access[$account->uid][$string]);
}
else {
$lookup[$string] = $nids;
}
if (!$lookup[$string]) {
unset($lookup[$string]);
}
}
foreach ($lookup as $string => $nids_array) {
$all_lookup += $nids_array;
}
// Group manager has all privileges (if variable is TRUE).
if ($all_lookup && !empty($account->uid) && variable_get('og_group_manager_full_access', TRUE)) {
$authored = db_query('SELECT nid FROM {node} WHERE nid in (:nids) AND uid = :uid', array(
':nids' => $all_lookup,
':uid' => $account->uid,
))
->fetchCol();
foreach ($authored as $nid) {
foreach ($strings as $string) {
$access[$account->uid][$string][$nid] = TRUE;
unset($lookup[$string][$nid]);
unset($all_lookup[$nid]);
}
}
}
if ($all_lookup && !empty($account->uid)) {
// Add administer group as first permission checked (as most likely).
if (!$ignore_admin) {
array_unshift($strings, 'administer group');
}
/**
* The query needed is quite complex for regular oa access.
*
* The rid in og_users_roles can either be associated with no group if
* the roles have not been overriden or with the group if they have.
*
* The rid can either be associated with the user or the 'member' role
* which is added automatically.
*
* Below is a sample query.
* @code
* SELECT om.gid, orp.permission
* FROM og_role_permission orp
* INNER JOIN og_role oro ON oro.rid = orp.rid
* INNER JOIN og_membership om ON om.group_type = 'node' AND oro.group_type = 'node' AND om.entity_type='user'
* INNER JOIN node n ON n.nid = om.gid
* INNER JOIN field_data_og_roles_permissions fdp on fdp.entity_type = 'node' AND fdp.entity_id = om.gid
* LEFT JOIN og_users_roles our ON our.rid = oro.rid AND our.uid = om.etid AND our.group_type = "node" AND our.gid = om.gid
* WHERE om.etid = 159
* AND (oro.gid = om.gid OR (fdp.og_roles_permissions_value = 0 and oro.gid = 0 AND oro.group_bundle = n.type))
* AND (our.uid = 159 OR oro.name = 'member')
* AND om.state = 1 AND om.gid IN (139);
* @endcode
*
* @todo BUNDLE DAMMIT
*/
$query = db_select('og_role_permission', 'orp');
$query
->innerJoin('og_role', 'oro', 'oro.rid = orp.rid');
$query
->innerJoin('og_membership', 'om', "om.group_type = 'node' AND oro.group_type = 'node' AND om.entity_type='user'");
$query
->innerJoin('node', 'n', "n.nid = om.gid");
$query
->innerJoin('field_data_og_roles_permissions', 'fdp', "fdp.entity_type = 'node' AND fdp.entity_id = om.gid");
$query
->leftJoin('og_users_roles', 'our', "our.rid = oro.rid AND our.uid = om.etid AND our.group_type = 'node' AND our.gid = om.gid");
$query
->fields('om', array(
'gid',
));
$query
->fields('orp', array(
'permission',
));
$query
->condition('om.etid', $account->uid);
$and = db_and()
->condition('fdp.og_roles_permissions_value', 0)
->condition('oro.gid', 0)
->where('n.type = oro.group_bundle');
$or = db_or()
->where('oro.gid = om.gid')
->condition($and);
$query
->condition($or);
$or = db_or()
->condition('our.uid', $account->uid)
->condition('oro.name', 'member');
$query
->condition($or);
$query
->condition('orp.permission', $strings);
$query
->condition('state', OG_STATE_ACTIVE);
$query
->condition('om.gid', $all_lookup);
$result = $query
->execute();
$rows = $result
->fetchAll();
foreach ($rows as $row) {
$access[$account->uid][$row->permission][$row->gid] = TRUE;
unset($lookup[$row->permission][$row->gid]);
}
}
// Check parent access.
// This needs further optomization.
if ($lookup && !$skip_alter && in_array('og_subgroups', module_implements('og_user_access_alter'))) {
$user_groups = og_subgroup_user_groups_load($account, FALSE);
foreach ($lookup as $string => $nids_array) {
foreach ($nids_array as $nid) {
if (oa_og_subgroups_check_access($string, $group_type, $nid, $user_groups, TRUE)) {
$access[$account->uid][$string][$nid] = TRUE;
unset($lookup[$string][$nid]);
}
}
}
}
// Any extra NID is no access.
foreach ($lookup as $string => $nids_array) {
foreach ($nids_array as $nid) {
// This should not be set, but just in case only set to false if not.
if (!isset($access[$account->uid][$string][$nid])) {
$access[$account->uid][$string][$nid] = FALSE;
}
}
}
// Figure out if user has any access.
$return = array();
$remaining_nids = $nids;
do {
$string = array_shift($strings);
if (!empty($access[$account->uid][$string])) {
$return += array_filter(array_intersect_key($access[$account->uid][$string], $remaining_nids));
}
$remaining_nids = array_diff_key($nids, $return);
} while ($remaining_nids && $strings);
// If no strings left, there are no access.
if ($remaining_nids) {
foreach ($remaining_nids as $nid) {
$return[$nid] = FALSE;
}
}
// Need to return array as combination of strings.
return $return;
}
/**
* Check access for this group's parents.
*
* Copy of _og_subgroups_check_access but using our functions for optomization.
*
* @see _og_subgroups_check_access().
*/
function oa_og_subgroups_check_access($string, $group_type, $id, $user_groups, $check_member_access = FALSE) {
// Check only one level at a time due to permission inheritance field.
if ($parent_groups = og_subgroups_intersect_groups(og_subgroups_parents_load($group_type, $id, TRUE, FALSE), $user_groups)) {
foreach ($parent_groups as $parent_group_type => $ids) {
// Find all groups that ahve inheritence set to child (assume inherit
// [default] otherwise).
$child_inheritence = _og_subgroups_get_field_matching($parent_group_type, $ids, OG_USER_INHERITANCE_PERMISSION_FIELD, OG_USER_INHERITANCE_PERMISSION_CHILD);
$parent_inheritence = array_diff($ids, $child_inheritence);
if ($parent_inheritence = array_diff($ids, $child_inheritence)) {
if (array_filter(oa_user_access_nids($parent_group_type, $parent_inheritence, $string, NULL, TRUE))) {
return TRUE;
}
}
if ($check_member_access) {
foreach ($child_inheritence as $parent_group_id) {
if (og_subgroups_check_member_user_access($group_type, $id, $string)) {
return TRUE;
}
}
}
// Check parents if neither current nor member gave access.
foreach ($parent_inheritence as $parent_group_id) {
if (oa_og_subgroups_check_access($string, $parent_group_type, $parent_group_id, $user_groups)) {
return TRUE;
}
}
}
}
return FALSE;
}
/**
* Replacement for og_user_access.
*
* Because we don't use the alter hook, we don't need to do the full entity load
* so often checking via direct query (if node is unlikely to have been loaded)
* is faster.
*
* @see og_user_access
*/
function oa_user_access($group_type, $gid, $string, $account = NULL, $skip_alter = FALSE, $ignore_admin = FALSE) {
$access = oa_user_access_nids($group_type, array(
$gid,
), $string, $account, $skip_alter, $ignore_admin);
return reset($access);
}
/**
* Helper function to return a file object that represents the default site banner.
*/
function oa_core_get_banner_default() {
global $base_url;
$filename = module_invoke_all('oa_core_banner_default');
if (!empty($filename)) {
$filename = current($filename);
$uri = $base_url . '/' . $filename;
}
else {
$uri = theme_get_setting('logo');
$filename = str_replace($base_url . '/', '', $uri);
}
if (file_exists($filename)) {
$site_file = new stdClass();
$site_file->uri = $uri;
$info = image_get_info($filename);
$site_file->metadata['height'] = $info['height'];
$site_file->metadata['width'] = $info['width'];
return $site_file;
}
return NULL;
}
/**
* Helper function to return/add classes to the body tag
* @param array $styles
*/
function oa_core_body_classes($new_classes = array()) {
static $classes = array();
if (!empty($new_classes)) {
if (!is_array($new_classes)) {
$new_classes = array(
$new_classes,
);
}
$classes = array_merge($classes, $new_classes);
}
return $classes;
}
/**
* Add the node layout as a body class to the page
* @param $node
*/
function oa_core_add_node_class($node) {
if (!empty($node->panelizer['page_manager']->name)) {
oa_core_add_layout_class($node->panelizer['page_manager']->name);
}
}
/**
* Add the taxonomy layout as a body class to the page
* @param $vocab_name
* @param $tid
*/
function oa_core_add_taxonomy_class($tid) {
if ($term = taxonomy_term_load($tid)) {
if (!empty($term->field_oa_section_layout[LANGUAGE_NONE][0]['value'])) {
oa_core_add_layout_class($term->field_oa_section_layout[LANGUAGE_NONE][0]['value']);
}
}
}
/**
* Add the given layout name as a body class on the page
* @param $name of layout. The node: part is skipped
* @param $prefix optional prefix for the class
*/
function oa_core_add_layout_class($name, $prefix = '') {
$list = explode(':', $name);
$prefix = !empty($prefix) ? $prefix . '-' : '';
if (count($list) > 1) {
$class_name = $prefix . $list[1];
if (count($list) > 2) {
$class_name .= '-' . $list[2];
oa_core_body_classes(drupal_html_class($class_name));
}
}
}
/**
* Wrapper around @see batch_set().
*
* @param array $batch
*/
function oa_core_batch_set(&$batch) {
// For Drush.
if (drupal_is_cli()) {
$batch['progressive'] = FALSE;
batch_set($batch);
if (function_exists('drush_backend_batch_process')) {
// Running via drush, to start the batch processing.
drush_backend_batch_process();
}
}
else {
// Running interactively via the UI so just set the batch.
batch_set($batch);
}
}
/**
* Helper to combine multiple calls in OA to features_template_revert() to
* just a single call.
*/
function oa_core_features_template_revert() {
if (!variable_get('oa_core_template_revert', FALSE)) {
variable_set('oa_core_template_revert', TRUE);
drupal_register_shutdown_function('oa_core_revert_template', 'oa_core_template_revert');
}
}
/**
* This is called when all modules, using oa_core_features_template_revert(),
* have registered for a template revert.
*
* @param string $variable
* Variable name to delete.
*/
function oa_core_revert_template($variable) {
features_template_revert();
variable_delete($variable);
}
Functions
Name![]() |
Description |
---|---|
oa_core_add_layout_class | Add the given layout name as a body class on the page |
oa_core_add_node_class | Add the node layout as a body class to the page |
oa_core_add_taxonomy_class | Add the taxonomy layout as a body class to the page |
oa_core_batch_set | Wrapper around |
oa_core_body_classes | Helper function to return/add classes to the body tag |
oa_core_create_node | Create a new content node within a space/section |
oa_core_create_term | Helper function to create a new Section or Space Type taxonomy term |
oa_core_entity_build_display | Convert known entities in to a simple array of title and picture. |
oa_core_features_template_revert | Helper to combine multiple calls in OA to features_template_revert() to just a single call. |
oa_core_get_all_groups | Return a list of all Groups |
oa_core_get_banner_default | Helper function to return a file object that represents the default site banner. |
oa_core_get_bootstrap_version | Returns the version of Bootstrap (via Radix) being used Returns 2 or 3 for Bootstrap, returns 0 if no Bootstrap is used |
oa_core_get_entity_vocabs | Return vocabularies assigned to a specific content type |
oa_core_get_groups_by_parent | Get child Spaces or Groups. |
oa_core_get_groups_by_user | Get the group IDs of all the groups a user is an approved member of. |
oa_core_get_groups_by_user_access | Get the group IDs and Titles of all the groups a user is an approved member of. |
oa_core_get_group_from_node | Helper function to return the id of the space/group that contains the nid node |
oa_core_get_group_users_for_space | Return the users that are the intersection of Group and Space membership. |
oa_core_get_inherited_users_for_space | Get the users that are in a space, including inherited users. |
oa_core_get_last_word | Helper to return last word of a string |
oa_core_get_membership_nodes | Helper to get membership nids for a group. |
oa_core_get_nids_title_matching | Get the nids => title matching criteria from a set of nids. |
oa_core_get_parents | Get parent Spaces and Groups. |
oa_core_get_parents_with_titles | Get parent Spaces and Groups. |
oa_core_get_public_spaces | Get a list of public spaces. |
oa_core_get_section_content | Get all the content within a particular Section. |
oa_core_get_section_context | Returns the current section id context |
oa_core_get_space_context | Returns the current space id context |
oa_core_get_space_home_context | Returns the current space id context Returns zero if on the site home page |
oa_core_get_summary | Helper function to return the summary of a node or the trimmed body text |
oa_core_get_titles | Helper function to retrieve an array of node titles and links given a list of node ids |
oa_core_get_top_parents | Get top-level spaces/groups (no parents of same bundle type) |
oa_core_get_users_for_space | Get the users that are in a space (excluding inherited users). |
oa_core_get_user_spaces Deprecated | Return a list of space ids that a user belongs to. |
oa_core_get_vocab_terms | Return terms for a list of vocabularies |
oa_core_list_content_types | Returns a list of group-content types throughout the system. List leaves out content types excluded by other modules/apps. |
oa_core_member_of_team | Determine if a user is a member of a team |
oa_core_realname | Return the name of a user |
oa_core_replace_tags | Helper function to replace HTML tags in $str. |
oa_core_revert_template | This is called when all modules, using oa_core_features_template_revert(), have registered for a template revert. |
oa_core_section_is_public | Returns TRUE if the section $node has open access to public |
oa_core_sort_users_by_name | Sort function to sort users by name. |
oa_core_space_sections | Return a list of sections within a space Uses access control, so only sections with access are returned |
oa_core_taxonomy_vocabulary | Returns the named vocabulary Cannot use core taxonomy_vocabulary_machine_name_load because og_vocab alters the query to only return vocabs associated with the current space see issue https://drupal.org/node/2270529 |
oa_core_trim_text | Trim text to a specified length Similar to views_trim_text or the Smart Trim module but includes consistent options for removing tags needed by OA2 |
oa_core_truncate_list | truncate a list to a given number of items with optional More link |
oa_og_subgroups_check_access | Check access for this group's parents. |
oa_user_access | Replacement for og_user_access. |
oa_user_access_nids | Determine whether a user has a given privilege for groups. |
_oa_core_get_all_children | Helper function to return children of a list of parents. |
Constants
Name![]() |
Description |
---|---|
OA_CACHE_GET_PARENTS | |
OA_CACHE_GROUPS_BY_PARENT | |
OA_CACHE_GROUPS_BY_USER | Name of various caches. |
OA_CACHE_GROUPS_BY_USER_ACCESS | |
OA_CACHE_TOP_PARENTS |