You are here

cmf.module in Content Management Filter 7

Same filename and directory in other branches
  1. 5 cmf.module
  2. 6.2 cmf.module
  3. 6 cmf.module

@brief Content management filter module file

This file contains all the common functions used by the module.

File

cmf.module
View source
<?php

/**
 * @file
 * @brief Content management filter module file
 *
 * This file contains all the common functions used by the module.
 */

/**
 * Adding a new filter:
 *
 *  Add an entry to function cmf_filters. Read the docs at the top of that function.
 *  If special form processing is needed (not likely), add it to function cmf_filter_form.
 *  Add handling to cmf_filter_form_refine, if needed (not likely: submit/validate preferred).
 *  Make sure all the database columns are present in cmf_perform_query.
 *
 * @TODO: Is it now possible to provide a hook to other contribs?
 */

/**
 * Implementation of hook_help().
 */
function cmf_help($path, $arg) {
  switch ($path) {
    case "admin/help#cmf":
      return '<p>' . t('This module adds an easier way for administrators to filter the content on a Drupal site for administration purposes.') . '</p>' . '<p>' . t('It is an improvement over the content page in the administration area of Drupal. It can show on the same page nodes and comments and adds new filters like role and author.') . '</p>';
  }
}

/**
 * Implementation of hook_perm().
 */
function cmf_perm() {
  return array(
    'filter and manage site content',
    'view user content list',
  );
}

/**
 * Implementation of hook_menu().
 */
