You are here

og_vocab.module in OG Vocabulary 7

Same filename and directory in other branches
  1. 5 og_vocab.module
  2. 6 og_vocab.module

Give each group its own system controlled vocabularies.

File

og_vocab.module
View source
<?php

/**
 * @file
 * Give each group its own system controlled vocabularies.
 */

/**
 * OG-vocab default field name.
 */
define('OG_VOCAB_FIELD', 'og_vocabulary');

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

  // Add our own autocomplete callback to pass also the group and
  // vocabulary info.
  $items['og-vocab/autocomplete/single/%entity_object'] = array(
    'load arguments' => array(
      'og_vocab',
    ),
    'title' => 'Entity Reference Autocomplete',
    'description' => 'Autocomplete a reference for the vocabulary and the group',
    'page callback' => 'og_vocab_autocomplete_callback',
    'page arguments' => array(
      2,
      3,
    ),
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  $items['og-vocab/autocomplete/tags/%entity_object'] = array(
    'load arguments' => array(
      'og_vocab',
    ),
    'title' => 'Entity Reference Autocomplete',
    'description' => 'Autocomplete a reference for the vocabulary and the group',
    'page callback' => 'og_vocab_autocomplete_callback',
    'page arguments' => array(
      2,
      3,
    ),
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  $items['admin/config/group/og-vocab'] = array(
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'og_vocab_admin_settings',
    ),
    'title' => 'OG vocabulary settings',
    'access arguments' => array(
      'administer group',
    ),
    'description' => 'OG vocabulary module settings.',
    'file' => 'includes/og_vocab.admin.inc',
  );
  return $items;
}

/**
 * Implements hook_menu_alter().
 */
function og_vocab_menu_alter(&$callbacks) {

  // Array with menu items, keyed by the ones we need to copy, and the value
  // is the destination menu item.
  $items = array(
    'taxonomy' => array(
      'argument position' => FALSE,
    ),
    'taxonomy/add' => array(
      'argument position' => FALSE,
    ),
    'taxonomy/%taxonomy_vocabulary_machine_name' => array(),
    'taxonomy/%taxonomy_vocabulary_machine_name/list' => array(),
    'taxonomy/%taxonomy_vocabulary_machine_name/edit' => array(),
    'taxonomy/%taxonomy_vocabulary_machine_name/add' => array(),
  );
  $path = drupal_get_path('module', 'taxonomy');
  foreach ($items as $key => $item) {
    $item += array(
      'argument position' => 2,
    );
    $item_path = 'group/%/%/admin/' . $key;
    $original_path = 'admin/structure/' . $key;
    $callbacks[$item_path] = $callbacks[$original_path];
    $callbacks[$item_path]['access callback'] = 'og_vocab_vocabulary_access';
    $access_arguments = array(
      1,
      2,
      'administer taxonomy',
    );
    if ($item['argument position']) {
      $argument_position = 3 + $item['argument position'];

      // Replace the last page argument if it is numeric.
      if (!empty($callbacks[$original_path]['page arguments'])) {
        $page_arguments = $callbacks[$original_path]['page arguments'];
        $pop = array_pop($page_arguments);
        if (is_numeric($pop)) {
          $page_arguments[] = $argument_position;
        }
        $callbacks[$item_path]['page arguments'] = $page_arguments;
      }

      // Set the access arguments.
      $access_arguments[] = $argument_position;

      // Set the title argument.
      if (!empty($callbacks[$item_path]['title arguments'])) {
        $callbacks[$item_path]['title arguments'] = array(
          'taxonomy_vocabulary',
          $argument_position,
        );
      }
    }
    $callbacks[$item_path]['access arguments'] = $access_arguments;
    $callbacks[$item_path]['file path'] = $path;
  }

  // Change the access callback for taxonomy/term/%taxonomy_term/edit
  $callbacks['taxonomy/term/%taxonomy_term/edit']['access callback'] = 'og_vocab_taxonomy_term_edit_access';
  if (variable_get('og_vocab_term_page_access', FALSE)) {
    $callbacks['taxonomy/term/%taxonomy_term']['access callback'] = 'og_vocab_term_page_access';
    $callbacks['taxonomy/term/%taxonomy_term']['access arguments'] = array(
      2,
    );
    $callbacks['taxonomy/term/%taxonomy_term/feed']['access callback'] = 'og_vocab_term_page_access';
    $callbacks['taxonomy/term/%taxonomy_term/feed']['access arguments'] = array(
      2,
    );
  }
}

/**
 * Determince access to term page if term belongs to a vocabulary owned by
 * OG-vocab.
 *
 * @param $term
 *   The taxonomy term object.
 */
function og_vocab_term_page_access($term) {
  if (!user_access('access content')) {
    return;
  }
  if (!($relation = og_vocab_relation_get($term->vid))) {

    // Term doesn't belong to OG-vocab
    return TRUE;
  }

  // Check if user has access to the group.
  $group = entity_load_single($relation->group_type, $relation->gid);
  return entity_access('view', $relation->group_type, $group);
}

/**
 * Implements hook_og_ui_get_group_admin()
 */
function og_vocab_og_ui_get_group_admin($group_type, $gid) {
  $items = array();
  if (og_user_access($group_type, $gid, 'administer taxonomy')) {
    $items['taxonomy'] = array(
      'title' => t('Taxonomy'),
      'description' => t('Show vocabularies related to this group.'),
      'href' => 'admin/taxonomy',
    );
  }
  return $items;
}

/**
 * Implements hook_og_permissions().
 */
function og_vocab_og_permission() {
  $permissions = array(
    'administer taxonomy' => array(
      'title' => t('Administer vocabularies and terms'),
    ),
    'edit terms' => array(
      'title' => t('Edit terms in group'),
    ),
    'delete terms' => array(
      'title' => t('Delete terms in group'),
    ),
  );
  return $permissions;
}

/**
 * Implements hook_entity_info().
 */
function og_vocab_entity_info() {
  $items['og_vocab'] = array(
    'label' => t('OG vocab'),
    'label callback' => 'og_vocab_label',
    'uri callback' => 'og_vocab_uri',
    'controller class' => 'EntityAPIController',
    'entity class' => 'OgVocab',
    'base table' => 'og_vocab',
    'fieldable' => TRUE,
    'entity keys' => array(
      'id' => 'id',
    ),
    'bundles' => array(
      'og_vocab' => array(
        'label' => t('OG vocab'),
      ),
    ),
    'module' => 'og_vocab',
  );
  return $items;
}

/**
 * Label callback.
 *
 * Return the taxonomy name for privleged users, otherwise the OG vocab ID.
 */
function og_vocab_label($og_vocab) {
  $relation = og_vocab_relation_get($og_vocab->vid);
  if (og_user_access($relation->group_type, $relation->gid, 'administer taxonomy')) {
    $entity_info = entity_get_info($og_vocab->entity_type);
    $vocab = taxonomy_vocabulary_load($og_vocab->vid);
    $params = array(
      '@id' => $og_vocab->id,
      '@bundle' => $entity_info['bundles'][$og_vocab->bundle]['label'],
      '@name' => $vocab->name,
    );
    return t('@bundle bundle in @name vocabulary (OG Vocabulary ID @id)', $params);
  }
  return t('OG Vocabulary ID @id', array(
    '@id' => $og_vocab->id,
  ));
}

