Index: skeleton.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/skeleton/skeleton.info,v
retrieving revision 1.2
diff -u -r1.2 skeleton.info
--- skeleton.info	8 Dec 2008 02:22:53 -0000	1.2
+++ skeleton.info	7 Jan 2009 02:24:13 -0000
@@ -2,4 +2,5 @@
 name = Skeleton
 description = Creates book outlines based on preset options.
 core=6.x
-dependencies[] = book
\ No newline at end of file
+dependencies[] = book
+dependencies[] = token
Index: skeleton_template.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/skeleton/skeleton_template.inc,v
retrieving revision 1.9
diff -u -r1.9 skeleton_template.inc
--- skeleton_template.inc	7 Jan 2009 01:54:08 -0000	1.9
+++ skeleton_template.inc	7 Jan 2009 02:24:14 -0000
@@ -215,9 +215,10 @@
  * There may be some issues with its handling of array data, like checkboxes
  * radios, and other odd CCK field types.
  *
- * Note that it does not support file uploads
+ * Note that it does not support file uploads.
  */
 function skeleton_form_alter(&$form, $form_state, $form_id) {
+  module_load_include('inc', 'skeleton', 'skeleton_token');
   // only alter forms handled through the skeleton interface
   $form_type = explode('_', $form_id);
   $form_node = $form_type[0] .'_node_form';
@@ -270,6 +271,31 @@
       '#description' => t('The node type for this template. <em>This value cannot be edited.</em>'),
       '#disabled' => TRUE
     );
+
+    // Add token help information below the skeleton fieldset.
+    $form['skeleton_tokens'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Skeleton replacement tokens'),
+      '#description' => t('These tokens will be replaced in the title and the body when instantiating a skeleton outline'),
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE,
+      '#weight' => -99,
+    );
+    $form['skeleton_tokens']['tokens'] = array(
+      '#value' => _skeleton_build_token_help('skeleton'),
+    );
+    $form['node_tokens'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Node replacement tokens'),
+      '#description' => t('These tokens will be replaced in the title and the body when instantiating a skeleton outline'),
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE,
+      '#weight' => -98,
+    );
+    $form['node_tokens']['tokens'] = array(
+      '#value' => _skeleton_build_token_help('node'),
+    );
+
     // unset the values we don't want or cannot save
     $unset = array('parent', 'weight', 'menu', 'book', 'path', 'attachments', 'field_file', 'revision_information', 'log', 'buttons', '#after_build', '#submit');
     foreach ($unset as $item) {
Index: skeleton_instance.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/skeleton/skeleton_instance.inc,v
retrieving revision 1.9
diff -u -r1.9 skeleton_instance.inc
--- skeleton_instance.inc	7 Jan 2009 01:54:08 -0000	1.9
+++ skeleton_instance.inc	7 Jan 2009 02:24:14 -0000
@@ -451,6 +451,47 @@
     '#type' => 'value',
     '#value' => $skeleton->skeleton_id,
   );
