privatemsg_filter.module

  1. 1 privatemsg_filter/privatemsg_filter.module
  2. 7-1 privatemsg_filter/privatemsg_filter.module
  3. 7-2 privatemsg_filter/privatemsg_filter.module
  4. 6-2 privatemsg_filter/privatemsg_filter.module

Allows users to tag private messages and to filter based upon those tags.

File

privatemsg_filter/privatemsg_filter.module
View source
  1. <?php
  2. /**
  3. * @file
  4. * Allows users to tag private messages and to filter based upon those tags.
  5. */
  6. /**
  7. * Implements hook_permission().
  8. */
  9. function privatemsg_filter_permission() {
  10. return array(
  11. 'filter private messages' => array(
  12. 'title' => t('Filter private messages'),
  13. 'description' => t('Use the search and filter widget'),
  14. ),
  15. 'tag private messages' => array(
  16. 'title' => t('Tag private messages'),
  17. 'description' => t('Tag private messages'),
  18. ),
  19. 'create private message tags' => array(
  20. 'title' => t('Create private message tags'),
  21. 'description' => t('Create new private message tags'),
  22. ),
  23. );
  24. }
  25. /**
  26. * Implements hook_menu().
  27. */
  28. function privatemsg_filter_menu() {
  29. $items['admin/config/messaging/privatemsg/tags'] = array(
  30. 'title' => 'Tags',
  31. 'description' => 'Configure tags.',
  32. 'page callback' => 'privatemsg_tags_admin',
  33. 'file' => 'privatemsg_filter.admin.inc',
  34. 'access arguments' => array('administer privatemsg settings'),
  35. 'type' => MENU_LOCAL_TASK,
  36. );
  37. $items['admin/config/messaging/privatemsg/tags/list'] = array(
  38. 'title' => 'List',
  39. 'description' => 'Configure tags.',
  40. 'page callback' => 'privatemsg_tags_admin',
  41. 'file' => 'privatemsg_filter.admin.inc',
  42. 'access arguments' => array('administer privatemsg settings'),
  43. 'type' => MENU_DEFAULT_LOCAL_TASK,
  44. 'weight' => -5,
  45. );
  46. $items['admin/config/messaging/privatemsg/tags/add'] = array(
  47. 'title' => 'Add tag',
  48. 'description' => 'Configure tags.',
  49. 'page callback' => 'drupal_get_form',
  50. 'page arguments' => array('privatemsg_tags_form'),
  51. 'file' => 'privatemsg_filter.admin.inc',
  52. 'access arguments' => array('administer privatemsg settings'),
  53. 'type' => MENU_LOCAL_ACTION,
  54. );
  55. $items['admin/config/messaging/privatemsg/tags/rebuild'] = array(
  56. 'title' => 'Rebuild inbox',
  57. 'page callback' => 'drupal_get_form',
  58. 'page arguments' => array('privatemsg_filter_inbox_rebuild_form'),
  59. 'file' => 'privatemsg_filter.admin.inc',
  60. 'access arguments' => array('administer privatemsg settings'),
  61. 'type' => MENU_LOCAL_TASK,
  62. );
  63. $items['admin/config/messaging/privatemsg/tags/edit/%'] = array(
  64. 'title' => 'Edit tag',
  65. 'description' => 'Configure tags.',
  66. 'page callback' => 'drupal_get_form',
  67. 'page arguments' => array('privatemsg_tags_form', 6),
  68. 'file' => 'privatemsg_filter.admin.inc',
  69. 'access arguments' => array('administer privatemsg settings'),
  70. 'type' => MENU_CALLBACK,
  71. );
  72. $items['admin/config/messaging/privatemsg/tags/delete/%'] = array(
  73. 'title' => 'Delete tag',
  74. 'description' => 'Configure tags.',
  75. 'page callback' => 'drupal_get_form',
  76. 'page arguments' => array('privatemsg_filter_tags_delete', 6),
  77. 'file' => 'privatemsg_filter.admin.inc',
  78. 'access arguments' => array('administer privatemsg settings'),
  79. 'type' => MENU_CALLBACK,
  80. );
  81. $items['messages/inbox'] = array(
  82. 'title' => 'Inbox',
  83. 'page callback' => 'privatemsg_list_page',
  84. 'page arguments' => array('inbox'),
  85. 'file' => 'privatemsg.pages.inc',
  86. 'file path' => drupal_get_path('module', 'privatemsg'),
  87. 'access callback' => 'privatemsg_user_access',
  88. 'type' => variable_get('privatemsg_filter_default_list', 0) ? MENU_LOCAL_TASK : MENU_DEFAULT_LOCAL_TASK,
  89. 'weight' => -15,
  90. 'menu_name' => 'user-menu',
  91. );
  92. $items['messages/sent'] = array(
  93. 'title' => 'Sent messages',
  94. 'page callback' => 'privatemsg_list_page',
  95. 'page arguments' => array('sent'),
  96. 'file' => 'privatemsg.pages.inc',
  97. 'file path' => drupal_get_path('module', 'privatemsg'),
  98. 'access callback' => 'privatemsg_user_access',
  99. 'type' => MENU_LOCAL_TASK,
  100. 'weight' => -12,
  101. 'menu_name' => 'user-menu',
  102. );
  103. $items['messages/filter/autocomplete'] = array(
  104. 'page callback' => 'privatemsg_autocomplete',
  105. 'file' => 'privatemsg.pages.inc',
  106. 'file path' => drupal_get_path('module', 'privatemsg'),
  107. 'access callback' => 'privatemsg_user_access',
  108. 'access arguments' => array('write privatemsg'),
  109. 'type' => MENU_CALLBACK,
  110. 'weight' => -10,
  111. );
  112. $items['messages/filter/tag-autocomplete'] = array(
  113. 'page callback' => 'privatemsg_filter_tags_autocomplete',
  114. 'file' => 'privatemsg_filter.pages.inc',
  115. 'access callback' => 'privatemsg_user_access',
  116. 'access arguments' => array('tag private messages'),
  117. 'type' => MENU_CALLBACK,
  118. 'weight' => -10,
  119. );
  120. return $items;
  121. }
  122. /**
  123. * Implement hook_menu_alter().
  124. */
  125. function privatemsg_filter_menu_alter(&$items) {
  126. // Rename messages to "All messages".
  127. $items['messages/list']['title'] = 'All messages';
  128. if (variable_get('privatemsg_filter_default_list', 0) == 0) {
  129. // Change default argument of /messages to inbox. and set the task to MENU_LOCAL_TASK.
  130. $items['messages']['page arguments'] = array('inbox');
  131. $items['messages/list']['type'] = MENU_LOCAL_TASK;
  132. }
  133. }
  134. /**
  135. * Implements hook_form_FORM_ID_alter() to add a filter widget to the message listing pages.
  136. */
  137. function privatemsg_filter_form_privatemsg_admin_settings_alter(&$form, $form_state) {
  138. $form['privatemsg_listing']['privatemsg_filter_default_list'] = array(
  139. '#type' => 'radios',
  140. '#default_value' => variable_get('privatemsg_filter_default_list', 0),
  141. '#options' => array(t('Inbox'), t('All messages')),
  142. '#title' => t('Choose the default list option'),
  143. '#description' => t('Choose which of the two lists are shown by default when following the messages link.'),
  144. );
  145. $form['privatemsg_listing']['privatemsg_filter_searchbody'] = array(
  146. '#type' => 'checkbox',
  147. '#title' => t('Search message body'),
  148. '#description' => t('WARNING: turning on this feature will slow down search performance by a large factor. Gets worse as your messages database increases.'),
  149. '#default_value' => variable_get('privatemsg_filter_searchbody', FALSE),
  150. );
  151. $form['#submit'][] = 'privatemsg_filter_settings_submit';
  152. }
  153. /**
  154. * Rebuilding the menu if necessary.
  155. */
  156. function privatemsg_filter_settings_submit($form, &$form_state) {
  157. if ($form['privatemsg_listing']['privatemsg_filter_default_list']['#default_value'] != $form_state['values']['privatemsg_filter_default_list']) {
  158. menu_rebuild();
  159. }
  160. }
  161. /**
  162. * Function to create a tag
  163. *
  164. * @param $tags A single tag or an array of tags.
  165. */
  166. function privatemsg_filter_create_tags($tags) {
  167. if (!is_array($tags)) {
  168. $tags = array($tags);
  169. }
  170. $tag_ids = array();
  171. foreach ($tags as $tag) {
  172. $tag = trim($tag);
  173. if (empty($tag)) {
  174. // Do not save a blank tag.
  175. continue;
  176. }
  177. // Check if the tag already exists and only create the tag if it does not.
  178. $tag_id = db_query("SELECT tag_id FROM {pm_tags} WHERE tag = :tag", array(':tag' => $tag))->fetchField();
  179. if (empty($tag_id) && privatemsg_user_access('create private message tags')) {
  180. $tag_id = db_insert('pm_tags')
  181. ->fields(array('tag' => $tag))
  182. ->execute();
  183. }
  184. elseif (empty($tag_id)) {
  185. // The user does not have permission to create new tags - disregard this tag and move onto the next.
  186. drupal_set_message(t('Tag %tag was ignored because you do not have permission to create new tags.', array('%tag' => $tag)));
  187. continue;
  188. }
  189. $tag_ids[] = $tag_id;
  190. }
  191. return $tag_ids;
  192. }
  193. /**
  194. * Tag one or multiple threads with a tag.
  195. *
  196. * @param $threads A single thread id or an array of thread ids.
  197. * @param $tag_id Id of the tag.
  198. */
  199. function privatemsg_filter_add_tags($threads, $tag_ids, $account = NULL) {
  200. if (!is_array($threads)) {
  201. $threads = array($threads);
  202. }
  203. if (!is_array($tag_ids)) {
  204. $tag_ids = array($tag_ids);
  205. }
  206. if (empty($account)) {
  207. global $user;
  208. $account = clone $user;
  209. }
  210. foreach ($tag_ids as $tag_id) {
  211. foreach ($threads as $thread) {
  212. // Make sure that we don't add a tag to a thread twice,
  213. // only insert if there is no such tag yet.
  214. db_merge('pm_tags_index')
  215. ->key(array(
  216. 'tag_id' => $tag_id,
  217. 'uid' => $account->uid,
  218. 'thread_id' => $thread,
  219. ))
  220. ->execute();
  221. }
  222. }
  223. }
  224. /**
  225. * Remove tag from one or multiple threads.
  226. *
  227. * @param $threads A single thread id or an array of thread ids.
  228. * @param $tag_id Id of the tag - set to NULL to remove all tags.
  229. */
  230. function privatemsg_filter_remove_tags($threads, $tag_ids = NULL, $account = NULL) {
  231. if (!is_array($threads)) {
  232. $threads = array($threads);
  233. }
  234. if (empty($account)) {
  235. global $user;
  236. $account = $user;
  237. }
  238. if (is_null($tag_ids)) {
  239. //Delete all tag mapping - all except for the inbox tag if it exists.
  240. db_delete('pm_tags_index')
  241. ->condition('uid', $account->uid)
  242. ->condition('thread_id', $threads)
  243. ->condition('tag_id', variable_get('privatemsg_filter_inbox_tag', ''), '<>')
  244. ->execute();
  245. }
  246. else {
  247. if (!is_array($tag_ids)) {
  248. $tag_ids = array($tag_ids);
  249. }
  250. //Delete tag mapping for the specified tag.
  251. db_delete('pm_tags_index')
  252. ->condition('uid', $account->uid)
  253. ->condition('thread_id', $threads)
  254. ->condition('tag_id', $tag_ids)
  255. ->execute();
  256. }
  257. }
  258. function privatemsg_filter_get_filter($account) {
  259. $filter = array();
  260. // Filtering by tags is either allowed if the user can use tags or he can
  261. // filter.
  262. if (privatemsg_user_access('filter private messages') || privatemsg_user_access('tag private messages')) {
  263. if (isset($_GET['tags'])) {
  264. $_GET['tags'] = urldecode($_GET['tags']);
  265. $tag_data = privatemsg_filter_get_tags_data($account);
  266. foreach (explode(',', $_GET['tags']) as $tag) {
  267. if (isset($tag_data[$tag])) {
  268. $filter['tags'][$tag] = $tag;
  269. }
  270. elseif (in_array($tag, $tag_data)) {
  271. $filter['tags'][array_search($tag, $tag_data)] = array_search($tag, $tag_data);
  272. }
  273. }
  274. }
  275. }
  276. // Users can only use the text search or search by author if they have the
  277. // necessary permission.
  278. if (privatemsg_user_access('filter private messages')) {
  279. if (isset($_GET['author'])) {
  280. list($filter['author']) = _privatemsg_parse_userstring($_GET['author']);
  281. }
  282. if (isset($_GET['search'])) {
  283. $filter['search'] = $_GET['search'];
  284. }
  285. }
  286. if (!empty($filter)) {
  287. return $filter;
  288. }
  289. if (!empty($_SESSION['privatemsg_filter'])) {
  290. return $_SESSION['privatemsg_filter'];
  291. }
  292. }
  293. function privatemsg_filter_get_tags_data($account) {
  294. static $tag_data;
  295. if (is_array($tag_data)) {
  296. return $tag_data;
  297. }
  298. // Only show the tags that a user have used.
  299. return $tag_data = _privatemsg_assemble_query(array('tags', 'privatemsg_filter'), $account)->execute()->fetchAllKeyed();
  300. }
  301. function privatemsg_filter_dropdown(&$form_state, $account) {
  302. $form['#attached']['css'][] = drupal_get_path('module', 'privatemsg_filter') . '/privatemsg_filter.css';
  303. $form['filter'] = array(
  304. '#type' => 'fieldset',
  305. '#title' => t('Filter Messages'),
  306. '#collapsible' => TRUE,
  307. '#collapsed' => TRUE,
  308. '#weight' => -20,
  309. // The form is always called when search arguments are passed in, even if
  310. // they don't have access to it. This is necessary to process the search
  311. // query. But we don't want to show them the form.
  312. '#access' => privatemsg_user_access('filter private messages'),
  313. );
  314. $form['filter']['search'] = array(
  315. '#type' => 'textfield',
  316. '#title' => variable_get('privatemsg_filter_searchbody', FALSE) ? t('By message text') : t('By subject'),
  317. '#weight' => -20,
  318. '#size' => 25,
  319. );
  320. $form['filter']['author'] = array(
  321. '#type' => 'textfield',
  322. '#title' => t('By participant'),
  323. '#weight' => -5,
  324. '#size' => 25,
  325. '#autocomplete_path' => 'messages/filter/autocomplete',
  326. );
  327. // Only show form if the user has some messages tagged.
  328. if (count($tag_data = privatemsg_filter_get_tags_data($account))) {
  329. $form['filter']['tags'] = array(
  330. '#type' => 'select',
  331. '#title' => t('By tags'),
  332. '#options' => $tag_data,
  333. '#multiple' => TRUE,
  334. '#weight' => 0
  335. );
  336. }
  337. $form['filter']['actions'] = array(
  338. '#type' => 'actions',
  339. '#attributes' => array('class' => array('privatemsg-filter-actions')),
  340. );
  341. $form['filter']['actions']['submit'] = array(
  342. '#type' => 'submit',
  343. '#value' => t('Filter'),
  344. '#weight' => 10,
  345. '#submit' => array('privatemsg_filter_dropdown_submit'),
  346. );
  347. $form['filter']['actions']['save'] = array(
  348. '#type' => 'submit',
  349. '#value' => t('Save filter'),
  350. '#weight' => 11,
  351. '#submit' => array('privatemsg_filter_dropdown_submit'),
  352. );
  353. if ($filter = privatemsg_filter_get_filter($account)) {
  354. // Display a message if the user will not see the filter form.
  355. if (!empty($filter['tags']) && !empty($_GET['tags']) && !privatemsg_user_access('filter private messages')) {
  356. drupal_set_message(t('Messages tagged with %tags are currently displayed. <a href="@remove_filter_url">Click here to remove this filter</a>.', array('%tags' => $_GET['tags'], '@remove_filter_url' => url($_GET['q']))));
  357. }
  358. privatemsg_filter_dropdown_set_active($form, $filter);
  359. }
  360. return $form;
  361. }
  362. function privatemsg_filter_dropdown_set_active(&$form, $filter) {
  363. $form['filter']['#title'] = t('Filter Messages (Active)');
  364. $form['filter']['#collapsed'] = FALSE;
  365. if (isset($filter['author'])) {
  366. $string = '';
  367. foreach ($filter['author'] as $author) {
  368. $string .= privatemsg_recipient_format($author, array('plain' => TRUE)) . ', ';
  369. }
  370. $form['filter']['author']['#default_value'] = $string;
  371. }
  372. if (isset($filter['tags'])) {
  373. $form['filter']['tags']['#default_value'] = $filter['tags'];
  374. }
  375. if (isset($filter['search'])) {
  376. $form['filter']['search']['#default_value'] = $filter['search'];
  377. }
  378. $form['filter']['actions']['reset'] = array(
  379. '#type' => 'submit',
  380. '#value' => t('Reset'),
  381. '#weight' => 12,
  382. '#submit' => array('privatemsg_filter_dropdown_submit'),
  383. );
  384. }
  385. function privatemsg_filter_dropdown_submit($form, &$form_state) {
  386. if (!empty($form_state['values']['author'])) {
  387. list($form_state['values']['author']) = _privatemsg_parse_userstring($form_state['values']['author']);
  388. }
  389. switch ($form_state['values']['op']) {
  390. case t('Save filter'):
  391. $filter = array();
  392. if (!empty($form_state['values']['tags'])) {
  393. $filter['tags'] = $form_state['values']['tags'];
  394. }
  395. if (!empty($form_state['values']['author'])) {
  396. $filter['author'] = $form_state['values']['author'];
  397. }
  398. if (!empty($form_state['values']['search'])) {
  399. $filter['search'] = $form_state['values']['search'];
  400. }
  401. $_SESSION['privatemsg_filter'] = $filter;
  402. break;
  403. case t('Filter'):
  404. drupal_goto($_GET['q'], array('query' => privatemsg_filter_create_get_query($form_state['values'])));
  405. return;
  406. break;
  407. case t('Reset'):
  408. $_SESSION['privatemsg_filter'] = array();
  409. break;
  410. }
  411. $form_state['redirect'] = $_GET['q'];
  412. }
  413. /**
  414. * Creates a GET query based on the selected filters.
  415. */
  416. function privatemsg_filter_create_get_query($filter) {
  417. $query = array();
  418. if (isset($filter['tags']) && !empty($filter['tags'])) {
  419. $ids = array();
  420. foreach ($filter['tags'] as $tag) {
  421. if ((int)$tag > 0) {
  422. $ids[] = $tag;
  423. }
  424. else {
  425. $query['tags'][] = $tag;
  426. }
  427. }
  428. $sql = 'SELECT pmt.tag FROM {pm_tags} pmt WHERE pmt.tag_id IN (:tags)';
  429. $query['tags'] = db_query($sql, array(':tags' => $filter['tags']))->fetchCol();
  430. if (isset($query['tags'])) {
  431. $query['tags'] = implode(',', $query['tags']);
  432. }
  433. }
  434. if (isset($filter['author']) && !empty($filter['author'])) {
  435. foreach ($filter['author'] as $author) {
  436. if (is_object($author) && isset($author->uid) && isset($author->name)) {
  437. $query['author'][] = privatemsg_recipient_format($author, array('plain' => TRUE));
  438. }
  439. elseif (is_int($author) && $author_obj = array_shift(privatemsg_user_load_multiple(array($author)))) {
  440. $query['author'][] = privatemsg_recipient_format($author_obj, array('plain' => TRUE));
  441. }
  442. }
  443. if (isset($query['author'])) {
  444. $query['author'] = implode(',', $query['author']);
  445. }
  446. }
  447. if (isset($filter['search']) && !empty($filter['search'])) {
  448. $query['search'] = $filter['search'];
  449. }
  450. return $query;
  451. }
  452. /**
  453. * Implements hook_form_FORM_ID_alter() to add a filter widget to the message listing pages.
  454. */
  455. function privatemsg_filter_form_privatemsg_list_alter(&$form, $form_state) {
  456. global $user;
  457. if (privatemsg_user_access('filter private messages') && !empty($form['updated']['list']['#options']) || privatemsg_filter_get_filter($user)) {
  458. $form += privatemsg_filter_dropdown($form_state, $form['account']['#value']);
  459. }
  460. $fields = privatemsg_get_enabled_headers();
  461. if (privatemsg_user_access('tag private messages') && in_array('tags', $fields) && !empty($form['updated']['list']['#options'])) {
  462. // Load thread id's of the current list.
  463. $threads = array_keys($form['updated']['list']['#options']);
  464. // Fetch all tags of those threads.
  465. $query = _privatemsg_assemble_query(array('tags', 'privatemsg_filter'), $user, $threads, 3);
  466. // Add them to tableselect options.
  467. foreach ($query->execute() as $tag) {
  468. $form['updated']['list']['#options'][$tag->thread_id]['tags'][$tag->tag_id] = $tag->tag;
  469. }
  470. // Avoid notices for threads without tags.
  471. foreach ($form['updated']['list']['#options'] as &$thread) {
  472. if (empty($thread['tags'])) {
  473. $thread['tags'] = array();
  474. }
  475. }
  476. }
  477. if (privatemsg_user_access('tag private messages') && !empty( $form['updated']['list']['#options'])) {
  478. $form['updated']['actions']['tag-add'] = array(
  479. '#type' => 'textfield',
  480. '#size' => 15,
  481. '#autocomplete_path' => 'messages/filter/tag-autocomplete',
  482. );
  483. $form['updated']['actions']['tag-add-submit'] = array(
  484. '#type' => 'submit',
  485. '#value' => t('Apply Tag'),
  486. '#submit' => array('privatemsg_filter_add_tag_submit'),
  487. '#ajax' => array(
  488. 'callback' => 'privatemsg_list_js',
  489. 'wrapper' => 'privatemsg-list-form',
  490. 'effect' => 'fade',
  491. ),
  492. );
  493. $tags = privatemsg_filter_get_tags_data($user);
  494. if (!empty($tags)) {
  495. $options[0] = t('Remove Tag...');
  496. foreach ($tags as $tag_id => $tag) {
  497. $options[$tag_id] = $tag;
  498. }
  499. $form['updated']['actions']['tag-remove'] = array(
  500. '#type' => 'select',
  501. '#options' => $options,
  502. '#default_value' => 0,
  503. '#ajax' => array(
  504. 'callback' => 'privatemsg_list_js',
  505. 'wrapper' => 'privatemsg-list-form',
  506. 'effect' => 'fade',
  507. ),
  508. '#submit' => array('privatemsg_filter_remove_tag_submit'),
  509. '#executes_submit_callback' => TRUE,
  510. );
  511. $form['updated']['actions']['tag-remove-submit'] = array(
  512. '#type' => 'submit',
  513. '#value' => t('Remove Tag'),
  514. '#submit' => array('privatemsg_filter_remove_tag_submit'),
  515. '#attributes' => array('class' => array('form-item')),
  516. '#states' => array(
  517. 'visible' => array(
  518. // This is never true, button is always hidden when JS is enabled.
  519. ':input[name=operation]' => array('value' => 'fake'),
  520. ),
  521. ),
  522. );
  523. }
  524. }
  525. }
  526. /**
  527. * Form callback for removing a tag to threads.
  528. */
  529. function privatemsg_filter_privatemsg_thread_operations($type) {
  530. if ($type == 'inbox') {
  531. $archive = array(
  532. 'label' => t('Archive'),
  533. 'callback' => 'privatemsg_filter_remove_tags',
  534. 'callback arguments' => array('tag_id' => variable_get('privatemsg_filter_inbox_tag', '')),
  535. 'success message' => t('The messages have been archived.'),
  536. 'undo callback' => 'privatemsg_filter_add_tags',
  537. 'undo callback arguments' => array('tag_id' => variable_get('privatemsg_filter_inbox_tag', '')),
  538. );
  539. return array('archive' => $archive);
  540. }
  541. }
  542. /**
  543. * Implements hook_privatemsg_header_info().
  544. */
  545. function privatemsg_filter_privatemsg_header_info() {
  546. return array(
  547. 'tags' => array(
  548. 'data' => t('Tags'),
  549. 'class' => array('privatemsg-header-tags'),
  550. '#weight' => -23,
  551. '#access' => privatemsg_user_access('tag private messages'),
  552. ),
  553. );
  554. }
  555. /**
  556. * Default theme pattern function to display tags.
  557. *
  558. * @see theme_privatemsg_list_field()
  559. */
  560. function theme_privatemsg_list_field__tags($arguments) {
  561. $thread = $arguments['thread'];
  562. if (!empty($thread['tags'])) {
  563. $tags = array();
  564. foreach ($thread['tags'] as $tag) {
  565. $tags[] = l(drupal_strlen($tag) > 15 ? drupal_substr($tag, 0, 13) . '...' : $tag, 'messages', array(
  566. 'attributes' => array('title' => $tag),
  567. 'query' => array('tags' => $tag)
  568. ));
  569. }
  570. return array(
  571. 'data' => implode(', ', $tags),
  572. 'class' => array('privatemsg-list-tags'),
  573. );
  574. }
  575. // Return an empty row.
  576. return array('data' => '');
  577. }
  578. /**
  579. * Form callback for adding a tag to threads.
  580. */
  581. function privatemsg_filter_add_tag_submit($form, &$form_state) {
  582. // Check if textfield is not empty.
  583. if (empty($form_state['values']['tag-add'])) {
  584. return;
  585. }
  586. $tags = explode(',', $form_state['values']['tag-add']);
  587. $tag_ids = privatemsg_filter_create_tags($tags);
  588. if (empty($tag_ids)) {
  589. return;
  590. }
  591. $operation = array(
  592. 'callback' => 'privatemsg_filter_add_tags',
  593. 'callback arguments' => array('tag_id' => $tag_ids),
  594. 'success message' => t('The selected conversations have been tagged.'),
  595. 'undo callback' => 'privatemsg_filter_remove_tags',
  596. 'undo callback arguments' => array('tag_id' => $tag_ids),
  597. );
  598. privatemsg_operation_execute($operation, $form_state['values']['list']);
  599. $form_state['rebuild'] = TRUE;
  600. $form_state['input'] = array();
  601. }
  602. /**
  603. * Form callback for removing a tag to threads.
  604. */
  605. function privatemsg_filter_remove_tag_submit($form, &$form_state) {
  606. $operation = array(
  607. 'callback' => 'privatemsg_filter_remove_tags',
  608. 'callback arguments' => array('tag_id' => $form_state['values']['tag-remove']),
  609. 'success message' => t('The tag has been removed from the selected conversations.'),
  610. 'undo callback' => 'privatemsg_filter_add_tags',
  611. 'undo callback arguments' => array('tag_id' => $form_state['values']['tag-remove']),
  612. );
  613. privatemsg_operation_execute($operation, $form_state['values']['list']);
  614. $form_state['rebuild'] = TRUE;
  615. $form_state['input'] = array();
  616. }
  617. /**
  618. * Hook into the query builder to add the tagging info to the correct query
  619. */
  620. function privatemsg_filter_query_privatemsg_list_alter($query) {
  621. $account = $query->getMetaData('arg_1');
  622. $argument = $query->getMetaData('arg_2');
  623. // Add all conditions to the count query too.
  624. $count_query = $query->getCountQuery();
  625. // Check if its a filtered view.
  626. if ($argument == 'sent') {
  627. $query->condition('pm.author', $account->uid);
  628. $count_query->condition('pm.author', $account->uid);
  629. }
  630. $filter = privatemsg_filter_get_filter($account);
  631. if ($argument == 'inbox') {
  632. $filter['tags'][] = variable_get('privatemsg_filter_inbox_tag', '');
  633. }
  634. // Filter the message listing by any set tags.
  635. if ($filter) {
  636. if (!empty($filter['tags'])) {
  637. foreach ($filter['tags'] as $tag) {
  638. $alias = $query->join('pm_tags_index', 'pmti', "%alias.thread_id = pmi.thread_id AND %alias.uid = pmi.recipient AND pmi.type IN ('user', 'hidden')");
  639. $query->condition($alias . '.tag_id', $tag);
  640. $alias = $count_query->join('pm_tags_index', 'pmti',"%alias.thread_id = pmi.thread_id AND %alias.uid = pmi.recipient AND pmi.type IN ('user', 'hidden')");
  641. $count_query->condition($alias . '.tag_id', $tag);
  642. }
  643. }
  644. if (isset($filter['author']) && !empty($filter['author'])) {
  645. foreach ($filter['author'] as $author) {
  646. $alias = $query->join('pm_index', 'pmi', '%alias.mid = pm.mid');
  647. $query->condition($alias . '.recipient', $author->uid);
  648. $query->condition($alias . '.type', 'user');
  649. $alias = $count_query->join('pm_index', 'pmi', '%alias.mid = pm.mid');
  650. $count_query->condition($alias . '.recipient', $author->uid);
  651. $count_query->condition($alias . '.type', 'user');
  652. }
  653. }
  654. if (!empty($filter['search'])) {
  655. if (variable_get('privatemsg_filter_searchbody', FALSE)) {
  656. $search = db_or()
  657. ->condition('pm.subject', '%' . $filter['search'] . '%', 'LIKE')
  658. ->condition('pm.body', '%' . $filter['search'] . '%', 'LIKE');
  659. // Clone the condition so that they are both compiled.
  660. $query->condition(clone $search);
  661. $count_query->condition($search);
  662. }
  663. else {
  664. $query->condition('pm.subject', '%'. $filter['search'] .'%', 'LIKE');
  665. $count_query->condition('pm.subject', '%'. $filter['search'] .'%', 'LIKE');
  666. }
  667. }
  668. }
  669. }
  670. /**
  671. * Implements hook_privatemsg_view_alter().
  672. */
  673. function privatemsg_filter_privatemsg_view_alter(&$content) {
  674. if (privatemsg_user_access('tag private messages')) {
  675. $content['tags'] = privatemsg_filter_show_tags($content['#thread']['thread_id'], !empty($_GET['show_tags_form']));
  676. }
  677. }
  678. function privatemsg_filter_show_tags($thread_id, $show_form) {
  679. global $user;
  680. $element = array(
  681. '#prefix' => '<div id="privatemsg-filter-tags">',
  682. '#suffix' => '</div>',
  683. '#weight' => -10,
  684. '#attached' => array(
  685. 'css' => array(
  686. drupal_get_path('module', 'privatemsg_filter') . '/privatemsg_filter.css',
  687. ),
  688. ),
  689. );
  690. if (!$show_form) {
  691. $query = _privatemsg_assemble_query(array('tags', 'privatemsg_filter'), $user, array($thread_id));
  692. if ($query->countQuery()->execute()->fetchField() == 0) {
  693. $element['link'] = array(
  694. '#type' => 'link',
  695. '#href' => $_GET['q'],
  696. '#options' => array(
  697. 'query' => array('show_tags_form' => TRUE),
  698. 'attributes' => array('class' => array('privatemsg-filter-tags-add')),
  699. ),
  700. '#title' => t('Tag this conversation'),
  701. );
  702. }
  703. else {
  704. $element['label'] = array(
  705. '#prefix' => '<span class="privatemsg-filter-tags-label">',
  706. '#suffix' => '</span>',
  707. '#markup' => t('Tags:'),
  708. );
  709. foreach ($query->execute()->fetchCol(1) as $tag) {
  710. $element['tags'][] = array(
  711. '#type' => 'link',
  712. '#title' => $tag,
  713. '#href' => 'messages',
  714. '#options' => array(
  715. 'attributes' => array('title' => $tag),
  716. 'query' => array('tags' => $tag),
  717. ),
  718. );
  719. }
  720. $element['link'] = array(
  721. '#type' => 'link',
  722. '#href' => $_GET['q'],
  723. '#options' => array(
  724. 'query' => array('show_tags_form' => TRUE),
  725. 'attributes' => array('class' => array('privatemsg-filter-tags-modify')),
  726. ),
  727. '#title' => t('(modify tags)'),
  728. );
  729. }
  730. return $element;
  731. }
  732. else {
  733. return drupal_get_form('privatemsg_filter_form', $thread_id) + $element;
  734. }
  735. }
  736. /**
  737. * Form to show and allow modification of tagging information for a conversation.
  738. */
  739. function privatemsg_filter_form($form, &$form_state, $thread_id) {
  740. global $user;
  741. // Get a list of current tags for this thread
  742. $query = _privatemsg_assemble_query(array('tags', 'privatemsg_filter'), $user, array($thread_id));
  743. $tags = implode(', ', $query->execute()->fetchCol(1));
  744. $form['user_id'] = array(
  745. '#type' => 'value',
  746. '#value' => $user->uid,
  747. );
  748. $form['thread_id'] = array(
  749. '#type' => 'value',
  750. '#value' => $thread_id,
  751. );
  752. $form['tags'] = array(
  753. '#type' => 'textfield',
  754. '#title' => t('Tags for this conversation'),
  755. '#title_display' => 'invisible',
  756. '#size' => 30,
  757. '#default_value' => $tags,
  758. '#autocomplete_path' => 'messages/filter/tag-autocomplete',
  759. );
  760. $form['modify_tags'] = array(
  761. '#type' => 'submit',
  762. '#value' => t('Tag this conversation'),
  763. );
  764. $form['cancel'] = array(
  765. '#type' => 'link',
  766. '#href' => $_GET['q'],
  767. '#title' => t('Cancel'),
  768. '#attributes' => array('id' => 'privatemsg-filter-tags-cancel'),
  769. '#weight' => 50,
  770. );
  771. return $form;
  772. }
  773. /**
  774. * Form builder function, display a form to modify tags on a thread.
  775. */
  776. function privatemsg_filter_form_submit($form, &$form_state) {
  777. $tags = explode(',', $form_state['values']['tags']);
  778. // Step 1 - Delete all tag mapping.
  779. privatemsg_filter_remove_tags($form_state['values']['thread_id']);
  780. // Step 2 - Get the id for each of the tags.
  781. $tag_ids = privatemsg_filter_create_tags($tags);
  782. // Step 3 - Save all the tagging data.
  783. foreach ($tag_ids as $tag_id) {
  784. privatemsg_filter_add_tags($form_state['values']['thread_id'], $tag_id);
  785. }
  786. $form_state['redirect'] = current_path();
  787. drupal_set_message(t('Your conversation tags have been saved.'));
  788. }
  789. /**
  790. * Limit the user autocomplete for the filter widget.
  791. */
  792. function privatemsg_filter_query_privatemsg_autocomplete_alter($query) {
  793. global $user;
  794. if (arg(1) == 'filter') {
  795. $query->join('pm_index', 'pip', "pip.recipient = u.uid AND pip.type = 'user'");
  796. $query->join('pm_index', 'piu', "piu.recipient = :uid_index AND piu.type = 'user' AND pip.mid = piu.mid", array(':uid_index' => $user->uid));
  797. }
  798. }
  799. /**
  800. * Query definition to get the tags in use by the specified user.
  801. *
  802. * @param $user
  803. * User object for whom we want the tags.
  804. * @param $threads
  805. * Array of thread ids, defaults to all threads of a user.
  806. * @param $limit
  807. * Limit the number of tags *per thread*.
  808. */
  809. function privatemsg_filter_sql_tags($user = NULL, $threads = NULL, $limit = NULL, $showHidden = FALSE) {
  810. $query = db_select('pm_tags', 't')
  811. ->fields('t', array('tag_id', 'tag', 'public'))
  812. ->orderBy('t.tag', 'ASC');
  813. if (!empty($threads)) {
  814. $query->addField('ti', 'thread_id');
  815. $query->join('pm_tags_index', 'ti', 'ti.tag_id = t.tag_id');
  816. $query->condition('ti.thread_id', $threads);
  817. }
  818. else {
  819. $query->addExpression('COUNT(ti.thread_id)', 'count');
  820. $query->leftJoin('pm_tags_index', 'ti', 'ti.tag_id = t.tag_id');
  821. $query
  822. ->groupBy('t.tag_id')
  823. ->groupBy('t.tag')
  824. ->groupBy('t.public');
  825. }
  826. if (!empty($user)) {
  827. $query->condition('ti.uid', $user->uid);
  828. }
  829. if (!$showHidden) {
  830. $query->condition(db_or()->condition('t.hidden', 0)->isNull('t.hidden'));
  831. }
  832. // Only select n tags per thread (ordered per tag_id), see
  833. // http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/.
  834. //
  835. // It does select how many tags for that thread/uid combination exist that
  836. // have a lower tag_id and does only select those that have less than $limit.
  837. //
  838. // This should only have a very minor performance impact as most users won't
  839. // tag a thread with 1000 different tags.
  840. if ($limit) {
  841. $query->where('(SELECT count(*) FROM {pm_tags_index} AS pmtic
  842. WHERE pmtic.thread_id = ti.thread_id
  843. AND pmtic.uid = ti.uid
  844. AND pmtic.tag_id < ti.tag_id) < :limit', array(':limit' => $limit));
  845. }
  846. elseif (!empty($thread_id) || !empty($user)) {
  847. $query->orderBy('t.tag', 'ASC');
  848. }
  849. return $query;
  850. }
  851. /**
  852. * Query definition to get autocomplete suggestions for tags
  853. *
  854. * @param $search
  855. * String fragment to use for tag suggestions.
  856. * @param $tags
  857. * Array of tags not to be used as suggestions.
  858. */
  859. function privatemsg_filter_sql_tags_autocomplete($search, $tags) {
  860. $query = db_select('pm_tags', 'pmt')
  861. ->fields('pmt', array('tag'))
  862. ->condition('pmt.tag', $search . '%%', 'LIKE')
  863. ->orderBy('pmt.tag', 'ASC')
  864. ->range(0, 10);
  865. if (!empty($tags)) {
  866. $query->condition('pmt.tag', $tags, 'NOT IN');
  867. }
  868. return $query;
  869. }
  870. /**
  871. * Implements hook_user_cancel().
  872. */
  873. function privatemsg_filter_user_cancel($edit, $account, $method) {
  874. // Always delete since this is only visible for the user anyway.
  875. db_delete('pm_tags_index')
  876. ->condition('uid', $account->uid)
  877. ->execute();
  878. }
  879. /**
  880. * Implements hook_privatemsg_message_insert().
  881. */
  882. function privatemsg_filter_privatemsg_message_insert($message) {
  883. foreach ($message->recipients as $recipient) {
  884. if ($recipient->type == 'user' || $recipient->type == 'hidden') {
  885. privatemsg_filter_add_tags(array($message->thread_id), variable_get('privatemsg_filter_inbox_tag', ''), $recipient);
  886. }
  887. }
  888. }
  889. /**
  890. * Implements hook_privatemsg_message_recipient_changed().
  891. */
  892. function privatemsg_filter_privatemsg_message_recipient_changed($mid, $thread_id, $recipient, $type, $added) {
  893. if ($added && ($type == 'user' || $type == 'hidden')) {
  894. privatemsg_filter_add_tags(array($thread_id), variable_get('privatemsg_filter_inbox_tag', ''), (object)array('uid' => $recipient));
  895. }
  896. }

