You are here

feedback.module in Feedback 5

Enables a site-wide feedback page.

File

feedback.module
View source
<?php

define('FEEDBACK_MAX_NAME', 64);
define('FEEDBACK_MAX_SUBJECT', 64);
define('FEEDBACK_MAX_BODY', 1000);
define('FEEDBACK_MAX_POSTAL', 200);

/**
 * @file
 * Enables a site-wide feedback page.
 */

/**
* Implementation of hook_menu().
*/
function feedback_menu($may_cache) {
  $items = array();
  if ($may_cache) {
    $items[] = array(
      'path' => 'feedback',
      'title' => t('Feedback'),
      'callback' => 'feedback_mail_page',
      'access' => user_access('can send feedback'),
      'type' => MENU_SUGGESTED_ITEM,
    );
    $items[] = array(
      'path' => 'admin/settings/feedback',
      'title' => t('Feedback'),
      'description' => t('Configure the feedback page'),
      'callback' => 'feedback_settings_page',
      'access' => user_access('administer feedback'),
      'type' => MENU_NORMAL_ITEM,
    );
  }
  return $items;
}

/**
 * Implementation of hook_perm().
 */
function feedback_perm() {
  return array(
    'can send feedback',
    'administer feedback',
  );
}

/**
 * feedback admin center
 */
function feedback_settings_page() {
  $page = arg(3);
  if (empty($page)) {
    $output = feedback_settings_overview($page);
  }
  else {
    if (arg(4) == 'delete' && $page != 'default') {
      $output = feedback_settings_delete($page);
    }
    else {
      $output = drupal_get_form('feedback_settings_form', $page);
    }
  }
  print theme('page', $output);
}

/**
 * admin settings overview page
 */
function feedback_settings_overview($page) {

  //show feedback pages overview
  $header = array(
    t('Feedback page name'),
    t('Feedback page location'),
    '',
  );
  $pages = _feedback_get_page_names();
  foreach ($pages as $page) {
    $delete = $page != 'default' ? ' ' . l(t('delete'), 'admin/settings/feedback/' . $page . '/delete') : '';
    $path = $page != 'default' ? 'feedback/' . $page : 'feedback';
    $rows[] = array(
      $page,
      l(url($path), $path),
      l(t('edit'), 'admin/settings/feedback/' . $page) . $delete,
    );
  }
  $output = theme('table', $header, $rows);
  $output .= drupal_get_form('feedback_settings_overview_add', $pages);
  return $output;
}
function feedback_settings_overview_add($pages) {
  $form['group']['page_name'] = array(
    '#type' => 'textfield',
    '#title' => t('Feedback page name'),
    '#size' => 50,
    '#maxlength' => 60,
    '#required' => TRUE,
  );
  $form['group'][] = array(
    '#type' => 'submit',
    '#value' => t('Add page'),
  );
  $form['group'] += array(
    '#type' => 'fieldset',
    '#title' => t('Add a further feedback page'),
  );
  $form['pages'] = array(
    '#type' => 'value',
    '#value' => $pages,
  );
  return $form;
}
function feedback_settings_overview_add_validate($form_id, &$edit) {
  if (in_array($edit['page_name'], $edit['pages'])) {
    form_set_error('page_name', t('This page already exists.'));
  }
}
function feedback_settings_overview_add_submit($form_id, &$edit) {
  _feedback_add_page($edit['page_name']);
  drupal_set_message(t('Your feedback page has been added.'));
}

/**
 * Shows a form for editing a feedback page
 */
