#!/usr/bin/perl # HyperCal by Richard Bowen # A HTML datebook. # # This part draws the calendar and links it to the other scripts. # Can be called as http://URL/hypercal, or with arguments # as http://URL/hypercal?month&year require `httools.pl'; require `variables'; $args=$ENV{`QUERY_STRING'}; ($sub=$ENV{`PATH_INFO'})=~ s#^/##; if ($sub=~/personal/) { &personal }; if ($sub eq "goto") { &goto } else { &main }; sub main { &header; ($this_month,$this_year)=split(/&/,$args); if ($args eq "") { # Defaults to current date if none specified. &date; # Calls the todays date subroutine from httools.pl $this_month=$month; $this_year=$year;} ($junksec,$junkmin,$junkhour,$today_day,$today_month,$today_year,$junkwday, $junkyday,$junkisdst)=localtime(time);$today_year+=1900;$today_month+=1; &month_txt("$this_month"); print "<html><head><title>$title - $month_txt, $this_year</title></head>\n"; print "<body"; $month_image=@month_images[$this_month-1]; ($icon,$bg,$color,$text,$link,$vlink)=split(/~~/,$month_image); print " background=\"$bg\"" unless ($bg eq "none"); print " bgcolor=\"$color\"" unless ($color eq "none"); print " text=\"$text\"" unless ($text eq "none"); print " link=\"$link\"" unless ($link eq "none"); print " vlink=\"$vlink\"" unless ($vlink eq "none"); print ">\n"; open (DATES, $datebook); @datebook=<DATES>; @months=("December", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"); @last_days=(31,31,28,31,30,31,30,31,31,30,31,30,31); @days_of_week=("Sun","Mon","Tue","Wed","Thu","Fri","Sat"); @month_offset=(3,3,0,3,2,3,2,3,3,2,3,2,3); print "<center>"; print "<table border=6 cellpadding=5 width=100%>\n"; print "<tr><td align=center colspan=7><center><h2>@months[$this_month], $this_year</h2>"; if ($multi_user eq "yes" && $personal_on eq "no") { print "<form method=post action=\"$base_url$hypercal/personal\">"; print "<input type=submit value=\"Go To Personal Calendar\">";} elsif ($personal_on eq "yes") { ($link=$hypercal)=~s/\/personal//g; print "<form method=post action=\"$base_url$link\">"; print "<input type=submit value=\"Go To Public Calendar\">";} print "<img src=\"$icon\" alt=\"\" align=middle hspace=30>" unless ($icon eq "none"); print "</form>"; print "</center>"; $week_days=join(" <th> ",@days_of_week); print "<tr><th>$week_days<br>\n"; for ($i=1906; $i<$this_year; $i++) { $days_offset++; if (($i)%4==0) {$days_offset++}; # Leap years } # end for if (($this_year%4)==0 && ($this_month>2)) {$days_offset++}; # Current year is leap year for ($j=1; $j<($this_month);$j++) { $days_offset+=@month_offset[$j]}; $first_day_of_month=($days_offset%7); $last_day_in_month=@last_days[$this_month]; if (($this_month==2)&&($this_year%4==0)){$last_day_in_month=29}; $date_place=0; while ($date_place<$last_day_in_month) { print "<tr>"; for ($j=0; $j<=6; $j++) { if (($first_day_of_month>=0)||($date_place>=$last_day_in_month)) {print "<td align=center>-"; $first_day_of_month--} else { $date_place++; print "<td align=center> <a href=\"$base_url$disp_day?$this_month &$date_place&$this_year\"> $date_place </a>"; &appoints($this_month,$date_place,$this_year); if ($today_day==$date_place && $today_month==$this_month && $today_year==$this_year){print"<br><b><font color=red>TODAY</font></b>"}; } # end else } #end for print "<br>\n"; } # end while open (ANNO, "$announce"); @announce=<ANNO>; $search=$this_month."_".$this_year; $any_announce="no"; for $announces (@announce) { ($mo,$msg,$aid)=split(/&&/,$announces); if ($mo eq $search){ if ($any_announce eq "no"){print "<br><tr><td align=center colspan=7>";} print "<center><b>$msg</b></center><br>"; $any_announce="yes";} } print "</table></center><br>\n"; print "<center>Select a day to see the appointments for that day. Numbers in parentheses indicate how many appointments are on that day.</center>"; print "<hr>\n"; print "<center>Go to:</center><br>\n"; print "<center>"; # print "<table border=0 cellpadding=5 width=100%>"; $last_year=$this_year; $last_month=($this_month-1); if ($last_month == 0) {$last_month=12; $last_year=($this_year-1);} print "[ <a href=\"$base_url$hypercal?$last_month&$last_year\"> Previous month </a>|\n"; $next_year=$this_year; $next_month=($this_month+1); if ($next_month == 13) {$next_month=1; $next_year=($this_year+1);} print "<a href=\"$base_url$hypercal?$next_month&$next_year\"> Next month </a>|"; print "<a href=\"$base_url$hypercal\"> Current month </a>] </center><br>\n"; print "<center><form method=get action=$base_url$hypercal/goto>"; print "<input type=submit value=\"Jump\"> to <input name=\"month\" size=2> \/ <input name=\"year\" size=4 value=\"$this_year\">"; print "<input type=hidden name=\"this_year\" value=\"$this_year\">"; print "</form></center><hr>"; print "<center>"; # print "<table width=100%><tr><td>"; print "[ <a href=\"$base_url$edit_announce?$this_month&$this_year\"> Add an announcement</a> for this month. "; print "| <a href=\"$base_url$edit_announce?$this_month&$this_year&delete\"> Delete an announcement</a> from this month." unless ($any_announce eq "no"); print " ]"; print "</center>"; print "<center>"; print "<hr>[ <a href=\"http://www.rcbowen.com/perl/HyperCal.html\"> About HyperCal</a>."; foreach $item (@linkto) { ($url, $page_title)=split(/~~/,$item); print " | <a href=\"$url\">$page_title</a>"; } if ($multi_user eq "yes") { print " | <a href=\"$base_url$change_passwd\">Change your user password</a>"} print " ]</center><br>\n"; print "HyperCal, Version $version, Copyright © 1996, Richard Bowen. All rights reserved.<br>\n"; &footer; } # End of sub main sub appoints { $found=0; &julean($_[0],$_[1],$_[2]); # Julean date of day for $entry (@datebook) { @temporary=split(/~~~/,$entry); if (@temporary[0]==$jule) {$found++}}; if ($found != 0) {print " ($found)"}; } sub goto { ($pair1, $pair2, $pair3)=split(/&/,$args); ($junk, $month)=split(/=/, $pair1); ($junk, $year)=split(/=/, $pair2); ($junk, $this_year)=split(/=/, $pair3); # Need some error checking ... if ($month eq ""){$month=1}; if ($year eq""){$year=$this_year}; if ($month>12) {$month=12}; if ($month<1) {$month=1}; if ($year<1) {$year=1}; if ($year>9999) {$year=9999}; $args="$month&$year"; &main } sub personal { $sub=~s/personal\///; $user_id=$ENV{`REMOTE_USER'}; $user_variables=~s/USERNAME/$user_id/; require "$user_variables"; }
Let's take a look at how HyperCal works. HyperCal uses a library called httools.pm
, which takes care of things like printing headers and parsing forms. CGI.pm could be used instead of httools.pm with a few simple modifications to HyperCal's code. The httools.pm module supports the following functions in HyperCal:
Function | Usage and Description |
header |
Prints MIME content type header. |
title |
Prints title for HTML page: &title(`Desired title') |
form_parse |
Parses form, places variables in $FORM{`variable_name'} . |
footer |
Prints generic footer. |
date |
Returns today's date in nicer format. |
julean |
Returns Julian date with Jan 1, 1995, as day 1. |
todayjulean | Returns today's Julian date, calls julean. |
HyperCal stores the URI encoded path info in the QUERY_STRING to a variable named $args
. Path info (variables encoded in the URI) is next filtered into the variable $sub
. The variable $sub
is then evaluated to determine what output the program should generate.
The hypercal.cgi program will do different things based on what variables are present (or not present) in the QUERY_STRING (data appended to the URI) when it is run. The principle behind this concept is simple. The HTML output (calendar) generated by running the program contains HTML links back to the program. In other words, the HTML calendar contains links and forms whose URIs reference back to the hypercal.cgi script again. However, the links also contain variables appended to the URI. When a user clicks on the URI with the appended variables, the program is run again. This time it parses the variables in the URI and can react differently based on those self-supplied variables. This is precisely what is happening in lines 15-17. If no variables are found in the URI, the else
statement instructs the program to execute the &main
subroutine that prints the HTML for the calendar.
If variables are supplied to HyperCal in its URI, one of two subroutines is called first. The &personal
subroutine determines if a personal calendar is being used and alters a few variables accordingly. I'll discuss using personal calendars in more depth in a moment. The other subroutine that may be called is &goto
. The &goto
subroutine directs HyperCal to display a specific month. The month and year must be supplied in the URI, in the following format: http://URI/hypercal.cgi/goto?month=9&year=1999&this_year=1996
The &goto
subroutine reads the three name/value pairs, trashes the names, and stores the values to $month
, $year
, and $this_year
. The month and year are then stored in $args
, and the main subroutine is called to display the requested month. Note that if $args
does not contain a prespecified month, the current month is used.
Using multiple users requires a few extra steps. In the configuration file (discussed next), the $multi_user
variable must be set to yes
. Note that in the &main
subroutine in hypercal.cgi, if this variable is set to yes
, a button Go To Personal Calendar
is displayed.
Personal calendars are protected by restricting access to the user directory in which HyperCal keeps the user data using the .htgroup
and .htpasswd
file. The usage of .htpasswd
is discussed more thoroughly in Chapter 3. The cal_admin.cgi program is used to add users and passwords to the .htpasswd
file, essentially adding users to the calendar. To set up HyperCal in multi-user mode, variables in the config file need to be set.