/**
 * URI callback.
 *
 * Return the link of admin/structure/taxonomy for privileged users,
 * otherwise the group's admin link. If user can't access any of these,
 * return nothing.
 */
function og_vocab_uri($og_vocab) {
  $url = array();
  $vocab = taxonomy_vocabulary_load($og_vocab->vid);
  if (user_access('administer taxonomy')) {

    // URL to taxonomy administration.
    $url = array(
      'path' => "admin/structure/taxonomy/{$vocab->machine_name}/edit",
    );
  }
  else {
    $relation = og_vocab_relation_get($og_vocab->vid);
    if (og_user_access($relation->group_type, $relation->gid, 'administer taxonomy')) {

      // URL to taxonomy administration in the group context.
      $url = array(
        'path' => "group/{$relation->group_type}/{$relation->gid}/admin/taxonomy/{$vocab->machine_name}/edit",
      );
    }
  }
  if ($url) {
    $entity_type = $og_vocab->entity_type;
    $bundle = $og_vocab->bundle;
    $url['options']['fragment'] = drupal_clean_css_identifier("og-vocab-{$entity_type}-{$bundle}");
  }
  return $url;
}

/**
 * Implements hook_migrate_api().
 */
function og_vocab_migrate_api() {
  $api = array(
    'api' => 2,
  );
  return $api;
}

/**
 * Helper to easily create views-queries.
 */
function og_vocab_create_og_vocab($vid, $entity_type, $bundle, $field_name = OG_VOCAB_FIELD, $settings = array()) {
  $values = array(
    'vid' => $vid,
    'entity_type' => $entity_type,
    'bundle' => $bundle,
    'field_name' => $field_name,
    'settings' => $settings,
  );
  $values['settings'] += array(
    'required' => FALSE,
    'widget_type' => 'options_select',
    'cardinality' => FIELD_CARDINALITY_UNLIMITED,
  );
  return entity_create('og_vocab', $values);
}

/**
 * Get OG vocabulary settings by vocabulary ID.
 */
function og_vocab_load_og_vocab($vid, $entity_type, $bundle, $field_name = NULL, $defaults = FALSE) {
  $query = new EntityFieldQuery();
  $query
    ->entityCondition('entity_type', 'og_vocab')
    ->propertyCondition('vid', $vid)
    ->propertyCondition('entity_type', $entity_type)
    ->propertyCondition('bundle', $bundle)
    ->range(0, 1);
  if (!empty($field_name)) {
    $query
      ->propertyCondition('field_name', $field_name);
  }
  $result = $query
    ->execute();
  if (empty($result['og_vocab'])) {
    if (!$defaults) {
      return;
    }
    if (empty($field_name)) {

      // Get the field name from the bundle, or default to OG_VOCAB_FIELD.
      $field_names = og_vocab_get_og_vocab_fields($entity_type, $bundle);
      $field_name = !empty($field_names) ? key($field_names) : OG_VOCAB_FIELD;
    }
    return og_vocab_create_og_vocab($vid, $entity_type, $bundle, $field_name);
  }
  $id = key($result['og_vocab']);
  return entity_load_single('og_vocab', $id);
}

/**
 * Implements hook_og_fields_info().
 */
function og_vocab_og_fields_info() {
  $items[OG_VOCAB_FIELD] = array(
    'type' => array(
      'group content',
    ),
    'description' => t('Complex widget to reference taxonomy terms related to accessible groups.'),
    // Allow multiple OG-vocab fields on the same bundle.
    'multiple' => TRUE,
    'field' => array(
      'settings' => array(
        'handler' => 'base',
        'target_type' => 'taxonomy_term',
        'handler_settings' => array(
          'target_bundles' => array(),
          'behaviors' => array(
            'og_vocab' => array(
              'status' => TRUE,
            ),
            'taxonomy-index' => array(
              'status' => TRUE,
            ),
          ),
        ),
      ),
      'field_name' => OG_VOCAB_FIELD,
      'type' => 'entityreference',
      'cardinality' => FIELD_CARDINALITY_UNLIMITED,
    ),
    'instance' => array(
      'label' => t('OG vocabulary'),
      'widget' => array(
        'module' => 'og_vocab',
        'settings' => array(),
        'type' => 'og_vocab_complex',
      ),
      'display' => array(
        'default' => array(
          'type' => 'og_vocab',
        ),
      ),
    ),
  );
  return $items;
}

/**
 * Return TRUE if field is associated with OG-vocab.
 *
 * @param $entity_type
 *   The entity type.
 * @param $field_name
 *   The field name.
 * @param $bundle_name
 *   The bundle name to be checked.
 *
 * @see og_is_group_audience_field()
 */
function og_vocab_is_og_vocab_field($entity_type, $field_name, $bundle_name) {
  $field = field_info_field($field_name);
  $instance = field_info_instance($entity_type, $field_name, $bundle_name);
  return $field['type'] == 'entityreference' && $field['settings']['target_type'] == 'taxonomy_term' && $instance['widget']['type'] == 'og_vocab_complex';
}

/**
 * Get the name of the group-audience type field.
 *
 * @param $entity_type
 *   The entity type.
 * @param $bundle_name
 *   The bundle name to be checked.
 *
 * @return
 *   Array keyed with the field name and the field label as the value.
 *
 * @see og_get_group_audience_fields()
 */
function og_vocab_get_og_vocab_fields($entity_type, $bundle_name) {
  $return =& drupal_static(__FUNCTION__, array());
  $identifier = $entity_type . ':' . $bundle_name;
  if (isset($return[$identifier])) {
    return $return[$identifier];
  }
  $return[$identifier] = array();

  // field_info_instances() doesn't give the field type, so we have to use
  // field_info_fields().
  foreach (field_info_instances($entity_type, $bundle_name) as $field_name => $instance) {
    if (!og_vocab_is_og_vocab_field($entity_type, $field_name, $bundle_name)) {
      continue;
    }
    $return[$identifier][$field_name] = $instance['label'];
  }
  return $return[$identifier];
}

/**
 * Implements hook_field_widget_info().
 */
function og_vocab_field_widget_info() {
  $widgets['og_vocab_complex'] = array(
    'label' => t('OG vocab'),
    'description' => t('Complex widget to reference taxonomy terms related to accessible groups.'),
    'field types' => array(
      'entityreference',
    ),
  );
  return $widgets;
}

/**
 * Implements hook_field_widget_form().
 */