+
+  // First, we need to determine if any skeleton tokens are used in this skeleton instance.
+  $templates = skeleton_get_tree($skeleton->skeleton_id);
+
+  // Fetch all of the custom tokens.
+  $skeleton_tokens = array();
+  $result = db_query("SELECT * FROM {skeleton_token}");
+  while ($token = db_fetch_object($result)) {
+    $token->description = check_plain($token->description);
+    $skeleton_tokens[] = $token;
+  }
+
+  // For each template, find any tokens used and put them in $required_tokens.
+  $required_tokens = array();
+  foreach ($templates as $template_data) {
+    $node = (object)unserialize($template_data->node_data);
+    foreach ($skeleton_tokens as $token) {
+      if (strstr($node->title, $token->token) || strstr($node->body, $token->token)) {
+        $required_tokens[] = $token;
+      }
+    }
+  }
+
+  // If there are required tokens, generate the form.
+  if (!empty($required_tokens)) {
+    $form['skeleton_tokens'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Skeleton tokens'),
+      '#description' => t('The following tokens are used in this skeleton outline. Please provide values for the following tokens.'),
+      '#collapsible' => TRUE,
+      '#tree' => TRUE,
+    );
+    foreach ($required_tokens as $token) {
+      $form['skeleton_tokens'][$token->token] = array(
+        '#type' => 'textfield',
+        '#title' => $token->token,
+        '#description' => $token->description,
+        '#required' => TRUE,
+      );
+    }
+  }
   $form['submit'] = array(
     '#type' => 'submit',
     '#value' => t('Create new book from skeleton'),
@@ -468,8 +509,10 @@
 function skeleton_create_instance_form_submit($form, &$form_state) {
   include_once drupal_get_path('module', 'node') . '/node.pages.inc';
   global $user;
+  module_load_include('inc', 'node', 'node.pages');
+  module_load_include('inc', 'skeleton', 'skeleton_token');
+
   $nodes = skeleton_get_tree($form_state['values']['skeleton_id']);
-  //dpr ("Inserting nodes...");
   if (!empty($nodes)) {
     $parents = array();
     // get the next node id and use it for the parent
@@ -508,6 +551,14 @@
       $node['values']['book']['bid'] = $book_id;
       $node['values']['book']['plid'] = $parent_node->book['mlid'];
       $node['values']['book']['options'] = array();
+
+      // Replace custom tokens in the title and body fields. Replace skeleton
+      // tokens first so they may be used as node tokens such as [title].
+      $node['values']['title'] = token_replace($node['values']['title'], 'skeleton', $form_state['values']['skeleton_tokens']);
+      $node['values']['title'] = token_replace($node['values']['title'], 'node', (object)$form_state['values']);
+      $node['values']['body'] = token_replace($node['values']['body'], 'skeleton', $form_state['values']['skeleton_tokens']);
+      $node['values']['body'] = token_replace($node['values']['body'], 'node', (object)$form_state['values']);
+
       $node['values']['op'] = t('Save');
       drupal_execute($node['values']['type'] .'_node_form', $node, (object)$node['values']);
       // Store all of the menu id's for each inserted template.
Index: skeleton.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/skeleton/skeleton.install,v
retrieving revision 1.4
diff -u -r1.4 skeleton.install
--- skeleton.install	8 Dec 2008 22:05:01 -0000	1.4
+++ skeleton.install	7 Jan 2009 02:24:13 -0000
@@ -77,6 +77,37 @@
       'template_id',
     ),
   );
+  
+  $schema['skeleton_token'] = array(
+    'fields' => array(
+      'token_id' => array('type' => 'serial', 'length' => 11),
+      'token' => array('type' => 'varchar', 'length' => 80),
+      'description' => array('type' => 'varchar', 'length' => 80),
+    ),
+    'primary key' => array(
+      'token_id',
+    ),
+  );
 
   return $schema;
 }
+
+/**
+ * Add a table for storing custom tokens and their descriptions.
+ */
+function skeleton_update_6001() {
+  $ret = array();
+  $schema = array();
+  $schema['skeleton_token'] = array(
+    'fields' => array(
+      'token_id' => array('type' => 'serial', 'length' => 11),
+      'token' => array('type' => 'varchar', 'length' => 80),
+      'description' => array('type' => 'varchar', 'length' => 80),
+    ),
+    'primary key' => array(
+      'token_id',
+    ),
+  );
+  db_create_table($ret, 'skeleton_token', $schema['skeleton_token']);
+  return $ret;
+}
Index: skeleton.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/skeleton/skeleton.module,v
retrieving revision 1.12
diff -u -r1.12 skeleton.module
--- skeleton.module	7 Jan 2009 01:54:08 -0000	1.12
+++ skeleton.module	7 Jan 2009 02:24:13 -0000
@@ -70,17 +70,59 @@
     'title' => 'Add skeleton',
     'file' => 'skeleton_admin.inc',
   );
