View source
<?php
define('HASHTAGS_FILTER_NAME', 'filter_hashtags');
function hashtags_help($path, $arg) {
switch ($path) {
case "admin/help#hashtags":
return '<p>' . t("Configure default behaviour of hashtags, including in which vocabulary it\\'ll be stored, outputing, showing field.") . '</p>';
break;
}
}
function hashtags_init() {
drupal_add_css(drupal_get_path('module', 'hashtags') . '/hashtags.css');
}
function hashtags_permission() {
return array(
'administer hashtags' => array(
'title' => t('Administer hashtags'),
'restrict access' => TRUE,
),
);
}
function hashtags_menu() {
$items['admin/config/content/hashtags'] = array(
'title' => 'Hashtags',
'description' => 'Configure default behavior of hashtags, including in which vocabulary it\'ll be stored, outputing, showing field.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'hashtags_configuration_form',
),
'access arguments' => array(
'administer hashtags',
),
'weight' => -10,
);
return $items;
}
function hashtags_form_field_ui_field_edit_form_alter(&$form, &$form_state) {
$field_name = variable_get('hashtags_terms_field', 'field_hashtags');
$instance = $form['#instance'];
$entity_type = $instance['entity_type'];
$bundle = $instance['bundle'];
if ($entity_type == 'comment') {
$entity_type = 'node';
$bundle = substr($bundle, 13);
}
if ($form['#field']['module'] != 'text' || !_hashtags_is_field_exists($field_name, $entity_type, $bundle)) {
return;
}
$hashtag_value = isset($instance['hashtag_settings']['hashtag_field']) ? $instance['hashtag_settings']['hashtag_field'] : FALSE;
$form['instance']['hashtag_settings'] = array(
'#type' => 'fieldset',
'#title' => t('Hashtag settings'),
'#description' => t('Track hashtags for this field'),
'#weight' => 5,
'#collapsible' => FALSE,
);
$form['instance']['hashtag_settings']['hashtag_field'] = array(
'#type' => 'checkbox',
'#title' => t('Enable this field for tracking hashtags'),
'#default_value' => $hashtag_value,
);
}
function hashtags_entity_presave($entity, $entity_type) {
if ($entity_type == 'comment') {
return;
}
if (isset($entity->hashtags_ignore) && $entity->hashtags_ignore) {
return;
}
$field_name = variable_get('hashtags_terms_field', 'field_hashtags');
$vid = variable_get('hashtags_vocabulary', 3);
$wrapper = entity_metadata_wrapper($entity_type, $entity);
$bundle = $wrapper
->getBundle();
if (!_hashtags_is_field_exists($field_name, $entity_type, $bundle)) {
return;
}
$entity_id = $wrapper
->getIdentifier();
$entity_id = empty($entity_id) ? 0 : $entity_id;
$text = _hashtags_get_tracked_text($wrapper, $entity_type, $bundle);
$current_terms = array();
$current_terms_titles = hashtags_parse_tags($text, FALSE);
$prev_terms = _hashtags_index_get_entity_tags($entity_id, $entity_type);
$prev_terms_titles = array_keys($prev_terms);
$terms_to_add = array_diff($current_terms_titles, $prev_terms_titles);
foreach ($terms_to_add as $name) {
$tid = _hashtags_add_term($name, $vid);
_hashtags_index_add($entity_id, $entity_type, $tid);
}
if ($entity_id) {
$terms_to_delete = array_diff($prev_terms_titles, $current_terms_titles);
foreach ($terms_to_delete as $name) {
$tid = $prev_terms[$name];
$left_hashtags = _hashtags_index_remove($entity_id, $entity_type, $tid);
if (!$left_hashtags && _hashtags_is_last_term($tid, $field_name)) {
taxonomy_term_delete($tid);
}
}
}
_hashtags_index_sync_tags($wrapper, $entity_type);
}
function hashtags_entity_insert($entity, $entity_type) {
if ($entity_type == 'comment') {
return;
}
if (isset($entity->hashtags_ignore) && $entity->hashtags_ignore) {
return;
}
$field_name = variable_get('hashtags_terms_field', 'field_hashtags');
$vid = variable_get('hashtags_vocabulary', 3);
$wrapper = entity_metadata_wrapper($entity_type, $entity);
$bundle = $wrapper
->getBundle();
if (!_hashtags_is_field_exists($field_name, $entity_type, $bundle)) {
return;
}
$entity_id = $wrapper
->getIdentifier();
db_update('hashtags_index')
->fields(array(
'entity_id' => $entity_id,
))
->condition('entity_id', 0)
->execute();
}
function hashtags_comment_presave($comment) {
$field_name = variable_get('hashtags_terms_field', 'field_hashtags');
$vid = variable_get('hashtags_vocabulary', 3);
$entity_type = 'node';
$entity = entity_load_single($entity_type, $comment->nid);
$entity_wrapper = entity_metadata_wrapper($entity_type, $entity);
$bundle = $entity_wrapper
->getBundle();
if (!_hashtags_is_field_exists($field_name, $entity_type, $bundle)) {
return;
}
$comment_wrapper = entity_metadata_wrapper('comment', $comment);
$text = _hashtags_get_tracked_text($comment_wrapper, 'comment', $comment->node_type);
$entity_id = $entity_wrapper
->getIdentifier();
$comment_id = empty($comment->cid) ? 0 : $comment->cid;
$current_terms = array();
$current_terms_titles = hashtags_parse_tags($text, FALSE);
$prev_terms = _hashtags_index_get_comment_tags($comment_id, $entity_type);
$prev_terms_titles = array_keys($prev_terms);
$terms_to_add = array_diff($current_terms_titles, $prev_terms_titles);
foreach ($terms_to_add as $name) {
$tid = _hashtags_add_term($name, $vid);
_hashtags_index_add($entity_id, $entity_type, $tid, $comment_id);
}
if ($comment_id != 0) {
$terms_to_delete = array_diff($prev_terms_titles, $current_terms_titles);
foreach ($terms_to_delete as $name) {
$tid = $prev_terms[$name];
$left_hashtags = _hashtags_index_remove($entity_id, $entity_type, $tid, $comment_id);
if (!$left_hashtags && _hashtags_is_last_term($tid, $field_name)) {
taxonomy_term_delete($tid);
}
}
}
_hashtags_index_sync_tags($entity_wrapper, $entity_type, TRUE);
}
function hashtags_comment_insert($comment) {
$field_name = variable_get('hashtags_terms_field', 'field_hashtags');
$vid = variable_get('hashtags_vocabulary', 3);
$entity_type = 'node';
$entity = entity_load_single($entity_type, $comment->nid);
$wrapper = entity_metadata_wrapper($entity_type, $entity);
$bundle = $wrapper
->getBundle();
if (!_hashtags_is_field_exists($field_name, $entity_type, $bundle)) {
return;
}
db_update('hashtags_index')
->fields(array(
'comment_id' => $comment->cid,
))
->condition('comment_id', 0)
->execute();
}
function hashtags_filter_info() {
$filters = array();
$filters[HASHTAGS_FILTER_NAME] = array(
'title' => t('Convert Hashtags into links'),
'description' => t('Turn #words into links which lead to taxonomy terms'),
'process callback' => '_hashtags_filter_process',
'cache' => TRUE,
);
return $filters;
}
function _hashtags_get_tracked_text(&$wrapper, $entity_type, $bundle) {
$text = '';
$fields = field_info_instances($entity_type, $bundle);
foreach ($fields as $field_name => $field) {
if (isset($field['hashtag_settings']) && $field['hashtag_settings']['hashtag_field']) {
$text .= ' ';
if ($field['settings']['text_processing']) {
$text .= $wrapper->{$field_name}->value
->value();
}
else {
$text .= $wrapper->{$field_name}
->value();
}
}
}
return $text;
}
function _hashtags_get_prev_terms(&$wrapper, $field_name = 'field_hashtags') {
$prev_terms = array();
$terms = $wrapper->{$field_name}
->value();
foreach ($terms as $term) {
if (isset($term->tid)) {
$prev_terms[$term->name] = $term->tid;
}
}
return $prev_terms;
}
function _hashtags_is_last_term($tid, $field_name = 'field_hashtags') {
$query = new EntityFieldQuery();
$query
->fieldCondition($field_name, 'tid', $tid, '=');
$entity_types = $query
->execute();
$count = 0;
foreach ($entity_types as $entity_type) {
$count += sizeof($entity_type);
}
return $count == 1;
}
function _hashtags_add_term($name, $vid) {
$term = taxonomy_term_load_multiple(array(), array(
'name' => trim($name),
'vid' => $vid,
));
$term = array_shift($term);
if (empty($term)) {
$term = new stdClass();
$term->name = $name;
$term->vid = $vid;
taxonomy_term_save($term);
}
return isset($term->tid) ? $term->tid : '';
}
function _hashtags_is_field_exists($field_name, $entity_type, $bundle = '') {
if (empty($bundle)) {
$field_names = field_info_instances($entity_type);
}
else {
$field_names = field_info_instances($entity_type, $bundle);
}
return isset($field_names[$field_name]);
}
function _hashtags_index_add($entity_id, $type, $tid, $comment_id = NULL) {
db_insert('hashtags_index')
->fields(array(
'entity_id' => $entity_id,
'type' => $type,
'tid' => $tid,
'comment_id' => $comment_id,
))
->execute();
return TRUE;
}
function _hashtags_index_remove($entity_id, $type, $tid, $comment_id = NULL) {
db_delete('hashtags_index')
->condition('entity_id', $entity_id)
->condition('type', $type)
->condition('tid', $tid)
->condition('comment_id', $comment_id)
->execute();
return _hashtags_index_get_tags_count($entity_id, $type, $tid);
}
function _hashtags_index_get_entity_tags($entity_id, $type) {
$result = db_query('SELECT hi.tid, td.name
FROM {hashtags_index} hi
INNER JOIN {taxonomy_term_data} td ON hi.tid = td.tid
WHERE hi.entity_id = :entity_id
AND hi.type = :type
AND hi.comment_id IS NULL', array(
':entity_id' => $entity_id,
':type' => $type,
));
$tags = array();
foreach ($result as $data) {
$tags[$data->name] = $data->tid;
}
return $tags;
}
function _hashtags_index_get_all_tags($entity_id, $type) {
$result = db_query('SELECT hi.tid, td.name
FROM {hashtags_index} hi
INNER JOIN {taxonomy_term_data} td ON hi.tid = td.tid
WHERE hi.entity_id = :entity_id
AND hi.type = :type
GROUP BY hi.tid', array(
':entity_id' => $entity_id,
':type' => $type,
));
$tags = array();
foreach ($result as $data) {
$tags[$data->name] = $data->tid;
}
return $tags;
}
function _hashtags_index_get_comment_tags($comment_id, $type) {
$result = db_query('SELECT hi.tid, td.name
FROM {hashtags_index} hi
INNER JOIN {taxonomy_term_data} td ON hi.tid = td.tid
WHERE hi.comment_id = :comment_id AND hi.type = :type', array(
':comment_id' => $comment_id,
':type' => $type,
));
$tags = array();
foreach ($result as $data) {
$tags[$data->name] = $data->tid;
}
return $tags;
}
function _hashtags_index_is_exists($entity_id, $type, $tid, $comment_id = NULL) {
$count = db_query('SELECT COUNT(*)
FROM {hashtags_index}
WHERE entity_id = :entity_id
AND type = :type
AND tid = :tid
AND comment_id = :comment_id', array(
':entity_id' => $entity_id,
':type' => $type,
':tid' => $tid,
':comment_id' => $comment_id,
))
->fetchField();
return $count ? TRUE : FALSE;
}
function _hashtags_index_get_tags_count($entity_id, $type, $tid) {
$count = db_query('SELECT COUNT(*)
FROM {hashtags_index}
WHERE entity_id = :entity_id
AND type = :type
AND tid = :tid', array(
':entity_id' => $entity_id,
':type' => $type,
':tid' => $tid,
))
->fetchField();
return $count;
}
function _hashtags_index_sync_tags(&$wrapper, $entity_type, $save = FALSE, $field_name = "field_hashtags", $tags = array()) {
if (!sizeof($tags)) {
$entity_id = $wrapper
->getIdentifier();
$tags = _hashtags_index_get_all_tags($entity_id, $entity_type);
}
$wrapper->{$field_name}
->set(array_values($tags));
if ($save) {
$wrapper
->save();
}
return TRUE;
}
function _hashtags_filter_process($text, $filter, $format, $langcode, $cache, $cache_id) {
$hashtags_array = hashtags_parse_tags($text, FALSE);
if (!sizeof($hashtags_array)) {
return $text;
}
$hashtags_tids = hashtags_get_terms_by_names($hashtags_array);
if (!sizeof($hashtags_tids)) {
return $text;
}
$replace_parameter = new HashtagsLinksConverter();
$replace_parameter->hashtags_tids = $hashtags_tids;
$pattern = "/([\\s>]*?)(#)([[:alpha:]][[:alnum:]_]*[^<\\s[:punct:]])/iu";
$text = preg_replace_callback($pattern, array(
&$replace_parameter,
'replace',
), $text);
return $text;
}
function hashtags_parse_tags($text, $is_string_return = TRUE, $capture_position = FALSE) {
$flag = $capture_position ? PREG_OFFSET_CAPTURE : PREG_PATTERN_ORDER;
$tags_list = array();
$pattern = "/[\\s>]+?#([[:alpha:]][[:alnum:]_]*[^<\\s[:punct:]])/iu";
preg_match_all($pattern, '<htest>' . $text . '<htest>', $tags_list, $flag);
if (isset($tags_list[0]) && !sizeof($tags_list[0])) {
if ($is_string_return) {
return '';
}
return array();
}
if ($capture_position) {
foreach ($tags_list[1] as $key => $data) {
$result[$data[1]] = strtolower($data[0]);
}
}
else {
foreach ($tags_list[1] as $key => $tag) {
$tags_list[1][$key] = strtolower($tag);
}
if ($is_string_return) {
$result = implode(',', $tags_list[1]);
}
else {
$result = $tags_list[1];
}
}
return array_unique($result);
}
function hashtags_get_terms_by_names($names) {
$terms = array();
$vid = variable_get('hashtags_vocabulary', '');
$sql = "SELECT ttd.name, ttd.tid FROM {taxonomy_term_data} ttd \n WHERE lower(ttd.name) IN (:names) AND ttd.vid = :vid";
$result = db_query($sql, array(
':names' => $names,
':vid' => $vid,
));
foreach ($result as $term) {
$terms[$term->name] = $term->tid;
}
return $terms;
}
function _hashtags_node_check_node_type($node) {
$field_name = variable_get('hashtags_terms_field', '');
$field = field_info_instance('node', $field_name, $node->type);
return is_array($field);
}
function hashtags_configuration_form($form, &$form_state) {
$field_name = variable_get('hashtags_terms_field', '');
$types = node_type_get_names();
$types_keys = array_keys($types);
$default_values = array();
$vocabulary = taxonomy_vocabulary_load(variable_get('hashtags_vocabulary', 0));
foreach ($types_keys as $type) {
if (field_info_instance('node', $field_name, $type)) {
$default_values[$type] = $type;
}
else {
$default_values[$type] = 0;
}
}
$values = array();
$form['hashtags_content_types'] = array(
'#type' => 'checkboxes',
'#title' => t('Content types'),
'#options' => $types,
'#default_value' => $default_values,
'#description' => t('Check for what content types you want Hashtags functionality to have'),
);
$form['advanced'] = array(
'#title' => t('Advanced'),
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#collapsed' => TRUE,
);
$form['advanced']['clean_orphaned_terms'] = array(
'#type' => 'submit',
'#value' => t('Clean orphaned hashtags'),
'#submit' => array(
'hashtags_clean_orphaned_terms_submit',
),
);
$form['advanced']['clean_hash_symbol'] = array(
'#type' => 'submit',
'#value' => t('Clean hash symbol'),
'#submit' => array(
'hashtags_clean_hash_symbol_submit',
),
);
$form['advanced']['import_to_index'] = array(
'#type' => 'submit',
'#value' => t('Import hashtags to index'),
'#submit' => array(
'hashtags_import_to_index_submit',
),
);
$form['#submit'][] = 'hashtags_fields_content_types_submit';
return system_settings_form($form);
}
function hashtags_import_to_index_submit($form, &$form_state) {
if ($form_state['clicked_button']['#parents'][0] == 'import_to_index') {
hashtags_index_import();
}
}
function hashtags_clean_hash_symbol_submit($form, &$form_state) {
if ($form_state['clicked_button']['#parents'][0] == 'clean_hash_symbol') {
$vid = variable_get('hashtags_vocabulary', '');
$count = hashtags_clean_hash_symbol();
drupal_set_message(t(':count hashtags has been updated', array(
':count' => $count,
)));
}
}
function hashtags_clean_orphaned_terms_submit($form, &$form_state) {
if ($form_state['clicked_button']['#parents'][0] == 'clean_orphaned_terms') {
$count = hashtags_clean_orphaned_terms();
drupal_set_message(t(':count orphaned hastags have been cleaned'), array(
':count' => $count,
));
}
}
function hashtags_index_import() {
db_delete('hashtags_index')
->execute();
$query = new EntityFieldQuery();
$query
->entityCondition('entity_type', 'node')
->fieldCondition('field_hashtags');
$entity_types = $query
->execute();
$batch = array(
'title' => t('Import data to Hashtags index table from nodes that related to hashtags'),
'operations' => array(),
'finished' => '_hashtags_index_batch_finished',
);
$count = 0;
foreach ($entity_types['node'] as $nid => $item) {
$batch['operations'][] = array(
'_hashtags_index_batch_process',
array(
$nid,
),
);
$count++;
}
batch_set($batch);
}
function _hashtags_index_batch_process($nid, &$context) {
$node = node_load($nid);
node_save($node);
}
function _hashtags_index_batch_finished($success, $results, $operations) {
if ($success) {
drupal_set_message('Hashtags data have been imported to Hashtags index');
}
else {
$error_operation = reset($operations);
$message = t('An error occurred while processing %error_operation with arguments: @arguments', array(
'%error_operation' => $error_operation[0],
'@arguments' => print_r($error_operation[1], TRUE),
));
drupal_set_message($message, 'error');
}
}
function hashtags_fields_activate($entity_type = 'node', $field_activate = 'body') {
$field_name = variable_get('hashtags_terms_field', 'field_hashtags');
$fields = field_info_instances($entity_type);
foreach ($fields as $bundle => $fields) {
if (isset($fields[$field_name]) && isset($fields[$field_activate])) {
$fields[$field_activate]['hashtag_settings']['hashtag_field'] = 1;
field_update_instance($fields[$field_activate]);
$comment_instance = field_info_instance('comment', 'comment_body', 'comment_node_' . $bundle);
$comment_instance['hashtag_settings']['hashtag_field'] = 1;
field_update_instance($comment_instance);
}
}
}
function hashtags_clean_hash_symbol($vid = NULL) {
if (is_null($vid)) {
$vid = variable_get('hashtags_vocabulary', '');
}
$result = db_query("UPDATE {taxonomy_term_data} \n SET name = SUBSTR(name, 2) \n WHERE vid = :vid AND \n name LIKE '#%'", array(
':vid' => $vid,
));
return $result
->rowCount();
}
function hashtags_clean_orphaned_terms($vid = NULL) {
if (is_null($vid)) {
$vid = variable_get('hashtags_vocabulary', '');
}
$sql = "SELECT td.tid, (SELECT COUNT(*) \n FROM {field_data_field_hashtags} fh \n WHERE fh.field_hashtags_tid = td.tid) AS attached_count \n FROM {taxonomy_term_data} td \n WHERE vid = :vid \n HAVING attached_count = 0";
$result = db_query($sql, array(
':vid' => $vid,
));
foreach ($result as $term) {
taxonomy_term_delete($term->tid);
}
return $result
->rowCount();
}
function hashtags_configuration_form_validate($form, &$form_state) {
if ($form_state['clicked_button']['#parents'][0] == 'submit') {
$vocabulary = taxonomy_vocabulary_load(variable_get('hashtags_vocabulary', 0));
if (!$vocabulary) {
form_set_error('', t('Hashtags vocabulary has n\'t been created or has been deleted manually.. try to reinstall module.'));
return;
}
$field_name = variable_get('hashtags_terms_field', '');
if (!field_info_field($field_name)) {
form_set_error('', t('Hashtags field has n\'t been created or has been deleted manually.. try to reinstall module.'));
return;
}
}
}
function hashtags_fields_content_types_submit($form, &$form_state) {
if ($form_state['clicked_button']['#parents'][0] == 'submit') {
$field_name = variable_get('hashtags_terms_field', '');
foreach ($form_state['values']['hashtags_content_types'] as $content_type => $checked) {
$instance = field_info_instance('node', $field_name, $content_type);
if ($content_type === $checked) {
if (!is_array($instance)) {
$instance = array(
'field_name' => $field_name,
'entity_type' => 'node',
'label' => 'Hashtags',
'bundle' => $content_type,
'widget' => array(
'type' => 'taxonomy_autocomplete',
'weight' => -4,
),
'display' => array(
'default' => array(
'type' => 'taxonomy_term_reference_link',
'weight' => 10,
),
'teaser' => array(
'type' => 'taxonomy_term_reference_link',
'weight' => 10,
),
),
);
field_create_instance($instance);
}
}
else {
if (is_array($instance)) {
field_delete_instance($instance, FALSE);
}
}
}
hashtags_fields_activate();
drupal_set_message(t('Content types and Hashtags fields chain have been saved.'));
}
}
function hashtags_add_filter() {
$added_status = array();
$format_id = 'filtered_html';
$is_hashtag_filter_exists = db_query('SELECT COUNT(*) FROM {filter} WHERE format = :format AND module = :module AND name = :name', array(
':format' => $format_id,
':module' => 'hashtags',
':name' => HASHTAGS_FILTER_NAME,
))
->fetchField();
if (!$is_hashtag_filter_exists) {
$max_filter_weight = db_query('SELECT MAX(weight) FROM {filter} WHERE format = :format', array(
':format' => $format_id,
))
->fetchField();
db_insert('filter')
->fields(array(
'format',
'name',
'weight',
'status',
'module',
'settings',
))
->values(array(
'format' => $format_id,
'name' => 'filter_hashtags',
'weight' => $max_filter_weight + 1,
'status' => 1,
'module' => 'hashtags',
'settings' => serialize(array()),
))
->execute();
$added_status[] = $format_id;
drupal_set_message(t('Hashtags filter has been added to "Filter HTML" input format'));
watchdog('Input format', t('Hashtags filter has been added to "Filter HTML" input format'));
}
$format_id = 'full_html';
$is_hashtag_filter_exists = db_query('SELECT COUNT(*) FROM {filter} WHERE format = :format AND module = :module AND name = :name', array(
':format' => $format_id,
':module' => 'hashtags',
':name' => HASHTAGS_FILTER_NAME,
))
->fetchField();
if (!$is_hashtag_filter_exists) {
$max_filter_weight = db_query('SELECT MAX(weight) FROM {filter} WHERE format = :format', array(
':format' => $format_id,
))
->fetchField();
db_insert('filter')
->fields(array(
'format',
'name',
'weight',
'status',
'module',
'settings',
))
->values(array(
'format' => $format_id,
'name' => 'filter_hashtags',
'weight' => $max_filter_weight + 1,
'status' => 1,
'module' => 'hashtags',
'settings' => serialize(array()),
))
->execute();
$added_status[] = $format_id;
drupal_set_message(t('Hashtags filter has been added to "Full HTML" input format'));
watchdog('Input format', t('Hashtags filter has been added to "Full HTML" input format'));
}
filter_formats_reset();
if (sizeof($added_status)) {
return TRUE;
}
return FALSE;
}
function hashtags_remove_filter() {
$module = 'hashtags';
db_query("DELETE FROM {filter} WHERE module = :module", array(
':module' => $module,
));
filter_formats_reset();
drupal_set_message(t('Hashtags filter has been removed from all input format'));
watchdog('Input format', t('Hashtags filter has been removed from all input format'));
}
class HashtagsLinksConverter {
function replace($matches) {
if (isset($this->hashtags_tids)) {
$hashtags_tids = $this->hashtags_tids;
}
$first_delimeter = isset($matches[1]) ? $matches[1] : '';
$hashtag_name = isset($matches[3]) ? $matches[3] : '';
$hashtag_tid = isset($hashtags_tids[strtolower($hashtag_name)]) ? $hashtags_tids[strtolower($hashtag_name)] : '';
$hashtag_name = '#' . $hashtag_name;
if (empty($hashtag_tid)) {
return $first_delimeter . $hashtag_name;
}
if ($_GET['q'] == 'taxonomy/term/' . $hashtag_tid) {
$hashtag_link = l($hashtag_name, 'taxonomy/term/' . $hashtag_tid);
}
else {
$hashtag_link = l($hashtag_name, 'taxonomy/term/' . $hashtag_tid, array(
'attributes' => array(
'class' => 'hashtag',
),
));
}
return $first_delimeter . $hashtag_link;
}
}