You are here

weight.module in Weight 6

This module uses the sticky column of the node table to add weighting to nodes.

File

weight.module
View source
<?php

/**
 * @file
 * This module uses the sticky column of the node table
 * to add weighting to nodes.
 */

/**
 * Implementation of hook_help().
 */
function weight_help($path, $args) {
  switch ($path) {
    case 'admin/setting/weight':
    case 'admin/modules#description':
      return t('Add weight-based sorting to nodes.');
    case 'admin/help#weight':
      return t('<h3>Description:</h3><p>The weight module adds a weight option to enabled node types. It uses the "sticky" field in the database to store weights as well as sticky information (so that feature is not lost). Nodes will be sorted first by stickiness, then by weight (lightest to heaviest), then by creation date.</p>
        <h4>Setup:</h4><p>To enable weight sorting on existing nodes, visit the <a href="@setup">weight db setup page</a> and select which node types to allow weighting. When you click "Save configuration," the module will convert old sticky values to new weight-encoded values for proper sorting. If you de-select a type, weights on all nodes of that type will be converted back to standard sticky values.</p>
        <h4>Permissions:</h4><p>Users with "administer nodes" permission will always be able to adjust weight for enabled node types. However, enabling "assign node weight" will allow non-node-admin users to adjust weight on their own nodes. Find these settings <a href="@access">here</a>.</p>
        <h4>Bulk weight management</h4><p>You may easily manage the weight of multiple nodes simultaneously by using the <a href="@node_admin"> node admin page</a>.</p>', array(
        '@setup' => url('admin/settings/weight/setup'),
        '@access' => url('admin/user/permissions'),
        '@node_admin' => url('admin/content/node'),
      ));
    case 'admin/content/node':
      return t('<strong>Note:</strong> When the weight module is enabled, it is not possible to filter based on sticky status.');
  }
}

/**
 * Implements hook_perm().
 */
function weight_perm() {
  return array(
    'assign node weight',
  );
}

/**
 * Implementation of hook_menu().
 */
function weight_menu() {
  $items = array();

  // Ajax callback for weight changer page.
  $items['admin/node/weight/_weight_change'] = array(
    'page callback' => '_weight_change',
    'access arguments' => array(
      'administer nodes',
    ),
    'type' => MENU_CALLBACK,
  );

  // Top level settings.
  $items['admin/settings/weight'] = array(
    'title' => 'Weight',
    'access arguments' => array(
      'administer site configuration',
    ),
    'description' => 'Add weight-based sorting to nodes.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'weight_settings_form',
    ),
    'file' => 'weight.admin.inc',
  );
  return $items;
}

/**
 * Implementation of hook_theme_registry_alter().
 *
 * Swap in our own replacement for theme_node_admin_nodes(), allowing us to
 * access the node list early on.
 */
function weight_theme_registry_alter(&$theme_registry) {
  if (variable_get('weight_content_admin', FALSE)) {
    $path = drupal_get_path('module', 'weight');
    $theme_registry['node_admin_nodes']['function'] = 'theme_weight_node_admin_nodes';
    $theme_registry['node_admin_nodes']['file'] = $path . '/weight.module';
    $theme_registry['node_admin_nodes']['theme path'] = $path;
    $theme_registry['node_admin_nodes']['theme paths'][0] = $path;
  }
}
function weight_nodeapi(&$node, $op) {
  $weight_node_types = variable_get('weight_node_types', array_flip(node_get_types('names')));
  if (in_array($node->type, $weight_node_types)) {
    switch ($op) {
      case 'presave':

        // Non-weighted nodes have a weight of zero.
        if (is_null($node->node_weight)) {
          $node->node_weight = variable_get('weight_default', 0);
        }

        // If the admin wants to use the menu weight, see if there is one.
        if (variable_get('weight_use_menu', FALSE)) {
          $node->node_weight = isset($node->menu['link_title']) && !empty($node->menu['link_title']) ? $node->menu['weight'] : $node->node_weight;
        }

        // Encode weight into the sticky value for the database.
        _weight_encode($node);
        break;
      case 'load':
        _weight_decode($node);
        break;
    }
  }
}

