Please note that by creating a template for this content type, you are taking full control of its output and you will need to manually add all of the fields that you would like to see in the output. Click reset to remove template control for this content type.

'. theme('more_help_link', url('admin/help/contemplate'))); case 'admin/help#contemplate': return t('

The Content Templates (a.k.a. contemplate) module allows modification of the teaser and body fields using administrator defined templates. These templates use PHP code and all of the node object variables are available for use in the template. An example node object is displayed and it is as simple as clicking on its properties to add them to the current template.

This module was written to solve a need with the Content Construction Kit (CCK), where it had a tendency toward outputting content in a not-very-pretty way. And as such, it dovetails nicely with CCK, adding a "template" tab to CCK content-type editing pages and pre-populating the templates with CCK\'s default layout. This makes it easy to rearrange fields, output different fields for teaser and body, remove the field title headers, output fields wrapped for use with tabs.module (part of JSTools), or anything you need.

But Content Template can actually be used on any content type and allows modification of the teaser and body properties before they go out in an RSS feed or are handed off to the theme.

Creating templates

Enter PHP code similar to PHPTemplate. The main difference is that you only have access to the $node object. However, PHPTemplate templates only affect output to the page. Contemplate additionally affects display in RSS feeds and search results.

'); } } /** * Implementation of hook_menu(). */ function contemplate_menu($may_cache) { $items = array(); if ($may_cache) { $items[] = array( 'path' => 'admin/content/templates', 'title' => t('Content templates'), 'description' => t('Create templates to customize output of teaser and body content.'), 'access' => user_access('administer templates'), 'callback' => 'contemplate_edit_type' ); } else { if (arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'types' && arg(3)) { $access = user_access('administer templates'); $items[] = array( 'path' => 'admin/content/types/'. arg(3) .'/template', 'title' => t('Template'), 'callback' => 'contemplate_edit_type', 'access' => $access, 'callback arguments' => array(arg(3)), 'type' => MENU_LOCAL_TASK, 'weight' => 7, ); } } return $items; } /** * Implementation of hook_perm() * */ function contemplate_perm(){ return array('administer templates'); } /** * Implementation of hook_nodeapi(). */ function contemplate_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) { switch ($op) { case 'rss item': if ($template = contemplate_get_template($node->type)){ if (CONTEMPLATE_RSS_ENABLED & $template->flags && trim($template->rss)) { // only if there's content in teaser field $rss = contemplate_eval($template->rss, $node); // set both teaser and body because we don't know how they've set Drupal $node->teaser = $rss; $node->body = $rss; if ($template->enclosure) { if ($file = contemplate_eval_enclosure($template->enclosure, $node)) { return array( array( 'key' => 'enclosure', 'attributes' => array( 'url' => file_create_url($file->filepath), 'length' => $file->filesize, 'type' => $file->filemime ) ) ); } } } } break; case 'alter': if ($template = contemplate_get_template($node->type)){ if($teaser){ if (CONTEMPLATE_TEASER_ENABLED & $template->flags && trim($template->teaser)) { // only if there's content in teaser field $node->teaser = contemplate_eval($template->teaser, $node); } } elseif (CONTEMPLATE_BODY_ENABLED & $template->flags && trim($template->body)) { // only if there's content in the body field $node->body = contemplate_eval($template->body, $node); } } break; } } /** * Admin page... list out the node types * */ function contemplate_admin(){ $types = node_get_types(); $templates = contemplate_get_templates(); foreach($types as $type){ $rows[] = array( $type->name, l($templates[$type->type] ? t('edit template') : t('create template'), 'admin/content/templates/'. $type->type), ); } $header = array( t('content type'), '' ); $output .= theme("table", $header, $rows); $output .= contemplate_version(); return $output; } /** * Menu callback * Edit the template for a specific node-type * * @param string $type */ function contemplate_edit_type_form($type = NULL){ $example = contemplate_examples($type); $template = contemplate_get_template($type); if($default = contemplate_cck_get_fields($type)){ $default_teaser = $default_body = $default; } else { $default_teaser = "\n"; $default_body = "\n"; } $form['teaser'] = array( '#type' => 'fieldset', '#title' => t('teaser'), '#collapsible' => TRUE, '#collapsed' => CONTEMPLATE_TEASER_ENABLED & $template->flags ? FALSE : TRUE, ); $form['teaser']['teaser-enable'] = array( '#type' => 'checkbox', '#title' => ''. t('Affect teaser output') .'', '#default_value' => CONTEMPLATE_TEASER_ENABLED & $template->flags ? TRUE : FALSE, '#attributes' => array('toggletarget' => '#edit-teaser'), ); $form['teaser']['teaser'] = array( '#type' => 'textarea', '#title' => t('Teaser Template'), '#default_value' => $template->teaser ? $template->teaser : $default_teaser, '#rows' => 15, '#description' => t('Leave this field blank to leave teaser unaffected.'), '#prefix' => '
', '#suffix' => '
', ); $intro = t("