function og_vocab_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  $entity_type = $instance['entity_type'];
  $entity = isset($element['#entity']) ? $element['#entity'] : NULL;
  $field_name = $field['field_name'];
  if (!$entity) {
    return;
  }

  // Cache the processed entity, to make sure we call the widget only once.
  $cache =& drupal_static(__FUNCTION__, array());
  list($id, , $bundle) = entity_extract_ids($entity_type, $entity);
  $identifier = $entity_type . ':' . $id . ':' . $field_name;
  if (isset($cache[$identifier])) {
    return array();
  }
  $cache[$identifier] = TRUE;
  if (!($vids = og_vocab_get_accessible_vocabs($entity_type, $bundle, $field_name))) {
    return;
  }

  // Iterate over vocabularies and build each one.
  foreach ($vids as $vid) {
    $og_vocab = og_vocab_load_og_vocab($vid, $entity_type, $bundle);
    $element[$vid] = $og_vocab
      ->getFormElement($entity_type, $entity, $form, $form_state);
  }
  return $element;
}

/**
 * @see _entityreference_autocomplete_tags_validate()
 */
function _og_vocab_autocomplete_tags_validate($element, &$form_state, $form) {
  $value = array();

  // If a value was entered into the autocomplete...
  if (!empty($element['#value'])) {
    $entities = drupal_explode_tags($element['#value']);
    $value = array();
    foreach ($entities as $entity) {

      // Indicate if a value was found, or we need to create a new one.
      $found = FALSE;
      if ($tid = _og_vocab_term_exists_in_vocab($entity, $element['#vid'])) {

        // The term name already exists in the vocabulary.
        $value[] = array(
          'target_id' => $tid,
        );
        $found = TRUE;
      }
      elseif (preg_match("/.+\\((\\d+)\\)/", $entity, $matches)) {

        // Take "label (entity id)', match the id from parenthesis.
        $value[] = array(
          'target_id' => $matches[1],
        );
        $found = TRUE;
      }
      else {

        // Try to get a match from the input string when the user didn't use the
        // autocomplete but filled in a value manually.
        $field = field_info_field($element['#field_name']);
        $handler = entityreference_get_selection_handler($field);

        // TODO: We can't use validateAutocompleteInput()
        // We might need to create our own Selection plugin.

        //if ($input = $handler->validateAutocompleteInput($entity, $element, $form_state, $form)) {

        //$value[] = array('target_id' => $input);

        //}
      }
      if (!$found) {
        $vocabulary = taxonomy_vocabulary_load($element['#vid']);
        $term = array(
          'target_id' => 'autocreate',
          'vid' => $vocabulary->vid,
          'name' => $entity,
          'vocabulary_machine_name' => $vocabulary->machine_name,
        );
        $value[] = (array) $term;
      }
    }
  }
  form_set_value($element, $value, $form_state);
}

/**
 * Checks if the term already exists in the vocabulary.
 *
 * @param $name
 *   The term name.
 * @param $vid
 *   The vocabulary ID.
 *
 * @return
 *   The term ID if found, or FALSE.
 */
function _og_vocab_term_exists_in_vocab($name, $vid) {
  $query = new EntityFieldQuery();
  $result = $query
    ->entityCondition('entity_type', 'taxonomy_term')
    ->propertyCondition('vid', $vid)
    ->propertyCondition('name', $name)
    ->execute();
  return !empty($result['taxonomy_term']) ? reset(array_keys($result['taxonomy_term'])) : FALSE;
}

/**
 * Menu callback: autocomplete the label of an entity.
 *
 * @todo: Get the field in instance settings, based on the group and
 * vocabulary ID.
 *
 * @param $type
 *   The widget type (i.e. 'single' or 'tags').
 * @param $field_name
 *   The name of the entity-reference field.
 * @param $entity_type
 *   The entity type.
 * @param $bundle_name
 *   The bundle name.
 * @param $vid
 *   The vocabulary ID.
 * @param $entity_id
 *   Optional; The entity ID the entity-reference field is attached to.
 *   Defaults to ''.
 * @param $string
 *   The label of the entity to query by.
 */
function og_vocab_autocomplete_callback($type, OgVocab $og_vocab, $entity_id = '', $string = '') {
  $mocked_field = $og_vocab
    ->getMockedField();
  $instance = $mocked_field['instance'];
  $field = $mocked_field['field'];
  $entity_type = $og_vocab->entity_type;
  if (!$field || !$instance || $field['type'] != 'entityreference' || !field_access('edit', $field, $entity_type)) {
    return MENU_ACCESS_DENIED;
  }
  return entityreference_autocomplete_callback_get_matches($type, $field, $instance, $entity_type, $entity_id, $string);
}

/**
 * Implement hook_field_formatter_info().
 */
function og_vocab_field_formatter_info() {
  return array(
    'og_vocab' => array(
      'label' => t('OG vocabulary'),
      'field types' => array(
        'entityreference',
      ),
      'settings' => array(
        'concatenate' => FALSE,
      ),
    ),
  );
}

/**
 * Implements hook_field_formatter_settings_form().
 */
function og_vocab_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];
  $element['concatenate'] = array(
    '#type' => 'checkbox',
    '#title' => t('Concatenate'),
    '#description' => t('Show concatenated terms, separeated by comma. If disalbed, show list of terms, grouped by vocabulary.'),
    '#default_value' => $settings['concatenate'],
  );
  return $element;
}

/**
 * Implements hook_field_formatter_settings_summary().
 */
function og_vocab_field_formatter_settings_summary($field, $instance, $view_mode) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];
  $summary = array();
  $summary[] = $settings['concatenate'] ? t('Show concatenated terms') : t('Show list of terms, grouped by vocabulary');
  return implode('<br />', $summary);
}

/**
 * Implements hook_field_formatter_view().
 *
 * @todo Allow limiting by group visibilty.
 * @todo Deal with taxonomy_select_nodes() not showing our nodes as they
 * are not inside Taxonomy terms' tables.
 */
function og_vocab_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  global $user;
  list(, , $bundle) = entity_extract_ids($entity_type, $entity);
  if (!og_vocab_is_og_vocab_field($entity_type, $field['field_name'], $bundle)) {
    return array(
      '#markup' => t('OG Vocabulary formatter should be used only on "Entity reference" fields with "OG complex" widgets.'),
    );
  }
  if (!$items) {
    return;
  }
  $tids = array();
  foreach ($items as $item) {
    $tids[] = $item['target_id'];
  }
  $terms = taxonomy_term_load_multiple($tids);
  $ordered_terms = array();
  foreach ($terms as $term) {
    $vocabulary = taxonomy_vocabulary_load($term->vid);
    $ordered_terms[] = array(
      'term' => $term,
      'weight' => $vocabulary->weight,
      'vid' => $vocabulary->vid,
    );
  }
  uasort($ordered_terms, 'og_vocab_sort_weight');

  // Re-build the terms array, as it is now ordered.
  $terms = array();
  foreach ($ordered_terms as $info) {
    $terms[] = $info['term'];
  }
  $settings = $display['settings'];
  if ($settings['concatenate']) {
    $values = array();
    foreach ($terms as $term) {
      $url = entity_uri('taxonomy_term', $term);
      $values[] = l($term->name, $url['path']);
    }
    $element[0] = array(
      '#markup' => implode(', ', $values),
    );
    return $element;
  }

  // Iterate over each term and group by vocabulary.
  $data = array();
  foreach ($terms as $term) {
    if (empty($data['vocabylary'][$term->vid])) {
      $vocab = taxonomy_vocabulary_load($term->vid);
      $data['vocabylary'][$term->vid] = check_plain($vocab->name);
    }
    $url = entity_uri('taxonomy_term', $term);
    $data['term'][$term->vid][] = l($term->name, $url['path']);
  }
  if (!$data) {
    return;
  }
  $element = array();
  foreach ($data['term'] as $vid => $values) {
    $element[0][$vid] = array(
      '#theme' => 'item_list',
      '#items' => $values,
      '#title' => $data['vocabylary'][$vid],
    );
  }
  return $element;
}

