feedback.admin.inc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. <?php
  2. /**
  3. * @file
  4. * Administrative functionality for Feedback module.
  5. */
  6. /**
  7. * Build a (sortable) form containing a checkbox for each feedback entry.
  8. */
  9. function feedback_admin_view_form($form, &$form_state) {
  10. $form['#attached']['js'][] = drupal_get_path('module', 'feedback') . '/feedback.admin.js';
  11. $status_headings = array(
  12. FEEDBACK_OPEN => t('Open feedback messages'),
  13. FEEDBACK_PROCESSED => t('Processed feedback messages'),
  14. );
  15. $form['#feedback_header'] = array(
  16. array(),
  17. array('data' => t('Location'), 'field' => 'f.location_masked', 'sort' => 'asc'),
  18. array('data' => t('Date'), 'field' => 'f.timestamp'),
  19. array('data' => t('User'), 'field' => 'u.name'),
  20. t('Message'),
  21. t('Operations'),
  22. );
  23. // Hack to prevent pager_query() from issuing PHP notices.
  24. if (!isset($_GET['page'])) {
  25. $_GET['page'] = '';
  26. }
  27. if (count(explode(',', $_GET['page'])) < 2) {
  28. $_GET['page'] .= ',0';
  29. }
  30. $form['feedback-messages'] = array('#tree' => TRUE);
  31. $query = db_select('feedback', 'f')->extend('PagerDefault')->extend('TableSort');
  32. $query->join('users', 'u', 'f.uid = u.uid');
  33. $query->fields('f')
  34. ->limit(50);
  35. foreach (array(FEEDBACK_OPEN, FEEDBACK_PROCESSED) as $status) {
  36. $status_query = clone $query;
  37. $fids = $status_query->element($status)
  38. ->condition('f.status', $status)
  39. ->execute()->fetchCol();
  40. $form['feedback-messages'][$status] = array(
  41. '#type' => 'fieldset',
  42. '#title' => $status_headings[$status],
  43. '#collapsible' => TRUE,
  44. '#collapsed' => $status,
  45. '#attributes' => array('class' => array('feedback-messages')),
  46. );
  47. if (!empty($fids)) {
  48. $entries = feedback_load_multiple($fids);
  49. foreach ($entries as $fid => $entry) {
  50. $form['feedback-messages'][$status][$fid] = array(
  51. '#type' => 'checkbox',
  52. '#return_value' => FEEDBACK_PROCESSED,
  53. '#default_value' => FALSE,
  54. );
  55. $form['feedback-messages'][$status][$fid]['location'] = array(
  56. '#markup' => l(truncate_utf8($entry->location, 32, FALSE, TRUE), $entry->url),
  57. );
  58. $form['feedback-messages'][$status][$fid]['date'] = array(
  59. '#markup' => format_date($entry->timestamp, 'small'),
  60. );
  61. $form['feedback-messages'][$status][$fid]['user'] = array(
  62. '#markup' => check_plain(format_username($entry)),
  63. );
  64. feedback_build_content($entry, 'teaser');
  65. $form['feedback-messages'][$status][$fid]['message'] = $entry->content;
  66. unset($entry->content);
  67. $form['feedback-messages'][$status][$fid]['operations'] = array(
  68. '#theme' => 'links',
  69. '#links' => array(
  70. 'edit' => array(
  71. 'title' => t('edit'),
  72. 'href' => "admin/reports/feedback/$fid/edit"
  73. ),
  74. 'delete' => array(
  75. 'title' => t('delete'),
  76. 'href' => "admin/reports/feedback/$fid/delete"
  77. ),
  78. ),
  79. '#attributes' => array(),
  80. );
  81. }
  82. }
  83. }
  84. $form['submit'] = array(
  85. '#type' => 'submit',
  86. '#value' => t('Update'),
  87. // Hide the submit button, if there are no entries at all.
  88. '#access' => !empty($entries),
  89. );
  90. return $form;
  91. }
  92. /**
  93. * Output a sortable table containing all feedback entries.
  94. */
  95. function theme_feedback_admin_view_form($variables) {
  96. $form = $variables['form'];
  97. $output = '';
  98. foreach (element_children($form['feedback-messages']) as $status) {
  99. $item = &$form['feedback-messages'][$status];
  100. if (!isset($item['#type']) || $item['#type'] != 'fieldset') {
  101. continue;
  102. }
  103. // Build the table.
  104. $rows = array();
  105. foreach (element_children($item) as $element_entry) {
  106. $entry = &$item[$element_entry];
  107. // Render the data first.
  108. $rows[] = array(
  109. 0,
  110. drupal_render($entry['location']),
  111. drupal_render($entry['date']),
  112. drupal_render($entry['user']),
  113. drupal_render($entry['message']),
  114. drupal_render($entry['operations']),
  115. );
  116. // Render the checkbox.
  117. $rows[count($rows) - 1][0] = drupal_render($entry);
  118. }
  119. if (empty($rows)) {
  120. $rows[] = array(array('data' => t('No feedback entries available.'), 'colspan' => 6));
  121. }
  122. // Inject the table.
  123. $item['messages'] = array(
  124. '#markup' => theme('table', array('header' => $form['#feedback_header'], 'rows' => $rows)),
  125. '#suffix' => theme('pager', array('element' => $status)),
  126. '#weight' => -1,
  127. );
  128. // Render the fieldset.
  129. $output .= drupal_render($item);
  130. }
  131. // Render internal FAPI and potential extra form elements.
  132. $output .= drupal_render_children($form);
  133. return $output;
  134. }
  135. /**
  136. * Form submit callback for admin view form.
  137. */
  138. function feedback_admin_view_form_submit($form, &$form_state) {
  139. $update = array();
  140. // Determine feedback entries to update.
  141. foreach ($form_state['values']['feedback-messages'] as $status => $values) {
  142. $values = array_filter($values);
  143. if (!empty($values)) {
  144. $entries = feedback_load_multiple(array_keys($values));
  145. foreach ($entries as $fid => $entry) {
  146. $entry->status = ($status == FEEDBACK_OPEN ? FEEDBACK_PROCESSED : FEEDBACK_OPEN);
  147. feedback_save($entry);
  148. }
  149. }
  150. }
  151. }
  152. /**
  153. * Form builder; Feedback entry edit form.
  154. *
  155. * @ingroup forms
  156. */
  157. function feedback_entry_form($form, &$form_state, $entry) {
  158. $form['#fid'] = $entry->fid;
  159. $form['location'] = array(
  160. '#type' => 'textfield',
  161. '#title' => t('Location'),
  162. '#required' => TRUE,
  163. '#default_value' => $entry->location,
  164. );
  165. $account = user_load($entry->uid);
  166. $form['user'] = array(
  167. '#title' => t('User'),
  168. '#type' => 'item',
  169. '#markup' => theme('username', array('account' => $account)),
  170. );
  171. $form['status'] = array(
  172. '#title' => t('Processed'),
  173. '#type' => 'radios',
  174. '#options' => array(
  175. FEEDBACK_OPEN => 'Open',
  176. FEEDBACK_PROCESSED => 'Processed',
  177. ),
  178. '#default_value' => $entry->status,
  179. );
  180. $form['message'] = array(
  181. '#type' => 'textarea',
  182. '#title' => t('Message'),
  183. '#required' => TRUE,
  184. '#wysiwyg' => FALSE,
  185. '#default_value' => $entry->message,
  186. );
  187. field_attach_form('feedback', $entry, $form, $form_state);
  188. $form['actions'] = array(
  189. '#type' => 'actions',
  190. );
  191. $form['actions']['submit'] = array(
  192. '#type' => 'submit',
  193. '#value' => t('Submit'),
  194. );
  195. $form['actions']['delete'] = array(
  196. '#type' => 'submit',
  197. '#value' => t('Delete'),
  198. '#submit' => array('feedback_entry_form_delete_submit'),
  199. );
  200. return $form;
  201. }
  202. /**
  203. * Form submit callback for entry edit form.
  204. *
  205. * @todo Duplicates feedback_form_submit(). Add all default entity properties
  206. * as #type 'value' to feedback_form() and merge the two submit handlers.
  207. */
  208. function feedback_entry_form_submit(&$form, &$form_state) {
  209. $entry = feedback_load($form['#fid']);
  210. entity_form_submit_build_entity('feedback', $entry, $form, $form_state);
  211. $entry->message = $form_state['values']['message'];
  212. $entry->location = $form_state['values']['location'];
  213. $entry->location_masked = feedback_mask_path($entry->location);
  214. $entry->url = url($entry->location, array('absolute' => TRUE));
  215. $entry->status = $form_state['values']['status'];
  216. feedback_save($entry);
  217. drupal_set_message(t('The entry has been updated.'));
  218. }
  219. /**
  220. * Button submit function: handle the 'Delete' button on the feedback entry edit form.
  221. */
  222. function feedback_entry_form_delete_submit($form, &$form_state) {
  223. $destination = array();
  224. if (isset($_GET['destination'])) {
  225. $destination = drupal_get_destination();
  226. unset($_GET['destination']);
  227. }
  228. $fid = $form['#fid'];
  229. $form_state['redirect'] = array('admin/reports/feedback/' . $fid . '/delete', array('query' => $destination));
  230. }
  231. /**
  232. * Form builder; The general feedback settings form.
  233. *
  234. * @ingroup forms
  235. */
  236. function feedback_admin_settings_form($form, &$form_state) {
  237. $form['feedback_excluded_paths'] = array(
  238. '#type' => 'textarea',
  239. '#title' => t('Paths to exclude from feedback display'),
  240. '#default_value' => variable_get('feedback_excluded_paths', 'admin/reports/feedback'),
  241. '#description' => t("Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array('%blog' => 'blog', '%blog-wildcard' => 'blog/*', '%front' => '<front>')),
  242. );
  243. return system_settings_form($form);
  244. }
  245. /**
  246. * Generate a render array for viewing a feedback entry.
  247. *
  248. * @param $entry
  249. * A feedback entry object.
  250. * @param $view_mode
  251. * View mode, e.g. 'full', 'teaser'...
  252. * @param $langcode
  253. * (optional) A language code to use for rendering. Defaults to the global
  254. * content language of the current request.
  255. *
  256. * @return
  257. * An array as expected by drupal_render().
  258. *
  259. * @todo This is an API function; move into feedback.module.
  260. */
  261. function feedback_view($entry, $view_mode = 'full', $langcode = NULL) {
  262. if (!isset($langcode)) {
  263. $langcode = $GLOBALS['language_content']->language;
  264. }
  265. // Populate $entry->content with a render() array.
  266. feedback_build_content($entry, $view_mode, $langcode);
  267. $build = $entry->content;
  268. unset($entry->content);
  269. $build += array(
  270. '#theme' => 'feedback_entry',
  271. '#feedback' => $entry,
  272. '#view_mode' => $view_mode,
  273. '#language' => $langcode,
  274. );
  275. // Allow modules to modify the structured entry.
  276. $type = 'feedback';
  277. drupal_alter(array('feedback_view', 'entity_view'), $build, $type);
  278. return $build;
  279. }
  280. /**
  281. * Builds a structured array representing the feedback entry's content.
  282. *
  283. * @param $entry
  284. * A feedback entry object.
  285. * @param $view_mode
  286. * View mode, e.g. 'full', 'teaser'...
  287. * @param $langcode
  288. * (optional) A language code to use for rendering. Defaults to the global
  289. * content language of the current request.
  290. *
  291. * @todo This is an API function; move into feedback.module.
  292. */
  293. function feedback_build_content($entry, $view_mode = 'full', $langcode = NULL) {
  294. if (!isset($langcode)) {
  295. $langcode = $GLOBALS['language_content']->language;
  296. }
  297. // Remove previously built content, if exists.
  298. $entry->content = array();
  299. // Allow modules to change the view mode.
  300. $context = array(
  301. 'entity_type' => 'feedback',
  302. 'entity' => $entry,
  303. 'langcode' => $langcode,
  304. );
  305. drupal_alter('entity_view_mode', $view_mode, $context);
  306. $entry->content['message'] = array(
  307. '#markup' => check_plain($entry->message),
  308. );
  309. if (!empty($entry->useragent)) {
  310. $entry->content['browser'] = array(
  311. '#theme' => 'container',
  312. '#attributes' => array('class' => array('browserinfo', 'description')),
  313. );
  314. if (module_exists('browscap')) {
  315. $browserinfo = browscap_get_browser($entry->useragent);
  316. // Browscap returns useragent but not always parent info.
  317. $browser = (isset($browserinfo['parent']) ? $browserinfo['parent'] . ' / ' . $browserinfo['platform'] : $browserinfo['useragent']);
  318. $entry->content['browser']['#markup'] = check_plain($browser);
  319. }
  320. else {
  321. $entry->content['browser']['#markup'] = check_plain($entry->useragent);
  322. }
  323. }
  324. // Build fields content.
  325. field_attach_prepare_view('feedback', array($entry->fid => $entry), $view_mode, $langcode);
  326. entity_prepare_view('feedback', array($entry->fid => $entry), $langcode);
  327. $entry->content += field_attach_view('feedback', $entry, $view_mode, $langcode);
  328. $entry->content['links'] = array(
  329. '#theme' => 'links__feedback',
  330. '#pre_render' => array('drupal_pre_render_links'),
  331. '#attributes' => array('class' => array('links', 'inline')),
  332. );
  333. $uri = entity_uri('feedback', $entry);
  334. if ($uri['path'] != $_GET['q'] && arg(0) != 'admin') {
  335. $entry->content['links']['#links']['view'] = array('title' => t('view'), 'href' => $uri['path']);
  336. }
  337. // Allow modules to make their own additions to the entry.
  338. module_invoke_all('feedback_view', $entry, $view_mode, $langcode);
  339. module_invoke_all('entity_view', $entry, 'feedback', $view_mode, $langcode);
  340. }
  341. /**
  342. * Process variables for feedback-entry.tpl.php.
  343. *
  344. * The $variables array contains the following arguments:
  345. * - $entry
  346. *
  347. * @see feedback-entry.tpl.php
  348. */
  349. function template_preprocess_feedback_entry(&$variables) {
  350. foreach (element_children($variables['elements']) as $key) {
  351. $variables['content'][$key] = $variables['elements'][$key];
  352. }
  353. $entry = $variables['elements']['#feedback'];
  354. // Preprocess fields.
  355. field_attach_preprocess('feedback', $entry, $variables['elements'], $variables);
  356. $variables['location'] = l($entry->location, $entry->url);
  357. $variables['date'] = format_date($entry->timestamp, 'small');
  358. $variables['account'] = check_plain(format_username($entry));
  359. }
  360. /**
  361. * Form constructor for the feedback delete confirmation form.
  362. *
  363. * @param $entry
  364. * The feedback entry to delete.
  365. *
  366. * @see feedback_delete_confirm_submit()
  367. * @ingroup forms
  368. */
  369. function feedback_delete_confirm($form, &$form_state, $entry) {
  370. $form['fid'] = array('#type' => 'value', '#value' => $entry->fid);
  371. $output = confirm_form($form,
  372. t('Are you sure you want to delete the feedback entry?'),
  373. 'admin/reports/feedback',
  374. NULL,
  375. t('Delete'));
  376. return $output;
  377. }
  378. /**
  379. * Process feedback_delete_confirm() form submissions.
  380. */
  381. function feedback_delete_confirm_submit($form, &$form_state) {
  382. feedback_delete($form_state['values']['fid']);
  383. drupal_set_message(t('The feedback entry was deleted.'));
  384. $form_state['redirect'] = 'admin/reports/feedback';
  385. }