You are here

community_tags.admin.inc in Community Tags 6.2

Same filename and directory in other branches
  1. 6 community_tags.admin.inc
  2. 7 community_tags.admin.inc

community_tags.admin.inc

Admin forms and supporting functions.

File

community_tags.admin.inc
View source
<?php

/**
 * @file
 * community_tags.admin.inc
 *
 * Admin forms and supporting functions.
 */

/**
 * Main settings page.
 */
function community_tags_settings() {

  // community_tags_rehash();
  if ($broken = _community_tags_get_count_of_broken_tags()) {
    drupal_set_message(t('There are @count broken community tags that reference missing terms, nodes, or users. Click <a href="@url">here</a> to delete them.', array(
      '@count' => $broken,
      '@url' => url('admin/settings/community-tags/ops/broken'),
    )), 'warning');
  }

  // Build list of available free-tagging vocabularies
  $vocabularies = _community_tags_get_settings();

  // stat query for all ctags even for invalid combos - e.g. unassigned content type
  $counts = _community_tags_get_tag_counts_by_type_and_vocabulary();

  // put tag counts into vocabularies array. If a new type is found for a vocabulary
  // then tags are left over from a previous configuration - i.e. content type was assigned for tagging but now is not.
  foreach ($counts as $vid => $counts_by_type) {
    foreach ($counts_by_type as $type => $count) {
      if (!isset($vocabularies[$vid]['types'][$type])) {
        $content_type = node_get_types('type', $type);
        $vocabularies[$vid]['types'][$type] = array(
          'type_name' => $content_type->name,
          'tag_count' => $count->tag_count,
          'assigned' => FALSE,
        );
      }
      else {
        $vocabularies[$vid]['types'][$type]['tag_count'] = $count->tag_count;
      }
    }
  }
  return theme('community_tags_settings', $vocabularies);
}

/**
 * Theme function for community tags settings.
 */