/**
 * Sorts a structured array by the 'weight' element, and if they are equal then
 * by the vocabylary ID.
 *
 * @param $a
 *   First item for comparison.
 * @param $b
 *   Second item for comparison.
 *
 * @see drupal_sort_weight()
 */
function og_vocab_sort_weight($a, $b) {
  $result = drupal_sort_weight($a, $b);
  if ($result !== 0) {
    return $result;
  }

  // Sort by the vocabulary ID.
  return $a['vid'] < $b['vid'] ? -1 : 1;
}

/**
 * Save relation between a vocabulary and a group.
 */
function og_vocab_relation_save($vid, $group_type, $gid) {
  if ($og_vocab = og_vocab_relation_get($vid)) {
    if ($og_vocab->group_type != $group_type || $og_vocab->gid != $gid) {
      throw new OgVocabException('Vocabulary ID is already associated with another group.');
    }
    return;
  }
  $og_vocab = array(
    'vid' => $vid,
    'group_type' => $group_type,
    'gid' => $gid,
  );
  drupal_write_record('og_vocab_relation', $og_vocab);
}

/**
 * Get the group related to a vocabulary.
 */
function og_vocab_relation_get($vid) {
  $cache = drupal_static(__FUNCTION__, array());
  if (!isset($cache[$vid])) {
    $cache[$vid] = db_select('og_vocab_relation', 'ogr')
      ->fields('ogr')
      ->condition('vid', $vid)
      ->execute()
      ->fetch();
  }
  return $cache[$vid];
}

/**
 * Get all Vocabularies related to a group.
 */
function og_vocab_relation_get_by_group($group_type, $gid) {
  return db_select('og_vocab_relation', 'ogr')
    ->fields('ogr')
    ->condition('group_type', $group_type)
    ->condition('gid', $gid)
    ->execute()
    ->fetchAll();
}

/**
 * Delete a relation between a group and a vocabulary and all its OG-vocabs.
 *
 * @param $entity_type
 *   The entity type is deleted.
 * @param $entity_id
 *   The entity ID that is deleted.
 *
 */
function og_vocab_realtion_delete($entity_type, $entity_id) {
  $vids = array();
  if ($entity_type == 'taxonomy_vocabulary') {
    if (!($relation = og_vocab_relation_get($entity_id))) {
      return;
    }
    $vids[] = $entity_id;
  }
  else {

    // Get the vocabylary ID from the group.
    if (!($relations = og_vocab_relation_get_by_group($entity_type, $entity_id))) {
      return;
    }
    foreach ($relations as $relation) {
      $vids[] = $relation->vid;
    }
  }
  db_delete('og_vocab_relation')
    ->condition('vid', $vids, 'IN')
    ->execute();

  // Get all OG vocabs related to the entity_type, and delete them.
  $query = new EntityFieldQuery();
  $result = $query
    ->entityCondition('entity_type', 'og_vocab')
    ->propertyCondition('vid', $vids, 'IN')
    ->execute();
  if (empty($result['og_vocab'])) {
    return;
  }
  entity_delete_multiple('og_vocab', array_keys($result['og_vocab']));
}

/**
 * Replacement for core's taxonomy_autocomplete().
 *
 * @see taxonomy_autocomplete()
 */
function og_vocab_taxonomy_autocomplete($vid, $field_name, $tags_typed = '') {

  // If the request has a '/' in the search text, then the menu system will have
  // split it into multiple arguments, recover the intended $tags_typed.
  $args = func_get_args();

  // Shift off the $field_name argument.
  array_shift($args);
  $tags_typed = implode('/', $args);

  // The user enters a comma-separated list of tags. We only autocomplete the last tag.
  $tags_typed = drupal_explode_tags($tags_typed);
  $tag_last = drupal_strtolower(array_pop($tags_typed));
  $matches = array();
  if ($tag_last != '') {

    // Part of the criteria for the query come from the field's own settings.
    $vids = array(
      $vid,
    );
    $query = db_select('taxonomy_term_data', 't');
    $query
      ->addTag('translatable');
    $query
      ->addTag('term_access');

    // Do not select already entered terms.
    if (!empty($tags_typed)) {
      $query
        ->condition('t.name', $tags_typed, 'NOT IN');
    }

    // Select rows that match by term name.
    $tags_return = $query
      ->fields('t', array(
      'tid',
      'name',
    ))
      ->condition('t.vid', $vids)
      ->condition('t.name', '%' . db_like($tag_last) . '%', 'LIKE')
      ->range(0, 10)
      ->execute()
      ->fetchAllKeyed();
    $prefix = count($tags_typed) ? drupal_implode_tags($tags_typed) . ', ' : '';
    $term_matches = array();
    foreach ($tags_return as $tid => $name) {
      $n = $name;

      // Term names containing commas or quotes must be wrapped in quotes.
      if (strpos($name, ',') !== FALSE || strpos($name, '"') !== FALSE) {
        $n = '"' . str_replace('"', '""', $name) . '"';
      }
      $term_matches[$prefix . $n] = check_plain($name);
    }
  }
  drupal_json_output($term_matches);
}
function og_vocab_taxonomy_autocomplete_validate($element, &$form_state) {

  // Autocomplete widgets do not send their tids in the form, so we must detect
  // them here and process them independently.
  $value = array();
  if ($tags = $element['#value']) {

    // Collect candidate vocabularies.
    $field = field_widget_field($element, $form_state);
    $field = $element['#og_vocab'];
    $vocabularies = array();
    foreach ($field['settings']['allowed_values'] as $tree) {
      if ($vocabulary = taxonomy_vocabulary_machine_name_load($tree['vocabulary'])) {
        $vocabularies[$vocabulary->vid] = $vocabulary;
      }
    }

    // Translate term names into actual terms.
    $typed_terms = drupal_explode_tags($tags);
    foreach ($typed_terms as $typed_term) {

      // See if the term exists in the chosen vocabulary and return the tid;
      // otherwise, create a new 'autocreate' term for insert/update.
      if ($possibilities = taxonomy_term_load_multiple(array(), array(
        'name' => trim($typed_term),
        'vid' => array_keys($vocabularies),
      ))) {
        $term = array_pop($possibilities);
      }
      else {
        $vocabulary = reset($vocabularies);
        $term = array(
          'tid' => 'autocreate',
          'vid' => $vocabulary->vid,
          'name' => $typed_term,
          'vocabulary_machine_name' => $vocabulary->machine_name,
        );
      }
      $value[] = (array) $term;
    }
  }
  form_set_value($element, $value, $form_state);
}

/**
 * Implements hook_ctools_plugin_directory().
 */
