You are here

commons_bw.module in Drupal Commons 7.3

File

modules/commons/commons_bw/commons_bw.module
View source
<?php

/**
 * @file
 * Code for the Commons Browsing Widget feature.
 */
include_once 'commons_bw.features.inc';

/**
 * Implements hook_hook_info().
 */
function commons_bw_hook_info() {
  $hooks = array(
    'commons_bw_group_widget',
    'commons_bw_create_all_widget',
  );
  return array_fill_keys($hooks, array(
    'group' => 'commons',
  ));
}

/**
 * Implements hook_system_info_alter().
 */
function commons_bw_system_info_alter(&$info, $file, $type) {

  // Commons BW dynamically adds the title_field field to content types that
  // request it.
  // We must add a corresponding line for each field instance to commons_bw.info
  // so that Features is aware of the instance and can successfully revert the
  // field_instance component back to its default state.
  if ($file->name == 'commons_bw') {
    foreach (node_type_get_types() as $node_type_object) {
      $node_type = $node_type_object->type;
      if (commons_bw_node_auto_title_instance($node_type)) {
        $info['features']['field_instance'][] = "node-{$node_type}-title_field";
      }
    }
  }

  // Dynamically adding a field to a content type results in features
  // automatically detecting Commons BW as a dependency.
  // We manually exclude the dependency in order to prevent the content type
  // modules from appearing overridden and to allow them to be used
  // independently of Commons BW.
  $node_types =& drupal_static(__FUNCTION__);
  if (!isset($node_types)) {
    foreach (module_implements('node_info') as $module) {
      $node_types[$module] = call_user_func($module . '_node_info');
    }
  }
  if (isset($node_types[$file->name])) {
    foreach ($node_types[$file->name] as $node_type => $node_info) {
      if (commons_bw_node_auto_title_instance($node_type)) {
        $info['features_exclude']['dependencies']['commons_bw'] = 'commons_bw';
      }
    }
  }
}

/**
 * Implements hook_modules_enabled().
 */
function commons_bw_modules_enabled($modules) {

  // Ensure that dynamically added title_field fields are in the default state
  // when modules that provide content types are enabled.
  foreach ($modules as $module) {
    if (module_hook($module, 'node_info')) {
      features_revert(array(
        'commons_bw' => array(
          'field_instance',
        ),
      ));
    }
  }
}

/**
 * Implements hook_forms().
 *
 * The bundle is added to the partial node form ID, to prevent duplicate IDs on
 * the same page, but all of the partial forms are built with the same function.
 */
