<?php
// $Id: auto_nodetitle.module,v 1.20.2.8 2010/10/29 13:25:03 fago Exp $

/**
 * @file
 * Allows hiding of the node title field and automatic title creation
 */

define('AUTO_NODETITLE_DISABLED', 0);
define('AUTO_NODETITLE_ENABLED', 1);
define('AUTO_NODETITLE_OPTIONAL', 2);

/**
 * Implementation of hook_perm()
 */
function auto_nodetitle_perm() {
  return array('use PHP for title patterns');
}

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

  if (isset($form['#node_type']) && 'node_type_form' == $form_id) {
    auto_nodetitle_node_settings_form($form);
  }
  else if (isset($form['#node']) && isset($form['#method']) && $form['#node']->type .'_node_form' == $form_id) {
    //this is a node form
    if (auto_nodetitle_get_setting($form['#node']->type) == AUTO_NODETITLE_ENABLED) {
      // we will autogenerate the title later, just hide the title field in the meanwhile
      $form['title']['#value'] = 'ant';
      $form['title']['#type'] = 'value';
      $form['title']['#required'] = FALSE;
      $form['#submit'][] = 'auto_nodetitle_node_form_submit';
    }
    else if (auto_nodetitle_get_setting($form['#node']->type) == AUTO_NODETITLE_OPTIONAL) {
      // we will make the title optional
      $form['title']['#required'] = FALSE;
      $form['#submit'][] = 'auto_nodetitle_node_form_submit';
    }
  }
}

/**
 * Makes sure the node preview is shown right.
 * It gets the node object, modifies the title, and updates the node in the form_state.
 */
function auto_nodetitle_node_form_submit($form, &$form_state) {
  // Only do something, if the user clicked the preview button.
  if (isset($form_state['clicked_button']['#submit']) && in_array('node_form_build_preview', $form_state['clicked_button']['#submit'])) {
    $setting = auto_nodetitle_get_setting($form_state['values']['type']);
    if ($setting == AUTO_NODETITLE_ENABLED || ($setting == AUTO_NODETITLE_OPTIONAL && empty($form_state['values']['title']))) {
      $node = node_submit($form_state['values']);
      auto_nodetitle_set_title($node);
      $form_state['values'] = (array)$node;
    }
  }
}

/**
 * Implementation of hook_nodeapi().
 */
function auto_nodetitle_nodeapi(&$node, $op) {
  if ($op == 'presave' && auto_nodetitle_is_needed($node)) {
    auto_nodetitle_set_title($node);
  }
}

/**
 * Returns whether the auto nodetitle has to be set.
 */
function auto_nodetitle_is_needed($node) {
  return empty($node->auto_nodetitle_applied) && ($setting = auto_nodetitle_get_setting($node->type)) && !($setting == AUTO_NODETITLE_OPTIONAL && !empty($node->title));
}

/**
 * Sets the automatically generated nodetitle for the node
 */
function auto_nodetitle_set_title(&$node) {
  $types = node_get_types();
  $pattern = variable_get('ant_pattern_'. $node->type, '');
  if (trim($pattern)) {
    $node->changed = time();
    $node->title = _auto_nodetitle_patternprocessor($pattern, $node);
  }
  else if ($node->nid) {
    $node->title = t('@type @node-id', array('@type' => $types[$node->type]->name, '@node-id' => $node->nid));
  }
  else {
    $node->title = t('@type', array('@type' => $types[$node->type]->name));
  }
  // Ensure the generated title isn't too long.
  $node->title = substr($node->title, 0, 255);
  // With that flag we ensure we don't apply the title two times to the same node.
  $node->auto_nodetitle_applied = TRUE;
}

/**
 * Implementation of hook_node_operations().
 */
function auto_nodetitle_node_operations() {
  $operations = array(
    'nodetitle_update' => array(
      'label' => t('Update automatic nodetitles'),
      'callback' => 'auto_nodetitle_operations_update',
    ),
  );
  return $operations;
}

/**
 * Callback function for updating node titles.
 */
function auto_nodetitle_operations_update($nodes) {
  foreach ($nodes as $nid) {
    $node = node_load($nid);
    if ($node && auto_nodetitle_is_needed($node)) {
      $previous_title = $node->title;
      auto_nodetitle_set_title($node);
      // Only save if the title has actually changed.
      if ($node->title != $previous_title) {
        node_save($node);
      }
    }
  }
}

/**
  * Helper function to generate the title according to the PHP code.
  * Right now its only a wrapper, but if this is to be expanded, here is the place to be.
  * @return a title string
  */