function og_vocab_ctools_plugin_directory($module, $plugin) {
  if ($module == 'entityreference') {
    return "plugins/{$plugin}";
  }
}

//////////////////////////////////////////////////////////////////////////

/// Form alters

/**
 * Implements hook_query_TAG_alter().
 *
 * Alter loading vocabularies. If the static variable is set to "skip",
 * no altering will occour.
 *
 * @see og_vocab_machine_name_validate()
 */
function og_vocab_query_taxonomy_vocabulary_load_multiple_alter(QueryAlterableInterface $query) {
  $cache =& drupal_static(__FUNCTION__, FALSE);
  if ($cache == 'skip') {
    return;
  }
  if ($cache) {

    // Prevent recursion.
    return;
  }
  $cache = TRUE;
  if (!($context = og_vocab_is_group_admin_context())) {
    return;
  }
  $group_type = $context['group_type'];
  $gid = $context['gid'];
  $query
    ->innerJoin('og_vocab_relation', 'ogr', 'ogr.vid = base.vid');
  $query
    ->condition('ogr.group_type', $group_type)
    ->condition('ogr.gid', $gid);
}

/**
 * Implements hook_form_FORM-ID_alter().
 *
 * Vocabularies overview.
 */
function og_vocab_form_taxonomy_overview_vocabularies_alter(&$form, $form_state) {
  if (!($context = og_vocab_is_group_admin_context())) {
    return;
  }
  $group_type = $context['group_type'];
  $gid = $context['gid'];
  $path_prefix = "group/{$group_type}/{$gid}/admin";
  foreach (array_keys($form) as $key) {
    if (!is_numeric($key)) {
      continue;
    }
    $ops = array(
      'edit',
      'list',
      'add',
    );
    foreach ($ops as $op) {
      $form[$key][$op]['#href'] = $path_prefix . str_replace('admin/structure', '', $form[$key][$op]['#href']);
    }
  }
}

/**
 * Implements hook_theme_registry_alter().
 *
 * @see theme_og_vocab_taxonomy_overview_vocabularies()
 */
function og_vocab_theme_registry_alter(&$theme_registry) {
  $theme_registry['taxonomy_overview_vocabularies']['function'] = 'theme_og_vocab_taxonomy_overview_vocabularies';
}

/**
 * Override theme_taxonomy_overview_vocabularies() to make it group aware.
 *
 * The empty text (i.e. when there are no vocabularies) will redirect to
 * the correct path.
 *
 * @param $variables
 *   An associative array containing:
 *   - form: A render element representing the form.
 *
 * @see theme_taxonomy_overview_vocabularies()
 */
function theme_og_vocab_taxonomy_overview_vocabularies($variables) {
  if (!($context = og_vocab_is_group_admin_context())) {
    return theme_taxonomy_overview_vocabularies($variables);
  }
  $item = menu_get_item();
  $empty = t('No vocabularies available. <a href="@link">Add vocabulary</a>.', array(
    '@link' => url($item['href'] . '/add'),
  ));
  $form = $variables['form'];
  $rows = array();
  foreach (element_children($form) as $key) {
    if (isset($form[$key]['name'])) {
      $vocabulary =& $form[$key];
      $row = array();
      $row[] = drupal_render($vocabulary['name']);
      if (isset($vocabulary['weight'])) {
        $vocabulary['weight']['#attributes']['class'] = array(
          'vocabulary-weight',
        );
        $row[] = drupal_render($vocabulary['weight']);
      }
      $row[] = drupal_render($vocabulary['edit']);
      $row[] = drupal_render($vocabulary['list']);
      $row[] = drupal_render($vocabulary['add']);
      $rows[] = array(
        'data' => $row,
        'class' => array(
          'draggable',
        ),
      );
    }
  }
  $header = array(
    t('Vocabulary name'),
  );
  if (isset($form['actions'])) {
    $header[] = t('Weight');
    drupal_add_tabledrag('taxonomy', 'order', 'sibling', 'vocabulary-weight');
  }
  $header[] = array(
    'data' => t('Operations'),
    'colspan' => '3',
  );
  return theme('table', array(
    'header' => $header,
    'rows' => $rows,
    'empty' => $empty,
    'attributes' => array(
      'id' => 'taxonomy',
    ),
  )) . drupal_render_children($form);
}

/**
 * Implements hook_form_FORM-ID_alter().
 *
 * 1) Add group id to the vocabulary if it's a group context.
 * 2) Add own submit handler.
 *
 * @see og_vocab_form_taxonomy_form_vocabulary_submit().
 */