function theme_community_tags_settings($vocabularies) {
  $rows = array();
  $header_rows = array();
  $first_row = TRUE;
  $first_type_row = TRUE;
  $vocab_index = 0;
  $type_column_count = 1;
  $header_rows[] = array(
    array(
      'data' => t('Vocabulary'),
      'rowspan' => 2,
    ),
    array(
      'data' => t('Free-tagging?'),
      'rowspan' => 2,
    ),
    array(
      'data' => t('Assigned content types'),
      'colspan' => 6,
    ),
  );
  $header_rows[] = array(
    array(
      'data' => t('Type'),
    ),
    array(
      'data' => t('Enabled?'),
    ),
    array(
      'data' => t('Tag count'),
    ),
    array(
      'data' => t('Status'),
    ),
    array(
      'data' => t('Operations'),
    ),
    array(
      'data' => t('Settings'),
    ),
  );
  foreach ($vocabularies as $vid => $vocabulary) {

    // content types enabled for this vocabulary - may not be any
    $types = $vocabulary['types'];
    $content_type_count = count($types);
    $rowspan = $content_type_count == 0 ? 1 : $content_type_count;

    // stripe per vocabulary
    $row_class = $vocab_index++ % 2 == 0 ? 'odd' : 'even';

    // First column 'vocabulary' - if first pass process header - get description if any
    $row_data = array();
    $row_data[] = array(
      'data' => l($vocabulary['name'], 'admin/content/taxonomy/edit/vocabulary/' . $vid, array(
        'query' => drupal_get_destination(),
      )),
      'rowspan' => $rowspan,
    );
    $row_data[] = array(
      'data' => $vocabulary['tagging'] ? t('yes') : t('no'),
      'rowspan' => $rowspan,
    );

    // Vocabulary level columns - if first pass process header - get descriptions if any
    if (!empty($vocabulary['types'])) {
      foreach ($vocabulary['types'] as $type_id => $type) {
        $row_data[] = array(
          'data' => l($type['type_name'], 'admin/content/node-type/' . $type_id, array(
            'query' => drupal_get_destination(),
          )),
        );
        $row_data[] = array(
          'data' => $type['enabled'] ? t('yes') : t('no'),
        );
        $row_data[] = array(
          'data' => $type['tag_count'],
        );

        // status
        $statuses = array();
        if ($type['enabled']) {
          $statuses = module_invoke_all('community_tags_admin_status', $vid, $type_id);
        }
        $row_data[] = empty($statuses) ? t('OK') : array(
          'data' => implode('; ', $statuses),
        );

        // operations
        $operations = array();
        if ($type['enabled']) {
          $operations = module_invoke_all('community_tags_admin_operations', $vid, $type_id);
        }
        if (isset($type['tag_count']) && $type['tag_count'] > 0) {

          // operations for any CT vocabulary that has tags
          $operations[] = array(
            'title' => t('purge'),
            'href' => "admin/settings/community-tags/ops/purge/{$vid}/{$type_id}",
            'query' => drupal_get_destination(),
          );
        }
        $row_data[] = array(
          'data' => theme('links', $operations, array(
            'class' => 'links inline',
          )),
        );
        $row_data[] = array(
          'data' => l(t('edit'), "admin/settings/community-tags/{$vid}/{$type_id}", array(
            'query' => drupal_get_destination(),
          )),
        );
        $rows[] = array(
          'data' => $row_data,
          'class' => $row_class,
        );
        $row_data = array();
      }
    }
    else {
      $row_data[] = array(
        'data' => t('No assigned content types.'),
        'colspan' => 6,
      );
      $rows[] = array(
        'data' => $row_data,
        'class' => $row_class,
      );
    }
  }

  // output the table - don't use standard theme because want bespoke striping
  $output .= '<table><thead>';
  foreach ($header_rows as $header_row) {
    $output .= '<tr>';
    foreach ($header_row as $header_cell) {
      $output .= _theme_table_cell($header_cell, TRUE);
    }
    $output .= '</tr>';
  }
  $output .= '</thead><tbody>';
  foreach ($rows as $row) {
    $output .= '<tr class="' . $row['class'] . '">';
    foreach ($row['data'] as $data_cell) {
      $output .= _theme_table_cell($data_cell);
    }
    $output .= '</tr>';
  }
  $output .= '</tbody></table>';
  return $output;
}

/**
 * Form builder; Builds the sub settings form.
 *
 * @ingroup forms
 */
function community_tags_sub_settings(&$form_state, $vocabulary, $content_type) {
  $form = array();
  $type_obj = node_get_types('type', $content_type);
  $form['#vid'] = $vid = $vocabulary->vid;
  $form['#content_type'] = $content_type;
  $settings = _community_tags_get_settings($vocabulary->vid, $content_type);

  // stat query for all ctags even for invalid combos - e.g. unassigned content type
  $counts = _community_tags_get_tag_counts_by_type_and_vocabulary();
  $settings['tag_count'] = $counts[$vid][$content_type];
  $display_handler_options = _community_tags_get_display_handler_options();
  $form['core'] = array(
    '#tree' => TRUE,
  );
  $form['core']['enabled'] = array(
    '#title' => t('Enable community tagging'),
    '#type' => 'checkbox',
    '#default_value' => $settings['enabled'],
  );
  $form['core']['display_handler'] = array(
    '#title' => t('Display'),
    '#type' => 'select',
    '#default_value' => isset($display_handler_options[$settings['display_handler']]) ? $settings['display_handler'] : 'links',
    '#options' => $display_handler_options,
  );
  $form['core']['redundant_terms'] = array(
    '#title' => t('Delete redundant terms'),
    '#type' => 'checkbox',
    '#default_value' => $settings['opmode'] & COMMUNITY_TAGS_OPMODE_DELETE_ORPHAN_TERMS,
    '#description' => t('When checked, terms that are no longer used as a result of a community tag being removed are deleted.'),
  );
  $form['core']['tag_links'] = array(
    '#title' => t('Show "Tag this" link on'),
    '#type' => 'checkboxes',
    '#options' => array(
      'teaser' => t('Node teasers'),
      'full' => t('Full nodes'),
    ),
    '#default_value' => $settings['tag_links'] ? $settings['tag_links'] : array(),
  );
  $form['plugins'] = array(
    '#tree' => TRUE,
  );
  $form['buttons']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save configuration'),
  );
  return $form;
}