function feedback_settings_form($pagename) {
  $page = _feedback_get_page($pagename);
  $form['email'] = array(
    '#type' => 'textfield',
    '#title' => t('Default Email Address'),
    '#default_value' => $page->email,
    '#size' => 80,
    '#maxlength' => 300,
    '#description' => t('The email address which should receive all form submissions'),
    '#required' => TRUE,
  );
  $form['title'] = array(
    '#type' => 'textfield',
    '#title' => t('Title of the feedback page'),
    '#default_value' => $page->title,
    '#size' => 80,
    '#maxlength' => 80,
    '#required' => TRUE,
  );
  $form['instructions'] = array(
    '#type' => 'textarea',
    '#title' => t('Instructions'),
    '#default_value' => $page->instructions,
    '#cols' => 60,
    '#rows' => 10,
    '#description' => t('The instructions that will be displayed for the user on how to fill the form'),
  );
  $form['subject'] = array(
    '#type' => 'fieldset',
    '#title' => t('Subject'),
  );
  $form['subject']['subject_prefix'] = array(
    '#type' => 'textfield',
    '#title' => t('Subject Prefix'),
    '#default_value' => $page->subject_prefix,
    '#size' => 80,
    '#maxlength' => 80,
    '#description' => t('The prefix that should be added before the subject on each email'),
  );
  $form['subject']['field_subject'] = array(
    '#type' => 'checkbox',
    '#title' => t('Include a subject textfield'),
    '#return_value' => 1,
    '#default_value' => $page->field_subject,
  );
  $form['subject']['field_category'] = array(
    '#type' => 'textarea',
    '#title' => t('Categories'),
    '#default_value' => $page->field_category,
    '#cols' => 40,
    '#rows' => 6,
    '#description' => t('Put each subject category on a separate line or separate them by commas.  No HTML allowed.') . '<br />' . t('Leave this field empty to disable this feature.'),
  );
  $form['fields'] = array(
    '#type' => 'fieldset',
    '#title' => t('Fields to include on the form'),
  );
  $form['fields']['field_name'] = array(
    '#type' => 'checkbox',
    '#title' => t('Sender Name'),
    '#return_value' => 1,
    '#default_value' => $page->field_name,
  );
  $form['fields']['field_postal'] = array(
    '#type' => 'checkbox',
    '#title' => t('Postal Address'),
    '#return_value' => 1,
    '#default_value' => $page->field_postal,
  );
  $form['fields']['field_phone'] = array(
    '#type' => 'checkbox',
    '#title' => t('Phone Number'),
    '#return_value' => 1,
    '#default_value' => $page->field_phone,
  );
  $form['fields']['field_body'] = array(
    '#type' => 'checkbox',
    '#title' => t('Message Body'),
    '#return_value' => 1,
    '#default_value' => $page->field_body,
  );
  $form['misc'] = array(
    '#type' => 'fieldset',
    '#title' => t('Miscellaneous Settings'),
  );
  $form['misc']['hourly_threshold'] = array(
    '#type' => 'select',
    '#title' => t('Hourly threshold'),
    '#default_value' => $page->hourly_threshold,
    '#options' => drupal_map_assoc(array(
      1,
      2,
      3,
      4,
      5,
      6,
      7,
      8,
      9,
      10,
      20,
      30,
      40,
      50,
    )),
    '#description' => t('The maximum number of form submissions a user can perform per hour.'),
  );
  $form['misc']['logging'] = array(
    '#type' => 'checkbox',
    '#title' => t('Log all feedback attempts to watchdog'),
    '#return_value' => 1,
    '#default_value' => $page->logging,
  );
  $form['page'] = array(
    '#type' => 'value',
    '#value' => $page,
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save configuration'),
  );
  return $form;
}
function feedback_settings_form_submit($form_id, $edit) {
  $page = $edit['page'];
  unset($edit['submit'], $edit['form_id'], $edit['page']);
  db_query("UPDATE {feedback_pages} SET data = '%s' WHERE name = '%s'", serialize($edit), $page->name);
  drupal_set_message(t('The configuration options have been saved.'));
  return array(
    'admin/settings/feedback',
  );
}

/**
 * Deletes a feedback page, if the user confirms
 */
function feedback_settings_delete($pagename) {
  if ($_POST['op'] == t('Delete')) {
    db_query("DELETE FROM {feedback_pages} WHERE name ='%s'", $pagename);
    drupal_set_message(t('The feedback page has been deleted.'));
    drupal_goto('admin/settings/feedback');
  }
  else {
    $message = t('Are you sure you want to delete the feedback page "!page"?', array(
      '!page' => $pagename,
    ));
    $output = drupal_get_form('confirm_form', array(), $message, 'admin/settings/feedback', $message, t('Delete'), t('Cancel'));
    return $output;
  }
}

/**
 * Site-wide feedback page
 */
