Skip to content
panels_mini.module 12.8 KiB
Newer Older
<?php
// $Id$

/**
 * @file panels_mini.module
 *
 * This module provides mini panels which are basically panels that can be
 * used within blocks or other panels.
 */

/**
 * Implementation of hook_perm().
 */
function panels_mini_perm() {
  return array('create mini panels', 'administer mini panels');
}

/**
 * Implementation of hook_menu().
 */
function panels_mini_menu() {
  require_once drupal_get_path('module', 'panels_mini') . '/panels_mini.admin.inc';
  return _panels_mini_menu();
}

// ---------------------------------------------------------------------------
// Allow the rest of the system access to mini panels

/**
 * Implementation of hook_block().
 *
 * Expose qualifying mini panels to Drupal's block system.
 */
function panels_mini_block($op = 'list', $delta = 0, $edit = array()) {
  if ($op == 'list') {
    $blocks = array();

    $minis = panels_mini_load_all();
    foreach ($minis as $panel_mini) {
      if (empty($mini->disabled) && empty($mini->requiredcontext)) {
        $blocks[$panel_mini->pid] = array(
          'info' => t('Mini panel: "@title"', array('@title' => $panel_mini->title)),
        );
      }
    }

    return $blocks;
  }
  elseif ($op == 'view') {
    $panel_mini = panels_mini_load($delta);
    panels_load_include('plugins');
    $panel_mini->context = $panel_mini->display->context = panels_context_load_contexts($panel_mini);
    $panel_mini->display->css_id = panels_mini_get_id($panel_mini->name);

    $block = array(
      'subject' => $panel_mini->title,
      'content' => panels_render_display($panel_mini->display),
    );
    return $block;
  }
}

/**
 * Expose all mini panels to our own system.
 */
function panels_mini_panels_content_types() {
  $items['panels_mini'] = array(
    'title' => t('Mini panels'),
    'content_types' => 'panels_mini_content_types',
    'render callback' => 'panels_mini_content',
    'add callback' => 'panels_mini_add_mini_panel',
    'edit callback' => 'panels_mini_edit_mini_panel',
    'title callback' => 'panels_mini_title_mini_panel',
  );
  return $items;
}

/**
 * Return each available mini panel available as a subtype.
 */
function panels_mini_content_types() {
  $types = array();
  foreach (panels_mini_load_all() as $mini) {
    if (!empty($mini->disabled)) {
      continue;
    }

    $types[$mini->name] = array(
      'title' => filter_xss_admin($mini->title),
      // For now mini panels will just use the contrib block icon.
      'icon' => 'icon_contrib_block.png',
      'path' => panels_get_path("content_types/block"),
      'description' => filter_xss_admin($mini->title),
      'category' => array(t(!empty($mini->category) ? $mini->category : 'Mini panel'), -8),
    );
    if (!empty($mini->requiredcontexts)) {
      $types[$mini->name]['required context'] = array();
      foreach ($mini->requiredcontexts as $context) {
        $info = panels_get_context($context['name']);
        // TODO: allow an optional setting
        $types[$mini->name]['required context'][] = new panels_required_context($context['identifier'], $info['context name']);
      }
    }
  }
  return $types;
}

/**
 * Statically store all used IDs to ensure all mini panels get a unique id.
 */
function panels_mini_get_id($name) {
  static $id_cache = array();

  $id = 'mini-panel-' . $name;
  if (!empty($id_cache[$name])) {
    $id .= "-" . $id_cache[$name]++;
  }
  else {
    $id_cache[$name] = 1;
  }

  return $id;
}

/**
 * Render a mini panel called from a panels display.
 */
function panels_mini_content($conf, $panel_args, &$contexts) {
  $mini = panels_mini_load($conf['name']);
  if (!$mini) {
    return FALSE;
  }

  panels_load_include('plugins');

  // Load up any contexts we might be using.
  $context = panels_context_match_required_contexts($mini->requiredcontexts, $contexts);
  $mini->context = $mini->display->context = panels_context_load_contexts($mini, FALSE, $context);

  if (empty($mini) || !empty($mini->disabled)) {
    return;
  }

  $mini->display->args = $panel_args;
  $mini->display->css_id = panels_mini_get_id($conf['name']);
  $mini->display->owner = $mini;
  // unique ID of this mini.
  $mini->display->owner->id = $mini->name;

  $block = new stdClass();
  $block->module  = 'panels_mini';
  $block->delta   = $conf['name'];
  $block->subject = filter_xss_admin($mini->title);
  $block->content = panels_render_display($mini->display);
  return $block;
}

/**
 * Form to add a mini panel to a panel.
 */
