Creating a CCK Field module with an "invisible" widget
Most CCK Field modules have a widget where the user adds information upon creating a node which is then saved with the node. For the recent Etherpad module I wrote, I needed an "invisible" widget which saved with each new node some information from the field definition as well as autogenerated information. As I didn't any documentation on how to do this, I thought I'd document it here quickly.
The first thing you do is define your database columns for your field in hook_field_settings.
<span style="color: #000000"><span style="color: #0000BB"><?php<br></span><span style="color: #007700">function </span><span style="color: #0000BB">etherpad_field_settings</span><span style="color: #007700">(</span><span style="color: #0000BB">$op</span><span style="color: #007700">, </span><span style="color: #0000BB">$field</span><span style="color: #007700">) {<br> switch (</span><span style="color: #0000BB">$op</span><span style="color: #007700">) {<br> </span><span style="color: #FF8000">// Code removed.<br> </span><span style="color: #007700">case </span><span style="color: #DD0000">'database columns'</span><span style="color: #007700">:<br> return array(<br> </span><span style="color: #DD0000">'etherpad_url' </span><span style="color: #007700">=> array(</span><span style="color: #DD0000">'type' </span><span style="color: #007700">=> </span><span style="color: #DD0000">'varchar'</span><span style="color: #007700">, </span><span style="color: #DD0000">'length' </span><span style="color: #007700">=> </span><span style="color: #0000BB">1024</span><span style="color: #007700">, </span><span style="color: #DD0000">'not null' </span><span style="color: #007700">=> </span><span style="color: #0000BB">FALSE</span><span style="color: #007700">,),<br> </span><span style="color: #DD0000">'etherpad_text' </span><span style="color: #007700">=> array(</span><span style="color: #DD0000">'type' </span><span style="color: #007700">=> </span><span style="color: #DD0000">'text'</span><span style="color: #007700">, </span><span style="color: #DD0000">'not null' </span><span style="color: #007700">=> </span><span style="color: #0000BB">TRUE</span><span style="color: #007700">, </span><span style="color: #DD0000">'size' </span><span style="color: #007700">=> </span><span style="color: #DD0000">'big'</span><span style="color: #007700">),<br> </span><span style="color: #DD0000">'attributes' </span><span style="color: #007700">=> array(</span><span style="color: #DD0000">'type' </span><span style="color: #007700">=> </span><span style="color: #DD0000">'text'</span><span style="color: #007700">, </span><span style="color: #DD0000">'size' </span><span style="color: #007700">=> </span><span style="color: #DD0000">'medium'</span><span style="color: #007700">, </span><span style="color: #DD0000">'not null' </span><span style="color: #007700">=> </span><span style="color: #0000BB">FALSE</span><span style="color: #007700">),<br> );<br> }<br>}<br></span><span style="color: #0000BB">?></span></span>
Next, you define your widget form inside hook_widget. Some tutorials I saw suggest you define your widget form in hook_elements/hook_process. I did that at first but decided against it as a) I never got it to work and b) it just adds needless complexity. Generally you'll just want to define your widget in hook_widgets.
Two really important things here to get your "invisible" widget to work correctly. First, you must name your form keys the same as you named your database column names in hook_field_settings. This tripped me up for a long time. CCK saves data by magic (you never explicitly save anything from a widget) and this is the key to getting the incantation to take. Second, using the "value" field type was the key to creating an "invisible" field and getting my data saved correctly.
<span style="color: #000000"><span style="color: #0000BB"><?php<br></span><span style="color: #FF8000">/**<br> * Implementation of hook_widget().<br> */<br></span><span style="color: #007700">function </span><span style="color: #0000BB">etherpad_widget</span><span style="color: #007700">(&</span><span style="color: #0000BB">$form</span><span style="color: #007700">, &</span><span style="color: #0000BB">$form_state</span><span style="color: #007700">, </span><span style="color: #0000BB">$field</span><span style="color: #007700">, </span><span style="color: #0000BB">$items</span><span style="color: #007700">, </span><span style="color: #0000BB">$delta </span><span style="color: #007700">= </span><span style="color: #0000BB">0</span><span style="color: #007700">) {<br> </span><span style="color: #0000BB">$element</span><span style="color: #007700">[</span><span style="color: #DD0000">'etherpad_url'</span><span style="color: #007700">] = array(<br> </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=> </span><span style="color: #DD0000">'value'</span><span style="color: #007700">,<br> </span><span style="color: #DD0000">'#value' </span><span style="color: #007700">=> (isset(</span><span style="color: #0000BB">$items</span><span style="color: #007700">[</span><span style="color: #0000BB">$delta</span><span style="color: #007700">][</span><span style="color: #DD0000">'etherpad_url'</span><span style="color: #007700">]) && !empty(</span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'nid'</span><span style="color: #007700">][</span><span style="color: #DD0000">'#value'</span><span style="color: #007700">])) ? </span><span style="color: #0000BB">$items</span><span style="color: #007700">[</span><span style="color: #0000BB">$delta</span><span style="color: #007700">][</span><span style="color: #DD0000">'etherpad_url'</span><span style="color: #007700">] : </span><span style="color: #0000BB">$field</span><span style="color: #007700">[</span><span style="color: #DD0000">'etherpad_url'</span><span style="color: #007700">] . </span><span style="color: #0000BB">etherpad_generate_padid</span><span style="color: #007700">(</span><span style="color: #0000BB">$field</span><span style="color: #007700">[</span><span style="color: #DD0000">'etherpad_url'</span><span style="color: #007700">]),<br> );<br> </span><span style="color: #0000BB">$element</span><span style="color: #007700">[</span><span style="color: #DD0000">'etherpad_text'</span><span style="color: #007700">] = array(<br> </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=> </span><span style="color: #DD0000">'value'</span><span style="color: #007700">,<br> </span><span style="color: #DD0000">'#value' </span><span style="color: #007700">=> (isset(</span><span style="color: #0000BB">$items</span><span style="color: #007700">[</span><span style="color: #0000BB">$delta</span><span style="color: #007700">][</span><span style="color: #DD0000">'etherpad_text'</span><span style="color: #007700">]) && !empty(</span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'nid'</span><span style="color: #007700">][</span><span style="color: #DD0000">'#value'</span><span style="color: #007700">])) ? </span><span style="color: #0000BB">$items</span><span style="color: #007700">[</span><span style="color: #0000BB">$delta</span><span style="color: #007700">][</span><span style="color: #DD0000">'etherpad_text'</span><span style="color: #007700">] : </span><span style="color: #DD0000">"default value for now until we have a function to generate one"</span><span style="color: #007700">,<br> );<br> </span><span style="color: #0000BB">$element</span><span style="color: #007700">[</span><span style="color: #DD0000">'attributes'</span><span style="color: #007700">] = array(<br> </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=> </span><span style="color: #DD0000">'value'</span><span style="color: #007700">,<br> </span><span style="color: #DD0000">'#value' </span><span style="color: #007700">=> (isset(</span><span style="color: #0000BB">$items</span><span style="color: #007700">[</span><span style="color: #0000BB">$delta</span><span style="color: #007700">][</span><span style="color: #DD0000">'attributes'</span><span style="color: #007700">]) && !empty(</span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'nid'</span><span style="color: #007700">][</span><span style="color: #DD0000">'#value'</span><span style="color: #007700">])) ? </span><span style="color: #0000BB">$items</span><span style="color: #007700">[</span><span style="color: #0000BB">$delta</span><span style="color: #007700">][</span><span style="color: #DD0000">'attributes'</span><span style="color: #007700">] : </span><span style="color: #0000BB">serialize</span><span style="color: #007700">(</span><span style="color: #0000BB">$field</span><span style="color: #007700">[</span><span style="color: #DD0000">'attributes'</span><span style="color: #007700">]),<br> );<br> </span><span style="color: #FF8000">// Used so that hook_field('validate') knows where to <br> // flag an error in deeply nested forms.<br> </span><span style="color: #007700">if (empty(</span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'#parents'</span><span style="color: #007700">])) {<br> </span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'#parents'</span><span style="color: #007700">] = array();<br> }<br> </span><span style="color: #0000BB">$element</span><span style="color: #007700">[</span><span style="color: #DD0000">'_error_element'</span><span style="color: #007700">] = array(<br> </span><span style="color: #DD0000">'#type' </span><span style="color: #007700">=> </span><span style="color: #DD0000">'value'</span><span style="color: #007700">,<br> </span><span style="color: #DD0000">'#value' </span><span style="color: #007700">=> </span><span style="color: #0000BB">implode</span><span style="color: #007700">(</span><span style="color: #DD0000">']['</span><span style="color: #007700">, </span><span style="color: #0000BB">array_merge</span><span style="color: #007700">(</span><span style="color: #0000BB">$form</span><span style="color: #007700">[</span><span style="color: #DD0000">'#parents'</span><span style="color: #007700">], array(</span><span style="color: #DD0000">'value'</span><span style="color: #007700">))),<br> );<br><br> return </span><span style="color: #0000BB">$element</span><span style="color: #007700">;<br>}<br></span><span style="color: #0000BB">?></span></span>
And that's it! Read this and you'll save yourself hours of frustration :)
One other note, the Devel module's "reinstall module" function is very useful as you'll be reinstalling the module often to reset the database w/ your changes. Enable the Devel block to access it.
This documentation doesn't cover most of what you'll need to know to write a CCK module. I relied heavily on the following tutorials.