Commit e6a88be5 authored by Gábor Hojtsy's avatar Gábor Hojtsy

#193333 by quicksketch et al: taxonomy drag and drop support

parent 9a96837b
......@@ -2012,9 +2012,10 @@ function drupal_get_js($scope = 'header', $javascript = NULL) {
* group. Depth updates the target element with the current indentation.
* @param $relationship
* String describing where the $action variable should be performed. Either
* 'parent', 'sibling', or 'self'. Parent will only look for fields up the
* tree. Sibling will look for fields in the same group in rows above and
* below it. Self affects the dragged row itself.
* 'parent', 'sibling', 'group', or 'self'. Parent will only look for fields
* up the tree. Sibling will look for fields in the same group in rows above
* and below it. Self affects the dragged row itself. Group affects the
* dragged row, plus any children below it (the entire dragged group).
* @param $group
* A class name applied on all related form elements for this action.
* @param $subgroup
......
......@@ -413,7 +413,20 @@ Drupal.tableDrag.prototype.dropRow = function(event) {
var droppedRow = self.rowObject.element;
// The row is already in the right place so we just release it.
if (self.rowObject.changed == true) {
// Update the fields in the dropped row.
self.updateFields(droppedRow);
// If a setting exists for affecting the entire group, update all the
// fields in the entire dragged group.
for (var group in self.tableSettings) {
var rowSettings = self.rowSettings(group, droppedRow);
if (rowSettings.relationship == 'group') {
for (n in self.rowObject.children) {
self.updateField(self.rowObject.children[n], group);
}
}
}
self.rowObject.markChanged();
if (self.changed == false) {
$(Drupal.theme('tableDragChangedWarning')).insertAfter(self.table).hide().fadeIn('slow');
......@@ -562,114 +575,127 @@ Drupal.tableDrag.prototype.updateFields = function(changedRow) {
for (var group in this.tableSettings) {
// Each group may have a different setting for relationship, so we find
// the source rows for each seperately.
var rowSettings = this.rowSettings(group, changedRow);
this.updateField(changedRow, group);
}
}
// Set the row as it's own target.
if (rowSettings.relationship == 'self') {
var sourceRow = changedRow;
}
// Siblings are easy, check previous and next rows.
else if (rowSettings.relationship == 'sibling') {
var previousRow = $(changedRow).prev('tr').get(0);
var nextRow = $(changedRow).next('tr').get(0);
var sourceRow = changedRow;
if ($(previousRow).is('.draggable') && $('.' + group, previousRow).length) {
if (this.indentEnabled) {
if ($('.indentations', previousRow).size() == $('.indentations', changedRow)) {
sourceRow = previousRow;
}
}
else {
/**
* After the row is dropped, update a single table field according to specific
* settings.
*
* @param changedRow
* DOM object for the row that was just dropped.
* @param group
* The settings group on which field updates will occur.
*/
Drupal.tableDrag.prototype.updateField = function(changedRow, group) {
var rowSettings = this.rowSettings(group, changedRow);
// Set the row as it's own target.
if (rowSettings.relationship == 'self' || rowSettings.relationship == 'group') {
var sourceRow = changedRow;
}
// Siblings are easy, check previous and next rows.
else if (rowSettings.relationship == 'sibling') {
var previousRow = $(changedRow).prev('tr').get(0);
var nextRow = $(changedRow).next('tr').get(0);
var sourceRow = changedRow;
if ($(previousRow).is('.draggable') && $('.' + group, previousRow).length) {
if (this.indentEnabled) {
if ($('.indentations', previousRow).size() == $('.indentations', changedRow)) {
sourceRow = previousRow;
}
}
else if ($(nextRow).is('.draggable') && $('.' + group, nextRow).length) {
if (this.indentEnabled) {
if ($('.indentations', nextRow).size() == $('.indentations', changedRow)) {
sourceRow = nextRow;
}
}
else {
sourceRow = nextRow;
}
else {
sourceRow = previousRow;
}
}
// Parents, look up the tree until we find a field not in this group.
// Go up as many parents as indentations in the changed row.
else if (rowSettings.relationship == 'parent') {
var previousRow = $(changedRow).prev('tr');
while (previousRow.length && $('.indentation', previousRow).length >= this.rowObject.indents) {
previousRow = previousRow.prev('tr');
}
// If we found a row.
if (previousRow.length) {
sourceRow = previousRow[0];
else if ($(nextRow).is('.draggable') && $('.' + group, nextRow).length) {
if (this.indentEnabled) {
if ($('.indentations', nextRow).size() == $('.indentations', changedRow)) {
sourceRow = nextRow;
}
}
// Otherwise we went all the way to the left of the table without finding
// a parent, meaning this item has been placed at the root level.
else {
// Use the first row in the table as source, because it's garanteed to
// be at the root level. Find the first item, then compare this row
// against it as a sibling.
sourceRow = $('tr.draggable:first').get(0);
if (sourceRow == this.rowObject.element) {
sourceRow = $(this.rowObject.group[this.rowObject.group.length - 1]).next('tr.draggable').get(0);
}
var useSibling = true;
sourceRow = nextRow;
}
}
}
// Parents, look up the tree until we find a field not in this group.
// Go up as many parents as indentations in the changed row.
else if (rowSettings.relationship == 'parent') {
var previousRow = $(changedRow).prev('tr');
while (previousRow.length && $('.indentation', previousRow).length >= this.rowObject.indents) {
previousRow = previousRow.prev('tr');
}
// If we found a row.
if (previousRow.length) {
sourceRow = previousRow[0];
}
// Otherwise we went all the way to the left of the table without finding
// a parent, meaning this item has been placed at the root level.
else {
// Use the first row in the table as source, because it's garanteed to
// be at the root level. Find the first item, then compare this row
// against it as a sibling.
sourceRow = $('tr.draggable:first').get(0);
if (sourceRow == this.rowObject.element) {
sourceRow = $(this.rowObject.group[this.rowObject.group.length - 1]).next('tr.draggable').get(0);
}
var useSibling = true;
}
}
// Because we may have moved the row from one category to another,
// take a look at our sibling and borrow its sources and targets.
this.copyDragClasses(sourceRow, changedRow, group);
rowSettings = this.rowSettings(group, changedRow);
// Because we may have moved the row from one category to another,
// take a look at our sibling and borrow its sources and targets.
this.copyDragClasses(sourceRow, changedRow, group);
rowSettings = this.rowSettings(group, changedRow);
// In the case that we're looking for a parent, but the row is at the top
// of the tree, copy our sibling's values.
if (useSibling) {
rowSettings.relationship = 'sibling';
rowSettings.source = rowSettings.target;
}
// In the case that we're looking for a parent, but the row is at the top
// of the tree, copy our sibling's values.
if (useSibling) {
rowSettings.relationship = 'sibling';
rowSettings.source = rowSettings.target;
}
var targetClass = '.' + rowSettings.target;
var targetElement = $(targetClass, changedRow).get(0);
// Check if a target element exists in this row.
if (targetElement) {
var sourceClass = '.' + rowSettings.source;
var sourceElement = $(sourceClass, sourceRow).get(0);
switch (rowSettings.action) {
case 'depth':
// Get the depth of the target row.
targetElement.value = $('.indentation', $(sourceElement).parents('tr:first')).size();
break;
case 'match':
// Update the value.
targetElement.value = sourceElement.value;
break;
case 'order':
var siblings = this.rowObject.findSiblings(rowSettings);
if ($(targetElement).is('select')) {
// Get a list of acceptable values.
var values = new Array();
$('option', targetElement).each(function() {
values.push(this.value);
});
// Populate the values in the siblings.
$(targetClass, siblings).each(function() {
this.value = values.shift();
});
}
else {
// Assume a numeric input field.
var weight = parseInt($(targetClass, siblings[0]).val()) || 0;
$(targetClass, siblings).each(function() {
this.value = weight;
weight++;
});
}
break;
}
var targetClass = '.' + rowSettings.target;
var targetElement = $(targetClass, changedRow).get(0);
// Check if a target element exists in this row.
if (targetElement) {
var sourceClass = '.' + rowSettings.source;
var sourceElement = $(sourceClass, sourceRow).get(0);
switch (rowSettings.action) {
case 'depth':
// Get the depth of the target row.
targetElement.value = $('.indentation', $(sourceElement).parents('tr:first')).size();
break;
case 'match':
// Update the value.
targetElement.value = sourceElement.value;
break;
case 'order':
var siblings = this.rowObject.findSiblings(rowSettings);
if ($(targetElement).is('select')) {
// Get a list of acceptable values.
var values = new Array();
$('option', targetElement).each(function() {
values.push(this.value);
});
// Populate the values in the siblings.
$(targetClass, siblings).each(function() {
this.value = values.shift();
});
}
else {
// Assume a numeric input field.
var weight = parseInt($(targetClass, siblings[0]).val()) || 0;
$(targetClass, siblings).each(function() {
this.value = weight;
weight++;
});
}
break;
}
}
};
......
......@@ -24,38 +24,6 @@ function comment_update_1() {
return array();
}
function comment_update_6001() {
$ret[] = update_sql("ALTER TABLE {comments} DROP score");
$ret[] = update_sql("ALTER TABLE {comments} DROP users");
return $ret;
}
/**
* Changed comment settings from global to per-node -- copy global
* settings to all node types.
*/
function comment_update_6002() {
$settings = array(
'comment_default_mode' => COMMENT_MODE_THREADED_EXPANDED,
'comment_default_order' => COMMENT_ORDER_NEWEST_FIRST,
'comment_default_per_page' => 50,
'comment_controls' => COMMENT_CONTROLS_HIDDEN,
'comment_anonymous' => COMMENT_ANONYMOUS_MAYNOT_CONTACT,
'comment_subject_field' => 1,
'comment_preview' => COMMENT_PREVIEW_REQUIRED,
'comment_form_location' => COMMENT_FORM_SEPARATE_PAGE,
);
$types = node_get_types();
foreach ($settings as $setting => $default) {
$value = variable_get($setting, $default);
foreach ($types as $type => $object) {
variable_set($setting .'_'. $type, $value);
}
variable_del($setting);
}
return array();
}
/**
* Implementation of hook_schema().
*/
......
......@@ -214,26 +214,33 @@ function forum_admin_settings() {
/**
* Returns an overview list of existing forums and containers
*/
function forum_overview() {
$header = array(t('Name'), t('Operations'));
function forum_overview(&$form_state) {
include_once(drupal_get_path('module', 'taxonomy') .'/taxonomy.admin.inc');
$vid = variable_get('forum_nav_vocabulary', '');
$tree = taxonomy_get_tree($vid);
if ($tree) {
foreach ($tree as $term) {
if (in_array($term->tid, variable_get('forum_containers', array()))) {
$rows[] = array(str_repeat(' -- ', $term->depth) .' '. l($term->name, 'forum/'. $term->tid), l(t('edit container'), 'admin/content/forum/edit/container/'. $term->tid));
$vocabulary = taxonomy_vocabulary_load($vid);
$form = taxonomy_overview_terms($form_state, $vocabulary);
drupal_set_title('Forums');
foreach (element_children($form) as $key) {
if (isset($form[$key]['#term'])) {
$term = $form[$key]['#term'];
$form[$key]['view']['#value'] = l($term['name'], 'forum/'. $term['tid']);
if (in_array($form[$key]['#term']['tid'], variable_get('forum_containers', array()))) {
$form[$key]['edit']['#value'] = l(t('edit container'), 'admin/content/forum/edit/container/'. $term['tid']);
}
else {
$rows[] = array(str_repeat(' -- ', $term->depth) .' '. l($term->name, 'forum/'. $term->tid), l(t('edit forum'), 'admin/content/forum/edit/forum/'. $term->tid));
}
$form[$key]['edit']['#value'] = l(t('edit forum'), 'admin/content/forum/edit/forum/'. $term['tid']);
}
}
}
else {
$rows[] = array(array('data' => '<em>'. t('There are no existing containers or forums. You may add some on the <a href="@container">add container</a> or <a href="@forum">add forum</a> pages.', array('@container' => url('admin/content/forum/add/container'), '@forum' => url('admin/content/forum/add/forum'))) .'</em>', 'colspan' => 2));
}
return theme('table', $header, $rows);
// The form needs to have submit and validate handlers set explicitly.
$form['#theme'] = 'taxonomy_overview_terms';
$form['#submit'] = array('taxonomy_overview_terms_submit'); // Use the existing taxonomy overview submit handler.
$form['#validate'] = array('taxonomy_overview_terms_validate');
$form['#empty_text'] = '<em>'. t('There are no existing containers or forums. You may add some on the <a href="@container">add container</a> or <a href="@forum">add forum</a> pages.', array('@container' => url('admin/content/forum/add/container'), '@forum' => url('admin/content/forum/add/forum'))) .'</em>';
return $form;
}
/**
......
......@@ -92,7 +92,8 @@ function forum_menu() {
$items['admin/content/forum'] = array(
'title' => 'Forums',
'description' => 'Control forums and their hierarchy and change forum settings.',
'page callback' => 'forum_overview',
'page callback' => 'drupal_get_form',
'page arguments' => array('forum_overview'),
'access arguments' => array('administer forums'),
'file' => 'forum.admin.inc',
);
......
......@@ -15,126 +15,6 @@ function locale_install() {
db_query("INSERT INTO {languages} (language, name, native, direction, enabled, weight, javascript) VALUES ('en', 'English', 'English', '0', '1', '0', '')");
}
/**
* @defgroup updates-5.x-to-6.x Locale updates from 5.x to 6.x
* @{
*/
/**
* {locales_meta} table became {languages}.
*/
function locale_update_6001() {
$ret = array();
switch ($GLOBALS['db_type']) {
case 'mysql':
case 'mysqli':
$ret[] = update_sql("CREATE TABLE {languages} (
language varchar(12) NOT NULL default '',
name varchar(64) NOT NULL default '',
native varchar(64) NOT NULL default '',
direction int NOT NULL default '0',
enabled int NOT NULL default '0',
plurals int NOT NULL default '0',
formula varchar(128) NOT NULL default '',
domain varchar(128) NOT NULL default '',
prefix varchar(128) NOT NULL default '',
weight int NOT NULL default '0',
PRIMARY KEY (language)
) /*!40100 DEFAULT CHARACTER SET UTF8 */ ");
break;
case 'pgsql':
$ret[] = update_sql("CREATE TABLE {languages} (
language varchar(12) NOT NULL default '',
name varchar(64) NOT NULL default '',
native varchar(64) NOT NULL default '',
direction int NOT NULL default '0',
enabled int NOT NULL default '0',
plurals int NOT NULL default '0',
formula varchar(128) NOT NULL default '',
domain varchar(128) NOT NULL default '',
prefix varchar(128) NOT NULL default '',
weight int NOT NULL default '0',
PRIMARY KEY (language)
)");
break;
}
// Save the languages
$ret[] = update_sql("INSERT INTO {languages} (language, name, native, direction, enabled, plurals, formula, domain, prefix, weight) SELECT locale, name, name, 0, enabled, plurals, formula, '', locale, 0 FROM {locales_meta}");
// Save the language count in the variable table
$count = db_result(db_query('SELECT COUNT(*) FROM {languages} WHERE enabled = 1'));
variable_set('language_count', $count);
// Save the default language in the variable table
$default = db_fetch_object(db_query('SELECT * FROM {locales_meta} WHERE isdefault = 1'));
variable_set('language_default', (object) array('language' => $default->locale, 'name' => $default->name, 'native' => '', 'direction' => 0, 'enabled' => 1, 'plurals' => $default->plurals, 'formula' => $default->formula, 'domain' => '', 'prefix' => $default->locale, 'weight' => 0));
$ret[] = update_sql("DROP TABLE {locales_meta}");
return $ret;
}
/**
* Change locale column to language. The language column is added by
* update_fix_d6_requirements() in update.php to avoid a large number
* of error messages from update.php. All we need to do here is copy
* locale to language and then drop locale.
*/
function locale_update_6002() {
$ret = array();
$ret[] = update_sql('UPDATE {locales_target} SET language = locale');
db_drop_field($ret, 'locales_target', 'locale');
return $ret;
}
/**
* Adds a column to store the filename of the JavaScript translation file.
*/
function locale_update_6003() {
$ret = array();
db_add_field($ret, 'languages', 'javascript', array('type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => ''));
return $ret;
}
/**
* Remove empty translations, we don't need these anymore.
*/
function locale_update_6004() {
$ret = array();
$ret[] = update_sql("DELETE FROM {locales_target} WHERE translation = ''");
return $ret;
}
/**
* Prune strings with no translations (will be automatically re-registered if still in use)
*/
function locale_update_6005() {
$ret = array();
$ret[] = update_sql("DELETE FROM {locales_source} WHERE lid NOT IN (SELECT lid FROM {locales_target})");
return $ret;
}
/**
* Fix remaining inconsistent indexes.
*/
function locale_update_6006() {
$ret = array();
db_add_index($ret, 'locales_target', 'language', array('language'));
switch ($GLOBALS['db_type']) {
case 'pgsql':
db_drop_index($ret, 'locales_source', 'source');
db_add_index($ret, 'locales_source', 'source', array(array('source', 30)));
break;
}
return $ret;
}
/**
* @} End of "defgroup updates-5.x-to-6.x"
*/
/**
* Implementation of hook_uninstall().
*/
......
This diff is collapsed.
This diff is collapsed.
tr.taxonomy-term-preview {
background-color: #EEE;
}
tr.taxonomy-term-divider-top {
border-bottom: none;
}
tr.taxonomy-term-divider-bottom {
border-top: 1px dotted #CCC;
}
\ No newline at end of file
// $Id $
/**
* Move a block in the blocks table from one region to another via select list.
*
* This behavior is dependent on the tableDrag behavior, since it uses the
* objects initialized in that behavior to update the row.
*/
Drupal.behaviors.termDrag = function(context) {
var table = $('#taxonomy', context);
var tableDrag = Drupal.tableDrag.taxonomy; // Get the blocks tableDrag object.
var rows = $('tr', table).size();
// When a row is swapped, keep previous and next page classes set.
tableDrag.row.prototype.onSwap = function(swappedRow) {
$('tr.taxonomy-term-preview', table).removeClass('taxonomy-term-preview');
$('tr.taxonomy-term-divider-top', table).removeClass('taxonomy-term-divider-top');
$('tr.taxonomy-term-divider-bottom', table).removeClass('taxonomy-term-divider-bottom');
if (Drupal.settings.taxonomy.backPeddle) {
for (var n = 0; n < Drupal.settings.taxonomy.backPeddle; n++) {
$(table[0].tBodies[0].rows[n]).addClass('taxonomy-term-preview');
}
$(table[0].tBodies[0].rows[Drupal.settings.taxonomy.backPeddle - 1]).addClass('taxonomy-term-divider-top');
$(table[0].tBodies[0].rows[Drupal.settings.taxonomy.backPeddle]).addClass('taxonomy-term-divider-bottom');
}
if (Drupal.settings.taxonomy.forwardPeddle) {
for (var n = rows - Drupal.settings.taxonomy.forwardPeddle - 1; n < rows - 1; n++) {
$(table[0].tBodies[0].rows[n]).addClass('taxonomy-term-preview');
}
$(table[0].tBodies[0].rows[rows - Drupal.settings.taxonomy.forwardPeddle - 2]).addClass('taxonomy-term-divider-top');
$(table[0].tBodies[0].rows[rows - Drupal.settings.taxonomy.forwardPeddle - 1]).addClass('taxonomy-term-divider-bottom');
}
};
};
......@@ -24,6 +24,12 @@ function taxonomy_theme() {
'taxonomy_term_page' => array(
'arguments' => array('tids' => array(), 'result' => NULL),
),
'taxonomy_overview_vocabularies' => array(
'arguments' => array('form' => array()),
),
'taxonomy_overview_terms' => array(
'arguments' => array('form' => array()),
),
);
}
......@@ -106,7 +112,8 @@ function taxonomy_menu() {
$items['admin/content/taxonomy'] = array(
'title' => 'Taxonomy',
'description' => 'Manage tagging, categorization, and classification of your content.',
'page callback' => 'taxonomy_overview_vocabularies',
'page callback' => 'drupal_get_form',
'page arguments' => array('taxonomy_overview_vocabularies'),
'access arguments' => array('administer taxonomy'),
'file' => 'taxonomy.admin.inc',
);
......@@ -158,8 +165,8 @@ function taxonomy_menu() {
);
$items['admin/content/taxonomy/%taxonomy_vocabulary'] = array(
'title' => 'List terms',
'page callback' => 'taxonomy_overview_terms',
'page arguments' => array(3),
'page callback' => 'drupal_get_form',
'page arguments' => array('taxonomy_overview_terms', 3),
'access arguments' => array('administer taxonomy'),
'type' => MENU_CALLBACK,
'file' => 'taxonomy.admin.inc',
......@@ -241,6 +248,47 @@ function taxonomy_del_vocabulary($vid) {
return SAVED_DELETED;
}
/**
* Dynamicly check and update the hierarachy flag of a vocabulary.
*
* Checks the current parents of all terms in a vocabulary and updates the
* vocabularies hierarchy setting to the lowest possible level. A hierarchy with
* no parents in any of its terms will be given a hierarchy of 0. If terms
* contain at most a single parent, the vocabulary will be given a hierarchy of
* 1. If any term contain multiple parents, the vocabulary will be given a
* hieararchy of 2.
*
* @param $vocabulary
* An array of the vocabulary structure.
* @param $changed_term
* An array of the term structure that was updated.
*/
function taxonomy_check_vocabulary_hierarchy($vocabulary, $changed_term) {
$tree = taxonomy_get_tree($vocabulary['vid']);
$hierarchy = 0;
foreach ($tree as $term) {
// Update the changed term with the new parent value before comparision.
if ($term->tid == $changed_term['tid']) {
$term = (object)$changed_term;
$term->parents = $term->parent;
}
// Check this term's parent count.
if (count($term->parents) > 1) {
$hierarchy = 2;
break;
}
elseif (count($term->parents) == 1 && 0 !== array_shift($term->parents)) {
$hierarchy = 1;
}
}
if ($hierarchy != $vocabulary['hierarchy']) {
$vocabulary['hierarchy'] = $hierarchy;
taxonomy_save_vocabulary($vocabulary);
}
return $hierarchy;
}
/**
* Helper function for taxonomy_form_term_submit().
*
......@@ -412,7 +460,7 @@ function taxonomy_get_vocabularies($type = NULL) {
// If no node types are associated with a vocabulary, the LEFT JOIN will
// return a NULL value for type.
if (isset($voc->type)) {
$node_types[$voc->vid][] = $voc->type;
$node_types[$voc->vid][$voc->type] = $voc->type;
unset($voc->type);
$voc->nodes = $node_types[$voc->vid];
}
......@@ -928,7 +976,7 @@ function taxonomy_vocabulary_load($vid) {
$node_types = array();
while ($voc = db_fetch_object($result)) {
if (!empty($voc->type)) {
$node_types[] = $voc->type;
$node_types[$voc->type] = $voc->type;
}
unset($voc->type);
$voc->nodes = $node_types;
......@@ -1182,6 +1230,19 @@ function taxonomy_help($path, $arg) {
return $output;
case 'admin/content/taxonomy':
return '<p>'. t("The taxonomy module allows you to categorize your content using both tags and administrator defined terms. It is a flexible tool for classifying content with many advanced features. To begin, create a 'Vocabulary' to hold one set of terms or tags. You can create one free-tagging vocabulary for everything, or seperate controlled vocabularies to define the various properties of your content, for example 'Countries' or 'Colours'.") .'</p>';
case 'admin/content/taxonomy/%':
$vocabulary = taxonomy_vocabulary_load($arg[3]);
if ($vocabulary->tags) {
return '<p>'. t('%capital_name is a free-tagging vocabulary. To change the name or description of a term, click the <em>edit</em> link next to the term.', array('%capital_name' => drupal_ucfirst($vocabulary->name))) .'</p>';
}
switch ($vocabulary->hierarchy) {
case 0:
return '<p>'. t('%capital_name is a flat vocabulary. You may organize the terms in the %name vocabulary by using the handles on the left side of the table. To change the name or description of a term, click the <em>edit</em> link next to the term.', array('%capital_name' => drupal_ucfirst($vocabulary->name), '%name' => $vocabulary->name)) .'</p>';
case 1:
return '<p>'. t('%capital_name is a single hierarchy vocabulary. You may organize the terms in the %name vocabulary by using the handles on the left side of the table. To change the name or description of a term, click the <em>edit</em> link next to the term.', array('%capital_name' => drupal_ucfirst($vocabulary->name), '%name' => $vocabulary->name)) .'</p>';
case 2:
return '<p>'. t('%capital_name is a multiple hierarchy vocabulary. To change the name or description of a term, click the <em>edit</em> link next to the term. Drag and drop of multiple hierarchies is not supported, but you can re-enable drag and drop support by editing each term to include only a single parent.', array('%capital_name' => drupal_ucfirst($vocabulary->name))) .'</p>';
}
case 'admin/content/taxonomy/add/vocabulary':
return '<p>'. t('Define how your vocabulary will be presented to administrators and users, and which content types to categorize with it. Tags allows users to create terms when submitting posts by typing a comma separated list. Otherwise terms are chosen from a select list and can only be created by users with the "administer taxonomy" permission.') .'</p>';
}
......
......@@ -59,6 +59,10 @@ tr.menu-disabled {
height: 1em;
}
tr.taxonomy-term-preview {
filter: alpha(opacity=50);
}
#attach-hide label, #uploadprogress div.message {
/* Fading elements in IE causes the text to bleed unless they have a background. */
background-color: #ffffff;
......
......@@ -976,6 +976,18 @@ tr.selected td a:link, tr.selected td a:visited, tr.selected td a:active {