Archive for April, 2007

Simple bookmark manager in 20 lines of PHP.

April 28, 2007

There are dozens of bookmarks managers on the Web, but for my most simple needs, I decided to make my own. I have bookmarks at Yahoo, but do use them efficiently, I have to install their Toolbar, and you can never know what does it do with your private infomation. Del.icio.us is good too, but the default setting is that your bookmarks are public (well, it’s the main idea of the service actually). So, there are no ideal services, and tired of keeping different sets of bookmarks on my home and office computers, I implemented a very rudimentary but my own bookmarks storage script. It takes less than twenty lines of PHP but it does what I need. To use it, you have to have your own PHP-capable site on the Web (even a free one should work). It’s important to note that there are no login checks and the only way to prevent the owner from troubles is to use some cryptic file names (instead of ones below). “Troubles” means not making your private bookmarks public (which is not good too), but allowing anyone to post there, which can be used to plant some nasty Javascript into your file to hack your browser.

The principle is very simple. If you supply at least the URL parameter, it adds a new bookmark and then displays the whole list. If no URL parameter is given – it’s just the display. To make things easier, you have to create a bookmark in your browser with the following Javascript instead of usual URL (note that there is no http:// stuff, and don’t forget to replace localhost and bm.php with your own host and file names):

javascript:location.href=’http://localhost/bm/bm.php?url=’+encodeURIComponent(location.href)+’&title=’+encodeURIComponent(document.title)

The PHP code is below. Normally, WordPress screws up the quote symbols (both single and double), so you have to retype them. To prevent warnings on the first use, either create the include file or try it right with adding a bookmark.


<?
print '<html><title>Bookmarks</title><body>Bookmarks<br><br><br>';

if( isset($_GET['url']) ) {
$url = $_GET['url'];
$title = ”;
if( isset($_GET['title']) ) $title = $_GET['title'];
if( $title == ” ) $title = $url;
$handle = fopen(“bm.inc”, “a”);
fwrite($handle, ‘<a href=”‘.$url.’”>’.$title.’<br>’);
fclose($handle);
}

include ‘bm.inc’;

print ‘</body></html>’;

?>

Obviously, it's not a full manager because there are no edit or delete features. But as I almost never to that, it's fine with me to edit the file directly when needed.

Webmaster notification functions in PHP

April 18, 2007

I wanted to add notification functionalities to Kyusl.com, so that I could be informed about certain events, but at the same time there will be a limit on how many notifications will I get on a given day. My cell phone plan at German Vodafone comes with an E-mail account, and every time I get an E-mail, Vodafone sends me an SMS that includes the subject of the E-mail. To read the message, you have to have an appropriate cell phone, but for short notifications you can pack most important things right into the subject line. That allows me to get the most important information for free almost anywhere in the world, which is important if I go on vacation (normally, SMS roaming doesn’t cost anything).

I called the concept “counted events”. One of the “events” would happen when someone uses the contact form, which sends a message (not E-mail!) to the administrative user. If that happens, I want to inform myself about that, but not more often than two times per day. Then I can login into Kyusl and check the message.

I have decided to keep the track of one “counted event” in one database record per user. Obviously, you have to reasonably limit the number of events per given period if you don’t want to have several records per event. I allow maximum five events per period, here’s MySQL declaration:

create table ceventlog (
    uid           integer,
    cevent        varchar(8) not null,
    alimit        integer,
    period        integer,
    t1            integer,
    t2            integer,
    t3            integer,
    t4            integer,
    t5            integer,
    primary key ceventlog_key (uid,cevent)
);

Period field measures the period in seconds, and t1 to t5 represent a rolling log of timestamps telling when has the event happened. Every time the event happens, I shift the values “down” and write the current UNIX timestamp into t1. Then if, say, I want to check whether I have reached four events in the given period, I check the time difference between now and t4, whether it exceeds the time limit in the “period” field.

Two functions can either init an event or reset it. First I had a reset in the init function (to count for the case when calling insert on existing record), but then decided that if I don’t reset the event in the init, I can safely call the init every time without zapping the log:

function InitCEvent($uid, $cevent, $limit, $period) {
  $query = "insert into ceventlog (uid,cevent,alimit,period,t1,t2,t3,t4,t5)".
               " values ($uid,'$cevent',$limit,$period,0,0,0,0,0)";
  $result = mysql_query($query);
  // if we don't call Reset - insert will fail if the record exists,
  // so Init can be called every time, and Reset can be called on demand.
  // ResetCEvent($uid, $cevent);  // just in case we had a record already
}

function ResetCEvent($uid, $cevent) {
  $query = "update ceventlog set t1=0 t2=0 t3=0 t4=0 t5=0".
                 " where uid=$uid and cevent='$cevent'";
  $result = mysql_query($query);
}

When the event happens, I shift the values in the event log with the following function:

function ShiftCEvent($uid, $cevent) {
  $query = "select alimit,t1,t2,t3,t4,t5 from ceventlog".
                      " where uid=$uid and cevent='$cevent'";
  $result = mysql_query($query);
  if (!$result) {
    return 0;
  }
  else {
    $num_rows = mysql_num_rows($result);
    if( $num_rows == 0 ) {
      return 0;
    }
    else {
      $row = mysql_fetch_row($result);
      list($limit,$t1,$t2,$t3,$t4,$t5) = $row;
      $t5 = $t4;
      $t4 = $t3;
      $t3 = $t2;
      $t2 = $t1;
      $t1 = time();
      $query = "update ceventlog set t1=$t1, t2=$t2, t3=$t3, t4=$t4, t5=$t5".
                         " where uid=$uid and cevent='$cevent'";
      $result = mysql_query($query);
    }
  }
}

And finally, to check whether the limit is reached, I have this:

function CEventLimitReached($uid, $cevent) {
  $query = "select alimit,period,t1,t2,t3,t4,t5 from ceventlog".
                       " where uid=$uid and cevent='$cevent'";
  $result = mysql_query($query);
  if (!$result) {
    return 0;
  }
  else {
    $num_rows = mysql_num_rows($result);
    if( $num_rows == 0 ) {
      return 0;
    }
    else {
      $row = mysql_fetch_row($result);
      list($limit,$period,$t1,$t2,$t3,$t4,$t5) = $row;
      $now = time();
      if( $limit == 1 ) {
	if( $t1 == 0 || $now - $t1 > $period ) return 0;
	else return 1;
      }
      elseif( $limit == 2 ) {
	if( $t2 == 0 || $now - $t2 > $period ) return 0;
	else return 1;
      }
      elseif( $limit == 3 ) {
	if( $t3 == 0 || $now - $t3 > $period ) return 0;
	else return 1;
      }
      elseif( $limit == 4 ) {
	if( $t4 == 0 || $now - $t4 > $period ) return 0;
	else return 1;
      }
      elseif( $limit == 5 ) {
	if( $t5 == 0 || $now - $t5 > $period ) return 0;
	else return 1;
      }
    }
  }
}

For each event, I have written a corresponding notification function. For notifications about admin messages mentioned above, I have the code similar to:

function AdminMessageNotify() {
  InitCEvent(1,'ADM_MSG',2,3600*24);
  if( !CEventLimitReached(1,'ADM_MSG') ) {
    $message = 'New message to admin!';
    $email = 'my_email_id_at@vodafone.de';
    $send = @mail("$email", "New adm msg", $message,
		 "From: myuser@mydomain.com\\r\\n"
		 ."Reply-To: myuser@mydomain.com\\r\\n" );
  }
  ShiftCEvent(1,'ADM_MSG');
}

The code in the contact.php, after sending a message to administrator, is simply the call of AdminMessageNotify.

That simple set of functions can be used easily to notify webmasters about data processing errors (database, for example), traffic and many others that may require webmaster’s attention. I will greatly appreciate any improvement ideas you may want to share.