| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305 | From 235e57d8e944175ddfd7907598ada29c5c2ebb3d Mon Sep 17 00:00:00 2001From: Bachir Soussi Chiadmi <bachir@g-u-i.net>Date: Sat, 12 Oct 2013 13:27:26 +0200Subject: [PATCH] patched for email sending on each feedback creation https://drupal.org/node/353548#comment-7130746--- feedback-353548-45.patch | 338 +++++++++++++++++++++++++++ feedback.module          | 229 ++++++++++++++++++ feedback.module.orig     | 593 +++++++++++++++++++++++++++++++++++++++++++++++ tests/feedback.test      |  75 +++++- 4 files changed, 1231 insertions(+), 4 deletions(-) create mode 100644 feedback-353548-45.patch mode change 100755 => 100644 feedback.module create mode 100755 feedback.module.orig mode change 100755 => 100644 tests/feedback.testdiff --git a/feedback-353548-45.patch b/feedback-353548-45.patchnew file mode 100644index 0000000..999e88a--- /dev/null+++ b/feedback-353548-45.patch@@ -0,0 +1,338 @@+diff --git a/feedback.module b/feedback.module+index 9763575..ef5b24a 100644+--- a/feedback.module++++ b/feedback.module+@@ -534,6 +534,235 @@ function feedback_user_delete($account) {+ }+ + /**++ * Implements hook_action_info().++ */++function feedback_action_info() {++  return array(++    'feedback_process_action' => array(++      'label' => t('Process entry'),++      'type' => 'feedback',++      'configurable' => FALSE,++      'behavior' => array('changes_property'),++      'triggers' => array('feedback_insert', 'feedback_update'),++    ),++    'feedback_open_action' => array(++      'label' => t('Open entry'),++      'type' => 'feedback',++      'configurable' => FALSE,++      'behavior' => array('changes_property'),++      'triggers' => array('feedback_insert', 'feedback_update'),++    ),++    'feedback_delete_action' => array(++      'label' => t('Delete entry'),++      'type' => 'feedback',++      'configurable' => FALSE,++      'triggers' => array('feedback_insert', 'feedback_update'),++    ),++    'feedback_send_email_action' => array(++      'label' => t('Send e-mail of feedback'),++      'type' => 'feedback',++      'configurable' => TRUE,++      'triggers' => array('feedback_insert', 'feedback_update'),++    ),++  );++}++++/**++* Implements hook_trigger_info().++*/++function feedback_trigger_info() {++  return array(++    'feedback' => array(++      'feedback_insert' => array(++        'label' => t('New feedback is added.'),++      ),++      'feedback_update' => array(++        'label' => t('Feedback is marked processed or open.'),++      ),++    ),++  );++}++++/**++ * Implements hook_feedback_insert().++ */++function feedback_feedback_insert($entry) {++  $aids = trigger_get_assigned_actions('feedback_insert');++  if (!$aids) {++    return;++  }++++  $context = array(++    'group' => 'feedback',++    'hook' => 'feedback_insert',++  );++++  foreach ($aids as $aid => $info) {++    actions_do($aid, $entry, $context);++  }++}++++/**++ * Implements hook_feedback_update().++ */++function feedback_feedback_update($entry) {++  $aids = trigger_get_assigned_actions('feedback_update');++  if (!$aids) {++    return;++  }++++  $context = array(++    'group' => 'feedback',++    'hook' => 'feedback_update',++  );++++  foreach ($aids as $aid => $info) {++    actions_do($aid, $entry, $context);++  }++}++++/**++ * Sets the status of an entry to processed.++ *++ * @ingroup actions++ */++function feedback_process_action($entry, $context) {++  $entry->status = FEEDBACK_PROCESSED;++  drupal_write_record('feedback', $entry, 'fid');++}++++/**++ * Sets the status of an entry to open.++ *++ * @ingroup actions++ */++function feedback_open_action($entry, $context) {++  $entry->status = FEEDBACK_OPEN;++  drupal_write_record('feedback', $entry, 'fid');++}++++/**++ * Deletes a feedback entry.++ *++ * @ingroup actions++ */++function feedback_delete_action($entry, $context) {++  feedback_delete($entry->fid);++}++++/**++ * Return a form definition so the Feedback send email action can be configured.++ *++ * @param $context++ *   Default values (if we are editing an existing action instance).++ * @return++ *   Form definition.++ */++function feedback_send_email_action_form($context = array()) {++  // Set default values for form.++  $context += array(++   'recipients' => '',++   'subject' => '',++   'message' => '',++  );++++  $form['recipients'] = array(++    '#type' => 'textarea',++    '#title' => t('Recipients'),++    '#default_value' => $context['recipients'],++    '#description' => t("Example: 'webmaster@example.com' or 'dev@example.com,support@example.com'. To specify multiple recipients, separate each e-mail address with a comma."),++    '#required' => TRUE,++  );++  $form['subject'] = array(++    '#type' => 'textfield',++    '#title' => t('Subject'),++    '#default_value' => $context['subject'],++    '#maxlength' => '254',++    '#description' => t('The subject of the message.'),++    '#required' => TRUE,++  );++  $form['message'] = array(++    '#type' => 'textarea',++    '#title' => t('Message'),++    '#default_value' => $context['message'],++    '#cols' => '80',++    '#rows' => '20',++    '#description' => t('The message that should be sent. You may include the following variables: !uid, !username, !usermail, !useragent (of the user who gave the feedback), !site_name, !status, !message, !url, !date.'),++    '#required' => TRUE,++  );++  return $form;++}++++/**++ * Validate the send e-mail action form submission.++ */++function feedback_send_email_action_validate($form, &$form_state) {++  if (empty($form_state['values']['recipients'])) {++    form_set_error('recipients', t('You must enter one or more recipients.'));++  }++  else {++    $recipients = explode(',', $form_state['values']['recipients']);++    foreach ($recipients as $recipient) {++      if (!valid_email_address(trim($recipient))) {++        form_set_error('recipients', t('%recipient is an invalid e-mail address.', array('%recipient' => $recipient)));++      }++    }++  }++}++++/**++ * Process the send e-mail action form submission.++ */++function feedback_send_email_action_submit($form, $form_state) {++  // Process the HTML form to store configuration. The keyed array that++  // we return will be serialized to the database.++  $params = array(++    'recipients' => $form_state['values']['recipients'],++    'subject'    => $form_state['values']['subject'],++    'message'    => $form_state['values']['message'],++  );++  return $params;++}++++/**++ * Implements the feedback send e-mail action.++ */++function feedback_send_email_action($entry, $context) {++  $account = user_load($entry->uid);++  $from = variable_get('site_name', 'Drupal') . ' <' . variable_get('site_mail', '') . '>';++  $params = array('entry' => $entry, 'account' => $account, 'context' => $context);++  // Send the e-mail to the recipients using the site default language.++  drupal_mail('feedback', 'send_email_action', $context['recipients'], language_default(), $params, $from);++  watchdog('feedback', 'Feedback information sent to %recipients', array('%recipients' => $context['recipients']));++}++++/**++ * Implements hook_mail().++ */++function feedback_mail($key, &$message, $params) {++  $language = $message['language'];++  $entry = $params['entry'];++  $account = $params['account'];++  $context = $params['context'];++  $variables = array(++    '!site_name' => variable_get('site_name', 'Drupal'),++    '!uid' => $account->uid,++    '!username' => $account->name ? $account->name : t('Anonymous'),++    '!usermail' => $account->mail ? $account->mail : t('unknown'),++    '!status' => $entry->status ? t('Processed') : t('Open'),++    '!message' => $entry->message,++    '!url' => url($entry->location, array('absolute' => TRUE, 'language' => $language)),++    '!useragent' => $entry->useragent,++    '!date' => format_date($entry->timestamp, 'small', '', NULL, $language->language),++  );++  $subject = strtr($context['subject'], $variables);++  $body = strtr($context['message'], $variables);++  $message['subject'] .= str_replace(array("\r", "\n"), '', $subject);++  $message['body'][] = drupal_html_to_text($body);++}++++++/**+  * Implements hook_mollom_form_list().+  */+ function feedback_mollom_form_list() {+diff --git a/tests/feedback.test b/tests/feedback.test+index 4d4244c..09ce922 100644+--- a/tests/feedback.test++++ b/tests/feedback.test+@@ -20,8 +20,7 @@ class FeedbackTestCase extends DrupalWebTestCase {+   }+ +   function setUp() {+-    // @todo Remove soft-dependency on Block.+-    parent::setUp(array('block', 'feedback'));++    parent::setUp(array('feedback', 'trigger'));+ +     $this->admin_user = $this->drupalCreateUser(array('access feedback form', 'view feedback messages', 'administer feedback'));+     $this->web_user = $this->drupalCreateUser(array('access feedback form'));+@@ -49,7 +48,7 @@ class FeedbackTestCase extends DrupalWebTestCase {+     $edit = array(+       'feedback-messages[0][1]' => TRUE,+     );+-    $this->drupalPost(NULL, $edit, t('Submit'));++    $this->drupalPost(NULL, $edit, t('Update'));+     $this->assertFieldByName('feedback-messages[1][1]', 1, t('Processed message found.'));+   }+ +@@ -89,4 +88,72 @@ class FeedbackTestCase extends DrupalWebTestCase {+     $this->assertNoLinkByHref('admin/reports/feedback/1/delete');+     $this->assertNoRaw(check_plain($message), t('Message not found.'));+   }+-}++++  /**++   * Test the feedback triggers and actions.++   */++  function testFeedbackEmailAction() {++    $test_user = $this->drupalCreateUser(array('administer actions', 'administer feedback', 'access feedback form'));++    $this->drupalLogin($test_user);++    $this->assignFeedbackEmailAction('feedback_insert', $test_user->mail);++++    // Insert a feedback entry.++    $message = $this->randomString();++    $edit = array(++      'message' => $message,++    );++    $this->drupalPost('node', $edit, t('Send feedback'));++    $this->assertFeedbackEmailTokenReplacement($test_user->mail, $message);++++    $this->assignFeedbackEmailAction('feedback_update', $test_user->mail);++++    // Update a feedback entry.++    $fid = db_query("SELECT fid FROM {feedback} LIMIT 0,1")->fetchField();++    $entry = feedback_load($fid);++    feedback_save($entry);++    $this->assertFeedbackEmailTokenReplacement($test_user->mail, $entry->message);++  }++++  /**++   * Assigns a system_send_email_action to the passed-in trigger.++   *++   * @param $trigger++   *   For example, 'user_login'++   */++  function assignFeedbackEmailAction($trigger, $mail) {++    $form_name = "trigger_{$trigger}_assign_form";++    $form_html_id = strtr($form_name, '_', '-');++++    $edit = array(++      'actions_label' => $trigger . "_feedback_send_email_action",++      'recipients' => $mail,++      'subject' => $this->randomName(),++      'message' => '!message',++    );++++    $hash = drupal_hash_base64('feedback_send_email_action');++    $this->drupalPost("admin/config/system/actions/configure/$hash", $edit, t('Save'));++    $this->assertText(t('The action has been successfully saved.'));++++    // Now we have to find out the action ID of what we created.++    $aid = db_query('SELECT aid FROM {actions} WHERE callback = :callback AND label = :label', array(':callback' => 'feedback_send_email_action', ':label' => $edit['actions_label']))->fetchField();++++    $edit = array('aid' => drupal_hash_base64($aid));++    $this->drupalPost('admin/structure/trigger/feedback', $edit, t('Assign'), array(), array(), $form_html_id);++  }++++++  /**++   * Asserts correct token replacement for the given trigger and account.++   *++   * @param $account++   *   The user account which triggered the action.++   * @param $email_depth++   *   Number of emails to scan, starting with most recent.++   */++  function assertFeedbackEmailTokenReplacement($mail, $message, $email_depth = 1) {++    $this->verboseEmail($email_depth);++    $this->assertMailString('body', $message, $email_depth);++    $this->assertMail('to', $mail, t('Mail sent to correct destination'));++  }++}+\ No newline at end of filediff --git a/feedback.module b/feedback.moduleold mode 100755new mode 100644index a4acfc0..fef1eda--- a/feedback.module+++ b/feedback.module@@ -550,6 +550,235 @@ function feedback_user_delete($account) { }  /**+ * Implements hook_action_info().+ */+function feedback_action_info() {+  return array(+    'feedback_process_action' => array(+      'label' => t('Process entry'),+      'type' => 'feedback',+      'configurable' => FALSE,+      'behavior' => array('changes_property'),+      'triggers' => array('feedback_insert', 'feedback_update'),+    ),+    'feedback_open_action' => array(+      'label' => t('Open entry'),+      'type' => 'feedback',+      'configurable' => FALSE,+      'behavior' => array('changes_property'),+      'triggers' => array('feedback_insert', 'feedback_update'),+    ),+    'feedback_delete_action' => array(+      'label' => t('Delete entry'),+      'type' => 'feedback',+      'configurable' => FALSE,+      'triggers' => array('feedback_insert', 'feedback_update'),+    ),+    'feedback_send_email_action' => array(+      'label' => t('Send e-mail of feedback'),+      'type' => 'feedback',+      'configurable' => TRUE,+      'triggers' => array('feedback_insert', 'feedback_update'),+    ),+  );+}++/**+* Implements hook_trigger_info().+*/+function feedback_trigger_info() {+  return array(+    'feedback' => array(+      'feedback_insert' => array(+        'label' => t('New feedback is added.'),+      ),+      'feedback_update' => array(+        'label' => t('Feedback is marked processed or open.'),+      ),+    ),+  );+}++/**+ * Implements hook_feedback_insert().+ */+function feedback_feedback_insert($entry) {+  $aids = trigger_get_assigned_actions('feedback_insert');+  if (!$aids) {+    return;+  }++  $context = array(+    'group' => 'feedback',+    'hook' => 'feedback_insert',+  );++  foreach ($aids as $aid => $info) {+    actions_do($aid, $entry, $context);+  }+}++/**+ * Implements hook_feedback_update().+ */+function feedback_feedback_update($entry) {+  $aids = trigger_get_assigned_actions('feedback_update');+  if (!$aids) {+    return;+  }++  $context = array(+    'group' => 'feedback',+    'hook' => 'feedback_update',+  );++  foreach ($aids as $aid => $info) {+    actions_do($aid, $entry, $context);+  }+}++/**+ * Sets the status of an entry to processed.+ *+ * @ingroup actions+ */+function feedback_process_action($entry, $context) {+  $entry->status = FEEDBACK_PROCESSED;+  drupal_write_record('feedback', $entry, 'fid');+}++/**+ * Sets the status of an entry to open.+ *+ * @ingroup actions+ */+function feedback_open_action($entry, $context) {+  $entry->status = FEEDBACK_OPEN;+  drupal_write_record('feedback', $entry, 'fid');+}++/**+ * Deletes a feedback entry.+ *+ * @ingroup actions+ */+function feedback_delete_action($entry, $context) {+  feedback_delete($entry->fid);+}++/**+ * Return a form definition so the Feedback send email action can be configured.+ *+ * @param $context+ *   Default values (if we are editing an existing action instance).+ * @return+ *   Form definition.+ */+function feedback_send_email_action_form($context = array()) {+  // Set default values for form.+  $context += array(+   'recipients' => '',+   'subject' => '',+   'message' => '',+  );++  $form['recipients'] = array(+    '#type' => 'textarea',+    '#title' => t('Recipients'),+    '#default_value' => $context['recipients'],+    '#description' => t("Example: 'webmaster@example.com' or 'dev@example.com,support@example.com'. To specify multiple recipients, separate each e-mail address with a comma."),+    '#required' => TRUE,+  );+  $form['subject'] = array(+    '#type' => 'textfield',+    '#title' => t('Subject'),+    '#default_value' => $context['subject'],+    '#maxlength' => '254',+    '#description' => t('The subject of the message.'),+    '#required' => TRUE,+  );+  $form['message'] = array(+    '#type' => 'textarea',+    '#title' => t('Message'),+    '#default_value' => $context['message'],+    '#cols' => '80',+    '#rows' => '20',+    '#description' => t('The message that should be sent. You may include the following variables: !uid, !username, !usermail, !useragent (of the user who gave the feedback), !site_name, !status, !message, !url, !date.'),+    '#required' => TRUE,+  );+  return $form;+}++/**+ * Validate the send e-mail action form submission.+ */+function feedback_send_email_action_validate($form, &$form_state) {+  if (empty($form_state['values']['recipients'])) {+    form_set_error('recipients', t('You must enter one or more recipients.'));+  }+  else {+    $recipients = explode(',', $form_state['values']['recipients']);+    foreach ($recipients as $recipient) {+      if (!valid_email_address(trim($recipient))) {+        form_set_error('recipients', t('%recipient is an invalid e-mail address.', array('%recipient' => $recipient)));+      }+    }+  }+}++/**+ * Process the send e-mail action form submission.+ */+function feedback_send_email_action_submit($form, $form_state) {+  // Process the HTML form to store configuration. The keyed array that+  // we return will be serialized to the database.+  $params = array(+    'recipients' => $form_state['values']['recipients'],+    'subject'    => $form_state['values']['subject'],+    'message'    => $form_state['values']['message'],+  );+  return $params;+}++/**+ * Implements the feedback send e-mail action.+ */+function feedback_send_email_action($entry, $context) {+  $account = user_load($entry->uid);+  $from = variable_get('site_name', 'Drupal') . ' <' . variable_get('site_mail', '') . '>';+  $params = array('entry' => $entry, 'account' => $account, 'context' => $context);+  // Send the e-mail to the recipients using the site default language.+  drupal_mail('feedback', 'send_email_action', $context['recipients'], language_default(), $params, $from);+  watchdog('feedback', 'Feedback information sent to %recipients', array('%recipients' => $context['recipients']));+}++/**+ * Implements hook_mail().+ */+function feedback_mail($key, &$message, $params) {+  $language = $message['language'];+  $entry = $params['entry'];+  $account = $params['account'];+  $context = $params['context'];+  $variables = array(+    '!site_name' => variable_get('site_name', 'Drupal'),+    '!uid' => $account->uid,+    '!username' => $account->name ? $account->name : t('Anonymous'),+    '!usermail' => $account->mail ? $account->mail : t('unknown'),+    '!status' => $entry->status ? t('Processed') : t('Open'),+    '!message' => $entry->message,+    '!url' => url($entry->location, array('absolute' => TRUE, 'language' => $language)),+    '!useragent' => $entry->useragent,+    '!date' => format_date($entry->timestamp, 'small', '', NULL, $language->language),+  );+  $subject = strtr($context['subject'], $variables);+  $body = strtr($context['message'], $variables);+  $message['subject'] .= str_replace(array("\r", "\n"), '', $subject);+  $message['body'][] = drupal_html_to_text($body);+}+++/**  * Implements hook_mollom_form_list().  */ function feedback_mollom_form_list() {diff --git a/feedback.module.orig b/feedback.module.orignew file mode 100755index 0000000..a4acfc0--- /dev/null+++ b/feedback.module.orig@@ -0,0 +1,593 @@+<?php++/**+ * @file+ * Allows site visitors and users to report issues about this site.+ */++/**+ * Open state (unprocessed) for feedback entries.+ */+define('FEEDBACK_OPEN', 0);++/**+ * Processed state for feedback entries.+ */+define('FEEDBACK_PROCESSED', 1);++/**+ * Implements hook_theme().+ */+function feedback_theme() {+  return array(+    'feedback_admin_view_form' => array(+      'render element' => 'form',+    ),+    'feedback_entry' => array(+      'render element' => 'elements',+      'template' => 'feedback-entry',+      'file' => 'feedback.admin.inc',+    ),+    'feedback_form_display' => array(+      'template' => 'feedback-form-display',+      'variables' => array('title' => NULL, 'content' => NULL),+    ),+  );+}++/**+ * Implements hook_entity_info().+ */+function feedback_entity_info() {+  $return = array(+    'feedback' => array(+      'label' => t('Feedback'),+      'controller class' => 'FeedbackController',+      'base table' => 'feedback',+      'uri callback' => 'feedback_uri',+      'fieldable' => TRUE,+      'entity keys' => array(+        'id' => 'fid',+      ),+      'bundles' => array(+        'feedback' => array(+          'label' => t('Feedback'),+          'admin' => array(+            'path' => 'admin/config/user-interface/feedback',+            'access arguments' => array('administer feedback'),+          ),+        ),+      ),+      'view modes' => array(+        'full' => array(+          'label' => t('Full feedback entry'),+          'custom settings' => FALSE,+        ),+        'teaser' => array(+          'label' => t('Teaser'),+          'custom settings' => FALSE,+        ),+        'widget' => array(+          'label' => t('Widget'),+          'custom settings' => FALSE,+        ),+      ),+      // Disable Metatags (metatag) module's entity form additions.+      'metatags' => FALSE,+    ),+  );++  return $return;+}++/**+ * Implements hook_field_extra_fields().+ */+function feedback_field_extra_fields() {+  $extras['feedback']['feedback']['form']['help'] = array(+    'label' => t('Help'),+    'description' => t('Feedback submission guidelines'),+    'weight' => -10,+  );+  $extras['feedback']['feedback']['form']['messages'] = array(+    'label' => t('Entries'),+    'description' => t('Existing feedback entries for the current page'),+    'weight' => -5,+  );+  $extras['feedback']['feedback']['form']['message'] = array(+    'label' => t('Message'),+    'description' => t('Feedback message form text field'),+    'weight' => 0,+  );++  $extras['feedback']['feedback']['display']['location'] = array(+    'label' => t('Location'),+    'description' => t('The URL of the page the message was submitted on'),+    'weight' => -15,+  );+  $extras['feedback']['feedback']['display']['date'] = array(+    'label' => t('Date'),+    'description' => t('The submission date of the message'),+    'weight' => -10,+  );+  $extras['feedback']['feedback']['display']['user'] = array(+    'label' => t('User'),+    'description' => t('The name of the user who submitted the message'),+    'weight' => -5,+  );+  $extras['feedback']['feedback']['display']['message'] = array(+    'label' => t('Message'),+    'description' => t('The main feedback message'),+    'weight' => 0,+  );+  return $extras;+}++/**+ * Entity uri callback.+ */+function feedback_uri($entry) {+  return array(+    'path' => 'admin/reports/feedback/' . $entry->fid,+  );+}++/**+ * Implements hook_permission().+ */+function feedback_permission() {+  return array(+    'access feedback form' => array(+      'title' => t('Access feedback form'),+      'description' => t('Submit feedback messages.'),+    ),+    'view feedback messages' => array(+      'title' => t('View feedback messages'),+      'description' => t('View, process, and delete submitted feedback messages.'),+    ),+    'administer feedback' => array(+      'title' => t('Administer feedback settings'),+    ),+  );+}++/**+ * Implements hook_menu().+ */+function feedback_menu() {+  $items['admin/reports/feedback'] = array(+    'title' => 'Feedback messages',+    'description' => 'View feedback messages.',+    'page callback' => 'drupal_get_form',+    'page arguments' => array('feedback_admin_view_form'),+    'access arguments' => array('view feedback messages'),+    'file' => 'feedback.admin.inc',+  );+  $items['admin/reports/feedback/%feedback'] = array(+    'title' => 'Feedback entry',+    'page callback' => 'feedback_view',+    'page arguments' => array(3),+    'access arguments' => array('view feedback messages'),+    'file' => 'feedback.admin.inc',+  );+  $items['admin/reports/feedback/%feedback/view'] = array(+    'title' => 'View',+    'type' => MENU_DEFAULT_LOCAL_TASK,+    'weight' => -10,+  );+  $items['admin/reports/feedback/%feedback/edit'] = array(+    'title' => 'Edit',+    'page callback' => 'drupal_get_form',+    'page arguments' => array('feedback_entry_form', 3),+    'access arguments' => array('view feedback messages'),+    'type' => MENU_LOCAL_TASK,+    'file' => 'feedback.admin.inc',+  );+  $items['admin/reports/feedback/%feedback/delete'] = array(+    'title' => 'Delete feedback entry',+    'page callback' => 'drupal_get_form',+    'page arguments' => array('feedback_delete_confirm', 3),+    'access arguments' => array('view feedback messages'),+    'type' => MENU_CALLBACK,+    'file' => 'feedback.admin.inc',+  );+  $items['admin/config/user-interface/feedback'] = array(+    'title' => 'Feedback',+    'description' => 'Administer feedback settings.',+    'page callback' => 'drupal_get_form',+    'page arguments' => array('feedback_admin_settings_form'),+    'access arguments' => array('administer feedback'),+    'file' => 'feedback.admin.inc',+  );+  $items['admin/config/user-interface/feedback/settings'] = array(+    'title' => 'Settings',+    'type' => MENU_DEFAULT_LOCAL_TASK,+    'weight' => -10,+  );++  return $items;+}++/**+ * Implements hook_page_build().+ */+function feedback_page_build(&$page) {+  if (user_access('access feedback form') && !feedback_match_path(variable_get('feedback_excluded_paths', 'admin/reports/feedback'))) {+    $page['page_bottom']['feedback'] = array(+      '#theme' => 'feedback_form_display',+      '#title' => t('Feedback'),+      '#content' => drupal_get_form('feedback_form'),+    );+    $path = drupal_get_path('module', 'feedback');+    $page['page_bottom']['feedback']['#attached']['css'][] = $path . '/feedback.css';+    $page['page_bottom']['feedback']['#attached']['js'][] = $path . '/feedback.js';+  }+}++/**+ * Check if the current path matches any pattern in a set of patterns.+ *+ * @param $patterns+ *   String containing a set of patterns separated by \n, \r or \r\n.+ *+ * @return+ *   Boolean value: TRUE if the current path or alias matches a pattern.+ */+function feedback_match_path($patterns) {+  // Convert path to lowercase. This allows comparison of the same path+  // with different case. Ex: /Page, /page, /PAGE.+  $patterns = drupal_strtolower($patterns);++  // Convert the current path to lowercase.+  $path = drupal_strtolower(drupal_get_path_alias($_GET['q']));++  // Compare the lowercase internal and lowercase path alias (if any).+  $page_match = drupal_match_path($path, $patterns);+  if ($path != $_GET['q']) {+    $page_match = $page_match || drupal_match_path($_GET['q'], $patterns);+  }++  return $page_match;+}++/**+ * Form constructor for the feedback form.+ *+ * @see feedback_form_submit()+ * @ingroup forms+ */+function feedback_form($form, &$form_state) {+  $form['#attributes']['class'] = array('feedback-form');++  // Store the path on which this form is displayed.+  if (!isset($form_state['inline']['location'])) {+    $form_state['inline']['location'] = $_GET['q'];+  }+  $form['location'] = array(+    '#type' => 'value',+    '#value' => $form_state['inline']['location'],+  );++  $form['help'] = array(+    '#prefix' => '<div class="feedback-help">',+    '#markup' => t('If you experience a bug or would like to see an addition on the current page, feel free to leave us a message.'),+    '#suffix' => '</div>',+  );+  if (user_access('view feedback messages')) {+    if (arg(0) != 'node') {+      $feedbacks = feedback_load_multiple(array(), array('status' => FEEDBACK_OPEN, 'location_masked' => feedback_mask_path($_GET['q'])));+    }+    else {+      $feedbacks = feedback_load_multiple(array(), array('status' => FEEDBACK_OPEN, 'location' => $_GET['q']));+    }+    if ($feedbacks) {+      form_load_include($form_state, 'inc', 'feedback', 'feedback.admin');+      $form['messages'] = array(+        '#prefix' => '<div class="feedback-messages">',+        '#suffix' => '</div>',+      );+      foreach ($feedbacks as $fid => $feedback) {+        $form['messages'][$fid]['#feedback'] = $feedback;+        $form['messages'][$fid]['submitted'] = array('#markup' => t('@feedback-author !feedback-date:', array('@feedback-author' => format_username($feedback), '!feedback-date' => format_date($feedback->timestamp, 'small'))));+        $form['messages'][$fid]['submitted']['#prefix'] = '<div class="feedback-submitted">';+        $form['messages'][$fid]['submitted']['#suffix'] = '</div>';+        $form['messages'][$fid]['body'] = feedback_build_content($feedback, 'widget');+        $form['messages'][$fid]['body']['#prefix'] = '<div class="feedback-body">';+        $form['messages'][$fid]['body']['#suffix'] = '</div>';+      }+    }+  }+  $form['message'] = array(+    '#type' => 'textarea',+    '#attributes' => array('class' => array('feedback-message')),+    '#cols' => 20,+    '#title' => t('Message'),+    '#required' => TRUE,+    '#wysiwyg' => FALSE,+  );++  $entry = new stdClass();+  field_attach_form('feedback', $entry, $form, $form_state);++  $form['actions'] = array(+    '#type' => 'actions',+    // Without clearfix, the AJAX throbber wraps in an ugly way.+    // @todo Patch #type actions in core?+    '#attributes' => array('class' => array('clearfix')),+  );+  $form['actions']['submit'] = array(+    '#type' => 'submit',+    '#value' => t('Send feedback'),+    '#id' => 'feedback-submit',+    '#ajax' => array(+      'wrapper' => 'feedback-form',+      'callback' => 'feedback_form_ajax_callback',+      'progress' => array(+        'type' => 'throbber',+        'message' => '',+      ),+    ),+  );++  return $form;+}++/**+ * Form submission handler for feedback_form().+ */+function feedback_form_submit($form, &$form_state) {+  $entry = new stdClass();+  entity_form_submit_build_entity('feedback', $entry, $form, $form_state);+  $entry->message = $form_state['values']['message'];+  $entry->location = $form_state['values']['location'];+  feedback_save($entry);++  drupal_set_message(t('Thanks for your feedback!'));+}++/**+ * AJAX callback for feedback_form() submissions.+ */+function feedback_form_ajax_callback($form, &$form_state) {+  // If there was a form validation error, re-render the entire form.+  if (!$form_state['executed']) {+    return $form;+  }++  // Otherwise, return a fresh copy of the form, so the user may post additional+  // feedback.+  // Reset the static cache of drupal_html_id().+  // @see drupal_process_form()+  // @see drupal_html_id()+  $seen_ids = &drupal_static('drupal_html_id');+  $seen_ids = array();++  // Prevent the form from being processed again.+  // @see drupal_build_form()+  list($form, $new_form_state) = ajax_get_form();+  $new_form_state['input'] = array();+  drupal_process_form($form['#form_id'], $form, $new_form_state);++  // Return AJAX commands in order to output the special success message.+  // @see ajax_deliver()+  $build = array('#type' => 'ajax');+  $html = drupal_render($form);+  $build['#commands'][] = ajax_command_insert(NULL, $html);++  // A successful form submission normally means that there were no errors, so+  // we only render status messages.+  $messages = drupal_get_messages();+  $messages += array('status' => array());+  $messages = implode('<br />', $messages['status']);+  $html = '<div id="feedback-status-message">' . $messages . '</div>';+  $build['#commands'][] = ajax_command_append('#block-feedback-form', $html);+  return $build;+}++/**+ * Loads a feedback entry from the database.+ *+ * @param $fid+ *   Integer specifying the feedback ID to load.+ *+ * @return+ *   A loaded feedback entry object upon successful load, or FALSE if the entry+ *   cannot be loaded.+ *+ * @see feedback_load_multiple()+ */+function feedback_load($fid) {+  $entries = feedback_load_multiple(array($fid));+  return (isset($entries[$fid]) ? $entries[$fid] : FALSE);+}++/**+ * Loads feedback entries from the database.+ *+ * @param $fids+ *   An array of feedback entry IDs.+ * @param $conditions+ *   An associative array of conditions on the {feedback} table, where the keys+ *   are the database fields and the values are the values those fields+ *   must have.+ *+ * @return+ *   An array of feedback entry objects indexed by fid.+ *+ * @see hook_feedback_load()+ * @see feedback_load()+ * @see entity_load()+ * @see EntityFieldQuery+ */+function feedback_load_multiple($fids = array(), $conditions = array()) {+  return entity_load('feedback', $fids, $conditions);+}++/**+ * Saves changes to a feedback entry or adds a new feedback entry.+ *+ * @param $entry+ *   The feedback entry object to modify or add. If $entry->fid is omitted, a+ *   new entry will be added.+ *+ * @see hook_feedback_insert()+ * @see hook_feedback_update()+ */+function feedback_save($entry) {+  global $user;++  // Load the stored entity, if any.+  if (!empty($entry->fid) && !isset($entry->original)) {+    $entry->original = entity_load_unchanged('feedback', $entry->fid);+  }++  field_attach_presave('feedback', $entry);++  // Allow modules to alter the feedback entry before saving.+  module_invoke_all('feedback_presave', $entry);+  module_invoke_all('entity_presave', $entry, 'feedback');++  if (empty($entry->fid)) {+    $entry->message = trim($entry->message);++    $defaults = array(+      'uid' => $user->uid,+      'location_masked' => feedback_mask_path($entry->location),+      'url' => url($entry->location, array('absolute' => TRUE)),+      'timestamp' => REQUEST_TIME,+      'useragent' => $_SERVER['HTTP_USER_AGENT'],+    );+    foreach ($defaults as $key => $default) {+      if (!isset($entry->$key)) {+        $entry->$key = $default;+      }+    }++    $status = drupal_write_record('feedback', $entry);+    field_attach_insert('feedback', $entry);+    module_invoke_all('feedback_insert', $entry);+    module_invoke_all('entity_insert', $entry, 'feedback');+  }+  else {+    $status = drupal_write_record('feedback', $entry, 'fid');++    field_attach_update('feedback', $entry);+    module_invoke_all('feedback_update', $entry);+    module_invoke_all('entity_update', $entry, 'feedback');+  }+  unset($entry->original);++  return $status;+}++/**+ * Deletes a feedback entry.+ *+ * @param $fid+ *   A feedback entry ID.+ */+function feedback_delete($fid) {+  feedback_delete_multiple(array($fid));+}++/**+ * Deletes multiple feedback entries.+ *+ * @param $fids+ *   An array of feedback entry IDs.+ */+function feedback_delete_multiple($fids) {+  if (!empty($fids)) {+    $entries = feedback_load_multiple($fids);+    foreach ($entries as $fid => $entry) {+      field_attach_delete('feedback', $entry);+      module_invoke_all('feedback_delete', $entry);+      module_invoke_all('entity_delete', $entry, 'feedback');+    }+    db_delete('feedback')+      ->condition('fid', $fids, 'IN')+      ->execute();+  }+}++/**+ * 'Mask' a path, i.e. replace all numeric arguments in a path with '%' placeholders.+ *+ * Please note that only numeric arguments with a preceding slash will be+ * replaced.+ *+ * @param $path+ *   An internal Drupal path, f.e. 'user/123/edit'.+ * @return+ *   A 'masked' path, for above example 'user/%/edit'.+ *+ * @todo Use the untranslated patch of menu_get_item() instead.+ */+function feedback_mask_path($path) {+  return preg_replace('@/\d+@', '/%', $path);+}++/**+ * Implements hook_user_cancel().+ */+function feedback_user_cancel($edit, $account, $method) {+  switch ($method) {+    case 'user_cancel_reassign':+      db_update('feedback')+        ->fields(array('uid' => 0))+        ->condition('uid', $account->uid)+        ->execute();+      break;+  }+}++/**+ * Implements hook_user_delete().+ */+function feedback_user_delete($account) {+  $fids = db_query('SELECT fid FROM {feedback} WHERE uid = :uid', array(':uid' => $account->uid))->fetchCol();+  feedback_delete_multiple($fids);+}++/**+ * Implements hook_mollom_form_list().+ */+function feedback_mollom_form_list() {+  $forms['feedback_form'] = array(+    'title' => t('Feedback form'),+    'entity' => 'feedback',+    'bundle' => 'feedback',+    'entity delete multiple callback' => 'feedback_delete_multiple',+    'delete form' => 'feedback_delete_confirm',+    'delete form file' => array(+      'name' => 'feedback.admin',+    ),+    'report access' => array('view feedback messages'),+  );+  return $forms;+}++/**+ * Implements hook_mollom_form_info().+ */+function feedback_mollom_form_info($form_id) {+  $form_info = array(+    'mode' => MOLLOM_MODE_ANALYSIS,+    'bypass access' => array('administer feedback'),+    'elements' => array(+      'message' => t('Message'),+    ),+  );+  mollom_form_info_add_fields($form_info, 'feedback', 'feedback');+  return $form_info;+}++/**+ * Implements hook_views_api();+ */+function feedback_views_api() {+  return array(+    'api' => 3.0,+    'path' => drupal_get_path('module', 'feedback') . '/views',+  );+}diff --git a/tests/feedback.test b/tests/feedback.testold mode 100755new mode 100644index 4d4244c..09ce922--- a/tests/feedback.test+++ b/tests/feedback.test@@ -20,8 +20,7 @@ class FeedbackTestCase extends DrupalWebTestCase {   }    function setUp() {-    // @todo Remove soft-dependency on Block.-    parent::setUp(array('block', 'feedback'));+    parent::setUp(array('feedback', 'trigger'));      $this->admin_user = $this->drupalCreateUser(array('access feedback form', 'view feedback messages', 'administer feedback'));     $this->web_user = $this->drupalCreateUser(array('access feedback form'));@@ -49,7 +48,7 @@ class FeedbackTestCase extends DrupalWebTestCase {     $edit = array(       'feedback-messages[0][1]' => TRUE,     );-    $this->drupalPost(NULL, $edit, t('Submit'));+    $this->drupalPost(NULL, $edit, t('Update'));     $this->assertFieldByName('feedback-messages[1][1]', 1, t('Processed message found.'));   } @@ -89,4 +88,72 @@ class FeedbackTestCase extends DrupalWebTestCase {     $this->assertNoLinkByHref('admin/reports/feedback/1/delete');     $this->assertNoRaw(check_plain($message), t('Message not found.'));   }-}++  /**+   * Test the feedback triggers and actions.+   */+  function testFeedbackEmailAction() {+    $test_user = $this->drupalCreateUser(array('administer actions', 'administer feedback', 'access feedback form'));+    $this->drupalLogin($test_user);+    $this->assignFeedbackEmailAction('feedback_insert', $test_user->mail);++    // Insert a feedback entry.+    $message = $this->randomString();+    $edit = array(+      'message' => $message,+    );+    $this->drupalPost('node', $edit, t('Send feedback'));+    $this->assertFeedbackEmailTokenReplacement($test_user->mail, $message);++    $this->assignFeedbackEmailAction('feedback_update', $test_user->mail);++    // Update a feedback entry.+    $fid = db_query("SELECT fid FROM {feedback} LIMIT 0,1")->fetchField();+    $entry = feedback_load($fid);+    feedback_save($entry);+    $this->assertFeedbackEmailTokenReplacement($test_user->mail, $entry->message);+  }++  /**+   * Assigns a system_send_email_action to the passed-in trigger.+   *+   * @param $trigger+   *   For example, 'user_login'+   */+  function assignFeedbackEmailAction($trigger, $mail) {+    $form_name = "trigger_{$trigger}_assign_form";+    $form_html_id = strtr($form_name, '_', '-');++    $edit = array(+      'actions_label' => $trigger . "_feedback_send_email_action",+      'recipients' => $mail,+      'subject' => $this->randomName(),+      'message' => '!message',+    );++    $hash = drupal_hash_base64('feedback_send_email_action');+    $this->drupalPost("admin/config/system/actions/configure/$hash", $edit, t('Save'));+    $this->assertText(t('The action has been successfully saved.'));++    // Now we have to find out the action ID of what we created.+    $aid = db_query('SELECT aid FROM {actions} WHERE callback = :callback AND label = :label', array(':callback' => 'feedback_send_email_action', ':label' => $edit['actions_label']))->fetchField();++    $edit = array('aid' => drupal_hash_base64($aid));+    $this->drupalPost('admin/structure/trigger/feedback', $edit, t('Assign'), array(), array(), $form_html_id);+  }+++  /**+   * Asserts correct token replacement for the given trigger and account.+   *+   * @param $account+   *   The user account which triggered the action.+   * @param $email_depth+   *   Number of emails to scan, starting with most recent.+   */+  function assertFeedbackEmailTokenReplacement($mail, $message, $email_depth = 1) {+    $this->verboseEmail($email_depth);+    $this->assertMailString('body', $message, $email_depth);+    $this->assertMail('to', $mail, t('Mail sent to correct destination'));+  }+}\ No newline at end of file-- 2.3.5
 |