# Copyright (C) 2006 Affelio Inc.
#
# 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.

package Diary;
{
    use strict;
    use lib("../../extlib");
    use lib("../../lib");
    use Diary::L10N;
    use DBI;
    use AffelioApp;
    use HTML::Template;
    use CGI;
    use Cwd;
    use Affelio::misc::Debug qw( debug_print);
    use Affelio::misc::WebInput;
    use Affelio::exception::SystemException;
    use Error qw(:try);

    ######################################################################
    #Constructor
    ######################################################################
    sub new{
	my $class = shift;
	my %param = @_;
	
	debug_print("Diary::new: start.");
	
	my $cgi = new CGI;
	my $wi = new Affelio::misc::WebInput();
	###########################
	#Load afap
	###########################
	my $afap = new AffelioApp(ConfigDir => Cwd::getcwd(), cgi => $cgi);
	debug_print("Diary::new: AFAP loaded.");
	
	my $self = {
		    cgi => $cgi,
		    afap => $afap,
		    wi => $wi};

	$self->{afap}  = $afap;
	$self->{uname} = $afap->{af}->{site__username};
	$self->{datadir} = $afap->get_userdata_dir();
	$self->{dbh}   = $afap->get_userdata_dbh;
	$self->{entry_table} = "diary_$afap->{install_name}_entries";
	$self->{pref_table} = "diary_$afap->{install_name}_pref";
	$self->{category_table} = "diary_$afap->{install_name}_categories";
	$self->{comment_table} = "diary_$afap->{install_name}_comments";
	$self->{tb_table} = "diary_$afap->{install_name}_tb";
	$self->{recent_entries_no} = 10;
	$self->{header_title} = 'Affelio Diary';
	$self->{header_show} = 0;

	###########################
	#Locale init
	###########################
	$self->{lh} = Diary::L10N->get_handle(($afap->get_site_info("locale"),$afap->get_site_info("locale")));
	###########################
	
	my $DBConfig = Config::Tiny->new();
	$DBConfig = Config::Tiny->read("$self->{afap}->{af}->{site__user_dir}/db.cfg");
	$self->{dbtype} = $DBConfig->{db}->{type};
	
	my @rets;
	if ($self->{dbtype} eq 'mysql') {
	    @rets = getall($self, "SHOW TABLES like '$self->{entry_table}'");
	}
	else { # SQLite
	    @rets = getall($self, "SELECT * FROM sqlite_master WHERE type = 'table' AND name = '$self->{entry_table}'");
	}
	
	# entries
	my $pkey_modifier = $self->{dbtype} eq 'mysql' ? " AUTO_INCREMENT PRIMARY KEY " : " PRIMARY KEY ";
	create_table($self, $self->{entry_table},
		     "CREATE TABLE $self->{entry_table} (
		id		INTEGER $pkey_modifier ,
		title		TEXT,
		contents	TEXT,
		year		INTEGER,
		month		INTEGER,
		day		INTEGER,
		timestamp	INTEGER,
c_id INTEGER,
user TEXT,
uri TEXT,
pwd TEXT,
draft INTEGER
	)");

	# categories
	my $pkey_modifier = $self->{dbtype} eq 'mysql' ? " AUTO_INCREMENT PRIMARY KEY " : " PRIMARY KEY ";
	if (create_table($self, $self->{category_table},
	"CREATE TABLE $self->{category_table} (
		id		INTEGER $pkey_modifier ,
		category	TEXT
	)")){
	    $self->{dbh}->do("INSERT INTO $self->{category_table} (id, category) VALUES (NULL, 'etc')");
	}

	# comments
	create_table($self, $self->{comment_table},
	"CREATE TABLE $self->{comment_table} (
		id		INTEGER,
		user		TEXT,
		comment		TEXT,
		timestamp	INTEGER
	)");

	# trackbacks
	create_table($self, $self->{tb_table},
	"CREATE TABLE $self->{tb_table} (
		id		INTEGER,
		title		TEXT,
		url		TEXT,
		excerpt		TEXT,
		blog_name	TEXT,
		timestamp	INTEGER
	)");

	#general setting table
	if (create_table($self, $self->{pref_table},
	"CREATE TABLE $self->{pref_table} (
		key TEXT, value TEXT)")){
	$self->{dbh}->do("INSERT INTO $self->{pref_table} (key, value) VALUES ('email', '$self->{afap}->{af}->{user__email1}')");
	$self->{dbh}->do("INSERT INTO $self->{pref_table} (key, value) VALUES ('max_entries', '10000')");
	$self->{dbh}->do("INSERT INTO $self->{pref_table} (key, value) VALUES ('max_comments', '256')");
	$self->{dbh}->do("INSERT INTO $self->{pref_table} (key, value) VALUES ('max_commentlen', '1000')");
	$self->{dbh}->do("INSERT INTO $self->{pref_table} (key, value) VALUES ('max_textlen', '10000')");
	$self->{dbh}->do("INSERT INTO $self->{pref_table} (key, value) VALUES ('show_author', '0')");
	$self->{dbh}->do("INSERT INTO $self->{pref_table} (key, value) VALUES ('image_size', '300')");
	}
	bless $self, $class;
	debug_print("Diary::new: end.");
	return $self;
    }


    ######################################################################
    #run
    ######################################################################
    sub run{
	my $self = shift;
	my $afap = $self->{afap};
	my $cgi = $self->{cgi};
	my $wi = $self->{wi};
	
	my %handlers = ("top","Top",
			"show_diary", "ShowDiary",
			"show_image", "ShowImage",
			"show_tb", "ShowTb",
			"write_diary", "WriteDiary",
			"write_comment", "WriteComment",
			"read_rdf", "ReadRdf");

	###########################
	#Check DF_access
	###########################
	unless ($afap->check_access("DF_access")) {
	    $self->accessErrorExit('Access Denied. You don\'t have permission to this application.');
	}   

	$self->{year} = $wi->PTN_num($cgi->url_param("year"));
	$self->{month} = $wi->PTN_num($cgi->url_param("month"));
	$self->{day} = $wi->PTN_num($cgi->url_param("day"));
	$self->{id} = $wi->PTN_num($cgi->url_param("id"));
	$self->{header_title} = "Diary";
	$self->{afid} = $afap->get_owner_info("afid");
	$self->{nickname} = $afap->get_owner_info("nickname");

	##############################################################
	#prep vars
	##############################################################
	my %output_data = ("tmpl_path", Cwd::getcwd()."/templates/");

	##############################################################
	#Model invocation
	##############################################################
	my $mode = $wi->PTN_mode($cgi->param("mode"));
	if ($mode eq "") {$mode="top";}
	debug_print("Diary::run: mode=$mode");

	if ($mode eq "write_diary" && !$afap->check_access("write_diary")) {
	    $self->accessErrorExit("Access Error.");
	}
	if ($mode eq "top" && (-f "$self->{datadir}url")) {
	    $self->{header_show} = 1;
	    $mode="read_rdf";
	}

	my $classname = "Diary::" . $handlers{$mode};
	debug_print("Diary::run: handler= $classname");
	eval "use $classname";
	if($@){
	    throw Affelio::exception::SystemException("Could not load handler [$mode]");
	}
	debug_print("Diary::run: handler function loaded.");
	
	my $ret="";
	try{
	    debug_print("Diary::run: handler function..... ");
	    handler($cgi, $self, \%output_data);
	    debug_print("Diary::run: handler function done.");
	}catch Error with{
	    my $e = shift;
	    $output_data{"err_msg"} .= $e->stacktrace;
	};
	##############################################################
	#Output View
	##############################################################
	my $tmpl = new HTML::Template(filename => $output_data{tmpl_file},
				      die_on_bad_params => 0);
	$tmpl->param(%output_data);
	
	print "Content-type: text/html; charset=UTF-8\n";
	print "Pragma: no-cache", "\n\n";
	print $self->{afap}->get_HTML_header("Diary");
	print $self->translate_templateL10N($self->get_HTML_header);
	print $self->translate_templateL10N($tmpl->output);
#	print $afap->get_HTML_footer;
	print $self->get_HTML_footer;
	
}

##############################################
# get_HTML_header
##############################################

sub get_HTML_header {
    my $self = shift;
    debug_print("Diary::get_HTML_headr start.");
    return "" if ($self->{header_show} == 1) ;
    my $header = ""; 

    #Initiate Template
    my $tmpl_menu = new HTML::Template(filename => "./templates/menu.tmpl",
				  die_on_bad_params => 0);
    # calender
    my $calender;
    if($self->{year} and $self->{month}) {
	$calender = $self->getCalender($self->{year}, $self->{month});
    }
    elsif($self->{id}) {
	my @date = $self->getall("SELECT year, month FROM $self->{entry_table} WHERE id = ".$self->{id});
	$calender = $self->getCalender($date[0]->{year}, $date[0]->{month});
    }
    else {
	$calender = $self->getCalender;
    }
    
    # archives
    my @archives = $self->getall("SELECT DISTINCT year, month, count(id) as count_acvs FROM $self->{entry_table} GROUP BY year, month ORDER BY year,month DESC LIMIT 10");
    if ($#archives >= 0) {
	shift @archives unless $archives[0]->{year};
	$tmpl_menu->param(ARCHIVES => \@archives);
    }

    # categories
    my @categories = $self->getall("select distinct c_id, count($self->{entry_table}.id) as count_cid, category from $self->{entry_table} inner join $self->{category_table} on $self->{entry_table}.c_id = $self->{category_table}.id group by $self->{entry_table}.c_id ORDER BY c_id DESC");

    if ($#categories >= 0) {
	$tmpl_menu->param(CATEGORIES => \@categories);
    }
    
    # recent entries
    my @entries = $self->getall("SELECT id, title FROM $self->{entry_table} ORDER BY timestamp DESC LIMIT 10");
    if ($#entries >= 0) {
	$tmpl_menu->param(RECENT_ENTRIES => \@entries);
    }
    
    # recent comments
    my @comments = $self->getall("SELECT $self->{comment_table}.id, title, $self->{comment_table}.user FROM $self->{entry_table}, $self->{comment_table} WHERE $self->{entry_table}.id = $self->{comment_table}.id ORDER BY $self->{comment_table}.timestamp DESC LIMIT 10");
    if ($#comments >= 0) {
	$tmpl_menu->param(RECENT_COMMENTS => \@comments);
    }
    
    # recent trackbacks
    my @trackbacks = $self->getall("SELECT id, blog_name, title FROM $self->{tb_table} ORDER BY timestamp DESC LIMIT 10");
    if ($#trackbacks >= 0) {
	$tmpl_menu->param(RECENT_TRACKBACKS => \@trackbacks);
    }
    
    $tmpl_menu->param(CALENDER => $calender);
    
    if ($self->{afap}->check_access('write_diary')) {
	$tmpl_menu->param(EDITABLE => 1);
	unless (eval { require XML::Parser; }) {
	    $tmpl_menu->param(NO_PARSER => 1);
	}
    }
    
    $header .= $tmpl_menu->output;
    
    $self->{header_show} = 1;
    debug_print("Diary::get_HTML_headr end.");
    
    return $header;
}

##############################################
# get_HTML_footer
##############################################

sub get_HTML_footer {
	my $self = shift;
	my $tmpl = HTML::Template->new(filename => "./templates/footer.tmpl");
	return $tmpl->output().$self->{afap}->get_HTML_footer;
}

##############################################
# getCalender
##############################################

sub getCalender {
	my $self = shift;
	my $year = $self->escape(shift, 'int');
	my $mon  = $self->escape(shift, 'int');
	
	unless ($mon) { my $d; ($d, $d, $d, $d, $mon, $year) = localtime(time); $year += 1900; $mon += 1;  }
	my @weeks = $self->weekly_days($year, $mon);
	
	my $tmpl = HTML::Template->new(filename => "./templates/calender.tmpl");

	my $last_mon = $mon - 1;
	my $next_mon = $mon + 1;
	my $lastyear = $year;
	my $nextyear = $year;
	if ($mon == 1) {
		$last_mon = 12;
		$lastyear = $year - 1;
	}
	elsif ($mon == 12) {
		$next_mon = 1;
		$nextyear = $year + 1;
	}
	
	$tmpl->param(
		YEAR => $year, MONTH => $mon,
		LAST_MON => $last_mon, NEXT_MON => $next_mon,
		LASTYEAR => $lastyear, NEXTYEAR => $nextyear,
	);

	my @days = $self->getall("SELECT day FROM $self->{entry_table} WHERE year = $year AND month = $mon");
	my @daytable = (0 .. 31);
	$daytable[0] = '';
	foreach(@days) {
		$daytable[$_->{day}] = "<a href=\"index.cgi?year=$year&month=$mon&day=$_->{day}\"><b>$_->{day}</b></a>";
	}

	my @weeks_param;
	foreach(@weeks) {
		if($_->[0] == '' and $_->[6] == '') { next; }
		push @weeks_param,
		{
			SUN => $daytable[$_->[0]],
			MON => $daytable[$_->[1]],
			TUE => $daytable[$_->[2]],
			WED => $daytable[$_->[3]],
			THU => $daytable[$_->[4]],
			FRI => $daytable[$_->[5]],
			SAT => $daytable[$_->[6]],
		};
	}

	$tmpl->param(WEEKS => \@weeks_param);

	return $tmpl->output;
}

sub weekly_days {
	my ($self, $year, $mon) = @_;
	my @weeks;
	my @mday  = (31,31,28,31,30,31,30,31,31,30,31,30,31);
	if (($year % 4 == 0) and ($year % 100) or ($year % 400 == 0)) { $mday[2]  = 29 };
	
	my $lastday = $mday[$mon];
	@mday = (1 .. $mday[$mon]);
	if($mon < 3){ $mon += 12; $year--; }
	my $first_day = ($year+int($year/4)-int($year/100)+int($year/400)+int((13*$mon+8)/5)+1)% 7;

	my $day = 1;
	for my $week (0 .. 7) {
		my @days;
		for(my $i = 0; $i < 7; $i++) {
			push @days, 
			(($week == 0 and $i < $first_day) or ($day > $lastday)) ? 
			'' : $day++;
		}
		$weeks[$week] = \@days;
	}
	
	return @weeks;
}

##############################################
# getCommentsNo
##############################################

sub getCommentsNo {
	my $self = shift;
	my $id   = $self->escape(shift, 'int');
	return $self->getColumn("SELECT count(*) FROM $self->{comment_table} WHERE id = $id");
}

##############################################
# existsEntry
##############################################

sub existsEntry {
	my $self = shift;
	my $id   = $self->escape(shift, 'int');
	return $self->getColumn("SELECT COUNT(*) FROM $self->{entry_table} WHERE id = $id") > 0;
}

##############################################
# Internal functions
##############################################
sub getall {
	my ($self, $query) = @_;
	
	my $sth = $self->{dbh}->prepare($query);
	$sth->execute;
	
	my @ret;
	while(my $row = $sth->fetchrow_hashref) {
	    push @ret, $row;
	}
	$sth->finish;
	
	return @ret;
}

sub getColumn {
	my ($self, $query) = @_;
	my $sth = $self->{dbh}->prepare($query);
	$sth->execute;
	my $num;
	$sth->bind_columns(undef, \$num);
	$sth->fetch;
	$sth->finish;
	if($num) {
		return $num;
	}
	else {
		return 0;
	}
}

sub create_table {
	my ($self, $table_name, $sql) = @_;
	
	my @rets;
	my $ret=0;
	if ($self->{dbtype} eq 'mysql') {
		@rets = getall($self, "SHOW TABLES like '$table_name'");
	}
	else { # SQLite
		@rets = getall($self, "SELECT * FROM sqlite_master WHERE type = 'table' AND name = '$table_name'");
	}

	if ($#rets < 0) {
		$self->{dbh}->do($sql);
		$ret=1;
	}
	return $ret;
}

sub escape {
	my ($self, $str, $type) = @_;
	
	if ($type eq 'int') {
		return int($str);
	}
	else {
		$str =~ s/[\t\a]//g;
		$str =~ s/&/&amp;/g;
		$str =~ s/"/&quot;/g;
		$str =~ s/'/&#039;/g;
		$str =~ s/</&lt;/g;
		$str =~ s/>/&gt;/g;
		$str =~ s/&lt;(\/?)(a|p|i|b|big|strong|small|em|u|blockquote|br)&gt;/<$1$2>/ig;
		$str =~ s/&lt;image=&quot;([A-Za-z0-9\-\_]*\.(jpg|png|gif|bmp|jpeg))&quot;&gt;/<image="$1">/ig;

		$str =~ s/&lt;a +href=(&quot;)?(s?https?:\/\/[-_.!~*'()a-zA-Z0-9;\/?:\@&=+\$,%#]+) *(&quot;)? *&gt;/<a href="$2">/ig;
		$str =~ s/&quot;"/"/g;
#		$str =~ s/(\r\n|\r|\n)/<br \/>/g;
		$str =~ s/\x0D\x0A/<br \/>/g;
		$str =~ s/\x0D/<br \/>/g;
		$str =~ s/\x0A/<br \/>/g;

		while ($str =~ /(<(a|p|i|b|big|strong|small|em|u|blockquote)\b(?:(?!<\/\2>).)*(?:<\2>|$))/sigx) {
			$self->errorExit("Error: You may mistype a tag or forget to close it.");
		}
	}

	return $str;
}

sub br2n {
    my ($self, $str) = @_;
    $str =~ s/(<|&lt;)br \/(>|&gt;)/\x0D\x0A/ig;
    $str =~ s/(<|&lt;)br(>|&gt;)/\x0D\x0A/ig;
    return $str;
}
##############################################
# errorExit
##############################################

sub errorExit {
	my $self = shift;
	my $msg  = $self->escape(shift);
	
	my $tmpl = new HTML::Template(filename => './templates/error.tmpl');
	$tmpl->param(MESSAGE => $msg);

	unless ($self->{header_show}) {
		print "Content-type: text/html; charset=UTF-8\n\n";
		print $self->translate_templateL10N($self->{afap}->get_HTML_header($self->{header_title}));
	}
	print $self->translate_templateL10N($tmpl->output);
	print $self->translate_templateL10N($self->{afap}->get_HTML_footer);
	exit;
}

##############################################
# accessErrorExit
##############################################

sub accessErrorExit {
    my $self = shift;
    my $msg  = $self->escape(shift);
    my $affelio_id = $self->{afap}->get_visitor_info("afid");
    my $visitor_type=$self->{afap}->get_visitor_info("type");
    
    $visitor_type="pb" if ($visitor_type eq "");
    
    my $tmpl = new HTML::Template(filename => $self->{afap}->{app__fs_root}."/templates/access_error.tmpl");
    $tmpl->param(
	AFID	=> $affelio_id,
	VIS_TYPE=> $visitor_type,
	MESSAGE	=> $msg,
	);

    unless ($self->{header_show}) {
	print "Content-type: text/html; charset=UTF-8\n\n";
	print $self->translate_templateL10N($self->{afap}->get_HTML_header($self->{header_title}));
    }
    print $self->translate_templateL10N($tmpl->output);
    print $self->translate_templateL10N($self->{afap}->get_HTML_footer);
    exit;
}


############################################################################
#L10N added by slash
############################################################################
sub translate_templateL10N{
    my $af=shift;
    my $mesg = shift;
    
    my $tag_body ="";
    my $text_value="";
    my $param_value="";
    
    while( $mesg =~ /<AF_M ([^>]+)>/ ){
	$tag_body = $1;
	
	$tag_body =~ /text(\s*)=(\s*)["']([^"']*)["'](\s*)param(\s*)=(\s*)["']([^"']*)["']/;
	$text_value=$3;
	$param_value=$7;
	if($text_value eq ""){
	    $tag_body =~ /text(\s*)=(\s*)["']([^"']*)["']/;
	    $text_value=$3;
	}
	
	my $sbst = $af->{lh}->maketext($text_value, $param_value);
	
#	debug_print("Diary::translate tag_body = [$tag_body]\n");
#	debug_print("Diary::translate \t text=[$text_value]\n");
#	debug_print("Diary::translate \t param=[$param_value]\n");
#	debug_print("Diary::translate \t sbst=[$sbst]\n");
	
	$mesg =~ s/\Q<AF_M $tag_body>\E/$sbst/g;
    }
    return($mesg);
}

##############################################
# checkAccess
##############################################

sub checkAccess {
    my ($self, $page_name) = @_;
    unless ($self->{afap}->check_access($page_name)) {
	$self->accessErrorExit("You have no permittion on this page");
    }
}

##############################################
# getRDFURL
##############################################

sub getRDFURL {
	my $self = shift;
	if (-f "$self->{datadir}url") {
		local (*IN);
		open (IN, "$self->{datadir}url");
		my $rssfile = <IN>;
		$rssfile =~ s/[\r\n]//g;
		close(IN);
		return $rssfile;
	}
	return undef;
}

##############################################
# addTrackback
##############################################

sub addTrackback {
	my $self      = shift;
	my $id        = $self->escape(shift, 'int');
	my $title     = $self->escape(shift);
	my $url       = $self->escape(shift);
	my $excerpt   = $self->escape(shift);
	my $blog_name = $self->escape(shift);
	my $timestamp = $self->escape(shift, 'int');
	$self->{dbh}->do("INSERT INTO $self->{tb_table} VALUES($id, '$title', '$url', '$excerpt', '$blog_name', $timestamp)");
}

}
1;
