The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl -w
# This code is a part of Slash, and is released under the GPL.
# Copyright 1997-2001 by Open Source Development Network. See README
# and COPYING for more information, or see http://slashcode.com/.
# $Id: gallery.pl,v 1.5 2001/12/31 18:31:16 pudge Exp $

use strict;
use Apache::File;
use File::Basename;
use File::Path;
use File::Spec::Functions ':ALL';
use Imager;
use Image::Info qw(image_info dim);

use Slash 2.003;	# require Slash 2.3.x
use Slash::Constants qw(:web);
use Slash::Display;
use Slash::Utility;
use vars qw($VERSION);

($VERSION) = ' $Revision: 1.5 $ ' =~ /\$Revision:\s+([^\s]+)/;

sub main {
	my $gallery   = getObject('Slash::Gallery');
	my $slashdb   = getCurrentDB();
	my $constants = getCurrentStatic();
	my $user      = getCurrentUser();
	my $form      = getCurrentForm();

	my $is_admin = $user->{state}{gallery_admin} =
		($user->{seclev} >= $constants->{gallery_admin_seclev})
		|| $user->{gallery_admin};

# 	return unless $user->{state}{gallery_admin};	# for dev

	my %ops = (
		render_pictures	=> [ $is_admin,	\&render_pictures	],	# ?
		add_pictures	=> [ $is_admin,	\&add_pictures		],	# ?
		list_pictures	=> [ $is_admin,	\&list_pictures		],
		save_picture	=> [ $is_admin,	\&save_picture		],	# 1
		find_unassigned_pictures
				=> [ $is_admin,	\&find_unassigned_pictures ],

		list_groups	=> [ 1,		\&list_groups		],
		edit_group	=> [ $is_admin,	\&edit_group		],
		save_group	=> [ $is_admin,	\&save_group		],	# 1

		display		=> [ 1,		\&display		],
		view		=> [ 1,		\&view			],	# 2
		list		=> [ 1,		\&list			],

		default		=> [ 1,		\&list_groups		]
	);

	# prepare op to proper value if bad value given
	my $op = $form->{op};
	if (!$op || !exists $ops{$op} || !$ops{$op}[ALLOWED]) {
		$op = 'default';
	}

	if ($op eq 'view') {
		my($content, $type, $date) = $ops{$op}[FUNCTION]->(
			$gallery, $constants, $user, $form, $slashdb
		);

		if ($content) {
			$type ||= 'image/jpeg';

			my $r = Apache->request;
			$r->header_out('Cache-control', 'private');
			$r->content_type($type);
			$r->set_last_modified($date) if $date;
			$r->status(200);
			$r->send_http_header;
			$r->rflush;
			$r->print($content);
			$r->status(200);
			return 1;

		} else {  # not handled, fall through
			$op = 'default';
		}
	}

	header(getData('header'));
	print getData('galleryhead');

	$ops{$op}[FUNCTION]->($gallery, $constants, $user, $form, $slashdb);

	print getData('galleryfoot');
	footer();
}

sub edit_group {
	my($gallery, $constants, $user, $form, $slashdb) = @_;

	my $group_id = $form->{group_id};
	my $group;
	if ($group_id) {
		my $groups = $gallery->get_groups({ id => $group_id });
		$group  = $groups->{$group_id};
	} else {
		$group = {};
	}

	my $users = join ', ', keys %{$group->{users}};
	$slashdb->createFormkey('gallery');

	slashDisplay('edit_group', {
		group	=> $group,
		users	=> $users,
	});
}

sub save_group {
	my($gallery, $constants, $user, $form, $slashdb) = @_;

	my $group_id = $form->{group_id};
	my %data = (
		name		=> $form->{name},
		description	=> $form->{description},
		public		=> $form->{public},
	);

	if (_validFormkey()) {
		if ($group_id) {
			$gallery->set_group($group_id, \%data);
		} elsif ($form->{name}) {
			$group_id = $gallery->create_group(\%data);
		}

		if ($group_id) {
			my @users = split /\s*,\s*/, $form->{users};
			$gallery->set_users_group($group_id, \@users);
		}
	}

	list_groups(@_);
}

sub list_groups {
	my($gallery, $constants, $user, $form, $slashdb) = @_;

	my $groups = $gallery->get_groups;

	slashDisplay('list_groups', {
		groups		=> $groups,
		is_admin	=> $user->{state}{gallery_admin},
	});
}

sub add_pictures {
	my($gallery, $constants, $user, $form, $slashdb) = @_;

	$gallery->add_pictures_from_disk;

	list_pictures(@_);
}


