discourse-legacysite-perl/site/slowtwitch.com/cgi-bin/articles/admin/Plugins/UI.pm
2024-06-17 21:49:12 +10:00

704 lines
25 KiB
Perl

# ==================================================================
# Plugins::UI - Auto Generated Program Module
#
# Plugins::UI
# Author : Bao Phan
# Version : 1.0
# Updated : Tue Mar 15 12:59:20 2016
#
# ==================================================================
#
package Plugins::UI;
# ==================================================================
use strict;
use GT::Base;
use GT::Plugins qw/STOP CONTINUE/;
use Links qw/:objects :payment/;
use Links::Build;
use Links::SiteHTML;
use constants
VIDEO => 'video',
PHOTO => 'photo',
ARTICLE => 'article'
;
# Inherit from base class for debug and error methods
@Plugins::UI::ISA = qw(GT::Base);
require Plugins::SlideShow;
require Plugins::ConvertVideo;
require GT::SQL::Condition;
require GT::Date;
sub cat_url {
my $full_name = shift || return;
return "$CFG->{build_root_url}/" . $DB->table('Category')->as_url($full_name) . "/" . ($CFG->{build_index_include} ? $CFG->{build_index} : '');
}
sub update_featured_links {
my ($type, $id, $add) = @_;
return if $USER->{Status} ne 'Administrator';
return unless $id and $type;
return if $type !~ /^(article|photo|video)$/i;
my $name = $type eq 'article' ? 'featured_articles' : 'featured_photos';
my $ids = $CFG->{$name} || [];
my (@ids, $changed);
my %ids = map { $_ => 1 } @$ids;
if ($add) {
if ($ids{$id}) {
@ids = @$ids;
}
else {
@ids = ($id, @$ids);
my $hits = scalar @ids;
if ($type eq 'article' and $hits > 4) {
pop @ids;
}
elsif ($type ne 'article' and $hits > 2) {
pop @ids;
}
$changed = 1;
}
}
elsif ($ids{$id}) {
@ids = map $_, grep { $_ != $id } @$ids;
$changed = 1;
}
return unless $changed;
$CFG->{$name} = \@ids;
$CFG->save;
return;
}
sub is_featured {
my ($type, $id) = @_;
return unless $id and $type;
return if $type !~ /^(article|photo|video)$/i;
my $name = $type eq 'article' ? 'featured_articles' : 'featured_photos';
my $ids = $CFG->{$name} || [];
my %ids = map { $_ => 1 } @$ids;
return $ids{$id} ? 1 : 0;
}
sub fetch_categories {
my $ids = shift;
$ids = ref $ids ? @$ids : [$ids] if $ids;
my $tab = $DB->table('Category');
$tab->select_options('ORDER BY Name');
my $cond = GT::SQL::Condition->new( CatDepth => '=' => 0);
$cond->add(ID => '=' => $ids) if $ids;
my $cats = $tab->select($cond)->fetchall_hashref;
foreach (@$cats) {
$_->{URL} = "$CFG->{build_root_url}/" . $tab->as_url($_->{Full_Name}) . "/" . ($CFG->{build_index_include} ? $CFG->{build_index} : '');
}
return $cats;
}
sub fetch_links {
my %args = @_;
return unless %args;
my $maxhits = $args{max_hits} || 1;
my $cond = new GT::SQL::Condition;
my $url = $CFG->{build_detail_url};
my (@ids, %paging);
my $tab_cat = $DB->table('Category');
my $tab_lnks = $DB->table('Links');
my $tab_catlnks = $DB->table(qw/Links CatLinks Category/);
if ($args{type}) {
$cond->add(Type => '=' => $args{type});
$tab_catlnks->select_options("GROUP BY LinkID", "ORDER BY Add_Date DESC", "LIMIT $maxhits");
}
elsif ($args{ids}) {
if (ref $args{ids} eq 'ARRAY') {
@ids = @{$args{ids}};
}
else {
@ids = split(/\,|\r?\n/, $args{ids});
}
return unless scalar @ids;
$cond->add(LinkID => '=' => \@ids);
$tab_catlnks->select_options("ORDER BY Add_Date DESC");
$tab_catlnks->select_options("LIMIT $maxhits") if $args{max_hits};
}
elsif ($args{category}) {
my $category = $tab_cat->get($args{category});
return unless $category;
$cond->add(Full_Name => 'like' => $category->{Full_Name} . '%');
$tab_catlnks->select_options("ORDER BY Add_Date DESC", "LIMIT $maxhits");
}
elsif ($args{tag} and $args{tag} =~ /^(?:swim|bike|run)$/i) {
my $vars = GT::Template->vars;
my $tag = lc $args{tag};
$url .= "/$tag";
$paging{max_hits} = $args{max_hits} || 25;
$paging{current_page} = $vars->{nh} || 1;
$paging{page} = $args{url} || ($tag . '/');
my $offset = $paging{current_page} == 1 ? 0 : ($paging{current_page} - 1) * $paging{max_hits};
$cond->add('tag_' . $tag => '=' => 1);
$tab_catlnks->select_options("GROUP BY LinkID", "ORDER BY Add_Date DESC", "LIMIT $paging{max_hits} OFFSET $offset");
}
elsif ($args{link_type} and $args{link_type} =~ /^(?:photo|video)$/i) {
my $vars = GT::Template->vars;
$url .= "/" . ($args{link_type} eq 'video' ? 'Videos' : 'Photos');
$paging{max_hits} = $args{max_hits} || 25;
$paging{current_page} = $vars->{nh} || 1;
$paging{page} = ($args{link_type} eq 'video' ? 'Videos' : 'Photos') . '/';
my $offset = $paging{current_page} == 1 ? 0 : ($paging{current_page} - 1) * $paging{max_hits};
$cond->add(Link_Type => '=' => $args{link_type});
$tab_catlnks->select_options("GROUP BY LinkID", "ORDER BY Add_Date DESC", "LIMIT $paging{max_hits} OFFSET $offset");
}
else {
$tab_catlnks->select_options("GROUP BY LinkID", "ORDER BY Add_Date DESC", "LIMIT $maxhits");
}
my $links = ($tab_catlnks->select(qw/Links.* Name Full_Name/, $cond) or die $GT::SQL::error)->fetchall_hashref;
return unless scalar @$links;
foreach my $l (@$links) {
$l->{detailed_url} = "$url/" . $tab_lnks->detailed_url($l->{ID}) if $CFG->{build_detailed};
$l->{URL} = "$CFG->{build_root_url}/" . $tab_cat->as_url($l->{Full_Name}) . "/" . ($CFG->{build_index_include} ? $CFG->{build_index} : '');
$l->{thumbnail_url} = fetch_thumbnail($l);
}
if (scalar @ids and !$args{db}) {
my @links_output;
my %links = map { $_->{ID} => $_ } @$links;
foreach my $id (@ids) {
push @links_output, $links{$id} if $links{$id};
}
return \@links_output;
}
elsif (
($args{link_type} and $args{link_type} =~ /^(?:photo|video)$/) or
($args{tag} and $args{tag} =~ /^(?:swim|bike|run)$/)
) {
$paging{num_hits} = $tab_lnks->select('COUNT(*)', $cond)->fetchrow;
my @features;
if ($paging{current_page} == 1) {
@features = splice @$links, 0, 3;
}
return { loop => $links, features => \@features, paging => \%paging };
}
else {
return $links;
}
}
sub fetch_category {
my $catid = shift || return;
return $DB->table('Category')->get($catid);
}
sub fetch_widget {
my $id = shift || return;
my $tab = $DB->table('Widgets');
my $widget = $tab->get($id);
return unless $widget;
if ($widget->{Image}) {
my $fh = $tab->file_info('Image', $widget->{ID});
$widget->{Image_URL} = '/images/widgets/' . $fh->File_RelativeURL;
}
return $widget;
}
sub fetch_widgets {
my ($page, $catid) = @_;
return unless $page;
my $tab_pgwidgets = $DB->table(qw/Widgets PageWidgets/);
$tab_pgwidgets->select_options('ORDER BY Sort_Pos');
my $widgets = [];
if ($catid =~ /^\d+$/) {
$widgets = $tab_pgwidgets->select(qw/Widgets.* Sort_Pos/, { Page => $catid })->fetchall_hashref;
}
unless (scalar @$widgets) {
$widgets = $tab_pgwidgets->select(qw/Widgets.* Sort_Pos/, { Page => $page })->fetchall_hashref;
}
my $tab = $DB->table('Widgets');
foreach my $w (@$widgets) {
next unless $w->{Image};
my $fh = $tab->file_info('Image', $w->{ID});
next unless $fh;
$w->{Image_URL} = '/images/widgets/' . $fh->File_RelativeURL;
}
return $widgets;
}
sub generate_widget {
my $id = shift || return;
my $widget = fetch_widget($id);
return unless $widget;
return Links::SiteHTML::display('include_single_widget', $widget);
}
sub friendly_date {
my ($date, $timestmp, $format) = @_;
return unless $date;
my $days = GT::Date::date_diff(GT::Date::date_get(time, '%yyyy%-%mm%-%dd%'), $date);
unless ($days) {
my $time = GT::Date::timelocal(GT::Date::parse_format($timestmp, "%yyyy%-%mm%-%dd% %HH%:%MM%:%ss%"));
my $secs = time - $time;
return unless $secs;
$date = $timestmp;
return "$secs seconds ago" if $secs < 60;
my $mins = int($secs / 60);
return "$mins minutes ago" if $mins < 60;
my $hours = int($secs / 3600);
return $hours . ($hours > 1 ? " hours ago" : " hour ago");
}
else {
my $time = GT::Date::timelocal(GT::Date::parse_format($date, "%yyyy%-%mm%-%dd%"));
$date = $time;
return ($format or $days > 7)
? GT::Date::date_get($date, $format || "%mmm% %dd%, %yyyy%")
: $days . ($days > 1 ? " days ago" : " day ago");
}
}
sub retrieve_param {
my ($field, $count) = @_;
return unless $field;
$count ||= 1;
my $vars = GT::Template->vars;
if ($field eq 'Image_description') {
return $vars->{"Image${count}_description"};
}
elsif ($field =~ /^Image_(.*)$/) {
return $vars->{"Image${count}_$1"} || $vars->{"Image${count}_path"};
}
else {
return $vars->{"$field$count"};
}
}
sub fetch_thumbnail {
my $link = shift || return;
if ($link->{Link_Type} eq VIDEO) {
if ($link->{Thumbnail_URL} and $link->{Thumbnail_URL} ne 'http://') {
return { small => $link->{Thumbnail_URL} };
}
else {
my $field = Plugins::ConvertVideo::get_file_path($link->{ID}, "thumbnail_file_field");
return $field->{thumbnail_file_field_path}
? {
small => $field->{thumbnail_file_field_path},
medium => $field->{thumbnail_file_field_path},
large => $field->{thumbnail_file_field_path},
largest => $field->{thumbnail_file_field_path},
}
: undef;
}
}
else {
my $slideshow = Plugins::SlideShow::generate_paths($link->{ID});
if ($slideshow and $slideshow->{image_loop}) {
return {
small => $slideshow->{image_loop}->[0]{_thumbnail_path},
medium => $slideshow->{image_loop}->[0]{_medium_path},
large => $slideshow->{image_loop}->[0]{_large_path},
largest => $slideshow->{image_loop}->[0]{_largest_path}
};
}
}
}
sub slideshow_url {
my $url = shift || return;
$url =~ s,^$CFG->{build_detail_url},$CFG->{build_detail_url}/Photos,;
return $url;
}
sub rewrite_breadcrumbs {
my ($title_loop, $mode) = @_;
return unless ref $title_loop;
my @loop;
foreach my $i (0 .. scalar @$title_loop - 1) {
my $item = $title_loop->[$i];
if ($i == 1) {
if ($item->{Name} =~ /rd\s*aids/i) {
push @loop, {
Name => "Races",
URL => "$CFG->{build_root_url}/Races/index.html"
};
}
elsif ($mode) {
push @loop, {
Name => $mode eq 'photo' ? 'Photo Galleries' : 'Videos',
URL => "$CFG->{build_root_url}/" . ($mode eq 'photo' ? 'Photos' : 'Videos') . "/index.html"
};
}
elsif ($item->{Name} !~ /home|bike\s*fit|products|races|articles|photos|videos|coaching|podcast|privacy|about|agreement/i) {
push @loop, {
Name => "Articles",
URL => "$CFG->{build_root_url}/Articles/index.html"
};
}
}
push @loop, $item;
}
return \@loop;
}
sub build_category {
my $opts = shift;
$opts->{id} ||= $IN->param('ID');
return @_ unless $opts->{id};
my $cfg = Links::Plugins->get_plugin_user_cfg('UI');
return @_ unless $cfg and $cfg->{merge_categories};
my %ids = map { $_ => 1 } split(/\s*,\s*/, $cfg->{merge_categories});
return @_ unless $ids{$opts->{id}};
GT::Plugins->action( STOP );
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');
$Links::Build::GRAND_TOTAL ||= Links::Build::_grand_total();
if (ref $opts ne 'HASH') {
Links::debug("Invalid argument passed to build_category: $opts") if $Links::DEBUG;
return @_;
}
# Load our category info.
my $category;
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} = Links::Build::build('title', $build_title);
$tplvars{title_linked} = sub { Links::Build::build('title_linked', $build_title) };
$tplvars{title} = sub { Links::Build::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|([^/]+)$|;
# CUSTOMIZED: show all links in a category as well as subcategories
my $categories = $cat_db->children($category->{ID});
push @$categories, $category->{ID};
my $cond = GT::SQL::Condition->new(
CategoryID => '=' => $categories,
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 @ids = map $_->[0], @{$catlink_db->select('DISTINCT LinkID', $cond)->fetchall_arrayref};
$link_db->select_options(@select_options);
my $sth = $link_db->select({ ID => \@ids });
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', $_, undef) => @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} = Links::Build::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);
}
sub fetch_widget_external_links {
my $id = shift || return;
return $DB->table('WidgetLinks')->select({ WidgetID => $id })->fetchall_hashref;
}
1;