You are here

nd.module in Node displays 6

Same filename and directory in other branches
  1. 6.3 nd.module
  2. 6.2 nd.module
  3. 7 nd.module

Main node displays file.

File

nd.module
View source
<?php

/**
 * @file
 * Main node displays file.
 */

/**
 * Constants for field types.
 */
define('ND_FIELD_THEME', 1);
define('ND_FIELD_FUNCTION', 2);
define('ND_FIELD_PREPROCESS', 3);
define('ND_FIELD_IGNORE', 4);
define('ND_FIELD_CUSTOM', 5);
define('ND_FIELD_OVERRIDABLE', 6);
define('ND_FIELD_OVERRIDDEN', 7);
define('ND_FIELD_BLOCK', 8);

/**
 * Constants for content field default values
 */
define('ND_DEFAULT_REGION', 'disabled');
define('ND_DEFAULT_FORMAT', 'default');
define('ND_DEFAULT_LABEL_FORMAT', 'hidden');
define('ND_DEFAULT_WEIGHT', -19);

/**
 * Contants for block fields rendering.
 */
define('BLOCK_TEMPLATE', 1);
define('BLOCK_TITLE_CONTENT', 2);
define('BLOCK_CONTENT', 3);

/**
 * Implementation of hook_nodeapi().
 */
function nd_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
  switch ($op) {

    // Add has body property.
    case 'load':
      $node->has_body = node_get_types('type', $node->type)->has_body;
      break;

    // Determine build mode.
    case 'view':
      if ($node->build_mode == NODE_BUILD_RSS) {

        // For the RSS build mode, we need to manipulate right now.
        _nd_nodeapi($node);
      }
      elseif ($node->build_mode == NODE_BUILD_PREVIEW) {
        $node->build_mode = $teaser == TRUE ? 'teaser' : 'full';
        $node->has_body = node_get_types('type', $node->type)->has_body;
      }
      elseif ($node->build_mode === NODE_BUILD_NORMAL) {
        $build_mode = $page ? 'full' : 'teaser';
        $node->build_mode = $build_mode;
      }
      if ($node->build_mode == 'teaser' && $node->sticky == 1) {
        $node->build_mode = 'nd_sticky';
      }
      break;

    // Alter the node object for viewing.
    case 'alter':

      // We ignore the RSS build mode, which is handled in the view operation.
      if ($node->build_mode == NODE_BUILD_RSS) {
        return;
      }
      _nd_nodeapi($node);
      break;
  }
}

/**
 * Helper function to alter node properties
 *
 * @param stdClass $node The complete node object.
 */
