==================================================================================== Multiple-Image Upload Mod v0.1 ==================================================================================== This modification puts a multiple-image upload option in both add.cgi and modify.cgi, and lets the admin see what's being submitted prior to approval. Images are saved to a new directory with the link ID number, and can be deleted from the admin or by link owner.The directory structure is like so: /pages/pics/14/myphoto.jpg /pages/pics/14/anotherphoto.gif As written, this mod allows three uploads. Allowing fewer is no problem, but having more will require adding more code. Note that the when user deletes an image via modify.cgi, it is deleted as soon as they submit the form, with no validation from the admin. Validation is required to remove the image name and caption from the database, however. If user's link had the max number of uploads, then deletes one or more, they will need to can refresh the modify page to get the option to upload more. New uploads will then overwrite the image name and caption fields when validated. The Modify Security mod in modify.cgi used in this code is not secure, as it is set up for testing purposes and uses the link Title to access the modify page. Once you have the whole mod working, change it to use another field, such as Contact Email, or a password field that you will have to add. This mod is in need of more testing, but I'm sure it is 95% functional. These areas need attention: - If the second (or later) image a user uploads is of the incorrect type (extension), it will not upload, and they will be sent to the error page. Thing is, any pic before the bad one WAS uploaded, but they don't know that. Need to have add_error.html show them what, if anything, was uploaded prior to error. - When user deletes an image via modify, the image is removed upon submission, but the db will still think it is there until after validation. This will result in a broken image (red x) in their link. Need a placeholder image, such as "Image not available." - When admin deletes link, the corresponding pic directory may not be deleted. - A few other minor issues will undoubtedly arise... The mod was created by Leonard Taylor (aka PerlFlunkie), thunder21@cox.net. It is largely based on my 'External Text File Mod', which was adapted from one written by JPDeni for dbman. It also contains code from JPDeni's 'Image Upload 3 Mod.' Be sure to make backups of your files! You assume ALL responsibility for anything that might happen to your files or server. If you have suggestions for changes to simplify the code, corrections that need to be made, or features to add, let me know. ==================================================================================== 1. add.cgi ==================================================================================== *** Replace the parser call: # >>> Image Upload Mod sub main { # -------------------------------------------------------- # local (%in) = &parse_form; local (%in) = &parse_form_upload; # <<< *** replace everything between '# Validate the form input;' and '# Update the counter.' with this: # >>> Image Upload Mod (next 26 lines) $status = &validate_record(%in); if (($status eq "ok") && ($in{'file-to-upload-1'})) { $status = &validate_upload; } #If there's an upload, get it. if ($status eq "ok") { if (-e "$upload_directory/$in{'ID'}") { # Check if there's any pics opendir (GRAPHIC, "$upload_directory/$in{'ID'}") or &cgierr("unable to open directory: $upload_directory/$in{'ID'}. Reason: $!"); @files = readdir(GRAPHIC); closedir (GRAPHIC); @photo= qw(); # Put 'em in an array so we can get the ones we want foreach $file (@files) { next if ($file =~ /^\./); # Skip "." and ".." entries.. push (@photo, $file); } if ($photo[0]) { $in{'Imagename_1'} = $photo[0]; } if ($photo[1]) { $in{'Imagename_2'} = $photo[1]; } if ($photo[2]) { $in{'Imagename_3'} = $photo[2]; } } # <<< ==================================================================================== 2. modify,cgi ==================================================================================== *** To make this work, we need to include a version of Paul's Modify Security Mod. This pre-fills the form fields with data from links.db Replace all of 'sub main' with this: sub main { # -------------------------------------------------------- # >>> Image Upload Mod > # local (%in) = &parse_form; local (%in) = &parse_form_upload; # <<< # >>> Security Mod # We are processing the form.. if ($in{'do'} eq "check") { &check(); } # We are processing the form.. elsif (!$in{'do'}&&keys %in != 0) { &process_form; } # Otherwise we are displaying the form (in site_html.pl). else { &site_html_modify_form_up; } } # <<< Security Mod *** Then 'sub process_form' gets all these changes: sub process_form { # -------------------------------------------------------- my ($key, $status, @values, $found); local (%original); # Make sure we have a link to modify. # >>> Security Mod - removed next line # !$in{'Current URL'} and &site_html_modify_failure ("did not specify link to modify") and return; # <<< # Let's check to make sure the link we want to update is actually # in the database. open (DB, "<$db_file_name") or &cgierr("error in validate_records. unable to open db file: $db_file_name. Reason: $!"); $found = 0; LINE: while () { (/^#/) and next LINE; (/^\s*$/) and next LINE; chomp; @data = &split_decode($_); # >>> Security Mod - removed next and replace with new line # if ($data[$db_url] eq $in{'Current URL'}) { if ($data[$db_key] eq $in{'ID'}) { # This field is picked up by hidden input from modify_up form. # <<< $in{$db_key} = $data[0]; $found = 1; %original = &array_to_hash (0, @data); last LINE; } } close DB; !$found and &site_html_modify_failure ("link was not found in the database") and return; # Since we have a valid link, let's make sure the system fields are set to their # proper values. We will simply copy over the original field values. This is to stop # people from trying to modify system fields like number of hits, etc. foreach $key (keys %add_system_fields) { $in{$key} = $original{$key}; } # Set date variable to today's date. $in{$db_cols[$db_modified]} = &get_date; # Validate the form input.. # >>> Image Upload Mod (next 31 lines) # If user wants to delete an image, do it foreach $key (keys %in) { if ($in{'delete_1'} eq "yes") { unlink "$upload_directory/$in{'ID'}/$in{'Imagename_1'}"; $in{'Imagename_1'} = ''; # Delete Imagename entry. $in{'Caption_1'} = ''; # Delete caption entry,too } if ($in{'delete_2'} eq "yes") { unlink "$upload_directory/$in{'ID'}/$in{'Imagename_2'}"; $in{'Imagename_2'} = ''; # Delete Imagename entry. $in{'Caption_2'} = ''; # Delete caption entry,too } if ($in{'delete_3'} eq "yes") { unlink "$upload_directory/$in{'ID'}/$in{'Imagename_3'}"; $in{'Imagename_3'} = ''; # Delete Imagename entry. $in{'Caption_3'} = ''; # Delete caption entry,too } } $status = &validate_record(%in); if (($status eq "ok") && ($in{'file-to-upload-1'})) { $status = &validate_upload; } #If there's an upload, get it. if ($status eq "ok") { # Did they upload new images? If so, get their names into the db. if (-e "$upload_directory/$in{'ID'}") { # Check if there's any pics opendir (GRAPHIC, "$upload_directory/$in{'ID'}") or &cgierr("unable to open directory: $upload_directory/$in{'ID'}. Reason: $!"); @files = readdir(GRAPHIC); closedir (GRAPHIC); @photo= qw(); # Put 'em in an array so we can get the ones we want foreach $file (@files) { next if ($file =~ /^\./); # Skip "." and ".." entries. push (@photo, $file); } if ($photo[0]) { # Put the image names into the database $in{'Imagename_1'} = $photo[0]; # so they'll be much easier to display } if ($photo[1]) { $in{'Imagename_2'} = $photo[1]; } if ($photo[2]) { $in{'Imagename_3'} = $photo[2]; } } # <<< # First make sure the link isn't already in there. open (MOD, "<$db_modified_name") or &cgierr ("error opening modified database: $db_modified_name. Reason: $!"); while () { chomp; @values = split /\|/; if ($values[0] eq $in{$db_key}) { close MOD; &site_html_modify_failure("A request to modify this record has already been received. Please try again later."); return; } } close MOD; # Print out the modified record to a "modified database" where it is stored until # the admin decides to add it into the real database. open (MOD, ">>$db_modified_name") or &cgierr("error in modify.cgi. unable to open modification database: $db_modified_name. Reason: $!"); flock(MOD, $LOCK_EX) unless (!$db_use_flock); print MOD &join_encode(%in); close MOD; # automatically removes file lock # Send the admin an email message notifying of new addition. &send_email; # Send the visitor to the success page. &site_html_modify_success; } else { # Let's change that error message from a comma delimted list to an html # bulleted list. &site_html_modify_failure($status); } } *** Then add this new sub to the end of the file: # >>> Security Mod sub check { #------------------------------------------------------- # Make sure we have a Title (Name) for the resource to modify. # If missing, return an error message to the submitter. (!$in{'Title'}) and &site_html_modify_up_failure (qq|Missing or incorrect Name of the listing to update.|) and return; # You can change which fields are used to verify. # ((!$in{'Title'}) or # (!$in{'Password'}) and &site_html_modify_up_failure (qq|Missing or incorrect Name or Password of the listing to update.|) and return; # This version uses two fields to verify. open (DB, "<$db_file_name") or &cgierr("error in validate_records. unable to open db file: $db_file_name. Reason: $!"); $found = 0; LINE: while () { (/^#/) and next LINE; (/^\s*$/) and next LINE; chomp; @data = &split_decode($_); if ($data[$db_title] eq $in{'Title'}) { # You can change which fields are used to verify. # if (($data[$db_title] eq $in{'Title'}) and ($data[$db_password] eq $in{'Password'})) { # This version uses two fields to verify. $id = $data[0]; $found = 1; last LINE; } } close DB; # If no matching record found, display error message to user !$found and &site_html_modify_up_failure (qq|
  1. The specified Last Name was not found in the database.
  2. The specified Password does not match the database entry for the specified Last Name.
