Konstrukt

 view release on metacpan or  search on metacpan

lib/Konstrukt/Plugin/wiki/backend/image/DBI.pm  view on Meta::CPAN

#TODO: update CONFIGURATION docs and quote the right sql tables

=head1 NAME

Konstrukt::Plugin::wiki::backend::image::DBI - Image backend driver for storage
inside a database that can be accessed through DBI.

=head1 SYNOPSIS
	
	my $image_backend = use_plugin 'Konstrukt::Plugin::wiki::backend::image::DBI' or die;
	$image_backend->do_stuff(); #see the methods description
	
=head1 DESCRIPTION

This image backend implements the storage in a database that will be accessed
through perl DBI.

Actually this one was implemented using a MySQL database. But as the queries
don't use special MySQL-functions it is very likely that it will run on other
databases without modification.

This one is very similar to L<Konstrukt::Plugin::wiki::backend::file::DBI> but adds
some image-specific funtionality.

=head1 CONFIGURATION

You have to create the tables C<wiki_image>, C<wiki_image_content> and C<wiki_image_description>,
which will be used to store the data.
You may turn on the C<install> setting (see L<Konstrukt::Handler/CONFIGURATION>)
or use the C<KonstruktBackendInitialization.pl> script to accomplish this task.

Furtheron you have to define those settings to use this backend:

	#backend
	wiki/backend_type       DBI
	wiki/backend/DBI/source dbi:mysql:database:host
	wiki/backend/DBI/user   user
	wiki/backend/DBI/pass   pass

If no database settings are set the defaults from L<Konstrukt::DBI/CONFIGURATION> will be used.

=cut

package Konstrukt::Plugin::wiki::backend::image::DBI;

use strict;
use warnings;

use base 'Konstrukt::Plugin::wiki::backend::image';

use Image::Magick;

=head1 METHODS

=head2 new

=head2 init

Initialization of this class. Loads the settings.

=cut
sub init {
	my $self = shift;
	
	#initialization of the base class
	$self->SUPER::init(@_);
	
	my $db_source = $Konstrukt::Settings->get('wiki/backend/DBI/source');
	my $db_user   = $Konstrukt::Settings->get('wiki/backend/DBI/user');
	my $db_pass   = $Konstrukt::Settings->get('wiki/backend/DBI/pass');
	
	$self->{db_settings} = [$db_source, $db_user, $db_pass];
	
	return 1;
}
#= /init

=head2 install

Installs the backend (e.g. create tables).

B<Parameters:>

none

=cut
sub install {
	my $self = shift;
	return (
		$Konstrukt::Lib->plugin_dbi_install_helper($self->{db_settings}) and
		$self->SUPER::install(@_)
	);
}
# /install

=head2 exists

See L<Konstrukt::Plugin::wiki::backend::image/exists>

=cut
sub exists {
	my ($self, $title, $revision) = @_;
	
	#get connection
	my $dbh = $Konstrukt::DBI->get_connection(@{$self->{db_settings}}) or return undef;
	
	#quoting
	$title    = $dbh->quote($self->normalize_link($title));
	$revision = $dbh->quote($revision) if defined $revision;
	
	#query

lib/Konstrukt/Plugin/wiki/backend/image/DBI.pm  view on Meta::CPAN

			$query = "SELECT width, height, mimetype FROM wiki_image_content WHERE title = $title AND original = 1 AND revision = ".$dbh->quote($entry->{content_revision});
			($entry->{width}, $entry->{height}, $entry->{mimetype}) = ($dbh->selectrow_array($query));
		}

		return $entry;
	} else {
		return undef;
	}
}
#= /get

=head2 get_content

See L<Konstrukt::Plugin::wiki::backend::image/get_content>

=cut
sub get_content {
	my ($self, $title, $revision, $width) = @_;
	
	#get connection
	my $dbh = $Konstrukt::DBI->get_connection(@{$self->{db_settings}}) or return undef;
	
	$title = $self->normalize_link($title);
	my $qtitle = $dbh->quote($title);
	
	#get latest revision, if not specified
	$revision = $self->revision($title) unless defined $revision;
	my $qrevision = $dbh->quote($revision);
	
	#get current content revision
	my $query = "SELECT content_revision FROM wiki_image WHERE title = $qtitle AND revision = $qrevision";
	my ($content_revision) = ($dbh->selectrow_array($query));
	
	#is there any content yet?
	if (defined $content_revision and $content_revision > 0) {
		#get original width
		my $qcontent_revision = $dbh->quote($content_revision);
		my $query = "SELECT width FROM wiki_image_content WHERE title = $qtitle AND revision = $qcontent_revision AND original = 1";
		my ($original_width) = ($dbh->selectrow_array($query));
		
		#adjust requested width. width must not be < 0 or > original width
		$width = $original_width if not defined $width or $width < 0 or $width > $original_width;
		my $qwidth = $dbh->quote($width);
		my $qoriginal_width = $dbh->quote($original_width);
		 
		#get (resized) image
		$query = "SELECT content, mimetype, width, height, original FROM wiki_image_content WHERE title = $qtitle AND revision = $qcontent_revision AND width = $qwidth";
		my $result = $dbh->selectall_arrayref($query, { Columns=>{} });
		
		#does a (resized) image with that resolution exist?
		if (@{$result}) {
			return $result->[0];
		} else {
			#get original image, resize the image, save the resized image
			$query = "SELECT content, mimetype, width, height, original FROM wiki_image_content WHERE title = $qtitle AND revision = $qcontent_revision AND width = $qoriginal_width";
			$result = $dbh->selectall_arrayref($query, { Columns=>{} });
			if (@{$result}) {
				#resize the image
				my $image = $result->[0];
				#load image
				my $im = Image::Magick->new();
				$im->BlobToImage($image->{content});
				#resize
				my ($old_width, $old_height) = $im->Get('width', 'height');
				my $aspect = $old_width / $old_height;
				my $height = $width / $aspect;
				$im->Resize(width => $width, height => $height, blur => 0.8);
				$image->{content} = $im->ImageToBlob(quality => $Konstrukt::Settings->get("wiki/image_quality"));
				if (defined $image->{content}) {
					$image->{width} = $width;
					$image->{height} = $height;
					
					#store image
					my $query = "INSERT INTO wiki_image_content(title, revision, content, mimetype, width, height) values (?, ?, ?, ?, ?, ?)";	
					my $sth = $dbh->prepare($query);
					my $rv = $sth->execute($title, $content_revision, $image->{content}, $image->{mimetype} || '', $image->{width}, $image->{height});
					
					#return the resized image
					return $image;
				} else {
					#error
					$Konstrukt::Debug->error_message("Could not resize image") if Konstrukt::Debug::ERROR;
					return undef;
				}
			} else {
				$Konstrukt::Debug->debug_message("Could not get original image") if Konstrukt::Debug::DEBUG;
				return undef;
			}
		}
	} else {
		$Konstrukt::Debug->debug_message("Could not determine content revision") if Konstrukt::Debug::DEBUG;
		return undef;
	}
}
#= /get_content

