=head1 NAME

MT::Plugins::CommentIcon - MoveableType plugin to add user icons to comment posts

=cut

package MT::Plugins::CommentIcon;

=head1 SYNOPSIS

Once installed in your MovableType plugins directory, a new template tag,
<$MTCommentAuthorIcon$>, will be available for use within the scope of a
<MTComments> tag.

=head1 REQUIRES

 XML::RSS
 XML::FOAF
 File::Spec
 LWP::Simple
 HTML::RSSAutodiscovery
 Image::Magick::Thumbnail

=head1 DESCRIPTION

When the <$MTCommentAuthorIcon$> tag is used within a <MTComments> tag,
and the comment in question has an associated URL field, MT::Plugins::CommentIcon
will attempt to discover a user icon for the comment. First, a FOAF file
containing a foaf:depiction is searched for at the specified URL. If found,
the referenced image is scaled and used as the icon. If no FOAF icon is found,
an RSS feed containing an Image attribute is searched at the specified URL.
If found, the image is scaled and used as the icon.

If an image was found, it is scaled so that its maximum dimension
(either height or width) in pixels is set to the constant
MT::Plugins::CommentIcon::THUMB_SIZE (defaults to 64 pixels).

The scaled icon is cached locally in the 'people' directory in the archive_path.
If a cached file is available, attempts to discover FOAF and RSS feeds are
bypassed in favor of the cached version.

=cut

use strict;
use warnings;
use MT::Template::Context;

use XML::RSS;
use XML::FOAF;
use File::Spec;
use LWP::Simple qw(get);
use HTML::RSSAutodiscovery;
use File::Temp qw(tempfile);
use Image::Magick::Thumbnail;

our $VERSION	= '1.001';

use constant THUMB_SIZE => 64;

MT::Template::Context->add_tag( CommentAuthorIcon => sub {
	my $ctx		= shift;
	my $args	= shift;
	my $comment	= $ctx->stash( 'comment' );
	my $url		= $comment->url();
	if ($url) {
		return geticonfrom( $url, $ctx );
	} else {
		return '';
	}
} );

sub geticonfrom {
	my $url		= shift;
	my $ctx		= shift;
	my $blog	= $ctx->stash( 'blog' );
	my $imagename	= $url;
	$imagename	=~ s#^[^:]+://##;
	$imagename	=~ tr#/#_#;
	my $imagefile	= File::Spec->catfile( $blog->archive_path, 'people', "${imagename}.jpg" );
	if (-r $imagefile) {
		my ($href, $title, $width, $height);
		my $img	= $blog->archive_url . "people/${imagename}.jpg";
                return img_html($img, $href, $width, $height, $title);
	} else {
                my $foaf    = new XML::FOAF ( new URI $url );
                if (ref($foaf)) {
                    my $person  = $foaf->person;
                    my $depict  = $person->depiction;
                    if ($depict) {
			my $thumbfile	= File::Spec->catfile( $blog->archive_path, 'people', $imagename . ".jpg" );
			my ($w, $h)	= make_thumbnail( $depict, $thumbfile );
			my $img	= $blog->archive_url . "people/${imagename}.jpg";
                        return img_html( $img, $person->weblog, $w, $h );
                    }
                }

		my $parser  = new HTML::RSSAutodiscovery;
		my $feeds	= eval { $parser->parse( $url ) };
		if ($@) {
			warn $@;
			return '';
		}
		if (ref($feeds) and scalar(@{$feeds})) {
			foreach my $feed (@{ $feeds }) {
				my $rssparser   = new XML::RSS;
				next unless ($feed->{'type'} eq 'application/rss+xml');
				my $feedurl = $feed->{'href'};
				unless ($feedurl =~ m(^[^:]+://|^/)) {
					$url    =~ s#/[^/]*$#/#;
					$feedurl    = $url . $feedurl;
				}
				my $rss	= get( $feedurl ) || next;
				eval { $rssparser->parse( $rss ) or next; };
				if ($@) {
					warn $@;
					return '';
				}
				if (exists $rssparser->{'image'}) {
					my $href    = $rssparser->{'image'}{ 'link' };
					my $imgurl  = $rssparser->{'image'}{ 'url' };
					my $width   = $rssparser->{'image'}{ 'width' };
					my $height  = $rssparser->{'image'}{ 'height' };
					my $title   = $rssparser->{'image'}{ 'title' };
					next unless ($imgurl);
				
					# add image to cache
					my $thumbfile	= File::Spec->catfile( $blog->archive_path, 'people', $imagename . ".jpg" );
					($width,$height)	= make_thumbnail( $imgurl, $thumbfile ) or next;
					my $img	= $blog->archive_url . "people/${imagename}.jpg";
				
                                        return img_html($img, $href, $width, $height, $title);
				}
			}
		}
	}
	return '';
}

sub make_thumbnail {
	my $img		= shift;
	my $thumbfile	= shift;
	my ($fh,$imgfile)	= tempfile();
	unless (-w $imgfile) {
		warn $!;
		return 0;
	}

	my ($thumb,$x,$y);
	eval {
		# open( my $fh, ">$imagefile" ) or do { warn $!; return 0 };
		my $imgdata	= get( $img );
		print { $fh } $imgdata;
		close( $fh );
		my $src	= new Image::Magick || die $!;
		$src->Read( $imgfile );
		($thumb,$x,$y)	= Image::Magick::Thumbnail::create($src,THUMB_SIZE);
		$thumb->[0]->Write($thumbfile);
	};
	unlink( $imgfile );
	if ($@) {
		warn $@;
		return 0;
	} else {
		return ($x, $y);
	}
}

sub img_html {
    my ($img, $href, $width, $height, $title)   = @_;
    $title	||= '';
    my $html    = qq|<div class="commenticon">|
                . ($href ? qq|<a href="${href}"| : '')
                . (($href && $title) ? qq| title="${title}"| : '')
                . ($href ? qq|>| : '')
                . qq|<img src="${img}" |
                . qq|alt="${title}" |
                . ($width ? qq|width="${width}" | : '')
                . ($height ? qq|height="${height}" | : '')
                . qq| />|
                . ($href ? qq|</a>| : '')
                . qq|</div>|;
    return $html;
}

1;

__END__

=head1 SEE ALSO

 L<XML::FOAF>
 L<HTML::RSSAutodiscovery>

=head1 KNOWN BUGS

None

=head1 TODO

The commenticon idea can be extended to trackbacks as well. In practice, images from
a FOAF profile make sense for a comment icon while images from an RSS feed make more
sense for a trackback icon. Implementing this *should* be a rather trivial matter
once I find time to do it (patches welcome ;).

=head1 LICENSE

Copyright (c) 2003 Gregory Williams. All rights reserved. This program is free software;
you can redistribute it and/or modify it under the same terms as Perl itself.

=head1 AUTHORS

 Gregory Williams <gwilliams@cpan.org>

=cut
