// Probe for a language include with constants. Still include defines later on, if some constants were missing $probelang = dirname(__FILE__) . '/' . $serendipity['charset'] . 'lang_' . $serendipity['lang'] . '.inc.php'; if (file_exists($probelang)) { include $probelang; } include dirname(__FILE__) . '/lang_en.inc.php'; include dirname(__FILE__) . '/plugin_version.inc.php'; require_once dirname(__FILE__) . '/classes/Twitter.php'; include dirname(__FILE__) . '/classes/json.php4.include.php'; class serendipity_plugin_twitter extends serendipity_plugin { var $title = PLUGIN_TWITTER_TITLE; var $debug = false; function introspect(&$propbag) { $this->title = $this->get_config('title', $this->title); $propbag->add('name', PLUGIN_TWITTER_TITLE); $propbag->add('description', PLUGIN_TWITTER_DESC); $propbag->add('stackable', false); $propbag->add('author', 'Grischa Brockhaus, Damian Luszczymak, Garvin Hicking'); //$propbag->add('website', 'http://board.s9y.org'); $propbag->add('version', PLUGIN_TWITTER_VERSION); $propbag->add('requirements', array( 'serendipity' => '0.8', 'smarty' => '2.6.7', 'php' => '5.1.0' )); $configuration = array('title', 'number', 'service', 'username', 'showformat', 'toall_only', 'filter_all_user', 'filter_rt', 'use_time_ago', 'dateformat', 'linktext', 'followme_link', 'followme_widget', 'followme_widget_counter','followme_widget_dark', 'cachetime', 'backup' ); // Twitter API 1.1 is supported only via the event plugin if (class_exists('serendipity_event_twitter')) { $configuration = array_merge($configuration, array('twitter_api' , 'twitter_oauth')); } if (!class_exists('serendipity_event_twitter')) { $configuration = array_merge($configuration, array('event_not_installed')); } $propbag->add('configuration', $configuration); $propbag->add('groups', array('FRONTEND_VIEWS')); } function introspect_config_item($name, &$propbag) { global $serendipity; switch($name) { case 'event_not_installed': $propbag->add('type', 'content'); $propbag->add('default', PLUGIN_TWITTER_EVENT_NOT_INSTALLED); break; break; case 'title': $propbag->add('type', 'string'); $propbag->add('name', TITLE); $propbag->add('description', TITLE_FOR_NUGGET); $propbag->add('default', PLUGIN_TWITTER_TITLE); break; case 'service': $propbag->add('type', 'radio'); $propbag->add('name', PLUGIN_TWITTER_SERVICE); $propbag->add('description', PLUGIN_TWITTER_SERVICE_DESC); $propbag->add('radio', array( 'value' => array('twitter.com', 'identi.ca'), 'desc' => array('Twitter.com', 'identi.ca') )); $propbag->add('default', 'twitter.com'); break; case 'twitter_api': $propbag->add('type', 'radio'); $propbag->add('name', PLUGIN_TWITTER_API11); $propbag->add('description', PLUGIN_TWITTER_API11_DESC); $propbag->add('radio', array( 'value' => array('1.0', '1.1'), 'desc' => array('API 1.0 [depr]', 'API 1.1') )); $propbag->add('default', '1.0'); break; case 'twitter_oauth': $propbag->add('name', PLUGIN_TWITTER_OAUTHACC); $propbag->add('description', PLUGIN_TWITTER_OAUTHACC_DESC); $propbag->add('type', 'select'); $propbag->add('select_values', serendipity_event_twitter::getTwitterOauths()); $propbag->add('default', '1'); break; case 'username': $propbag->add('type', 'string'); $propbag->add('name', PLUGIN_TWITTER_USERNAME); $propbag->add('description', PLUGIN_TWITTER_USERNAME_DESC); $propbag->add('default', 'username'); break; case 'number': $propbag->add('type', 'string'); $propbag->add('name', PLUGIN_TWITTER_NUMBER); $propbag->add('description', PLUGIN_TWITTER_NUMBER_DESC); $propbag->add('default', 10); break; case 'toall_only': // filter only tweets starting with @ $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_TWITTER_TOALL_ONLY); $propbag->add('description', PLUGIN_TWITTER_TOALL_ONLY_DESC); $propbag->add('default', false); break; case 'filter_all_user': // filter tweets containing @ $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_TWITTER_FILTER_ALL); $propbag->add('description', PLUGIN_TWITTER_FILTER_ALL_DESC); $propbag->add('default', false); break; case 'filter_rt': // filter native retweets $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_TWITTER_FILTER_RT); $propbag->add('description', PLUGIN_TWITTER_FILTER_RT_DESC); $propbag->add('default', true); break; case 'followme_link': $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_TWITTER_FOLLOWME_LINK); $propbag->add('description', PLUGIN_TWITTER_FOLLOWME_LINK_DESC); $propbag->add('default', false); break; case 'followme_widget': $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_TWITTER_FOLLOWME_WIDGET); $propbag->add('description', PLUGIN_TWITTER_FOLLOWME_WIDGET_DESC); $propbag->add('default', false); break; case 'followme_widget_counter': $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_TWITTER_FOLLOWME_WIDGET_COUNT); $propbag->add('description', PLUGIN_TWITTER_FOLLOWME_WIDGET_COUNT_DESC); $propbag->add('default', true); break; case 'followme_widget_dark': $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_TWITTER_FOLLOWME_WIDGET_DARK); $propbag->add('description', PLUGIN_TWITTER_FOLLOWME_WIDGET_DARK_DESC); $propbag->add('default', false); break; case 'use_time_ago': $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_TWITTER_USE_TIME_AGO); $propbag->add('description', PLUGIN_TWITTER_USE_TIME_AGO_DESC); $propbag->add('default', 'false'); break; case 'dateformat': $propbag->add('type', 'string'); $propbag->add('name', GENERAL_PLUGIN_DATEFORMAT); $propbag->add('description', sprintf(GENERAL_PLUGIN_DATEFORMAT_BLAHBLAH, '%A, %B %e %Y')); $propbag->add('default', '%A, %B %e %Y'); break; case 'linktext': $propbag->add('type', 'string'); $propbag->add('name', PLUGIN_TWITTER_LINKTEXT); $propbag->add('description', PLUGIN_TWITTER_LINKTEXT_DESC); $propbag->add('default', 'link'); break; case 'cachetime': $propbag->add('type', 'string'); $propbag->add('name', PLUGIN_TWITTER_CACHETIME); $propbag->add('description', PLUGIN_TWITTER_CACHETIME_DESC); $propbag->add('default', '300'); break; case 'showformat': $propbag->add('type', 'radio'); $propbag->add('name', PLUGIN_TWITTER_SHOWFORMAT); $propbag->add('description', PLUGIN_TWITTER_SHOWFORMAT_DESC); $propbag->add('radio', array( 'value' => array('javascript', 'PHP'), 'desc' => array(PLUGIN_TWITTER_SHOWFORMAT_RADIO_JAVASCRIPT, PLUGIN_TWITTER_SHOWFORMAT_RADIO_PHP) )); $propbag->add('default', 'javascript'); break; case 'backup': $propbag->add('type', 'boolean'); $propbag->add('name', PLUGIN_TWITTER_BACKUP); $propbag->add('description', PLUGIN_TWITTER_BACKUP_DESC); $propbag->add('default', 'false'); break; default: return false; } return true; } function cleanup() { global $serendipity; $service = $this->get_config('service', 'twitter.com'); $username = $this->get_config('username'); // If followme widget is set, disable linḱ if ($service=='twitter.com' && serendipity_db_bool($this->get_config('followme_widget'))) { $this->set_config('followme_link', FALSE); } $cache_user = md5($service) . md5($username); $cachefile = $serendipity['serendipityPath'] . PATH_SMARTY_COMPILE . "/twitterresult.$cache_user.json"; // Remove Cachefile @unlink($cachefile); // Recreate cache serendipity_request_start(); $title = "#caching#"; $this->generate_content($title); serendipity_request_end(); } function output($out) { if (LANG_CHARSET == 'UTF-8') { echo $out; } else { echo utf8_decode($out); } } function generate_content(&$title) { global $serendipity; $hideDisplay = "#caching#" == $title; $number = $this->get_config('number'); $service = $this->get_config('service', 'twitter.com'); $username = $this->get_config('username'); $dateformat = $this->get_config('dateformat'); $title = $this->get_config('title', $this->title); $showformat = $this->get_config('showformat'); $cachetime = (int)$this->get_config('cachetime', 300); if (!is_numeric($number)) { $number = 10; } if ($service == 'identi.ca') { $followme_url = 'http://identi.ca/' . $username; $service_url = 'http://identi.ca/api'; $status_url = 'http://identi.ca/notice/'; //$JSONcallback = 'identicaCallback2'; $JSONcallback = 'twitterCallback2'; // We call the twitter widget. It is working with identi.ca too, but the callback name is twitter! $timelineurl = 'http://identi.ca/api/statuses/user_timeline/' . $username . '.json?callback=' . $JSONcallback . '&count=' . $number; $api = new Twitter(true); } else // if ($this->get_config('twitter_api','1.0') == '1.0') { $followme_url = 'http://twitter.com/' . $username; $service_url = 'http://twitter.com'; $status_url = 'http://twitter.com/' . $username . '/statuses/'; $JSONcallback = 'twitterCallback2'; $timelineurl = 'http://api.twitter.com/1/statuses/user_timeline.json?screen_name=' . $username . '&count=' . $number . '&callback=' . $JSONcallback; $api = new Twitter(false); } if (!$dateformat || strlen($dateformat) < 1) { $dateformat = '%A, %B %e %Y'; } if ($showformat == 'PHP') { $cache_user = md5($service) . md5($username); $cachefile = $serendipity['serendipityPath'] . PATH_SMARTY_COMPILE . "/twitterresult.$cache_user.json"; // If the Event Plugin is not installed, we have to fill the cachefile on our own.. // To immidiately display a result, the file_exists check is added. if (!class_exists('serendipity_event_twitter') || !file_exists($cachefile)) { // If we have twitter using API 1.1 the sidebarplugin *needs* the eventplugin to update if ($this->get_config('service','twitter.com') == 'identi.ca' || $this->get_config('twitter_api','1.0') == '1.0') { $this->updateTwitterTimelineCache($cachefile); } } // Get xml from cache: if (file_exists($cachefile)) { $xml = json_decode(unserialize(file_get_contents($cachefile))); } $str_output = array(); // now process it: $str_output[] = '
' . PLUGIN_TWITTER_FOLLOWME_LINK_TEXT . '
' . "\n"; } if ($service == 'twitter.com' && serendipity_db_bool($this->get_config('followme_widget', false))) { $extra_style = ''; if (serendipity_db_bool($this->get_config('followme_widget_dark', false))) { $extra_style .= ' data-button="grey" data-text-color="#FFFFFF" data-link-color="#00AEFF"'; } if (!serendipity_db_bool($this->get_config('followme_widget_counter', true))) { $extra_style .= ' data-show-count="false"'; } echo 'Follow @'.$username.''; } } if ($showformat == 'PHP') { // If the twitter event plugin is installed, too, save cache file in background. // When twitter is blocking, the blog isn't when using this background caching. // Background caching is done by a external plugin call, that is executed by the event plugin if (class_exists('serendipity_event_twitter')) { // add png that reloads the cache: $pluginurl = $serendipity['baseURL'] . $serendipity['indexFile'] . '?/' . $this->getPermaPluginPath(); $png_url = $pluginurl . '/cacheplugintwitter' .$this->cache_img_link_pars(); echo ''; } } if (serendipity_db_bool($this->get_config('backup')) && $service == 'twitter.com') { $last_backup = $this->get_config('last_backup', 0); if (date('Ymd') == date('Ymd', $last_backup)) { return true; } $this->checkTable(); $this->makeBackup($username, $last_backup); } } function cache_img_link_pars() { $service = $this->get_config('service'); $username = str_replace("_","!", $this->get_config('username')); $number = $this->get_config('number'); if (serendipity_db_bool($this->get_config('toall_only', false))) { $number = 50; // Fetch many in the hope, that there are enough globals with it. } $cachetime = $this->get_config('cachetime', 300); $suffix = "_{$service}_{$username}_{$number}_{$cachetime}"; if ($this->get_config('twitter_api', '1.0') == '1.1') { $idx = $this->get_config('twitter_oauth',0,FALSE); if (!empty($idx)) { $suffix = "{$suffix}_{$idx}_" . md5($this->pluginSecret() . "_{$idx}"); } // This is relevant only for API 1.1 if (!serendipity_db_bool($this->get_config('filter_rt', true))) { $suffix = "{$suffix}_r"; } } return $suffix; } function updateTwitterTimelineCache($cachefile){ global $serendipity; $cachetime = (int)$this->get_config('cachetime', 300); if (!file_exists($cachefile) || filemtime($cachefile) < (time()-$cachetime)) { require_once S9Y_PEAR_PATH . 'HTTP/Request.php'; $service = $this->get_config('service'); $username = $this->get_config('username'); $number = $this->get_config('number'); if (serendipity_db_bool($this->get_config('toall_only', false))) { $number = 50; // Fetch many in the hope, that there are enough globals with it. } if ($service == 'identi.ca') { $service_url = 'http://identi.ca/api'; $status_url = 'http://identi.ca/notice/'; $search_twitter_uri = $service_url . '/statuses/user_timeline/' . $username . '.json?count=' . $number; } else { $followme_url = 'http://twitter.com/' . $username; $service_url = 'http://api.twitter.com'; $status_url = 'http://twitter.com/' . $username . '/statuses/'; $search_twitter_uri = 'http://api.twitter.com/1/statuses/user_timeline.json?screen_name=' . $username . '&count=' . $number; } serendipity_request_start(); $req = new HTTP_Request($search_twitter_uri); $req->sendRequest(); $response = trim($req->getResponseBody()); $error = $req->getResponseCode(); serendipity_request_end(); if ($error==200 &&!empty($response)) { $fp = fopen($cachefile, 'w'); fwrite($fp, serialize($response)); fflush($fp); fclose($fp); } } } /** * Return binary response for an image */ function show_img($filename, $mime_type='image/png') { header("Content-type: $mime_type"); header("Date: " . date("D, d M Y H:i:s T")); $fp = @fopen($filename, "rb"); if ($fp) { header('X-TwitterPluginPng: Found'); $filemtime = filemtime($filename); header("Content-Length: ". filesize($filename), true); header("Last-Modified: " . date("D, d M Y H:i:s T", $filemtime), true); fpassthru($fp); fclose($fp); } else { header('X-TwitterPluginPng: No-Image'); header("Content-Length: 0", true); header("Last-Modified: " . date("D, d M Y H:i:s T"), true); } return true; } function checkTable() { global $serendipity; $q = "CREATE TABLE IF NOT EXISTS {$serendipity['dbPrefix']}tweets ( id bigint(11) {PRIMARY}, tweetdate int(11), tweet longtext, username varchar(255), reply_to_status int(11), reply_to_user int(11), source varchar(255) );"; serendipity_db_schema_import($q); $db_version = $this->get_config("db_version"); // Convert tweet id to bigint! if (empty($db_version)) { $q = "ALTER TABLE {$serendipity['dbPrefix']}tweets CHANGE id id bigint(11)"; serendipity_db_schema_import($q); } $this->set_config("db_version",1); } function debugOut($string) { global $serendipity; $fp = fopen($serendipity['serendipityPath'] . PATH_SMARTY_COMPILE . '/twitter.log', 'a'); fwrite($fp, date('Y-m-d H:i') . ' ' . $string . "\n"); fclose($fp); echo $string . "