# BetterFileUploader.pm
# Better File Uploader 2.0 plugin for Movable Type
# http://www.eatdrinksleepmovabletype.com/plugins/better_file_uploader/
# by Dan Wolfgang
# http://www.danandsherree.com
# last modified 11/04/2006

package BetterFileUploader;

use strict;
use base 'MT::App';
use base 'MT::App::CMS';
use MT::Util qw( encode_url decode_url encode_html );
use Symbol qw( gensym );
use File::Basename;

my $debug = 0;
my @extra_paths;

sub init {
    my $app = shift;
    $app->SUPER::init(@_) or return;
    $app->add_methods(
		'start_bfu'                   => \&start_bfu,
		'speeddemon-prefs'            => \&speeddemon_prefs,
		'upload_file'                 => \&upload_file,
		'show_upload_html'            => \&show_me_the_html,
		'start_new_entry'             => \&start_new_entry,
		'start_upload_entry'          => \&start_upload_entry,
		'start_upload_extended_entry' => \&start_upload_extended_entry,
        'view'                        => \&view,
        );
    $app->{default_mode}   = 'view';
    $app->{requires_login} = 1;
    $app;
}

sub _debug {
    MT::log( $_[0] ) if $debug;
}


# Get configuration options, for system-wide or blog-specific setup.
sub _blog_config {
	my ($blog_id) = @_;
	my $sys       = MT::Plugin::BetterFileUploader->instance->get_config_hash('system');
	if ($sys->{'system_override'}) {
		return $sys;
	}
    else {
		return MT::Plugin::BetterFileUploader->instance->get_config_hash('blog:' . $blog_id);
	}
}



# The default mode. You only get here by calling the script directly... which of course doesn't work.
sub view {
	my $app = shift;
    $app->error( $app->translate(
        "Better File Uploader can't be used this way! "
        . "Log into MT and use it by uploading a file."
        ) );
}


# This is called when you click the "Upload File" link.
sub start_bfu {
	my $app     = shift;
	my $q       = $app->param;
	my $blog_id = $app->param('blog_id');
	my $perms   = MT::Permission->load({ 
                    author_id => $app->{author}->id,
					blog_id => $blog_id,
                    })
		or return $app->error( $app->translate("No permissions!") );
	return $app->error( $app->translate("Permission denied.") )
		unless $perms->can_upload;
	my $config = &_blog_config($blog_id);
	my %param;

	use MT::Blog;
	my $blog = MT::Blog->load($blog_id, {cached_ok=>1});
	$app->add_breadcrumb( $app->translate('Upload File') );
	$param{local_site_path} = $blog->site_path;

	my $pathdefault = $config->{'pathdefault'};
	my $default;
	
	if ($config->{'path_upload_a'} || $config->{'path_append_year_a'}) {
		$default = ($pathdefault eq 'a') ? 1 : 0;
		&_create_extra_paths($blog_id, 
                            $default,
                            $config->{'path_upload_a'}, 
                            $config->{'path_append_year_a'}, 
                            $config->{'path_append_month_a'}, 
                            $config->{'path_append_day_a'},
                            );
	}
	if ($config->{'path_upload_b'} || $config->{'path_append_year_b'}) {
		$default = ($pathdefault eq 'b') ? 1 : 0;
		&_create_extra_paths($blog_id,
                            $default, 
                            $config->{'path_upload_b'},
                            $config->{'path_append_year_b'}, 
                            $config->{'path_append_month_b'}, 
                            $config->{'path_append_day_b'} ,
                            );
	}
	if ($config->{'path_upload_c'} || $config->{'path_append_year_c'}) {
		$default = ($pathdefault eq 'c') ? 1 : 0;
		&_create_extra_paths($blog_id, 
                            $default, 
                            $config->{'path_upload_c'}, 
                            $config->{'path_append_year_c'}, 
                            $config->{'path_append_month_c'}, 
                            $config->{'path_append_day_c'} 
                            );
	}
	if ( $config->{'path_site'} ) {
		$default = ($pathdefault eq 'site') ? 1 : 0;
		&_create_extra_paths($blog_id, $default );	
	}
	if ( $config->{'path_categories'} ) {
		my @categories = MT::Category->top_level_categories($blog_id);
		foreach my $cat (@categories) {
			$default = 0;
			&_create_extra_paths($blog_id, $default, $cat->basename );
		}
	}
	# if Result Stacker isn't being used, delete any existing data .
	if (!$q->param('result_stacker')) {
		my $session = MT::Session->new;
		$session->id('BFU Result Stacker');
		$session->kind('rs');
		$session->data('');
		$session->save;
	}		
	$param{blog_id}            = $blog_id;
	$param{blog_name}          = $blog->name;
	$param{multiple}           = $config->{'multiple'};
	$param{defaultpath}        = $config->{'defaultpath'};
	$param{path_upload_a}      = $config->{'path_upload_a'};
	$param{path_upload_b}      = $config->{'path_upload_b'};
	$param{path_upload_c}      = $config->{'path_upload_c'};
	$param{extra_path}         = $config->{'extra_path'};
	$param{append_basename}    = $config->{'append_basename'};
	$param{extra_paths}        = \@extra_paths;
	$param{use_result_stacker} = $q->param('result_stacker');
	$param{tooltips}           = $config->{'tooltips'};

	if ($config->{'speeddemon_use'}) {
		$param{speeddemon} = 1;
		
		if (!$config->{'speeddemon_wrapper'}) { $param{wrapper_0} = 1;
			#Necessary because system-level doesn't recognize config option, for some reason.
		}
        elsif ($config->{'speeddemon_wrapper'} == 0) { $param{wrapper_0} = 1;
		} 
        elsif ($config->{'speeddemon_wrapper'} == 1) { $param{wrapper_1} = 1;
		} 
        elsif ($config->{'speeddemon_wrapper'} == 2) { $param{wrapper_2} = 1;
		}
		$param{wrapper_name_a} = $config->{'wrapper_name_a'};
		$param{wrapper_name_b} = $config->{'wrapper_name_b'};
		if (!$config->{'speeddemon_publish'}) { $param{speeddemon_publish_popup} = 1;
			#Necessary because system-level doesn't recognize config option, for some reason.
		} 
        elsif ($config->{'speeddemon_publish'} eq 'popup') { $param{speeddemon_publish_popup} = 1;
		} 
        elsif ($config->{'speeddemon_publish'} eq 'embedded') { $param{speeddemon_publish_embedded} = 1;
		} 
        elsif ($config->{'speeddemon_publish'} eq 'pagebuilder') { $param{speeddemon_publish_pagebuilder} = 1;
		}
		&_debug('SpeedDemon wrapper option: ' . $config->{'speeddemon_wrapper'} . '. Publish option: ' . $config->{'speeddemon_publish'});
	}
	$app->build_page('upload.tmpl', \%param);
}

