| 1 |
<?php
|
| 2 |
// $Id:$
|
| 3 |
|
| 4 |
/**
|
| 5 |
* @file
|
| 6 |
* You've just created a new content type and re-used some of
|
| 7 |
* the existing fields from an existing content type for your
|
| 8 |
* new content type. Shortly thereafter, you've discovered
|
| 9 |
* the custom reporting queries to built based on your old
|
| 10 |
* content type don't work anymore. That's because CCK moved
|
| 11 |
* them to a new table. It's more efficient, right? Maybe not
|
| 12 |
* if you have go to go mod all your existing queries in the
|
| 13 |
* sake of not duping a field in your database.
|
| 14 |
*
|
| 15 |
* This mod helps automate the reversal of the CCKism
|
| 16 |
* that splits fields into a separate table
|
| 17 |
* when you re-use an existing field.
|
| 18 |
*
|
| 19 |
* Here is your second chance to move the fields back to
|
| 20 |
* their original table and re-create the fields on the new
|
| 21 |
* content type anew, like you should have in the first place.
|
| 22 |
*
|
| 23 |
* Module development sponsored by Classic Graphics for Drupal 4.7.
|
| 24 |
*
|
| 25 |
* @todo some insert variables as part of the sql string
|
| 26 |
* instead of using the more proper replacement notation
|
| 27 |
* the DB abstraction layer can filter in part because at the
|
| 28 |
* time of writing this, David didn't know if the replacement
|
| 29 |
* of variables or table names for prefixes happened first
|
| 30 |
* and part of the whole point of this script is to dynamically
|
| 31 |
* work with different tables.
|
| 32 |
* @todo support tables and fields with long names that CCK normally
|
| 33 |
* truncates
|
| 34 |
* @author David Kent Norman
|
| 35 |
* @link http://deekayen.net/
|
| 36 |
* @link http://www.cgraphics.com/ Classic Graphics
|
| 37 |
*/
|
| 38 |
|
| 39 |
function cck_table_despliter_help($section) {
|
| 40 |
switch ($section) {
|
| 41 |
case 'admin/modules#description':
|
| 42 |
return t('<strong>CCK:</strong> <strong>DEVELOPMENT CODE ONLY!</STRONG> Merges node_data_field_* tables back into fields on the node_content_* tables. This module was written for two specific situations. If you use this module outside its intended use, you could make a huge mess of your data. You should review the PHP source before using. This module is not maintained, so <strong>you are on your own</strong>.');
|
| 43 |
case 'admin/node/types/table_despliter':
|
| 44 |
return t('This process attempts to preserve the data from the old and new table, but if you made more than one new content type with the same field, this script will probably make a big mess. The new content types and their permissions will remain, but the fields will change when attempting the preserve option.');
|
| 45 |
}
|
| 46 |
} // */
|
| 47 |
|
| 48 |
/**
|
| 49 |
* hook_menu()
|
| 50 |
*
|
| 51 |
* @return array
|
| 52 |
*/
|
| 53 |
function cck_table_despliter_menu($may_cache) {
|
| 54 |
$items = array();
|
| 55 |
$access = user_access('administer content types');
|
| 56 |
|
| 57 |
if ($may_cache) {
|
| 58 |
$items[] = array(
|
| 59 |
'path' => 'admin/node/types/table_despliter',
|
| 60 |
'title' => t('table despliter'),
|
| 61 |
'callback' => 'cck_table_despliter_edit',
|
| 62 |
'access' => $access,
|
| 63 |
'type' => MENU_LOCAL_TASK,
|
| 64 |
);
|
| 65 |
}
|
| 66 |
|
| 67 |
return $items;
|
| 68 |
} // */
|
| 69 |
|
| 70 |
function cck_table_despliter_edit() {
|
| 71 |
$form = array();
|
| 72 |
$node_types = array();
|
| 73 |
$field_names = array();
|
| 74 |
|
| 75 |
$result = db_query('SELECT DISTINCT(n.type_name) FROM {node_field_instance} n ORDER BY n.type_name');
|
| 76 |
while ($type = db_fetch_object($result)) {
|
| 77 |
$node_types[$type->type_name] = $type->type_name;
|
| 78 |
}
|
| 79 |
unset($type, $result);
|
| 80 |
|
| 81 |
// field_name is the PK of node_field
|
| 82 |
$result = db_query('SELECT n.field_name FROM {node_field} n ORDER BY n.field_name');
|
| 83 |
while ($field = db_fetch_object($result)) {
|
| 84 |
$field_names[$field->field_name] = $field->field_name;
|
| 85 |
}
|
| 86 |
unset($field, $result);
|
| 87 |
|
| 88 |
$form['current_field_name'] = array(
|
| 89 |
'#title' => t('Current field name'),
|
| 90 |
'#type' => 'select',
|
| 91 |
'#options' => $field_names,
|
| 92 |
'#required' => true,
|
| 93 |
'#description' => t('This content type will get the field name added back to the original node_content_* table.')
|
| 94 |
);
|
| 95 |
$form['original_content_type'] = array(
|
| 96 |
'#title' => t('Original content type'),
|
| 97 |
'#type' => 'select',
|
| 98 |
'#options' => $node_types,
|
| 99 |
'#required' => true,
|
| 100 |
'#description' => t('Original content "owner" of the field you want to merge back from the node_data_field_* table it was split to.')
|
| 101 |
);
|
| 102 |
$form['new_content_type'] = array(
|
| 103 |
'#title' => t('New content type'),
|
| 104 |
'#type' => 'select',
|
| 105 |
'#options' => $node_types,
|
| 106 |
'#required' => false,
|
| 107 |
'#description' => t('This table will get the original field, but with a _# on the end, which is standard CCK notation for similarly named fields in different tables.')
|
| 108 |
);
|
| 109 |
$form['numerical_increment'] = array(
|
| 110 |
'#title' => t('Numerical increment'),
|
| 111 |
'#type' => 'textfield',
|
| 112 |
'#default_value' => 0,
|
| 113 |
'#required' => false,
|
| 114 |
'#size' => 1,
|
| 115 |
'#maxlength' => 1,
|
| 116 |
'#description' => t('If you know _0 is used already, change this to a higher number. This script is not smart enough (yet) to detect which number to use on its own.')
|
| 117 |
);
|
| 118 |
$form['use_new_content_type'] = array(
|
| 119 |
'#title' => t('Preserve new content type'),
|
| 120 |
'#type' => 'checkbox',
|
| 121 |
'#description' => t('Attempt to recreate the new field, but with the _# on the end, and its data. Leave unchecked to ignore new content type.')
|
| 122 |
);
|
| 123 |
$form['submit'] = array(
|
| 124 |
'#value' => t('Reverse field split'),
|
| 125 |
'#type' => 'submit',
|
| 126 |
'#submit' => true
|
| 127 |
);
|
| 128 |
return drupal_get_form('cck_table_despliter_edit', $form);
|
| 129 |
} // */
|
| 130 |
|
| 131 |
/**
|
| 132 |
* Moves the data from the node_data_field_* table
|
| 133 |
* back to the original table
|
| 134 |
*
|
| 135 |
* tables that have to update in some way:
|
| 136 |
* node_field: copy row based on field_name
|
| 137 |
* node_field_instance: update/rename the field_name for the newer content_type - MUST MATCH node_field entry
|
| 138 |
* node_group_fields: update/rename the field_name for the newer type_name
|
| 139 |
*/
|
| 140 |
function cck_table_despliter_edit_submit($form_id, $form_values) {
|
| 141 |
|
| 142 |
// should pull Field, Type, Null, Key, Default, Extra
|
| 143 |
$result = db_query("SHOW COLUMNS FROM {node_data_{$form_values['current_field_name']}} WHERE field != 'vid' AND field != 'nid'");
|
| 144 |
$field = db_fetch_object($result);
|
| 145 |
unset($result);
|
| 146 |
|
| 147 |
// use $field->Field because it will have the added _value part to the field name
|
| 148 |
// that's needed when the field is in the content type's table
|
| 149 |
db_query("ALTER TABLE {node_{$form_values['original_content_type']}} ADD {$field->Field} {$field->Type} %s DEFAULT '%s'", $field->Null == 'NO' ? 'NOT NULL' : '', $field->Default);
|
| 150 |
|
| 151 |
if (!empty($form_values['use_new_content_type'])) {
|
| 152 |
$new_field = $field->Field;
|
| 153 |
$field_len = strlen($new_field);
|
| 154 |
// 100% BUGGY if true - doesn't insert the _# in the middle like it does if false
|
| 155 |
if ($field_len > 30) {
|
| 156 |
// replacement of the last 2 chars is different from the
|
| 157 |
// addition to which is possible if it's less than 31 chars
|
| 158 |
$new_field{$field_len-1} = $form_values['numerical_increment'];
|
| 159 |
$new_field{$field_len-2} = _;
|
| 160 |
}
|
| 161 |
else {
|
| 162 |
$new_field = substr($new_field, 0, strrpos($new_field, '_')) .'_' . $form_values['numerical_increment'] . substr($new_field, strrpos($new_field, '_'));
|
| 163 |
}
|
| 164 |
unset($field_len);
|
| 165 |
db_query("ALTER TABLE {node_{$form_values['new_content_type']}} ADD $new_field {$field->Type} %s DEFAULT '%s'", $field->Null == 'NO' ? 'NOT NULL' : '', $field->Default);
|
| 166 |
}
|
| 167 |
|
| 168 |
$result = db_query("SELECT n.vid, n.nid, n.{$field->Field} AS field FROM {node_data_{$form_values['current_field_name']}} n");
|
| 169 |
while ($field_value = db_fetch_object($result)) {
|
| 170 |
db_query("UPDATE {node_{$form_values['original_content_type']}} SET {$field->Field} = '%s' WHERE vid = %d AND nid = %d", $field_value->field, $field_value->vid, $field_value->nid);
|
| 171 |
if (!empty($form_values['use_new_content_type'])) {
|
| 172 |
db_query("UPDATE {node_{$form_values['new_content_type']}} SET $new_field = '%s' WHERE vid = %d AND nid = %d", $field_value->field, $field_value->vid, $field_value->nid);
|
| 173 |
}
|
| 174 |
}
|
| 175 |
unset($result);
|
| 176 |
|
| 177 |
// clean up the map of where fields are stored
|
| 178 |
if (!empty($form_values['use_new_content_type'])) {
|
| 179 |
db_query("UPDATE {node_field_instance} SET field_name = '%s' WHERE field_name = '%s' AND type_name = '%s'", "{$form_values['current_field_name']}_{$form_values['numerical_increment']}", $form_values['current_field_name'], $form_values['new_content_type']);
|
| 180 |
|
| 181 |
// tells CCK the field is in the content type's table (node_content_*), not a table of a field (node_data_field_*)
|
| 182 |
// it's set to 0 when the field is moved to a node_data_field_* table
|
| 183 |
db_query("UPDATE {node_field} SET db_storage = 1 WHERE field_name = '%s'", $form_values['current_field_name']);
|
| 184 |
|
| 185 |
$result = db_query("SELECT field_name, type, global_settings, required, multiple, db_storage FROM {node_field} WHERE field_name = '%s'", $form_values['current_field_name']);
|
| 186 |
// should return exactly one result, but just in case it doesn't
|
| 187 |
// the while statement will keep the query from throwing warnings
|
| 188 |
while ($field = db_fetch_object($result)) {
|
| 189 |
db_query("INSERT INTO {node_field} (field_name, type, global_settings, required, multiple, db_storage) VALUES ('%s', '%s', '%s', %d, %d, %d)", "{$field->field_name}_{$form_values['numerical_increment']}", $field->type, $field->global_settings, $field->required, $field->multiple, 1);
|
| 190 |
drupal_set_message(t('Inserted !new in the content type map', array('!new' => "{$field->field_name}_{$form_values['numerical_increment']}")), 'status');
|
| 191 |
}
|
| 192 |
|
| 193 |
// THIS QUERY COULD MAKE A MESS if you use it on a field_name that exists on more than one content type
|
| 194 |
db_query("UPDATE {node_group_fields} SET field_name = '%s_%d' WHERE field_name = '%s' AND type_name != '%s'", $form_values['current_field_name'], $form_values['numerical_increment'], $form_values['current_field_name'], $form_values['original_content_type']);
|
| 195 |
}
|
| 196 |
else {
|
| 197 |
db_query("DELETE FROM {node_field_instance} WHERE field_name = '%s' AND type_name != '%s'", $form_values['current_field_name'], $form_values['original_content_type']);
|
| 198 |
drupal_set_message(t('Deleted field instances where other content types shared the same field name.'), 'status');
|
| 199 |
}
|
| 200 |
|
| 201 |
db_query("DROP TABLE {node_data_{$form_values['current_field_name']}}");
|
| 202 |
|
| 203 |
} // */
|