Index: modules/taxonomy/taxonomy.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.admin.inc,v
retrieving revision 1.104
diff -u -p -r1.104 taxonomy.admin.inc
--- modules/taxonomy/taxonomy.admin.inc	6 May 2010 05:59:31 -0000	1.104
+++ modules/taxonomy/taxonomy.admin.inc	11 May 2010 16:17:56 -0000
@@ -109,6 +109,7 @@ function taxonomy_form_vocabulary($form,
     'description' => '',
     'hierarchy' => 0,
     'weight' => 0,
+    'sort_order' => 'alpha',
   );
   $form['#vocabulary'] = (object) $edit;
   // Check whether we need a deletion confirmation form.
@@ -158,7 +159,15 @@ function taxonomy_form_vocabulary($form,
     '#type' => 'value',
     '#value' => '0',
   );
-
+  $sort_orders = array('alpha' => t('Alphabetical'), 'created' => t('Created'));
+  // Set the default sort order to either alphabetical or created.
+  $form['sort_order'] = array(
+    '#type' => 'select',
+    '#title' => t('Default term sort order'),
+    '#options' => $sort_orders,
+    '#default_value' => $edit['sort_order'],
+    '#description' => t('Changing this does not sort existing taxonomy terms in this vocabulary.')
+  );
   $form['actions'] = array('#type' => 'actions');
   $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save'));
   if (isset($edit['vid'])) {
@@ -621,13 +630,18 @@ function taxonomy_form_term($form, &$for
     $vocabulary = taxonomy_vocabulary_load($edit->vid);
     $edit = (array) $edit;
   }
+
+  // Get the correct weight for a newly created term.
+  if (!isset($edit['weight'])) {
+    $edit['weight'] = taxonomy_term_get_new_weight($vocabulary);
+  }
+
   $edit += array(
     'name' => '',
     'description' => '',
     'format' => filter_default_format(),
     'vocabulary_machine_name' => $vocabulary->machine_name,
     'tid' => NULL,
-    'weight' => 0,
   );
 
   // Take into account multi-step rebuilding.
Index: modules/taxonomy/taxonomy.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.install,v
retrieving revision 1.42
diff -u -p -r1.42 taxonomy.install
--- modules/taxonomy/taxonomy.install	6 May 2010 06:08:28 -0000	1.42
+++ modules/taxonomy/taxonomy.install	11 May 2010 16:17:56 -0000
@@ -154,6 +154,13 @@ function taxonomy_schema() {
         'default' => 0,
         'description' => 'The weight of this vocabulary in relation to other vocabularies.',
       ),
+      'sort_order' => array(
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'The vocabulary\'s default sort order. (alpha = alphabetical, created = created)',
+      ),
     ),
     'primary key' => array('vid'),
     'indexes' => array(
@@ -504,3 +511,25 @@ function taxonomy_update_7008() {
     ),
   ));
 }
+
+/**
+ * Add {vocabulary}.sort_order column and update it to the default value.
+ */
+function taxonomy_update_7009() {
+  $field = array(
+    'type' => 'varchar',
+    'length' => 32,
+    'not null' => TRUE,
+    'default' => '',
+    'description' => 'The vocabulary\'s default sort order. (alpha = alphabetical, created = created)',
+  );
+
+  db_add_field('taxonomy_vocabulary', 'sort_order', $field);
+
+  foreach (taxonomy_get_vocabularies() as $vid => $vocabulary) {
+    db_update('taxonomy_vocabulary')
+      ->fields(array('sort_order' => 'alpha'))
+      ->condition('vid', $vid)
+      ->execute();
+  }
+}
Index: modules/taxonomy/taxonomy.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.module,v
retrieving revision 1.589
diff -u -p -r1.589 taxonomy.module
--- modules/taxonomy/taxonomy.module	6 May 2010 05:59:31 -0000	1.589
+++ modules/taxonomy/taxonomy.module	11 May 2010 16:17:57 -0000
@@ -1293,6 +1293,7 @@ function taxonomy_field_formatter_prepar
         // Rekey the items array.
         $items[$id] = array_values($items[$id]);
       }