sub _create_extra_paths {
	my ($blog_id, $default, $path, $year, $month, $day) = @_;
	use MT::Util qw( epoch2ts );
	use MT::Blog;
	my $blog       = MT::Blog->load($blog_id, {cached_ok=>1});
	my $date_stamp = epoch2ts($blog, time);

	#Add the year/month/ bit, if necessary.
	if ($year && $month && $day) {
		$date_stamp =~ s!^(\d\d\d\d)(\d\d)(\d\d).*!$1/$2/$3!;
		$path .= $date_stamp;
	}
    elsif ($year && $month) {
		$date_stamp =~ s!^(\d\d\d\d)(\d\d)(\d\d).*!$1/$2!;
		$path .= $date_stamp;
	} 
    elsif ($year) {
		$date_stamp =~ s!^(\d\d\d\d)(\d\d)(\d\d).*!$1!;
		$path .= $date_stamp;
	}
	my $label_path = 'Site URL';
	
	push @extra_paths, { path => $path,
                         label => "<$label_path>/" . $path,
                         default => $default,
                         };
	return @extra_paths;
}

# This does the actual uploading.
sub upload_file {
	my $app = shift;
	my $blog_id = $app->param('blog_id');
	my $perms = MT::Permission->load({ author_id => $app->{author}->id,
								 blog_id => $blog_id, })
		or return $app->error( $app->translate("No permissions!") );
	return $app->error( $app->translate("Permission denied.") )
		unless $perms->can_upload;
	$app->validate_magic() or return;

	my $q = $app->param;
	my(@files, $up, $uploaded_file, $no_upload, %param, @upload_data, $has_overwrite, $overwrite, 
		$basename, $fmgr, $relative_path, $middle_path, $extra_path_save, $url, $is_image,
		$path, $relative_url, $absolute_file_path, $tmp, $tmp_dir, $tmp_file, $tmp_fh, $vol,
		$thumb_w,	$thumb_h, $preview_w, $preview_h, @upload_confirm, $file_exists, $dir,
		$file, $ext, $relative_path_save, @tmp, $umask, $old, $bytes, $w, $h, $id, $first,
		$wrapper_0, $wrapper_1, $wrapper_2, $chmod_perms, );
	my $config = &_blog_config($blog_id);
	use MT::Blog;
	my $blog = MT::Blog->load($blog_id, {cached_ok=>1});

	if ( $q->param('file') ) {
		&_debug("Uploading a file(s).");
		@files = $q->upload('file');
	} 
    elsif ( $q->param('fname') ) {
		&_debug("File exists. Renaming or overwriting a file.");
		@files = $q->param('fname');
		@tmp = $q->param('temp');
	} 
    else {
        # We shouldn't ever even get to this point, because there's some JavaScript in the tempalte to detect no file
        # selected. Might get here if JavaScript is disabled, but MT's kinda useless without it, so not likely.
		return $app->error( $app->translate("You did not choose a file to upload.") );
	}
	my $count = 0;

	foreach $uploaded_file (@files) {
		&_debug("File to upload: $uploaded_file");
		$has_overwrite = $q->param('has_overwrite');
		$overwrite     = $q->param('overwrite');
		$tmp           = $tmp[$count];
		$basename      = $uploaded_file;
        
        $basename =~ s!^.*(/|\\)!!; # Remove everything from the path but the file name. Don't need directories.
		if ($basename =~ m!\.\.|\0|\|!) {
			return $app->error($app->translate("Invalid filename '[_1]'", $basename));
		}
		$fmgr = $blog->file_mgr;

		# Build the full path to the file's destination.
		$path            = $blog->site_path;
		$relative_path   = $q->param('extra_path');
		$middle_path     = $q->param('middle_path') || '';
		$extra_path_save = $relative_path;
		if ($middle_path ne '') {
			$relative_path = $middle_path . ($relative_path ? '/' . $relative_path : '');
		}
		if ($relative_path) {
			if ($relative_path =~ m!\.\.|\0|\|!) {
				return $app->error( $app->translate("Invalid extra path '[_1]'", $relative_path) );
			}
			$path = File::Spec->catdir($path, $relative_path);
			unless ($fmgr->exists($path)) {
				$fmgr->mkpath($path)
					or return $app->error($app->translate(
						"Can't make path '[_1]': [_2]", $path, $fmgr->errstr));
			}
		}
		$relative_path_save = $relative_path;
		$relative_url       = File::Spec->catfile($relative_path, encode_url($basename));
		$relative_path      = File::Spec->catfile($relative_path, $basename);
		$absolute_file_path         = File::Spec->catfile($path, $basename);
		&_debug("The absolute (full) local file path will be $absolute_file_path");

		if ($fmgr->exists($absolute_file_path)) {
			&_debug("File $absolute_file_path exists.");
			if ($has_overwrite) {
				if ($tmp) {
					&_debug("File exists, and should be overwritten or renamed.");
					if ($tmp =~ m!([^/]+)$!) {
						$tmp = $1;
					} 
                    else {
						return $app->error($app->translate(
							"Invalid temp file name '[_1]'", $tmp));
					}
					$tmp_dir  = $app->config('TempDir');
					$tmp_file = File::Spec->catfile($tmp_dir, $tmp);
					if ($overwrite eq '0') { # Cancel the upload; delete the temp file.
						if (-e $tmp_file) { #Delete the temp file.
							unlink($tmp_file)
								or return $app->error($app->translate(
									"Error deleting '[_1]': [_2]", $tmp_file, "$!"));
						}
						return &start_bfu($app);
					} 
                    elsif ($overwrite eq '1') { # Overwrite the file (by simply renaming it).
						$fmgr->rename($tmp_file, $absolute_file_path);
							#Temp files have permissions of 0600. After being moved, they need to be changed
							#so that the file can be read. Umask can't be used; that's for creating new files.
							#File must be chmod'd to new permissions, but I can't figure out how to calculate them.
							#Hardcoding 0644 is safe. Also done for rewriting, below.
						#$chmod_perms = 777 - int $app->config('UploadUmask');
						#$chmod_perms = oct $chmod_perms;
						chmod 0644, $absolute_file_path;
					} 
                    elsif ($overwrite eq '2') { # Rename the file, don't overwrite it.
											# Renaming isn't perfect. It only works nicely with alpha-numeric characters.
											# Maybe one day I can figure out how to do it more intelligently.
						($file, $dir, $ext) = fileparse($absolute_file_path, qr/\.\D.*/);
						while ($fmgr->exists($absolute_file_path)) {
							++$file;
							$basename   = $file . $ext;
							$absolute_file_path = File::Spec->catfile($path, $basename);
						}
						$fmgr->rename($tmp_file, $absolute_file_path);
						#$chmod_perms = 777 - int $app->config('UploadUmask');
						#$chmod_perms = oct $chmod_perms;
						chmod 0644, $absolute_file_path;
						# Update these variables to reflect the renamed file.
						$relative_url  = File::Spec->catfile($relative_path_save, encode_url($basename));
						$relative_path = File::Spec->catfile($relative_path_save, $basename);
					}
				&_debug("$absolute_file_path is $bytes large.");
				}
			} 
            else {
				eval { use File::Temp };
				if ($@) {
					return $app->error($app->translate(
						"File with name '[_1]' already exists. (Install " .
						"File::Temp if you'd like to be able to overwrite " .
						"existing uploaded files.)", $basename));
				}
				&_debug("File exists, and is being written to a temporary location.");
				$tmp_dir = $app->config('TempDir');
				eval {
					($tmp_fh, $tmp_file) = File::Temp::tempfile(DIR => $tmp_dir);
				};
				if ($@) {
					return $app->errtrans( "Error creating temporary file; please ".
						"check your TempDir setting in mt-config.cgi (currently '[_1]') " .
						"this location should be writable.",
						($tmp_dir ? $tmp_dir : '['.$app->translate('unassigned').']'));
				}
				# Write the file to the temp location, then close the filehandle.
				defined($bytes = $fmgr->put($uploaded_file, $tmp_file, 'upload'))
					or return $app->error($app->translate( "File with name '[_1]' already exists; ".
						"Tried to write to tempfile, but open failed: [_2]", $basename, "$!"));
				close $uploaded_file;
				&_debug("$tmp_file is $bytes large");

				($vol, $path, $tmp) = File::Spec->splitpath($tmp_file);
				$file_exists = 1;
				push @upload_confirm, { temp  => $tmp,
                                         fname => $basename,
                                         text  => '<span style="color: red;">&#0149;</span> <MT_TRANS phrase="A file named <strong>' . $basename . '</strong> already exists.">'};
				next;	#If a file exists, stop here and go to the next file. No point in 
						#creating the "processing" page if we need the file exists page.
			}
		} 
        else {
			&_debug("File does not exist, so will be put there.");
			push @upload_confirm, {fname => $basename,
                                    text  => '&#0149; <MT_TRANS phrase="The file ' . $basename . ' was successfully uploaded.">'};
			# File does not exist, or else we have confirmed that we can overwrite.
			$umask = oct $app->config('UploadUmask');
			$old   = umask($umask);
			defined($bytes = $fmgr->put($uploaded_file, $absolute_file_path, 'upload'))
				or return $app->error($app->translate(
					"Error writing upload to '[_1]': [_2]", $absolute_file_path,
					$fmgr->errstr));
			umask($old);
			close $uploaded_file;
		}
		
	
		# If overwriting the file, that means there's a temp file saved. Delete it, since it's not needed anymore.
		if ($has_overwrite) {
			if ($tmp) {
				if ($overwrite eq '1' || $overwrite eq '2') {
					if ($tmp =~ m!([^/]+)$!) {
						$tmp = $1;
					} 
                    else {
						return $app->error($app->translate(
							"Invalid temp file name '[_1]'", $tmp));
					}
					my $tmp_file = File::Spec->catfile($app->config('TempDir'), $tmp);
					if (-e $tmp_file) {
						unlink($tmp_file)
							or return $app->error($app->translate(
								"Error deleting '[_1]': [_2]", $tmp_file, "$!"));
					}
				}
			}
		}

		# Check if the uploaded file is an image, and if so, record additional image info (width, height).
		eval { use Image::Size; };
		return $app->error($app->translate("Perl module Image::Size is required to determine ".
			"width and height of uploaded images.")) if $@;
		($w, $h, $id) = Image::Size::imgsize($absolute_file_path);
		
		$relative_path =~ s!\\!/!g;
		$relative_path =~ s!^/!!;
		$relative_url =~ s!\\!/!g;
		$relative_url =~ s!^/!!;
		$url = $blog->site_url;
		$url .= '/' unless $url =~ m!/$!;
		$relative_url =~ s!^/!!;
		$url .= $relative_url;
		
		$is_image = defined($w) && defined($h);
	
		if ($is_image) {
			eval { use MT::Image; MT::Image->new or die; };
			$param{do_thumb} = !$@ ? 1 : 0;
			MT->run_callbacks('CMSUploadFile',
							File => $absolute_file_path, Url => $url, Size => $bytes,
							Type => 'image', Blog => $blog);
			MT->run_callbacks('CMSUploadImage',
							File => $absolute_file_path, Url => $url, Size => $bytes,
							Height => $h, Width => $w, Type => 'image',
							ImageType => $id, Blog => $blog);
		} 
        else {
			MT->run_callbacks('CMSUploadFile',
							File => $absolute_file_path, Url => $url, Size => $bytes,
							Type => 'file', Blog => $blog);
		}

		$param{is_image} = $is_image;
		if ($is_image) {
			if ($w > $h) {
				$preview_w = 190;
				$preview_h = int( ( ($h * 190) / $w) + 0.5);
			} else {
				$preview_h = 190;
				$preview_w = int( ( ($w * 190) / $h) + 0.5);
			}
		}
		$first = ($count == 0) ? 1 : 0; #To make the first file on the processing screen "open."
		if ($config->{'wrapper'} == 0) { $wrapper_0 = 1;
		} 
        elsif ($config->{'wrapper'} == 1) { $wrapper_1 = 1;
		} 
        elsif ($config->{'wrapper'} == 2) { $wrapper_2 = 1;
		}
		if ($is_image && $param{do_thumb}) {
			if ($config->{'thumbnail_size_type'} == 1) {
				if ($w >= $h) {
					$thumb_w = $config->{'thumbnail_size_longest'};
					$thumb_h = int( (($h * $config->{'thumbnail_size_longest'}) / $w) + 0.5);
				} 
                else {
					$thumb_h = $config->{'thumbnail_size_longest'};
					$thumb_w = int( (($w * $config->{'thumbnail_size_longest'}) / $h) + 0.5);
				}
			} 
            else {
				if ($config->{'thumbnail_fixed_dimension'} == 0) {
					$thumb_w = $config->{'thumbnail_size_fixed'};
					$thumb_h = int( (($h * $config->{'thumbnail_size_fixed'}) / $w) + 0.5);
				} 
                else {
					$thumb_h = $config->{'thumbnail_size_fixed'};
					$thumb_w = int( (($w * $config->{'thumbnail_size_fixed'}) / $h) + 0.5);
				}
			}
		}
		$bytes = (-s $absolute_file_path);
		push @upload_data, {
                            width          => $w,
    						height         => $h, 
    						bytes          => $bytes,
    						image_type     => $id, 
    						fname          => $relative_path,
    						temp           => $tmp,
    						site_path      => scalar $q->param('site_path'),
    						url            => $url, 
    						is_image       => $is_image,
    						alt            => $config->{'alt'},
    						alt_text       => $basename,
    						preview        => $config->{'preview'},
    						title          => $config->{'title'},
    						editable_image_attributes => $config->{'editable_image_attributes'},
    						image_attributes => $config->{'image_attributes'},
    						thumb_w        => $thumb_w,
    						thumb_h        => $thumb_h,
    						thumbnail_create => $config->{'thumbnail_create'},
    						preview_w      => $preview_w,
    						preview_h      => $preview_h,
    						first          => $first,
    						use_wrapper    => $config->{'use_wrapper'},
    						wrapper_name_a => $config->{'wrapper_name_a'},
    						wrapper_name_b => $config->{'wrapper_name_b'},
    						wrapper_0      => $wrapper_0,
    						wrapper_1      => $wrapper_1,
    						wrapper_2      => $wrapper_2,
                            current_file   => $count,
    						};
	$count++; # To move on to the next file.
	}
	$param{upload_data} = \@upload_data;

	$param{can_post}  = $perms->can_post;
	$param{blog_id}   = $blog_id;
	$param{blog_name} = $blog->name;

	$param{thumbnail_options}         = $config->{'thumbnail_options'};
	$param{thumbnail_size_type}       = $config->{'thumbnail_size_type'};
	$param{thumbnail_size_longest}    = $config->{'thumbnail_size_longest'};
	$param{thumbnail_size_fixed}      = $config->{'thumbnail_size_fixed'};
	$param{thumbnail_fixed_dimension} = $config->{'thumbnail_fixed_dimension'};
	$param{constrain_proportions}     = $config->{'constrain_proportions'};
	
	$param{use_result_stacker} = $q->param('use_result_stacker');
	if ($config->{'new_entry_type_html'} ) { $param{new_entry_type_html} = 1; }
	if ($config->{'new_entry_type_entry'} ) { $param{new_entry_type_entry} = 1; }
	if ($config->{'new_entry_type_extended'} ) { $param{new_entry_type_extended} = 1; }
	if ($config->{'new_entry_type'} == 0 && $config->{'new_entry_type_html'} ) {
		$param{new_entry_type_0} = $config->{'new_entry_type'};
	} 
    elsif ($config->{'new_entry_type'} == 1 && $config->{'new_entry_type_entry'} ) {
		$param{new_entry_type_1} = $config->{'new_entry_type'};
	} 
    elsif ($config->{'new_entry_type'} == 2 && $config->{'new_entry_type_extended'} ) {
		$param{new_entry_type_2} = $config->{'new_entry_type'};
	}
	$param{popup}                   = $config->{'popup'};
	$param{embedded}                = $config->{'embedded'};
	$param{pagebuilder}             = $config->{'pagebuilder'};
	$param{pagebuilder_title}       = $config->{'pagebuilder_title'};
	$param{pagebuilder_description} = $config->{'pagebuilder_description'};
	if ($config->{'gallery'}) {
		$param{gallery} = 1;
		if ($config->{'gallery_type'} == 0) {
			$param{gallery_type_0} = 1;
		} 
        elsif ($config->{'gallery_type'} == 1) {
			$param{gallery_type_1} = 1;
		} 
        elsif ($config->{'gallery_type'} == 2) {
			$param{gallery_type_2} = 1;
		}
	}
	$param{tooltips} = $config->{'tooltips'};

	if ($file_exists) {
		return $app->build_page('upload_confirm.tmpl', {
                                            upload_confirm  => \@upload_confirm,
											blog_name       => $blog->name,
											tooltips        => $config->{'tooltips'},
											extra_path      => $extra_path_save,
											site_path       => scalar $q->param('site_path'),
											middle_path     => $middle_path,
											blog_id         => $blog_id,
											speeddemon      => $q->param('speeddemon'),
											use_result_stacker => $q->param('use_result_stacker'),
											}
                                            );
	}

	if ($q->param('speeddemon')) {
		&_debug('Using the SpeedDemon to upload files.');
		my $speeddemon = 1;
		&show_me_the_html($app, $speeddemon, \@upload_data, \%param);
	} 
    else {
		$app->build_page('upload_complete.tmpl', \%param);
	}
}


