Programatic CCK Now Possible!

joshk's picture
public
joshk - Mon, 2007-07-23 20:40

I just wanted to point out a very important change to content_copy.module that makes it much more fasible to create CCK field programatically as part of an install profile. As of last week, the DRUPAL-5 CVS version of content_copy.module no longer ends content_copy_import_form_submit() with a drupal_goto().

Previously, if you attemted to import a CCK node-type using drupal_execute(), your script would redirect to an admin page, which is problematic during an install profile as it short-cirtuits the process. This is no longer the case.

You can now run code similar to the following:

<?php
 
include_once './'. drupal_get_path('module', 'node') .'/content_types.inc';
  include_once(
'./'. drupal_get_path('module', 'content') .'/content_admin.inc');
 
$values = array();
 
$values['type_name'] ='<create>';
 
$values['macro'] = /** YOUR CCK EXPORT DUMP HERE **/
 
drupal_execute("content_copy_import_form", $values);
?>

And continue with your install script. Happy profiling!


SWEEEET!

jwolf - Tue, 2007-07-24 00:45

Thanks for the heads up.

Very exciting

Amazon's picture
Amazon - Tue, 2007-07-24 00:54

Josh, if you have some simple examples I am sure we could start building snippets.

Kieran

To seek, to strive, to find, and not to yield

New Drupal career! Drupal profile builders.
Try pre-configured and updatable profiles on CivicSpaceOnDemand


Real soon

joshk's picture
joshk - Wed, 2007-07-25 01:49

We'll be making an initial check in of our conference organizing distribution in the next two or three days. This will show how we're using the above code in conjunction with flat text files containing the CCK information.

http://www.chapterthreellc.com | http://www.outlandishjosh.com


Will add to install profile api

Boris Mann's picture
Boris Mann - Tue, 2007-07-24 01:14

Something like:

<?php
function install_create_content($macro) {
  include_once
'./'. drupal_get_path('module', 'node') .'/content_types.inc';
  include_once(
'./'. drupal_get_path('module', 'content') .'/content_admin.inc');
 
$values = array();
 
$values['type_name'] ='<create>';
 
$values['macro'] = $macro;
 
drupal_execute("content_copy_import_form", $values);
}
?>

Except those include_once's need some love depending on where you have your code, methinks.

Used in an install profile as:

  install_create_content($macro);

Where the best way to maintain the macro would likely be to suck this in from an external file -- e.g. cck_userprofile.inc, etc.


are includes needed?

mikey_p - Fri, 2007-07-27 01:43

I may be missing something here, but from my testing a few months back, no includes are needed since the form being called is in content_copy.module and an install profile only executes after a full bootstrap.

Would it be considered bad form to just call content_copy_import_form_submit with the values requried? It wouldn't really be bypassing any submit handlers, just avoiding an extra call to drupal_execute (which is what content_copy_import_form_submit calls anyway).

I'll work on some code to parse an external file if I get a chance. I think there is some applicable code in panels that could be reused ;)

There are a few gotchas here

verbal@drupal.org's picture
verbal@drupal.org - Tue, 2007-07-24 11:22