sub render_pictures {
	my($gallery, $constants, $user, $form, $slashdb) = @_;

	my $sizes = $gallery->get_sizes();
	my $pictures = $gallery->get_pictures;

	for my $size (reverse sort keys %$sizes) {
		$form->{size} = $size;
		for my $pic_id (sort { $a <=> $b } keys %$pictures) {
			$form->{pic_id} = $pic_id;
			print STDERR "Rendering $pic_id : $size\n";
			view(@_);
		}
	}
	list_groups(@_);
}

sub find_unassigned_pictures {
	my($gallery, $constants, $user, $form, $slashdb) = @_;

	my $pictures = $gallery->get_unassigned_pictures;

	slashDisplay('list_pictures', { pictures => $pictures });
}

sub list_pictures {
	my($gallery, $constants, $user, $form, $slashdb) = @_;

	my $pictures = $gallery->get_pictures;

	slashDisplay('list_pictures', { pictures => $pictures });
}

sub save_picture {
	my($gallery, $constants, $user, $form, $slashdb) = @_;

	my $pic_id = $form->{pic_id};
	if ($pic_id && _validFormkey()) {
		my $pictures = $gallery->get_pictures({ id => $pic_id });
		my $picture  = $pictures->{$pic_id};
		$form->{rotate} ||= 0;

		if ($picture->{rotate} != $form->{rotate}) {
			my $sizes = $gallery->get_sizes();
			for my $size (reverse sort keys %$sizes) {
				$form->{size} = $size;
				$form->{pic_id} = $pic_id;
				print STDERR "Rendering $pic_id : $size\n";
				view(@_);
			}
		}

		$gallery->set_picture($pic_id, {
			name		=> $form->{name},
			uid		=> $form->{uid},
			date		=> $form->{date},
			description	=> $form->{description},
			rotate		=> $form->{rotate},
		});	

		my @groups = grep $_, map { s/^(\d+).*$/$1/s; $_ }
			@{$form->{groups_multiple}}
			if $form->{groups_multiple};
		$gallery->set_groups_picture($pic_id, \@groups);
	}

	display(@_);
}

sub list {
	my($gallery, $constants, $user, $form, $slashdb) = @_;

	my $pictures;
	my $group = $form->{group_id};

	if ($user->{state}{gallery_admin} && !$group) {
		# get all
		$pictures = $gallery->get_pictures;
	} else {
		if (!allow_user(@_, { group_id => $group })) {
			return list_groups(@_);
		}

		$pictures = $gallery->get_pictures({ group => $group });
	}

	my $sizes = $gallery->get_sizes();
	slashDisplay('list', {
		pictures	=> $pictures,
		sizes		=> $sizes,
		group		=> $gallery->get_groups({ id => $group })->{$group},
	});
}

sub display {
	my($gallery, $constants, $user, $form, $slashdb) = @_;

	my $pic_id = $form->{pic_id};
	my $pictures = $gallery->get_pictures({ id => $pic_id });
	my $picture  = $pictures->{$pic_id};
	unless ($picture && allow_user(@_, { picture => $picture })) {
		return list_groups(@_);
	}

	$picture->{info} = image_info(catfile(
		$constants->{datadir}, 'gallery', 'full',
		$picture->{filename}
	));

	my $sizes = $gallery->get_sizes();
	$slashdb->createFormkey('gallery');

	slashDisplay('display', {
		picture 	=> $picture,
		sizes		=> $sizes,
		pic_id		=> $pic_id,
		is_admin	=> $user->{state}{gallery_admin},
		gallery		=> $gallery,
	});
}