# Show the uploaded file's resultant HTML, so it can be copied and pasted.
sub show_me_the_html {
	my ($app, $speeddemon) = @_;
	my %param;
	my @upload_data;
	if ($speeddemon) {
		%param = %{(pop)};
		@upload_data = @{(pop)};
	}
	my $q = $app->param;
	defined(my $text = &_process_upload($app, $speeddemon, \@upload_data, \%param)) or return;
	&_debug("Resultant HTML: $text");
	my $config = &_blog_config($q->param('blog_id'));
	my $session;
	if ($q->param('use_result_stacker')) {
		# Check to see if Result Stacker has anything stored in the last 100 minutes.
		$session = MT::Session::get_unexpired_value(6000, { id => 'BFU Result Stacker',
												kind=> 'rs' } );
		# If anything was retreived, append the newly-uploaded HTML to it.
		if ($session) {
			$text = $session->data . $text;
		}
	}
	# Save the HTML for later use.
	$session = MT::Session->new;
	$session->id('BFU Result Stacker');
	$session->kind('rs');
	$session->data($text);
	$session->start(time());
	$session->save;

	$param{blog_id}          = $q->param('blog_id');
	$param{upload_html}      = $text;
	$param{one_click_select} = $config->{'one_click_select'};
	if ($config->{'append_to_entry'} || $config->{'append_to_extended_entry'}) {
		$param{use_append_button} = 1; }
	$param{append_to_entry} = $config->{'append_to_entry'};
	$param{append_to_extended_entry} = $config->{'append_to_extended_entry'};
	$param{result_stacker} = $config->{'result_stacker'};
	$param{blog_name}      = $q->param('blog_name');
	$param{tooltips}       = $config->{'tooltips'};

	$app->build_page('upload_show.tmpl', \%param);
}

