package Kwiki::EmailNotify;
# original by Alexander Goller
# modified to work with Kwiki 0.38 by Dave Holland
# TODO: actually use the email template
use strict;
use warnings;
use Kwiki::Plugin '-Base';
use Kwiki::Installer '-base';
use Email::Valid;
use Mail::Sender;
use DBI;
use DBD::SQLite;
use Data::Dumper;

our $VERSION = '0.02';

const class_id        => 'email_notify';
const class_title     => 'Email Notify';

sub register
{
	my $registry = shift;
	$registry->add(
		action => 'notify_add'
	);

	$registry->add(
		action => 'notify_remove'
	);

	$registry->add(
		widget   => 'notification_query',
		template => 'email_notify_query.html',
		show_for => 'display',
	);

	$registry->add(
		hook => 'page:store',
		post => 'check_notifications',
	);

}

sub check_notifications
{
	my $page = $self;
	my $hook = pop;
	my $message = "";
	$self = $page->hub->email_notify;
	my $meta_data = $self->hub->edit->pages->current->metadata;
	my $site_title = $self->hub->config->site_title;
	my $edited_by = $meta_data->{edit_by} || '(unknown name)';
	my $ipaddr = $ENV{'REMOTE_ADDR'} || '(unknown host)';
	my @recipients = ();
	map { push(@recipients, ${$_}[0]) } @{$self->get_recipients()};
	$self->log("check_notifications: ".join(" ",@recipients));

	# oh, this is horrible
	my $p = $self->pages->current;
	$p->load;
	my $prev = $p->revision_numbers->[0] - 1;
	my $archive = $self->hub->archive;

	# I would dearly love to use Algorithm::Diff instead of
	# calling a shell, but it's way too much hassle.
	open(O,">/tmp/diff.$$.this") or die "open1 failed: $!\n";
	print O $page->content;
	close(O);
	open(O,">/tmp/diff.$$.prev") or die "open2 failed: $!\n";
	print O $archive->fetch($page, $prev);
	close(O);
	$message = `diff -u /tmp/diff.$$.prev /tmp/diff.$$.this`;
	unlink "/tmp/diff.$$.prev", "/tmp/diff.$$.this";

	$self->send_mail($edited_by, $ipaddr, $message, \@recipients);
	$self->log("mail sent");
}

sub get_recipients
{
	$self->database_connect();
#	my $get_recipients_query = <<ENDSQL;
#		select email_address from email
#          where email_id =
#			( select email_id from mapping where page_id = 
#				(select page_id from pages where page_name = ? )
#            )
#ENDSQL
#;
	# this SQL is less efficient but (a) it works, and (b) on a small scale we don't care about efficiency!
	my $get_recipients_query = "select distinct email_address from email,mapping,pages where page_name = ? and pages.page_id=mapping.page_id and mapping.email_id=email.email_id";
	my $sth = $self->{dbh}->prepare( $get_recipients_query );
	$sth->bind_param(1, $self->cgi->page_name);
	my $rv = $sth->execute();
	$self->log("executed get_recipients_query");
	return($sth->fetchall_arrayref);
}

sub send_mail
{
	use Mail::Sender;
	my $edited_by = shift();
	my $ipaddr = shift();
	my $message = shift();
	my $sender = Mail::Sender->new( {
		from    => 'kwiki@localhost',
	});
	my $rec = shift(); # that's an array reference
	my $to;
	while($to=pop(@{$rec})) {
	$sender->MailMsg( {
		smtp    => 'localhost',
		to      => $to,
		subject => 'Wiki edit: ' . $self->cgi->page_name,
		msg     => 'Wiki page ' . $self->cgi->page_name .
			" was edited by " . $edited_by . " from " .
			$ipaddr . "\n\n" . $message . "\n",
	});
	}
}

# when adding your email address to the notification list
sub notify_add
{
	my $page_id       = $self->cgi->page_name;
	my $notifications = $self->hub->cookie->jar->{notify} || {};

	$self->log("notify_add: id=".$page_id.", notifications=".$notifications);
	$notifications->{$page_id} = 1;
	$self->hub->cookie->jar->{notify} = $notifications;
	$self->database_add();
	return "$page_id added";
}

# when removing your email address from the notification list
sub notify_remove
{
	my $page_id       = $self->cgi->page_name;
	my $notifications = $self->hub->cookie->jar->{notify} || {};

	$self->log("notify_remove: id=".$page_id.", notifications=".$notifications);
	delete $notifications->{$page_id};
	$self->hub->cookie->jar->{notify} = $notifications;
	$self->database_delete();
	return "$page_id deleted";
}

sub database_delete
{
	$self->database_connect;
	$self->{dbh}->begin_work;
	if ( $self->delete_mapping() )
	{
		$self->{dbh}->commit;
		$self->log("database delete commited");
	}
	else
	{
		$self->{dbh}->rollback;
		$self->log("database delete rollback done");
	}
}

sub database_connect
{
	$self->{dbh}  = DBI->connect("dbi:SQLite:dbname=/var/cache/apache/emailnotify.db");
	$self->check_tables;
}