function og_vocab_form_taxonomy_form_vocabulary_alter(&$form, $form_state) {
  if (!empty($form_state['triggering_element']['#parents'][0]) && $form_state['triggering_element']['#parents'][0] == 'delete') {

    // This is delete confirmation page.
    return;
  }
  if (!empty($form['#vocabulary']->vid)) {
    if (!($relation = og_vocab_relation_get($form['#vocabulary']->vid))) {

      // This is an existing vocabulary, not associated with a group.
      return;
    }
    $group_type = $relation->group_type;
    $gid = $relation->gid;
  }
  elseif ($context = og_vocab_is_group_admin_context()) {

    // We are inside a group context.
    $group_type = $context['group_type'];
    $gid = $context['gid'];
    $form['#validate'][] = 'og_vocab_form_taxonomy_form_vocabulary_validate';
  }
  if (empty($group_type)) {

    // We don't have enough context, about which group this vocabulary
    // should belong to.
    return;
  }
  $group = entity_load_single($group_type, $gid);
  list(, , $group_bundle) = entity_extract_ids($group_type, $group);

  // Validate the machine name when creating a new vocabulary.
  if (empty($form_state['vocabulary']->vid)) {
    $form['machine_name']['#element_validate'][] = 'og_vocab_machine_name_validate';
  }
  $form['og_vocab_relation'] = array(
    '#type' => 'value',
    '#value' => array(
      'group_type' => $group_type,
      'gid' => $gid,
    ),
  );

  // Add widget settings per group content. We discover all the group
  // content that reference this group type.
  $entity_info = entity_get_info();
  $options = array();
  foreach (og_get_all_group_content_bundle() as $entity_type => $bundles) {
    if ($entity_type == 'user') {

      // Adding terms to the user is probably not the use case, so skip
      // it.
      continue;
    }
    foreach ($bundles as $bundle => $bundle_label) {
      foreach (og_get_group_audience_fields($entity_type, $bundle, FALSE) as $field_name => $field_label) {
        $field = field_info_field($field_name);
        if (empty($field['settings']['handler_settings']['target_bundles']) || in_array($group_bundle, $field['settings']['handler_settings']['target_bundles'])) {
          $options[$entity_type][$bundle] = $entity_info[$entity_type]['label'] . ' - ' . $bundle_label;
        }
      }
    }
  }
  if (!$options) {
    $form['og_vocab'] = array(
      '#markup' => t('There are no group contents referencing this group type.'),
    );
    return;
  }
  $form['og_vocab'] = array(
    '#tree' => TRUE,
  );
  $vocabulary = $form['#vocabulary'];
  $vid = !empty($vocabulary->vid) ? $vocabulary->vid : 0;
  foreach ($options as $entity_type => $values) {
    foreach ($values as $bundle => $name) {

      // Load the OG-vocab record if exists, or a default one.
      $form['og_vocab']["{$entity_type}:{$bundle}"] = array(
        '#type' => 'fieldset',
        '#title' => check_plain($name),
        '#id' => drupal_clean_css_identifier("og-vocab-{$entity_type}-{$bundle}"),
      );

      // Load an existing OG-vocab, or get default values.
      $og_vocab = og_vocab_load_og_vocab($vid, $entity_type, $bundle, NULL, TRUE);
      $form['og_vocab']["{$entity_type}:{$bundle}"]['entity'] = array(
        '#type' => 'value',
        '#value' => $og_vocab,
      );

      // Get all the entity reference widget types, except OG-vocab's.
      $form['og_vocab']["{$entity_type}:{$bundle}"]['status'] = array(
        '#type' => 'checkbox',
        '#title' => t('Enable'),
        '#description' => t('Show term reference in @name add and edit form', array(
          '@name' => $name,
        )),
        '#default_value' => empty($og_vocab->is_new),
      );
      module_load_include('inc', 'field_ui', 'field_ui.admin');
      $widget_types = field_ui_widget_type_options('entityreference');
      unset($widget_types['og_vocab_complex'], $widget_types['og_complex']);
      $element_name = "og_vocab[{$entity_type}:{$bundle}][status]";
      $states = array(
        '#states' => array(
          'visible' => array(
            ':input[name="' . $element_name . '"]' => array(
              'checked' => TRUE,
            ),
          ),
        ),
      );
      $form['og_vocab']["{$entity_type}:{$bundle}"]['widget_type'] = array(
        '#type' => 'select',
        '#title' => t('Widget type'),
        '#required' => TRUE,
        '#options' => $widget_types,
        '#default_value' => $og_vocab->settings['widget_type'],
        '#description' => t('The type of form element you would like to present to the user when creating this field in the %type type.', array(
          '%type' => $bundle_label,
        )),
      ) + $states;
      $form['og_vocab']["{$entity_type}:{$bundle}"]['required'] = array(
        '#type' => 'checkbox',
        '#title' => t('Required field'),
        '#default_value' => $og_vocab->settings['required'],
      ) + $states;
      $form['og_vocab']["{$entity_type}:{$bundle}"]['cardinality'] = array(
        '#type' => 'select',
        '#title' => t('Number of values'),
        '#options' => array(
          FIELD_CARDINALITY_UNLIMITED => t('Unlimited'),
        ) + drupal_map_assoc(range(1, 10)),
        '#default_value' => $og_vocab->settings['cardinality'],
        '#description' => t('Maximum number of values users can enter for this field.'),
      ) + $states;

      // Add the field name to associate with. If there's just a single or
      // it still doesn't exist we hide it, and select it as the default.
      $og_vocab_fields = og_vocab_get_og_vocab_fields($entity_type, $bundle);
      if (!empty($og_vocab_fields) && count($og_vocab_fields) > 1) {
        $form['og_vocab']["{$entity_type}:{$bundle}"]['field_name'] = array(
          '#title' => t('Field name'),
          '#type' => 'select',
          '#options' => $og_vocab_fields,
          '#default_value' => $og_vocab->field_name,
        );
      }
      else {

        // Pass it as value.
        $form['og_vocab']["{$entity_type}:{$bundle}"]['field_name'] = array(
          '#type' => 'value',
          '#value' => $og_vocab->field_name,
        );
      }
    }
  }
  $form['#submit'][] = 'og_vocab_form_taxonomy_form_vocabulary_submit';
}

/**
 * Implements hook_form_alter().
 *
 * Using the hook_form_alter and not hook_form_FORM_ID_alter due to the
 * invokes order of the hooks.
 */
function og_vocab_form_alter(&$form, $form_state, $form_id) {
  if (!in_array($form_id, array(
    'taxonomy_form_vocabulary',
    'taxonomy_overview_terms',
  )) || !($context = og_vocab_is_group_admin_context())) {
    return;
  }
  if ($form_id == 'taxonomy_overview_terms') {

    // Alter the 'Add term' link that is displayed when no terms have yet been
    // added.
    $og_vocab = taxonomy_vocabulary_load($form['#vocabulary']->vid);
    $relation = og_vocab_relation_get($og_vocab->vid);
    if (og_user_access($relation->group_type, $relation->gid, 'administer taxonomy')) {
      $path = "group/{$relation->group_type}/{$relation->gid}/admin/taxonomy/{$og_vocab->machine_name}/add";
      $form['#empty_text'] = t('No terms available. <a href="@link">Add term</a>.', array(
        '@link' => url($path),
      ));
    }
  }
  $form['#submit'][] = 'og_vocab_redirect_to_group_vocabularies';
}

/**
 * After deleting the group vocabulary, redirect to the taxonomy group admin
 * page.
 */
function og_vocab_redirect_to_group_vocabularies($form, &$form_state) {
  if (!($context = og_vocab_is_group_admin_context())) {
    return;
  }
  $form_state['redirect'] = 'group/' . $context['group_type'] . '/' . $context['gid'] . '/admin/taxonomy';
  if ($form['#form_id'] == 'taxonomy_overview_terms' && !empty($form_state['confirm_reset_alphabetical']) && !empty($form_state['complete form']['machine_name']['#value'])) {

    // Redirect back to the terms overview, after "Reset alphabetical"
    // was submitted.
    $form_state['redirect'] .= '/' . $form_state['complete form']['machine_name']['#value'] . '/list';
  }
}

/**
 * Element validate; Auto-set a new machine name if vocabulary already
 * exists.
 */
function og_vocab_machine_name_validate($element, &$form_state) {
  if (empty($form_state['values']['machine_name'])) {
    return;
  }
  $machine_name = $form_state['values']['machine_name'];

  // Set the static variable to "skip" to prevent altering while we verify
  // the machine name doesn't exist yet.
  $cache =& drupal_static('og_vocab_query_taxonomy_vocabulary_load_multiple_alter', FALSE);
  $cache = 'skip';
  if (!taxonomy_vocabulary_machine_name_load($machine_name)) {
    return;
  }

  // Start iterating over machine names until we hit one that doesn't
  // exist yet.
  $i = 1;
  while (taxonomy_vocabulary_machine_name_load($machine_name)) {
    $machine_name = substr($machine_name, 0, 32 - strlen($i)) . $i;
    ++$i;
  }
  form_set_value($element, $machine_name, $form_state);
}

/**
 * Validate handler; Redirect back to taxonomy overview.
 */
function og_vocab_form_taxonomy_form_vocabulary_validate(&$form, $form_state) {

  // TODO: Why do we need to set the $_GET['destination']?
  $group_type = $form_state['values']['og_vocab_relation']['group_type'];
  $gid = $form_state['values']['og_vocab_relation']['gid'];
  $_GET['destination'] = $form_state['redirect'] = "group/{$group_type}/{$gid}/admin/taxonomy";
}

/**
 * Submit handler; Save the OG-vocab record.
 */
