Index: term_display.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/term_display/term_display.module,v
retrieving revision 1.12
diff -u -p -r1.12 term_display.module
--- term_display.module	19 Jun 2009 09:15:59 -0000	1.12
+++ term_display.module	19 Nov 2009 16:30:33 -0000
@@ -193,3 +193,16 @@ function term_display_nodeapi(&$node, $o
       break;
   }
 }
+
+/**
+ * Implementation of hook_ctools_plugin_directory().
+ */
+function term_display_ctools_plugin_directory($module, $plugin) {
+  // Safety: go away if CTools is not at an appropriate version.
+  if (!module_invoke('ctools', 'api_version', PANELS_REQUIRED_CTOOLS_API)) {
+    return;
+  }
+  if ($module == 'ctools' && $plugin == 'content_type') {
+    return 'plugins/content_type';
+  }
+}
Index: plugins/content_types/node_terms.inc
===================================================================
RCS file: plugins/content_types/node_terms.inc
diff -N plugins/content_types/node_terms.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ plugins/content_types/node_terms.inc	19 Nov 2009 16:30:50 -0000
@@ -0,0 +1,102 @@
+<?php
+// $Id$
+
+/**
+ * Callback function to supply a list of content types.
+ */
+function term_display_node_terms_ctools_content_types() {
+  return array(
+    'single' => TRUE,
+    'title' => t('Taxonomy terms'),
+    'icon' => 'icon_term.png',
+    'icon path' => drupal_get_path('module', 'ctools') . '/plugins/content_types/term_context',
+    'description' => t('The assigned terms of the referenced node.'),
+    'required context' => new ctools_context_required(t('Node'), 'node'),
+    'category' => t('Node'),
+    'defaults' => array(
+      'vocabulary' => NULL,
+      'style' => TERM_DISPLAY_LIST,
+    ),
+  );
+}
+
+/**
+ * Render the custom content type.
+ */
+function term_display_node_terms_content_type_render($subtype, $conf, $panel_args, $context) {
+  if (empty($context) || empty($context->data)) {
+    return;
+  }
+
+  // Get a shortcut to the node.
+  $node = $context->data;
+  $vocabulary = taxonomy_vocabulary_load($conf['vocabulary']);
+  if (!isset($node->taxonomy) || !$vocabulary) {
+    return;
+  }
+
+  $terms = array();
+  foreach ($node->taxonomy as $tid => $term) {
+    if ($term->vid == $conf['vocabulary']) {
+      $terms[$tid] = $term;
+    }
+  }
+  if (empty($terms)) {
+    return;
+  }
+
+  // Build the content type block.
+  $block = new stdClass();
+  $block->module = 'term_display';
+  $block->delta = $conf['vocabulary'];
+  $block->title = check_plain($vocabulary->name);
+  $block->content = theme('term_display_' . $conf['style'], $vocabulary, $terms);
+
+  return $block;
+}
+
+/**
+ * Returns an edit form for custom type settings.
+ */
+function term_display_node_terms_content_type_edit_form(&$form, &$form_state) {
+  $conf = $form_state['conf'];
+
+  $options = array();
+  foreach (taxonomy_get_vocabularies() as $vid => $vocabulary) {
+    $options[$vid] = check_plain($vocabulary->name);
+  }
+  $form['vocabulary'] = array(
+    '#type' => 'radios',
+    '#title' => t('Vocabulary to display'),
+    '#options' => $options,
+    '#default_value' => $conf['vocabulary'],
+  );
+
+  $options = array(
+    TERM_DISPLAY_LIST => t('List'),
+    TERM_DISPLAY_CUSTOM => t('Custom (defaults to comma separated)'),
+  );
+  $form['style'] = array(
+    '#type' => 'select',
+    '#title' => t('Display style'),
+    '#options' => $options,
+    '#default_value' => $conf['style'],
+  );
+}
+
+/**
+ * Submit handler for the custom type settings form.
+ */
+function term_display_node_terms_content_type_edit_form_submit(&$form, &$form_state) {
+  // Copy everything from our defaults.
+  foreach (array_keys($form_state['plugin']['defaults']) as $key) {
+    $form_state['conf'][$key] = $form_state['values'][$key];
+  }
+}
+
+/**
+ * Returns the administrative title for a type.
+ */
+function term_display_node_terms_content_type_admin_title($subtype, $conf, $context) {
+  return t('"@s" taxonomy terms', array('@s' => $context->identifier));
+}