Functions

Namesort descending Description
privatemsg_filter_add_tags Tag one or multiple threads with a tag.
privatemsg_filter_add_tag_submit Form callback for adding a tag to threads.
privatemsg_filter_create_get_query Creates a GET query based on the selected filters.
privatemsg_filter_create_tags Function to create a tag
privatemsg_filter_dropdown
privatemsg_filter_dropdown_set_active
privatemsg_filter_dropdown_submit
privatemsg_filter_form Form to show and allow modification of tagging information for a conversation.
privatemsg_filter_form_privatemsg_admin_settings_alter Implements hook_form_FORM_ID_alter() to add a filter widget to the message listing pages.
privatemsg_filter_form_privatemsg_list_alter Implements hook_form_FORM_ID_alter() to add a filter widget to the message listing pages.
privatemsg_filter_form_submit Form builder function, display a form to modify tags on a thread.
privatemsg_filter_get_filter
privatemsg_filter_get_tags_data
privatemsg_filter_menu Implements hook_menu().
privatemsg_filter_menu_alter Implement hook_menu_alter().
privatemsg_filter_permission Implements hook_permission().
privatemsg_filter_privatemsg_header_info Implements hook_privatemsg_header_info().
privatemsg_filter_privatemsg_message_insert Implements hook_privatemsg_message_insert().
privatemsg_filter_privatemsg_message_recipient_changed Implements hook_privatemsg_message_recipient_changed().
privatemsg_filter_privatemsg_thread_operations Form callback for removing a tag to threads.
privatemsg_filter_privatemsg_view_alter Implements hook_privatemsg_view_alter().
privatemsg_filter_query_privatemsg_autocomplete_alter Limit the user autocomplete for the filter widget.
privatemsg_filter_query_privatemsg_list_alter Hook into the query builder to add the tagging info to the correct query
privatemsg_filter_remove_tags Remove tag from one or multiple threads.
privatemsg_filter_remove_tag_submit Form callback for removing a tag to threads.
privatemsg_filter_settings_submit Rebuilding the menu if necessary.
privatemsg_filter_show_tags
privatemsg_filter_sql_tags Query definition to get the tags in use by the specified user.
privatemsg_filter_sql_tags_autocomplete Query definition to get autocomplete suggestions for tags
privatemsg_filter_user_cancel Implements hook_user_cancel().
theme_privatemsg_list_field__tags Default theme pattern function to display tags.