+  $items['admin/content/skeleton/token'] = array(
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('skeleton_token_form'),
+    'access arguments' => array('configure skeleton outlines'),
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 10,
+    'title' => 'Tokens',
+    'file' => 'skeleton_token.inc',
+  );
+  $items['admin/content/skeleton/token/view'] = array(
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('skeleton_token_form'),
+    'access arguments' => array('configure skeleton outlines'),
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -10,
+    'title' => 'List',
+    'file' => 'skeleton_token.inc',
+  );
+  $items['admin/content/skeleton/token/add'] = array(
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('skeleton_add_token_form'),
+    'access arguments' => array('configure skeleton outlines'),
+    'type' => MENU_LOCAL_TASK,
+    'title' => 'Add token',
+    'file' => 'skeleton_token.inc',
+  );
+  $items['admin/content/skeleton/token/%skeleton_token/edit'] = array(
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('skeleton_add_token_form', 4),
+    'access arguments' => array('configure skeleton outlines'),
+    'type' => MENU_CALLBACK,
+    'title' => 'Edit token',
+    'file' => 'skeleton_token.inc',
+  );
+  $items['admin/content/skeleton/token/%skeleton_token/delete'] = array(
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('skeleton_delete_token_form', 4),
+    'access arguments' => array('configure skeleton outlines'),
+    'type' => MENU_CALLBACK,
+    'title' => 'Delete token',
+    'file' => 'skeleton_token.inc',
+  );
   // Template items.
   $items['admin/content/skeleton/template'] = array(
     'page callback' => 'skeleton_list_template',
     'page arguments' => array('0'),
     'access arguments' => array('configure skeleton outlines'),
     'type' => MENU_LOCAL_TASK,
-    'weight' => 10,
+    'weight' => 8,
     'title' => 'Templates',
     'file' => 'skeleton_template.inc',
   );
