You are here

panels_node.module in Panels 5.2

panels_node.module

This module provides the "panel" node type. Panel nodes are useful to add additional content to the content area on a per-node base.

File

panels_node/panels_node.module
View source
<?php

/**
 * @file panels_node.module
 *
 * This module provides the "panel" node type.
 * Panel nodes are useful to add additional content to the content area
 * on a per-node base.
 */

// ---------------------------------------------------------------------------
// General Drupal hooks

/**
 * Implementation of hook_perm().
 */
function panels_node_perm() {
  return array(
    'create panel-nodes',
    'edit own panel-nodes',
    'administer panel-nodes',
  );
}

/**
 * Implementation of hook_menu().
 */
function panels_node_menu($may_cache) {
  if ($may_cache) {
    $items[] = array(
      'path' => 'node/add/panel',
      'title' => t('Panel'),
      'access' => user_access('create panel-nodes'),
      'type' => MENU_NORMAL_ITEM,
    );
    $items[] = array(
      'path' => 'admin/panels/panel-nodes',
      'title' => t('Panel nodes'),
      'access' => user_access('create panel-nodes') && user_access('access administration pages'),
      'type' => MENU_NORMAL_ITEM,
      'callback' => 'panels_node_admin',
      'description' => t('Information about panel nodes.'),
    );
    $items[] = array(
      'path' => 'admin/panels/panel-nodes/information',
      'title' => t('Information'),
      'access' => user_access('create panel-nodes') && user_access('access administration pages'),
      'type' => MENU_DEFAULT_LOCAL_TASK,
    );
    $items[] = array(
      'path' => 'admin/panels/panel-nodes/settings',
      'title' => t('Settings'),
      'description' => t('Configure panel node content availability.'),
      'access' => user_access('administer panel-nodes'),
      'callback' => 'panels_node_settings',
      'type' => MENU_LOCAL_TASK,
    );
  }
  else {
    if (arg(0) == 'node' && is_numeric(arg(1))) {
      $node = node_load(arg(1));
      if ($node && $node->type == 'panel' && node_access('update', $node)) {
        $base = 'node/' . arg(1) . '/panel_';
        $items[] = array(
          'path' => $base . 'layout',
          'title' => t('Panel layout'),
          'access' => TRUE,
          'callback' => 'panels_node_edit_layout',
          'callback arguments' => array(
            $node,
          ),
          'weight' => 2,
          'type' => MENU_LOCAL_TASK,
        );
        $items[] = array(
          'path' => $base . 'settings',
          'title' => t('Panel layout settings'),
          'access' => TRUE,
          'callback' => 'panels_node_edit_layout_settings',
          'callback arguments' => array(
            $node,
          ),
          'weight' => 2,
          'type' => MENU_LOCAL_TASK,
        );
        $items[] = array(
          'path' => $base . 'content',
          'title' => t('Panel content'),
          'access' => TRUE,
          'callback' => 'panels_node_edit_content',
          'callback arguments' => array(
            $node,
          ),
          'weight' => 3,
          'type' => MENU_LOCAL_TASK,
        );
        $items[] = array(
          'path' => $base . 'context',
          'title' => t('Context'),
          'access' => TRUE,
          'callback' => 'panels_node_context_edit',
          'callback arguments' => array(
            $node,
          ),
          'weight' => 4,
          'type' => MENU_LOCAL_TASK,
        );
      }
    }

    // Hard override of node/add
    if (arg(0) == 'node' && arg(1) == 'add' && arg(2) == 'panel' && arg(3) == NULL) {
      $items[] = array(
        'path' => 'node/add/panel',
        'title' => t('Panel'),
        'access' => user_access('create panel-nodes'),
        'callback' => 'panels_node_add',
        'type' => MENU_NORMAL_ITEM,
      );
    }
  }
  return $items;
}

/**
 * Page callback for the very short admin page.
 */
