'. t('The trackback module allows users to give a blog post a contextual link to another. A context is made because the trackbacking poster is, in theory, writing about something mentioned on another blogger\'s trackbacked post. Using the trackback URL accompanying each post, another website can send a ping to your website. Once received, the trackback will show up on the trackback page accompanying the post. It also includes auto-discovery, spam moderation queues, and the ability to manually ping another site.') .'

'; $output .= '

'. t('If trackback autodisovery is enabled on your website, someone need only visit your post via a link from another website post to have trackback discover the linking site and create the trackback. Trackback auto-discovery also works internally within a website, automatically creating connections between pages which link to each other. To manually send a ping to another site, edit your post and use the trackback URL field at the bottom of the edit page to submit the trackback URL for the post on the other site. Once you enter submit, your website will ping the other site for you. With trackback autodiscovery enabled, your site will attempt to do this automatically without your intervention.') .'

'; $output .= '

'. t('To enable the moderation queue, go to the administer trackbacks page and select the configure tab. To view, approve, or delete trackbacks awaiting moderation, go to the administer trackbacks page and select the approval queue. To administer the trackback settings for a particular content type go to that content types administration page.') .'

'; $output .= '

'. t('For more information please read the configuration and customization handbook Trackback page.', array('@trackback' => 'http://www.drupal.org/handbook/modules/trackback')) .'

