#!/usr/bin/perl #----------------------------------------------------------------------- # display_results.fcg # # ** MYSQL VERSION ** # Fast CGI handler for displaying the search form submit and # handling the results of the search criteria. #----------------------------------------------------------------------- # Copyright (c) 2002 by Myxa Corporation. All rights reserved. # RCSID: $Id: display_results.fcg,v 1.71 2006/04/29 00:50:14 syd Exp $ # $RCSfile: display_results.fcg,v $ # $Revision: 1.71 $ # $Author: syd $ # $Date: 2006/04/29 00:50:14 $ #----------------------------------------------------------------------- use strict; BEGIN {use CGI::Carp qw(fatalsToBrowser);} use FindBin; use lib "$FindBin::Bin/../lib"; use Localize; use lib @Localize::LocalLibPaths; use AppConfig qw/ :argcount /; use AppConfig::File; use Date::Manip qw/ UnixDate ParseDate/; use PageNav qw/:custom :page_vars/; use SearchLog qw(search_started search_ended); use mysql; use Display; use FcgiLoop; use Debug; use Groups; use Session; use CGI_Startup; use get_view_dates; use vars qw/$style_img_path/; Debug::trace('trace', 'display_results.fcg starting'); our %LINK_STYLE = ( 'text' => sub { my ($cp, $lp) = @_; PageNav::set_link_style(jump=>sub{qq($cp of $lp)}); }, 'text_var' => sub { my ($cp, $lp, $b, $f, $l, $n) = @_; PageNav::set_link_style( jump=>sub{qq($cp of $lp)}, back => $b, next => $n, first=> $f, last => $l); }, 'image' => sub { my ($cp,$lp,$b,$f,$l,$n) = @_; PageNav::set_link_style( back =>qq(Back), next =>qq(Next), first=>qq(First), last =>qq(Last), jump =>qq(Page $cp of $lp)); }, 'default' => sub { my ($cp, $lp) = @_; PageNav::set_link_style( back =>qq(Back), next =>qq(Next), first=>qq(First), last =>qq(Last), jump =>qq(Page $cp of $lp)); }, ); our @search_fields = qw / artist_or_title artist_title category_id conductor end_date featured org org_id perf region region_id series series_id season season_id start_date subcategory_id subscr ta title_or_composer type_id venue venue_id /; FcgiLoop { my $cgi = shift; Debug::fcg_dump($cgi, 'trace', "Entering main loop. Running as " . (getpwuid $>)[0] . ", group " . (getgrgid $))[0] . "."); # # get existing session, if any # my $input = get_session_id($cgi); my $context = $input->{context}; my $ncontext = $input->{ncontext}; my $id = $input->{ncontext_id}; my $html_file = $input->{file}; my $style = $input->{style}; my $group = $input->{group}; my @srch_vars = (); Debug::trace(trace => "Style = $style, group = $group"); my @cfg_dirs; my $CONFIG = AppConfig->new({GLOBAL=>{ARGCOUNT => ARGCOUNT_ONE}}); $CONFIG->define(qw( LINK_STYLE LINK_FIRST LINK_LAST LINK_BACK LINK_NEXT TEMPLATE_RESULT TEMPLATE_INFRAME TERM_START TERM_END STYLE_AVAILABLE_ICON STYLE_BUY_ICON STYLE_SOLDOUT_ICON STYLE_CANCELED_ICON STYLE_POSTPONED_ICON ) ); push @cfg_dirs, grep {-e $_} ( "$Localize::cfg_dir/process_search.cfg", "$Localize::cfg_dir/$style/process_search.cfg", ); $CONFIG->file(@cfg_dirs); # # pre-load config style items # $CONFIG::Style_icons{available_icon} = $CONFIG->STYLE_AVAILABLE_ICON(); $CONFIG::Style_icons{buy_icon} = $CONFIG->STYLE_BUY_ICON(); $CONFIG::Style_icons{soldout_icon} = $CONFIG->STYLE_SOLDOUT_ICON(); $CONFIG::Style_icons{canceled_icon} = $CONFIG->STYLE_CANCELED_ICON(); $CONFIG::Style_icons{postponed_icon} = $CONFIG->STYLE_POSTPONED_ICON(); my $items; # # Sanity check # items must be defined and an array, or its not proper # if ($context) { $items = $context->get('items'); my $num = ref $items eq 'ARRAY' ? scalar(@$items) : 0; Debug::trace(trace => "\$context->get('items') has $num items in it."); } else { Debug::trace(trace => "no context, items count is 0"); } # # If a pagenav variable exists, and $context->{items} exists, # then we've already done the search and are just paging around. # my $sql_query; unless ($input->{nav} && defined $items) { Debug::trace(trace => 'No pre-existing search! Searching anew.'); # # stuff for logging the search # my $referer = $ENV{HTTP_REFERER}; my $query = $cgi->url(-query=>1); my $userip = join ',', grep $_, @ENV{'REMOTE_HOST', 'REMOTE_ADDR'}; my $sid; if ($input->{session}) { $sid = $input->{session_id}; } else { $sid = "none"; } load_prior_search($input, $context); my $which = $input->{search} eq 'perfs'? 'perfs' : 'events'; search_started("style=$style","EventDB: display_results($which)", "URL=$query;REFERER=$referer;IP=$userip;SID=$sid"); $items = do_search($input, $ncontext, $which, \@srch_vars); Debug::trace(trace => "do_search[$which]:"); Debug::trace(trace => "\$items: '$items'") if ref $items eq ''; Debug::trace(trace => 'Num items: ' . scalar(@$items) ) if ref $items eq 'ARRAY'; # END SEARCH LOGGING if ($items && ref $items eq 'ARRAY' && @$items > 0 ) { my $sr; if ($which eq 'perfs') { $sr = 'p: '; foreach (@$items) { $sr .= $_->{org_id} . ':' . $_->{perf_id} . ','; } } else { $sr = 'e: '; foreach (@$items) { $sr .= $_->{org_id} . ':' . $_->{event_id} . ','; } } chop $sr; search_ended('ok', $sr); } else { search_ended('0', ''); } } else { Debug::trace(trace => "Using existing search items."); if ($ncontext) { $ncontext->set('search_vars', $context->get('search_vars')); } } if ($ncontext) { $ncontext->set(items => $items); # save! } # Set up page navigation PageNav::reset(); PageNav::set_cgi_handler( $cgi->url(-absolute => 1) ); my %pagenav_vars; PageNav::set_cgi_vars(id => $id); PageNav::set_nav_border(0); PageNav::set_nav_cellalign('center'); PageNav::set_nav_cellvalign('middle'); my $rows_per_page = $input->{itemsperpage} || 25; PageNav::set_page($items,$rows_per_page,$input->{nav}); # Handle configuration items $style_img_path = Groups::img_path($style); if (exists $LINK_STYLE{$CONFIG->LINK_STYLE()}) { if ($CONFIG->LINK_STYLE eq 'image' || $CONFIG->LINK_STYLE eq 'text_var') { $LINK_STYLE{$CONFIG->LINK_STYLE()} -> ( $PageNav::cur_page, $PageNav::last_page, $CONFIG->LINK_BACK(), $CONFIG->LINK_FIRST(), $CONFIG->LINK_LAST(), $CONFIG->LINK_NEXT() ); } else # text { $LINK_STYLE{$CONFIG->LINK_STYLE()}->($PageNav::cur_page, $PageNav::last_page); } } else { $LINK_STYLE{'default'}->(); } # Get PageNav variables my $pn_vars = PageNav::get_pagenav(); # store query string my $query = $cgi->self_url; $query =~ s/.*\?//; # # pre template debug dump # $context->dump(trace => "Context before display_template") if $context; $ncontext->dump(trace => "New Context before display_template") if $ncontext; my $search_vars; if ($ncontext) { $search_vars = $ncontext->get('search_vars'); } # Display the search result page with the results inserted. my %args = ( %{$input}, id => $id, sid => $input->{session_id}, myurl => $cgi->url(), pn => $pn_vars, items => $items, curr_page_items => [ @$items[$pn_vars->{cur_top_index} .. $pn_vars->{cur_bot_index}] ], search_criteria => $query, search_vars => $search_vars, document_name => $ENV{DOCUMENT_NAME}, host_uri => get_host_uri(), ); # choose template to display my $template = $input->{file}; $template ||= $CONFIG->TEMPLATE_INFRAME() if $cgi->param('inframeset'); $template ||= $CONFIG->TEMPLATE_RESULT (); if (exists $input->{cookies}) { print $cgi->header(-cookie => $input->{cookies}); } else { print $cgi->header; } display_template($template, \%args, $input); }; sub do_search { my $input = shift; # hash ref of input values my $nctx = shift; # new context to save search params my $search = shift; # perfs or events my $srch_vars = shift; # Search variable array to save what is being searched for Debug::trace(trace => "In do_search(input, nctx, $search, srch_vars)"); my %search; # User's search parameters $search{search} = $search; # -------------------- # Gather Form Data: # -------------------- # If there's a view associated with the query, adjust the start and end dates if necessary if ($input->{view}) { # Compute new start/end dates for view period my $sd = $input->{start_date} || undef; my $ed = $input->{end_date} || undef; my ($nsd, $ned) = get_view_dates($input->{view}, $sd, $ed); $search{start_date} = $nsd; $search{end_date} = $ned; $input->{start_date} = $nsd; $input->{end_date} = $ned; # Remove prev_ or next_ from view variable my $new_view = $input->{view}; $new_view =~ s/^(?: prev_ | next_ )//x; $input->{view} = $new_view; } my $start_date = mysql::mysql_date( $input->{start_date} || 'today' , 0); $input->{start_date} = $start_date; $search{start_date} = $start_date; my $end_date = $input->{end_date}; if (defined $end_date) { $end_date = mysql::mysql_date($end_date), 0; $input->{end_date} = $end_date; $search{end_date} = $end_date; } foreach (qw(view start_date end_date)) { if ($input->{$_}) { push @$srch_vars, $_; } } # -------------------- # Build SQL # -------------------- # FROM and WHERE clauses my %from = map {$_=>1} ('event ev', 'performance perf'); my @where = ('ev.org_id=perf.org_id', 'ev.event_id=perf.event_id'); # event type where clause if (my $parm = $input->{type_id}) { my @type_ids = ref $parm eq 'ARRAY'? @$parm : ($parm); @type_ids = grep $_, @type_ids; if (@type_ids) { push @$srch_vars, 'type_id'; $search{type_id} = \@type_ids; my $type_ids = join ',', @type_ids; push @where, "ev.type_id IN ($type_ids)"; } } # category where clause if (my $parm = $input->{category_id}) { my @cat_ids = ref $parm eq 'ARRAY'? @$parm : ($parm); @cat_ids = grep $_, @cat_ids; if (@cat_ids) { push @$srch_vars, 'category_id'; $search{category_id} = \@cat_ids; my $cat_list = join ',', @cat_ids; $from{'organization org'} = 1; push @where, "( ev.category_id IN ($cat_list) OR (ev.category_id=0 AND ev.org_id=org.org_id AND org.category_id IN ($cat_list)) )"; } } # subcategory where clause if (my $parm = $input->{subcategory_id}) { my @subcats = ref $parm eq 'ARRAY'? @$parm : ($parm); @subcats = grep $_, @subcats; if (@subcats) { push @$srch_vars, 'subcategory_id'; $search{subcategory_id} = \@subcats; my $subcats = join ',', @subcats; push @where, "ev.subcategory_id in ($subcats)"; } } # Date range push @where, "perf.perf_end_date >= '$start_date'" if $start_date; push @where, "perf.perf_date <= '$end_date'" if $end_date; # Specific performance id? if (my $perf = $input->{perf}) { push @$srch_vars, 'perf'; $search{perf} = $perf; push @where, "perf.perf_id = $perf"; } # region(s) where clause if (my $parm = $input->{region}) { my @reg_ids = ref $parm eq 'ARRAY'? @$parm : ($parm); @reg_ids = grep $_, @reg_ids; if (@reg_ids) { push @$srch_vars, 'region'; $search{region} = \@reg_ids; my $reg_list = join ',', @reg_ids; $from{'region_venue rv'} = 1; push @where, "perf.venue_id=rv.venue_id", "rv.region_id IN ($reg_list)"; } } # region id where clause if (my $region_id = lc $input->{region_id}) { push @$srch_vars, 'region_id'; $search{region_id} = $region_id; $region_id =~ tr/\'%//d; $from{'region_venue rv'} = 1; push @where, "perf.venue_id=rv.venue_id", "rv.region_id = $region_id"; } # organization where clause if (my $org = lc $input->{org}) { push @$srch_vars, 'org'; $search{org} = $org; $org =~ tr/\'%//d; $from{'organization org'} = 1; push @where, "org.org_id=ev.org_id", "LOWER(org.name) LIKE '%$org%'"; } # organization id where clause if (my $org_id = lc $input->{org_id}) { push @$srch_vars, 'org_id'; $search{org_id} = $org_id; $org_id =~ tr/\'%//d; push @where, "ev.org_id=$org_id"; } # group where clause - allow child groups if (my $grp = $input->{restrict_group}) { push @$srch_vars, 'group'; $search{group} = $grp; $grp =~ tr/\'//d; $from{'groups gr'} = 1; $from{'group_org go'} = 1; push @where, "ev.org_id=go.org_id", "go.group_id=gr.group_id", "gr.name='$grp'"; } elsif (my $rgrp = $input->{group}) { push @$srch_vars, 'group'; $search{group} = $rgrp; $rgrp =~ tr/\'//d; $from{'groups gr'} = 1; $from{'group_org go'} = 1; push @where, "ev.org_id=go.org_id", "go.group_id=gr.group_id", "gr.name='$rgrp'"; } # series where clause if (my $series = lc $input->{series}) { push @$srch_vars, 'series'; $search{series} = $series; $series =~ tr/\'%//d; $from{'series ser'} = 1; my ($id, $code) = split('\|', $series); push @where, "ser.series_id=ev.series_id", "ser.org_id=$id", "LOWER(ser.description) LIKE '%$code%'"; } # series_id where clause if (my $sid = $input->{series_id}) { push @$srch_vars, 'series_id'; $search{series_id} = $sid; my ($id, $code) = split('\|', $sid); push @where, "ev.org_id=$id", "ev.series_id=$code"; } # season where clause if (my $season = lc $input->{season}) { push @$srch_vars, 'season'; $search{season} = $season; $season =~ tr/\'%//d; $from{'season sea'} = 1; my ($id, $code) = split('\|', $season); push @where, "sea.season_id=ev.season_id", "sea.org_id=$id", "LOWER(sea.description) LIKE '%$code%'"; } # season_id where clause if (my $sid = $input->{season_id}) { push @$srch_vars, 'season_id'; $search{season_id} = $sid; my ($id, $code) = split('\|', $sid); push @where, "ev.org_id=$id", "ev.season_id=$code"; } # venue where clause if (my $venue = lc $input->{venue}) { push @$srch_vars, 'venue'; $search{venue} = $venue; push @where, "perf.venue_id=$venue" unless $venue eq 'any'; } # venue_id where clause if (my $venue_id = lc $input->{venue_id}) { push @$srch_vars, 'venue_id'; $search{venue_id} = $venue_id; push @where, "perf.venue_id=$venue_id" unless $venue_id eq 'any'; } # featured where clause if (my $featured = lc $input->{featured}) { push @$srch_vars, 'featured'; $search{featured} = $featured; $featured =~ tr/\'%//d; $from{'artist a1'} = 1; $from{'artist_event ae1'} = 1; push @where, "ae1.org_id=ev.org_id", "ae1.event_id=ev.event_id", "ae1.org_id = a1.org_id", "ae1.artist_id=a1.artist_id", "LOWER(a1.name) LIKE '%$featured%'"; } # conductor where clause if (my $conductor = lc $input->{conductor}) { push @$srch_vars, 'conductor'; $search{conductor} = $conductor; $conductor =~ tr/\'%//d; $from{'artist a2'} = 1; $from{'artist_event ae2'} = 1; push @where, "ae2.org_id=ev.org_id", "ae2.event_id=ev.event_id", "ae2.org_id = a2.org_id", "ae2.artist_id=a2.artist_id", "lower(a2.name) LIKE '%$conductor%'", "lower(a2.title)='conductor'"; } # "artist title" where clause if (my $artist_title = lc $input->{artist_title}) { push @$srch_vars, 'artist_title'; $search{artist_title} = $artist_title; $artist_title =~ tr/\'%//d; $from{'artist a3'} = 1; $from{'artist_event ae3'} = 1; push @where, "ae3.org_id=ev.org_id", "ae3.event_id=ev.event_id", "ae3.org_id = a3.org_id", "ae3.artist_id=a3.artist_id", "a3.title LIKE '%$artist_title%'"; } # "artist name or title" where clause if (my $artist_or_title = lc $input->{artist_or_title}) { push @$srch_vars, 'artist_or_title'; $search{artist_or_title} = $artist_or_title; $from{'artist a4'} = 1; $from{'artist_event ae4'} = 1; push @where, "ae4.org_id=ev.org_id", "ae4.event_id=ev.event_id", "ae4.org_id = a4.org_id", "ae4.artist_id=a4.artist_id", "(a4.name LIKE '%$artist_or_title%' OR a4.title LIKE '%$artist_or_title%')"; } # title or composer where clause if (my $torc = lc $input->{title_or_composer}) { push @$srch_vars, 'title_or_composer'; $search{title_or_composer} = $torc; $torc =~ tr/\'%//d; $from{'program pgm'} = 1; push @where, "pgm.org_id=ev.org_id", "pgm.event_id=ev.event_id", "(LOWER(pgm.title) LIKE '%$torc%' OR lower(pgm.composer) LIKE '%$torc%' OR ev.title LIKE '%$torc%' )"; } # subscription where clause if (my $subscr = $input->{subscr}) { push @$srch_vars, 'subscr'; $search{subscr} = $subscr; $from{'subscription sub'} = 1; $from{'subscr_perf sp'} = 1; my ($id, $code) = split('\|', $subscr); push @where, "sub.code='$code'", "sub.org_id=$id", "perf.org_id=sp.org_id", "perf.perf_id=sp.perf_id", "sp.org_id=sub.org_id", "sp.pkg_id=sub.pkg_id"; } # ticket agent where clause if (my $tckt_agent = $input->{ta}) { push @$srch_vars, 'ta'; $search{ta} = $tckt_agent; push @where, "ev.agent_id = $tckt_agent"; } # Restrict to published events push @where, 'ev.published >= 3'; # Save input search parameters if ($nctx) { $nctx->set(search_vars => \%search); } # Put it all together my $from = join ', ', sort keys %from; my $where = join ' AND ', @where; my ($sql, $field); if ($search eq 'events') { $sql = "SELECT DISTINCT ev.org_id, ev.event_id FROM $from WHERE $where ORDER BY perf.perf_date, perf.perf_time, perf.perf_end_date, perf.perf_end_time, ev.org_id, ev.event_id"; $field = 'event_id'; } else { $sql = "SELECT DISTINCT perf.org_id, perf.perf_id FROM $from WHERE $where ORDER BY perf.perf_date, perf.perf_time, perf.perf_end_date, perf.perf_end_time, perf.org_id, perf.perf_id"; $field = 'perf_id'; } # -------------------- # Process SQL results # -------------------- if (mysql::multiple_select($sql)) { my @items; my %found; foreach (@$dbi::data_ref) { # # there is a bug in mysql where the distinct select here doesn't work # we need to also filter it ourselves # my $key = $_->[0] . '|' . $_->[1]; next if $found{$key}; $found{$key}++; # # end of work around # my %rec; $rec{org_id} = $_->[0]; $rec{$field} = $_->[1]; push @items, \%rec; } return \@items; } return undef; } sub load_prior_search { my $input = shift; # ref of input values my $ctx = shift; # Context object Debug::trace(trace => "In load_prior_search($input, $ctx)"); return unless $ctx; my $search = $ctx->get('search_vars'); my $found = 0; # # look for any prior input values in input that could be in search # if none found, set input to search from session # foreach (@search_fields) { if ($input->{$_}) { Debug::trace(trace => "input{$_} = '" . $input->{$_} . "'"); $found = 1; last; } } Debug::trace(trace => "found = $found"); return if $found; # # load up the input with the found search parameters # foreach (keys %{$search}) { my $old = $input->{$_}; $input->{$_} = $search->{$_}; Debug::trace(trace => "Restoring $_ from '$old' to " . $input->{$_}); } }