function panels_node_admin() {
  $output = '<p>';
  $output .= t('Panel nodes do not have a normal administrative UI, such as panels pages or mini panels. With this module, a new node type is created: a "panel" node. These are nodes that have panel layouts, but do not have the breadth of features that panel pages do; these features are sacrificed so that you gain all the capabilities of nodes.');
  $output .= '</p><p>';
  $output .= t('You may create a !panel_node using the normal !create_content menu, and you can administer your panel nodes under the normal !administer_nodes menu (administrative permission required).', array(
    '!panel_node' => l(t('panel node'), 'node/add/panel'),
    '!create_content' => l(t('create content'), 'node/add'),
    '!administer_nodes' => l(t('administer nodes'), 'admin/content/node'),
  ));
  $output .= '</p><p>';
  $output .= t('On the !settings page, you may control which panes may be added to panel nodes; this can be very valuable to limit what content is available if your users who can create panel nodes are not administrators.', array(
    '!settings' => l(t('settings'), 'admin/panels/panel-nodes/settings'),
  ));
  $output .= '</p>';
  return $output;
}

// ---------------------------------------------------------------------------
// Node hooks

/**
 * Implementation of hook_node_info().
 */
function panels_node_node_info() {
  return array(
    'panel' => array(
      'name' => t('Panel'),
      'module' => 'panels_node',
      'body_label' => t('Teaser'),
      'description' => t("A panel a page layout broken up into rows and columns."),
    ),
  );
}

/**
 * Implementation of hook_access().
 */
function panels_node_access($op, $node = NULL) {
  if (user_access('administer panel-nodes')) {
    return TRUE;
  }
  if ($op == 'create' && user_access('create panel-nodes')) {
    return TRUE;
  }
  if ($op == 'update' && $node->uid == $user->uid && user_access('edit own panel-nodes')) {
    return TRUE;
  }
}
function panels_node_add() {
  $output = '';
  panels_load_include('plugins');

  // If no layout selected, present a list of choices.
  foreach (panels_get_layouts() as $id => $layout) {
    $output .= panels_print_layout_link($id, $layout, $_GET['q'] . '/' . $id);
  }
  return $output;
}

/**
 * Implementation of hook_form().
 */
function panels_node_form(&$node, &$param) {
  $form['panels_node']['#tree'] = TRUE;
  if (!$node->nid) {

    // Grab our selected layout from the $node, If it doesn't exist, try arg(3)
    // and if that doesn't work present them with a list to pick from.
    $panel_layout = $node->panel_layout ? $node->panel_layout : arg(3);
    panels_load_include('plugins');
    $layout = panels_get_layout($panel_layout);
    if (empty($layout)) {
      return drupal_not_found();
    }
    $form['panels_node']['layout'] = array(
      '#type' => 'value',
      '#value' => $panel_layout,
    );
  }
  $type = node_get_types('type', $node);
  $form['title'] = array(
    '#type' => 'textfield',
    '#title' => check_plain($type->title_label),
    '#required' => TRUE,
    '#default_value' => $node->title,
  );
  if (!empty($type->body_label)) {
    $form['body'] = array(
      '#type' => 'textarea',
      '#title' => check_plain($type->body_label),
      '#rows' => 10,
      '#required' => TRUE,
      '#description' => t('The teaser is a piece of text to describe when the panel is listed (such as when promoted to front page); the actual content will only be displayed on the full node view.'),
      '#default_value' => $node->body,
    );
  }

  //  drupal_set_message('<pre>' . check_plain(var_export($node, true)) . '</pre>');
  $css_id = '';
  if (!empty($node->panels_node['css_id'])) {
    $css_id = $node->panels_node['css_id'];
  }
  $form['panels_node']['css_id'] = array(
    '#type' => 'textfield',
    '#title' => t('CSS ID'),
    '#size' => 30,
    '#description' => t('An ID that can be used by CSS to style the panel.'),
    '#default_value' => $css_id,
  );
  return $form;
}

/**
 * Implementation of hook_validate().
 */
function panels_node_validate($node) {
  if (!$node->nid && empty($node->panels_node['layout'])) {
    form_set_error('', t('Please select a layout.'));
  }
}

