# ================================================================== # 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|
  • | . ($related_ids{$cat->{ID}} || $cat->{Full_Name}) . "
  • "; } } } # 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;