Error in Include(/www/htdocs/nge/dispatcher.html):
Can't call method "region_name" on an undefined value at /www/lib/perl/NatGeo/Expeditions/Widget/Expertlist.pm line 200.

[    1]    1: package NatGeo::Expeditions::Widget::Expertlist;
[    2]    2: use strict;
[    3]    3: use vars qw(@ISA);
[    4]    4: use NatGeo::Expeditions::Widget;
[    5]    5: use Data::Dumper;
[    6]    6: use POSIX qw{ceil};
[    7]    7: use Cheetah::Config;
[    8]    8: 
[    9]    9: @ISA = qw(NatGeo::Expeditions::Widget);
[   10]   10: 
[   11]   11: my $config = Cheetah::Config->new;
[   12]   12: my $schema = $config->get('NatGeo::Expeditions::DBSchema');
[   13]   13: my $PER_PAGE;
[   14]   14: 
[   15]   15: sub initialize {
[   16]   16:     my $self = shift;
[   17]   17:     my @experts;
[   18]   18: 
[   19]   19:     # Need to set this here, at initialize-time, to make sure that EVERY apache child has the default value
[   20]   20:     # when this logic starts.  We hack this for the featured expert page, so it needs to be reset since
[   21]   21:     # it's a global variable and may have changed.
[   22]   22:     $PER_PAGE = $config->get(__PACKAGE__ . '::num_per_page');
[   23]   23:    
[   24]   24:     $self->__load('templates/globaldata.xml');
[   25]   25:     $self->featured(0);
[   26]   26: 
[   27]   27:     # This is more or less guaranteed to not have duplicates, FYI.
[   28]   28:     my $expert_ids = $self->get_expert_ids;
[   29]   29:     my $ids = join ', ', @{$expert_ids};
[   30]   30: 
[   31]   31:     # First, grab traditional experts
[   32]   32:     my %opts = (
[   33]   33:         with => { biographies => {},
[   34]   34:                   photo => {},
[   35]   35:                   tripdates => {
[   36]   36:                       itinerary => {
[   37]   37:                           expedition => {}    # traditional part, expedition associations via tripdates
[   38]   38:                       }
[   39]   39:                   },
[   40]   40:                   specialties => {},
[   41]   41:                   expeditions => {},          # custom part, only expeditions tagged as custom will be considered later
[   42]   42:                 },
[   43]   43:         where => "tripdates.begin_date >= now()
[   44]   44:                   AND expeditions.expedition_status_id = 1
[   45]   45:                   AND tripdate_status_id = 1
[   46]   46:                   AND experts.expert_id IN ($ids)",
[   47]   47:     );
[   48]   48:     @experts = NatGeo::Expeditions::Model::Expert->search_deep(%opts) if $ids;
[   49]   49: 
[   50]   50:     # Second, grab custom trip-only associations
[   51]   51:     %opts = (
[   52]   52:         with => {
[   53]   53:             biographies => {},
[   54]   54:             photo => {},
[   55]   55:             specialties => {},
[   56]   56:             expeditions => {},
[   57]   57:         },
[   58]   58:         where => qq{
[   59]   59:             expeditions.expedition_status_id = 1
[   60]   60:             AND expeditions.is_custom IS TRUE
[   61]   61:             AND experts.expert_id IN ($ids)
[   62]   62:         },
[   63]   63:     );
[   64]   64:     my @custom_experts = NatGeo::Expeditions::Model::Expert->search_deep(%opts) if $ids;
[   65]   65: 
[   66]   66:     # Traditional search should be a superset with regards to custom trips, so we only graft in
[   67]   67:     # custom trips if they don't exist already, then sort by last name for the final list.
[   68]   68:     # Maintain special-ness though, those should still bubble to the top.
[   69]   69:     foreach my $exp (@custom_experts) {
[   70]   70:         push @experts, $exp unless (grep { $_->id() == $exp->id() } @experts);
[   71]   71:     }
[   72]   72:     $self->experts([
[   73]   73:         sort { $b->is_special <=> $a->is_special || $a->lastname cmp $b->lastname } @experts
[   74]   74:     ]);
[   75]   75: 
[   76]   76:     return;
[   77]   77: }
[   78]   78: 
[   79]   79: sub get_expert_ids {
[   80]   80:     my $self = shift;
[   81]   81:     my $arg;
[   82]   82:     my ($from, $where);
[   83]   83:     my @rv;
[   84]   84: 
[   85]   85:     my @options = split '/', $self->{request}->{_page}->{real_path};
[   86]   86:     shift @options; # pop the /experts off the top
[   87]   87: 
[   88]   88:     if(!scalar(@options)) {                                    # no options
[   89]   89:         $self->page_number(1);
[   90]   90:         $self->featured(1);
[   91]   91:         $self->search_type('featured');
[   92]   92:     } elsif(scalar(@options) == 1 && $options[0] =~ /^\d+$/) { # page n
[   93]   93:         $self->page_number($options[0]);
[   94]   94:     } elsif(scalar(@options) == 2) {                           # filter
[   95]   95:         $self->search_type($options[0]);
[   96]   96:         $self->search_param($options[1]);
[   97]   97:         $self->page_number(1);
[   98]   98:     } elsif(scalar(@options) == 3 && $options[2] =~ /^\d+$/) {  # filter, page n
[   99]   99:         $self->search_type($options[0]);
[  100]  100:         $self->search_param($options[1]);
[  101]  101:         $self->page_number($options[2]);
[  102]  102:     }
[  103]  103: 
[  104]  104:     if($self->search_type eq 'last_name') {
[  105]  105:         $where = "upper(e.lastname) LIKE ? AND";
[  106]  106:         $arg = uc($self->search_param).'%';
[  107]  107:     } elsif ($self->search_type eq 'destination') {
[  108]  108:         $where = "x.expedition_id = er.expedition_id
[  109]  109:                   AND er.region_id = r.region_id
[  110]  110:                   AND make_url_safe(r.region_name) = ? AND";
[  111]  111:         $from = ", $schema.expeditions_regions er, $schema.regions r";
[  112]  112:         $arg = $self->search_param;
[  113]  113:     } elsif ($self->search_type eq 'specialty') {
[  114]  114:         $where = "s.specialty_id = es.specialty_id 
[  115]  115:                   AND es.expert_id = e.expert_id
[  116]  116:                   AND make_url_safe(s.description) = ? AND";
[  117]  117:         $from = ", $schema.specialties s, $schema.experts_specialties es";
[  118]  118:         $arg = $self->search_param;
[  119]  119:     } elsif($self->search_type eq 'featured') {
[  120]  120:         $where = 'e.is_special IS TRUE AND';
[  121]  121: 
[  122]  122:         # hack to make a relatively-infinite per-page setting because the featured experts
[  123]  123:         # page does not have pagination
[  124]  124:         $PER_PAGE = 999;
[  125]  125:     }
[  126]  126: 
[  127]  127:     $self->offset(($self->page_number - 1) * $PER_PAGE);
[  128]  128: 
[  129]  129:     my $sql = qq{
[  130]  130: SELECT x.* FROM (
[  131]  131:     -- traditional search, go through active tripdates
[  132]  132:     SELECT DISTINCT e.expert_id, e.is_special, e.is_featured, e.lastname
[  133]  133:     FROM $schema.experts e, $schema.experts_tripdates et, $schema.tripdates t, $schema.itineraries i, $schema.expeditions x $from
[  134]  134:     WHERE $where
[  135]  135:     e.expert_id = et.expert_id
[  136]  136:     AND t.itinerary_id = i.itinerary_id
[  137]  137:     AND i.expedition_id = x.expedition_id
[  138]  138:     AND x.expedition_status_id = 1
[  139]  139:     AND t.tripdate_id = et.tripdate_id
[  140]  140:     AND t.tripdate_status_id = 1 
[  141]  141:     AND t.begin_date > NOW()
[  142]  142: 
[  143]  143:     UNION ALL
[  144]  144: 
[  145]  145:     -- custom trip integration, go through expert assignments and look for custom trips
[  146]  146:     SELECT DISTINCT e.expert_id, e.is_special, e.is_featured, e.lastname
[  147]  147:     FROM $schema.experts e, $schema.expert_assignments ea, $schema.expeditions x $from
[  148]  148:     WHERE $where
[  149]  149:     ea.expert_id = e.expert_id
[  150]  150:     AND ea.expedition_id = x.expedition_id
[  151]  151:     AND x.expedition_status_id = 1
[  152]  152:     AND x.is_custom IS TRUE
[  153]  153: ) x};
[  154]  154: 
[  155]  155:     my $dbh = NatGeo::Expeditions::DB->new;
[  156]  156:     my $sth = $dbh->prepare($sql);
[  157]  157:     if($arg) { 
[  158]  158:         $sth->execute(map { $arg } (1 .. 2));
[  159]  159:     } else {
[  160]  160:         $sth->execute;
[  161]  161:     }
[  162]  162: 
[  163]  163:     my $i = 0;
[  164]  164:     while(my $r = $sth->fetchrow_hashref) {
[  165]  165:         if(($i >= $self->offset && scalar @rv <= ($PER_PAGE + 1))
[  166]  166:            || $self->featured) {
[  167]  167:             push @rv, $r->{expert_id} unless (grep { $_ == $r->{expert_id} } @rv);  # no dupes, please
[  168]  168:         }
[  169]  169:         $i++;
[  170]  170:     }
[  171]  171:     
[  172]  172:     my $page_count = ceil($i / $PER_PAGE);
[  173]  173:     $self->pages($page_count);
[  174]  174: 
[  175]  175:     return \@rv;
[  176]  176: }
[  177]  177: 
[  178]  178: sub render {
[  179]  179:     my $self = shift;
[  180]  180:     my $rv;
[  181]  181: 
[  182]  182:     my $title;
[  183]  183:     if($self->search_type =~ /^(specialty|last_name|destination)$/i) {
[  184]  184:         my $title_type = join ' ', map {ucfirst($_)} split '_', $self->search_type;
[  185]  185:         my $title_param;
[  186]  186: 
[  187]  187:         if($self->search_type eq 'last_name') {
[  188]  188:             $title_param = uc($self->search_param);
[  189]  189:         } elsif($self->search_type eq 'specialty') {
[  190]  190:             my $specialty = NatGeo::Expeditions::Model::Specialty->search(
[  191]  191:                 where => 'make_url_safe(description) = ?',
[  192]  192:                 execargs => [$self->search_param]
[  193]  193:             );
[  194]  194:             $title_param = $specialty->description;
[  195]  195:         } elsif($self->search_type eq 'destination') {
[  196]  196:             my $region = NatGeo::Expeditions::Model::Region->search(
[  197]  197:                 where => 'make_url_safe(region_name) = ?',
[  198]  198:                 execargs => [$self->search_param]
[  199]  199:             );
[* 200]  200:             $title_param = $region->region_name;
[  201]  201:         }
[  202]  202: 
[  203]  203:         $title = "Experts by $title_type: $title_param";
[  204]  204:     } elsif($self->featured) {
[  205]  205:         $title = 'Featured Experts';
[  206]  206:     }
[  207]  207: 
[  208]  208:     $rv .= "<h3>$title</h3>";
[  209]  209:     if($self->featured) {
[  210]  210:         $rv .= qq{<p class="divider">}.$self->intro.qq{</p>};
[  211]  211:     }
[  212]  212: 
[  213]  213:     if(!scalar @{$self->experts}) {
[  214]  214:         $rv.= $self->__no_results;
[  215]  215:         return $rv;
[  216]  216:     }
[  217]  217: 
[  218]  218:     $rv .= $self->__page_links;
[  219]  219:     $rv .= '<div id="results" class="trip">';
[  220]  220: 
[  221]  221:     for(1..$PER_PAGE) {
[  222]  222:         my $index = $_ - 1;
[  223]  223:         if($self->experts->[$index]) {
[  224]  224:             $rv .= $self->__render_expert($self->experts->[$index]);
[  225]  225:         }
[  226]  226:     }
[  227]  227: 
[  228]  228:     $rv .= '</div><!-- results -->';
[  229]  229:     $rv .= $self->__page_links;
[  230]  230: 
[  231]  231:     $rv .= $self->all_experts_butan if $self->featured;
[  232]  232: 
[  233]  233:     return $rv;
[  234]  234: }
[  235]  235: 
[  236]  236: sub __render_expert {
[  237]  237:     my $self = shift;
[  238]  238:     my $expert = shift;
[  239]  239:     my $rv;
[  240]  240: 
[  241]  241:     my $teaser;
[  242]  242:     eval { $teaser = $expert->find_bio->biography_text; };
[  243]  243: 
[  244]  244:     if ($teaser) {
[  245]  245:         my @teaser_parts = split /\./, $teaser;
[  246]  246:         $teaser = (join '.', @teaser_parts[0..1]) . '...';
[  247]  247:     }
[  248]  248: 
[  249]  249:     my $url = $self->url_base.'/experts/'.$expert->link_text.'/detail';
[  250]  250:     my $img = ($expert->photo) ? $expert->photo->image_path('expert_listing') : '';
[  251]  251: 
[  252]  252:     $rv .= qq{
[  253]  253:     <div class="browse_expert">
[  254]  254:         <a href="$url" ><img src="$img" width="135" class="shadow thumb" /></a>
[  255]  255:         <h4><a href="$url">}.$expert->full_name.qq{</a></h4>}
[  256]  256:     # .$self->__expert_specialties($expert)
[  257]  257:     .qq{<p>$teaser <a href="$url">More&nbsp;&raquo;</a></p>
[  258]  258:         <div class="clearfix">}
[  259]  259:             .$self->__expert_trips($expert)
[  260]  260:         .qq{</div>
[  261]  261:     </div><!-- browse_expert -->
[  262]  262:     };
[  263]  263: 
[  264]  264:     return $rv;
[  265]  265: }
[  266]  266: 
[  267]  267: sub __expert_trips {
[  268]  268:     my $self = shift;
[  269]  269:     my $expert = shift;
[  270]  270:     my $rv;
[  271]  271: 
[  272]  272:     my %expeditions;
[  273]  273: 
[  274]  274:     # Traditional search
[  275]  275:     if ($expert->can('tripdates') && $expert->tripdates->is_populated()) {
[  276]  276:         %expeditions = (%expeditions, map { $_->itinerary->expedition->expedition_id => $_->itinerary->expedition } $expert->tripdates->all);
[  277]  277:     }
[  278]  278:     # Custom trips - note that when going through the expert assignments relation, we can only consider CUSTOM trips
[  279]  279:     if ($expert->can('expeditions') && $expert->expeditions->is_populated()) {
[  280]  280:         %expeditions = (%expeditions, map { $_->expedition_id() => $_ } grep { $_->is_custom() } $expert->expeditions->all());
[  281]  281:     }
[  282]  282: 
[  283]  283:     foreach(sort { $expeditions{$a}->expedition_name cmp $expeditions{$b}->expedition_name } keys %expeditions) {
[  284]  284:         my $expedition = $expeditions{$_};
[  285]  285:         my $url = $self->url_base;
[  286]  286:         if ($expedition->is_custom()) {
[  287]  287:             $url .= $expedition->custom_landing_url();
[  288]  288:         }
[  289]  289:         else {
[  290]  290:             $url .= '/expeditions/'.$self->make_url_safe($expedition->link_text).'/detail';
[  291]  291:         }
[  292]  292:         my $text = $expedition->expedition_name;
[  293]  293:         $rv .= qq{<a href="$url">$text</a><br />};
[  294]  294:     }
[  295]  295: 
[  296]  296:     # Don't show the header if no trips were collated
[  297]  297:     return unless $rv;
[  298]  298: 
[  299]  299:     # Otherwise, results with header.
[  300]  300:     return qq{
[  301]  301: <div class="metadata trips">
[  302]  302:     <h5>Trips:</h5>
[  303]  303:     $rv
[  304]  304: </div>
[  305]  305: };
[  306]  306: 
[  307]  307: }
[  308]  308: 
[  309]  309: sub __expert_specialties {
[  310]  310:     my $self = shift;
[  311]  311:     my $expert = shift;
[  312]  312:     my $rv;
[  313]  313: 
[  314]  314:     return unless ($expert->specialties->count > 0);
[  315]  315: 
[  316]  316:     if($expert->specialties->count > 0) {
[  317]  317:         $rv .= '<div class="">';
[  318]  318:         $rv .= ($expert->specialties->count > 1)
[  319]  319:                 ? "<h5 style='display:inline;'>Specialties: &nbsp;</h5>  "
[  320]  320:                 :  "<h5 style='display:inline;'>Specialty: &nbsp;</h5>  ";
[  321]  321:         foreach($expert->specialties->all) {
[  322]  322:             $rv .= $_->description.", ";
[  323]  323:         }
[  324]  324:         $rv =~ s/, $//;
[  325]  325:         $rv .= "<br /></div>";
[  326]  326:     }
[  327]  327: 
[  328]  328:     return $rv;
[  329]  329: }
[  330]  330: 
[  331]  331: sub __page_links {
[  332]  332:     my $self = shift;
[  333]  333:     my ($rv, $next, $previous, $link_base, $link);
[  334]  334: 
[  335]  335:     return if $self->featured; #no pagination on the Featured page.
[  336]  336:     return $self->{page_links} if $self->{page_links};
[  337]  337:      # no sense doing this twice if we don't have to.
[  338]  338: 
[  339]  339:     my $link_base = $self->url_base.'/'.$self->{request}->{_page}->{real_path};
[  340]  340:     $link_base =~ s/\/\d+$//;
[  341]  341: 
[  342]  342:     if($self->page_number > 1 || $self->experts->[$PER_PAGE]) {
[  343]  343:         if($self->page_number > 1) {
[  344]  344:             $link = $link_base.'/'.($self->page_number - 1);
[  345]  345:             $previous = qq{<li><a href="$link">&laquo; Prev</a></li>};
[  346]  346:         }
[  347]  347: 
[  348]  348:         if($self->experts->[$PER_PAGE]) {
[  349]  349:             $link = $link_base.'/'.($self->page_number + 1);
[  350]  350:             $next = qq{<li><a href="$link">Next &raquo;</a></li>};
[  351]  351:         }
[  352]  352: 
[  353]  353:         $rv = qq{<ul class="selectable_list pagination">
[  354]  354:         $previous
[  355]  355:         <li><span>Page }.$self->page_number.qq{ of }.$self->pages
[  356]  356:          .qq{</span></li>
[  357]  357:         $next
[  358]  358:         </ul>};
[  359]  359:     } 
[  360]  360: 
[  361]  361:     $self->{page_links} = $rv;
[  362]  362: 
[  363]  363:     return $rv;
[  364]  364: }
[  365]  365: 
[  366]  366: sub __no_results {
[  367]  367:     my $self = shift;
[  368]  368:     my $rv;
[  369]  369:     $rv = '<p>No experts matched your search criteria.</p>';
[  370]  370:     return $rv;
[  371]  371: }
[  372]  372: 
[  373]  373: sub intro { 
[  374]  374:     my $self = shift;
[  375]  375:     return $self->{_data}->{experts_hub}->{intro}; 
[  376]  376: }
[  377]  377: 
[  378]  378: sub all_experts_butan {
[  379]  379:     my $self = shift;
[  380]  380:     my $rv;
[  381]  381: 
[  382]  382:     $rv = qq{
[  383]  383:     <div style="text-align:center;">
[  384]  384:     <br/>
[  385]  385:     <form action="}.$self->url_base.qq{/experts/1">
[  386]  386:         <p><button type="submit" value="See All Experts" />See All Experts</button></p>
[  387]  387:     </form>
[  388]  388:     </div>
[  389]  389:     };
[  390]  390: 
[  391]  391:     return $rv;
[  392]  392: }
[  393]  393: 
[  394]  394: foreach(qw{opts search_type search_param page_number offset with where experts pages featured}) {
[  395]  395:     eval "sub $_ {
[  396]  396:               my \$self = shift;
[  397]  397:               \$self->{uc($_)} = shift if(\@_);
[  398]  398:               \$self->{uc($_)};
[  399]  399:           }";
[  400]  400: }
[  401]  401: 
[  402]  402: 1;