|) and return; my (%record) = &get_record($id); &site_html_modify_form(%record) and return; } # <<< ==================================================================================== 3. Links.cfg ==================================================================================== *** Add this to 'Paths and URL's to Important Stuff': #>>> Image Upload Mod # PATH and URL of Image Upload Folder $upload_directory = "$build_root_path/pics"; $upload_directory_url = "$build_root_url/pics"; # <<< *** Above 'Email Options' add: # >>> Image Upload Mod # Image Upload Options # -------------------------------------------------------- # Defines the total number of bytes that can be uploaded. Files that exceed # this limit will not be saved on the server. Set this to zero in order to # disable size checking. $maximum_upload = 200000; # Defines the total number of files that can be attached to a file. If a user # tries to exceed the limit, no files will be added. You must set this to a value. # It cannot be 0. $maximum_files = 3; # List of allowable file extensions. If the file does not have one of the extensions # listed, it will not be saved to the server. The format for the setting is # \.[extension]$ If you want to allow more than one extension, separate the options by # a | character. Note that case counts! Example: $allowed_ext = '\.gif$|\.jpg$|\.GIF$|\.JPG$'; $allowed_ext = '\.gif$|\.jpg$|\.GIF$|\.JPG$'; # <<< ==================================================================================== 4. Links.def ==================================================================================== *** Add to 'Database Definition: LINKS' and remember to add comma after bracket on line above it. Change field number if needed. # >>> Image Upload mod Imagename_1 => [14, 'alpha', 0, 255, 0, '', ''], Imagename_2 => [15, 'alpha', 0, 255, 0, '', ''], Imagename_3 => [16, 'alpha', 0, 255, 0, '', ''], Caption_1 => [17, 'alpha', 0, 255, 0, '', ''], Caption_2 => [18, 'alpha', 0, 255, 0, '', ''], Caption_3 => [19, 'alpha', 0, 255, 0, '', ''] # <<< ); *** Add this to '# Field Number of some important fields.' # <<< Image Upload Mod $db_imagename_1 = 14; $db_imagename_2 = 15;$db_imagename_3 = 16; $db_caption_1 = 17; $db_caption_2 = 18; $db_caption_3 = 19; # >>> ==================================================================================== 5. db_utils.pl ==================================================================================== *** Make these changes to 'sub build_html_record_form': sub build_html_record_form { # -------------------------------------------------------- # Builds a record form based on the config information. # my ($output, $field, $multiple, $name); ($_[0] eq "multiple") and ($multiple = 1) and shift; my (%rec) = @_; # >>> Image Upload mod (next 13 lines) if ($in{'db'} eq 'links') { # Make sure we're looking at links, not categories. # Get the file from /pics if there is one. if ($rec{'Imagename_1'}) { # Display image one. $image_1 = qq|<$font>Image 1:\n <$font>Delete Image 1? <$font>If deleting image, clear the corresponding Imagename and Caption fields, too.\n|; } if ($rec{'Imagename_2'}) { # Display image two $image_2 = qq|<$font>Image 2:\n <$font>Delete Image 2? <$font>If deleting image, clear the corresponding Imagename and Caption fields, too.\n|; } if ($rec{'Imagename_3'}) { # Display image three $image_3 = qq|<$font>Image 3:\n <$font>Delete Image 3? <$font>If deleting image, clear the corresponding Imagename and Caption fields, too.\n|; } } # <<< $output = "

"; # Go through little hoops to only load category list when absolutely neccessary. if ($in{'db'} eq 'links') { exists $db_select_fields{$db_cols[$db_category]} or ($db_select_fields{$db_cols[$db_category]} = join (",", &category_list)); } else { $db_select_fields{'Related'} or ($db_select_fields{'Related'} = $db_select_fields{'Mult-Related'} = join ",", &category_list); } foreach $field (@db_cols) { # Set the field name to field-key if we are doing multiple forms. $multiple ? ($name = "$field-$rec{$db_key}") : ($name = $field); if ($db_select_fields{"Mult-$field"}) { $output .= "\n"; } elsif ($db_select_fields{$field}) { $output .= "\n"; } elsif ($db_radio_fields{$field}) { $output .= "\n"; } elsif ($db_checkbox_fields{$field}) { $output .= "\n"; } elsif ($db_form_len{$field} =~ /(\d+)x(\d+)/) { $output .= qq~\n~; } elsif ($db_form_len{$field} == -1) { $output = qq~\n$output~; } else { $output .= qq~\n~; } } # >>> Image Upload Mod (next 4 lines) $output .= $image_1; $output .= $image_2; $output .= $image_3; # <<< $output .= "
<$font>$field:" . &build_select_field($field, $rec{$field}, $name, "MULTIPLE SIZE=3") . "
<$font>$field:" . &build_select_field($field, $rec{$field}, $name) . "
<$font>$field:" . &build_radio_field($field, $rec{$field}, $name) . "
<$font>$field:" . &build_checkbox_field ($field, $rec{$field}, $name) . "
<$font>$field:
<$font>$field:

\n"; return $output; } *** Add these two routines above 'sub cgi_error': # >>> Image Upload Mod, two new sub routines sub parse_form_upload{ # -------------------------------------------------------- # Used by add.cgi and modify.cgi only my (%in); my ($buffer, $pair, $name, $value); use CGI; $query = new CGI; PAIR: foreach $name ($query->param()) { $value = $query->param("$name"); $name =~ tr/+/ /; $name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $value =~ s///g; if ($value eq "---") { next PAIR; } unless ($value) { next PAIR; } (exists $in{$name}) ? ($in{$name} .= "~~$value") : ($in{$name} = $value); } return %in; } sub validate_upload { # -------------------------------------------------------- use CGI; $query = new CGI; my($filekey,$filename,$extlength,$filehandle,$totalbytes,$buffer,$bytes,@extensions,@ext,$newdirname,$dirsuccess,$num_files,$prev_files,$prev_bytes); $| = 1; $newdirname = $in{$db_key}; if (!(-e "$upload_directory/$newdirname")) { $dirsuccess = mkdir "$upload_directory/$newdirname", 0777; opendir (GRAPHIC, "$upload_directory/$newdirname") or &cgierr("unable to open directory. Reason: $!"); @files = readdir(GRAPHIC); closedir (GRAPHIC); foreach $file (@files) { next if ($file =~ /^\./); # Skip "." and ".." entries.. next if ($file =~ /^index/); # Skip index.htm type files.. ++$prev_files; @stats = stat "$upload_directory/$newdirname/$file"; $prev_bytes +=$stats[7]; } } foreach $key (sort {$a <=> $b} $query->param()) { next if ($key =~ /^\s*$/); next if ($query->param($key) =~ /^\s*$/); next if ($key !~ /^file-to-upload-(\d+)$/); $Number = $1; ++$num_files; if ($query->param($key) =~ /([^\/\\]+)$/) { $filename = $1; $File_Handle = $query->param($key); unless ($filename =~ /$allowed_ext/) { $allowed_ext =~ s/\\//g; $allowed_ext =~ s/\$//g; @ext = split (/\Q|\E/o,$allowed_ext); $allowed_ext = join(" or ",@ext); return "Only files with the following extension(s) are allowed: $allowed_ext"; } } else { return "You attempted to upload $filekey that isn't properly formatted. Please rename the file on your computer, and attempt to upload it again. Files may not have forward or backward slashes in their names. Also, they may not be prefixed with one (or more) periods."; } if (!open(OUTFILE, ">$upload_directory\/$newdirname\/$filename")) { return "There was an error opening '$upload_directory\/$newdirname\/$filename' for Writing.\n"; } binmode(OUTFILE); # This is needed to work on Windows/NT platforms. undef $BytesRead; undef $Buffer; while ($bytes = read($File_Handle,$buffer,1024)) { $totalbytes += $bytes; print OUTFILE $buffer; } push(@Files_Written, "$upload_directory\/$newdirname\/$filename"); close($File_Handle); close(OUTFILE); chmod (0666, "$upload_directory\/$newdirname\/$filename"); } if (($totalbytes + $prev_bytes) > $maximum_upload && $maximum_upload > 0) { foreach $written (@Files_Written) { unlink "$written"; } return "You have exceeded your upload limit for this record.
Your files contain $totalbytes bytes.
Combined with previous uploads totaling $prev_bytes, this exceeds the maximum limit of $maximum_upload bytes per record.
Your files were not saved.
Please try again."; } if (($num_files + $prev_files) > $maximum_files) { foreach $written (@Files_Written) { unlink "$written"; } return "You have exceeded your upload limit for this record.
You uploaded $num_files files.
Combined with previous $prev_files uploads, this exceeds the maximum limit of $maximum_files files per record.
Your files were not saved.
Please try again."; } return "ok"; } # <<< ==================================================================================== 6. db.pl ==================================================================================== *** !!! Note, there are a few places in this file where the new code goes just before an existing closing bracket '}' so you need to pay close attention. I have shown the complete subs with changes noted. Note also some code is replaced/commented out. **** Make these changes to sub delete_records: sub delete_records { # -------------------------------------------------------- # Deletes a single or multiple records. First the routine goes thrrough # the form input and makes sure there are some records to delete. It then goes # through the database deleting each entry and marking it deleted. If there # are any keys not deleted, an error message will be returned saying which keys # were not found and not deleted, otherwise the user will go to the success page. my ($key, %delete_list, $rec_to_delete, @data, $errstr, $succstr, $output); $rec_to_delete = 0; foreach $key (keys %in) { # Build a hash of keys to delete. if ($in{$key} eq "delete") { $delete_list{$key} = 1; $rec_to_delete = 1; } } $rec_to_delete or (&html_generic("Error: $html_object(s) Not Deleted", "no records specified.") and return); # Search the database for a record to delete. open (DB, "<$db_file_name") or &cgierr("error in delete_records. unable to open db file: $db_file_name.\nReason: $!"); if ($db_use_flock) { flock(DB, 1); } LINE: while () { (/^#/) and ($output .= $_ and next LINE); (/^\s*$/) and next LINE; chomp; @data = &split_decode($_); # # >>> Image Upload mod (next 26 lines) # Find and delete the image if deleting the link. # We're replacing these stock lines: # $delete_list{$data[$db_key_pos]} ? # If this id is one we want to delete, # ($delete_list{$data[$db_key_pos]} = 0) : # then mark it deleted and don't print it to the new database, # ($output .= "$_\n"); # otherwise print it. # # With all this: # Check if there is an image file, and remove it. if ($delete_list{$data[$db_key_pos]}) { # If this id is one we want to delete, $delete_list{$data[$db_key_pos]} = 0; # then mark it deleted and don't print it to the new database. if ($db_upload) { # Did we allow uploads? Set in links.def. # Find the image... if (-e "$upload_directory/$data[$db_key_pos]") { # Check if there's a pics directory for this link. opendir (DELGRAPHIC, "$upload_directory/$data[$db_key_pos]") or &cgierr("Unable to open directory in validate records: $upload_directory/$data[$db_key_pos]. Reason: $!"); @delfiles = readdir(DELGRAPHIC); closedir (DELGRAPHIC); foreach $delfile (@delfiles) { unlink ("$upload_directory/$data[$db_key_pos]/$delfile") or die "Delete failed: $!"; # Delete the images. } rmdir "$upload_directory/$data[$db_key_pos]"; # Then delete the directory from /pics. } } } else { $output .= "$_\n"; } # If no image, proceed as normal # <<< Image Upload mod } # !! Watch out for this bracket! close DB; # Reprint out the database. open (DB, ">$db_file_name") or &cgierr("error in delete_records. unable to open db file: $db_file_name.\nReason: $!"); if ($db_use_flock) { flock(DB, 2) or &cgierr("unable to get exclusive lock on $db_file_name.\nReason: $!"); } print DB $output; close DB; # automatically removes file lock # Build success/error messages. foreach $key (keys %delete_list) { $delete_list{$key} ? # Check to see if any items weren't deleted ($errstr .= "$key,") : # that should have been. ($succstr .= "$key,"); # For logging, we'll remember the one's we deleted. } chop($succstr); # Remove trailing delimeter chop($errstr); # Remove trailing delimeter $errstr ? # Do we have an error? &html_generic("Error: $html_object(s) Not Deleted", qq|The records with the following keys were not found in the database: '$errstr'.|) : # If so, then let's report go to the failure page &html_generic("$html_object(s) Deleted", "The following records were deleted from the database: '$succstr'"); # else, everything went fine. } *** And these in sub modify_record: sub modify_record { # -------------------------------------------------------- # This routine does the actual modification of a record. It expects # to find in %in a record that is already in the database, and will # rewrite the database with the new entry. First it checks to make # sure that the modified record is ok with validate record. # It then goes through the database looking for the right record to # modify, if found, it prints out the modified record, and returns # the user to a success page. Otherwise the user is returned to an error # page with a reason why. my ($status, @data, $output, $found); $status = &validate_record (%in); # Check to make sure the modifications are ok! if ($status eq "ok") { # >>> Image Upload Mod (next 21 lines) # Admin delete checkbox; if admin wants to delete just an image, do it. if (-e "$upload_directory/$in{$db_key}") { if ($in{'delete_1'} eq 'yes'){ unlink ("$upload_directory/$in{$db_key}/$in{'Imagename_1'}") or die "Delete failed: $!"; # Delete the image from /pics. $in{'Imagename_1'} = ""; # Make the field empty. $in{'Caption_1'} = ""; # Make the field empty. } if ($in{'delete_2'} eq 'yes'){ unlink ("$upload_directory/$in{$db_key}/$in{'Imagename_2'}") or die "Delete failed: $!"; # Delete the image from /pics. $in{'Imagename_2'} = ""; # Make the field empty. $in{'Caption_2'} = ""; # Make the field empty. } if ($in{'delete3'} eq 'yes'){ unlink ("$upload_directory/$in{$db_key}/$in{'Imagename_3'}") or die "Delete failed: $!"; # Delete the image from /pics. $in{'Imagename_3'} = ""; # Make the field empty. $in{'Caption_3'} = ""; # Make the field empty. } if ((!$in{'Imagename_1'}) && (!$in{'Imagename_2'}) && (!$in{'Imagename_3'})) { # If there are no images, rmdir "$upload_directory/$in{$db_key}"; # delete the directory. } } ## <<< $found = 0; # Make sure the record is in here! open (DB, "<$db_file_name") or &cgierr("error in modify_records. unable to open db file: $db_file_name.\nReason: $!"); if ($db_use_flock) { flock(DB, 1); } LINE: while () { (/^#/) and ($output .= $_ and next LINE); (/^\s*$/) and next LINE; chomp; @data = &split_decode($_); if ($data[$db_key_pos] eq $in{$db_key}) { $output .= &join_encode(%in); $found = 1; } else { $output .= "$_\n"; # else print regular line. } } close DB; if ($found) { open (DB, ">$db_file_name") or &cgierr("error in modify_records. unable to open db file: $db_file_name.\nReason: $!"); if ($db_use_flock) { flock(DB, 2) or &cgierr("unable to get exclusive lock on $db_file_name.\nReason: $!"); } print DB $output; close DB; # automatically removes file lock &html_modify_success; } else { &html_modify_failure("$in{$db_key} (can't find requested record)"); } } else { &html_modify_failure($status); # Validation Error } } *** Make these changes in 'sub validate_records'. You need to pay close attention to brackets! sub validate_records { # -------------------------------------------------------- # This routine takes a list of records to either delete, validate # or modify and does the appropriate action. my ($rec_to_delete, $rec_to_validate, $rec_to_modify, %delete_list, %validate_list, %modify_list, %links, @lines, @data, $id, $first, $last, $errstr, $output); # First let's go through %in and see what we have to delete, modify # and/or validate. We also store all the links in easy to get at hashes. # We know what fields go with what records as they should all be of the form # ID-Field_Name. For instance: 12-URL is the URL field for record number 12. $rec_to_delete = $rec_to_validate = $rec_to_modify = 0; foreach $key (keys %in) { # Build a hash of keys to delete, validate and modify. ($in{$key} eq "delete") and $delete_list{$key} = 1 and $rec_to_delete = 1; ($in{$key} eq "validate") and $validate_list{$key} = 1 and $rec_to_validate = 1; ($in{$key} eq "modify") and $modify_list{$key} = 1 and $rec_to_modify = 1; ($key =~ /^(.*)-(\d+)$/) and $links{$2}{$1} = $in{$key}; } # If there isn't anything to do, let's complain. if (!$rec_to_validate and !$rec_to_delete and !$rec_to_modify) { &html_generic ("Problems Validating $html_objects", "Error: No records specified."); return; } # Let's go through the validation file and remove all the ones # we want to validate as well as all the ones we want to delete. if ($rec_to_validate or $rec_to_delete) { open (VAL, "<$db_valid_name") or &cgierr("error in validate_records. unable to open validate file: $db_valid_name. Reason: $!"); if ($db_use_flock) { flock (VAL, 1); } LINE: while () { (/^#/) and ($output .= $_ and next LINE); (/^\s*$/) and next LINE; chomp; @data = &split_decode($_); $id = $data[$db_key_pos]; # >>> Image Upload mod [1] (next 16 lines) ## OK! if ($delete_list{$id}) { $delete_list{$id} = 0; # If declining addition from add.cgi, delete the image from /pics. $deldirname = $data[$db_key_pos]; # Get link ID. if (-e "$upload_directory/$deldirname") { # Check if there's a pics directory opendir (GRAPHIC, "$upload_directory/$deldirname") or &cgierr("unable to open directory: $upload_directory/$deldirname. Reason: $!"); @files = readdir(GRAPHIC); closedir (GRAPHIC); foreach $file (@files) { next if ($file =~ /^\./); # Skip "." and ".." entries. unlink ("$upload_directory/$deldirname/$file") or die "Delete failed: $!"; # Delete the pics. } rmdir ("$upload_directory/$deldirname"); # Then delete the directory from /pics. } } # Watch this bracket # <<< elsif ($validate_list{$id}) { $validate_list{$id} = 2; } else { $output .= "$_\n"; } } close VAL; open (VAL, ">$db_valid_name") or &cgierr("error in validate_records. unable to open validate file: $db_valid_name. Reason: $!"); flock(VAL, 2) unless (!$db_use_flock); print VAL $output; close VAL; # automatically removes file lock undef $output; } # Now if we have something to delete from the modify list, let's get rid of it. if ($rec_to_modify or $rec_to_delete) { open (MOD, "<$db_modified_name") or &cgierr("error in validate_records. unable to open modified database: $db_modified_name. Reason: $!"); if ($db_use_flock) { flock (MOD, 1); } LINE: while () { (/^#/) and ($output .= $_ and next LINE); (/^\s*$/) and next LINE; chomp; @data = &split_decode($_); $id = $data[$db_key_pos]; if ($delete_list{$id}) { $delete_list{$id} = 0; # >>> Image Upload mod [2] TESTING if ($in{'delete_1'} eq "yes") { $pic_1 = $in{'image_1'}; unlink ("$upload_directory/$id/$pic_1") or die "Delete failed: [2] $upload_directory/$id/$pic_1 $!"; # Delete the pics. } } # KEEP BRACKET! elsif ($modify_list{$id}) { $modify_list{$id} = 2; } else { $output .= "$_\n"; } } close MOD; open (MOD, ">$db_modified_name") or &cgierr("error in validate_records. unable to open modified database: $db_modified_name. Reason: $!"); flock(MOD, 2) unless (!$db_use_flock); print MOD $output; close MOD; # automatically removes file lock undef $output; } # Now we update any modifications to the database. if ($rec_to_modify) { $found = 0; # Make sure the record is in here! # >>> Image Upload mod [3] TESTING if ($in{'delete_1'} eq "yes") { unlink ("$upload_directory/$id/$pic_1") or die "Delete failed: [3] $upload_directory/$id/$pic_1 $!"; # Delete the pics. } open (DB, "<$db_file_name") or &cgierr("error in validate_records. unable to open db file: $db_file_name. Reason: $!"); if ($db_use_flock) { flock (DB, 1); } LINE: while () { (/^#/) and ($output .= $_ and next LINE); (/^\s*$/) and next LINE; chomp; @data = &split_decode($_); $id = $data[$db_key_pos]; if ($modify_list{$id} == 2) { # If this is the one we are looking for $output .= &join_encode(%{$links{$id}}); $modify_list{$id} = 0; $found = 1; } else { $output .= "$_\n"; # else print regular line. } } close DB; if ($found) { open (DB, ">$db_file_name") or &cgierr("error in validate_records. unable to open db file: $db_file_name. Reason: $!"); flock(DB, 2) unless (!$db_use_flock); print DB $output; close DB; # automatically removes file lock } undef $output; } # Now let's see if we have something to add to the real database, then let's do it. if ($rec_to_validate) { open (DB, ">>$db_file_name") or &cgierr("error in validate_records, unable to open db file: $db_file_name. Reason: $!"); flock(DB, 2) if ($db_use_flock); foreach $id (keys %validate_list) { if ($validate_list{$id} == 2) { # >>> Image Upload Mod [4] ## OK! if (-e "$upload_directory/$id") { # Check if there's a pic directory if ($in{'delete_1'} eq "yes") { $pic_1 = $in{'image_1'}; unlink ("$upload_directory/$id/$pic_1") or die "Delete failed: [4] $!"; # Delete the pics. } if ($in{'delete_2'} eq "yes") { $pic_2 = $in{'image_2'}; unlink ("$upload_directory/$id/$pic_2") or die "Delete failed: [4] $!"; # Delete the pics. } if ($in{'delete_3'} eq "yes") { $pic_3 = $in{'image_3'}; unlink ("$upload_directory/$id/$pic_3") or die "Delete failed: [4] $!"; # Delete the pics. } # Check if there any pics left in the directory... opendir (GRAPHIC, "$upload_directory/$id") or &cgierr("unable to open directory: $upload_directory/$id. Reason: $!"); @files = readdir(GRAPHIC); closedir (GRAPHIC); foreach $file (@files) { next if ($file =~ /^\./); # Skip "." and ".." entries. } if (!$file) { rmdir ("$upload_directory/$id"); # If not, delete it from /pics. } } # <<< print DB &join_encode(%{$links{$id}}); $validate_list{$id} = 0; } } close DB; } # Now let's check to make sure everything that was asked to be val/del/mod # actually happened. If not, let's complain. foreach $key (keys %validate_list) { if ($validate_list{$key}) { $errstr .= "
  • Validate Error: $key. Couldn't find record in validation database."; } else { $valsuc .= "$key,"; } } foreach $key (keys %delete_list) { if ($delete_list{$key}) { $errstr .= "
  • Delete Error: $key. Couldn't find record in validation/modified database."; } else { $delsuc .= "$key,"; } } foreach $key (keys %modify_list) { if ($modify_list{$key}) { $errstr .= "
  • Modify Error: $key. Couldn't find record in modified/links database."; } else { $modsuc .= "$key,"; } } chop($errstr); chop($valsuc); chop ($delsuc); chop ($modsuc); # Before we display the HTML, let's fire off some validate/modify/delete emails # lettings visitors know we've added their link. We only send the mail # if $modify_list{$id} = 0 (if it's still 1, that means there was an error). # NOTE: You can modify the text of the email in the email templates. &html_print_headers; # Just in case sendmail coughs up an error. if ($db_email_modify) { ID: foreach $id (keys %modify_list) { if ($modify_list{$id}) { next ID; } elsif (${$links{$id}}{'Contact Email'} =~ /(@.*@)|(\.\.)|(@\.)|(\.@)|(^\.)/ || ${$links{$id}}{'Contact Email'} !~ /^.+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,3}|[0-9]{1,3})(\]?)$/) { $errstr .= ($errstr, "
  • Email Error: $id. Record validated, but couldn't send auto email. Reason: Bad Email address: '${$links{$id}}{'Contact Email'}'."); } else { &html_modify_email (%{$links{$id}}); } } } if ($db_email_add) { ID: foreach $id (keys %validate_list) { if ($validate_list{$id}) { next ID; } elsif (${$links{$id}}{'Contact Email'} =~ /(@.*@)|(\.\.)|(@\.)|(\.@)|(^\.)/ || ${$links{$id}}{'Contact Email'} !~ /^.+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,3}|[0-9]{1,3})(\]?)$/) { $errstr .= ($errstr, "
  • Email Error: $id. Record validated, but couldn't send auto email. Reason: Bad Email address: '${$links{$id}}{'Contact Email'}'."); } else { &html_validate_email (%{$links{$id}}); } } } ID: foreach $id (keys %delete_list) { if ($delete_list{$id}) { next ID; } elsif (!$in{"reason-$id"}) { next ID; } elsif (${$links{$id}}{'Contact Email'} =~ /(@.*@)|(\.\.)|(@\.)|(\.@)|(^\.)/ || ${$links{$id}}{'Contact Email'} !~ /^.+\@(\[?)[a-zA-Z0-9\-\.]+\.([a-zA-Z]{2,3}|[0-9]{1,3})(\]?)$/) { $errstr .= ($errstr, "
  • Email Error: $id. Record deleted, but couldn't send rejection letter. Reason: Bad Email addres: '${$links{$id}}{'Contact Email'}'."); } else { &html_reject_email (%{$links{$id}}); } } # Now let's go to the error page or the success page depending on # what $errstr is. $errstr ? &html_generic ("Validate Links", "Error validating links:
      $errstr
    ") : &html_validate_success($valsuc, $modsuc, $delsuc); } ==================================================================================== 6. admin.cgi ==================================================================================== *** An easy one! Add this before 'sub main'. BTW, you need to have the 'File::Copy' perl module on your server. use File::Copy; ==================================================================================== 7. site_html_templates.pl ==================================================================================== *** Make changes as shown for all these subs: sub site_html_link { # -------------------------------------------------------- # This routine is used to display what a link should look # like. my %rec = @_; # Set new and pop to either 1 or 0 for templates. ($rec{'isNew'} eq 'Yes') ? ($rec{'isNew'} = 1) : (delete $rec{'isNew'}); ($rec{'isPopular'} eq 'Yes') ? ($rec{'isPopular'} = 1) : (delete $rec{'isPopular'}); return &load_template ('link.html', { detailed_url => "$db_detailed_url/$rec{'ID'}$build_extension", upload_directory_url => $upload_directory_url, # <<< Image Upload Mod %rec, %globals }); } sub site_html_add_form { # -------------------------------------------------------- # This routine determines how the add form page will look like. # &html_print_headers; my $category = shift; $category ? ($category = qq~$category ~) : ($category = &build_select_field ("Category", "$in{'Category'}")); print &load_template ('add.html', { Category => $category, upload_pictures => &upload_pictures, # <<< Upload Images Mod %globals }); } # <<< Upload Images Mod, new subroutine. sub upload_pictures { # --------------------------------------------------------- # Create the file upload fields for add.cgi $upload = qq|
     
    You may upload up to $maximum_files files, for a total of $maximum_upload bytes.\n|; for ($u=1;$u<=$maximum_files ;++$u) { # Maximum Files is set in links.cfg, Image Upload Options, $upload .= qq|Browse Files:\n Caption $u:

    \n|; } return $upload; } # <<< end new sub sub site_html_add_failure { # -------------------------------------------------------- # This routine determines how the add failure page will look like. my ($errormsg) = shift; $in{'Category'} ? ($in{'Category'} = qq~$in{'Category'}~) : ($in{'Category'} = &build_select_field ("Category")); &html_print_headers; print &load_template ('add_error.html', { error => $errormsg, upload_pictures => &upload_pictures, # <<< Upload Images Mod %in, %globals }); } # >>> Security Mod sub site_html_modify_form_up { # -------------------------------------------------------- # This routine determines how the modify form page will look like. &html_print_headers; print &load_template ('modify_up.html', { %globals }); } sub site_html_modify_up_failure { # -------------------------------------------------------- # This routine determines how the modify form page will look like. my ($error) = shift; &html_print_headers; print &load_template ('modify_up_failure.html', { error => $error, %in, %globals }); } # <<< Security Mod sub site_html_modify_form { # -------------------------------------------------------- # This routine determines how the modify form page will look like. my (%record) = @_; my $cat = &build_select_field ("Category", "$record{'Category'}"); my $delete_image = &build_checkbox_field ("DeleteImage", ($rec{'DeleteImage'})); # <<< Image Upload Mod &html_print_headers; print &load_template ('modify.html', { Cat => $cat, # <<< Security Mod ID => $ID, # <<< Image Upload Mod %in,# <<< Image Upload Mod upload_directory_url => $upload_directory_url, # <<< Image Upload Mod upload_pictures_mod => &upload_pictures_mod, # <<< Image Upload Mod %record, %globals }); } # >>> Image Upload Mod, new subroutine. sub upload_pictures_mod { # -------------------------------------------------------- # Create the file upload fields for modif.cgi. $upload = qq|
    \n|; # Need to give '$upload' an initial value, so it can build the rest as needed. if (-e "$upload_directory/$rec{$db_key}") { opendir (GRAPHIC, "$upload_directory/$rec{$db_key}") or &cgierr("unable to open directory: $upload_directory/$rec{$db_key}. Reason: $!"); @files = readdir(GRAPHIC); closedir (GRAPHIC); foreach $file (@files) { next if ($file =~ /^\./); # Skip "." and ".." entries.. ++$num_files; @stats = stat "$upload_directory/$rec{$db_key}/$file"; $prev_bytes +=$stats[7]; } foreach $file (@files) { # Next we create the HTML for each image, with delete checkbox and caption field. next if ($file =~ /^\./); # Skip "." and ".." entries. if ($rec{'Imagename_1'} eq $file) { $upload .= qq|Image 1:\n Delete image 1? (Deleting image automatically deletes caption.)\n Caption 1:

    \n|; } if ($rec{'Imagename_2'} eq $file) { $upload .= qq|Image 2:\n Delete image 2? (Deleting image automatically deletes caption.)\n Caption 2:

    \n|; } if ($rec{'Imagename_3'} eq $file) { $upload .= qq|Image 3:\n Delete image 3? (Deleting image automatically deletes caption.)\n Caption 3:

    \n|; } } } $new_files=$maximum_files - $num_files; $new_bytes=$maximum_upload - $prev_bytes; $upload .= qq| 
    You currently have $num_files files associated with this record, for a total of $prev_bytes bytes.
    You may upload up to $new_files additional files, with a total byte size of $new_bytes bytes.|; for ($u=1;$u<=$new_files ;++$u) { # If user is allowed to upload more files, show them the option. $upload .= qq| Browse Files:Caption:\n|; } return $upload; } # <<< sub site_html_modify_failure { # -------------------------------------------------------- # This routine determines how the modify failure page will look like. my (%record) = @_; my $errormsg = shift; $in{'Category'} = &build_select_field ("Category", $in{'Category'}); my $delete_image = &build_checkbox_field ("DeleteImage", ($in{'DeleteImage'})); # <<< Image Upload Mod &html_print_headers; print &load_template ('modify_error.html', { error => $errormsg, upload_directory_url => $upload_directory_url, # <<< Image Upload Mod upload_pictures_mod => &upload_pictures_mod, # <<< Image Upload Mod %in, %globals }); } ==================================================================================== 8. Template changes ==================================================================================== *** In add.html, add_error.html, modify.html, and modify_error.html, change the 'form action' line to include 'enctype':
    *** I placed the following code just above the Submit button row. *** add.html <%upload_pictures%> *** add_error.html <%upload_pictures%> *** modify.html Security Mod removes next section...

    Please enter the URL of the link you wish to update. Make sure it is identical to the one already in the database:

    Then add this above the Submit button row. <%upload_pictures_mod%> *** modify_error.html <%upload_pictures_mod%> *** New template, modify_up.html <%site_title%>: Modify a Resource

    <%site_title%>: Modify a Resource, Step 1

    | Home | Add a Resource | Modify a Resource | What's New | What's Cool | Top Rated | Email Updates | Random Link | Search |

    Update Your Listing, Step 1

    Please enter the Title for the listing you wish to update. This information must match what is currently in the directory.

    Title:

    Search

    Looking for something in particular?
    More search options

    Pages Updated On: <%date%> - <%time%>
    Links Engine Powered By: Gossamer Threads Inc.

    *** New template, modify_up_failure.html <%site_title%>: Modify a Resource

    <%site_title%>: Modify a Resource, Step 1 Error

    | Home | Add a Resource | Modify a Resource | What's New | What's Cool | Top Rated | Email Updates | Random Link | Search |

    There was an error trying to log in:

    <%error%>

    Please enter the Title for the listing you wish to update. This information must match what is currently in the directory.

    Title:

    Search

    Looking for something in particular?
    More search options

    Pages Updated On: <%date%> - <%time%>
    Links Engine Powered By: Gossamer Threads Inc.

    ==================================================================================== 9. Create Directory ==================================================================================== *** You need to create a new directory for the image directories in the same directory that your links pages will be built in, Call it 'pics'. (See step 3.) ==================================================================================== 10. Other ==================================================================================== *** If you have an existing links.db file, you need to add a new field to the end of each entry by adding a pipe '|' to each one for each new db field. If using three upload fields as shown in these instructions, you need to add six new pipe delimiters: 2009|Test_1||bob|bob@bob.com|0|Yes|No|0|0|Yes|24.jpg| 25|test5000||23-Nov-2009|Test_1||||0|Yes|No|0|0|Yes|||||||