/**
 * Theme node administration overview.
 * Mostly copied from node.admin.inc.
 *
 * @ingroup themeable
 */
function theme_weight_node_admin_nodes($form) {

  // If there are rows in this form, then $form['title'] contains a list of
  // the title form elements.
  $has_posts = isset($form['title']) && is_array($form['title']);
  $select_header = $has_posts ? theme('table_select_header_cell') : '';
  $header = array(
    $select_header,
    t('Title'),
    t('Type'),
    t('Author'),
    t('Status'),
  );
  if (isset($form['language'])) {
    $header[] = t('Language');
  }
  $header[] = t('Operations');
  $header[] = t('Weight');
  $output = '';
  $output .= drupal_render($form['options']);
  if ($has_posts) {
    foreach (element_children($form['title']) as $key) {
      $row = array();
      $row[] = drupal_render($form['nodes'][$key]);
      $row[] = drupal_render($form['title'][$key]);
      $row[] = drupal_render($form['name'][$key]);
      $row[] = drupal_render($form['username'][$key]);
      $row[] = drupal_render($form['status'][$key]);
      if (isset($form['language'])) {
        $row[] = drupal_render($form['language'][$key]);
      }
      $row[] = drupal_render($form['operations'][$key]);
      $row[] = drupal_render($form['weight'][$key]);
      $rows[] = $row;
    }
  }
  else {
    $rows[] = array(
      array(
        'data' => t('No posts available.'),
        'colspan' => '6',
      ),
    );
  }
  $output .= theme('table', $header, $rows);
  if ($form['pager']['#value']) {
    $output .= drupal_render($form['pager']);
  }
  $output .= drupal_render($form);
  return $output;
}

/**
 * Implementation of hook_form_alter().
 *
 * This is where we tweak the admin/content/node to include our weight
 * selector; hide the 'sticky' filter (it won't work when using weight module),
 * and add some help text to the form.
 */
function weight_form_alter(&$form, $form_state, $form_id) {
  $weight_node_types = variable_get('weight_node_types', array_flip(node_get_types('names')));

  // Node edit page weight selector.
  if (isset($form['type']) && $form['type']['#value'] . '_node_form' == $form_id) {
    $node = $form['#node'];
    if (user_access('assign node weight') || user_access('administer nodes')) {
      if (in_array($node->type, $weight_node_types)) {
        $range = variable_get('weight_range', 20);
        $position = variable_get('weight_position', 0);
        $default = variable_get('weight_default', 0);
        $where = 'weight_form';
        if ($position == 10 && user_access('administer nodes')) {

          // We will add it to the Workflow fieldset.
          $where = 'options';
          $form['options']['#collapsed'] = isset($node->node_weight) ? $node->node_weight == 0 : FALSE;
        }
        else {

          // Add the node weight selector fieldset.
          $form['weight_form'] = array(
            '#type' => 'fieldset',
            '#title' => variable_get('weight_label', t('Node Weight')),
            '#collapsible' => TRUE,
            '#collapsed' => isset($node->node_weight) ? $node->node_weight == 0 : FALSE,
            '#group' => 'additional_settings',
            '#weight' => $position,
          );
        }
        $form[$where]['node_weight'] = array(
          '#type' => 'weight',
          '#title' => t('Weight'),
          '#default_value' => isset($node->node_weight) ? (int) $node->node_weight : $default,
          '#delta' => $range,
          '#description' => t('In a node list context (such as the front page or term pages), list items (e.g. "teasers") will be ordered by "stickiness" then by "node weight" then by "authored on" datestamp. Items with a lower (lighter) node weight value will appear above those with a higher (heavier) value.'),
        );
        if (variable_get('weight_use_menu', FALSE)) {
          $form[$where]['node_weight']['#description'] .= '<br /> ' . t('<strong>Note</strong>: If this node is used in a menu, then this weight will be ignored.');
        }
      }
    }
    else {
      $form['node_weight'] = array(
        '#type' => 'value',
        '#value' => (int) $node->node_weight,
      );
    }
  }
}

