Fun with Digg's API
Digg's api was released about a week ago a while ago, and since that time, various flash projects have surfaced, probably due to that pesky contest. But I don't have any flash experience, so PHP, here we come.
We're going to make various tag clouds, a Reddit clone, Reddit/Better RSS feed, and a live diggs app.
Update – 05/09/07 – 3 PM – Stupid host seems to be having database issues. Great. I wish I could afford a dedicated, or I guess VPS, so I don't have to put with this crap.
Update – 05/09/07 – 6 PM – It seems that it was a combination of their db sucking and the fact they got rid of some essential pear packages. WTF?
Update – 05/12/07 – 11 AM – Yup. They cut off my MySQL abilities. I can't connect to any of my databases on xrho.com. This site is fine however. I guess inserting 100+ entries a minute might have annoyed them?
Update – 05/17/07 – 7 PM – The MySQL abilities are still cut off. Fucking netfirms. Also, it seems in one of my updates, it cut off the bottom, which also happened to include the download links. You can download a ZIP or Gzip of the files. Since I'm having problems with that, feel free to download them and run them on your own site. Let me know if you do so I can add the link.
Demos of most of the scripts are available here, and more specifically:
Due to the length of this post, I'm splitting it. I hate making people click through, but it's long enough to constitute it.
Part A: The Setup
First, we need the Pear package, from http://bugs.joestump.net/code/Services_Digg/Services_Digg-0.0.2.tgz. You can install it using the Pear install command, but I just copied the folder into my directory.
We're going to be using MySQL database, so create a new database and here is the table structure I used:
-- -- Table structure for table `diggslive` -- CREATE TABLE `diggslive` ( `digging_id` int(10) unsigned NOT NULL auto_increment, `story_id` int(10) unsigned NOT NULL, `id` int(10) unsigned NOT NULL, `username` varchar(250) collate latin1_general_ci NOT NULL, `time` int(10) NOT NULL, `status` varchar(45) collate latin1_general_ci NOT NULL, PRIMARY KEY (`digging_id`), UNIQUE KEY `id` (`id`), KEY `username` (`username`), KEY `story_id` (`story_id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=137897 ; -- -------------------------------------------------------- -- -- Table structure for table `diggstories` -- CREATE TABLE `diggstories` ( `digg_id` int(10) unsigned NOT NULL auto_increment, `title` text character set latin1 collate latin1_general_ci NOT NULL, `description` text character set latin1 collate latin1_general_ci NOT NULL, `diggs` int(5) NOT NULL, `comments` int(4) NOT NULL, `id` int(10) unsigned NOT NULL, `link` varchar(1000) character set latin1 collate latin1_general_ci NOT NULL, `submitted` int(10) NOT NULL, `promoted` int(10) NOT NULL, `href` varchar(1000) character set latin1 collate latin1_general_ci NOT NULL, `status` varchar(45) character set latin1 collate latin1_general_ci NOT NULL, `user_id` int(10) unsigned NOT NULL, `username` varchar(250) character set latin1 collate latin1_general_ci NOT NULL, `icon` text character set latin1 collate latin1_general_ci NOT NULL, `registered` int(10) NOT NULL, `profileviews` int(10) unsigned NOT NULL, `topic_long` varchar(100) character set latin1 collate latin1_general_ci NOT NULL, `topic_short` varchar(100) character set latin1 collate latin1_general_ci NOT NULL, `container_long` varchar(100) character set latin1 collate latin1_general_ci NOT NULL, `container_short` varchar(100) character set latin1 collate latin1_general_ci NOT NULL, `host` varchar(250) collate utf8_bin default NULL, PRIMARY KEY (`digg_id`), UNIQUE KEY `href` (`href`), FULLTEXT KEY `title` (`title`), FULLTEXT KEY `description` (`description`), FULLTEXT KEY `title_2` (`title`,`description`), FULLTEXT KEY `link` (`link`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=3818 ; -- -------------------------------------------------------- -- -- Table structure for table `diggusers` -- CREATE TABLE `diggusers` ( `user_id` int(10) unsigned NOT NULL auto_increment, `username` varchar(250) collate latin1_general_ci NOT NULL, `icon` text collate latin1_general_ci, `registered` int(10) NOT NULL, `profileviews` int(10) unsigned NOT NULL default '0', PRIMARY KEY (`user_id`), UNIQUE KEY `username` (`username`), KEY `username_2` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci AUTO_INCREMENT=2550 ;
There are 3 tables. Table diggstories will store the all the info about the the popular stories that is given to us through the api. There are 3 FULLTEXT indecies to enable a better search as shown later.
Now for the config file. Edit as necessary for your setup:
config.php:
<?php $dbhost = "localhost"; $dbuser = "root"; $dbpass = ""; $dbname = "digg"; // Connect to DB, now! // Regular connection @mysql_connect($dbhost,$dbuser,$dbpass) or die("MySQL Connection error"); // Persistent conenction // @mysql_pconnect($dbhost,$dbuser,$dbpass) or deadlyerror("MySQL Connection error (persistent) "); @mysql_select_db($dbname) or die("Unable to select database"); ?>
Here are some functions that we'll use later.
functions.php:
<?php function sqlquote($value) { if(get_magic_quotes_gpc()) { $value = stripslashes($value); } //check if this function exists if( function_exists("mysql_real_escape_string") ) { $value = mysql_real_escape_string($value); } //for PHP version < 4.3.0 use addslashes else { $value = addslashes($value); } return $value; } function dehtml($content) { $content = preg_replace("/&(?!(amp|[#0-9]+|lt|gt|quot|copy|nbsp);)/ix","&",$content); $content = str_replace(array(" ","­","‍","‌","‭","‮"),"",$content); //$content = preg_replace('/&(?![a-z0-9]+;)/i', '&', $content); return str_replace(array("<",">","'","""),array("<",">","'","""),$content); } function timeparse($time) { $since = time() - $time; if($since < 60) { return ($since == 1) ? "$since second ago" : "$since seconds ago"; } elseif($since < 3600) { $since = floor($since/60); return ($since == 1) ? "$since minute ago" : "$since minutes ago"; } elseif($since < 86400) { $since = floor($since/3600); return ($since == 1) ? "$since hour ago" : "$since hours ago"; } else { $since = floor($since/86400); return ($since == 1) ? "$since day ago" : "$since days ago"; } } function toshort($text) { return strtolower(str_replace(" ","_",$text)); } function host($url) { $exploded = explode("/",$url); return str_replace("www.","",$exploded[2]); } ?>
Part B: Getting data
Getting the stories
First off, let's populate our database with stories. This will take as many stories as possible and inserts them into out `diggstories` table. We are also going to insert our user info that it finds.
insert.php:
<?php ini_set('user_agent', 'Trendds/1.0'); ini_set('max_execution_time', 3600); require 'config.php'; include 'functions.php'; require_once 'Services/Digg.php'; Services_Digg::$appKey = 'http://www.ja.meswilson.com/blog/Services_Digg_Proxy.php'; Services_Digg::$uri = 'http://services.digg.com'; $count = 3525; $offset = 0; while($offset<$count) { $params = array('count' => 100,'offset' => $offset); $request = Services_Digg::factory('Stories')->popular($params); foreach ($request->stories as $story) { $userinfo = mysql_query("SELECT `user_id`, `profileviews` FROM `diggusers` WHERE `username` = '".sqlquote($story->user->name)."' LIMIT 1"); if(mysql_numrows($userinfo) != 1) { mysql_query("INSERT INTO `diggusers` ( `user_id` , `username`, `icon`, `registered`, `profileviews` ) VALUES ( NULL, '".sqlquote($story->user->name)."', '".sqlquote($story->user->icon)."', '".sqlquote($story->user->registered)."', '".sqlquote($story->user->profileviews)."' ) ") or die("Inserting user: ".mysql_error()); $user_id = mysql_insert_id(); } else { $user_id = mysql_result($userinfo,0,"user_id"); } mysql_query("INSERT INTO `diggstories` ( `digg_id`, `title`, `description`, `diggs`, `comments`, `id`, `link`, `submitted`, `promoted`, `href`, `status`, `user_id` , `username`, `icon`, `registered`, `profileviews`, `topic_long`, `topic_short`, `container_long`, `container_short`, `host` ) VALUES ( NULL, '".sqlquote($story->title)."', '".sqlquote($story->description)."', '".sqlquote($story->diggs)."', '".sqlquote($story->comments)."', '".sqlquote($story->id)."', '".sqlquote($story->link)."', '".sqlquote($story->submit_date)."', '".sqlquote($story->promote_date)."', '".sqlquote($story->href)."', '".sqlquote($story->status)."', '".sqlquote($user_id)."', '".sqlquote($story->user->name)."', '".sqlquote($story->user->icon)."', '".sqlquote($story->user->registered)."', '".sqlquote($story->user->profileviews)."', '".sqlquote($story->topic->name)."', '".sqlquote($story->topic->short_name)."', '".sqlquote($story->container->name)."', '".sqlquote($story->container->short_name)."', '".sqlquote(host($story->link))."' ) ") or mysql_query("UPDATE `diggstories` SET `diggs` = '".sqlquote($story->diggs)."', `comments` = '".sqlquote($story->comments)."' WHERE `id` = '".sqlquote($story->id)."' LIMIT 1 ") or die(mysql_error()); } echo "<br />".$offset; $offset += 100; $count = $request->total; } ?>
The first lines,
ini_set('user_agent', 'Trendds/1.0');
ini_set('max_execution_time', 3600);
set the User-Agent and increases the max execution time, since getting over 3000 stories takes some time, and setting a user agent is required.
Services_Digg::$appKey = 'http://www.ja.meswilson.com/blog/Services_Digg_Proxy.php';
Services_Digg::$uri = 'http://services.digg.com';
set the api key and uri, as shown in the Pear tests.
Next, we start requesting and inserting the info. This runs through a while and for each loop inserting each story.
After running that, you should now have about a 3000 entry `diggstories` table along with a couple thousand entry `diggusers` table.
In order to keep these updated, we can run update.php every so often. I did 5 minutes, but that seems to be a bit overkill.
update.php:
<?php ini_set('user_agent', 'Trendds/1.0'); ini_set('max_execution_time', 3600); require 'config.php'; include 'functions.php'; require_once 'Services/Digg.php'; Services_Digg::$appKey = 'http://www.ja.meswilson.com/blog/Services_Digg_Proxy.php'; Services_Digg::$uri = 'http://services.digg.com'; $params = array('count' => 100); $request = Services_Digg::factory('Stories')->popular($params); //print_r($request); //exit; foreach ($request->stories as $story) { $userinfo = mysql_query("SELECT `user_id`, `profileviews` FROM `diggusers` WHERE `username` = '".sqlquote($story->user->name)."' LIMIT 1"); if(mysql_numrows($userinfo) != 1) { mysql_query("INSERT INTO `diggusers` ( `user_id` , `username`, `icon`, `registered`, `profileviews` ) VALUES ( NULL, '".sqlquote($story->user->name)."', '".sqlquote($story->user->icon)."', '".sqlquote($story->user->registered)."', '".sqlquote($story->user->profileviews)."' ) ") or die("Inserting user: ".mysql_error()); $user_id = mysql_insert_id(); } else { $user_id = mysql_result($userinfo,0,"user_id"); } mysql_query("INSERT INTO `diggstories` ( `digg_id`, `title`, `description`, `diggs`, `comments`, `id`, `link`, `submitted`, `promoted`, `href`, `status`, `user_id` , `username`, `icon`, `registered`, `profileviews`, `topic_long`, `topic_short`, `container_long`, `container_short`, `host` ) VALUES ( NULL, '".sqlquote($story->title)."', '".sqlquote($story->description)."', '".sqlquote($story->diggs)."', '".sqlquote($story->comments)."', '".sqlquote($story->id)."', '".sqlquote($story->link)."', '".sqlquote($story->submit_date)."', '".sqlquote($story->promote_date)."', '".sqlquote($story->href)."', '".sqlquote($story->status)."', '".sqlquote($user_id)."', '".sqlquote($story->user->name)."', '".sqlquote($story->user->icon)."', '".sqlquote($story->user->registered)."', '".sqlquote($story->user->profileviews)."', '".sqlquote($story->topic->name)."', '".sqlquote($story->topic->short_name)."', '".sqlquote($story->container->name)."', '".sqlquote($story->container->short_name)."', '".sqlquote(host($story->link))."' ) ") or mysql_query("UPDATE `diggstories` SET `diggs` = '".sqlquote($story->diggs)."', `comments` = '".sqlquote($story->comments)."' WHERE `id` = '".sqlquote($story->id)."' LIMIT 1 ") or die(mysql_error()); } ?>
In order to not refresh or have a cron job (though if you plan on using this more, a cron job would be good), you can just run crondigg.py, or crondigg.pyw if you don't want to see a window, to keep the database updated. You will need to edit value for url to point to the location of your update.php.
crondigg.py
#! /usr/bin/env python url = 'http://localhost/digg/update.php' wait = 300 import urllib,time while 1: try: print 'Requesting %s \r' % url, urllib.urlopen(url).read() print " " * (len(url)+12),'\r', count = 0 while count < wait: print 'Waiting %d seconds \r' % (wait-count), count+=1 time.sleep(1) except KeyboardInterrupt: import sys sys.exit(2)
Note: This python script was written on Windows, where it works fine. On linux however, it's a different story. It still works, just the output doesn't.
Gettings diggs
Next, we're going to get individual diggs. We're not going to go back in time and get as many diggs as possible. The database will get really big pretty quickly, so don't worry about it.
updatediggs.php:
<?php ini_set('user_agent', 'Trendds/1.0'); ini_set('max_execution_time', 3600); require 'config.php'; include 'functions.php'; require_once 'Services/Digg.php'; Services_Digg::$appKey = 'http://www.ja.meswilson.com/blog/Services_Digg_Proxy.php'; Services_Digg::$uri = 'http://services.digg.com'; $api = Services_Digg::factory('Stories'); // To keep track of the current timestamp. This could just be mysql_result(mysql_query("SELECT `time` FROM `diggslive` ORDER BY `time` DESC LIMIT 1"),0,"time") but a txt file is easier. //$mindate = file_get_contents('lastdiggs.txt')-1; $mindate = mysql_result(mysql_query("SELECT `time` FROM `diggslive` ORDER BY `time` DESC LIMIT 1"),0,"time")-1 ; //$mindate = time() - 3600; $offset = 0; $total = 1; $runs = 0; // Get as many diggs as possible since the last update while ($offset < $total) { $params = array('count' => 100,'min_date' => $mindate,'offset'=>$offset); $diggs = $api->diggs($params); // Go through each digg //print_r($diggs); foreach ($diggs->diggs as $digg) { // This is pretty annoying. It's true if the insert failed. It might not be the most sensible thing, but it's about 2 in the morning, and I like it $inserted = false; // Insert the digg mysql_query("INSERT INTO `diggslive` (`digging_id`, `story_id`, `id`, `username`, `time`, `status`) VALUES ( NULL, '".sqlquote($digg->story)."', '".sqlquote($digg->id)."', '".sqlquote($digg->user)."', '".sqlquote($digg->date)."', '".sqlquote($digg->status)."' )") or setcheck(true); if(!$inserted) { // Increase the digg count of the story, since it just got dugg mysql_query("UPDATE `diggstories` SET `diggs` = `diggs`+1 WHERE `id` = '".sqlquote($digg->id)."' LIMIT 1") or die(mysql_error()); } } // If this is the first run, update the timestamp. We could use time(), but it might be slightly off. If we take the timestamp of a later run, it'll be later than what we got back. if($runs == 0 AND $diggs->timestamp != "") { $timestamp = $diggs->timestamp; $runs = 1; } $total = $diggs->total; print $offset."<br />"; $offset += 100; } // If the timestamp isn't foobared, update the file. If there are network problems or something, this can be wrong. I don't think I've still truely fixed that, but whatever. if(isset($timestamp) AND $timestamp != "" AND is_numeric($timestamp) AND $timestamp > 1000000) { $file = fopen('lastdiggs.txt','w'); fwrite($file,$timestamp); fclose($file); } // This will change the variable to say if it inserted successfully or not, because doing ... or $inserted = true; doesn't work. -_- function setcheck($bool) { global $inserted; $inserted = $bool; } ?>
Before you can use this, you need to insert some digg or something into `diggslive` with `time` set to the current timestamp. After another digg gets inserted, you can get rid of this entry.
This just gets the diggs of all the stories since the last digg we got. The timestamp is one less than the time of the last digg. This is to make sure we got the diggs that occureed at the same second as the previous one.
This also edits a file called lastdigg.txt, which is just an alternative to the sql query.
As I said earlier, the table gets big. Mine was at around 330k entries before I started running deletes. Let's get rid of any diggs older than a day. We don't really need them.
removediggs.p:
<?php require 'config.php'; $query = "DELETE FROM `diggslive` WHERE `time` < ".(time() - 24*60*60).""; mysql_query($query)or die(mysql_error()); mysql_query("OPTIMIZE TABLE `diggslive` ") or die(mysql_error()); ?>
To keep this updated, you can run crondiggs.py. This requests your updatediggs.php file every 30 seconds.
crondiggs.py
#! /usr/bin/env python url = 'http://localhost/digg/updatediggs.php' url2 = 'http://localhost/digg/removediggs.php' wait = 30 import urllib,time while 1: try: print 'Requesting %s \r' % url, urllib.urlopen(url).read() if int(time.time()) % 10 == 0: print 'Requesting %s \r' % url2, urllib.urlopen(url2).read() print " " * (len(url)+12),'\r', count = 0 while count < wait: print 'Waiting %d seconds \r' % (wait-count), count+=1 time.sleep(1) except KeyboardInterrupt: import sys sys.exit(2)
Part Gamma: Tag Clouds
Now it's time to use all this new fangled data we're collecting, and what's more Web 2.0 than tag clouds? And since we're lazy, we're going to use this tag cloud script.
First cloud set, the container cloud set. The containers are the main topics, like Technology, Science, Sports, etc.
concloud.php:
<?php // most from: http://prism-perfect.net/archive/php-tag-cloud-tutorial/ // connect to database at some point // In the SQL below, change these three things: // thing is the column name that you are making a tag cloud for // id is the primary key // my_table is the name of the database table require 'config.php'; $query = "SELECT container_long AS tag, COUNT(digg_id) AS quantity, container_short FROM diggstories GROUP BY container_long ORDER BY container_long ASC"; $result = mysql_query($query); // here we loop through the results and put them into a simple array: // $tag['thing1'] = 12; // $tag['thing2'] = 25; // etc. so we can use all the nifty array functions // to calculate the font-size of each tag while ($row = mysql_fetch_array($result)) { $shortname[$row['tag']] = $row['container_short']; $tags[$row['tag']] = $row['quantity']; } // change these font sizes if you will $max_size = 250; // max font size in % $min_size = 100; // min font size in % // get the largest and smallest array values $max_qty = max(array_values($tags)); $min_qty = min(array_values($tags)); // find the range of values $spread = $max_qty - $min_qty; if (0 == $spread) { // we don't want to divide by zero $spread = 1; } // determine the font-size increment // this is the increase per tag quantity (times used) $step = ($max_size - $min_size)/($spread); // loop through our tag array foreach ($tags as $key => $value) { // calculate CSS font-size // find the $value in excess of $min_qty // multiply by the font-size increment ($size) // and add the $min_size set above $size = $min_size + (($value - $min_qty) * $step); // uncomment if you want sizes in whole %: // $size = ceil($size); // you'll need to put the link destination in place of the # // (assuming your tag links to some sort of details page) echo '<a href="'.$shortname[$key].'" style="font-size: '.$size.'%"'; // perhaps adjust this title attribute for the things that are tagged echo ' title="'.$value.' things tagged with '.$key.'"'; echo '>'.$key.'</a> '; // notice the space at the end of the link } ?>
That's pretty boring though. Maybe regular categories, like Apple, Linux/Unix, Playable Web Games, etc, will be more interesting.
catcloud.php
<?php // most from: http://prism-perfect.net/archive/php-tag-cloud-tutorial/ // connect to database at some point // In the SQL below, change these three things: // thing is the column name that you are making a tag cloud for // id is the primary key // my_table is the name of the database table require 'config.php'; require 'functions.php'; $query = "SELECT topic_long AS tag, COUNT(digg_id) AS quantity, topic_short FROM diggstories GROUP BY topic_long ORDER BY topic_long ASC"; $result = mysql_query($query); // here we loop through the results and put them into a simple array: // $tag['thing1'] = 12; // $tag['thing2'] = 25; // etc. so we can use all the nifty array functions // to calculate the font-size of each tag while ($row = mysql_fetch_array($result)) { $shortname[$row['tag']] = $row['topic_short']; $tags[$row['tag']] = $row['quantity']; } // change these font sizes if you will $max_size = 250; // max font size in % $min_size = 100; // min font size in % // get the largest and smallest array values $max_qty = max(array_values($tags)); $min_qty = min(array_values($tags)); // find the range of values $spread = $max_qty - $min_qty; if (0 == $spread) { // we don't want to divide by zero $spread = 1; } // determine the font-size increment // this is the increase per tag quantity (times used) $step = ($max_size - $min_size)/($spread); // loop through our tag array foreach ($tags as $key => $value) { // calculate CSS font-size // find the $value in excess of $min_qty // multiply by the font-size increment ($size) // and add the $min_size set above $size = $min_size + (($value - $min_qty) * $step); // uncomment if you want sizes in whole %: // $size = ceil($size); // you'll need to put the link destination in place of the # // (assuming your tag links to some sort of details page) echo '<a href="http://digg.com/'.$shortname[$key].'" style="font-size: '.$size.'%"'; // perhaps adjust this title attribute for the things that are tagged echo ' title="'.$value.' things tagged with '.$key.'"'; echo '>'.$key.'</a> '; // notice the space at the end of the link } ?>
A little better. How about a user cloud based on submissions?
usercloud.php:
<?php // most from: http://prism-perfect.net/archive/php-tag-cloud-tutorial/ // connect to database at some point // In the SQL below, change these three things: // thing is the column name that you are making a tag cloud for // id is the primary key // my_table is the name of the database table require 'config.php'; $query = "SELECT username AS tag, COUNT(digg_id) AS quantity FROM diggstories GROUP BY username ORDER BY username ASC"; $result = mysql_query($query); // here we loop through the results and put them into a simple array: // $tag['thing1'] = 12; // $tag['thing2'] = 25; // etc. so we can use all the nifty array functions // to calculate the font-size of each tag while ($row = mysql_fetch_array($result)) { //$shortname[$row['tag']] = $row['container_short']; $tags[$row['tag']] = $row['quantity']; } // change these font sizes if you will $max_size = 250; // max font size in % $min_size = 100; // min font size in % // get the largest and smallest array values $max_qty = max(array_values($tags)); $min_qty = min(array_values($tags)); // find the range of values $spread = $max_qty - $min_qty; if (0 == $spread) { // we don't want to divide by zero $spread = 1; } // determine the font-size increment // this is the increase per tag quantity (times used) $step = ($max_size - $min_size)/($spread); // loop through our tag array foreach ($tags as $key => $value) { // calculate CSS font-size // find the $value in excess of $min_qty // multiply by the font-size increment ($size) // and add the $min_size set above $size = $min_size + (($value - $min_qty) * $step); // uncomment if you want sizes in whole %: // $size = ceil($size); // you'll need to put the link destination in place of the # // (assuming your tag links to some sort of details page) echo '<a href="http://digg.com/users/'.urlencode($key).'" style="font-size: '.$size.'%"'; // perhaps adjust this title attribute for the things that are tagged echo ' title="'.$value.' things tagged with '.$key.'"'; echo '>'.$key.'</a> '; // notice the space at the end of the link } ?>
Pretty big, but let's try bigger. A user cloud based on number of diggs.
diggusercloud.php:
<?php // most from: http://prism-perfect.net/archive/php-tag-cloud-tutorial/ // connect to database at some point // In the SQL below, change these three things: // thing is the column name that you are making a tag cloud for // id is the primary key // my_table is the name of the database table require 'config.php'; $query = "SELECT username AS tag, COUNT(digging_id) AS quantity FROM diggslive GROUP BY username ORDER BY username ASC"; $result = mysql_query($query); // here we loop through the results and put them into a simple array: // $tag['thing1'] = 12; // $tag['thing2'] = 25; // etc. so we can use all the nifty array functions // to calculate the font-size of each tag while ($row = mysql_fetch_array($result)) { $href[$row['tag']] = $row['href']; $tags[$row['tag']] = $row['quantity']; } // change these font sizes if you will $max_size = 250; // max font size in % $min_size = 100; // min font size in % // get the largest and smallest array values $max_qty = max(array_values($tags)); $min_qty = min(array_values($tags)); // find the range of values $spread = $max_qty - $min_qty; if (0 == $spread) { // we don't want to divide by zero $spread = 1; } // determine the font-size increment // this is the increase per tag quantity (times used) $step = ($max_size - $min_size)/($spread); // loop through our tag array foreach ($tags as $key => $value) { // calculate CSS font-size // find the $value in excess of $min_qty // multiply by the font-size increment ($size) // and add the $min_size set above $size = $min_size + (($value - $min_qty) * $step); // uncomment if you want sizes in whole %: // $size = ceil($size); // you'll need to put the link destination in place of the # // (assuming your tag links to some sort of details page) echo '<a href="http://digg.com/users/'.$key.'" style="font-size: '.$size.'%"'; // perhaps adjust this title attribute for the things that are tagged echo ' title="'.$value.' things tagged with '.$key.'"'; echo '>'.$key.'</a> '; // notice the space at the end of the link } ?>
Great. How about a story cloud based on the number of diggs?
diggcloud.php:
<?php // most from: http://prism-perfect.net/archive/php-tag-cloud-tutorial/ // connect to database at some point // In the SQL below, change these three things: // thing is the column name that you are making a tag cloud for // id is the primary key // my_table is the name of the database table require 'config.php'; $query = "SELECT title AS tag, COUNT(digg_id) AS quantity, href FROM diggstories GROUP BY diggs ORDER BY diggs ASC"; $result = mysql_query($query); // here we loop through the results and put them into a simple array: // $tag['thing1'] = 12; // $tag['thing2'] = 25; // etc. so we can use all the nifty array functions // to calculate the font-size of each tag while ($row = mysql_fetch_array($result)) { $href[$row['tag']] = $row['href']; $tags[$row['tag']] = $row['quantity']; } // change these font sizes if you will $max_size = 250; // max font size in % $min_size = 100; // min font size in % // get the largest and smallest array values $max_qty = max(array_values($tags)); $min_qty = min(array_values($tags)); // find the range of values $spread = $max_qty - $min_qty; if (0 == $spread) { // we don't want to divide by zero $spread = 1; } // determine the font-size increment // this is the increase per tag quantity (times used) $step = ($max_size - $min_size)/($spread); // loop through our tag array foreach ($tags as $key => $value) { // calculate CSS font-size // find the $value in excess of $min_qty // multiply by the font-size increment ($size) // and add the $min_size set above $size = $min_size + (($value - $min_qty) * $step); // uncomment if you want sizes in whole %: // $size = ceil($size); // you'll need to put the link destination in place of the # // (assuming your tag links to some sort of details page) echo '<a href="'.$href[$key].'" style="font-size: '.$size.'%"'; // perhaps adjust this title attribute for the things that are tagged echo ' title="'.$value.' things tagged with '.$key.'"'; echo '>'.$key.'</a> '; // notice the space at the end of the link } ?>
But wait, that doesn't look right. That's because we can't use basically the same SQL query as before. It's grouping the stories by the amount of diggs, so that if there are 3 stories with 750 diggs, and only 1 story with 751 diggs, the 3 stories will be larger. Since the weight of the story is already set as an entry, we don't need to group it, so we can just use this SQL query:
SELECT title AS tag, diggs, href FROM diggstories ORDER BY title ASC
<?php // most from: http://prism-perfect.net/archive/php-tag-cloud-tutorial/ // connect to database at some point // In the SQL below, change these three things: // thing is the column name that you are making a tag cloud for // id is the primary key // my_table is the name of the database table require 'config.php'; $query = "SELECT title AS tag, diggs, href FROM diggstories ORDER BY title ASC"; $result = mysql_query($query); // here we loop through the results and put them into a simple array: // $tag['thing1'] = 12; // $tag['thing2'] = 25; // etc. so we can use all the nifty array functions // to calculate the font-size of each tag while ($row = mysql_fetch_array($result)) { $href[$row['tag']] = $row['href']; $tags[$row['tag']] = $row['diggs']; } // change these font sizes if you will $max_size = 250; // max font size in % $min_size = 100; // min font size in % // get the largest and smallest array values $max_qty = max(array_values($tags)); $min_qty = min(array_values($tags)); // find the range of values $spread = $max_qty - $min_qty; if (0 == $spread) { // we don't want to divide by zero $spread = 1; } // determine the font-size increment // this is the increase per tag quantity (times used) $step = ($max_size - $min_size)/($spread); // loop through our tag array foreach ($tags as $key => $value) { // calculate CSS font-size // find the $value in excess of $min_qty // multiply by the font-size increment ($size) // and add the $min_size set above $size = $min_size + (($value - $min_qty) * $step); // uncomment if you want sizes in whole %: // $size = ceil($size); // you'll need to put the link destination in place of the # // (assuming your tag links to some sort of details page) echo '<a href="'.$href[$key].'" style="font-size: '.$size.'%"'; // perhaps adjust this title attribute for the things that are tagged echo ' title="'.$value.' things tagged with '.$key.'"'; echo '>'.$key.'</a> '; // notice the space at the end of the link } ?>
If you noticed in insert.php and update.php, we had an entry called 'host'. This is equal to whatever the hostname of the link. Let's make a host cloud based on the amount of stories.
hostcloud.php:
<?php // most from: http://prism-perfect.net/archive/php-tag-cloud-tutorial/ // connect to database at some point // In the SQL below, change these three things: // thing is the column name that you are making a tag cloud for // id is the primary key // my_table is the name of the database table require 'config.php'; $query = "SELECT host AS tag, COUNT(digg_id) AS quantity FROM diggstories GROUP BY host ORDER BY host ASC"; $result = mysql_query($query); // here we loop through the results and put them into a simple array: // $tag['thing1'] = 12; // $tag['thing2'] = 25; // etc. so we can use all the nifty array functions // to calculate the font-size of each tag while ($row = mysql_fetch_array($result)) { $href[$row['tag']] = $row['href']; $tags[$row['tag']] = $row['quantity']; } // change these font sizes if you will $max_size = 250; // max font size in % $min_size = 100; // min font size in % // get the largest and smallest array values $max_qty = max(array_values($tags)); $min_qty = min(array_values($tags)); // find the range of values $spread = $max_qty - $min_qty; if (0 == $spread) { // we don't want to divide by zero $spread = 1; } // determine the font-size increment // this is the increase per tag quantity (times used) $step = ($max_size - $min_size)/($spread); // loop through our tag array foreach ($tags as $key => $value) { // calculate CSS font-size // find the $value in excess of $min_qty // multiply by the font-size increment ($size) // and add the $min_size set above $size = $min_size + (($value - $min_qty) * $step); // uncomment if you want sizes in whole %: // $size = ceil($size); // you'll need to put the link destination in place of the # // (assuming your tag links to some sort of details page) echo '<a href="search.php?links='.urlencode($key).'" style="font-size: '.$size.'%"'; // perhaps adjust this title attribute for the things that are tagged echo ' title="'.$value.' things tagged with '.$key.'"'; echo '>'.$key.'</a> '; // notice the space at the end of the link } ?>
Where to go: Tag clouds are fun. You can easily set it to only show a certain time period, like the past day or week, or you could use both the diggstories and diggslive tables to see what people are digging. You could also combine it with the search that we'll talk about next.
Part Dogma – Improved Search
As I said earlier, the `diggstories` table has 4 FULLTEXT indecies, so let's use those to create a better search. It's not necessarily all that better than the current one, and won't search upcoming stories, but to some, it might be better.
This will allow you to search using multiple queries, like let's say you want to find all of the stories dealing nintendo from arstechnica.com. This search can easily handle that.
It uses MySQL FULLTEXT search to handle it. This has the basic format of WHERE MATCH(`columns`) AGAINST ('query') and will return the results based on the relevance.
search.php:
<?php if(!isset($_GET['q']) AND !isset($_GET['title']) AND !isset($_GET['desc']) AND !isset($_GET['links'])) { ?> <form action="search.php" method="get"> Search title and desc: <input type="text" name="q" /><br /><br /> <b>OR</b> <br /><br /> Search in title: <input type="text" name="title" /><br /> Search in desc: <input type="text" name="desc" /><br /> Search in links: <input type="text" name="links" /><br /><br /> <br /> <input type="submit" value="Search" /> </form> <?php } else { require 'config.php'; require 'functions.php'; if(isset($_GET['q']) AND $_GET['q'] != "") { $where = " MATCH (`title`, `description`) AGAINST ('".sqlquote($_GET['q'])."') "; } else { if(isset($_GET['title']) AND $_GET['title'] != "") { $where = " MATCH (`title`) AGAINST ('".sqlquote($_GET['title'])."') "; } if(isset($_GET['desc']) AND $_GET['desc'] != "") { $where .= ( ($where == "") ? "" : "AND")." MATCH (`description`) AGAINST ('".sqlquote($_GET['desc'])."') "; } if(isset($_GET['links']) AND $_GET['links'] != "") { $where .= ( ($where == "") ? "" : "AND")." MATCH (`link`) AGAINST ('".sqlquote($_GET['links'])."') "; } } $result = mysql_query("SELECT `title`, `description`, `diggs`, `comments`, `link`, `href` FROM `diggstories` WHERE $where") or die(mysql_error()); $num = mysql_numrows($result); if($num == 0) { echo "No results found"; exit; } echo "<table align="center"> <thead><tr><th>Story</th><th>Diggs</th><th>Comments</th></tr></thead>"; for($i=0;$i<$num;$i++) { echo "<tr><td><a href="".dehtml(mysql_result($result,$i,"link"))."" title="".dehtml(mysql_result($result,$i,"description"))."">".dehtml(mysql_result($result,$i,"title"))."</a> [<a href="".dehtml(mysql_result($result,$i,"href"))."">More</a>]</td> <td>".mysql_result($result,$i,"diggs")."</td> <td>".mysql_result($result,$i,"comments")."</td> </tr>"; } echo "</table>"; echo "<span style="float:right;">$num results</span>"; }
Where to go: There are a lot of things that can be done to improve this. Maybe add a fetaure to only return stories with over a certain amount of diggs or comments, or that were submitted between certain dates
Part Alien – Reggit/Dreddit – Reddit clone
Homepage
Everyone loves the clean, sexy layout of reddit, so let's take Digg's data and throw it into a reddit layout.
Pretty easy to make. Just copy their page layout and CSS, and throw the digg story information in there. I removed the header and footer from it, since none will really work, and we don't have some badass logo. It's just sorted by promoted time, and not popularity, though it could probably be modded to take account that.
reddit.php
<?php require 'config.php'; require 'functions.php'; ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <meta http-equiv='Content-Type' content='text/html; charset=UTF-8' /> <title>digg + reddit = reggit... or dreddit</title> <script src="http://reddit.com/static/psrs.js" language="javascript" type="text/javascript"></script> <script src="http://reddit.com/static/reddit.js" language="javascript" type='text/javascript'></script> <link rel='stylesheet' href='http://reddit.com/static/styles.css' type='text/css' /> <link rel='shortcut icon' href='http://reddit.com/favicon.ico' type="image/x-icon" /> <script language='javascript'> var a = new Image(); a.src ="http://static.reddit.com/aupmod.png"; var b = new Image(); b.src = "http://static.reddit.com/adownmod.png"; </script> </head> <body> <?php $offset = (isset($_GET['offset']) AND is_numeric($_GET['offset'])) ? $_GET['offset'] : 0; $result = mysql_query("SELECT `title`, `diggs`, `comments`, `link`, `href`, `host`, `id`, `username`, `submitted` FROM `diggstories` ORDER BY `promoted` DESC LIMIT $offset, 25") or die(mysql_error()); $num = mysql_numrows($result); ?> <div id='main'> <table id="siteTable"> <?php for($i=0;$i<$num;$i++) { echo "<tr id="story".mysql_result($result,$i,"id")."">"; echo ($i % 2 == 0) ? "<td colspan="1" class="evenRow spacing top"></td> <td colspan="4" class="evenRow spacing top"></td> </tr> <tr class="evenRow">" : "<td colspan="1" class="oddRow spacing top"></td> <td colspan="4" class="oddRow spacing top"></td> </tr> <tr class="oddRow">"; echo "<td valign="top" class="numbercol" rowspan="3">".($i+$offset+1).".</td>"; echo "<td valign="top" rowspan="3"> <div id="up".mysql_result($result,$i,"id")."" class="arrow up" onclick="javascript:mod(".mysql_result($result,$i,"id").", 1, '')"> </div> <div id="down".mysql_result($result,$i,"id")."" class="arrow down" onclick="javascript:mod(".mysql_result($result,$i,"id").", 0, '')"/> </td>"; echo " <td colspan="3" id="titlerow".mysql_result($result,$i,"id").""> <a id="title".mysql_result($result,$i,"id")."" class="title" href="".dehtml(mysql_result($result,$i,"link"))."" > ".dehtml(mysql_result($result,$i,"title"))."</a> <span class="little"> (".dehtml(mysql_result($result,$i,"host")).")</span> </td> </tr>"; echo ($i % 2 == 0) ? "<tr class="evenRow">" : "<tr class="oddRow">"; echo "<td valign="top" class="wide little" colspan="3"> <span id="score".mysql_result($result,$i,"id")."">".mysql_result($result,$i,"diggs")." diggs</span> posted ".( floor( (time() - mysql_result($result,$i,"submitted")) / 3600) )." hours ago by <a href="http://digg.com/user/".mysql_result($result,$i,"username")."">".mysql_result($result,$i,"username")." </a> <a href="".mysql_result($result,$i,"href")."" class="bylink" > ".mysql_result($result,$i,"comments")." comments </a> </td> </tr>"; echo "<tr ><td colspan="3" class="".( ($i % 2 == 0) ? "evenRow" : "oddRow" )." spacing"></td></tr>"; } ?> </table> <p class="menu"> view more: <a href="reddit.php?offset=<?php echo $offset+25; ?>">next »</a> </p> </div> </body></html>
Where to go: Subreddits, like technology.redditclone to only show the stories from the container 'Technology', or apple.redditclone or apple.technology.redditclone to only show stories from the topic 'Apple'. Sort by popularity or "hotness", like reddit's homepage. Maybe something like ORDER BY `diggs` / (".time()." - `promoted`), but better.
RSS
Another great part of reddit is their RSS feed. No fluff, no mess, just links, so let's rid that off too. This is also really easy to make. If you've ever had to make an RSS feed from a DB, you'll have no trouble.
The RSS feed consists of a title that links directly to the article, with the content having links [link] which goes to the article, [more] which goes to the Digg page, and a [dugg] links which goes to the Duggmirror link.
redditrss.php:
<?php require 'config.php'; require 'functions.php'; $stories = mysql_query("SELECT `title`, `href`, `link`,`promoted` FROM `diggstories` ORDER BY `promoted` DESC LIMIT 0, 40"); rss_start(); $count = mysql_numrows($stories); echo $count; for($i=0;$i<$count;$i++) { rss_row(mysql_result($stories,$i,"title"), mysql_result($stories,$i,"href"), mysql_result($stories,$i,"link"),mysql_result($stories,$i,"promoted")); } rss_stop(); function rss_start() { header("Content-Type: text/xml"); echo "<?xml version="1.0"?>"; ?> <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel> <title>reggit/dreddit: what's new on digg</title> <link>http://digg.com/</link> <description>The latest stories, voted on by users like you.</description> <?php } function rss_stop() { ?> </channel></rss> <?php } function rss_row($title,$href,$link,$time) { echo "\n\t<item>\n\t\t<title>".dehtml($title)."</title>\n\t\t<link>".dehtml($link)."</link>\n\t\t<dc:date>".gmdate("T",$time)."</dc:date>\n\t\t \t<description><![CDATA[<a href="".dehtml($link)."">[link]</a> <a href="$href">[more]</a> <a href="".str_replace("digg.com","duggmirror.com",$href)."">[dugg]</a>]]></description> </item>"; }
Where to go: The RSS feed can further be customized to enable the ability to have the RSS feed of only certain categories, or of only stories containing certain phrases.
Part Fun – Live Diggs – A real application
After screwing with pointless tag clouds and a neat, although not important, reddit clone, we can now build an actual application.
This app will allow you to enter a title of a stories and watch as people digg it. It will display the username and time dugg, and refresh all ajaxily, cause it's got to web 2.0, right?
First, you enter the title of the story, which then will try to find it's story id first by searching our database, then, if that fails, using the Digg API story search.
Once it has the story id, it now displays at most the last 30 diggs, and refreshing every 30 seconds to add new diggs. The diggs are retrieved from our database, though using the digg api to select the new diggs could be done just as well, but we have a table full of diggs, and we're going to use it, dammit! Plus, I think it's a little nicer on digg, since if you have a fair amount of users, instead of that lot of people requesting or your server on their behalf requesting diggs every 30 seconds, there is just a consistent 1 request every 30 seconds.
Anyways, the javascript in this is just hacked together using prototype.js, meaning there are probably better ways to do this, but whatever. This works….for the most part. Sometimes there might be repeat here and there, but nothing devastating.
Here it is:
livediggs.php:
<?php require 'config.php'; require 'functions.php'; require_once 'Services/Digg.php'; if(isset($_GET['title']) AND $_GET['title'] != "") { $result = mysql_query("SELECT `id` FROM `diggstories` WHERE `title` = '".sqlquote($_GET['title'])."' LIMIT 1"); if(mysql_numrows($result) == 1) { if($_GET['mode'] == "ajax") { echo "storyid=".mysql_result($result,0,"id"); exit; } else { header("Location: livediggs.php?storyid=".mysql_result($result,0,"id")); exit; } } // This doesn't work? $api = Services_Digg::factory('Stories'); $story = $api->getStoryByTitle(preg_replace("/[^a-z0-9]/ix","_",$_GET['title'])); //print_r($story); if(isset($story->id) AND $story->id != "" AND is_numeric($story->id)) { if($_GET['mode'] == "ajax") { echo "storyid=".$story->id; exit; } else { header("Location: livediggs.php?storyid=".$story->id); exit; } } echo "storynotfound"; } elseif(isset($_GET['storyid']) AND $_GET['storyid'] != "" AND is_numeric($_GET['storyid'])) { if($_GET['mode'] == "ajax") { if(isset($_GET['time']) AND is_numeric($_GET['time'])) { if($_GET['time'] == 0) { echo "&&×tamp=".time()."&&&"; exit; } $where = " AND `digging_id` > '".(sqlquote($_GET['time']))."' "; $query = "SELECT * FROM `diggslive` WHERE `story_id` = '".sqlquote($_GET['storyid'])."' $where ORDER BY `time` DESC"; //echo $query; $result = mysql_query($query); $num = mysql_numrows($result); for($i=0;$i<$num;$i++) { //echo "digger=".mysql_result($result,$i,"user")."&time=".timeparse(mysql_result($result,$i,"time"))."\n"; echo "<tr class="digg"><td><a href="http://digg.com/users/".mysql_result($result,$i,"username")."">".mysql_result($result,$i,"username")."</a></td><td class="time" id="".mysql_result($result,$i,"time")."">".timeparse(mysql_result($result,$i,"time"))."</td></tr>"; } echo "&&×tamp=".( ($num == 0) ? $_GET['time'] : mysql_result($result,$num-1,"digging_id"))."&&&"; exit; } else { $result = mysql_query("SELECT * FROM `diggslive` WHERE `story_id` = '".sqlquote($_GET['storyid'])."' ORDER BY `time` DESC LIMIT 0, 30"); $num = mysql_numrows($result); if($num < 1) { echo "No diggs :("; std_stop(); } echo "<table id="diggs" align="center">"; for($i=0;$i<$num;$i++) { echo "<tr class="digg"><td><a href="http://digg.com/users/".mysql_result($result,$i,"username")."">".mysql_result($result,$i,"username")."</a></td><td class="time" id="".mysql_result($result,$i,"time")."">".timeparse(mysql_result($result,$i,"time"))."</td></tr>"; } echo "</table>"; echo "<script type="text/javascript"> time = ".mysql_result($result,0,"digging_id")."; </script>"; } } else { std_start(); $result = mysql_query("SELECT * FROM `diggslive` WHERE `story_id` = '".sqlquote($_GET['storyid'])."' ORDER BY `time` LIMIT 0, 30"); $num = mysql_numrows($result); if($num < 1) { echo "No diggs :("; std_stop(); } echo "<table id="diggs" align="center">"; for($i=0;$i<$num;$i++) { echo "<tr class="digg"><td><a href="http://digg.com/user/".mysql_result($result,$i,"username")."">".mysql_result($result,$i,"username")."</a></td><td>".timeparse(mysql_result($result,$i,"time"))."</td></tr>"; } //$lastdiggid = mysql_result($result,$num-1,"digging_id"); //echo $lastdiggid."!!@#!@#"; echo "</table>"; std_stop(); } } else { std_start(); form(); std_stop(); } function std_start() { ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>Live Diggs</title> <script type="text/javascript" src="js/prototype.js"></script> <!--<script type="text/javascript" src="js/scriptaculous.js"></script>--> <script type="text/javascript"> var time = <?php echo time();// global $lastdiggid; echo $lastdiggid; ?>; var resp = ""; function submitform(title) { $('loading').show(); var uri = "livediggs.php"; var params = "title="+escape(title)+"&mode=ajax"; var ajax = new Ajax.Request( uri, {method:'get', parameters: params, onFailure: loadpage, onComplete: getdiggs} ); return false; } function getdiggs(info) { resp = info.responseText; if(resp == "storynotfound") { $('error').show(); $('loading').hide(); return false; } var uri = "livediggs.php"; var params = resp+"&mode=ajax"; var ajax = new Ajax.Request( uri, {method:'get', parameters: params, onFailure: loadpage, onComplete:firstdone } ); //update(resp); } function firstdone(info) { response = info.responseText; response.evalScripts(); $('content').innerHTML = response; update(resp); } function update(story) { updater(story); setTimeout('update("'+story+'")',30000); } function updater(story) { var uri = 'livediggs.php'; var params = "time="+time+"&mode=ajax&"+story; var ajax = new Ajax.Request( uri, { method:'get',parameters: params, onFailure:loadpage, onComplete:updatepage} ); return true; } function updatepage(resp) { var diggs = resp.responseText; var pos = diggs.indexOf("&&×tamp="); var timestamp = diggs.substring(pos+13,pos+23); diggs = diggs.substr(0,pos); time = timestamp; $('diggs').innerHTML = diggs+$('diggs').innerHTML; updatetimes(); removeold(); return true; } function updatetimes() { var times = $$('td.time'); for(var i=0;i<times.length;i++) { times[i].innerHTML = timeparse(times[i].id); } } function removeold() { var olds = $$('tr.digg'); for(var i=30;i<olds.length;i++) { olds[i].remove(olds[i]); } } function timeparse(thetime) { var curtime = new Date(); curtime = Math.floor(curtime.getTime()/1000); var since = curtime - thetime; if(since < 60) { return (since == 1) ? since+" second ago" : since+" seconds ago"; } else if(since < 3600) { since = Math.floor(since/60); return (since == 1) ? since+" minute ago" : since+" minutes ago"; } else if(since < 86400) { since = Math.floor(since/3600); return (since == 1) ? since+" hour ago" : since+" hours ago"; } else { since = Math.floor(since/86400); return (since == 1) ? since+" day ago" : since+" days ago"; } } function loadpage() { $('error').innerHTML = "Some crazy AJAX error probably"; $('error').show(); } </script> </head> <body> <?php } function std_stop() { echo "</body></html>"; exit; } function form() { echo "<div id="content"> <div style="text-align:center; font-size: 150%;">This will allow you to watch people digg your story, live!<br /> <span id="error" style="display: none; color:red;" > Story not found<br /></span> <form action="livediggs.php" onsubmit="return submitform(this.title.value)" id="titleform"> <input type="hidden" name="nothing" value="" /><!-- This is a stupid hack, because for some reason the first input form couldn't have a starting value attribute. --> <input type="text" name="title" value="Enter your story title" style="font-size: 125%; width:75%; text-align: center; " onfocus="if(this.value == 'Enter your story title') { this.value = ''; } " /> <br /><input type="submit" value="Watch Diggs" style="font-size: 125%; text-align: center;" /> <span id="loading" style="display:none;"><img style="display:inline" src="ajax-loader.gif" alt="loading..." /></span> </form> </div> </div> "; }
Conclusion
:
If you want to download all of these scripts, you can do so here:
Download – ZIP
Download – Gzip
They're licensed under GPL. If you have any questions, suggestions, or comments, just leave a comment or contact me.













Just a little note that Services_Digg was accepted into the PEAR repository so you'll be able to install it via the normal PEAR procedures Real Soon Now[tm].
Yay! It's now live on pear.php.net
http://pear.php.net/package/Services_Digg
You can install it via this command:
pear install channel://pear.php.net/Services_Digg-0.1.0You can't install it via just
pear install Services_Digg, since it wants a stable version, but it's only in alpha, or at least that was the case for me.Congrats on getting it accepted.
Hey, I'm just getting to grips with the Digg API and this is a really great walk-around and demo. Thank you
great stuff man thank you for all the efford wish there was a clean and simple digg like clone
raf, there's pligg.
yes I know skeet but i was looking for something more simple , without any copyright credit and all.
hi, thanks for this code,
when i do the update.php i got a blank page. how can i fix this? thanks