function commons_bw_forms($form_id, $args) {
  $forms = array();
  if (strpos($form_id, 'commons_bw_partial_node_form__') === 0) {
    $forms[$form_id] = array(
      'callback' => 'commons_bw_partial_node_form',
    );
  }
  return $forms;
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function commons_bw_form_views_exposed_form_alter(&$form, &$form_state, $form_id) {

  // Implements tweaks to exposed filters and sorts per the Commons designs.
  if (strpos($form['#id'], 'views-exposed-form-commons-bw') === 0) {

    // Remove the sort order (eg, descending vs ascending).
    $form['sort_order']['#access'] = FALSE;
    $form['sort_by']['#title'] = t('Sorted by');
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Add a setting to group content fields, to determine whether they will be
 * displayed on the mini node form of the browsing widget.
 */
function commons_bw_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) {
  if (!og_is_group_content_type($form['instance']['entity_type']['#value'], $form['instance']['bundle']['#value'])) {
    return;
  }

  // See if we're building for the first time, or getting pre-saved values.
  $field_name = $form['#field']['field_name'];
  if (!empty($form_state['field'][$field_name][LANGUAGE_NONE]['instance']['display_in_partial_form'])) {
    $display_default = $form_state['field'][$field_name][LANGUAGE_NONE]['instance']['display_in_partial_form'];
  }
  else {
    if (isset($form_state['build_info']['args'][0]['display_in_partial_form'])) {
      $display_default = $form_state['build_info']['args'][0]['display_in_partial_form'];
    }
    else {
      $display_default = FALSE;
    }
  }
  $form['instance']['display_in_partial_form'] = array(
    '#type' => 'checkbox',
    '#title' => t('Display in the browsing widget mini-form'),
    '#default_value' => $display_default,
  );
}

/**
 * Partial node form for the browsing widget.
 */
function commons_bw_partial_node_form($form, &$form_state, $bundle, $group_id = NULL) {
  global $user;
  global $language;
  if (!$group_id) {

    // Reset the og_field_widget_form cache because otherwise it ignores
    // multiple tries to render the same group audience widget (We have the
    // same group audience widget on the All and Posts tabs, when displaying
    // this form without group context).
    drupal_static_reset('og_field_widget_form');
  }
  if ($group_id) {
    $form_state['group_id'] = $group_id;
  }
  $instances = field_info_instances('node', $bundle);

  // Remove all fields except those marked as "display_in_partial_form".
  foreach ($instances as $field_name => $instance) {
    if (empty($instance['display_in_partial_form'])) {
      unset($instances[$field_name]);
    }
  }

  // Make sure there's a field left to display.
  if (empty($instances)) {
    return $form;
  }

  // Create a dummy node for field_attach_form().
  $node = new stdClass();
  $node->type = $bundle;
  node_object_prepare($node);
  if (module_exists('locale')) {
    if (locale_multilingual_node_type($node->type)) {
      $node->language = $language->language;
    }
    else {
      $default = language_default();
      $node->language = $default->language;
    }
  }
  else {
    $node->language = LANGUAGE_NONE;
  }
  field_attach_form('node', $node, $form, $form_state, entity_language('node', $node));
  foreach (element_children($form) as $field_name) {
    if (empty($instances[$field_name])) {
      $form[$field_name]['#access'] = FALSE;
    }
  }
  if (!empty($form['#metatags'])) {
    unset($form['#metatags']);
  }

  // When not in a group context, enable the group audience widget.
  $form[OG_AUDIENCE_FIELD]['#weight'] = 100;
  $form[OG_AUDIENCE_FIELD]['#access'] = !$group_id;

  // Add a default form title.
  $form['title'] = array(
    '#markup' => t('Create content'),
    '#weight' => -100,
  );

  // Display the user's picture.
  $wrapper = entity_metadata_wrapper('user', $user);
  $path = empty($user->picture) ? variable_get('user_picture_default') : $wrapper
    ->value()->picture->uri;
  $form['user_picture'] = array(
    '#theme' => 'image_style',
    '#style_name' => '50x50_avatar',
    '#path' => $path,
    '#prefix' => '<div class="user-picture">',
    '#suffix' => '</div>',
    '#weight' => -20,
  );
  $form['actions'] = array(
    '#type' => 'actions',
    '#weight' => 200,
  );
  $form['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save'),
  );

  // Attach the browsing widget JS and give it a higher weight than
  // quicktabs.js.
  $form['#attached']['js'][] = array(
    'data' => drupal_get_path('module', 'commons_bw') . '/js/partial_node_form.js',
    'type' => 'file',
    'weight' => 100,
  );

  // Add in some descriptive classes for css down the line.
  $form['#attributes']['class'][] = 'node';
  $form['#attributes']['class'][] = 'commons-bw-partial-node-form';
  $form['#attributes']['class'][] = 'commons-bw-partial-node-form-' . $bundle;

  // Add a link to the full node form.
  $form['full_form'] = array(
    '#theme' => 'link',
    '#text' => t('Go to full form'),
    '#path' => 'node/add/' . str_replace('_', '-', $bundle),
    '#options' => array(
      'attributes' => array(
        'class' => array(
          'full-form',
        ),
      ),
      'html' => FALSE,
    ),
    '#weight' => 100,
  );
  if ($group_id) {
    $form['full_form']['#options']['query'] = array(
      OG_AUDIENCE_FIELD => $group_id,
    );
  }

  // Add the commons_bw after build first, in case other pre-renders needs need
  // to address fields by there CSS ID.
  array_unshift($form['#pre_render'], 'commons_bw_partial_node_form_after_build');
  $form['#validate'][] = 'commons_bw_partial_node_form_validate';
  return $form;
}

/**
 * After-build call-back. See commons_bw_partial_node_form().
 */
function commons_bw_partial_node_form_after_build($form) {
  $bundle = $form['#bundle'];

  // Set the form action to the form's tab.
  $tabs = commons_bw_get_tab_definitions();

  // Search for the tab displaying the current bundle.
  foreach ($tabs as $tab_id => $settings) {
    if ($settings['bundle'] == $bundle) {
      break;
    }
  }
  $form['#action'] = url(current_path(), array(
    'query' => array(
      'qt-commons_bw' => $tab_id,
    ),
  ));
  return $form;
}

/**
 * Validation handler; Attach the node validation to the partial node form.
 */
function commons_bw_partial_node_form_validate($form, $form_state) {
  $node = $form['#entity'];
  field_attach_validate('node', $node);
  node_validate($node, $form, $form_state);
  if ((!module_exists('commons_trusted_contacts') || module_exists('commons_trusted_contacts') && !module_exists('og_access')) && empty($form_state['group_id']) && empty($form_state['values'][OG_AUDIENCE_FIELD][LANGUAGE_NONE][0])) {
    form_set_error(OG_AUDIENCE_FIELD, t('Please enter one or more groups where this content will be posted.'));
    return FALSE;
  }
}

/**
 * Submit handler; Create a node from the partial node form.
 */
function commons_bw_partial_node_form_submit($form, $form_state) {
  $node = $form['#entity'];
  node_submit($node);

  // Mark the node as created with the partial form
  $node->partial_node_form = TRUE;
  field_attach_submit('node', $node, $form, $form_state);
  $wrapper = entity_metadata_wrapper('node', $node);

  // If the node has a body and doesn't has a title, create a title from the
  // body.
  if ((empty($wrapper->title_field) || !$wrapper->title_field
    ->value()) && empty($node->title)) {
    if (!empty($wrapper->body) && $wrapper->body
      ->value()) {
      $title = htmlspecialchars_decode($wrapper->body->value
        ->value());

      // Strip tags and whitespaces.
      $title = preg_replace('/[\\t\\n\\r\\0\\x0B]/', '', strip_tags($title));

      // Shorten the title.
      $node->title = truncate_utf8($title, 30, TRUE, TRUE);
    }
  }

  // Set the group audience.
  if (!empty($form_state['group_id'])) {
    $wrapper->{OG_AUDIENCE_FIELD}
      ->set(array(
      $form_state['group_id'],
    ));
  }
  $node->form_state = $form_state;
  $wrapper
    ->save();

  // Notify about the node creation.
  $arguments = array(
    '@type' => node_type_get_name($node),
    '%title' => $node->title,
  );
  drupal_set_message(t('@type %title has been created.', $arguments));
}

/**
 * Get a list of modules that add content to a particular type of widget.
 *
 * The only currently supported widget type is 'group', but this
 * could be extended to support other entities.
 *
 * @param $widget_type
 *   An optional type of widget to restrict results to, defaults to 'group'.
 *
 * @return array
 *   An array of return values of the hook implementations.
 */
function commons_bw_get_tab_definitions($widget_type = 'group') {
  $hook_name = 'commons_bw_' . $widget_type . '_widget';
  $tabs = module_invoke_all($hook_name);
  drupal_alter($hook_name, $tabs);
  return $tabs;
}

/**
 * Helper function to determine whether Commons_BW should define a title field
 * instance on behalf of a content type.
 *
 * @param $node_type
 *   The type of the node to check auto title settings for.
 *
 * @return boolean
 *   The value of the auto title setting if available, TRUE otherwise.
 */
function commons_bw_node_auto_title_instance($node_type) {
  $commons_groups_entity_types = commons_groups_get_group_content_entity_types();
  return isset($commons_groups_entity_types['node'][$node_type]['auto_title_instance']) ? $commons_groups_entity_types['node'][$node_type]['auto_title_instance'] : TRUE;
}

/**
 * Provides a styled content creation dropdown widget for the 'all' tab of the
 * group homepage browsing widget.
 *
 * @param $group
 *   The group node associated with the group homepage.
 *
 * @return string
 *   The content creation dropdown widget HTML.
 */
function commons_bw_create_all_widget($group) {
  $links = array();

  // Collect definitions from implementing modules.
  $items = module_invoke_all('commons_bw_create_all_widget', $group);
  uasort($items, 'element_sort');
  foreach ($items as $module => $item) {
    $links[] = $item['link'] . ' ' . $item['text'];

    // Populate the default content creation link.
    if (isset($item['default']) && $item['default']) {
      $default = $item;
    }
  }
  $output = '';
  if (!empty($default)) {
    $output .= $default['link'] . '<a class="commons-bw-create-choose"><span></span></a>';
  }
  $output .= '<div class="commons-bw-create-choose-bg"></div><div class="commons-bw-create-choose-holder">' . theme('item_list', array(
    'items' => $links,
    'type' => 'ul',
    'attributes' => array(
      'class' => 'commons-bw-create-all-widget-types',
    ),
  )) . '</div>';
  return $output;
}

/**
 * Generate a renderable group widget.
 *
 * @param $group
 *   An optional group node to be used as a tab and views argument.
 *
 * @return array
 *   An array in the format expected by drupal_render().
 */
function commons_bw_generate_group_widget($group = NULL) {

  // Prepare an array of default quicktabs settings.
  $settings = array(
    'style' => 'Commons Pills',
    'ajax' => FALSE,
    'html' => TRUE,
  );

  // Load the browsing widget tab definitions.
  $tabs = commons_bw_get_tab_definitions('group');
  foreach ($tabs as $machine_name => $tab_settings) {

    // Populate the group argument.
    $tabs[$machine_name]['args'] = $group ? $group->nid : 0;

    // Add the result count to the title for 'view' tabs.
    if ($tab_settings['type'] == 'view') {

      // Get the view specified by the tab settings.
      $view = views_get_view($tab_settings['vid']);

      // If the tab specified a view display use it, otherwise the view will be
      // rendered using the default display.
      if (isset($tab_settings['display'])) {
        $view
          ->set_display($tab_settings['display']);
      }

      // If the tab references a group, set it as a tab argument.
      if ($group) {
        $view
          ->set_arguments(array(
          $group->nid,
        ));
      }
      $view->display_handler->options['filters']['flagged']['value'] = 'All';
      $view->get_total_rows = TRUE;
      $view
        ->execute();

      // Append the result count to the tab title.
      $tabs[$machine_name]['title'] = $tabs[$machine_name]['title'] . ' <span class="commons-bw-result-count">' . $view->total_rows . '</span>';
    }

    // Use the current tab as the quicktabs default if the tab settings specify.
    if (!empty($tabs[$machine_name]['default'])) {
      $settings['default_tab'] = $machine_name;
    }
  }
  return quicktabs_build_quicktabs('commons_bw', $settings, $tabs);
}

/**
 * Implements hook_quicktabs_tabstyles().
 */
function commons_bw_quicktabs_tabstyles() {
  $path = drupal_get_path('module', 'commons_bw');
  return array(
    $path . '/plugins/quicktabs_styles/commons_pills/commons_pills.css' => t('Commons Pills'),
    $path . '/plugins/quicktabs_styles/commons_tabs/commons_tabs.css' => t('Commons Tabs'),
  );
}

Functions

Namesort descending Description
commons_bw_create_all_widget Provides a styled content creation dropdown widget for the 'all' tab of the group homepage browsing widget.
commons_bw_forms Implements hook_forms().
commons_bw_form_field_ui_field_edit_form_alter Implements hook_form_FORM_ID_alter().
commons_bw_form_views_exposed_form_alter Implements hook_form_FORM_ID_alter().
commons_bw_generate_group_widget Generate a renderable group widget.
commons_bw_get_tab_definitions Get a list of modules that add content to a particular type of widget.
commons_bw_hook_info Implements hook_hook_info().
commons_bw_modules_enabled Implements hook_modules_enabled().
commons_bw_node_auto_title_instance Helper function to determine whether Commons_BW should define a title field instance on behalf of a content type.
commons_bw_partial_node_form Partial node form for the browsing widget.
commons_bw_partial_node_form_after_build After-build call-back. See commons_bw_partial_node_form().
commons_bw_partial_node_form_submit Submit handler; Create a node from the partial node form.
commons_bw_partial_node_form_validate Validation handler; Attach the node validation to the partial node form.
commons_bw_quicktabs_tabstyles Implements hook_quicktabs_tabstyles().
commons_bw_system_info_alter Implements hook_system_info_alter().