#!/usr/local/bin/perl # my $VERSION='1.8.4'; # # Jonathan Cross : www.JonathanCross.com : 2004-2008 # This script may be used free for any non-commercial purpose. # # JCDSee creates a user-customizable linked directory listing in HTML format. # Will also generate and cache thumbnails of any GIF, JPEG or PNG images it finds. # Also contains a sequential image viewer, slideshow and various navigation devices. # Best viewed with IE6+ or Mozilla/Firefox 1.0+ # # COMMANDLINE DEBUG PARAMS (not tested recently): # [script-name] debug # All args in <> are optional. # # URl PARAMS (Everything is optional. If no url is provided, the current location of the script is used): # index.cgi?pic=[full path to picture]&display_mode=[LIST|THUMBS|SINGLE|SLIDESHOW]&zoom_mode=[ZOOMED_IN|ZOOMED_OUT]&window_mode=[WIN_NORMAL|WIN_FULL_SCREEN]&cur_url=[full path to folder] # Defaults: display_mode=LIST # zoom_mode=ZOOMED_OUT # window_mode=WIN_NORMAL # cur_url= # # EXAMPLE URLs (assuming JCDSee is in the web root): # http://zzz.com/ # http://zzz.com/jcdsee.cgi?cur_url=/pics/ # http://zzz.com/jcdsee.cgi?pic=/pics/my_pic.jpg # http://zzz.com/jcdsee.cgi?pic=/pics/my_pic.jpg&display_mode=SLIDESHOW # http://zzz.com/jcdsee.cgi?pic=/pics/my_pic.jpg&zoom_mode=ZOOMED_IN&display_mode=THUMBS # If you can use mod_rewrite then you can access folders like so: # http://zzz.com/pics/2/ (results in: http://zzz.com/jcdsee.cgi?cur_url=/pics/2/) # # FEATURES: # - Able to recognize 5 file types (image, folder, music, "text document" and "other") # "other" file types can include video, or anything else, but will not have an icon unless you provide one manually. # - Will generate & cache thumbnail icons of any jpeg, gif or png files it finds. # - 2 additional thumbnail resolutions + HTML based pseudo-ZOOM within browser. (6 resolution settings!) # - Optimized to only read / write necessary image information. # - 4 display modes ("list","thumbnail", "single image" and DHTML "slideshow"). # - In "single" (full-size) image display mode, the script will pre-cache next and previous images in browser. # - Support a full-screen window mode for most if not all browsers (not just IE). # - Will maintain display, zoom and window size settings across folders (assuming you only use the links on the page) # - Clean URLs # - Uses advanced page transitions in IE and CSS transparency in Mozilla. # - Adding a new folder of images just requires a unix "hard link" to the script and you are done! # - All "rollovers" are done with CSS transparency and A:hover pseudo-class (no messy javascript) # # JCDSEE ADMIN (admin script available upon request): # - Easily add new folders # - Write file / folder descriptions in browser # - Create google sitemap.xml file. # - Various backup, cleaning and undo tasks. # - Even re-program JCDSee or the admin script itself! # # BUGS: # - pic parameter needs to handle trailing slashes and non-existant files better? # - broken image name in database just stops processing the list. # - thumbs in SINGLE mode loose transparency after full is loaded. doesn't happen when zoomed! # - FIXED: UTF-8 chars get munged... # - FIXED: Cool CSS hover effect only works in Mozilla right now. # - FIXED: 'filter' /jcdsee/jcdsee.css Line: 52 # # WISHLIST: # - rss, remove all file system IO, REAL video & audio support and inline player. # re-write in php, search, more robust slideshow with thumbs. # mpeg video: http://fresh.t-systems-sfr.com/unix/src/misc/mpeg2vidcodec_v12.tar.gz/ # PSD icons: complex to flatten... # 404, 500 error pages would be nice. # convert databases to xml and allow in-browser editing of descriptions. # non-destructive folder "refresh" when files are added or removed. # ADDED: global database 2/2008 # ADDED: 404 Error page 3/2008. # DONE: get rid of usage.cgi - re-write in perl # DONE: dir descriptions at top of page # thumb mode = remove #_ prefix just like list # A million other things if I had the time :) # # ========================================================================= use strict; use Image::Magick; use CGI ':standard'; use CGI::Carp 'fatalsToBrowser'; use Fcntl ':mode'; use Time::HiRes 'gettimeofday'; use XML::LibXSLT; use XML::LibXML; my %TIMER; $TIMER{'total_s'}=gettimeofday(); # Allow commandline execution / debugging my ${COMMANDLINE} = 0; if( defined($ARGV[0]) && ($ARGV[0] eq "debug") ) { ${COMMANDLINE} = 1; print "\n\nDEBUG MODE\n\n"; } # GLOBAL VARIABLES ================================================================================================== # DIRECTORY LIST ARRAYS my(@dir_list,@image_array,%image_hash,%file_descriptions,%file_types); my ${tmp}; my ${DUMP}=''; my ${ERROR} = 0; my ${pic_root}='/pics'; my ${assets_root} = '/jcdsee'; #folder containing assets used by this script. (icons, buttons, css, etc) my ${script_name} = 'jcdsee.cgi'; #Name of the stub script, we need this so we don't show it in the dir listing. my ${script_url} = "/${script_name}"; # The main script URL used by links. Because we name it "index.cgi", we can just use a question mapk after folder name. This one is always used unless user hits a folder directly. my ${test_url} = "test"; # Special test mode staging url my $amp='&'; # XML / XSL objects my $XMLparser = XML::LibXML->new(); my $xslt = XML::LibXSLT->new(); my $xml_sitemap_file = "$ENV{DOCUMENT_ROOT}/sitemap.xml"; my $xsl_file = "$ENV{DOCUMENT_ROOT}${assets_root}/jcdsee.xsl"; if (! -f $xml_sitemap_file) { return "lost XML: $xml_sitemap_file"; } if (! -f $xsl_file) { return "lost xsl: $xsl_file"; } my $sitemap = $XMLparser->parse_file($xml_sitemap_file); my $style_doc = $XMLparser->parse_file($xsl_file); my $stylesheet = $xslt->parse_stylesheet($style_doc); # ONE STEP # my $stylesheet = $xslt->parse_stylesheet_file($xsl_file); # VARS FOR IMAGE PROCESSING my %IMAGE = ( thumb_quality => 65, #Thumbnail jpeg quality resolution => 0, #Will hold a value for maximum dimension of thumbnail images zoom_factor => 0, #Multiplier for ZOOMED_IN display mode. Use odd number to make the browser interpolate. prefix_small => '.S.', #File name prefix to use for Small generated thumbnail images prefix_large => '.L.', #File name prefix to use for Large generated thumbnail images thumb_ext => '.jpg', #File extension of generated thumbnail images width => 0, height => 0, ); #DISPLAY STATE VARS my %STATE = ( dir => '', # Full server path to the folder being listed. url => '', # The path from the webserver document root. url_encoded => '', # The path from the webserver document root. (URL encoded) title => 'Adventures of Jonathan Cross', # Document title base cur_dir_name => ''. # Will have only the name of the current directory (no slashes) display_mode => '', # Display mode (SINGLE|LIST|THUMBS|SLIDESHOW) Default=LIST window_mode => '', # Window mode (WIN_FULL_SCREEN|WIN_NORMAL) Default=WIN_NORMAL zoom_mode => '', # Zoom mode (ZOOMED_IN|ZOOMED_OUT) Default=ZOOMED_OUT test_mode => 0, # Test mode (0 or 1) Default=0 page_description => '', # Description of current page (from xml database) prefix_cur => '', # Will hold the current prefix (prefix_small or prefix_large) thumb_ext_cur => '', # Will hold the current extension (thumbnail pics are always jpg for example) is_default => 1, # A boolean to tell us if all the display settings are at default. # VARS below used for current position in photo list pic_last_idx => 0, pic_array_length => 0, pic_cur_idx => 0, pic_next_idx => 0, pic_previous_idx => 0, pic_cur_number => 0, pic_previous_file => '', pic_cur_file => '', pic_next_file => '', ); my %DEFAULTS = ( display_mode => 'LIST', window_mode => 'WIN_NORMAL', zoom_mode => 'ZOOMED_OUT', test_mode => 0, ); # LOAD / SET PARAMS & DEFAULTS $STATE{'display_mode'} = param('display_mode'); # We disable ZOOMED_IN when in SLIDESHOW display mode (looks ugly) $STATE{'zoom_mode'} = (param('zoom_mode') eq "ZOOMED_IN" && $STATE{'display_mode'} ne 'SLIDESHOW')? "ZOOMED_IN" : 'ZOOMED_OUT'; $STATE{'window_mode'} = (param('window_mode') eq "WIN_FULL_SCREEN")? "WIN_FULL_SCREEN" : 'WIN_NORMAL'; $STATE{'pic_cur_file'} = (param('pic_cur_file'))? param('pic_cur_file') : ''; #Make sure pic_cur_idx is a normal number, otherwise set to 0 $STATE{'pic_cur_idx'} = ( param('pic_cur_idx') =~ /^[0-9]+$/ ) ? int(param('pic_cur_idx')) : 0; $STATE{'test_mode'} = ( $ENV{SERVER_NAME} =~ /^$test_url/ ) ? 1 : 0; # CURRENT URL FROM PARAM -OR- PATH if (param('cur_url')) { $STATE{'url'} = param('cur_url'); $STATE{'url'} =~ s:[^/]$:$&/:; $STATE{'display_mode'} = ($STATE{'display_mode'})? $STATE{'display_mode'} : 'LIST'; } elsif (param('pic') && (param('pic') =~ m:(.*/)(.*):) ) { #get folder url from the pic param $STATE{'url'} = $1; $STATE{'pic_cur_file'} = $2; $STATE{'display_mode'} = ($STATE{'display_mode'})? $STATE{'display_mode'} : 'SINGLE'; } else { $STATE{'url'} = $ENV{REQUEST_URI}; $STATE{'url'} =~ s/%20/ /g; $STATE{'display_mode'} = ($STATE{'display_mode'})? $STATE{'display_mode'} : 'LIST'; } if(${COMMANDLINE}) { $STATE{'url'} = (${ARGV[1]})? ${ARGV[1]} : $pic_root; # HAVE DIR DEFAULT TO "." FOR COMMANDLINE DEBUGGING $STATE{'dir'} = $STATE{'url'}; $STATE{'display_mode'} = ${ARGV[2]}; $STATE{'pic_cur_idx'} = int(${ARGV[3]}); $STATE{'zoom_mode'} = ${ARGV[4]}; $STATE{'window_mode'} = ${ARGV[5]}; }elsif ($STATE{'url'} =~ m:^/: && $STATE{'url'} !~ m:/[.][.]: && -d $ENV{DOCUMENT_ROOT}.$STATE{'url'}){ # Have to validate the directory for security reasons $STATE{'dir'} = $ENV{DOCUMENT_ROOT}.$STATE{'url'}; $STATE{'cur_dir_name'} = $STATE{'url'}; $STATE{'cur_dir_name'} =~ s:^.*/([^/]+)/:$1:; #get last dir name from the url and remove all slashes }else{ # Still getting 500 error in browser? print "HTTP/1.1 404 Not Found\n"; print "Content-type: text/html\n\n"; #die; #die "404 Not Found\nYour requested folder $STATE{'url'} is not found\n"; } $STATE{'title'} .= getCurrentPageTitle($STATE{'url'}); # Figure out the titele of the page # FIGURE OUT IF ALL SETTINGS ARE THE DEFAULT while ((my $key, my $value) = each(%DEFAULTS)) { if (${value} ne $STATE{$key}) { $STATE{'is_default'} = 0; last; } } #DISPLAY MODE SETUP & DEFAULT if ($STATE{'display_mode'} eq "THUMBS") { $IMAGE{'zoom_factor'} = 1.5; $IMAGE{'resolution'} = 140; #max dimension of large thumbnail $STATE{'prefix_cur'} = $IMAGE{'prefix_large'}; $STATE{'thumb_ext_cur'} = $IMAGE{'thumb_ext'}; }elsif ($STATE{'display_mode'} =~ /^SINGLE|SLIDESHOW$/) { $IMAGE{'zoom_factor'} = 1.3; #do not prefix image name, use full-size image $STATE{'prefix_cur'} = ''; $STATE{'thumb_ext_cur'} = ''; }else{ $IMAGE{'zoom_factor'} = 1.5; $STATE{'display_mode'} = "LIST"; $IMAGE{'resolution'} = 50; #max dimension of small thumbnail $STATE{'prefix_cur'} = $IMAGE{'prefix_small'}; $STATE{'thumb_ext_cur'} = $IMAGE{'thumb_ext'}; } # CONVIENIENCE VARIABLES my ${icon_unknown} = "${assets_root}/icon_unknown$STATE{'prefix_cur'}gif"; my ${icon_folder} = "${assets_root}/icon_folder$STATE{'prefix_cur'}gif"; my ${icon_music} = "${assets_root}/icon_music$STATE{'prefix_cur'}gif"; my ${icon_doc} = "${assets_root}/icon_doc$STATE{'prefix_cur'}gif"; my ${icon_copyleft} = "${assets_root}/icon_copyleft.png"; my ${database_file} = $STATE{'dir'}.'.jcdsee'; #SHOULD BE PUT INTO STATE # BEGIN FUNCTIONS ============================================================================ sub buildDirList { my @database_raw = 'null'; my @dir_list_raw = 'null'; if(! -e ${database_file}) { opendir(DIR,$STATE{'dir'}) or die "Cant open this directory: \"$STATE{'dir'}\"."; @dir_list_raw = readdir DIR; closedir(DIR); open(DATA,">>${database_file}") or die "Cant open file: \"${database_file}\"."; # Filter the list to remove thumbnail images, hidden files and the stub script. foreach my ${line} (sort @dir_list_raw) { #Filter out the muck if( ${line} !~ /^[.]|${script_name}/ ) { print DATA "${line}|\n"; } } close(DATA); } open(DATA, ${database_file}) or die "Content-type: html/text\n\nCant open file: \"${database_file}\"."; @database_raw = ; close(DATA); #PROCESS THE LIST my ${i} = 0; my ${j} = 0; # Fill file info arrays from data foreach my ${line} (@database_raw){ chop(${line}); my($file_name,$description)=split(/[|]/,$line); if (${file_name} ne '') { if( ${file_name} =~ /[.](jp[e]?g|gif|png)$/i) { #File is an image $image_array[${i}] = ${file_name}; $image_hash{${file_name}} = ${i}; $file_types{${file_name}} = 'pic'; ${i}++; }elsif( -d "$STATE{'dir'}${file_name}") { #Folder $file_types{${file_name}} = 'folder'; }elsif(${file_name} =~ /[.]mp3$|[.]wav$|[.]as[xf]$|[.]wm[a]$|[.]m3u$|[.]m[io]d$|[.]aif+$/i) { #Music (.mpeg,mpg,mp4,mp3,mp2,mp1,wav,asx,asf,wmx,wma,m3u,mid,mod,aif,aiff,qt) $file_types{${file_name}} = 'music'; #}elsif(${file_name} =~ /[.](mp[e]?g|avi|mov|flv|wmv|qt)$/i) { # Video (.mpeg,mpg,avi,mov,flv,wmv,qt) # $file_types{${file_name}} = 'video'; }elsif(${file_name} =~ /[.](pdf|doc|htm[l]?|txt|nfo|css|js)$/i) { #Text Document (pdf,doc,txt,htm,html,nfo,css,js) $file_types{${file_name}} = 'doc'; }else { #Unknown file $file_types{${file_name}} = 'unknown'; } $dir_list[${j}] = ${file_name}; $file_descriptions{${file_name}} = ${description}; ${j}++; } } } # getImageTag returns an tag. Will create thumbs if necessary. # getImageTag("file name without path","prefix for image") sub getImageTag { my (${image_obj},${border},$alt,${tag}); my ${image_name} = $_[0]; my ${image_prefix} = $_[1]; my ${image_full} = $STATE{'dir'}.${image_name}; my ${image_thumb_name} = ${image_prefix}.${image_name}.$STATE{'thumb_ext_cur'}; my ${image_cur} = $STATE{'dir'}.${image_thumb_name}; #This holds the filename of the current image you will be reading and or writing. Can be a small thumbnail, large thumbnail or full-size image. my ${image_thumb_url} = $STATE{'url'}.${image_thumb_name}; #Image url for browser # Make a Thumbnail if necessary # Datestamp isn't really important so i'm removing test to speed up display in 99% of cases # if( ! (-e ${image_cur}) || (((stat(${image_full}))[9]) > ((stat(${image_cur}))[9])) ) { if( $STATE{'prefix_cur'} && ! (-e ${image_cur})) { #Make a thumbnail of the image ${image_obj} = Image::Magick->new; ${tmp} = ${image_obj}->Read(${image_full}); warn ${tmp} if ${tmp}; ${tmp} = ${image_obj}->Flatten(); # for PSD files ${tmp} = ${image_obj}->Resize(geometry=>"$IMAGE{'resolution'}x$IMAGE{'resolution'}"); ${tmp} = ${image_obj}->Profile(name=>undef); warn ${tmp} if ${tmp}; # Set JPEG compression level for thumb ${tmp} = ${image_obj}->Set(compression=>"JPEG"); ${tmp} = ${image_obj}->Set(quality=>$IMAGE{'thumb_quality'}); ${tmp} = ${image_obj}->Set(type=>"Optimize"); ${tmp} = ${image_obj}->Write(${image_cur}); warn ${tmp} if ${tmp}; }elsif($STATE{'zoom_mode'} eq "ZOOMED_IN"){ # Slow zooming, requires us to load image! # ($width, $height, $size, $format) = $image->Ping('xxx.gif'); # ${image_obj} = Image::Magick->new; ${tmp} = ${image_obj}->Read(${image_cur}); warn ${tmp} if ${tmp}; #get dimensions $IMAGE{'width'} = ${image_obj}->Get('columns'); $IMAGE{'height'} = ${image_obj}->Get('height'); #Enlarge acordingly $IMAGE{'width'} *= $IMAGE{'zoom_factor'}; $IMAGE{'height'} *= $IMAGE{'zoom_factor'}; } $image_thumb_url = escapeURL($image_thumb_url); $border = ($STATE{'display_mode'} eq 'LIST') ? '1' : '3'; $alt = ($STATE{'display_mode'} eq 'THUMBS') ? stripHTML($file_descriptions{${image_name}}):''; ${tag} = "${alt}transform($sitemap, NAME => "'$item'", VALUE => "'$STATE{'url_encoded'}'"); my $string = $stylesheet->output_string($results); chomp($string); return $string; } # gettitle returns an html formatted string representing the filename passed in # getTitle("file name to be parsed") sub getTitle { #remove numbered prefix, extension and convert _ into spaces. my ${file_name} = $_[0]; my ${strip_date} = ($STATE{'display_mode'} =~ /^THUMBS|SINGLE|SLIDESHOW$/)?1:0; if (${strip_date}) { if (${file_name} =~ /^([0-9]{4}[-][0-9]{2}[-][0-9]{2})[_-]?(.*)/) { return ${2}; }else{ return ${file_name}; } }elsif (${file_name} =~ /^([0-9]{4}[-][0-9]{2}[-][0-9]{2})[_-]?(.*)/) { #DATED return "${1}${2}"; }elsif (${file_name} =~ /^[0-9]+[_-](.+)/) { #NUMBERED return "${1}"; }else{ return "${file_name}"; } } # stripHTML returns a string with HTMl tags removed and quotes encoded (used by alt tags) # stripHTML("string") sub stripHTML { my ${string} = $_[0]; ${string} =~ s/<[^>]+>//g; ${string} =~ s/"/"/g; #quotes ${string} =~ s/'/'/g; #apos return ${string}; } # escapeURL returns a URL with spaces escaped. # escapeURL("URL") sub escapeURL { my $URL = $_[0]; $URL =~ s: :%20:g; return $URL; } # getHREF Builds a custom HREF given the object you want to link to. # getHREF(action[pic|dir|display_mode|zoom_mode], value[pic=url|dir=folder_name|display_mode,zoom_mode = "normal settings" (ZOOMED_IN, etc)]) sub getHREF { # display_mode # dir = norm # pic = do not use # display_mode = use supplied # zoom_mode = norm # window_mode = norm # # zoom_mode # dir = norm # pic = norm # display_mode = norm # zoom_mode = change to reverse # window_mode = norm # # window_mode # dir = norm # pic = norm # display_mode = norm # zoom_mode = norm # window_mode = use javascript # --------------------------------------- # *norm = use if not default. # *NOTE: action will never be window_mode my ${action} = $_[0]; my ${value} = $_[1]; my ${HREF}; #my $local_path = (${action} eq 'dir') ? ${value} : $STATE{'url'} ; #BASE SCRIPT NAME if (${action} eq 'dir' && ($STATE{'window_mode'} eq $DEFAULTS{'window_mode'})) { # Special case for dir when we can dump display_mode + zoom_mode setting (have to remember window_mode though!) ${HREF} = "${value}"; return ${HREF}; }elsif (${action} eq 'dispaly_mode' && ${value} eq $DEFAULTS{'display_mode'} && $STATE{'window_mode'} eq $DEFAULTS{'window_mode'} && $STATE{'zoom_mode'} eq $DEFAULTS{'zoom_mode'}) { # Special case when button turns it into the default ${HREF} = "$STATE{'url'}"; return ${HREF}; }else{ ${HREF} = "${script_url}?"; } #SET URL PARAM if (${action} eq 'pic') { ${HREF} .= "pic=$STATE{'url'}${value}"; }elsif (${action} =~ /^display_mode|zoom_mode$/) { ${HREF} .= "pic=$STATE{'url'}$STATE{'pic_cur_file'}"; }elsif (${action} eq 'dir') { ${HREF} .= "cur_url=${value}"; } #DISPLAY MODE PARAM if (${action} ne 'pic' && ${action} ne 'dir') { #for 'pic' and 'dir' we have pre-defined display modes so they are excluded here if (${action} eq 'display_mode') { ${HREF} .= "${amp}display_mode=${value}"; }else{ ${HREF} .= "${amp}display_mode=$STATE{'display_mode'}"; #persist the display mode } } #WINDOW MODE PARAM if (${action} =~ /^pic|dir|display_mode|zoom_mode$/) { if ($STATE{'window_mode'} ne $DEFAULTS{'window_mode'}) { ${HREF} .= "${amp}window_mode=$STATE{'window_mode'}"; } } # $action will never be "window_mode" button (it uses javascript). Therefore we don't need the elsif action=window_mode. #ZOOM MODE PARAM if (${action} =~ /^pic|display_mode$/) { if ($STATE{'zoom_mode'} ne $DEFAULTS{'zoom_mode'}) { ${HREF} .= "${amp}zoom_mode=$STATE{'zoom_mode'}"; } }elsif (${action} eq 'zoom_mode') { #Special reverse zoom mode for button ${HREF} .= "${amp}zoom_mode="; ${HREF} .= (${value} eq 'ZOOMED_OUT')? 'ZOOMED_IN':'ZOOMED_OUT'; } return escapeURL($HREF); } # Functions for file type booleans # isFileType ("file name", "type") sub isFileType { return ($file_types{$_[0]} eq $_[1])? 1:0; } # getDepthPath () sub getDepthPath { print 'home'; foreach my ${path} (split( '/',$STATE{'url'})) { if (${path} ne '/') { $STATE{'url'} =~ m:(^/${path}/|^/.+/${path}/):; if (${1} ne '') { my $itemTitle = getNiceFilename(${path}); print "/${itemTitle}"; } }else{ print '@'; } } } # getIcon returns a linked image tag representing the file provided by $file_name # getIcon("name of file") sub getIcon { my ${file_name} = $_[0]; my ${link_content}; my ${icon_file}; my ${class}; my ${desc} = ${file_name}; ${desc} .= ($file_descriptions{${file_name}} ne "")? " - ".stripHTML($file_descriptions{${file_name}}) : ""; if(isFileType(${file_name},'pic')) { #Image icon ${class} = ($STATE{'pic_cur_file'} eq ${file_name})? "current_pic" : "pic" ; ${link_content} = getImageTag(${file_name},$STATE{'prefix_cur'}); } else { #we can create and upload a static thumbnail icon for any filetype... will replace default question mark my ${static_thumbnail_path} = $STATE{'dir'}.$STATE{'prefix_cur'}.${file_name}.$STATE{'thumb_ext_cur'}; #Use built-in icon if (isFileType(${file_name},'folder')) { ${icon_file} = ${icon_folder}; } elsif (isFileType(${file_name},'doc')) { ${icon_file} = ${icon_doc}; } elsif (isFileType(${file_name},'music')) { ${icon_file} = ${icon_music}; } elsif (-e ${static_thumbnail_path}) { ${icon_file} = $STATE{'url'}.$STATE{'prefix_cur'}.${file_name}.$STATE{'thumb_ext_cur'}; } else { ${icon_file} = ${icon_unknown}; } ${link_content} = "\"${desc}\""; # valign=\"middle\" } return getLinkTag(${file_name},${link_content},${desc},${class}); } # getLinkTag returns an tag containing appropriate href based on the type of file, state, etc. # getLinkTag("name of file","link content","file description","CSS class name") sub getLinkTag { my ${file_name} = $_[0]; my ${link_content} = $_[1]; my ${desc} = $_[2]; my ${class} = $_[3]; my ${link_tag}; #This is a bit annoying, see if google picks up the site if (! "${desc}") { ${desc} = ${file_name}; ${desc} .= ($file_descriptions{${file_name}} ne "")? " - ".stripHTML($file_descriptions{${file_name}}) : ""; } if(isFileType(${file_name},'folder')) { #Folder ${link_tag} = "${link_content}\n"; }elsif(isFileType(${file_name},'pic')) { #Image ${link_tag} = "".${link_content}.''; }else { # Music, text or other. Just link to the file ${link_tag} = "${link_content}\n"; } return ${link_tag}; } # getNavButton("mode","value","text description") sub getNavButton { my ${mode}=${_[0]}; my ${value}=${_[1]}; my ${desc}=${_[2]}; my ${toggle} = ( ${value} =~ /^(ZOOMED_IN|WIN_FULL_SCREEN|$STATE{'display_mode'})$/ )? "on" : "off"; my ${icon_modifier} = lc(${value}); #Lowercase my ${HREF}; my ${rel} = ''; if ($STATE{'display_mode'} eq ${value} ) { ${HREF} = 'javascript:void(0)'; }elsif (${mode} eq 'window_mode') { ${HREF} = 'javascript:refresh("window_mode","'.${value}.'")'; }else{ ${HREF} = getHREF( ${mode} , ${value} ); } # Exclude some buttons from google #if (${mode} =~ /^(window_mode|zoom_mode)$/ || ${value} eq 'SLIDESHOW' ) { ${rel} = 'nofollow'; #} print "${desc}"; } sub dumpDirList { #my(@time_info,@month_list); #my(${year},${month},${minute},${day},${hour},${file_size},${is_dir}); my ${file_name}; my @file_info; my ${file_size}; #LIST AND THUMBNAIL DISPLAY MODES if ($STATE{'display_mode'} =~ /^(LIST|THUMBS)$/) { foreach ${file_name} (@dir_list) { ${file_size} = ''; @file_info = stat $STATE{'dir'}.${file_name}; #ALL THIS ISDIR SHOULD GO IN THE CACHE FILE! ALSO NEED TO BE ABLE TO DELETE / RECACHE WITHOUT LOOSING INFO # Not used anymoer... ${is_dir} = S_ISDIR(${file_info[2]}); if ($STATE{'display_mode'} eq "LIST") { #@time_info = localtime ${file_info[9]}; #EXTRACT FILE INFO FROM ARRAY AND PAD #${year} = ${time_info[5]} + 1900; #${month} = (qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)) [(${time_info[4]})]; #${minute} = (${time_info[1]} < 10) ? "0".${time_info[1]} : ${time_info[1]}; #${day} = (${time_info[3]} < 10) ? "0".${time_info[3]} : ${time_info[3]}; #${hour} = (${time_info[2]} < 10) ? "0".${time_info[2]} : ${time_info[2]}; if ( ! isFileType(${file_name},'folder')) { if (${file_info[7]} > 10000000) { ${file_size} = int(${file_info[7]} / 1048576) . " MB"; }elsif (${file_info[7]} > 1000000) { ${file_size} = sprintf("%.1f", (${file_info[7]} / 1048576)) . " MB"; }elsif (${file_info[7]} > 2047){ ${file_size} = int(${file_info[7]} / 1024) . " KB"; }elsif (${file_info[7]} > 1023){ ${file_size} = sprintf("%.1f", (${file_info[7]} / 1024)) . " KB"; }elsif (${file_info[7]} > 0){ ${file_size} = ${file_info[7]} . " B"; }else{ ${file_size} = "0 B"; } } print " "; #GET THE APPROPRIATE ICON FOR THE FILE, FOLDER, IMAGE, ETC. print getIcon(${file_name}); print " "; #date isn't really used, but maybe in future? #my ${date} = "${day}-${month}-${year} ${hour}:${minute}"; print getLinkTag(${file_name},getTitle(${file_name}),'','simple'); if ($file_descriptions{${file_name}}) { print " - $file_descriptions{${file_name}}"; } print " ${file_size}   "; }elsif ($STATE{'display_mode'} eq "THUMBS") { #GET THE APPROPRIATE ICON FOR THE FILE, FOLDER, IMAGE, ETC. print '
'; print getIcon(${file_name}); print '
'.getTitle(${file_name}).'
'; } } if ($STATE{'display_mode'} eq 'THUMBS') { print '
 