function _nd_nodeapi(&$node) {

  // See if rendering is needed later on by the preprocess hook.
  // There are two ways of excluding: global exclude on content type
  // or per build mode per content type.
  // We don't return here, because we want to add all fields on the node object
  // so themers can use it in their template.
  $exclude_build_modes = variable_get('nd_buildmodes_exclude', array());
  $node->render_by_nd = isset($exclude_build_modes[$node->type][$node->build_mode]) && $exclude_build_modes[$node->type][$node->build_mode] == TRUE || variable_get('nd_contenttype_' . $node->type, FALSE) == TRUE ? FALSE : TRUE;
  $regions = array();
  $nd_fields = array();
  $nd_display_settings = variable_get('nd_display_settings_' . $node->type, array());

  // Get all fields.
  $fields = nd_get_fields($node->type, $node->has_body, $node->build_mode);
  if (!empty($fields)) {
    foreach ($fields as $key => $field) {

      // Exclude now depends on the region, previous it was an exclude property.
      $region = nd_default_value($nd_display_settings, $node->build_mode, 'fields', $key, 'region', ND_DEFAULT_REGION);
      if ($region != 'disabled') {
        $weight = nd_default_value($nd_display_settings, $node->build_mode, 'fields', $key, 'weight', ND_DEFAULT_WEIGHT);
        $node->content[$key]['#weight'] = $weight;
        $regions[$region][$key] = $weight;
        $nd_fields[$key] = array(
          'key' => $key,
          'title' => $field['title'],
          'labelformat' => nd_default_value($nd_display_settings, $node->build_mode, 'fields', $key, 'labelformat', ND_DEFAULT_LABEL_FORMAT),
          'type' => isset($field['display_settings']) ? 'cck' : 'nd',
        );

        // If the field has the storage key, it means the theming is done in that module.
        if (isset($field['storage'])) {
          continue;
        }

        // Theming can either be done in preprocess, or with a custom funtion or an
        // existing formatter theming function. For a custom function we'll pass
        // on the $node object, the field name and the complete field array.
        switch ($field['type']) {
          case ND_FIELD_PREPROCESS:
          case ND_FIELD_IGNORE:
            break;
          case ND_FIELD_CUSTOM:
          case ND_FIELD_OVERRIDABLE:
          case ND_FIELD_OVERRIDDEN:
            nd_eval_code($key, $field, $node);
            break;
          case ND_FIELD_BLOCK:
            nd_eval_block($key, $field, $node);
            break;
          case ND_FIELD_FUNCTION:
            if (isset($field['file'])) {
              include_once $field['file'];
            }
            $function = nd_default_value($nd_display_settings, $node->build_mode, 'fields', $key, 'format', key($field['formatters']));
            $function($node, $key, $field);
            break;
          case ND_FIELD_THEME:
            $format = nd_default_value($nd_display_settings, $node->build_mode, 'fields', $key, 'format', key($field['formatters']));
            theme($format, $node);
            break;
        }
      }
    }
  }

  // Add fields and regions to node object.
  // Also reset render_by_nd property if needed.
  $node->nd_fields = $nd_fields;
  $node->regions = $regions;
  if (empty($regions)) {
    $node->render_by_nd = FALSE;
  }

  // Special support for RSS.
  if ($node->build_mode == NODE_BUILD_RSS && $node->render_by_nd == TRUE) {
    $node->content['body']['#access'] = FALSE;
    foreach (element_children($node->content) as $key => $field) {
      if (!isset($nd_fields[$field])) {
        $node->content[$field]['#access'] = FALSE;
      }
      elseif (isset($nd_fields[$field]) && $nd_fields[$field]['type'] == 'nd') {
        $key = $field . '_rendered';
        $field_key = strtr($key, '_', '-');
        $node->content[$field]['#value'] = theme('nd_field', $node->{$key}, $field, $nd_fields[$field]);
      }
    }
  }
}

/**
 * Implementation of moduleName_preprocess_hook().
 * The node data will be rendered in regions. This uses a helper function
 * so themers/developers can call that helper function from within
 * their preprocess_hooks if they are fiddling with some data. For information
 * about this decision see http://drupal.org/node/570592 (issue) and
 * http://drupal.org/node/572614 for information on howto implement.
 */
function nd_preprocess_node(&$vars, $hook) {
  if (!variable_get('nd_preprocess_override', FALSE)) {
    _nd_preprocess_node($vars, $hook);
  }
}

/**
 * Helper function used in either nd_preprocess_node or other preprocess function.
 */