function feedback_mail_page() {
  if ($pagename = arg(1)) {

    //check if this feedback page exists
    $pages = _feedback_get_page_names();
    if (!in_array($pagename, $pages)) {
      drupal_not_found();
      exit;
    }
  }
  else {
    $pagename = 'default';
  }
  $page = _feedback_get_page($pagename);
  $breadcrumb[] = array(
    'path' => 'feedback',
    'title' => $page->title,
  );
  menu_set_location($breadcrumb);
  drupal_set_title($page->title);
  if (!flood_is_allowed('feedback' . $page->name, $page->hourly_threshold)) {
    $message = t('You cannot send more than !number messages per hour. Please try again later.', array(
      '!number' => $page->hourly_threshold,
    ));
    drupal_set_message($message, 'error');
    return;
  }
  return drupal_get_form('feedback_mail_form', $page);
}
function feedback_mail_form($page) {
  global $user;
  $form['instructions'] = array(
    '#value' => $page->instructions,
  );
  if ($page->field_name) {
    $form['emailname'] = array(
      '#type' => 'textfield',
      '#title' => t('Your full name'),
      '#default_value' => $user->uid ? $user->name : '',
      '#size' => 60,
      '#maxlength' => 64,
      '#description' => NULL,
      '#attributes' => NULL,
      '#required' => TRUE,
    );
  }
  $form['mail'] = array(
    '#type' => 'textfield',
    '#title' => t('Your e-mail address'),
    '#default_value' => $user->uid ? $user->mail : '',
    '#size' => 60,
    '#maxlength' => 64,
    '#description' => NULL,
    '#attributes' => NULL,
    '#required' => TRUE,
  );
  if ($page->field_postal) {
    $form['postal'] = array(
      '#type' => 'textarea',
      '#title' => t('Your postal address'),
      '#cols' => 50,
      '#rows' => 4,
    );
  }
  if ($page->field_phone) {
    $form['phone'] = array(
      '#type' => 'textfield',
      '#title' => t('Your phone number'),
      '#size' => 60,
      '#maxlength' => 64,
    );
  }
  if ($page->field_subject) {
    $form['subject'] = array(
      '#type' => 'textfield',
      '#title' => t('Subject'),
      '#size' => 60,
      '#maxlength' => 64,
      '#description' => NULL,
      '#attributes' => NULL,
      '#required' => TRUE,
    );
  }
  if ($page->field_category) {
    $form['category'] = array(
      '#type' => 'select',
      '#title' => t('Category'),
      '#options' => _feedback_get_options($page->field_category),
      '#description' => NULL,
      '#extra' => 0,
      '#multiple' => FALSE,
      '#required' => TRUE,
    );
  }
  if ($page->field_body) {
    $form['body'] = array(
      '#type' => 'textarea',
      '#title' => t('Message'),
      '#cols' => 60,
      '#rows' => 15,
      '#description' => NULL,
      '#attributes' => NULL,
      '#required' => TRUE,
    );
  }
  $form['referer'] = array(
    '#type' => 'hidden',
    '#value' => $_SERVER[HTTP_REFERER],
  );
  $form['op'] = array(
    '#type' => 'submit',
    '#value' => t('Send message'),
  );
  $form['page'] = array(
    '#type' => 'value',
    '#value' => $page,
  );
  return $form;
}

/**
 * Validate the site-wide form submission.
 */
function feedback_mail_form_validate($form_id, &$edit) {
  $page = $edit['page'];
  if (!valid_email_address($edit['mail'])) {
    form_set_error('mail', t('You must enter a valid e-mail address.'));
  }
  if (preg_match("/\r|\n/", $edit['subject'])) {
    form_set_error('subject', t('The subject cannot contain linebreaks.'));
    watchdog('mail', 'Email injection exploit attempted in feedback form subject: ' . check_plain($edit['subject']), WATCHDOG_NOTICE);
  }
  if (!$edit['emailname'] && $page->field_name) {
    form_set_error('emailname', t('Please enter your full name'));
  }
  else {
    if ($page->field_name && drupal_strlen($edit['emailname']) > FEEDBACK_MAX_NAME) {
      form_set_error('emailname', t('Too long input.'));
    }
  }
  if ($page->field_category && (!$edit['category'] || !in_array($edit['category'], _feedback_get_options($page->field_category)))) {
    form_set_error('category', t('Please choose a category'));
  }
  if (!$edit['subject'] && $page->field_subject) {
    form_set_error('subject', t('Please enter a subject'));
  }
  else {
    if ($page->field_subject && drupal_strlen($edit['subject']) > FEEDBACK_MAX_SUBJECT) {
      form_set_error('subject', t('Too long input.'));
    }
  }
  if (!$edit['body'] && $page->field_body) {
    form_set_error('body', t('Please enter your message.'));
  }
  else {
    if ($page->field_body && drupal_strlen($edit['body']) > FEEDBACK_MAX_BODY) {
      form_set_error('body', t('Please shorten your input. Up to !limit characters are allowed.', array(
        '!limit' => FEEDBACK_MAX_BODY,
      )));
    }
  }
  if ($page->field_postal && drupal_strlen($edit['postal']) > FEEDBACK_MAX_POSTAL) {
    form_set_error('postal', t('Please shorten your input. Up to !limit characters are allowed.', array(
      '!limit' => FEEDBACK_MAX_POSTAL,
    )));
  }
  if ($page->field_phone && drupal_strlen($edit['phone']) > 64) {
    form_set_error('phone', t('Too long input.'));
  }
}

