Does anybody know how to programmatically add a CCK field to a content type? I want to do this on module install, as the field in question is required for the module I'm using. I usually don't use CCK, but It's a quick job I'm doing here, and it's not worth it for me to build my own content type in a module like I would usually do.

I've taken care of all the submission of CCK data, I just need to know how to attach CCK fields to content types.

Comments

bscott’s picture

I couldn't find a way to do this and ended up exporting the content type to a file and importing it in my install routine.

jaypan’s picture

Can you explain a little more about how you did that? Thanks.

Contact me to contract me for D7 -> D10/11 migrations.

bscott’s picture

In Administer -> Content Management -> Content Types select Export and then the content type you want to export then Export. Cut and paste the export data into a file and save.

In my case I have exported 3 content types as above into club_event_artist_cck_node.def, club_event_cck_node.def and club_event_type_cck_node.def and saved the files in my module directory.

My code to import them is as follows:-

function club_event_install() {
  club_event_create_content_type('./'.drupal_get_path('module', 'club_event').'/club_event_artist_cck_node.def');
  club_event_create_content_type('./'.drupal_get_path('module', 'club_event').'/club_event_cck_node.def');   
  club_event_create_content_type('./'.drupal_get_path('module', 'club_event').'/club_event_type_cck_node.def');  
  _club_event_upgrade(); 
}

function club_event_create_content_type($cck_definition_file) {
  include_once('./'. drupal_get_path('module', 'node') .'/content_types.inc');
  include_once('./'. drupal_get_path('module', 'content') .'/includes/content.admin.inc');
  $values = array();
  $values['type_name'] = '<create>';
  $values['macro'] = file_get_contents($cck_definition_file);
  $form_state = array();
  $form_state['values'] = $values;
  drupal_execute("content_copy_import_form", $form_state);
}

I'm afraid I don't remember where I originally got this from - if I can fid it I will add a link.

This works for me - I hope it works for you.

jaypan’s picture

Thanks mate. It's not exactly what I'm looking for, but I think I can use it to do what I want!

Contact me to contract me for D7 -> D10/11 migrations.

Ram_doss’s picture

Is it in module.mod file or module.install file?????????????????............................

bscott’s picture

module.install

Ram_doss’s picture

Still i didnt get the content types and fields when i enable the module .

Ram_doss’s picture

I created the module.install with this codes

function interactive_map_install()
{
	global $base_path;
	interactive_map_content_type($base_path .drupal_get_path('module', 'interactive_map').'/interactive_map_content_type.def');
       interactive_map_content_type($base_path.drupal_get_path('module', 'interactive_map').'/hotspot_content_type.def');    
}
function interactive_map_content_type($cck_definition_file) 
{
	global $base_path;
  include_once($base_path. drupal_get_path('module', 'node') .'/content_types.inc');
  include_once($base_path. drupal_get_path('module', 'content') .'/includes/content.admin.inc');
  $values = array();
  $values['type_name'] = '<create>';
  $values['macro'] = file_get_contents($cck_definition_file);
  $form_state = array();
  $form_state['values'] = $values; 
  drupal_execute("content_copy_import_form", $form_state);
}

also created mod_content_type.def files and placed them inside the module folder.

when i enable the module ,the module content types are not being created .. dont know why ?? can u help me out ????

blainelang’s picture

Also been trying to do the same - add a filefield CCK field to a a new module custom content type.

Had no success using the CCK import method as it would silently fail. See my post: http://drupal.org/node/722268 (my last post Mar 10) where I outline that even using the manual CCK import would fail unless I modified the generated CCK export data to use module => 'node' which effectively then makes it useless for my module put narrows down the reason why the CCK import is failing.

There are multiple topics in this forum where other developers are having the same issue without any resolution. Can anyone point us to another module that does this?

Focusing on Business Applications but heck we do anything Drupal

dambrisco’s picture

I might be misunderstanding the goal, but would be using hook_alter_form() be a possible solution?

blainelang’s picture

My first attempt was to use the HOOK_form API specifying the 'filefield wiget' which is what the CCK filefield uses to define it's field but kept getting errors.

The filefield_widget #process callback is complaining:
* warning: Invalid argument supplied for foreach() in F:\web4\nexdrupal\sites\all\modules\filefield\filefield_widget.inc on line 384.
* warning: implode() [function.implode]: Invalid arguments passed in F:\web4\nexdrupal\sites\all\modules\filefield\filefield_widget.inc on line 393.

It was also complaining that no default_value was passed in and now it wants to know about the validators. I would prefer that it just use default validators.

$form['attachment'] = array(
'#type' => 'filefield_widget',
'#title' => 'Attachment',
'#default_value' => array(
'fid' => 0,
'list' => 1,
'data' => array(
'description' => ''
)
),
'#field_name' => 'field_nexfile_file',
'#upload_validators' => array()
);

Focusing on Business Applications but heck we do anything Drupal

dambrisco’s picture

Are you creating a new form specifically or are you using an existing form and wanting to add this field?

blainelang’s picture

The module has HOOK_node_info() which I believe is what triggers the initial creation/definition of the module's content type.

function nexfile_node_info() {
return array(
'nexfile_folder' => array(
'name' => t('Nexfile Folder'),
'module' => 'nexfile',
'description' => t('Folder for storing documents'),
'has_title' => TRUE,
'title_label' => t('Folder Name'),
'has_body' => TRUE,
'body_label' => t('Folder Description'),
'locked' => FALSE
)
);
}

I then tried to extend the content type form to add the CCK field field but that failed as I noted above in the filefield widget code.

function nexfile_form($node, $form_state) {

$nodetype = node_get_types('type', $node);
if ($node->type = 'nexfile_folder') {
$form['title'] = array(
'#type' => 'textfield',
'#title' => check_plain($nodetype->title_label),
'#required' => TRUE,
'#default_value' => $node->title,
'#weight' => -5,
'#maxlength' => 255
);

$form['body_filter']['folderdesc'] = array(
'#type' => 'textarea',
'#title' => check_plain($nodetype->body_label),
'#required' => FALSE,
'#rows' => 2
);
$form['body_filter']['filter'] = filter_form($node->format);

if (user_access('administer nexfile', $user)) {
$parentFolders = array(0 => 'Top Level Folder');
}
$parentFolders += nexdoc_recursiveAccessArray(array('upload', 'upload_dir'));

$form['parentfolder'] = array(
'#type' => 'select',
'#title' => t('Parent Folder'),
'#required' => TRUE,
'#options' => $parentFolders
);

if (isset($node->folderdesc) AND !empty($node->folderdesc)) {
$form['body_filter']['folderdesc']['#default_value'] = check_plain($node->folderdesc);
}
else {
$form['body_filter']['folderdesc']['#default_value'] = $node->body;
}

if (isset($node->parentfolder)) {
$form['parentfolder']['#default_value'] = $node->parentfolder;
}
return $form;
}

}

Presently after the module installs, I manually edit the content type to add the 'CCK filefleld' field to the modules content type.

Focusing on Business Applications but heck we do anything Drupal

milesw’s picture

This guy found a beautifully simple way of adding CCK fields programmatically: http://drewish.com/node/118

Overview:

1. Grab an export of an existing field definition, as an array:
$field = var_export(content_fields('field_myfieldname', 'content_type'));

2. Adjust the field's components as necessary. For example:
$field['field_name'] = 'field_myimprovedfieldname';

3. To save the field definition as a new field:
content_field_instance_create($field);

blainelang’s picture

This method of importing in a CCK exported content type works for standard 'module' => node or maybe 'text' as in the example you noted but fails as I've noted in my previous comments.

I am not even able to import the exported content type into another clean site. Please do try and see if you can see why this is failing.
$content['type'] = array (
'name' => 'Nexfile Folder',
'type' => 'nexfile_folder',
'description' => 'Folder for storing documents',
'title_label' => 'Folder Name',
'body_label' => 'Folder Description',
'min_word_count' => 0,
'help' => '',
'node_options' =>
array (
'status' => true,
'promote' => true,
'sticky' => false,
'revision' => false,
),
'old_type' => 'nexfile_folder',
'orig_type' => 'nexfile_folder',
'module' => 'nexfile',
'custom' => false,
'modified' => false,
'locked' => false,
'reset' => 'Reset to defaults',
'comment' => 2,
'comment_default_mode' => 4,
'comment_default_order' => 1,
'comment_default_per_page' => 50,
'comment_controls' => 3,
'comment_anonymous' => 0,
'comment_subject_field' => 1,
'comment_preview' => 1,
'comment_form_location' => 0,
);
$content['fields'] = array (
0 =>
array (
'label' => 'Folder File',
'field_name' => 'field_nexfile_file',
'type' => 'filefield',
'widget_type' => 'filefield_widget',
'change' => 'Change basic information',
'weight' => '31',
'file_extensions' => '',
'progress_indicator' => 'bar',
'file_path' => '',
'max_filesize_per_file' => '',
'max_filesize_per_node' => '',
'description' => '',
'required' => 0,
'multiple' => '1',
'list_field' => '1',
'list_default' => 1,
'description_field' => '0',
'op' => 'Save field settings',
'module' => 'filefield',
'widget_module' => 'filefield',
'columns' =>
array (
'fid' =>
array (
'type' => 'int',
'not null' => false,
'views' => true,
),
'list' =>
array (
'type' => 'int',
'size' => 'tiny',
'not null' => false,
'views' => true,
),
'data' =>
array (
'type' => 'text',
'serialize' => true,
'views' => true,
),
),
'display_settings' =>
array (
'label' =>
array (
'format' => 'above',
'exclude' => 0,
),
'teaser' =>
array (
'format' => 'default',
'exclude' => 0,
),
'full' =>
array (
'format' => 'default',
'exclude' => 0,
),
4 =>
array (
'format' => 'default',
'exclude' => 0,
),
),
),
);
$content['extra'] = array (
'title' => '-5',
'body_field' => '0',
'revision_information' => '20',
'comment_settings' => '30',
'menu' => '-2',
);

