# Selima Website Content Management System # Guestbook.pm: The guestbook-related subroutines. # Copyright (c) 2004-2018 imacat. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Author: imacat # First written: 2004-10-23 package Selima::Guestbook; use 5.008; use strict; use warnings; use base qw(Exporter); use vars qw(@EXPORT @EXPORT_OK); BEGIN { @EXPORT = qw(update_pageno); @EXPORT_OK = @EXPORT; # Prototype declaration sub update_pageno($$$;$); sub split_page(\@\%$;$); sub update_old_gbpageno($$;$); } use Selima::DataVars qw($DBH); use Selima::Format; # update_pageno: Update the guestbook page number sub update_pageno($$$;$) { local ($_, %_); my ($table, $page_size, $cols, $from, $sql, $sth, $count, $row, $len, $where); my (@ents, %sizes, %orig, %pagenos, $startno, $startat, $commit); ($table, $page_size, $cols, $from) = @_; # If we should begin and commit here $commit = $DBH->{"AutoCommit"}; $DBH->begin_work if $commit; # Update the current page number # Check the page to start from undef $startno; undef $startat; $from = fmttime $from if defined $from; if (defined $from) { $sql = "SELECT pageno FROM $table WHERE NOT hid AND created<'$from'" . " ORDER BY created DESC LIMIT 1;\n"; $sth = $DBH->prepare($sql); $sth->execute; undef $from if $sth->rows != 1; } if (defined $from) { $startno = ${$sth->fetch}[0]; undef $sth; $sql = "SELECT created FROM $table WHERE NOT hid AND pageno=$startno" . " ORDER BY created LIMIT 1;\n"; $sth = $DBH->prepare($sql); $sth->execute; $startat = ${$sth->fetch}[0]; } @_ = qw(); push @_, "created>='" . fmttime($startat) . "'" if defined $startat; $where = (@_ > 0)? " WHERE " . join(" AND ", @_): ""; # Ge the size of everyhing $len = join " + ", map "CASE WHEN $_ IS NULL THEN 0 ELSE char_length($_) END", @$cols; $len = "CASE WHEN hid THEN 0 ELSE $len END AS len"; $sql = "SELECT sn, $len, pageno FROM $table $where ORDER BY created;\n"; $sth = $DBH->prepare($sql); $sth->execute; $count = $sth->rows; for ($_ = 0, @ents = qw(), %sizes = qw(), %orig = qw(); $_ < $count; $_++) { $row = $sth->fetchrow_hashref; push @ents, $$row{"sn"}; $sizes{$$row{"sn"}} = $$row{"len"}; $orig{$$row{"sn"}} = $$row{"pageno"}; } # Split page %pagenos = split_page @ents, %sizes, $page_size, $startno; # Update it foreach (@ents) { next if $orig{$_} == $pagenos{$_}; $sql = "UPDATE $table SET pageno=$pagenos{$_} WHERE sn=$_;\n"; $DBH->gdo($sql); } # Update the old page number # No need to update it anymore. Let it go. $DBH->commit if $commit; return; } # split_page: Split page according to the entry sizes sub split_page(\@\%$;$) { local ($_, %_); my ($ents, $sizes, $page_size, $startno, $cursize, %pagenos); ($ents, $sizes, $page_size, $startno) = @_; $startno = 1 if !defined $startno; # Bounce for nothing return if scalar @$ents == 0; # Split pages %pagenos = qw(); $pagenos{$$ents[0]} = $startno; $cursize = $$sizes{$$ents[0]}; for ($_ = 1; $_ < scalar(@$ents); $_++) { my ($hi_gap, $lo_gap); # We need at least one record if ($cursize == 0) { $pagenos{$$ents[$_]} = $pagenos{$$ents[$_-1]}; $cursize += $$sizes{$$ents[$_]}; next; # Not oversized yet } elsif ($cursize + $$sizes{$$ents[$_]} < $page_size) { $pagenos{$$ents[$_]} = $pagenos{$$ents[$_-1]}; $cursize += $$sizes{$$ents[$_]}; next; } $hi_gap = $cursize + $$sizes{$$ents[$_]} - $page_size; $lo_gap = $page_size - $cursize; # The upper boundary is closer, and the page is not too oversized if ($hi_gap < $lo_gap && $hi_gap <= $page_size / 4) { $pagenos{$$ents[$_]} = $pagenos{$$ents[$_-1]}; $cursize += $$sizes{$$ents[$_]}; # Or, we prefer the lower, since the page is not oversized } else { $pagenos{$$ents[$_]} = $pagenos{$$ents[$_-1]} + 1; $cursize = $$sizes{$$ents[$_]}; } } return %pagenos; } # update_old_gbpageno: Update the old guestbook page number sub update_old_gbpageno($$;$) { local ($_, %_); my ($table, $page_size, $from, $sql, $sth, $count, $row, $len, $cond); my (@ents, %sizes, %orig, %pagenos, $startno, $startat, $commit); ($table, $page_size, $from) = @_; # If we should begin and commit here $commit = $DBH->{"AutoCommit"}; $DBH->begin_work if $commit; # Update the old page number undef $startno; undef $startat; if (defined $from) { $sql = "SELECT oldpageno FROM $table WHERE NOT hid AND created<'$from'" . " ORDER BY created DESC LIMIT 1;\n"; $sth = $DBH->prepare($sql); $sth->execute; $startno = ${$sth->fetch}[0]; undef $sth; $sql = "SELECT created FROM $table WHERE NOT hid AND oldpageno=$startno" . " ORDER BY created LIMIT 1;\n"; $sth = $DBH->prepare($sql); $sth->execute; $startat = ${$sth->fetch}[0]; } @_ = qw(); push @_, "NOT hid"; push @_, "created>='$startat'" if defined $startat; $cond = join " AND ", @_; # Ge the size of everyhing $len = "${table}_oldlen(created, ip, host, name, identity, location, email, url, message, updated, updatedby) AS len"; # Tavern Backalley has a special table structure $len = "garbage_oldlen(created, ip, host, message, updated, updatedby) AS len" if $table eq "garbage"; $sql = "SELECT sn, $len, oldpageno FROM $table WHERE $cond ORDER BY created;\n"; $sth = $DBH->prepare($sql); $sth->execute; $count = $sth->rows; for ($_ = 0, @ents = qw(), %sizes = qw(), %orig = qw(); $_ < $count; $_++) { $row = $sth->fetchrow_hashref; push @ents, $$row{"sn"}; $sizes{$$row{"sn"}} = $$row{"len"}; $orig{$$row{"sn"}} = $$row{"oldpageno"}; } # Split page # Old page size is always 10240 %pagenos = split_page @ents, %sizes, 10240, $startno; # Update it foreach (@ents) { next if $orig{$_} == $pagenos{$_}; $sql = "UPDATE $table SET oldpageno=$pagenos{$_} WHERE sn=$_;\n"; $DBH->gdo($sql); } $DBH->commit if $commit; return; } return 1;