'admin/content/types/export',
'title' => t('Export'),
'callback' => 'drupal_get_form',
'callback arguments' => 'content_copy_export_form',
'access' => $access,
'type' => MENU_LOCAL_TASK,
'weight' => 3,
);
$items[] = array(
'path' => 'admin/content/types/import',
'title' => t('Import'),
'callback' => 'drupal_get_form',
'callback arguments' => 'content_copy_import_form',
'access' => $access,
'type' => MENU_LOCAL_TASK,
'weight' => 4,
);
}
return $items;
}
/**
* A form to export field definitions.
*/
function content_copy_export_form($form_values = NULL) {
$step = intval($form_values['step'] + 1);
$type_name = $form_values['type_name'];
$types = content_copy_types();
$fields = content_copy_fields($type_name);
if (module_exists('fieldgroup')) {
$groups = content_copy_groups($type_name);
}
// If a content type has been selected and there are no fields or groups to select,
// jump straight to export.
if ($step == 2 && !($groups) && !($fields)) {
$step = 3;
}
$form['step'] = array(
'#type' => 'hidden',
'#value' => $step,
);
$form['#prefix'] = t('This form will process a content type and one or more fields from that type and export the settings. The export created by this process can be copied and pasted as an import into the current or any other database. The import will add the fields to into an existing content type or create a new content type that includes the selected fields.');
$form['#multistep'] = TRUE;
$form['#redirect'] = FALSE;
switch ($step) {
case 1: // Select a content type.
$form['type_name'] = array(
'#title' => t('Types'),
'#type' => 'radios',
'#multiple' => FALSE,
'#options' => $types,
'#description' => t('Select the content type to export.'),
);
// For some reason using #default_value in step 2 won't initialize
// all fields as checked, but setting a hidden value in step 1 will do it.
if (module_exists('fieldgroup')) {
$form['groups'] = array(
'#type' => 'hidden',
'#value' => serialize(array_keys(content_copy_groups())),
);
}
$form['fields'] = array(
'#type' => 'hidden',
'#value' => serialize(array_keys(content_fields())),
);
break;
case 2: // Select groups and fields.
$form['type_name'] = array(
'#type' => 'hidden',
'#value' => $type_name,
);
if (module_exists('fieldgroup') && !empty($groups)) {
$form['groups'] = array(
'#title' => t('Groups'),
'#type' => 'checkboxes',
'#multiple' => TRUE,
'#options' => $groups,
'#description' => t('Select the group definitions to export from %type.', array(
'%type' => node_get_types('name', $type_name
))),
);
}
$form['fields'] = array(
'#title' => t('Fields'),
'#type' => 'checkboxes',
'#multiple' => TRUE,
'#options' => $fields,
'#description' => t('Select the field definitions to export from %type.', array(
'%type' => node_get_types('name', $type_name
))),
);
break;
case 3: // Display the export macro.
$GLOBALS['content_copy']['count'] = 0;
$form['export'] = array(
'#title' => t('Export data'),
'#type' => 'textarea',
'#cols' => 60,
'#value' => content_copy_export($form_id, $form_values),
'#rows' => max(40, $GLOBALS['content_copy']['count']),
'#description' => t('Copy the export text and paste it into another content type using the import function.'),
);
break;
}
if ($step < 3) { // Omit submit button on the textarea block to display the export data.
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
}
return $form;
}
/**
* Process the export, get field admin forms for all requested fields
* and save the form values as formatted text.
*/
function content_copy_export($form_id, $form_values) {
// Set a global variable to tell when to intervene with form_alter().
$GLOBALS['content_copy']['status'] = 'export';
// Get the content type info by submitting the content type form.
drupal_execute('node_type_form', array(), node_get_types('type', $form_values['type_name']));
// Get an array of groups to export.
// Record a macro for each group by submitting the group edit form.
if (is_array($form_values['groups']) && module_exists('fieldgroup')) {
$content_type = content_types($form_values['type_name']);
foreach ($form_values['groups'] as $group) {
drupal_execute('fieldgroup_edit_group_form', array(), $content_type, $group, 'edit');
}
}
// Get an array of fields to export
// Record a macro for each field by submitting the field settings form.
// Omit fields from the export if their module is not currently installed
// otherwise the system will generate errors when the macro tries to execute their forms.
$type = content_types($form_values['type_name']);
foreach ((array) $form_values['fields'] as $field_name) {
$field = $type['fields'][$field_name];
$field_types = _content_field_types();
$field_module = $field_types[$field['type']]['module'];
$widget_types = _content_widget_types();
$widget_module = $widget_types[$field['widget']['type']]['module'];
if (!empty($field_module) && module_exists($field_module) && !empty($widget_module) && module_exists($widget_module)) {
drupal_execute('_content_admin_field', array(), $form_values['type_name'], $field_name);
}
}
// Convert the macro array into formatted text.
return content_copy_get_macro();
}
/**
* A form to import formatted text created with export.
*/
function content_copy_import_form($type_name = '') {
$form['#prefix'] = t('This form will import field definitions exported from another content type or another database.
Note that fields cannot be duplicated within the same content type, so imported fields will be added only if they do not already exist in the selected type.');
$form['type_name'] = array(
'#type' => 'select',
'#options' => array('' => t('')) + node_get_types('names'),
'#default_value' => $type_name,
'#title' => t('Content type'),
'#description' => t('Select the content type to import these fields into.
Select to create a new content type to contain the fields.'),
);
$form['macro'] = array(
'#type' => 'textarea',
'#rows' => 40,
'#title' => t('Import data'),
'#required' => TRUE,
'#description' => t('Paste the text created by a content export into this field.'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
/**
* Submit handler for import form.
* For each submitted field:
* 1) add new field to the database
* 2) execute the imported field macro to update the settings to the imported values
*/
function content_copy_import_form_submit($form_id, $form_values) {
// Get the content type we are importing into.
$type_name = $form_values['type_name'];
$type_label = node_get_types('name', $type_name);
$content = NULL;
// Convert the import formatted text back into a $content array.
// Return if errors generated or not an array.
eval($form_values['macro']);
// Preliminary error trapping, must have valid arrays to work with.
if (!is_array($content) || !is_array($content['type'])) {
form_set_error('macro', t('The import data is not valid import text.'));
return;
}
// Get all type and field info for this database.
$content_info = _content_type_info();
$imported_type = $content['type'];
$imported_type_name = $imported_type['type'];
$imported_type_label = $imported_type['name'];
// It is allowed to import a type with no fields,
// so the fields array could be empty and must be cast as an array.
$imported_fields = (array) $content['fields'];
// Perform more pre-import error trapping.
// If there are potential problems, exit without doing the import.
$not_enabled = array();
// The groups array could be empty and still valid, make sure to cast it as an array.
// If there are groups in the import, make sure the fieldgroup module is enabled.
if (module_exists('fieldgroup')) {
$imported_groups = (array) $content['groups'];
}
elseif (is_array($content['groups'])) {
$not_enabled[] = 'fieldgroup';
}
// Make sure that all the field and widget modules in the import are enabled in this database.
foreach ($imported_fields as $field) {
if (empty($field['module'])) {
$not_enabled[] = $field['field_name'];
}
else {
$modules = explode(', ', $field['module']);
foreach ($modules as $module) {
if (!module_exists($module)) {
$not_enabled[] = $module;
}
}
}
}
// If any required module is not enabled, set an error message and exit.
if ($not_enabled) {
form_set_error('macro', t('The following modules must be enabled for this import to work: %modules.', array(
'%modules' => implode(', ', array_unique($not_enabled))
)));
}
// Make sure the imported content type doesn't already exist in the database.
if ($form_values['type_name'] == t('')) {
if (in_array($imported_type_name, array_keys($content_info['content types']))) {
form_set_error('macro', t('The content type %type already exists in this database.', array(
'%type' => $imported_type_name
)));
}
}
if (form_set_error()) {
drupal_set_message(t('Exiting. No import performed.'), 'error');
return;
}
// Create the content type, if requested.
if ($form_values['type_name'] == t('')) {
$node = (object) $imported_type;
$values = $imported_type;
drupal_execute('node_type_form', $values, $node);
// Reset type and database values once new type has been added.
$type_name = $imported_type_name;
$type_label = node_get_types('name', $type_name);
$content_info = _content_type_info();
if (form_get_errors() || !is_array($content_info['content types'][$type_name])) {
drupal_set_message(t("An error has occured adding the content type %type.
Please check the errors displayed for more details.", array(
'%type' => $imported_type_name
)));
return;
}
}
// Create the groups for this type, if they don't already exist.
if (module_exists('fieldgroup')) {
$groups = fieldgroup_groups($type_name);
$content_type = content_types($type_name);
$errors = FALSE;
foreach ($imported_groups as $group) {
$group_name = $group['group_name'];
if (!is_array($groups[$group_name])) {
$values = (array) $group;
drupal_execute('fieldgroup_edit_group_form', $values, $content_type, $group_name, 'add');
}
}
cache_clear_all('fieldgroup_data', 'cache_content');
// Reset the static variable in fieldgroup_groups() with new data.
fieldgroup_groups('', FALSE, TRUE);
}
// Iterate through the field forms in the import and execute each.
foreach ($imported_fields as $field) {
// Make sure the field doesn't already exist in the type.
// If so, do nothing, fields can't be duplicated within a content type.
$field_name = $field['field_name'];
$field_values = $field;
$field_label = $field_values['label'];
if (!empty($field['field_name']) && is_array($content_info['content types'][$type_name]['fields'][$field_name])) {
drupal_set_message(t("The imported field %field_label (%field_name) was not added to %type because that field already exists in %type.", array(
'%field_label' => $field_label,
'%field_name' => $field_name,
'%type' => $type_name
)));
}
else {
$values = array();
$errors = FALSE;
// Check if field already exists in the database.
// If not, add new, otherwise, add existing.
if (!array_key_exists($field_name, $content_info['fields'])) {
$values['field_name'] = $field_values['field_name'];
$values['label'] = $field_values['label'];
$values['field_widget_type'] = $field_values['field_type'] .'-'. $field_values['widget_type'];
$values['type_name'] = $type_name;
$field_url = drupal_execute('_content_admin_field_add_new', $values, $type_name, $field_name);
if (form_get_errors()) {
drupal_set_message(t("An error has occured adding the field %field_label (%field_name).
Please check the errors displayed for more details.", array(
'%field_label' => $field_label,
'%field_name' => $field_name
)));
$errors = TRUE;
}
else {
// Retrieve the new field name by picking out the arg from the url returned by
// _content_admin_field_add_new(), needed so the right field can be updated in the next step.
$args = explode('/', $field_url);
$field_name = array_pop($args);
$field_values['field_name'] = $field_name;
}
}
else {
$values['field_name'] = $field_values['field_name'];
$values['type_name'] = $type_name;
drupal_execute('_content_admin_field_add_existing', $values, $type_name);
if (form_get_errors()) {
drupal_set_message(t("An error has occured adding the field %field_label (%field_name).
Please check the errors displayed for more details.", array(
'%field_label' => $field_label,
'%field_name' => $field_name
)));
$errors = TRUE;
}
}
// Once the field has been added, update the settings with the macro values.
// Replace the export type name with the import type name.
if (!$errors) {
$field_values['type_name'] = $type_name;
drupal_execute('_content_admin_field', $field_values, $type_name, $field_name);
if (form_get_errors()) {
drupal_set_message(t("The field %field_label (%field_name) was added to the content type %type, but an error has occured updating the field settings.
Please check the errors displayed for more details.", array(
'%field_label' => $field_label,
'%field_name' => $field_name,
'%type' => $type_label
)));
}
}
}
}
if (!form_get_errors()) {
if (sizeof($imported_fields) > 0 || sizeof($imported_groups) > 0) {
return 'admin/content/types/'. $content_info['content types'][$type_name]['url_str'] .'/fields';
}
else {
return 'admin/content/types';
}
}
}
/**
* Implementation of hook_form_alter().
* Intervene to run form through macro when doing export
*/
function content_copy_form_alter($form_id, &$form) {
$alter_forms = array('node_type_form', '_content_admin_field', 'fieldgroup_edit_group_form');
if ($GLOBALS['content_copy']['status'] == 'export' && in_array($form_id, $alter_forms)) {
$form['#submit'] = array('content_copy_record_macro' => array($form));
}
}
/**
* Get all the fields for a content type.
*/
function content_copy_fields($type_name) {
$fields = array();
if (!$type_name) {
return $fields;
}
$content_info = _content_type_info();
foreach ($content_info['content types'][$type_name]['fields'] as $field_name => $field) {
// Omit fields from the export if their module is not currently installed
// otherwise the system will generate errors when the macro tries to execute their forms.
$field_types = _content_field_types();
$field_module = $field_types[$field['type']]['module'];
$widget_types = _content_widget_types();
$widget_module = $widget_types[$field['widget']['type']]['module'];
if (!empty($field_module) && module_exists($field_module) && !empty($widget_module) && module_exists($widget_module)) {
$fields[$field_name] = $field['widget']['label'] .' ('. $field['field_name'] .')';
}
}
return $fields;
}
/**
* Get all the groups for a content type.
*/
function content_copy_groups($type_name = '') {
$groups = array();
if (!$type_name || !module_exists('fieldgroup')) {
return $groups;
}
$data = fieldgroup_groups($type_name);
foreach ($data as $group_name => $group) {
$groups[$group_name] = $group['label'] .' ('. $group['group_name'] .')';
}
return $groups;
}
/**
* Get all content types.
*/
function content_copy_types() {
$types = array();
$content_info = _content_type_info();
foreach ($content_info['content types'] as $type_name => $val) {
$types[$type_name] = $val['name'] .' ('. $type_name .')';
}
return $types;
}
/**
* A handler that stores the form submissions into a $GLOBALS array
*/
function content_copy_record_macro($form_id, $edit, $form) {
$subs = $GLOBALS['content_copy']['submissions'];
// Get the form values and store them in a $GLOBALS['content_copy']['submissions'] array.
// Update $GLOBALS['content_copy']['count'] with an approximation of the number of rows in this item.
// Count is used to approximate necessary size of textarea in form.
unset($edit['type_name'], $edit['submit'], $edit['delete'], $edit['form_id']);
switch ($form_id) {
case 'node_type_form':
$subs['type'] = $edit;
$GLOBALS['content_copy']['count'] += sizeof($edit) + 5;
break;
case 'fieldgroup_edit_group_form':
$subs['groups'][] = $edit;
$GLOBALS['content_copy']['count'] += sizeof($edit) + 5;
break;
default:
if ($edit['field_widget_type']) {
$tmp = explode('-', $edit['field_widget_type']);
$field_name = $tmp[0];
}
else {
$field_name = $edit['field_name'];
}
$subs['fields'][] = $edit;
$GLOBALS['content_copy']['count'] += sizeof($edit) + 5;
}
$GLOBALS['content_copy']['submissions'] = $subs;
}
/**
* @return a code representation of the recorded macro.
*/
function content_copy_get_macro() {
foreach ($GLOBALS['content_copy']['submissions'] as $form_type => $form) {
$string .= "\$content[$form_type] = ". var_export((array) $form, TRUE) .";\n";
}
return $string;
}