#!/usr/bin/perl
use lib '/home/martin/ethereal/mgmt';

################################################################################
# Created       : Martin Foster
# Modified      : 27-Oct-2005
################################################################################
#
# Gallery View - Script part of Ethereal Realms, designed to display and list
#                galleries and associated works
# Copyright (C) 2000-2006  Martin Foster
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
#
# Author of this script can be contacted at the following:
#       E-Mail  : martin@ethereal-realms.org
#       Address : 29-26503 TWP RD 511
#                 Spruce Grove, Alberta
#                 T7Y 1G4
#
#################################################################################

use CGI;							# Common gateway interface
use CGI::Carp qw(fatalsToBrowser);				# CGI Error logs
use Number::Format qw(:subs);					# Number formatting library
use strict;							# Strict variable enforcement

use Ethereal::Database;						# Database handler
use Ethereal::Filter;						# Filter handling
use Ethereal::Login;						# Login functionality
use Ethereal::Mail;						# Mail handler
use Ethereal::Option;						# Option handler
use Ethereal::Param;						# Parameter control
use Ethereal::Template;						# Template handler
use Ethereal::Words;						# Word processing and stemming

#################################################################################
# Gobal override
#################################################################################
$CGI::POST_MAX=1024 * 50;					# Maximum posts
$CGI::DISABLE_UPLOADS = 1;					# Disable uploads

################################################################################
# Data Members
################################################################################
my $cgi;							# Common gateway interface handle
my $database;							# Database handle
my $login;							# Login handle
my $param;							# Parameter hash
my $tmpl;							# Template handle

my $ready   = 'False';						# Ready for processing
my $sname;							# Simply the script name
my $sparam;							# Scripted parameter string

my @pass;							# Passage of variables

my %gal;							# Gallery hash
my %sparam;							# Scripted parameters

