Permalink
Browse files

first commit alpha code base

  • Loading branch information...
1 parent ceef91e commit 6ba4d558b0e141389f15958f692618d9063c1713 @natepixel committed Sep 26, 2010
Showing with 590 additions and 0 deletions.
  1. +28 −0 config_default.php
  2. +9 −0 interfaces/model.php
  3. +10 −0 interfaces/view.php
  4. +173 −0 models/abstract.php
  5. +44 −0 models/home_timeline.php
  6. +21 −0 models/public_timeline.php
  7. +224 −0 sweeter_tweet_reader.php
  8. +40 −0 views/sweetness.php
  9. +23 −0 www/agent.php
  10. +18 −0 www/demo/index.php
View
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Sweeter Tweet Reader configuration file.
+ *
+ * Copy this to a file called config.php.
+ *
+ * Edit config.php with your settings.
+ */
+
+/**
+ * The full path to the sweeter_tweet_reader_directory
+ */
+define("SWEETER_TWEET_READER_PATH", dirname(__FILE__)."/");
+
+/**
+ * aboslute url of the www directory alias
+ */
+define("SWEETER_TWEET_READER_WWW_URL", 'http://127.0.0.1:8888/sweeter_tweet_reader/');
+
+/**
+ * The path to an instance of twitteroauth - defaults to the one distributed with Sweeter Tweet Reader
+ */
+define("TWITTEROAUTH_PATH", dirname(__FILE__)."/lib/twitteroauth/");
+define("TWITTEROAUTH_CONSUMER_KEY", "");
+define("TWITTEROAUTH_CONSUMER_SECRET","");
+define("TWITTEROAUTH_TOKEN","");
+define("TWITTEROAUTH_SECRET","");
+?>
View
@@ -0,0 +1,9 @@
+<?php
+interface SweeterTweetReaderModel
+{
+ /**
+ * Returns tweets
+ */
+ public function get_data();
+}
+?>
View
@@ -0,0 +1,10 @@
+<?php
+interface SweeterTweetReaderView
+{
+ /**
+ * @param array tweets
+ * @return string html
+ */
+ public function get($data);
+}
+?>
View
@@ -0,0 +1,173 @@
+<?php
+/**
+ * Our basic abstract class requires any model to define these three methods
+ *
+ * get_remote_data
+ * get_data_id
+ *
+ * When get_data is called, the following happens:
+ *
+ * - We get the data from a local source
+ * - If the local source is not available, we get the data from a remote source
+ * - If the local source data is older than config['lifespan']
+ * - create a randomized nonce that is associated with the config and model name
+ * - request sweeter_tweet_reader www/agent.php using the nonce key - the agent will update local source data with the remote data
+ * - if the remote data is not available, update the timestamp of the data_id with all the needed details, essentially resetting the timer.
+ *
+ * @todo should the nonce stuff be moved to controller??
+ */
+abstract class AbstractSweeterTweetReaderModel
+{
+ private $connection;
+ private $config;
+
+ final function __construct($conn, $name, &$config)
+ {
+ $this->connection = $conn;
+ $this->model_name = $name;
+ $this->config =& $config;
+ if (!isset($this->config['lifespan'])) $this->config['lifespan'] = 60; // default to one minute lifespan
+ }
+
+ /**
+ * Live retrieval of the data - should have a reasonable timeout and return false in the case of failure.
+ */
+ abstract public function get_remote_data();
+
+ /**
+ * The data id should be derived from non-random elements such that it will always be a same for a cacheable request
+ */
+ abstract public function get_data_id();
+
+ /**
+ * @todo update local data if we have to fetch remotely ...
+ */
+ public function get_data()
+ {
+ $data = $this->get_local_data();
+ if ($data === false) // cache does not exist - lets live update
+ {
+ $data = $this->update_data();
+ }
+ if ($this->data_is_expired())
+ {
+ $nonce = $this->create_nonce();
+ $this->curl_post_async(SWEETER_TWEET_READER_WWW_URL . 'agent.php', array('sweeter_tweet_reader_nonce' => $nonce));
+ }
+ return $data;
+ }
+
+ /**
+ * Fetch remote data and update the cache timestamp
+ *
+ * If not remote data is grabbed and we have local data, we refresh the timestamp for the local data
+ */
+ public function update_data()
+ {
+ $data = $this->get_remote_data();
+ if (!empty($data))
+ {
+ $this->_twitterify($data);
+ $id = $this->get_data_id();
+ $cache = new ObjectCache($id);
+ $cache->set($data);
+ }
+ else // lets update the timestamp anyway by fetching and resetting the local data (if it is fetchable)
+ {
+ $data = $this->get_local_data();
+ if ($data)
+ {
+ $id = $this->get_data_id();
+ $cache = new ObjectCache($id);
+ $cache->set($data);
+ }
+ }
+ return $data;
+ }
+
+ /**
+ * twitterify found here - thanks!
+ * http://www.snipe.net/2009/09/php-twitter-clickable-links/
+ */
+ function _twitterify(&$tweets)
+ {
+ foreach ($tweets as $k=>$tweet)
+ {
+ $tweet_html = $tweet->text;
+ $tweet_html = preg_replace("#(^|[\n ])([\w]+?://[\w]+[^ \"\n\r\t< ]*)#", "\\1<a href=\"\\2\" target=\"_blank\">\\2</a>", $tweet_html);
+ $tweet_html = preg_replace("#(^|[\n ])((www|ftp)\.[^ \"\t\n\r< ]*)#", "\\1<a href=\"http://\\2\" target=\"_blank\">\\2</a>", $tweet_html);
+ $tweet_html = preg_replace("/@(\w+)/", "<a href=\"http://www.twitter.com/\\1\" target=\"_blank\">@\\1</a>", $tweet_html);
+ $tweet_html = preg_replace("/#(\w+)/", "<a href=\"http://search.twitter.com/search?q=\\1\" target=\"_blank\">#\\1</a>", $tweet_html);
+ $tweets[$k]->html = $tweet_html;
+ }
+ }
+
+ final function data_is_expired()
+ {
+ $id = $this->get_data_id();
+ $cache = new ObjectCache($id, $this->config['lifespan']); // lets see if the data if within our acceptable lifespan
+ $data =& $cache->fetch();
+ return (empty($data));
+ }
+
+ final function create_nonce()
+ {
+ $nonce_key = md5(uniqid(mt_rand(), true));
+ $update_data['config'] = $this->get_config();
+ $update_data['model'] = $this->model_name;
+ $update = new ObjectCache($nonce_key);
+ $update->set($update_data);
+ return $nonce_key;
+ }
+
+ /**
+ * Using the data_id, retrieve the data from our local cache
+ */
+ final function get_local_data()
+ {
+ $id = $this->get_data_id();
+ $cache = new ObjectCache($id, 999999999); // we use an enormous expiration ... we want to get whatever exists.
+ $data =& $cache->fetch();
+ return $data;
+ }
+
+ public function get_connection()
+ {
+ return $this->connection;
+ }
+
+ public function get_config()
+ {
+ return $this->config;
+ }
+
+ /**
+ * asynchronous post request - grabbed from stack overflow - we'll try it unless something better comes along
+ *
+ * note that due to the nature of this if it starts failing you will never know it ... maybe we can build in an alert system?
+ *
+ * http://stackoverflow.com/questions/962915/how-do-i-make-an-asynchronous-get-request-in-php
+ *
+ * @todo consider mechanism to alert if the asynchronous update fails
+ */
+ final function curl_post_async($abs_url, $params)
+ {
+ foreach ($params as $key => &$val)
+ {
+ if (is_array($val)) $val = implode(',', $val);
+ $post_params[] = $key.'='.urlencode($val);
+ }
+ $post_string = implode('&', $post_params);
+ $parts=parse_url($abs_url);
+ $fp = fsockopen($parts['host'], isset($parts['port'])?$parts['port']:80, $errno, $errstr, 30);
+ $out = "POST ".$parts['path']." HTTP/1.1\r\n";
+ $out.= "Host: ".$parts['host']."\r\n";
+ $out.= "Content-Type: application/x-www-form-urlencoded\r\n";
+ $out.= "Content-Length: ".strlen($post_string)."\r\n";
+ $out.= "Connection: Close\r\n\r\n";
+ if (isset($post_string)) $out.= $post_string;
+ fwrite($fp, $out);
+ fclose($fp);
+ }
+}
+?>
View
@@ -0,0 +1,44 @@
+<?php
+include_once(realpath(dirname(__FILE__) . '/../interfaces/model.php'));
+include_once(realpath(dirname(__FILE__) . '/abstract.php'));
+
+/**
+ * Should we put twitterify and such in the base class?
+ */
+class HomeTimeline extends AbstractSweeterTweetReaderModel implements SweeterTweetReaderModel
+{
+ /**
+ * @todo the error stuff should be abstracted out and not implemented per model
+ */
+ public function get_remote_data()
+ {
+ $connection = $this->get_connection();
+ $connection->decode_json = false;
+ $json = $connection->get("statuses/home_timeline", array('count' => 20));
+ $tweets = json_decode($json);
+ if (empty($tweets->error))
+ {
+ $this->filter_protected($tweets);
+ }
+ else
+ {
+ trigger_error('Could not get remote data - twitteroauth returned error "'.$tweets->error.'"', E_USER_WARNING);
+ $tweets = false;
+ }
+ return $tweets;
+ }
+
+ public function get_data_id()
+ {
+ return 'home_timeline';
+ }
+
+ private function filter_protected(&$tweets)
+ {
+ foreach ($tweets as $k=>$tweet)
+ {
+ if ($tweet->user->protected) unset ($tweets[$k]);
+ }
+ }
+}
+?>
View
@@ -0,0 +1,21 @@
+<?php
+include_once(realpath(dirname(__FILE__) . '/../interfaces/model.php'));
+include_once(realpath(dirname(__FILE__) . '/abstract.php'));
+
+class PublicTimeline extends AbstractSweeterTweetReaderModel implements SweeterTweetReaderModel
+{
+ public function get_remote_data()
+ {
+ $connection = $this->get_connection();
+ $connection->decode_json = false;
+ $content = $connection->get("statuses/public_timeline", array('count' => 20));
+ $decoded_content = json_decode($content);
+ return $decoded_content;
+ }
+
+ public function get_data_id()
+ {
+ return 'public_timeline';
+ }
+}
+?>
Oops, something went wrong.

0 comments on commit 6ba4d55

Please sign in to comment.