Main image
1st October
2008
written by david

I updated the code to provide content type and cck field information for custom modules to work with Drupal 6.

This is the method I'm using to run the import. This method resides in a site_profile module I use for common functions for restoring sites via installation profiles for deployment.

It works using exported 'macros' from the content_copy module data export form. The cck content_copy handles all the content type and field importing. If the types or fields already exist, they are not duplicated and are handled gracefully. Feedback is provided as fields and types are added.

I usually add a folder called 'cck' to my module folder and copy and paste the content_copy export data structure into separate files, like this

  • .../modules/my_module
  • .../modules/my_module/cck
  • .../modules/my_module/cck/my_content_type.inc
  • .../modules/my_module/cck/my_other_content_type.inc

I simply copy and paste the exported data from admin > content types > export into the .inc files.

Here's the importer function that does the fetching and submitting of the macro data via file_get_contents and drupal_execute for Drupal 6.

PHP:
  1. /**
  2. * Programmatically create CCK fields and types using the content copy module
  3. * @param $type string
  4. * content type to create, defaults to new type, if type exists, only fields will be added
  5. * @param $macro array
  6. * exported array from content types -> export. If file is not specified, macro will be used
  7. * @param $file string
  8. * path to file containing content copy exported macro data structure. no escaping needed.
  9. */
  10. function site_profile_import_content_type($type = '<create>', $macro = '', $file = '') {
  11.   if(!module_exists("content_copy")){
  12.     drupal_set_message('Programmatically creating CCK fields requires the Content Copy module. Exiting.');
  13.     return;
  14.   }
  15.  
  16.   include_once( $_SERVER['DOCUMENT_ROOT']. drupal_get_path('module', 'content') .'/includes/content.admin.inc');
  17.   include_once( $_SERVER['DOCUMENT_ROOT']. drupal_get_path('module', 'node') .'/content_types.inc');
  18.  
  19.   $values = array();
  20.   $values['type_name'] = $type;
  21.   if($file){
  22.     if(file_exists($file)){
  23.       $values['macro'] = file_get_contents($file);
  24.     }else{
  25.       drupal_set_message('Unable to read input file for import. Exiting.');
  26.       return;
  27.     }
  28.   }elseif($macro){
  29.     $values['macro'] = $macro;
  30.   }
  31.   $form_state = array();
  32.   $form_state['values'] = $values;
  33.   //drupal_set_message('<pre>DEBUG: '.print_r($values['macro'],1).'</pre>');
  34.   drupal_execute("content_copy_import_form", $form_state);
  35.   content_clear_type_cache();
  36. }

I had some difficulty calling the field importer from the hook_install and from hook_updates, due to the functions available and includes required at that time, so I used a hook_requirements function to get the user to manually call the fields installer from admin. The requirements hook goes in the .install file and looks like this:

PHP:
  1. /**
  2. * check that content types and fields have been installed
  3. * if not, set a message in admin
  4. */
  5. function project_requirements($phase){
  6.   $requirements = array();
  7.   // Ensure translations don't break at install time.
  8.   $t = get_t();
  9.  
  10.   $type = 'Project';
  11.  
  12.   if ($phase == 'runtime') {
  13.     //check if any cck fields exist for this type
  14.     if( ! variable_get( 'project_content_types_installed', 0 ) ){
  15.       $requirements['project_cck_warning'] = array(
  16.         'title' => $t('Project Content Types'),
  17.         'value' => 'CCK fields not found',
  18.         'severity' => REQUIREMENT_ERROR,
  19.         'description' => $t('Click '.l('here','project/install').' to install the content types and/or CCK fields for the %type module.', array('%type'=> $type)),
  20.       );
  21.     }
  22.   }
  23.   return $requirements;
  24. }

project/install in this case is a menu callback that starts the manual install of content types and CCK fields. The hook_menu code was this:

PHP:
  1. /**
  2. * Implementation of hook_menu().
  3. */
  4. function project_menu() {
  5.   $items['project/install'] = array(
  6.     'page callback' => 'project_install_content_types',
  7.     'access callback' => TRUE,
  8.     'type' => MENU_CALLBACK,
  9.   );
  10.   return $items;
  11. }

project_install_content_types simply calls the function to import the content type definition generated by content_copy, submitting the exported data array via drupal_execute. It then sets a config variable to indicate the processing has completed.

PHP:
  1. /**
  2. * call macro files and import data
  3. **/
  4. function project_install_content_types(){
  5.  
  6.   if( ! user_access( 'administer content types' )){
  7.     drupal_set_message( 'Sorry, You don\'t have permission to do that' );
  8.     return drupal_access_denied();
  9.   }
  10.   if( ! variable_get( 'project_content_types_installed', 0 ) ){
  11.     drupal_set_message('Installing content types and cck fields');
  12.     site_profile_import_content_type($type = 'project', $macro = '', $file = $_SERVER['DOCUMENT_ROOT']. drupal_get_path('module', 'project').'/cck/project.inc');
  13.     site_profile_import_content_type($type = 'project_tab', $macro = '', $file = $_SERVER['DOCUMENT_ROOT']. drupal_get_path('module', 'project').'/cck/project_tab.inc');
  14.     variable_set( 'project_content_types_installed', 1 );
  15.   }else{
  16.     drupal_set_message( 'Project content types are already installed.');
  17.   }
  18.   drupal_goto( 'admin/reports/status' );
  19.  
  20. }

I also added this to hook_uninstall, to clear the config variable

PHP:
  1. function project_uninstall() {
  2.   drupal_set_message('Uninstalling project.');
  3.   //clear installed status
  4.   variable_del( 'project_content_types_installed' );
  5. }

Download sample project files used in this tutorial.

  1. Marc posted the following on November 21, 2008 at 2:01 am.

    Hello I cant get things to work. I tried putting drupal_set_message('DEBUG: '.print_r($form_state,1).''); under content_copy_import_form_submit on content_copy.module but the values are all empty and so I end up with the message "The import data is not valid import text."

    please help.

  2. davexoxide posted the following on November 21, 2008 at 9:12 am.

    This is a really useful concept. I just have a couple questions.

    What's the dependencies on jquery_ui and views modules?
    I don't see where they are used in the module, maybe I'm overlooking something.

    Why is this module named the same as project module, there's going to be conflicts with function names.

    Other than that, I think this is a really important feature and should be released on Drupal.org.

    Thanks for sharing the code.

  3. david posted the following on November 21, 2008 at 9:12 am.

    @marc try uncommenting the debug line in site_profile_import_content_type here:

    PHP:
    1. //drupal_set_message('<pre>DEBUG:'.print_r($values['macro'],1).'</pre>');

    and check that $form_state has some values. It sounds like your export macro isn't been passed correctly.
    Or, if you're using the sample code, check that the macro files are being included correctly. You might need a trailing slash for $_SERVER['document_root'] or use realpath(.)

  4. david posted the following on November 21, 2008 at 9:19 am.

    @davexoxide heh, thanks for the comment those dependencies are not needed ( other than CCK and content_copy ). They're just a copy/paste relic. The 'project' name was just a name I chose for the demo. I'll tidy that up there when I get the chance. cheers.

  5. Marc posted the following on November 21, 2008 at 12:39 pm.

    @david

    when Im checking the values at my modules, the dump displays all the macro, althought the type_name is missing.

    but in the content_copy.module, their totally empty...
    Im using D6.

  6. david posted the following on November 21, 2008 at 10:28 pm.

    @marc
    check that you're passing the type value properly in your equivalent to project_install_content_types()

    In any case, the concept works, any bugs will be related to your implementation.


Leave a reply