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 »</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: </h5> "
[ 320] 320: : "<h5 style='display:inline;'>Specialty: </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">« 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 »</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;