function _nd_preprocess_node(&$vars, $hook) {
  $node = $vars['node'];

  // Add node-content_type-build_mode(-nid) template suggestion.
  $vars['template_files'][] = 'node-' . $node->type . '-' . $node->build_mode;
  $vars['template_files'][] = 'node-' . $node->type . '-' . $node->build_mode . '-' . $node->nid;

  // Break all the rendering if needed. The render_by_nd key is set in nodeapi above.
  if (!$node->render_by_nd) {
    return;
  }
  $all_regions = nd_regions('anything', TRUE);
  $regions = $vars['regions'];
  $themed_regions = array();
  $nd_display_settings = variable_get('nd_display_settings_' . $node->type, array());

  // Create key_rendered fields from ND_FIELD_PREPROCESS types.
  $fields = nd_get_fields($node->type, $node->has_body, $node->build_mode);
  if (!empty($fields)) {
    foreach ($fields as $key => $field) {
      if ($field['type'] == ND_FIELD_PREPROCESS && isset($node->nd_fields[$key])) {
        $field_key = $key . '_rendered';
        $vars[$field_key] = $vars[$key];
        unset($vars[$key]);
      }
      if ($field['type'] == ND_FIELD_IGNORE) {
        $field_key = $key . '_rendered';
        $vars[$field_key] = $node->content[$key]['#value'];
      }
    }
  }

  // Loop through all regions and content and classes.
  $region_classes = array();
  foreach ($all_regions as $region_name => $region_title) {
    if (isset($regions[$region_name])) {
      $region_content = '';
      $region = $regions[$region_name];

      // Loop through all fields after ordering on weight.
      asort($region);
      foreach ($region as $key => $weight) {
        $field_content = isset($vars[$key . '_rendered']) ? $vars[$key . '_rendered'] : '';
        if (!empty($field_content)) {
          $field_key = strtr($key, '_', '-');
          $region_content .= theme('nd_field', $field_content, $field_key, $node->nd_fields[$key]);
        }
        unset($node->content[$key]);
      }

      // Render region.
      if (!empty($region_content)) {
        if ($region_name == 'left' || $region_name == 'right') {
          $region_classes[$region_name] = $region_name;
        }
        $themed_regions[$region_name] = array(
          'content' => $region_content,
        );
      }
    }
  }
  $node_display = new stdClass();
  $node_display->all_regions = $all_regions;
  $node_display->themed_regions = $themed_regions;
  $node_display->region_classes = $region_classes;

  // Plugins.
  nd_plugins_process($vars, $node_display, $nd_display_settings);

  // Add classes based on node regions.
  $nd_middle_classes = 'no-sidebars';
  if (isset($node_display->region_classes['left']) && isset($node_display->region_classes['right'])) {
    $nd_middle_classes = 'two-sidebars';
  }
  elseif (isset($node_display->region_classes['left'])) {
    $nd_middle_classes = 'one-sidebar sidebar-left';
  }
  elseif (isset($node_display->region_classes['right'])) {
    $nd_middle_classes = 'one-sidebar sidebar-right';
  }

  // Build the $content variable.
  $content = '';
  foreach ($node_display->themed_regions as $region_name => $region_data) {
    $content .= '<div class="nd-region-' . $region_name;
    if ($region_name == 'middle') {
      $content .= ' ' . $nd_middle_classes;
    }
    if (isset($region_data['extra_class'])) {
      $content .= $region_data['extra_class'];
    }
    $content .= '" ';
    if (isset($region_data['inline_css'])) {
      $content .= $region_data['inline_css'];
    }
    $content .= '><div class="nd-region-inner">' . $region_data['content'] . '</div></div>';
  }
  $vars['content'] = $content;
}

/**
 * Process plugins.
 *
 * @param array $vars The variables currently processed.
 * @param stdClass $node_display Collection of arrays with node data.
 * @param array $nd_display_settings Display settings.
 */
function nd_plugins_process(&$vars, &$node_display, $nd_display_settings) {
  $plugins = variable_get('nd_plugin_settings', array());
  if (!empty($plugins)) {
    foreach ($plugins as $key => $plugin) {
      if (isset($plugin['filename'])) {
        require_once $plugin['filename'];
      }
      $function = $plugin['process_callback'];
      $function($vars, $node_display, $nd_display_settings);
    }
  }
}

/**
 * Function to return a value or return the default if empty.
 *
 * @param array $settings The settings loaded for a content type.
 * @param string $build_mode The name of the build mode.
 * @param string $type The name of the type to search (ie fields, regions)
 * @param string $key The name of the key to search in $type.
 * @param string $search_key The name of the key to search in $key.
 * @param string $default The default value.
 */
function nd_default_value($settings, $build_mode, $type, $key, $search_key, $default) {
  return isset($settings[$build_mode][$type][$key][$search_key]) ? $settings[$build_mode][$type][$key][$search_key] : $default;
}

/**
 * Evaluate custom code.
 *
 * @param string $key The name of the key to create.
 * @param array $value The field array.
 * @param stdClass The node object.
 */
function nd_eval_code($key, $field, &$node) {
  if (isset($field['code'])) {
    $node_key = $key . '_rendered';
    $value = nd_eval($field['code'], $node);

    // Token support.
    if (module_exists('token')) {
      $value = token_replace($value, 'node', $node);
    }
    $node->{$node_key} = $value;
  }
}

/**
 * Evaluate block field.
 *
 * @param string $key The name of the key to create.
 * @param array $field The field array.
 * @param stdClass The node object.
 */