/**
 * Implementation of hook_load().
 *
 * Panels does not use revisions for nodes because that would open us up
 * to have completely separate displays, and we'd have to copy them,
 * and that's going to be a LOT of data.
 */
function panels_node_load($node) {

  // We shortcut this because only in some really drastic corruption circumstance will this
  // not work.
  $additions['panels_node'] = db_fetch_array(db_query("SELECT * FROM {panels_node} WHERE nid = %d", $node->nid));
  $additions['panels_node']['contexts'] = !empty($additions['panels_node']['contexts']) ? unserialize($additions['panels_node']['contexts']) : array();
  $additions['panels_node']['relationships'] = !empty($additions['panels_node']['relationships']) ? unserialize($additions['panels_node']['relationships']) : array();
  return $additions;
}

/**
 * Implementation of hook_insert().
 */
function panels_node_insert(&$node) {

  // Create a new display and record that.
  $display = panels_new_display();
  $display->layout = $node->panels_node['layout'];
  panels_save_display($display);
  $css_id = $node->panels_node['css_id'];
  db_query("INSERT INTO {panels_node} (nid, did, css_id) VALUES (%d, %d, '%s')", $node->nid, $display->did, $node->panels_node['css_id']);
  $node->panels_node['did'] = $display->did;
}

/**
 * Implementation of hook_delete().
 */
function panels_node_delete(&$node) {
  db_query("DELETE FROM {panels_node} WHERE nid = %d", $node->nid);
  if (!empty($node->panels_node['did'])) {
    panels_delete_display($node->panels_node['did']);
  }
}

/**
 * Implementation of hook_update().
 */
function panels_node_update($node) {
  db_query("UPDATE {panels_node} SET css_id = '%s' WHERE nid = %d", $node->panels_node['css_id'], $node->nid);
}

/**
 * Implementation of hook_view().
 */
function panels_node_view($node, $teaser = FALSE, $page = FALSE) {
  panels_load_include('plugins');
  if ($teaser) {

    // Do the standard view for teaser.
    $node = node_prepare($node, $teaser);
  }
  else {
    $display = panels_load_display($node->panels_node['did']);
    $display->css_id = $node->panels_node['css_id'];

    // TODO: Find a way to make sure this can't node_view.
    $display->context = array(
      'panel-node' => panels_context_create('node', $node),
    );

    // Load additional contexts.
    $panel_node = (object) $node->panels_node;
    $display->context += panels_context_load_contexts($panel_node);
    $node->content['body'] = array(
      '#value' => panels_render_display($display),
      '#weight' => 0,
    );
  }
  return $node;
}

/**
 * Save a panel node.
 *
 * While panels_node_update() is run when a panel node is edited, this function
 * must be run to update the enhanced panel node configuration, f.e. contexts,
 * which are not edited on the regular node edit page.
 *
 * @see panels_node_update(), panels_node_context_form_submit()
 */
function panels_node_save($panel_node) {
  db_query("UPDATE {panels_node} SET contexts = '%s', relationships = '%s' WHERE nid = %d", serialize($panel_node->contexts), serialize($panel_node->relationships), $panel_node->nid);
}

// ---------------------------------------------------------------------------
// Administrative pages

/**
 * Settings for panel nodes.
 */
function panels_node_settings() {
  panels_load_include('common');
  return drupal_get_form('panels_common_settings', 'panels_node');
}

// ---------------------------------------------------------------------------
// Meat of the Panels API; almost completely passing through to panels.module

/**
 * Pass through to the panels layout editor.
 */
function panels_node_edit_layout($node) {
  panels_load_include('plugins');
  $display = panels_load_display($node->panels_node['did']);
  $display->context = array(
    'panel-node' => panels_context_create('node', $node),
  );
  return panels_edit_layout($display, t('Save'), "node/{$node->nid}/panel_layout");
}

/**
 * Pass through to the panels layout settings editor.
 */
function panels_node_edit_layout_settings($node) {
  panels_load_include('plugins');
  $display = panels_load_display($node->panels_node['did']);
  return panels_edit_layout_settings($display, t('Save'), "node/{$node->nid}/panel_settings");
}