-    $items['admin/content/skeleton/template/view'] = array(
+  $items['admin/content/skeleton/template/view'] = array(
     'page callback' => 'skeleton_view_template',
     'page arguments' => array('0'),
     'access arguments' => array('configure skeleton outlines'),
@@ -180,6 +222,15 @@
     'skeleton_define_form' => array(
       'arguments' => array('form' => NULL),
     ),
+    'skeleton_token_form' => array(
+      'arguments' => array('form' => NULL),
+    ),
+    'skeleton_token_help' => array(
+      'arguments' => array('tokens' => NULL),
+    ),
+    'skeleton_other_token_help' => array(
+      'arguments' => array('tokens' => NULL),
+    ),
     'skeleton_na_form_element' => array(
       'arguments' => array('form' => NULL),
     ),
@@ -216,3 +267,16 @@
   return $result;
 }
 
+/**
+ * Load a skeleton token from the database.
+ *
+ * @param $token_id
+ *   The id of the token to load.
+ * @return
+ *   The fully loaded skeleton token object. This object contains three
+ *   properties: token_id, token, and description.
+ */
+function skeleton_token_load($token_id) {
+  $result = db_fetch_object(db_query("SELECT * FROM {skeleton_token} WHERE token_id = %d", $token_id));
+  return empty($result) ? FALSE : $result;
+}
Index: skeleton_token.inc
===================================================================
RCS file: skeleton_token.inc
diff -N skeleton_token.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ skeleton_token.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,336 @@
+<?php
+
+// $Id
+
+/**
+ * Form API callback for Tokens tab
+ */
+function skeleton_token_form() {
+  $form = array();
+  $form['skeleton_tokens_help'] = array(
+    '#prefix' => '<p>',
+    '#suffix' => '</p>',
+    '#value' => t('Skeleton tokens allows the use of tokens to automatically customize instantiated templates. For example, the [site-mail] token will insert the site administrative email address. Custom tokens added on this page will be presented in a form to fill in values when instantiating a template.'),
+  );
+  $form['skeleton_tokens'] = array(
+    '#type' => 'markup',
+    '#value' => _skeleton_build_token_help('skeleton'),
+  );
+  $form['tokens_fieldset'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Node tokens'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+  );
+  $form['tokens_fieldset']['tokens'] = array(
+    '#type' => 'markup',
+    '#value' => _skeleton_build_token_help('node'),
+  );
+  return $form;
+}
+
+/**
+ * Implementation of theme function for the skeleton token page.
+ *
+ * @param $form
+ *   The Form API array to display
+ * @return
+ *   The themed HTML.
+ */
+function theme_skeleton_token_form($form) {
+  $output .= drupal_render($form);
+  return $output;
+}
+
+/**
+ * Forms API for the add token form. This allows new skeleton tokens to be
+ * added and existing tokens to be edited.
+ *
+ * @param $form_id
+ *   The form_id of the current form.
+ * @param $token
+ *   The token object to edit, or NULL if adding a new token.
+ * @return
+ *   Form API form array.
+ */
+function skeleton_add_token_form($form_id, $token = NULL) {
+  $form = array();
+  $form['token_name'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Token name'),
+    '#description' => t('Enter the name of a new token to create. Do not include the enclosing brackets. Only letters, numbers, and dashes are allowed.'),
+    '#default_value' => empty($token) ? "" : $token->token,
+    '#maxlength' => 80,
+    '#required' => TRUE,
+  );
+  $form['token_description'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Token description'),
+    '#description' => t('Enter a description for this token describing what it is for.'),
+    '#default_value' => empty($token) ? "" : check_plain($token->description),
+    '#maxlength' => 80,
+    '#required' => TRUE,
+  );
+  if (!empty($token)) {
+    $form['token_id'] = array(
+      '#type' => 'value',
+      '#value' => $token->token_id,
+    );
+  }
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+  );
+  $form['cancel'] = array(
+    '#type' => 'submit',
+    '#value' => t('Cancel'),
+  );
+  $form['#redirect'] = 'admin/content/skeleton/token';
+  return $form;
+}
+
+/**
+ * Validation function for skeleton_add_token_form.
+ * 
+ * We only allow letters, numbers, and dashes in the token name.
+ * 
+ * @param $form
+ *   The form being validated.
+ * @param $form_state
+ *   The current state of skeleton_add_token_form.
+ */
+function skeleton_add_token_form_validate($form, &$form_state) {
+  if (!($form_state['values']['op'] == t('Cancel'))) {
+    if (!preg_match('!^[a-zA-Z0-9\-]+$!', $form_state['values']['token_name'])) {
+      form_set_error('token_name', t('Only letters, numbers, and dashes are allowed.'));
+    }
+  }
+}
+
+/**
+ * Submit function for skeleton_add_token_form.
+ * 
+ * Adds or updates a token to the database.
+ * 
+ * @param $form
+ *   The form being submitted.
+ * @param $form_state
+ *   The current state of the skeleton_add_token_form.
+ */
+function skeleton_add_token_form_submit($form, &$form_state) {
+  if (!($form_state['values']['op'] == t('Cancel'))) {
+    $token = new stdClass();
+    $token->token = $form_state['values']['token_name'];
+    $token->description = $form_state['values']['token_description'];
+    if ($form_state['values']['token_id']) {
+      $token->token_id = $form_state['values']['token_id'];
+      db_query("UPDATE {skeleton_token} SET token = '%s', description = '%s' WHERE token_id = %d", $token->token, $token->description, $token->token_id);
+    }
+    else {
+      db_query("INSERT INTO {skeleton_token} (token, description) VALUES('%s', '%s')", $token->token, $token->description);
+    }
+  }
+}
+
+/**
+ * Form API for the delete token form.
+ * 
+ * @param $form_id
+ *   The form_id of the current form.
+ * @param $token
+ *   The token object being deleted.
+ * @return
+ *   Form API array of the deletion form.
+ */
+function skeleton_delete_token_form($form_id, $token) {
+  $form['token_id'] = array(
+    '#type' => 'value',
+    '#value' => $token->token_id,
+  );
+  return confirm_form($form,
+    t('Are you sure you want to delete the token %token?', array('%token' => $token->token)),
+    'admin/content/skeleton/token',
+    t('This action cannot be undone.'),
+    t('Delete token'),
+    t('Cancel')
+  );
+}
+
+/**
+ * Submit function for skeleton_delete_token_form.
+ * 
+ * @param $form
+ *   The current form.
+ * @param $form_state
+ *   The state of the current form.
+ */
+function skeleton_delete_token_form_submit($form, &$form_state) {
+  db_query("DELETE from {skeleton_token} WHERE token_id = %d", $form_state['values']['token_id']);
+  drupal_set_message(t('Token deleted.'));
+  drupal_goto('admin/content/skeleton/token');
+}
+
+/**
+ * Implementation of hook_token_list.
+ *
+ * @param $type
+ *   The type of token list to return. Either 'skeleton' or 'all'.
+ * @return
+ *   An array of skeleton tokens.
+ */
+function skeleton_token_list($type = 'all') {
+  if ($type == 'skeleton' || $type == 'all') {
+    $tokens = array();
+    $tokens['skeleton'] = array();
+    $result = db_query("SELECT * FROM {skeleton_token}");
+    while ($token = db_fetch_object($result)) {
+      $tokens['skeleton'][$token->token] = check_plain($token->description);
+    }
+    return $tokens;
+  }
+}
+
+/**
+ * Implementation of hook_token_values.
+ *
+ * @param $type
+ *   The type of tokens to return.
+ * @param $object
+ *   An array of skeleton tokens with keys as tokens as values as the value to
+ *   be replaced.
+ * @param $options
+ *   Any options passed to token_replace.
+ * @return
+ *   An array of tokens and their replacement values.
+ */
+function skeleton_token_values($type, $object = NULL, $options = array()) {
+  $tokens = array();
+  if ($type == 'skeleton') {
+    $skeleton_tokens = $object;
+    foreach ($skeleton_tokens as $token => $value) {
+      $tokens[$token] = $value;
+    }
+    return $tokens;
+  }
+}
+
+/**
+ * Private function to generate HTML for showing the available tokens.
+ *
+ * @param type
+ *   The token type to return, or 'all' for all tokens.
+ * @return
+ *   The themed representation of the available tokens.
+ */
+function _skeleton_build_token_help($type = 'all') {
+  if ($type == 'skeleton') {
+    return _skeleton_build_skeleton_token_help();
+  }
+  else {
+    return _skeleton_build_other_token_help($type);
+  }
+}
+
+/**
+ * Build the help for custom skeleton tokens.
+ * 
+ * @return
+ *   The themed representation of available skeleton tokens.
+ */
+function _skeleton_build_skeleton_token_help() {
+  static $help_html;
+  if (empty($help_html)) {
+    $skeleton_tokens = array();
+    $result = db_query("SELECT * FROM {skeleton_token}");
+    while ($token = db_fetch_object($result)) {
+      $token->description = check_plain($token->description);
+      $skeleton_tokens[] = $token;
+    }
+    if (empty($skeleton_tokens)) {
+      $help_html = '<p>' . t('No custom tokens have been created. <a href="@add-url">Add a token</a> to use them in skeleton outlines.', array('@add-url' => url('admin/content/skeleton/token/add')));
+    }
+    else {
+      $help_html = theme('skeleton_token_help', $skeleton_tokens);
+    }
+  }
+  return $help_html;
+}
+
+/**
+ * Private function to generate HTML for showing the available tokens
+ *
+ * @param type
+ *   The token type to return, or 'all' for all tokens.
+ * @return
+ *   The themed representation of the available tokens.
+ */
+function _skeleton_build_other_token_help($type = 'all') {
+  if (!isset($help_html)) {
+    static $help_html = array();
+  }
+  $help_html[$type] = '';
+  if (empty($help_html[$type])) {
+    if ($type == 'all') {
+      $patterns = token_get_list();
+    }
+    else {
+      $patterns = token_get_list($type);
+      // We unset global tokens so they only show when 'all' is passed.
+      unset($patterns['global']);
+    }
+    foreach ($patterns as $pattern_type => $pattern_set) {
+      foreach ($pattern_set as $pattern => $description) {
+        $tokens[$pattern] = $description;
+      }
+    }
+    $help_html[$type] = theme('skeleton_other_token_help', $tokens);
+  }
+  return $help_html[$type];
+}
+
+/**
+ * theme_skeleton_token_help()
+ *
+ * @param $tokens
+ *   An array of skeleton token objects.
+ *
+ * @return
+ *   The themed representation of the tokens with the appropriate operations.
+ */
+function theme_skeleton_token_help($tokens) {
+  $rows = array();
+  foreach ($tokens as $token) {
+    $row = array();
+    $row[] = '[' . $token->token . ']';
+    $row[] = $token->description;
+    $row[] = theme('item_list', array(l(t('edit token'), 'admin/content/skeleton/token/' . $token->token_id . '/edit'), l(t('delete token'), 'admin/content/skeleton/token/' . $token->token_id . '/delete')));
+    $rows[] = $row;
+  }
+  $header = array(t('Token'), t('Description'), t('Operations'));
+  $tokens_html .= theme('table', $header, $rows);
+
+  return $tokens_html;
+}
+
+/**
+ * Theme help for tokens other than skeleton tokens.
+ *
+ * @param $tokens
+ *   An array of tokens to display.
+ * @return
+ *   The themed representation of the tokens with the appropriate operations.
+ */
+function theme_skeleton_other_token_help($tokens) {
+  $tokens_html = "";
+  $rows = array();
+  foreach ($tokens as $name => $description) {
+    $row = array();
+    $row[] = '['. $name .']';
+    $row[] = $description;
+    $rows[] = $row;
+  }
+  $header = array(t('Token'), t('Description'));
+  $tokens_html .= theme('table', $header, $rows);
+
+  return $tokens_html;
+}
