1278 lines
48 KiB
Perl
1278 lines
48 KiB
Perl
# ==================================================================
|
|
# Gossamer Links - enhanced directory management system
|
|
#
|
|
# Website : http://gossamer-threads.com/
|
|
# Support : http://gossamer-threads.com/scripts/support/
|
|
# CVS Info : 087,071,086,086,085
|
|
# Revision : $Id: Build.pm,v 1.136 2008/06/25 19:23:48 brewt Exp $
|
|
#
|
|
# Copyright (c) 2001 Gossamer Threads Inc. All Rights Reserved.
|
|
# Redistribution in part or in whole strictly prohibited. Please
|
|
# see LICENSE file for full details.
|
|
# ==================================================================
|
|
|
|
package Links::Build;
|
|
# ==================================================================
|
|
use strict;
|
|
use Links qw/:objects :payment/;
|
|
use Links::SiteHTML;
|
|
use GT::AutoLoader;
|
|
use vars qw/$GRAND_TOTAL/;
|
|
|
|
sub build {
|
|
# -----------------------------------------------------------------
|
|
# Returns a specified template parsed.
|
|
#
|
|
my $build = shift;
|
|
my $code = exists $Links::Build::{"build_$build"} ? *{$Links::Build::{"build_$build"}}{CODE} : _compile("build_$build");
|
|
defined $code or die "Invalid method: build_$build called.";
|
|
|
|
$PLG->dispatch("build_$build", $code, @_);
|
|
}
|
|
|
|
$COMPILE{build_home} = __LINE__ . <<'END_OF_SUB';
|
|
sub build_home {
|
|
# ------------------------------------------------------------------
|
|
# Generate the home page.
|
|
#
|
|
$GRAND_TOTAL ||= _grand_total();
|
|
|
|
my $category = $DB->table('Category');
|
|
$category->select_options("ORDER BY $CFG->{build_category_sort}") if $CFG->{build_category_sort};
|
|
my $sth = $category->select({ FatherID => 0 });
|
|
|
|
my $root = [];
|
|
while (my $cat = $sth->fetchrow_hashref) {
|
|
$cat->{URL} = "$CFG->{build_root_url}/" . $category->as_url($cat->{Full_Name}) . "/" . ($CFG->{build_index_include} ? $CFG->{build_index} : '');
|
|
push @$root, $cat;
|
|
}
|
|
|
|
# Plugins can use the build_category_loop hook to change the category
|
|
# results before they are returned to the template.
|
|
$PLG->dispatch(build_category_loop => sub { } => $root);
|
|
|
|
my $print_cat;
|
|
my $cat_list = sub {
|
|
return $print_cat if defined $print_cat;
|
|
return $print_cat = Links::SiteHTML::display('print_cat', [{}, @$root])
|
|
};
|
|
|
|
return Links::SiteHTML::display('home', { category => $cat_list, category_loop => $root, grand_total => $GRAND_TOTAL });
|
|
}
|
|
END_OF_SUB
|
|
|
|
$COMPILE{build_new} = __LINE__ . <<'END_OF_SUB';
|
|
sub build_new {
|
|
# ------------------------------------------------------------------
|
|
# Generate the what's new page. Takes as options:
|
|
#
|
|
# mh => new links per page
|
|
# nh => page number
|
|
# sb => field to sort by
|
|
# so => ascending or descending order
|
|
# gb => 1/0 (group by category name).
|
|
#
|
|
my $opts = shift;
|
|
if (ref $opts ne 'HASH') {
|
|
Links::debug("Invalid option passed to build_new: $opts") if $Links::DEBUG;
|
|
return;
|
|
}
|
|
if (! exists $opts->{sb}) {
|
|
my $sort = $CFG->{build_sort_order_new} || '';
|
|
$opts->{sb} = $sort;
|
|
$opts->{so} = '';
|
|
}
|
|
$opts->{mh} = exists $opts->{mh} ? $opts->{mh} : 1000;
|
|
$opts->{nh} = exists $opts->{nh} ? $opts->{nh} : 1;
|
|
$opts->{sb} = exists $opts->{sb} ? $opts->{sb} : 'Add_Date';
|
|
$opts->{gb} = exists $opts->{gb} ? $opts->{gb} : $CFG->{build_new_gb};
|
|
|
|
my $cat_db = $DB->table('Category');
|
|
my $link_db = $DB->table('Links');
|
|
$GRAND_TOTAL ||= _grand_total();
|
|
|
|
my %filter = (
|
|
isValidated => 'Yes',
|
|
isNew => 'Yes',
|
|
mh => $opts->{mh},
|
|
nh => $opts->{nh},
|
|
sb => $opts->{sb},
|
|
so => $opts->{so},
|
|
ww => 1
|
|
);
|
|
|
|
# Should not select unpaid and expired links if payment is enabled
|
|
$filter{ExpiryDate} = '>=' . time if $CFG->{payment}->{enabled};
|
|
|
|
my $sth = $link_db->query_sth(\%filter);
|
|
my $total = $link_db->hits;
|
|
my $cur_date = '';
|
|
Links::init_date();
|
|
|
|
my (@date_order, %grouped_output);
|
|
|
|
# Get the links.
|
|
my $results = $sth->fetchall_hashref;
|
|
$link_db->add_reviews($results);
|
|
|
|
# Get the category names.
|
|
my $catlink = $DB->table('CatLinks','Category');
|
|
my %names = $catlink->select('LinkID', 'Full_Name', { LinkID => [map $_->{ID}, @$results] })->fetchall_list;
|
|
|
|
for my $link (@$results) {
|
|
my $date = $link->{Add_Date};
|
|
my $long_date = GT::Date::date_transform($link->{Add_Date}, GT::Date::FORMAT_DATE, $CFG->{date_long_format});
|
|
$date ne $cur_date and push @date_order, $long_date;
|
|
if ($opts->{gb}) {
|
|
push @{$grouped_output{$long_date}->{$names{$link->{ID}}}}, Links::SiteHTML::tags('link', $link);
|
|
}
|
|
else {
|
|
push @{$grouped_output{$long_date}->{'none'}}, Links::SiteHTML::tags('link', $link);
|
|
|
|
}
|
|
$cur_date = $date;
|
|
}
|
|
my @dates;
|
|
foreach my $date (@date_order) {
|
|
my @links;
|
|
if ($opts->{gb}) {
|
|
foreach my $cat (sort keys %{$grouped_output{$date}}) {
|
|
$grouped_output{$date}->{$cat}->[0]->{title_linked} = sub { build('title_linked', { name => $cat, complete => 1 }) };
|
|
$grouped_output{$date}->{$cat}->[0]->{title_loop} = build('title', $cat);
|
|
push @links, @{$grouped_output{$date}->{$cat}};
|
|
}
|
|
}
|
|
else {
|
|
push @links, @{$grouped_output{$date}->{'none'}};
|
|
}
|
|
push @dates, { new_date => $date, links => \@links };
|
|
}
|
|
|
|
my $ret;
|
|
my $output = sub {
|
|
return $ret if defined $ret;
|
|
$ret = '';
|
|
for my $date (@date_order) {
|
|
$ret .= "</blockquote></p>" if $ret;
|
|
$ret .= "<p><b>$date</b><blockquote>";
|
|
my @links;
|
|
if ($opts->{gb}) {
|
|
for my $cat (sort keys %{$grouped_output{$date}}) {
|
|
my $title = build('title_linked', { name => $cat, complete => 1 });
|
|
$ret .= $title;
|
|
$ret .= join("", map { Links::SiteHTML::display('link', $_) } @{$grouped_output{$date}->{$cat}});
|
|
}
|
|
}
|
|
else {
|
|
$ret .= join("", map { Links::SiteHTML::display('link', $_) } @{$grouped_output{$date}->{'none'}});
|
|
}
|
|
}
|
|
$ret .= "</blockquote></p>";
|
|
return $ret;
|
|
};
|
|
|
|
my $new = Links::language('LINKS_NEW');
|
|
return Links::SiteHTML::display(new => {
|
|
total => $total,
|
|
grand_total => $GRAND_TOTAL,
|
|
link_results => $output,
|
|
link_results_loop => $results,
|
|
main_title_loop => build('title', $new),
|
|
title => sub { build('title_unlinked', $new) },
|
|
main_title_linked => sub { build('title_linked', $new) },
|
|
title_linked_loop => \@dates
|
|
});
|
|
}
|
|
END_OF_SUB
|
|
|
|
$COMPILE{build_new_index} = __LINE__ . <<'END_OF_SUB';
|
|
sub build_new_index {
|
|
# ------------------------------------------------------------------
|
|
# Build a what's new index page grouped by long date.
|
|
#
|
|
Links::init_date();
|
|
|
|
require GT::SQL::Condition;
|
|
my $cond = GT::SQL::Condition->new(
|
|
isNew => '=' => 'Yes',
|
|
VIEWABLE
|
|
);
|
|
|
|
my $link_db = $DB->table('Links');
|
|
$link_db->select_options("GROUP BY Add_Date", "ORDER BY Add_Date DESC");
|
|
my $sth = $link_db->select(Add_Date => 'COUNT(*)' => $cond);
|
|
my $total = 0;
|
|
$GRAND_TOTAL ||= _grand_total();
|
|
|
|
my $results = '';
|
|
my @loop;
|
|
while (my ($date, $count) = $sth->fetchrow_array) {
|
|
my $long_date = GT::Date::date_transform($date, GT::Date::FORMAT_DATE, $CFG->{date_long_format});
|
|
$date =~ s/\s(.*)//;
|
|
$results .= qq|<li><a href="$CFG->{build_new_url}/$date$CFG->{build_extension}">$long_date</a> ($count)</li>|;
|
|
$total += $count;
|
|
push @loop, { date => $long_date, url => "$CFG->{build_new_url}/$date$CFG->{build_extension}", count => $count };
|
|
}
|
|
|
|
my $new = Links::language('LINKS_NEW');
|
|
return Links::SiteHTML::display(new => {
|
|
total => $total,
|
|
grand_total => $GRAND_TOTAL,
|
|
link_results => $results,
|
|
link_results_loop => \@loop,
|
|
main_title_loop => build('title', $new),
|
|
title => sub { build(title_unlinked => $new) },
|
|
main_title_linked => sub { build(title_linked => $new) },
|
|
new_index => 1
|
|
});
|
|
}
|
|
END_OF_SUB
|
|
|
|
$COMPILE{build_new_subpage} = __LINE__ . <<'END_OF_SUB';
|
|
sub build_new_subpage {
|
|
# -----------------------------------------------------------------------------
|
|
# Generate a what's new sub page. Takes a hash reference containing a date key
|
|
# and value.
|
|
#
|
|
Links::init_date();
|
|
|
|
my $opts = shift;
|
|
if (ref $opts ne 'HASH') {
|
|
Links::debug("Invalid argument to build_new_subpage: $opts") if $Links::DEBUG;
|
|
return;
|
|
}
|
|
my $date = $opts->{date};
|
|
if (!GT::Date::date_is_valid($date)) {
|
|
Links::debug("Invalid date passed to build_new_subpage: $date") if $Links::DEBUG;
|
|
return;
|
|
}
|
|
my $link_db = $DB->table('Links');
|
|
my $cat_db = $DB->table('Category');
|
|
my $mh = $opts->{mh};
|
|
my $nh = $opts->{nh};
|
|
$link_db->select_options("ORDER BY $CFG->{build_sort_order_new}") if $CFG->{build_sort_order_new};
|
|
if ($mh and $nh) {
|
|
my $offset = ($nh-1) * $mh;
|
|
$link_db->select_options("LIMIT $offset, $mh");
|
|
}
|
|
|
|
my $sth = $link_db->select({ isNew => 'Yes', Add_Date => $date }, VIEWABLE);
|
|
my $new_total = $link_db->hits;
|
|
my $long_date = GT::Date::date_transform($date, GT::Date::FORMAT_DATE, $CFG->{date_long_format});
|
|
my $total = $link_db->hits;
|
|
my $gb = $CFG->{build_new_gb};
|
|
$GRAND_TOTAL ||= _grand_total();
|
|
my $output = {};
|
|
my $out = '';
|
|
|
|
# Get the links.
|
|
my $results = $sth->fetchall_hashref;
|
|
$link_db->add_reviews($results);
|
|
my @link_results_loop;
|
|
@link_results_loop = map Links::SiteHTML::tags('link', $_) => @$results unless $gb;
|
|
|
|
# Get the category names.
|
|
my $catlink = $DB->table('CatLinks','Category');
|
|
my %names = $catlink->select('LinkID', 'Full_Name', { LinkID => [map $_->{ID}, @$results] })->fetchall_list;
|
|
if ($gb) {
|
|
for my $link (@$results) {
|
|
push @{$output->{$names{$link->{ID}}}}, Links::SiteHTML::tags('link', $link);
|
|
}
|
|
}
|
|
my ($toolbar, %paging);
|
|
if ($mh and ($total > $mh)) {
|
|
my $first_url = $CFG->{build_new_url} . "/" . $date . $CFG->{build_extension};
|
|
my $next_url = $CFG->{build_new_url} . "/" . $date . "_";
|
|
$toolbar = build('toolbar', { first_url => $first_url, next_url => $next_url, numlinks => $new_total, nh => $nh, mh => $mh });
|
|
my ($new_url) = $CFG->{build_new_url} =~ m|^\Q$CFG->{build_root_url}\E/(.+)|;
|
|
%paging = (
|
|
page => "$new_url/$date",
|
|
page_format => 2,
|
|
num_hits => $new_total,
|
|
max_hits => $mh,
|
|
current_page => $nh
|
|
);
|
|
}
|
|
if ($gb) {
|
|
foreach my $name (sort { lc $a cmp lc $b } keys %$output) {
|
|
$output->{$name}->[0]->{title_linked} = sub { build('title_linked', { name => $name, complete => 1 }) };
|
|
$output->{$name}->[0]->{title_loop} = build('title', $name);
|
|
push @link_results_loop, @{$output->{$name}};
|
|
}
|
|
}
|
|
my $links;
|
|
$out = sub {
|
|
return $links if defined $links;
|
|
$gb or return $links = join "", map { Links::SiteHTML::display(link => $_) } @$results;
|
|
for my $name (sort { lc $a cmp lc $b } keys %$output) {
|
|
my $title = build(title_linked => { name => $name, complete => 1 });
|
|
$links .= "<p>$title" . join "", map { Links::SiteHTML::display(link => $_) } @{$output->{$name}};
|
|
}
|
|
return $links;
|
|
};
|
|
|
|
my $new = Links::language('LINKS_NEW');
|
|
return Links::SiteHTML::display(new => {
|
|
total => $total,
|
|
grand_total => $GRAND_TOTAL,
|
|
link_results => $out,
|
|
link_results_loop => \@link_results_loop,
|
|
main_title_loop => build('title', "$new/$long_date"),
|
|
title => sub { build('title_unlinked', "$new/$long_date") },
|
|
main_title_linked => sub { build('title_linked', "$new/$long_date") },
|
|
next_span => $toolbar,
|
|
paging => \%paging
|
|
});
|
|
}
|
|
END_OF_SUB
|
|
|
|
$COMPILE{build_cool} = __LINE__ . <<'END_OF_SUB';
|
|
sub build_cool {
|
|
# ------------------------------------------------------------------
|
|
# Generate the what's cool page. Takes as options:
|
|
#
|
|
# mh => new links per page
|
|
# nh => page number
|
|
# sb => field to sort by
|
|
# so => ascending or descending order
|
|
# gb => 1/0 (group by category name).
|
|
#
|
|
my $opts = shift;
|
|
if (ref $opts ne 'HASH') {
|
|
Links::debug("Invalid option passed to build_cool: $opts") if $Links::DEBUG;
|
|
return;
|
|
}
|
|
if (! exists $opts->{sb}) {
|
|
$opts->{sb} = $CFG->{build_sort_order_cool} || '';
|
|
$opts->{so} = '';
|
|
}
|
|
$opts->{mh} ||= $CFG->{build_links_per_page} || 25;
|
|
$opts->{nh} ||= 1;
|
|
$opts->{gb} ||= $CFG->{build_cool_gb};
|
|
|
|
my $cat_db = $DB->table('Category');
|
|
my $link_db = $DB->table('Links');
|
|
$GRAND_TOTAL ||= _grand_total();
|
|
|
|
my %filter = (
|
|
isPopular => 'Yes',
|
|
isValidated => 'Yes',
|
|
mh => $opts->{mh},
|
|
nh => $opts->{nh},
|
|
sb => $opts->{sb},
|
|
so => $opts->{so},
|
|
ww => 1
|
|
);
|
|
|
|
# Should not select unpaid and expired links if payment is enabled
|
|
$filter{ExpiryDate} = '>=' . time if $CFG->{payment}->{enabled};
|
|
|
|
my $sth = $link_db->query_sth(%filter);
|
|
my $total = $link_db->hits;
|
|
my $sub_total = 0;
|
|
my $output = '';
|
|
my %grouped_output;
|
|
|
|
# Get the links.
|
|
my $results = $sth->fetchall_hashref;
|
|
$link_db->add_reviews($results);
|
|
my @link_results_loop;
|
|
@link_results_loop = map Links::SiteHTML::tags('link', $_) => @$results unless $opts->{gb};
|
|
|
|
# Get the category names.
|
|
my $catlink = $DB->table('CatLinks','Category');
|
|
my %names = $catlink->select(LinkID => Full_Name => { LinkID => [map $_->{ID}, @$results] })->fetchall_list;
|
|
for my $link (@$results) {
|
|
$opts->{gb} and push @{$grouped_output{$names{$link->{ID}}}}, Links::SiteHTML::tags('link', $link);
|
|
$sub_total++;
|
|
}
|
|
if ($opts->{gb}) {
|
|
foreach my $cat (sort { lc $a cmp lc $b } keys %grouped_output) {
|
|
$grouped_output{$cat}->[0]->{title_linked} = sub { build(title_linked => { name => $cat, complete => 1 }) };
|
|
$grouped_output{$cat}->[0]->{title_loop} = build('title', $cat);
|
|
push @link_results_loop, @{$grouped_output{$cat}};
|
|
}
|
|
}
|
|
my $links;
|
|
$output = sub {
|
|
return $links if defined $links;
|
|
$opts->{gb} or return $links = join '', map { Links::SiteHTML::display('link', $_) } @$results;
|
|
$links = '';
|
|
foreach my $cat (sort { lc $a cmp lc $b } keys %grouped_output) {
|
|
my $title = build('title_linked', { name => $cat, complete => 1 });
|
|
$links .= "<p>$title" . join '', map { Links::SiteHTML::display(link => $_) } @{$grouped_output{$cat}};
|
|
}
|
|
return $links;
|
|
};
|
|
my $percent = ($CFG->{build_pop_cutoff} < 1) ? $CFG->{build_pop_cutoff} * 100 . "%" : $CFG->{build_pop_cutoff};
|
|
|
|
my ($toolbar, %paging);
|
|
if ($total > $opts->{mh}) {
|
|
$toolbar = build(toolbar => { url => "$CFG->{build_cool_url}/", numlinks => $total, nh => $opts->{nh}, mh => $opts->{mh} });
|
|
my ($cool_url) = $CFG->{build_cool_url} =~ m|^\Q$CFG->{build_root_url}\E/(.+)|;
|
|
%paging = (
|
|
page => "$cool_url/",
|
|
page_format => 1,
|
|
num_hits => $total,
|
|
max_hits => $opts->{mh},
|
|
current_page => $opts->{nh}
|
|
);
|
|
}
|
|
|
|
my $cool = Links::language('LINKS_COOL');
|
|
return Links::SiteHTML::display(cool => {
|
|
total => $total,
|
|
grand_total => $GRAND_TOTAL,
|
|
link_results => $output,
|
|
main_title_loop => build('title', $cool),
|
|
title => sub { build(title_unlinked => $cool) },
|
|
main_title_linked => sub { build(title_linked => $cool) },
|
|
percent => $percent,
|
|
next_span => $toolbar,
|
|
paging => \%paging,
|
|
link_results_loop => \@link_results_loop
|
|
});
|
|
}
|
|
END_OF_SUB
|
|
|
|
$COMPILE{build_rating} = __LINE__ . <<'END_OF_SUB';
|
|
sub build_rating {
|
|
# ------------------------------------------------------------------
|
|
# Generate the rating pages.
|
|
#
|
|
my $links = $DB->table('Links');
|
|
|
|
require GT::SQL::Condition;
|
|
my $cond = GT::SQL::Condition->new('Votes', '>=', 10, VIEWABLE);
|
|
$GRAND_TOTAL ||= _grand_total();
|
|
|
|
$links->select_options("ORDER BY Rating DESC", "LIMIT 10");
|
|
my $rated = $links->select($cond);
|
|
|
|
$links->select_options("ORDER BY Votes DESC", "LIMIT 10");
|
|
my $voted = $links->select($cond);
|
|
|
|
# Now build the html.
|
|
my ($top_rated, $top_votes) = ('', '');
|
|
|
|
my (@top_votes_loop, @top_rated_loop);
|
|
while (my $link = $voted->fetchrow_hashref) {
|
|
$link->{detailed_url} = $CFG->{build_detail_url} . '/' . $links->detailed_url($link->{ID}) if $CFG->{build_detailed};
|
|
push @top_votes_loop, $link;
|
|
$top_votes .= qq~<tr><td align=center>$link->{Rating}</td><td align=center>$link->{Votes}</td><td><a href="$link->{URL}">$link->{Title}</a></td></tr>\n~;
|
|
}
|
|
while (my $link = $rated->fetchrow_hashref) {
|
|
$link->{detailed_url} = $CFG->{build_detail_url} . '/' . $links->detailed_url($link->{ID}) if $CFG->{build_detailed};
|
|
push @top_rated_loop, $link;
|
|
$top_rated .= qq~<tr><td align=center>$link->{Rating}</td><td align=center>$link->{Votes}</td><td><a href="$link->{URL}">$link->{Title}</a></td></tr>\n~;
|
|
}
|
|
|
|
# And write it to a file.
|
|
return Links::SiteHTML::display(rate_top => {
|
|
top_rated => $top_rated,
|
|
top_rated_loop => \@top_rated_loop,
|
|
top_votes_loop => \@top_votes_loop,
|
|
top_votes => $top_votes,
|
|
main_title_loop => build('title', Links::language('LINKS_TOPRATED'), $CFG->{build_ratings_url})
|
|
});
|
|
}
|
|
END_OF_SUB
|
|
|
|
$COMPILE{build_detailed} = __LINE__ . <<'END_OF_SUB';
|
|
sub build_detailed {
|
|
# ------------------------------------------------------------------
|
|
# Builds a single detailed page, takes either a link hash ref, or
|
|
# a link id.
|
|
#
|
|
if (! $CFG->{build_detailed}) {
|
|
return Links::SiteHTML::display(error => { error => Links::language('BUILD_DETAILED_DISABLED') });
|
|
}
|
|
|
|
my $link_db = $DB->table('Links');
|
|
$GRAND_TOTAL ||= _grand_total();
|
|
|
|
my $link = shift;
|
|
if (ref $link ne 'HASH') {
|
|
return Links::SiteHTML::display(error => { error => Links::language('BUILD_DETAILED_ARGS', $link) });
|
|
}
|
|
$link->{ID} ||= $IN->param('ID');
|
|
if (exists $link->{ID}) {
|
|
my $id = $link->{ID};
|
|
my $link_row = $link_db->get($id, 'HASH');
|
|
@$link{keys %$link_row} = values %$link_row;
|
|
$link_db->add_reviews([ $link ]);
|
|
if (! $link) {
|
|
return Links::SiteHTML::display(error => { error => Links::language('BUILD_DETAILED_INVALIDID', $id) });
|
|
}
|
|
elsif ($link->{isValidated} ne 'Yes') {
|
|
return Links::SiteHTML::display(error => { error => Links::language('BUILD_DETAILED_UNVAL') });
|
|
}
|
|
elsif ($CFG->{payment}->{enabled} and $link->{ExpiryDate} < time) {
|
|
return Links::SiteHTML::display(error => { error => Links::language('BUILD_DETAILED_EXPIRED') });
|
|
}
|
|
}
|
|
|
|
my ($cat_id, $cat_name);
|
|
if ($link->{CategoryID} and $link->{Full_Name}) {
|
|
$cat_id = $link->{CategoryID};
|
|
$cat_name = $link->{Full_Name};
|
|
}
|
|
else {
|
|
($cat_id, $cat_name) = each %{$link_db->get_categories($link->{ID})};
|
|
}
|
|
|
|
# Figure out the next/prev links.
|
|
my $catlnk_db = $DB->table('Links', 'CatLinks');
|
|
$catlnk_db->select_options("ORDER BY $CFG->{build_sort_order_category}") if $CFG->{build_sort_order_category};
|
|
my $sth = $catlnk_db->select('Links.ID' => { CategoryID => $cat_id }, VIEWABLE);
|
|
my ($next, $prev);
|
|
while (my $id = $sth->fetchrow) {
|
|
if ($id == $link->{ID}) {
|
|
$next = $sth->fetchrow;
|
|
last;
|
|
}
|
|
else {
|
|
$prev = $id;
|
|
}
|
|
}
|
|
|
|
my ($next_url, $prev_url) = $DB->table('Links')->category_detailed_url($cat_id, $next, $prev);
|
|
|
|
$link->{Category_Template} = $DB->table('Category')->template_set($cat_id);
|
|
|
|
# build_title generates the title loop by using / as a category delimiter.
|
|
# Since the link title can have /'s in it, we'll push that entry onto the loop
|
|
# ourselves. Note that the deprecated title and title_linked variables are
|
|
# still broken.
|
|
my $title_loop = build('title', $cat_name);
|
|
push @$title_loop, { Name => $link->{Title}, URL => $CFG->{build_detail_url} . '/' . $link_db->detailed_url($link->{ID}) };
|
|
|
|
return Links::SiteHTML::display('detailed', {
|
|
%$link,
|
|
title_loop => $title_loop,
|
|
title => sub { build('title_unlinked', "$cat_name/$link->{Title}") },
|
|
title_linked => sub { build('title_linked', "$cat_name/$link->{Title}") },
|
|
grand_total => $GRAND_TOTAL,
|
|
next => $next,
|
|
prev => $prev,
|
|
next_url => $next_url ? "$CFG->{build_detail_url}/$next_url" : '',
|
|
prev_url => $prev_url ? "$CFG->{build_detail_url}/$prev_url" : ''
|
|
});
|
|
}
|
|
END_OF_SUB
|
|
|
|
$COMPILE{build_category} = __LINE__ . <<'END_OF_SUB';
|
|
sub build_category {
|
|
# ------------------------------------------------------------------
|
|
# Build a single category page. Takes as argument a category hash or id
|
|
# number and an options hash.
|
|
#
|
|
my $cat_db = $DB->table('Category');
|
|
my $link_db = $DB->table('Links');
|
|
my $catlink_db = $DB->table('Links', 'CatLinks');
|
|
my $related_db = $DB->table('CatRelations');
|
|
$GRAND_TOTAL ||= _grand_total();
|
|
|
|
my $opts = shift;
|
|
if (ref $opts ne 'HASH') {
|
|
Links::debug("Invalid argument passed to build_category: $opts") if $Links::DEBUG;
|
|
return;
|
|
}
|
|
|
|
# Load our category info.
|
|
my $category;
|
|
$opts->{id} ||= $IN->param('ID');
|
|
if ($opts->{id}) {
|
|
$category = $cat_db->get($opts->{id}, 'HASH');
|
|
if (! $category) {
|
|
Links::debug("Invalid category id passed to build_category: $opts->{id}") if $Links::DEBUG;
|
|
return;
|
|
}
|
|
}
|
|
|
|
# Get our options.
|
|
$opts->{mh} = exists $opts->{mh} ? $opts->{mh} : $CFG->{build_span_pages} ? $CFG->{build_links_per_page} : 5000;
|
|
$opts->{nh} = exists $opts->{nh} ? $opts->{nh} : 1;
|
|
$opts->{sb} = exists $opts->{sb} ? $opts->{sb} : $CFG->{build_sort_order_category};
|
|
$opts->{so} = exists $opts->{so} ? $opts->{so} : '';
|
|
if ($opts->{sb} =~ /\b(?:asc|desc)\b/i) {
|
|
$opts->{so} = '';
|
|
}
|
|
$opts->{cat_sb} = exists $opts->{cat_sb} ? $opts->{cat_sb} : $CFG->{build_category_sort};
|
|
$opts->{cat_so} = exists $opts->{cat_so} ? $opts->{cat_so} : '';
|
|
if ($opts->{cat_sb} =~ /\b(?:asc|desc)\b/i) {
|
|
$opts->{cat_so} = '';
|
|
}
|
|
|
|
# Figure out the template set to use.
|
|
$category->{Category_Template} ||= $cat_db->template_set($category->{ID});
|
|
|
|
# Get our output vars.
|
|
my %tplvars = (
|
|
%$category,
|
|
category_id => $category->{ID},
|
|
category_name => $category->{Full_Name},
|
|
header => $category->{Header},
|
|
footer => $category->{Footer},
|
|
meta_name => $category->{Meta_Description},
|
|
meta_keywords => $category->{Meta_Keywords},
|
|
description => $category->{Description},
|
|
random => int rand 10000,
|
|
random1 => int rand 10000,
|
|
random2 => int rand 10000,
|
|
random3 => int rand 10000
|
|
);
|
|
|
|
# Clean up the name.
|
|
my $clean_name = $cat_db->as_url($category->{Full_Name});
|
|
my $build_title = $category->{Full_Name};
|
|
$build_title .= '/' . Links::language('LINKS_PAGE', $opts->{nh}) if $opts->{nh} and $opts->{nh} > 1;
|
|
|
|
$tplvars{title_loop} = build('title', $build_title);
|
|
$tplvars{title_linked} = sub { build('title_linked', $build_title) };
|
|
$tplvars{title} = sub { build('title_unlinked', $build_title) };
|
|
|
|
$tplvars{category_name_escaped} = GT::CGI->escape($category->{Full_Name});
|
|
$tplvars{category_clean} = $tplvars{title};
|
|
($tplvars{category_short}) = $tplvars{category_name} =~ m|([^/]+)$|;
|
|
|
|
# Prepare the condition; don't add the ExpiryDate handling - it gets added later
|
|
my $cond = GT::SQL::Condition->new(
|
|
CategoryID => '=' => $category->{ID},
|
|
isValidated => '=' => 'Yes'
|
|
);
|
|
|
|
# "Optional" payment categories are a hassle, as we have to do two selects,
|
|
# then balance out the mh/nh variables between the two.
|
|
my ($optional_sth, $sth);
|
|
my @select_options;
|
|
push @select_options, "ORDER BY $opts->{sb} $opts->{so}" if $opts->{sb};
|
|
|
|
# Load payment info if payment is enabled. Change sort order by paid links
|
|
# first then free links if payment for this category is optional. If payment
|
|
# is required, we need to remove unpaid links
|
|
if ($CFG->{payment}->{enabled}) {
|
|
require Links::Payment;
|
|
my $payment_info = Links::Payment::cat_payment_info($opts->{id});
|
|
|
|
if ($payment_info->{mode} == OPTIONAL and $CFG->{build_sort_paid_first}) {
|
|
my $paycond = GT::SQL::Condition->new($cond);
|
|
$paycond->add(ExpiryDate => '>=' => time, ExpiryDate => '<=' => UNLIMITED);
|
|
|
|
my $offset = ($opts->{nh} - 1) * $opts->{mh};
|
|
$catlink_db->select_options(@select_options);
|
|
$catlink_db->select_options("LIMIT $opts->{mh} OFFSET $offset");
|
|
$optional_sth = $catlink_db->select('Links.*', $paycond);
|
|
|
|
$cond->add(ExpiryDate => '=' => FREE);
|
|
}
|
|
else {
|
|
# 1) This is an else (instead of elsif ($payment_info->{mode} == REQUIRED)) because the
|
|
# run-time count updating code cannot efficiently take category settings into account
|
|
# as doing so requires either subselects (which older MySQL doesn't support), or a fair
|
|
# bit of Perl code; a single fast count to determine whether the check is necessary
|
|
# won't work. The end result is that counts would be off.
|
|
# 2) Even if this was an elsif, we can't include ExpiryDate <= UNLIMITED (to exclude
|
|
# free links) because links being free is the default for imported, upgraded, and
|
|
# admin-added links, which we don't want to exclude from REQUIRED categories.
|
|
$cond->add(ExpiryDate => '>=' => time);
|
|
}
|
|
}
|
|
|
|
my @results;
|
|
my ($paid_hits, $paid_rows, $offset, $max_hits) = (0, 0, ($opts->{nh} - 1) * $opts->{mh}, $opts->{mh});
|
|
if ($optional_sth) {
|
|
push @results, @{$optional_sth->fetchall_hashref};
|
|
$paid_rows = $optional_sth->rows;
|
|
$paid_hits = $catlink_db->hits;
|
|
if ($paid_rows == $opts->{mh}) {
|
|
$offset = $max_hits = 0;
|
|
}
|
|
elsif ($paid_rows > 0) {
|
|
$offset = 0;
|
|
$max_hits = $opts->{mh} - $paid_rows;
|
|
}
|
|
else {
|
|
$offset -= $paid_hits;
|
|
}
|
|
}
|
|
my $hits;
|
|
# Select links from required categories, not-accepted categories, and optional
|
|
# categories whose paid hits haven't filled the page
|
|
if ($max_hits) { # $max_hits will be 0 when mh paid links are already listed
|
|
$catlink_db->select_options(@select_options);
|
|
$catlink_db->select_options("LIMIT $max_hits OFFSET $offset");
|
|
my $sth = $catlink_db->select('Links.*' => $cond);
|
|
push @results, @{$sth->fetchall_hashref};
|
|
$hits = $catlink_db->hits;
|
|
}
|
|
else {
|
|
$hits = $catlink_db->count($cond);
|
|
}
|
|
|
|
my $numlinks = $tplvars{total} = $hits + $paid_hits;
|
|
$tplvars{total_optional_paid} = $paid_hits;
|
|
|
|
# Get the links.
|
|
$link_db->add_reviews(\@results);
|
|
my @links_loop = map Links::SiteHTML::tags('link', $_, $category->{ID}) => @results;
|
|
$tplvars{links_loop} = \@links_loop;
|
|
$tplvars{links_count} = @links_loop;
|
|
my $links;
|
|
$tplvars{links} = sub {
|
|
return $links if defined $links;
|
|
$links = '';
|
|
for my $link (@results) {
|
|
$link->{Category_Template} = $category->{Category_Template} if $category->{Category_Template};
|
|
$links .= Links::SiteHTML::display('link', $link);
|
|
}
|
|
return $links;
|
|
};
|
|
|
|
# Get the subcategories and related categories as either Yahoo style (integrated) or
|
|
# separated into two outputs..
|
|
my @cat_loop;
|
|
$tplvars{category_loop} = \@cat_loop;
|
|
if ($CFG->{build_category_yahoo}) {
|
|
my @subcat_ids = $cat_db->select(ID => { FatherID => $category->{ID} })->fetchall_list;
|
|
my %related_ids = $related_db->select(qw/RelatedID RelationName/ => { CategoryID => $category->{ID} })->fetchall_list;
|
|
if (@subcat_ids or keys %related_ids) {
|
|
$cat_db->select_options("ORDER BY $opts->{cat_sb} $opts->{cat_so}") if $opts->{cat_sb};
|
|
my $sth = $cat_db->select({ ID => [@subcat_ids, keys %related_ids] });
|
|
my @rel_loop;
|
|
while (my $cat = $sth->fetchrow_hashref) {
|
|
$cat->{URL} = "$CFG->{build_root_url}/" . $cat_db->as_url($cat->{Full_Name}) . "/" . ($CFG->{build_index_include} ? $CFG->{build_index} : '');
|
|
$cat->{RelationName} = '';
|
|
if (exists $related_ids{$cat->{ID}}) {
|
|
$cat->{Related} = 1;
|
|
$cat->{RelationName} = $related_ids{$cat->{ID}};
|
|
# Relations with a custom name need to be re-sorted
|
|
if ($cat->{RelationName}) {
|
|
push @rel_loop, $cat;
|
|
next;
|
|
}
|
|
}
|
|
push @cat_loop, $cat;
|
|
}
|
|
# Re-sort related categories using their RelationName rather than the related
|
|
# category's name
|
|
RELATION: while (my $cat = pop @rel_loop) {
|
|
for (my $i = 0; $i < @cat_loop; $i++) {
|
|
my $name = $cat_loop[$i]->{RelationName} ? $cat_loop[$i]->{RelationName} : $cat_loop[$i]->{Name};
|
|
if (lc $cat->{RelationName} lt lc $name) {
|
|
splice @cat_loop, $i, 0, $cat;
|
|
next RELATION;
|
|
}
|
|
}
|
|
push @cat_loop, $cat;
|
|
}
|
|
my $print_cat;
|
|
$tplvars{category} = sub {
|
|
return $print_cat if defined $print_cat;
|
|
return $print_cat = Links::SiteHTML::display('print_cat', [$category, @cat_loop]);
|
|
};
|
|
}
|
|
else {
|
|
$tplvars{category} = '';
|
|
}
|
|
}
|
|
else {
|
|
# Separate the output.
|
|
$cat_db->select_options("ORDER BY $opts->{cat_sb} $opts->{cat_so}") if $opts->{cat_sb};
|
|
$sth = $cat_db->select({ FatherID => $category->{ID} });
|
|
while (my $cat = $sth->fetchrow_hashref) {
|
|
$cat->{URL} = "$CFG->{build_root_url}/" . $cat_db->as_url($cat->{Full_Name}) . "/" . ($CFG->{build_index_include} ? $CFG->{build_index} : '');
|
|
push @cat_loop, $cat;
|
|
}
|
|
if (@cat_loop) {
|
|
my $print_cat;
|
|
$tplvars{category} = sub {
|
|
return $print_cat if defined $print_cat;
|
|
return $print_cat = Links::SiteHTML::display('print_cat', [$category, @cat_loop]);
|
|
};
|
|
}
|
|
else {
|
|
$tplvars{category} = '';
|
|
}
|
|
$tplvars{related} = '';
|
|
$tplvars{related_loop} = [];
|
|
|
|
my %related_ids = $related_db->select(qw/RelatedID RelationName/ => { CategoryID => $category->{ID} })->fetchall_list;
|
|
if (keys %related_ids) {
|
|
$cat_db->select_options("ORDER BY $opts->{cat_sb} $opts->{cat_so}") if $opts->{cat_sb};
|
|
my $sth = $cat_db->select({ ID => [keys %related_ids] });
|
|
while (my $cat = $sth->fetchrow_hashref) {
|
|
my $url = $CFG->{build_root_url} . "/" . $cat_db->as_url($cat->{Full_Name}) . "/" . ($CFG->{build_index_include} ? $CFG->{build_index} : '');
|
|
$cat->{URL} = $url;
|
|
$cat->{RelationName} = $related_ids{$cat->{ID}};
|
|
push @{$tplvars{related_loop}}, $cat;
|
|
$tplvars{related} .= qq|<li><a href="$url">| . ($related_ids{$cat->{ID}} || $cat->{Full_Name}) . "</a></li>";
|
|
}
|
|
}
|
|
}
|
|
|
|
# Plugins can use the build_category_loop hook to change the category
|
|
# results before they are returned to the template.
|
|
$PLG->dispatch(build_category_loop => sub { } => \@cat_loop);
|
|
|
|
# Get the header and footer from file if it exists, otherwise assume it is html.
|
|
if ($tplvars{header} and $tplvars{header} =~ /^\S{1,20}$/ and -e "$CFG->{admin_root_path}/headers/$tplvars{header}") {
|
|
local (@ARGV, $/) = "$CFG->{admin_root_path}/headers/$tplvars{header}";
|
|
$tplvars{header} = <>;
|
|
}
|
|
if ($tplvars{footer} and $tplvars{footer} =~ /^\S{1,20}$/ and -e "$CFG->{admin_root_path}/footers/$tplvars{footer}") {
|
|
local (@ARGV, $/) = "$CFG->{admin_root_path}/footers/$tplvars{footer}";
|
|
$tplvars{footer} = <>;
|
|
}
|
|
|
|
# If we are spanning pages, figure out toolbars and such.
|
|
if ($CFG->{build_span_pages}) {
|
|
my $lpp = $CFG->{build_links_per_page};
|
|
my $nh = $opts->{nh};
|
|
my $url = $CFG->{build_root_url} . "/" . $clean_name;
|
|
$tplvars{next} = $tplvars{prev} = "";
|
|
if ($numlinks > ($nh * $lpp)) {
|
|
$tplvars{next} = "$url/$CFG->{build_more}" . ($nh + 1) . "$CFG->{build_extension}";
|
|
}
|
|
if ($nh == 2) {
|
|
$tplvars{prev} = "$url/" . ($CFG->{build_index_include} ? $CFG->{build_index} : '');
|
|
}
|
|
elsif ($nh > 2) {
|
|
$tplvars{prev} = "$url/$CFG->{build_more}" . ($nh - 1) . "$CFG->{build_extension}";
|
|
}
|
|
if ($tplvars{next} or $tplvars{prev}) {
|
|
$tplvars{next_span} = build('toolbar', { url => $url, numlinks => $numlinks, nh => $nh });
|
|
$tplvars{paging} = {
|
|
page => "$clean_name/",
|
|
page_format => 1,
|
|
num_hits => $numlinks,
|
|
max_hits => $opts->{mh},
|
|
current_page => $opts->{nh}
|
|
};
|
|
}
|
|
}
|
|
|
|
return Links::SiteHTML::display('category', \%tplvars);
|
|
}
|
|
END_OF_SUB
|
|
|
|
$COMPILE{build_reset_hits} = __LINE__ . <<'END_OF_SUB';
|
|
sub build_reset_hits {
|
|
# ------------------------------------------------------------------
|
|
# Remove old hit and rate tracking information.
|
|
#
|
|
Links::init_date();
|
|
my $delete_by = GT::Date::date_get(time - 2*24*60*60, '%yyyy%-%mm%-%dd%');
|
|
my $click = $DB->table('ClickTrack');
|
|
$click->delete(GT::SQL::Condition->new('Created', '<', $delete_by));
|
|
}
|
|
END_OF_SUB
|
|
|
|
$COMPILE{build_orphan_check} = __LINE__ . <<'END_OF_SUB';
|
|
sub build_orphan_check {
|
|
# ------------------------------------------------------------------
|
|
# Checks for orphaned links and returns a list of ids.
|
|
#
|
|
my $opts = shift;
|
|
my $sel = $opts->{select} || 'ID';
|
|
my $rel = $DB->table('Links', 'CatLinks');
|
|
my $sth = $rel->select('left_join', $sel, { LinkID => undef });
|
|
my @output;
|
|
if ($opts->{select}) {
|
|
while (my $link = $sth->fetchrow_hashref) {
|
|
push @output, $link;
|
|
}
|
|
}
|
|
else {
|
|
while (my $id = $sth->fetchrow_array) {
|
|
push @output, $id;
|
|
}
|
|
}
|
|
return @output;
|
|
}
|
|
END_OF_SUB
|
|
|
|
$COMPILE{build_catlinks_orphan_check} = __LINE__ . <<'END_OF_SUB';
|
|
sub build_catlinks_orphan_check {
|
|
# ------------------------------------------------------------------
|
|
# Checks for orphaned CatLinks and returns a list of CategoryID/LinkIDs.
|
|
#
|
|
return (@{$DB->table('CatLinks', 'Links')->select('left_join', 'CategoryID', 'LinkID', { ID => undef })->fetchall_hashref},
|
|
@{$DB->table('CatLinks', 'Category')->select('left_join', 'CategoryID', 'LinkID', { ID => undef })->fetchall_hashref});
|
|
}
|
|
END_OF_SUB
|
|
|
|
$COMPILE{build_new_flags} = __LINE__ . <<'END_OF_SUB';
|
|
sub build_new_flags {
|
|
# ------------------------------------------------------------------
|
|
# Update the isNew flag based on Add_Date.
|
|
#
|
|
Links::init_date();
|
|
my $opts = shift || {};
|
|
my $date = GT::Date::date_sub(GT::Date::date_get(), $CFG->{build_new_cutoff});
|
|
my $links = $DB->table('Links');
|
|
$links->indexing(0);
|
|
my $cond = GT::SQL::Condition->new(
|
|
isNew => '=' => 'Yes',
|
|
Add_Date => '<' => $date,
|
|
VIEWABLE
|
|
);
|
|
# Unset isNew for links which aren't new anymore
|
|
my $old_rows = $links->count($cond);
|
|
if ($old_rows) {
|
|
$links->update({ isNew => 'No' }, $cond, { GT_SQL_SKIP_CHECK => 1 });
|
|
}
|
|
|
|
# Find links which need isNew set, but isn't set (the Links table subclass
|
|
# already update's the Category table's Has_New_Links flag)
|
|
$cond = GT::SQL::Condition->new(
|
|
isNew => '=' => 'No',
|
|
Add_Date => '>=' => $date,
|
|
VIEWABLE
|
|
);
|
|
my $new_rows = $links->count($cond);
|
|
if ($new_rows) {
|
|
$links->update({ isNew => 'Yes' }, $cond, { GT_SQL_SKIP_CHECK => 1 });
|
|
}
|
|
|
|
# Update the category flags
|
|
if ($new_rows or $old_rows or $opts->{reset}) {
|
|
my $links_cl = $DB->table('Links', 'CatLinks');
|
|
my $category = $DB->table('Category');
|
|
$category->indexing(0);
|
|
my %stalelinks = map { $_ => 1 } $category->select('ID', { Has_New_Links => 'Yes' })->fetchall_list;
|
|
$links_cl->select_options('ORDER BY Add_Date ASC');
|
|
my $sth = $links_cl->select('CategoryID', 'Add_Date', { isNew => 'Yes' }, VIEWABLE);
|
|
my %updated;
|
|
while (my ($cat_id, $date) = $sth->fetchrow) {
|
|
next if exists $updated{$cat_id};
|
|
my @ids = ($cat_id, @{$category->parents($cat_id)});
|
|
$category->update(
|
|
{ Newest_Link => $date, Has_New_Links => 'Yes' },
|
|
{ ID => \@ids, Has_New_Links => 'No' },
|
|
{ GT_SQL_SKIP_CHECK => 1 }
|
|
);
|
|
for (@ids) {
|
|
delete $stalelinks{$_};
|
|
$updated{$_}++;
|
|
}
|
|
}
|
|
# Whatever's left in %stalelinks are the categories which don't have new links anymore
|
|
$category->update({ Has_New_Links => 'No' }, { ID => [keys %stalelinks] }, { GT_SQL_SKIP_CHECK => 1 });
|
|
$category->indexing(1);
|
|
}
|
|
$links->indexing(1);
|
|
return $new_rows + $old_rows;
|
|
}
|
|
END_OF_SUB
|
|
|
|
$COMPILE{build_changed_flags} = __LINE__ . <<'END_OF_SUB';
|
|
sub build_changed_flags {
|
|
# ------------------------------------------------------------------
|
|
# Update isChanged flags, based on Mod_Date.
|
|
#
|
|
Links::init_date();
|
|
my $opts = shift || {};
|
|
my $date = GT::Date::date_sub(GT::Date::date_get(), $CFG->{build_new_cutoff});
|
|
my $links = $DB->table('Links');
|
|
$links->indexing(0);
|
|
my $cond = GT::SQL::Condition->new(
|
|
isChanged => '=' => 'Yes',
|
|
VIEWABLE,
|
|
GT::SQL::Condition->new(
|
|
Mod_Date => '<' => $date,
|
|
isNew => '=' => 'Yes',
|
|
'OR'
|
|
)
|
|
);
|
|
# Unset isChanged for links which aren't 'new' anymore
|
|
my $old_rows = $links->count($cond);
|
|
if ($old_rows) {
|
|
$links->update({ isChanged => 'No' }, $cond, { GT_SQL_SKIP_CHECK => 1 });
|
|
}
|
|
|
|
# Find links which need isChanged set
|
|
$cond = GT::SQL::Condition->new(
|
|
isChanged => '=' => 'No',
|
|
Mod_Date => '>=' => $date,
|
|
Add_Date => '<' => $date,
|
|
VIEWABLE
|
|
);
|
|
my $new_rows = $links->count($cond);
|
|
if ($new_rows) {
|
|
$links->update({ isChanged => 'Yes' }, $cond, { GT_SQL_SKIP_CHECK => 1 });
|
|
}
|
|
|
|
# Update the category flags
|
|
if ($new_rows or $old_rows or $opts->{reset}) {
|
|
my $category = $DB->table('Category');
|
|
$category->indexing(0);
|
|
my %stalelinks = map { $_ => 1 } $category->select('ID', { Has_Changed_Links => 'Yes' })->fetchall_list;
|
|
my $sth = $DB->table('Links', 'CatLinks')->select('CategoryID', 'Add_Date', { isChanged => 'Yes' });
|
|
my %updated;
|
|
while (my ($cat_id, $date) = $sth->fetchrow_array) {
|
|
next if exists $updated{$cat_id};
|
|
my @ids = ($cat_id, @{$category->parents($cat_id)});
|
|
$category->update(
|
|
{ Has_Changed_Links => 'Yes' },
|
|
{ ID => \@ids, Has_Changed_Links => 'No' },
|
|
{ GT_SQL_SKIP_CHECK => 1 }
|
|
);
|
|
for (@ids) {
|
|
delete $stalelinks{$_};
|
|
$updated{$_}++;
|
|
}
|
|
}
|
|
# Whatever's left in %stalelinks are the categories which don't have changed links anymore
|
|
$category->update({ Has_Changed_Links => 'No' }, { ID => [keys %stalelinks] }, { GT_SQL_SKIP_CHECK => 1 });
|
|
$category->indexing(1);
|
|
}
|
|
$links->indexing(1);
|
|
return $new_rows + $old_rows;
|
|
}
|
|
END_OF_SUB
|
|
|
|
$COMPILE{build_cool_flags} = __LINE__ . <<'END_OF_SUB';
|
|
sub build_cool_flags {
|
|
# ------------------------------------------------------------------
|
|
# Update the isPopular flag determined on number of Hits.
|
|
#
|
|
|
|
# Work out based either on percentage, or top x links.
|
|
my $link_db = $DB->table('Links');
|
|
$GRAND_TOTAL ||= _grand_total();
|
|
my $limit = int($CFG->{build_pop_cutoff} > 1 ? $CFG->{build_pop_cutoff} : $CFG->{build_pop_cutoff} * $GRAND_TOTAL) || 1;
|
|
|
|
# Select the popular links and update the flags if neccessary.
|
|
$link_db->indexing(0);
|
|
$link_db->select_options("ORDER BY Hits DESC", "LIMIT $limit");
|
|
my $sth = $link_db->select(
|
|
'ID', 'isPopular',
|
|
GT::SQL::Condition->new(
|
|
Hits => '>=' => 2,
|
|
VIEWABLE
|
|
)
|
|
);
|
|
my (@ids, @update_pop);
|
|
while (my ($id, $ispop) = $sth->fetchrow) {
|
|
push @ids, $id;
|
|
push @update_pop, $id unless $ispop eq 'Yes';
|
|
}
|
|
$link_db->update({ isPopular => 'Yes' }, { ID => \@update_pop }, { GT_SQL_SKIP_CHECK => 1 })
|
|
if @update_pop;
|
|
|
|
# Change any links that aren't popular back.
|
|
my $cond = GT::SQL::Condition->new('isPopular', '=', 'Yes');
|
|
$cond->add(ID => '!=' => \@ids) if @ids;
|
|
$link_db->update({ isPopular => 'No' }, $cond);
|
|
$link_db->indexing(1);
|
|
}
|
|
END_OF_SUB
|
|
|
|
$COMPILE{build_title} = __LINE__ . <<'END_OF_SUB';
|
|
sub build_title {
|
|
# ------------------------------------------------------------------
|
|
# Generate a title. Unlike build_title_{un,}linked, returns something more
|
|
# useful: an array ref of hashes of Name and URL.
|
|
# The 2nd argument is an optional override URL for the last item.
|
|
#
|
|
my ($input, $url) = @_;
|
|
my $cat = $DB->table('Category');
|
|
my $top = Links::language('LINKS_TOP');
|
|
my @cats = ({ Name => $top, URL => "$CFG->{build_root_url}/" . ($CFG->{build_home} || ($CFG->{build_index_include} ? $CFG->{build_index} : '')) });
|
|
|
|
my @si = split /\//, $input;
|
|
for (0 .. $#si) {
|
|
my $curl;
|
|
if ($_ == 0 and $si[$_] eq Links::language('LINKS_NEW')) {
|
|
$curl = "$CFG->{build_new_url}/";
|
|
}
|
|
elsif ($_ == $#si and $url) {
|
|
$curl = $url;
|
|
}
|
|
else {
|
|
$curl = "$CFG->{build_root_url}/" . $cat->as_url(join "/", @si[0 .. $_]) . "/" . ($CFG->{build_index_include} ? $CFG->{build_index} : '');
|
|
}
|
|
push @cats, { Name => $si[$_], URL => $curl };
|
|
}
|
|
|
|
return \@cats;
|
|
}
|
|
END_OF_SUB
|
|
|
|
$COMPILE{build_title_linked} = __LINE__ . <<'END_OF_SUB';
|
|
sub build_title_linked {
|
|
# ------------------------------------------------------------------
|
|
# Generate a linked title.
|
|
#
|
|
my $input = shift;
|
|
my $complete = 0;
|
|
my $home = 1;
|
|
if (ref $input) {
|
|
$complete = $input->{complete} || 0;
|
|
$home = defined $input->{home} ? $input->{home} : 1;
|
|
$input = $input->{name};
|
|
}
|
|
|
|
my $db = $DB->table('Category');
|
|
my $top = Links::language('LINKS_TOP');
|
|
|
|
my @dirs = split /\//, $input;
|
|
my $last;
|
|
$last = pop @dirs unless $complete;
|
|
|
|
my @paths;
|
|
push @paths, qq{<a href="$CFG->{build_root_url}/} . ($CFG->{build_home} || ($CFG->{build_index_include} ? $CFG->{build_index} : '')) . qq{">$top</a>} if $home;
|
|
for (0 .. $#dirs) {
|
|
my $path = "/" . $db->as_url(join "/", @dirs[0 .. $_]);
|
|
push @paths, qq{<a href="$CFG->{build_root_url}$path/} . ($CFG->{build_index_include} ? $CFG->{build_index} : '') . qq{">$dirs[$_]</a>};
|
|
}
|
|
push @paths, $last unless $complete;
|
|
return join ': ', @paths;
|
|
}
|
|
END_OF_SUB
|
|
|
|
$COMPILE{build_title_unlinked} = __LINE__ . <<'END_OF_SUB';
|
|
sub build_title_unlinked {
|
|
# --------------------------------------------------------
|
|
# Returns a string of the current category broken up by section.
|
|
# Useful for printing in the title.
|
|
#
|
|
my $input = shift;
|
|
my $output = join ': ', split /\//, $input;
|
|
|
|
return $output;
|
|
}
|
|
END_OF_SUB
|
|
|
|
$COMPILE{build_toolbar} = __LINE__ . <<'END_OF_SUB';
|
|
sub build_toolbar {
|
|
# --------------------------------------------------------
|
|
# Create an Altavista style toolbar for the next and previous pages.
|
|
#
|
|
my $opts = shift;
|
|
|
|
my $root_url = $opts->{url};
|
|
my $first_url = $opts->{first_url} || "$root_url/" . ($CFG->{build_index_include} ? $CFG->{build_index} : '');
|
|
my $next_url = $opts->{next_url} || "$root_url/$CFG->{build_more}";
|
|
my $numhits = $opts->{numlinks};
|
|
my $nh = $opts->{nh};
|
|
my $maxhits = $opts->{mh} || $CFG->{build_links_per_page};
|
|
|
|
my ($next_hit, $prev_hit) = ($nh + 1, $nh - 1);
|
|
|
|
# First, set how many pages we have on the left and the right.
|
|
my ($left, $right) = ($nh, int($numhits / $maxhits) - $nh);
|
|
|
|
# Then work out what page number we can go above and below.
|
|
my $lower = $left > 7 ? $left - 7 : 1;
|
|
my $upper = $right > 7 ? $nh + 7 : int($numhits/$maxhits) + 1;
|
|
|
|
# Finally, adjust those page numbers if we are near an endpoint.
|
|
$upper += 8 - $nh if 7 - $nh >= 0;
|
|
$lower -= $nh - int($numhits/$maxhits - 7) - 1 if $nh > $numhits / $maxhits - 7;
|
|
|
|
my $url = "";
|
|
# Then let's go through the pages and build the HTML.
|
|
$url .= $nh == 2 ? qq~<a href="$first_url">[<<]</a> ~ : qq~<a href="$next_url$prev_hit$CFG->{build_extension}">[<<]</a> ~
|
|
if $nh > 1;
|
|
|
|
for (my $i = 1; $i <= int($numhits/$maxhits) + 1; $i++) {
|
|
if ($i < $lower) { $url .= " ... "; $i = $lower - 1; next }
|
|
if ($i > $upper) { $url .= " ... "; last }
|
|
$url .= $i == $nh
|
|
? "$i "
|
|
: $i == 1
|
|
? qq~<a href="$first_url">$i</a> ~
|
|
: qq~<a href="$next_url$i$CFG->{build_extension}">$i</a> ~;
|
|
|
|
if ($i * $maxhits == $numhits) { $next_hit = $i if $nh == $i; last }
|
|
}
|
|
$url .= qq~<a href="$next_url$next_hit$CFG->{build_extension}">[>>]</a> ~
|
|
unless $next_hit == $nh or $nh * $maxhits > $numhits;
|
|
|
|
$url;
|
|
}
|
|
END_OF_SUB
|
|
|
|
$COMPILE{build_search_toolbar} = __LINE__ . <<'END_OF_SUB';
|
|
sub build_search_toolbar {
|
|
# --------------------------------------------------------
|
|
# Create an Altavista style toolbar for the next and previous pages.
|
|
#
|
|
my $opts = shift;
|
|
|
|
my $root_url = $opts->{url};
|
|
my $numhits = $opts->{numlinks};
|
|
my $nh = $opts->{nh};
|
|
my $maxhits = $opts->{mh} || $CFG->{build_links_per_page};
|
|
|
|
my ($next_hit, $prev_hit) = ($nh + 1, $nh - 1);
|
|
|
|
# First, set how many pages we have on the left and the right.
|
|
my ($left, $right) = ($nh, int($numhits / $maxhits) - $nh);
|
|
|
|
# Then work out what page number we can go above and below.
|
|
my $lower = $left > 7 ? $left - 7 : 1;
|
|
my $upper = $right > 7 ? $nh + 7 : int($numhits/$maxhits) + 1;
|
|
|
|
# Finally, adjust those page numbers if we are near an endpoint.
|
|
$upper += 8 - $nh if 7 - $nh >= 0;
|
|
$lower -= $nh - int($numhits/$maxhits - 7) - 1 if $nh > $numhits / $maxhits - 7;
|
|
|
|
my $url = "";
|
|
# Then let's go through the pages and build the HTML.
|
|
$url .= qq~<a href="$root_url;nh=$prev_hit">[<<]</a> ~ if $nh > 1;
|
|
for (my $i = 1; $i <= int($numhits/$maxhits) + ($numhits % $maxhits ? 1 : 0); $i++) {
|
|
if ($i < $lower) { $url .= " ... "; $i = $lower - 1; next }
|
|
if ($i > $upper) { $url .= " ... "; last }
|
|
$url .= $i == $nh ? "$i " : qq~<a href="$root_url;nh=$i">$i</a> ~;
|
|
if ($i * $maxhits == $numhits) { $next_hit = $i if $nh == $i; last }
|
|
}
|
|
$url .= qq~<a href="$root_url;nh=$next_hit">[>>]</a> ~
|
|
unless $next_hit == $nh or $nh * $maxhits > $numhits;
|
|
|
|
$url;
|
|
}
|
|
END_OF_SUB
|
|
|
|
sub _compile {
|
|
# -------------------------------------------------------------------
|
|
# Compile will check %COMPILE for the subroutine, and if it exists compile
|
|
# it (with an eval). Returns a code reference.
|
|
#
|
|
my $sub = shift;
|
|
if (exists $COMPILE{$sub}) {
|
|
GT::AutoLoader::_compile(\%COMPILE, __PACKAGE__, $sub) if $COMPILE{$sub};
|
|
return *{$Links::Build::{$sub}}{CODE};
|
|
}
|
|
else {
|
|
die "Invalid method: $sub";
|
|
}
|
|
}
|
|
|
|
sub _grand_total {
|
|
# -------------------------------------------------------------------
|
|
# Calculates the total in three queries as it can be significantly faster
|
|
# on large db's.
|
|
#
|
|
|
|
# Create a category object just to make sure the db maintenance code has run:
|
|
$DB->table('Category');
|
|
|
|
my $total = $DB->table('CatLinks')->count - $DB->table('Links')->count({ isValidated => 'No' });
|
|
|
|
if ($CFG->{payment}->{enabled}) {
|
|
$total -= $DB->table('Links')->count(
|
|
GT::SQL::Condition->new(
|
|
ExpiryDate => '<' => time,
|
|
isValidated => '=' => 'Yes'
|
|
)
|
|
);
|
|
}
|
|
|
|
return $total;
|
|
}
|
|
|
|
1;
|