contact_attach.module in Contact Attach 7
Same filename and directory in other branches
Allows attaching files to messages sent using contact forms.
This module gives users the ability of attaching files to messages sent using the site-wide contact form or a user's personal contact form.
File
contact_attach.moduleView source
<?php
/**
* @file
* Allows attaching files to messages sent using contact forms.
*
* This module gives users the ability of attaching files to messages sent using
* the site-wide contact form or a user's personal contact form.
*/
/**
* Default number of attachments on contact forms.
*/
define('CONTACT_ATTACH_DEFAULT_NUMBER', 1);
/**
* Default attachment extensions that will be permitted.
*/
define('CONTACT_ATTACH_DEFAULT_EXTENSIONS', '');
/**
* Default maximum attachment size that will be permitted, in megabytes.
*/
define('CONTACT_ATTACH_DEFAULT_UPLOADSIZE', 0.0009765625);
/**
* Implements hook_help().
*/
function contact_attach_help($path, $arg) {
switch ($path) {
case 'admin/config/media/contact_attach':
return '<p>' . t('The roles that are listed here are those that have the necessary permissions to attach files on the specified contact form. To make a role appear here so that the settings for that role can be changed, grant the necessary permissions in the module\'s section on the <a href="@permissions">permissions</a> page.', array(
'@permissions' => url('admin/people/permissions'),
)) . '</p>';
}
}
/**
* Implements hook_permission().
*/
function contact_attach_permission() {
$allowed_permissions = array(
'attach files on site-wide contact form' => array(
'title' => t('Attach files on the site-wide contact form'),
'description' => t('Send messages with attachments from the site-wide contact form.'),
),
'attach files on personal contact forms' => array(
'title' => t('Attach files on personal contact forms'),
'description' => t('Send messages with attachments from personal contact forms.'),
),
);
return $allowed_permissions;
}
/**
* Implements hook_menu().
*/
function contact_attach_menu() {
$items['admin/config/media/contact_attach'] = array(
'title' => 'Contact form attachments',
'description' => 'Configure settings for attaching files on contact forms.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'contact_attach_admin_settings',
),
'access arguments' => array(
'administer site configuration',
),
'file' => 'contact_attach.admin.inc',
);
return $items;
}
/**
* Implements hook_form_alter().
*/
function contact_attach_form_alter(&$form, &$form_state, $form_id) {
if ($form_id === 'contact_personal_form' && user_access('attach files on personal contact forms') || $form_id === 'contact_site_form' && user_access('attach files on site-wide contact form')) {
switch ($form_id) {
case 'contact_site_form':
$contact_form_short = 'site';
$contact_form_permission = 'attach files on site-wide contact form';
break;
case 'contact_personal_form':
$contact_form_short = 'user';
$contact_form_permission = 'attach files on personal contact forms';
break;
}
$contact_attach_numbers = variable_get('contact_attach_number_' . $contact_form_short, array());
$roles = _contact_attach_get_valid_roles($contact_form_permission, $contact_attach_numbers);
if (module_exists('file') && variable_get('contact_attach_simple_field', 0) !== 1) {
$file_field_type = 'managed_file';
}
else {
$file_field_type = 'file';
}
// Send these values along to the form validation and submit handlers.
$form['file_field_type'] = array(
'#type' => 'value',
'#value' => $file_field_type,
);
$form['attachments_allowed'] = array(
'#type' => 'value',
'#value' => _contact_attach_return_max_attachments($roles, $contact_attach_numbers),
);
$form['allowed_extensions'] = array(
'#type' => 'value',
'#value' => _contact_attach_return_allowed_extensions($roles, $contact_form_short),
);
$form['file_size_limit'] = array(
'#type' => 'value',
'#value' => _contact_attach_return_max_file_size($roles, $contact_form_short),
);
$description = t('Files must be less than !size.', array(
'!size' => '<strong>' . format_size($form['file_size_limit']['#value']) . '</strong>',
));
$description .= '<br />' . t('Allowed file types: !extensions.', array(
'!extensions' => '<strong>' . $form['allowed_extensions']['#value'] . '</strong>',
));
if ($form['attachments_allowed']['#value'] !== 1) {
$form['attachments'] = array(
'#type' => 'fieldset',
'#title' => t('Attachments'),
);
$form['attachments']['#description'] = $description;
}
for ($i = 1; $i <= $form['attachments_allowed']['#value']; $i++) {
$form['attachments']['contact_attach_' . $i] = array(
'#type' => $form['file_field_type']['#value'],
'#weight' => $i,
);
if ($form['attachments_allowed']['#value'] !== 1) {
$form['attachments']['contact_attach_' . $i]['#title'] = t('Attachment #@i', array(
'@i' => $i,
));
}
else {
$form['attachments']['contact_attach_' . $i]['#title'] = t('Attachment');
$form['attachments']['contact_attach_' . $i]['#description'] = $description;
}
if ($form['file_field_type']['#value'] === 'managed_file') {
$form['attachments']['contact_attach_' . $i]['#upload_validators'] = array(
'file_validate_extensions' => array(
$form['allowed_extensions']['#value'],
),
'file_validate_size' => array(
(string) $form['file_size_limit']['#value'],
),
);
$form['attachments']['contact_attach_' . $i]['#progress_message'] = t('Attaching...');
}
}
// Use only our form submission handler.
$form['#submit'] = array(
'contact_attach_' . $form_id . '_submit',
);
$form['actions']['submit']['#weight'] = $i + 1;
if ($form['file_field_type']['#value'] === 'file') {
// Add a form validation handler to validate attachments.
$form['#validate'][] = 'contact_attach_contact_form_validate';
}
}
}
/**
* Form validation handler for contact_site_form() and contact_personal_form().
*
* Validates the attachments.
*
* @see contact_attach_contact_site_form_submit()
* @see contact_attach_contact_personal_form_submit()
* @see contact_attach_form_alter()
*/
function contact_attach_contact_form_validate($form, &$form_state) {
$validators = array(
'file_validate_extensions' => array(
$form_state['values']['allowed_extensions'],
),
'file_validate_size' => array(
$form_state['values']['file_size_limit'],
),
);
// Loop through each possible attachment.
foreach ($_FILES['files']['name'] as $temp_name => $file_name) {
$file = file_save_upload($temp_name, $validators);
if ($file === FALSE) {
form_set_error($temp_name, t('Failed to attach the file %name.', array(
'%name' => $file_name,
)));
}
}
}
/**
* Form submission handler for contact_site_form().
*
* Overrides contact_site_form_submit().
*
* @see contact_attach_contact_form_validate()
* @see contact_attach_form_alter()
*/
function contact_attach_contact_site_form_submit($form, &$form_state) {
global $user, $language;
$values = $form_state['values'];
$values['sender'] = $user;
$values['sender']->name = $values['name'];
$values['sender']->mail = $values['mail'];
$values['category'] = contact_load($values['cid']);
// Save the anonymous user information to a cookie for reuse.
if (!$user->uid) {
user_cookie_save(array_intersect_key($values, array_flip(array(
'name',
'mail',
))));
}
// Get the to and from e-mail addresses.
$to = $values['category']['recipients'];
$from = $values['sender']->mail;
// Send the e-mail to the recipients using the site default language.
$results['mail'] = drupal_mail('contact', 'page_mail', $to, language_default(), $values, $from);
// If the user requests it, send a copy using the current language.
if ($values['copy']) {
$results['copy'] = drupal_mail('contact', 'page_copy', $from, $language, $values, $from);
}
// Send an auto-reply if necessary using the current language.
if ($values['category']['reply']) {
$results['autoreply'] = drupal_mail('contact', 'page_autoreply', $from, $language, $values, $to);
}
if (!empty($results['mail']['result'])) {
flood_register_event('contact', variable_get('contact_threshold_window', 3600));
watchdog('mail', '%sender-name (@sender-from) sent an e-mail regarding %category.', array(
'%sender-name' => $values['name'],
'@sender-from' => $from,
'%category' => $values['category']['category'],
));
$user_message = t('Your message has been sent.');
if ($values['copy'] && empty($results['copy']['result'])) {
watchdog('mail', 'The mail system failed to send a copy of the message to the site-wide contact form user.', array(), WATCHDOG_ERROR);
$user_message .= ' ' . t('However, the copy asked for failed to send.');
}
if ($values['category']['reply'] && empty($results['autoreply']['result'])) {
watchdog('mail', 'The mail system failed to send an auto-reply to the site-wide contact form user.', array(), WATCHDOG_ERROR);
}
drupal_set_message($user_message);
}
else {
watchdog('mail', '%sender-name (@sender-from) attempted to send an e-mail regarding %category, but was unsuccessful.', array(
'%sender-name' => $values['name'],
'@sender-from' => $from,
'%category' => $values['category']['category'],
), WATCHDOG_ERROR);
drupal_set_message(t('There was a problem sending your message. Please try again later.'), 'error');
}
// Jump to home page rather than back to contact page to avoid contradictory
// messages if flood control has been activated.
$form_state['redirect'] = '';
}
/**
* Form submission handler for contact_personal_form().
*
* Overrides contact_personal_form_submit().
*
* @see contact_attach_contact_form_validate()
* @see contact_attach_form_alter()
*/
function contact_attach_contact_personal_form_submit($form, &$form_state) {
global $user, $language;
$values = $form_state['values'];
$values['sender'] = $user;
$values['sender']->name = $values['name'];
$values['sender']->mail = $values['mail'];
// Save the anonymous user information to a cookie for reuse.
if (!$user->uid) {
user_cookie_save(array_intersect_key($values, array_flip(array(
'name',
'mail',
))));
}
// Get the to and from e-mail addresses.
$to = $values['recipient']->mail;
$from = $values['sender']->mail;
// Send the e-mail in the requested user language.
$results['mail'] = drupal_mail('contact', 'user_mail', $to, user_preferred_language($values['recipient']), $values, $from);
// Send a copy if requested, using current page language.
if ($values['copy']) {
$results['copy'] = drupal_mail('contact', 'user_copy', $from, $language, $values, $from);
}
if (!empty($results['mail']['result'])) {
flood_register_event('contact', variable_get('contact_threshold_window', 3600));
watchdog('mail', '%sender-name (@sender-from) sent %recipient-name an e-mail.', array(
'%sender-name' => $values['name'],
'@sender-from' => $from,
'%recipient-name' => $values['recipient']->name,
));
$user_message = t('Your message has been sent.');
if ($values['copy'] && empty($results['copy']['result'])) {
watchdog('mail', 'The mail system failed to send a copy of the message to the personal contact form user.', array(), WATCHDOG_ERROR);
$user_message .= ' ' . t('However, the copy asked for failed to send.');
}
drupal_set_message($user_message);
}
else {
watchdog('mail', '%sender-name (@sender-from) attempted to send %recipient-name an e-mail, but was unsuccessful.', array(
'%sender-name' => $values['name'],
'@sender-from' => $from,
'%recipient-name' => $values['recipient']->name,
), WATCHDOG_ERROR);
drupal_set_message(t('There was a problem sending your message. Please try again later.'), 'error');
}
// Jump to the contacted user's profile page if the user is allowed.
$form_state['redirect'] = user_access('access user profiles') ? 'user/' . $values['recipient']->uid : '';
}
/**
* Implements hook_mail_alter().
*/
function contact_attach_mail_alter(&$message) {
if (isset($message['params']['attachments_allowed'])) {
switch ($message['id']) {
case 'contact_page_mail':
case 'contact_page_copy':
case 'contact_user_mail':
case 'contact_user_copy':
$return_message = _contact_attach_process_attachments($message);
if (!empty($return_message)) {
$message['headers'] = $return_message['headers'];
$message['body'] = $return_message['body'];
}
break;
}
}
}
/**
* Checks for attachments and processes them, if one or more exist.
*
* @param array $message
* The message, as it exists so far.
*
* @return array
* The message, including processed attachment(s).
*/
function _contact_attach_process_attachments($message) {
$return_message = array();
if ($message['params']['file_field_type'] === 'managed_file') {
// Loop through each possible attachment when using managed_file fields.
for ($i = 1; $i <= $message['params']['attachments_allowed']; $i++) {
if ($message['params']['contact_attach_' . $i] !== 0) {
// An attachment exists, so save it to an array for later processing.
$files[] = file_load($message['params']['contact_attach_' . $i]);
}
}
}
else {
// Loop through each possible attachment when using simple file fields.
foreach ($_FILES['files']['name'] as $temp_name => $file_name) {
if ($file = file_save_upload($temp_name)) {
// Check to see if the attachment exists.
if ($file->filesize > 0) {
// An attachment exists, so save it to an array for later processing.
$files[] = $file;
}
}
}
}
// If the array contains something, we have one or more attachments to
// process. If it does not contain anything, we send back an empty $body,
// indicating no attachments exist.
if (!empty($files)) {
// Set initial values.
$attachments = '';
$body_text = '';
$boundary_id = md5(uniqid(time()));
$mail_system = variable_get('mail_system', array());
$message['headers']['Content-Type'] = 'multipart/mixed; boundary="' . $boundary_id . '"';
// Add the body text.
$body_text = "\n--{$boundary_id}\n";
$body_text .= "Content-Type: text/plain; charset=UTF-8; format=flowed;\n\n";
$body_text .= implode("\n\n", $message['body']);
$body_text .= "\n\n\n";
// Add the attachments.
// Loop through each possible attachment.
foreach ($files as $file_object) {
// Process the attachment.
$attachments .= "--{$boundary_id}\n";
$attachments .= _contact_attach_add_attachment($file_object, $mail_system);
$attachments .= "\n\n";
}
$attachments .= "--{$boundary_id}--\n\n";
$return_message['headers'] = $message['headers'];
$return_message['body'][0] = $body_text;
$return_message['body'][1] = $attachments;
}
return $return_message;
}
/**
* Returns a fully-encoded attachment ready to be included into a message body.
*
* @param object $file
* An attachment to add to the message.
* @param array $mail_system
* (optional) An associative array containing the contents of the persistent
* variable mail_system. Defaults to array().
* @param bool $delete
* (optional) A boolean indicating if the file will be deleted after it has
* been base64 encoded. Tests can set this to FALSE so that they can use the
* file to compare against after the message has been sent. Defaults to TRUE.
*
* @return string
* The processed attachment, ready for appending to the message.
*/
function _contact_attach_add_attachment($file, $mail_system = array(), $delete = TRUE) {
$attachment = 'Content-Type: ' . $file->filemime . '; name="' . basename($file->filename) . "\"\n";
$attachment .= "Content-Transfer-Encoding: base64\n";
// SMTP module pulls the file path from the filename attribute in the header,
// so it can not contain only the file name if the SMTP module is used.
if (!empty($mail_system) && $mail_system['default-system'] === 'SmtpMailSystem') {
$attachment .= 'Content-Disposition: attachment; filename="' . $file->uri . "\"\n\n";
}
else {
$attachment .= 'Content-Disposition: attachment; filename="' . $file->filename . "\"\n\n";
}
if (file_exists($file->uri)) {
$attachment .= chunk_split(base64_encode(file_get_contents($file->uri)));
}
else {
$attachment .= chunk_split(base64_encode(file_get_contents(file_directory_temp() . '/' . $file->filename)));
}
if ($delete) {
// Delete the file after it has been embedded, as it no longer serves a
// purpose. Drupal deletes them after DRUPAL_MAXIMUM_TEMP_FILE_AGE has
// passed on the next cron run, but we can save Drupal the trouble by
// cleaning up after ourselves. This can also be important for privacy.
file_delete($file);
}
return $attachment;
}
/**
* Gets active user's valid roles to be considered in aggregation of settings.
*
* @param string $contact_form_permission
* The contact form permission to check permissions against.
* @param array $contact_attach_numbers
* An associative array of the number of attachments allowed for each role.
*
* @return array
* An associative array of the active user's valid roles that will be
* considered in the aggregation and overriding of settings.
*/
function _contact_attach_get_valid_roles($contact_form_permission, $contact_attach_numbers) {
global $user;
$permitted_roles = user_roles(FALSE, $contact_form_permission);
if (count($user->roles) === 1) {
$roles = array_keys($user->roles);
}
elseif (array_key_exists(DRUPAL_AUTHENTICATED_RID, $user->roles) && array_key_exists(DRUPAL_AUTHENTICATED_RID, $permitted_roles)) {
// If the user has the authenticated user role and it is permitted to attach
// files, all of the user's roles are valid, as all created roles inherit
// this role. Hence, no use in checking if they have permissions.
$roles = $user->roles;
// Exclude the authenticated user role when the user has settings set by
// other roles, as all created users automatically get this role and it
// should not override the settings for the explicitly assigned role.
$has_specific_setting = FALSE;
foreach ($roles as $rid => $name) {
if ($rid !== DRUPAL_AUTHENTICATED_RID && array_key_exists($rid, $contact_attach_numbers)) {
$has_specific_setting = TRUE;
break;
}
}
if (array_key_exists(DRUPAL_AUTHENTICATED_RID, $roles) && $has_specific_setting) {
unset($roles[DRUPAL_AUTHENTICATED_RID]);
}
$roles = array_keys($roles);
}
else {
// Figure out which of the user's roles are permitted to add attachments.
$roles = array_keys(array_intersect_assoc($user->roles, $permitted_roles));
}
return $roles;
}
/**
* Returns maximum number of attachments allowed based on all supplied roles.
*
* @param array $roles
* An associative array of the active user's valid roles.
* @param array $contact_attach_numbers
* An associative array containing the number of allowed attachments for every
* role that has this setting defined.
*
* @return int
* The maximum number of attachments allowed based on all supplied roles.
*/
function _contact_attach_return_max_attachments($roles, $contact_attach_numbers) {
$attachments_allowed = 1;
foreach ($roles as $rid) {
$attachments_allowed_role = !empty($contact_attach_numbers[$rid]) ? (int) $contact_attach_numbers[$rid] : CONTACT_ATTACH_DEFAULT_NUMBER;
if ($attachments_allowed_role > $attachments_allowed) {
$attachments_allowed = $attachments_allowed_role;
}
}
return $attachments_allowed;
}
/**
* Returns allowed extensions for attachments based on all supplied roles.
*
* @param array $roles
* An associative array of the active user's valid roles.
* @param string $contact_form
* Short form of the contact form to return allowed extensions for.
*
* @return string
* An aggregated set of allowed extensions based on all supplied roles.
*/
function _contact_attach_return_allowed_extensions($roles, $contact_form) {
$extensions = '';
$contact_attach_extensions = variable_get('contact_attach_extensions_' . $contact_form, array());
// Build an aggregated set of allowed extensions.
foreach ($roles as $rid) {
// Concatenate the role's allowed file extensions with what we already have.
$extensions .= !empty($contact_attach_extensions[$rid]) ? $contact_attach_extensions[$rid] . ' ' : CONTACT_ATTACH_DEFAULT_EXTENSIONS;
}
// Remove duplicates from the aggregated set of allowed extensions.
$extensions = rtrim(implode(' ', array_unique(explode(' ', $extensions))));
return $extensions;
}
/**
* Returns the allowed file size for attachments based on all supplied roles.
*
* @param array $roles
* An associative array of the active user's valid roles.
* @param string $contact_form
* Short form of the contact form to return the maximum allowed file size for.
*
* @return float
* The maximum allowed file size for attachments based on all supplied roles.
*/
function _contact_attach_return_max_file_size($roles, $contact_form) {
$file_size_limit = 0;
$contact_attach_uploadsizes = variable_get('contact_attach_uploadsize_' . $contact_form, array());
foreach ($roles as $rid) {
// Get the role's allowed file size.
$file_size_limit_role = (!empty($contact_attach_uploadsizes[$rid]) ? (double) $contact_attach_uploadsizes[$rid] : CONTACT_ATTACH_DEFAULT_UPLOADSIZE) * 1024 * 1024;
// If the role's allowed file size is greater than what we already have, use
// it instead.
if ($file_size_limit_role > $file_size_limit) {
$file_size_limit = $file_size_limit_role;
}
}
return $file_size_limit;
}
Functions
Name | Description |
---|---|
contact_attach_contact_form_validate | Form validation handler for contact_site_form() and contact_personal_form(). |
contact_attach_contact_personal_form_submit | Form submission handler for contact_personal_form(). |
contact_attach_contact_site_form_submit | Form submission handler for contact_site_form(). |
contact_attach_form_alter | Implements hook_form_alter(). |
contact_attach_help | Implements hook_help(). |
contact_attach_mail_alter | Implements hook_mail_alter(). |
contact_attach_menu | Implements hook_menu(). |
contact_attach_permission | Implements hook_permission(). |
_contact_attach_add_attachment | Returns a fully-encoded attachment ready to be included into a message body. |
_contact_attach_get_valid_roles | Gets active user's valid roles to be considered in aggregation of settings. |
_contact_attach_process_attachments | Checks for attachments and processes them, if one or more exist. |
_contact_attach_return_allowed_extensions | Returns allowed extensions for attachments based on all supplied roles. |
_contact_attach_return_max_attachments | Returns maximum number of attachments allowed based on all supplied roles. |
_contact_attach_return_max_file_size | Returns the allowed file size for attachments based on all supplied roles. |
Constants
Name | Description |
---|---|
CONTACT_ATTACH_DEFAULT_EXTENSIONS | Default attachment extensions that will be permitted. |
CONTACT_ATTACH_DEFAULT_NUMBER | Default number of attachments on contact forms. |
CONTACT_ATTACH_DEFAULT_UPLOADSIZE | Default maximum attachment size that will be permitted, in megabytes. |