################################################################################
# Program Area
################################################################################

	# Initial handles
	$cgi      = new CGI;
	$database = new Ethereal::Database();


	# Connect and fetch
	$database->Connect($cgi);

	# Parameter handling
	$param    = new Ethereal::Param($database, $cgi);
	$param->GetParam();

	# Link with hash
	$database->GetHashGallery(\%gal);

	# Initial hash
	$tmpl  = new Ethereal::Template(\%gal);


	# Authenthication instance
	$login = new Ethereal::Login($database, $cgi, $param, \%gal);

	# Enable custom header and footer
	$login->{'HEADER'} = $database->DocumentGetGalleryHeader();
	$login->{'FOOTER'} = $database->DocumentGetGalleryFooter();


	# Pull script name
	$sname = $cgi->url(-full=>1);
	$sparam = $cgi->url(-path_info=>1);


	# Determine need
	# Escape
	$sname = quotemeta($sname);

	# Needed addition
	$sparam = ($sparam =~ /\/$/) ? $sparam : "$sparam/";

	# Truncate
	$sparam =~ s/^$sname\///;


	# Retreive parameters
	# Differs based on what is pulled
	if ($sparam =~ /^(image|text)/i)
	{
		# This requires a tree-like depth in order to move freely
		# between galleries and genres.
		($sparam{'TYP'},
		 $sparam{'SUB'},
		 $sparam{'GRE'},
		 $sparam{'GAL'},
		 $sparam{'LYR'},
		 $sparam{'WRK'},
		 $sparam{'POS'}) = split(/\//, $cgi->unescape($sparam));
	}

	else
	{
		# This is more direct, works have no need for that depth
		# as they are viewed indepepdantly.
		($sparam{'ACT'},
		 $sparam{'GAL'},
		 $sparam{'LYR'},
		 $sparam{'WRK'},
	 	 $sparam{'ADD'}) = split(/\//, $cgi->unescape($sparam));
	}


	# Possible authentication methods
 	# Determine state of readiness
	if (defined($param->{'USER'}))
	{
		# Authenticate
		# Assign if true
		$ready = 'True'
		  if ($login->GetVerificationNormal());
	}

	else
	{
		# Require a header
		print $cgi->header();

		# Change state
		$ready = 'True';
	}

 	# Bypass if necessary
	if ($ready eq 'True')
	{
		# Disable independance and header generation
		$login->{'AUTOMATE'} = 0;
		$login->{'NODEPEND'} = 0;

		# Page header
		HeaderRewrite($database, $cgi, $tmpl, \%sparam, \%gal);

		# Generate cleaner parameter list
		push(@pass, $database, $login, $cgi, $param, $tmpl, \%sparam, \%gal);


		# Gallery's root definition
		# Root defined
		if ((defined($sparam{'GRE'}))
		&& (length($sparam{'GRE'}) > 3))
	 	{
			# Work details
			# Shows works allowing linear movement
			if ((defined($sparam{'GAL'}))
			 && (defined($sparam{'WRK'}))
			 && ($sparam{'GAL'} =~ /^\d+$/)
			 && ($sparam{'WRK'} =~ /^\d+$/))
		 	{
				# Work specific display
				WorkDetail(@pass);
			}

			# Work listing
			# Lists all works within gallery
			elsif ((defined($sparam{'GAL'}))
			 && ($sparam{'GAL'} =~ /^\d+$/))
			{
				# Work list
				WorkList(@pass);
			}

			# Final step of the puzzle
			else
			{
				# List galleries
				GalleryList(@pass);
			}
		}

		# Searching capability
		# Work search
		elsif ((defined($sparam{'ACT'}))
		  && ($sparam{'ACT'} eq 'search'))
		{
			# Simply one window
			WorkSearch(@pass);
		}

		# Commenting ability
		# Comment viewer
		elsif ((defined($sparam{'ACT'}))
		  && ($sparam{'ACT'} eq 'comment'))
		{
			# Basic check
			if ((defined($sparam{'GAL'}))
			 && (defined($sparam{'WRK'})))
			{	
				# Verify for operation
				# Removal of comment
				if ((defined($sparam{'ADD'}))
				 && ($sparam{'ADD'} =~ /^\d+$/))
				{
					# Remove comment
					CommentRemove(@pass);
				}
			
				# Creation of a new comment
				elsif ((defined($sparam{'ADD'}))
				 && ($sparam{'ADD'} eq 'new'))
				{
					# Remove comment
					# Create comments
					CommentWrite(@pass);
				}

				# Simply display
				else
				{
					# Display comments
					# Directly display to screen
					print CommentDisplay(@pass);	
				}
			}
		}

		# Default behaviour
		# Show member which genres are available
		else
		{
			# Select the genre TYP and SUB-type
			GenreList(@pass);
		}
	
		# Document footer
		print $database->DocumentGetGalleryFooter(), "\n";
	}


################################################################################
# Sub-Routines
################################################################################

#####################
# Check Authorization
#
# In certain causes, realms will choose to carry an adult rating.  In such a
# case, there is a need to prevent people from using the system when such
# a circumstance takes place.

sub CheckAuth
{
	#####################
	# Data members

	my $database = shift;					# Database handler
	my $login    = shift;					# Authenthication handler
	my $param    = shift;					# Paramater handler
	my $sparam   = shift;					# Parameter line hash
	my $tmpl     = shift;					# Template handle
	my $gal      = shift;					# Gallery hash
	my $choice   = shift;					# Choice of the matter

	my $rating;						# Rating to realm
	my $verify;						# Verified access


	#####################
	# Program area

	# Retreive information
	($rating) = $database->DataGet("SELECT GalleryRating
		FROM Gallery
		WHERE GalleryID=?",

	 $sparam->{'GAL'}
	);


	# Reasons to check
	if ((defined($param->{'USER'}))
	 || ($rating eq $gal->{'SetRestricted'})
	 || (defined($choice)))
	{
		# Authenthicate
		if ($login->GetVerificationNormal())
		{
			# More checks
			if ($rating eq $gal->{'SetRestricted'})
			{
				# Retreive verification
				($verify) = $database->DataGetVerified($param->{'USER'});

				# Invalid
				if ($verify eq 'no')
				{
					# Display error
					$tmpl->Show('TmplMsg',
						MMESSAGE => $gal->{'MsgRestricted'}
					 );

					# False
					return 0;
				}
			}

			# Return true
			return 1;
		}


		# Return false
		return 0;
	} 

	# Always return true
	return 1;
}


#####################
# Comment Display
#
# If one has the capability of composing messages, then one will also need to
# be able to view them.   This is a general, all purpose method that allows
# for attached and semi-independant use.

sub CommentDisplay
{
	#####################
	# Data members
	my $database = shift;					# Database handler
	my $login    = shift;					# Authenthication handler
	my $cgi      = shift;					# CGI interface
	my $param    = shift;					# Paramater handler
	my $tmpl     = shift;					# Template handler
	my $sparam   = shift;					# Parameter line hash
	my $gal      = shift;					# Gallery hash
	my $limit    = shift;					# Retreival limit

	my $res;						# Results
	my $rsth;						# Query handle

	my $comment;						# Comment settings
	my $name;						# Name of work
	my $access;						# Username

	my $gummy;						# Gummy name
	my $rate;						# Rating
	my $vote;						# Votes

	my $ibio;						# Biographical line
	my $idelete;						# Delete link
	my $inline;						# Inline parameters
	my $url;						# Self referencing link

	my $listing;						# Comments listing
	my $nav;						# Navigational business
	my $return;						# Returned template
	my $votes;						# Votes generated


	#####################
	# Program Area

	# Initialize
	$return = '';

	# Make sure values are sane
	if (($sparam->{'WRK'} =~ /^\d+$/)
	 && ($sparam->{'GAL'} =~ /^\d+$/))
	{
		# Things are in order
		($name) = $database->DataGetWorkName($sparam->{'WRK'}, $sparam->{'GAL'});

		# Continue as planned
		if (defined($name))
		{
			# Force authenthication reguardless
			return 0 unless (CheckAuth($database, $login, $param, $sparam, $tmpl, $gal));

			# Retreive values
			($comment) = $database->DataGetGalleryComment($sparam->{'GAL'});
			($access)  = $database->DataGetGalleryJanitor($sparam->{'GAL'}, $param->{'USER'})
			  if (defined($param->{'USER'}));


			# Initialize elements
			$nav     = '';
			$listing = '';
			$votes   = '';
			
			# Only display if needed
			# Comments
			if (($comment eq 'comment')
			 || ($comment eq 'combo'))
			{
				# Determine limit
				$limit = (defined($limit)) ? "LIMIT $limit" : "";


				# Prepare query
				# Set handle if need be
				$database->Pull(\$rsth, "SELECT
					 c.CommentID                 AS \"CommentID\",
					 c.PuppetName                AS \"PuppetName\",
					 p.PuppetGummy               AS \"PuppetGummy\",
					 c.PuppeteerLogin            AS \"PuppeteerLogin\",
					 c.CommentPost               AS \"CommentPost\",
					 getDate(c.CommentTimestamp) AS \"CommentTimestamp\"
					 FROM Comment c
					INNER JOIN Puppet p
					   ON (c.PuppetName=p.PuppetName AND PuppetPool='system')
					WHERE WorkID=?
					 AND GalleryID=?
					  AND CommentPost IS NOT NULL
					ORDER BY CommentTimestamp DESC $limit",

				 $sparam->{'WRK'},
				 $sparam->{'GAL'}
				);


				# Self referencing links
				$url = $cgi->url(-absolute=>1) . '/comment/'
			       	     . $sparam->{'GAL'} . '/'
				     . $sparam->{'LYR'} . '/'
				     . $sparam->{'WRK'};

				# Inline parameters general
				$inline = (defined($param->{'USER'}))
				  ? $param->EmbedInline(USER=>$param->{'USER'}, CRYPT=>$param->{'CRYPT'})
				  : '';


				# Necessary fetching
				# Initial acid test
				if ($res = $rsth->fetchrow_hashref())
				{
					# Initialize
					$idelete = '';
					
					# Loop and retrieve
					do
					{
						# Biographical link
						$ibio = (defined($param->{'USER'}))
						 ? $param->EmbedInline(
							 USER  => $param->{'USER'},
							 CRYPT => $param->{'CRYPT'},
							 BCHAR => $res->{'PuppetName'},
							 BPOOL => 'system')
						 : $param->Crypt(
							 BCHAR => $res->{'PuppetName'},
							 BPOOL => 'system');


						# Deletion handle
						$idelete = $tmpl->Pass('TagDelete',
						  MLINK => $url . '/' . $res->{'CommentID'} . $inline
					          ) if ((defined($param->{'USER'}))
						     && (defined($access)));


						# Check gummy
						$gummy = (defined($res->{'PuppetGummy'}))
						  ? $res->{'PuppetGummy'}
						  : $gal->{'TagGummyDef'};

	
						# Subsequent rows
						$listing .= $tmpl->Pass('TmplCommentList',
							MCOMMENT => $res->{'CommentPost'},
							MDATE    => $res->{'CommentTimestamp'},
							MDELE    => $idelete,
							MPUPPET  => $res->{'PuppetName'},
							MGUMMY   => $gummy,
							MLINK    => $gal->{'LnkBio'} . $ibio
						 );

					} while ($res = $rsth->fetchrow_hashref());
				}
				 
				# Finish
				$rsth->finish();


				# Navigational propreties
				# Complete listing
				$nav .= $tmpl->Pass('TagNavTarget',
					MNAME   => $gal->{'TagNavComplete'},
					MLINK   => $url. $inline,
					MTARGET => 'COMMENT_' . $sparam->{'GAL'} . 'x' . $sparam->{'WRK'}
				 ) if ($limit =~ /^LIMIT/);

				# Write to comment
				$nav .= $tmpl->Pass('TagNavTarget',
					MNAME   => $gal->{'TagNavComment'},
					MLINK   => $url . '/new' .  $inline,
					MTARGET => 'COMMENT_' . $sparam->{'GAL'} . 'x' . $sparam->{'WRK'}
				 ) if ($sparam->{'WRK'} != 1);


			}


			# Rating
			if (($comment eq 'rating')
			 || ($comment eq 'combo'))
			{
				# Retrieve values
				($rate, $vote) = $database->DataGetWorkAvg($sparam->{'GAL'}, $sparam->{'WRK'});
	
				# Display
				$votes .= $tmpl->Pass('TmplVotes',
					MITENAME => $name,
					MOLVOTES => $vote,
					MRATINGS => $rate
				 );
			}

			# Generate  final template
			$return = $tmpl->Pass('TmplComment',
				MNAME    => $name,
				MCOMMENT => $listing,
				MNAV     => $nav,
				MVOTES   => $votes
			 );
		}
	}

	# Return comments
	return $return;
}


#####################
# Comment Remove
#
# In order to cope with double posts, annoying messages or things that the artists
# simply cannot agree upon, the comments can be managed by owner.

sub CommentRemove
{
	#####################
	# Data members
	my $database = shift;					# Database handler
	my $login    = shift;					# Authenthication handler
	my $cgi      = shift;					# CGI interface
	my $param    = shift;					# Paramater handler
	my $tmpl     = shift;					# Template handler
	my $sparam   = shift;					# Parameter line hash
	my $gal      = shift;					# Gallery hash

	my $comment;						# Comment settings
	my $access;						# Access privs


	#####################
	# Program Area

	# Force authenthication reguardless
	return 0 unless (CheckAuth($database, $login, $param, $sparam, $tmpl, $gal, 'true'));

	# Retreive values
	($comment) = $database->DataGetGalleryComment($sparam->{'GAL'});
	($access)  = $database->DataGetGalleryJanitor($sparam->{'GAL'}, $param->{'USER'})
	  if (defined($param->{'USER'}));

	# Only display if needed
	if (($comment eq 'comment')
	 || ($comment eq 'combo'))
	{
		# User matches
		if (defined($access))
		{
			# Remove entry
			$database->Quick("DELETE FROM Comment
				WHERE CommentID=?
				  AND WorkID=?
				  AND GalleryID=?",

			  $sparam->{'ADD'},
			  $sparam->{'WRK'},
			  $sparam->{'GAL'}
			);
		}
	}

	# Standard entrance
	print CommentDisplay($database, $login, $cgi, $param, $tmpl, $sparam, $gal);
}


#####################
# Comment Write
#
# Part of the system, as to allow for rating and commenting of works themselves. 

sub CommentWrite
{
	#####################
	# Data members
	my $database = shift;					# Database handler
	my $login    = shift;					# Authenthication handler
	my $cgi      = shift;					# CGI interface
	my $param    = shift;					# Paramater handler
	my $tmpl     = shift;					# Template handler
	my $sparam   = shift;					# Parameter line hash
	my $gal      = shift;					# Gallery hash
	
	my $filter;						# Filter handler
	my $send;						# Mail sender
	my $table;						# Table handler

	my $comment;						# Comment settings
	my $name;						# Name of work
	my $puppet;						# Puppet default

	my $mail;						# Mail comment
	my $post;						# Post
	my $rate;						# Rating

	my $wcomment;						# Commenting field
	my $wpuppet;						# Puppet popup
	my $wrating;						# Puppet popup
	my $wsubmit;						# Post submit

	my @puppet;						# List of puppets
	my @rating   =  qw(1 2 3 4 5 6 7 8 9 10);		# Rating scale

	my %system;						# System hash


	#####################
	# Program Area

	# Things are in order
	($name) = $database->DataGetWorkName($sparam->{'WRK'}, $sparam->{'GAL'});

	# Continue as planned
	if (defined($name))
	{
		# Force authenthication reguardless
		return 0 unless (CheckAuth($database, $login, $param, $sparam, $tmpl, $gal, 'true'));

		# Retreive gallery comment
		($comment) = $database->DataGetGalleryComment($sparam->{'GAL'});


		# Proceed with comment generation
		if ($comment ne 'none')
		{
			# Write post
			if (defined($param->{'POST'}))
			{
				# Evaluate change
				eval
				{
					# Post handling
					if (($comment eq 'comment')
					 || ($comment eq 'combo'))
					{
						# Create instance
						$filter = new Ethereal::Filter();

						# Safety
						$post = $filter->StripHTML($param->{'CPOST'});
						$mail = $post;

						# Verify
						if (length($post) > 3)
						{
							# Post handling
							$post =~ s/$/\n/;

							# Truncate Length
							$post =  substr(DBI::neat($post, 1026), 1, -1);


							# Hyperlinks
							$post =~ s/(http:\/\/.*?)(\s|$)/<A HREF="$1" TARGET="_blank">$gal->{TagHyperlink}<\/A> /gm;
							$post =~ s/([\w\.\-]*?\@[\w\.\-]*?\..*?)(\s|$)/<A HREF="mailto:$1">$gal->{TagEmail}<\/A> /gm;


							# Human readability
							$post =~ s/\n/<BR>/gs;
						}
						else
						{
							# Undefine as to not see
							$post = undef;
						}
					}

					# Basic ranting handling
					$rate = ((defined($param->{'CRATING'})) && ($param->{'CRATING'} =~ /^\d+$/))
					 ? $param->{'CRATING'}
					 : 0;
				

					# Insert row
					$database->Write("INSERT INTO Comment
						(WorkID,
						 GalleryID,
						 PuppetName,
						 PuppeteerLogin,
						 CommentPost,
						 CommentRating)
						VALUES(?,?,?,?,?,?)",

					 $sparam->{'WRK'},
					 $sparam->{'GAL'},
					 $param->{'CPUPPET'},
					 $param->{'USER'},
					 $post,
					 $rate);
				 
				
					# Rating update
					if (($comment eq 'rate')
					 || ($comment eq 'combo'))
					{
						# Calculate average
						($rate) = $database->DataGetWorkAvg($sparam->{'GAL'}, $sparam->{'WRK'});


						# Update
						$database->Write("UPDATE Work
							SET WorkRating=?
							WHERE WorkID=?
							  AND GalleryID=?",

						 $rate,
						 $sparam->{'WRK'},
						 $sparam->{'GAL'});
					}

						
					# Commit
					$database->Commit();
				};


				# If block failed
				if ($@)
				{
					# Issue warning
					warn("Transaction aborted: $@");

					# Undo incomplete changes
					$database->Rollback();
				}

				else
				{
					# Mail notification to user
					if ((defined($mail))
				  	 && (length($mail) > 3))
					{
						# Postage handling
						# Link with hash
						$database->GetHashSystem(\%system);

						# Retreive notice message and contact
						my ($notice) = $database->DocumentGetGalleryNotice();
						my ($user)   = $database->DataGetGalleryOwner($sparam->{'GAL'});

						# Full contact
						my ($email)  = $database->DataGetPuppeteerEmail($user);
						my ($fname)  = $database->DataGetPuppeteerName($user);

						# Create instance
						$send = tie(*MAIL, 'Ethereal::Mail', $database);

						# Initial setup
						$send->SetSubject($system{'SetGalleryPrefix'} . $name);

						# Search and replace
						$send->SetSearch(
							MDATE => $database->DataGetDate(),
							MFROM => $param->{'CPUPPET'},
							MMESG => $mail,
							MMAIL => "$system{SetInfoContactName}\n$system{SetInfoContactAddress}",
							MWORK => $name
							 );

						# Recipients
						$send->AddTo("$fname <$email>");

						# Add message
						print MAIL $notice;

						# Close and send
						close(MAIL);
					}

					# Normal works
					# Simply display here and now
					print CommentDisplay($database, $login, $cgi, $param, $tmpl, $sparam, $gal);
				}
			}


			# Display form
			# Necessary for people to post comments
			else
			{
				# Puppet names
				$database->GetListPuppetYours(\@puppet, $param->{'USER'});

				# Puppet default
				($puppet) = $database->DataGetDefault($param->{'USER'});


				# Append abstaining
				unshift(@rating, $gal->{'TxtAbstain'});


				# Generate widget
				$wpuppet = $cgi->popup_menu('CPUPPET', \@puppet, $puppet);
				$wrating = $cgi->popup_menu('CRATING', \@rating, $rating[0]);

				# Submit button
				$wsubmit = ($comment eq 'rating')
				  ? $gal->{'TxtRate'}
				  : $gal->{'TxtPost'};
	

				# Display information
				# Form
				print $cgi->start_form();
				print $param->EmbedNormal($param->Flat(), POST=>'True');


				# Rating indicator
				$wrating  = (($comment eq 'rating') || ($comment eq 'combo'))
				  ? $tmpl->Pass('TmplWriterRate', WRATING=>$wrating)
				  : '';
				
				# Comments
				$wcomment = (($comment eq 'comment') || ($comment eq 'combo'))
				  ? $tmpl->Pass('TmplWriterComment',
					  WCOMMENT => $cgi->textarea(
						-name    => 'CPOST',
						-rows    => 3,
						-columns => 75,
						-wrap    => 'SOFT'
					)
				    )
				  : '';
				

				# Display completed template
				$tmpl->Show('TmplWriter',
					MRATE    => $wrating,
					MCOMMENT => $wcomment,
					WPUPPET  => $wpuppet,
					WSUBMIT  => $cgi->submit($wsubmit)
				 );

				# Form
				print $cgi->end_form(), "\n";
			}
		}

		# Warn author has disabled comments
		else
		{
			# Warn users comments are disabled
			$tmpl->Show('TmplMsg', MMESSAGE=>$gal->{'MsgComment'});	
		}
	}

	# Invalid entry	
	else
	{
		# Warn that things are off
		$tmpl->Show('TmplMsg', MMESSAGE=>$gal{'MsgParam'});
	}
}


#####################
# Gallery List
#
# Provides the front end to the gallery system itself.   Will allow the user
# to select from a list of galleries and move on from there.

sub GalleryList
{
	#####################
	# Data members
	my $database = shift;					# Database handler
	my $login    = shift;					# Authenthication handler
	my $cgi      = shift;					# CGI interface
	my $param    = shift;					# Paramater handler
	my $tmpl     = shift;					# Template handler
	my $sparam   = shift;					# Parameter line hash
	my $gal      = shift;					# Gallery hash
	
	my $statement;						# Query statement
	my $res;						# Query results

	my $descr;						# Description field
	my $highlight;						# Highlighted image
	my $list;						# List of galleries
	my $newness;						# Newness factor
	my $page;						# Nav page

	my $url;						# Self-referencing url
	my $link;						# Generated link
	my $inline;						# Standard inline

	my $genre;						# Gallery genre
	my $pos;						# Positional handling
	my $sub;						# Gallery sub type
	my $type;						# Gallery type
	my $sort;						# Sort order
	my $osort;						# Original sort order

	my $offset;						# Starting position
	my $total;						# Amount of entries


	#####################
	# Program area

	# Capture self-referencing link
	$url = $cgi->url(-absolute=>1);
	
	
	# Sorting method
	# Determine sort
	$sort  = (defined($sparam->{'ACT'}))
	  ? $sparam->{'ACT'}
	  : 'name';
	$osort = $sort;

	# Translate to query
	if    ($sort eq 'date')   { $sort = 'GalleryUpdated DESC'; }
	elsif ($sort eq 'rating') { $sort = 'GalleryRating ASC, GalleryName ASC'; }
	else  { $sort = '"GalleryNewness" DESC, GalleryName ASC' }


	# Quickly grab total entries
	($total) = $database->DataGet("SELECT COUNT(*)
		FROM Exhibition 
		WHERE GalleryType=?
		  AND GallerySub=?
		  AND GalleryGenre=?", 

	 $sparam->{'TYP'},
	 $sparam->{'SUB'},
	 $sparam->{'GRE'}
	);


	# Position based handling
	# Adjusting
	$pos = $sparam->{'GAL'};

	# Determine position
	$pos = ((defined($pos)) && ($pos =~ /^\d+$/))
	  ? $pos
	  : 1;

	# Determine offset
	$offset = ($pos - 1) * $gal->{'SetRetrieve'};


	# Information display
	# Main display
	$database->Pull(\$statement, "SELECT
		  GalleryID               AS \"GalleryID\",
		  PuppeteerLogin          AS \"PuppeteerLogin\",
		  GalleryName             AS \"GalleryName\",
		  GalleryRating           AS \"GalleryRating\",
		  GalleryDescription      AS \"GalleryDescription\",
		  GalleryThumbnail        AS \"GalleryThumbnail\",
		  getDate(GalleryCreated) AS \"GalleryCreated\",
		  getDate(GalleryUpdated) AS \"GalleryUpdated\",
		  getNew(GalleryUpdated)  AS \"GalleryNewness\",
		  WorkCount               AS \"WorkCount\"
		FROM Exhibition 
		WHERE GalleryType=?
		  AND GallerySub=?
		  AND GalleryGenre=?
		ORDER BY $sort LIMIT $gal->{SetRetrieve} OFFSET $offset",

	 $sparam->{'TYP'},
	 $sparam->{'SUB'},
	 $sparam->{'GRE'}
	);

	# Quick verification
	if ($res = $statement->fetchrow_hashref())
	{
		# Navigational menu
		$type  = $cgi->escape($sparam->{'TYP'});
		$sub   = $cgi->escape($sparam->{'SUB'});
		$genre = $cgi->escape($sparam->{'GRE'}); 

		# Self referencing link
		$link   = $url . '/' . $type  . '/'
	        	. $sub . '/' . $genre . '/' 
			. $osort;

		# Inline parameter enbed
		$inline = (defined($param->{'USER'}))
		 ? $param->EmbedInline($param->Flat())
		 : '';

		# Capture nav line
		$page = Navigation($tmpl, $gal, $gal->{'SetRetrieve'}, $total, $pos, $link, $inline);

		# Initialize
		$list = '';
		
		# Continue 
		do
		{
			# Determine highlighting
			$highlight = (($sparam->{'TYP'} =~ /image/i) && (defined($res->{'GalleryThumbnail'})))
			  ? $tmpl->Pass('TmplGalleryHighlight', LTHUMB => $res->{'GalleryThumbnail'})
			  : '';

			# Newness calculation
			$newness = ($res->{'GalleryNewness'} != 0) 
			 ? $gal->{'TagNewness'}
			 : '';

			# Link creation
			$link   = $url . '/' . $type  . '/'
	       		        . $sub . '/' . $genre . '/' 
			        . $res->{'GalleryID'};

			# Description
			$descr = (defined($res->{'GalleryDescription'}))
			  ? $res->{'GalleryDescription'}
			  : $gal->{'TxtDescription'};
				
			# Display information
			$list .= $tmpl->Pass('TmplGalleryList',
				MAUTHOR  => PenNames($database, $param, $tmpl, $gal, $res->{'GalleryID'}),
				MCREATED => $res->{'GalleryCreated'},
				MDESCR   => $descr,
				MHLIGHT  => $highlight,
				MLINK    => $link . $inline,
				MNAME    => $res->{'GalleryName'},
				MCOUNT   => $res->{'WorkCount'},
				MRATING  => $res->{'GalleryRating'},
				MUPDATED => $res->{'GalleryUpdated'},
				MNEWNESS => $newness
			 );

		} while ($res = $statement->fetchrow_hashref());

		# Self referencing link
		$link   = $url . '/' . $type  . '/'
	        	. $sub . '/' . $genre;
		

		  
		# Display template
		$tmpl->Show('TmplGallery',
			LSTGAL  => $list,
			LSTNAV  => $page,
			LSTSORT => $sort,
			MSUB    => $sparam->{'SUB'},
			MGENRE  => $sparam->{'GRE'},
			LTYPE   => $url  . $inline,
			LSUB    => $url  . '/' . $type . '/' . $sub . $inline,
			LGENRE  => $url  . '/' . $type . '/' . $sub . '/' . $genre . $inline,
			LSDATE  => $link . '/date'   . $inline,
			LSRATE  => $link . '/rate'   . $inline,
			LSNAME  => $link . '/name'   . $inline,
			LSEARCH => $url  . '/search' . $inline
		 );
	}

	# Nothing to see
	# Move along
	else
	{
		# Display error message
		$tmpl->Show('TmplSrch', MMESSAGE=>$gal->{'MsgListing'});
	}


	# End statement
	$statement->finish();
}


#####################
# Genre List
#
# Exists to provide a fuller front-end to the galleries.   Which would allow for
# spidering and even a nicer interface for people to use when wanting to see what
# is available.

sub GenreList
{
	#####################
	# Data members
	my $database = shift;					# Database handler
	my $login    = shift;					# Authenthication handler
	my $cgi      = shift;					# CGI interface
	my $param    = shift;					# Paramater handler
	my $tmpl     = shift;					# Template handler
	my $sparam   = shift;					# Parameter line hash
	my $gal      = shift;					# Gallery hash

	my $statement;						# Query handle
	my $cres;						# Count results
	my $dres;						# Description results list
	my $gres;						# Genres results list
	my $tres;						# Types results list

	my $count;						# Occurences of type
	my $description;					# Description

	my $inline;						# Inline parameter set
	my $link;						# Temporary constructed url
	my $url;						# Self referencing link

	my %descr;						# Description hash
	my %num;						# Number of entries reached

	#####################
	# Program Area
	
	# Self-referencing link
	$url = $cgi->url(-absolute=>1);

	# Inline parameter enbed
	$inline = (defined($param->{'USER'}))
	 ? $param->EmbedInline($param->Flat())
	 : '';

	# Type definition
	# Users have selected a type
	if ((defined($sparam->{'TYP'}))
	&& (defined($sparam->{'SUB'})))
	{
		# Descriptions handling
		# Decriptions are not native to exhibition table
		$database->Pull(\$statement, "SELECT
			GenreName        AS \"Genre\",
			GenreDescription AS \"Description\"
			FROM Genre
			WHERE GenreType ILIKE ?
			  AND GenreSub  ILIKE ?",

		 $sparam->{'TYP'},
		 $sparam->{'SUB'}
	 	);

		# Loop and place in a hash
		while ($dres = $statement->fetchrow_hashref())
		{
			# Creation of hash
			$descr{$dres->{'Genre'}} = $dres->{'Description'};
		}

		# End query
		$statement->finish();

		
		# Main display
		# Initialize
		my $list = '';

		# Retreive sections list
		# Prepare and execute
		$database->Pull(\$statement, "SELECT
			 GalleryGenre AS \"Genre\",
			 COUNT(*)     AS \"Count\"
			FROM Exhibition
			WHERE GalleryType ILIKE ?
			  AND GallerySub  ILIKE ?
			GROUP BY GalleryGenre
			ORDER BY GalleryGenre",
		
		 $sparam->{'TYP'},
		 $sparam->{'SUB'}
		);
	
		# Display all available genres
		# Loop and display
		while ($gres = $statement->fetchrow_hashref())
		{
			# Generate link
			$link = $url . '/' 
			      . $sparam->{'TYP'} . '/' 
			      . $cgi->escape($sparam->{'SUB'}) . '/'
			      . $cgi->escape($gres->{'Genre'});
			
			# Description
			$description = (defined($descr{$gres->{'Genre'}}))
			  ? $descr{$gres->{'Genre'}}
			  : $gal->{'TxtDescription'}; 

			# Append output
			$list .= $tmpl->Pass('TmplGenreDescription',
				MCOUNT => $gres->{'Count'},
				MDESCR => $description,
				MGENRE => $gres->{'Genre'},
				LGENRE => $link . $inline
			 );
		}

		# Finish up
		$statement->finish();

		# Generate link
		$link = $url . '/' 
		      . $sparam->{'TYP'} . '/' 
		      . $cgi->escape($sparam->{'SUB'});
		
		# Display wrapper template
		$tmpl->Show('TmplGenreSub',
			LSTSUB  => $list,
			MSUB    => $sparam->{'SUB'},
			LSUB    => $link . $inline,
			LTYPE   => $url,
			LSEARCH => $url . '/search'
		 );
	}

	# No major-type selected
	# Mostly users coming in from cold
	else
	{
		# Get a count for every subtype
		$database->Pull(\$statement, "SELECT
			  GalleryType AS \"Type\",
			  GallerySub  AS \"Sub\",
			  COUNT(*)    AS \"Count\"
			 FROM Exhibition
			 GROUP BY GalleryType, GallerySub");

		# Populate hash
		while ($cres = $statement->fetchrow_hashref())
		{
			# Put in the number
			$num{"$cres->{Type}.$cres->{Sub}"} = $cres->{'Count'};
		}
		
		# Finish query
		$statement->finish();
	

		# Main display
		# Initialize
		my $img   = '';
		my $txt   = '';

		# Define
		my $count = 0;

		# Retreive sections list
		# Prepare and execute
		$database->Pull(\$statement, "SELECT
			  GenreType        AS \"Type\",
			  GenreName        AS \"Sub\",
			  GenreDescription AS \"Description\"
			 FROM Genre
			WHERE GenreType IN ('image','text')
			  AND GenreSub='<root>'
			GROUP BY GenreType, GenreName, GenreDescription
			ORDER BY GenreName",
		);
	
		# Display all available genres
		# Loop and display
		while ($tres = $statement->fetchrow_hashref())
		{
			# Define count
			$count = (defined($num{"$tres->{Type}.$tres->{Sub}"}))
			  ? $num{"$tres->{Type}.$tres->{Sub}"}
			  : 0;

			# Skip if necessary
			next if ($count == 0);

			
			# Generate link
			$link = $url . '/' 
			      . $tres->{'Type'} . '/' 
			      . $cgi->escape($tres->{'Sub'});
			
			# Description
			$description = (defined($tres->{'Description'}))
			  ? $tres->{'Description'}
			  : $gal->{'TxtDescription'}; 


			# Append output
			# Reason why grouping was not necessary
			if ($tres->{'Type'} eq 'image')
			{
				# Append to image list
				$img .= $tmpl->Pass('TmplGenreDescription',
					MCOUNT => $count,
					MDESCR => $description,
					MGENRE => $tres->{'Sub'},
					LGENRE => $link . $inline
				 );
			}
			else
			{
				# Append to text list
				$txt .= $tmpl->Pass('TmplGenreDescription',
					MCOUNT => $count,
					MDESCR => $description,
					MGENRE => $tres->{'Sub'},
					LGENRE => $link . $inline
				 );
			}
		}

		# Finish up
		$statement->finish();

		# Display wrapper template
		$tmpl->Show('TmplGenreType',
			LSTIMAGE => $img,
			LSTTEXT  => $txt,
			LSEARCH  => $url . '/search'
		 );
	}

	# Return
	return 1;
}


#####################
# Header rewrite
#
# Used to rewrite the HTML header as to properly list the Authors name
# and gallery name in the title field.

sub HeaderRewrite
{
	#####################
	# Data members
	my $database = shift;					# Database handler
	my $cgi      = shift;					# Common gateway interface
	my $tmpl     = shift;					# Template handler
	my $sparam   = shift;					# Parameter line hash
	my $gal      = shift;					# Gallery parameter

	my $link;						# Link name
	my $tree;						# Tree interface
	
	my $author;						# Author of gallery
	my $id;							# Gallery ID
	my $name;						# Gallery/Work name
	my $header;						# HTML Header

	#####################
	# Program area

	# Retrieve header
	($header) = $database->DocumentGet('GalleryHeader');


	# Tree link handling
	# Base link
	if ((defined($sparam->{'SUB'}))
	 && (length($sparam->{'SUB'}) > 3))
 	{
		# The root
		# Self referencing link
		$link  = $cgi->url(-absolute=>1);

		# Template
		$tree  = $tmpl->Pass('TagTree',
			MNAME => '',
			MLINK => $link
		 );


		# Listing for genre
		# Additional link
		$link .= '/' . $sparam->{'TYP'}
		       . '/' . $sparam->{'SUB'}; 
		
		# Template
		$tree .= $tmpl->Pass('TagTree',
			MNAME => $sparam->{'SUB'},
			MLINK => $link
		 );
		
		if ((defined($sparam->{'GRE'}))
		 && (length($sparam->{'GRE'}) > 3))
 		{
			# Listing for galleries
			# Additional link
			$link .= '/' . $sparam->{'GRE'}; 
		
			# Template
			$tree .= $tmpl->Pass('TagTree',
				MNAME => $sparam->{'GRE'},
				MLINK => $link
			 );
		}
	}
	

	# Check for rewrite
	if ((defined($sparam->{'GAL'}))
	 && ($sparam->{'GAL'} =~ /^\d+$/))
 	{
		# Gallery details
		# Pull details
		($id, $author, $name) = $database->DataGet("SELECT
			 GalleryID,
			 GalleryPenName,
			 GalleryName
			FROM Gallery
			WHERE GalleryID=?", $sparam->{'GAL'});

		# Addition to the tree
		if (defined($name))
		{
			# Listing for galleries
			if (defined($tree))
			{
				# Additional link
				$link .= '/' . $sparam->{'GAL'}; 
		
				# Template
				$tree .= $tmpl->Pass('TagTree',
					MNAME => $name,
					MLINK => $link
				 );
			}

			# Work takes over title	
			if ((defined($sparam->{'WRK'}))
			 && ($sparam->{'WRK'} =~ /^\d+$/))
			{
				# Pull details
				($id, $author, $name) = $database->DataGet("SELECT
					 g.GalleryID,
				 	 g.GalleryPenName,
					 w.WorkName
					FROM Gallery g, Work w
					WHERE w.GalleryID=g.GalleryID
					  AND g.GalleryID=?
					  AND w.WorkID=?",
	
				 $sparam->{'GAL'},
				 $sparam->{'WRK'}
				);

				# Replace if need be
				if (defined($name))
				{
					# Listing for galleries
					if (defined($tree))
					{
						# Additional link
						$link .= '/' . $sparam->{'WRK'};

						# Template
						$tree .= $tmpl->Pass('TagTree',
							MNAME => $name,
							MLINK => $link
						 );
					}


					# Search and replace
					# Look for instances of the title field
					$header =~ s/(<TITLE.*?>).+(<\/TITLE.*?>)/${1}${name} by ${author}${2}/gsi;	
				}
			}
	
			# Galleries
			# This is the default
			else
			{
				# Search and replace
				# Look for instances of the title field
				$header =~ s/(<TITLE.*?>).+(<\/TITLE.*?>)/${1}${author}: ${name}${2}/gsi;	
			}
		}
	}

	# Safety
	$tree = (defined($tree))
	 ? $tree
	 : $gal->{'TagTreeBlank'};

	# Replace
	$header =~ s/MTREE/$tree/gs;


	# Print out modified header
	print $header;
}


#####################
# Navigation
#
# Used to consolidate navigational methods on the site employing OFFSET and LIMIT
# which are available on PostgreSQL.

sub Navigation
{
	#####################
	# Data members
	my $tmpl      = shift;					# Template handler
	my $gal       = shift;					# Gallery hash
	my $display   = shift;					# Entries to show off
	my $total     = shift;					# Total entries
	my $offset    = shift;					# Current position
	my $url       = shift;					# Link to display
	my $inline    = shift;					# Inline parameters

	my $count;						# Basic counter
	my $frst;						# First entry
	my $last;						# Last entry
	my $next;						# Next entry
	my $prev;						# Prev entry
	
	my $link;						# Generated link
	my $nav;						# Completed navigational page
	my $pass;						# What is passed
	

	#####################
	# Program area

	# Set values
	$count = 0;
	$nav   = '';
	$pass  = '&nbsp;';
	
	# Calculate next and previous
	$frst = 1;
	$last = (($total - ($total % $display)) / $display) + 1;
	$prev = (($offset - 1) > 0) ? $offset - 1 : 1;
	$next = (($offset + 1) <= $last) ? $offset + 1 : $last;
	
	# Loop
	do
	{
		# Remove mount to verify spacing
		$total -= $display;

		# Increment
		$count++;

		# Assign
		$link  = $url . '/' . $count . $inline;

		# Append
		$nav .= ($offset == $count)
		  ? $tmpl->Pass('TagNavView', MNAME=>$count, MLINK=>$link)
		  : $tmpl->Pass('TagNav',     MNAME=>$count, MLINK=>$link);

	} while ($total > 0);

	# Generate template
	# Only if there is a need
	$pass = $tmpl->Pass('TagNav', MNAME=>$gal->{'TxtFirst'}, MLINK=>$url . '/' . $frst . $inline)
	     .  $tmpl->Pass('TagNav', MNAME=>$gal->{'TxtPrev'},  MLINK=>$url . '/' . $prev . $inline)
	     .  $nav
	     .  $tmpl->Pass('TagNav', MNAME=>$gal->{'TxtNext'},  MLINK=>$url . '/' . $next . $inline)
	     .  $tmpl->Pass('TagNav', MNAME=>$gal->{'TxtLast'},  MLINK=>$url . '/' . $last . $inline) if ($count > 1);

	# Return completed
	return $pass;
}


#####################
# Pen Names
#
# Will take a list of all contributors and make it in such a way that it 
# can display all of the contributors on one line.

sub PenNames
{
	#####################
	# Data members
	my $database = shift;					# Database handler
	my $param    = shift;					# Parameter list
	my $tmpl     = shift;					# Template handler
	my $gal      = shift;					# Gallery parameter
	my $id       = shift;					# Gallery ID
	my $owner    = shift;					# Rightful owner

	my $count;						# Basic counter
	my $entries;						# Number entries
	
	my $consol;						# Consolidated lines
	my $inline;						# Inline handling
	my $spacer;						# Employed spacer

	my @args;						# Arguments to send
	my @contribs;						# Complete list of contributors
	
	#####################
	# Program area

	# Owner of gallery
	# Owner specified
	if (defined($owner))
	{
		# Pass arguments
		push(@args, $id, $owner);

		# Throw in additional formatting
		$owner = 'AND PuppeteerLogin=? ';
	}

	# Normal dual entry
	else
	{
		# Normal passage
		push(@args, $id);

		# Nothing to see
		$owner = '';
	}
	
	# Pull list of all contributors
	$database->GetList(\@contribs, "SELECT PuppetName
		FROM GalleryAccess
		WHERE GalleryID=? $owner
		ORDER BY AccessOwner DESC, PuppetName", @args);


	# Amount of entries
	$entries = @contribs;

	# Loop and search
	for ($count=0; $count < $entries; $count++)
	{
		# Generate biographical link
		$inline = (defined($param->{'USER'}))
		 ? $param->EmbedInline(
			 USER  => $param->{'USER'},
			 CRYPT => $param->{'CRYPT'},
			 BCHAR => $contribs[$count],
			 BPOOL => 'system')
		 : $param->Crypt(
			 BCHAR => $contribs[$count],
			 BPOOL => 'system');


		# Information handling
		# Initialize
		if ($count == 0)
		{
			# Set authors information first
			$consol = $tmpl->Pass('TagPenName',
				MNAME => $contribs[$count],
				MLINK => $gal->{'LnkBio'} . $inline
			);
		}

		# Appending
		else
		{
			# Spacer
			$spacer = ($count != ($entries-1))
			  ? $gal->{'TagPenNameSpacer'}
			  : $gal->{'TagPenNameFinalize'}; 

			# Additional contributors
			$consol = $consol . $spacer . $tmpl->Pass('TagPenName',
				MNAME => $contribs[$count],
				MLINK => $gal->{'LnkBio'} . $inline
			 );
		}
	}

	# Safety measure
	$consol = '' unless (defined($consol));
	
	# Return consolidated line
	return $consol;
}


#####################
# Work Detail
#
# Work display allows a user to view the works themselves in full
# detail.    

sub WorkDetail
{
	#####################
	# Data members
	my $database = shift;					# Database handler
	my $login    = shift;					# Authenthication handler
	my $cgi      = shift;					# CGI interface
	my $param    = shift;					# Paramater handler
	my $tmpl     = shift;					# Template handler
	my $sparam   = shift;					# Parameter line hash
	my $gal      = shift;					# Gallery hash

	my $option;						# Option handler
	
	my $layer;						# Layer management
	my $res;						# Results
	my $revisions;						# Query for revisions
	my $statement;						# Query statement

	my $cmnt;						# Comment preferences
	my $name;						# Verification check
	my $type;						# Gallery type
	my $view;						# Type of view

	my $order;						# Sort order
	my $sort;						# Sort type

	my $comments;						# Comments template
	my $descr;						# Description
	my $pen;						# Pen names formatted
	my $work;						# Work template
	
	my $next = 0;						# Next sequence
	my $curr = 0;						# Current position
	my $prev = 0;						# Previous record
	my $totl = 0;						# Total amount of works

	my $rate;						# Rating
	my $user;						# Username
	my $vote;						# Votes

	my $url;						# Self referencing link
	my $inline;						# Inline parameters

	my @seq;						# Logical squence


	#####################
	# Program Area

	# Things are in order
	($name) = $database->DataGetWorkName($sparam->{'WRK'}, $sparam->{'GAL'});

	# Continue as planned
	if (defined($name))
	{
		# Things are proper
		return 0 unless (CheckAuth($database, $login, $param, $sparam, $tmpl, $gal));

		# Create instance
		$option = new Ethereal::Option;


		# Determine layer handling
		$layer = ($sparam->{'LYR'} > 0)
		  ? 'AND WorkLayer=' . $sparam->{'LYR'}
		  : 'AND WorkLayer IS NULL';


		# Information gathering
		# Retreive gallery basics
		($sort, $cmnt, $type, $user) = $database->DataGet("SELECT
			  GallerySort,
			  GalleryComments,
			  GalleryType,
			  PuppeteerLogin
			FROM Gallery
			WHERE GalleryID=?",

		 $sparam->{'GAL'}
		);


		# Ordering type
		$order = $option->GallerySortReal($sort);
			
		# Retrieve next of kin
		$database->GetList(\@seq, "SELECT WorkID
			FROM Work
			WHERE GalleryID=? $layer 
			  AND WorkID > 1
			ORDER BY $order",

		 $sparam->{'GAL'}
		);

		# Total
		$totl = @seq;


		# Where we are
		for ($curr=0; $curr < $totl; $curr++)
		{
			# Find and leave loop
			last unless ($seq[$curr] != $sparam->{'WRK'});
		}

		# Position of cousins
		$next = ($curr+1 >= $totl) ? $seq[0] : $seq[$curr+1];
		$prev = $seq[$curr-1];
		

		# Linking
		# Fetch link
		$url = $cgi->url(-absolute=>1) . '/'
		     . $sparam->{'TYP'} . '/'
		     . $sparam->{'SUB'} . '/'
		     . $sparam->{'GRE'} . '/'
		     . $sparam->{'GAL'} . '/'
		     . $sparam->{'LYR'};

		# Inline parameters general
		$inline  = (defined($param->{'USER'}))
		 ? $param->EmbedInline(USER=>$param->{'USER'}, CRYPT=>$param->{'CRYPT'})
		 : '';


		# Works of art
		# Retrieve rating
		($rate, $vote) = $database->DataGetWorkAvg($sparam->{'WRK'}, $sparam->{'GAL'});


		# Prepare and execute
		$database->Pull(\$statement, "SELECT
			  PuppeteerLogin     AS \"WorkOwner\",
			  WorkName           AS \"WorkName\",
			  WorkDescription    AS \"WorkDescription\",
			  WorkSort           AS \"WorkSort\",
			  WorkImage          AS \"WorkImage\",
			  WorkImageThumbnail AS \"WorkImageThumbnail\",
			  WorkImageSize      AS \"WorkImageSize\",
			  WorkImageWidth     AS \"WorkImageWidth\",
			  WorkImageHeight    AS \"WorkImageHeight\",
			  WorkImageView      AS \"WorkImageView\",
			  WorkImageFull      AS \"WorkImageFull\",
			  WorkImageExif      AS \"WorkImageExif\",
			  WorkText           AS \"WorkText\",
			  WorkTextPreserve   AS \"WorkTextPreserve\",
			  getDate(WorkTimestamp) AS \"WorkTimestamp\"
			FROM Work
			WHERE WorkID=?
			  AND GalleryID=?",

		 $sparam->{'WRK'},
		 $sparam->{'GAL'}
		);
	
		# Make available
		if ($res = $statement->fetchrow_hashref())
		{
			# Fork for gallery styles
			# Image gallery
			if ($type eq 'image')
			{
				# Exif information
				my $exif = (defined($res->{'WorkImageExif'}))
				  ? $tmpl->Pass('TmplImageExif', MEXIF=>$res->{'WorkImageExif'})
				  : '';
				
				# View related plesantries
				# Full view
				if ((defined($sparam->{'POS'}))
				 && ($sparam->{'POS'} eq 'full'))
			 	{
					# Adjust link to work
					$view = $res->{'WorkImage'};
					
					# Replace appropriatly
					$view =~ s/^(.+\/)(view-)(work\.\w+)$/$1$3/;


					# Display image specific
					# Full view
					$work = $tmpl->Pass('TmplImageWork',
						LSTEXIF   => $exif,
						LIMAGE  => $view,
						LTOGGLE => $url . '/' . $sparam->{'WRK'},
						MNAME   => $res->{'WorkName'},
						MWDHT   => $res->{'WorkImageFull'},
						MTOGGLE => $gal->{'TagImageView'}
					 );
				}

				# Regular view
				else
				{
					# Display image specific
					# Normal view
					$work = $tmpl->Pass('TmplImageWork',
						LSTEXIF => $exif,
						LIMAGE  => $res->{'WorkImage'},
						LTOGGLE => $url . '/' . $sparam->{'WRK'} . '/full', 
						MNAME   => $res->{'WorkName'},
						MWDHT   => $res->{'WorkImageView'},
						MTOGGLE => $gal->{'TagImageFull'}
					 );
				}
			}

			# Text based galleries
			elsif ($type eq 'text')
			{
				# Local variables
				my ($rdate, $rstamp);

				# Initial assignment
				my $print = '';
				my $revs  = '';
				my $post  = $res->{'WorkText'};


				# Make use of revision instead
				if ((defined($sparam->{'POS'}))
				 && ($sparam->{'POS'} =~ /^\w{30,}/))
				{
					# Make use of revision
					($post, $rdate) = $database->DataGet("SELECT
						  RevisionText,
						  getDate(RevisionTimestamp)
						FROM Revision
						WHERE WorkID=?
						  AND GalleryID=?
						  AND RevisionTimestamp=?",

					  $sparam->{'WRK'},
					  $sparam->{'GAL'},
					  pack('H*', $sparam->{'POS'})
					 );

					 # Append notice
					 $post .= $tmpl->Pass('TagRevNotice',
						 MDATE => $rdate
					   );
				}

				
 				# Rewriting
				# Basic replacements
				$post =~ s/$/\n/;
					
				# Maintain spacing
				$post =~ s/\n/<BR>/gs;					
 

				# Revision control
				if ($type eq 'text')
				{
					
					# Pull new list
					$database->Pull(\$revisions, "SELECT
						  RevisionTimestamp          AS \"Timestamp\",
						  getDate(RevisionTimestamp) AS \"Date\"
						FROM Revision
						WHERE WorkID=?
						  AND GalleryID=?",
						  
					 $sparam->{'WRK'},
					 $sparam->{'GAL'}
					);

					# Verification
					if (($rstamp, $rdate) = $revisions->fetchrow())
					{	
						# Initialize template
						$revs = $gal->{'TagRevTitle'};

						# Initial link
						$revs .= $tmpl->Pass('TagRevLink',
							MLINK => $url . '/' . $sparam->{'WRK'} . $inline,
							MNAME => $gal->{'TxtRecent'}
						 );

						
						# Check first
						do
						{
							# Append additional links
							$revs .= $tmpl->Pass('TagRevLink',
								MLINK => $url . '/' . $sparam->{'WRK'} . '/' . unpack('H*', $rstamp) . $inline,
								MNAME => $rdate
							 );

						} while (($rstamp, $rdate) = $revisions->fetchrow());

						# Close off
						$revs .= $gal->{'TagRevClose'}; 

						# Close handle
						$revisions->finish();
					}
				}

				
				# Printing option
				if (-f "$gal->{SetRoot}/$sparam->{GAL}/$sparam->{WRK}/work.pdf")
				{
					# Show print option
					$print = $tmpl->Pass('TagPrint', 
						MLINK => "$gal->{SetUrl}/$sparam->{GAL}/$sparam->{WRK}/work.pdf"
					 );
				}
				

				# Display image specific
				$work = $tmpl->Pass('TmplTextWork',
					MTEXT  => $post,
					MREVS  => $revs,
					MPRINT => $print
				 );
			}

			# Additional information
			# List comments
			$comments = CommentDisplay(
				$database,
				$login,
				$cgi,
				$param,
				$tmpl,
				$sparam,
				$gal,
				$gal->{'SetRetrieve'}
			  );

			# Pen names
			$pen      = PenNames(
				$database,
				$param,
				$tmpl,
				$gal,
				$sparam->{'GAL'},
				$res->{'WorkOwner'}
			 );

			# Establishment of views
			$view = ($sparam->{'POS'} eq 'full')
			  ? '/full'
			  : '';
			 

			# Safety
			$descr = (defined($res->{'WorkDescription'}))
			  ? $res->{'WorkDescription'}
			  : $gal->{'TxtDescription'};
			  
	
			# Common display
			# Header
			$tmpl->Show('TmplWork',
				MAUTHOR  => $pen,
				MCOMMENT => $comments,
				MCURR    => $curr+1,
				MDESC    => $descr,
				MNAME    => $res->{'WorkName'},
				MWORK    => $work,
				MTOTAL   => $totl,
				LGTOP    => $url . '/' . $inline,
				LNEXT    => $url . '/' . $next . $view . $inline,
				LPREV    => $url . '/' . $prev . $view . $inline
			 );
		}

		# Finish query
		$statement->finish();
	}

	# Signal error
	else
	{
		# Invalid entry
		$tmpl->Show('TmplMsg', MMESSAGE=>$gal{'MsgParam'});
	}
}


#####################
# Work Listing
#
# Work listing will allow casual viewers to see an overview of every
# work available.   This includes a naivgational system, and an overview of
# the more recent comments made on works.

sub WorkList
{
	#####################
	# Data members
	my $database = shift;					# Database handler
	my $login    = shift;					# Authenthication handler
	my $cgi      = shift;					# CGI interface
	my $param    = shift;					# Paramater handler
	my $tmpl     = shift;					# Template handler
	my $sparam   = shift;					# Parameter line hash
	my $gal      = shift;					# Gallery hash

	my $filter;						# Filtering options
	my $option;						# Option handler
	
	my $gsth;						# Gallery query
	my $lsth;						# Link query
	my $msth;						# Map query
	my $nsth;						# News query
	my $ssth;						# Layers query
	my $wsth;						# Works query
	
	my $gres;						# Information on gallery
	my $lres;						# Link results page
	my $mres;						# Mapping results
	my $nres;						# Information on news items
	my $sres;						# Information on layers
	my $wres;						# Information on works

	my $descr;						# Description
	my $length;						# Text length
	my $rating;						# Rating
	my $thumb;						# Thumbnail config

	my $expand;						# Expansion handling
	my $layer;						# Layer to search through
	my $newness;						# Newness factor
	my $offset;						# Where to start from
	my $order;						# Searching order
	my $parent;						# Parent layer
	my $total;						# Entries to look out for
	my $user;						# Username of owner
	my $watch;						# Watch gallery

	my $authors;						# Author management
	my $comments;						# Comments template
	my $genres;						# Formatted genres
	my $intro;						# Intro section
	my $layers;						# Layer page
	my $links;						# Links page
	my $map;						# The map action button/menu
	my $news;						# News entry
	my $page;						# Navigational page
	my $single;						# Single line to work with
	my $works;						# Works page

	my $int_id;						# Intro's used identifier
	my $int_name;						# Intro's used name
	my $int_descr;						# Intro's used description
	my $int_level;						# Intro's used level
	my $int_light;						# Intro's used highlight
	my $int_short;						# Intro's used short name
	my $int_thumb;						# Intro's used thumbnail
	
	my $inline;						# Inline parameters
	my $iwatch;						# Puppet watch option

	my $link_work;						# Link pertaining to works
	my $link;						# Managed link
	my $url;						# Self referencing link

	my @genres;						# Multiple genres
	my @layers    = qw(Zero One Two Three Four Five);	# Levels for layers
	my @levels;						# Levels captured
	
	my %pass;						# Temporary passthrough for parameters
	my %system;						# System hash


	#####################
	# Program Area

	# Authentication
	# Exit unless things are proper
	return 0 unless (CheckAuth($database, $login, $param, $sparam, $tmpl, $gal));


	# Create instance
	$option = new Ethereal::Option();

	# Safety
	$sparam->{'LYR'} = 0
	 unless ((defined($sparam->{'LYR'}))
	  && ($sparam->{'LYR'} =~ /^(\d+|map)$/));

  	# Work cannot be defined
	# If made it this far
	$sparam->{'WRK'} = 0;

	
	# Link management
	# Self referencing link
	$url = $cgi->url(-absolute=>1);

	# Generate primary working link
	$link = $url . '/' 
	      . $sparam->{'TYP'} . '/'
	      . $sparam->{'SUB'} . '/'
	      . $sparam->{'GRE'} . '/'
	      . $sparam->{'GAL'};
	
	# Generate link required for works
	$link_work = $url .'/' 
	      . $sparam->{'TYP'} . '/'
	      . $sparam->{'SUB'} . '/'
	      . $sparam->{'GRE'} . '/'
	      . $sparam->{'GAL'} . '/'
	      . $sparam->{'LYR'};

	      
	# Inline parameters general
	$inline  = (defined($param->{'USER'}))
	 ? $param->EmbedInline(USER=>$param->{'USER'}, CRYPT=>$param->{'CRYPT'})
	 : '';


	# Mapping option
	# General to both
	$map = $tmpl->Pass('TagNav',
		MNAME => $gal->{'TxtMap'},
		MLINK => $link . '/map' . $inline
	 );

	 
	# Base information
	# Gallery information
	$database->Pull(\$gsth, "SELECT
		  PuppeteerLogin     AS \"PuppeteerLogin\",
		  GalleryName        AS \"GalleryName\",
		  GalleryRating      AS \"GalleryRating\",
		  GalleryDescription AS \"GalleryDescription\",
		  GalleryHighlight   AS \"GalleryHighlight\",
		  GalleryThumbnail   AS \"GalleryThumbnail\",
		  GallerySort        AS \"GallerySort\",
		  GalleryType        AS \"GalleryType\",
		  GalleryComments    AS \"GalleryComments\",
		  GalleryDisplay     AS \"GalleryDisplay\",
		  getDate(GalleryCreated) AS \"GalleryCreated\",
		  getDate(GalleryUpdated) AS \"GalleryUpdated\"
		FROM Gallery
		WHERE GalleryID=?",

	 $sparam->{'GAL'}
	);


	# Common controls without depth
	# Check values
	if ($gres = $gsth->fetchrow_hashref())
	{
		# Genres
		# Genre searching
		$database->GetListEstablished(\@genres, $sparam->{'GAL'});

		# Format genre line
		$genres = join("/", @genres);

		
		# Authors
		# Determine authors
		$authors = PenNames(
			$database,
			$param,
			$tmpl,
			$gal,
			$sparam->{'GAL'}
		 );


		# Watchful processing
		# Watch allows the notification of changes to user
		if (defined($param->{'USER'}))
		{
			# Determine watch
			($watch) = $database->DataGetGalleryWatch($sparam->{'GAL'}, $param->{'USER'});


			# Toggle watch
			if (defined($param->{'WATCH'}))
			{
				# Change standing behaviour
				if (defined($watch))
				{
					# Remove database entry
					$database->Quick("DELETE FROM GalleryNotify
						WHERE GalleryID=?
						  AND PuppeteerLogin=?",
				  
					 $sparam->{'GAL'}, 
					 $param->{'USER'}
				 	);

					# Change logic
					$watch = undef;
				}

				else
				{
					# Create entry
					$database->Quick("INSERT INTO GalleryNotify
						(GalleryID,
						 PuppeteerLogin)
						VALUES (?,?)",
				  
					 $sparam->{'GAL'}, 
					 $param->{'USER'}
					);

					# Change logic
					$watch = $param->{'USER'};
				}

				# Cleanup
				$param->Cleanup('WATCH');
			}

			# Generate inline
			$iwatch = $param->EmbedInline(
				USER  => $param->{'USER'},
				CRYPT => $param->{'CRYPT'},
				WATCH => 'True'
			 );

			# Change langauge
			$watch  = (defined($watch)) 
			 ? $tmpl->Pass('TagGalIgnore', MLINK=>$link . $iwatch)
			 : $tmpl->Pass('TagGalWatch',  MLINK=>$link . $iwatch);
		}

		# Safety
		# Generate widget
		$watch  = $tmpl->Pass('TagGalWatch', MLINK=>"$link\?USER\=\&WATCH\=True")
		  unless (defined($watch));
	}
	
	
	# Pull in values
	# Do not show a site map
	if ((defined($gres))
	 && ($sparam->{'LYR'} =~ /^\d+$/))
	{
		# Assign username
		$user = $gres->{'PuppeteerLogin'};

		
		# Sorting order
		# Ordering type
		$order = $option->GallerySortReal($gres->{'GallerySort'});


		# Offset management
		# Starting point
		$sparam->{'POS'} = 1 
		  unless ((defined($sparam->{'POS'}))
		       && ($sparam->{'POS'} =~ /^\d+$/));

		# Offset
		$offset = ($sparam->{'POS'} - 1) * $gres->{'GalleryDisplay'};

	
		# Layer handling
		$layer = ($sparam->{'LYR'} > 0)
		  ? 'AND WorkLayer=' . $sparam->{'LYR'} . ' '
		  : 'AND WorkLayer IS NULL ';


		# Layer information
		if ($sparam->{'LYR'} != 0)
		{
			# Get specific information in case
			($int_id,
			 $int_short,
			 $int_name,
			 $int_descr,
			 $int_light,
			 $int_thumb,
		 	 $int_level,
		 	 @levels) = $database->DataGet("SELECT
			 	  LayerID          AS \"LayerID\",
			 	  LayerShort       AS \"LayerShort\",
			 	  LayerFull        AS \"LayerFull\",
				  LayerDescription AS \"LayerDescription\",
				  LayerHighlight   AS \"LayerHighlight\",
				  LayerThumbnail   AS \"LayerThumbnail\",
				  LayerLevel       AS \"LayerLevel\",
				  LevelZero        AS \"LevelZero\",
				  LevelOne         AS \"LevelOne\",
				  LevelTwo         AS \"LevelTwo\",
				  LevelThree       AS \"LevelThree\",
				  LevelFour        AS \"LevelFour\",
				  LevelFive        AS \"LevelFive\"
				 FROM Layer
				WHERE GalleryID=?
				  AND LayerID=?",

			  $sparam->{'GAL'},
			  $sparam->{'LYR'}
			);

			# Determine parent
			$parent = $levels[$int_level-1] if ($int_level > 0);
		}
		
		# Make sure information is present
		# Else override with gallery information
		$int_descr = (defined($int_descr)) ? $int_descr : $gres->{'GalleryDescription'};
		$int_light = (defined($int_light)) ? $int_light : $gres->{'GalleryHighlight'};
		$int_name  = (defined($int_name))  ? $int_name  : $gres->{'GalleryName'};

		# Images may have issues
		$int_thumb = (defined($int_thumb)) ? $int_thumb : $gres->{'GalleryThumbnail'};
		$int_thumb = (defined($int_thumb)) ? $int_thumb : $gal->{'SetImgBlank'};
		
		# No description then put one in
		$int_descr = (defined($int_descr)) ? $int_descr : $gal->{'TxtDescription'};


		# Embeded Layers
		# No need for a sub-page
		if ($sparam->{'POS'} == 1)
		{
			# Safety
			$int_level = (defined($int_level)) ? $int_level : 0;
		
			# Expand query as necessary
			$expand = (defined($int_id))
			  ? "AND LayerLevel=" . ($int_level+1) . " AND Level$layers[$int_level]=" . DBI::neat($int_short)
			  : "AND LayerLevel=" . $int_level;

		
			# Prepare query as these are
			# general to both types
			$database->Pull(\$ssth, "SELECT
				  LayerID           AS \"LayerID\",
				  LayerFull         AS \"LayerFull\",
				  LayerDescription  AS \"LayerDescription\",
				  LayerThumbnail    AS \"LayerThumbnail\"
				 FROM Layer
				WHERE GalleryID=? $expand
				ORDER BY LayerFull",

			 $sparam->{'GAL'}
			);
		}




		# Specific type processing
		# Image galleries
		if ($gres->{'GalleryType'} eq 'image')
		{
			# Information display
			# Intro and summary
			$intro = $tmpl->Pass('TmplImageIntro',
				MDESCR   => $int_descr,
				MHLIGHT  => $int_light,
				MTHUMB   => $int_thumb,
				MGENRE   => $genres,
				MAUTHOR  => $authors,
				MRATING  => $gres->{'GalleryRating'},
				MTITLE   => $int_name,
				MCREATED => $gres->{'GalleryCreated'},
				MUPDATED => $gres->{'GalleryUpdated'},
				MWATCH   => $watch
			 );

			
			# Link with hash
			$database->GetHashSystem(\%system);

			# Work information
			# Amount of works
			($total) = $database->DataGet("SELECT COUNT(*)
				FROM Work
				WHERE GalleryID=?
				  AND WorkImage IS NOT NULL $layer",

			 $sparam->{'GAL'}
			);

			# Capture nav line
			$page = Navigation(
				$tmpl,
				$gal,
				$gres->{'GalleryDisplay'},
				$total,
				$sparam->{'POS'},
				$link . '/' . $sparam->{'LYR'} .'/',
			       	$inline
			  );


			# Layer management
			if (defined($ssth))
			{
				# Parent return
				# Parent is a layer
				if (defined($parent))
				{
					# Get specific information in case
					($int_id,
					 $int_name,
					 $int_descr,
					 $int_light,
					 $int_thumb) = $database->DataGet("SELECT
					 	  LayerID          AS \"LayerID\",
					 	  LayerFull        AS \"LayerFull\",
						  LayerDescription AS \"LayerDescription\",
						  LayerHighlight   AS \"LayerHighlight\",
						  LayerThumbnail   AS \"LayerThumbnail\",
						  LayerLevel       AS \"LayerLevel\"
						 FROM Layer
						WHERE GalleryID=?
						  AND LayerShort=?",

					  $sparam->{'GAL'},
					  $parent
					);


					# Make sure thumnail is assigned
					$thumb = (defined($int_thumb))
					 ? $int_thumb
					 : $gal->{'SetImgBlank'};

					# Layer has a layer parent
					$works = $tmpl->Pass('TmplImageLayer',
				 		MNAME    => $int_name,
						MDESCR   => $int_descr,
						MIMAGE   => $thumb,
						MTYPE    => $gal->{'SetLayerParent'},
						MLINK    => $link . '/' . $int_id . $inline
					);
				}

				# Parent is the root
				elsif ((!defined($parent))
				 && ($sparam->{'LYR'} != 0))
				{
					# Make sure thumnail is assigned
					$thumb = (defined($gres->{'GalleryThumbnail'}))
					 ? $gres->{'GalleryThumbnail'}
					 : $gal->{'SetImgBlank'};

					# Layer has the root as a parent
					$works = $tmpl->Pass('TmplImageLayer',
				 		MNAME    => $gres->{'GalleryName'},
						MDESCR   => $gres->{'GalleryDescription'},
						MIMAGE   => $gres->{'GalleryThumbnail'},
						MTYPE    => $gal->{'SetLayerParent'},
						MLINK    => $link . $inline
					);
				}
				
				# Children layers
				# Loop and manage
				while ($sres = $ssth->fetchrow_hashref())
				{
					# Make sure thumnail is assigned
					$thumb = (defined($sres->{'LayerThumbnail'}))
					 ? $sres->{'LayerThumbnail'}
					 : $gal->{'SetImgBlank'};
				
					# Generate template
					# Store for addition into main list
					$works .= $tmpl->Pass('TmplImageLayer',
				 		MNAME    => $sres->{'LayerFull'},
						MDESCR   => $sres->{'LayerDescription'},
						MIMAGE   => $thumb,
						MTYPE    => $gal->{'SetLayerChild'},
						MLINK    => $link . '/' . $sres->{'LayerID'} . $inline
					);
				}

				# End query
				$ssth->finish();
			}
			
			# Works
			# Prepare query
			$database->Pull(\$wsth, "SELECT
				  WorkID             AS \"WorkID\",
				  WorkName           AS \"WorkName\",
				  WorkDescription    AS \"WorkDescription\",
				  WorkRating         AS \"WorkRating\",
				  WorkImageThumbnail AS \"WorkImageThumbnail\",
				  WorkImageSize      AS \"WorkImageSize\",
				  WorkImageWidth     AS \"WorkImageWidth\",
				  WorkImageHeight    AS \"WorkImageHeight\",
				  WorkImageView      AS \"WorkImageView\",
				  WorkImageFull      AS \"WorkImageFull\",
				  getDate(WorkTimestamp) AS \"WorkTimestamp\",
				  getNew(WorkTimestamp)  AS \"WorkNewness\"
			 	 FROM Work
				WHERE GalleryID=?
				  AND WorkImage IS NOT NULL $layer
				ORDER BY \"WorkNewness\" DESC, $order
				LIMIT $gres->{GalleryDisplay} 
				OFFSET $offset",

			 $sparam->{'GAL'}
			);

			
			# Test the waters
			# Cycle through
			while ($wres = $wsth->fetchrow_hashref())
			{
				# Tag adjustements
				# Newness tag
				$newness = ($wres->{'WorkNewness'} != 0)
				  ? $gal->{'TagNewness'}
				  : '';
					
				# If need exists
				if (($gres->{'GalleryComments'} eq 'comment')
				 || ($gres->{'GalleryComments'} eq 'combo'))
				{
					# Caculate
					$rating = $wres->{'WorkRating'};
				}
				else
				{
					# Assign nil
					$rating = $gal->{'SetNil'};
				}


				# Generate template
				# Store for addition into main list
				$works .= $tmpl->Pass('TmplImageList',
			 		MNAME    => $wres->{'WorkName'},
					MDESCR   => $wres->{'WorkDescription'},
					MIMAGE   => $wres->{'WorkImageThumbnail'},
					MLINK    => $link_work . '/' . $wres->{'WorkID'} . $inline,
					MRATING  => $rating,
					MSIZE    => $wres->{'WorkImageSize'},
					MHEIGHT  => $wres->{'WorkImageHeight'},
					MWIDTH   => $wres->{'WorkImageWidth'},
					MTHUMB   => $system{'SetThumbnail'},
					MVIEW    => $wres->{'WorkImageView'},
					MUPDATED => $wres->{'WorkTimestamp'},
					MNEWNESS => $newness,
					MWINDOW  => 'WIN' . $sparam->{'GAL'} . 'x' . $sparam->{'LYR'}
				);
			}

			# Finish query
			$wsth->finish();
		}

		# Text based galleries
		elsif ($gres->{'GalleryType'} eq 'text')
		{
			# Information display
			# Intro and summary
			$intro = $tmpl->Pass('TmplTextIntro',
				MDESCR   => $int_descr,
				MGENRE   => $genres,
				MAUTHOR  => $authors,
				MRATING  => $gres->{'GalleryRating'},
				MTITLE   => $int_name,
				MCREATED => $gres->{'GalleryCreated'},
				MUPDATED => $gres->{'GalleryUpdated'},
				MWATCH   => $watch
			 );

			
			# Work information
			# Calculate totals
			($total) = $database->DataGet("SELECT COUNT(*)
				FROM Work
				WHERE GalleryID=?
				  AND WorkText IS NOT NULL $layer",

			 $sparam->{'GAL'}
			);
			
			# Capture nav line
			$page = Navigation(
				$tmpl,
				$gal,
				$gres->{'GalleryDisplay'},
				$total,
				$sparam->{'POS'},
				$link . '/' . $sparam->{'LYR'} . '/',
			       	$inline
			  );


			# Layer management
			if (defined($ssth))
			{
				# Parent return
				# Parent is a layer
				if (defined($parent))
				{
					# Get specific information in case
					($int_id,
					 $int_name,
					 $int_descr) = $database->DataGet("SELECT
					 	  LayerID          AS \"LayerID\",
					 	  LayerFull        AS \"LayerFull\",
						  LayerDescription AS \"LayerDescription\"
						 FROM Layer
						WHERE GalleryID=?
						  AND LayerID=?",

					  $sparam->{'GAL'},
					  $parent
					);

					# Layer has a layer parent
					$works = $tmpl->Pass('TmplTextLayer',
				 		MNAME    => $int_name,
						MDESCR   => $int_descr,
						MTYPE    => $gal->{'SetLayerParent'},
						MLINK    => $link . '/' . $int_id . $inline
					);
				}

				# Parent is the root
				elsif ((!defined($parent))
				 && ($sparam->{'LYR'} != 0))
	 			{
					# Layer has the root as a parent
					$works = $tmpl->Pass('TmplTextLayer',
				 		MNAME    => $gres->{'GalleryName'},
						MDESCR   => $gres->{'GalleryDescription'},
						MTYPE    => $gal->{'SetLayerParent'},
						MLINK    => $link . $inline
					);
				}	
				
				# Loop and manage
				while ($sres = $ssth->fetchrow_hashref())
				{
					# Generate template
					# Store for addition into main list
					$works .= $tmpl->Pass('TmplTextLayer',
				 		MNAME    => $sres->{'LayerFull'},
						MDESCR   => $sres->{'LayerDescription'},
						MTYPE    => $gal->{'SetLayerChild'},
						MLINK    => $link . '/' . $sres->{'LayerID'} . $inline
					);
				}

				# End query
				$ssth->finish();
			}


			# Work management
			# Detailed information
			$database->Pull(\$wsth, "SELECT
				  WorkID          AS \"WorkID\",
				  WorkName        AS \"WorkName\",
				  WorkDescription AS \"WorkDescription\",
				  WorkRating      AS \"WorkRating\",
				  WorkText        AS \"WorkText\",
				  getDate(WorkTimestamp) AS \"WorkTimestamp\",
				  getNew(WorkTimestamp)  AS \"WorkNewness\"
				FROM Work
				WHERE GalleryID=?
				  AND WorkText IS NOT NULL $layer
				ORDER BY \"WorkNewness\" DESC, $order
				LIMIT $gres->{GalleryDisplay}
				OFFSET $offset",

			 $sparam->{'GAL'}
			);


			# Create instance
			$filter = new Ethereal::Filter();

			# Safety check
			while ($wres = $wsth->fetchrow_hashref())
			{
				# Newness tag
				$newness = ($wres->{'WorkNewness'} != 0)
				  ? $gal->{'TagNewness'}
				  : '';

				# Tag assignments
				# If need exists
				if (($gres->{'GalleryComments'} eq 'comment')
				 || ($gres->{'GalleryComments'} eq 'combo'))
				{
					# Calculate
					$rating = $wres->{'WorkRating'};
				}
				else
				{
					# Assign nil
					$rating = $gal->{'SetNil'};
				}

				# Description
				$descr  = $wres->{'WorkDescription'};
				$descr  = (defined($descr))
			       	  ? $descr
			       	  : $filter->StripHTML(substr(DBI::neat($wres->{'WorkText'}, 252), 1, -1));

				# Length
				$length = length($wres->{'WorkText'});


				# Display
				$works .= $tmpl->Pass('TmplTextList',
			 		MNAME    => $wres->{'WorkName'},
					MDESCR   => $descr,
					MLENGTH  => format_number($length, 0),
					MLINK    => $link_work . '/' . $wres->{'WorkID'} . $inline,
					MRATING  => $rating,
					MUPDATED => $wres->{'WorkTimestamp'},
					MNEWNESS => $newness,
					MWINDOW  => 'WIN' . $sparam->{'GAL'} . 'x' . $sparam->{'LYR'}
				);
			}

			# Finish query
			$wsth->finish();
		}

		# Additional features
		# Generate and display
		if (($sparam->{'POS'} == 1)
		 && ($sparam->{'LYR'} == 0))
		{
			# Gallery Comments
			# Passing of temporary values
			%pass = (%{$sparam}, WRK=>0); 

			# Comments
			# Initialize
			$comments = '';

			# List comments
			$comments = CommentDisplay(
				$database,
				$login,
				$cgi,
				$param,
				$tmpl,
				\%pass,
				$gal,
				$gal->{'SetRetrieve'}
			  );


			# News Items
			# Initalize
			$news = '';

			# Expansion handling
			$expand = (defined($param->{'EXPAND'}))
			 ? '' 
			 : 'LIMIT 5';

			# Prepare query
			$database->Pull(\$nsth, "SELECT
				 CommentID		   AS \"CommentID\",
				 CommentPost               AS \"CommentPost\",
				 getDate(CommentTimestamp) AS \"CommentTimestamp\"
				FROM Comment
				WHERE GalleryID=?
				  AND WorkID=1
				ORDER BY CommentTimestamp DESC $expand", $sparam->{'GAL'});

			# Pull information
			# Verify need
			if ($nres = $nsth->fetchrow_hashref())
			{	
				# Ability to expand
				$expand = $param->EmbedInline($param->Flat(), EXPAND=>'True');
				$expand = $link . '/' . $sparam->{'LYR'} . $expand;


				# Self referencing links
				my $idelete = '';
				my $ldelete = $gal->{'LnkGallery'} . '/comment/' . $sparam->{'GAL'} . '/' . 1;

				# Initialize
				$single  = '';
				

				# Loop and display
				do
				{
					# Check for need to generate
					$idelete = $tmpl->Pass('TagDelete',
						MLINK => $ldelete . '/' .  $nres->{'CommentID'} . $inline
					  ) if ((defined($param->{'USER'})) && ($user eq $param->{'USER'}));

					# Display
					$single .= $tmpl->Pass('TmplQNewsEntry',
				 		MDATE => $nres->{'CommentTimestamp'},
						MTEXT => $nres->{'CommentPost'},
						MDELL => $idelete
				 	 );

				} while ($nres = $nsth->fetchrow_hashref());
	
				# Run through final template
				$news .= $tmpl->Pass('TmplQNews',
					LSTNEWS => $single,
					LNEXP   => $expand
				 );
			}

			# Finish query
			$nsth->finish();


			# Quick Links
			# Initalize
			$links = '';
			
			# Prepare retreival
			$database->Pull(\$lsth, "SELECT
				 LinkName    AS \"LinkName\",
				 LinkAddress AS \"LinkAddress\"
				FROM Link
				WHERE GalleryID=?
				ORDER BY LinkName",

			 $sparam->{'GAL'}
			);

			# Check and initial setup
			if ($lres = $lsth->fetchrow_hashref())
			{
				# Intialize
				$single = '';
				
				# Loop and populate main body
				do
				{
					# Display
					$single .= $tmpl->Pass('TmplQLinksEntry',
						MLINK => $lres->{'LinkAddress'},
						MNAME => $lres->{'LinkName'}
				 	 );

				} while ($lres = $lsth->fetchrow_hashref());


				# Generate final template
				$links .= $tmpl->Pass('TmplQLinks',
					LSTLINKS => $single
				 );
			}

			# Finish query
			$lsth->finish();


			# Is there still a need
			# All necessary components in place
			if ((length($news) > 5)
			 && (length($links) > 5))
		 	{
				# Complete view
				$tmpl->Show('TmplListingPri',
					MINTRO   => $intro,
					MNAV     => $page,
					MMAP     => $map,
					MNEWS    => $news,
					MQUICK   => $links,
					MCOMMENT => $comments,
					LSTWORKS => $works
				 );
			}

			else
			{
				# Simplified view
				$tmpl->Show('TmplListingSec',
					MINTRO   => $intro,
					MNAV     => $page,
					MMAP     => $map, 
					LSTWORKS => $works
				 );
			}
		}

		# Simple view
		# Simply display appropriate template 
		else
		{
			# Display final work
			# Simplified view
			$tmpl->Show('TmplListingSec',
				MINTRO   => $intro,
				MNAV     => $page,
				MMAP     => $map, 
				LSTWORKS => $works
			 );
		}
	}

	# Do not show a site map
	elsif ((defined($gres))
	 && ($sparam->{'LYR'} =~ /^map$/))
 	{
		# Pull necessary information for data map
		$database->Pull(\$msth, "SELECT
			  l.LayerID          AS \"LayerID\",
			  l.LayerFull        AS \"LayerFull\",
			  l.LayerLevel       AS \"LayerLevel\",
			  COUNT(w.GalleryID) AS \"LayerCount\"
			 FROM Layer l
			 LEFT OUTER JOIN Work w
			   ON (l.GalleryID=w.GalleryID AND l.LayerID=w.WorkLayer)
			 WHERE l.GalleryID=?
			GROUP BY l.LayerID, l.LayerFull, l.LayerLevel,
				 l.LevelZero, l.LevelOne, l.LevelTwo,
				 l.LevelThree, l.LevelFour, l.LevelFive
			ORDER BY l.LevelZero, l.LevelOne, l.LevelTwo,
		       	 	 l.LevelThree, l.LevelFour, l.LevelFive",

		 $sparam->{'GAL'}
	 	);


		# Initialize
		$map = '';

		# There is mapping information
		if ($mres = $msth->fetchrow_hashref())
		{
			# Initliaze
			my $count;
			my $spacer;
			my $olayer = $mres->{'LayerLevel'};


			# Loop through
			do
			{
				# Level additions
				# Change based on up or down
				if ($olayer > $mres->{'LayerLevel'})
				{
					# Drop as far as necessary
					$count = $olayer;

					# Go down, way down
					do
					{
						# Generate spacer
						$spacer =  ' ' x ($count-1);
				
						# Move down a level
						$map .= $spacer . $gal->{'TagMapStepDown'} . "\n";

						# Decrement
						$count--;

					} while ($count > $mres->{'LayerLevel'});
				}

				# Moving up
				elsif ($olayer < $mres->{'LayerLevel'})
				{
			
					# Generate spacer
					$spacer = ' ' x ($mres->{'LayerLevel'});

					# Move up a level
					$map .= $spacer . $gal->{'TagMapStepUp'} . "\n";

				}


				# General statement
				# Spacer for this level
				$spacer = ' ' x ($mres->{'LayerLevel'}+1);

				# Addition of unit
				$map .= $spacer . $tmpl->Pass('TmplMapStep',
					MNAME  => $mres->{'LayerFull'},
					MCOUNT => $mres->{'LayerCount'},
					MLINK  => $link . '/' . $mres->{'LayerID'} . $inline
				 );

				# Re-assign for next pass
				$olayer = $mres->{'LayerLevel'};

			} while ($mres = $msth->fetchrow_hashref());


			# Close up properly
			# Assign
			$count = $olayer;

			# Look until the end
			while ($count > 0)
			{
				# Generate spacer
				$spacer = ' ' x ($count-1);
				
				# Move down a level
				$map .= $spacer . $gal->{'TagMapStepDown'} . "\n";

				# Decrement
				$count--;
			}
		}

		# End query if defined
		$msth->finish() if (defined($msth));


		# Irrelevant if there are no layers
		# Generate template
		$works = $tmpl->Pass('TmplMap',
			LSTMAP => $map,
			MROOT  => $gres->{'GalleryName'},
			LROOT  => $link . $inline
		 );
		
	
		# Generate introduction
		# Gallery type
		if ($gres->{'GalleryType'} eq 'image')
		{
			# Image galleries
			# Paste the introductions first
			$intro = $tmpl->Pass('TmplImageIntro',
				MDESCR   => $gres->{'GalleryDescription'},
				MHLIGHT  => $gres->{'GalleryHighlight'},
				MTHUMB   => $gres->{'GalleryThumbnail'},
				MGENRE   => $genres,
				MAUTHOR  => $authors,
				MRATING  => $gres->{'GalleryRating'},
				MTITLE   => $gres->{'GalleryName'},
				MCREATED => $gres->{'GalleryCreated'},
				MUPDATED => $gres->{'GalleryUpdated'},
				MWATCH   => $watch
			 );
		}

		# Text based galleries
		else
		{
			# Text galleries
			$intro = $tmpl->Pass('TmplTextIntro',
				MDESCR   => $gres->{'GalleryDescription'},
				MGENRE   => $genres,
				MAUTHOR  => $authors,
				MRATING  => $gres->{'GalleryRating'},
				MTITLE   => $gres->{'GalleryName'},
				MCREATED => $gres->{'GalleryCreated'},
				MUPDATED => $gres->{'GalleryUpdated'},
				MWATCH   => $watch
			 );
		}
		

		# Display final work
		# Use all of the toys
		$tmpl->Show('TmplListingSec',
			MINTRO   => $intro,
			LSTWORKS => $works,
			MNAV     => '',
			MMAP     => ''
		 );
	}

	# End query if defined
	$gsth->finish() if (defined($gsth));
}


#####################
# Work Search
#
# Allows for searchign through works for specific words and phrases
# reguardless of standard word search of boolean searches.

sub WorkSearch
{
	#####################
	# Data members
	my $database = shift;					# Database handler
	my $login    = shift;					# Authenthication handler
	my $cgi      = shift;					# CGI interface
	my $param    = shift;					# Paramater handler
	my $tmpl     = shift;					# Template handler
	my $sparam   = shift;					# Parameter line hash
	my $gal      = shift;					# Gallery hash
	
	my $res;						# Work results
	my $statement;						# Work query statement
	my $words;						# Words handler

	my $length;						# Length of string
	my $offset;						# Where to begin
	my $total;						# Total amount of entries

	my $where;						# Where clause

	my $url;						# Base URL
	my $link;						# Self-referencing link
	my $inline;						# Inline parameters

	my $descr;						# Description
	my $layer;						# LayerID
	my $newness;						# Newness factor
	my $page;						# Page number
	my $text;						# Text type
	my $window;						# Window to open to

	my $genre;						# Genre sort
	my $sub;						# Sub-types
	my $type;						# Search type

	my $sgnre;						# Search genre
	my $ssmbt;						# Submit
	my $stext;						# Text handler


	my $list;						# List of works
	my $body;						# Search results body
	my $search;						# Search bar
	my $nav;						# Navigational bar

	my @genre;						# Option for genres
	my @sub;						# Sub genres
	my @type;						# Gallery Types 
	
	my %system;						# System hash

	#####################
	# Data members

	# Genres list
	# Pull genre lists
	$database->GetList(\@type, "SELECT GenreName || '/'
		FROM Genre
		WHERE GenreSub='<root>' AND GenreType IN ('image','text')");

	# Sub-genres
	$database->GetList(\@sub, "SELECT GenreSub || '/' || GenreName
		FROM Genre
		WHERE GenreSub != '<root>'");
	
	# Merge
	push(@genre, sort(@type, @sub));
	
	
	# Generate widgets
	# Pupup menu
	$sgnre = $cgi->popup_menu('SGNRE', \@genre, $param->{'SGNRE'});

	# Submit and submit text field
	$ssmbt = $cgi->submit($gal->{'TxtSearch'});
	$stext = $cgi->textfield('STEXT', $param->{'STEXT'}, 25, 75);


	# Searching template
	$search = $tmpl->Pass('TmplSearchForm', 
		WGENRE  => $sgnre,
		WSEARCH => $ssmbt,
		WTEXT   => $stext
	 );


	# Need exists
	if (length($param->{'STEXT'}) > 2)
	{
		# Determine position
		$sparam->{'POS'} = ($sparam->{'GAL'} =~ /\d+/)
		  ? $sparam->{'GAL'}
		  : 1;

		
		# Pull text
		$text  = $param->{'STEXT'};
		

		# Generate query base
		# Create new instance
		$words = new Ethereal::Words();

		# Determine where statement
		$where = $words->Query($text);

		
		# Type and sub-types
		($type, $genre) = split(/\//, $param->{'SGNRE'});

		# Ensure definition
		$genre = ((defined($genre)) && (length($genre) > 2))
		  ? "GenreSub=\'$type\' AND GenreName=\'$genre\'"
		  : "GenreSub=\'$type\'";


		# Fetching of necessary information
		# Need weights
		$database->Write("SELECT
			  GalleryID              AS GalleryID,
			  WorkID                 AS WorkID,
			  SUM(IndexScore)        AS IndexScore, 
			  COUNT(IndexScore)      AS IndexMatches
			INTO TEMP Weight
			FROM Index
			WHERE IndexWord IN ($where)
			  AND EXISTS (SELECT GalleryID
			               FROM GalleryGenre
			              WHERE $genre
			                AND GalleryGenre.GalleryID=Index.GalleryID)
			GROUP BY WorkID, GalleryID
			LIMIT $gal->{SetMax}");

		# Pull total
		($total) = $database->DataGet("SELECT COUNT(*) FROM Weight");


		# Run based on a temp table
		# Prepare query
		if ($total > 0)
		{
			# Navigational
			# Self referencing link
			$url    = $cgi->url(-absolute=>1) . '/search';
			$inline = (defined($param->{'USER'}))
			 ? $param->EmbedInline($param->Flat())
			 : $param->Crypt($param->Flat());

			# Generate template
			$nav = Navigation(
				  $tmpl,
				  $gal,
				  $gal->{'SetRetrieve'},
				  $total,
				  $sparam->{'POS'},
				  $url,
				  $inline
			  );

			# Calculate offset
			$offset = ($sparam->{'POS'} - 1) * $gal->{'SetRetrieve'};


			# Type and sub-types
			($type, $genre) = split(/\//, $param->{'SGNRE'});
			
			# This has enough info for a direct query
			if (length($genre) > 2)
			{
				# Fetch direct match
				($type, $sub, $genre) = $database->DataGet("SELECT
					     GenreType,
					     GenreSub,
					     GenreName
					 FROM Genre 
					WHERE GenreSub=? 
					  AND GenreName=?", $type, $genre);
			}


			# Make use of temp table
			# Pull full list
			$database->Pull(\$statement, "SELECT
				  wrk.GalleryID              AS \"GalleryID\",
				  wrk.WorkID                 AS \"WorkID\",
				  wrk.WorkName               AS \"WorkName\",
				  wrk.WorkLayer              AS \"WorkLayer\",
				  wrk.WorkDescription        AS \"WorkDescription\",
				  wrk.WorkRating             AS \"WorkRating\",
				  wrk.WorkImage              AS \"WorkImage\",
				  wrk.WorkImageSize          AS \"WorkImageSize\",
				  wrk.WorkImageWidth         AS \"WorkImageWidth\",
				  wrk.WorkImageHeight        AS \"WorkImageHeight\",
				  wrk.WorkImageThumbnail     AS \"WorkImageThumbnail\",
				  wrk.WorkText               AS \"WorkText\",
				  getDate(wrk.WorkTimestamp) AS \"WorkTimestamp\",
				  getNew(wrk.WorkTimestamp)  AS \"WorkNewness\"
				 FROM Work wrk
			   INNER JOIN Weight wgt
				   ON wrk.GalleryID=wgt.GalleryID
				  AND wrk.WorkID=wgt.WorkID
			     ORDER BY wgt.IndexMatches, wgt.IndexScore
				LIMIT $gal->{SetRetrieve} OFFSET $offset");
			
			# Pull all records
			if ($res = $statement->fetchrow_hashref())
			{
				# Adjustment
				# Self referencing link
				$url    = $cgi->url(-absolute=>1);

				# Inline parameters
				$inline = (defined($param->{'USER'}))
				 ? $param->EmbedInline(
					USER=>$param->{'USER'},
					CRYPT=>$param->{'CRYPT'})
				 : '';


				# Associate with hash
				$database->GetHashSystem(\%system);


				# Loop through
				do
				{
					# Last ditch genre find
					($type, $sub, $genre) = $database->DataGet("SELECT
						GenreType,
						GenreSub,
						GenreName
						FROM GalleryGenre
						WHERE GalleryID=?", $res->{'GalleryID'}

					) unless (defined($sub));

					
					# Determine newness
					$newness = ($res->{'WorkNewness'} != 0)
					  ? $gal->{'TagNewness'}
					  : '';

					# Handle layers
					$layer = (defined($res->{'WorkLayer'}))
					  ? $res->{'WorkLayer'}
					  : 0;

					
					# Specific link adjustment
					$link = $url   . '/'
					      . $type  . '/'
					      . $sub   . '/'
					      . $genre . '/'
					      . $res->{'GalleryID'} . '/'
					      . $layer . '/'
					      . $res->{'WorkID'};


					# Window generation
					$window = 'WIN' . $res->{'GalleryID'} . 'x' . $res->{'WorkLayer'};

					  
					# Display
					# Image gallery
					if ($res->{'GenreType'} eq 'image')
					{
						# Display
						$list .= $tmpl->Pass('TmplImageList',
					 		MNAME    => $res->{'WorkName'},
							MDESCR   => $res->{'WorkDescription'},
							MIMAGE   => $res->{'WorkImageThumbnail'},
							MLINK    => $link . $inline,
							MRATING  => $res->{'WorkRating'},
							MSIZE    => $res->{'WorkImageSize'},
							MWIDTH   => $res->{'WorkImageWidth'},
							MHEIGHT  => $res->{'WorkImageHeight'},
							MTHUMB   => $system{'SetThumbnail'},
							MUPDATED => $res->{'WorkTimestamp'},
							MNEWNESS => $newness,
							MWINDOW  => $window
						);
					}

					# Text gallery
					else
					{
						# Description
						$descr  = (defined($res->{'WorkDescription'}))
						  ? $res->{'WorkDescription'}
						  : substr(DBI::neat($res->{'WorkText'}, 252), 1, -1);

						# Length
						$length = length($res->{'WorkText'});


						# Display information
						$list .= $tmpl->Pass('TmplTextList',
					 		MNAME    => $res->{'WorkName'},
							MDESCR   => $descr,
							MLENGTH  => $length,
							MLINK    => $link .$inline,
							MRATING  => $res->{'WorkRating'},
							MUPDATED => $res->{'WorkTimestamp'},
							MNEWNESS => $newness,
							MWINDOW  => $window
						);
					}

				} while ($res = $statement->fetchrow_hashref());


				# Compose body
				$body = $tmpl->Pass('TmplSearchBody',
					MNAV     => $nav,
					LSTWORKS => $list
				 );
			}

			else
			{
				# No results
				$body = $tmpl->Pass('TmplSearchNotice',
					MMESSAGE=>$gal->{'MsgSrchResults'}
				 );
			}

			# End execution
			$statement->finish();
		}

		else
		{
			# Error in execution
			$body = $tmpl->Pass('TmplSearchNotice',
				MMESSAGE=>$gal->{'MsgSrchResults'}
			 );
		}


		# Commit changes
		# Ensures that things drop
		$database->Write("DROP TABLE Weight");
		$database->Commit();
	}


	# Dsplay to user
	# Forms
	print $cgi->start_form(-action=>$cgi->url(-absolute=>1) . '/search');
	print $cgi->hidden('SNEW', 'True'), "\n";
	print $param->EmbedNormal($param->Flat()) if (defined($param->{'USER'}));

	# Display
	$tmpl->Show('TmplSearch',
		MBODY   => $body,
		MSEARCH => $search
	 );

	# End form
	print $cgi->end_form(), "\n";
}