function og_vocab_form_taxonomy_form_vocabulary_submit(&$form, $form_state) {
  $vocab = $form_state['vocabulary'];
  $group_type = $form_state['values']['og_vocab_relation']['group_type'];
  $gid = $form_state['values']['og_vocab_relation']['gid'];

  // Save the relation.
  og_vocab_relation_save($vocab->vid, $group_type, $gid);
  foreach ($form_state['values']['og_vocab'] as $key => $value) {
    $og_vocab = $form_state['values']['og_vocab'][$key]['entity'];
    $og_vocab->vid = $vocab->vid;

    // Check if status was disable, and if so delete the og-vocab.
    if (!$value['status'] && empty($og_vocab->is_new)) {
      $og_vocab
        ->delete();
    }
    elseif ($value['status']) {

      // Rebuild the settings.
      $settings = array(
        'required',
        'widget_type',
        'cardinality',
      );
      foreach ($settings as $setting) {
        $og_vocab->settings[$setting] = $form_state['values']['og_vocab'][$key][$setting];
      }
      $og_vocab->field_name = $form_state['values']['og_vocab'][$key]['field_name'];
      $og_vocab
        ->save();
    }
  }
}

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Term edit page.
 */
function og_vocab_form_taxonomy_form_term_alter(&$form, $form_state) {
  $vocab = $form['#vocabulary'];
  if (!($relation = og_vocab_relation_get($vocab->vid))) {
    return;
  }
  $form['actions']['delete']['#access'] = og_user_access($relation->group_type, $relation->gid, 'delete terms') || og_user_access($relation->group_type, $relation->gid, 'administer taxonomy');
}

/**
 * API function; Get all the vocabs a user may access.
 *
 * This will include the global vocabualries (i.e. ones that aren't associated
 * with a group), and the ones that are associated with a group the user is a
 * member.
 *
 * @todo: Take care of administrators, we need to show ALL widgets?
 *
 * @param $entity_type
 *   The entity type.
 * @param $bundle
 *   The entity bundle.
 * @param $field_name
 *   The field name that is associated with the OG-vocab.
 * @param $account
 *   User object. Defaults to current user.
 *
 * @return
 *   An array with the vocabulary IDs or an empty array if no vocabulary
 *   was found.
 */
function og_vocab_get_accessible_vocabs($entity_type, $bundle, $field_name, $account = NULL) {
  if (empty($account)) {
    global $user;
    $account = clone $user;
  }
  $field = field_info_field($field_name);
  $use_context = !empty($field['settings']['handler_settings']['behaviors']['og_vocab']['use_context']) ? $field['settings']['handler_settings']['behaviors']['og_vocab']['use_context'] : 'yes';
  $context = FALSE;
  $gids = array();
  if (in_array($use_context, array(
    'force',
    'yes',
  )) && module_exists('og_context') && ($context = og_context())) {
    $gids[$context['group_type']][$context['gid']] = array(
      $context['gid'],
    );
  }
  elseif (in_array($use_context, array(
    'yes',
    'no',
  )) && !$gids) {
    $gids = og_get_entity_groups('user', $account);
  }
  if (!$gids) {
    return;
  }
  $query = db_select('og_vocab_relation', 'ogr');
  $query
    ->fields('ogr', array(
    'vid',
  ));

  // Prepare a query according to the group types. In the common case that
  // the user has only a single group type (e.g. node) we can make a simpler
  // query.
  if (count($gids) == 1) {
    $group_type = key($gids);
    $query
      ->condition('group_type', $group_type)
      ->condition('gid', $gids[$group_type], 'IN');
  }
  else {

    // TODO
  }

  // Join with the OG-vocab.
  $query
    ->innerJoin('og_vocab', 'ogv', 'ogr.vid = ogv.vid');
  $query
    ->condition('ogv.entity_type', $entity_type)
    ->condition('ogv.bundle', $bundle)
    ->condition('ogv.field_name', $field_name);
  $result = $query
    ->execute()
    ->fetchAllAssoc('vid');
  return !empty($result) ? array_keys($result) : FALSE;
}

/**
 * Access function to determine if a user has access to the menu item.
 *
 * @param $entity_type
 *   The entity type.
 * @param $entity_id
 *   The entity ID.
 * @param $perm
 *   The permission to check is user has.
 * @param $vocabulary
 *   Optional; The vocabulary ID.
 *
 * @return
 *   TRUE if user has access to the menu item.
 */
function og_vocab_vocabulary_access($entity_type, $entity_id, $perm, $vocabulary = NULL) {
  if (!($entity = entity_load_single($entity_type, $entity_id))) {
    return FALSE;
  }
  if ($vocabulary) {

    // Make sure vocabulary ID belongs to the group node.
    if (!($og_vocab = og_vocab_relation_get($vocabulary->vid))) {
      return FALSE;
    }

    // Check the group matches our passed group.
    if ($og_vocab->group_type != $entity_type || $og_vocab->gid != $entity_id) {
      return FALSE;
    }
  }
  return og_user_access($entity_type, $entity_id, $perm);
}

/**
 * Return edit access for a given term.
 */
function og_vocab_taxonomy_term_edit_access($term) {
  if ($og_vocab = og_vocab_relation_get($term->vid)) {
    return og_user_access($og_vocab->group_type, $og_vocab->gid, 'edit terms') || og_user_access($og_vocab->group_type, $og_vocab->gid, 'administer taxonomy');
  }
  return taxonomy_term_edit_access($term);
}

/**
 * Implements hook_entity_delete().
 */
function og_vocab_entity_delete($entity, $entity_type) {
  if ($entity_type == 'taxonomy_vocabulary') {
    og_vocab_realtion_delete($entity_type, $entity->vid);
  }
  elseif (og_is_group($entity_type, $entity)) {
    list($id) = entity_extract_ids($entity_type, $entity);
    og_vocab_realtion_delete($entity_type, $id);
  }
}

/**
 * Check if a given page is inside a group admin context.
 *
 * We determine the context by the menu item path, and if it doesn't exist
 * and OG context module is enabled, we try to check if we have a group
 * context avialable.
 *
 * @return
 *   Array keyed with the group type and group ID, if context found.
 */
function og_vocab_is_group_admin_context() {
  if (strpos($_GET['q'], 'group/') === 0 && ($item = menu_get_item()) && !empty($item['map'][2])) {
    return array(
      'group_type' => $item['map'][1],
      'gid' => $item['map'][2],
    );
  }

  // Iterate over modules implementing context, and return early once
  // a context was found.
  foreach (module_implements('og_vocab_is_admin_context') as $module) {
    $function = $module . '_og_vocab_is_admin_context';
    if ($context = $function()) {
      return $context;
    }
  }
}

/**
 * Implements hook_modules_enabled().
 *
 * Register dynamic migrate plugins for upgrading from OG6.
 */
function og_vocab_modules_enabled($modules) {
  if (!variable_get('og_vocab_7000', FALSE)) {
    return;
  }
  if (!in_array('migrate', $modules) && !module_exists('migrate')) {
    return;
  }
  foreach (node_type_get_names() as $bundle => $value) {

    // Register a dynamic migration.
    Migration::registerMigration('OgVocabMigrate', 'OgVocabMigrate' . ucfirst($bundle), array(
      'bundle' => $bundle,
    ));
  }
}