I guess this bug had been submitted many times, but my last submission of it finally got it fixed (http://drupal.org/node/160130). When you do an export of a cck type you have to change the exported code a little bit. Most notably, the export will use single quotes so you cant wrap your string in single quotes, so you can use either double quotes or heredoc syntax. Also in PHP when using variables inside a heredoc statement or double quotes, the variable will get replaced and not be taken as the literal string, for example:

<?php
  $var
= 1;
  print
"$var"; // 1
 
print '$var'; // $var
?>

So we have to escape our variables. So a dump of a simple cck type named 'topics', which has 'title' and 'description' would be:

$content[type]  = array (
  'name' => 'Topic',
  'type' => 'topic',
  'description' => 'A Topic is the overarching category which contains Goals.',
  'title_label' => 'Title',
  'body_label' => 'Description',
  'min_word_count' => '0',
  'help' => '',
  'node_options' =>
  array (
    'status' => true,
    'promote' => false,
    'sticky' => false,
    'revision' => false,
  ),
  'comment' => '2',
  'upload' => '1',
  //'event_nodeapi' => 'never',
  'upload_inline' => 0,
  'old_type' => 'topic',
  'orig_type' => '',
  'module' => 'node',
  'custom' => '1',
  'modified' => '1',
  'locked' => '0',
);

And the code to submit this programatically:

<?php
//////// define the 'topic' node type ///////
   
$form_values['type_name'] = '<create>';
   
$form_values['macro'] = <<<TOPICS
        \$content[type]  = array (
          'name' => 'Topic',
          'type' => 'topic',
          'description' => 'A Topic is the overarching category which contains Goals.',
          'title_label' => 'Title',
          'body_label' => 'Description',
          'min_word_count' => '0',
          'help' => '',
          'node_options' =>
          array (
            'status' => true,
            'promote' => false,
            'sticky' => false,
            'revision' => false,
          ),
          'comment' => '2',
          'upload' => '1',
          //'event_nodeapi' => 'never',
          'upload_inline' => 0,
          'old_type' => 'topic',
          'orig_type' => '',
          'module' => 'node',
          'custom' => '1',
          'modified' => '1',
          'locked' => '0',
        );
TOPICS;
   
drupal_execute('content_copy_import_form', $form_values);
?>

Note: if your content type has additional fields it would have a 2 variables that need to be escaped, $content[type] and $content[fields]

I didn't know how to do heredoc syntax until chx showed me a snip with some heredoc in it, and I got caught up because i was indenting the the close tag "TOPICS;" and that needs to be on its own line and NOT indented.

-Steve


Yes

joshk's picture
joshk - Wed, 2007-07-25 01:48

Gotta watch out handling text. I am actually pursuing the method of keeping the cck export data as a flat text files and packaging that as part of the install profile.

http://www.chapterthreellc.com | http://www.outlandishjosh.com


Can I help?

verbal@drupal.org's picture
verbal@drupal.org - Wed, 2007-07-25 19:33

I've been trying to get into to development for the community for a while. I have been doing private development for my job, but nothing I am allowed to contribute back ( I know.... I don't like it either ). I've already got a few ideas of how to accomplish this.
- With cck in core, we could think about adding an additional profile hook. Something like _profile_content_types() which would search the profile folder for cck text file exports and import those types.
- Another idea I had was to check the array returned by _proifle_modules() and use this to define content types as well. So while going through the returned array turning on modules, if the modules wasn't found via standard module means, check the profile folder for a cck text file that would create the type.

-Steve


Can't get this to work

Wonder95@drupal.org - Sun, 2007-09-23 04:35

Okay, I've been fighting with this for a couple weeks now and have been trying to follow this, but I can't get it to work. If I had much hair left, I'd have pulled it out by now. I'm trying to create a CCK content type as part of a module install, but no matter which way I go and how I follow this example, I get an error.

Here's what I have to this point:

<?php
function imagelist_install() {
  drupal_set_message('Installing imagelist');
  $form_values['type_name'] = '<create>';
  $form_values['macro'] = <<<IMAGES
    \$content[type]  = array (
      'name' => 'Image',
      'type' => 'image',
      'description' => 'Image to be displayed',
      'title_label' => 'Title',
      'body_label' => 'Description',
      'min_word_count' => '0',
      'help' => 'Size must be less than 1.5 MB',
      'node_options' =>
      array (
        'status' => true,
        'promote' => false,
        'sticky' => false,
        'revision' => false,
      ),
      'comment' => '2',
      'old_type' => 'image',
      'orig_type' => '',
      'module' => 'node',
      'custom' => '1',
      'modified' => '1',
      'locked' => '0',
    );
    \$content[fields]  = array (
      0 =>
      array (
        'widget_type' => 'image',
        'label' => 'Photo',
        'weight' => '0',
        'max_resolution' => 0,
        'image_path' => 'photos',
        'custom_alt' => 0,
        'custom_title' => 1,
        'description' => '',
        'group' => false,
        'required' => '1',
        'multiple' => '0',
        'field_name' => 'field_photo',
        'field_type' => 'image',
        'module' => 'imagefield',
      ),
    );
IMAGES;
  drupal_execute('content_copy_import_form', $form_values);
}