sub view {
	my($gallery, $constants, $user, $form, $slashdb) = @_;

	return if  $ENV{HTTP_REFERER}
		&& $ENV{HTTP_REFERER} !~ /^(?:https?:)?\Q$constants->{rootdir}\E/;

	my($file, $full, $content, $type, $X, $Y, $qual);
	my $noreturn = $form->{op} eq 'render_pictures' || $form->{op} eq 'save_picture';
	my $size = $form->{size};
	my $sizes = $gallery->get_sizes();

	if (exists $sizes->{$size}) {
		($X, $Y, $qual) = @{$sizes->{$size}}{qw[width height jpegquality]};
	} else {
		$size = 'full';
	}

	# only max_gallery_viewings per time period unless admin
	unless ($user->{state}{gallery_admin} || $noreturn) {
		if (!$sizes->{$size}{id} || $sizes->{$size}{id} > 2) { # greater than small
			$slashdb->createFormkey('galleryview');
			return unless _validFormkey('galleryview', 'max_reads_check');
		}
	}

	my $pic_id = $form->{pic_id};
	my $pictures = $gallery->get_pictures({ id => $pic_id });
	my $picture  = $pictures->{$pic_id};

	unless ($picture && allow_user(@_, { picture => $picture })) {
		return;
	}

	$file = catfile($constants->{datadir}, 'gallery',
		$size, $picture->{filename}
	);
	$full = catfile($constants->{datadir}, 'gallery',
		'full', $picture->{filename}
	);

	if ($noreturn && defined $form->{rotate}) {
		unlink $file unless $file eq $full;
	}

	# if it exists, and is a scaled image and the original
	# has not been modified since the scaled was created,
	# then just show the image from disk ...
	if (-e $file && ($file eq $full || -M $file < -M $full)) {
		unless ($noreturn) {
			my $fh = gensym();
			open $fh, "<" . $file or errorLog("Can't open $file: $!"), return;
			{	local $/;
				$content = <$fh>;
			}
			close $fh;

			my $info = image_info($file);
			$type = $info->{file_media_type};
		}

	# ... else create scaled image on the fly, saving it to disk
	# and returning the image data
	} elsif ($X) {
		my $img = new Imager;
		$img->open(file => $full);

		if (($noreturn && defined $form->{rotate}) || $picture->{rotate}) {
			my $rotate = defined $form->{rotate} ? $form->{rotate} : $picture->{rotate};
			my $rotated = $img->rotate(right => (
				$rotate == 1 ? 90 :
				$rotate == 2 ? 180 :
				$rotate == 3 ? 270 : 0
			));
			$img = $rotated;
		}

		if ($img->getwidth < $img->getheight) {
			($X, $Y) = ($Y, $X);
		}

		my $scaled = $img->scale(xpixels => $X);
		mkpath(dirname($file), 0, 0775);
		$scaled->write(file => $file, jpegquality => $qual);
		$scaled->write(data => \$content, type => 'jpeg', jpegquality => $qual)
			unless $noreturn;
	}

# if we want to include last-modified date for some reason ... ?
# 	return($content, $type, (stat $file)[9]) unless $noreturn;
	return($content, $type) unless $noreturn;
}

sub allow_user {
	my($gallery, $constants, $user, $form, $slashdb, $data) = @_;
	return 1 if $user->{state}{gallery_admin};

	# check if group ID is available for user
	if ($data->{group_id}) {
		my $id = $data->{group_id};

		$user->{gallery_groups} ||= $gallery->get_groups_user($user->{uid});
		return 1 if exists $user->{gallery_groups}{$id};

		my $group = $gallery->get_groups({ id => $id });
		return 1 if $group->{$id}{public};

	# check if user ID is assigned to group
	} elsif ($data->{group}) {
		return 1 if $data->{group}{public};
		return 1 if exists $data->{group}{users}{$user->{uid}};
		return 1 if exists $data->{group}{users}{$constants->{anonymous_coward_uid}};

	# check if picture is in group available to user
	} elsif ($data->{picture}) {
		my $seen;
		$user->{gallery_groups} ||= $gallery->get_groups_user($user->{uid});
		for (keys %{$data->{picture}{groups}}) {
			return 1 if exists $user->{gallery_groups}{$_};
		}

		for (keys %{$data->{picture}{groups}}) {
			my $group = $gallery->get_groups({ id => $_ });
			return 1 if $group->{$_}{public};
		}
	}

	return 0;
}

sub _validFormkey {
	my $error;
	my $formname = shift;
	# this is a hack, think more on it, OK for now -- pudge
	Slash::Utility::Anchor::getSectionColors();

	for (@_, qw(valid_check formkey_check)) {
		last if formkeyHandler($_, $formname, 0, \$error);
	}

	if ($error) {
		return 0;
	} else {
		# why does anyone care the length?
		getCurrentDB()->updateFormkey(0, 1);
		return 1;
	}
}

# get array from rational object (for templates)
sub Image::TIFF::Rational::list { [ $_[0]->[0], $_[0]->[1] ] }

# get a nicer rational number
sub Image::TIFF::Rational::smart {
	my($a, $b) = @{$_[0]}[0, 1];
	my $z = $a/$b;

	if ($z < 1) {
		return sprintf "1/%d", $b/$a;
	} else {
		return sprintf "%.1f", $z;
	}
}

createEnvironment();
main();


1;