/**
 * Implementation of hook_form_FORM_ID_alter().
 */
function weight_form_node_admin_content_alter(&$form, $form_state) {
  if (!variable_get('weight_content_admin', FALSE)) {
    return;
  }
  $weight_node_types = variable_get('weight_node_types', array_flip(node_get_types('names')));
  $weight_node_type_names = array();
  foreach ($weight_node_types as $type) {
    $weight_node_type_names[] = node_get_types('name', $type);
  }
  $weight_range = variable_get('weight_range', 20);
  $weights = array();
  for ($i = -$weight_range; $i <= $weight_range; ++$i) {
    $weights[$i] = $i;
  }
  $form['admin']['weight']['#tree'] = TRUE;
  foreach ($form['admin']['title'] as $nid => $value) {
    $one_of_ours = in_array($form['admin']['name'][$nid]['#value'], $weight_node_type_names);
    if ($one_of_ours) {

      // Get more stuff about the node.
      $node = db_fetch_object(db_query("SELECT nid, status, sticky, promote, translate, moderate FROM {node} WHERE nid = %d", $nid));

      // Convert to our weight range.
      _weight_decode($node);
      $weight = $node->node_weight;
      $form['admin']['weight'][$nid] = array(
        '#type' => 'select',
        '#options' => $weights,
        '#default_value' => $weight,
        '#ahah' => array(
          'path' => 'admin/node/weight/_weight_change/' . $nid,
          'event' => 'change',
        ),
      );
      $status = $node->status ? t('published') : t('not published');
      $status .= $node->sticky ? '<br />' . t('sticky') : NULL;
      $status .= $node->promote ? '<br />' . t('promoted') : NULL;
      $status .= $node->translate ? '<br />' . t('translate') : NULL;
      $status .= $node->moderate ? '<br />' . t('moderated') : NULL;
      $form['admin']['status'][$nid]['#value'] = $status;
    }
    else {
      $form['admin']['weight'][$nid]['#value'] = '';
    }
  }
}

/**
 * Implementation of hook_forms().
 */
function weight_forms($form_id, $args) {
  if (strpos($form_id, 'weight_view_weight_form') !== FALSE) {
    $forms[$form_id] = array(
      'callback' => 'weight_view_weight_form',
    );
    return $forms;
  }
}

/**
 * Display a view as a weight changing table.
 */
function weight_view_weight_form($form_state, $view, $renders) {
  $options = $view->style_plugin->options;
  $results = $view->result;
  $fields = $view->field;
  $id = 'weight_view_weight_form_' . $view->name . '_' . $view->current_display;
  drupal_add_tabledrag($id, 'order', 'sibling', 'weight_dragger');
  parse_str(tablesort_get_querystring(), $query);
  if (isset($view->exposed_raw_input)) {
    $query += $view->exposed_raw_input;
  }
  $query = empty($query) ? '' : '&' . http_build_query($query, '', '&');
  $header = array();
  $rows = array();
  foreach ($fields as $key => $field) {
    if (empty($field->options['exclude'])) {
      $header[$key] = check_plain($field->options['label']);
    }
  }
  $form['#tree'] = TRUE;
  $form['header'] = array(
    '#type' => 'value',
    '#value' => $header,
  );
  foreach ($renders as $num => $result) {
    foreach ($result as $field => $value) {
      if ($field == 'weight') {
        if (user_access('assign node weight')) {
          $form['rows'][$num][$field] = array(
            '#type' => 'weight',
            '#delta' => variable_get('weight_range', 20),
            '#default_value' => $value,
          );
        }
        else {
          $form['rows'][$num][$field] = array(
            '#value' => $value,
          );
        }
      }
      else {
        $form['rows'][$num][$field] = array(
          '#type' => 'markup',
          '#value' => $value,
        );
      }
    }
    $form['rows'][$num]['nid_hidden'] = array(
      '#type' => 'value',
      '#value' => $view->result[$num]->nid,
    );
  }
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save'),
  );
  $class = 'views-table-weight';
  if (!empty($options['sticky'])) {
    drupal_add_js('misc/tableheader.js');
    $class .= " sticky-enabled";
  }
  $form['class'] = array(
    '#type' => 'value',
    '#value' => $class,
  );
  $form['id'] = array(
    '#type' => 'value',
    '#value' => $id,
  );
  $form['fields'] = array(
    '#type' => 'value',
    '#value' => $fields,
  );
  $form['#theme'] = 'weight_view_weight';
  if (user_access('assign node weight')) {
    $form['#submit'][] = 'weight_view_weight_form_submit';
  }
  return $form;
}