sub check_tables
{
	my @mandatory = (
		['table', 'email'],
		['table', 'pages'],
        ['table', 'mapping'],
		['trigger', 'delete_email_pages'],
	);
	foreach my $tab (@mandatory)
	{
		my $sth = $self->{dbh}->prepare( qq{
			select type, name from sqlite_master
              where type = ?
				and name = ?
		});
		my $rv = $sth->execute( $tab->[0], $tab->[1] );
		my $ar = $sth->fetchall_arrayref();
		if (@{$ar} < 1)
		{
			$self->create_table($tab->[0], $tab->[1]);
		}
		$sth->finish();
	}
}

sub database_add
{
	$self->database_connect;
	$self->{dbh}->begin_work;
	if ( $self->add_email() && $self->add_page() && $self->create_mapping() )
	{
		$self->{dbh}->commit;
		$self->log("database add commited");
	}
	else
	{
		$self->{dbh}->rollback;
		$self->log("database add rollback done");
	}
}

sub create_mapping
{
	my $sth = $self->{dbh}->prepare( qq{
		insert into mapping values (
          (select page_id from pages where page_name = ? ),
          (select email_id from email where email_address = ? )
		)
	});
	$sth->bind_param(1, $self->cgi->page_name);
	$sth->bind_param(2, $self->hub->cookie->jar->{preferences}->{email});
	my $rv = $sth->execute();
	$self->log("mapping created") if ($rv);
	return($rv);
}

sub delete_mapping
{
	my $sth = $self->{dbh}->prepare( qq{
		delete from mapping where 
			page_id = (select page_id from pages where page_name = ? )
		  and
            email_id = (select email_id from email where email_address = ? )
	});
	$sth->bind_param(1, $self->cgi->page_name);
	$sth->bind_param(2, $self->hub->cookie->jar->{preferences}->{email});
	my $rv = $sth->execute();
	$self->log("mapping deleted") if ($rv);
	return($rv);
}

sub add_email
{
	my $sth = $self->{dbh}->prepare( qq{
		insert or ignore into email (email_address) values (?)
	});
	$sth->bind_param(1, $self->hub->cookie->jar->{preferences}->{email} );
	my $rv = $sth->execute();
	$self->log("inserted email") if ($rv);
	return($rv);
}

sub add_page
{
	my $sth = $self->{dbh}->prepare( qq{
		insert or ignore into pages (page_name) values (?)
	});
	$sth->bind_param(1, $self->cgi->page_name );
	my $rv = $sth->execute();
	$self->log("inserted page") if ($rv);
	return($rv);
}

sub log
{
	io("/tmp/log.txt")->append(
      scalar(localtime(time)) . ": " .
      caller() . ": " . 
      __LINE__ . ": " .
      shift() . "\n"
	);
}

sub create_table
{
	my ($type, $tab) = @_;

	my $create_pages_query =<<ENDSQL;
    CREATE TABLE pages (
		page_id    integer unique primary key,	
		page_name  varchar(255) unique
	)
ENDSQL
;
	my $create_email_query =<<ENDSQL;
	CREATE TABLE email (
		email_id      integer unique primary key,
		email_address varchar(255) unique
	)
ENDSQL
;

	my $create_mapping_query =<<ENDSQL;
	CREATE TABLE mapping (
		page_id    integer not null,
		email_id   integer not null
	)
ENDSQL
;
	my $create_delete_email_pages_query =<<ENDSQL;
		CREATE TRIGGER delete_email_pages AFTER DELETE ON mapping
		BEGIN
			delete from pages 
              where
                page_id = old.page_id and 
                  ( select count(page_id) from mapping 
                      where page_id = old.page_id 
                  ) = 0;
			delete from email 
              where email_id = old.email_id and 
                ( select count(email_id) from mapping
                    where email_id = old.email_id
                ) = 0;
		END;
ENDSQL
;

	my $sth = $self->{dbh}->prepare( eval "\$create_" . $tab . "_query" );
	my $rv  = $sth->execute();
	$self->log("$type created: $tab, status: $rv");
	return($rv);
}

sub create_trigger
{
	my $create_trigger_query =<<ENDSQL;
		CREATE TRIGGER delete_email_pages AFTER DELETE ON mapping
		BEGIN
			delete from pages 
              where
                page_id = old.page_id and 
                  ( select count(page_id) from mapping 
                      where page_id = old.page_id 
                  ) = 0;
			delete from email 
              where email_id = old.email_id and 
                ( select count(email_id) from mapping
                    where email_id = old.email_id
                ) = 0;
		END;
ENDSQL
;
	my $sth = $self->{dbh}->prepare( $create_trigger_query );
	my $rv  = $sth->execute();
	$self->log("trigger creation status: $rv");
	return($rv);
}

1;

__DATA__
__template/tt2/email_notify_email.txt__

Dear kwiki user,

[% script_name %]?[% page_id %] has changed.

your kwiki
__template/tt2/email_notify_query.html__
<!-- BEGIN email_notify_query.html -->
<script type="text/javascript">
function notification_change(self)
{
	iframe = document.getElementsByTagName("iframe")[0];
	if (self.checked)
	{
		iframe.src = 
                 '[% script_name %]?action=notify_add&page_name=[% page_name %]';
	}
	else
	{
		iframe.src =
                 '[% script_name %]?action=notify_remove&page_name=[% page_name %]';
	}
}
</script>
<form>
<input type="checkbox" name="notify" onchange="notification_change(this)" [% IF hub.cookie.jar.notify.$page_name %]checked[% END %] />Notify me?
<iframe height="0" width="0" frameborder="0"></iframe>
</form>
<!-- END email_notify.html -->
