privatemsg_filter.module in Privatemsg 6.2
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() {
$url_prefix = variable_get('privatemsg_url_prefix', 'messages');
$user_arg_position = array_search('%user', explode('/', $url_prefix));
$items['admin/settings/messages/filter'] = array(
'title' => 'Filter',
'description' => 'Configure filter settings.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'privatemsg_filter_admin',
),
'file' => 'privatemsg_filter.admin.inc',
'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',
'file' => 'privatemsg_filter.admin.inc',
'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' => 'List all existing tags.',
'page callback' => 'privatemsg_tags_admin',
'file' => 'privatemsg_filter.admin.inc',
'access arguments' => array(
'administer privatemsg settings',
),
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['admin/settings/messages/tags/add'] = array(
'title' => 'Add',
'description' => 'Add a new tag.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'privatemsg_tags_form',
),
'file' => 'privatemsg_filter.admin.inc',
'access arguments' => array(
'administer privatemsg settings',
),
'type' => MENU_LOCAL_TASK,
);
$items['admin/settings/messages/tags/rebuild'] = array(
'title' => 'Rebuild inbox',
'description' => 'Tag all thread which should be displayed in the inbox.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'privatemsg_filter_inbox_rebuid_form',
),
'file' => 'privatemsg_filter.admin.inc',
'access arguments' => array(
'administer privatemsg settings',
),
'type' => MENU_LOCAL_TASK,
);
$items['admin/settings/messages/tags/edit/%'] = array(
'title' => 'Add',
'description' => 'Configure tags.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'privatemsg_tags_form',
5,
),
'file' => 'privatemsg_filter.admin.inc',
'access arguments' => array(
'administer privatemsg settings',
),
'type' => MENU_CALLBACK,
);
$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,
),
'file' => 'privatemsg_filter.admin.inc',
'access arguments' => array(
'administer privatemsg settings',
),
'type' => MENU_CALLBACK,
);
$items[$url_prefix . '/inbox'] = array(
'title' => 'Inbox',
'page callback' => 'privatemsg_list_page',
'page arguments' => array(
'inbox',
$user_arg_position,
),
'file' => 'privatemsg.pages.inc',
'file path' => drupal_get_path('module', 'privatemsg'),
'access callback' => 'privatemsg_menu_access',
'type' => variable_get('privatemsg_filter_default_list', 0) ? MENU_LOCAL_TASK : MENU_DEFAULT_LOCAL_TASK,
'weight' => -15,
);
$items[$url_prefix . '/sent'] = array(
'title' => 'Sent messages',
'page callback' => 'privatemsg_list_page',
'page arguments' => array(
'sent',
$user_arg_position,
),
'file' => 'privatemsg.pages.inc',
'file path' => drupal_get_path('module', 'privatemsg'),
'access callback' => 'privatemsg_menu_access',
'type' => MENU_LOCAL_TASK,
'weight' => -12,
);
$items['messages/filter/autocomplete'] = array(
'page callback' => 'privatemsg_autocomplete',
'file' => 'privatemsg.pages.inc',
'file path' => drupal_get_path('module', 'privatemsg'),
'access callback' => 'privatemsg_menu_access',
'access arguments' => array(
'write privatemsg',
),
'type' => MENU_CALLBACK,
);
$items['messages/filter/tag-autocomplete'] = array(
'page callback' => 'privatemsg_filter_tags_autocomplete',
'access callback' => 'privatemsg_menu_access',
'access arguments' => array(
'tag private messages',
),
'type' => MENU_CALLBACK,
'weight' => -10,
);
return $items;
}
/**
* Implements hook_menu_alter().
*/
function privatemsg_filter_menu_alter(&$items) {
$url_prefix = variable_get('privatemsg_url_prefix', 'messages');
$user_arg_position = array_search('%user', explode('/', $url_prefix));
// Rename messages to "All messages".
$items[$url_prefix . '/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[$url_prefix]['page arguments'] = array(
'inbox',
$user_arg_position,
);
$items[$url_prefix . '/list']['type'] = MENU_LOCAL_TASK;
}
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Add a filter widget to the message listing pages.
*/
function privatemsg_filter_form_privatemsg_admin_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.'),
);
$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_ids, $account = NULL) {
if (!is_array($threads)) {
$threads = array(
$threads,
);
}
if (!is_array($tag_ids)) {
$tag_ids = array(
$tag_ids,
);
}
if (empty($account)) {
global $user;
$account = drupal_clone($user);
}
foreach ($tag_ids as $tag_id) {
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 1 FROM {pm_tags_index} WHERE tag_id = %d AND (uid = %d AND thread_id = %d)', $tag_id, $account->uid, $thread))) {
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_ids = NULL, $account = NULL) {
if (!is_array($threads)) {
$threads = array(
$threads,
);
}
if (!is_null($tag_ids) && !is_array($tag_ids)) {
$tag_ids = array(
$tag_ids,
);
}
if (empty($account)) {
global $user;
$account = drupal_clone($user);
}
if (is_null($tag_ids)) {
// Delete all tag mapping - all except for the inbox tag if it exists.
$inbox_tag = variable_get('privatemsg_filter_inbox_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, $inbox_tag);
}
}
else {
// Delete tag mapping for the specified tags and threads.
$sql = 'DELETE FROM {pm_tags_index} WHERE uid = %d AND thread_id IN (' . db_placeholders($threads) . ') AND tag_id IN (' . db_placeholders($tag_ids) . ')';
db_query($sql, array_merge(array(
$account->uid,
), $threads, $tag_ids));
}
}
function privatemsg_filter_get_filter($account) {
$filter = array();
// Filtering by tags is either allowed if the user can use tags or he can
// filter.
if (privatemsg_user_access('filter private messages') || privatemsg_user_access('tag private messages')) {
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);
}
}
}
}
// Users can only use the text search or search by author if they have the
// necessary permission.
if (privatemsg_user_access('filter private messages')) {
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,
// The form is always called when search arguments are passed in, even if
// they don't have access to it. This is necessary to process the search
// query. But we don't want to show them the form.
'#access' => privatemsg_user_access('filter private messages'),
'#weight' => -20,
);
$form['filter']['search'] = array(
'#type' => 'textfield',
'#title' => variable_get('privatemsg_filter_searchbody', FALSE) ? t('By content') : t('By subject'),
'#weight' => -20,
'#size' => 25,
);
$form['filter']['author'] = array(
'#type' => 'textfield',
'#title' => t('By participant'),
'#weight' => -5,
'#size' => 25,
'#autocomplete_path' => 'messages/filter/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('By tags'),
'#options' => $tag_data,
'#multiple' => TRUE,
'#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)) {
// Display a message if the user will not see the filter form.
if (!empty($filter['tags']) && !empty($_GET['tags']) && !privatemsg_user_access('filter private messages')) {
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']),
)));
}
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 .= privatemsg_recipient_format($author, array(
'plain' => TRUE,
)) . ', ';
}
$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'];
}
/**
* Creates a GET query based on the selected filters.
*/
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'][] = privatemsg_recipient_format($author, array(
'plain' => TRUE,
));
}
elseif (is_int($author) && ($author_obj = _privatemsg_user_load($author))) {
$query['author'][] = privatemsg_recipient_format($author, array(
'plain' => TRUE,
));
}
}
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 (!empty($form['#data']) || privatemsg_filter_get_filter($user)) {
$form += privatemsg_filter_dropdown($form_state, $form['#account']);
}
$fields = privatemsg_get_enabled_headers();
if (privatemsg_user_access('tag private messages') && in_array('tags', $fields) && !empty($form['#data'])) {
// 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'];
}
}
if (privatemsg_user_access('tag private messages') && !empty($form['#data'])) {
$form['actions']['tag-add'] = array(
'#type' => 'textfield',
'#size' => 15,
'#autocomplete_path' => 'messages/filter/tag-autocomplete',
);
$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',
),
);
$tags = privatemsg_filter_get_tags_data($user);
if (!empty($tags)) {
$options[0] = t('Remove Tag...');
foreach ($tags as $tag_id => $tag) {
$options[$tag_id] = $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');
}
}
/**
* Implements hook_privatemsg_thread_operations().
*/
function privatemsg_filter_privatemsg_thread_operations($type) {
if ($type == 'inbox') {
$archive = array(
'label' => t('Archive'),
'callback' => 'privatemsg_filter_remove_tags',
'callback arguments' => array(
'tag_id' => variable_get('privatemsg_filter_inbox_tag', ''),
),
'success message' => t('The messages have been archived.'),
'undo callback' => 'privatemsg_filter_add_tags',
'undo callback arguments' => array(
'tag_id' => variable_get('privatemsg_filter_inbox_tag', ''),
),
);
return array(
'archive' => $archive,
);
}
}
/**
* Implements hook_privatemsg_header_info().
*/
function privatemsg_filter_privatemsg_header_info() {
return array(
'tags' => array(
'data' => t('Tags'),
'class' => 'privatemsg-header-tags',
'#weight' => -23,
'#access' => privatemsg_user_access('tag private messages'),
),
);
}
/**
* 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(drupal_strlen($tag) > 15 ? drupal_substr($tag, 0, 13) . '...' : $tag, 'messages', array(
'attributes' => array(
'title' => $tag,
),
'query' => array(
'tags' => $tag,
),
));
}
return array(
'data' => implode(', ', $tags),
'class' => 'privatemsg-list-tags',
);
}
}
/**
* @addtogroup sql
* @{
*/
/**
* 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;
}
$filter = privatemsg_filter_get_filter($account);
if ($argument == 'inbox') {
$filter['tags'][] = variable_get('privatemsg_filter_inbox_tag', '');
}
// Filter the message listing by any set tags.
if ($filter) {
$count = 0;
if (!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.recipient AND pmi.type IN ('user', 'hidden'))";
$fragments['where'][] = "pmti{$count}.tag_id = %d";
$fragments['query_args']['where'][] = $tag;
$count++;
}
}
if (!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}.recipient = %d AND pmi{$count}.type = 'user'";
$fragments['query_args']['where'][] = $author->uid;
$count++;
}
}
if (!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 && privatemsg_user_access('tag private messages')) {
$content['tags'] = privatemsg_filter_show_tags($thread['thread_id'], !empty($_GET['show_tags_form']));
}
}
function privatemsg_filter_show_tags($thread_id, $show_form) {
global $user;
drupal_add_css(drupal_get_path('module', 'privatemsg_filter') . '/privatemsg_filter.css');
$element = array(
'#prefix' => '<div id="privatemsg-filter-tags">',
'#suffix' => '</div>',
'#weight' => -10,
);
$query = _privatemsg_assemble_query(array(
'tags',
'privatemsg_filter',
), $user, array(
$thread_id,
));
if (!$show_form) {
if (db_result(db_query($query['count'])) == 0) {
$element['link'] = array(
'#value' => l(t('Tag this conversation'), $_GET['q'], array(
'query' => array(
'show_tags_form' => TRUE,
),
'attributes' => array(
'class' => array(
'privatemsg-filter-tags-add',
),
),
)),
);
}
else {
$element['label'] = array(
'#value' => '<span class="privatemsg-filter-tags-label">' . t('Tags:') . '</span>',
'#weight' => 0,
);
$result = db_query($query['query']);
$tags = array();
$element['tags']['#weight'] = 5;
while ($tag = db_fetch_object($result)) {
$element['tags'][]['#value'] = l($tag->tag, 'messages', array(
'attributes' => array(
'title' => $tag->tag,
),
'query' => array(
'tags' => $tag->tag,
),
));
}
$element['link'] = array(
'#value' => l(t('(modify tags)'), $_GET['q'], array(
'query' => array(
'show_tags_form' => TRUE,
),
'attributes' => array(
'class' => array(
'privatemsg-filter-tags-add',
),
),
)),
'#weight' => 10,
);
}
return $element;
}
else {
return array(
'#value' => drupal_get_form('privatemsg_filter_form', $thread_id),
);
}
}
/**
* Form to show and allow modification of tagging information of a conversation.
*/
function privatemsg_filter_form(&$form_state, $thread_id) {
global $user;
// 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 = array(
'#prefix' => '<div id="privatemsg-filter-tags">',
'#suffix' => '</div>',
'#weight' => -10,
);
$form['user_id'] = array(
'#type' => 'value',
'#value' => $user->uid,
);
$form['thread_id'] = array(
'#type' => 'value',
'#value' => $thread_id,
);
$form['tags'] = array(
'#type' => 'textfield',
'#size' => 30,
'#default_value' => $tags,
'#autocomplete_path' => 'messages/filter/tag-autocomplete',
);
$form['modify_tags'] = array(
'#type' => 'submit',
'#value' => t('Tag this conversation'),
);
$form['cancel'] = array(
'#value' => l(t('Cancel'), $_GET['q'], array(
'attributes' => array(
'id' => 'privatemsg-filter-tags-cancel',
),
)),
);
return $form;
}
function privatemsg_filter_form_submit($form, &$form_state) {
$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('Your conversation tags have 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.recipient = u.uid AND pip.type = 'user'";
// 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.recipient = %d AND piu.type = 'user' 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, $showHidden = FALSE) {
$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;
}
if (!$showHidden) {
$fragments['where'][] = 't.hidden = 0 OR t.hidden IS NULL';
}
// 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;
}
}
/**
* Implements hook_privatemsg_message_insert().
*/
function privatemsg_filter_privatemsg_message_insert($message) {
foreach ($message['recipients'] as $recipient) {
if ($recipient->type == 'user' || $recipient->type == 'hidden') {
privatemsg_filter_add_tags(array(
$message['thread_id'],
), variable_get('privatemsg_filter_inbox_tag', ''), $recipient);
}
}
}
/**
* Implements hook_privatemsg_message_recipient_changed().
*/
function privatemsg_filter_privatemsg_message_recipient_changed($mid, $thread_id, $recipient, $type, $added) {
if ($added && ($type == 'user' || $type == 'hidden')) {
privatemsg_filter_add_tags(array(
$thread_id,
), variable_get('privatemsg_filter_inbox_tag', ''), (object) array(
'uid' => $recipient,
));
}
}
/**
* Form callback for adding a tag to threads.
*/
function privatemsg_filter_add_tag_submit($form, &$form_state) {
// Check if textfield is not empty.
if (empty($form_state['values']['tag-add'])) {
return;
}
$tags = explode(',', $form_state['values']['tag-add']);
$tag_ids = privatemsg_filter_create_tags($tags);
if (empty($tag_ids)) {
return;
}
$operation = array(
'callback' => 'privatemsg_filter_add_tags',
'callback arguments' => array(
'tag_id' => $tag_ids,
),
'sucess message' => t('The selected conversations have been tagged.'),
'undo callback' => 'privatemsg_filter_remove_tags',
'undo callback arguments' => array(
'tag_id' => $tag_ids,
),
);
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'],
),
'success message' => t('The tag has been removed from the selected conversations.'),
'undo callback' => 'privatemsg_filter_add_tags',
'undo callback arguments' => array(
'tag_id' => $form_state['values']['tag-remove'],
),
);
privatemsg_operation_execute($operation, $form_state['values']['threads']);
}