'; return $output; } } function trackback_page($nid = NULL) { if (is_numeric($nid) and $node = node_load($nid) and !empty($node->can_receive)) { $output[] = ''; $output[] = ''; $trackback = trackback_receive($node); if (empty($trackback->error)) { $trackback->trid = db_next_id('{trackback_received}_trid'); db_query("INSERT INTO {trackback_received} (trid, nid, created, site, name, subject, url, excerpt, status) VALUES (%d, %d, %d, '%s', '%s', '%s', '%s', '%s', %d)", $trackback->trid, $trackback->nid, $trackback->created, $trackback->site, $trackback->name, $trackback->subject, $trackback->url, $trackback->excerpt, $trackback->status); trackback_invoke_trackback($trackback, 'insert'); watchdog('trackback', t('Added trackback %subject.', array('%subject' => $trackback->subject)), WATCHDOG_NOTICE, l(t('view trackback'), 'node/'. $node->nid, NULL, NULL, 'trackback-'. $trackback->trid)); $output[] = '0'; } else { $output[] = '1'; $output[] = ''. $trackback->error .''; } $output[] = ''; header('Content-Type: text/xml'); print implode("\n", $output) ."\n"; } else { drupal_goto(); } } function trackback_receive($node) { $trackback = new stdClass(); if (!_trackback_valid_url($_REQUEST['url'])) { $trackback->error = t('Missing TrackBack url.'); } elseif (variable_get('trackback_reject_oneway', 0)) { $reply = drupal_http_request($_REQUEST['url']); if (!empty($reply->error)) { $trackback->error = t('Could not retrieve the sender page.'); } elseif (stristr($reply->data, $GLOBALS['base_url'] .'/') === FALSE) { $trackback->error = t('The sender page does not refer to recipient site.'); } } if (empty($trackback->error)) { $trackback->nid = $node->nid; $trackback->created = time(); $trackback->site = $_SERVER['REMOTE_ADDR']; list($name, $subject, $excerpt) = _trackback_optional_params('blog_name', 'title', 'excerpt'); $trackback->name = strip_tags($name ? $name : $_REQUEST['url']); $trackback->subject = $subject ? $subject : $_REQUEST['url']; $trackback->url = $_REQUEST['url']; $trackback->excerpt = strlen($excerpt) > 255 ? truncate_utf8($excerpt, 252) .'...' : $excerpt; $trackback->status = (variable_get('trackback_moderation', 0) == 0) ? 1 : 0; trackback_invoke_trackback($trackback, 'receive'); } return $trackback; } function _trackback_valid_url($url) { $uc = '[a-z0-9;/?:@&=+$,_.!~*\'()%-]'; return preg_match('`^(http|https)://'. $uc .'+(#'. $uc .'*)?$`i', $url); } function _trackback_optional_params() { $args = func_get_args(); foreach ($args as $i) { $params[] = isset($_REQUEST[$i]) ? $_REQUEST[$i] : ''; } if (preg_match('/;\s*charset=([^\s;]+)/i', $_SERVER['CONTENT_TYPE'], $match)) { $charset = $match[1]; } else { $utf8 = '/^(?:[\s\x21-\x7F]|[\xC2-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3}|[\xF8-\xFB][\x80-\xBF]{4}|[\xFC-\xFD][\x80-\xBF]{5})*$/'; $sample = implode(' ', $params); if (!preg_match($utf8, $sample)) { global $locale; $defaults = array( 'be' => 5, 'cs' => 2, 'el' => 7, 'hr' => 2, 'hu' => 2, 'pl' => 2, 'ro' => 2, 'ru' => 5, 'sk' => 2, 'sl' => 2, 'tr' => 9, 'uk' => 5, 'ja' => array('ISO-2022-JP', 'EUC-JP', 'SJIS'), 'ko' => array('ISO-2022-KR', 'EUC-KR'), 'zh-hans' => array('HZ', 'EUC-CN'), 'zh-hant' => array('BIG-5', 'EUC-TW') ); if ($charset = $defaults[$locale] and is_array($charset)) { if (function_exists('mb_detect_encoding')) { $charset = @mb_detect_encoding($sample, $charset); } else { foreach ($charset as $charset) { if (drupal_convert_to_utf8($sample, $charset) != '') break; $charset = NULL; } } } if (!$charset) { $charset = 'ISO-8859-1'; // or 'ISO-8859-15' } elseif (!is_string($charset)) { $charset = 'ISO-8859-'. $charset; } } } if ($charset && strcasecmp($charset, 'UTF-8') != 0) { foreach ($params as $i => $t) { if ($t != '') { $t = drupal_convert_to_utf8($t, $charset); if ($t != '') { $params[$i] = $t; } } } } return $params; } /** * Invoke a hook_trackback() operation in all modules. * * @param &$trackback * A trackback object. * @param $op * A string containing the name of the trackback operation. * @return * The returned value of the invoked hooks. */ function trackback_invoke_trackback(&$trackback, $op) { $return = array(); foreach (module_implements('trackback') as $name) { $function = $name .'_trackback'; $result = $function($trackback, $op); if (isset($result)) { if (is_array($result)) { $return = array_merge($return, $result); } else { $return[] = $result; } } } return $return; } /** * Implementation of hook_trackback() */ function trackback_trackback(&$trackback, $op) { switch ($op) { case 'receive': // drop silently if this is from a known spammer IP address if (variable_get('trackback_spam_filter', 1)) { module_invoke('spam', 'ip_filter', 'trackback', 0); } break; case 'insert': if (variable_get('trackback_spam_filter', 1)) { // invoke spam.module's spam filter module_invoke('spam', 'content_filter', 'trackback', $trackback->trid, $trackback->subject .' '. $trackback->url, $trackback->excerpt); } break; case 'delete': if (module_exists('spam')) { db_query("DELETE FROM {spam_tracker} WHERE source='trackback' AND id=%d", $trackback->trid); spam_log(SPAM_LOG, t('spam_delete_trackback: deleted trackback "%subject".', array('%subject' => $trackback->subject)), 'trackback', $trackback->trid); } break; } } /** * Modeled after theme_comment(); * Code was taken directly from there and then mutilated. * */ function theme_trackback($trackback, $links = NULL) { $output = '
\n"; $output .= '
'. l($trackback->subject, $trackback->url) ."
\n"; $output .= '
'. t('from %sitename on %date', array('%sitename' => $trackback->name, '%date' => format_date($trackback->created))) ."
\n"; $output .= '
'. check_markup($trackback->excerpt) ."
\n"; if ($links) { $output .= '\n"; } $output .= "
\n"; return $output; } function theme_trackbacks($trackbacks) { $output = '
'."\n"; $output .= $trackbacks ."\n"; $output .= '
'."\n"; return $output; } function theme_trackback_url($url) { return '
'. theme('box', t('Trackback URL for this post:'), $url) .'
'; } function trackback_form_alter($form_id, &$form) { if ($form_id == 'node_type_form' && isset($form['identity']['type'])) { $form['workflow']['trackback'] = array( '#type' => 'radios', '#title' => t('Trackbacks'), '#options' => array(t('Disabled'), t('Enabled')), '#default_value' => _trackback_node_type($form['#node_type']->type), '#description' => t('Enable trackbacks for this node type.') ); } else if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) { $node = $form['#node']; if (_trackback_node_type($node->type)) { $form['trackback'] = array( '#type' => 'fieldset', '#title' => t('Trackbacks'), '#collapsible' => TRUE ); $form['trackback']['can_receive'] = array( '#type' => 'checkbox', '#title' => t('Allow Trackbacks'), '#default_value' => isset($node->can_receive) ? $node->can_receive : 1, '#description' => t('Allow other posts to send trackbacks to this content.') ); $form['trackback']['trackback_urls'] = array( '#type' => 'textarea', '#title' => t('Send Trackbacks'), '#default_value' => isset($node->trackback_urls) ? $node->trackback_urls : '', '#cols' => 80, '#rows' => 4, '#description' => t('Enter one URL per line for each trackback you wish to send.') ); // if there are any past successful trackbacks from this posting, add them to the node editing page. // if there are any past unsuccessful trackbacks from this posting, add checkmarks to enable resending them $past_successes_listing = array(); $options = array(); $result = db_query('SELECT url, successful FROM {trackback_sent} WHERE nid = %d', $node->nid); while ($url = db_fetch_object($result)) { if ($url->successful) { $past_successes_listing[] = $url->url; } else { $options[$url->url] = $url->url; } } // add listing of successfully trackbacked URLs if (count($past_successes_listing)) { $form['trackback'][] = array( '#value' => theme('item_list', $past_successes_listing, t('Successful URLs')) ); //t('These URLs have been successfuly pinged by this post.') } // add listing of unsuccessfully trackbacked URLs if (count($options)) { $form['trackback']['trackback_urls_to_retry'] = array( '#type' => 'checkboxes', '#title' => t('Unsuccessful URLs'), '#default_value' => array(), '#options' => $options, '#description' => t('Attempts to ping these URLs with this post have failed. Mark a check next to the trackback URLs you wish to retry for this post.') ); } } } } function trackback_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) { switch ($op) { case 'load': if (_trackback_node_type($node->type)) { $q = db_fetch_object(db_query('SELECT * FROM {trackback_node} WHERE nid=%d', $node->nid)); return array('can_receive' => $q ? $q->can_receive : 1); } break; // // TODO: // If exists HTTP Referrer & Can Receive = 1 // Fetch from database all records in trackback_received where the node is the same // if the current referrer already has a record in trackback_receieved for this node do nothing // else process trackback as at trackback_receive() // endif // endif // // case 'alter': if ($node->can_receive && !$node->in_preview) { $url = url('node/'. $node->nid, NULL, NULL, TRUE); $tb_url = url('trackback/'. $node->nid, NULL, NULL, TRUE); $autodetect = "\n\n"; if ($teaser) { $node->teaser .= $autodetect; } else { $node->trackback = array( 'autodetect' => $autodetect, 'url' => theme('trackback_url', $tb_url) ); $result = db_query('SELECT * FROM {trackback_received} WHERE nid = %d AND status = 1 ORDER BY created DESC', $node->nid); if ($tb = db_fetch_object($result)) { $link = (user_access('administer trackbacks') || node_access('update', $node)); $received = ''; do { $received .= theme('trackback', $tb, $link ? module_invoke_all('link', 'trackback', $tb, FALSE) : array()); } while ($tb = db_fetch_object($result)); $node->trackback['received'] = theme('trackbacks', $received); } $node->body .= implode('', $node->trackback); } } break; case 'validate': if (!empty($node->trackback_urls)) { foreach (explode("\n", $node->trackback_urls) as $url) { $url = trim($url); if ($url && !_trackback_valid_url($url)) { form_set_error('trackback_urls', t('The trackback url %url is not a valid url.', array('%url' => $url))); } } } break; case 'insert': case 'update': if (_trackback_node_type($node->type)) { register_shutdown_function('_trackback_send', $node); $cron = ($node->status && variable_get('trackback_auto_detection_enabled', 0) == 2); db_lock_table('trackback_node'); db_query("UPDATE {trackback_node} SET awaiting_cron=%d, can_receive=%d WHERE nid=%d", $cron, $node->can_receive, $node->nid); if (!db_affected_rows()) { db_query("INSERT INTO {trackback_node}(nid, awaiting_cron, can_receive) VALUES(%d, %d, %d)", $node->nid, $cron, $node->can_receive); } db_unlock_tables(); } break; case 'delete': if (module_exists('spam')) { db_query("DELETE FROM {spam_tracker} USING ({spam_tracker}, {trackback_received}) WHERE {spam_tracker}.source='trackback' AND {spam_tracker}.id={trackback_received}.trid AND {trackback_received}.nid=%d", $node->nid); } db_query("DELETE FROM {trackback_node} WHERE nid=%d", $node->nid); db_query("DELETE FROM {trackback_sent} WHERE nid=%d", $node->nid); db_query("DELETE FROM {trackback_received} WHERE nid=%d", $node->nid); break; } } function _trackback_links($tb, $edit = FALSE) { static $spam; if (!isset($spam)) { $spam = module_exists('spam'); } if (!$edit) { $links['trackback_edit'] = array( 'title' => t('edit'), 'href' => 'admin/content/trackback/edit/'. $tb->trid ); } $links['trackback_delete'] = array( 'title' => t('delete'), 'href' => 'admin/content/trackback/delete/'. $tb->trid ); if ($spam) { $links = array_merge($links, trackback_spam_link($tb)); } return $links; } function trackback_link($type, $node = NULL, $teaser = FALSE) { $links = array(); switch ($type) { case 'node': if ($teaser && $node->can_receive) { $count = db_result(db_query("SELECT count(*) FROM {trackback_received} WHERE nid=%d AND status=1", $node->nid)); if ($count) { $links[] = array( 'title' => format_plural($count, '1 trackback', '@count trackbacks'), 'href' => 'node/'. $node->nid, 'fragment' => 'trackbacks' ); } } break; case 'trackback': $links = _trackback_links($node, $teaser); break; } return $links; } function trackback_menu($may_cache = FALSE) { $items = array(); if ($may_cache) { $access = user_access('administer trackbacks'); $items[] = array('path' => 'trackback', 'title' => t('Trackbacks'), 'callback' => 'trackback_page', 'access' => user_access('access content'), 'type' => MENU_DYNAMIC); $items[] = array('path' => 'admin/content/trackback', 'title' => t('Trackbacks'), 'description' => t('List and edit site trackbacks and the trackback moderation queue.'), 'callback' => 'drupal_get_form', 'access' => $access, 'callback arguments' => array('trackback_admin_overview')); // Tabs: $items[] = array('path' => 'admin/content/trackback/list', 'title' => t('List'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10); $items[] = array('path' => 'admin/content/trackback/settings', 'title' => t('Settings'), 'callback' => 'drupal_get_form', 'access' => $access, 'callback arguments' => array('trackback_configure'), 'type' => MENU_LOCAL_TASK, 'weight' => 10); // Subtabs: $items[] = array('path' => 'admin/content/trackback/list/new', 'title' => t('New trackbacks'), 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10); $items[] = array('path' => 'admin/content/trackback/list/approval', 'title' => t('Approval queue'), 'callback' => 'drupal_get_form', 'access' => $access, 'callback arguments' => array('trackback_admin_overview', 'approval'), 'type' => MENU_LOCAL_TASK); if (module_exists('spam')) { $items[] = array('path' => 'admin/content/trackback/list/spam', 'title' => t('Spam'), 'callback' => 'drupal_get_form', 'access' => $access, 'callback arguments' => array('trackback_admin_overview', 'spam'), 'type' => MENU_LOCAL_TASK, 'weight' => 10 ); } // Other stuff: $items[] = array('path' => 'admin/content/trackback/edit', 'title' => t('Moderate trackback'), 'callback' => 'trackback_admin_edit', 'access' => $access, 'type' => MENU_CALLBACK ); } else { if (arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'trackback' && arg(3) == 'delete' && is_numeric(arg(4))) { $trackback = trackback_load(arg(4)); if (isset($trackback->nid)) { $items[] = array('path' => 'admin/content/trackback/delete', 'title' => t('Delete trackback'), 'callback' => 'trackback_admin_delete', 'access' => (user_access('administer trackbacks') || node_access('update', node_load($trackback->nid))), 'type' => MENU_CALLBACK); } } } return $items; } function trackback_perm() { return array('administer trackbacks'); } /** * Menu callback; present an administrative trackback listing. */ function trackback_admin_overview($type = 'new') { $spam_module_exists = module_exists('spam'); $operations = array(); if ($type == 'new') { $operations['spam_unpublish_trackback'] = t('Unpublish the selected trackbacks'); } else { $operations['spam_publish_trackback'] = t('Publish the selected trackbacks'); } if ($spam_module_exists) { $operations['spam_spam_trackback'] = t('Mark the selected trackbacks as spam'); $operations['spam_notspam_trackback'] = t('Mark the selected trackbacks as not spam'); } $operations['spam_delete_trackback'] = t('Delete the selected trackbacks (no confirmation)'); $form = array(); $form['update'] = array( '#type' => 'fieldset', '#title' => t('Update options'), 'operations' => array('#type' => 'value', '#value' => $operations) ); $form['update']['operation'] = array( '#prefix' => '
', '#type' => 'select', '#options' => $operations ); $form['update']['op'] = array( '#type' => 'submit', '#value' => t('Update trackbacks'), '#suffix' => '
' ); $form['trackbacks'] = array('#theme' => 'trackback_admin_table'); $form['trackbacks']['header'] = array( '#type' => 'value', '#value' => trackback_admin_table_header($type, $spam_module_exists) ); $status = ($type == 'approval') ? 0 : 1; if ($spam_module_exists) { $spam_threshold = (int)variable_get('spam_threshold', 80); if ($type == 'spam') { $sql = 'SELECT tr.*, s.probability FROM {trackback_received} tr LEFT JOIN {spam_tracker} s ON tr.trid = s.id WHERE s.source = \'trackback\' AND s.probability >= '. $spam_threshold; } else { $sql = 'SELECT tr.*, s.probability FROM {trackback_received} tr LEFT JOIN {spam_tracker} s ON tr.trid = s.id WHERE tr.status = '. $status; } } else { $sql = 'SELECT tr.* FROM {trackback_received} tr WHERE tr.status = '. $status; } $sql .= tablesort_sql($form['trackbacks']['header']['#value']); $result = pager_query($sql, 50); $form['trackbacks']['status'] = array('#tree' => TRUE); while ($trackback = db_fetch_object($result)) { $form['trackbacks']['status'][$trackback->trid] = array('#type' => 'checkbox'); $form['trackbacks'][$trackback->trid] = array(); $form['trackbacks'][$trackback->trid][] = array('#value' => l($trackback->subject, $trackback->status ? 'node/'. $trackback->nid : 'admin/content/trackback/edit/'. $trackback->trid, array('title' => truncate_utf8($trackback->excerpt, 128)), NULL, $trackback->status ? 'trackback-'. $trackback->trid : NULL) .' '. theme('mark', node_mark($trackback->nid, $trackback->created))); $form['trackbacks'][$trackback->trid][] = array('#value' => drupal_substr($trackback->name, 0, 15) .'...'); $form['trackbacks'][$trackback->trid][] = array('#value' => $trackback->site); if ($spam_module_exists) { if ($type == 'spam') { $form['trackbacks'][$trackback->trid][] = array('#value' => $trackback->status != 0 ? t('Published') : t('Not published')); } else { $form['trackbacks'][$trackback->trid][] = array('#value' => $trackback->probability >= $spam_threshold ? t('Spam') : t('Not Spam')); } } $form['trackbacks'][$trackback->trid][] = array('#value' => format_date($trackback->created, 'small')); $form['trackbacks'][$trackback->trid][] = array('#value' => l(t('edit'), 'admin/content/trackback/edit/'. $trackback->trid)); $form['trackbacks'][$trackback->trid][] = array('#value' => l(t('delete'), 'admin/content/trackback/delete/'. $trackback->trid)); } $form['pager'] = array('#value' => theme('pager', NULL, 50, 0)); return $form; } function trackback_admin_overview_validate($form_id, $form_values) { $form_values['status'] = array_diff((array)$form_values['status'], array(0)); if (count($form_values['status']) == 0) { form_set_error('', t('Please select one or more trackbacks to perform the update on.')); drupal_goto($_GET['q']); } } function trackback_admin_overview_submit($form_id, $form_values) { if (isset($form_values['operations'][$form_values['operation']])) { $function = $form_values['operation']; foreach ($form_values['status'] as $trid => $value) { if ($value) { $function($trid); } } drupal_set_message(t('The update has been performed.')); } } function trackback_admin_table_header($type, $spam_module_exists) { $header = array(); $header[] = theme('table_select_header_cell'); $header[] = array('data' => t('Subject'), 'field' => 'tr.subject'); $header[] = array('data' => t('Author'), 'field' => 'tr.name'); $header[] = array('data' => t('Host'), 'field' => 'tr.site'); if ($spam_module_exists) { if ($type == 'spam') { $header[] = array('data' => t('Status'), 'field' => 'tr.status'); } else { $header[] = array('data' => t('Spam'), 'field' => 's.probability'); } } $header[] = array('data' => t('Time'), 'field' => 'created', 'sort' => 'desc'); $header[] = array('data' => t('Operations') , 'colspan' => '2'); return $header; } function theme_trackback_admin_table($form) { $header = $form['header']['#value']; $rows = array(); foreach (element_children($form['status']) as $key) { $row = array(drupal_render($form['status'][$key])); foreach (element_children($form[$key]) as $column_key) { $row[] = drupal_render($form[$key][$column_key]); } $rows[] = $row; } if (count($rows) == 0) { $rows[] = array(array('data' => t('No trackbacks available.'), 'colspan' => count($header))); } return theme('table', $header, $rows); } function _trackback_send($node) { $urls = array(); if (!empty($node->trackback_urls)) { foreach (explode("\n", $node->trackback_urls) as $url) { if ($url = trim($url)) { $urls[$url] = TRUE; } } } if ($node->status && variable_get('trackback_auto_detection_enabled', 0) == 1) { $urls += trackback_urls_via_nodebody($node); } $retry = array(); if (isset($node->trackback_urls_to_retry)) { $retry = array_diff($node->trackback_urls_to_retry, array(0)); } _trackback_ping($node, $urls, $retry); } function trackback_urls_via_nodebody($node) { $trackback_urls = array(); // First, grab anything that looks like a url from the body of the node. $node = node_build_content($node); $content = drupal_render($node->content); $pattern = '((?:http|https)://[a-z0-9;/?:@&=+#$,_.!~*()%-]+)'; if (variable_get('trackback_link_only', 0)) { $content = strip_tags($content, ''); // remove comment. $pattern = ']*href\s*=\s*(?:"|\')'. $pattern; } if (preg_match_all('`'. $pattern .'`i', $content, $parsed_urls)) { $parsed_urls = array_unique($parsed_urls[1]); foreach ($parsed_urls as $url) { // Now, send http HEAD requests so we can see if the content type is something that *might* contain autodetection text. // In other words, check if Content-Type of each URL is text based rather than digital. $url = html_entity_decode($url, ENT_QUOTES); if (_trackback_url_parsable_content($url)) { //Finally, download each page, scan each, and compile a list of all the trackback URLs listed in the first RDF of each scanned page. $reply = drupal_http_request($url); if (empty($reply->error)) { $url = preg_replace('/.*.*/s', '\1', $reply->data); if (_trackback_valid_url($url)) { $trackback_urls[$url] = FALSE; } } } } } return $trackback_urls; } // Since autodetection might encounter a link to a media file, we first want to make a // simple 'HEAD' HTTP request instead of an actual GET. This results in having to make // an extra drupal_http_request() later for an actual GET, but it is worth it considering // the strong likelihood that auto-detection may encounter a URL that links to a media file. function _trackback_url_parsable_content($url) { global $base_url; if (!strstr($url, $base_url)) { $http_reply = drupal_http_request($url, array(), 'HEAD'); $content_type = $http_reply->headers['Content-Type']; return (substr_count($content_type, 'text/html') || substr_count($content_type, 'application/xhtml+xml') || substr_count($content_type, 'application/xml') || substr_count($content_type, 'text/xml')); } } function trackback_cron() { // query for all nodes where $result = db_query('SELECT t.nid, n.status FROM {trackback_node} t INNER JOIN {node} n ON t.nid = n.nid WHERE t.awaiting_cron = 1'); while ($node = db_fetch_object($result)) { // First things first, let's unset the 'awaiting_cron' bit in the {trackback_node} table. db_query('UPDATE {trackback_node} SET awaiting_cron = 0 WHERE nid = %d', $node->nid); if ($node->status) { $node = node_load($node->nid); _trackback_ping($node, trackback_urls_via_nodebody($node)); } } if (variable_get('trackback_auto_detection_enabled', 0)) { $nodepath = '^node/[0-9][0-9]*$'; $webpage = '^https*://'; $notmypage = '^https*://'.$_SERVER['SERVER_NAME']; $earliest = variable_get('cron_last',0); $visits = db_query("SELECT a.path, a.url, a.timestamp, a.hostname FROM {accesslog} a WHERE a.path REGEXP '%s' AND a.url REGEXP '%s' AND a.url NOT REGEXP '%s' AND a.timestamp >= %d",$nodepath,$webpage,$notmypage,$earliest); while ($visit = db_fetch_object($visits)) { if (!_trackback_exists(substr($visit->path,5), $visit->url)) { $visitor = drupal_http_request($visit->url); if (stristr($visitor->data, $GLOBALS['base_url'] .'/') == FALSE) { $visitor->error = t('The sender page does not refer to recipient site.'); } else { $feeds = _trackback_discover_feed($visitor->headers, $visitor->data, $visit->url); $link = $GLOBALS['base_url'] .'/'; $trackback_found = FALSE; foreach($feeds as $key => $feed) { if ($feed["type"] == "atom" && $feed["content"] == "main") { $trackback_found = _trackback_feed_search($feed["url"], $link, $visit->path); if ($trackback_found) break; } } if ($trackback_found == FALSE ) { foreach($feeds as $key => $feed) { if ($feed["type"] == "rss" && $feed["content"] == "main") { $trackback_found = _trackback_feed_search($feed["url"], $link, $visit->path); if ($trackback_found) break; } } } if ($trackback_found == FALSE ) { foreach($feeds as $key => $feed) { if ($feed["type"] == "atom" && $feed["content"] == "comments") { $trackback_found = _trackback_feed_search($feed["url"], $link, $visit->path); if ($trackback_found) break; } } } if ($trackback_found == FALSE ) { foreach($feeds as $key => $feed) { if ($feed["type"] == "rss" && $feed["content"] == "comments") { $trackback_found = _trackback_feed_search($feed["url"], $link, $visit->path); if ($trackback_found) break; } } } if (!$trackback_found == FALSE) { $trackback_found['trid'] = db_next_id('{trackback_received}_trid'); $trackback_found['site'] = $visit->hostname; $trackback_found['status'] = (variable_get('trackback_moderation', 0) == 0) ? 1 : 0; db_query("INSERT INTO {trackback_received} (trid, nid, created, site, name, subject, url, excerpt, status) VALUES (%d, %d, %d, '%s', '%s', '%s', '%s', '%s', %d)", $trackback_found['trid'], substr($visit->path,5), $trackback_found['created'], $trackback_found['site'], $trackback_found['name'], $trackback_found['subject'], $trackback_found['url'], $trackback_found['excerpt'], $trackback_found['status']); watchdog('trackback', t('Added trackback %subject.', array('%subject' => $trackback_found['subject'])), WATCHDOG_NOTICE, l(t('view trackback'), 'node/'. substr($visit->path,5), NULL, NULL, 'trackback-'. $trackback_found['trid'])); } } } } } /* When cron runs, ** For each row in the access_log table that accesses a Drupal node and has a referrer URL since the last time cron ran ** Try to fetch an RSS type feed url from the referrer site. ** If the site doesn't provide a feed url, then it's likely it's not a blog or news type site and we can stop processing. ** If it does provide a feed url, fetch the feed and parse it. ** For each article in that feed check whether it contains a reference to the Drupal node referred to in the access_log record. ** If it does not contain a reference to said Drupal node we can stop processing. ** If it does, extract an excerpt and title from the article in the feed and save the whole lot into the trackbacks_received table. It should then appear under the node on my site when it is viewed. */ } function _trackback_discover_feed($headers, $data, $url) { /* ** Shamelessly hacked from the gFeed phpClass gifted to the world by Cesar Rodas at http://www.phpclasses.org/browse/package/4037.html */ $feed_url = array(); if ( !isset($headers) || !is_array($headers)) return; /* gHttp is not used */ /* ** This archive is a text/xml file, let pass it ** to analize later! */ if ( strpos(strtolower($headers['Content-Type']),'text/xml') !== false) { $feed_url[] = array("url" => $url, "type" => "xml", "content" => "unknown"); return $feed_url; } /* ** The given page is not a RSS, download the hole page ** and search there the RSS link */ /* $response=''; ** while ($c = fread($this->fp, 1024)) ** $response .= $c; */ /* search the RSS */ $return = false; $pattern = "//i"; preg_match_all($pattern,$data,$result); if (! is_array($result) ) return false; foreach($result[0] as $info) { preg_match("/type\s*=\s*['|\"|\s*](.*?)['|\"|>]/i",$info,$type); preg_match("/rel\s*=\s*['|\"|\s*](.*?)['|\"|>]/i",$info,$rel); /* ** "rel" property must be 'alternate' if exists ** "type" must be 'application/rss+xml' */ if ( (!isset($rel[1]) || strtolower($rel[1]) == 'alternate') && (strtolower($type[1]) == 'application/rss+xml' || strtolower($type[1]) == 'application/atom+xml') ) { /* The RSS address is found! */ preg_match("/href\s*=\s*['|\"|\s*](.*?)['|\"|>]/i",$info,$href); $comment = stripos ($info, "comment"); $feed_url[] = array("url" => $href[1], "type" => (strtolower($type[1]) == 'application/rss+xml') ? "rss" : ((strtolower($type[1]) == 'application/atom+xml') ? "atom" : "unknown"), "content" => (($comment == FALSE) ? "main" : "comment")); /* fclose($this->fp); close this file $this->fp = fopen($href[1], 'r'); open the RSS file $return = $this->fp === false ? false : true; Could open? $return = true; break; why search more? */ } } return $feed_url; } function _trackback_exists($nid, $url) { $result = 0 ; $result = db_result(db_query("SELECT count(*) FROM {trackback_received} WHERE nid=%d AND url='%s'", $nid, $url)); return $result; } function _trackback_feed_search($feed_url, $link, $path) { require_once('simplepie.inc'); $feed_data = new SimplePie(); $feed_data->enable_cache(false); $feed_data->timeout = 30; $feed_data->set_feed_url($feed_url); $feed_data->init(); $feed_data->handle_content_type(); // Check if there's any problems loading the feed $feed_error = $feed_data->error; if (isset($feed_error)) { watchdog('aggregator', t('The RSS-feed from %site seems to be broken, due to "%error".', array('%site' => theme('placeholder', $feed_data->get_feed_title()), '%error' => $feed_error)), WATCHDOG_WARNING); drupal_set_message(t('The RSS-feed from %site seems to be broken, because of error "%error".', array('%site' => theme('placeholder', $feed_data->get_feed_title()), '%error' => $feed_error))); return FALSE; } else if ($feed_data->data) { foreach ($feed_data->get_items() as $item) { $feed_item = new StdClass(); $feed_item->permalink = $item->get_permalink(); // unique SHA-1 hash for a feed item $feed_item->iid = $item->get_id(true); $feed_item->title = $item->get_title(); $feed_item->time = strtotime($item->get_date()); $feed_item->content = $item->get_description(); if (strpos($feed_item->content,$link) && ((strpos($feed_item->content, $path)) || (strpos($feed_item->content, drupal_get_path_alias($path))))) { $trackback_name = $feed_data->get_title(); $trackback_subject = $feed_item->title; $trackback_url = $feed_item->permalink; $trackback_time = $feed_item->time; $excerpt = strip_tags($feed_item->content); $trackback_excerpt = strlen($excerpt) > 255 ? truncate_utf8($excerpt, 252) .'...' : $excerpt; $trackback_found = array('created'=> $trackback_time, 'name'=> $trackback_name, 'subject' => $trackback_subject, 'url'=> $trackback_url, 'excerpt'=> $trackback_excerpt); return $trackback_found; } $trackback_found = FALSE; } return $trackback_found; } } // Code that adds configurability for trackback features. function _trackback_node_type($type) { static $types = array('story', 'forum', 'blog'); return variable_get('trackback_'. $type, in_array($type, $types) ? 1 : 0); } function _trackback_ping($node, $urls, $force = array()) { if ($urls) { $result = db_query('SELECT url FROM {trackback_sent} WHERE nid=%d', $node->nid); while ($sent = db_fetch_object($result)) { unset($urls[$sent->url]); } } $urls += $force; if ($urls) { $node = node_build_content($node, TRUE); $node->teaser = drupal_render($node->content); $node->teaser = preg_replace(array('/teaser); $params = array( 'title' => $node->title, 'excerpt' => truncate_utf8(trim(strip_tags($node->teaser)), 255), 'blog_name' => variable_get('site_name', ''), 'url' => url('node/'. $node->nid, NULL, NULL, TRUE) ); $query = array(); foreach ($params as $key => $value) { $query[] = $key .'='. urlencode($value); } $query = implode('&', $query); foreach ($urls as $url => $type) { $reply = drupal_http_request($url, array('Content-Type' => 'application/x-www-form-urlencoded; charset=utf-8'), 'POST', $query); $succ = 0; if (empty($reply->error) && preg_match('|([0-9]+)|', $reply->data, $match)) { $succ = $match[1] ? 0 : 1; } db_lock_table('trackback_sent'); db_query("UPDATE {trackback_sent} SET successful=%d WHERE nid=%d AND url='%s'", $succ, $node->nid, $url); if (!db_affected_rows()) { db_query("INSERT INTO {trackback_sent} (nid, url, successful) VALUES (%d, '%s', %d)", $node->nid, $url, $succ); } db_unlock_tables(); } } } function trackback_configure() { $form = array(); $form['trackback_auto_detection_enabled'] = array( '#type' => 'radios', '#title' => t('Auto-detection'), '#default_value' => variable_get('trackback_auto_detection_enabled', 0), '#options' => array(t('Disabled'), t('Enabled'), t('Run auto-detection on cron')), '#description' => t('If auto-detection is enabled, each URL in any posted content (whether in textile, link, or plain-text form) will be checked for a trackback URL upon submission. For each URL in the body of the posted content, trackback will check to see if that URL accepts trackbacks from other sites. If a URL accepts trackbacks, trackback will ping the trackback URL found on that page if one has been posted at that URL.
*note: This has the potential to take a very long time depending on the amount of links you have in your posts. Using the \'Run auto-detection on cron\' option delays the most time consuming part of the process to when cron is run on the site. This speeds perfomance when editing and creating content, but delays trackbacks until cron is run.') ); $form['trackback_link_only'] = array( '#type' => 'checkbox', '#title' => t('Link only'), '#default_value' => variable_get('trackback_link_only', 0), '#description' => t('If checked, auto-detection will check link only.') ); $form['trackback_moderation'] = array( '#type' => 'radios', '#title' => t('Trackback moderation'), '#default_value' => variable_get('trackback_moderation', 0), '#options' => array(t('Disabled'), t('Enabled')), '#description' => t('Enabling moderation forces every received trackback to be approved before it will appear on your site. The moderation queue can then be viewed on the !linked_page.', array('!linked_page' => l(t('trackback administration page'), 'admin/content/trackback/list/approval'))) ); $form['trackback_reject_oneway'] = array( '#type' => 'radios', '#title' => t('Reject one-way trackbacks'), '#default_value' => variable_get('trackback_reject_oneway', 0), '#options' => array(t('Disabled'), t('Enabled')), '#description' => t('If enabled, trackbacks that the sender page does not refer to your site will be rejected.') ); return system_settings_form($form); } function trackback_spam($op, $a2, $a3, $a4) { $return = array(); switch ($op) { case 'filter_settings': $return['group']['trackback_spam_filter'] = array( '#type' => 'checkbox', '#title' => t('Filter trackbacks'), '#default_value' => variable_get('trackback_spam_filter', 1), '#description' => t('Enable this option to filter new trackbacks as they are posted, determining whether or not they are spam.') ); break; case 'page': if (arg(1) == 'trackback') { $trackback = trackback_load($a2); $return = array( 'old' => spam_load('trackback', $a2), 'header' => $trackback->subject .' '. $trackback->url, 'body' => $trackback->excerpt, 'goto' => 'node/'. $trackback->nid, 'goto_fragment' => 'trackbacks' ); } break; } return $return; } function trackback_admin_delete($trid = 0) { if ($tb = trackback_load($trid)) { return drupal_get_form('_trackback_admin_delete', $tb); } drupal_not_found(); } function _trackback_admin_delete($tb) { return confirm_form(array('trackback' => array('#type' => 'value', '#value' => $tb)), t('Are you sure you want to delete the trackback %title?', array('%title' => $tb->subject)), 'node/'. $tb->nid, t('This action cannot be undone.'), t('Delete'), t('Cancel')); } function _trackback_admin_delete_submit($form_id, $form_values) { $tb = $form_values['trackback']; trackback_delete($tb); return array('node/'. $tb->nid, NULL, 'trackbacks'); } function trackback_admin_edit($trid = 0) { if ($tb = trackback_load($trid)) { return drupal_get_form('_trackback_admin_edit', $tb); } drupal_not_found(); } function _trackback_admin_edit($tb) { $node = node_build_content(node_load($tb->nid), TRUE); $node->teaser = drupal_render($node->content); $form = array('trackback' => array('#type' => 'value', '#value' => $tb)); $form['preview'] = array( '#prefix' => '
', '#value' => theme('trackback', $tb, module_invoke_all('link', 'trackback', $tb, TRUE)), '#suffix' => '
' ); $form['status'] = array( '#type' => 'radios', '#title' => t('Status'), '#default_value' => $tb->status, '#options' => array(1 => t('Published'), 0 => t('Not published')) ); $form['op'] = array('#type' => 'submit', '#value' => t('Submit')); $form['node'] = array( '#value' => theme('box', t('This trackback is in response to: '), theme('node', $node, TRUE, FALSE)) ); return $form; } function _trackback_admin_edit_submit($form_id, $form_values) { $status = $form_values['status']; $tb = $form_values['trackback']; db_query("UPDATE {trackback_received} SET status = %d WHERE trid = %d", $status, $tb->trid); if ($status != $tb->status) { drupal_set_message($status ? t('The trackback is now published.') : t('The trackback was un-published')); } return 'admin/content/trackback'; } function trackback_spam_link($trackback) { $output = array(); if (!variable_get('trackback_spam_filter', 1)) { return $output; } $p = db_fetch_object(db_query("SELECT probability FROM {spam_tracker} WHERE id = %d AND source = 'trackback'", $trackback->trid)); $spam = array( 'href' => 'spam/trackback/'. $trackback->trid .'/spam', 'title' => t('mark as spam') ); $notspam = array( 'href' => 'spam/trackback/'. $trackback->trid .'/notspam', 'title' => t('mark as not spam') ); $access = user_access('access spam'); $admin = user_access('administer spam'); if (variable_get('spam_display_probability', 0)) { if (variable_get('spam_log_level', SPAM_LOG)) { $prob = l($p->probability, 'admin/content/spam/logs/trackback/'. $trackback->trid); } else { $prob = $p->probability; } $prob = ' ('. $prob .')'; } if (!$p->probability && $admin) { $output['spam-spam'] = $spam; $output['spam-notspam'] = $notspam; } else if ($p->probability < variable_get('spam_threshold', 80)) { if ($access) { $output['spam-probability'] = array('title' => t('not spam') . $prob, 'html' => TRUE); } if ($admin) { $output['spam-spam'] = $spam; } } else { if ($access) { $output['spam-probability'] = array('title' => t('spam') . $prob, 'html' => TRUE); } if ($admin) { $output['spam-notspam'] = $notspam; } } return $output; } /** * Implementation of hook_block(). */ function trackback_block($op = 'list', $delta = 0, $edit = array()) { switch ($op) { case 'list': $blocks[0]['info'] = t('Recent trackbacks'); return $blocks; case 'configure': $form['trackback_display_number'] = array( '#type' => 'select', '#title' => t('Number of trackbacks to display'), '#default_value' => variable_get('trackback_display_number', 10), '#options' => drupal_map_assoc(array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30)), '#description' => t('How many trackbacks are displayed in the recent trackbacks block') ); return $form; case 'save': variable_set('trackback_display_number', $edit['trackback_display_number']); break; case 'view': $result = db_query_range('SELECT * FROM {trackback_received} WHERE status=1 ORDER BY created DESC', 0, variable_get('trackback_display_number', 10)); $items = array(); while ($tb = db_fetch_object($result)) { $items[] = l(truncate_utf8($tb->subject, 27), 'node/'. $tb->nid, NULL, NULL, 'trackback-'. $tb->trid) .'
'. t('%time ago', array('%time' => format_interval(time() - $tb->created))); } $block['subject'] = t('Recent trackbacks'); $block['content'] = theme('item_list', $items); return $block; } } function trackback_delete($trackback) { db_query('DELETE FROM {trackback_received} WHERE trid=%d', $trackback->trid); trackback_invoke_trackback($trackback, 'delete'); watchdog('trackback', t('Deleted trackback %subject. The trackback was posted to !link.', array('%subject' => $trackback->subject, '!link' => l(url('node/'. $trackback->nid, NULL, NULL, TRUE), 'node/'. $trackback->nid)))); } function trackback_load($trid) { return db_fetch_object(db_query('SELECT * FROM {trackback_received} WHERE trid=%d', $trid)); } function _trackback_wd_subject($tb, $key = '!subject') { return array($key => l($tb->subject, 'admin/content/trackback/edit/'. $tb->trid)); } function _trackback_wd_link($tb) { if ($tb->status) { return l(t('view trackback'), 'node/'. $tb->nid, NULL, NULL, 'trackback-'. $tb->trid); } return l(t('view trackback'), 'admin/content/trackback/edit/'. $tb->trid); } /** spam module support functions **/ function spam_publish_trackback($trid) { $trackback = trackback_load($trid); db_query('UPDATE {trackback_received} SET status = 1 WHERE trid = %d', $trackback->trid); cache_clear_all(); watchdog('spam', t('Spam: published trackback !subject', _trackback_wd_subject($trackback)), WATCHDOG_NOTICE, _trackback_wd_link($trackback)); if (module_exists('spam')) { spam_log(SPAM_LOG, t('spam_publish_trackback: published trackback "%subject".', array('%subject' => $trackback->subject)), 'trackback', $trackback->trid); } } function spam_unpublish_trackback($trid) { $trackback = trackback_load($trid); db_query('UPDATE {trackback_received} SET status = 0 WHERE trid = %d', $trackback->trid); cache_clear_all(); watchdog('spam', t('Spam: unpublished trackback !subject', _trackback_wd_subject($trackback)), WATCHDOG_NOTICE, _trackback_wd_link($trackback)); if (module_exists('spam')) { spam_log(SPAM_LOG, t('spam_unpublish_trackback: unpublished trackback "%subject".', array('%subject' => $trackback->subject)), 'trackback', $trackback->trid); } } function spam_delete_trackback($trid) { if ($trackback = trackback_load($trid)) { trackback_delete($trackback); } } function _trackback_spam_modify($tb, $spam) { $tokens = spam_tokenize($tb->subject .' '. $tb->url, 'header*'); $tokens = array_merge($tokens, spam_tokenize($tb->excerpt)); spam_tokens_unsave($tokens, $spam); spam_tokens_save($tokens, $spam); $prob = $spam ? 99 : 1; db_query("UPDATE {spam_tracker} SET probability=%d, timestamp=%d WHERE source='trackback' AND id=%d", $prob, time(), $tb->trid); spam_default_actions('trackback', $tb->trid, $tb->subject, $tb->excerpt, $prob, NULL, FALSE); } function spam_notspam_trackback($trid) { $trackback = trackback_load($trid); _trackback_spam_modify($trackback, 0); watchdog('spam', t('Spam: marked trackback !subject as not spam.', _trackback_wd_subject($trackback)), WATCHDOG_NOTICE, _trackback_wd_link($trackback)); spam_log(SPAM_LOG, t('trackback manually marked as not spam'), 'trackback', $trackback->trid); } function spam_spam_trackback($trid) { $trackback = trackback_load($trid); _trackback_spam_modify($trackback, 1); watchdog('spam', t('Spam: marked trackback !subject as spam.', _trackback_wd_subject($trackback)), WATCHDOG_NOTICE, _trackback_wd_link($trackback)); spam_log(SPAM_LOG, t('trackback manually marked as spam'), 'trackback', $trackback->trid); } /** end of spam module support functions */