if you change 'module' => 'nexfile' to => 'node' then it imports but thats not going to work for my module.

Thanks!

Focusing on Business Applications but heck we do anything Drupal

mawi27’s picture

Hi Blaine,

you can create a combined content type with custom 'schema' fields AND cck fields this way:

1. In your main module you define the custom content type using hook_schema() and hook_node_info() as usual
2. In an additional module you add the cck fields to the existing content type

Install your main module 1st and then the additional module.

new module needs 4 files:
mymodule.info --> standard
mymodule.module --> only hook_help() needed

mymodule.ccknodedef.inc

function _mymodule_export() {
  --> put the cck export file here
  return $content;
}

mymodule.install

function mymodule_install() {
  // CCK content_copy.module may not be enabled, so make sure it is included
  require_once './' . drupal_get_path('module', 'content'). '/modules/content_copy/content_copy.module';
  module_load_include('inc', 'mymodule', 'mymodule.ccknodedef');
  $content = _mymodule_export();  // in modulename.ccknodedef.inc
  $form_state['values']['type_name'] = 'mymodule'; //don't use <create> here as you want to extend existing content type
  $form_state['values']['macro'] = '$content = ' . var_export($content, TRUE) . ';';
  // form provided by content_copy.module
  drupal_execute('content_copy_import_form', $form_state);
  content_clear_type_cache();
}

I tried it out and it extends the content type and displays the additional fields, but I didn't test any functions (CRUD).
Maybe this helps you as a starting point.

Marco

blainelang’s picture

Thanks Marco,

Your solution put me on the right track and can report that it's now working. A lot of the CCK field import examples state to use <create> and the other day I had tried my module name and it still failed. What $form_state['values']['type_name'] really wants is the name of the content type which in my case is 'nexfile_folder'
> $form_state['values']['type_name'] = 'nexfile_folder';

I had used the CCK export to create the CCK schema for the one CCK field that needs to be added to the 'nexfile_folder' content type, and previously tried after a clean module install to import it in using the CCK import and it had failed as well. The reason it had failed was that I had used the default <create> option in the content type dropdown selection in the CCK import screen.

It was tonight, that I noticed this and that the options were content type names which is why your tested solution initially failed for me, I realized it was the content type name and not the module.

Instead of creating another module, I have used a menu callback that includes the cckimportdef.inc file (in my case) and executes the nexfile_install_cck_filefield() function.

Still not as clean as I'd like as I'd prefer that this be done automatically as part of the install but we can not execute the CCK import after the schema_install because the module's HOOK_node_info function has not yet run to create content type.

Hum .... is it acceptable to use a persistent variable and if not yet set run the CCK import.

We need a HOOK_postinstall_runonce API :)

Thanks again Marco for your assistance!
Blaine

Focusing on Business Applications but heck we do anything Drupal

redben’s picture

I can't understand why the content_field_instance_create fails for you could you elaborate ? AFAIK features module uses this method to create content fields

alunyov’s picture

@milesw:Thank you very much! It is exactly what I need.

So my story in few words: I have user profile with a lots of cck fields. I had to create module that allows to add new content type 'badge' with few cck fields AND to add nodereference field 'Badge' to user profile content type. So I follow your comment and I added the field programmatically. That works fine for me!

Anonymous’s picture

Hi Jay
I hope the following comment will help you out.
First you have to create a content type from UI. Then try to export the filed that you want to add in code. But to do the same you have to enable "Content Copy" from cck module. Once you will export you will find the code some how like below

// Exported field: field_lil_class_id
$fields['lil_class-field_lil_class_id'] = array(
'field_name' => 'field_lil_class_id',
'type_name' => 'lil_class',
'display_settings' => array(
'weight' => '-2',
'parent' => '',
'label' => array(
'format' => 'above',
),
'teaser' => array(
'format' => 'default',
'exclude' => 1,
),
'full' => array(
'format' => 'default',
'exclude' => 1,
),
'4' => array(
'format' => 'default',
'exclude' => 0,
),
),
'widget_active' => '1',
'type' => 'text',
'required' => '0',
'multiple' => '0',
'module' => 'text',
'active' => '1',
'text_processing' => '0',
'max_length' => '',
'allowed_values' => '',
'allowed_values_php' => '',
'widget' => array(
'rows' => 5,
'size' => '60',
'default_value' => array(
'0' => array(
'value' => '',
'_error_element' => 'default_value_widget][field_lil_class_id][0][value',
),
),
'default_value_php' => NULL,
'label' => 'LIL Class ID',
'weight' => '-2',
'description' => '',
'type' => 'text_textfield',
'module' => 'text',
),
);