Projet

Général

Profil

Paste
Télécharger (32,9 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / privatemsg / privatemsg_filter / privatemsg_filter.module @ e3063c4a

1
<?php
2

    
3
/**
4
 * @file
5
 * Allows users to tag private messages and to filter based upon those tags.
6
 */
7

    
8
/**
9
 * Implements hook_permission().
10
 */
11
function privatemsg_filter_permission() {
12
  return array(
13
    'filter private messages' => array(
14
      'title' => t('Filter private messages'),
15
      'description' => t('Use the search and filter widget'),
16
    ),
17
    'tag private messages' => array(
18
      'title' => t('Tag private messages'),
19
      'description' => t('Tag private messages'),
20
    ),
21
    'create private message tags' => array(
22
      'title' => t('Create private message tags'),
23
      'description' => t('Create new private message tags'),
24
    ),
25
  );
26
}
27

    
28
/**
29
 * Implements hook_menu().
30
 */
31
function privatemsg_filter_menu() {
32
  $items['admin/config/messaging/privatemsg/tags'] = array(
33
    'title'            => 'Tags',
34
    'description'      => 'Configure tags.',
35
    'page callback'    => 'privatemsg_tags_admin',
36
    'file'             => 'privatemsg_filter.admin.inc',
37
    'access arguments' => array('administer privatemsg settings'),
38
    'type'             => MENU_LOCAL_TASK,
39
  );
40
  $items['admin/config/messaging/privatemsg/tags/list'] = array(
41
    'title'            => 'List',
42
    'description'      => 'Configure tags.',
43
    'page callback'    => 'privatemsg_tags_admin',
44
    'file'             => 'privatemsg_filter.admin.inc',
45
    'access arguments' => array('administer privatemsg settings'),
46
    'type'             => MENU_DEFAULT_LOCAL_TASK,
47
    'weight'           => -5,
48
  );
49
  $items['admin/config/messaging/privatemsg/tags/add'] = array(
50
    'title'            => 'Add tag',
51
    'description'      => 'Configure tags.',
52
    'page callback'    => 'drupal_get_form',
53
    'page arguments'   => array('privatemsg_tags_form'),
54
    'file'             => 'privatemsg_filter.admin.inc',
55
    'access arguments' => array('administer privatemsg settings'),
56
    'type'             => MENU_LOCAL_ACTION,
57
  );
58
  $items['admin/config/messaging/privatemsg/tags/rebuild'] = array(
59
    'title'            => 'Rebuild inbox',
60
    'page callback'    => 'drupal_get_form',
61
    'page arguments'   => array('privatemsg_filter_inbox_rebuid_form'),
62
    'file'             => 'privatemsg_filter.admin.inc',
63
    'access arguments' => array('administer privatemsg settings'),
64
    'type'             => MENU_LOCAL_TASK,
65
  );
66

    
67
  $items['admin/config/messaging/privatemsg/tags/edit/%'] = array(
68
    'title'            => 'Edit tag',
69
    'description'      => 'Configure tags.',
70
    'page callback'    => 'drupal_get_form',
71
    'page arguments'   => array('privatemsg_tags_form', 6),
72
    'file'             => 'privatemsg_filter.admin.inc',
73
    'access arguments' => array('administer privatemsg settings'),
74
    'type'             => MENU_CALLBACK,
75
  );
76
  $items['admin/config/messaging/privatemsg/tags/delete/%'] = array(
77
    'title'            => 'Delete tag',
78
    'description'      => 'Configure tags.',
79
    'page callback'    => 'drupal_get_form',
80
    'page arguments'   => array('privatemsg_filter_tags_delete', 6),
81
    'file'             => 'privatemsg_filter.admin.inc',
82
    'access arguments' => array('administer privatemsg settings'),
83
    'type'             => MENU_CALLBACK,
84
  );
85
  $items['messages/inbox'] = array(
86
    'title'            => 'Inbox',
87
    'page callback'    => 'privatemsg_list_page',
88
    'page arguments'   => array('inbox'),
89
    'file'             => 'privatemsg.pages.inc',
90
    'file path'        => drupal_get_path('module', 'privatemsg'),
91
    'access callback'  => 'privatemsg_user_access',
92
    'type'             => variable_get('privatemsg_filter_default_list', 0) ? MENU_LOCAL_TASK : MENU_DEFAULT_LOCAL_TASK,
93
    'weight'           => -15,
94
    'menu_name'        => 'user-menu',
95
  );
96
  $items['messages/sent'] = array(
97
    'title'            => 'Sent Messages',
98
    'page callback'    => 'privatemsg_list_page',
99
    'page arguments'   => array('sent'),
100
    'file'             => 'privatemsg.pages.inc',
101
    'file path'        => drupal_get_path('module', 'privatemsg'),
102
    'access callback'  => 'privatemsg_user_access',
103
    'type'             => MENU_LOCAL_TASK,
104
    'weight'           => -12,
105
    'menu_name'        => 'user-menu',
106
  );
107
  $items['messages/filter/autocomplete'] = array(
108
    'page callback'    => 'privatemsg_autocomplete',
109
    'file'             => 'privatemsg.pages.inc',
110
    'file path'        => drupal_get_path('module', 'privatemsg'),
111
    'access callback'  => 'privatemsg_user_access',
112
    'access arguments' => array('write privatemsg'),
113
    'type'             => MENU_CALLBACK,
114
    'weight'           => -10,
115
  );
116
  $items['messages/filter/tag-autocomplete'] = array(
117
    'page callback'    => 'privatemsg_filter_tags_autocomplete',
118
    'file'             => 'privatemsg_filter.pages.inc',
119
    'access callback'  => 'privatemsg_user_access',
120
    'access arguments' => array('tag private messages'),
121
    'type'             => MENU_CALLBACK,
122
    'weight'           => -10,
123
  );
124
  return $items;
125
}
126

    
127
/**
128
 * Implement hook_menu_alter().
129
 */
130
function privatemsg_filter_menu_alter(&$items) {
131
  // Rename messages to "All messages".
132
  $items['messages/list']['title'] = 'All messages';
133

    
134
  if (variable_get('privatemsg_filter_default_list', 0) == 0) {
135
    // Change default argument of /messages to inbox. and set the task to MENU_LOCAL_TASK.
136
    $items['messages']['page arguments'] = array('inbox');
137
    $items['messages/list']['type'] = MENU_LOCAL_TASK;
138
  }
139
}
140

    
141
/**
142
 * Implements hook_form_FORM_ID_alter() to add a filter widget to the message listing pages.
143
 */
144
function privatemsg_filter_form_privatemsg_admin_settings_alter(&$form, $form_state) {
145
  $form['privatemsg_listing']['privatemsg_filter_default_list'] = array(
146
    '#type' => 'radios',
147
    '#default_value' => variable_get('privatemsg_filter_default_list', 0),
148
    '#options' => array(t('Inbox'), t('All messages')),
149
    '#title' => t('Choose the default list option'),
150
    '#description' => t('Choose which of the two lists are shown by default when following the messages link.'),
151
  );
152

    
153
  $form['privatemsg_listing']['privatemsg_filter_searchbody'] = array(
154
    '#type'          => 'checkbox',
155
    '#title'         => t('Search message body'),
156
    '#description'   => t('WARNING: turning on this feature will slow down search performance by a large factor. Gets worse as your messages database increases.'),
157
    '#default_value' => variable_get('privatemsg_filter_searchbody', FALSE),
158
  );
159

    
160
  // Add tags to the list of possible columns.
161
  $form['privatemsg_listing']['privatemsg_display_fields']['#options']['tags'] = t('Tags');
162

    
163
  $form['#submit'][] = 'privatemsg_filter_settings_submit';
164
}
165

    
166
/**
167
 * Rebuilding the menu if necessary.
168
 */
169
function privatemsg_filter_settings_submit($form, &$form_state) {
170
  if ($form['privatemsg_listing']['privatemsg_filter_default_list']['#default_value'] != $form_state['values']['privatemsg_filter_default_list']) {
171
    menu_rebuild();
172
  }
173
}
174

    
175
/**
176
 * Function to create a tag
177
 *
178
 * @param $tags A single tag or an array of tags.
179
 */
180
function privatemsg_filter_create_tags($tags) {
181
  if (!is_array($tags)) {
182
    $tags = array($tags);
183
  }
184

    
185
  $tag_ids = array();
186

    
187
  foreach ($tags as $tag) {
188
    $tag = trim($tag);
189
    if (empty($tag)) {
190
      // Do not save a blank tag.
191
      continue;
192
    }
193

    
194
    // Check if the tag already exists and only create the tag if it does not.
195
    $tag_id = db_query("SELECT tag_id FROM {pm_tags} WHERE tag = :tag", array(':tag' => $tag))->fetchField();
196
    if (empty($tag_id) && privatemsg_user_access('create private message tags')) {
197
      $tag_id = db_insert('pm_tags')
198
        ->fields(array('tag' => $tag))
199
        ->execute();
200
    }
201
    elseif (empty($tag_id)) {
202
      // The user does not have permission to create new tags - disregard this tag and move onto the next.
203
      drupal_set_message(t('Tag %tag was ignored because you do not have permission to create new tags.', array('%tag' => $tag)));
204
      continue;
205
    }
206
    $tag_ids[] = $tag_id;
207
  }
208
  return $tag_ids;
209
}
210

    
211
/**
212
 * Tag one or multiple threads with a tag.
213
 *
214
 * @param $threads A single thread id or an array of thread ids.
215
 * @param $tag_id Id of the tag.
216
 */
217
function privatemsg_filter_add_tags($threads, $tag_ids, $account = NULL) {
218
  if (!is_array($threads)) {
219
    $threads = array($threads);
220
  }
221
  if (!is_array($tag_ids)) {
222
    $tag_ids = array($tag_ids);
223
  }
224
  if (empty($account)) {
225
    global $user;
226
    $account = clone $user;
227
  }
228
  foreach ($tag_ids as $tag_id) {
229
    foreach ($threads as $thread) {
230
      // Make sure that we don't add a tag to a thread twice,
231
      // only insert if there is no such tag yet.
232
        db_merge('pm_tags_index')
233
          ->key(array(
234
            'tag_id' => $tag_id,
235
            'uid' => $account->uid,
236
            'thread_id' => $thread,
237
          ))
238
          ->execute();
239
    }
240
  }
241
}
242

    
243
/**
244
 * Remove tag from one or multiple threads.
245
 *
246
 * @param $threads A single thread id or an array of thread ids.
247
 * @param $tag_id Id of the tag - set to NULL to remove all tags.
248
 */
249
function privatemsg_filter_remove_tags($threads, $tag_ids = NULL, $account = NULL) {
250
  if (!is_array($threads)) {
251
    $threads = array($threads);
252
  }
253
  if (empty($account)) {
254
    global $user;
255
    $account = $user;
256
  }
257

    
258
  if (is_null($tag_ids)) {
259
    //Delete all tag mapping - all except for the inbox tag if it exists.
260
    db_delete('pm_tags_index')
261
      ->condition('uid', $account->uid)
262
      ->condition('thread_id', $threads)
263
      ->condition('tag_id', variable_get('privatemsg_filter_inbox_tag', ''), '<>')
264
      ->execute();
265
  }
266
  else {
267
    if (!is_array($tag_ids)) {
268
      $tag_ids = array($tag_ids);
269
    }
270

    
271
    //Delete tag mapping for the specified tag.
272
    db_delete('pm_tags_index')
273
      ->condition('uid', $account->uid)
274
      ->condition('thread_id', $threads)
275
      ->condition('tag_id', $tag_ids)
276
      ->execute();
277
  }
278
}
279

    
280
function privatemsg_filter_get_filter($account) {
281
  $filter = array();
282
  // Filtering by tags is either allowed if the user can use tags or he can
283
  // filter.
284
  if (privatemsg_user_access('filter private messages') || privatemsg_user_access('tag private messages')) {
285
    if (isset($_GET['tags'])) {
286
      $_GET['tags'] = urldecode($_GET['tags']);
287
      $tag_data = privatemsg_filter_get_tags_data($account);
288
      foreach (explode(',', $_GET['tags']) as $tag) {
289
        if (isset($tag_data[$tag])) {
290
          $filter['tags'][$tag] = $tag;
291
        }
292
        elseif (in_array($tag, $tag_data)) {
293
          $filter['tags'][array_search($tag, $tag_data)] = array_search($tag, $tag_data);
294
        }
295
      }
296
    }
297
  }
298

    
299
  // Users can only use the text search or search by author if they have the
300
  // necessary permission.
301
  if (privatemsg_user_access('filter private messages')) {
302
    if (isset($_GET['author'])) {
303
      list($filter['author']) = _privatemsg_parse_userstring($_GET['author']);
304
    }
305

    
306
    if (isset($_GET['search'])) {
307
      $filter['search'] = $_GET['search'];
308
    }
309
  }
310

    
311
  if (!empty($filter)) {
312
    return $filter;
313
  }
314

    
315
  if (!empty($_SESSION['privatemsg_filter'])) {
316
    return $_SESSION['privatemsg_filter'];
317
  }
318

    
319
}
320

    
321
function privatemsg_filter_get_tags_data($account) {
322
  static $tag_data;
323

    
324
  if (is_array($tag_data)) {
325
    return $tag_data;
326
  }
327

    
328
  // Only show the tags that a user have used.
329
  return $tag_data = _privatemsg_assemble_query(array('tags', 'privatemsg_filter'), $account)->execute()->fetchAllKeyed();
330
}
331

    
332
function privatemsg_filter_dropdown(&$form_state, $account) {
333

    
334
  drupal_add_css(drupal_get_path('module', 'privatemsg_filter') . '/privatemsg_filter.css');
335

    
336
  $form['filter'] = array(
337
    '#type' => 'fieldset',
338
    '#title' => t('Filter Messages'),
339
    '#collapsible' => TRUE,
340
    '#collapsed' => TRUE,
341
    '#weight' => -20,
342
    // The form is always called when search arguments are passed in, even if
343
    // they don't have access to it. This is necessary to process the search
344
    // query. But we don't want to show them the form.
345
    '#access' => privatemsg_user_access('filter private messages'),
346
  );
347
  $form['filter']['search'] = array(
348
    '#type' => 'textfield',
349
    '#title' => variable_get('privatemsg_filter_searchbody', FALSE) ? t('By message text') : t('By subject'),
350
    '#weight' => -20,
351
    '#size' => 25,
352
  );
353

    
354
  $form['filter']['author'] = array(
355
    '#type'               => 'textfield',
356
    '#title'              => t('By participant'),
357
    '#weight'             => -5,
358
    '#size'               => 25,
359
    '#autocomplete_path'  => 'messages/filter/autocomplete',
360
  );
361

    
362
  // Only show form if the user has some messages tagged.
363
  if (count($tag_data = privatemsg_filter_get_tags_data($account))) {
364
    $form['filter']['tags'] = array(
365
      '#type' => 'select',
366
      '#title' => t('By tags'),
367
      '#options' => $tag_data,
368
      '#multiple' => TRUE,
369
      '#weight' => 0
370
    );
371
  }
372
  $form['filter']['actions'] = array(
373
    '#type' => 'actions',
374
    '#attributes' => array('class' => array('privatemsg-filter-actions')),
375
  );
376
  $form['filter']['actions']['submit'] = array(
377
    '#type'     => 'submit',
378
    '#value'    => t('Filter'),
379
    '#weight'   => 10,
380
    '#submit'   => array('privatemsg_filter_dropdown_submit'),
381
  );
382

    
383
  $form['filter']['actions']['save'] = array(
384
    '#type'     => 'submit',
385
    '#value'    => t('Save filter'),
386
    '#weight'   => 11,
387
    '#submit'   => array('privatemsg_filter_dropdown_submit'),
388
  );
389

    
390
  if ($filter = privatemsg_filter_get_filter($account)) {
391
    // Display a message if the user will not see the filter form.
392
    if (!empty($filter['tags']) && !empty($_GET['tags']) && !privatemsg_user_access('filter private messages')) {
393
      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']))));
394
    }
395
    privatemsg_filter_dropdown_set_active($form, $filter);
396
  }
397

    
398
  return $form;
399
}
400

    
401
function privatemsg_filter_dropdown_set_active(&$form, $filter) {
402
  $form['filter']['#title'] = t('Filter Messages (Active)');
403
  $form['filter']['#collapsed'] = FALSE;
404

    
405
  if (isset($filter['author'])) {
406
    $string = '';
407
    foreach ($filter['author'] as $author) {
408
      $string .= privatemsg_recipient_format($author, array('plain' => TRUE)) . ', ';
409
    }
410
    $form['filter']['author']['#default_value'] = $string;
411
  }
412
  if (isset($filter['tags'])) {
413
    $form['filter']['tags']['#default_value'] = $filter['tags'];
414
  }
415
  if (isset($filter['search'])) {
416
    $form['filter']['search']['#default_value'] = $filter['search'];
417
  }
418

    
419
  $form['filter']['actions']['reset'] = array(
420
      '#type'     => 'submit',
421
      '#value'    => t('Reset'),
422
      '#weight'   => 12,
423
      '#submit'   => array('privatemsg_filter_dropdown_submit'),
424
  );
425
}
426

    
427
function privatemsg_filter_dropdown_submit($form, &$form_state) {
428

    
429
  if (!empty($form_state['values']['author'])) {
430
    list($form_state['values']['author']) = _privatemsg_parse_userstring($form_state['values']['author']);
431
  }
432

    
433
  switch ($form_state['values']['op']) {
434
    case t('Save filter'):
435
      $filter = array();
436
      if (!empty($form_state['values']['tags'])) {
437
        $filter['tags'] = $form_state['values']['tags'];
438
      }
439
      if (!empty($form_state['values']['author'])) {
440
        $filter['author'] = $form_state['values']['author'];
441
      }
442
      if (!empty($form_state['values']['search'])) {
443
        $filter['search'] = $form_state['values']['search'];
444
      }
445
      $_SESSION['privatemsg_filter'] = $filter;
446
      break;
447
    case t('Filter'):
448
      drupal_goto($_GET['q'], array('query' => privatemsg_filter_create_get_query($form_state['values'])));
449
      return;
450
      break;
451
    case t('Reset'):
452
      $_SESSION['privatemsg_filter'] = array();
453
      break;
454
  }
455
  $form_state['redirect'] = $_GET['q'];
456
}
457

    
458
/**
459
 * Creates a GET query based on the selected filters.
460
 */
461
function privatemsg_filter_create_get_query($filter) {
462
  $query = array();
463
  if (isset($filter['tags']) && !empty($filter['tags'])) {
464
    $ids = array();
465
    foreach ($filter['tags'] as $tag) {
466
      if ((int)$tag > 0) {
467
        $ids[] = $tag;
468
      }
469
      else {
470
        $query['tags'][] = $tag;
471
      }
472
    }
473
    $sql = 'SELECT pmt.tag FROM {pm_tags} pmt WHERE pmt.tag_id IN (:tags)';
474
    $query['tags'] = db_query($sql, array(':tags' => $filter['tags']))->fetchCol();
475

    
476
    if (isset($query['tags'])) {
477
      $query['tags'] = implode(',', $query['tags']);
478
    }
479
  }
480

    
481
  if (isset($filter['author']) && !empty($filter['author'])) {
482
    foreach ($filter['author'] as $author) {
483
      if (is_object($author) && isset($author->uid) && isset($author->name)) {
484
        $query['author'][] = privatemsg_recipient_format($author, array('plain' => TRUE));
485
      }
486
      elseif (is_int($author) && $author_obj = array_shift(privatemsg_user_load_multiple(array($author)))) {
487
        $query['author'][] = privatemsg_recipient_format($author_obj, array('plain' => TRUE));
488
      }
489
    }
490
    if (isset($query['author'])) {
491
      $query['author'] = implode(',', $query['author']);
492
    }
493
  }
494

    
495
  if (isset($filter['search']) && !empty($filter['search'])) {
496
    $query['search'] = $filter['search'];
497
  }
498
  return $query;
499
}
500

    
501
/**
502
 * Implements hook_form_FORM_ID_alter() to add a filter widget to the message listing pages.
503
 */
504
function privatemsg_filter_form_privatemsg_list_alter(&$form, $form_state) {
505
  global $user;
506
  if (privatemsg_user_access('filter private messages') && !empty($form['updated']['list']['#options']) || privatemsg_filter_get_filter($user)) {
507
    $form += privatemsg_filter_dropdown($form_state, $form['account']['#value']);
508
  }
509

    
510
  $fields = array_filter(variable_get('privatemsg_display_fields', array('participants')));
511
  if (privatemsg_user_access('tag private messages') && in_array('tags', $fields) && !empty($form['updated']['list']['#options'])) {
512
    // Load thread id's of the current list.
513
    $threads = array_keys($form['updated']['list']['#options']);
514

    
515
    // Fetch all tags of those threads.
516
    $query = _privatemsg_assemble_query(array('tags', 'privatemsg_filter'), $user, $threads, 3);
517

    
518
    // Add them to tableselect options.
519
    foreach ($query->execute() as $tag) {
520
      $form['updated']['list']['#options'][$tag->thread_id]['tags'][$tag->tag_id] = $tag->tag;
521
    }
522
    // Avoid notices for threads without tags.
523
    foreach ($form['updated']['list']['#options'] as &$thread) {
524
      if (empty($thread['tags'])) {
525
        $thread['tags'] = array();
526
      }
527
    }
528
  }
529

    
530
  if (privatemsg_user_access('tag private messages') && !empty( $form['updated']['list']['#options'])) {
531
    $form['updated']['actions']['tag-add'] = array(
532
       '#type'               => 'textfield',
533
       '#size'               => 15,
534
       '#autocomplete_path'  => 'messages/filter/tag-autocomplete',
535
    );
536
    $form['updated']['actions']['tag-add-submit'] = array(
537
      '#type'       => 'submit',
538
      '#value'      => t('Apply Tag'),
539
      '#submit'     => array('privatemsg_filter_add_tag_submit'),
540
      '#ajax' => array(
541
        'callback' => 'privatemsg_list_js',
542
        'wrapper' => 'privatemsg-list-form',
543
        'effect' => 'fade',
544
      ),
545
    );
546
    $tags = privatemsg_filter_get_tags_data($user);
547
    if (!empty($tags)) {
548
      $options[0] = t('Remove Tag...');
549
      foreach ($tags as $tag_id => $tag) {
550
        $options[$tag_id] = $tag;
551
      }
552
      $form['updated']['actions']['tag-remove'] = array(
553
        '#type'          => 'select',
554
        '#options'       => $options,
555
        '#default_value' => 0,
556
        '#ajax' => array(
557
          'callback' => 'privatemsg_list_js',
558
          'wrapper' => 'privatemsg-list-form',
559
          'effect' => 'fade',
560
        ),
561
        '#submit'     => array('privatemsg_filter_remove_tag_submit'),
562
        '#executes_submit_callback' => TRUE,
563
      );
564
      $form['updated']['actions']['tag-remove-submit'] = array(
565
        '#type'       => 'submit',
566
        '#value'      => t('Remove Tag'),
567
        '#submit'     => array('privatemsg_filter_remove_tag_submit'),
568
        '#attributes' => array('class' => array('form-item')),
569
        '#states' => array(
570
          'visible' => array(
571
            // This is never true, button is always hidden when JS is enabled.
572
            ':input[name=operation]' => array('value' => 'fake'),
573
          ),
574
        ),
575
      );
576
    }
577
  }
578
}
579

    
580
/**
581
 * Form callback for removing a tag to threads.
582
 */
583
function privatemsg_filter_privatemsg_thread_operations($type) {
584
  if ($type == 'inbox') {
585
    $archive = array(
586
      'label' => t('Archive'),
587
      'callback' => 'privatemsg_filter_remove_tags',
588
      'callback arguments' => array('tag_id' => variable_get('privatemsg_filter_inbox_tag', '')),
589
      'success message' => t('The messages have been archived.'),
590
      'undo callback' => 'privatemsg_filter_add_tags',
591
      'undo callback arguments' => array('tag_id' => variable_get('privatemsg_filter_inbox_tag', '')),
592
    );
593
    return array('archive' => $archive);
594
  }
595
}
596

    
597
/**
598
 * Define the header for the tags column.
599
 *
600
 * @see theme_privatemsg_list_header()
601
 */
602
function theme_privatemsg_list_header__tags() {
603
  if (privatemsg_user_access('tag private messages')) {
604
    return array(
605
      'data' => t('Tags'),
606
      'class' => 'privatemsg-header-tags',
607
      '#weight' => -42,
608
    );
609
  }
610
}
611

    
612

    
613
/**
614
 * Default theme pattern function to display tags.
615
 *
616
 * @see theme_privatemsg_list_field()
617
 */
618
function theme_privatemsg_list_field__tags($arguments) {
619
  $thread = $arguments['thread'];
620
  if (!empty($thread['tags'])) {
621
    $tags = array();
622

    
623
    foreach ($thread['tags'] as $tag) {
624
      $tags[] = l(drupal_strlen($tag) > 15 ? drupal_substr($tag, 0, 13) . '...' : $tag, 'messages', array(
625
        'attributes' => array('title' => $tag),
626
        'query' => array('tags' => $tag)
627
      ));
628
    }
629
    return array(
630
      'data'  => implode(', ', $tags),
631
      'class' => array('privatemsg-list-tags'),
632
    );
633
  }
634
  // Return an empty row.
635
  return array('data' => '');
636
}
637

    
638
/**
639
 * Form callback for adding a tag to threads.
640
 */
641
function privatemsg_filter_add_tag_submit($form, &$form_state) {
642

    
643
  // Check if textfield is not empty.
644
  if (empty($form_state['values']['tag-add'])) {
645
    return;
646
  }
647

    
648
  $tags = explode(',', $form_state['values']['tag-add']);
649
  $tag_ids = privatemsg_filter_create_tags($tags);
650
  if (empty($tag_ids)) {
651
    return;
652
  }
653

    
654
  $operation = array(
655
    'callback' => 'privatemsg_filter_add_tags',
656
    'callback arguments' => array('tag_id' => $tag_ids),
657
    'success message' => t('The selected conversations have been tagged.'),
658
    'undo callback' => 'privatemsg_filter_remove_tags',
659
    'undo callback arguments' => array('tag_id' => $tag_ids),
660
  );
661
  privatemsg_operation_execute($operation, $form_state['values']['list']);
662
  $form_state['rebuild'] = TRUE;
663
  $form_state['input'] = array();
664
}
665

    
666
/**
667
 * Form callback for removing a tag to threads.
668
 */
669
function privatemsg_filter_remove_tag_submit($form, &$form_state) {
670
  $operation = array(
671
    'callback' => 'privatemsg_filter_remove_tags',
672
    'callback arguments' => array('tag_id' => $form_state['values']['tag-remove']),
673
    'success message' => t('The tag has been removed from the selected conversations.'),
674
    'undo callback' => 'privatemsg_filter_add_tags',
675
    'undo callback arguments' => array('tag_id' => $form_state['values']['tag-remove']),
676
  );
677
  privatemsg_operation_execute($operation, $form_state['values']['list']);
678
  $form_state['rebuild'] = TRUE;
679
  $form_state['input'] = array();
680
}
681

    
682
/**
683
 * Hook into the query builder to add the tagging info to the correct query
684
 */
685
function privatemsg_filter_query_privatemsg_list_alter($query) {
686

    
687
  $account = $query->getMetaData('arg_1');
688
  $argument = $query->getMetaData('arg_2');
689

    
690
  // Add all conditions to the count query too.
691
  $count_query = $query->getCountQuery();
692

    
693
  // Check if its a filtered view.
694
  if ($argument == 'sent') {
695
    $query->condition('pm.author', $account->uid);
696
    $count_query->condition('pm.author', $account->uid);
697
  }
698
  $filter = privatemsg_filter_get_filter($account);
699
  if ($argument == 'inbox') {
700
    $filter['tags'][] = variable_get('privatemsg_filter_inbox_tag', '');
701
  }
702

    
703
  // Filter the message listing by any set tags.
704
  if ($filter) {
705
    if (!empty($filter['tags'])) {
706
      foreach ($filter['tags'] as $tag) {
707
        $alias = $query->join('pm_tags_index', 'pmti', "%alias.thread_id = pmi.thread_id AND %alias.uid = pmi.recipient AND pmi.type IN ('user', 'hidden')");
708
        $query->condition($alias . '.tag_id', $tag);
709
        $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')");
710
        $count_query->condition($alias . '.tag_id', $tag);
711
      }
712
    }
713

    
714
    if (isset($filter['author']) && !empty($filter['author'])) {
715
      foreach ($filter['author'] as $author) {
716
        $alias = $query->join('pm_index', 'pmi', '%alias.mid = pm.mid');
717
        $query->condition($alias . '.recipient', $author->uid);
718
        $query->condition($alias . '.type', 'user');
719
        $alias = $count_query->join('pm_index', 'pmi', '%alias.mid = pm.mid');
720
        $count_query->condition($alias . '.recipient', $author->uid);
721
        $count_query->condition($alias . '.type', 'user');
722
      }
723
    }
724

    
725
    if (!empty($filter['search'])) {
726
      if (variable_get('privatemsg_filter_searchbody', FALSE)) {
727
        $search = db_or()
728
          ->condition('pm.subject', '%' . $filter['search'] . '%', 'LIKE')
729
          ->condition('pm.body', '%' . $filter['search'] . '%', 'LIKE');
730
        // Clone the condition so that they are both compiled.
731
        $query->condition(clone $search);
732
        $count_query->condition($search);
733
      }
734
      else {
735
        $query->condition('pm.subject', '%'. $filter['search'] .'%', 'LIKE');
736
        $count_query->condition('pm.subject', '%'. $filter['search'] .'%', 'LIKE');
737
      }
738
    }
739
  }
740
}
741

    
742
/**
743
 * Implements hook_privatemsg_view_alter().
744
 */
745
function privatemsg_filter_privatemsg_view_alter(&$content) {
746
  if (privatemsg_user_access('tag private messages')) {
747
    $content['tags'] = privatemsg_filter_show_tags($content['#thread']['thread_id'], !empty($_GET['show_tags_form']));
748
  }
749
}
750

    
751
function privatemsg_filter_show_tags($thread_id, $show_form) {
752
  global $user;
753

    
754
  drupal_add_css(drupal_get_path('module', 'privatemsg_filter') . '/privatemsg_filter.css');
755
  $element = array(
756
    '#prefix' => '<div id="privatemsg-filter-tags">',
757
    '#suffix' => '</div>',
758
    '#weight' => -10,
759
  );
760

    
761
  if (!$show_form) {
762
    $query = _privatemsg_assemble_query(array('tags', 'privatemsg_filter'), $user, array($thread_id));
763
    if ($query->countQuery()->execute()->fetchField() == 0) {
764
      $element['link'] = array(
765
        '#type' => 'link',
766
        '#href' => $_GET['q'],
767
        '#options' => array(
768
          'query' => array('show_tags_form' => TRUE),
769
          'attributes' => array('class' => array('privatemsg-filter-tags-add')),
770
        ),
771
        '#title' => t('Tag this conversation'),
772
      );
773
    }
774
    else {
775
      $element['label'] = array(
776
        '#prefix' => '<span class="privatemsg-filter-tags-label">',
777
        '#suffix' => '</span>',
778
        '#markup' => t('Tags:'),
779
      );
780

    
781
      foreach ($query->execute()->fetchCol(1) as $tag) {
782
        $element['tags'][] = array(
783
          '#type' => 'link',
784
          '#title' => $tag,
785
          '#href' => 'messages',
786
          '#options' => array(
787
            'attributes' => array('title' => $tag),
788
            'query' => array('tags' => $tag),
789
          ),
790
        );
791
      }
792

    
793
      $element['link'] = array(
794
        '#type' => 'link',
795
        '#href' => $_GET['q'],
796
        '#options' => array(
797
          'query' => array('show_tags_form' => TRUE),
798
          'attributes' => array('class' => array('privatemsg-filter-tags-modify')),
799
        ),
800
        '#title' => t('(modify tags)'),
801
      );
802
    }
803
    return $element;
804
  }
805
  else {
806
    return drupal_get_form('privatemsg_filter_form', $thread_id) + $element;
807
  }
808
}
809

    
810
/**
811
 * Form to show and allow modification of tagging information for a conversation.
812
 */
813
function privatemsg_filter_form($form, &$form_state, $thread_id) {
814
  global $user;
815

    
816
  // Get a list of current tags for this thread
817
  $query = _privatemsg_assemble_query(array('tags', 'privatemsg_filter'), $user, array($thread_id));
818
  $tags = implode(', ', $query->execute()->fetchCol(1));
819

    
820
  $form['user_id'] = array(
821
    '#type' => 'value',
822
    '#value' => $user->uid,
823
  );
824
  $form['thread_id'] = array(
825
    '#type' => 'value',
826
    '#value' => $thread_id,
827
  );
828

    
829
  $form['tags'] = array(
830
    '#type'               => 'textfield',
831
    '#title'              => t('Tags for this conversation'),
832
    '#title_display'      => 'invisible',
833
    '#size'               => 30,
834
    '#default_value'      => $tags,
835
    '#autocomplete_path'  => 'messages/filter/tag-autocomplete',
836
  );
837

    
838
  $form['modify_tags'] = array(
839
    '#type'     => 'submit',
840
    '#value'    => t('Tag this conversation'),
841
  );
842

    
843
  $form['cancel'] = array(
844
    '#type' => 'link',
845
    '#href' => $_GET['q'],
846
    '#title' => t('Cancel'),
847
    '#attributes' => array('id' => 'privatemsg-filter-tags-cancel'),
848
    '#weight' => 50,
849
  );
850

    
851
  return $form;
852
}
853

    
854
/**
855
 * Form builder function, display a form to modify tags on a thread.
856
 */
857
function privatemsg_filter_form_submit($form, &$form_state) {
858
  $tags = explode(',', $form_state['values']['tags']);
859

    
860
  // Step 1 - Delete all tag mapping.
861
  privatemsg_filter_remove_tags($form_state['values']['thread_id']);
862

    
863
  // Step 2 - Get the id for each of the tags.
864
  $tag_ids = privatemsg_filter_create_tags($tags);
865

    
866
  // Step 3 - Save all the tagging data.
867
  foreach ($tag_ids as $tag_id) {
868
    privatemsg_filter_add_tags($form_state['values']['thread_id'], $tag_id);
869
  }
870
  $form_state['redirect'] = current_path();
871
  drupal_set_message(t('Your conversation tags have been saved.'));
872
}
873

    
874
/**
875
 * Limit the user autocomplete for the filter widget.
876
 */
877
function privatemsg_filter_query_privatemsg_autocomplete_alter($query) {
878
  global $user;
879
  if (arg(1) == 'filter') {
880
    $query->join('pm_index', 'pip', "pip.recipient = u.uid AND pip.type = 'user'");
881
    $query->join('pm_index', 'piu', "piu.recipient = :uid_index AND piu.type = 'user' AND pip.mid = piu.mid", array(':uid_index' => $user->uid));
882
  }
883
}
884

    
885
/**
886
 * Query definition to get the tags in use by the specified user.
887
 *
888
 * @param $user
889
 *   User object for whom we want the tags.
890
 * @param $threads
891
 *   Array of thread ids, defaults to all threads of a user.
892
 * @param $limit
893
 *   Limit the number of tags *per thread*.
894
 */
895
function privatemsg_filter_sql_tags($user = NULL, $threads = NULL, $limit = NULL, $showHidden = FALSE) {
896
  $query = db_select('pm_tags', 't')
897
    ->fields('t', array('tag_id', 'tag', 'public'))
898
    ->orderBy('t.tag', 'ASC');
899
  if (!empty($threads)) {
900
    $query->addField('ti', 'thread_id');
901
    $query->join('pm_tags_index', 'ti', 'ti.tag_id = t.tag_id');
902
    $query->condition('ti.thread_id', $threads);
903
  }
904
  else {
905
    $query->addExpression('COUNT(ti.thread_id)', 'count');
906
    $query->leftJoin('pm_tags_index', 'ti', 'ti.tag_id = t.tag_id');
907
    $query
908
      ->groupBy('t.tag_id')
909
      ->groupBy('t.tag')
910
      ->groupBy('t.public');
911
  }
912
  if (!empty($user)) {
913
    $query->condition('ti.uid', $user->uid);
914
  }
915

    
916
  if (!$showHidden) {
917
    $query->condition(db_or()->condition('t.hidden', 0)->isNull('t.hidden'));
918
  }
919

    
920
  // Only select n tags per thread (ordered per tag_id), see
921
  // http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/.
922
  //
923
  // It does select how many tags for that thread/uid combination exist that
924
  // have a lower tag_id and does only select those that have less than $limit.
925
  //
926
  // This should only have a very minor performance impact as most users won't
927
  // tag a thread with 1000 different tags.
928
  if ($limit) {
929
    $query->where('(SELECT count(*) FROM {pm_tags_index} AS pmtic
930
                    WHERE pmtic.thread_id = ti.thread_id
931
                    AND pmtic.uid = ti.uid
932
                    AND pmtic.tag_id < ti.tag_id) < :limit', array(':limit' => $limit));
933
  }
934
  elseif (!empty($thread_id) || !empty($user)) {
935
    $query->orderBy('t.tag', 'ASC');
936
  }
937
  return $query;
938
}
939

    
940
/**
941
 * Query definition to get autocomplete suggestions for tags
942
 *
943
 * @param $search
944
 *  String fragment to use for tag suggestions.
945
 * @param $tags
946
 *  Array of tags not to be used as suggestions.
947
 */
948
function privatemsg_filter_sql_tags_autocomplete($search, $tags) {
949
  $query = db_select('pm_tags', 'pmt')
950
    ->fields('pmt', array('tag'))
951
    ->condition('pmt.tag', $search . '%%', 'LIKE')
952
    ->orderBy('pmt.tag', 'ASC')
953
    ->range(0, 10);
954

    
955
  if (!empty($tags)) {
956
    $query->condition('pmt.tag', $tags, 'NOT IN');
957
  }
958
  return $query;
959
}
960

    
961
/**
962
 * Implements hook_user_cancel().
963
 */
964
function privatemsg_filter_user_cancel($edit, $account, $method) {
965
  // Always delete since this is only visible for the user anyway.
966
  db_delete('pm_tags_index')
967
    ->condition('uid', $account->uid)
968
    ->execute();
969
}
970

    
971
/**
972
 * Implements hook_privatemsg_message_insert().
973
 */
974
function privatemsg_filter_privatemsg_message_insert($message) {
975
  foreach ($message->recipients as $recipient) {
976
    if ($recipient->type == 'user' || $recipient->type == 'hidden') {
977
      privatemsg_filter_add_tags(array($message->thread_id), variable_get('privatemsg_filter_inbox_tag', ''), $recipient);
978
    }
979
  }
980
}
981
/**
982
 * Implements hook_privatemsg_message_recipient_changed().
983
 */
984
function privatemsg_filter_privatemsg_message_recipient_changed($mid, $thread_id, $recipient, $type, $added) {
985
  if ($added && ($type == 'user' || $type == 'hidden')) {
986
    privatemsg_filter_add_tags(array($thread_id), variable_get('privatemsg_filter_inbox_tag', ''), (object)array('uid' => $recipient));
987
  }
988
}