/**
 * Pass through to the panels content editor.
 */
function panels_node_edit_content($node) {
  panels_load_include('plugins');
  $display = panels_load_display($node->panels_node['did']);
  $display->context = array(
    'panel-node' => panels_context_create('node', $node),
  );
  $display->context['panel-node']->identifier = $node->title;

  // Load additional contexts.
  $panel_node = (object) $node->panels_node;
  $display->context += panels_context_load_contexts($panel_node);
  panels_load_include('common');
  $content_types = panels_common_get_allowed_types('panels_node', $display->context);

  // Print this with theme('page') so that blocks are disabled while editing a display.
  // This is important because negative margins in common block layouts (i.e, Garland)
  // messes up the drag & drop.
  print theme('page', panels_edit($display, "node/{$node->nid}/panel_content", $content_types), FALSE);
}

/**
 * Edit contexts of a panel node.
 *
 * FIXME: panels_node_edit_context() already defined in arguments/node_edit.inc.
 */
function panels_node_context_edit($node) {
  panels_load_include('plugins');
  $panel_node = (object) $node->panels_node;
  $panel_node->nid = $node->nid;
  $cache = panels_common_cache_get('panel_object:panel_node', $panel_node->did);
  if (!$cache) {
    panels_common_cache_set('panel_object:panel_node', $panel_node->did, $panel_node);
  }
  else {
    $panel_node = $cache;
  }
  drupal_set_title(check_plain($node->title));
  return drupal_get_form('panels_node_context_form', $panel_node);
}

/**
 * The form to edit the context settings of a panel node.
 */
function panels_node_context_form($panel_node) {
  drupal_add_css(panels_get_path('css/panels_admin.css'));
  $form['panel_node'] = array(
    '#type' => 'value',
    '#value' => $panel_node,
  );
  $form['right'] = array(
    '#prefix' => '<div class="right-container">',
    '#suffix' => '</div>',
  );
  $form['left'] = array(
    '#prefix' => '<div class="left-container">',
    '#suffix' => '</div>',
  );
  panels_load_include('common');

  // FIXME: Common panels forms are based on $object->name instead of did.
  $panel_node->name = $panel_node->did;
  $settings = panels_common_add_context_form('panel_node', $form, $form['right']['contexts_table'], $panel_node);
  $settings += panels_common_add_relationship_form('panel_node', $form, $form['left']['relationships_table'], $panel_node);
  panels_common_add_context_js($settings);
  $label = t('Save');
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => $label,
  );
  return $form;
}

/**
 * Process submission of the panel node edit form.
 */
function panels_node_context_form_submit($form_id, $form_values) {
  $panel_node = $form_values['panel_node'];

  // Organize these from the common form.
  panels_common_save_context('context', $panel_node->contexts, $form_values);
  panels_common_save_context('relationship', $panel_node->relationships, $form_values);
  drupal_set_message(t('Your changes have been saved.'));
  panels_node_save($panel_node);
  panels_common_cache_clear('panel_object:panel_node', $panel_node->did);
}

Functions

Namesort descending Description
panels_node_access Implementation of hook_access().
panels_node_add
panels_node_admin Page callback for the very short admin page.
panels_node_context_edit Edit contexts of a panel node.
panels_node_context_form The form to edit the context settings of a panel node.
panels_node_context_form_submit Process submission of the panel node edit form.
panels_node_delete Implementation of hook_delete().
panels_node_edit_content Pass through to the panels content editor.
panels_node_edit_layout Pass through to the panels layout editor.
panels_node_edit_layout_settings Pass through to the panels layout settings editor.
panels_node_form Implementation of hook_form().
panels_node_insert Implementation of hook_insert().
panels_node_load Implementation of hook_load().
panels_node_menu Implementation of hook_menu().
panels_node_node_info Implementation of hook_node_info().
panels_node_perm Implementation of hook_perm().
panels_node_save Save a panel node.
panels_node_settings Settings for panel nodes.
panels_node_update Implementation of hook_update().
panels_node_validate Implementation of hook_validate().
panels_node_view Implementation of hook_view().