#!/usr/local/bin/perl ######################################################################### ## ## Content Rotator ## By Larry Israel ## ## Version 1.00 December 18, 2002 ## http://www.larry.israel.name/scripts/ ## ######################################################################### # COPYRIGHT NOTICE # # Copyright 2002 Larry Israel. All Rights Reserved. # # You can use, modify, and distribute this script freely, as long as # this copyright notice and the header above remain intact. You may not # sell this script unless you have obtained prior written permission # from the author. # # This program is distributed as-is and without warranty of any kind, # either express or implied. In no event shall Larry Israel be liable # for any damages, losses, or causes of action. ######################################################################### # # DESCRIPTION # # Content Rotator is a "photo of the day" script that delivers a # different block of text each day to your web pages. It was designed to # rotate images and the HTML code surrounding the tag, but it will # rotate any type of web content. The script can be used on multiple web # pages and will deliver different output to each page. # # Daily rotation discourages visitors from reloading the page and # provides consistency for all visitors (in case someone wants to tell # a friend to look at the picture on some page). The script outputs the # code blocks in sequential order, not at random, which gives you # control over the order in which they will appear. I wrote this script # because I couldn't find an existing script that did what I wanted. # # Content Rotator is freeware. If you use it, it would be nice to hear # about it. Please send me the URL, I'm curious to see how it's being # used. If you encounter a problem with the script, please let me know. # Maybe I'll fix it. You're welcome to send suggestions or mods. If I # like them, I'll incorporate them. # # Warning: This script is not intended for use on a high-traffic site. # There is no file locking, so the data file could get corrupted if hit # by simultaneous calls to the script. # # # REQUIREMENTS # # 1. Perl. This is a perl script. It works under Perl 5.0x. # It's untested with other versions but will probably work. # 2. Designed to run on Unix, but should also work on other platforms. # 3. Designed to be called via SSIs (server side includes). To use the # script in this manner, your server must support SSIs. (Depending # on the server configuration, you probably need to name your web # pages with a .shtml extension.) Check with your web host. # Alternatively, the script can be called directly from web browsers # to rotate complete web pages (read about the options, below). # # # VERSION HISTORY # # December 18, 2002: Version 1.00 released. # # December 12-17, 2002: Added $timediff variable. Improved the way the # script determines the page name and resolved potential problems if # called from unusual URLs. # # December 12, 2002: First used on a public web site. # # Early December, 2002: Script created and tested privately. # # # NARRATIVE INSTRUCTIONS # # Place an SSI at the spot on your web page(s) where you want the HTML # output to be located. Alternatively, you could put a link on your site # that goes directly to this script, if you want to rotate complete web # pages. # # The script does not generate any HTML. You must put all the output # (HTML or whatever you want) into the rotate_output.txt file. Each day, # the script delivers the same block of code to everyone who visits that # page. The output rotates daily, in sequence, based on the number you # assign to each code block in the rotate_output.txt file. Once the code # blocks have all been used, the script begins again at the first (001). # The script stores data about the rotation in the rotate_data.txt file. # # # STEP-BY-STEP INSTRUCTIONS # # 1. Put your output code blocks into the rotate_output.txt file. # Assign each code block a different 3-digit number (see the # following example and also the rotate_output.txt file example). # End each code block with ~; # Example: # $outputnum{'001'} = qq~~; # Each $outputnum must be three digits and must be different from # the rest. The numbers should be in sequence, for example: 001, 002, # 003, 004, 005. Use as many lines as you want for each entry. # 2. Configure the script, below. Also, make sure the top line points # to the location of Perl on your server. # 3. Upload the two files: rotate.cgi and rotate_output.txt. Usually, # these will go in your cgi-bin. Check with your web host if you're # not sure where to put scripts. # 4. Set permissions on rotate.cgi to 755 (world executable -rwxr-xr-x). # rotate_output.txt just needs to be readable by the script; 744 # (-rwxr--r--) is the usual permission setting. # 5. The script must have permission to create and write to a # rotate_data.txt file, at the location you define in the # configuration. The script will create this file the first time it # runs as long as it has permission to do so. (Depending on how your # server is configured, you may need to upload a blank # rotate_data.txt file. You may also need to chmod it 766 # [-rwxrw-rw-], but if so, it would be wise to put it in a different # directory from where scripts are run.) # 6. Add an SSI to your web pages at the locations where you want the # code blocks to appear. Example: # # 7. Upload the web pages, then call them all with your browser. Make # sure it's working right. # # # OPTIONS FOR MULTIPLE WEB PAGES AND/OR MULTIPLE BLOCKS PER PAGE # # The script can be called from multiple web pages and will deliver # different output to each page. (In fact, the script was written for # this purpose.) To implement this: # 1. Add the SSI calls to all your web pages. # 2. Call each page from your browser. # 3. If you want, go into the rotate_data.txt file and change some of # the 3-digit count numbers at the end of each line. # 4. Setting $allpagesdifferent to 1 is also wise, but it may not work # exactly as you wish, if not all your pages are visited every day. # Therefore, if you're picky about the rotation, you may want to # check the rotate_data.txt file occasionally and edit the counts # as desired. # # If you want the same output to appear in multiple places on a page, # just put the same SSI in more than one place. (The extent to which the # script can handle multiple simultaneous calls, I'm not sure, but you # can try it and see. Maybe there's no problem.) # # You can have several rotating blocks on a single web page, with each # SSI displaying different output. To do this, use muliple copies of # the script and point each SSI to a different script file. # For example, put these three SSIs on one web page: # # # # You must point each copy of the script to its own data file. # Go into rotate1.cgi and set $datafile = "rotate_data1.txt". # In rotate2.cgi, set $datafile = "rotate_data2.txt", etc. # You will need to make sure the counts for a given page are different # in the various data files, or else point to output files containing # different output. You can point each script to its own output file, or # they can all share the same output file, your choice. To top it all # off, you could even put these three SSI calls on multiple pages as # well. # # Finally, you can use the script to rotate complete web pages. To do # this, point links directly to the script. Put the complete HTML code # of each of the pages you want to rotate into the output file, as # numbered blocks. # # ##### CONFIGURATION ##################################################### # During installation only, uncomment the following line to help you debug: # use CGI::Carp qw(fatalsToBrowser); # Define the location (the path and file name) of the # rotate_output.txt and rotate_data.txt files: $outputfile = "rotate_output.txt"; $datafile = "rotate_data.txt"; # Define the highest $outputnum that exists in the output file. # This must be a 3-digit number only. $highestoutputnum = "109"; # On what number shall we begin the rotation for any new web pages? # This must be a 3-digit number that is included in the $outputfile. # In most cases, you'll want "001". The first time the script is # called from a specific URL, this output block will be delivered. $newcount = "001"; # Define the file name of your home page. (The script may need this if # someone goes to the root of your site without entering any file name.) $homepage = "index.html"; # If the script will be called from more than one web page and you want # to make sure no two pages will display the same output block on the # same day, set $allpagesdifferent to 1. If you prefer each page to # display the next block in sequence even if some are the same, turn # this option off (set to 0). If you use $allpagesdifferent, you also # have the option to define a $priority_webpage, so that this one page # (perhaps your home page) always displays the next output block in # sequence, regardless of a number conflict with another page. Define # the $priority_webpage variable as the location (path from web root and # file name, exactly as it appears in the data file) of the page that # you want to give priority. $allpagesdifferent = 1; $priority_webpage = "/index.html"; # The rotation occurs at midnight. If you are in one time zone and your # web host is in another, you can correct the time difference. Define # $timediff as the number of hours you want to adjust the server clock. # For example, if you're in the Pacific time zone but your server is in # the Eastern time zone, set this to "-3". $timediff = -3; # The following two options may be useful in case you haven't set up the # script correctly. # To show error messages on your web pages, set $printerrors = 1. If you # want to hide errors from your visitors, turn this option off. (If you # have $printerrors turned on, you can format any error messages that # may appear by using the CSS class="scripterror" in your style sheet.) $printerrors = 0; # To skip any output numbers that are missing from the output file (or # those that exist but have no contents), set $skipmissingoutput to 1. # The script will deliver the next existing non-blank code block. With # $skipmissingoutput = 0, if the code block ($outputnum) does not exist # or is empty, there will be an error (but the error message will appear # on the web page only if $printerrors = 1). $skipmissingoutput = 1; # That's all. Leave the rest alone unless you know what you're doing. ##### MAIN PROGRAM ##################################################### # Initialize variables print "Content-type: text/html\n\n"; if ($ENV{'DOCUMENT_URI'} =~ /\S/) {$url = "$ENV{'DOCUMENT_URI'}"} elsif ($ENV{'REQUEST_URI'}) { $url = "$ENV{'REQUEST_URI'}"; if ($ENV{'QUERY_STRING'}) {$url =~ s/\?$ENV{'QUERY_STRING'}//} $url =~ s/\?//; } if ($ENV{'HTTP_HOST'}) {$url =~ s/$ENV{'HTTP_HOST'}/$'/} $url =~ s/\/\//\//g; $url =~ s/\/\//\//g; $homepage =~ s/^\///; if ($url eq "/") {$url = "$homepage"} $url =~ s/\/$//; if ($url !~ m/^\//) {$url = "/$url"} if (!($url) || ($url eq "/")) {&Error("[Error: Script is unable to determine the URL of this page]")} $today = &DateToday; $date = "$today"; unless (($newcount) && ($newcount <= $highestoutputnum)) {$newcount = "001"} $newcount = sprintf ("%03d", $newcount); $count = $newcount; $newline = "$url $date $count\n"; $priority_webpage =~ s/^\///; if ($priority_webpage !~ m/^\//) {$priority_webpage = "/$priority_webpage"} $urldataexists = $datachange = $samecount = $changeurldata = $startedover = 0; # Read data from disk. If needed, increment the count, add the url to the data, or create the data if (-e "$datafile") { open (DATA, "<$datafile") || &Error("[Error: Script can't open the data file to read]"); @data = ; close (DATA); foreach $line (@data) { $line =~ s/^ //; $line =~ s/^\s//; $line =~ s/ $//; $line =~ s/\s$/\n/; if ($line =~ /^$url/) { $urldataexists = 1; chomp ($line); ($url, $date, $count) = split (/ /, $line); if ($date ne $today) { $date = "$today"; $count++; $count = sprintf ("%03d", $count); if ($count > $highestoutputnum) {$count = "001"} $line = "$url $date $count\n"; $datachange = 1; } } } unless ($urldataexists) { push (@data, $newline); $datachange = 1; } } else { @data = "$newline"; $datachange = 1; } # If $allpagesdifferent setting is on and two counts are the same, # make one of the counts totally different if ($allpagesdifferent) { foreach $line (@data) { if (($line !~ /^$url/) && ($line =~ /$today/) && ($line =~ /$count/)) { if ($url eq $priority_webpage) { chomp ($line); ($otherurl, $otherdate, $othercount) = split (/ /, $line); $othercount = $othercount + ($highestoutputnum/2); if ($othercount > $highestoutputnum) { $othercount = $othercount - $highestoutputnum; } if (($othercount < 1) || ($othercount > $highestoutputnum)) {$othercount = "001"} $othercount = sprintf ("%03d", $othercount); $line = "$otherurl $otherdate $othercount\n"; $datachange = 1; } else {$changeurldata = 1} } } if ($changeurldata) { foreach $line (@data) { if ($line =~ /^$url/) { chomp ($line); ($url, $date, $count) = split (/ /, $line); $count = $count + ($highestoutputnum/2); if ($count > $highestoutputnum) {$count = $count - $highestoutputnum} if (($count < 1) || ($count > $highestoutputnum)) {$count = "001"} $count = sprintf ("%03d", $count); $line = "$url $date $count\n"; $changeurldata = 0; $datachange = 1; } } } } # If two counts are still the same, add one repeatedly until they're different if (($allpagesdifferent) && ($datachange)) { $changeurldata = 0; &SameCount; while ($samecount) { if ($startedover == 2) {last} foreach $line (@data) { if (($line !~ /^$url/) && ($line =~ /$today/) && ($line =~ /$count/)) { if ($url eq $priority_webpage) { chomp ($line); ($otherurl, $otherdate, $othercount) = split (/ /, $line); $othercount++; if ($othercount > $highestoutputnum) { $othercount = "001"; $startedover++; } $othercount = sprintf ("%03d", $othercount); $line = "$otherurl $otherdate $othercount\n"; } else {$changeurldata = 1} } } if ($changeurldata) { foreach $line (@data) { if ($line =~ /^$url/) { chomp ($line); ($url, $date, $count) = split (/ /, $line); $count++; if ($count > $highestoutputnum) { $count = "001"; $startedover++; } $count = sprintf ("%03d", $count); $line = "$url $date $count\n"; $changeurldata = 0; } } } &SameCount; } } # If any data has changed, write it to disk if ($datachange) { open (DATA, ">$datafile") || &Error("[Error: Script can't open the data file to write]"); print DATA @data; close (DATA); } # Read and print the output if (-e "$outputfile") { $startedover = $samecount = 0; require "$outputfile"; unless ($outputnum{$count} =~ /\S/) {$outputnum{$count} = "blank"} while (($skipmissingoutput) && ($outputnum{$count} eq "blank")) { if ($startedover == 2) {last} $count++; if ($count > $highestoutputnum) { $count = "001"; $startedover++; } $count = sprintf ("%03d", $count); unless ($outputnum{$count} =~ /\S/) {$outputnum{$count} = "blank"} } if ($outputnum{$count} eq "blank") {&Error("[Error: There is no text in \$outputnum{\'$count\'}]")} print "$outputnum{$count}"; } else {&Error("[Error: Script can't find the output file]")} ##### SUBROUTINES ####################################################### sub DateToday { unless ($timediff) {$timediff = 0} ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime (time + ($timediff * 3600)); @months = ('01','02','03','04','05','06','07','08','09','10','11','12'); $month = $months[$mon]; $mday = sprintf ("%02d", $mday); $year = $year - 100; $year = sprintf ("%02d", $year); $today = "$month/$mday/$year"; return $today; } sub SameCount { $samecount = 0; foreach $line (@data) { if (($line !~ /^$url/) && ($line =~ /$today/) && ($line =~ /$count/)) {$samecount = 1} } } sub Error { if ($printerrors) {print "

$_[0]

\n"} exit; }