+      usort($items[$id], '_taxonomy_term_prepare_view_compare');
     }
   }
 }
@@ -1318,6 +1319,9 @@ function taxonomy_field_widget_form(&$fo
     $tags[$item['tid']] = isset($item['taxonomy_term']) ? $item['taxonomy_term'] : taxonomy_term_load($item['tid']);
   }
 
+  // Sort the tags according to weight then name.
+  usort($tags, '_taxonomy_term_compare');
+
   $element += array(
     '#type' => 'textfield',
     '#default_value' => taxonomy_implode_tags($tags),
@@ -1358,6 +1362,7 @@ function taxonomy_autocomplete_validate(
           'vid' => $vids[0],
           'name' => $typed_term,
           'vocabulary_machine_name' => $vocabulary->machine_name,
+          'weight' => taxonomy_term_get_new_weight($vocabulary),
         );
         taxonomy_term_save($term);
       }
@@ -1457,6 +1462,57 @@ function taxonomy_rdf_mapping() {
 }
 
 /**
+ * Returns the weight for a new taxonomy term depending upon the vocabulary's sort order.
+ *
+ * @param $vocabulary
+ *   A vocabulary object.
+ * @return
+ *   The weight for the new term.
+ */
+function taxonomy_term_get_new_weight($vocabulary) {
+  $weight = 0;
+  if ($vocabulary->sort_order == 'created') {
+    $max_weight = db_query("SELECT MAX(weight) FROM {taxonomy_term_data} WHERE vid = :vid", array(':vid' => $vocabulary->vid))->fetchField();
+    $weight = isset($max_weight) ? $max_weight + 1 : 0;
+  }
+  return $weight;
+}
+
+/**
+ * Helper function to sort an array of terms for taxonomy_field_formatter_prepare_view().
+ *
+ * @param $a
+ *   An array containing a taxonomy term object.
+ * @param $b
+ *   An array containing a taxonomy term object.
+ * @return
+ *   An integer less than, equal to, or greater than zero if the first argument is considered to be respectively
+ *   less than, equal to, or greater than the second.
+ */
+function _taxonomy_term_prepare_view_compare($a, $b) {
+  return _taxonomy_term_compare($a['taxonomy_term'], $b['taxonomy_term']);
+}
+
+/**
+ * Helper function to sort an array of taxonomy term objects.
+ *
+ * @param $a
+ *   A taxonomy term object.
+ * @param $b
+ *   A taxonomy term object.
+ * @return
+ *   An integer less than, equal to, or greater than zero if the first argument is considered to be respectively
+ *   less than, equal to, or greater than the second.
+ */
+function _taxonomy_term_compare($a, $b) {
+  if (isset($a->weight) && isset($b->weight) && !($a->weight == $b->weight)) {
+    return ($a->weight < $b->weight) ? -1 : 1;
+  }
+  // The terms were the same weight, so sort alphabetically.
+  return strcmp($a->name, $b->name);
+}
+
+/**
  * @defgroup taxonomy indexing Taxonomy functions maintaining {taxonomy_index}.
  *
  * Taxonomy uses default field storage to store canonical relationships
Index: modules/taxonomy/taxonomy.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/taxonomy/taxonomy.test,v
retrieving revision 1.78
diff -u -p -r1.78 taxonomy.test
--- modules/taxonomy/taxonomy.test	30 Apr 2010 12:52:10 -0000	1.78
+++ modules/taxonomy/taxonomy.test	11 May 2010 16:17:57 -0000
@@ -38,6 +38,22 @@ class TaxonomyWebTestCase extends Drupal
     taxonomy_term_save($term);
     return $term;
   }
+
+  /**
+   * Create a term through administration form.
+   */
+  function createTermThroughInterface($vocabulary, $name = NULL) {
+    $edit = array(
+      'name' => is_null($name) ? $this->randomName() : $name,
+      'description[value]' => $this->randomName(100),
+    );
+    // Explicitly set the parents field to 'root', to ensure that
+    // taxonomy_form_term_submit() handles the invalid term ID correctly.
+    $edit['parent[]'] = array(0);
+
+    // Create the term.
+    $this->drupalPost('admin/structure/taxonomy/' . $vocabulary->machine_name . '/add', $edit, t('Save'));
+  }
 }
 
 /**
@@ -1007,3 +1023,173 @@ class TaxonomyTokenReplaceTestCase exten
     }
   }
 }
+
+/**
+ * Tests for taxonomy term ordering functions.
+ */
+class TaxonomyTermOrderTestCase extends TaxonomyWebTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Taxonomy term ordering.',
+      'description' => 'Test multiple taxonomy terms ordering. How they are created and appear on a node.',
+      'group' => 'Taxonomy',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('taxonomy');
+    $this->admin_user = $this->drupalCreateUser(array('administer taxonomy', 'bypass node access'));
+    $this->drupalLogin($this->admin_user);
+    $this->vocabulary = $this->createVocabulary();
+
+    $field = array(
+      'field_name' => 'taxonomy_' . $this->vocabulary->machine_name,
+      'type' => 'taxonomy_term_reference',
+      'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+      'settings' => array(
+        'allowed_values' => array(
+          array(
+            'vid' => $this->vocabulary->vid,
+            'parent' => 0,
+          ),
+        ),
+      ),
+    );
+    field_create_field($field);
+
+    $this->instance = array(
+      'field_name' => 'taxonomy_' . $this->vocabulary->machine_name,
+      'bundle' => 'article',
+      'entity_type' => 'node',
+      'widget' => array(
+        'type' => 'options_select',
+      ),
+      'display' => array(
+        'full' => array(
+          'type' => 'taxonomy_term_reference_link',
+        ),
+      ),
+    );
+    field_create_instance($this->instance);
+  }
+
+  /**
+  * Save 3 terms using the user interface and test ordering.
+  */
+  function testTermAddOrderAlpha() {
+    // Create a list of names in the reverse of alphabetic order
+    $names = array(
+      'z' . $this->randomName(),
+      'y' . $this->randomName(),
+      'x' . $this->randomName(),
+    );
+
+    $this->createTermThroughInterface($this->vocabulary, $names[0]);
+    $this->createTermThroughInterface($this->vocabulary, $names[1]);
+    $this->createTermThroughInterface($this->vocabulary, $names[2]);
+
+    // Fetch the created terms in the default order, i.e. the order in
+    // which they were created.
+    drupal_static_reset('taxonomy_get_tree');
+    drupal_static_reset('taxonomy_get_treeparent');
+    drupal_static_reset('taxonomy_get_treeterms');
+    list($term1, $term2, $term3) = taxonomy_get_tree($this->vocabulary->vid);
+    $this->assertEqual(array($term1->name, $term2->name, $term3->name), array_reverse($names), t('Terms created through admin interface are listed in alphabetical order.'));
+  }
+
+  /**
+  * Save 3 terms using the user interface and test ordering.
+  */
+  function testTermAddOrderAlphaNode() {
+    // Create a list of names in the reverse of alphabetic order
+    $names = array(
+      'z' . $this->randomName(),
+      'y' . $this->randomName(),
+      'x' . $this->randomName(),
+    );
+    //Create a node a add the terms to it in a free tagging field in non alphabetic order
+    $instance = $this->instance;
+    $instance['widget'] = array('type' => 'taxonomy_autocomplete');
+    $instance['bundle'] = 'page';
+    field_create_instance($instance);
+    $edit = array();
+    $langcode = LANGUAGE_NONE;
+    $edit["title"] = $this->randomName();
+    $edit["body[$langcode][0][value]"] = $this->randomName();
+    // Insert the terms in a comma separated list. Vocabulary 1 is a
+    // free-tagging field created by the default profile.
+    $edit[$instance['field_name'] . "[$langcode]"] = implode(', ', $names);
+    $this->drupalPost('node/add/page', $edit, t('Save'));
+    $this->assertRaw(t('@type %title has been created.', array('@type' => t('Basic page'), '%title' => $edit["title"])), t('The node was created successfully'));
+    $this->assertPattern('|' . implode('.*', array_reverse($names)) . '|', t('The terms were saved and appear in alphabetical order on the node page'));
+
+    // Edit node to check term order.
+    $node = $this->drupalGetNodeByTitle($edit["title"]);
+    $this->drupalGet('node/' . $node->nid . '/edit');
+    $this->assertPattern('|' . implode('.*', array_reverse($names)) . '|', t('The terms appear in alphabetical order on the node edit page'));
+  }
+
+  /**
+  * Save 3 terms using the user interface and test ordering.
+  */
+  function testTermAddOrderCreated() {
+    //Change default order to created
+    $this->vocabulary->sort_order = 'created';
+    taxonomy_vocabulary_save($this->vocabulary);
+
+    // Create a list of names in the reverse of alphabetic order
+    $names = array(
+      'z' . $this->randomName(),
+      'y' . $this->randomName(),
+      'x' . $this->randomName(),
+    );
+
+    $this->createTermThroughInterface($this->vocabulary, $names[0]);
+    $this->createTermThroughInterface($this->vocabulary, $names[1]);
+    $this->createTermThroughInterface($this->vocabulary, $names[2]);
+
+    // Fetch the created terms in the default order, i.e. the order in
+    // which they were created.
+    drupal_static_reset('taxonomy_get_tree');
+    drupal_static_reset('taxonomy_get_treeparent');
+    drupal_static_reset('taxonomy_get_treeterms');
+    list($term1, $term2, $term3) = taxonomy_get_tree($this->vocabulary->vid);
+    $this->assertEqual(array($term1->name, $term2->name, $term3->name), $names, t('Terms created through admin interface are listed in the order in which they were created.'));
+  }
+  /**
+  * Save 3 terms using the user interface and test ordering.
+  */
+  function testTermAddOrderCreatedNode() {
+    //Change default order to created
+    $this->vocabulary->sort_order = 'created';
+    taxonomy_vocabulary_save($this->vocabulary);
+
+    // Create a list of names in the reverse of alphabetic order
+    $names = array(
+      'z' . $this->randomName(),
+      'y' . $this->randomName(),
+      'x' . $this->randomName(),
+    );
+    //Create a node a add the terms to it in a free tagging field in alphabetic order
+    $instance = $this->instance;
+    $instance['widget'] = array('type' => 'taxonomy_autocomplete');
+    $instance['bundle'] = 'page';
+    field_create_instance($instance);
+    $edit = array();
+    $langcode = LANGUAGE_NONE;
+    $edit["title"] = $this->randomName();
+    $edit["body[$langcode][0][value]"] = $this->randomName();
+    // Insert the terms in a comma separated list. Vocabulary 1 is a
+    // free-tagging field created by the default profile.
+    $edit[$instance['field_name'] . "[$langcode]"] = implode(', ', $names);
+    $this->drupalPost('node/add/page', $edit, t('Save'));
+    $this->assertRaw(t('@type %title has been created.', array('@type' => t('Basic page'), '%title' => $edit["title"])), t('The node was created successfully'));
+    $this->assertPattern('|' . implode('.*', $names) . '|', t('The terms were saved and appear in created order on the node page'));
+
+    // Edit node to check term order.
+    $node = $this->drupalGetNodeByTitle($edit["title"]);
+    $this->drupalGet('node/' . $node->nid . '/edit');
+    $this->assertPattern('|' . implode('.*', $names) . '|', t('The terms appear in created order on the node edit page'));
+  }
+}
