Defining Organic Groups Context on Custom Pages

Organic Groups is one of the most powerful social networking modules for Drupal. It allows users to maintain groups. The managers of these groups can control who can join, or post or view content in their groups.

Essentially, these groups can act as microsites inside the site; they have their own user list, content, and access control. For example, a football site might want to maintain groups for football teams, and post news and announcements that are relevant to the team in the team's group. The players would then join the OG group of their team. When players visit the group's page, they would see only that team's content (nodes, technically).

From usability's perspective, it is beneficial for the users to constantly see some indication of what group they are in. They may get lost when, for example, they click on a post in a group, and the page they arrive at doesn't show any indication of the group that they arrived from.

This is where OG Context module comes into play. As part of Organic Groups, OG Context module determines which group the user is in. By 'being in a group' we mean looking at a page that is relevant to the group. When the visitor is viewing a post, OG Context checks which group that post belongs to, or, when the user is about to create content, OG Context checks which group it will be posted to. The developer can then use this information in Views or in code. A common way to use this is to put group relevant content in the sidebars. For example, we might want to see a list of users that are members of the current group (this view is provided by OG Extras module, by the way). So, if the user is looking at a piece of content (a node) that was posted in Group A, they see a list of Group A members. If they are looking at a node that was posted in Group B, or looking at Group B itself (which is, very often, also a node), they see a list of Group B members.

The example above works because OG Context module extracts the group data from the post being viewed, and passed that along to the block that shows the user list. But if we have a custom page that we have declared with hook_menu, that page will not be tied to any group, so the user list block will not know what users to show. Back to our football site example, let's say we would like to build a page that shows a summary of certain statistics about the team's matches. We declare a path for our page using hook_menu in our .module file, as the code below illustrates. Let's call our module custom_page.

 

/**
 * Implements hook_menu().
 */
function custom_page_menu() {
  $items = array();

  $items['summary/%node'] = array(
    'title' => 'Summary page',
    'page callback' => 'custom_page_summary_page',
    'page arguments' => array(1),
    'access arguments' => array('access content'),
  );

  return $items;
}

 

The first argument in the path is the node id of the group. Let's suppose the page callback does some calculation and displays the desired summary statistics about the group (not illustrated here). For the sake of completeness, let's define the example page callback as follows. We will just return a sample text with the group's name in it in the example.

 

/**
 * Page callback for the summary page.
 */
function custom_page_summary_page($node) {
  $build = array();

  // Validate the argument.
  if (!og_is_group('node', $node)) {
    return MENU_NOT_FOUND;
  }

  // Build content here
  $build['test'] = array(
    '#markup' => 'Summary for group "' . check_plain($node->title) . '" goes here...',
  );

  return $build;
}

 

Let's suppose we have set up the group user list block that was discussed in the example above. The block is provided by OG Extras module, so basically, all we need to do is enable the block on the Drupal blocks configuration page. If we go to the summary page of an imaginary test group with node id 51, our page looks as follows:

It does not show the group user list block (even though the block is enabled), because OG Context module has no idea about which group we are displaying data about, and therefore cannot pass this data along to the block. The group id happens to be in the url, but OG Context cannot know where to look for it.

We need to tell OG Context what group our page is related to. The module provides a hook that serves this exact purpose: hook_og_context_negotiation_info. Using that hook, we can register a callback function that tells OG Context the information it needs when our page is being displayed. The implementation looks as follows.

 

/**
 * Implements hook_og_context_negotiation_info().
 */
function custom_page_og_context_negotiation_info() {
  $providers = array();

  $providers['custom_page'] = array(
    'name' => t('Custom summary page'),
    'description' => t('Determine context from the custom summary page being viewed.'),
    'callback' => 'custom_page_context_callback',
    'menu path' => array('summary/%'),
  );

  return $providers;
}

 

Basically, we need to return an array of 'providers' that OG Context can ask to help it determine the group context. The 'name' and 'description' elements define text that will show up on the user interface in OG Context configuration. The 'callback' element defines the function that will be called, and the value of 'menu path' restricts our context provider so that it will only be called on the path that is listed.

Essentially, we are telling OG Context to call our function when the user views the page at summary/%, and use the function's return value when determining the group context. We implement the callback as shown below.

 

/**
 * Group context callback. Helps determine the group context.
 */
function custom_page_context_callback() {
  $context = array();

  // We are at summary/%
  $group_node = node_load(arg(1));
  
  // This function may get called even when summary/%
  // returns MENU_NOT_FOUND, so validation is needed here.
  if (!og_is_group('node', $group_node)) {
    return;
  }

  // Context is keyed by the group's entity type.
  $context['node'][] = $group_node->nid;

  return $context;
}

 

The implementation is very straightforward: we grab the node id from the path, and, after validating that the node is indeed a group, we pass it along to OG Context in the returned array. The return value needs to be an array of arrays. The keys in the outer array are group entity types, which is almost always 'node', and the elements of this array are arrays of group id's.

Now we have our context provider in place, but it is not active yet. It needs to be enable on the user interface. OG Context's configuration page is at admin/config/group/context. Going to that page, we should see the name of the context provider that we just implemented among other built-in ones. The following picture illustrates how that configuration page looks like.

Enabling our context provider, and going back to our custom page, we should now see the user list of the group that we are viewing the summary of. As a result of our work, OG Context asks our module for group data, and passes it to the user list block, so that the block knows what users it should display.