function panels_mini_add_mini_panel($id, $parents, $conf = array()) {
  $conf['name'] = $id;
  return panels_mini_edit_mini_panel($id, $parents, $conf);
}

/**
 * Returns an edit form for the mini panel.
 *
 * There isn't much here as most of this is set up at mini panel creation time.
 */
function panels_mini_edit_mini_panel($id, $parents, $conf) {
  $form['name'] = array(
    '#type' => 'value',
    '#value' => $conf['name'],
  );

  return $form;
}

function panels_mini_title_mini_panel($conf) {
  $mini = panels_mini_load($conf['name']);
  if (!$mini) {
    return t('Deleted/missing mini panel @name', array('@name' => $conf['name']));
  }

  $title = filter_xss_admin($mini->title);
  if (empty($title)) {
    $title = t('Untitled mini panel');
  }
  return $title;
}

// ---------------------------------------------------------------------------
// Database functions.

/**
 * A list of the fields used in the panel_mini table.
 */
function panels_mini_fields() {
  return array(
    'name' => "'%s'",
    'category' => "'%s'",
    'title' => "'%s'",
    'contexts' => "'%s'",
    'requiredcontexts' => "'%s'",
    'relationships' => "'%s'",
  );
}


/**
 * Sanitize a mini panel, to guarantee certain data is as we believe it will be.
 */
function panels_mini_sanitize($mini) {
  foreach (array('contexts', 'relationships', 'requiredcontexts') as $id) {
    if (!isset($mini->$id) || !is_array($mini->$id)) {
      $mini->$id = array();
    }
  }

  return $mini;
}

/**
 * Fetch all mini panels in the system.
 *
 * This function does not cache.
 */
function panels_mini_load_all($page_size = 0) {
  static $results = array();

  if (array_key_exists($page_size, $results)) {
    return $results[$page_size];
  }

  $panels = $dids = array();
  $query = "SELECT * FROM {panels_mini}";
  if ($page_size) {
    $result = pager_query($query, $page_size);
  }
  else {
    $result = db_query($query);
  }

  while ($mini = db_fetch_object($result)) {
    $mini->contexts = (!empty($mini->contexts)) ? unserialize($mini->contexts) : array();
    $mini->requiredcontexts = (!empty($mini->requiredcontexts)) ? unserialize($mini->requiredcontexts) : array();
    $mini->relationships = (!empty($mini->relationships)) ? unserialize($mini->relationships) : array();
    $mini->category = (!empty($mini->category)) ? $mini->category : 'Mini panels';

    $mini->type = t('Local');
    $panels[$mini->name] = panels_mini_sanitize($mini);
  }

  $status = variable_get('panel_mini_defaults', array());
  foreach (panels_mini_default_panels() as $mini) {
    // Determine if default panel is enabled or disabled.
    if (isset($status[$mini->name])) {
      $mini->disabled = $status[$mini->name];
    }

    if (!empty($panels[$mini->name])) {
      $panels[$mini->name]->type = t('Overridden');
    }
    else {
      $mini->type = t('Default');
      $panels[$mini->name] = $mini;
    }
  }
  $results[$page_size] = $panels;

  return $results[$page_size];
}

/**
 * Load a mini panel.
 */
function panels_mini_load($pid) {
  static $cache = array();

  if (array_key_exists($pid, $cache)) {
    return $cache[$pid];
  }

  if (!is_numeric($pid)) {
    $where = "name = '%s'";
  }
  else {
    $where = 'pid = %d';
  }
  $panel_mini = db_fetch_object(db_query("SELECT * FROM {panels_mini} WHERE $where", $pid));
  if (!$panel_mini) {
    $defaults = panels_mini_default_panels();
    if (isset($defaults[$pid])) {
      $panel_mini = $defaults[$pid];
      $status = variable_get('panel_mini_defaults', array());
      // Determine if default panel is enabled or disabled.
      if (isset($status[$panel_mini->name])) {
        $panel_mini->disabled = $status[$panel_mini->name];
      }
      $cache[$pid] = $panel_mini;
      return $panel_mini;
    }
    return;
  }

  $panel_mini->contexts = (!empty($panel_mini->contexts)) ? unserialize($panel_mini->contexts) : array();
  $panel_mini->requiredcontexts = (!empty($panel_mini->requiredcontexts)) ? unserialize($panel_mini->requiredcontexts) : array();
  $panel_mini->relationships = (!empty($panel_mini->relationships)) ? unserialize($panel_mini->relationships) : array();

  $cache[$pid] = panels_mini_sanitize($panel_mini);
  $cache[$pid]->display = panels_load_display($cache[$pid]->did);

  return $cache[$pid];
}

/**
 * Save a mini panel.
 */