'; } }elsif ($STATE{'display_mode'} eq 'SINGLE') { #SINGLE IMAGE MODE #Make sure large thumbs exist ${file_name} = $STATE{'pic_cur_file'}; if (${file_name}) { ${tmp} = getImageTag($STATE{'pic_previous_file'},$IMAGE{'prefix_large'}); ${tmp} = getImageTag($STATE{'pic_next_file'},$IMAGE{'prefix_large'}); print "
".getTitle(${file_name})." ($STATE{'pic_cur_number'} of $STATE{'pic_array_length'} images)"; if ($file_descriptions{${file_name}}) { print " - $file_descriptions{${file_name}}"; } print "
 
PREVIOUS&"."lt; PREVIOUS ".getImageTag(${file_name},'')." NEXTNEXT &"."gt;  

"; }else{ #COPIED TWICE. Move this to the top... mode should change to list if there are no images... print ''; } }elsif ($STATE{'display_mode'} eq 'SLIDESHOW') { #SLIDESHOW IMAGE MODE #Make sure large thumbs exist ${file_name} = $STATE{'pic_cur_file'}; if (${file_name}) { print "
 
".getTitle(${file_name})." ($STATE{'pic_cur_number'} of $STATE{'pic_array_length'} images)"; if ($file_descriptions{${file_name}}) { print " - $file_descriptions{${file_name}}"; } print "
 
".getImageTag(${file_name},'')."

"; }else{ #COPIED TWICE. Move this to the top... mode should change to list if there are no images... may be cause of bug??? print ''; } } } # This function figures out the page context, setting, etc. based on the current url. sub calculateImageListState { if (@image_array > 0) { $STATE{'pic_last_idx'} = $#{image_array}; $STATE{'pic_array_length'} = $STATE{'pic_last_idx'} + 1; if ($STATE{'pic_cur_file'}){ if ( "$image_hash{ $STATE{'pic_cur_file'} }" ne "" ){ $STATE{'pic_cur_idx'} = $image_hash{ $STATE{'pic_cur_file'} }; }else{ $ERROR=1; $DUMP.="DID NOT FIND '$STATE{'pic_cur_file'}' IN THE LIST!
"; } } $STATE{'pic_next_idx'} = $STATE{'pic_cur_idx'} + 1; $STATE{'pic_previous_idx'} = $STATE{'pic_cur_idx'} - 1; # This chunk will just loop the image array around if it is out of bounds. if ($STATE{'pic_cur_idx'} >= $STATE{'pic_last_idx'}){ $STATE{'pic_cur_idx'} = $STATE{'pic_last_idx'}; $STATE{'pic_next_idx'} = 0; $STATE{'pic_previous_idx'} = $STATE{'pic_cur_idx'} - 1; }elsif ($STATE{'pic_cur_idx'} <= 0) { $STATE{'pic_cur_idx'} = 0; $STATE{'pic_next_idx'} = $STATE{'pic_cur_idx'} + 1; $STATE{'pic_previous_idx'} = $STATE{'pic_last_idx'}; } $STATE{'pic_cur_number'} = $STATE{'pic_cur_idx'} + 1; $STATE{'pic_cur_file'} = ${image_array[ $STATE{'pic_cur_idx'} ]}; $STATE{'pic_previous_file'} = ${image_array[ $STATE{'pic_previous_idx'} ]}; $STATE{'pic_next_file'} = ${image_array[ $STATE{'pic_next_idx'} ]}; } #Would be ideal if we did'nt unencode then re-encode the url_encoded. In many cases, $ENV{REQUEST_URI} has what we need! #Problem is that there are several ways to get the 'url' (cur_url or directly from path if no mod_re-write) $STATE{'url_encoded'} = $STATE{'url'}; $STATE{'url_encoded'} =~ s/ /%20/g; $STATE{'page_description'} = stripHTML(getSitemapData('pageDescription')); } # GET READY TO PARTY! buildDirList(); calculateImageListState(); # RENDER HTML ================================================================================================== # RENDER HEAD print "Content-type: text/html\n\n"; print " $STATE{'title'} "; ############ # Get the description here # if ($STATE{'display_mode'} =~ /^SINGLE|SLIDESHOW$/) { #Allows IE to fade between pages print ''; } if ($STATE{'display_mode'} eq "SLIDESHOW") { print " "; } my ${onload}=""; if ($STATE{'display_mode'} eq 'SLIDESHOW') { ${onload}="startSlideshow();"; }elsif ($STATE{'display_mode'} eq 'SINGLE') { ${onload}="cacheImages('$STATE{'url'}$STATE{'pic_previous_file'}','$STATE{'url'}$STATE{'pic_next_file'}');"; } if ($STATE{'test_mode'}) { # make it pink if in test mode print ""; } if ($STATE{'display_mode'} eq 'SINGLE' && $STATE{'pic_array_length'} <= 1) { # This is sloppy, I should not send the code if its not needed print ""; } print " "; # print "
Admin Console
'; if ($STATE{'display_mode'} eq "SLIDESHOW") { print ' '; } print '
"; #GENERATE THE DEPTH PATH getDepthPath(); print ' '; getNavButton("display_mode","LIST","LIST MODE"); getNavButton("display_mode","THUMBS","THUMBNAIL IMAGE MODE"); getNavButton("display_mode","SINGLE","SINGLE IMAGE MODE"); getNavButton("display_mode","SLIDESHOW","SLIDESHOW DISPLAY MODE"); print '  '; getNavButton("zoom_mode",$STATE{'zoom_mode'},"ZOOM"); getNavButton("window_mode",$STATE{'window_mode'},"WINDOW MODE"); print '
SLIDESHOW:   play  pause  reset '.$STATE{'pic_cur_number'}.' of '.$STATE{'pic_array_length'}.' speed: 
'; if ($STATE{'display_mode'} eq "LIST") { print '
'.$STATE{'cur_dir_name'}.''; if ("$STATE{'page_description'}") { print ' : '.$STATE{'page_description'}; } print '
'; print ' '; } #CALL THE LOOP THAT RENDERS THE FILE LIST dumpDirList(); #FINISH TABLE FOR LIST if ($STATE{'display_mode'} eq "LIST") { print '
'; } # CREATIVE COMMONS LICENSE print ' '; #DEBUG ERROR INFORMATION if (${ERROR}) { ${DUMP} .= "FILE TYPES: \n"; foreach my $k (sort keys %file_types) { ${DUMP} .= " $k = $file_types{$k}\n"; } ${DUMP} .= "\nIMAGES: \n"; foreach my $k (sort keys %image_hash) { ${DUMP} .= " $k = $image_hash{$k}\n"; } ${DUMP} .= "\nSTATE: \n"; foreach my $k (sort keys %STATE) { ${DUMP} .= " $k = $STATE{$k}\n"; } print "
ERROR:
${DUMP}
"; } #HIDE STATS FOR SLIDESHOW MODE if($STATE{'display_mode'} ne "SLIDESHOW") { $TIMER{'total_e'} = gettimeofday(); $TIMER{'total'} = sprintf("%.3f", ($TIMER{'total_e'} - $TIMER{'total_s'})); print '
Creative Commons License
'; print " JCDSee ${VERSION}
Script executed in: $TIMER{'total'} seconds.
"; print getSitemapData('pageDate'); print getSitemapData('pageSize'); print "
"; } print "
 
";