# ================================================================== # 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: Utils.pm,v 1.61 2008/07/15 19:50:11 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::Utils; # ================================================================== # This package contains some builtin functions useful in your templates. # use strict; use Links qw/$IN $DB $CFG $USER/; sub is_editor { # ------------------------------------------------------------------- # Returns true if the current user is an editor. # return unless $USER and $USER->{Status} ne 'Not Validated'; return $DB->table('Editors')->count({ Username => $USER->{Username} }); } sub load_editors { # ------------------------------------------------------------------- # You call this tag by placing <%Links::Utils::load_editors%> in your # category.html template. It will then make available an <%editors%> # tag that you can use in your template. For example: # <%Links::Utils::load_editors%> # <%if editors%> # The following users are editors in this category: <%editors%> # <%endif%> # my $vars = GT::Template->vars; my $cat_id = $vars->{category_id} or return "No category_id tag found! This tag can only be used on category.html template"; my $cat_db = $DB->table('Category'); my @parents = @{$cat_db->parents($cat_id)}; push @parents, $cat_id; my $ed_db = $DB->table('Editors', 'Users'); my $sth = $ed_db->select(GT::SQL::Condition->new('CategoryID', 'IN', \@parents)); return {} unless ($sth->rows); # Make any formatting changes you need here. my $output = '"; return { editors => $output, editors_loop => \@editors }; } sub load_user { # ------------------------------------------------------------------- # You call this tag in your link.html or detailed.html template. It will # provide all the information about the user who owns the link, and also # create a Contact_Name and Contact_Email tag for backwards compatibility. # So you would put: # <%Links::Utils::load_user%> # This link is owned by <%Username%>, whose email is <%Email%> # and password is <%Password%>. They are a <%Status%> user. # my $vars = GT::Template->vars; my $username = $vars->{LinkOwner} or return "No LinkOwner tag found! This tag can only be used on link.html or detailed.html templates."; require Links::Authenticate; my $user_r = Links::Authenticate->auth('get_user', { Username => $username } ); return $user_r; } sub load_reviews { # ------------------------------------------------------------------- # You call this tag in link.html or detailed.html template. It will # load all the reviews associated with this link. # So you would put: # <%Links::Utils::load_reviews($ID, $max_reviews)%> # This link has <%Review_Total%> reviews. # <%loop Reviews_Loop%><%Review_Subject%> - <%Review_ByLine%><%endloop%> # Review_Count is a deprecated backwards compatible variable # my ($id, $max) = @_; unless ($id) { my $vars = GT::Template->vars; $id = $vars->{ID}; } my $reviews = $DB->table('Reviews'); if ($CFG->{review_sort_by}) { my $order = $CFG->{review_sort_order} || 'DESC'; $reviews->select_options("ORDER BY $CFG->{review_sort_by} $order"); } if ($max and $max =~ /^\d+$/) { $reviews->select_options("LIMIT $max"); } my $review_total = $reviews->count({ Review_LinkID => $id, Review_Validated => 'Yes' }); my $sth = $reviews->select({ Review_LinkID => $id, Review_Validated => 'Yes' }); my @reviews; Links::init_date(); require Links::User::Review; my $today = GT::Date::date_get(); while (my $rev = $sth->fetchrow_hashref) { $rev->{Review_IsNew} = (GT::Date::date_diff($today, $rev->{Review_Date}) < $CFG->{review_days_old}); $rev->{Review_CanModify} = 0; if ($CFG->{review_allow_modify} and $USER->{Username} eq $rev->{Review_Owner}) { if ($CFG->{review_modify_timeout}) { my $oldfmt = GT::Date::date_get_format(); GT::Date::date_set_format(GT::Date::FORMAT_DATETIME); my $timeout = GT::Date::date_get(time - $CFG->{review_modify_timeout} * 60); my $date = $rev->{Review_ModifyDate} =~ /^0000-00-00 00:00:00/ ? $rev->{Review_Date} : $rev->{Review_ModifyDate}; if (GT::Date::date_is_greater($date, $timeout)) { $rev->{Review_CanModify} = 1; } GT::Date::date_set_format($oldfmt); } else { $rev->{Review_CanModify} = 1; } } if ($rev->{Review_ModifyDate} ne $rev->{Review_Date} and $rev->{Review_ModifyDate} !~ /^0000-00-00 00:00:00/) { $rev->{Review_ModifyDate} = GT::Date::date_transform($rev->{Review_ModifyDate}, GT::Date::FORMAT_DATETIME, $CFG->{date_review_format}); } else { delete $rev->{Review_ModifyDate}; } $rev->{Review_Date} = GT::Date::date_transform($rev->{Review_Date}, GT::Date::FORMAT_DATETIME, $CFG->{date_review_format}); $rev->{Num} = $rev->{Review_WasHelpful} + $rev->{Review_WasNotHelpful}; $CFG->{review_convert_br_tags} and $rev->{Review_Contents} = Links::User::Review::_translate_html($rev->{Review_Contents}); push @reviews, $rev; } return { Review_Total => $review_total, Review_Count => scalar @reviews, Review_Loop => \@reviews }; } sub load_link { # ------------------------------------------------------------------- # This will return a fully formatted link. Deprecated in favour of # using load_link_info() + <%include link.html%> # my %vars = %{GT::Template->vars}; if ($Links::GLOBALS) { delete @vars{keys %$Links::GLOBALS}; } return Links::SiteHTML::display('link', \%vars); } sub load_link_info { # ------------------------------------------------------------------- # This will return the vars needed to display a fully formatted link (i.e. by # including link.html) # return Links::SiteHTML::tags(link => GT::Template->vars); } sub paging { # ------------------------------------------------------------------- # Generate the html needed for a paging toolbar # # The paging hash (retrieved from vars) should contain: # url # page # Only one of url or page should be included. # url is used when the generated url will be <%url%>;nh=<%page_number%> # page is used when the generated url will be <%build_root_url%>/<%page%>... # page_format # 1: <%build_root_url%>/<%page%>{index,more<%current_page%>}.html # Used in category, cool, new pages # 2: <%build_root_url%>/<%page%>{,_<%current_page%>}.html # Used in new page # num_hits # max_hits # current_page # # Options: # max_pages # The maximum number of pages to display (excluding boundary pages) # boundary_pages # When there are more pages than max_pages, this number of boundary # pages are added to the paging toolbar # style # 1: |< < [1 of 20] > >| # 2: [1 of 20] < > # 3: |< < 1 2 3 4 5 6 7 8 9 ... 20 > >| # style_next # style_prev # style_first # style_last # style_nonext # style_noprev # style_nofirst # style_nolast # These options allow you to change what's shown for the next/prev/etc # actions # lang_of # For styles 1 and 2, they use the format of " ". # This option allows you to change the english text of "of". # lang_button # For styles 1 and 2, a "Go" button is used for users which do not have # javascript support. This option allows you to change the button's # label. # button_id # If you've got two paging toolbars on a page, then you will need to # change the button_id so that the javascript can remove the button. # paging_pre # paging_post # This text or html is added before and after the paging html. # # There are two ways of setting the above options: # 1) Pass them in as arguments # 2) Create a global code ref named 'paging_options' and return the options # as a hash reference # Options passed as arguments override all options passed in via other methods, # followed by the global options and lastly the defaults contained in this # function. # # Note 1: You can override this function by creating a paging_override global # Note 2: The arguments to paging_override are slightly different. To keep # duplicated code to a minimum, %paging with the paging calculations done # is passed as the first argument (it also contains a few helper code # refs), and the second argument contains the options with defaults set. # The left over arguments are the passed in options (shouldn't be needed # since they have been merged into the options already). # my $vars = GT::Template->vars; return unless ref $vars->{paging} eq 'HASH'; my %paging = %{$vars->{paging}}; return if not $paging{num_hits} or $paging{num_hits} < $paging{max_hits}; %paging = ( page_format => 1, current_page => 1, form_hidden => '', %paging ); # Setup the default options my %paging_options; %paging_options = %{$vars->{paging_options}->()} if ref $vars->{paging_options} eq 'CODE'; my %options = ( max_pages => 10, boundary_pages => 1, style => 1, style_next => '>', style_prev => '<', style_first => '|<', style_last => '>|', style_nonext => '', style_noprev => '', style_nofirst => '', style_nolast => '', lang_of => 'of', lang_button => 'Go', button_id => 'paging_button', paging_pre => '', paging_post => '', %paging_options, @_ ); # Make all the page calculations $paging{num_pages} = int($paging{num_hits} / $paging{max_hits}); $paging{num_pages}++ if $paging{num_hits} % $paging{max_hits}; my ($start, $end); if ($paging{num_pages} <= $options{max_pages}) { $start = 1; $end = $paging{num_pages}; } elsif ($paging{current_page} >= $paging{num_pages} - $options{max_pages} / 2) { $end = $paging{num_pages}; $start = $end - $options{max_pages} + 1; } elsif ($paging{current_page} <= $options{max_pages} / 2) { $start = 1; $end = $options{max_pages}; } else { $start = $paging{current_page} - int($options{max_pages} / 2) + 1; $start-- if $options{max_pages} % 2; $end = $paging{current_page} + int($options{max_pages} / 2); } my ($left_boundary, $right_boundary); if ($end >= $paging{num_pages} - $options{boundary_pages} - 1) { $end = $paging{num_pages}; } else { $right_boundary = 1; } if ($start <= $options{boundary_pages} + 2) { $start = 1; } else { $left_boundary = 1; } my @pages; push @pages, 1 .. $options{boundary_pages}, '...' if $left_boundary; push @pages, $start .. $end; push @pages, '...', $paging{num_pages} - $options{boundary_pages} + 1 .. $paging{num_pages} if $right_boundary; $paging{pages} = \@pages; $paging{create_link} = sub { my ($page, $disp) = @_; my $ret = ''; $ret .= qq|{build_index_include} ? $CFG->{build_index} : '') : "$CFG->{build_more}$page$CFG->{build_extension}"; } elsif ($paging{page_format} == 2) { $ret .= "_$page" if $page > 1; $ret .= $CFG->{build_extension}; } } $ret .= qq|">$disp|; return $ret; }; $paging{select_value} = sub { my $page = shift; if ($paging{url}) { return $page; } else { my $ret = $paging{page}; if ($paging{page_format} == 1) { $ret .= $page == 1 ? ($CFG->{build_index_include} ? $CFG->{build_index} : '') : "$CFG->{build_more}$page$CFG->{build_extension}"; } elsif ($paging{page_format} == 2) { $ret .= "_$page" if $page > 1; $ret .= $CFG->{build_extension}; } return $ret; } }; if ($paging{url}) { # Figure out what needs to be submitted with the form (it *should* have ? in it # since with these queries, it *will* have other arguments) ($paging{form_action}, my $args) = $paging{url} =~ /^(.*?)\?(.*)$/; NV: for (split /[;&]/, $args) { my ($name, $val) = /([^=]+)=(.*)/ or next; $name = $IN->unescape($name); $val = $IN->unescape($val); # Skip these since Links::clean_output will put them in automatically for (@{$CFG->{dynamic_preserve}}, 'nh') { next NV if $name eq $_; } $paging{form_hidden} .= qq||; } $paging{select_name} = 'nh'; } else { $paging{form_action} = "$CFG->{db_cgi_url}/page.cgi"; $paging{select_name} = 'g'; } # Override this function. Pass in the updated %paging and %options hashes so # the calculations don't have to be duplicated in the override. if (ref $vars->{paging_override} eq 'CODE') { return $vars->{paging_override}->(\%paging, \%options, @_); } my $html; if ($options{style} == 1) { # |< < [1 of 20] > >| $html .= qq|
$paging{form_hidden}$options{paging_pre}|; if ($paging{current_page} != 1) { $html .= $paging{create_link}->(1, $options{style_first}) . ' ' . $paging{create_link}->($paging{current_page} - 1, $options{style_prev}) . ' '; } else { $html .= "$options{style_nofirst} $options{style_noprev} "; } $html .= qq| |; if ($paging{current_page} != $paging{num_pages}) { $html .= $paging{create_link}->($paging{current_page} + 1, $options{style_next}) . ' ' . $paging{create_link}->($paging{num_pages}, $options{style_last}); } else { $html .= "$options{style_nonext} $options{style_nolast}"; } $html .= qq|$options{paging_post}
|; } elsif ($options{style} == 2) { # [1 of 20] < > $html .= qq|
$paging{form_hidden}$options{paging_pre} |; if ($paging{current_page} != 1) { $html .= $paging{create_link}->($paging{current_page} - 1, $options{style_prev}) . ' '; } else { $html .= "$options{style_noprev} "; } if ($paging{current_page} != $paging{num_pages}) { $html .= $paging{create_link}->($paging{current_page} + 1, $options{style_next}); } else { $html .= $options{style_nonext}; } $html .= qq|$options{paging_post}
|; } elsif ($options{style} == 3) { # |< < 1 2 3 4 5 6 7 8 9 ... 20 > >| $html .= $options{paging_pre}; if ($paging{current_page} != 1) { $html .= $paging{create_link}->(1, $options{style_first}) . ' ' . $paging{create_link}->($paging{current_page} - 1, $options{style_prev}) . ' '; } else { $html .= "$options{style_nofirst} $options{style_noprev} "; } for (@{$paging{pages}}) { if ($_ eq '...') { $html .= "$_ "; } elsif ($_ == $paging{current_page}) { $html .= "$_ "; } else { $html .= $paging{create_link}->($_, $_) . ' '; } } if ($paging{current_page} != $paging{num_pages}) { $html .= $paging{create_link}->($paging{current_page} + 1, $options{style_next}) . ' ' . $paging{create_link}->($paging{num_pages}, $options{style_last}); } else { $html .= "$options{style_nonext} $options{style_nolast}"; } $html .= $options{paging_post}; } return \$html; } sub format_title { # ------------------------------------------------------------------- # Format a title # # Options: # separator (required) # The separator used to join the items. # no_escape_separator # Set this to a true value if you do not wish to HTML escape the separator. # include_home # Whether or not to include Home as the first entry. Default is no. # include_last # Whether or not to include the last entry. Default is yes. # link_type # How the items should be linked: # 0: No items linked # 1: All items linked separately # 2: All except the last item linked separately # 3: All items linked as one single link (using the last item's URL) # no_span # Don't add the span tags around the last portion of the title. Default is to include the span tags. # # Note: You can override this function by creating a format_title_override global # my ($title_loop, %options) = @_; return unless ref $title_loop eq 'ARRAY'; my $vars = GT::Template->vars; if (exists $vars->{format_title_override}) { return $vars->{format_title_override}->(@_); } if (!exists $options{include_last}) { $options{include_last} = 1; } if (!$options{include_last}) { pop @$title_loop; } my $ret; $options{separator} = GT::CGI::html_escape($options{separator}) unless $options{no_escape_separator}; for (0 .. $#$title_loop) { next unless $_ or $options{include_home}; $ret .= '' if $_ == $#$title_loop and not $options{no_span} and $options{include_last}; if ($options{link_type} == 1 or ($options{link_type} == 2 and $_ != $#$title_loop)) { $ret .= qq|$title_loop->[$_]->{Name}|; } else { $ret .= $title_loop->[$_]->{Name}; } $ret .= $options{separator} unless $_ == $#$title_loop; $ret .= '' if $_ == $#$title_loop and not $options{no_span} and $options{include_last}; } if ($options{link_type} == 3) { $ret = qq|$ret|; } return \$ret; } sub column_split { # ------------------------------------------------------------------- # Calculate where the columns should be # my ($items, $columns) = @_; if ($items % $columns > 0) { $items += ($columns - $items % $columns); } return $items / $columns; } sub image_url { # ------------------------------------------------------------------- # Takes an filename and using the current template set and theme, returns # the url of the image. It first checks if the file exists in the theme's # image directory, checks the template's image directory, and then tries # to check the template inheritance tree for more image directories. # my $image = shift; my ($template, $theme) = Links::template_set(); if (-e "$CFG->{build_static_path}/$template/images/$theme/$image") { return "$CFG->{build_static_url}/$template/images/$theme/$image"; } # Grab the inheritance tree of the template set and grab the basename of # each template set path (making an assumption that they won't do anything # crazy with their inheritance). require GT::File::Tools; require GT::Template::Inheritance; my @paths = GT::Template::Inheritance->tree(path => "$CFG->{admin_root_path}/templates/$template", local => 0); for (@paths) { my $tpl = GT::File::Tools::basename($_); next if $tpl eq 'browser'; if (-e "$CFG->{build_static_path}/$tpl/images/$image") { return "$CFG->{build_static_url}/$tpl/images/$image"; } } # The image doesn't exist here, but return it anyway return "$CFG->{build_static_url}/$template/images/$image"; } 1;