function nd_eval_block($key, $field, &$node) {
  if (isset($field['code'])) {
    list($module, $delta) = explode('|', $field['code']);
    $block = module_invoke($module, 'block', 'view', $delta);
    if (!empty($block)) {
      switch ($field['render']) {
        case BLOCK_TEMPLATE:
          global $theme_key;
          $block = (object) $block;
          $block->module = $module;
          $block->delta = $delta;
          $block_title = db_result(db_query("SELECT title FROM {blocks} WHERE module = '%s' AND delta = '%s' AND theme = '%s'", $module, $delta, $theme_key));
          if (!empty($block_title)) {
            $block->subject = $block_title == '<none>' ? '' : check_plain($block_title);
          }
          $content = theme('block', $block);
          break;
        case BLOCK_TITLE_CONTENT:
          $content = '<h2 class="block-title">' . $block['subject'] . '</h2>';
          $content .= $block['content'];
          break;
        case BLOCK_CONTENT:
          $content = $block['content'];
          break;
      }
      $node_key = $key . '_rendered';
      $node->{$node_key} = $content;
    }
  }
}

/**
 * Wrapper function around PHP eval(). We don't use drupal_eval
 * because custom fields might need properties from the current
 * node object.
 *
 * @param string $code The code to evaluate from the custom field.
 * @param stdClass $node The current node object.
 * @return string $output The output from eval.
 */
function nd_eval($code, $node) {
  global $theme_path, $theme_info, $conf;

  // Store current theme path.
  $old_theme_path = $theme_path;

  // Restore theme_path to the theme, as long as drupal_eval() executes,
  // so code evaluted will not see the caller module as the current theme.
  // If theme info is not initialized get the path from theme_default.
  if (!isset($theme_info)) {
    $theme_path = drupal_get_path('theme', $conf['theme_default']);
  }
  else {
    $theme_path = dirname($theme_info->filename);
  }
  ob_start();
  print eval('?>' . $code);
  $output = ob_get_contents();
  ob_end_clean();

  // Recover original theme path.
  $theme_path = $old_theme_path;
  return $output;
}

/**
 * API function to get all fields.
 */
function nd_get_fields($node_type, $has_body, $build_mode) {
  static $static_fields = array();
  if (!isset($static_fields[$node_type][$build_mode])) {

    // Fields in code.
    $fields = module_invoke_all('nd_fields', $node_type, $has_body, $build_mode);

    // Fields via the UI.
    $db_fields = variable_get('nd_fields', array());
    if (!empty($db_fields)) {
      foreach ($db_fields as $key => $field) {
        $fields[$key] = array(
          'title' => check_plain($field['title']),
          'code' => isset($field['block']) ? $field['block'] : $field['code'],
          'type' => $field['type'],
          'render' => isset($field['render']) ? $field['render'] : '',
        );
        $exclude = isset($field['exclude'][$node_type]) && $field['exclude'][$node_type] === $node_type ? TRUE : FALSE;
        if ($exclude) {
          unset($fields[$key]);
        }
      }
    }

    // Give modules a change to alter fields.
    drupal_alter('nd_fields', $fields);
    $static_fields[$node_type][$build_mode] = $fields;
  }
  return $static_fields[$node_type][$build_mode];
}

/**
 * Api function to return all build modes.
 *
 * @param string $selector Return one build mode.
 * @param boolean $reset Whether to reset the build modes.
 * @return array All or one build mode(s).
 */
function nd_get_build_modes($selector = NULL, $reset = FALSE) {
  $build_modes = variable_get('nd_all_build_modes', array());
  if (empty($build_modes) || $reset) {
    require_once 'includes/nd.registry.inc';
    $build_modes = _nd_register_build_modes();
  }
  if ($selector != NULL) {
    return $build_modes[$selector]['build modes'];
  }
  else {
    return $build_modes;
  }
}

/**
 * Return array of available regions.
 *
 * @param string $build_mode The build mode. Needed for a check on RSS build mode.
 */
function nd_regions($build_mode = 'anything', $render = FALSE) {
  if ($build_mode != NODE_BUILD_RSS) {
    if ($render == FALSE) {
      return array(
        'header' => t('Header'),
        'left' => t('Left'),
        'middle' => t('Middle'),
        'right' => t('Right'),
        'footer' => t('Footer'),
        'disabled' => t('Disabled'),
      );
    }
    else {
      return array(
        'header' => t('Header'),
        'left' => t('Left'),
        'right' => t('Right'),
        'middle' => t('Middle'),
        'footer' => t('Footer'),
        'disabled' => t('Disabled'),
      );
    }
  }
  else {
    return array(
      'middle' => t('Enabled'),
      'disabled' => t('Disabled'),
    );
  }
}

/**
 * Implementation of hook_init().
 */