/**
 * Implements hook_cron_queue_info().
 */
function og_vocab_cron_queue_info() {
  $items['og_vocab'] = array(
    'title' => t('OG Vocabulary'),
    'worker callback' => 'og_vocab_remove_referenced_items_queue_worker',
    'time' => 60,
  );
  return $items;
}

/**
 * Queue worker; Process a queue item.
 *
 * When a OG vocabulary is being deleted we need to remove the reference between
 * the nodes to the terms.
 */
function og_vocab_remove_referenced_items_queue_worker($data, $end_time = FALSE) {
  extract($data);

  // Get range of the group content.
  // We don't use entityFieldQuery() as we want to join to the entity base
  // table, to make sure we process only entities from the correct bundle.
  $entity_info = entity_get_info($entity_type);
  $query = db_select('og_membership', 'ogm');
  $query
    ->fields('ogm', array(
    'id',
    'entity_type',
    'etid',
  ));
  $query
    ->condition('ogm.group_type', $group_type)
    ->condition('ogm.gid', $gid)
    ->condition('ogm.entity_type', $entity_type)
    ->condition('ogm.id', $last_id, '>')
    ->orderBy('ogm.id');
  $base_table = $entity_info['base table'];
  $bundle_key = $entity_info['entity keys']['bundle'];
  $id_key = $entity_info['entity keys']['id'];
  $alias = $query
    ->innerJoin($base_table, NULL, "ogm.etid = {$base_table}.{$id_key}");
  $result = $query
    ->condition("{$alias}.{$bundle_key}", $bundle)
    ->range(0, 100)
    ->execute()
    ->fetchAllAssoc('id');
  if (empty($result)) {

    // No more group-content for this entity, so we can delete the task item.
    return;
  }
  foreach ($result as $row) {
    $last_id = $row->id;
    $wrapper = entity_metadata_wrapper($row->entity_type, $row->etid);
    if (!($terms = $wrapper->{$field_name}
      ->value())) {

      // Field doesn't have any term, so we can skip.
      continue;
    }

    // Array with the original and new term IDs.
    $original_tids = array();
    $new_tids = array();
    foreach ($terms as $term) {
      $original_tids[] = $term->tid;
      if ($term->vid != $vid) {
        $new_tids[] = $term->tid;
      }
    }
    if ($original_tids != $new_tids) {

      // Some terms were removed so re-save the entity.
      $wrapper->{$field_name}
        ->set($new_tids);
      $wrapper
        ->save();
    }
  }

  // Update the item with the last OG-membership ID.
  $data = array(
    'vid' => $vid,
    'entity_type' => $entity_type,
    'bundle' => $bundle,
    'field_name' => $field_name,
    'group_type' => $group_type,
    'gid' => $gid,
    'last_id' => $last_id,
  );
  $queue = DrupalQueue::get('og_vocab');
  return $queue
    ->createItem($data);
}

Functions

Namesort descending Description
og_vocab_autocomplete_callback Menu callback: autocomplete the label of an entity.
og_vocab_create_og_vocab Helper to easily create views-queries.
og_vocab_cron_queue_info Implements hook_cron_queue_info().
og_vocab_ctools_plugin_directory Implements hook_ctools_plugin_directory().
og_vocab_entity_delete Implements hook_entity_delete().
og_vocab_entity_info Implements hook_entity_info().
og_vocab_field_formatter_info Implement hook_field_formatter_info().
og_vocab_field_formatter_settings_form Implements hook_field_formatter_settings_form().
og_vocab_field_formatter_settings_summary Implements hook_field_formatter_settings_summary().
og_vocab_field_formatter_view Implements hook_field_formatter_view().
og_vocab_field_widget_form Implements hook_field_widget_form().
og_vocab_field_widget_info Implements hook_field_widget_info().
og_vocab_form_alter Implements hook_form_alter().
og_vocab_form_taxonomy_form_term_alter Implements hook_form_FORM_ID_alter().
og_vocab_form_taxonomy_form_vocabulary_alter Implements hook_form_FORM-ID_alter().
og_vocab_form_taxonomy_form_vocabulary_submit Submit handler; Save the OG-vocab record.
og_vocab_form_taxonomy_form_vocabulary_validate Validate handler; Redirect back to taxonomy overview.
og_vocab_form_taxonomy_overview_vocabularies_alter Implements hook_form_FORM-ID_alter().
og_vocab_get_accessible_vocabs API function; Get all the vocabs a user may access.
og_vocab_get_og_vocab_fields Get the name of the group-audience type field.
og_vocab_is_group_admin_context Check if a given page is inside a group admin context.
og_vocab_is_og_vocab_field Return TRUE if field is associated with OG-vocab.
og_vocab_label Label callback.
og_vocab_load_og_vocab Get OG vocabulary settings by vocabulary ID.
og_vocab_machine_name_validate Element validate; Auto-set a new machine name if vocabulary already exists.
og_vocab_menu Implements hook_menu().
og_vocab_menu_alter Implements hook_menu_alter().
og_vocab_migrate_api Implements hook_migrate_api().
og_vocab_modules_enabled Implements hook_modules_enabled().
og_vocab_og_fields_info Implements hook_og_fields_info().
og_vocab_og_permission Implements hook_og_permissions().
og_vocab_og_ui_get_group_admin Implements hook_og_ui_get_group_admin()
og_vocab_query_taxonomy_vocabulary_load_multiple_alter Implements hook_query_TAG_alter().
og_vocab_realtion_delete Delete a relation between a group and a vocabulary and all its OG-vocabs.
og_vocab_redirect_to_group_vocabularies After deleting the group vocabulary, redirect to the taxonomy group admin page.
og_vocab_relation_get Get the group related to a vocabulary.
og_vocab_relation_get_by_group Get all Vocabularies related to a group.
og_vocab_relation_save Save relation between a vocabulary and a group.
og_vocab_remove_referenced_items_queue_worker Queue worker; Process a queue item.
og_vocab_sort_weight Sorts a structured array by the 'weight' element, and if they are equal then by the vocabylary ID.
og_vocab_taxonomy_autocomplete Replacement for core's taxonomy_autocomplete().
og_vocab_taxonomy_autocomplete_validate
og_vocab_taxonomy_term_edit_access Return edit access for a given term.
og_vocab_term_page_access Determince access to term page if term belongs to a vocabulary owned by OG-vocab.
og_vocab_theme_registry_alter Implements hook_theme_registry_alter().
og_vocab_uri URI callback.
og_vocab_vocabulary_access Access function to determine if a user has access to the menu item.
theme_og_vocab_taxonomy_overview_vocabularies Override theme_taxonomy_overview_vocabularies() to make it group aware.
_og_vocab_autocomplete_tags_validate
_og_vocab_term_exists_in_vocab Checks if the term already exists in the vocabulary.

Constants

Namesort descending Description
OG_VOCAB_FIELD OG-vocab default field name.