text:
Creating a Compound Field Module for CCK in Drupal 6.x
When setting up a site using the Drupal content management system, you'll often find that you need to define content types that have fields attached to them, beyond the default Title and Body. In Drupal version 7.x and later, this field functionality is planned to be in the core distribution of Drupal, but in 6.x and previous versions of Drupal, it is provided by the contributed Content Construction Kit (CCK) module and the many associated modules that provide additional field types for CCK.
For some sites, you may find that you need to define fields that have multiple values; for example, you might want to place several images along the right side of a page (each one being an Image field). That is no problem, in itself -- CCK allows you to set any field to accept multiple values, and in version 2 of CCK, you can easily re-order, delete, and add new items to the list using a very nice AJAX interface.
But what if you need to associate, for instance, a caption and a taxonomy term with each of those images? Or in other words, what if you want to add fields to your content type as a group? With the current CCK version, you have several choices of how to do that:
- Several separate fields: Create a multiple-valued field for the image, another for the caption, and another for the taxonomy term, and tell the people editing content on the site to try to keep them synchronized. This is not really workable in general -- users would need to scroll up and down the content entry screen to do their data entry, and inevitably someone will make a mistake and you'll have a caption or term associated with the wrong image. Also, for this specific case, Content Taxonomy as a multiple-valued field just gives you one multi-select list, so there is no way to choose a given taxonomy term more than once, or indicate the order (term 1 goes with image 1, etc.).
- Content sub-type: Create a second content type "Image Caption Term" to hold an image and its associated data. Then add a multiple "Node Reference" field to your original content type, which will associate your page with its images. This can be made to work, but in practice the editing is at best clumsy. (The Modal Nodereference module may help in making the editing less clumsy.) Also, you'll end up with a lot of these little "Image Caption Term" items cluttering up your content management screens, which will confuse novice users.
- Content Multigroup module: Try out an experimental "Content Multigroup" module that is supposed to let you group multiple CCK fields together. As of this writing (April 2010), this module is not stable (not even released as an "alpha" version), and last time I tried it (October 2008), it didn't work at all with image fields. It may have improved since then.
- Flexifield Module: This may be working better now than the last time I tested it (October 2008).
- Custom CCK field: Create your own custom CCK field module that contains the desired grouping of information. Doing this is not very difficult, works very well, and is the subject of this article.
With that motivation, in this article I describe how to create a module that implements a custom "compound" or "grouped" CCK field for Drupal 6.x and CCK 2.x. The article shows two examples, both of which allow you to add the field to a CCK content type as a multiple-valued field, which will allow a content editor to add, edit, delete, and re-order all this information as a group. The two examples in this article (both of which can be downloaded below):
- A compound image, caption, and taxonomy field: Allows a user to upload an image, provide "alt" and "title" text for the image, enter a caption (arbitrary HTML text), and choose one or more taxonomy terms. You can click the image to the left to see what it looks like in action.
- A simpler person field, with fields for displayed name, job title, phone number, and email address, which you could use if you didn't want to have full nodes for each "person". It also provides an illustration of how to make a compound field that contains just simple text fields.
If you need the exact functionality of one of the two examples here, you can just use the modules I created (download them below) -- they should work. If you need a different set of fields, you should be able to follow the steps in this article to create your own compound CCK field. I'd also recommend checking out the Link module as an example.
I also recently had to create a compound CCK field that includes a node reference component, so there a couple of notes about what to do for that below as well.
If creating your own Drupal module is beyond you, you can also hire Poplar ProductivityWare to create a module for your Drupal site -- click here to contact Poplar ProductivityWare.
Here's what this article contains:
- Introduction and Motivation (above)
- Getting Started
- Defining the Field
- Defining the Widget
- Defining the Formatter and Theming
- Final Details
- Downloads, Background, and References
To understand this article, you will need a basic understanding of PHP, Drupal and its terminology, Drupal module development (hooks, Forms API, and basic module file structure), and the CCK and Views modules (i.e. how to define content types and views -- you don't need to know all about the inner workings of the modules). Check the Downloads, Background, and References section below for more information, if you encounter terminology or concepts that you are unfamiliar with. The completed module is also provided as a download in that section of the article -- it is GPL licensed, so you can use it and modify it as you wish under the terms of the license. You might want to download it now so you can refer to it as you read this article. The actual module code has more comments, and a couple of functions that were omitted from the article.
And before going any further, I'd like to thank Josh Kopel of Kolaboration Studio, who sponsored part of the development of the initial image/caption/taxonomy field module and the original article.
Getting Started
The first step in creating our module is to choose some names:
- The first module we are creating implements a compound CCK field for an image, caption, and taxonomy term. So, I have chosen to call the CCK field "Image Caption Taxonomy", with machine-readable name "img_cap_tax". To distinguish the name of the CCK field from the name of the module, I have chosen to call the module "Image Caption Taxonomy Field", and to use the machine-readable name "img_cap_tax_fld" for the module; I'll refer to this machine-readable name as "(module)".
- The second module is for a person field, so the CCK field will be called "Person", with machine readable name "person". The module will be "Person Field", with machine readable name "person_fld".
Below, when you see "(field)", it refers to the machine-readable name of the field, and "(module)" refers to the machine-readable name of the module.
Next, we need to create files "(module).info" and "(module).module", in order for this to be a Drupal module. A couple of notes:
- Because this module implements a CCK field, we can rely on the CCK module to take care of all the database tables for us. For that reason, we don't need a .install file for this module to define the database schema.
- We do need to define some module dependencies in the .info file, as we will see below. Check the completed .info file in the module download for a list.
- It is possible to define multiple CCK fields in a single module. However, having done it once, I really wouldn't recommend it -- it makes the module file confusing, and less "modular" (i.e. someone might only need one of your fields, but have to load the entire module with several fields in order to get that functionality on their site).
Defining the Field
Now that we have our files created, the next step is to implement some core and CCK-specific hooks that tell CCK about our field. These hook implementations go into the (module).module file.
CCK hook_field_info()
First, we'll give CCK the field's machine-readable name, human-readable name, and a longer description, by implementing the CCK hook_field_info(). In the first example (the second example is nearly the same):
function img_cap_tax_fld_field_info() { return array( 'img_cap_tax' => array( 'label' => t('Image Caption Taxonomy'), 'description' => t('Stores an image file, text for alt and title tags, a caption, and a taxonomy term'), ) ); }
As you can see, the return value for hook_field_info() is an associative array, keyed on the machine-readable name of the CCK field we are defining. Each element of the returned array is an associative array, with components 'label' giving the human-readable name of our field, and 'description' giving a more verbose description. And since all human-readable text in Drupal modules should be internationalization-ready, we enclose it in the t() function.
Core hook_install(), hook_uninstall(), hook_enable(), hook_disable()
Next, we will implement the core Drupal hook_install(), hook_uninstall(), hook_enable(), and hook_disable() so that our field will be properly added and removed from Drupal and CCK. These all work the same way: we just tell Drupal to let CCK handle the process through its content_notify() function, which takes care of all of the details. For example, here is the function for the core Drupal hook_install():
function img_cap_tax_fld_install() { content_notify('install', 'img_cap_tax'); }
The other three hooks are exactly the same -- just substitute "uninstall", "enable", or "disable" for "install". Note that it is possible to put these functions into a module's (module).install file instead of (module).module, but since they are so small in this case (and we have nothing else to go in the (module).install file), I've chosen to leave them in (module).module.
One other detail: these functions are calling the CCK module function content_notify() directly. So, we need to make sure that function is loaded when we call it:
- Make our module dependent on the CCK module (whose machine-readable name is "content") -- that dependency goes into the .info file.
- Verify which file the content_notify() function is defined in -- if it had been in an include file rather than the main .module file, we would have needed to put an include directive in the hook_init() implementation in our module.
We'll need to do this for other module functions we'll be calling directly later in this article, so I'll just suggest that you look at the completed module's .info file (or the reference section below) to see the module dependencies, and the img_cap_tax_fld_init() function (implementation of hook_init() ) to see what include files we needed to load explicitly.
CCK hook_field_settings()
Now, we need to tell CCK about the "settings" for this field, using CCK hook_field_settings(). This is a sort of catch-all hook for CCK, with several "operations", each one letting our field module give a different piece of information to CCK. The example CCK field based on an image needs to behave very similarly to the Image field from the existing ImageField module. That field, in turn (in the current implementation of ImageField) is actually just a FileField file field, with a special "widget" and "formatter" (see sections below) that makes it show as an image rather than a file. So, for the first example field, we will borrow heavily from the FileField module in our field settings function. For the operations that need to do something different from FileField, we'll call into other functions, which are described in the next section. Here's the function:
function img_cap_tax_fld_field_settings( $op, $field ) { switch( $op ) { case 'form': return img_cap_tax_fld_field_settings_form( $field ); case 'save': return img_cap_tax_fld_field_settings_save( $field ); default: return filefield_field_settings( $op, $field ); } }
(Of course, we'll need to make sure that FileField is a dependency of this module, as described above.)
For the second example, which is built up of text fields, we need to do something slightly different, because we don't have the FileField module around to help us with settings. We don't need any settings for this field, but we do need to define the database storage:
function person_fld_field_settings($op, $field) { switch ($op) { case 'database columns': $columns['displayed_name'] = array('type' => 'varchar', 'length' => 255, 'not null' => FALSE, 'sortable' => TRUE, 'default' => ''); $columns['job_title'] = array('type' => 'varchar', 'length' => 255, 'not null' => FALSE, 'sortable' => TRUE, 'default' => ''); $columns['phone'] = array('type' => 'varchar', 'length' => 255, 'not null' => FALSE, 'sortable' => FALSE, 'default' => ''); $columns['email'] = array('type' => 'varchar', 'length' => 255, 'not null' => FALSE, 'sortable' => FALSE, 'default' => ''); return $columns; } }
Here's another example - this one has a node reference field and a text field, and was used to store a URL for each "Location" node reference on a "Doctor" content type for a recent project of mine. In this case, you want to let the node reference module take care of most of the operations, making sure that the database column for the node reference part of the field is set to 'nid' so that the node reference module can recognize it:
function doctor_fld_field_settings($op, $field) { switch ($op) { case 'database columns': $columns = array(); // From nodereference. $columns['nid'] = array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'index' => TRUE); // Plain text field to store the URL for this doctor. $columns['URL'] = array('type' => 'varchar', 'length' => 255, 'not null' => FALSE, 'sortable' => TRUE, 'default' => ''); return $columns; // Let nodereference.module handle most of the operations. case 'form': case 'save': case 'views data': return nodereference_field_settings($op, $field); } }
For another example of a text-based field, but which has a settings form, you can check out the Link module.
filefield_field_settings() Operations Functions (image example only)
The next step (for the image field example only) is to define the individual operations functions that are called from the field settings function in the previous section. By convention, in this module the functions are located in an include file, (module)_field.inc. Let's start with the "form" and "save" operations, which define the field settings form the user can use to customize the field (apart from the sections already provided by the FileField module), and which fields in the settings form should be saved to the database. There is also a "validate" operation, which lets you run checks on the submitted data, but we don't need anything beyond what FileField does for this, so I won't describe that here.
In our field settings form, we want to do start with what the FileField module does for its field, add in most of what the Content Taxonomy module does for its field, and also allow the user to choose between plain text and having an Input Format (filtered) for the caption field.
We'll also make a couple of changes to what Content Taxonomy does. First, Content Taxonomy has a setting that allows you to take the taxonomy choices from its field and apply them to the node as a whole. We won't allow for that option on our field, as it doesn't make sense in a compound field. Second, we also want to let the user choose whether the taxonomy term is optional, and whether it should be multiple-valued. We will also add some fieldsets to the form for grouping.
So, with all of that motivation, here are our "form" and "save" operations functions:
function img_cap_tax_fld_field_settings_form( $field ) { $form1 = filefield_field_settings( 'form', $field ); $form2 = content_taxonomy_field_settings( 'form', $field ); $form2['save_term_node']['#type'] = 'hidden'; $form2['taxonomy_group'] = array( '#type' => 'fieldset', '#title' => 'Taxonomy', '#collapsible' => 0, ); $form2['taxonomy_group']['vid'] = $form2['vid']; unset( $form2['vid'] ); $form2['taxonomy_group']['allow_multiple'] = array( '#type' => 'checkbox', '#title' => t('Allow multiple taxonomy terms'), '#default_value' => is_numeric($field['allow_multiple']) ? $field['allow_multiple'] : 0, '#description' => t('If this option is checked, the user can select multiple taxonomy terms for each image; otherwise, at most one.'), ); $form2['taxonomy_group']['required_term'] = array( '#type' => 'checkbox', '#title' => t('Taxonomy required'), '#default_value' => is_numeric($field['required_term']) ? $field['required_term'] : 0, '#description' => t('If this option is checked, the user must select at least one taxonomy term for each image; otherwise, it is optional.'), ); $form2['hierarchical_vocabulary']['#weight'] = 100; $form3 = array( 'text_processing' => array( '#type' => 'radios', '#title' => t('Text processing for Caption'), '#default_value' => is_numeric($field['text_processing']) ? $field['text_processing'] : 0, '#options' => array( 0 => t('Plain text'), 1 => t('Filtered text (user selects input format)')), )); return $form1 + $form3 + $form2; } function img_cap_tax_fld_field_settings_save( $field ) { $flds1 = filefield_field_settings( 'save', $field ); $flds2 = content_taxonomy_field_settings( 'save', $field ); $flds2[] = 'allow_multiple'; $flds2[] = 'required_term'; $flds3 = array( 'text_processing' ); return array_merge( $flds1, $flds2, $flds3 ); }
There is also a hook_field_settings() operation for "database columns", where you can define the database columns that CCK will use to store your field's data (you can see this in the second example above). For the image-based field, however, the FileField module creates a serialized "data" field in the database for us to use. We'll put all of the alt, title, caption, and taxonomy information into the "data" array, so we don't have to add any additional database columns.
Finally, there is a hook_field_settings() operation for "views data", which allows a CCK field to return information about how it can be used for sorting and filtering in the Views module. (Any CCK field can automatically be included as a Field in Views, so we don't have to worry about that.) Keeping in mind that our field is mainly useful if it is added to a content type as a multiple-valued field (otherwise, you wouldn't really need a compound field), probably sorting the nodes in a view based on some aspect of this field doesn't really make sense. However, I can envision filtering a view using this field in a couple of ways:
- Whether or not the content has at least one image attached to it. This should come from the FileField module.
- Whether a particular taxonomy term is present or not.
As of this writing, this hasn't been implemented. Sorry!
CCK hook_field()
The next set of things we need to define is included in CCK hook_field(), which basically tells CCK if it needs to do anything special when a the field is loaded from or saved to the database. For the image field example, there is a special action: move the image file from its temporary location to a permanent location, and make note of that location in the database field (the FileField module has a function that does this). Also, all text fields need to be "sanitized", for both field examples, according to the input format (i.e. make sure it contains only the allowed HTML tags, or no text if the input format is plain text). And the email address in the person field example needs to be validated. All of these are operstaions in hook_field().
For the first (image) field example, here's the hook_field() implementation, which calls a function we'll define below for the "sanitize" operation, and lets FileField handle the rest:
function img_cap_tax_fld_field($op, $node, $field, &$items, $teaser, $page) { if( $op == 'sanitize' ) { img_cap_tax_fld_field_sanitize( $node, $field, $items, $teaser, $page ); } return filefield_field( $op, $node, $field, $items, $teaser, $page ); }
In our "sanitize" operation function, we will do pretty much what the Text field module in CCK does to sanitize text fields: figure out which input format was chosen, filter using that input format, and save the result with a "safe" prefix so it can be used for theming. Here is the function (I've put it into the (module)_field.inc file):
function img_cap_tax_fld_field_sanitize($node, $field, &$items, $teaser, $page) { $isplain = empty( $field['text_processing'] ); $check_access = is_null( $node ) || ( isset($node->build_mode) && $node->build_mode == NODE_BUILD_PREVIEW ); foreach( $items as $delta => $item ) { $dat = $item['data']; if( !is_array( $dat )) { $dat = unserialize( $dat ); } $text = isset( $dat['caption'] ) ? $dat['caption'] : ''; if( $isplain ) { $text = check_plain( $text ); } else { $text = check_markup( $text, $item['format'], $check_access ); } $items[$delta]['safe_caption'] = $text; } }
One detail to note here is that I found that the "data" array from FileField is sometimes coming into this function in a serialized format (I'm not sure exactly why). So this function checks to see whether it needs to be unserialized before making use of it.
In the second example, as we mentioned, we need to support sanitation and validation. The validation checks to see whether the email address is in a valid format, and the sanitation ensures that only plain text has been entered for the other field components. Here's the function:
function person_fld_field($op, &$node, $field, &$items, $teaser, $page) { switch ($op) { case 'validate': if (is_array($items)) { foreach ($items as $delta => $item) { if ($item['email'] != '' && !valid_email_address(trim($item['email']))) { form_set_error($field['field_name'],t('"%mail" is not a valid email address',array('%mail' => $item['email']))); } } } break; case 'sanitize': foreach ($items as $delta => $item) { foreach ( $item as $col => $dat ) { $items[$delta]['safe_' . $col ] = check_plain($item[ $col ]); } } break; } }
The node reference example is pretty similar to the plain text field example.
CCK hook_content_is_empty(), hook_default_value()
There are a few more pieces of information CCK needs about our fields: how to tell if it is "empty" of information (CCK hook_content_is_empty() ), and what the default value should be (CCK hook_default_value() ). In the first example, since the primary component of our field is an image, we will say that the field is considered "empty" if it has no image file, and we'll let FileField handle that:
function img_cap_tax_fld_content_is_empty( $item, $field ) { return filefield_content_is_empty( $item, $field ); }
In the second example, the person field is considered empty if there is no name entered (the node reference field can work in a similar manner):
function person_fld_content_is_empty($item, $field) { if (empty($item['displayed_name'])) { return TRUE; } return FALSE; }
In our image/caption field we don't have a default value, actually (it isn't too common), but just in case FileField implements a default value setting in the future, we'll let FileField define the default value:
function img_cap_tax_fld_default_value(&$form, &$form_state, $field, $delta) { return filefield_default_value($form, $form_state, $field, $delta); }
Our person field doesn't have a default value either. If you want a default value for your field, you'll probably need to set something up in your settings form so that your users can define what the default value is, and then use the default value hook to set it up. I don't know of a good example for this.
Defining the Widget
Now that we have the CCK field itself defined, our next task is to define a "widget", which is the CCK name for a form used to edit the field.
For the first example, our widget will need to provide a way to upload the image file and preview/change the uploaded file; enter the alt, title, and caption text; choose an input format for the caption; and choose a taxonomy term (we'll use a drop-down select list for that). As before, we'll let FileField and Content Taxonomy handle their parts, so all we'll need to do is add the text fields and image format. We'll create a widget called "Image, Caption, Taxonomy Select", with machine-readable name "img_cap_tax_sel_widget", referred to below as "(widget)".
For the second example, our person widget will just need to let us enter text for the name, position, phone, and email address. We'll define a widget with a human-readable label of "Text fields", and machine-readable name "person_entry", referred to below as "(widget)".
I will note here that it is possible for a CCK field module to define more than one widget for a single field. For instance, the Content Taxonomy module has several options: a drop-down select list, a type-ahead auto-complete text field, etc. There are notes below on what you'd need to change to add additional widgets.
CCK hook_widget_info()
The first step in providing a widget is to give CCK the basic widget information (machine-readable name, human-readable name, what field types it applies to, etc.), by implementing the CCK hook_widget_info():
function img_cap_tax_fld_widget_info() { return array( 'img_cap_tax_sel_widget' => array( 'label' => t('Image, Caption, Taxonomy Select'), 'field types' => array('img_cap_tax'), 'multiple values' => CONTENT_HANDLE_CORE, 'callbacks' => array('default value' => CONTENT_CALLBACK_CUSTOM), 'description' => t('An edit widget for Image Caption Taxonomy fields that allows upload/preview of the image, and chooses taxonomy terms from a drop-down select list.' ), ), ); }
As is common in Drupal, the return value of this hook is an associative array of associative arrays, and if we wanted to provide multiple widgets in our module, we would just need to add additional elements to the outer array.
The person example is similar:
function person_fld_widget_info() { return array( 'person_entry' => array( 'label' => t('Text fields'), 'field types' => array('person'), 'multiple values' => CONTENT_HANDLE_CORE, 'callbacks' => array( 'default value' => CONTENT_CALLBACK_DEFAULT, ), ), ); }
(The nodereference field doesn't do anything particularly special here either.)
FAPI hook_elements()
Next, we need to define the widget's data entry form and how to process it. This is done using the core Forms API hook_elements(), which returns an associative array with one element (or more, if you have multiple widgets) whose key is the machine-readable name of our widget, and whose value is an associative array that either defines the form completely (as is done in the Content Taxonomy module), or gives a "process" callback function and some other information (as is done in ImageField; this is also the method recommended, in general, for the Forms API). We'll use the process callback method, and in the first example, let ImageField and FileField set up most of the array:
function img_cap_tax_fld_elements() { $imgel = imagefield_elements(); $elements = array( 'img_cap_tax_sel_widget' => $imgel[ 'imagefield_widget' ]); $elements['img_cap_tax_sel_widget']['#process'][] = 'img_cap_tax_fld_widget_process'; $elements['img_cap_tax_sel_widget']['#element_validate']= array('img_cap_tax_fld_widget_validate'); return $elements; }
Here's the second example, which is even simpler:
function person_fld_elements() { $elements = array( 'person_entry' => array( '#input' => TRUE, '#process' => array( 'person_fld_person_entry_process' ), ), ); return $elements; }
The node reference field works like this one.
We will need to define the "process" callback functions for both examples, which is what actually defines the form elements -- see section below. Also, for the image example we have a custom validate callback, because the one used by FileField (and ImageField) does not work for our module -- see section below. One other detail is that we'll also need to register a themeable element for this widget form; we'll cover that in the "Formatter and Theming" section below with the other theme information, and show the theming functions below.
Process and Validate Callbacks for Widget Form
The "process" callbacks from the form array of the previous section are what actually define and return the form elements (a Forms API array). In the first example, we've left ImageField to take care of its processing, and added our processing function to the end; we'll put the function in file (module)_widget.inc. So, our function needs to add a text area and input format selector for the "caption" field and a drop-down list for the Taxonomy term. All of this information will get stored in FileField's "data" array, and we'll have to be careful to choose the same array keys as the Content Taxonomy module for the taxonomy fields. For the caption's input format, we want to choose "format" as the array key, because this is the standard field name for input formats. (Some other modules depend on that convention; for example, the WYSIWYG editor module chooses which fields to attach editors to by looking for textarea fields that are followed by "format" fields.)
function img_cap_tax_sel_widget_process($element, $edit, &$form_state, $form) { $defaults = $element['#value']['data']; if( !is_array( $defaults )) { $defaults = unserialize( $defaults ); } $field = content_fields($element['#field_name'], $element['#type_name']); $element['data']['caption'] = array( '#title' => t( 'Caption' ), '#type' => 'textarea', '#rows' => $field['widget']['rows'], '#cols' => $field['widget']['cols'], '#default_value' => $defaults['caption'], '#weight' => 4, ); if (!empty($field['text_processing'])) { $filt = isset( $defaults['format'] ) ? $defaults['format'] : FILTER_FORMAT_DEFAULT; $par = $element['#parents']; $par[] = 'data'; $par[] = 'format'; $element['data']['format'] = filter_form( $filt, 1, $par ); $element['data']['format']['#weight'] = 5; } $mult = $field['allow_multiple']; $req = $field['required_term']; $opts = content_taxonomy_allowed_values( $field ); if( !$req && !$mult ) { $none = theme( 'content_taxonomy_options_widgets_none', $field ); $opts = array( '' => $none ) + $opts; } $element['data']['value'] = array( '#title' => t( 'Taxonomy Terms' ), '#type' => 'select', '#default_value' => $defaults['value'], '#options' => $opts, '#weight' => 6, ); if( $mult ) { $element['data']['value']['#multiple'] = TRUE; } return $element; }
Note that the rows and columns settings for the caption, as well the settings for whether the taxonomy term is a single or multiple select and required or not, come from the widget settings form -- see CCK hook_widget_settings() below.
In the second example, we just need to define the text fields for entering person information:
function person_fld_person_entry_process($element, $edit, &$form_state, $form) { $defaults = $element['#value']; $field = content_fields($element['#field_name'], $element['#type_name']); $element['displayed_name'] = array( '#title' => t( 'Name' ), '#type' => 'textfield', '#default_value' => $defaults['displayed_name'], '#weight' => 2, ); $element['job_title'] = array( '#title' => t( 'Job Title' ), '#type' => 'textfield', '#default_value' => $defaults['job_title'], '#weight' => 3, ); $element['phone'] = array( '#title' => t( 'Phone' ), '#type' => 'textfield', '#default_value' => $defaults['phone'], '#weight' => 4, ); $element['email'] = array( '#title' => t( 'Email' ), '#type' => 'textfield', '#default_value' => $defaults['email'], '#weight' => 5, ); return $element; }
The node reference example actually gave me a little bit of trouble with the widget process function. I tried to follow what the node reference module does it its widget process function, and it didn't work right. So I ended up just building a select field for the node reference myself. Here's the function I used that actually works:
function doctor_fld_doctor_entry_process($element, $edit, &$form_state, $form) { $defaults = $element['#value']; $field = content_fields($element['#field_name'], $element['#type_name']); $options = nodereference_allowed_values($field); $options = array('' => t('- None -')) + $options; $element['nid'] = array( '#title' => t('Location'), '#type' => 'select', '#default_value' => $defaults['nid'], '#options' => $newoptions, ); $element['URL'] = array( '#title' => t('URL'), '#type' => 'textfield', '#default_value' => $defaults['URL'], ); return $element; }
The first example module also needs a validation callback, which will verify information when the node edit form is submitted. In FileField, the validation checks to make sure the file exists, and that if the field can only reference a file if it has been uploaded via this field module (so that the field will not be deleted erroneously if the node is deleted). Unfortunately, the existing FileField validate function (filefield_widget_validate() in filefield_widget.inc) will not work as-is for our module, because it tacitly assumes that the field type name is 'filefield', which is not the case for our field. So, we have to create our own function to replicate it. Assuming that the person using this field is uploading their own images and not doing anything crazy, all we really need to do is make sure the file exists. Here's the function:
function img_cap_tax_fld_widget_validate(&$element, &$form_state) { if (empty($element['fid']['#value'])) { return; } $field = content_fields($element['#field_name'], $element['#type_name']); $ftitle = $field['widget']['label']; if ( !( $file = field_file_load($element['fid']['#value']))) { form_error($element, t('The file referenced by the %field field does not exist.', array('%field' => $ftitle ))); } }
Widget theme function
In order to display the editing widget, we need to define a theme function, theme_(widget), which is called as an envelope for each item when it is added to the node editing form. This function is simple for both cases: we just tell Drupal to render the form, and our processing functions will take care of the rest. Here are the functions for the two examples:
function theme_img_cap_tax_sel_widget(&$element) { return theme('form_element', $element, $element['#children']); } function theme_person_entry($element) { return $element['#children']; }
The node reference example is just like the plain-text example here.
Note that there appears to be a rather annoying choice in the FileField CSS file that makes the editing widget get very narrow. You may want to add this to your theme's CSS file:
.filefield-element .widget-edit, .filefield-element .widget-preview { float: none; }
CCK hook_widget()
The next hook we need to implement in order to define the editing widget is CCK hook_widget(). This hook will be called each time one of our fields is added to the form, with the $delta parameter set to the multiple-value index (0 for the first item, 1 for the second, etc.). The return value is a Forms API array that should set up default values for the form and define callbacks. In the first example, as usual, we'll let FileField and ImageField handle most of the details (Content Taxonomy doesn't need to do anything special), by calling the filefield_widget() function. That function assumes that:
- Our module contains a file called (module)_widget.inc (the filefield_widget() function will load it).
- The $items['delta'] array has been set up with an array of the default values for the text fields in our compound field, before filefield_widget() is called.
So, our hook_widget() implementation is:
function img_cap_tax_fld_widget(&$form, &$form_state, $field, $items, $delta = 0) { if (empty($items[$delta])) { $items[$delta] = array('alt' => '', 'title' => '', 'caption' => '', 'value' => 0); } $element = filefield_widget($form, $form_state, $field, $items, $delta); $element['#upload_validators'] += imagefield_widget_upload_validators($field); return $element; }
The person field example's hook_widget() is even simpler, since all it has to do is tell CCK to load the field type we've already defined:
function person_fld_widget(&$form, &$form_state, $field, $items, $delta = 0) { $element = array( '#type' => $field['widget']['type'], '#default_value' => isset($items[$delta]) ? $items[$delta] : '', ); return $element; }
The node reference field works just like this one.
If your module contains more than one widget, you'll want to do something like this in your hook_widget() implementation:
switch( $field['widget']['type'] ) { case 'first_widget_machine_name': (code for this widget) break; case 'second_widget_machine_name': (code for this widget) break; }
CCK hook_widget_settings() (image field only)
The final piece in defining the widget is to create a widget settings form, which is done by implementing CCK hook_widget_settings(). Like many of the other CCK hooks, hook_widget_settings() has several operations: one to create a settings form, one to validate the form, and one to save the form. Our second example has no settings for the widget, so we don't need to implement this hook. For the first example, we'll let ImageField take care of validation, and build our own functions for the form and save operations:
function img_cap_tax_fld_widget_settings( $op, $widget ) { switch ($op) { case 'form': return img_cap_tax_fld_widget_settings_form($widget); case 'validate': return imagefield_widget_settings_validate($widget); case 'save': return img_cap_tax_fld_widget_settings_save($widget); } }
Note that if you have multiple widgets in your module, you can do a switch on $widget['type'] to handle the different widgets in your hook_widget_settings() implementation.
filefield_widget_settings() callbacks (image field only)
There are two operation callbacks defined in our hook_widget_settings() implementation for the image field example. The 'form' operation returns the widget settings form; the 'save' operation returns a list of which form data should be saved to the database.
First, let's work on the settings form. The existing FileField and ImageField modules have a widget settings form that lets the user set a file path, allowed file extensions, maximum file size, and other settings. We'll use that form, but modify it so that alt and title can always be customized rather than leaving those as options as they are in ImageField (we still want the settings to be there, so that other ImageField functions we call will have the right values set). The Content Taxonomy select list widget has settings for indentation and grouping terms, which we'll also want. Finally, most multi-line text fields let the user choose how many rows and/or columns to display, so we'll want that setting for the caption field. Putting this all together:
function img_cap_tax_fld_widget_settings_form( $widget ) { $form = imagefield_widget_settings_form( $widget ); $form['custom_alt'] = $form['alt_settings']['custom_alt']; $form['custom_alt']['#type'] = 'hidden'; $form['custom_alt']['#value'] = 1; $form['alt'] = $form['alt_settings']['alt']; $form['alt']['#type'] = 'hidden'; $form['alt']['#value'] = ''; unset( $form['alt']['#suffix'] ); unset( $form['alt_settings'] ); $form['custom_title'] = $form['title_settings']['custom_title']; $form['custom_title']['#type'] = 'hidden'; $form['custom_title']['#value'] = 1; $form['title'] = $form['title_settings']['title']; $form['title']['#type'] = 'hidden'; $form['title']['#value'] = ''; unset( $form['title']['#suffix'] ); unset( $form['title_settings'] ); $rows = (isset($widget['rows']) && is_numeric($widget['rows'])) ? $widget['rows'] : 5; $form['rows'] = array( '#type' => 'textfield', '#title' => t('Number of rows in caption field'), '#default_value' => $rows, '#element_validate' => array('_text_widget_settings_row_validate'), '#required' => TRUE, '#weight' => 8, ); $cols = (isset($widget['cols']) && is_numeric($widget['cols'])) ? $widget['cols'] : 40; $form['cols'] = array( '#type' => 'textfield', '#title' => t('Number of columns in caption field'), '#default_value' => $cols, '#element_validate' => array('_text_widget_settings_row_validate'), '#required' => TRUE, '#weight' => 9, ); $form2 = content_taxonomy_options_widget_settings( 'form', $widget ); $form2['settings']['#title'] = t( 'Settings for Taxonomy' ); $form = $form + $form2; return $form; }
We don't need to do anything special for the 'validate' operation, as neither ImageField nor Content Taxonomy needs us to do anything beyond what FileField does. So, we won't define this function. The 'save' operation returns a list of fields to save from the settings form:
function img_cap_tax_fld_widget_settings_save( $widget ) { $arr = imagefield_widget_settings_save( $widget ); $arr[] = 'rows'; $arr[] = 'cols'; $arr2 = content_taxonomy_options_widget_settings( 'save', $widget ); $arr2[] = 'allow_multiple'; $arr2[] = 'required_term'; return array_merge( $arr, $arr2 ); }
Defining the Formatter and Theming
Having now defined our field and the editing widget, the final thing we need to do is to define how it will look to someone who is visiting the site (of course, the theme can override this). We do this by defining a "formatter" for the field; like widgets, there can be more than one formatter defined for a field, but we'll only create one in this example. We'll give our formatter the machine name 'default', which we'll refer to as "(formatter)" below, and human-readable name 'Image with Caption and Taxonomy Terms' for the first example, and 'Person display' in the second example. Note that machine-readable names for formatters do not need to be unique, except within your module.
CCK hook_field_formatter_info()
To tell CCK about our formatter, we implement CCK hook_field_formatter_info():
function img_cap_tax_fld_field_formatter_info() { return array( 'default' => array( 'label' => t( 'Image with Caption and Taxonomy Terms' ), 'field types' => array( 'img_cap_tax' ), ), ); }
(This is nearly identical for the person field and node reference examples.)
Core hook_theme()
Once the formatter has been defined, CCK will assume there is a corresponding themeable element called (module)_formatter_(formatter), which we need to register in the core hook_theme(). As noted above, we also need to register the themeable element for the widget form, so our hook_theme() implementation for the first example is (the second is quite similar):
function img_cap_tax_fld_theme() { return array( 'img_cap_tax_sel_widget' => array( 'arguments' => array('element' => NULL), ), 'img_cap_tax_fld_formatter_default' => array( 'arguments' => array('element' => NULL), ), ); }
Theme Functions
Finally, we need to create a theme_(element) function for the formatter (the widget theme functions were given in previous sections). For the first example, the formatter theme function displays the image, caption, and taxonomy term:
function theme_img_cap_tax_fld_formatter_default( $element = NULL ) { if( empty( $element['#item'] )) { return ''; } $img = theme( 'imagefield_formatter_image_plain', $element ); $cap = $element['#item']['safe_caption']; $tax = ''; $sep = ''; $val = $element['#item']['data']['value']; if( !is_array( $val )) { $val = array( $val ); } foreach( $val as $tid ) { $term = taxonomy_get_term( $tid ); $tax .= $sep . check_plain( $term->name ); $sep = ', '; } return '<div class="imgcaptax_outer">' . '<div class="imgcaptax_img">' . $img . '</div>' . '<div class="imgcaptax_cap">' . $cap . '</div>' . '<div class="imgcaptax_tax">' . $tax . '</div>' . '</div>'; }
For the second example:
function theme_person_fld_formatter_default($element = NULL) { if(empty($element['#item'])) { return ''; } $stuff = $element['#item']; $flds = array('displayed_name', 'job_title', 'phone'); $ret = '<div class="staff_info">'; $sep = ''; foreach($flds as $fld) { if(!empty($stuff['safe_' . $fld ])) { $ret .= $sep . '<span class="' . $fld . '">' . $stuff['safe_' . $fld ] . '</span>'; $sep = "<br />\n"; } } if(!empty($stuff['safe_email' ])) { $ret .= $sep . '<a class="staff_email" href="mailto:' . $stuff['safe_email' ] . '">' . $stuff['safe_email' ] . "</a>"; } $ret .= '</div>'; return $ret; }
The node reference example works pretty much like the person field example.
Final Details
One further detail... If you are using the "private" file download method on your Drupal site (in the Site Configuration section of administration, under File System), you will find that the Image Caption Taxonomy field doesn't work unless you implement hook_file_download(). The reason for this is that when you use the private file download method, the system asks each module to verify that a given file can be downloaded, before allowing someone access to that file. So, our field module needs to tell the system which files it contains, to allow them to be downloaded. This function is included in the sample module.
Downloads, Background, and References
Below you can download the completed modules that this article describes.
To keep up with new releases (if any), subscribe to the RSS feed for this site. If you would like to hire Poplar ProductivityWare to create a module for your Drupal site, please contact Poplar ProductivityWare.
The latest versions of the sample modules were tested using Drupal 6.16 and the following module dependencies:
- Content Construction Kit (CCK) version 6.x-2.6 and its included Option Widgets and Text modules
- ImageField version 6.x-3.2
- FileField version 6.x-3.2
- ImageAPI version 6.x-1.6
- Content Taxonomy version 6.x-1.0-rc2, and its included Content Taxonomy Options module
Background information and references:
- Background material on Drupal, CCK, and Views: my Drupal Cheat Sheet article, and a video of a talk I did on CCK and Views.
- Terminology note: a PHP programmer who is new to Drupal module development will need to understand the concept of a "hook" in Drupal. Basically, a hook is a structured way for a module to add custom functionality to Drupal. A module whose machine-readable name is "abc" can implement hook "foo", simply by defining a PHP function called "abc_foo()". When Drupal gets to the right place in its execution to do action "foo", it will automatically call all module functions that implement the hook. The core of Drupal has some hooks (see Drupal API Reference below); the CCK module also defines its own hooks (no good documentation I am aware of, which is one reason I wrote this article, to describe which hooks you need to implement).
- Useful references:
- Drupal Module Developers Guide, including the Writing .info files guide page
- Drupal API Reference
- Drupal Forms API Quickstart Guide and Forms API Reference
- Guide to developing CCK field modules in Drupal 5 and an unfortunately only partially-complete guide to upgrading CCK modules from Drupal 5 to Drupal 6. This article differs from the official references in that it pertains to Drupal 6; it presents a complete, working module as its end product rather than disjointed examples; and it specifically addresses how to incorporate existing CCK fields into a compound field, and in particular ImageField and FileField, rather than creating a stand-alone CCK field type from scratch.
- Views module API documentation, and help file on describing tables to Views
I'm surprised there are no comments...
...as this is one of the most complete articles that I've come across. I first found this article when I was starting with Drupal and got something from it, and almost a year later I'm getting even more from it!
Great work.
Fatal error
Thanks you for this very nice tutorial.
I'm just going through it and I tried the module in ZIP attachment. After I added the field, created a node and tried to edit it, I obtained:
"Fatal error: Cannot unset string offsets in /var/my_web_on_localhost/sites/all/modules/filefield/filefield_widget.inc on line 442"
The problem is only with editing.
Please, do you have any idea about the reason? Thank you in advance, Ema
I have same problem with
I have same problem with filefield module.
Thank you again for this tutorial.
for someone who has fatal error with filefield_widget
for someone who has fatal error with filefield_widget,
just add this line in img_cap_tax_fld.module at line 192
$element['#default_value']['data'] = unserialize($element['#default_value']['data']);
I hope this help everybody
Thank you for your
Thank you for your reply.
what do I do to have some fields before uploading file?
because if file is not required, users don't know other existing fields.
I don't know if you get that I said.
thank's again
other data types
I'm following the Person example to create a field based on two textfields and a textarea. It's working fine, except: the textarea is getting truncated to 255 characters. I tried changing the type to 'longtext' and removing the length param of the "case 'database columns':" section of hook_field_settings. Is there something else that I'm missing? I looked around, but couldn't find any documentation on what the options for those db arrays are.
Any help would be appreciated.
if image is not required
what I have to do if image field is not required, and the other fields are required?
I would replace caption, taxonomy by name, email of users.
they don't have to upload image to fill theses fields.
I would like to have the name, email fields before uploading image. Is it possible?
Thanks for this comprehensive tutorial.
Very in-depth tutorial with just the right amount of notes to explain the code. Thanks for sharing your experience and saving me probably days of poring over the CCK source or inferior partial articles!
Excellent, how about with a Node Reference
Hello Jennifer,
Thanks for doing such an amazing job on this tutorial. I have used it successfully to create a compound field based on your person example. Now I need to create another compound field that also contains a node reference (to a particular node type). Is this possible, if so, any clues on how I'd go about it??
Thanks again,
Hi Jennifer, thanks for a
Hi Jennifer, thanks for a great tutorial. I managed to get it working without the Taxonomy (which i didn't need for my project). I was wondering, do you know how to include the functionality you get from the Filefield paths module? I'd like to be able to use Node tokens in renaming files on upload. I had a look but can't work it out. If you have an idea please could you point me in the right direction?
display fields on same line (input form)
Thanks very much for a great tutorial! It has been very helpful.
re: person module
I managed to change the field type to select list and reduced size of text fields. Now I would like to display the fields on the same line (input form)
How would I be able to make the fields appear on the same line (horizontally)?
I am quite new to drupal so any help on the topic will be greatly appreciated
Many thanks
Thanks you for this very nice tutorial!!!
I tried it but as you can see on this screenshot
I can't get the "browse" and "upload" image buttons as in your demo
How is it possible ? Did I forgot to configure something ?
Edit page looks odd
For some reason node which uses person field as a CCK field, field title is not displayed in the editing page. In the View-mode of the node, Person field shows the title correct, as defined in the theme_person_fld_formatter_default() -function. But why it doesn't display in the edit page?
To display these 4 fields of a field in other way than below each other is a CSS matter, isn't it?
Title isn't displayed in the
Title isn't displayed in the source code either, so CSS does not hide it.. And theme I'm using at the moment for developing is the default Drupal theme.. Same problem is with the framework theme.. Well, have to take a deeper look at the module to figure this out..
By default, CCK doesn't load
By default, CCK doesn't load the title and descriptions for the compound field unless you are using it as a multiple. I'm not sure what the thinking is on that, but if you're using it as a single field it only renders the titles/descriptions for the individual fields within the compound field.
The workaround is to add '#field' => $field to the hook_widget() implementation so that the FAPI theme field (in this case theme_person_entry) has access to add these to a single implementation:
Something like this:
function theme_person_entry($element) {
$output = $element['#children'];
if (empty($element['#field']['multiple'])) {
$output = '' . $element['#field']['widget']['label'] . '' . $output;
$output .= '' . $element['#field']['widget']['description'] . '';
return $output;
This is really clumsy - I'm sure you'd rather have some classes on the form-item div you're wrapping around your compound field - but I'm typing it into a comment so sue me.
Truly awesome
This is a great article indeed. Would like to thank you for taking the time and breaking it down.
First of all thank you for this article and for letting us download the modules.
It actually works great.
Just something I have noticed: when used with a Content-type in an array (number "Unlimited"): if one moves one of the compound field, the caption textarea becomes empty. However I have the same issues with textarea in general so it looks like an issue with my drupal installation and not with your field.
Again thanks for this great work!
ImageField and a textfield
this article is really great.
I want something similar. In that example it's only possible to save the caption and the taxonomy when you upload an image. I want to have a imagefield and a textfield in one field but it should be possible to save something in the textfield without uploading an image.
Is that possible?
I copied many things from your article and it doesn't work. I have database columns and I can save text in the textfield when I upload an image but not when I don't upload an image.
Okay, thanks. I will try
Okay, thanks. I will try that.
Do you think it's possible to use filefield/imagefield and textfields in one field?
Yes, thanks. I meant without
Yes, thanks. I meant without uploading the image. :-)
It's possible. I had to change the sanitize-function.
Sanitize function?
Thanks for the fantastic tutorial. I am facing the same issue as Lisa...
Lisa, you mentioned you "changed the sanitize-function" to get the filefield portion to work without an image file?
Could you describe what changes you made, or share the code (inside this function)?
Thanks again.
box size
This is a really useful post, and well written. Thanks!
I am trying to implement the person module, and was having some issues with the box size for the fields. I'm using a fluid frame theme and despite the box size being defined as 60, there seems to be something overriding it so that it fills 95% of the table width in which it sits. When the browser is open to full screen, the box sizes are too long. Any ideas on how to correct this?
According to Firebug, the style is coming from
.node-form .form-text {
, which is in a css table automatically generated by Drupal. Thanks.
FYI, your CAPTCHA for this page doesn't seem to work in Chrome (v 5.0.375.125). Here's the error I got:
"CAPTCHA validation error: unknown CAPTCHA session ID. Contact the site administrator if this problem persists."
Works fine in Firefox...
Thank you Jennifer for the
Thank you Jennifer for the great tutorial!
I successfully created a simple compound CCK field for cost/product.
Is there a way to expose my "cost" field to Views or can I only access the concatenated value?
Ciao, Wolfgang
cck hook documentation
I appreciate your article, and all the detail it goes to...thanks so much.
You say in the article: 'the CCK module also defines its own hooks (no good documentation I am aware of, which is one reason I wrote this article, to describe which hooks you need to implement).'
As it happens, in my quest for more and better documentation, I've found that the CCK hooks are documented at http://drupal.org/node/342987.
Thanks again!
thanks for sharing
thanks for sharing
Compound imagefield with views integration
Hi Jennifer,
Thank you for the tutorial. It's helped me a lot build my first compound fields.
Yet I'm facing a challenge and I'm wondering if you could advise me.
What I need to do is to get a compound field with an image, a caption field and an author field. And I would like them all to be accessible in views. Correct me if i'm wrong but the fields you add to your compound imagefield example don't show up in Views at the moment.
Is it because you store their data in the "data" column of filefield ? Could we create special columns for that ?
Thank you in advance,
multiple fields with same type
Thank you for your very usefull tutorial !!
I'm writing my first compound field which must have two datefields. Date module provides cck widgets, great, furthermore widget seems to have a "To Date" additional field which is necessary for one of the date field I have to add.
But form fields will have same array keys in _settings_form() and in hook_elements() (and thus in _widget_process()) because they are set from the return of one and the same date functions : date_field_settings() and date_elements().
So is there a simple mean to get two fields of same type in a widget ?
Compound Field Module including a user reference
Fantastic tutorial Jennifer - it has really got me on the road to completing something that has bugged me for a long time.
How can I make a custom CCK field that provides a user reference and a text field? I want the ability to be able to autocomplete when a user is typing in the username of another user.
Many thanks,
compound field not saving
Hi Jennifer,
Thanks for the great tutorial. I've implemented a custom multi field a couple of times successfully but now that I'm trying again for another project, I can't seem to get the data I input into the custom multi field to save! Somehow I've made a copy 'n paste error between previous instances of this and my current one although I can't find it.
Which function is it that is actioned on form submission that saves the data from the custom multi field to its own table or where should I look to find out what's going wrong?
Thanks heaps,
ignore last
Hi Jennifer,
Please ignore my last inquiry: I've now solved the saving issue and don't want you to spend needless time trying to help me out!
Thanks again,
Validation question for person field
Thank you for this excellent article on creating compound field modules in Drupal - it has taught me a lot. I have been experimenting with your person_fld example and I had a question about validation. If the email address is blank then you set the field as an error using:
form_set_error($field['field_name'],t('"%mail" is not a valid email address',array('%mail' => $item['email'])));
However this is making all fields in the compound field red. So in addition to the email field showing up as red and invalid after you submit the form the name, job title, etc are also red. What I would prefer is if only the invalid fields (in this case email) were marked as red. I have tried to figure out how to do this by specifying a different value for $field["field_name"] above but I do know know how to properly access the email field name (or if this is even the correct approach). Could you please help with this? Thanks very much.
Wysiwyg issue
First: thank you for this insightful article. It was really really helpful.
Now I have a question to ask you:
Do you have a working example of a compound field including an image field and a textarea with a wysiwyg?
I tried this tutorial and I can upload the first image fine but it does not work when I try to upload the second image. Instead I have an HTTP error 0 exception.
If I remove the wysiwyg, it works like a charm.
Thank you again for your article.
This a problem I thought of tackling a few times, but it was always low in priority. This is a great starting point. It would be great if a CCK module allowed building these compound fields (it's a feature sorely lacking IMO), but rolling custom modules will work for now.
Use as multifield
Hi, thank you for the very helpful post. I 'd like to ask if I create a compound cck field with 3 combined fields will I be able to create an exposed views filter in order to filter the compound field by its' values?
For example, if the compound field has a field for age (integer), a field for gender (male/female as text) and a field of price (integer) I can get only one filter based on the compound field or 3 separate filters (so one can filter values by age, gender or price)?
Thanks for any answer.
cck field - showing all available widgets
Great tutorial which I have put to good use.
I have adopted the image cap taxonomy module for my site. Is it possible to have all available widgets to be shown in the dropdown list when I select the custom field so that I can pick an imagecache preset. Currently, you are only given the default set option.
Is it easy to change the code to include all available widgets?
Big thanks
This tutorial was really good to me. The text itself was a bit messy to follow as you present two sample modules intertwined in the text. I couldn't be arsed to follow it or sort it out. But when I downloaded the person_fld example module it was clear enough.
My needs were for meeting protocol items. I needed:
* title - textfield
* notes - textarea
* decision - textarea
The CCK docs for Drupal 6 are miniscule and I would have wasted great amount of time for something that should be fairly simple.
So great thanks. If you want to simplify the tutorial, split it along the two different examples. This would mean that you can primarily look at the code snippets (which I, as a developer, tend to do) and if something doesn't make sense, read the text, without being confused by two rather different examples.
Either way. Great stuff. Big cred.
Content installation callbacks
I think that you should be passing the name of the module back to CCK, eg 'person_fld' and not just 'person'
Attaching Formatter to Textarea
I've tried my best to follow this example. However, I am still having a very tough time getting an HTML input formatter (and all of the related in/out filtering) to function in my much more simple example. Is there a way that you can show how to add a textarea field to your person example (versus the tax example)? The Tax example has so much going on with file and image fields that I am getting lost re: the much more simple task of attaching the HTML formatters to the caption field.
Views Interface
Terrific tutorial! Probably the best I have ever read.
I am developing a drupal system that includes a content type for which your Person compound field module is perfect. I tried the CCK3 Multigroup functionality but in situations where a content type includes both non-multigroup and multigroup fields Views generates multiple duplicate records. For instance if there are non-multigroup fields and a multigroup field with 3 entries, Views returns 3 records (the non-multigroup data plus each of the multigroup sets). As you can see, situations where there are several multigroup entries or - even worse - several mutigroup fields each with multiple entries - the situation quickly becomes unworkable.
My question is: Will this happen with your combined field technique?
Thank you again for taking the time to publish such a thorough tutorial.
Thank You
Thanks for the quick reply. I will begin implementation of this technique immediately.
Validation Problem
Terrific Tutorial! Best I have ever read.
I implemented the Person custom field and everything works as expected except the valid_email_address call verifies ANY text placed in the field - even a single character.
Any thoughts on how to fix this? I badly need to perform email validation on the field.
Thanks again for taking the time to produce this - it has been an enormous help.
Email Validation Issue Resolved
I am still learning the Drupal API.
I finally figured out that the validation problems I have been having were related to the hook_content_is_empty function in the Person example. I changed the function simply to return array(); and everything is now working.
I still don't quite understand how this function should be written explicitly for all of the fields but at least this is working for now.
Thanks . . . Mike
hook_form_alter vs hook_elements
Some people suggested using hook_form_alter to set the new $elements[$widget]['#process']. Although this works in most cases, it doesn't work at all in multi-value fields. It's advisable to us hook_elements instead.
nodereference as an element of compound field
I've followed your tutorial, successfully except for admin form.
theme and formatter functions work great in frontend, and widget is displayed correctly in admin form.
One of the element of the widget is a nodereference which works fine : autocompletion is correct. But when submitted, $items variable has empty values in hook_field() when $op is update :
... (Array, 1 element) {
0 (Array, 6 elements) {
nid (NULL)
value (NULL)
error (NULL)
status (NULL)
credits (NULL)
nb_visualisations (NULL)
The problem seems to be the name of form element is field_offres[0][produit][nid] : I think [produit] is the problem since nid is accessible at $node->field_offres[0]['nid'].
Any help would be greatly appreciated,
the compound widget uses two sub widgets
There are two sub widgets, date_popup and nodereference_autocomplete, and some keys in array are overlapping, I dont see how to have the two widgets processed.
The second issue is the theme function processes all widgets in element, so input fields are duplicated.
thank you for your quick answer anyway,
sounds like good news
can you explain a bit more, please ? I'm not used to fapi...
What if we have different people.
Hi Jennifer,
Thank you very much for your wonderful tutorial. I was curious to know what to do in the case that you have different people.
In the demo module, people field comes with four fields, what if we have a person that only has two fields? Do we need to make a separate module for each person or just a separate widget?
I tried fiddling with hook_widget. I was able to get another person to show up, but I my new less fields person didn't show up in the content add form.
Does that make sense?
In my example I made one person Mario and one person Luigi
This is fantastic and field Image Caption Taxonomy is exactly what I need for a portfolio site for an art degree show site I'm building.
I have installed on a fresh drupal 6.20 install, and created content type Portfolio Page, with an Image Caption Taxonomy field.
On create Portfolio Page form I get the error
warning: Parameter 1 to theme_img_cap_tax_sel_widget() expected to be a reference, value given in /home/pip/www/fineartdegreeshow.local/includes/theme.inc on line 656.
Any ideas?
It is...
Turns out it is a bug in FUpload or ImageField (nobody agreeing which) not getting along with php5.3...
Discussed here http://drupal.org/node/619632 and here http://drupal.org/node/581074
Labels displaying when fields are empty?
(I don't know if anyone's checking the comments here, but thought I'd ask.)
Thanks very much for this really clear tutorial and the accompanying downloads. I've used the "Person Field" module as a model for several compound field types that are now doing what I need them to, for the most part.
What I'm seeing, though, is that the field label continues to display, even when the field is empty (as defined in the various modules' hook_content_is_empty functions). The same thing happens with the person_fld module from the tutorial.
As best I could figure out, an empty div was getting created in the hook_formatter_default function: at line 246 in the person_fld module, $ret begins as div class="staff_info" (that div gets closed out at line 260, just before $ret is returned. Even if there's no content in the subfields, that empty div still turns up, so the label appears.
Removing that wrapping div and having $ret begin with the first subfield (still wrapped in a span) seems to clear things up (at least, the label no longer appears when the field is empty), but I was wondering if there was a better way to solve this: am I missing out on something by not having that wrapping div?
Thanks very much again!
Adding a URL field
Thanks for this in depth tutorial and the finished module. This is the most complete CCK tutorial I've seen on the web, and I've looked at a lot.
I'm going to attempt to add a URL field to the module, so that the image can be treated as a link to another node on the site or to another website. If I'm successful, would you like me to contribute the code?
Doug Gough
How to Delete
Hi I am using person person_fld. I have a content type Create class in that I am using person names. Now I am getting an option to add more option, to add "Name", "job title", "Phone" and "emial". Now when I click on Add another Item it is giving to add all above filed what exactly I want.
But now how can we remove those that is been added. We are not having an option Remove to remove that is been added using "Add another item".
Please suggest.
Thanks in Advance.
I downloaded this module, and it seems to work, but I don't see any database table being created and when I submit data to the field, it does not show up in the node in view or edit mode.
I must be missing something about getting cck to handle all the install uninstall business. Any tips would be appreciated.
Extra field to align left, right or middle
Thank you so much for the wonderful Module. Can you suggest me a method to have a tick box along with the upload button to align the image to the left, right or middle.
Thanks in advance.
Drupal 7 version?
Hello, this looks like a great tutorial. With Drupal 6 I never used cck image fields, I liked to have my images as Nodes and therefore the images had taxonomy and description and all in itself.
I started to play with Drupal 7 now and (big dissappointment!) no more image nodes but image fields on content types!
Your tutorial would now suddenly become very useful - if only it would work with Version 7. Any hints on how to convert this to work with Drupal 7?
Thank you
Drupal 7 version
For Drupal 7 it is possible to use the field collection module. It can all be done with no coding :^)
Add a field collection to the node type and then add fields to the collection.
Then you can view the multiple fields all on the node or teaser display. When working with views you can see all the fields in the collection by default or add a relationship to gain access to the individual fields in the collection.
PS This site seems to be written in English but the Captcha & comment form fields are in Spanish.
Best practice for custom cck module, field, and _entry naming
It turns out you can burn a lot of time if you misname the field in content_notify() in hook_install.
My custom cck edit form just absolutely would not show up! It was in kpr($elements), the functions were calling the callbacks, everything was great. Finally, I enabled another cck I was following as an example, and then voila, mine worked.
You've been warned... :)
HTML tags appearing
Hi Jennifer! This was a great tutorial, thank you so much for making it! For the person example, I changed of my fields from varchar to text and I used a textarea. My wyisiwig did a great job of taking over and worked seamlessly. Almost. The text is still being shown with the HTML tags. I was unsure of how to make the settings for this filtered text instead of plain text.
Nodereference widget without custom field
Hi, Jennifer,
Very helpful article, thanks for posting and updating... Not much good info on the web about how to do this kind of thing.
Last week I built a custom nodereference widget, and your guide was essential. However, I did not see the need to do a custom field, and we already had data in the nodereference field, so it took me a while to figure out how to get nodereference to correctly save the nid value of the referenced node.
The use case for this was because we wanted to have a referenced node embedded in the primary node's form, where users could update the value of a field on that node, directly in the form. We had started with the nodewidget module, but this didn't really support sharing the referenced nodes from other nodes -- it works for one-to-many, but not many-to-many.
Using the info in this article, I got everything working, except that because I was using an existing field instead of defining my own, the $form_state didn't have values set for the nodereference, and so the nodereferences all got unset on save.
The solution? An element_validate callback, which rearranged the $form_state to have the right structure. I did have a hidden "nid" field defined in the widget_process callback, but it was too deep in the hierarchy. This code fixed it up for me:
// now we need to add the nid to field_budget_codes[0]['nid']...
// if we don't do this, it would add to field_budget_codes[0]['budgetcode_master']
$element['#parents'][] = 'nid';
form_set_value($element, $node->nid, $form_state);
... the $element['#parents'] array was populated with the widget name as a tree containing the other fields in my widget. I needed to add the 'nid' element to the right level of $form_state so it would get picked up by nodereference and properly saved.
This same element_validate callback is where we saved the data on the other node -- probably not the most appropriate spot, but it's doing the trick.
Hi Jennifer Thanks a lot for
Hi Jennifer
Thanks a lot for your great tutorial! I'm using it to build a custom module on my own site, and it works great so far.
There is, however, one issue that's bothering me. I downloaded and installed your "Person Field" module and added a Person field to a content type. When I go to the form to edit a node of this content type, everything works as intended. BUT: The Drupal-internal bug-tracker records a problem. The error message is "Invalid argument supplied for foreach() in .../sites/all/modules/cck/content.module on line 1284." This happens only when visiting the form, not when submitting it.
Again, this error does not appear on the page itself (not even after submitting the form), but only in the error log. Do you know anything about it? Is this a problem? I usually tend not to ignore entries in the log, but maybe this is an exception.
Thank you and goodbye,
This is a great tutorial.
This is a great tutorial. Thanks!
Is it possible to embed this CCK widget in a FAPI form?
Autocomplete Widget?
I would very much appreciate any ideas as to why Autocomplete Widget does not function when I include it like the following in the compound field...
$element['contact_name'] = array(
'#title' => t('Name'),
'#type' => 'text',
'#default_value' => $defaults['contact_name'],
'#size' => '100',
'#max_length' => '255',
'#widget_type' => 'autocomplete_widgets_flddata',
'#autocomplete_match' => 'contains',
'#autocomplete_case' => '0',
'#module' => 'text',
'#widget_module' => 'autocomplete_widgets',
Zip of Nodereference example
Hi Jennifer,
Great tutorial. But I was wondering, would it be possible to put up the zip file of the nodereference example (like you did with the other two examples). It is exactly what I need and since you already have it...
It would be much easier to just tweak the existing module according to my needs than it would be to write it from scratch (even if I am following your instructions). Just thought I would ask.
In the node reference
In the node reference example, there should be "'#options' => $options," instead of "'#options' => $newoptions," -- then the selector works.
But how to get the node reference working as an autocomplete widget instead?
Drupal 7
This is exactly what I need, except I need it for Drupal 7, with entities and fields.
Is there a comparable Drupal 7 tutorial?
I specifically need a field combining a taxonomy term reference along with a date field.
Can anyone point me in the right direction.
Compound field where image is optional
Hi Jennifer,
Excellent tutorial. I've been adapting the "Image Caption Taxonomy" example to my needs and have run into a roadblock.
On my compound field I have added several other text fields, etc, but I want to have the image as optional (I guess it's more similar to the Person example, but the option to add an image --- saying that, I used the Image Cap Tax example because it deals with images and that seems the more complex part!).
Since filefield_field() checks to see if an image is present (line 116 and 125 in img_cap_tax_fld.module) , it will not save the row to the table. If I comment out those lines the rows are saved, but I also get values set for the file database columns instead of NULL (and that makes sense that it would). All else seems to be working as expected (although the formatter seems to break for the image, even though the file is there and all seems to be good in the db).
Any suggestions?
person field
I'm impressed with your skills. You have lucky customers. Did you ever make Person fields work in Drupal 7 as my attempt is failing. I lack the ability to make the widget or call Person and what ever happens after that.
Great tutorial.
Node reference
Can you please share module you implemented for node reference? I know you already declined this request but I am still unable to implement it correctly.
Thanks in advance.
Addition of new field in a existing compound field
Hi, i implemented compound field having 3 text fields insider that compound field by following your post. It was nice tutorial. The problem which i am facing now is that i want to add one more field to that existing compound field. But i am unsuccessful in doning so. I also followed this post http://nodesforbreakfast.com/article/2012/02/14/addremove-columns-compou..., but it didnot produced the result.
I want guidance from you. Please help me to reveal the solution. I shall be very thankful to you.
- Ata


text:
You Can Laugh at
Scraggly Lawns if You Follow Our Advice!
If you look at your neighbor's yard and couldn't help but think,
"The grass is always greener on the other side." Well, don't worry,
we have some tips to help you grow grass,
including easy ways to feed your lawn, water less and improve your
So why are some lawns always beautifully green, while others
are riddle with brown spots? The answers to successfully growing
grass lie in simply growing the right grass, providing the
proper nutrients, keeping your lawn well watered and improving
the soil in which your grass grows. It also helps to use our
Care or Lawn Start packages, the foolproof way to a beautiful
"I just had to let you know that your Soil Syrup
is a great product. As an Ag Engineer I have always known what
Humic Acid was, but I had never used it. That being said, I had to
re-sod part of my property three weeks ago. I sprayed Soil Syrup on
the prepared soil before I put down 100 sq ft of sod in the pack
yard. I put another 50 sq ft of sod in the front yard with not
treatment. You aught to see the back yard portion. I had to cut it
yesterday. It has taken so well that I can hardly see the
demarcations between the sod pieces. This stuff is
Bob W., Jacksonville, FL
Everything You Need For a Great Lawn
1) Lawn Care and Lawn Start 1-2-3
starts with Lawn Honey for moisture control in the soil.
With Lawn Honey, you'll water less, reduce the effects of
drought, and grow fuller grass seed and grass sod.
2) Next use, SoilSyrup to
condition the soil. Improve and encourages root
growth. Breaks down clay soils to make them more useful. Your
new grass sod will love it.
3) Use MegaGro for growth boosting.
Works great on brown patches, new grass, shrubs, foliage,
flower beds, plants. An excellent lawn care
product that will help you grow grass everywhere.
4) Use Algoflash
for balanced lawn feeding. Non-burning formula
delivers 12 essential nutrients lawns need for healthy growth.
pH balanced so your lawn can easily absorb all nutrients.
The Lawn Care 1-2-3 and Lawn Start 1-2-3
systems provides everything you need for a beautiful lawn!!
"I decided to try lawn
honey about 6 weeks ago. This first thing I noticed was that
the river that used to flow through my front yard is not longer
there. I couldn't believe it. After that I noticed that the bent
grass in the front yard started to turn green. For those who do not
know what bent grass is, it is considered the luxury grass of the
cool season grasses, but it is the hardest to maintain. Golf
courses use it for there putting greens. Then about 2 weeks ago I
used another application of Lawn Honey along with some Algoflash and SoilSyrup and the results were
AR, Manahawkin, NJ
How To
Grow Grass - Types of Grass
First it's important to make sure you're growing the right grass
for your region. Otherwise all the lawn care in the world won't
you to grow grass. There are many different types
of grasses that are grown in lawns, and these grasses have
different growing requirements. Many factors go into the selection
of a type of grass for a particular lawn. One of the overriding
factors is your local climate. The so-called "warm-season" grasses
are ideal for the southernmost states in the U.S., whereas
"cool-season" grasses predominate in the North and in Canada. In
between, for the Eastern U.S., lies the so-called "transition
zone," comprised of zones 6-7.
Common cool-season grasses include:
Common warm-season grasses are:
Bermuda grass
Buffalo grass
Zoysia grass
Many lawns are not always composed of just one type of grass,
but rather of a mixture, to take advantage of the strengths of each
The following are examples of other factors that go into your
selection of grass type, in addition to local climate (these
examples pertain to lawns in the Northern zone and in the
transition zone):
Shady areas are notorious obstacles to having green lawns. Among
cool-season grasses, fine fescues are the most tolerant of
Lawn areas with heavy foot traffic require a tough grass. A mix
of Kentucky bluegrass and perennial rye will fill the bill
Some regions are more prone to drought than others. The new,
improved strains of Kentucky bluegrass are relatively
But in addition to grass-type selection, don't forget about the
other steps to successfully grow
grass. If you're committed to growing a beautiful lawn
read on about the other key factors to grow
In order to grow grass successfully, you'll
need to know how much water your lawn gets. In dry climates,
installing an irrigation system is often necessary to grow grass
successfully. Meanwhile, in the Pacific Northwest, you may be able
to get by with whatever rainfall you get. For most homeowners, the
decision to install an irrigation system for watering lawns may not
be practical. Cost will be a definite consideration, but keep in
mind that, that there are other solutions to keeping your lawn
green that can also save you money.
One way or the other, your grass must have sufficient water on a
consistent schedule in order for you to achieve the goal of a lush
green lawn. If your neighbors are watering lawns with an automatic
irrigation system -- and you aren't -- you're not starting out with
an even playing field.
One way to level the playing field when you grow
grass is to use Lawn
Lawn Honey is an amazing, scientifically
advanced water absorbing gel. It stores water and nutrients in the
soil so they last longer. Giving you a greener, lusher lawn than
ever before, with less water and fertilizer and in a lot less time
than other lawn care treatments.
Lawn Honey will create thousands of tiny water
sponges in the lawn that will hold water and nutrients for your
lawn. These tiny water sponges will collect water from the ground
and store it, until the yard needs it. The tiny water sponges Lawn
Honey creates throughout your lawn will actually help aerate and
condition the soil and increase the growth of beneficial
microorganisms. Which means you'll have healthier more productive
You'd be amazed, but most of the water that makes it to your
lawn from rain or from sprinklers doesn't even get used. The sun
dries it up, or soaks through the ground and past the root system
of your lawn. Our Lawn Honey absorbs all that water right into the
root zone of your lawn, store watering for use when the lawn needs
it. Lawn Honey is also going to help retain any fertilizer you
apply to the lawn. It will hold on to the fertilizer until the
grass absorbs it from the Lawn Honey. So you can water less,
fertilize less.
Lawn Honey's ability to increase the water holding capability of
the soil is great if you have just put down sod or planted grass
seed. Lawn Honey will help keep it moist, which is critical for
helping new sod take root, and new grass seed germinate. And if you
live in an area with watering restrictions that means your lawn
will stay green when your neighbor's lawn is turning brown.
Lawn Honey will also help prevent erosion, so
if you've tried to grow grass on a hillside without much luck, then
Lawn Honey is for you. And if you live next to a stream or lake,
Lawn Honey will help prevent sediment and fertilizers from flowing
into the water. Keeping the water cleaner.
I was skeptical of Lawn Honey, so I did an
experiment. When I re-seeded my lawn this spring, I treated half
the lawn with Lawn Honey, and half without. The half that was
treated with Lawn Honey all grew in full and lush, and fast. The
other half looked so bad in comparison, I stopped the experiment
after 2 weeks, and treated the whole yard! Lawn Honey made growing
grass from seed a whole lot easier! My kids really love playing in
the soft full grass!
JJ, Naperville, IL
Before you start applying anything to your lawn, you'll want to
make sure that your grass isn't being hindered by lawn thatch. If
you have too much thatch, the lawn thatch layer will prevent water
from getting to the roots of your grass, effectively nullifying
your efforts to watering your lawn. And lawn thatch layer also
furnishes cover for unwanted insect pests.
To check and see if you have a lawn thatch problem, measure the
depth of the thatch.
If your lawn thatch layer is ½" or less, you're ready to
start applying our Lawn Care products.
SoilSyrup will help break down any thatch you may have, and the
thatch won't get in the way of effective watering. You can also
minimize your minor thatch problem by simply aerating the lawn.
SoilSyrup and Lawn Honey will help aerate your lawn chemically, but
it may help to also do so mechanically by walking over your lawn
with spiked shoes or raking up the thatch.
But if you're lawn thatch layer is, say, ¾" or more, you
have a major lawn thatch problem, for which you'll need the aid of
a vertical mower. Vertical mowers can be rented from your local
rental center. Also spread a soil conditioner like SoilSyrup over
the soil. By removing this heavy layer of thatch and applying
SoilSyrup, you'll help improve the soil conditions that you'll need
to effectively grow grass.
Now that we have the basics out of the way, you can move on to
actually applying products that will help you grow grass and make
you the envy of the neighborhood.
Your grass needs periodic feedings, and it is best to meet this
need for periodic feedings by fertilizing lawns with a balanced
fertilizer like Algoflash. Our Lawn
Care packages include Algoflash, and when used in conjunction
with SoilSyrup and Lawn Honey you'll be able to feed your lawn over
time, rather than all at once. Fertilizing lawns with our package
allows the grass to "eat" at its own leisure. As nutrients are
released, the root system of your grass fills in any bare patches,
depriving weed seeds of a place to germinate.
We suggest a four-step process to grow grass. Our Lawn Care products include Lawn
Honey, SoilSyrup,
Algoflash and MegaGro, all are
ready-to-use, no mixing or measuring, just hook up the hose-end
sprayer to your garden hose and spray. These products are all the
latest in consumer lawn care science and have been used by
commercial landscapers for years for growing grass!
Our lawn care products
will improve the way you grow grass in following
First, your soil is going to store more water and nutrients than
it ever did before. This means that all the nutrients that are in
the soil, or that get applied to the soil will stay in the soil
longer, and last longer. The rain that your yard gets, and the
sprinkling that you may do, will all last longer, providing more
food and water to grow grass. This means a healthier lawn with less
effort than you've ever had to do in the past.
Second, you'll improve the quality of your soil, so your growing
grass develops healthy root systems. If you have clay or sandy
soils, or otherwise difficult soils, this is extremely important to
do. Your lawn won't thrive if it doesn't have the basic building
blocks to start it off.
Third, you're going to boost growth throughout your lawn with
our complete fertilizers and micro-nutrients. So, once you've
prepped the soil, the growing grass is ready to thrive. Our
nutrients will give your grass that extra boost to get it growing
well and looking healthy and strong.
It's a smarter way to grow grass!
When starting new lawn, many people wonder which is better:
laying sod or seeding lawns. Either method offers benefits. Laying
sod is fast and produces high-quality new lawns, while seeding
lawns is cheaper and offers a wider variety of grass types. The
best solution for you will depend on your budget and the amount of
time your have to care for your new lawn. Either method will work
to grow grass.
If you want to learn more about how our Lawn Care products work
to help you grow grass from seed or sod, visit the
following links:
How To Grow Grass Seed
How To Grow Grass Sod
If you follow our tips and use our Lawn
Care products you can easily grow grass anywhere!
If you have questions, feel free to contact us.

subset: commoncrawl


text:
- Aug 14:
- Robin Williams had Parkinson's disease, wife reveals
- Robin Williams: Bay Area made him feel normal
- Marin sheriff defends release of Robin Williams' death details
- 'Aladdin' stage, film stars mourn Robin Williams
- Aug 13:
- Robin Williams' daughter deletes Twitter and Instagram accounts over online bullying
- Bay Area comics react to Robin Williams' death
- Robin Williams tribute by emotional Jimmy Fallon
- Quoted: Trolls drive Robin Williams' daughter away from Twitter, Instagram
- Aug 12:
- Marin residents share stories, fond memories of comedian Robin Williams
- Robin Williams' death makes public the usually private agony of suicide
- Contra Costa Times editorial: With Robin Williams, depression and bipolar claim another life
- Robin Williams remembered by Ben Affleck, Pam Dawber, Sally Field and more
- Robin Williams could appear in four films after his death
- Robin Williams death confirmed as suicide
- Obama calls Robin Williams 'one of a kind'
- Aug 11:
- Robin Williams fans flock to 'Mork and Mindy' house
- 'We knew him as a neighbor': Marin remembers Robin Williams
- Robin Williams suicide was 'selfish,' says Todd Bridges
- Apple ad was one of Robin Williams' final productions
- Obituary: Comedian Robin Williams dead of suspected suicide at 63
- Appreciation: Robin Williams, not a faster brain on the planet
- Robin Williams dies; sheriff investigating as suicide
A depressed Robin Williams had money problems before his suicide on Monday -- reportedly by hanging -- and was frustrated over having to take parts just for the money, according to RadarOnline.
A family friend, who reportedly spoke with Williams recently, told the website the 63-year-old "Good Morning Vietnam" actor was upset he had to work so much insure his family's financial security, especially after two pricey divorces.
"All he could talk about (was) serious money troubles," the friend reportedly said. "There were clearly other issues going on and Robin sounded distant during the telephone conversation."
Marin County authorities will hold an 11 a.m. news conference. CBS News reports it heard a Marin County fire scanner audio in which an operator describes what happened in Williams' Tiburon home as an "apparent suicide attempt by hanging."
TMZ reports Williams didn't leave a suicide note.
Williams was also reportedly upset over doing a "Mrs. Doubtfire" sequel, which was supposed to begin filming later this year, the friend told RadarOnline.
"There was also frustration that Robin expressed at having to take television and movie roles he didn't want to take, but had to for the paycheck," the source said. "Doing sequels was never Robin's thing, and he wasn't that excited at having to reprise the role of Mrs. Doubtfire."
CBS canceled Williams' sitcom "The Crazy Ones" earlier this year after just one season. Williams -- a well documented alcoholic and drug addict -- returned to rehab in July.
Williams joked last year about losing so much money via two divorces.
"Well, not all," he told Parade magazine last September. "Lost enough. Divorce is expensive. I used to joke they were going to call it 'all the money,' but they changed it to alimony. It's ripping your heart out through your wallet."
A 911 call came in at 11:55 a.m. Monday from his home in Tiburon, and the Oscar-winning actor and comedian was pronounced dead just seven minutes later.
Williams, 63, was last seen alive Sunday night about 10 p.m.
Williams' set up trust funds for his three children, according to TMZ. The kids receive payments on their 21st, 25th and 30th birthdays.
Tony Hicks provides celebrity commentary for the Bay Area News Group. Contact him at Facebook.com/BayAreaNewsGroup.TonyHicks or Twitter.com/tonyhicks67.


text:
Daughters of local metropolitan Washington D.C. service members became "princesses for a day" at a Princess Tea Party while their moms received some fashion and makeover tips, April 21, at USO-Metro's 2nd Annual Project Cinderella at Fort Meade, Maryland.
The girls learned how to be a lady and accessorized with tiaras, beads and rings before enjoying strawberries and apple juice while getting their nails done. – USO photos by Joseph Andrew Lee
More from the USO
Aug 24, 2016
USO and the What To Expect Foundation Host Special Delivery Baby Shower in Dover
Being pregnant isn't easy under the best of circumstances. But being pregnant and having a spouse in the military – or serving yourself – can make the expecting experience exponentially more difficult. That's why the USO and the What to Expect Foundation, led by best-selling author Heidi Murkoff, team to bring Special Delivery Baby Showers to military bases around the world.
Aug 24, 2016
A Dish in the Desert: USO Piloting Expeditionary Satellite Internet Kit Program for Service Members Downrange
After months of research, two reinforced plastic cases arrived at Camp Shorab, a once-bustling Marine Corps base in Southwestern Afghanistan. When unpacked the packages' color-coded contents provided a free USO internet connection for troops on the ground.


text:
Roche's Genetech Offer: The Rules of Engagement
Swiss drug giant Roche is offering $89 dollars a share for the 44% of Genentech it doesn't own already, as you may have heard by now. The offer price is about 9% more than Genentech's Friday close and represents a pretty thin premium compared with those paid by other drugmakers for lesser biotech companies.
Genentech shares have traded above $92 today, an indication that some expect a sweeter deal. But how much higher would Roche go? Maybe not that much. Under the terms of Roche's earlier acquisition of about 56% of Genentech, Roche can keep other companies from swooping in with higher offers. That means a bidding war of the sort that's driven up other recent biotech acquisitions isn't likely.
True, Genentech's non-Roche shareholders will get to vote on any deal before it goes through. But if the shareholders reject the deal, Roche has the right to hire outside investment banks and go ahead with the acquisition based on a share value the banks assign.
The next step in the process is for Genentech's independent directors to get together and consider the offer. They're likely to negotiate with Roche to try and get a higher price. Ultimately what we could end up seeing is a deal with a slightly richer price than Roche's initial offer, but a lower price than other recent biotech acquisitions might suggest.


text:
In response to the extreme temperatures forecast for the next few days, the Harvey Wheeler Community Center will be open as a cooling center Thursday and Friday, 12 noon until 9pm. There are no age restrictions. Please feel free to stop by if you would like to escape the extreme temperatures forecast. If transportation is an issue, during business hours please call 978-318-3020 (the COA Office), or after hours please call 978-318-3400 (Public Safety Dispatch).
Please remember that the Concord Public Library - Main Library and Fowler Branch - are also air conditioned and welcome the public. The Main Library and Fowler Branch summer hours vary somewhat, and are posted on the Library web site at: http://www.concordlibrary.org/pages/general.info.html#hours


text:
JUL 03, 2013 - New York Times
A Must Read - Share
At least 16 people were killed in the attack, which comes a month after Pakistan's new prime minister, Nawaz Sharif, called for an end to American drone strikes.
Editor's Picks
Hey you have no friends!!! Get to work and start inviting friends to help build your Comunitee. Click Here to invite friends.
© 2011 Comunitee, Inc.


text:
Birmingham City Message Board
V's Barnsley
Lucky to escape with a point!!
Does anyone know if Davies is out for some time?
I think Redmond needs a break and would like to see Beausejour play wide left with a central midfield of Elliot and Fahey or Gomis.
Looking forward to Zigic and King starting together, we have to plan for when Wood goes back to Albion (end October)
If Davies is out how would we line up at the back? either move Ridgewell into the centre and play Murphy or just replace Ridgewell with Murphy and bring in Ibanez?
KROSort: Newest | Oldest | Most Replied Expand all replies
Hi Jim,
We were a very poor second best until Elliot and Zigic came on. We were lucky to get a draw and unlucky not to snatch a win which would have been harsh on Barnsley.
I think King/ Wood play too far apart and are not as effective as King/Zigic.
Elliot would start if I was boss and maybe Fahey or N'Daw in the middle with Beausy and Burke wide.
Ridgewell is OK at central defense but he is prone to the odd mistake. Murphy gives us more options at LB. I would give Carr a rest and play Spector at r/b because he is not effective enough in c/m.
The other thing that strikes me is how lightweight Blues are. I've never seen so many Brum players back out of a challenge, three times Burke was favourite to win a loose ball but stopped short of making a tackle. This needs sorting out. We need to toughen up.
We also need to start closing down the opponents more and quicker.
part from that CH is performing miracles on less than a shoestring.
I need to add I am not happy that Parnaby has been told he will get no contract despite proving his fitness, passing a medical and agreeing terms before being told to do one.
Lastly, if you don't already know, Blues are live on ESPN Classic on Thursday evening at 6:00pm. If you haven't got ESPN try googling "Vipbox" and click on the football link.
- 1 Reply to Pikey
Hi Pikey thanks for the info regarding Thursday.
Also disappointed with the treatment of Parnaby he would provide some cover at full back.
N'Daw or Gomis would add some much needed bite to midfield with Burke & Beausy wide.
Elliot must start after his showing on saturday.
King and Zigic will cause massive problems to championship defences.
Still worried about Carr and Ridgewell, would replace them with Spector (not strong enough for midfield) & Murphy (who must be getting cheesed off)
After tonights and saturdays fixtures we will be three games behind and playing a massive catch up (i'd rather have the points than games in hand)