Module Study: CCK Email Field

Creating Custom CCK Fields

A Sample CCK Field

20 APIs in 20 Days

* email.info *

; $Id: email.info,v 1.1.2.2.4.1 2008/04/15 19:23:59 mh86 Exp $
name = Email
description = Defines an email field type for cck
dependencies[] = content
package = CCK
core = 6.x

; Information added by drupal.org packaging script on 2009-06-13
version = "6.x-1.2"
core = "6.x"
project = "email"
datestamp = "1244921128"
 

* email.install *

<?php

/**
 * Implementation of hook_install().
 */
function email_install() {
  drupal_load('module', 'content');
  content_notify('install', 'email');
}

/**
 * Implementation of hook_uninstall().
 */
function email_uninstall() {
  drupal_load('module', 'content');
  content_notify('uninstall', 'email');
}

/**
 * Implementation of hook_enable().
 *
 * Notify content module when this module is enabled.
 */
function email_enable() {
  drupal_load('module', 'content');
  content_notify('enable', 'email');
}

/**
 * Implementation of hook_disable().
 *
 * Notify content module when this module is disabled.hook_disable()
 */
function email_disable() {
  drupal_load('module', 'content');
  content_notify('disable', 'email');
}

/**
 * Implemenation of hook_update_N();
 *
 * Update from 5.x to 6.x
 *
 * Set all columns to accept NULL values and set empty string values in the
 * database to NULL.
 */
function email_update_6001() {
  if ($abort = content_check_update('email')) {
    return $abort;
  }
 
  drupal_load('module', 'content');
  $ret = array();
  $ret[] = update_sql("UPDATE {". content_instance_tablename() ."} SET widget_type = 'email_textfield' WHERE widget_type = 'email'");
  content_associate_fields('email');
  content_clear_type_cache(TRUE);

  include_once(drupal_get_path('module', 'content') .'/content.install');
  $types = content_types_install();
  foreach ($types as $type_name => $fields) {
    foreach ($fields as $field) {
      switch ($field['type']) {
        case 'email':
          $db_info = content_database_info($field);
          $table = $db_info['table'];
          foreach ($db_info['columns'] as $column => $attributes) {
            $attributes['not null'] = FALSE;
            $column = $attributes['column'];
            db_change_field($ret, $table, $column, $column, $attributes);
            db_field_set_no_default($ret, $table, $column);
            $ret[] = update_sql("UPDATE {". $table ."} SET ". $column ." = NULL WHERE ". $column ." = ''");

          }
          break;
      }
    }
  }
  return $ret;
}
 

* email.module *

<?php

// $Id: email.module,v 1.9.2.8.4.12 2009/06/13 19:01:32 mh86 Exp $

/**
 * Implementation of hook_theme().
 */
function email_theme() {
  return array(
    'email_textfield' => array(
      'arguments' => array('element' => NULL),
    ),
    'email_formatter_default' => array(
      'arguments' => array('element' => NULL),
    ),
    'email_formatter_spamspan' => array(
      'arguments' => array('element' => NULL),
    ),
    'email_formatter_contact' => array(
      'arguments' => array('element' => NULL),
    ),
  );
}

/**
 * Implementation of hook_field_info().
 */
function email_field_info() {
  return array(
    'email' => array(
      'label' => 'Email',
      'callbacks' => array(
        'tables' => CONTENT_CALLBACK_DEFAULT,
        'arguments' => CONTENT_CALLBACK_DEFAULT,
      ),
    ),
  );
}

/**
 * Implementation of hook_field_settings().
 */
function email_field_settings($op, $field) {
  switch ($op) {

    /* "database columns": Declare the columns that content.module should create and manage on behalf of the field. If the field module wishes to handle */
    case 'database columns':
      $columns['email'] = array('type' => 'varchar', 'length' => 255, 'not null' => FALSE, 'sortable' => TRUE);
      return $columns;
  }
}

/**
 * Implementation of hook_field().
 */
function email_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) {
        $items[$delta]['safe'] = check_plain($item['email']);
      }
      break;
  }
}

/**
 * Implementation of hook_content_is_empty().
 */
function email_content_is_empty($item, $field) {
  if (empty($item['email'])) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Implementation of hook_field_formatter_info().
 *
 */
function email_field_formatter_info() {
  $formats = array(
    'default' => array(
      'label' => t('Default email link'),
      'field types' => array('email'),
      'multiple values' => CONTENT_HANDLE_CORE,
    ),
    'contact' => array(
      'label' => t('Email contact form'),
      'field types' => array('email'),
      'multiple values' => CONTENT_HANDLE_CORE,
    ),
  );
  if (module_exists('spamspan')) {
    $formats += array(
      'spamspan' => array(
        'label' => t('Email SpamSpan'),
        'field types' => array('email'),
        'multiple values' => CONTENT_HANDLE_CORE,
      ),
    );
  }
  return $formats;
}

/**
 * Theme function for 'default' email field formatter.
 */
function theme_email_formatter_default($element) {
  return !empty($element['#item']['safe']) ? '<a href="mailto:'. $element['#item']['email'] .'">'. $element['#item']['safe'] .'</a>' : '';
}

/**
 * Theme function for 'spamspan' email field formatter.
 */
function theme_email_formatter_spamspan($element) {
  if (empty($element['#item']['safe'])) {
    return '';
  }
  if (module_exists('spamspan')) {
    return spamspan($element['#item']['email']);
  }
  else {
    return '<a href="mailto:'. $element['#item']['email'] .'">'. $element['#item']['safe'] .'</a>';
  }
}

/**
 * Theme function for 'contact' email field formatter.
 */
function theme_email_formatter_contact($element) {
  return !empty($element['#item']['safe']) ? l(t('Email contact form'), 'email/'. $element['#node']->nid .'/'. $element['#field_name']) : '';
}

/**
 * Implementation of hook_widget_info().
 */
function email_widget_info() {
  return array(
    'email_textfield' => array(
      'label' => t('Text field'),
      'field types' => array('email'),
      'multiple values' => CONTENT_HANDLE_CORE,
      'callbacks' => array(
        'default value' => CONTENT_CALLBACK_DEFAULT,
      ),
    ),
  );
}

/**
 * Implementation of FAPI hook_elements().
 */
function email_elements() {
  return array(
    'email_textfield' => array(
      '#input' => TRUE,
      '#columns' => array('email'),
      '#delta' => 0,
      '#process' => array('email_textfield_process'),
    ),
  );
}

/**
 * Implementation of hook_widget_settings().
 */
function email_widget_settings($op, $widget) {
  switch ($op) {
    case 'form':
      $size = (isset($widget['size']) && is_numeric($widget['size'])) ? $widget['size'] : 60;
      $form['size'] = array(
        '#type' => 'textfield',
        '#title' => t('Size of textfield'),
        '#default_value' => $size,
        '#element_validate' => array('_email_widget_settings_size_validate'),
        '#required' => TRUE,
      );
      return $form;
      
    case 'save':
      return array('size');
  }
}

function _email_widget_settings_size_validate($element, &$form_state) {
  $value = $form_state['values']['size'];
  if (!is_numeric($value) || intval($value) != $value || $value <= 0) {
    form_error($element, t('"Size" must be a positive integer.'));
  }
}

/**
 * Implementation of hook_widget().
 */
function email_widget(&$form, &$form_state, $field, $items, $delta = 0) {
  $element = array(
    '#type' => $field['widget']['type'],
    '#default_value' => isset($items[$delta]) ? $items[$delta] : '',
  );
  return $element;
}

/**
 * Process an individual element.
 */
function email_textfield_process($element, $edit, $form_state, $form) {
  $field = $form['#field_info'][$element['#field_name']];
  $field_key = $element['#columns'][0];
  $delta = $element['#delta'];
  $element[$field_key] = array(
    '#type' => 'textfield',
    '#title' => $element['#title'],
    '#description' => content_filter_xss($field['widget']['description']),
    '#required' => $element['#required'],
    '#maxlength' => 255,
    '#size' => !empty($field['widget']['size']) ? $field['widget']['size'] : 60,
    '#attributes' => array('class' => 'text', 'dir' => 'ltr'),
    '#default_value' => isset($element['#value'][$field_key]) ? $element['#value'][$field_key] : NULL,
  );
  return $element;
}

/**
 * FAPI theme for an individual text elements.
 */
function theme_email_textfield($element) {
  return $element['#children'];
}

/**
 * Implementation of hook_menu().
 */
function email_menu() {
  $items['email/%/%'] = array(
    'title' => 'Email Contact Form',
    'page callback' => 'email_mail_page',
    'page arguments' => array(1, 2),
    'access callback' => 'user_access',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
  );
  $items['admin/settings/email'] = array(
    'title' => 'CCK Email Contact Form Settings',
    'description' => 'Administer flood control settings for email contact forms',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('email_admin_settings'),
    'access arguments' => array('administer site configuration'),
  );
  return $items;
}

/**
 * The contact form page.
 */
function email_mail_page($nid, $field_name) {
  if (!is_numeric($nid)) {
    drupal_not_found();
    return;
  }
  if (module_exists('content_permissions')) {
    if (!user_access('view '. $field_name)) {
      drupal_not_found();
      return;
    }
  }
  $node = node_load(intval($nid));
  if (!$node) {
    drupal_not_found();
    return;
  }
  $field = $node->$field_name;
  $email = $field[0]['email'];
  $types = content_types($node->type);
  // Validate field name
  $types = content_types($node->type);
  if (empty($email) ||
      !isset($types['fields'][$field_name]) ||
      $types['fields'][$field_name]['type'] != 'email' ||
      ($types['fields'][$field_name]['display_settings']['teaser']['format'] != 'contact' &&
      $types['fields'][$field_name]['display_settings']['full']['format'] != 'contact')) {
    drupal_not_found();
    return;
  }

  if (!flood_is_allowed('email', variable_get('email_hourly_threshold', 3))) {
    $output = t("You cannot send more than %number messages per hour. Please try again later.", array('%number' => variable_get('email_hourly_threshold', 3)));
  }
  else {
    $output = drupal_get_form('email_mail_page_form', $node, $field_name, $email);
  }

  return $output;
}

/**
 * Contact form
 */
function email_mail_page_form($form_state, $node, $field_name, $email) {
  global $user;

  $form['node'] = array(
    '#type' => 'value',
    '#value' => $node,
  );
  $form['field_name'] = array(
    '#type' => 'value',
    '#value' => $field_name,
  );
  $form['email'] = array(
    '#type' => 'value',
    '#value' => $email,
  );
  $form['name'] = array(
    '#type' => 'textfield',
    '#title' => t('Your name'),
    '#maxlength' => 255,
    '#default_value' => $user->uid ? $user->name : '',
    '#required' => TRUE,
  );
  $form['mail'] = array(
    '#type' => 'textfield',
    '#title' => t('Your e-mail address'),
    '#maxlength' => 255,
    '#default_value' => $user->uid ? $user->mail : '',
    '#required' => TRUE,
  );
  $form['subject'] = array(
    '#type' => 'textfield',
    '#title' => t('Subject'),
    '#maxlength' => 255,
    '#required' => TRUE,
  );
  $form['message'] = array(
    '#type' => 'textarea',
    '#title' => t('Message'),
    '#required' => TRUE,
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Send e-mail'),
    '#validate' => array('email_mail_page_form_validate'),
    '#submit' => array('email_mail_page_form_submit'),
  );
  return $form;
}

/**
 * Validate the site-wide contact page form submission.
 */
function email_mail_page_form_validate($form, &$form_state) {
  if (!valid_email_address($form_state['values']['mail'])) {
    form_set_error('mail', t('You must enter a valid e-mail address.'));
  }
  if (preg_match("/\r|\n/", $form_state['values']['subject'])) {
    form_set_error('subject', t('The subject cannot contain linebreaks.'));
    watchdog('mail', 'Email injection exploit attempted in email form subject: '. check_plain($form_state['values']['subject']), WATCHDOG_NOTICE);
  }
}

/**
 * Process the site-wide contact page form submission.
 */
function email_mail_page_form_submit($form, &$form_state) {
  $node = $form_state['values']['node'];
  $field_name = $form_state['values']['field_name'];
  $email = $form_state['values']['email'];
 
  // E-mail address of the sender: as the form field is a text field,
  // all instances of \r and \n have been automatically stripped from it.

  $from = $form_state['values']['mail'];
 
  $params['node'] = $node;
  $params['subject'] = $form_state['values']['subject'];
  $params['name'] = $form_state['values']['name'];
  $params['message'] = $form_state['values']['message'];
  $params['url'] = url('node/' . $node->nid, array('absolute' => TRUE));
   
  // Send the e-mail to the recipients:
  drupal_mail('email', 'contact', $email, language_default(), $params, $from);
 
  // Log the operation:
  flood_register_event('email');
  watchdog('mail', t('%name-from sent an e-mail at %form.', array('%name-from' => theme('placeholder', $form_state['values']['name'] ." <$from>"), '%form' => url($_GET['q'], array('absolute' => TRUE)))));
 
  // Update user:
  drupal_set_message(t('Your message has been sent.'));
 
  // Jump to home page rather than back to contact page to avoid contradictory messages if flood control has been activated.
  $form_state['redirect'] = 'node/'. $node->nid;
}

/**
 * Implementation of hook_mail()
 */
function email_mail($key, &$message, $params) {
  $language = $message['language'];
  switch($key) {
    case 'contact':
      $node = $params['node'];
      // Compose the body:
      $msg[] = t('@name sent a message using the contact form at @node.', array('@name' => $params['name'], '@node' => $params['url']), $language->language);
      $msg[] = $params['message'];

      // Tidy up the body:
      foreach ($msg as $index_key => $value) {
        $msg[$index_key] = wordwrap($value);
      }

      // Prepare the body:
      $message['body'] = implode("\n\n", $msg);
      
      $message['subject'] = t('[@title] @subject', array('@title' => preg_replace("/\r|\n/",'', $node->title), '@subject' => $params['subject']), $language->language);
      break;
  }
}

/**
 * Implementation of hook token_list
 */
function email_token_list($type = 'all') {
  if ($type == 'field' || $type == 'all') {
    $tokens['email']['raw']       = t('Raw email address');
    $tokens['email']['formatted'] = t('Formatted email address');
    return $tokens;
  }
}

/**
 * Implementation of hook token_values
 */
function email_token_values($type, $object = NULL, $options = array()) {
  if ($type == 'field') {
    $item = $object[0];
    $tokens['raw']       = $item['email'];
    $tokens['formatted'] = $item['view'];
    return $tokens;
  }
}

/**
 * Settings for contact form
 */
function email_admin_settings() {
  $form['email_hourly_threshold'] = array('#type' => 'select',
    '#title' => t('Hourly threshold for a CCK Email contact form'),
    '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50)),
    '#default_value' => variable_get('email_hourly_threshold', 3),
    '#description' => t('The maximum number of contact form submissions a user can perform per hour.'),
  );
  return system_settings_form($form);
}
 

X
You may login with either your assigned username or your e-mail address.
The password field is case sensitive.
Loading
randomness