/**
 * Title callback for sub-settings configuration page.
 */
function _community_tags_sub_settings_title($arg1, $arg2) {
  $type = node_get_types('type', $arg2);
  return t('Community tags settings for @vocabulary on content of type @content_type', array(
    '@vocabulary' => $arg1->name,
    '@content_type' => $type->name,
  ));
}

/**
 * Submit handler for sub settings form.
 */
function community_tags_sub_settings_submit($form, &$form_state) {
  $settings = _community_tags_get_settings(NULL, NULL);
  $type_settings = $form_state['values']['core'];
  $type_settings['opmode'] = 0x0;
  $type_settings['opmode'] |= $type_settings['redundant_terms'] ? COMMUNITY_TAGS_OPMODE_DELETE_ORPHAN_TERMS : 0x0;
  unset($type_settings['redundant_terms']);
  $settings[$form['#vid']]['types'][$form['#content_type']] = $type_settings;
  variable_set('community_tags_vocabularies', $settings);
}

/**
 * "Private" implementation of hook_form_alter().
 */
function _community_tags_node_type_form_alter(&$form, &$form_state, $form_id) {

  // only show if content type is mapped to tagging vocabulary
  $supported_vids = _community_tags_vids_for_node_type($form['#node_type']->type);
  $type_settings = community_tags_get_content_type_settings($form['#node_type']->type);
  if (!empty($supported_vids)) {
    $form['community_tags'] = array(
      '#type' => 'fieldset',
      '#title' => 'Community tag settings',
      '#tree' => TRUE,
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    );
    $modes = array(
      COMMUNITY_TAGS_MODE_BLOCK => t('Block'),
      COMMUNITY_TAGS_MODE_TAB => t('Tab'),
      COMMUNITY_TAGS_MODE_INLINE => t('Inline'),
      COMMUNITY_TAGS_MODE_NONE => t('None'),
    );
    $form['community_tags']['community_tags_display'] = array(
      '#type' => 'radios',
      '#title' => t('Community tagging form'),
      '#default_value' => variable_get('community_tags_display_' . $form['#node_type']->type, COMMUNITY_TAGS_MODE_TAB),
      '#options' => $modes,
      '#description' => t('How should users be allowed to tag content?'),
    );
    $hide_terms_modes = array(
      'none' => t('No override'),
      'hide' => t('Hide terms'),
      'override' => t('Replace with community tags'),
    );

    // COMMUNITY_TAGS_MODE_HIDE_TERMS_PAGE => t('Page'),
    // COMMUNITY_TAGS_MODE_HIDE_TERMS_TEASER => t('Teaser'),
    $form['community_tags']['community_tags_terms_page'] = array(
      '#type' => 'select',
      '#title' => t('Node page term links display override'),
      '#options' => $hide_terms_modes,
      '#default_value' => $type_settings['community_tags_terms_page'],
    );
    $form['community_tags']['community_tags_terms_teaser'] = array(
      '#type' => 'select',
      '#title' => t('Node teaser term links display override'),
      '#options' => $hide_terms_modes,
      '#default_value' => $type_settings['community_tags_terms_teaser'],
    );
    array_unshift($form['#submit'], 'community_tags_node_type_form_submit');
  }
}
function community_tags_node_type_form_submit($form, &$form_state) {
  $settings = community_tags_get_content_type_settings();

  // set legacy setting
  $display_setting = $form_state['values']['community_tags']['community_tags_display'];
  variable_set('community_tags_display_' . $form['#node_type']->type, $display_setting);
  $settings[$form['#node_type']->type] = $form_state['values']['community_tags'];
  unset($form_state['values']['community_tags']);
  variable_set('community_tags_content_types', $settings);
}

/**
 * Get tag counts by vocabulary and type.
 */
