background image

Content tagged with: field

Eric's picture

This article will show you how to establish parent and child relationships between nodes. To start, you'll need to install and enable the CCK module, and node reference (which comes with CCK). Next, you'll need to create two node types (admin/content/types/add). For my example, I created two node types: Organization (parent) and Department (child), both consisting of a title and body (of course, you can add as many fields as you'd like).

Next, you'll want to add a new field to the parent node (organization) by editing the content type (admin/content/node-type/organization) and clicking on the "Manage fields" tab. Enter the information to add a new reference field on this screen. Add a label (ex: "Department Reference"), enter a field name (ex: field_ref_department), choose "Node reference" as the field type, and choose "Autocomplete text field" as the form element as shown below.

node reference add field 1

On the next screen you can choose the field options. For this example, I changed "Number of values" to "Unlimited" (to allow a one to many relationship), selected "Department" in the list of content types that can be referenced, and saved these settings.

node reference add field 2

Now, if you create a new parent node (Organization: node/add/organization), you'll have the option of adding pre-existing child (Department) nodes. As you start typing the title of the child nodes in the node reference auto-complete field, it will automatically populate the field with nodes that match. Of course this will only work if you have already created child nodes. At this point adding relationships is a two step process.

node reference add parent node 1

To improve usability, you can install the Popups API (http://drupal.org/project/popups) and Add and Reference (http://drupal.org/project/popups_reference) modules which allow you to add child nodes without leaving the node/add/PARENT-NODE screen. After installing and configuring these modules, you'll be able to add child nodes directly from the node/add/PARENT-NODE screen by clicking on the "Add new: Add Child Node link", which spawns a lightbox window to add a child node.

node reference add parent node 2

Here is a screenshot of a parent node view after adding child relationships:

node reference parent node view

Eric's picture

In this tutorial I'll show how I used the Forms API to add a file upload field and attach the uploaded file in an email. I have experience using the PEAR libraries Mail and Mail_MIME to handle MIME/HTML emails and file attachments, so I decided to use them.

If you are unfamiliar with PEAR, here are a few quick tips:

# install pear via YUM (this will vary across operating system)
$ sudo yum install php-pear

# upgrade all PEAR packages
$ sudo yum upgrade-all

# check for Mail and Mail_MIME libraries
$ pear list | grep -i mail
Mail             1.1.14  stable
Mail_Mime        1.5.2   stable

# install a PEAR library
$ sudo pear install Mail

Now we can create our form callback function. NOTE: I'm keeping this form as simple as possible to focus on the file attachment functionality.

<?php
function _MYMODULE_form() {
 
// create an empty form array
 
$form = array();

 
// set the form encoding type
 
$form['#attributes']['enctype'] = "multipart/form-data";

 
// add a file upload file
 
$form['upload'] = array(
   
'#type' => 'file',
   
'#title' => t('Attach a file'),
  );
   
 
// add a submit button
 
$form['submit'] = array(
   
'#type' => 'submit',
   
'#value' => 'Submit',
  );

}
?>

The above will create a basic form object with a file upload file and a submit button. The form can be included using the drupal_get_form function. Example:

<?php
$html
= drupal_get_form('_MYMODULE_form');
?>

Next I added a validation function to validate the file upload.

<?php
function _MYMODULE_form_validate($form, &$form_state) {

 
// define upload field name
  // NOTE: this should match the name of your form file field
 
$fieldName = 'upload';
   
 
// If a file was uploaded, process it.
 
if (isset($_FILES['files']) && is_uploaded_file($_FILES['files']['tmp_name'][$fieldName])) {

   
// attempt to save the uploaded file
   
$file = file_save_upload($fieldName);

   
// set error if file was not uploaded
   
if (!$file) {
     
form_set_error($fieldName, 'Error uploading file.');
      return;
    }
       
   
// set files to form_state, to process when form is submitted
   
$form_state['values']['file'] = $file;
       
  }
  else {
   
// set error
   
form_set_error($fieldName, 'Error uploading file.');
    return;   
  }
       
}
?>

The above validation form will check to see if a file has been uploaded and set a form error as necessary. The last line of the function sets information about the successfully uploaded file to the $form_state array, which will then be passed to the submit handler function.

Next I created a form submit handler function which will create a Mail_Mime object, attach the file, send the email, and set a drupal message for the user to see.

<?php
function _MYMODULE_form_submit($form, &$form_state) {

 
// create the email subject
 
$subject = 'File attachment form submitted';
   
 
// create the text version of the email body
 
$body_text = "Dear Eric,\n\nblah blah blah.\nblah blah blah.\n\nRegards,\nEric";
   
 
// create an html version of the email
 
$body_html = str_replace("\n", "<br>", $body_text);

 
// define who receives the email
 
$to = "YOUREMAILADDRESS";

 
// include pear libraries
 
require_once('Mail.php');
  require_once(
'Mail/mime.php');
   
 
// create new mail mime object
 
$mime = new Mail_mime("\n");

 
// add attachment
 
if ($form_state['values']['file']) {
   
$mime->addAttachment(
     
$form_state['values']['file']->filepath,
     
$form_state['values']['file']->filemime,
     
$form_state['values']['file']->filename
   
);
  }
   
 
// set text message
 
$mime->setTXTBody($body_text);
   
 
// set html message
 
$mime->setHTMLBody($body_html);
   
 
// get message body
 
$body = $mime->get();

 
// define headers
 
$hdrs = array(
   
'From' => variable_get('site_mail','YOUREMAILADDRESS'),
   
'Subject' => $subject,
  );
   
 
// process headers
 
$hdrs = $mime->headers($hdrs);
   
 
// create mail object
 
$mail =& Mail::factory('mail');

 
// send email
 
$mail->send($to, $hdrs, $body);  

 
// set message to user
 
drupal_set_message('The file attachment form has been submitted.');   

}
?>

If everything worked correctly, the form will be validated, submitted, and an email with the file attachment will be sent.

Eric's picture

Unfortunately, the stock Drupal contact module does not satisfy everyone's functional requirements. I decided to weigh alternative solutions, but since all I wanted to do was add an additional field to the form and email, I decided to add a few lines of module code instead. This solution consists of adding a form_alter hook to modify the contact form and submit handler, and an additional submit handler function:

<?php
function MYMODULE_form_alter(&$form, $form_state, $form_id) {
 
// check for contact form
 
if ($form_id == 'contact_mail_page') {
       
   
// prefix another submit handler
   
array_unshift($form['#submit'], '_MYMODULE_mail_page_submit');
       
   
// NOTE: since I want to insert the form element at a certain point,
    // I have to create a new $form object
   
$newForm = array();
       
   
// define insertion point
   
$ip = 'message';
       
   
// lop through $form object and duplicate the values
   
foreach ($form as $k => $v) {
           
     
// check for insertion point and add new form element
     
if ($k == $ip) {
       
// add phone number
       
$newForm['phone'] = array(
         
'#type' => 'textfield',
         
'#required' => true,
         
'#title' => 'Phone',
        );
      }
           
     
$newForm[$k] = $v;
    }
       
   
// replace $form object with new form
   
$form = $newForm;

  }
}

function
_MYMODULE_mail_page_submit($form, &$form_state) {   

 
// Since message is required by default,
  // I can concatenate the new form field onto the message variable
 
if (!empty($form_state['values']['phone'])) {
   
$form_state['values']['message'] .= "\n\nPhone: " . $form_state['values']['phone'];
  }
   
}
?>

Now, if I browse to the contact form, there is a new field (phone number in this example) inserted right above the message textarea. When I submit the form, the email sent contains the phone number as well.

Eric's picture

Hardcoding something in your module or theme can be just as bad as hacking the core. If the Drupal admin interface allows you the change and configure something, your modules and themes must be scalable enough to account for these changes. This code snippet (example) shows you how you can lookup the CCK field labels, so you'll never have to hard code a label in your theme/module again.

<?php
// create a container for the field labels
$fieldLabels = array();

// load node
$node = node_load('MYCCKNODEID');

// loop through node properties
foreach ($node as $k => $v) {
 
// ensure this property is a field
 
if (substr($k,0,6) == 'field_') {
   
// use the CCK function to get the field data for this field
   
$fieldData = content_fields($k, $node->type);

   
// add the label to the array
   
$fieldLabels[$k] = $fieldData['widget']['label'];
  }
}

// for debug, you could now show all the fields with their labels:
echo "<pre>";
print_r($fieldLabels);
echo
"</pre>";
?>

Eric's picture

Drupal 6 comes with greatly improved usability, such as the ability to reposition node form elements by dragging and dropping (unlike Drupal 5, which made you assign a numeric weight to each item). But what about the form elements that Drupal does not let you adjust, like the revision information? You can implement a form_alter hook to solve this one...

<?php
function MYMODULE_form_alter(&$form, $form_state, $form_id) {
 
// node add/edit form
 
if (substr($form_id, -10)=='_node_form') {
   
// manually set a weight on the revision fieldset
   
$form['revision_information']['#weight'] = -100;
  }
}
?>