function panels_mini_save(&$panel_mini) {
  $fields = $types = $values = $pairs = array();
  // Save the display if one was given to us.
  if (!empty($panel_mini->display)) {
    $display = panels_save_display($panel_mini->display);
  }

  // Ensure empty values get translated correctly.
  // Also make sure we don't mess up the original.
  $mini_clone = drupal_clone(panels_mini_sanitize($panel_mini));

  // If pid is set to our "magic value", this is an insert, otherwise an update.
  $insert = $mini_clone->pid && $mini_clone->pid == 'new';

  // Build arrays of fields and types (resp. pairs of both) and of values.
  foreach (panels_mini_fields() as $field => $type) {
    // Skip empty values.
    if (isset($mini_clone->$field)) {
      if ($insert) {
        $fields[] = $field;
        $types[] = $type;
      }
      else {
        $pairs[] = "$field = $type";
      }
      // Build the $values array, serializing some fields.
      $serialize = in_array($field, array('contexts', 'requiredcontexts', 'relationships'));
      $values[] = $serialize ? serialize($mini_clone->$field) : $mini_clone->$field;
    }
  }

  if ($insert) {
    // Build the query adding the new primary key and the did.
    $sql = 'INSERT INTO {panels_mini} (' . implode(', ', $fields) . ', did) VALUES (' . implode(', ', $types) . ', %d)';
    db_query($sql, $values);

    // Determine the new primary key.
    $mini_clone->pid = db_last_insert_id('panels_mini', 'pid');
  }
  else {
    // Build the query filtering by the primary key.
    $sql = 'UPDATE {panels_mini} SET ' . implode(', ', $pairs) . ' WHERE pid = %d';
    $values[] = $mini_clone->pid;
    db_query($sql, $values);
  }

  return $mini_clone->pid;
}

/**
 * Delete a mini panel.
 */
function panels_mini_delete($panel_mini) {
  db_query("DELETE FROM {panels_mini} WHERE pid = %d", $panel_mini->pid);
  db_query("DELETE FROM {blocks} WHERE module = 'panels_mini' AND delta = %d", $panel_mini->pid);
  return panels_delete_display($panel_mini->did);
}

/**
 * Export a mini panel into PHP code for use in import.
 *
 * The code returned from can be used directly in panels_mini_save().
 */
function panels_mini_export($panel_mini, $prefix = '') {
  $output = '';
  $fields = panels_mini_fields();
  $output .= $prefix . '$mini = new stdClass()' . ";\n";
  $output .= $prefix . '$mini->pid = \'new\'' . ";\n";
  foreach ($fields as $field => $sub) {
    $output .= $prefix . '  $mini->' . $field . ' = ' . panels_var_export($panel_mini->$field, '  ') . ";\n";
  }
  // Export the primary display
  $display = !empty($panel_mini->display) ? $panel_mini->display : panels_load_display($panel_mini->did);
  $output .= panels_export_display($display, $prefix);
  $output .= $prefix . '$mini->display = $display' . ";\n";

  return $output;
}

/**
 * Get all 'default' mini panels.
 */
function panels_mini_default_panels() {
  $panels = module_invoke_all('default_panel_minis');
  if (!is_array($panels)) {
    $panels = array();
  }

  return $panels;
}

/**
 * Remove the block version of mini panels from being available content types.
 */
function panels_mini_panels_block_info($module, $delta, &$info) {
  $info = NULL;
}

/**
 * Implementation of hook_panels_exportables().
 */
function panels_mini_panels_exportables($op = 'list', $panels = NULL, $name = 'foo') {
  static $all_panels = NULL;
  if ($op == 'list') {
    if (empty($all_panels)) {
      $all_panels = panels_mini_load_all();
    }

    foreach ($all_panels as $name => $panel) {
      $return[$name] = check_plain($name) . ' (' . check_plain($panel->title) . ')';
    }
    return $return;
  }

  if ($op == 'export') {
    $code = "/**\n";
    $code .= " * Implementation of hook_default_panel_minis()\n";
    $code .= " */\n";
    $code .= "function " . $name . "_default_panel_minis() {\n";
    foreach ($panels as $panel => $truth) {
      $code .= panels_mini_export($all_panels[$panel], '  ');
      $code .= '  $minis[\'' . check_plain($panel) . '\'] = $mini;' . "\n\n\n";
    }
    $code .= "  return \$minis;\n";
    $code .= "}\n";
    return $code;
  }
}


/**
 * Menu callback to check to see if a mini panel is valid as part
 * of a path, and if it is, return the mini.
 */
function panels_mini_admin_load($name) {
  $mini = panels_mini_load($name);
  if ($mini && empty($mini->disabled)) {
    return $mini;
  }
}