# Place the uploaded file info in a new entry's body field.
sub start_upload_entry {
	my $app = shift;
	my $q   = $app->param;
	$q->param('_type', 'entry');
	defined(my $text = &_process_upload($app)) or return;
	
	# Save the HTML for later use.
	my $session = MT::Session->new;
	$session->id('BFU New Entry Text');
	$session->kind('ne');
	$session->data($text);
	$session->start(time());
	$session->save;
	
	my %param;
	$param{blog_name}     = $q->param('blog_name');
	$param{blog_id}       = $q->param('blog_id');
	$param{new_entry_url} = $app->mt_uri . '?__mode=view&_type=entry&blog_id=' . $q->param('blog_id');
	$param{place}         = 1; # '1' places in the entry body field.
	
	$app->build_page('new_entry.tmpl', \%param);
}

# Place the uploaded file info in a new entry's extended field.
sub start_upload_extended_entry {
	my $app = shift;
	my $q   = $app->param;
	$q->param('_type', 'entry');
	defined(my $text = &_process_upload($app)) or return;

	# Save the HTML for later use.
	my $session = MT::Session->new;
	$session->id('BFU New Entry Text');
	$session->kind('ne');
	$session->data($text);
	$session->start(time());
	$session->save;
	
	my %param;
	$param{blog_name}     = $q->param('blog_name');
	$param{blog_id}       = $q->param('blog_id');
	$param{new_entry_url} = $app->mt_uri . '?__mode=view&_type=entry&blog_id=' . $q->param('blog_id');
	$param{place}         = 0; # '0' places in the extended entry field.
	
	$app->build_page('new_entry.tmpl', \%param);
}