/**
 * Process the site-wide form submission.
 */
function feedback_mail_form_submit($form_id, &$edit) {

  // Prepare the sender:
  $from = feedback_plain_text($edit['mail']);
  $page = $edit['page'];

  // Compose the body:
  if ($page->field_category) {
    $message[] = t('Category') . ': ' . check_plain($edit['category']);
  }
  if ($page->field_body) {
    $message[] = feedback_plain_text($edit['body']);
  }
  $message[] = t('Sent from @form by @name.', array(
    '@name' => $edit['emailname'],
    '@form' => url($_GET['q'], NULL, NULL, TRUE),
  ));
  $message[] = _feedback_user_info($edit, $page);

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

  // Prepare the body:
  $body = implode("\n\n", $message);
  $category = $page->field_category ? ' [' . $edit['category'] . '] ' : ' ';
  $subject = $page->subject_prefix . $category . $edit['subject'];

  // Send the e-mail to the recipients:
  drupal_mail('feedback', $page->email, $subject, $body, $from);

  // Log the operation:
  flood_register_event('feedback' . $page->name);
  if ($page->logging) {
    watchdog('mail', t('@name-from sent a feedback e-mail', array(
      '@name-from' => $edit['emailname'] . " <{$from}>",
    )));
  }

  // Update user:
  drupal_set_message(t('Your message has been sent.'));

  // Jump to home page rather than back to feedback page to avoid contradictory messages if flood control has been activated.
  drupal_goto();
}

/*
 * Returns some additional user information to append to the mail
 */
function _feedback_user_info(&$edit, $page) {
  global $user;
  $info = "\n" . t('Site Name') . ': ' . variable_get('site_name', '');
  if ($user->name) {
    $info .= "\n" . t('Registered name') . ': ' . feedback_plain_text($user->name);
  }
  if ($page->field_name) {
    $info .= "\n" . t('Entered Name') . ': ' . feedback_plain_text($edit['emailname']);
  }
  $info .= "\n" . t('E-mail address') . ': ' . $edit['mail'];
  if ($page->field_postal) {
    $info .= "\n" . t('Postal address') . ': ' . feedback_plain_text($edit['postal']);
  }
  if ($page->field_phone) {
    $info .= "\n" . t('Phone Number') . ': ' . feedback_plain_text($edit['phone']);
  }
  if ($edit['referer']) {
    $info .= "\n" . t('Referring page') . ': ' . feedback_plain_text($edit['referer']);
  }
  if (isset($_SERVER['REMOTE_ADDR'])) {
    $info .= "\n" . t('IP Address') . ': ' . 'http://whois.domaintools.com/' . feedback_plain_text($_SERVER['REMOTE_ADDR']);
  }
  if (isset($_SERVER['REMOTE_HOST'])) {
    $info .= "\n" . t('Machine name') . ': ' . feedback_plain_text($_SERVER['REMOTE_HOST']);
  }
  if (isset($_SERVER['HTTP_USER_AGENT'])) {
    $info .= "\n" . t('Browser info') . ': ' . feedback_plain_text($_SERVER['HTTP_USER_AGENT']);
  }
  return $info;
}

/*
 * Strips tags and makes entities readable
 */
function feedback_plain_text($string) {
  return html_entity_decode(strip_tags($string), ENT_QUOTES);
}
function _feedback_get_options($text) {
  $options = array(
    '0' => '--',
  );
  $lines = split("[,\n\r]", $text);
  foreach ($lines as $line) {
    if ($line = trim($line)) {
      $options[$line] = $line;
    }
  }
  return $options;
}

/*
 * Returns an array of all available feedback page names
 */
function _feedback_get_page_names() {
  $result = db_query('SELECT name from {feedback_pages}');
  $pages = array();
  while ($row = db_fetch_object($result)) {
    $pages[] = $row->name;
  }
  return $pages;
}

/*
 * Returns a data object from a feedback page
 */
function _feedback_get_page($name) {
  $result = db_query("SELECT * from {feedback_pages} WHERE name = '%s'", $name);
  if ($row = db_fetch_array($result)) {
    $row += (array) unserialize($row['data']);
    unset($row['data']);
  }
  return (object) $row;
}
function _feedback_add_page($pagename) {
  $defaults = array(
    'email' => variable_get('site_mail', ini_get('sendmail_from')),
    'title' => t('feedback'),
    'hourly_threshold' => 3,
    'logging' => 1,
    'field_name' => 1,
    'field_body' => 1,
  );
  db_query("INSERT INTO {feedback_pages} (name, data) VALUES('%s','%s')", $pagename, serialize($defaults));
}