function cmf_menu() {
  $items = array();
  $items['admin/content/filter'] = array(
    'title' => 'Content Management Filter',
    'description' => 'All-in-one advanced filter and management of your site content.',
    'page callback' => 'cmf_admin_content_page',
    'access arguments' => array(
      'filter and manage site content',
    ),
  );
  $items['user/%user/cmf'] = array(
    'title' => 'CMF',
    'description' => 'User-specific content management filter',
    'page callback' => 'cmf_admin_content_page',
    'page arguments' => array(
      1,
    ),
    'access arguments' => array(
      'view user content list',
    ),
    'type' => MENU_LOCAL_TASK,
  );
  $items['cmf/userauto'] = array(
    'title' => 'User autocomplete',
    'page callback' => '_cmf_user_autocomplete',
    'access callback' => 'user_access',
    'access callback' => '_cmf_userspace_perms',
    'access arguments' => array(
      'filter and manage site content',
      'view user content list',
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Check user permissions to see menu item under example.com/user/UID/cmf
 *
 * @param access strings
 * @param access strings
 * @return boolean
 */
function _cmf_userspace_perms($manage_access, $view_access) {
  return user_access($manage_access) || user_access($view_access);
}

/**
 * List node administration filters that can be applied.
 *
 * @param $user 
 *  (object) if a user profile page then user object (defaults to false).
 *
 * @return array with filter properties.
 *   Any property starting with '#' goes directly into the filter form.
 *   '#options' is also used in function cmf_filter_form_submit.
 *   'title' is the text that will show on the filter form.
 *   'single_use' (bool) determines if the filter will be disabled if it is already in use.
 *   'whole_value'  "before" if the value is complete and shown before "is",
 *      "after" if the value is complete and shown after "is",
 *      "no" if it is a portion ("contains"), such as a substring.
 *   'disable' is a list of other filters to remove if this one is selected.
 *   'where' sets a simple WHERE clause for the query (with substitution).
 *   'join' sets a simple JOIN clause for the query (with substitution).
 *   'query_build' provides a funtion for more complex query clauses.
 *   If validation and/or submission handling is needed, use the #element_validate and #submit elements.
 *   Submit handlers must set the return value in $form_state['values'] array.
 *
 *  NOTE: for the 'where' clause some node fields are translated to comment-equivalent
 *   field names if appropriate. See function cmf_perform_query for 'comment' and 'both.'
 */
function cmf_filters($user = FALSE) {

  // Make the array static so it is built only once per page load.
  static $filters;
  if (isset($filters)) {
    return $filters;
  }
  $filters = array();

  // Regular filters.
  $filters['status'] = array(
    'title' => t('node status'),
    'single_use' => FALSE,
    'whole_value' => 'before',
    'query_build' => '_cmf_status_query_build',
    '#type' => 'select',
    '#options' => array(
      'status-1' => t('published'),
      'status-0' => t('not published'),
      'promote-1' => t('promoted'),
      'promote-0' => t('not promoted'),
      'sticky-1' => t('sticky'),
      'sticky-0' => t('not sticky'),
    ),
  );
  $filters['type'] = array(
    'title' => t('node type'),
    'single_use' => TRUE,
    'whole_value' => 'before',
    'where' => "n.type = '%s'",
    '#type' => 'select',
    '#options' => node_type_get_names(),
  );

  //-- workflow filter
  if (module_exists('workflow') && function_exists('workflow_load_all')) {
    $filters['workflow_state'] = array(
      'title' => t('workflow state'),
      'single_use' => FALSE,
      'whole_value' => 'before',
      'query_build' => '_cmf_workflow_query_build',
      '#type' => 'select',
      '#options' => cmf_get_workflows(),
    );
  }

  // The taxonomy filter.
  if (module_exists('taxonomy')) {
    $filters['category'] = array(
      'title' => t('category'),
      'single_use' => FALSE,
      'whole_value' => 'before',
      'query_build' => '_cmf_category_query_build',
      '#type' => 'select',
      '#options' => taxonomy_form_all(TRUE),
    );
  }

  // Cmf filters.
  $filters['title'] = array(
    'title' => t('title/subject'),
    'single_use' => FALSE,
    'whole_value' => 'no',
    'where' => "LOWER(n.title) LIKE LOWER('%%%s%%')",
    '#type' => 'textfield',
    '#element_validate' => array(
      '_cmf_contains_validate',
    ),
  );
  $filters['body_contains'] = array(
    'title' => t('body'),
    'single_use' => FALSE,
    'whole_value' => 'no',
    'where' => "LOWER(r.body) LIKE LOWER('%%%s%%')",
    '#type' => 'textfield',
    '#element_validate' => array(
      '_cmf_contains_validate',
    ),
  );
  $filters['created_after'] = array(
    'title' => t('created after'),
    'single_use' => TRUE,
    'whole_value' => 'after',
    'where' => "n.created >= %d",
    '#type' => 'date',
    '#element_validate' => array(
      'date_validate',
      '_cmf_date_validate',
    ),
    '#submit' => array(
      'cmf_date_handler',
    ),
    '#default_value' => array(
      'year' => date('Y'),
      'month' => 1,
      'day' => 1,
    ),
    '#prefix' => '<div class="date-inline">',
    '#suffix' => '</div>',
  );
  $filters['created_before'] = array(
    'title' => t('created before'),
    'single_use' => TRUE,
    'whole_value' => 'after',
    'where' => "n.created <= %d",
    '#type' => 'date',
    '#element_validate' => array(
      'date_validate',
      '_cmf_date_validate',
    ),
    '#submit' => array(
      'cmf_date_handler',
    ),
    '#default_value' => array(
      'year' => date('Y'),
      'month' => 12,
      'day' => 31,
    ),
    '#prefix' => '<div class="date-inline">',
    '#suffix' => '</div>',
  );
  if (module_exists('locale')) {
    $lang_codes = array(
      '' => t('Neutral'),
    ) + locale_language_list('name');
    $filters['language'] = array(
      'title' => t('language'),
      'single_use' => TRUE,
      'whole_value' => 'before',
      'where' => "n.language ='%s'",
      '#type' => 'select',
      '#options' => $lang_codes,
    );
  }

  // Don't show these on a user page.
  if (!_cmf_valid_user($user)) {
    $filters['user'] = array(
      'title' => t('user list'),
      'single_use' => TRUE,
      'whole_value' => 'before',
      'disable' => array(
        'users',
        'role',
        'blocked',
      ),
      'where' => "u.uid = %d",
      '#type' => 'select',
      '#options' => cmf_get_users('names'),
    );
    $filters['users'] = array(
      'title' => t('user name'),
      'single_use' => TRUE,
      'whole_value' => 'before',
      'disable' => array(
        'user',
        'role',
        'blocked',
      ),
      'where' => "u.name = '%s'",
      '#type' => 'textfield',
      '#submit' => array(
        'cmf_users_handler',
      ),
      '#autocomplete_path' => 'cmf/userauto',
    );
    $filters['role'] = array(
      'title' => t('user role'),
      'single_use' => TRUE,
      'whole_value' => 'before',
      'where' => "u.uid = ur.uid AND ur.rid = %d",
      'join' => "INNER JOIN {users_roles} ur ON u.uid = ur.uid",
      '#type' => 'select',
      '#options' => cmf_get_roles('names'),
    );
    $filters['blocked'] = array(
      'title' => t('user status'),
      'single_use' => TRUE,
      'whole_value' => 'before',
      'where' => "u.status = %d AND u.uid != 0",
      '#type' => 'select',
      '#options' => array(
        1 => t('active'),
        0 => t('blocked'),
      ),
    );
  }
  drupal_alter('cmf_filters', $filters);
  return $filters;
}

/**
 * Called when user goes to example.com/admin/content/filter
 *
 * @return the HTML generated from the $form data structure
 */
function cmf_admin_content_page($user = NULL) {
  if (!isset($_SESSION['cmf_content_kind'])) {
    $_SESSION['cmf_content_kind'] = 'node';
  }
  $output = drupal_get_form('cmf_filter_form', $user);

  // Call the form first, to allow for the form_values array to be populated.
  switch ($_SESSION['cmf_content_kind']) {
    case 'node':
      if (isset($_POST['operation']) && $_POST['operation'] == 'delete' && $_POST['nodes']) {
        return drupal_get_form('node_multiple_delete_confirm', $_POST['nodes']);
      }
      else {
        $output .= drupal_get_form('cmf_admin_nodes_form', $user);
      }
      break;
    case 'comment':
      if ($_POST['operation'] == 'delete' && $_POST['comments']) {

        // The next line is because of http://drupal.org/node/521354.
        module_load_include('inc', 'comment', 'comment.admin');
        return drupal_get_form('comment_multiple_delete_confirm');
      }
      else {
        $output .= drupal_get_form('cmf_admin_comments_form', $user);
      }
      break;
    case 'both':
      $output .= drupal_get_form('cmf_admin_both_form', $user);
  }
  return $output;
}

/**
 * FILTERS
 */

/**
 * Defines the form for content administration filters.
 *
 * @ingroup forms
 *
 * @param (optional) true if the filter to be built serves the user profile page
 * @return array with forms properties
 */
function cmf_filter_form(&$form_state, $user = NULL) {
  $session =& $_SESSION['cmf_overview_filter'];
  $session = is_array($session) ? $session : array();
  $filters = cmf_filters($user);
  drupal_add_css(drupal_get_path('module', 'cmf') . '/cmf.css');
  drupal_add_js(drupal_get_path('module', 'cmf') . '/cmf.jquery.js');
  $form['#user_page_user'] = $user;

  // General settings display (max rows & content kind).
  $form['general'] = array(
    '#type' => 'fieldset',
    '#title' => t('General Settings'),
    '#collapsible' => TRUE,
    '#collapsed' => TRUE,
  );
  $form['general']['max-rows'] = array(
    '#type' => 'textfield',
    '#title' => t('Max rows'),
    '#size' => 5,
    '#default_value' => isset($_SESSION['cmf_max_rows']) ? $_SESSION['cmf_max_rows'] : 50,
    '#prefix' => '<div class="container-inline">',
    '#suffix' => '</div>',
  );
  $form['general']['show_nid'] = array(
    '#type' => 'radios',
    '#title' => t('Show node ID'),
    '#options' => array(
      0 => t('No'),
      1 => t('Yes'),
    ),
    '#default_value' => isset($_SESSION['cmf_show_nid']) ? $_SESSION['cmf_show_nid'] : 0,
    '#prefix' => '<div class="container-inline"><div class="cmf-radios">',
    '#suffix' => '</div></div><div class="clear-block"></div>',
  );
  $form['general']['kind'] = array(
    '#type' => 'select',
    '#title' => t('Content'),
    '#options' => array(
      'node' => t('node'),
      'comment' => t('comment'),
      'both' => t('both'),
    ),
    '#default_value' => isset($_SESSION['cmf_content_kind']) ? $_SESSION['cmf_content_kind'] : 'node',
    '#prefix' => '<div class="container-inline">',
  );
  $form['general']['buttons']['apply'] = array(
    '#type' => 'submit',
    '#value' => t('Apply'),
    '#submit' => array(
      'cmf_filter_form_apply',
    ),
    '#suffix' => '</div>',
  );
  $i = 0;
  $form['filters'] = array(
    '#type' => 'fieldset',
    '#title' => t('Show only items where'),
    '#theme' => 'node_filters',
  );
  foreach ($session as $filter) {
    list($type, $value) = $filter;

    // Special handling by type of filter.
    switch ($type) {
      case 'category':

        // Load term name from DB rather than search and parse options array.
        $value = taxonomy_get_term($value);
        $value = $value->name;
        break;
      case 'body_contains':
      case 'title':

        // Add quotes.
        $value = "'" . $value . "'";
        break;
      case 'created_after':
      case 'created_before':

        // Format as human date.
        $fmt = variable_get('date_format_short', variable_get('date_format_short_custom', 'Y M j'));
        $fmt = trim(str_replace(array(
          'H',
          'i',
          ':',
        ), '', $fmt));
        $value = format_date($value, 'custom', $fmt, 0);
        break;
      default:
        if (isset($filters[$type]['#options'])) {
          $value = $filters[$type]['#options'][$value];
        }
    }

    // Add applicable filter verbiage. Note '@' and '%' do check_plain.
    $trans_strings = array(
      '%filter_name' => $filters[$type]['title'],
      '%filter_value' => $value,
    );
    if ($i++) {
      $and = '<em>' . t('and') . '</em> ';
    }
    else {
      $and = NULL;
    }
    switch ($filters[$type]['whole_value']) {
      case 'after':
        $form['filters']['current'][] = array(
          '#value' => $and . t('is %filter_name %filter_value', $trans_strings),
        );
        break;
      case 'before':
        $form['filters']['current'][] = array(
          '#value' => $and . t('%filter_name is %filter_value', $trans_strings),
        );
        break;
      case 'no':
        $form['filters']['current'][] = array(
          '#value' => $and . t('%filter_name contains %filter_value', $trans_strings),
        );
        break;
    }

    // Remove mutually exclusive filters.
    if (isset($filters[$type]['disable'])) {
      foreach ($filters[$type]['disable'] as $exclude) {
        unset($filters[$exclude]);
      }
    }

    // Remove the single use option if it is already being filtered on.
    if ($type == 'status') {

      //      drupal_set_message('okay, what do I get rid of? '. print_r($filter,true));
      //      dsm(print_r($filters['status'],true));
      // Strip current value, then unset both values.
      $status = drupal_substr($filter[1], 0, -1);
      unset($filters['status']['#options'][$status . '0'], $filters['status']['#options'][$status . '1']);
    }
    if ($filters[$type]['single_use']) {
      unset($filters[$type]);
    }
  }

  // Prepare form fields for filters.
  foreach ($filters as $key => $filter) {
    $names[$key] = $filter['title'];

    // Normal form field handling.
    $form['filters']['status'][$key] = array();
    foreach ($filter as $element => $value) {

      // If the filter element name begins with '#' then it is for the form.
      if (drupal_substr($element, 0, 1) == '#') {
        $form['filters']['status'][$key][$element] = $value;
      }
    }

    // Special form field handling.
    // Uncomment the 'switch', add handling case adding to the current form field array.
    // The stuff here is for an example and can be removed.

    /*    switch ($key) {
          case 'created_before':
            $form['filters']['status'][$key] += array(
              '#default_value' => array('year' => date('Y'), 'month' => 12, 'day' =>31),
              );
            break;

          case 'created_after':
            $form['filters']['status'][$key] += array(
              '#default_value' => array('year' => date('Y'), 'month' => 1, 'day' =>1),
              );
            break;
        } /* */
  }

  // Building radio buttons.
  $keyz = array_keys($names);
  $form['filters']['filter'] = array(
    '#type' => 'radios',
    '#options' => $names,
    '#default_value' => $keyz[0],
    '#attributes' => array(
      'class' => 'multiselect',
    ),
  );

  // Building buttons depending on the filter state.
  $form['filters']['buttons']['submit'] = array(
    '#type' => 'submit',
    '#value' => count($session) ? t('Refine') : t('Filter'),
    '#submit' => array(
      'cmf_filter_form_refine',
    ),
  );
  if (count($session)) {
    $form['filters']['buttons']['undo'] = array(
      '#type' => 'submit',
      '#submit' => array(
        'cmf_filter_form_undo',
      ),
      '#value' => t('Undo'),
    );
    $form['filters']['buttons']['reset'] = array(
      '#type' => 'submit',
      '#submit' => array(
        'cmf_filter_form_reset',
      ),
      '#value' => t('Reset'),
    );
  }
  return $form;
}

/**
 * Handle post-validation form submission.
 *
 * @ingroup forms
 *
 * @param the ID of the passed form
 * @param array with the form properties values
 */
function cmf_filter_form_refine($form, &$form_state) {
  $filters = cmf_filters();
  if (isset($form_state['values']['filter'])) {
    $filter = $form_state['values']['filter'];

    // Convert AJAX search value to select box value.
    // If a filter has specified a submit function, call it. (Only allow one value.)
    if (isset($filters[$filter]['#submit'])) {
      $form_state['values'][$filter] = call_user_func($filters[$filter]['#submit'][0], $form, $form_state);
    }

    // Repeating this allows submit handlers (e.g. users) to alter the filter name.
    $filter = $form_state['values']['filter'];

    // Flatten the options array to accommodate hierarchical/nested options.
    // If there are no options, fake it.
    if (isset($filters[$filter]['#options'])) {
      $flat_options = form_options_flatten($filters[$filter]['#options']);
    }
    else {
      $flat_options = array(
        $form_state['values'][$filter] => 1,
      );
    }

    // If the option is selected or there are no options, set the session value.
    if (isset($flat_options[$form_state['values'][$filter]])) {
      $_SESSION['cmf_overview_filter'][] = array(
        $filter,
        $form_state['values'][$filter],
      );
    }
  }
}

/**
 * Submit handler for 'Undo' button.
 */
function cmf_filter_form_undo($form, &$form_state) {
  array_pop($_SESSION['cmf_overview_filter']);
}

/**
 * Submit handler for 'Reset' button.
 */
function cmf_filter_form_reset($form, &$form_state) {
  $_SESSION['cmf_overview_filter'] = array();
}

/**
 * Submit handler for 'Apply' button.
 */
function cmf_filter_form_apply($form, &$form_state) {
  $_SESSION['cmf_max_rows'] = $form_state['values']['max-rows'];
  $_SESSION['cmf_show_nid'] = $form_state['values']['show_nid'];
  $_SESSION['cmf_content_kind'] = $form_state['values']['kind'];
  $user = $form['#user_page_user'];
  if (_cmf_valid_user($user)) {
    $form_state['redirect'] = 'user/' . $user->uid . '/cmf';
  }
  else {
    $form_state['redirect'] = 'admin/content/filter';
  }
}
function _cmf_valid_user($user) {
  if (!empty($user->uid)) {
    return TRUE;
  }
  else {
    return FALSE;
  }
}

/**
 * Validation handler for filter doing "contains".
 */
function _cmf_contains_validate($form) {

  // Make sure this was triggered by the selected filter.
  if ($form['#name'] == $form['#post']['filter']) {

    // Strip blanks to see if the field is empty.
    $value = trim($form['#value']);
    if (empty($value)) {
      form_error($form, t('%name text value may not be empty.', array(
        '%name' => $form['#name'],
      )));
    }
  }
}

/**
 * Validation handler for filter doing "dates".
 */
function _cmf_date_validate($form) {

  // Make sure this was triggered by the selected filter.
  if ($form['#name'] != $form['#post']['filter']) {
    return;
  }

  // Determine correct time values depending on the filter name.
  if (strpos($form['#name'], 'before') === FALSE) {
    $entered_date = mktime(0, 0, 0, (int) $form['#value']['month'], (int) $form['#value']['day'], (int) $form['#value']['year'], 1);
  }
  else {
    $entered_date = mktime(11, 59, 59, (int) $form['#value']['month'], (int) $form['#value']['day'], (int) $form['#value']['year'], 1);
  }

  // drupal_set_message('_cmf_date_validate, filter='.$form['#post']['filter'].', name='.$form['#name'].' '.print_r($form['#value'], true).', calc='.format_date($entered_date));
  // drupal_set_message('current filters='.print_r($_SESSION['cmf_overview_filter'], true));
  if (empty($entered_date)) {
    form_error($form, t('%name date value may not be empty.', array(
      '%name' => $form['#name'],
    )));
  }
}

/**
 * Submit handler for 'users' filter.
 * Ignore the Coder flag - it is a false positive.
 */
function cmf_users_handler($form, &$form_state) {
  $name = $form_state['values']['users'];

  // Get uid to put in 'user' filter value.
  $form_state['values']['user'] = db_result(db_query('SELECT uid FROM {users} WHERE name = "%s"', $name));

  // Switch filter type to user.
  $form_state['values']['filter'] = 'user';
  return $name;
}

/**
 * Submit handler for date filters.
 * Ignore the Coder flag - it is a false positive.
 */
function cmf_date_handler($form, &$form_state) {
  $filter = $form_state['values']['filter'];
  return gmmktime(0, 0, 0, $form_state['values'][$filter]['month'], $form_state['values'][$filter]['day'], $form_state['values'][$filter]['year']);
}

/**
 * Theme cmf administration filter form.
 *
 * @themable
 */
function theme_cmf_filter_form($form) {
  return '<div id="cmf_header">' . drupal_render($form['general']) . '</div>' . '<div id="node-admin-filter">' . drupal_render($form['filters']) . '</div>' . drupal_render($form);
}

/**
 *  QUERIES
 */

/**
 * Build the variable parts of the query to be performed regarding the filter status.
 *
 * @return associative array with WHERE JOIN query parts and respective arguments
 */
function cmf_build_filter_query() {
  $filters = cmf_filters();

  // Build query
  $where = $args = array();
  $join = '';
  foreach ($_SESSION['cmf_overview_filter'] as $index => $filter) {
    list($key, $value) = $filter;

    // Does the filter have query_build function?
    if (isset($filters[$key]['query_build'])) {
      $result = call_user_func($filters[$key]['query_build'], $key, $value, $index);
      if (isset($result['where'])) {
        $where[] = $result['where'];
      }
      if (isset($result['join'])) {
        $join .= $result['join'] . ' ';
      }
      if (isset($result['value'])) {
        $value = $result['value'];
      }
    }
    else {

      // Does the filter have where and/or joins specified?
      if (isset($filters[$key]['where'])) {
        $where[] = $filters[$key]['where'];
      }
      if (isset($filters[$key]['join'])) {
        $join .= $filters[$key]['join'] . ' ';
      }
    }
    $args[] = $value;
  }
  $where = count($where) ? 'WHERE ' . implode(' AND ', $where) : '';
  return array(
    'where' => $where,
    'join' => $join,
    'args' => $args,
  );
}

/**
 * Query_build function for category field.
 */
function _cmf_category_query_build($key, $value, $index) {
  $table = "tn{$index}";
  return array(
    'where' => "{$table}.tid = %d",
    'join' => "INNER JOIN {term_node} {$table} ON n.nid = {$table}.nid ",
    'value' => $value,
  );
}

/**
 * Query_build function for status field.
 */
function _cmf_status_query_build($key, $value, $index) {

  // Note: no exploitable hole as $key/$value have already been checked when submitted.
  list($key, $value) = explode('-', $value, 2);
  if ($key == 'sticky') {

    // This allows sticky-encoded weighting (like Weight module) to also work.
    return array(
      'where' => 'n.' . $key . ' ' . ($value == 1 ? '>' : '<') . ' %d',
      'value' => 1 - $value,
    );
  }
  else {
    return array(
      'where' => 'n.' . $key . ' = %d',
      'value' => $value,
    );
  }
}

/**
 * Build the header of the result table.
 *
 * @param $user_page_user
 *    if we are on a user page, the user that page belongs to, not the current user
 *
 * @return array respecting tablesort_sql()
 */
function cmf_build_header($user_page_user = NULL) {
  $header = array();
  if (user_access('filter and manage site content')) {
    $header[] = theme('table_select_header_cell');
  }
  if ($_SESSION['cmf_show_nid']) {
    $header[] = array(
      'data' => t('ID'),
      'field' => 'nid',
    );
  }
  switch ($_SESSION['cmf_content_kind']) {
    case 'node':
      $header[] = array(
        'data' => t('Title'),
        'field' => 'title',
      );
      $header[] = array(
        'data' => t('Kind'),
      );
      $header[] = array(
        'data' => t('Node Type'),
        'field' => 'type',
      );
      if (!_cmf_valid_user($user_page_user)) {
        $header[] = array(
          'data' => t('Author'),
          'field' => 'username',
        );
      }
      $header[] = array(
        'data' => t('Status'),
        'field' => 'status',
      );
      $header[] = array(
        'data' => t('Time'),
        'field' => 'created',
        'sort' => 'desc',
      );
      if (module_exists('locale')) {
        $header[] = array(
          'data' => t('Language'),
          'field' => 'language',
        );
      }
      break;
    case 'comment':
      $header[] = array(
        'data' => t('Subject'),
        'field' => 'subject',
      );
      $header[] = array(
        'data' => t('Kind'),
      );
      $header[] = array(
        'data' => t('Node Type'),
        'field' => 'type',
      );
      if (!_cmf_valid_user($user_page_user)) {
        $header[] = array(
          'data' => t('Author'),
          'field' => 'username',
        );
      }
      $header[] = array(
        'data' => t('Status'),
        'field' => 'status',
      );
      $header[] = array(
        'data' => t('Time'),
        'field' => 'created',
        'sort' => 'desc',
      );
      break;
    case 'both':
      $header[] = array(
        'data' => t('Title') . '/' . t('Subject'),
        'field' => 'title',
      );
      $header[] = array(
        'data' => t('Kind'),
      );
      $header[] = array(
        'data' => t('Node Type'),
        'field' => 'type',
      );
      if (!_cmf_valid_user($user_page_user)) {
        $header[] = array(
          'data' => t('Author'),
          'field' => 'username',
        );
      }
      $header[] = array(
        'data' => t('Node Status'),
        'field' => 'status',
      );
      $header[] = array(
        'data' => t('Time'),
        'field' => 'created',
        'sort' => 'desc',
      );
      if (module_exists('locale')) {
        $header[] = array(
          'data' => t('Language'),
          'field' => 'language',
        );
      }
      break;
  }
  if (user_access('filter and manage site content')) {
    $header[] = array(
      'data' => t('Operations'),
    );
  }
  return $header;
}

/**
 * Perform adjusted query.
 * 
 * @param $user_page_user
 *    if we are on a user page, the user that page belongs to, not the current user
 *
 *
 * @param array respecting tablesort_sql()
 * @return result of permormed query
 */
function cmf_perform_query($header, $kind = NULL, $user_page_user = NULL) {
  $filter = cmf_build_filter_query();
  if (is_null($kind)) {
    $kind = $_SESSION['cmf_content_kind'];
  }
  $where = ' ' . $filter['where'];
  if (_cmf_valid_user($user_page_user)) {
    $where .= ' AND u.uid = ' . $user_page_user->uid;
  }
  $cwhere = str_replace(array(
    'n.title',
    'n.uid',
    'r.body',
  ), array(
    'c.subject',
    'c.uid',
    'c.comment',
  ), $where);
  switch ($kind) {
    case 'node':
      return pager_query('SELECT n.nid, n.title, n.type, n.status, n.created, ' . 'n.changed, n.promote, n.sticky, n.moderate, n.language, ' . 'u.name AS username, u.uid, r.body ' . 'FROM {node} n ' . 'JOIN {node_revisions} r ON r.vid = n.vid ' . 'INNER JOIN {users} u ON n.uid = u.uid ' . $filter['join'] . $where . tablesort_sql($header), isset($_SESSION['cmf_max_rows']) ? $_SESSION['cmf_max_rows'] : 50, 0, NULL, $filter['args']);
      break;
    case 'comment':
      return pager_query('SELECT c.cid, c.subject, c.nid, c.comment, c.timestamp AS created, ' . 'c.status, c.name, c.homepage, u.name AS username, u.uid, n.type, c.comment AS body ' . 'FROM {comments} c ' . 'INNER JOIN {node} n ON c.nid = n.nid ' . 'INNER JOIN {users} u ON u.uid = c.uid ' . $filter['join'] . $cwhere . tablesort_sql($header), isset($_SESSION['cmf_max_rows']) ? $_SESSION['cmf_max_rows'] : 50, 0, NULL, $filter['args']);
      break;
    case 'both':
      $args = array_merge($filter['args'], $filter['args']);
      $count_query = 'SELECT (' . 'SELECT COUNT(*) FROM {node} n ' . 'JOIN {node_revisions} r ON r.vid = n.vid ' . 'INNER JOIN {users} u ON n.uid = u.uid ' . $filter['join'] . $where . ') + (' . 'SELECT COUNT(*) FROM {comments} c INNER JOIN {node} n ON c.nid = n.nid ' . 'INNER JOIN {users} u ON u.uid = c.uid ' . $filter['join'] . $cwhere . ') AS count';
      return pager_query('SELECT 0 AS cid, n.nid, n.title, NULL AS comment, n.type, n.status, n.created, ' . 'n.changed, n.promote, n.sticky, n.moderate, n.language, ' . 'u.name AS username, u.uid, r.body ' . 'FROM {node} n ' . 'JOIN {node_revisions} r ON r.vid = n.vid ' . 'INNER JOIN {users} u ON n.uid = u.uid ' . $filter['join'] . $where . ' UNION SELECT c.cid, c.nid, c.subject AS title, c.comment, n.type, c.status, c.timestamp AS created, ' . '0 AS changed, 0 AS promote, 0 AS sticky, 0 AS moderate, "" AS language, ' . 'u.name AS username, u.uid, c.comment AS body ' . ' FROM {comments} c INNER JOIN {node} n ON c.nid = n.nid INNER JOIN {users} u ON u.uid = c.uid ' . $filter['join'] . $cwhere . tablesort_sql($header), isset($_SESSION['cmf_max_rows']) ? $_SESSION['cmf_max_rows'] : 50, 0, $count_query, $args);
      break;
  }
}

/**
 *  RESULTS
 */
module_load_include('inc', 'cmf', '/node');
module_load_include('inc', 'cmf', '/comment');
module_load_include('inc', 'cmf', '/both');

/*
 *  AUX
 */

/**
 * Builds a list of available users.
 *
 * @param the format in which to return the list
 * @return array of all available users
 */
function cmf_get_users($op) {
  switch ($op) {
    case 'names':
      $result = db_query('SELECT uid, name FROM {users} WHERE uid <> 0 ORDER BY name');
      break;
  }
  $users = array();
  $users[0] = variable_get('anonymous', t('anonymous'));
  while ($account = db_fetch_object($result)) {
    $users[$account->uid] = $account->name;
  }
  return $users;
}

/**
 * Menu callback; Retrieve a JSON object containing autocomplete suggestions for existing users.
 */
function _cmf_user_autocomplete($string = '') {
  $matches = array();
  if ($string) {
    $result = db_query("SELECT name FROM {users} WHERE LOWER(name) LIKE LOWER('%s%%')", $string);
    while ($account = db_fetch_object($result)) {
      $matches[$account->name] = check_plain($account->name);
    }
  }
  drupal_json($matches);
}

/**
 * Builds a list of available roles
 *
 * @param the format in which to return the list
 * @return array of all available roles
 */
function cmf_get_roles($op) {
  switch ($op) {
    case 'names':
      $result = db_query('SELECT rid, name FROM {role} ORDER BY name');
      break;
  }
  $roles = array();
  while ($role = db_fetch_object($result)) {
    $roles[$role->rid] = $role->name;
  }
  return $roles;
}

/**
 * Get the html code of an image
 *
 * @param the pretended image
 * @return html tag img
 */
function _cmf_get_img($action, $title) {
  $path = base_path() . drupal_get_path('module', 'cmf') . '/images/' . $action . '.png';
  if ($title == NULL) {
    $html = '<img src="' . $path . '" alt="untitled image" />';
  }
  else {
    $html = '<img src="' . $path . '" title="' . $title . '" alt="' . $title . '" />';
  }
  return $html;
}

/**
 * Theme (node) type cell on table result.
 *
 * @ingroup themable
 *
 * @param 0 or node type key
 * @return formated (node) type
 */
function theme_cmf_type($type) {
  return db_result(db_query('SELECT name FROM {node_type} WHERE type = "%s"', $type));
}

/**
 * Implementation of hook_theme().
 *
 * @ingroup themable
 */
function cmf_theme() {
  return array(
    'cmf_filter_form' => array(
      'arguments' => array(
        'form' => NULL,
      ),
    ),
    'cmf_user' => array(
      'arguments' => array(
        'uid' => NULL,
      ),
    ),
    'cmf_type' => array(
      'arguments' => array(
        'type' => NULL,
      ),
    ),
    'cmf_admin_nodes_form' => array(
      'file' => 'node.inc',
      'arguments' => array(
        'form' => NULL,
      ),
    ),
    'cmf_admin_comments_form' => array(
      'file' => 'comment.inc',
      'arguments' => array(
        'form' => NULL,
      ),
    ),
    'cmf_admin_both_form' => array(
      'file' => 'both.inc',
      'arguments' => array(
        'form' => NULL,
      ),
    ),
  );
}

/**
 * Theme user cell on table result.
 *
 * @ingroup themable
 *
 * @param user ID
 * @return clickable username with status
 */
function theme_cmf_user($uid) {
  if ($uid == 0) {
    return variable_get('anonymous', t('anonymous'));
  }
  $result = db_query('SELECT name, status FROM {users} WHERE uid = %d', $uid);
  $user = db_fetch_array($result);
  $url_alias = _cmf_get_user_path($uid);
  $display = $user['status'] ? $user['name'] : _cmf_get_img('blocked', t('blocked')) . ' ' . $user['name'];
  if (user_access('access user profiles')) {
    $url = $url_alias ? $url_alias : 'user/' . $uid;
    $output = l($display, $url, array(
      'html' => TRUE,
    ));
  }
  else {
    $output = $display;
  }
  return $output;
}

/**
 * Get the alias path to a user profile
 *
 * @param user ID
 * @return the relative URL of the user profile
 */
function _cmf_get_user_path($uid) {
  return db_result(db_query("\n    SELECT dst\n    FROM {url_alias}\n    WHERE src = '%s';", 'user/' . $uid));
}

/**
 * Get the term for a forum node
 *
 * @param node ID
 * @return the name and forum description
 */
function _cmf_get_forum($nid) {
  $path = array();
  $node = node_load($nid);
  $parents = taxonomy_get_parents_all($node->tid);
  foreach ($parents as $parent) {
    $path[] = $parent->name;
  }
  return implode(' > ', array_reverse($path));
}
function cmf_get_workflows() {
  if (!module_exists('workflow')) {
    return;
  }
  if (!function_exists('workflow_load_all')) {
    return;
  }
  $workflows = workflow_load_all();
  $states = array();
  if (is_array($workflows)) {
    foreach ($workflows as $workflow) {
      if (is_array($workflow->states)) {
        foreach ($workflow->states as $state) {
          $states[$state->name] = $workflow->label . " : " . $state->label;
        }
      }
    }
  }
  return $states;
}

/**
 * Query_build function for workflow
 */
function _cmf_workflow_query_build($key, $value, $index) {
  if (!module_exists('workflow')) {
    return;
  }
  if (!function_exists('workflow_load_all')) {
    return;
  }
  $table = "workflow_node";
  return array(
    'where' => "{$table}.state_name = '%s'",
    'join' => "INNER JOIN {$table} {$table} ON n.nid = {$table}.nid ",
    'value' => $value,
  );
}

/**
 * Implementation of hook_form_alter().
 */
function cmf_form_alter(&$form, $form_state, $form_id) {
  switch ($form_id) {

    // When CMF is only showing nodes or only showing comments, the deletion is handled by
    // re-using the multiple delete functions provided by node module and comment module.
    case 'node_multiple_delete_confirm':
    case 'comment_multiple_delete_confirm':

      // The target page after deleting is set in the corresponding submit function so we need
      // to add another submit function which will be executed after that one, to change the
      // redirect value to our page.
      $form['#submit'][] = '_cmf_redirect_to_cmf';

      // The cancel link will have been defined to return to admin/content/node (or admin/content/comment)
      // so we need to change it here to return to admin/content/filter instead.
      $form['actions']['cancel']['#value'] = l(t('Cancel'), 'admin/content/filter');
      break;
    default:
  }
}

/**
 * Additional node deletion 'submit' function.
 */
function _cmf_redirect_to_cmf($form, &$form_state) {

  // Set the final destination after deleting node(s) or comments(s).
  // Could have used a GOTO here, but safer to set redirect.
  $form_state['redirect'] = 'admin/content/filter';
}

Functions

Namesort descending Description
cmf_admin_content_page Called when user goes to example.com/admin/content/filter
cmf_build_filter_query Build the variable parts of the query to be performed regarding the filter status.
cmf_build_header Build the header of the result table.
cmf_date_handler Submit handler for date filters. Ignore the Coder flag - it is a false positive.
cmf_filters List node administration filters that can be applied.
cmf_filter_form Defines the form for content administration filters.
cmf_filter_form_apply Submit handler for 'Apply' button.
cmf_filter_form_refine Handle post-validation form submission.
cmf_filter_form_reset Submit handler for 'Reset' button.
cmf_filter_form_undo Submit handler for 'Undo' button.
cmf_form_alter Implementation of hook_form_alter().
cmf_get_roles Builds a list of available roles
cmf_get_users Builds a list of available users.
cmf_get_workflows
cmf_help Implementation of hook_help().
cmf_menu Implementation of hook_menu().
cmf_perform_query Perform adjusted query.
cmf_perm Implementation of hook_perm().
cmf_theme Implementation of hook_theme().
cmf_users_handler Submit handler for 'users' filter. Ignore the Coder flag - it is a false positive.
theme_cmf_filter_form Theme cmf administration filter form.
theme_cmf_type Theme (node) type cell on table result.
theme_cmf_user Theme user cell on table result.
_cmf_category_query_build Query_build function for category field.
_cmf_contains_validate Validation handler for filter doing "contains".
_cmf_date_validate Validation handler for filter doing "dates".
_cmf_get_forum Get the term for a forum node
_cmf_get_img Get the html code of an image
_cmf_get_user_path Get the alias path to a user profile
_cmf_redirect_to_cmf Additional node deletion 'submit' function.
_cmf_status_query_build Query_build function for status field.
_cmf_userspace_perms Check user permissions to see menu item under example.com/user/UID/cmf
_cmf_user_autocomplete Menu callback; Retrieve a JSON object containing autocomplete suggestions for existing users.
_cmf_valid_user
_cmf_workflow_query_build Query_build function for workflow