Index: README.txt
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/community_tags/README.txt,v
retrieving revision 1.3.4.1
diff -u -r1.3.4.1 README.txt
--- README.txt	11 Apr 2007 12:36:57 -0000	1.3.4.1
+++ README.txt	3 May 2007 00:40:40 -0000
@@ -23,7 +23,7 @@
 
      Administer > Site Building > Modules
 
-4. Create a free tagging vocabulary by going to:
+4. Create a vocabulary by going to:
 
      Administer > Content > Categories
 
Index: community_tags.css
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/community_tags/community_tags.css,v
retrieving revision 1.3
diff -u -r1.3 community_tags.css
--- community_tags.css	11 Apr 2007 12:31:32 -0000	1.3
+++ community_tags.css	10 May 2007 19:58:28 -0000
@@ -19,16 +19,20 @@
   position: relative;
   display: inline;
 }
-div.tag-widget ul.inline-tags li:hover, div.tag-widget ul.inline-tags li.hover {
+div.tag-widget ul.inline-tags li.community-tags-delete:hover, div.tag-widget ul.inline-tags li.community-tags-delete.hover {
   background: #fdd url('delete.png') no-repeat 100% 50%;
   z-index: 2;
 }
+div.tag-widget ul.inline-tags li.community-tags-add:hover, div.tag-widget ul.inline-tags li.community-tags-add.hover {
+  background: #98fb98 url('add.png') no-repeat 100% 50%;
+  z-index: 2;
+}
 div.tag-widget input.form-tags {
   width: 16em;
 }
 .block div.tag-widget input.form-tags {
   width: 12em;
 }
-html.js span.no-js, html.js input.form-tags {
+html.js span.no-js, html.js input.form-tags, html.js select.form-taxonomy {
   display: none;
 }
Index: community_tags.js
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/community_tags/community_tags.js,v
retrieving revision 1.1.2.1
diff -u -r1.1.2.1 community_tags.js
--- community_tags.js	11 Apr 2007 22:44:49 -0000	1.1.2.1
+++ community_tags.js	14 May 2007 20:55:20 -0000
@@ -13,7 +13,7 @@
   for (i in data) {
     var name = prefix.length ? (prefix +'[' + i +']') : i;
     if (out.length) out += '&';
-    if (typeof data[i] == 'object') {
+    if ((typeof data[i] == 'object') || (typeof data[i] == 'array')) {
       out += Drupal.serialize(data[i], name);
     }
     else {
@@ -24,78 +24,190 @@
   return out;
 }
 
-if (Drupal.jsEnabled) {
-  $(document).ready(function () {
-    // Note: all tag fields are autocompleted, and have already been initialized at this point.
-    $('input.form-tags').each(function () {
+Drupal.communityTags = {
+  data: null,
+  sequence: 0,
+  attach: function () {
+    Drupal.communityTags.data = Drupal.settings.communityTags;
+    // Within the jQuery block, 'this' becomes a matched element reference,
+    // so we need to explicitly invoke Drupal.communityTags methods and properties.
+    $('input.form-tags:not(.communityTags-processed), select.form-taxonomy:not(.communityTags-processed)').each(function () {
       // Hide submit buttons.
       $('input[@type=submit]', this.form).hide();
-
-      // Fetch settings.
-      var o = Drupal.settings.communityTags;
-      var sequence = 0;
-
-      // Show the textfield and empty its value.
-      var textfield = $(this).val('').css('display', 'inline');
-
-      // Prepare the add Ajax handler and add the button.
-      var addHandler = function () {
-        // Send existing tags and new tag string.
-        $.post(o.url, Drupal.serialize({ sequence: ++sequence, tags: o.tags, add: textfield[0].value }), function (data) {
-          data = Drupal.parseJson(data);
-          if (data.status && sequence == data.sequence) {
-            o.tags = data.tags;
-            updateList();
+      var nid = $('input[@name=nid]', this.form).attr('value');
+      var input = $(this);
+      // Determine the vocabulary type.
+      var type = input[0].nodeName.toLowerCase() == 'select' ? 'regular' : 'freetagging';
+
+      switch(type) {
+        case 'regular':
+          if (typeof Drupal.communityTags.data.tags != 'object') {
+            Drupal.communityTags.data.tags = {};
           }
-        });
-        
+          Drupal.communityTags.data.tags[nid] = [];
+    
+          // Hide the select.
+          input.css('display', 'none');
+    
+          var multiple = input.attr('multiple') == 'multiple';
+    
+          // Read in data from the select.
+          $('option', input).each(function () {
+            if (($(this).attr('value') != 'undefined') && ($(this).attr('value') != 0)) {
+              Drupal.communityTags.data.tags[nid].push({name: $(this).html(), tid: $(this).attr('value'), status: $(this).attr('selected') == 'selected', multiple: multiple});
+            }
+          });
+          var button = '';
+          break;
+        case 'freetagging':
+          // Show the textfield and empty its value.
+          input.val('').css('display', 'inline');
+          var button = $('<input type="button" value="'+ Drupal.checkPlain(Drupal.communityTags.data.addButton) +'" />').click(function () { Drupal.communityTags.add(nid, type, input)});
+          $(this.form).submit(function () { Drupal.communityTags.add(nid, type, input); return false; });
+          break;
+      }
+      // Create widget markup.
+      var widget = $('<div class="tag-widget" id="tag-widget-'+ nid +'"><ul class="inline-tags clear-block"></ul></div>');
+      input.before(widget);
+      widget.append(input).append(button);
+
+      Drupal.communityTags.updateList(nid, type);
+      $(this).addClass('communityTags-processed');
+    });  
+  },
+  add: function (nid, type, elt) {
+    switch (type) {
+      case 'freetagging':
         // Add tag to local list
-        o.tags.push(textfield[0].value);
-        o.tags.sort();
-        updateList();
-        
+        var item = $(elt).attr('value');
+        item = item.replace(', ', ',').split(',');
+        for (i in item) {
+          this.data.tags[nid].push(item[i]);
+        }
+        this.data.tags[nid].sort();
+       
         // Clear field and focus it.
-        textfield.val('').focus();
-      };
-      var button = $('<input type="button" value="'+ Drupal.checkPlain(o.add) +'" />').click(addHandler);
-      $(this.form).submit(function () { addHandler(); return false; });
+        $(elt).val('').focus();
+        break;
+      case 'regular':
+        // Set tag status in local list.
+        for (var i in this.data.tags[nid]) {
+          var prefix = 'community-tags-'+ nid +'-';
+          // The tid is the id minus the prefix 'community-tags-'.
+          if ($(elt).attr('id').substring(prefix.length) == this.data.tags[nid][i].tid) {
+            this.data.tags[nid][i].status = true;
+          }
+          // If it's not a multiple select vocabulary, only allow
+          // one term.
+          else if (!this.data.tags[nid][i].multiple) {
+            this.data.tags[nid][i].status = false;
+          }
+        }
+        break;
+    }
+    this.post(nid, type);
+    this.updateList(nid, type);
+  },
+  del:  function (nid, type, elt) {
+
+    var prefix = 'community-tags-'+ nid +'-';
+    var id = $(elt).attr('id').substring(prefix.length);
 
-      // Prepare the delete Ajax handler.
-      var deleteHandler = function () {
+    switch (type) {
+      case 'freetagging':
         // Remove tag from local list.
-        var i = $(this).attr('key');
-        o.tags.splice(i, 1);
-        updateList();
-
-        // Send new tag list.
-        $.post(o.url, Drupal.serialize({ sequence: ++sequence, tags: o.tags, add: '' }), function (data) {
-          data = Drupal.parseJson(data);
-          if (data.status && sequence == data.sequence) {
-            o.tags = data.tags;
-            updateList();
+        this.data.tags[nid].splice(id, 1);
+        // Clear field and focus it.
+        $(elt).val('').focus();
+        break;
+      case 'regular':
+        // Set tag status in local list.
+        for (var i in this.data.tags[nid]) {
+          if (id == this.data.tags[nid][i].tid) {
+            this.data.tags[nid][i].status = false;
+            break;
           }
-        });
-
-        // Clear textfield and focus it.
-        textfield.val('').focus();
-      };
-
-      // Callback to update the tag list.
-      function updateList() {
-        list.empty();
-        for (i in o.tags) {
-          list.append('<li key="'+ Drupal.checkPlain(i) +'">'+ Drupal.checkPlain(o.tags[i]) +'</li>');
         }
-        $('li', list).click(deleteHandler);
+        break;
+    }
+    this.post(nid, type);
+    this.updateList(nid, type);
+  },
+  post: function (nid, type) {
+    var postData = { sequence: ++this.sequence };
+    switch (type) {
+      case 'freetagging':
+        // For freetagging vocabularies, we post an array of tags.
+        postData.tags = this.data.tags[nid];
+        break;
+      case 'regular':
+        // For regular vocabularies, we post an array of tid values.
+        var taxonomy = [];
+        for (var i in this.data.tags[nid]) {
+          if (this.data.tags[nid][i].status == true) {
+            taxonomy.push(this.data.tags[nid][i].tid);
+          }
+        }
+        postData.taxonomy = taxonomy;
+        break;
+    }
+    $.post(this.data.url +'/'+ nid, Drupal.serialize(postData), function (data) {
+      data = Drupal.parseJson(data);
+      if (data.status && this.sequence == data.sequence) {
+        // Set tags in local list.
+        switch (type) {
+          case 'freetagging':
+            this.data.tags[data.nid] = data.taxonomy;
+            break;
+          case 'regular':
+            for (var i in this.data.tags[data.nid]) {
+              this.data.tags[data.nid][i].status = false;
+              for (var j in data.taxonomy) {
+                if (this.data.tags[data.nid][i].tid == data.taxonomy[j]) {
+                  this.data.tags[data.nid][i].status = true;
+                  break;
+                }
+              }
+            }
+            break;
+        }
+        this.updateList(data.nid, type);
+      }
+    });
+  },
+  // Update the tag list.
+  updateList: function (nid, type) {
+    var list = $('#tag-widget-'+ nid +' ul');
+    list.empty();
+
+    for (i in this.data.tags[nid]) {
+      switch (type) {
+        case 'freetagging':
+          var text = Drupal.checkPlain(this.data.tags[nid][i]);
+          var newClass = 'delete';
+          var id = Drupal.checkPlain(i);
+          break;
+        case 'regular':
+          // Regular vocabulary tag links can be either added or deleted
+          // depending on their current status.
+          var text = (this.data.tags[nid][i].status ? Drupal.checkPlain(this.data.remove) : Drupal.checkPlain(this.data.add)) +' '+ this.data.tags[nid][i].name;
+          var newClass = this.data.tags[nid][i].status ? 'delete' : 'add';
+          var id = this.data.tags[nid][i].tid;
+          break;
       }
 
-      // Create widget markup.
-      var widget = $('<div class="tag-widget"><ul class="inline-tags clear-block"></ul></div>');
-      textfield.before(widget);
-      widget.append(textfield).append(button);
-      var list = $('ul', widget);
+      list.append('<li id="community-tags-'+ nid +'-'+ id +'" class="community-tags-'+ newClass +'">'+ text +'</li>');
 
-      updateList();
+    }
+    $('li.community-tags-delete', list).click(function () {
+      Drupal.communityTags.del(nid, type, $(this)[0]);
+    });
+    $('li.community-tags-add', list).click(function () {
+      Drupal.communityTags.add(nid, type, $(this)[0]);
     });
-  });
+  }
+}
+
+if (Drupal.jsEnabled) {
+  $(document).ready(Drupal.communityTags.attach);
 }
\ No newline at end of file
Index: community_tags.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/community_tags/community_tags.module,v
retrieving revision 1.32.2.1
diff -u -r1.32.2.1 community_tags.module
--- community_tags.module	3 May 2007 09:14:45 -0000	1.32.2.1
+++ community_tags.module	11 May 2007 15:47:58 -0000
@@ -14,7 +14,7 @@
 function community_tags_help($section) {
   switch ($section) {
     case 'admin/settings/community-tags':
-      return t('To set up community tagging, you must first <a href="@taxonomy">create a normal free tagged vocabulary</a>. Then activate community tagging on such a vocabulary below, and set the <a href="@workflow">workflow options</a> for node types to control how users can tag nodes.', array('@taxonomy' => url('admin/content/taxonomy'), '@workflow' => url('admin/content/types')));
+      return t('To set up community tagging, you must first <a href="@taxonomy">create a normal vocabulary</a>. Then activate community tagging on such a vocabulary below, and set the <a href="@workflow">workflow options</a> for node types to control how users can tag nodes.', array('@taxonomy' => url('admin/content/taxonomy'), '@workflow' => url('admin/content/types')));
       break;
   }
 }
@@ -159,6 +159,8 @@
  */
 function community_tags_node_view($node, $inline = TRUE) {
   global $user;
+  static $sent;
+
   if (is_numeric($node)) {
     $node = node_load($node);
   }
@@ -167,30 +169,39 @@
     drupal_set_title(check_plain($node->title));
   }
 
-  $cloud = theme('community_tags', 'node', NULL, $node->nid);
-
   $vid = array_shift(community_tags_for_node($node));
+  $vocabulary = taxonomy_get_vocabulary($vid);
+
+  $cloud = $vocabulary->tags ? theme('community_tags', 'node', NULL, $node->nid) : NULL;
+
   $tags = community_tags_get_user_node_tags($user->uid, $node->nid);
   $names = array();
 
   if (!count($tags)) {
     // User has not yet added tags to this node yet. Show form.
-    $output .= drupal_get_form('community_tags_form', array('cloud' => $cloud, 'nid' => $node->nid, 'vid' => $vid, 'tags' => NULL, 'inline' => $inline));
+    $output .= drupal_get_form('community_tags_form', array('cloud' => $cloud, 'nid' => $node->nid, 'vid' => $vid, 'taxonomy' => NULL, 'inline' => $inline));
   }
   elseif (user_access('edit own tags')) {
     // User has already tagged this node, but can edit their tags. Show form
     // with the user's tags pre-populated.
     $names = community_tags_flatten($tags);
-    $tags = taxonomy_implode_tags($tags);
-    $output .= drupal_get_form('community_tags_form', array('cloud' => $cloud, 'nid' => $node->nid, 'vid' => $vid, 'tags' => $tags, 'inline' => $inline));
+    $taxonomy = $vocabulary->tags ? array('tags' => taxonomy_implode_tags($tags)) : array_keys($tags);
+    $output .= drupal_get_form('community_tags_form', array('cloud' => $cloud, 'nid' => $node->nid, 'vid' => $vid, 'taxonomy' => $taxonomy, 'inline' => $inline));
   }
   else {
     // Sorry, no more adding tags for you!
     $output .= '<p>'. t('You have already tagged this post. Your tags: ') . theme('community_tags', 'user_node', NULL, $user->uid, $node->nid) .'</p>';
   }
 
-  drupal_add_js(array('communityTags' => array('tags' => $names, 'url' => url('community-tags/'. $node->nid), 'add' => t('Add'))), 'setting');
-
+  // Only set Javascript once.
+  if ($sent == NULL) {
+    drupal_add_js(array('communityTags' => array('url' => url('community-tags'), 'addButton' => t('Add'), 'add' => t('add to'), 'remove' => t('remove from'))), 'setting');
+    $sent = TRUE;
+  }
+  // Set data for this node.
+  if ($vocabulary->tags) {
+    drupal_add_js(array('communityTags' => array('tags' => array($node->nid => $names))), 'setting');
+  }
   return $output;
 }
 
@@ -229,7 +240,7 @@
       global $user;
       // Show quick tag form for this node if we're on a node page view and the
       // form is enabled for this node and the default quick tag vocab is set.
-      if (!$teaser && $node->community_tags_form) {
+      if ($node->community_tags_form && (!$teaser || variable_get('community_tags_teaser_'. $node->type, 0))) {
         $node->content['community_tags'] = array(
           '#value' => community_tags_node_view($node, TRUE),
           '#weight' => 50,
@@ -247,8 +258,8 @@
 
   community_tags_rehash();
 
-  // Build list of available free-tagging vocabularies
-  $vocabs = db_query('SELECT v.vid, v.name FROM {vocabulary} v WHERE v.tags = 1 ORDER BY v.weight, v.name');
+  // Build list of available vocabularies.
+  $vocabs = db_query('SELECT v.vid, v.name FROM {vocabulary} v ORDER BY v.weight, v.name');
   while ($vocabulary = db_fetch_object($vocabs)) {
     $options[$vocabulary->vid] = $vocabulary->name;
   }
@@ -286,6 +297,12 @@
       '#options' => $modes,
       '#description' => t('How should users be allowed to tag content?')
     );
+    $form['workflow']['community_tags_teaser'] = array(
+      '#type' => 'checkbox',
+      '#title' => t('Show on node teasers'),
+      '#default_value' => variable_get('community_tags_teaser_'. $form['#node_type']->type, 0),
+      '#description' => t('When the tagging form is displayed inline, do you wish to show it on node teasers? If this option is not checked, the form only displays only on full node views.'),
+    );
   }
 }
 
@@ -306,29 +323,42 @@
  * Quick tag form
  */
 function community_tags_form($edit, $title = NULL) {
-  $form['cloud'] = array(
-    '#type' => 'markup',
-    '#title' => t('All tags'),
-    '#value' => $edit['cloud'],
-  );
-  
+
+  // Only show if there are terms already and this is a free tagging vocabulary.
+  if ($edit['cloud']) {
+    $form['cloud'] = array(
+      '#type' => 'markup',
+      '#title' => t('All tags'),
+      '#value' => $edit['cloud'],
+    );
+  }
+
+  $vocabulary = taxonomy_get_vocabulary($edit['vid']);
   $access = user_access('tag content');
 
-  $form['tags'] = array(
-    '#type' => 'textfield',
-    '#title' => t('My tags'),
-    '#maxlength' => 100,
-    '#default_value' => $edit['tags'],
-    '#required' => FALSE,
-    '#autocomplete_path' => 'taxonomy/autocomplete/'. $edit['vid'],
-    '#description' => t('<span class="no-js">A comma-separated list of terms describing this content. </span>Example: funny, bungee jumping, "Company, Inc.".'),
-    '#attributes' => array('class' => 'form-tags'),
-    '#access' => $access,
-  );
-  if ($edit['inline']) {
-    $form['tags']['#size'] = 20;
+  if ($vocabulary->tags) {
+    $form['taxonomy'] = array(
+      '#tree' => TRUE,
+    );
+    $form['taxonomy']['tags'] = array(
+      '#type' => 'textfield',
+      '#title' => t('My tags'),
+      '#maxlength' => 100,
+      '#default_value' => $edit['tags'],
+      '#required' => FALSE,
+      '#autocomplete_path' => 'taxonomy/autocomplete/'. $edit['vid'],
+      '#description' => t('<span class="no-js">A comma-separated list of terms describing this content. </span>Example: funny, bungee jumping, "Company, Inc.".'),
+      '#attributes' => array('class' => 'form-tags'),
+      '#access' => $access,
+    );
+    if ($edit['inline']) {
+      $form['taxonomy']['tags']['#size'] = 20;
+    }
+  }
+  else {
+    $form['taxonomy'] = taxonomy_form($edit['vid'], $edit['taxonomy']);
+    $form['taxonomy']['#attributes'] = array('class' => 'form-taxonomy');
   }
-  
   if (!$access) {
     $destination = drupal_get_destination();
     $form['login'] = array(
@@ -336,14 +366,13 @@
       '#value' => '<div>'. t('<a href="@login">Login</a> or <a href="@register">register</a> to tag items', array('@login' => url('user/login', $destination), '@register' => url('user/register', $destination))) .'</div>',
     );
   }
-
   $form['submit'] = array(
     '#type' => 'submit',
     '#value' => t('Save'),
   );
 
   $form['nid'] = array(
-    '#type' => 'value',
+    '#type' => 'hidden',
     '#value' => $edit['nid'],
   );
 
@@ -359,17 +388,25 @@
  * Validate the quick tag form.
  */
 function community_tags_form_validate($form_id, $form_values) {
-  if ($form_values['tags'] == '') {
-    form_set_error('tags', t('You must enter at least one tag.'));
+  if (isset($form_values['taxonomy']['tags']) && $form_values['taxonomy']['tags'] == '') {
+    form_set_error('taxonomy', t('You must enter at least one tag.'));
+  }
+  if (isset($form_values['taxonomy']) && empty($form_values['taxonomy'])) {
+    form_set_error('taxonomy', t('You must select at least one tag.'));
   }
 }
 
 /**
- * Submit callback for quick tag form.
+ * Submit callback for tag form.
  */
 function community_tags_form_submit($form_id, $form_values) {
+  // If a single select value has been submitted we need to translate it
+  // into an array.
+  if (is_numeric($form_values['taxonomy'])) {
+    $form_values['taxonomy'] = array($form_values['taxonomy']);
+  }
   global $user;
-  community_tags_taxonomy_node_save($form_values['nid'], array('tags' => array($form_values['vid'] => $form_values['tags'])), FALSE, $user->uid);
+  community_tags_taxonomy_node_save($form_values['nid'], $form_values['taxonomy'], FALSE, $user->uid);
 
   return array('node/'. $form_values['nid']);
 }
@@ -379,7 +416,7 @@
  * @ingroup themeable
  */
 function theme_community_tags_form($form) {
-  $output .= theme('form_element', array('#title' => t('All tags')), drupal_render($form['cloud']));
+  $output = isset($form['cloud']) ? theme('form_element', array('#title' => t('All tags')), drupal_render($form['cloud'])) : '';
 
   $output .= drupal_render($form);
 
@@ -410,29 +447,46 @@
     return;
   }
 
-  $tags = is_array($_POST['tags']) ? $_POST['tags'] : array();
-
-  // Merge in new tag and save
-  $tags = array_unique(array_merge($tags, taxonomy_explode_tags($_POST['add'])));
-  $vid = array_shift(community_tags_for_node($node));
-  community_tags_taxonomy_node_save($node->nid, array('tags' => array($vid => $tags)), FALSE, $user->uid);
-
-  // Fetch updated list
-  $tags = community_tags_flatten(community_tags_get_user_node_tags($user->uid, $node->nid));
+  // Process free tagging vocabulary.
+  if (isset($_POST['tags'])) {
+    $tags = is_array($_POST['tags']) ? $_POST['tags'] : array();
+
+    $vid = array_shift(community_tags_for_node($node));
+    community_tags_taxonomy_node_save($node->nid, array('tags' => array($vid => $tags)), FALSE, $user->uid);
+    // Fetch updated list
+    $tags = community_tags_flatten(community_tags_get_user_node_tags($user->uid, $node->nid));
+  }
+  // Process regular vocabulary.
+  else {
+    // If an empty taxonomy array was posted, $_POST['taxonomy'] will be null.
+    community_tags_taxonomy_node_save($node->nid, $_POST['taxonomy'] ? $_POST['taxonomy'] : array(), FALSE, $user->uid);
+    // Fetch updated list.
+    $tags = array_keys(community_tags_get_user_node_tags($user->uid, $node->nid));
+  }
 
   // Output JSON
-  print drupal_to_js(array('status' => TRUE, 'tags' => $tags, 'sequence' => $_POST['sequence']));
+  print drupal_to_js(array('status' => TRUE, 'nid' => $nid, 'taxonomy' => $tags, 'sequence' => $_POST['sequence']));
   drupal_set_header('Content-Type: text/javascript; charset=utf-8');
 }
 
 /**
  * Save community_tags term associations and counts for a given node.
  */
-function community_tags_taxonomy_node_save($nid, $terms, $is_owner, $uid) {
+function community_tags_taxonomy_node_save($nid, $taxonomy, $is_owner, $uid) {
   $community_tagged = variable_get('community_tags_vocabularies', array());
   if (count($community_tagged) == 0) {
     return;
   }
+  // Process an array of tids for a regular taxonomy, if present.
+  // Here we don't need to add new terms or delete existing ones.
+  if (is_array($taxonomy) && !isset($taxonomy['tags'])) {
+    // Delete terms by this user.
+    db_query('DELETE FROM {community_tags} WHERE nid = %d AND uid = %d', $nid, $uid);
+    foreach ($taxonomy as $tid) {
+      db_query('INSERT INTO {community_tags} (tid, nid, uid, date) VALUES (%d, %d, %d, %d)', $tid, $nid, $uid, time());
+    }
+    return;
+  }
 
   // If we're adding tags from the node add/edit page, then we want to delete
   // any tags that aren't entered to allow admins to remove node tags.
@@ -450,9 +504,9 @@
   $tids = array();
   // Free tagging vocabularies do not send their tids in the form,
   // so we'll detect them here and process them independently.
-  if (isset($terms['tags'])) {
-    $typed_input = $terms['tags'];
-    unset($terms['tags']);
+  if (isset($taxonomy['tags'])) {
+    $typed_input = $taxonomy['tags'];
+    unset($taxonomy['tags']);
 
     foreach ($typed_input as $vid => $vid_value) {
       $typed_terms = is_array($vid_value) ? $vid_value : taxonomy_explode_tags($vid_value);
@@ -547,7 +601,6 @@
       db_query('DELETE FROM {term_node} WHERE nid = %d AND tid = %d', $tag->nid, $tag->tid);
     }
   }
-
 }
 
 /**