An example node has been loaded and it's properties appear below. Click on the the property names to add them to your template.

"); $form['teaser']['teaser_example'] = array( '#type' => 'markup', '#value' => '
'. $intro . $example['teaser'] .'
' ); $form['body'] = array( '#type' => 'fieldset', '#title' => t('body'), '#collapsible' => TRUE, '#collapsed' => CONTEMPLATE_BODY_ENABLED & $template->flags ? FALSE : TRUE, ); $form['body']['body-enable'] = array( '#type' => 'checkbox', '#title' => ''. t('Affect body output') .'', '#default_value' => CONTEMPLATE_BODY_ENABLED & $template->flags ? TRUE : FALSE, '#attributes' => array('toggletarget' => '#edit-body'), ); $form['body']['body'] = array( '#type' => 'textarea', '#title' => t('Body Template'), '#default_value' => $template->body ? $template->body : $default_body, '#rows' => 15, '#description' => t('Leave this field blank to leave body unaffected.'), '#prefix' => '
', '#suffix' => '
', ); $intro = t("

An example node has been loaded and it's properties appear below. Click on the the property names to add them to your template.

"); $form['body']['body_example'] = array( '#type' => 'markup', '#value' => '
'. $intro . $example['body'] .'
' ); /* START RSS STUFF */ $form['rss'] = array( '#type' => 'fieldset', '#title' => t('RSS'), '#collapsible' => TRUE, '#collapsed' => CONTEMPLATE_RSS_ENABLED & $template->flags ? FALSE : TRUE, ); $form['rss']['rss-enable'] = array( '#type' => 'checkbox', '#title' => ''. t('Affect RSS output') .'', '#default_value' => CONTEMPLATE_RSS_ENABLED & $template->flags ? TRUE : FALSE, '#attributes' => array('toggletarget' => '#edit-rss'), '#description' => t('Note that if you do not enable this, Drupal will use either the teaser or body as specified in your RSS publishing settings.', array('@url' => url('admin/content/rss-publishing'))), ); $form['rss']['rss'] = array( '#type' => 'textarea', '#title' => t('RSS Template'), '#default_value' => $template->rss ? $template->rss : $default_body, '#rows' => 15, '#description' => t('Leave this field blank to leave RSS unaffected.'), '#prefix' => '
', '#suffix' => '
', ); $intro = t("

An example node has been loaded and it's properties appear below. Click on the the property names to add them to your template.

"); $form['rss']['rss_example'] = array( '#type' => 'markup', '#value' => '
'. $intro . $example['rss'] .'
' ); $form['rss'][] = array( '#type' => 'markup', '#value' => '
', ); //$fids = contemplate_get_fids(); global $contemplate_fids; if (is_array($contemplate_fids)) { $contemplate_fids = drupal_map_assoc(array_unique($contemplate_fids)); $contemplate_fids = array(0 => t('<none> (other modules may add)')) + $contemplate_fids; $form['rss']['enclosure'] = array( '#type' => 'radios', '#title' => t('RSS enclosures'), '#options' => $contemplate_fids, '#default_value' => $template->enclosure, ); } /* END RSS STUFF */ $form['type'] = array( '#type' => 'hidden', '#value' => $type, ); $form['reset'] = array( '#type' => 'submit', '#value' => t('Reset'), '#attributes' => array('onclick' => 'return(confirm("'. t("Are you sure you want to reset this form?\\nAny customizations will be lost.") .'"));'), ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Submit'), ); return $form; } function contemplate_edit_type($type = NULL) { $types = node_get_types(); if(!$types[$type]){ // if the argument isn't a valid node type, output admin page return contemplate_admin(); } drupal_set_title(t('Template for %type', array("%type" => $types[$type]->name))); if (arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'templates' && arg(3)){ $breadcrumbs = drupal_get_breadcrumb(); $breadcrumbs[] = l(t('Templates'), 'admin/content/templates'); drupal_set_breadcrumb($breadcrumbs); } $output = drupal_get_form('contemplate_edit_type_form', $type); return $output; } /** * Get a single template * */ function contemplate_get_template($type){ //only load each template once per page hit static $types = array(); if(!isset($types[$type])){ $types[$type] = db_fetch_object(db_query('SELECT * FROM {contemplate} WHERE type = "%s"', $type)); } return $types[$type]; } /** * Get all of the current templates * * @return unknown */ function contemplate_get_templates(){ $result = db_query('SELECT * FROM {contemplate}'); while($r = db_fetch_object($result)){ $templates[$r->type]['teaser'] = $r->teaser; $templates[$r->type]['body'] = $r->body; $templates[$r->type]['rss'] = $r->rss; $templates[$r->type]['enclosure'] = $r->enclosure; } return $templates; } function contemplate_edit_type_form_submit($form_id, $form_values){ $op = isset($_POST['op']) ? $_POST['op'] : ''; if ($op == t('Reset')) { contemplate_delete($form_values['type']); drupal_set_message(t('%type template has been reset.', array("%type" => $form_values['type']))); } else { contemplate_save($form_values); drupal_set_message(t('%type template saved.', array('%type' => $form_values['type']))); if (arg(0) == 'admin' && arg(1) == 'node' && arg(2) == 'template'){ drupal_goto('admin/content/templates'); } } } function contemplate_save($edit){ contemplate_delete($edit['type']); $flags |= $edit['teaser-enable'] ? CONTEMPLATE_TEASER_ENABLED : 0; $flags |= $edit['body-enable'] ? CONTEMPLATE_BODY_ENABLED : 0; $flags |= $edit['rss-enable'] ? CONTEMPLATE_RSS_ENABLED : 0; return db_query('INSERT INTO {contemplate} (type, teaser, body, rss, enclosure, flags) VALUES ("%s", "%s", "%s", "%s", "%s", %d)', $edit['type'], $edit['teaser'], $edit['body'], $edit['rss'], $edit['enclosure'], $flags); } function contemplate_delete($type){ return db_query('DELETE FROM {contemplate} WHERE type = "%s"', $type); } /** * Load an example node and display its parts * - used only on template edit page * * @param $type * node type * @return array */ function contemplate_node_views($type){ // get the nid of the latest node of this type $nid = db_result(db_query('SELECT nid FROM {node} WHERE type = "%s" ORDER BY created DESC', $type)); if($nid){ $node = node_load($nid); $bodynode = contemplate_node_view($node, FALSE, TRUE); unset($bodynode->teaser); // for debugging... // I'm outputting the length of the string representation of the $bodynode object so I can compare it // drupal_set_message('checksum/length of $bodynode: '. strlen(print_r($bodynode, TRUE))); // outputs the correct value $teasernode = contemplate_node_view($node, TRUE, FALSE); unset($teasernode->body); // drupal_set_message('checksum/length of $teasernode: '. strlen(print_r($teasernode, TRUE))); // here's the bug... In PHP5 this next line // outputs the same value as $teasernode... NOT $bodynode... very odd // In PHP4 it's fine // drupal_set_message('checksum/length of $bodynode: '. strlen(print_r($bodynode, TRUE))); return array('body'=>$bodynode, 'teaser'=>$teasernode); } else { return FALSE; } } /** * Load an example node and display its parts * - used only on template edit page * * @param $type * node type * @return * an array containing the 'body' and 'teaser' versions of the */ function contemplate_examples($type){ $path = drupal_get_path('module', 'contemplate'); drupal_add_js($path .'/contemplate.js'); drupal_add_js($path .'/divresizer.js'); drupal_add_css($path .'/contemplate.css'); if($views = contemplate_node_views($type)){ $boutput = contemplate_array_variables((array)$views['body'], 'body'); $toutput = contemplate_array_variables((array)$views['teaser'], 'teaser'); $routput = contemplate_array_variables((array)$views['teaser'], 'rss'); } else { $error = t('No %type content items exist to use as an example. Please create a %type item and then come back here to see an output of its parts.', array("%type"=> $type)); $toutput = $boutput = $routput = $error; } return array('body' => $boutput, 'teaser' => $toutput, 'rss' => $routput); } /** * Recursive function goes through node object * returns html representation of the node * strings are clickable and insert into teaser/body fields * * @param $array * array to recurse through * @param $target * target field for javascript insert * @param $parents * used by recursion * @param $object * used by recursion * @return unknown */ function contemplate_array_variables($array, $target, $parents = FALSE, $object = FALSE){ global $contemplate_fids; if(is_object($array)){ $array = (array)$array; } if(is_array($array)){ $output .= "
\n"; foreach($array as $field => $value){ if($parents){ if($object){ $field = $parents .'->'.$field; } else { if(is_int($field)){ $field = $parents .'['. $field .']'; } else { if ($field == 'fid') { // make a note of the fields named "fid" $contemplate_fids[] = "\$node->". $parents .'[\''. $field .'\']'; } $field = $parents .'[\''. $field .'\']'; } } } $type = ""; if(!is_string($value)){ $type = " (". gettype($value) .")"; } if(!is_array($value) && !is_object($value)){ $output .= "
". addslashes($field) ." ?>');return false;\" title=\"insert this variable into $target\">\$node->$field$type
\n"; } else { $output .= "
\$node->$field$type
\n"; } $output .= "
\n"; if(is_array($value)){ $output .= contemplate_array_variables($value, $target, $field); } elseif(is_object($value)){ $output .= contemplate_array_variables((array)$value, $target, $field, TRUE); } else { $value = is_bool($value) ? ($value ? 'TRUE' : 'FALSE') : $value; $output .= htmlspecialchars(print_r($value, TRUE)) ."\n"; } $output .= "
\n"; } $output .= "
\n"; } return $output; } /** * Run example node through view hooks to present the node object parts * * This is an exact copy of node_view() changed just to return the node object rather than the themed node view * * - used only on the template editing pages */ function contemplate_node_view($node, $teaser = FALSE, $page = FALSE, $links = TRUE){ $node = (object)$node; $node = node_build_content($node, $teaser, $page); if ($links) { $node->links = module_invoke_all('link', 'node', $node, !$page); foreach (module_implements('link_alter') AS $module) { $function = $module .'_link_alter'; $function($node, $node->links); } } // Set the proper node part, then unset unused $node part so that a bad // theme can not open a security hole. $content = drupal_render($node->content); /* if ($teaser) { $node->teaser = $content; unset($node->body); } else { $node->body = $content; unset($node->teaser); }*/ // Allow modules to modify the fully-built node. node_invoke_nodeapi($node, 'alter', $teaser, $page); // drupal_set_message('checksum/length returned: '. strlen(print_r($node, TRUE))); return $node; } /** * Copy of drupal_eval(), but extracts the node object so that variables are available to the template * * @param $tmplt * the template code * @param $obj * an object to extract into the local variables * @return * executed template code */ function contemplate_eval($tmplt, $obj){ extract((array)$obj); $node = $obj; ob_start(); print eval('?>'. $tmplt); $output = ob_get_contents(); ob_end_clean(); return $output; } function contemplate_eval_enclosure($field, $node) { $tmplt = ""; $fid = contemplate_eval($tmplt, $node); if (is_numeric($fid)) { $file = db_fetch_object(db_query('SELECT * FROM {files} WHERE fid = %d', $fid)); return $file; } return FALSE; } function contemplate_cck_get_fields($type_name){ if(module_exists('content')){ $return = array(); // for compatibility with both CVS and 4.7 versions of CCK // remove conditionals once "content_types()" is committed to 4.7 // start remove if(function_exists('content_types')){ $type = content_types($type_name); // <-- keep this part } else { $types = _content_types(); $type = (array)$types[$type_name]; } // end remove if($type){ // if this is a CCK field foreach($type['fields'] as $field_name => $field){ $return[] = theme('contemplate_field', $field); } $return = implode("\n", $return); } else { $return = FALSE; } } else { $return = FALSE; } return $return; } /** * Rewrite of theme_field to output default CCK output into the template. * * @param unknown_type $node * @param unknown_type $field * @param unknown_type $items * @param unknown_type $teaser * @param unknown_type $page * @return unknown */ function theme_contemplate_field(&$field) { $output = ''; $output .= '
'."\n"; $output .= '

'. $field['widget']['label'] .'

'."\n"; $output .= '
'."\n"; $output .= " "."\n"; $output .= '
'. "" .'
'."\n"; $output .= " "."\n"; $output .= '
'."\n"; $output .= '
'."\n"; return $output; } function contemplate_version(){ return str_replace(array('$RCSf'.'ile:', ',v', '$Re'.'vision: ', '$Da'.'te: ', '$'), '', '

$RCSfile: contemplate.module,v $ version: $Revision: 1.8 $, $Date: 2006-12-12 18:14:26 $

'); }