privatemsg_filter.module in Privatemsg 6
Same filename and directory in other branches
Allows users to tag private messages and to filter based upon those tags.
File
privatemsg_filter/privatemsg_filter.moduleView source
<?php
/**
* @file
* Allows users to tag private messages and to filter based upon those tags.
*/
/**
* Implements hook_perm().
*/
function privatemsg_filter_perm() {
return array(
'filter private messages',
'tag private messages',
'create private message tags',
);
}
/**
* Implements hook_menu().
*/
function privatemsg_filter_menu() {
$items['admin/settings/messages/filter'] = array(
'title' => 'Filter',
'description' => 'Configure filter settings.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'privatemsg_filter_admin',
),
'access arguments' => array(
'administer privatemsg settings',
),
'type' => MENU_LOCAL_TASK,
);
$items['admin/settings/messages/tags'] = array(
'title' => 'Tags',
'description' => 'Configure tags.',
'page callback' => 'privatemsg_tags_admin',
'access arguments' => array(
'administer privatemsg settings',
),
'type' => MENU_LOCAL_TASK,
'file' => 'privatemsg_filter.admin.inc',
);
$items['admin/settings/messages/tags/list'] = array(
'title' => 'List',
'description' => 'Configure tags.',
'page callback' => 'privatemsg_tags_admin',
'access arguments' => array(
'administer privatemsg settings',
),
'type' => MENU_DEFAULT_LOCAL_TASK,
'file' => 'privatemsg_filter.admin.inc',
'weight' => -10,
);
$items['admin/settings/messages/tags/add'] = array(
'title' => 'Add',
'description' => 'Configure tags.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'privatemsg_tags_form',
),
'access arguments' => array(
'administer privatemsg settings',
),
'type' => MENU_LOCAL_TASK,
'file' => 'privatemsg_filter.admin.inc',
);
$items['admin/settings/messages/tags/edit/%'] = array(
'title' => 'Add',
'description' => 'Configure tags.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'privatemsg_tags_form',
5,
),
'access arguments' => array(
'administer privatemsg settings',
),
'type' => MENU_CALLBACK,
'file' => 'privatemsg_filter.admin.inc',
);
$items['admin/settings/messages/tags/delete/%'] = array(
'title' => 'Add',
'description' => 'Configure tags.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'privatemsg_filter_tags_delete',
5,
),
'access arguments' => array(
'administer privatemsg settings',
),
'type' => MENU_CALLBACK,
'file' => 'privatemsg_filter.admin.inc',
);
$items['messages/inbox'] = array(
'title' => 'Inbox',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'privatemsg_list',
'inbox',
),
'access callback' => 'privatemsg_user_access',
'type' => variable_get('privatemsg_filter_default_list', 0) ? MENU_LOCAL_TASK : MENU_DEFAULT_LOCAL_TASK,
'weight' => -15,
);
$items['messages/sent'] = array(
'title' => 'Sent messages',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'privatemsg_list',
'sent',
),
'access callback' => 'privatemsg_user_access',
'type' => MENU_LOCAL_TASK,
'weight' => -12,
);
$items['messages/filter/user-name-autocomplete'] = array(
'page callback' => 'privatemsg_user_name_autocomplete',
'access callback' => 'privatemsg_user_access',
'access arguments' => array(
'write privatemsg',
),
'type' => MENU_CALLBACK,
);
$items['messages/filter/tag-autocomplete'] = array(
'page callback' => 'privatemsg_filter_tags_autocomplete',
'access callback' => 'privatemsg_user_access',
'access arguments' => array(
'tag private messages',
),
'type' => MENU_CALLBACK,
'weight' => -10,
);
return $items;
}
/**
* Implements hook_menu_alter().
*/
function privatemsg_filter_menu_alter(&$items) {
// Rename messages to "All messages".
$items['messages/list']['title'] = 'All messages';
if (variable_get('privatemsg_filter_default_list', 0) == 0) {
// Change default argument of /messages to inbox. and set the task to MENU_LOCAL_TASK.
$items['messages']['page arguments'] = array(
'privatemsg_list',
'inbox',
);
$items['messages/list']['type'] = MENU_LOCAL_TASK;
}
}
function privatemsg_filter_admin() {
$form = array();
$form['privatemsg_filter_searchbody'] = array(
'#type' => 'checkbox',
'#title' => t('Search message body'),
'#description' => t('WARNING: turning on this feature will slow down search performance by a large factor. Gets worse as your messages database increases.'),
'#default_value' => variable_get('privatemsg_filter_searchbody', FALSE),
);
$form['privatemsg_filter_tagfield_weight'] = array(
'#type' => 'textfield',
'#title' => t('Position of the tagging textfield'),
'#description' => t('Use higher values to push the form lower down the page, lower or negative values to raise it higher.'),
'#size' => 4,
'#default_value' => variable_get('privatemsg_filter_tagfield_weight', 10),
);
return system_settings_form($form);
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Add a filter widget to the message listing pages.
*/
function privatemsg_filter_form_private_message_settings_alter(&$form, $form_state) {
$form['privatemsg_listing']['privatemsg_filter_default_list'] = array(
'#type' => 'radios',
'#default_value' => variable_get('privatemsg_filter_default_list', 0),
'#options' => array(
t('Inbox'),
t('All messages'),
),
'#title' => t('Choose the default list option'),
'#description' => t('Choose which of the two lists are shown by default when following the messages link.'),
);
// Add tags to the list of possible columns.
$form['privatemsg_listing']['privatemsg_display_fields']['#options']['tags'] = t('Tags');
$form['#submit'][] = 'privatemsg_filter_settings_submit';
}
/**
* Rebuilding the menu if necessary.
*/
function privatemsg_filter_settings_submit($form, &$form_state) {
if ($form['privatemsg_listing']['privatemsg_filter_default_list']['#default_value'] != $form_state['values']['privatemsg_filter_default_list']) {
menu_rebuild();
}
}
/**
* Function to create a tag
*
* @param $tags
* A single tag or an array of tags.
*/
function privatemsg_filter_create_tags($tags) {
if (!is_array($tags)) {
$tags = array(
$tags,
);
}
$tag_ids = array();
foreach ($tags as $tag) {
$tag = trim($tag);
if (empty($tag)) {
// Do not save a blank tag.
continue;
}
// Check if the tag already exists and only create the tag if it does not.
$tag_id = db_result(db_query("SELECT tag_id FROM {pm_tags} WHERE tag = '%s'", $tag));
if (empty($tag_id) && privatemsg_user_access('create private message tags')) {
db_query("INSERT INTO {pm_tags} (tag) VALUES ('%s')", $tag);
$tag_id = db_last_insert_id('pm_tags', 'tag_id');
}
elseif (empty($tag_id)) {
// The user does not have permission to create new tags - disregard this tag and move onto the next.
drupal_set_message(t('Tag %tag was ignored because you do not have permission to create new tags.', array(
'%tag' => $tag,
)));
continue;
}
$tag_ids[] = $tag_id;
}
return $tag_ids;
}
/**
* Tag one or multiple threads with a tag.
*
* @param $threads
* A single thread id or an array of thread ids.
* @param $tag_id
* Id of the tag.
*/
function privatemsg_filter_add_tags($threads, $tag_id, $account = NULL) {
if (!is_array($threads)) {
$threads = array(
$threads,
);
}
if (empty($account)) {
global $user;
$account = drupal_clone($user);
}
foreach ($threads as $thread) {
// Make sure that we don't add a tag to a thread twice,
// only insert if there is no such tag yet.
if (db_result(db_query('SELECT COUNT(*) FROM {pm_tags_index} WHERE tag_id = %d AND (uid = %d AND thread_id = %d)', $tag_id, $account->uid, $thread)) == 0) {
db_query('INSERT INTO {pm_tags_index} (tag_id, uid, thread_id) VALUES (%d, %d, %d)', $tag_id, $account->uid, $thread);
}
}
}
/**
* Remove tag from one or multiple threads.
*
* @param $threads
* A single thread id or an array of thread ids.
* @param $tag_id
* Id of the tag - set to NULL to remove all tags.
*/
function privatemsg_filter_remove_tags($threads, $tag_id = NULL, $account = NULL) {
if (!is_array($threads)) {
$threads = array(
$threads,
);
}
if (empty($account)) {
global $user;
$account = drupal_clone($user);
}
if (is_null($tag_id)) {
// Delete all tag mapping.
foreach ($threads as $thread) {
db_query('DELETE FROM {pm_tags_index} WHERE uid = %d AND thread_id = %d', $account->uid, $thread);
}
}
else {
// Delete tag mapping for the specified tag.
foreach ($threads as $thread) {
db_query('DELETE FROM {pm_tags_index} WHERE uid = %d AND thread_id = %d AND tag_id = %d', $account->uid, $thread, $tag_id);
}
}
}
function privatemsg_filter_get_filter($account) {
$filter = array();
if (isset($_GET['tags'])) {
$_GET['tags'] = urldecode($_GET['tags']);
$tag_data = privatemsg_filter_get_tags_data($account);
foreach (explode(',', $_GET['tags']) as $tag) {
if (isset($tag_data[$tag])) {
$filter['tags'][$tag] = $tag;
}
elseif (in_array($tag, $tag_data)) {
$filter['tags'][array_search($tag, $tag_data)] = array_search($tag, $tag_data);
}
}
}
if (isset($_GET['author'])) {
list($filter['author']) = _privatemsg_parse_userstring($_GET['author']);
}
if (isset($_GET['search'])) {
$filter['search'] = $_GET['search'];
}
if (!empty($filter)) {
return $filter;
}
if (!empty($_SESSION['privatemsg_filter'])) {
return $_SESSION['privatemsg_filter'];
}
}
function privatemsg_filter_get_tags_data($account) {
static $tag_data;
if (is_array($tag_data)) {
return $tag_data;
}
// Only show the tags that a user have used.
$query = _privatemsg_assemble_query(array(
'tags',
'privatemsg_filter',
), $account);
$results = db_query($query['query']);
$tag_data = array();
while ($result = db_fetch_object($results)) {
$tag_data[$result->tag_id] = $result->tag;
}
return $tag_data;
}
function privatemsg_filter_dropdown(&$form_state, $account) {
drupal_add_css(drupal_get_path('module', 'privatemsg_filter') . '/privatemsg_filter.css');
$form['filter'] = array(
'#type' => 'fieldset',
'#title' => t('Filter messages'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['filter']['search'] = array(
'#type' => 'textfield',
'#title' => variable_get('privatemsg_filter_searchbody', FALSE) ? t('Search messages') : t('Search subjects'),
'#weight' => -20,
);
$form['filter']['author'] = array(
'#type' => 'textfield',
'#title' => t('Participants'),
'#description' => t('Separate multiple names with commas.'),
'#weight' => -5,
'#size' => 50,
'#autocomplete_path' => 'messages/filter/user-name-autocomplete',
);
// Only show form if the user has some messages tagged.
if (count($tag_data = privatemsg_filter_get_tags_data($account))) {
$form['filter']['tags'] = array(
'#type' => 'select',
'#title' => t('Tags'),
'#options' => $tag_data,
'#multiple' => TRUE,
'#size' => 5,
'#weight' => 0,
);
}
$form['filter']['submit'] = array(
'#type' => 'submit',
'#value' => t('Filter'),
'#prefix' => '<div id="privatemsg-filter-buttons">',
'#weight' => 10,
'#submit' => array(
'privatemsg_filter_dropdown_submit',
),
);
$form['filter']['save'] = array(
'#type' => 'submit',
'#value' => t('Save filter'),
'#suffix' => '</div>',
'#weight' => 11,
'#submit' => array(
'privatemsg_filter_dropdown_submit',
),
);
if ($filter = privatemsg_filter_get_filter($account)) {
privatemsg_filter_dropdown_set_active($form, $filter);
}
return $form;
}
function privatemsg_filter_dropdown_set_active(&$form, $filter) {
$form['filter']['#title'] = t('Filter messages (active)');
$form['filter']['#collapsed'] = FALSE;
if (isset($filter['author'])) {
$string = '';
foreach ($filter['author'] as $author) {
$string .= $author->name . ', ';
}
$form['filter']['author']['#default_value'] = $string;
}
if (isset($filter['tags'])) {
$form['filter']['tags']['#default_value'] = $filter['tags'];
}
if (isset($filter['search'])) {
$form['filter']['search']['#default_value'] = $filter['search'];
}
$form['filter']['reset'] = array(
'#type' => 'submit',
'#value' => t('Reset'),
'#suffix' => '</div>',
'#weight' => 12,
'#submit' => array(
'privatemsg_filter_dropdown_submit',
),
);
unset($form['filter']['save']['#suffix']);
}
function privatemsg_filter_dropdown_submit($form, &$form_state) {
if (!empty($form_state['values']['author'])) {
list($form_state['values']['author']) = _privatemsg_parse_userstring($form_state['values']['author']);
}
switch ($form_state['values']['op']) {
case t('Save filter'):
$filter = array();
if (!empty($form_state['values']['tags'])) {
$filter['tags'] = $form_state['values']['tags'];
}
if (!empty($form_state['values']['author'])) {
$filter['author'] = $form_state['values']['author'];
}
if (!empty($form_state['values']['search'])) {
$filter['search'] = $form_state['values']['search'];
}
$_SESSION['privatemsg_filter'] = $filter;
break;
case t('Filter'):
drupal_goto($_GET['q'], privatemsg_filter_create_get_query($form_state['values']));
return;
break;
case t('Reset'):
$_SESSION['privatemsg_filter'] = array();
break;
}
$form_state['redirect'] = $_GET['q'];
}
function privatemsg_filter_create_get_query($filter) {
$query = array();
if (isset($filter['tags']) && !empty($filter['tags'])) {
$ids = array();
foreach ($filter['tags'] as $tag) {
if ((int) $tag > 0) {
$ids[] = $tag;
}
else {
$query['tags'][] = $tag;
}
}
$sql = 'SELECT pmt.tag FROM {pm_tags} pmt WHERE pmt.tag_id IN (' . implode(', ', $filter['tags']) . ')';
$result = db_query($sql);
while ($row = db_fetch_object($result)) {
$query['tags'][] = $row->tag;
}
if (isset($query['tags'])) {
$query['tags'] = implode(',', $query['tags']);
}
}
if (isset($filter['author']) && !empty($filter['author'])) {
foreach ($filter['author'] as $author) {
if (is_object($author) && isset($author->uid) && isset($author->name)) {
$query['author'][] = $author->name;
}
elseif ($author_obj = user_load($author)) {
$query['author'][] = $author_obj->name;
}
}
if (isset($query['author'])) {
$query['author'] = implode(',', $query['author']);
}
}
if (isset($filter['search']) && !empty($filter['search'])) {
$query['search'] = $filter['search'];
}
return $query;
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Adds a filter widget to the message listing pages.
*/
function privatemsg_filter_form_privatemsg_list_alter(&$form, $form_state) {
global $user;
if (privatemsg_user_access('filter private messages') && !empty($form['#data'])) {
$form += privatemsg_filter_dropdown($form_state, $form['#account']);
}
$fields = array_filter(variable_get('privatemsg_display_fields', array(
'participants',
)));
if (in_array('tags', $fields)) {
// Load thread id's of the current list.
$threads = array_keys($form['#data']);
// Fetch all tags of those threads.
$query = _privatemsg_assemble_query(array(
'tags',
'privatemsg_filter',
), $user, $threads, 3);
// Add them to #data
$result = db_query($query['query']);
while ($tag = db_fetch_array($result)) {
$form['#data'][$tag['thread_id']]['tags'][$tag['tag_id']] = $tag['tag'];
}
}
$tags = privatemsg_filter_get_tags_data($user);
if (privatemsg_user_access('tag private messages') && !empty($tags) && !empty($form['#data'])) {
$options = array();
$options[] = t('Apply tag...');
foreach ($tags as $tag_id => $tag) {
$options[$tag_id] = $tag;
}
$form['actions']['tag-add'] = array(
'#type' => 'select',
'#options' => $options,
'#default_value' => 0,
);
$form['actions']['tag-add-submit'] = array(
'#prefix' => '<div class="privatemsg-tag-add-submit">',
'#suffix' => '</div>',
'#type' => 'submit',
'#value' => t('Apply Tag'),
'#submit' => array(
'privatemsg_filter_add_tag_submit',
),
'#attributes' => array(
'class' => 'privatemsg-action-button',
),
);
$options[0] = t('Remove Tag...');
$form['actions']['tag-remove'] = array(
'#type' => 'select',
'#options' => $options,
'#default_value' => 0,
);
$form['actions']['tag-remove-submit'] = array(
'#prefix' => '<div class="privatemsg-tag-remove-submit">',
'#suffix' => '</div>',
'#type' => 'submit',
'#value' => t('Remove Tag'),
'#submit' => array(
'privatemsg_filter_remove_tag_submit',
),
'#attributes' => array(
'class' => 'privatemsg-action-button',
),
);
// JS for hiding the submit buttons.
drupal_add_js(drupal_get_path('module', 'privatemsg_filter') . '/privatemsg-filter-list.js');
}
}
/**
* Define the header for the tags column.
*
* @see theme_privatemsg_list_header()
*/
function phptemplate_privatemsg_list_header__tags() {
return array(
'data' => t('Tags'),
'key' => 'tags',
'class' => 'privatemsg-header-tags',
'#weight' => -42,
);
}
/**
* Default theme pattern function to display tags.
*
* @see theme_privatemsg_list_field()
*/
function phptemplate_privatemsg_list_field__tags($thread) {
if (!empty($thread['tags'])) {
$tags = array();
foreach ($thread['tags'] as $tag_id => $tag) {
$tags[] = l(strlen($tag) > 15 ? substr($tag, 0, 13) . '...' : $tag, 'messages', array(
'attributes' => array(
'title' => $tag,
),
'query' => array(
'tags' => $tag,
),
));
}
return array(
'data' => implode(', ', $tags),
'class' => 'privatemsg-list-tags',
);
}
}
/**
* Form callback for adding a tag to threads.
*/
function privatemsg_filter_add_tag_submit($form, &$form_state) {
$operation = array(
'callback' => 'privatemsg_filter_add_tags',
'callback arguments' => array(
'tag_id' => $form_state['values']['tag-add'],
),
'undo callback' => 'privatemsg_filter_remove_tags',
'undo callback arguments' => array(
'tag_id' => $form_state['values']['tag-add'],
),
);
drupal_set_message(t('The selected conversations have been tagged.'));
privatemsg_operation_execute($operation, $form_state['values']['threads']);
}
/**
* Form callback for removing a tag to threads.
*/
function privatemsg_filter_remove_tag_submit($form, &$form_state) {
$operation = array(
'callback' => 'privatemsg_filter_remove_tags',
'callback arguments' => array(
'tag_id' => $form_state['values']['tag-remove'],
),
'undo callback' => 'privatemsg_filter_add_tags',
'undo callback arguments' => array(
'tag_id' => $form_state['values']['tag-remove'],
),
);
drupal_set_message(t('The tag has been removed from the selected conversations.'));
privatemsg_operation_execute($operation, $form_state['values']['threads']);
}
/**
* Hook into the query builder to add the tagging info to the correct query
*/
function privatemsg_filter_privatemsg_sql_list_alter(&$fragments, $account, $argument) {
// Check if its a filtered view.
if ($argument == 'sent') {
$fragments['where'][] = "pm.author = %d";
$fragments['query_args']['where'][] = $account->uid;
}
if ($argument == 'inbox') {
$fragments['having'][] = '((SELECT pmf.author FROM {pm_message} pmf WHERE pmf.mid = pmi.thread_id) = %d AND COUNT(pmi.thread_id) > 1) OR (SELECT COUNT(*) FROM {pm_message} pmf INNER JOIN {pm_index} pmif ON (pmf.mid = pmif.mid) WHERE pmif.thread_id = pmi.thread_id AND pmf.author <> %d) > 0';
$fragments['query_args']['having'][] = $account->uid;
$fragments['query_args']['having'][] = $account->uid;
}
// Filter the message listing by any set tags.
if ($filter = privatemsg_filter_get_filter($account)) {
$count = 0;
if (isset($filter['tags']) && !empty($filter['tags'])) {
foreach ($filter['tags'] as $tag) {
$fragments['inner_join'][] = "INNER JOIN {pm_tags_index} pmti{$count} ON (pmti{$count}.thread_id = pmi.thread_id AND pmti{$count}.uid = pmi.uid)";
$fragments['where'][] = "pmti{$count}.tag_id = %d";
$fragments['query_args']['where'][] = $tag;
$count++;
}
}
if (isset($filter['author']) && !empty($filter['author'])) {
foreach ($filter['author'] as $author) {
$fragments['inner_join'][] = "INNER JOIN {pm_index} pmi{$count} ON (pmi{$count}.mid = pm.mid)";
$fragments['where'][] = "pmi{$count}.uid = %d";
$fragments['query_args']['where'][] = $author->uid;
$count++;
}
}
if (isset($filter['search']) && !empty($filter['search'])) {
if (variable_get('privatemsg_filter_searchbody', FALSE)) {
$fragments['where'][] = "pm.subject LIKE '%s' OR pm.body LIKE '%s'";
$fragments['query_args']['where'][] = '%%' . $filter['search'] . '%%';
$fragments['query_args']['where'][] = '%%' . $filter['search'] . '%%';
}
else {
$fragments['where'][] = "pm.subject LIKE '%s'";
$fragments['query_args']['where'][] = '%%' . $filter['search'] . '%%';
}
}
}
}
/**
* Hook into the view messages page to add a form for tagging purposes.
*/
function privatemsg_filter_privatemsg_view_messages_alter(&$content, $thread) {
if (count($thread['messages']) > 0) {
$content['tags']['#value'] = drupal_get_form('privatemsg_filter_form');
$content['tags']['#weight'] = variable_get('privatemsg_filter_tagfield_weight', 10);
}
}
/**
* Form to show and allow modification of tagging information of a conversation.
*/
function privatemsg_filter_form(&$form_state) {
global $user;
$thread_id = arg(2);
// Get a list of current tags for this thread
$query = _privatemsg_assemble_query(array(
'tags',
'privatemsg_filter',
), $user, array(
$thread_id,
));
$results = db_query($query['query']);
$count = db_result(db_query($query['count']));
$tags = '';
while ($tag = db_fetch_array($results)) {
$tags .= $tag['tag'] . ', ';
}
$form['tags'] = array(
'#type' => 'fieldset',
'#title' => t('Tags'),
'#access' => privatemsg_user_access('tag private messages'),
'#collapsible' => TRUE,
'#collapsed' => empty($count) ? TRUE : FALSE,
);
$form['tags']['user_id'] = array(
'#type' => 'value',
'#value' => $user->uid,
);
$form['tags']['thread_id'] = array(
'#type' => 'value',
'#value' => $thread_id,
);
$form['tags']['tags'] = array(
'#type' => 'textfield',
'#title' => t('Tags for this conversation'),
'#description' => t('Separate multiple tags with commas.'),
'#size' => 50,
'#default_value' => $tags,
'#autocomplete_path' => 'messages/filter/tag-autocomplete',
);
$form['tags']['submit'] = array(
'#type' => 'submit',
'#value' => t('Tag this conversation'),
'#submit' => array(
'privatemsg_filter_form_submit',
),
);
return $form;
}
function privatemsg_filter_form_submit($form, &$form_state) {
if (isset($form_state['values']['submit'])) {
$tags = explode(',', $form_state['values']['tags']);
// Step 1 - Delete all tag mapping. I cannot think of a better way to remove tags that are no longer in the textfield, so ideas welcome.
privatemsg_filter_remove_tags($form_state['values']['thread_id']);
// Step 2 - Get the id for each of the tags.
$tag_ids = privatemsg_filter_create_tags($tags);
// Step 3 - Save all the tagging data.
foreach ($tag_ids as $tag_id) {
privatemsg_filter_add_tags($form_state['values']['thread_id'], $tag_id);
}
drupal_set_message(t('Tagging information has been saved.'));
}
}
/**
* Return autocomplete results for tags.
*
* Most of this code has been lifted/modified from
* privatemsg_user_name_autocomplete().
*/
function privatemsg_filter_tags_autocomplete($string) {
// 1: Parse $string and build a list of tags.
$tags = array();
$fragments = explode(',', $string);
foreach ($fragments as $index => $tag) {
$tag = trim($tag);
$tags[$tag] = $tag;
}
// 2: Find the next tag suggestion.
$fragment = array_pop($tags);
$matches = array();
if (!empty($fragment)) {
$query = _privatemsg_assemble_query(array(
'tags_autocomplete',
'privatemsg_filter',
), $fragment, $tags);
$result = db_query_range($query['query'], $fragment, 0, 10);
$prefix = count($tags) ? implode(", ", $tags) . ", " : '';
// 3: Build proper suggestions and print.
while ($tag = db_fetch_object($result)) {
$matches[$prefix . $tag->tag . ", "] = $tag->tag;
}
}
// convert to object to prevent drupal bug, see http://drupal.org/node/175361
drupal_json((object) $matches);
}
/**
* @addtogroup sql
* @{
*/
/**
* Limit the user autocomplete for the filter widget.
*
* @param $fragments
* Query fragments.
* @param $search
* Username search string.
* @param $names
* Array of names that are already part of the autocomplete field.
*/
function privatemsg_filter_privatemsg_sql_autocomplete_alter(&$fragments, $search, $names) {
global $user;
// arg(1) is an additional URL argument passed to the URL when only
// users that are listed as recipient for threads of that user should be
// displayed.
// @todo: Check if these results can be grouped to avoid unecessary loops.
if (arg(1) == 'filter') {
// JOIN on index entries where the to be selected user is a recipient.
$fragments['inner_join'][] = 'INNER JOIN {pm_index} pip ON pip.uid = u.uid';
// JOIN on rows where the current user is the recipient and that have the
// same mid as those above.
$fragments['inner_join'][] = 'INNER JOIN {pm_index} piu ON piu.uid = %d AND pip.mid = piu.mid';
$fragments['query_args']['join'][] = $user->uid;
}
}
/**
* Query definition to fetch tags.
*
* @param $fragments
* Query fragments array.
* @param $user
* User object for whom we want the tags.
* @param $threads
* Array of thread ids, defaults to all threads of a user.
* @param $limit
* Limit the number of tags *per thread*.
*/
function privatemsg_filter_sql_tags(&$fragments, $user = NULL, $threads = NULL, $limit = NULL) {
$fragments['primary_table'] = '{pm_tags} t';
$fragments['select'][] = 't.tag';
$fragments['select'][] = 't.tag_id';
$fragments['select'][] = 't.public';
if (!empty($threads)) {
// If the tag list needs to be for specific threads.
$fragments['select'][] = 'ti.thread_id';
$fragments['inner_join'][] = 'INNER JOIN {pm_tags_index} ti on ti.tag_id = t.tag_id';
$fragments['where'][] = 'ti.thread_id IN (' . db_placeholders($threads) . ')';
$fragments['query_args']['where'] += $threads;
}
else {
// Tag usage counter is only used when we select all tags.
$fragments['select'][] = 'COUNT(ti.thread_id) as count';
// LEFT JOIN so that unused tags are displayed too.
$fragments['inner_join'][] = 'LEFT JOIN {pm_tags_index} ti ON t.tag_id = ti.tag_id';
$fragments['group_by'][] = 't.tag_id';
$fragments['group_by'][] = 't.tag';
$fragments['group_by'][] = 't.public';
}
if (!empty($user)) {
$fragments['where'][] = 'ti.uid = %d';
$fragments['query_args']['where'][] = $user->uid;
}
// Only select n tags per thread (ordered per tag_id), see
// http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/.
//
// It does select how many tags for that thread/uid combination exist that
// have a lower tag_id and does only select those that have less than $limit.
//
// This should only have a very minor performance impact as most users won't
// tag a thread with 1000 different tags.
//
if ($limit) {
$fragments['where'][] = '(SELECT count(*) FROM {pm_tags_index} AS pmtic
WHERE pmtic.thread_id = ti.thread_id
AND pmtic.uid = ti.uid
AND pmtic.tag_id < ti.tag_id) < %d';
$fragments['query_args']['where'][] = $limit;
}
elseif (!empty($threads) || !empty($user)) {
// Only add a sort when we are not loading the tags for the admin page.
// Sorting is handled through tablesort_sql() then.
$fragments['order_by'][] = 't.tag ASC';
}
}
/**
* Query definition to get autocomplete suggestions for tags
*
* @param $fragments
* Query fragments array.
* @param $search
* String fragment to use for tag suggestions.
* @param $tags
* Array of tags not to be used as suggestions.
*/
function privatemsg_filter_sql_tags_autocomplete(&$fragments, $search, $tags) {
global $user;
$fragments['primary_table'] = '{pm_tags} pmt';
$fragments['select'][] = 'pmt.tag';
$fragments['where'][] = "pmt.tag LIKE '%s'";
// Escape % to get through the placeholder replacement.
$fragments['query_args']['where'][] = $search . '%%';
if (!empty($tags)) {
// Exclude tags.
$fragments['where'][] = "pmt.tag NOT IN (" . db_placeholders($tags, 'text') . ")";
$fragments['query_args']['where'] += $tags;
}
// LEFT JOIN to be able to load public, unused tags.
$fragments['inner_join'][] = 'LEFT JOIN {pm_tags_index} pmti ON pmt.tag_id = pmti.tag_id AND pmti.uid = %d';
$fragments['query_args']['join'][] = $user->uid;
// Autocomplete should only display Tags used by that user or public tags.
// This is done to avoid information disclosure as part of tag names.
$fragments['where'][] = '(pmti.uid IS NOT NULL OR pmt.public = 1)';
$fragments['order_by'][] = 'pmt.tag ASC';
}
/**
* @}
*/
/**
* Implement hook_user().
*/
function privatemsg_filter_user($op, &$edit, &$account, $category = NULL) {
switch ($op) {
case 'delete':
// Delete tag information of that user.
db_query("DELETE FROM {pm_tags_index} WHERE uid = %d", $account->uid, $account->uid);
break;
}
}