sub start_new_entry {
	my ($app) = @_;
	
	my $session = MT::Session::get_unexpired_value(600, { id => 'BFU New Entry Text',
												kind=> 'ne' } );
	
	$app->redirect( $app->mt_uri . '?__mode=view&_type=entry&blog_id=' . $app->param('blog_id') . '&' . $app->param('place') . '=' . encode_url($session->data) );
}

# This is the rewritten version of the _process_post_upload subroutine.
sub _process_upload {
	my ($app, $speeddemon, @upload_data) = @_;
	my $q       = $app->param;
	my $blog_id = $q->param('blog_id');
	my $config  = &_blog_config($blog_id);
	my (%param, @url, @full_width, @full_height, @bytes, @image_type, 
		@fname, @site_path, @alt_attribute, @thumb, @thumb_height, @thumb_width, 
		@use_wrapper, @wrapper, @image_attributes, @title_attribute, @pagebuilder_title, 
		@pagebuilder_description, @relative_file_path, $image_attributes, $thumb_height,
		$thumb_width, );

	# Set the defaults of how SpeedDemon should work.
	if ($speeddemon) {
		&_debug('Using the SpeedDemon to process uploaded files.');
		%param = %{(pop)};
		@upload_data = @{(pop)};

		$q->param('can_post', $param{can_post});
		$q->param('blog_name', $param{blog_name});
		$q->param('blog_id', $param{blog_id});

		$q->param('thumbnail_size_type', $param{thumbnail_size_type});
		$q->param('thumbnail_size_longest', $param{thumbnail_size_longest});
		$q->param('thumbnail_size_fixed', $param{thumbnail_size_fixed});
		$q->param('thumbnail_fixed_dimension', $param{thumbnail_fixed_dimension});
		$q->param('constrain_proportions', $param{constrain_proportions});

		my $num_of_files = @upload_data;
		my $count = 0;
		while ($count < $num_of_files) {
			$url[$count]           = $upload_data[$count]->{'url'};
			&_debug("SpeedDemon is preparing file $count; url is $url[$count]");
			$full_width[$count]    = $upload_data[$count]->{'width'};
			$full_height[$count]   = $upload_data[$count]->{'height'};
			$image_type[$count]    = $upload_data[$count]->{'image_type'};
			$fname[$count]         = $upload_data[$count]->{'fname'};
			$site_path[$count]     = $upload_data[$count]->{'site_path'};
			$alt_attribute[$count] = $upload_data[$count]->{'alt_text'};
			$thumb[$count]         = $upload_data[$count]->{'thumbnail_create'};
			$image_attributes      = $upload_data[$count]->{'image_attributes'};

            # Calculate the thumbnail size, based on all the various methods.
			if ($param{do_thumb} && $upload_data[$count]->{is_image}) {
				if ($param{thumbnail_options} == 1) {
					$q->param('thumb', $thumb[$count]);

					if ($param{thumbnail_size_type} == 1) {
						if ($full_width[$count] >= $full_height[$count]) {
							$thumb_width[$count] = $param{thumbnail_size_longest};
							$thumb_height[$count] = int( (($full_height[$count] * $param{thumbnail_size_longest}) / $full_width[$count]) + 0.5);
						} 
                        else {
							$thumb_height[$count] = $param{thumbnail_size_longest};
							$thumb_width[$count] = int( (($full_width[$count] * $param{thumbnail_size_longest}) / $full_height[$count]) + 0.5);
						}
					} 
                    else {
						if ($param{thumbnail_fixed_dimension} == 0) {
							$thumb_width[$count] = $param{thumbnail_size_fixed};
							$thumb_height[$count] = int( (($full_height[$count] * $param{thumbnail_size_fixed}) / $full_width[$count]) + 0.5);
						} 
                        else {
							$thumb_height[$count] = $param{thumbnail_size_fixed};
							$thumb_width[$count] = int( (($full_width[$count] * $param{thumbnail_size_fixed}) / $full_height[$count]) + 0.5);
						}
					}
				} 
                else { #don't make a thumbnail.
					$q->param('thumb', 0);
				}
			}
			if (!$config->{'speeddemon_wrapper'}) { $use_wrapper[$count] = 0;
				#Necessary because system-level doesn't recognize config option, for some reason.
			} elsif ($config->{'speeddemon_wrapper'} == 0) { $use_wrapper[$count] = 0;
			} elsif ($config->{'speeddemon_wrapper'} == 1) { $use_wrapper[$count] = 1; $wrapper[$count] = 1;
			} elsif ($config->{'speeddemon_wrapper'} == 2) { $use_wrapper[$count] = 1; $wrapper[$count] = 2; }
			
			$count++;
		}
		$q->param('url', @url);
		$q->param('full_width', @full_width);
		$q->param('full_height', @full_height);
		$q->param('image_type', @image_type);
		$q->param('fname', @fname);
		$q->param('site_path', @site_path);
		$q->param('alt_attribute', @alt_attribute);
		$q->param('title_attribute', '');
		$q->param('thumb', @thumb);
		$q->param('thumb_width', @thumb_width);
		$q->param('thumb_height', @thumb_height);
		$q->param('use_wrapper', @use_wrapper);
		$q->param('wrapper', @wrapper);
		$q->param('image_attributes', @image_attributes);
		
		$q->param('use_result_stacker', $param{use_result_stacker});
		if ($param{new_entry_type_html} ) { $q->param('new_entry_type_html', 1); }
		# if ($param{new_entry_type_entry} ) { $q->param('new_entry_type_entry', 1); }
		# if ($param{new_entry_type_extended} ) { $q->param('new_entry_type_extended', 1); }
		# if ($param{new_entry_type} == 0 && $param{new_entry_type_html} ) {
			$q->param('new_entry_type_0', $param{new_entry_type_0});
		# } elsif ($param{new_entry_type} == 1 && $param{new_entry_type_entry} ) {
			# $q->param('new_entry_type_1', $param{new_entry_type_1});
		# } elsif ($param{new_entry_type} == 2 && $param{new_entry_type_extended} ) {
			# $q->param('new_entry_type_2', $param{new_entry_type_2});
		# }

		if ($param{is_image}) {
			if (!$config->{'speeddemon_publish'}) { $q->param('publish_as', 'popup' );
				#Necessary because system-level doesn't recognize config option, for some reason.
			} elsif ($config->{'speeddemon_publish'} eq 'popup') {
				$q->param('publish_as', 'popup' );
			} elsif ($config->{'speeddemon_publish'} eq 'embedded') {
				$q->param('publish_as', 'embedded');
			} elsif ($config->{'speeddemon_publish'} eq 'pagebuilder') {
				$q->param('publish_as', 'pagebuilder');
			}
		} 
        else {
			$q->param('publish_as', 'link');
		}
	} # done with the SpeedDeomon processing; resume normal.
	@url                     = $q->param('url');
	@full_width              = $q->param('full_width');
	@full_height             = $q->param('full_height');
	@alt_attribute           = $q->param('alt_attribute');
	@title_attribute         = $q->param('title_attribute');
	@pagebuilder_title       = encode_url( $q->param('pagebuilder_title') );
	@pagebuilder_description = encode_url( $q->param('pagebuilder_description') );
	@wrapper                 = $q->param('wrapper');
	@thumb                   = $q->param('thumb');
	@relative_file_path      = $q->param('fname');
	@image_type              = $q->param('image_type');
	@thumb_width             = $q->param('thumb_width');
	@thumb_height            = $q->param('thumb_height');
	@image_attributes        = $q->param('image_attributes');
	my ($url, $width, $height, $alt_attribute, $title_attribute, $pagebuilder_title, 
		$pagebuilder_description, $wrapper, $thumb, $relative_file_path, $fname, 
		$image_type, $w, $h, $wrapper_open, $wrapper_close, $title_copy, $image_url, 
		$attributes, $base_url, $blog, $file, $base_path, $i_file, $fmgr, $data, $img, 
		$blob, $base, $path, $ext, $t_file, $tmpl, $rel_path, $ctx, $popup, $root_path, 
		$abs_file_path, $i, $rel_path_ext, $vol, $dirs, $basename, $rel_url_ext, $caption,
		$lightbox,);
	if (scalar @url > 1) {
		# For Lightbox galleries. This number will be used with multiple file uploads, to tie a gallery together. Individual uploads 
		# aren't part of that same gallery, and additional multiple uploads will be their own gallery (usually, more or less).
		$lightbox = '[gallery' . int(rand(9)) . ']';
	}
	my $link; # For helping to assemble the HTML.
	my $html = q{}; # The resultant HTML goes here. it might be a multiple file upload and this is the first step to letting BFU2 string the HTML together.

	my $num_of_files = @url;
	my $count = 0;
	while ($count < $num_of_files) {
		# Copy the current $count to the variable that gets used throughout this loop.
		# It just makes things easier than changing every variable throughout.
		$url = $url[$count];
		&_debug("Currently processing $url");
		$width                   = $full_width[$count];
		$height                  = $full_height[$count];
		$alt_attribute           = &encode_html( $alt_attribute[$count] );
		$title_attribute         = &encode_html( $title_attribute[$count] );
		$pagebuilder_title       = $pagebuilder_title[$count];
		$pagebuilder_description = $pagebuilder_description[$count];
		$wrapper                 = $wrapper[$count];
		$thumb                   = $thumb[$count];
		$relative_file_path      = $relative_file_path[$count];
		$image_type              = $image_type[$count];
		$w                       = $thumb_width[$count];
		$h                       = $thumb_height[$count];
		$image_attributes        = $image_attributes[$count];
        
        if ($image_attributes eq '') {
			$image_attributes = $config->{'image_attributes'};
		}
		if ($wrapper == 1) {
			$wrapper_open  = $config->{'wrapper_opening_a'};
			$wrapper_close = $config->{'wrapper_closing_a'};
		} 
        elsif ($wrapper == 2) {
			$wrapper_open  = $config->{'wrapper_opening_b'};
			$wrapper_close = $config->{'wrapper_closing_b'};
		} 
        else {
			# No wrapper selected, or not used.
			$wrapper_open  = '';
			$wrapper_close = '';
		}
		($base_url, $fname) = $url =~ m|(.*)/([^/]*)|;
		$url = $base_url . '/' . $fname; # no need to re-encode filename; url is already encoded
		if (!$alt_attribute) { $alt_attribute = $fname; }
		use MT::Blog;
		$blog = MT::Blog->load($blog_id, {cached_ok=>1});
		if ($thumb == 1) {
			&_debug('User requested that a thumbnail be made.');
			use MT::Image;
			$base_path = $blog->site_path;
			$file      = $relative_file_path;
			if ($file =~ m!\.\.|\0|\|!) {
				return $app->error($app->translate("Invalid filename '[_1]'", $file));
			}
			$i_file = File::Spec->catfile($base_path, $file);
			#Untaint. We checked $file for security holes above.
			($i_file) = $i_file =~ /(.+)/s;
			$fmgr = $blog->file_mgr;
			$data = $fmgr->get_data($i_file, 'upload')
				or return $app->error($app->translate(
					"Reading '[_1]' failed: [_2]", $i_file, $fmgr->errstr));
			$image_type = scalar $image_type;
			$img = MT::Image->new( Data => $data,
								Type => $image_type )
				or return $app->error($app->translate("Thumbnail failed: [_1]", MT::Image->errstr));
			(($blob), $thumb_width, $thumb_height) = $img->scale( Width => $w, Height => $h )
				or return $app->error($app->translate("Thumbnail failed: [_1]", $img->errstr));
			use File::Basename;
			($base, $path, $ext) = File::Basename::fileparse($i_file, '\.[^.]*');
			$t_file = $path . $base . '-thumb' . $ext;
			$fmgr->put_data($blob, $t_file, 'upload')
				or return $app->error($app->translate(
					"Error writing to '[_1]': [_2]", $t_file, $fmgr->errstr));
			$file =~ s/\Q$base$ext\E$//;
			$url = $blog->site_url;
			$url .= '/' unless $url =~ m!/$!;
			$url .= $file;
			$thumb = $url . encode_url($base . '-thumb' . $ext);
			$url = $base_url . '/' . $fname;
			MT->run_callbacks('CMSUploadFile',
							File => $t_file, Url => $thumb, Size => length($blob),
							Type => 'thumbnail',
							Blog => $blog);
			MT->run_callbacks('CMSUploadImage',
							File => $t_file, Url => $thumb,
							Width => $thumb_width, Height => $thumb_height,
							ImageType => $image_type,
							Size => length($blob),
							Type => 'thumbnail',
							Blog => $blog);
		}
		$attributes = ($title_attribute || $image_attributes) ? ' ' : '';
		$attributes .= $title_attribute ? "title=\"$title_attribute\" " : '';
		$attributes .= $image_attributes ? "$image_attributes " : '';

		if ($q->param('publish_as') eq 'popup') {
			&_debug('User requested a popup be made.');
			use MT::Template;
			if ($tmpl = MT::Template->load({ blog_id => $blog_id,
										name => 'Uploaded Image Popup Template' })) {
				($rel_path = $relative_file_path) =~ s!\.[^.]*$!!;
				if ($rel_path =~ m!\.\.|\0|\|!) {
					return $app->error($app->translate(
						"Invalid basename '[_1]'", $rel_path));
				}
				$ext = $blog->file_extension || '';
				$ext = '.' . $ext if $ext ne '';
				use MT::Template::Context;
				$ctx = MT::Template::Context->new;
				$ctx->stash('image_url', $url);
				$ctx->stash('image_width', $width);
				$ctx->stash('image_height', $height);
				$popup         = $tmpl->build($ctx);
				$fmgr          = $blog->file_mgr;
				$root_path     = $blog->site_path;
				$abs_file_path = File::Spec->catfile($root_path, $rel_path . $ext);
				#If the popup filename already exists, just increment it so nothing gets overwritten.
				($i, $rel_path_ext) = (0, $rel_path . $ext);
				while ($fmgr->exists($abs_file_path)) {
					$rel_path_ext  = $rel_path . ++$i . $ext;
					$abs_file_path = File::Spec->catfile($root_path, $rel_path_ext);
				}
				($vol, $dirs, $basename) = File::Spec->splitpath($rel_path_ext);
				$rel_url_ext = File::Spec->catpath($vol, $dirs, encode_url($basename));

				($abs_file_path) = $abs_file_path =~ /(.+)/s;
				$fmgr->put_data($popup, $abs_file_path, 'upload')
					or return $app->error($app->translate(
					"Error writing to '[_1]': [_2]", $abs_file_path,
												$fmgr->errstr));
				$url = $blog->site_url;
				$url .= '/' unless $url =~ m!/$!;
				$rel_url_ext =~ s!^/!!;
				$url .= $rel_url_ext;
				MT->run_callbacks('CMSUploadFile',
							File => $abs_file_path, Url => $url,
							Size => length($popup),
							Type => 'popup',
							Blog => $blog);
			}
			$link = $thumb ? qq(<img src="$thumb" width="$thumb_width" height="$thumb_height" alt="$alt_attribute"$attributes/>) : q{<MT_TRANS phrase="View image">};
			$html .= <<HTML;
$wrapper_open<a href="$url" onclick="window.open('$url', 'popup', 'width=$width,height=$height,scrollbars=no,resizable=no,toolbar=no,directories=no,location=no,menubar=no,status=no,left=0,top=0' ); return false">$link</a>$wrapper_close

HTML
		}
		# The Embedded Image option.
        elsif ($q->param('publish_as') eq 'embedded') {
			if ($thumb) {
				$html .= qq($wrapper_open<a href="$url"><img src="$thumb" width="$thumb_width" height="$thumb_height" alt="$alt_attribute"$attributes/></a>$wrapper_close\n\n);
			} 
            else {
				$html .= qq($wrapper_open<img src="$url" width="$width" height="$height" alt="$alt_attribute"$attributes/>$wrapper_close\n\n);
			}
        }
		# The Photogallery option.
		elsif ($q->param('publish_as') eq 'photoblog') {
			if (($config->{'gallery_type'} == 0) || ($config->{'gallery_type'} == 1)) {
				($fname = $url) =~ s!^(.*)\.(jpg|jpeg|gif)!$1!i;
				return $fname;
			} 
            elsif ($config->{'gallery_type'} == 2) {
				$caption = $title_attribute ? " title=\"$title_attribute\"" : '';
				if ($thumb) {
					$html .= qq($wrapper_open<a href="$url" rel="lightbox$lightbox"$caption><img src="$thumb" width="$thumb_width" height="$thumb_height" alt="$alt_attribute"$attributes/></a>$wrapper_close\n\n);
				} 
                else {
					$html .= qq($wrapper_open<a href="$url" rel="lightbox$lightbox"$caption><MT_TRANS phrase="View image">$wrapper_close\n\n);
				}
			}
        }
		# The link option (for uploading files only, not photos).
		elsif ($q->param('publish_as') eq 'link') {
			$html .= qq{$wrapper_open<a href="$url">$title_attribute</a>$wrapper_close\n\n};
        }
		# The Page Builder option.
		elsif ($q->param('publish_as') eq 'pagebuilder') {
			use MT::Template;
			# First, create the template if necessary.
			if (MT::Plugin::BetterFileUploader->instance->get_config_value('pagebuilder_installed',"blog:$blog_id") != 1) {
				$tmpl = MT::Template->new;
				$tmpl->blog_id($blog_id);
				$tmpl->type('index');
				$tmpl->rebuild_me(1);
				$tmpl->name('BFU: Page Builder');
				$tmpl->outfile( $config->{'pagebuilder_filename'} );
				my $text = q{
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" id="sixapart-standard">
<head>
   <meta http-equiv="Content-Type" content="text/html; charset=<$MTPublishCharset$>" />
   <meta name="generator" content="<$MTProductName version="1"$>" />

   <link rel="stylesheet" href="<$MTBlogURL$>styles-site.css" type="text/css" />
   <link rel="alternate" type="application/atom+xml" title="Atom" href="<$MTBlogURL$>atom.xml" />
   <link rel="alternate" type="application/rss+xml" title="RSS 2.0" href="<$MTBlogURL$>index.xml" />

   <title><?php echo $_GET['pb_title']; ?> (<$MTBlogName encode_html="1"$>)</title>

   <link rel="start" href="<$MTBlogURL$>" title="Home" />

   <MTBlogIfCCLicense>
   <$MTCCLicenseRDF$>
   </MTBlogIfCCLicense>
   <script type="text/javascript" src="<$MTBlogURL$>mt-site.js"></script>
</head>
<body class="layout-one-column individual-entry-archive" onload="individualArchivesOnLoad(commenter_name)">
   <div id="container">
      <div id="container-inner" class="pkg">

         <div id="banner">
            <div id="banner-inner" class="pkg">
               <h1 id="banner-header"><a href="<$MTBlogURL$>" accesskey="1"><$MTBlogName encode_html="1"$></a></h1>
               <h2 id="banner-description"><$MTBlogDescription$></h2>
            </div>
         </div>

         <div id="pagebody">
            <div id="pagebody-inner" class="pkg">
               <div id="alpha">
                  <div id="alpha-inner" class="pkg">



                     <?php echo '<h2 id="archive-title">' . $_GET['pb_title'] . '</h2>'; ?>
                     <div class="entry">
                        <div class="entry-content">
						<div>
							<img src="<?php echo $_GET['image_url']; ?>" width="<?php echo $_GET['image_width']; ?>" height="<?php echo $_GET['image_height']; ?>" title="<?php echo $_GET['title_attribute']; ?>" alt="<?php echo $_GET['alt_attribute']; ?>" style="border: 1px solid black;" />
						</div>
						
						<?php echo '<p>' . $_GET['pb_description'] . '</p>'; ?>
                        </div>
                     </div>

                  </div>
               </div>

            </div>
         </div>
      </div>
   </div>
</body>
</html>

};
				$tmpl->text($text);
				$tmpl->save;
				# Build the template so it works right away.
				use MT::WeblogPublisher;
				my $pub = MT::WeblogPublisher->new;
				$pub->rebuild_indexes( {	BlogID => $blog_id,
									Template => 'BFU: Page Builder',
									Force => 1 } );
				# Mark this blog as having the template installed, so we don't try again.
				MT::Plugin::BetterFileUploader->instance->set_config_value('pagebuilder_installed',1,"blog:$blog_id");
				MT::log("Better File Uploader successfully added the PageBuilder for Uploaded Images template to Blog $blog_id.");
			}
			if ($tmpl = MT::Template->load({ blog_id => $blog_id,
										name => 'BFU: Page Builder' })) {
				($rel_path = $fname) =~ s!\.[^.]*$!!;
				if ($rel_path =~ m!\.\.|\0|\|!) {
					return $app->error($app->translate("Invalid basename '[_1]'", $rel_path));
				}
				if ($title_attribute) { $title_attribute = "&title_attribute=$title_attribute"; }
				if ($pagebuilder_title) { $pagebuilder_title = "&pb_title=$pagebuilder_title"; }
				if ($pagebuilder_description) { $pagebuilder_description = "&pb_description=$pagebuilder_description"; }

				$image_url = encode_url($url);
				$url = $blog->site_url . $tmpl->outfile;
				$url .= "?image_url=$image_url&image_width=$width&image_height=$height&alt_attribute=$alt_attribute";
				$url .= "$title_attribute$pagebuilder_title$pagebuilder_description";
			}
			if ($title_attribute) { $title_attribute = " title=\"$title_attribute\" "; }
			$link = $thumb ? qq(<img src="$thumb" width="$thumb_width" height="$thumb_height" alt="$alt_attribute"$attributes/>) : q{<MT_TRANS phrase="View image">};
			$html .= qq{$wrapper_open<a href="$url" $title_copy>$link</a>$wrapper_close\n\n};
		}
	$count++;
	} #close the while loop, ending multi-file processing.
	return $app->translate_templatized($html); # Return the HTML. 
}

sub speeddemon_prefs {
	my $app     = shift;
	my $q       = $app->param;
	my $blog_id = $q->param('blog_id');
	my $plugin  = MT::Plugin::BetterFileUploader->instance;

	if ($plugin->get_config_value('system_override')) {
		$plugin->set_config_value('speeddemon_wrapper',$q->param('speeddemon_wrapper'));
		$plugin->set_config_value('speeddemon_publish',$q->param('speeddemon_publish'));
	} else {
		$plugin->set_config_value('speeddemon_wrapper',$q->param('speeddemon_wrapper'), "blog:$blog_id");
		$plugin->set_config_value('speeddemon_publish',$q->param('speeddemon_publish'), "blog:$blog_id");
	}
	&start_bfu($app);
}

1;

