Home : Products : Links 2.0 : Customization :

Products: Links 2.0: Customization: Here's a working image upload mod!: Edit Log

Here is the list of edits for this post
Here's a working image upload mod!
There are many pieces of code in these forums for an image upload mod, but most are incomplete or hard to understand. I have pieced this together, and it works great for me! A few features:
  • Upload from modify form so only pre-approved sites can upload. Can be changed to add form if desired.
  • Every image has the link ID attached to it as a prefix, making management easier. So, picture.gif becomes 47-picture.gif.
  • Uploading a new image to replace an existing one will delete the old one. A checkbox in the modify form must be checked in order to successfully replace a previous upload, to prevent mistakes.
  • If an image has been uploaded before, it will be shown in the modify form. It is not necessary to re-upload it in order to make a modification.
  • Images can also be deleted by the admin from the admin screen.


First, keep in mind that you will need to add the new fields to your links.db file. If you have a lot of links, this can be difficult, but there is a script available that simplifies the operation.

The changes to make are in red, and the code is also in the attached text file.

1. Make a new file in your admin directory, named upload_delete.pl (chmod 755):


usr/bin/perl
# Script to delete images via Links admin screen.
# Highly modified, but based on code Copyright (C) 2000 - 2001 Sierra Kempster <darkmoon@lunamorena.net>
# http://www.lunamorena.net/perl/
# Required Librariers
# --------------------------------------------------------
require "links.cfg";
&parse_form;
# Admin delete
if($ENV{QUERY_STRING} =~ /admindel/)
{
($action,$image) = split(/\+/,$ENV{QUERY_STRING},2);
unlink"$imagedir/$image";
&view; # View Archive list
}
elsif ($ENV{QUERY_STRING} =~ /view/){
($action,$image) = split(/\+/,$ENV{QUERY_STRING},2);
&view2; # View individual image
}
else
{
print"$invalid";
&view;
}

# Subroutines
sub parse_form {
# --------------------------------------------------------
read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'});
@pairs = split(/&/, $buffer);
foreach $pair (@pairs)
{
($name, $value) = split(/=/, $pair);
$value =~ tr/+/ /;
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
$value =~ s/~!/ ~!/g;
if($FORM{$name})
{
$FORM{$name} = "$FORM{$name};$value";
}
else
{
$FORM{$name} = $value;
}
}
}
sub view {
# --------------------------------------------------------
opendir(IMAGE, "$imagedir");
@images = grep /(\.jpe?g)$/i||/(\.gif)$/i||/(\.png)$/i, readdir IMAGE;
closedir(IMAGE);
@sorted = sort(@images);
$x = 0;
$y = 0;
print"\n";
print"Click the \'View Image\' link next to the image you want to delete; you will be shown the image with a \'Delete Image\' option.<br><hr>\n";
foreach $image (@sorted)
{
# print"<img src=\"$imageurl/$image\" width=\"$width\" height=\"$height\">";
print"$image";
print"\n\&#160;<a href=\"$script?view+$image\">View Image</a><br>";
print"<br>\n";
$x = $x + 1;
if($x eq "$perrow")
{
print"\n";
$x = 0;
}
}
}
sub view2 {
# --------------------------------------------------------
opendir(IMAGE, "$imagedir");
@images = grep /(\.jpe?g)$/i||/(\.gif)$/i||/(\.png)$/i, readdir IMAGE;
closedir(IMAGE);
@sorted = sort(@images);
$x = 0;
$y = 0;
print"\n";
print"Clicking \'Delete Image\' will remove the image from the server: this action cannot be undone. Be sure to also update the corresponding information in the links.db, via the\'Modify\' option in your Links Admin interface.<br><hr>\n";
{
print"<img src=\"$imageurl/$image\">";
print"<br><br>$image";
print"&#160;&#160;<a href=\"$script?admindel+$image\">Delete Image</a>";
print"<br>\n";
$x = $x + 1;
if($x eq "$perrow")
{
print"\n";
$x = 0;
}
}
}


2. To use the above file, add the following to your admin_html.pl:

sub html_navigation {
# --------------------------------------------------------
# Prints the navigation links.
#
&html_print_headers();
print qq|
<html>
<head>
<title>$html_title: Main Menu.</title>
<base target="cgimain">
</head>
<body bgcolor="#DDDDDD">
<p><$font><b>Links<br></b></font>
<$font>
<a href="$db_script_url?db=links&view_search=1">View</a><br>
<a href="$db_script_url?db=links&add_form=1">Add</a><br>
<a href="$db_script_url?db=links&delete_search=1">Delete</a><br>
<a href="$db_script_url?db=links&modify_search=1">Modify</a><br>
<a href="$db_script_url?db=links&validate_form=1">Validate</a><br>
<a href="$db_script_url?db=links&check_duplicates=1">Check Dup.</a><br>
<a href="$db_dir_url/upload_delete.pl">Image Delete</a>
</font>
</p>

3. Add these changes to your modify.cgi:

sub main {
# --------------------------------------------------------
# change for upload mod >
# local (%in) = &parse_form;
local (%in) = &parse_me_and_upload;

...edited...

sub process_form {
# --------------------------------------------------------
my ($key, $status, @values, $found);
local (%original);

# Make sure we have a link to modify.
!$in{'Current URL'} and &site_html_modify_failure ("You 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 (<DB>) {
(/^#/) and next LINE;
(/^\s*$/) and next LINE;
chomp;
@data = &split_decode($_);
if ($data[$db_url] eq $in{'Current URL'}) {
$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..
$status = &validate_record(%in);
if ($status eq "ok") {
# 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 (<MOD>) {
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.");
# Upload mod >
# Remove any new uploads.
unlink "$upload_path/$_" foreach @uploads;
# < Upload mod
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 {
# upload mod >
unlink "$upload_path/$_" foreach @uploads;
# < upload mod

# Let's change that error message from a comma delimted list to an html
# bulleted list.
&site_html_modify_failure($status);
}
}
sub send_email {
# --------------------------------------------------------
# Sends an email to the admin, letting him know that there is
# a new link waiting to be validated.
# Check to make sure that there is an admin email address defined.
$db_admin_email or &cgierr("Admin Email Address Not Defined in config file!");
my $to = $db_admin_email;
my $from = $in{$db_cols[$db_contact_email]};
my $subject = "Modification to Database: $in{'Title'}";
my $msg = qq|
The following link was modified and is awaiting validation:
ORIGINAL LINK:
===============================================
Title: $original{'Title'}
URL: $original{'URL'}
Description: $original{'Description'}
Contact Name: $original{'Contact Name'}
Contact Email: $original{'Contact Email'}
Category: $original{'Category'}
Image_1: $original{'Image_1'}
Caption_1: $original{'Caption_1'}
Image_2: $original{'Image_2'}
Caption_2: $original{'Caption_2'}


NEW LINK:
===============================================
Title: $in{'Title'}
URL: $in{'URL'}
Description: $in{'Description'}
Contact Name: $in{'Contact Name'}
Contact Email: $in{'Contact Email'}
Category: $in{'Category'}
Image_1: $original{'Image_1'}
Caption_1: $original{'Caption_1'}
Image_2: $original{'Image_2'}
Caption_2: $original{'Caption_2'}

Remote Host: $ENV{'REMOTE_HOST'}
Referer: $ENV{'HTTP_REFERER'}

To update, please go to:
$db_script_url

Sincerely,
Links Manager.
|;
# Then mail it away!
require "$db_lib_path/Mailer.pm";
my $mailer = new Mailer ( { smtp => $db_smtp_server,
sendmail => $db_mail_path,
from => $from,
subject => $subject,
to => $to,
msg => $msg,
log => $db_mailer_log
} ) or return undef;
$mailer->send or return undef;
}
# upload mod >
sub parse_me_and_upload {
#-------------------------------------
use CGI qw(:standard);

my (@pairs, %in);##
my ($buffer, $pair, $name, $value);##
my ($fnum,$file_field,$file_name,$ext,$bytes_count,$size,$buff);
my ($num)=1;
my ($IN) = new CGI;

# create a hash for all input - name => value
for ($IN->param) { $in{$_} = $IN->param($_) }

# loop through the input
for (keys %in) {

# if the field name matches File(DIGIT) this is a file upload field...
if (/^(Image_\d*)$/i) {


# establish and number associative files in array
# $in{$file_field} = name of file being uploaded from form.
# $1 = Form element name ie. File1

# Increment the form element counter
$fnum = $num++;
# do some stuff that I've yet to understand.
$file_field = $1;
$in{$file_field} =~ /([^\/\\]+)$/ and $file_name = $1;


# If the File(X) field is empty
if (length($in{$file_field}) < 1 ) {
# just to copy the value of File(X)Transfer
# into $in{$file_field}. This will either be an empty string or
# the previously uploaded filename.

$in{$file_field} = "$in{'Image_' . $fnum . '_Transfer'}";
# Other wise the File(x) form upload element has something in it.
}
else{
## LT added 'if delete_image' here...
if (($in{'image_' . $fnum . '_delete'} eq "yes") &&
(length($in{'Image_' . $fnum . '_Transfer'}) > 1)) {
# So first remove any previously uploaded files in that file(x) position
unlink "$upload_path/$in{'Image_' . $fnum . '_Transfer'}";

} # close LT 'if delete_image'
else { #LT
&site_html_modify_failure("To replace an existing image you need to check the \'delete image\' box.") and return;
} # close LT 'else'

# Now let's go ahead and upload that new file
# changed to append Link ID instead of random number >
$file_name = $in{$db_key} . $file_name; # Prevent files being overwritten by appending Link ID
# < changed to append Link ID


open U, ">$upload_path/$file_name" or &cgierr("Can't open $upload_path/$file_name : $!");
binmode U; # needed for Windows servers

while ($bytes_count = read($in{$file_field},$buff,2096)) {
$size += $bytes_count;
print U $buff;
}
close U;
$in{$file_field} = $file_name;

# create an array of upload fields so we can unlink if there's a booboo
push @uploads, $in{$file_field}
}
}
}
# return the %in hash as normal
return %in;

}
# < upload mod



4. Add these fields to your links.def file:

# Database Definition: LINKS
# --------------------------------------------------------
# Definition of your database file.
%db_def = (
ID => [0, 'numer', 5, 8, 1, '', ''],
Title => [1, 'alpha', 40, 75, 1, '', ''],
URL => [2, 'alpha', 40, 75, 1, 'http://', '^http|news|mailto|ftp'],
Date => [3, 'date', 15, 15, 1, \&get_date, ''],
Category => [4, 'alpha', 0, 150, 1, '', ''],
Description => [5, 'alpha', '40x3', 500, 0, '', ''],
'Contact Name' => [6, 'alpha', 40, 75, 0, '', ''],
'Contact Email' => [7, 'alpha', 40, 75, 0, '', '.+@.+\..+'],
Hits => [8, 'numer', 10, 10, 1, '0', '\d+'],
isNew => [9, 'alpha', 0, 5, 0, 'No', 'No|Yes'],
isPopular => [10, 'alpha', 0, 5, 0, 'No', 'No|Yes'],
Rating => [11, 'numer', 10, 10, 1, 0, '^[\d\.]+$'],
Votes => [12, 'numer', 10, 10, 1, 0, '^\d+$'],
ReceiveMail => [13, 'alpha', 10, 10, 1, 'Yes', 'No|Yes'],
Image_1 => [14, 'alpha', 0, 150, 0, '', '^[^\.]+\.(gif|GIF|jpg|JPG)$'],
Caption_1 => [15, 'alpha', 40, 75, 0, '', ''],
Image_2 => [16, 'alpha', 0, 150, 0, '', '^[^\.]+\.(gif|GIF|jpg|JPG)$'],
Caption_2 => [17, 'alpha', 40, 75, 0, '', '']
);

# Field Number of some important fields. The number is from %db_def above
# where the first field equals 0.
$db_title = 1; $db_url = 2; $db_modified = 3;
$db_category = 4;
$db_desc = 5;
$db_contact_name = 6; $db_contact_email = 7;
$db_hits = 8; $db_isnew = 9;
$db_ispop = 10; $db_rating = 11; $db_votes = 12;
$db_mail = 13;
$db_image_1 = 14; $db_caption_1 = 15; $db_image_2 = 16;
$db_caption_2 = 17;



5. Make these additions to the following subs in site_html_templates.pl:

sub site_html_modify_form {
# --------------------------------------------------------
# This routine determines how the modify form page will look like.
my $category = &build_select_field ("Category", "$in{'Category'}");
&html_print_headers;
print &load_template ('modify.html', {
Category => $category,
Image_1 => $image_1,
Caption_1 => $caption_1,
Image_2 => $image_2,
Caption_2 => $caption_2,
%globals
});
}

sub site_html_confirm_modify {
# --------------------------------------------------------
# This routine is used to display what a modify confirm page should look like.
&html_print_headers;

print &load_template ('modify_confirm.html', {
Category => $category,
Image_1 => $image_1,
Caption_1 => $caption_1,
Image_2 => $image_2,
Caption_2 => $caption_2,
%in,
%globals
});
}

sub site_html_modify_success {
# --------------------------------------------------------
# This routine determines how the modify success page will look like.
&html_print_headers;
print &load_template ('modify_success.html', {
Category => $category,
Image_1 => $image_1,
Caption_1 => $caption_1,
Image_2 => $image_2,
Caption_2 => $caption_2,
%in,
%globals
});
}

6. Add this code to your template files. Note that you can (need to) remove my site-specific html code, such as <dt> and <div=...>.

modify.html

<%if Image_1%>
<dt>Current Image 1:</dt><dd><img src="<%build_upload_url%>/<%Image_1%>" /><div class="formhelp">[<%Image_1%>]</div></dd>
<dt>Delete Image 1?</dt><dd><input type="checkbox" name="image_1_delete" value="yes"></dd>
<%endif%>
<dt>Image 1</dt><dd><input name="Image_1" type="file" value="" /><input type="hidden" name="Image_1_Transfer" value="<%Image_1%>" /></dd>
<dt>Caption 1:</dt><dd><textarea wrap="virtual" input name="Caption_1" value="" rows="3" cols="42"><%Caption_1%></textarea></dd>

<%if Image_2%>
<dt>Current Image 2:</dt><dd><img src="<%build_upload_url%>/<%Image_2%>" /><div class="formhelp">[<%Image_2%>]</div></dd>
<dt>Delete Image 2?</dt><dd><input type="checkbox" name="image_2_delete" value="yes"></dd>
<%endif%>
<dt>Image 2</dt><dd><input name="Image_2" type="file" value="" /><input type="hidden" name="Image_2_Transfer" value="<%Image_2%>" /></dd>
<dt>Caption 2:</dt><dd><textarea wrap="virtual" input name="Caption_2" value="" rows="3" cols="42"><%Caption_2%></textarea></dd>


modify_success.html template

<dt>Image 1:</dt><dd><%Image_1%></dd>
<dt>Caption 1:</dt><dd><%Caption_1%></dd>
<dt>Image 2:</dt><dd><%Image_2%></dd>
<dt>Caption 2:</dt><dd><%Caption_2%></dd>


detailed.html template

<div class="float_container">
<div class="float_left">

<%if Image_1%>
<center>
<img
alt="image"
src="<%build_upload_url%>/<%Image_1%>" />
<br />
<%Caption_1%>
</center>
<%endif%>
</div>
<div class="float_right">
<%if Image_2%>
<center>
<img
alt="image"
src="<%build_upload_url%>/<%Image_2%>" />
<br />
<%Caption_2%>
</center>
<%endif%>
</div>
<div class="spacer"> </div>
</div> <!-- /float_conatiner -->


I have this set up so that the images only show in the detailed page. You can add the same or similar code to your link.html template if you want the images with the normal link listings.

I also have it set to just have two images, but you can easily change it to have more, or to specify one (thumbnail) to go with the link.html. To do that, you should specify a size in the html.

Actually, a size would be great for every image, but I have not figured out how to use image::size yet...

I think I posted all the changes required. If you try this, and run into a problem, post it here, I'll do my best to help.


Leonard
aka PerlFlunkie

Last edited by:

PerlFlunkie: Feb 17, 2005, 10:40 PM

Edit Log: