There are several patterns that modules exposing a content type to Drupal Commons 3.x should expect to do.

I've been experimenting with Commons_Posts in a sandbox to get a sense of what these are. Checkout the latest status of the codebase at
http://drupalcode.org/sandbox/ezrag/1721532.git/tree/refs/heads/7.x-3.x

Here's an initial list:

- Add your content type to the Commons browsing widget:

Only local images are allowed.

Group home pages include a combination browsing/content creation widget.

Content types (Except for Events) should add themselves to this browsing widget. Notwithstanding #1782656: Panels exports don't revert, group home pages are panelized and include the browsing widget, which is powered by http://drupal.org/project/commons_bw.

Modules can add a tab to the widget by implementing hook_commons_bw() and returning an array of QuickTabs module formatted tabs.

This allows each content type to have its own unique view/presentation for how it should be displayed within its tab on the group homepage.

For example, Commons posts defines a simple view, and adds it as a tab on the group homepage like so:


/**
* Implements hook_commons_bw_group_widget().
*/
function commons_posts_commons_bw_group_widget() {
  return array(
    'commons_posts' => array(
      'title' => 'Posts',
      'type' => 'view',
      'vid' => 'commons_bw_posts',
      'display' => 'page_1',
      'weight' => 5,
    ),
  );
}


It exports the commons_bw_posts view (which would probably be better namespaced as commons_posts_group_bw).

We also implement hook_views_pre_render() to add the "Create a post" link to the tab for users who have access to do so:


/**
* Implements hook_views_pre_render().
*/
function commons_posts_views_pre_render(&$view) {
  if ($view->name == 'commons_bw_posts' && user_access('Post: Create new content')) {
    $group_id = $view->args[0];
    $view->attachment_before = l('Create a post', 'node/add/post', array('query' => array('og_group_ref' => $group_id)));
  }  
 }

We implement hook_strongarm_alter() in 2 places to integrate Commons content types with other Commons subsystems: Radioactivity, Liking and Groups:

Expose your content type to Commons Radioactivity

We alter the rate_widgets variable so that it is configured to include our content type and comments on that content type. For example:


/**
* Implements hook_strongarm_alter().
*/
 function commons_posts_strongarm_alter(&$items) {
  // Expose the Post content type for 'liking' via the Commons_like module
  // by altering the configuration for the Rate.module widget that it provides.
  if (!empty($items['rate_widgets']->value)) {
    foreach($items['rate_widgets']->value as $key => $widget) {
      if ($widget->name == 'commons_like') {
        if (!in_array('post', $items['rate_widgets']->value[$key]->node_types)) {
          $items['rate_widgets']->value[$key]->node_types[] = 'post';  
        }       
        if (!in_array('post', $items['rate_widgets']->value[$key]->comment_types)) {
          $items['rate_widgets']->value[$key]->comment_types[] = 'post';  
        }
      }
    }
  }

Expose your content type to "Liking" via Commons Like and as group content via Commons Groups

We expose our content type to Commons_radioactivity by overriding the commons_radioactivity_entity types variable. Currently, we also expose content types to Commons_groups in another variable with the same format, so we can do (in the same function):

  // Expose the post content type for integration with Commons Radioactivity
  // and Commons Groups.  
  foreach (array('commons_radioactivity_entity_types', 'commons_groups_entity_types') as $key) {
    if (isset($items[$key])) {  
      $items[$key]->value['node']['post'] = 1;
    }
  }

Note: You may need to revert features after enabling a module that alters a strong-arm value that has been stored to the database. We should consider automatically reverting these components or prompting the user to do so. The intended benefit of using Strongarm vs a simple hook to expose our content types is that it makes it easier to expose the variable to configuration through the UI in the future.

Commons Radioactivity and Commons Groups each looks for its respective variable and add its respective field instance onto the content type that requests it.

As a developer of a module that integrates with Commons that's all you need to know ;). However, if you're interested in seeing how this works in practice, for example, see Commons Radioactivity:

/**
* Implements hook_system_info_alter().
*/
function commons_radioactivity_system_info_alter(&$info, $file, $type) {
  // Commons Radioactivity dynamically adds field_radioactivity to
  // content types that implement commons_radioactivity_field.
  // We must add a corresponding line for each field instance
  // to commons_radioactivity.info so that Features is aware of the instance
  // and can sucessfully revert the field_instance component back
  // to its default state.
  if ($file->name == 'commons_radioactivity') {
    $commons_radioactivity_entity_types = variable_get('commons_radioactivity_entity_types', array());
    if (!empty($commons_radioactivity_entity_types['node'])) {
      foreach ($commons_radioactivity_entity_types['node'] as $type => $status) {
        if ($status) {
          $info['features']['field_instance'][] = "node-$type-field_radioactivity";
        }
      }
    }
  }
}

/**
 * Implements hook_field_default_field_instances().
 */
function commons_radioactivity_field_default_field_instances() {
  // Get a list of content types that should have the Radioactivity field added.
  // Modules can alter this list by implementing hook_strongarm_alter().
  $commons_radioactivity_entity_types = variable_get('commons_radioactivity_entity_types', array());
  if (!empty($commons_radioactivity_entity_types['node'])) {
    $field_instances = array();
    foreach($commons_radioactivity_entity_types['node'] as $type => $status) {
      if ($status) {
        commons_radioactivity_field_definition($field_instances, $type);  
      }
    }
    return $field_instances;  
  }
  
}

Panelized content type landing page as a submodule

It would be ideal for each content type to be panelized, to support customization to the page layout through the UI via Panelizer. To make it easier for site administrators to replace the default Commons panelized pages with their own configuration, each module that defines a content type should ideally export any landing pages or panelized content type pages into its own separate feature as a submodule in the main feature's Drupal.org.project.

As an alternative, we could include the default configuration as part of the main content type feature but encourage folks who define alternative Panels pages to alter the Commons default ones and make them disabled.

Comments

ezra-g’s picture

Status: Active » Needs review
ezra-g’s picture

Issue summary: View changes

Add documentation of Panels exports.

ezra-g’s picture

I added a note about Panelized content types and Panels landing pages.

ezra-g’s picture

Issue summary: View changes

Add note about disabled content type landing pages.

ezra-g’s picture

erikwebb pointed out that we could simplify the developer experience by having a single commons content type hook that modules could use to expose their content type(s) to the various integration points that Commons provides.

So, something like

/**
* Implements hook commons_content_types().
*/
function commons_posts_commons_content_types() {
  return array(
    'post' => array(
      'commons_radioactivity' => 1,
      'commons_groups' => 1,
      'commons_like' => 1,
    );
  );
}

It seems like content types could simply implement this hook, and if they're interacting with the group homepage browsing widget, also implement hook_views_pre_render() and hook_commons_bw() as appropriate.

erikwebb’s picture

This greatly simplifies the documentation as well as the developer experience. Especially for newer Drupal developers, this signifies Commons as more of a distinct, developer-friendly product, rather than requiring several uses of hook_strongarm_alter(), hook_flag_alter(), etc. to implement the equivalent features.

Since we will always know the available features in Commons, new features can be easily added to custom content types with a single line of code.

ezra-g’s picture

Title: Document pattern for creating Commons-compatible group content type features » Simplify pattern for creating Commons-compatible group content type features
Status: Needs review » Active

Thanks for the feedback - Updating the issue title to reflect the new action item here.

ezra-g’s picture

As we've used the current pattern so far, it seems like using a variable to store which content types are group-enabled may not be the best approach. The original motivation for this was because it would provide a natural place for us to add a UI that controls which content types are Radioactivity and Groups-enabled.

However, because of the nature of Strongarm, we've got to do additional feature reverts. For example, Commons posts alters the commons_radioactivity_entity_types variable, but we need to revert the Variable component on Commons radioactivity for this to be recognized. Then, once it is recognized by Commons Radioactivity, we may need to revert the field_instance component to define the field.

As an alternative, we could just use a hook implementation, rather than putting a variable on top of this.

ezra-g’s picture

Title: Simplify pattern for creating Commons-compatible group content type features » Document pattern for creating Commons-compatible group content type features
Anonymous’s picture

ezra-g,
Thanks for this page. It would be really nice to have a way to activate a Content Type in commons through the admin screen but I found it fairly easy to copy commons posts files and update to my Content Types. Thanks!

RobKoberg’s picture

Could we add an alter to affect the $settings and $tabs for quicktabs? I opened an issue with a patch here:

http://drupal.org/node/2003006

Anonymous’s picture

Category: task » support

I dont know if this is the right place to write this... but i'm just approaching tho drupal and followed link landing here...

This means there's a way to add other tabs (wit their own views) to the default group homepage tabbed widget?

can someone explain me how to do it?

thanks, and tell me if this is the wrong place asking for (i will delete the post)

BarisW’s picture

KarlKedrovsky’s picture

Version: » 7.x-3.0

I believe the first code sample should read:

/**
* Implements hook_commons_bw_group_widget().
*/
function commons_posts_commons_bw_group_widget() {
  return array(
    'commons_posts' => array(
      'title' => 'Posts',
      'type' => 'view',
      'vid' => 'commons_bw_posts',
      'display' => 'default',
      'weight' => 5,
    ),
  );
}

Changing the 'display' from 'page_1' to 'default'. Without that change I was getting an access denied error which I believe was the same as what is described in https://drupal.org/node/1855254.

RobKoberg’s picture

Please add the alter so we are not forced to use the commons quicktabs styling. Currently there is not way to affect quicktabs other than basic css which is not sufficient. I don't understand why this hasn't been done yet. It is a simple one line addition that does not affect how it operates if someone is happy with the default behavior and look and feel. Can there at least be a response as to why this won't get done?

ezra-g’s picture

Issue tags: +commons 7.x-3.5 radar

Adding to our 3.*5* radar. Let's finalize a documentation page based on this issue.

charlie charles’s picture

Sorry, where's the file I would I add this code to get a new tab please?

<?php
/**
* Implements hook_commons_bw_group_widget().
*/
function commons_posts_commons_bw_group_widget() {
  return array(
    'commons_posts' => array(
      'title' => 'Posts',
      'type' => 'view',
      'vid' => 'commons_bw_posts',
      'display' => 'page_1',
      'weight' => 5,
    ),
  );
}
?>
BetaTheta’s picture

@post #15, I might be mistaken but I believe you have to put that code into a custom module.

charlie charles’s picture

Thanks BetaTheta

charlie charles’s picture

Hi,where is it in the documentation
how to create a custom module for your own Commons browsing widget please?

https://docs.acquia.com/commons/

charlie charles’s picture

Issue summary: View changes

Correct closing tag.

thatguy’s picture

I am trying to add a tab to the browsing widget but when I made a custom module with this code in the .module file:

/**
* Implements hook_commons_bw_group_widget().
*/
function commons_posts_commons_bw_group_widget() {
  return array(
    'commons_posts' => array(
      'title' => 'Posts',
      'type' => 'view',
      'vid' => 'commons_bw_posts',
      'display' => 'page_1',
      'weight' => 5,
    ),
  );
}

it still doesnt show new tab in the browsing widget. What could I be doing wrong?

ceepeebee’s picture

@thatguy: have a look at the step-by-step guide I wrote a couple of days ago, that should help

btw, you shouldn't use 'commons_posts' as an array key, because it's already taken by commons_posts... and maybe try changing the title as well and use the proper hook name. ;) but I guess, you did that, even though you posted differently, didn't you?

Devin Carlson’s picture

Version: 7.x-3.0 » 7.x-3.x-dev
Issue summary: View changes

I've just released an initial beta of the Commons Browsing Widget User Interface module which provides a basic user interface for configuring the browsing widget tabs, including their settings and associated content types.

I'm hoping that it makes configuring the browsing widget easier for non-technical folks and serves as a reference for developers (which can also just export its configuration if they don't want to write the code themselves).

iaminawe’s picture

Devin - awesome module thank you - simplifies this process significantly of integrating other content types into Commons.