diff -ruN node_import.orig/import_node.inc node_import/import_node.inc
--- node_import.orig/import_node.inc	1970-01-01 01:00:00.000000000 +0100
+++ node_import/import_node.inc	2005-06-12 21:53:05.890593260 +0200
@@ -0,0 +1,14 @@
+<?php
+/* $Id$ */
+
+if (module_exist('node')) {
+
+  function node_node_import_static($type) {
+    global $user;
+
+    return array('name' => $user->name, 'uid' => $user->uid);
+  }
+
+}
+
+?>
diff -ruN node_import.orig/import_page.inc node_import/import_page.inc
--- node_import.orig/import_page.inc	1970-01-01 01:00:00.000000000 +0100
+++ node_import/import_page.inc	2005-06-12 21:53:05.890593260 +0200
@@ -0,0 +1,17 @@
+<?php
+/* $Id$ */
+
+if (module_exist('page')) {
+
+  function page_node_import_types() {
+    return array('page' => t('Page'));
+  }
+
+  function page_node_import_fields($type) {
+    if ($type == 'page') {
+      return array('body' => t('Body'));
+    }
+  }
+
+}
+?>
diff -ruN node_import.orig/import_story.inc node_import/import_story.inc
--- node_import.orig/import_story.inc	1970-01-01 01:00:00.000000000 +0100
+++ node_import/import_story.inc	2005-06-12 21:53:05.891593044 +0200
@@ -0,0 +1,18 @@
+<?php
+/* $Id$ */
+
+if (module_exist('story')) {
+
+  function story_node_import_types() {
+    return array('story' => t('Story'));
+  }
+
+  function story_node_import_fields($type) {
+    if ($type == 'story') {
+      return array('body' => t('Body'));
+    }
+  }
+
+}
+
+?>
diff -ruN node_import.orig/import_taxonomy.inc node_import/import_taxonomy.inc
--- node_import.orig/import_taxonomy.inc	1970-01-01 01:00:00.000000000 +0100
+++ node_import/import_taxonomy.inc	2005-06-12 21:53:05.892592828 +0200
@@ -0,0 +1,143 @@
+<?php
+/* $Id$ */
+
+if (module_exist('taxonomy')) {
+
+  /*
+  function taxonomy_node_import_types() {
+    //todo: all types? or only types with required vocabularies? or nothing?
+    return array();
+  }
+  */
+
+  function taxonomy_node_import_fields($type) {
+    return array('node_import_taxonomy_list' => t('List of taxonomy terms'));
+  }
+
+  function taxonomy_node_import_prepare(&$node, $preview = FALSE) {
+    // seperator of taxonomy terms
+    $seperator = '|'; //todo: option to set this? where?
+
+    // to avoid multiple messages about the same missing term, we save a list of
+    // terms we already outputted a message for
+    static $missing_terms = array();
+
+    if ($node->taxonomy) {
+      $taxonomy = is_array($node->taxonomy) ? $node->taxonomy : array($node->taxonomy);
+    }
+    else {
+      $taxonomy = array();
+    }
+
+    // add globally selected taxonomy terms
+    $taxonomy = array_merge($taxonomy, $node->node_import_taxonomy_global);
+    unset($node->node_import_taxonomy_global);
+
+    // add node specific taxonomy terms
+    if (strlen($node->node_import_taxonomy_list) > 0) {
+      $terms = explode($seperator, $node->node_import_taxonomy_list);
+
+      if ($node->node_import_taxonomy_options == 'add') {
+        foreach ($terms as $term) {
+          if (!is_numeric($term) && !taxonomy_get_term_by_name($term)) {
+            if (!$preview) {
+              taxonomy_save_term(array('name' => $term, 'description' => '', 'weight' => 0, 'vid' => $node->node_import_taxonomy_vocabulary));
+              drupal_set_message(t('Added term: "%term".', array('%term' => $term)), 'status');
+            }
+            else if (!in_array($term, $missing_terms)) {
+              drupal_set_message(t('Will add term: "%term".', array('%term' => $term)), 'status');
+              $missing_terms[] = $term;
+            }
+          }
+        }
+      }
+
+      foreach ($terms as $term) {
+        if (is_numeric($term)) {
+          $taxonomy[] = $term;
+        }
+        else {
+          $tids = taxonomy_get_term_by_name($term);
+          if ($tids) {
+            foreach ($tids as $tid) {
+              $taxonomy[] = $tid->tid;
+            }
+          }
+          else {
+            switch ($node->node_import_taxonomy_options) {
+              case 'warn':
+                form_set_error('node_import_taxonomy_options', t('Unknown term: "%term".', array('%term' => $term)));
+                break;
+
+              case 'ignore':
+                if ($preview && !in_array($term, $missing_terms)) {
+                  drupal_set_message(t('Will ignore term: "%term".', array('%term' => $term)), 'status');
+                  $missing_terms[] = $term;
+                }
+                break;
+
+              case 'add':
+                break;
+            }
+          }
+        }
+      }
+    }
+    unset($node->node_import_taxonomy_list);
+    unset($node->node_import_taxonomy_options);
+    unset($node->node_import_taxonomy_vocabulary);
+
+    $node->taxonomy = $taxonomy;
+  }
+
+  function taxonomy_node_import_global($type) {
+    // get previously selected options
+    $edit = array_merge($_SESSION['node_import'], $_POST['edit']);
+    $edit = $edit['global'] ? $edit['global'] : array();
+
+    $defaults = array('node_import_taxonomy_options' => 'warn',
+                      'node_import_taxonomy_vocabulary' => array(),
+                      'node_import_taxonomy_global' => array());
+    foreach ($defaults as $key => $value) {
+      if (!array_key_exists($key, $edit)) {
+        $edit[$key] = $value;
+      }
+    }
+
+    // create the form
+    $output = '';
+
+    // Part 1: taxonomy_node_form.
+    $form = implode(' ', taxonomy_node_form($type, array2object(array('taxonomy' => $edit['node_import_taxonomy_global'])), NULL, 'node_import_taxonomy_global'));
+    $description = t('Select the taxonomy terms that will be applied to <em>all</em> imported nodes.');
+    if ($form) {
+      $output .= form_group(t('Select taxonomy terms'), $form, $description);
+    }
+
+    // Part 2: how to handle missing terms.
+    $form = '<table border="0" cellpadding="0" cellspacing="0">';
+    $vocabularies = array();
+    foreach (taxonomy_get_vocabularies() as $voc) {
+      $vocabularies[$voc->vid] = $voc->name;
+    }
+    $options = array();
+    $options['ignore'] = t('Ignore unknown terms.');
+    $options['warn'] = t('Warn about unknown terms.');
+    foreach ($options as $option => $title) {
+      $form .= '<tr><td colspan="2">';
+      $form .= form_radio($title, 'node_import_taxonomy_options', $option, $option == $edit['node_import_taxonomy_options'] ? TRUE : FALSE);
+      $form .= '</td></tr>';
+    }
+    $form .= '<tr><td>';
+    $form .= form_radio(t('Add unknown terms to:'), 'node_import_taxonomy_options', 'add', 'add' == $edit['node_import_taxonomy_options'] ? TRUE : FALSE);
+    $form .= '</td><td>';
+    $form .= form_select('', 'node_import_taxonomy_vocabulary', $edit['node_import_taxonomy_vocabulary'], $vocabularies);
+    $form .= '</td></tr></table>';
+    $description = t('Select how to handle <em>unknown terms</em>. You can "ignore" them in which case the nodes will be imported without these terms. You can "add" the terms to a selected vocabulary prior to importing the nodes. Or you can get a "warning" for each unknown term. In the latter case, the node will not be imported.');
+    $output .= form_group(t('Unknown taxonomy terms'), $form, $description);
+
+    return $output;
+  }
+
+} // if (module_exist('taxonomy'))
+?>
diff -ruN node_import.orig/import_weblink.inc node_import/import_weblink.inc
--- node_import.orig/import_weblink.inc	1970-01-01 01:00:00.000000000 +0100
+++ node_import/import_weblink.inc	2005-06-12 21:53:05.892592828 +0200
@@ -0,0 +1,17 @@
+<?php
+/* $Id$ */
+
+if (module_exist('weblink')) {
+
+  function weblink_node_import_types() {
+    return array('weblink' => t('Link'));
+  }
+
+  function weblink_node_import_fields($type) {
+    if ($type == 'weblink') {
+      return array('url' => t('Link'));
+    }
+  }
+}
+
+?>
diff -ruN node_import.orig/node_import_hook_docs.php node_import/node_import_hook_docs.php
--- node_import.orig/node_import_hook_docs.php	2005-06-12 21:52:33.164672133 +0200
+++ node_import/node_import_hook_docs.php	2005-06-12 21:53:05.943581798 +0200
@@ -39,6 +39,27 @@
 }
 
 /**
+ * Hook that is called before node_validate. This allows a module to
+ * change the imported field from a user readable format to a format
+ * node_save understands.
+ *
+ * See import_taxonomy.inc for an example.
+ *
+ * @param &$node
+ *   A node object which can be changed if needed.
+ * @param $preview
+ *   Boolean. If TRUE this function is only run in a preview stage. The
+ *   function should then avoid making permanent changes to any database
+ *   table. If FALSE the function may commit permanent changes to the
+ *   database that are needed to import the node.
+ * @return
+ *   Nothing.
+ */
+function hook_node_import_prepare(&$node, $preview = FALSE) {
+  return;
+}
+
+/**
  * Provides a list of values which are static for all node imports of a given
  * type.
  *
diff -ruN node_import.orig/node_import.module node_import/node_import.module
--- node_import.orig/node_import.module	2005-06-12 21:52:33.165671917 +0200
+++ node_import/node_import.module	2005-06-12 21:53:05.944581582 +0200
@@ -3,6 +3,12 @@
 
 ini_set('auto_detect_line_endings', TRUE);
 include_once('import_flexinode.inc');
+include_once('import_event.inc');
+include_once('import_taxonomy.inc');
+include_once('import_weblink.inc');
+include_once('import_node.inc');
+include_once('import_story.inc');
+include_once('import_page.inc');
 
 function node_import_help($section) {
   switch ($section) {
@@ -245,11 +251,24 @@
   $j = 0;
   $success = 0;
   while (($row = fgetcsv($handle, 10000, ',')) && ($j++ < $preview || $preview == 0)) {
+
     $node = array2object(array_merge(array('type' => $type), module_invoke_all('node_import_static', $type), $global));
     foreach ($row as $i => $value) {
       $node->$match[$i] = $value;
     }
+
+    // module_invoke_all passes all args by value, not by ref. Otherwise following
+    // loop could have been written as:
+    // module_invoke_all('node_import_prepare', $node, $preview > 0);
+    foreach (module_list() as $name) {
+      $function = $name . '_node_import_prepare';
+      if (function_exists($function)) {
+        $function($node, $preview > 0); 
+      }
+    }
+
     $node = node_validate($node);
+
     if (!node_access('create', $node)) {
       drupal_set_message(t('You are not authorized to post this type of node.'), 'error'); // todo be more specific about what type
     }
@@ -277,7 +296,7 @@
   fclose($handle);
   if ($errors) {
     // todo use drupal_set_message?
-    drupal_set_message(t('%errors. Click \'Download rows with errors\' for a CSV file of the failed rows.', array('%count' => format_plural(count($errors), 'There was 1 error', 'There were %count errors'))));
+    drupal_set_message(t('%errors. Click \'Download rows with errors\' for a CSV file of the failed rows.', array('%errors' => format_plural(count($errors), 'There was 1 error', 'There were %count errors'))));
     $output = form_submit(t('Download rows with errors')) . $output;
     $preview = $errors;
   }
