View source
<?php
include_once 'tweet_feed.field_info.inc';
define('QUERY_SEARCH', 1);
define('QUERY_TIMELINE', 2);
define('QUERY_LIST', 3);
function tweet_feed_views_api($module = NULL, $api = NULL) {
return array(
"api" => "3.0",
);
}
function tweet_feed_menu() {
$items = array();
$items['admin/config/services/tweet_feed'] = array(
'title' => t('Tweet Feed'),
'description' => t('The settings for the Tweet Feed module.'),
'page callback' => 'tweet_feed_menu_page',
'access callback' => 'user_access',
'access arguments' => array(
'administer tweet feed settings',
),
'file' => 'tweet_feed_admin.inc',
'type' => MENU_NORMAL_ITEM,
'weight' => 1,
);
$items['admin/config/services/tweet_feed/settings'] = array(
'title' => t('Settings'),
'description' => t('The settings for the Tweet Feed module.'),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'tweet_feed_settings_form',
),
'access callback' => 'user_access',
'access arguments' => array(
'administer tweet feed settings',
),
'file' => 'tweet_feed_admin.inc',
'type' => MENU_NORMAL_ITEM,
'weight' => 2,
);
$items['admin/config/services/tweet_feed/accounts'] = array(
'title' => t('Twitter API Accounts'),
'description' => t('List of available API accounts used to collect feeds.'),
'page callback' => 'tweet_feed_accounts_table',
'access callback' => 'user_access',
'access arguments' => array(
'administer tweet feed settings',
),
'file' => 'tweet_feed_admin.inc',
'type' => MENU_NORMAL_ITEM,
'weight' => 3,
);
$items['admin/config/services/tweet_feed/feeds'] = array(
'title' => t('Twitter Feeds'),
'description' => t('List of configured feeds to aggregate.'),
'page callback' => 'tweet_feed_feeds_table',
'access callback' => 'user_access',
'access arguments' => array(
'administer tweet feed settings',
),
'file' => 'tweet_feed_admin.inc',
'type' => MENU_NORMAL_ITEM,
'weight' => 4,
);
$items['admin/config/services/tweet_feed/import-form'] = array(
'title' => t('Import Config'),
'description' => t('Import feed and account settings from JSON'),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'tweet_feed_import_form',
),
'access callback' => 'user_access',
'access arguments' => array(
'administer tweet feed settings',
),
'file' => 'tweet_feed_admin.inc',
'type' => MENU_NORMAL_ITEM,
'weight' => 5,
);
$items['admin/config/services/tweet_feed/export'] = array(
'title' => t('Export Config'),
'description' => t('Export all current feed and accounts settings'),
'page callback' => 'tweet_feed_export',
'access callback' => 'user_access',
'access arguments' => array(
'administer tweet feed settings',
),
'file' => 'tweet_feed_admin.inc',
'type' => MENU_NORMAL_ITEM,
'weight' => 6,
);
$items['admin/config/services/tweet_feed/feeds/run/%tweet_feed_id'] = array(
'title' => t('Import Feed'),
'description' => t('Import tweets from a specific feed.'),
'page callback' => 'tweet_feed_run_import',
'page arguments' => array(
6,
),
'access callback' => 'user_access',
'access arguments' => array(
'administer tweet feed settings',
),
'file' => 'tweet_feed_admin.inc',
'type' => MENU_CALLBACK,
);
$items['admin/config/services/tweet_feed/accounts/add'] = array(
'title' => t('Add Account'),
'description' => t('Add a new Twitter API account.'),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'tweet_feed_account_form',
),
'access callback' => 'user_access',
'access arguments' => array(
'administer tweet feed settings',
),
'file' => 'tweet_feed_admin.inc',
'type' => MENU_LOCAL_ACTION,
);
$items['admin/config/services/tweet_feed/accounts/edit/%tweet_feed_id'] = array(
'title' => t('Add Account'),
'description' => t('Add a new Twitter API account.'),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'tweet_feed_account_form',
6,
),
'access callback' => 'user_access',
'access arguments' => array(
'administer tweet feed settings',
),
'file' => 'tweet_feed_admin.inc',
'type' => MENU_CALLBACK,
);
$items['admin/config/services/tweet_feed/accounts/delete/%tweet_feed_id'] = array(
'title' => t('Add Account'),
'description' => t('Delete Twitter API account.'),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'tweet_feed_delete_account_form',
6,
),
'access callback' => 'user_access',
'access arguments' => array(
'administer tweet feed settings',
),
'file' => 'tweet_feed_admin.inc',
'type' => MENU_CALLBACK,
);
$items['admin/config/services/tweet_feed/feeds/add'] = array(
'title' => t('Add Feed'),
'description' => t('Add a new feed to the list of aggregated feeds.'),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'tweet_feed_feeds_form',
),
'access callback' => 'user_access',
'access arguments' => array(
'administer tweet feed settings',
),
'file' => 'tweet_feed_admin.inc',
'type' => MENU_LOCAL_ACTION,
);
$items['admin/config/services/tweet_feed/feeds/edit/%tweet_feed_id'] = array(
'title' => t('Edit Feed'),
'description' => t('Edit one of the listed feeds.'),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'tweet_feed_feeds_form',
6,
),
'access callback' => 'user_access',
'access arguments' => array(
'administer tweet feed settings',
),
'file' => 'tweet_feed_admin.inc',
'type' => MENU_CALLBACK,
);
$items['admin/config/services/tweet_feed/feeds/delete/%tweet_feed_id'] = array(
'title' => t('Delete Feed'),
'description' => t('Delete a feed from the list of aggregated feeds.'),
'page callback' => 'drupal_get_form',
'page arguments' => array(
'tweet_feed_delete_feed_form',
6,
),
'access callback' => 'user_access',
'access arguments' => array(
'administer tweet feed settings',
),
'file' => 'tweet_feed_admin.inc',
'type' => MENU_CALLBACK,
);
return $items;
}
function tweet_feed_id_load($value) {
return intval($value) ? $value : FALSE;
}
function tweet_feed_permission() {
return array(
'administer tweet feed settings' => array(
'title' => t('Access Tweet Feed Settings'),
'description' => t('Allow the changing of OAuth tokens and search queries.'),
),
);
}
function tweet_feed_cron() {
$tweet_feed_disable_cron = variable_get('tweet_feed_disable_cron', 0);
if (!empty($tweet_feed_disable_cron)) {
return TRUE;
}
$feed = array();
$result = db_select('tweet_feeds', 'f')
->fields('f', array(
'fid',
))
->orderBy('fid', 'ASC')
->execute();
while ($fdata = $result
->fetchObject()) {
$feed[] = $fdata->fid;
}
if (empty($feed)) {
return FALSE;
}
$last_fid = variable_get('tweet_feed_cron_last_feed', NULL);
if ($last_fid === NULL) {
$current_fid = $feed[0];
}
else {
$key = array_search($last_fid, $feed) + 1;
if (!isset($feed[$key])) {
$current_fid = $feed[0];
}
else {
$current_fid = $feed[$key];
}
}
variable_set('tweet_feed_pull_data_from_feed', $current_fid);
variable_set('tweet_feed_cron_last_feed', $current_fid);
$tweets = tweet_feed_pull_data_from_feed($current_fid, TRUE);
if (!in_array($current_fid, $feed)) {
variable_del('tweet_feed_pull_data_from_feed');
variable_del('tweet_feed_cron_last_feed');
return FALSE;
}
if (!is_array($tweets) || count($tweets) < 1) {
return FALSE;
}
$queue = drupalQueue::get('tweet_feed_queue');
$queue_size = $queue
->numberOfItems();
if ($queue_size < 1) {
foreach ($tweets as $key => $tweet) {
$update_node_id = 0;
$hash = NULL;
$result = db_select('tweet_hashes', 'h')
->fields('h', array(
'nid',
'tid',
'hash',
))
->condition('h.tid', $tweet->id_str)
->execute();
if ($result
->rowCount() > 0) {
$tdata = $result
->fetchObject();
$hash = md5(serialize($tweet->text));
if ($hash == $tdata->hash) {
continue;
}
else {
$update_node_id = $tdata->nid;
}
}
$queue
->createItem(array(
'tweet' => $tweet,
'feed' => $feed,
'update_node_id' => $update_node_id,
'hash' => $hash,
));
}
$queue_size = $queue
->numberOfItems();
}
for ($i = 0; $i < $queue_size; $i++) {
if ($item = $queue
->claimItem($i)) {
$feed_object = tweet_feed_get_feed_object($item->data['feed'][0]);
if (!empty($feed_object)) {
tweet_feed_save_tweet($item->data['tweet'], $feed_object, $item->data['update_node_id'], $item->data['hash']);
}
$queue
->releaseItem($item);
$queue
->deleteItem($item);
}
}
}
function tweet_feed_process_feed($fid = NULL) {
if ($fid == NULL) {
$result = db_select('tweet_feeds', 'f')
->fields('f', array(
'feed_name',
'fid',
))
->orderBy('feed_name', 'ASC')
->execute();
}
else {
$result = db_select('tweet_feeds', 'f')
->fields('f', array(
'feed_name',
'fid',
))
->condition('f.fid', $fid)
->orderBy('f.feed_name', 'ASC')
->execute();
}
while ($fdata = $result
->fetchObject()) {
tweet_feed_set_message('Processing Feed: ' . $fdata->feed_name, 'ok', FALSE);
$tweets = tweet_feed_pull_data_from_feed($fdata->fid, FALSE);
}
}
function tweet_feed_get_feed_object($fid) {
if (empty($fid)) {
return FALSE;
}
$query = db_select('tweet_feeds', 'f');
$query
->join('tweet_accounts', 'a', 'a.aid = f.aid');
$query
->fields('f', array(
'fid',
'query_type',
'timeline_id',
'search_term',
'list_name',
'pull_count',
'clear_prior',
'new_window',
'hash_taxonomy',
));
$query
->fields('a', array(
'consumer_key',
'consumer_secret',
'oauth_token',
'oauth_token_secret',
));
$query
->condition('f.fid', $fid);
$result = $query
->execute();
if ($result
->rowCount() > 0) {
$feed = $result
->fetchObject();
return $feed;
}
else {
return FALSE;
}
}
function tweet_feed_pull_data_from_feed($fid, $web_interface = FALSE) {
if (empty($fid)) {
return FALSE;
}
$feed = tweet_feed_get_feed_object($fid);
if (empty($feed)) {
return FALSE;
}
module_load_include('inc', 'tweet_feed', 'inc/twitter-oauth');
if (!empty($feed->clear_prior)) {
tweet_feed_set_message('Clearing Previous Tweets', 'ok', $web_interface);
$query = new EntityFieldQuery();
$result = $query
->entityCondition('entity_type', 'node')
->entityCondition('bundle', 'twitter_tweet_feed')
->fieldCondition('field_tweet_feed_id', 'value', $fid, '=')
->execute();
if (isset($result['node'])) {
foreach ($result['node'] as $nid => $node) {
node_delete($nid);
}
}
tweet_feed_set_message('All previous tweets for this feed are deleted.', 'ok', $web_interface);
}
$con = new TwitterOAuth($feed->consumer_key, $feed->consumer_secret, $feed->oauth_token, $feed->oauth_token_secret);
$number_to_get = $feed->pull_count * 100;
$run_count = 0;
$lowest_id = -1;
$tweets = array();
$params = $feed->query_type == QUERY_TIMELINE ? array(
'user_id' => $feed->timeline_id,
'count' => 100,
'tweet_mode' => 'extended',
) : array(
'q' => $feed->search_term,
'count' => 100,
'tweet_mode' => 'extended',
);
while (count($tweets) < $number_to_get && $lowest_id != 0) {
tweet_feed_set_message('Tweets Imported: ' . count($tweets) . ', Total To Import: ' . $number_to_get, 'ok', $web_interface);
if (!empty($tdata->search_metadata->next_results)) {
$next = substr($tdata->search_metadata->next_results, 1);
$parts = explode('&', $next);
foreach ($parts as $part) {
list($key, $value) = explode('=', $part);
if ($key == 'max_id') {
$lowest_id = $value;
}
$params[$key] = $value;
}
}
$data = new stdClass();
switch ($feed->query_type) {
case QUERY_TIMELINE:
if (count($tweets) < 1) {
tweet_feed_set_message('Retrieving Timeline Status Messages For ID: ' . $feed->timeline_id, 'ok', $web_interface);
}
if ($lowest_id > 0) {
$params['max_id'] = $lowest_id;
}
$tdata = json_decode($con
->oAuthRequest('https://api.twitter.com/1.1/statuses/user_timeline.json', 'GET', $params));
break;
case QUERY_LIST:
if (count($tweets) < 1) {
tweet_feed_set_message('Retrieving List Status Messages For List Name: ' . $feed->list_name, 'ok', $web_interface);
}
$params = array(
'slug' => $feed->list_name,
'owner_id' => $feed->timeline_id,
'count' => 100,
);
if ($lowest_id > 0) {
$params['max_id'] = $lowest_id;
}
$tdata = json_decode($con
->oAuthRequest('https://api.twitter.com/1.1/lists/statuses.json', 'GET', $params));
break;
case QUERY_SEARCH:
default:
if (count($tweets) < 1) {
tweet_feed_set_message('Retrieving Status Messages For Search Term: ' . $feed->search_term, 'ok', $web_interface);
}
$tdata = json_decode($con
->oAuthRequest('https://api.twitter.com/1.1/search/tweets.json', 'GET', $params));
if (empty($tdata->search_metadata->next_results)) {
$lowest_id = 0;
}
break;
}
if (!empty($tdata)) {
tweet_feed_set_message('Processing Tweets', 'ok', $web_interface);
if (!empty($tdata->errors)) {
foreach ($tdata->errors as $error) {
tweet_feed_set_message(t('Tweet Feed Fail: ') . $error->message . ': ' . $error->code, 'error', $web_interface);
$lowest_id = 0;
$tweets = array();
if ($web_interface == TRUE) {
drupal_set_message(t('Tweet Feed Fail: ') . $error->message . ': ' . $error->code, 'error');
}
}
}
else {
if ($feed->query_type == QUERY_TIMELINE || $feed->query_type == QUERY_LIST) {
$end_of_the_line = array_pop($tdata);
array_push($tdata, $end_of_the_line);
$lowest_id = $end_of_the_line->id_str;
$tweet_data = $tdata;
}
else {
$tweet_data = $tdata->statuses;
}
if (isset($tweet_data['tweets']) && $tweet_data['tweets'] === FALSE) {
break;
}
$tweet_data = tweet_feed_process_tweets($tweet_data, $feed, $number_to_get, $run_count, $web_interface);
if (count($tweet_data) == 0) {
$lowest_id = 0;
}
else {
$tweets = array_merge($tweets, $tweet_data);
}
}
$run_count++;
}
else {
tweet_feed_set_message('No tweets available for this criteria.', 'ok', $web_interface);
break;
}
}
if ($web_interface == TRUE) {
return $tweets;
}
}
function tweet_feed_process_tweets($tweet_data, $feed, $number_to_get = 0, $run_count = 0, $web_interface = FALSE) {
$tweets = array();
$total_hashi = 0;
foreach ($tweet_data as $key => $tweet) {
$update_node_id = 0;
$result = db_select('tweet_hashes', 'h')
->fields('h', array(
'nid',
'tid',
'hash',
))
->condition('h.tid', $tweet->id_str)
->execute();
if ($result
->rowCount() > 0) {
$tdata = $result
->fetchObject();
$hash = md5(serialize($tweet->full_text));
$update = $hash == $tdata->hash;
continue;
}
if ($web_interface == FALSE) {
tweet_feed_save_tweet($tweet, $feed, $update_node_id, $tdata->hash);
if ($key > 1 && !($key % 20) || $key + 1 == count($tweet_data)) {
tweet_feed_set_message('Loaded ' . ($key + $run_count * 100) . ' out of ' . $number_to_get . ' (' . number_format(($key + $run_count * 100) / $number_to_get * 100) . '%)', 'ok', $web_interface);
}
}
$tweets[] = $tweet;
}
return $tweets;
}
function tweet_feed_process_taxonomy($entities, $taxonomy) {
$terms = array();
foreach ($entities as $entity) {
switch ($taxonomy) {
case 'hashtag_terms':
$taxonomy_name = $entity->text;
break;
case 'user_mention_terms':
$taxonomy_name = $entity->screen_name;
break;
default:
break;
}
$vocabulary = taxonomy_vocabulary_machine_name_load($taxonomy);
if ($vocabulary == FALSE) {
tweet_feed_set_message('The ' . $taxonomy . ' taxonomy vocabulary could not be found. Please uninstall and re-install Tweet Feed', 'fatal', $web_interface);
return FALSE;
}
$result = db_select('taxonomy_term_data', 'td')
->fields('td', array(
'tid',
))
->condition('td.vid', $vocabulary->vid)
->condition('td.name', $taxonomy_name)
->execute();
if ($result
->rowCount() > 0) {
$tid = $result
->fetchField();
}
else {
$term = new stdClass();
$term->vid = $vocabulary->vid;
$term->name = $taxonomy_name;
taxonomy_term_save($term);
$tid = $term->tid;
}
$terms[] = $tid;
}
return $terms;
}
function tweet_feed_format_output($tweet, $new_window = FALSE, $hash_taxonomy = FALSE, $hashtags = array()) {
$target = $new_window == 1 ? '_blank' : '_self';
$tweet = preg_replace('/(((f|ht){1}tp:\\/\\/)[-a-zA-Z0-9@:%_\\+.~#?&\\/\\/=]+)/i', '<a target="' . $target . '" href="\\1">\\1</a>', $tweet);
$tweet = preg_replace('/(((f|ht){1}tps:\\/\\/)[-a-zA-Z0-9@:%_\\+.~#?&\\/\\/=]+)/i', '<a target="' . $target . '" href="\\1">\\1</a>', $tweet);
$tweet = preg_replace('/([[:space:]()[{}])(www.[-a-zA-Z0-9@:%_\\+.~#?&\\/\\/=]+)/i', '\\1<a target="' . $target . '" href="http:\\/\\/\\2">\\2</a>', $tweet);
$tweet = preg_replace('/([_\\.0-9a-z-]+@([0-9a-z][0-9a-z-]+\\.)+[a-z]{2,3})/i', '<a href="mailto:\\1">\\1</a>', $tweet);
$pattern = '/@([A-Za-z0-9_]{1,15})(?![.A-Za-z])/';
$replace = '<a target="' . $target . '" href="http://twitter.com/' . strtolower('\\1') . '">@\\1</a>';
$tweet = preg_replace($pattern, $replace, $tweet);
$tweet = preg_replace('/(^|\\s)#(\\w*+)/u', '\\1<a target="' . $target . '" href="http://twitter.com/search?q=%23\\2">#\\2</a>', $tweet);
return $tweet;
}
function tweet_feed_set_message($message, $type = 'status', $web_interface = FALSE) {
if ($web_interface != FALSE) {
return;
}
global $user;
if (function_exists('drush_print')) {
if ($type != 'error' && $type != 'warning' && $type != 'fatal') {
drush_log($message, 'ok');
}
else {
if ($type == 'fatal') {
drush_set_error($message);
}
else {
drush_log($message, $type);
}
}
}
else {
if ($type != 'drush') {
$type = $type == 'fatal' ? 'error' : $type;
$type = $type == 'ok' ? 'status' : $type;
if ($user->uid == 1) {
drupal_set_message(check_plain($message), $type);
}
}
}
}
function tweet_feed_save_tweet($tweet, $feed, $update_node_id = 0, $hash = NULL, $cron = FALSE) {
$creation_timestamp = strtotime($tweet->created_at);
$hashtags = tweet_feed_process_taxonomy($tweet->entities->hashtags, 'hashtag_terms');
if ($hashtags === FALSE) {
tweet_feed_bail();
}
$user_mentions = tweet_feed_process_taxonomy($tweet->entities->user_mentions, 'user_mention_terms');
if ($user_mentions === FALSE) {
tweet_feed_bail();
}
if (!empty($tweet->retweeted_status)) {
$tweet_text = 'RT @' . $tweet->retweeted_status->user->screen_name . ': ' . $tweet->retweeted_status->full_text;
}
else {
if ($feed->query_type == QUERY_LIST) {
$tweet_text = $tweet->text;
}
else {
$tweet_text = $tweet->full_text;
}
}
$tweet_html = tweet_feed_format_output($tweet_text, $feed->new_window, $feed->hash_taxonomy, $hashtags);
$node = new stdClass();
$node->created = $creation_timestamp;
if ($update_node_id > 0) {
$node->nid = $update_node_id;
node_load($node->nid);
}
$tweet_hash = md5(serialize($tweet_text));
if ($tweet_hash == $hash) {
return NULL;
}
$node->type = 'twitter_tweet_feed';
$node->uid = 1;
$node_type = variable_get('node_options_twitter_tweet_feed', array());
if (empty($node_type)) {
$node_type = [
'status',
];
}
$node->status = in_array('status', $node_type);
$node->comment = 0;
$node->promote = in_array('promote', $node_type);
$node->moderate = 0;
$node->sticky = in_array('sticky', $node_type);
$node->language = LANGUAGE_NONE;
$connection = Database::getConnection();
$utf8mb4_configurable = $connection
->utf8mb4IsConfigurable();
$utf8mb4_active = $connection
->utf8mb4IsActive();
$utf8mb4_supported = $connection
->utf8mb4IsSupported();
$utf8 = $utf8mb4_configurable && $utf8mb4_active && $utf8mb4_supported ? TRUE : FALSE;
if ($utf8 === TRUE) {
$node->title = mb_substr(check_plain($tweet->user->screen_name) . ': ' . check_plain($tweet_text), 0, 255);
$node->field_tweet_contents[$node->language][0] = array(
'value' => $tweet_html,
'format' => 'full_html',
);
$node->field_tweet_author_name[$node->language][0]['value'] = $tweet->user->name;
}
else {
$node->title = mb_substr(check_plain(tweet_feed_filter_iconv_text(tweet_feed_filter_smart_quotes($tweet->user->screen_name)) . ': ' . tweet_feed_filter_iconv_text(tweet_feed_filter_smart_quotes($tweet_text))), 0, 255);
$node->field_tweet_contents[$node->language][0] = array(
'value' => htmlspecialchars_decode(utf8_encode(tweet_feed_filter_smart_quotes($tweet_html))),
'format' => 'full_html',
);
$node->field_tweet_author_name[$node->language][0]['value'] = tweet_feed_filter_iconv_text(tweet_feed_filter_smart_quotes($tweet->user->name));
}
$node->field_tweet_author_verified[$node->language][0]['value'] = (int) $tweet->user->verified;
$node->field_tweet_feed_id[$node->language][0]['value'] = $feed->fid;
if (!empty($tweet->place) && is_object($tweet->place)) {
$bb = json_encode($tweet->place->bounding_box->coordinates[0]);
$node->field_geographic_coordinates[$node->language][0] = array(
'value' => $bb,
'safe_value' => $bb,
);
}
if (!empty($tweet->entities->media) && is_array($tweet->entities->media) && variable_get('tweet_feed_fetch_images', 1)) {
foreach ($tweet->entities->media as $key => $media) {
if (is_object($media)) {
$image = file_get_contents($media->media_url . ':large');
if (!empty($image)) {
tweet_feed_check_path('public://tweet-feed-tweet-images');
$file_temp = file_save_data($image, 'public://tweet-feed-tweet-images/' . $tweet->id_str . '.jpg', FILE_EXISTS_REPLACE);
if (is_object($file_temp)) {
$file = array();
$file[$node->language][0] = array(
'fid' => $file_temp->fid,
'filename' => $file_temp->filename,
'filemime' => $file_temp->filemime,
'uid' => 1,
'uri' => $file_temp->uri,
'status' => 1,
);
$node->field_tweet_linked_images = $file;
}
unset($file);
unset($file_temp);
unset($image);
}
}
}
}
if (!empty($tweet->place->full_name)) {
$node->field_geographic_place[$node->language][0] = array(
'value' => $tweet->place->full_name,
'safe_value' => $tweet->place->full_name,
);
if (!empty($tweet->place->country)) {
$node->field_geographic_place[$node->language][0]['value'] .= ', ' . $tweet->place->country;
$node->field_geographic_place[$node->language][0]['safe_value'] .= ', ' . $tweet->place->country;
}
}
if ($utf8 === TRUE) {
$node->field_tweet_author[$node->language][0] = array(
'value' => $tweet->user->screen_name,
'safe_value' => $tweet->user->screen_name,
);
}
else {
$node->field_tweet_author[$node->language][0] = array(
'value' => tweet_feed_filter_iconv_text(tweet_feed_filter_smart_quotes($tweet->user->screen_name)),
'safe_value' => tweet_feed_filter_iconv_text(tweet_feed_filter_smart_quotes($tweet->user->screen_name)),
);
}
$node->field_tweet_author_id[$node->language][0] = array(
'value' => $tweet->user->id,
'safe_value' => $tweet->user->id,
);
$created_dt = DateTime::createFromFormat('D M d H:i:s O Y', $tweet->created_at);
$node->field_tweet_creation_date[$node->language][0] = array(
'value' => $created_dt
->format('Y-m-d H:i:s'),
'timezone' => 'UTC/GMT',
'timezone_db' => 'UTC/GMT',
'datatype' => 'datetime',
);
$node->field_tweet_id[$node->language][0] = array(
'value' => $tweet->id_str,
'safe_value' => $tweet->id_str,
);
$node->field_twitter_favorite_count[$node->language][0]['value'] = $tweet->favorite_count;
foreach ($hashtags as $hashtag) {
$node->field_twitter_hashtags[$node->language][] = array(
'target_id' => $hashtag,
);
}
$node->field_twitter_retweet_count[$node->language][0]['value'] = $tweet->retweet_count;
$node->field_tweet_source[$node->language][0] = array(
'value' => $tweet->source,
'safe_value' => strip_tags($tweet->source),
);
$node->field_link_to_tweet[$node->language][0]['value'] = 'https://twitter.com/' . $tweet->user->screen_name . '/status/' . $tweet->id_str;
if (!empty($tweet->entities->user_mentions) && is_array($tweet->entities->user_mentions)) {
foreach ($tweet->entities->user_mentions as $key => $mention) {
if ($utf8 == TRUE) {
$node->field_tweet_user_mentions[$node->language][$key] = array(
'tweet_feed_mention_name' => $mention->name,
'tweet_feed_mention_screen_name' => $mention->screen_name,
'tweet_feed_mention_id' => $mention->id,
);
}
else {
$node->field_tweet_user_mentions[$node->language][$key] = array(
'tweet_feed_mention_name' => tweet_feed_filter_iconv_text(tweet_feed_filter_smart_quotes($mention->name)),
'tweet_feed_mention_screen_name' => tweet_feed_filter_iconv_text(tweet_feed_filter_smart_quotes($mention->screen_name)),
'tweet_feed_mention_id' => $mention->id,
);
}
}
foreach ($user_mentions as $mention) {
$node->field_twitter_mentions_in_tweet[$node->language][] = array(
'target_id' => $mention,
);
}
}
$tweet->user->profile_image_url = str_replace('_normal', '', $tweet->user->profile_image_url);
$file = tweet_feed_process_twitter_image($tweet->user->profile_image_url, 'tweet-feed-profile-image', $tweet->id_str);
if ($file !== NULL) {
$node->field_profile_image[$node->language][0] = (array) $file;
}
drupal_alter('tweet_feed_tweet_save', $node, $tweet);
if (empty($node)) {
return;
}
node_save($node);
$nid = $node->nid;
db_delete('tweet_hashes')
->condition('nid', $node->nid)
->execute();
$hash_insert = array(
'tid' => $tweet->id_str,
'nid' => $node->nid,
'hash' => $tweet_hash,
);
db_insert('tweet_hashes')
->fields($hash_insert)
->execute();
unset($node);
if (variable_get('tweet_feed_get_tweeter_profiles', FALSE) == TRUE) {
$user_hash = md5(serialize($tweet->user));
$query = new EntityFieldQuery();
$result = $query
->entityCondition('entity_type', 'node')
->entityCondition('bundle', 'twitter_user_profile')
->fieldCondition('field_twitter_user_id', 'value', $tweet->user->id, '=')
->execute();
if (isset($result['node'])) {
$result = db_select('tweet_user_hashes', 'h')
->fields('h', array(
'nid',
'tuid',
'hash',
))
->condition('h.tuid', $tweet->user->id)
->execute();
if ($result
->rowCount() > 0) {
$tdata = $result
->fetchObject();
if ($user_hash == $tdata->hash) {
return;
}
else {
$update_node_id = $tdata->nid;
}
}
}
$node = new stdClass();
if ($update_node_id > 0) {
$node->nid = $update_node_id;
}
$node->type = 'twitter_user_profile';
$node->uid = 1;
$node->created = $creation_timestamp;
$node->status = 1;
$node->comment = 0;
$node->promote = 0;
$node->moderate = 0;
$node->sticky = 0;
$node->language = LANGUAGE_NONE;
$node->field_twitter_user_id[$node->language][0]['value'] = $tweet->user->id_str;
$node->title = $tweet->user->name;
if ($utf8 == TRUE) {
$node->body[$node->language][0]['value'] = $tweet->user->description;
$node->field_twitter_a_screen_name[$node->language][0]['value'] = $tweet->user->screen_name;
}
else {
$node->body[$node->language][0]['value'] = tweet_feed_filter_iconv_text(tweet_feed_filter_smart_quotes($tweet->user->description));
$node->field_twitter_a_screen_name[$node->language][0]['value'] = tweet_feed_filter_iconv_text(tweet_feed_filter_smart_quotes($tweet->user->screen_name));
}
$node->field_twitter_location[$node->language][0]['value'] = $tweet->user->location;
$node->field_twitter_a_profile_url[$node->language][0]['value'] = $tweet->user->entities->url->urls[0]->url;
$node->field_twitter_profile_url[$node->language][0]['value'] = $tweet->user->entities->url->urls[0]->display_url;
$node->field_twitter_followers[$node->language][0]['value'] = $tweet->user->followers_count;
$node->field_twitter_following[$node->language][0]['value'] = $tweet->user->friends_count;
$node->field_twitter_favorites_count[$node->language][0]['value'] = $tweet->user->favourites_count;
$node->field_twitter_tweet_count[$node->language][0]['value'] = $tweet->user->statuses_count;
$node->field_tweet_author_verified[$node->language][0]['value'] = (int) $tweet->user->verified;
$file = tweet_feed_process_twitter_image($tweet->user->profile_background_image_url, 'tweet-feed-profile-background-image', $tweet->user->id_str);
if ($file !== NULL) {
$node->field_background_image[$node->language][0] = (array) $file;
}
$file = tweet_feed_process_twitter_image($tweet->user->profile_image_url, 'tweet-feed-profile-user-profile-image', $tweet->user->id_str);
if ($file !== NULL) {
$node->field_profile_image[$node->language][0] = (array) $file;
}
$file = tweet_feed_process_twitter_image($tweet->user->profile_banner_url, 'tweet-feed-profile-banner-image', $tweet->user->id_str);
if ($file !== NULL) {
$node->field_banner_image[$node->language][0] = (array) $file;
}
$node->field_background_color[$node->language][0]['value'] = $tweet->user->profile_background_color;
$node->field_profile_text_color[$node->language][0]['value'] = $tweet->user->profile_text_color;
$node->field_link_color[$node->language][0]['value'] = $tweet->user->profile_link_color;
$node->field_sidebar_border_color[$node->language][0]['value'] = $tweet->user->profile_sidebar_border_color;
$node->field_sidebar_fill_color[$node->language][0]['value'] = $tweet->user->profile_sidebar_fill_color;
node_save($node);
db_delete('tweet_user_hashes')
->condition('nid', $node->nid)
->execute();
$hash_insert = array(
'tuid' => $tweet->user->id_str,
'nid' => $node->nid,
'hash' => $user_hash,
);
db_insert('tweet_user_hashes')
->fields($hash_insert)
->execute();
}
}
function tweet_feed_filter_iconv_text($text, $replace = '--') {
$altered = preg_replace('/[\\x00-\\x08\\x10\\x0B\\x0C\\x0E-\\x19\\x7F]' . '|[\\x00-\\x7F][\\x80-\\xBF]+' . '|([\\xC0\\xC1]|[\\xF0-\\xFF])[\\x80-\\xBF]*' . '|[\\xC2-\\xDF]((?![\\x80-\\xBF])|[\\x80-\\xBF]{2,})' . '|[\\xE0-\\xEF](([\\x80-\\xBF](?![\\x80-\\xBF]))|(?![\\x80-\\xBF]{2})|[\\x80-\\xBF]{3,})/S', '--', $text);
return preg_replace('/\\xE0[\\x80-\\x9F][\\x80-\\xBF]' . '|\\xED[\\xA0-\\xBF][\\x80-\\xBF]/S', $replace, $altered);
}
function tweet_feed_filter_smart_quotes($text) {
$search = array(
chr(0xe2) . chr(0x80) . chr(0x98),
chr(0xe2) . chr(0x80) . chr(0x99),
chr(0xe2) . chr(0x80) . chr(0x9c),
chr(0xe2) . chr(0x80) . chr(0x9d),
chr(0xe2) . chr(0x80) . chr(0x93),
chr(0xe2) . chr(0x80) . chr(0x94),
);
$ascii_replace = array(
"'",
"'",
'"',
'"',
'-',
'—',
);
return str_replace($search, $ascii_replace, $text);
}
function tweet_feed_node_delete($node) {
switch ($node->type) {
case 'twitter_tweet_feed':
db_delete('tweet_hashes')
->condition('nid', $node->nid)
->execute();
break;
case 'twitter_user_profile':
db_delete('tweet_user_hashes')
->condition('nid', $node->nid)
->execute();
break;
default:
break;
}
}
function tweet_feed_node_presave($node) {
if ($node->type == 'twitter_tweet_feed') {
$node->changed = $node->created;
}
}
function tweet_feed_process_twitter_image($url, $type, $tid = NULL) {
if (!isset($url)) {
return NULL;
}
if (variable_get('tweet_feed_fetch_images', 1) == 0) {
return NULL;
}
if (!file_exists('public://' . $type)) {
drupal_mkdir('public://' . $type);
}
$contents = @file_get_contents($url);
if (empty($contents)) {
return NULL;
}
tweet_feed_check_path('public://' . $type);
$file = file_save_data($contents, 'public://' . $type . '/' . md5($url) . '.jpg', FILE_EXISTS_REPLACE);
if ($file === FALSE) {
watchdog('tweet_feed', 'The :type for :tid could not be properly saved.', array(
':type' => $type,
':tid' => $tid,
), WATCHDOG_ERROR, NULL);
return NULL;
}
$file->uid = 1;
$file->status = 1;
file_save($file);
file_usage_add($file, 'tweet_feed', 'file', $file->fid);
return $file;
}
function tweet_feed_bail($admin = FALSE) {
if (function_exists('drush_print')) {
drush_set_error('Exiting.', 'fatal');
}
else {
if (!empty($admin)) {
drupal_goto('admin/config/services/tweet_feed');
}
else {
return;
}
}
}
function tweet_feed_check_path($uri) {
$instance = file_stream_wrapper_get_instance_by_uri($uri);
$real_path = $instance
->realpath();
if (!file_exists($real_path)) {
@mkdir($real_path, 0777, TRUE);
}
return file_exists($real_path);
}
function tweet_feed_help($path, $arg) {
switch ($path) {
case 'admin/help#tweet_feed':
module_load_include('inc', 'pathauto');
$output = '<h4>' . t('Overview') . '</h4>';
$output .= '<p>' . t('The Tweet Feed module is an advanced importing, displaying and data association module that allows you to pull in tweets by search, user, or list. The parameters of what is pulled in falls under the guidelines of ') . '<a href="https://dev.twitter.com/rest/public/rate-limiting">' . t('Twitter\'s REST API') . '.</a></p>';
$output .= '<p>' . t('Tweets can be displayed as nodes or in views as well as displayed by hash tag or user mention. All hash tags and user mentions are stored as references im the tweet nodes to their corresponding taxonomy term. This gives you great power in terms of displaying tweets with specific content in specific places by leveraging the power of contextual filters and taxonomies.</p>');
$output .= '<p>' . t('Additional documentation and example use cases can be found on the ') . '<a href="https://github.com/ElusiveMind/tweet_feed/wiki/Tweet-Feed-v2.0-Drupal-Module-Introduction">' . t('Tweet Feed WIKI Documentation Page') . '</a>. This help document will basically link you to places where you can get detailed information on the various help topics.</a></p>';
$output .= '<div style="border-top: 2px solid #f8981d; margin: 15px 0px; width: 100%"></div>';
$output .= '<h4>' . t('Twitter Settings') . '</h4>';
$output .= '<p><a href="https://github.com/ElusiveMind/tweet_feed/wiki/Twitter-Settings">' . t('Configuring account and feed settings.') . '</a></p>';
$output .= '<div style="border-top: 2px solid #f8981d; margin: 15px 0px; width: 100%"></div>';
$output .= '<h4>' . t('Views Integration') . '</h4>';
$output .= '<p>' . t('Tweet Feed has full views integration. A basic view of tweets can be created at Administer -> Structure -> Views -> Add New View by selecting Twitter Tweet Feed as the content type to show in the view.') . '</p>';
$output .= '<p>' . t('Further filtering can be accomplished using either the standard filters or the contextual filters available in the collapsed advanced fieldset. An example use case of when contextual features would be helpful is to add a block of dynamically filtered tweets based on matching a hashtag entered in a given field on any node.') . '</p>';
$output .= '<p>' . t('Look on the Advanced Use Cases page for information on using the Tweet Feed module in powerful ways to display node specific Tweets, Tweets related to node content and some other more simplistic use cases.') . '</p>';
$output .= '<div style="border-top: 2px solid #f8981d; margin: 15px 0px; width: 100%"></div>';
$output .= '<h4>' . t('Ways to Run the Import.') . '</h4>';
$output .= '<ol><li>' . t('They can be periodically imported using Drupal\'s build in cron system. This requires the run cron option on the Tweet Feed Settings page be enabled. Also make sure PHP has enough memory allocated to it in order to run the process along with all of the other tasks it needs to run.') . '</li>';
$output .= '<li>' . t('They can be manually imported by clicking the **Import** link on the right hand side of each feed in the feed information table.') . '</li>';
$output .= '<li>' . t('They can be brought in by <a href="https://github.com/ElusiveMind/tweet_feed/wiki/Drush-Commands">Drush</a> commands.') . '</li></ol>';
$output .= '<div style="border-top: 2px solid #f8981d; margin: 15px 0px; width: 100%"></div>';
$output .= '<h4>' . t('Drush Commands') . '</h4>';
$output .= '<a href="https://github.com/ElusiveMind/tweet_feed/wiki/Drush-Commands">' . t('Learning how to use Tweet Feed drush commands.') . '</a>';
$output .= '<div style="border-top: 2px solid #f8981d; margin: 15px 0px; padding-top: 15px; width: 100%; font-size: 8pt; line-height: 1.3em;">';
$output .= '<img src="/' . drupal_get_path('module', 'tweet_feed') . '/images/flyingflip-logo.png" width="151" height="50" alt="FlyingFlip Studios" align="right" /><br />';
$output .= 'Released 2016 - v7.x-3.x - FlyingFlip Studios, LLC. - Released under GNU GENERAL PUBLIC LICENSE - Version 2, June 1991<br />http://www.flyingflip.com - @flyingflip - @mbagnall17<br clear="right">';
$output .= '<style>.item-list, hr { display: none; }</style></div>';
return $output;
}
}