function nd_init() {
  if (variable_get('nd_regions_css', TRUE)) {
    drupal_add_css(drupal_get_path('module', 'nd') . '/css/regions.css');
  }
}

/**
 * Implementation of hook_menu().
 */
function nd_menu() {
  require_once 'includes/nd.registry.inc';
  return _nd_menu();
}

/**
 * Implementation of hook_theme().
 */
function nd_theme() {
  require_once 'includes/nd.registry.inc';
  return _nd_theme();
}

/**
 * Implementation of hook_content_build_modes().
 */
function nd_content_build_modes() {
  require_once 'includes/nd.registry.inc';
  return _nd_content_build_modes();
}

/**
 * Implementation of hook_nd_plugins().
 */
function nd_nd_plugins() {
  require_once 'includes/nd.registry.inc';
  return _nd_plugins();
}

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

/**
 * Implementation of hook_nd_fields().
 */
function nd_nd_fields($node_type, $has_body, $build_mode) {
  $fields = array(
    'title' => array(
      'title' => t('Title'),
      'formatters' => array(
        'nd_title_h1_nolink' => t('H1 title'),
        'nd_title_h1_link' => t('H1 title, linked to node'),
        'nd_title_h2_nolink' => t('H2 title'),
        'nd_title_h2_link' => t('H2 title, linked to node'),
        'nd_title_h2_block_nolink' => t('H2 block title'),
        'nd_title_h2_block_link' => t('H2 block title, linked to node'),
        'nd_title_p_nolink' => t('Paragraph title'),
        'nd_title_p_link' => t('Paragraph title, linked to node'),
      ),
      'type' => ND_FIELD_THEME,
    ),
    'author' => array(
      'title' => t('Author'),
      'formatters' => array(
        'nd_author_nolink' => t('Author'),
        'nd_author_link' => t('Author linked to profile'),
      ),
      'type' => ND_FIELD_THEME,
    ),
    'links' => array(
      'title' => t('Links'),
      'type' => ND_FIELD_PREPROCESS,
    ),
    'read_more' => array(
      'title' => t('Read more'),
      'code' => '<?php echo l(t("Read more"), "node/$node->nid"); ?>',
      'type' => ND_FIELD_OVERRIDABLE,
    ),
    'post_date' => array(
      'title' => t('Post date'),
      'code' => '<?php echo format_date($node->created, "custom", "d/m/Y"); ?>',
      'type' => ND_FIELD_OVERRIDABLE,
    ),
  );

  // Check for body.
  if ($has_body == TRUE) {
    $fields['body'] = array(
      'title' => t('Core body'),
      'formatters' => array(
        'nd_bodyfield' => t('Body'),
      ),
      'type' => ND_FIELD_THEME,
    );
  }
  if (module_exists('taxonomy')) {
    $fields['terms'] = array(
      'title' => t('Taxonomy'),
      'type' => ND_FIELD_PREPROCESS,
    );
  }
  if (module_exists('upload') && $build_mode != 'teaser' && variable_get("upload_{$node_type}", 1)) {
    $fields['files'] = array(
      'title' => t('Core upload'),
      'type' => ND_FIELD_IGNORE,
    );
  }
  return $fields;
}

Functions

Namesort descending Description
nd_content_build_modes Implementation of hook_content_build_modes().
nd_default_value Function to return a value or return the default if empty.
nd_eval Wrapper function around PHP eval(). We don't use drupal_eval because custom fields might need properties from the current node object.
nd_eval_block Evaluate block field.
nd_eval_code Evaluate custom code.
nd_get_build_modes Api function to return all build modes.
nd_get_fields API function to get all fields.
nd_init Implementation of hook_init().
nd_menu Implementation of hook_menu().
nd_nd_fields Implementation of hook_nd_fields().
nd_nd_plugins Implementation of hook_nd_plugins().
nd_nodeapi Implementation of hook_nodeapi().
nd_plugins_process Process plugins.
nd_preprocess_node Implementation of moduleName_preprocess_hook(). The node data will be rendered in regions. This uses a helper function so themers/developers can call that helper function from within their preprocess_hooks if they are fiddling with some data. For…
nd_regions Return array of available regions.
nd_theme Implementation of hook_theme().
nd_views_api Implementation of hook_views_api().
_nd_nodeapi Helper function to alter node properties
_nd_preprocess_node Helper function used in either nd_preprocess_node or other preprocess function.

Constants