=head2 store

See L<Konstrukt::Plugin::wiki::backend::image/store>

=cut
sub store {
	my ($self, $title, $store_description, $description, $store_content, $content, $mimetype, $author, $host) = @_;
	
	#get connection
	my $dbh = $Konstrukt::DBI->get_connection(@{$self->{db_settings}}) or return undef;
	
	#we need at least a title
	if (defined $title) {
		#normalize title
		$title = $self->normalize_link($title);
		
		#the description and the content are stored in separate tables
		my ($description_revision, $content_revision);
		
		#get latest image info
		my $latest = $self->get_info($title);
		
		#modify description?
		if (defined $store_description) {
			#new description?
			if (defined $description) {
				#get current entry and check if the new entry differs
				my $current = $self->get_info($title);
				if (not defined $current->{description} or $current->{description} ne $description) {
					#create entry
					my $query = "INSERT INTO wiki_image_description(title, description) values (?, ?);";	
					my $sth = $dbh->prepare($query);
					my $rv = $sth->execute($self->normalize_link($title), $description);
					#get new revision
					$query = "SELECT MAX(revision) AS revision FROM wiki_image_description WHERE title = " . $dbh->quote($title) . " LIMIT 1";
					$description_revision = ($dbh->selectrow_array($query));
				} else {
					#don't store new description
					$store_description = undef;
				}
			} else {
				#reset description
				$description_revision = 0;
			}
		} else {
			#use latest description revision
			$description_revision = defined $latest ? $latest->{description_revision} : 0;
		}
		
		#modify image content?
		if (defined $store_content) {
			#new image content?
			if (defined $content) {
				#get current entry and check if the new entry differs
				my $current = $self->get_content($title);
				if (not defined $current->{content} or $current->{content} ne $content) {
					#load image
					my $im = Image::Magick->new();
					$im->BlobToImage($content);
					#successfully loaded?
					if (defined $im->[0]) {
						#get mimetype if not defined
						$mimetype = $im->Get('MIME') unless defined $mimetype;
						#get size
						my($width, $height) = $im->Get('width', 'height');
						#create entry
						my $query = "INSERT INTO wiki_image_content(title, content, width, height, mimetype, original) values (?, ?, ?, ?, ?, 1);";	
						my $sth = $dbh->prepare($query);
						my $rv = $sth->execute($title, $content, $width, $height, $mimetype);
						#get new revision
						$query = "SELECT MAX(revision) AS revision FROM wiki_image_content WHERE title = " . $dbh->quote($title) . " LIMIT 1";
						$content_revision = ($dbh->selectrow_array($query));
					} else {
						#invalid image
						return -2;
					}
				} else {
					#don't store new content
					$store_content = undef;
				}
			} else {
				#reset content
				$content_revision = 0;
			}
		} else {
			#use latest content revision
			$content_revision = defined $latest ? $latest->{content_revision} : 0;
		}
		
		#don't create new revision if neither a description nor a image content is specified and an entry already exists
		my $exists = $self->revision($title);
		if (not $exists or defined $store_description or defined $store_content) {
			#create entry
			my $query = "INSERT INTO wiki_image(title, description_revision, content_revision, author, date, host) values (?, ?, ?, ?, NOW(), ?);";	
			my $sth = $dbh->prepare($query);
			return $sth->execute($title, $description_revision, $content_revision, $author, $host);
		} else {
			#no change
			return -1;
		}
	} else {
		$Konstrukt::Debug->error_message("Cannot store image. No title specified!") if Konstrukt::Debug::ERROR;
		return undef;
	}
}
#= /store

=head2 restore

See L<Konstrukt::Plugin::wiki::backend::image/restore_description>

=cut
sub restore {
	my ($self, $title, $revision, $restore_description, $restore_content, $author, $host) = @_;
	
	#get connection
	my $dbh = $Konstrukt::DBI->get_connection(@{$self->{db_settings}}) or return undef;



( run in 2.801 seconds using v1.01-cache-2.11-cpan-0bb4e1dffa6 )