<?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'];

  // Add the node's bundle to the IDs of inputs, to avoid having duplicate IDs.
  $id_suffix = '-' . str_replace('_', '-', $bundle);

  foreach(element_children($form) as $field_name) {
    if (!empty($form[$field_name]['#language'])) {
      $language = $form[$field_name]['#language'];

      if (!empty($form[$field_name][$language][0]['value']['#id'])) {
        $form[$field_name][$language][0]['value']['#id'] .= $id_suffix;
      }

      if (!empty($form[$field_name][$language][0]['default']['#id'])) {
        $form[$field_name][$language][0]['default']['#id'] .= $id_suffix;
      }
    }
  }

  // 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'),
  );
}