If I run it like this, I get the following error when I submit the Module admin page with my module checked:

    * Installing imagelist
    * The configuration options have been saved.
    * Installing imagelist
    * The configuration options have been saved.
    * Installing imagelist
    * An error has occured adding the content type image.
      Please check the errors displayed for more details.
    * The configuration options have been saved.

    * The import data is not valid import text.
    * warning: Cannot modify header information - headers already sent by (output started at C:\Program Files\Apache Software Foundation\Apache2.2\htdocs\test\sites\all\modules\cck\content_copy.module(251) : eval()'d code:1) in C:\Program Files\Apache Software Foundation\Apache2.2\htdocs\test\includes\common.inc on line 309.
    * The import data is not valid import text.
    * warning: Cannot modify header information - headers already sent by (output started at C:\Program Files\Apache Software Foundation\Apache2.2\htdocs\test\sites\all\modules\cck\content_copy.module(251) : eval()'d code:1) in C:\Program Files\Apache Software Foundation\Apache2.2\htdocs\test\includes\common.inc on line 309.
    * warning: call_user_func_array() [function.call-user-func-array]: First argument is expected to be a valid callback, 'node_type_form' was given in C:\Program Files\Apache Software Foundation\Apache2.2\htdocs\test\includes\form.inc on line 217.

Everything within the heredoc tags is a straight export from a working CCK type. Can anyone explain what I could be missing that is causing the error?

Thanks.

I'm liking it!

Benjamin Melançon's picture
Benjamin Melançon - Wed, 2007-07-25 00:35

As I posted over at 2bits:

How big a step [might this be] toward inline creation of nodes (say I'm creating a user profile node and I can create my organization profile node at the same time if it doesn't exist already)?

And it seems to get Drupal closer to my other dream: registering (or logging into) a site simultaneous with posting content for the first time...

Now I've put in my four cents twice, but I can't be alone in wanting more, more, more ;-)

~ ben melançon

member, Agaric Design Collective
http://AgaricDesign.com - "Open Source Web Development"


lol - you proved us wrong

dmitrig01's picture
dmitrig01 - Thu, 2007-07-26 03:54

Today we had a meetup and we were talking about CCK. Apparently we couldn't do this... now we can ;)... yay!


Uninstall hook

colan's picture
colan - Tue, 2008-04-15 21:02

Anyone know of a good example of how these content types can be removed in a module's uninstall hook?


Is this possible outside an

lefnire's picture
lefnire - Tue, 2008-05-13 07:13

Is this possible outside an install script? When I run the function from my module's functions, the content types don't get created.. but it works fine in an install script.

Reason I want to do it in a module is to create a content-type that represents the fdf fields of an uploaded PDF, so it's done on a regular basis not just on install.


programmatically create CCK fields

davidwhthomas - Mon, 2008-06-16 00:01

This function does it for me:

<?php
/**
* Programmatically create CCK fields and types using the content copy module
* @param $type string
* content type to create, defaults to new type, if type exists, only fields will be added
* @param $macro array
* exported array from content types -> export. If file is not specified, macro will be used
* @param $file string
* path to file containing content copy exported macro data structure. no escaping needed.
*/
function base_create_content($type = '<create>', $macro = '', $file = '') {
  if(!
module_exists("content_copy")){
   
drupal_set_message('Programmatically creating CCK fields requires the Content Copy module. Exiting.');
    return;
  }
 
$values = array();
 
$values['type_name'] = $type;
 
//get macro import data, prefer file first
 
if($file){
    if(
file_exists($file)){
     
$values['macro'] = file_get_contents($file);
    }else{
     
drupal_set_message('Unable to read input file for import. Exiting.');
      return;
    }
  }elseif(
$macro){
   
$values['macro'] = $macro;
  }
 
//include required files
 
include_once './'. drupal_get_path('module', 'node') .'/content_types.inc';
  include_once(
'./'. drupal_get_path('module', 'content') .'/content_admin.inc');
 
//import content by executing content copy import form and passing macro
 
drupal_execute("content_copy_import_form", $values);
}

?>

I then call it like:

<?php
 
//use absolute path to include file
 
base_create_content($type = 'my_type', $macro = '', $file = realpath('.') . '/sites/all/modules/my_module/cck/fields.inc');
?>

where fields.inc contains the exported macro and my_module defines the my_type type in hook_node_info. You could also make 'my_type' into '<create>' and create a new type.

hope that helps.

It's a good feature :-)
DT

can any one help me to do this in D6

kuldip's picture
kuldip - Fri, 2008-06-27 11:03

Hello Experts..

I am developing installer profile in Drupal 6 , i want to import my CCK's from the text file

i have tried this code but its not working for me,

<?php
 
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'] = implode("\n", file(dirname(__file__)."/cck_import.txt"));
 
drupal_execute("content_copy_import_form", $values);
?>

so please help me....