function _community_tags_get_tag_counts_by_type_and_vocabulary() {
  $counts = array();

  // stat query for all ctags even for invalid combos - e.g. unassigned content type
  $tag_count_result = db_query('SELECT td.vid, n.type, count(*) tag_count, count(distinct ct.nid) node_count, count(distinct ct.tid) term_count, count(distinct ct.uid) user_count
     FROM {community_tags} ct
     INNER JOIN {term_data} td ON td.tid = ct.tid
     INNER JOIN {node} n ON n.nid = ct.nid
     GROUP BY td.vid, n.type');

  // put tag counts into vocabularies array. If a new type is found for a vocabulary
  // then tags are left over from a previous configuration - i.e. content type was assigned for tagging but now is not.
  while ($row = db_fetch_object($tag_count_result)) {
    $counts[$row->vid][$row->type] = $row;
  }
  return $counts;
}

/**
 * Get the number of broken tags.
 *
 * @return
 *  The number of broken tags.
 */
function _community_tags_get_count_of_broken_tags() {
  $result = db_query('SELECT count(*) FROM {community_tags} ct
     LEFT JOIN {term_data} td ON td.tid = ct.tid
     LEFT JOIN {users} u ON u.uid = ct.uid
     LEFT JOIN {node} n ON n.nid = ct.nid
     WHERE td.tid IS NULL
     OR u.uid IS NULL
     OR n.nid IS NULL');
  return db_result($result);
}

/**
 * Confirmation for deleting broken tags.
 */
function community_tags_delete_broken_tags_form() {
  $form = array();
  $question = t('Delete %count broken community tags?', array(
    '%count' => $broken_ctag_count,
  ));
  $path = 'admin/settings/community-tags';
  $confirm_form = confirm_form($form, $question, $path);
  return $confirm_form;
}

/**
 * Submit handler for broken tag deletion form.
 */
function community_tags_delete_broken_tags_form_submit($form, &$form_state) {
  db_query('DELETE FROM ct
     USING {community_tags} ct
     LEFT JOIN {term_data} td ON td.tid = ct.tid
     LEFT JOIN {users} u ON u.uid = ct.uid
     LEFT JOIN {node} n ON n.nid = ct.nid
     WHERE td.tid IS NULL
     OR u.uid IS NULL
     OR n.nid IS NULL');
  $deleted = db_affected_rows();
  drupal_set_message(t('@count broken community tags deleted.', array(
    '@count' => $deleted,
  )));
  $form_state['redirect'] = "admin/settings/community-tags";
}

/**
 * Confirmation form for tag purge. Assumes defaults.
 */
function community_tags_delete_all_form(&$form_state, $vocabulary, $content_type) {
  $form = array();
  $type_obj = node_get_types('type', $content_type);

  // get the number of tags that will be deleted
  $counts = _community_tags_get_tag_counts_by_type_and_vocabulary();
  if (empty($counts[$vocabulary->vid][$content_type])) {
    drupal_set_message(t('There are no community tags to delete.'));
  }
  else {
    $counts = $counts[$vocabulary->vid][$content_type];
  }

  // get settings if valid
  $settings = _community_tags_get_settings($vocabulary->vid, $content_type, TRUE);
  $form['#delete_ops'] = array();
  $form['#ct_settings'] = $settings;
  $form['#vocabulary'] = $vocabulary;
  $form['#content_type_obj'] = $type_obj;
  $form['#counts'] = $counts;
  $question = t('Delete %count community tags from the %vocabulary vocabulary on nodes of type %type?.', array(
    '%count' => $counts->tag_count,
    '%vocabulary' => $vocabulary->name,
    '%type' => $type_obj->name,
  ));

  // @todo 2.x get the number of node terms that will be deleted
  // @todo 2.x get the number of orphaned terms that will be deleted
  $description = '';
  if ($settings) {
    $form['#delete_ops']['delete_tags'] = TRUE;
    $description .= '<p>' . t('WARNING: Community tagging is currently enabled for this combination of content type and vocabulary. ') . '</p>';
    $description = '<p>' . t('Community tags will be deleted from the community tags database table.') . '</p>';
    if ($settings['opmode'] & COMMUNITY_TAGS_OPMODE_DELETE_ORPHAN_TERMS) {
      $form['delete_policy'] = array(
        '#type' => 'checkbox',
        '#title' => t('Delete redundant terms'),
        '#weight' => 1,
        '#default_value' => COMMUNITY_TAGS_OPMODE_DELETE_ORPHAN_TERMS,
        '#description' => t('"Delete redundant term" mode is set. All redundant terms will be deleted i.e. all terms matching deleted tags that are no longer used. If you do not want to delete redundant terms as part of this purge operation, un-check this below.'),
      );
    }
  }
  else {
    $form['#delete_ops']['#quick_delete'] = TRUE;
    $description = '<p>' . t('Community tagging is currently disabled for this combination of content type and vocabulary. ') . '</p>';
    $description = '<p>' . t('Community tags will be deleted from the community tags database table and nothing else. ') . '</p>';
  }
  $path = 'admin/settings/community-tags';
  $confirm_form = confirm_form($form, $question, $path, $description);
  $confirm_form['actions']['#weight'] = 5;
  return $confirm_form;
}

/**
 * Submit handler for community_tags_delete_all_form().
 */
function community_tags_delete_all_form_submit($form, &$form_state) {
  $vid = $form['#vocabulary']->vid;
  $content_type = $form['#content_type_obj']->type;
  $settings = $form['#ct_settings'];
  if (isset($form['#delete_ops']['#quick_delete'])) {
    db_query("DELETE FROM ct\n       USING {community_tags} ct, {term_data} AS td, {node} AS n\n       WHERE ct.tid = td.tid\n       AND td.vid = %d\n       AND ct.nid = n.nid\n       AND n.type = '%s'", $vid, $content_type);
    drupal_set_message(t('@delete_count community tags deleted.', array(
      '@delete_count' => db_affected_rows(),
    )));
  }
  else {

    // do batched delete
    $operations = array();
    $mode = $form['#ct_settings']['opmode'];

    // set up the tag delete operations
    // remove mode flags for unchecked mode options
    if ($settings['opmode'] & COMMUNITY_TAGS_OPMODE_DELETE_ORPHAN_TERMS && empty($form_state['values']['delete_policy'])) {

      // unset
      $mode = $settings['opmode'] &= ~COMMUNITY_TAGS_OPMODE_DELETE_ORPHAN_TERMS;
    }
    $config = array(
      'ct_settings' => array(
        'opmode' => $mode,
      ),
    );
    $operations[] = array(
      'community_tags_delete_tags_batch_process',
      array(
        $vid,
        $content_type,
        $form['#counts'],
        $mode,
      ),
    );
    $batch = array(
      'operations' => $operations,
      'finished' => 'community_tags_batch_finished',
      'title' => t('Processing Community Tags Delete Batch'),
      'init_message' => t('Community Tags Delete Batch is starting.'),
      // 'progress_message' => t('Processed @current out of @total.'),
      'error_message' => t('Community Tags Delete Batch has encountered an error.'),
      'file' => drupal_get_path('module', 'community_tags') . '/community_tags.batch.inc',
    );
    batch_set($batch);
  }
}

Functions

Namesort descending Description
community_tags_delete_all_form Confirmation form for tag purge. Assumes defaults.
community_tags_delete_all_form_submit Submit handler for community_tags_delete_all_form().
community_tags_delete_broken_tags_form Confirmation for deleting broken tags.
community_tags_delete_broken_tags_form_submit Submit handler for broken tag deletion form.
community_tags_node_type_form_submit
community_tags_settings Main settings page.
community_tags_sub_settings Form builder; Builds the sub settings form.
community_tags_sub_settings_submit Submit handler for sub settings form.
theme_community_tags_settings Theme function for community tags settings.
_community_tags_get_count_of_broken_tags Get the number of broken tags.
_community_tags_get_tag_counts_by_type_and_vocabulary Get tag counts by vocabulary and type.
_community_tags_node_type_form_alter "Private" implementation of hook_form_alter().
_community_tags_sub_settings_title Title callback for sub-settings configuration page.