/**
 * Save the changed weights.
 */
function weight_view_weight_form_submit($form, &$form_state) {
  foreach ($form_state['values']['rows'] as $count => $value) {
    $weight = $value['weight'];
    $nid = $value['nid_hidden'];
    $node = node_load($nid);
    if ($node->sticky) {
      $node->sticky = -1 * $weight + 100;
    }
    else {
      $node->sticky = -1 * $weight - 100;
    }
    db_query("UPDATE {node} SET sticky = %d WHERE nid = %d", $node->sticky, $nid);
    if (module_exists('i18nsync') && in_array('sticky', i18nsync_node_fields($node->type))) {
      $nodes = translation_node_get_translations($node->tnid);
      if (count($nodes)) {
        foreach ($nodes as $tnode) {
          if ($tnode->nid != $node->nid) {
            db_query("UPDATE {node} SET sticky = %d WHERE nid = %d", $node->sticky, $tnode->nid);
          }
        }
        $synced = TRUE;
      }
    }
  }
  if ($synced) {
    drupal_set_message(t('Your weight changes have been saved and synchronized.'));
  }
  else {
    drupal_set_message(t('Your weight changes have been saved.'));
  }
}

/**
 * Prepare the weight form for its template file.
 */
function template_preprocess_weight_view_weight(&$vars) {
  $vars['header'] = $vars['form']['header']['#value'];
  $vars['class'] = $vars['form']['class']['#value'];
  $vars['id'] = $vars['form']['id']['#value'];
  $vars['fields'] = $vars['form']['fields']['#value'];
  foreach ($vars['form']['rows'] as $count => $item) {
    if (is_numeric($count)) {
      foreach ($item as $field => $value) {
        if (drupal_substr($field, 0, 1) != '#') {
          if (drupal_substr($field, 0, 6) == 'weight') {
            $value['#attributes']['class'] = 'weight_dragger';
          }
          $vars['rows'][$count][$field] = drupal_render($value);
        }
      }
    }
  }
  unset($vars['form']['rows']);
  $vars['submit'] = drupal_render($vars['form']);
}

/**
 * Ajax callback for weight manager page.
 */
function _weight_change($nid) {
  $form_state = array(
    'storage' => NULL,
    'rebuild' => TRUE,
  );
  $form_build_id = $_POST['form_build_id'];
  include_once 'modules/node/node.admin.inc';

  // Step #4.
  $form = form_get_cache($form_build_id, $form_state);

  // Preparing for #5.
  $args = $form['#parameters'];
  $form_id = array_shift($args);
  $form_state['post'] = $form['#post'] = $_POST;
  $form['#programmed'] = $form['#redirect'] = FALSE;

  // Step #5.
  drupal_process_form($form_id, $form, $form_state);

  // Step #6 and #7 and #8.
  $form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id);

  // Doing it this way preserves the revision information.
  $node = node_load($nid);
  $node->node_weight = $form_state['post']['weight'][$nid];
  node_save($node);
  $output = $form_state['post']['weight'][$nid];

  // Final rendering callback.
  drupal_json(array(
    'status' => TRUE,
    'data' => $output,
  ));
}