function _auto_nodetitle_patternprocessor($output, $node) {
  if (module_exists('token')) {
    $output = token_replace($output, 'node', $node);
  }
  if (variable_get('ant_php_'. $node->type, 0)) {
    $output = auto_nodetitle_eval($output, $node);
  }
  if (variable_get('ant_php_'. $node->type, 0) || module_exists('token')) {
    $output = preg_replace('/[\t\n\r\0\x0B]/', '', strip_tags($output));
  }
  return $output;
}

/**
  * Helper function for hook_form_alter() renders the settings per node-type.
  */
function auto_nodetitle_node_settings_form(&$form) {
  $default_value = auto_nodetitle_get_setting($form['#node_type']->type);
  $form['auto_nodetitle'] = array(
    '#type' => 'fieldset',
    '#title' => t('Automatic title generation'),
    '#weight' => 0,
    '#collapsible' => TRUE,
    '#collapsed' => !$default_value,
  );
  $form['auto_nodetitle']['ant'] = array(
    '#type' => 'radios',
    '#default_value' => $default_value,
    '#options' => array(
      t('Disabled'),
      t('Automatically generate the title and hide the title field'),
      t('Automatically generate the title if the title field is left empty'),
    )
  );

  if (module_exists('token') || user_access('use PHP for title patterns')) {

    $description = t('Leave blank for using the per default generated title. Otherwise this string will be used as title.');
    if (module_exists('token')) {
      $description .= ' '. t('Use the syntax [token] if you want to insert a replacement pattern.');
    }
    $form['auto_nodetitle']['ant_pattern'] = array(
      '#type' => 'textarea',
      '#title' => t('Pattern for the title'),
      '#description' => $description,
      '#default_value' => variable_get('ant_pattern_'. $form['#node_type']->type, ''),
    );

    // Don't allow editing of the pattern if PHP is used, but the users lacks
    // permission for PHP.
    if (variable_get('ant_php_' . $form['#node_type']->type, '') && !user_access('use PHP for title patterns')) {
      $form['auto_nodetitle']['ant_pattern']['#value'] = $form['auto_nodetitle']['ant_pattern']['#default_value'];
      $form['auto_nodetitle']['ant_pattern']['#disabled'] = TRUE;
      $form['auto_nodetitle']['ant_pattern']['#description'] = t('You are not allow the configure the pattern for the title, as you lack the %permission permission.', array('%permission' => t('Use PHP for title patterns')));
    }
  }

  if (module_exists('token')) {
    $form['auto_nodetitle']['token_help'] = array(
      '#title' => t('Replacement patterns'),
      '#type' => 'fieldset',
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
      '#description' => t('Prefer raw-text replacements for text to avoid problems with HTML entities!'),
    );
    $form['auto_nodetitle']['token_help']['help'] = array(
      '#value' => theme('token_help', 'node'),
    );
  }
  if (user_access('use PHP for title patterns')) {
    $form['auto_nodetitle']['ant_php'] = array(
      '#type' => 'checkbox',
      '#title' => t('Evaluate PHP in pattern.'),
      '#description' => t('Put PHP code above that returns your string, but make sure you surround code in &lt;?php and ?&gt;. Note that $node is available and can be used by your code.'),
      '#default_value' => variable_get('ant_php_'. $form['#node_type']->type, ''),
    );
  }
  else {
    $form['auto_nodetitle']['auto_nodetitle_php'] = array(
      '#type' => 'value',
      '#value' => variable_get('ant_php_'. $form['#node_type']->type, ''),
    );
  }
}

/**
 * Gets the auto node title setting associated with the given content type.
 */
function auto_nodetitle_get_setting($type) {
  return variable_get('ant_'. $type, AUTO_NODETITLE_DISABLED);
}

/**
 * Evaluates php code and passes $node to it
 */
function auto_nodetitle_eval($code, $node) {
  ob_start();
  print eval('?>'. $code);
  $output = ob_get_contents();
  ob_end_clean();
  return $output;
}

/**
 * Implementation of hook_node_type().
 */
function auto_nodetitle_node_type($op, $info) {
  switch ($op) {
    case 'delete':
      variable_del('ant_'. $info->type);
      variable_del('ant_pattern_'. $info->type);
      variable_del('ant_php_'. $info->type);
      break;
    case 'update':
      if (!empty($info->old_type) && $info->old_type != $info->type) {
        variable_set('ant_'. $info->type, auto_nodetitle_get_setting($info->old_type));
        variable_set('ant_pattern_'. $info->type, variable_get('ant_pattern_'. $info->old_type, ''));
        variable_set('ant_php_'. $info->type, variable_get('ant_php_'. $info->old_type, ''));
        variable_del('ant_'. $info->old_type);
        variable_del('ant_pattern_'. $info->old_type);
        variable_del('ant_php_'. $info->old_type);
      }
      break;
  }
}
