background image

Content tagged with: Mail

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

For my blog, I thought it would be great if I was notified when a comment was submitted. Like most of my articles, I try to explain how to accomplish something by implementing your own module hooks. In this article, I'll explain how I modified the comment form using a form_alter hook, and added an additional submit handler to send an email using drupal_mail.

First, I defined a form_alter hook to modify the comment form submit handlers

<?php
function MYMODULE_form_alter(&$form, $form_state, $form_id) {
 
// test for comment form
 
if ($form_id == 'comment_form') {
   
// add an additional submit handler
   
$form['#submit'][] = '_MYMODULE_comment_form_submit';
  }
}
?>

Next, I created the submit handler function

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

 
// create an array of parameters
 
$params = array(
   
// NOTE: I tried using l() here,
    // but links don't work very well in text emails
       
    // this link will send me back to the node with an anchor to the comment
   
'commentLink' => 'http://' . $_SERVER['HTTP_HOST'] . base_path() .
     
$form_state['redirect'][0] . '#' . $form_state['redirect'][2]
  );
   
 
// call mail function and send email
 
drupal_mail('MYMODULE', 'comment_submitted',
   
variable_get('site_mail','MYDEFAULTEMAIL'), language_default(), $params);

}
?>

Lastly, I defined the mail hook

<?php
function MYMODULE_mail($key, &$message, $params) {
 
$language = $message['language'];
  switch(
$key) {
    case
'comment_submitted':
           
     
// create an array of variables from the parameters argument
     
$variables = array(
       
'!commentLink' => $params['commentLink'],
      );
           
     
// define subject and body
     
$message['subject'] = t("A comment has been submitted");
     
$message['body'] = t("!commentLink", $variables, $language->language);
           
      break;
  }
}
?>

Now when a comment is posted, my submit handler is called which notifies me of the new comment.

Eric's picture

Back in September 2008, I wrote an article on how to configure your virtual machine to deliver email locally (using postfix, cyrus, imap, and sasl). I've had to revisit this article recently to test some bulk emailing functionality. I wanted to change my email server configuration to deliver all email locally to ensure clients and coworkers do not receive test emails. After reading a bunch of web articles, I decided to use Postfix's transport functionality (located /etc/postfix/transport). This configuration file allows you to map email addresses and hostnames to message delivery transports.

I edited this file (/etc/postfix/transport) and added the following to the end of the file:

* discard:

I then edited my postfix configuration file (/etc/postfix/main.cf) and added the following:

transport_maps = hash:/etc/postfix/transport
always_bcc = eric

Reload the transport and restart postfix using the following commands:

postmap /etc/postfix/transport
/etc/init.d/postfix restart

The first configuration change discards all outgoing email, and the second automatically BCC's my user. Although this is a drastic configuration change, it does exactly what I want: it ensures that email will never be delivered to real world addresses, and any email sent from my development server will end up in my local inbox.

You may have to tweak these settings to find a configuration that works with your development situation. For instance, if you wanted to continue delivery to a certain domain, you could add the following transport:

your.domain :

Eric's picture

A recent task that came my way way to create the functionality to capture email addresses using a simple subscription form. Since email is not a reliable means of capturing data, I used CCK to create nodes to capture the data and a view to show the results. First, I create a CCK node type called "email_subscription" which did not have a body or any custom fields, just the title for email address. I then created a view that shows the nodes in a table. Here's the module I created that generates a block module that contains the CCK node form. The block uses the CCK node form to capture the data. When the node is created, it uses the nodeapi hook to send a conformation email. There is also a function to validate the user's email address.

<?php
function MYMODULE_subscribe_block($op='list', $delta=0) {
 
// listing of blocks, such as on the admin/block page
 
if ($op == "list") {
   
$block[0]["info"] = t('Subscribe Block');
    return
$block;
  } else if (
$op == 'view') {
   
$block['subject'] = 'Subscribe';
   
$block['content'] = _MYMODULE_subscribe_block_main();
    return
$block;
  }
}

function
_MYMODULE_subscribe_block_main() {
 
// show the cck node form
 
return drupal_get_form('email_subscription_node_form',
    array(
     
'type' => 'email_subscription',
     
'uid' => $GLOBALS['user']->uid,
     
'name' => $GLOBALS['user']->name,
    )
  );
}

function
MYMODULE_subscribe_form_alter($form_id, &$form) {
  if (
$form_id == 'email_subscription_node_form') {
   
// remove preview button
   
unset($form['preview']);

   
// add custom validation
   
$form['#validate'] = array_merge(array('_MYMODULE_subscribe_node_form_validate' => array()), $form['#validate']);
  }
}

function
_MYMODULE_subscribe_node_form_validate($form_id, $form_values, $form) {
 
// validate title as email address
 
if (!valid_email_address($form_values['title'])) form_set_error('title', 'Email is not valid.');

 
// get list of already submitted emails
 
$sql = "select title from {node} where type = 'email_subscription'";

 
// if editing an existing node:
 
if ($form_values['nid']) $sql .= " and title!='" . db_escape_string($form_values['title']) . "'";

 
$resource = db_query($sql);
 
$results = array();
  while (
$row = db_fetch_array($resource)) $results[] = $row['title'];

 
// ensure email address has not already been subscribed
 
if (in_array($form_values['title'],$results)) form_set_error('title', 'Email has already been subscribed.');

}

function
MYMODULE_subscribe_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
  if (
$node->type == 'email_subscription' && $op=='insert') {
   
// send email to user
   
mail($node->title, variable_get('site_name') . ' Subscription', 'Thank you for subscribing!', "From: " . variable_get('site_mail'));
  }
}
?>

Eric's picture

Here's a quick debugging technique to capture your output and email it to yourself. This can be useful when you cannot send your debug output to the browser.

<?php
// start output buffer
ob_start();

// display the contents of your variable
print_r($YOURVARIABLE);

// get the contents of the output buffer
$ob = ob_get_contents();

// stop output buffer
ob_end_clean();

// mail contents of output buffer to yourself
mail('YOUREMAIL','Output Buffer Contents',$ob);
?>