/**
 * Convert our weight to 'encoded' sticky value for DB.
 * Stickiness is the inverse of weight - stickiness is sorted DESC while
 * weight is sorted ASC so we invert the weight before saving...
 * If the sticky box is checked, subtract weight from 100;
 * unweighted sticky nodes will have a value of 100.
 */
function _weight_encode(&$node) {
  if ($node->sticky == 1) {
    $node->sticky = 100 - $node->node_weight;
  }
  else {
    if ($node->sticky == 0) {
      $node->sticky = -($node->node_weight + 100);
    }
  }
}

/**
 * Convert our weight back out of sticky.
 */
function _weight_decode(&$node) {
  if ($node->sticky == 0 || $node->sticky == 1) {
    $node->node_weight = 0;
    return;
  }
  if ($node->sticky > 0) {
    $node->node_weight = 100 - $node->sticky;
    $node->sticky = 1;
  }
  else {
    $node->node_weight = -($node->sticky + 100);
    $node->sticky = 0;
  }
}

/**
 * Implementation of hook_views_api().
 */
function weight_views_api() {
  return array(
    'api' => 2,
    'path' => drupal_get_path('module', 'weight') . '/views',
  );
}

/**
 * Implementation of hook_theme().
 */
function weight_theme() {
  return array(
    'weight_view_weight' => array(
      'arguments' => array(
        'form' => NULL,
      ),
      'template' => 'weight-view-weight',
      'path' => drupal_get_path('module', 'weight') . '/theme',
    ),
    'weight_ui_style_plugin' => array(
      'arguments' => array(
        'form' => NULL,
      ),
    ),
    'weight_node_admin_nodes' => array(
      'arguments' => array(
        'form' => NULL,
      ),
    ),
  );
}

/**
 * Theme the form for the table style plugin
 */
function theme_weight_ui_style_plugin($form) {
  $output = drupal_render($form['description_markup']);
  $header = array(
    t('Field'),
    t('Column'),
    t('Separator'),
  );
  $rows = array();
  foreach (element_children($form['columns']) as $id) {
    $row = array();
    $row[] = drupal_render($form['info'][$id]['name']);
    $row[] = drupal_render($form['columns'][$id]);
    $row[] = drupal_render($form['info'][$id]['separator']);
    $rows[] = $row;
  }

  // Add the special 'None' row.
  $rows[] = array(
    t('None'),
    '',
    '',
    '',
    array(
      'align' => 'center',
      'data' => drupal_render($form['default'][-1]),
    ),
  );
  $output .= theme('table', $header, $rows);
  $output .= drupal_render($form);
  return $output;
}

Functions

Namesort descending Description
template_preprocess_weight_view_weight Prepare the weight form for its template file.
theme_weight_node_admin_nodes Theme node administration overview. Mostly copied from node.admin.inc.
theme_weight_ui_style_plugin Theme the form for the table style plugin
weight_forms Implementation of hook_forms().
weight_form_alter Implementation of hook_form_alter().
weight_form_node_admin_content_alter Implementation of hook_form_FORM_ID_alter().
weight_help Implementation of hook_help().
weight_menu Implementation of hook_menu().
weight_nodeapi
weight_perm Implements hook_perm().
weight_theme Implementation of hook_theme().
weight_theme_registry_alter Implementation of hook_theme_registry_alter().
weight_views_api Implementation of hook_views_api().
weight_view_weight_form Display a view as a weight changing table.
weight_view_weight_form_submit Save the changed weights.
_weight_change Ajax callback for weight manager page.
_weight_decode Convert our weight back out of sticky.
_weight_encode Convert our weight to 'encoded' sticky value for DB. Stickiness is the inverse of weight - stickiness is sorted DESC while weight is sorted ASC so we invert the weight before saving... If the sticky box is checked, subtract weight from…