diff --git feedback-entry.tpl.php feedback-entry.tpl.php new file mode 100644 index 0000000..4059a7f --- /dev/null +++ feedback-entry.tpl.php @@ -0,0 +1,30 @@ + +
> +
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+ + +
diff --git feedback.admin.inc feedback.admin.inc index 6256b76..dff926d 100644 --- feedback.admin.inc +++ feedback.admin.inc @@ -125,3 +125,105 @@ function feedback_admin_view_form_submit($form, &$form_state) { } } +/** + * Form builder; The general feedback settings form. + * + * Currently this form is a necessity as a parent for the field local tasks. + * + * @ingroup forms + */ +function feedback_admin_settings_form($form, &$form_state) { + drupal_set_message(t('There are no feedback settings yet, but you can configure fields.')); + $form = array(); + return $form; +} + +/** + * Generate an array for rendering the given entry. + * + * @param $entry + * A feedback entry object. + * @param $view_mode + * View mode, e.g. 'full'. + * @param $langcode + * (optional) A language code to use for rendering. Defaults to the global + * content language of the current request. + * + * @return + * An array as expected by drupal_render(). + */ +function feedback_view($entry, $view_mode = 'full', $langcode = NULL) { + if (!isset($langcode)) { + $langcode = $GLOBALS['language_content']->language; + } + + // Retrieve all entry fields and attach to $entry->content. + feedback_build_content($entry, $view_mode, $langcode); + + $build = $entry->content; + // We don't need duplicate rendering info in entry->content. + unset($entry->content); + + $build += array( + '#theme' => 'feedback_entry', + '#entry' => $entry, + '#view_mode' => $view_mode, + '#language' => $langcode, + ); + + // Allow modules to modify the structured entry. + $type = 'feedback'; + drupal_alter(array('feedback_view', 'entity_view'), $build, $type); + + return $build; +} + +/** + * Builds a structured array representing the feedback message content. + * + * @param $entry + * A feedback entry object. + * @param $view_mode + * View mode, e.g. 'full'. + * @param $langcode + * (optional) A language code to use for rendering. Defaults to the global + * content language of the current request. + */ +function feedback_build_content($entry, $view_mode = 'full', $langcode = NULL) { + if (!isset($langcode)) { + $langcode = $GLOBALS['language_content']->language; + } + + // Build fields content. + field_attach_prepare_view('feedback', array($entry->fid => $entry), $view_mode); + entity_prepare_view('feedback', array($entry->fid => $entry)); + $entry->content = field_attach_view('feedback', $entry, $view_mode, $langcode); + + module_invoke_all('feedback_view', $entry, $view_mode, $langcode); + module_invoke_all('entity_view', $entry, 'feedback', $view_mode, $langcode); +} + +/** + * Process variables for feedback-entry.tpl.php. + * + * The $variables array contains the following arguments: + * - $entry + * + * @see feedback-entry.tpl.php + */ +function template_preprocess_feedback_entry(&$variables) { + foreach (element_children($variables['elements']) as $key) { + $variables['content'][$key] = $variables['elements'][$key]; + } + + $entry = $variables['elements']['#entry']; + + // Preprocess fields. + field_attach_preprocess('feedback', $entry, $variables['elements'], $variables); + + $variables['location'] = l($entry->location, $entry->url); + $variables['date'] = format_date($entry->timestamp, 'small'); + $variables['account'] = format_username($entry); + $variables['message'] = feedback_format_message($entry); +} + diff --git feedback.api.php feedback.api.php index 290307f..824569e 100644 --- feedback.api.php +++ feedback.api.php @@ -24,6 +24,19 @@ function hook_feedback_load($entries) { } /** + * Act on a feedback entry before it is saved. + * + * Modules implementing this hook can act on the feedback entry object before it + * is inserted or updated. + * + * @param $entry + * The feedback entry object. + */ +function hook_feedback_presave($entry) { + $entry->foo = 'bar'; +} + +/** * Respond to creation of a new feedback entry. * * @param $entry @@ -70,5 +83,51 @@ function hook_feedback_delete($entry) { } /** + * The feedback entry is being displayed. + * + * The module should format its custom additions for display and add them to the + * $entry->content array. + * + * @param $entry + * The feedback entry object. + * @param $view_mode + * View mode, e.g. 'full'. + * @param $langcode + * The language code used for rendering. + * + * @see hook_feedback_view_alter() + * @see hook_entity_view() + */ +function hook_feedback_view($entry, $view_mode, $langcode) { + $entry->content['foo'] = array( + '#markup' => t('Bar'), + ); +} + +/** + * The feedback entry was built; the module may modify the structured content. + * + * This hook is called after the content has been assembled in a structured + * array and may be used for doing processing which requires that the complete + * content structure has been built. + * + * @param $build + * A renderable array representing the feedback entry. + * + * @see feedback_view() + * @see hook_entity_view_alter() + */ +function hook_feedback_view_alter(&$build) { + // Check for the existence of a field added by another module. + if (isset($build['an_additional_field'])) { + // Change its weight. + $build['an_additional_field']['#weight'] = -10; + } + + // Add a #post_render callback to act on the rendered HTML of the entry. + $build['#post_render'][] = 'my_module_feedback_post_render'; +} + +/** * @} End of "addtogroup hooks". */ diff --git feedback.controller.inc feedback.controller.inc new file mode 100644 index 0000000..d10f950 --- /dev/null +++ feedback.controller.inc @@ -0,0 +1,23 @@ +innerJoin('users', 'u', 'base.uid = u.uid'); + $query->fields('u', array('name')); + return $query; + } +} \ No newline at end of file diff --git feedback.info feedback.info index c42773f..db396bb 100644 --- feedback.info +++ feedback.info @@ -2,4 +2,6 @@ name = Feedback description = Allows site visitors and users to report issues about this site. package = Development core = 7.x +configure = admin/config/user-interface/feedback +files[] = feedback.controller.inc files[] = tests/feedback.test diff --git feedback.module feedback.module index 65c2acd..c216a20 100644 --- feedback.module +++ feedback.module @@ -6,6 +6,66 @@ */ /** + * 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', + ), + ); +} + +/** + * 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, + ), + ), + ), + ); + + return $return; +} + +/** + * Entity uri callback. + */ +function feedback_uri($entry) { + return array( + 'path' => 'admin/reports/feedback/' . $entry->fid, + ); +} + +/** * Implements hook_permission(). */ function feedback_permission() { @@ -18,16 +78,8 @@ function feedback_permission() { 'title' => t('View feedback messages'), 'description' => t('View, process, and delete submitted feedback messages.'), ), - ); -} - -/** - * Implements hook_theme(). - */ -function feedback_theme() { - return array( - 'feedback_admin_view_form' => array( - 'render element' => 'form', + 'administer feedback' => array( + 'title' => t('Administer feedback settings'), ), ); } @@ -44,6 +96,27 @@ function feedback_menu() { '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/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; } @@ -123,10 +196,10 @@ function feedback_form($form, &$form_state) { ); if (user_access('view feedback messages')) { if (arg(0) != 'node') { - $feedbacks = feedback_load_multiple(array(), array('f.status' => 0, 'f.location_masked' => feedback_mask_path($_GET['q']))); + $feedbacks = feedback_load_multiple(array(), array('status' => 0, 'location_masked' => feedback_mask_path($_GET['q']))); } else { - $feedbacks = feedback_load_multiple(array(), array('f.status' => 0, 'f.location' => $_GET['q'])); + $feedbacks = feedback_load_multiple(array(), array('status' => 0, 'location' => $_GET['q'])); } if ($feedbacks) { $rows = ''; @@ -149,6 +222,10 @@ function feedback_form($form, &$form_state) { '#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. @@ -177,6 +254,7 @@ function feedback_form($form, &$form_state) { */ 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); @@ -241,6 +319,10 @@ function feedback_format_message($entry) { $message .= '
(' . check_plain($entry->useragent) . ')
'; } } + $uri = entity_uri('feedback', $entry); + if ($uri['path'] != $_GET['q']) { + $message .= l("view full", $uri['path']); + } return $message; } @@ -273,23 +355,14 @@ function feedback_load($fid) { * * @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()) { - $query = db_select('feedback', 'f')->fields('f'); - $query->join('users', 'u', 'f.uid = u.uid'); - $query->fields('u', array('name')); - - if (!empty($fids)) { - $query->condition('fid', $fids, 'IN'); - } - if (!empty($conditions)) { - foreach ($conditions as $key => $value) { - $query->condition($key, $value); - } - } - $entries = $query->execute()->fetchAllAssoc('fid'); - module_invoke_all('feedback_load', $entries); - return $entries; + return entity_load('feedback', $fids, $conditions); } /** @@ -305,30 +378,47 @@ function feedback_load_multiple($fids = array(), $conditions = array()) { 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)) { - if (!isset($entry->uid)) { - $entry->uid = $user->uid; - } $entry->message = trim($entry->message); - if (!isset($entry->location_masked)) { - $entry->location_masked = feedback_mask_path($entry->location); - } - if (!isset($entry->url)) { - $entry->url = url($entry->location, array('absolute' => TRUE)); - } - if (!isset($entry->timestamp)) { - $entry->timestamp = REQUEST_TIME; - } - if (!isset($entry->useragent)) { - $entry->useragent = $_SERVER['HTTP_USER_AGENT']; + + $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; } @@ -352,7 +442,9 @@ 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')