1 package CXGN
::Page
::Secretary
;
9 use CXGN
::DB
::Connection
;
10 use CXGN
::Page
::Widgets
;
13 use base
('CXGN::Page');
17 my $page_name = shift;
20 ## I've decided to make the Secretary Page module create the default database handle for the Page-calling script
22 my $self = $class->SUPER::new
($page_name, $author);
24 my $dbh = CXGN
::DB
::Connection
->new();
25 $dbh->do("SET SEARCH_PATH=tsearch2,public,sgn_people");
29 my $evidef_q = $dbh->prepare("SELECT * FROM ara_evidef");
31 while(my $row = $evidef_q->fetchrow_hashref) {
32 $evidefs{$row->{code
}} = $row->{description
};
34 $self->{evicode2definition
} = \
%evidefs;
35 $self->{hotlist_button_ids
} = {};
37 #The project name will still be SGN for cookie compatibility, but we use the Secretary Page/VHost file
38 $self->{page_module
}="CXGN::Page::VHost::Secretary";
39 eval "require $self->{page_module}";
41 die("Secretary VHost Page module ($self->{page_module}) not found");
43 $self->{page_object
}=$self->{page_module
}->new($self->{dbh
});
44 $self->fetch_arguments();
51 ( $self->{gene
}, $self->{searchQuery
}, $self->{noCheck
}, $self->{leftBound
}, $self->{querySize
},
52 $self->{referenceGene
}, $self->{prevQuery
}, $self->{prevLB
}, $self->{physicalMode
}, $self->{username
},
53 $self->{pass
}, $self->{logout
}, $self->{newUser
}, $self->{passRep
}, $self->{email
},
54 $self->{fname
}, $self->{lname
}, $self->{org
}, $self->{error404
}
56 = map { HTML
::Entities
::decode
($_) }
57 $self->get_encoded_arguments
59 'g', 'query', 'nocheck', 'lb', 'qsize',
60 'referenceGene', 'prevQ', 'prevLB', 'physicalMode', 'username',
61 'password', 'logout', 'newuser', 'passwordrep', 'email',
62 'fname', 'lname', 'org', 'error404'
65 if(!($self->{leftBound
} =~ /^\d+$/) || $self->{leftBound
} <= 0) {
66 $self->{leftBound
} = 1;
70 $self->{searchQuery
} ||= "";
71 #$self->{searchQuery} =~ s/^\s*(.*?)\s*$/$1/;
73 my $querySize = $self->{querySize
};
74 if(defined $querySize && (!($querySize =~ /^\d+$/) || $querySize<=0)) { undef $querySize }
75 $self->{querySize
} = $querySize;
78 if($self->{referenceGene
}) { $physicalMode = 1}
79 $self->{physicalMode
} ||= $physicalMode;
82 ### User account functions #########################################
86 my ($username, $password) = ($self->{username
}, $self->{pass
});
89 my $login_controller = CXGN
::Login
->new({NO_REDIRECT
=>1});
90 my $login_info=$login_controller->login_user($username,$password);
91 $self->{login_info
} = $login_info;
96 my $logout_controller = CXGN
::Login
->new({NO_REDIRECT
=>1});
97 $logout_controller->logout_user();
103 my ($first_name, $last_name, $username, $password, $confirm_password, $email_address)
104 = ($self->{fname
}, $self->{lname
}, $self->{username
}, $self->{pass
}, $self->{passRep
}, $self->{email
});
108 # check password properties...
111 if (length($username) < 7) {
112 push @fail, "Username is too short. Username must be 7 or more characters";
114 # does user already exist?
116 my $existing_login = CXGN
::People
::Login
-> get_login
($username);
118 if ($existing_login->get_username()) {
119 push @fail, "Username \"$username\" is already in use. Please pick a different username.";
123 if (length($password) < 7) {
124 push @fail, "Password is too short. Password must be 7 or more characters";
126 if ("$password" ne "$confirm_password") {
127 push @fail, "Password and confirm password do not match.";
129 if ($password eq $username) {
130 push @fail, "Password must not be the same as your username.";
132 if ($email_address !~ m/[^\@]+\@[^\@]+/) {
133 push @fail, "Email address is invalid.";
138 return new_user_fail
(\
@fail);
141 my $confirm_code = $self->tempname();
142 my $new_user = CXGN
::People
::Login
->new();
143 $new_user -> set_username
($username);
144 $new_user -> set_password
($password);
145 $new_user -> set_pending_email
($email_address);
146 $new_user -> set_confirm_code
($confirm_code);
147 # $new_user -> set_disabled('unconfirmed account');
148 $new_user -> store
();
150 #this is being added because the person object still uses two different objects, despite the fact that we've merged the tables
151 my $person_id=$new_user->get_sp_person_id();
152 my $new_person=CXGN
::People
::Person
->new($person_id);
153 $new_person->set_first_name($first_name) if ($first_name);
154 $new_person->set_last_name($last_name) if ($last_name);
155 $new_person->store();
157 $self->{person_id
} = $person_id;
161 return "Username not provided";
169 foreach ( @
{$fail_ref} ) {
170 $fail_str .= "<li>$_</li>\n"
173 return <<END_HEREDOC;
175 <table summary="" width=80% align=center>
176 <tr><td style='color:black'>
177 <p style='color:#dd4444'>Your account could not be created for the following reasons</p>
183 <tr><td><br /></td></tr>
191 unless($self->{hotlist}){
192 $self->{hotlist} = $self->{page_object}->get_hotlist();
195 if($self->{hotlist}){
196 $self->{hotlist_content} = $self->{hotlist}->get_item_contents();
198 return $self->{hotlist};
204 my $just_logged_in = 0; #we can't check a cookie that we just set, so use this to properly display page elements on first page-load of login
205 my $login_warning = '';
206 my $extra_notification = '';
207 my $login_info_person_id = 0;
211 if($self->{newUser}){
212 $login_warning = $self->new_user();
213 $login_info = $self->login();
214 $extra_notification = "New account with the username \"$self->{username}\" created!";
216 elsif($self->{username}){
217 $login_info = $self->login();
218 if($login_info->{incorrect_password}) { $login_warning = "You provided an incorrect password for this account." }
219 if($login_info->{account_disabled}) {
220 $login_warning = "Your account has been disabled. Please contact <a href='mailto:support\@sgn.cornell.edu'>support\@sgn.cornell.edu</a>";
222 if($login_info->{duplicate_cookie_string}) { $login_warning = "A duplicate cookie string has been issued (rare). Please try again." }
223 if($login_info->{logins_disabled}) { $login_warning = "Sorry, logins have been temporarily disabled on this system" }
224 if(!$login_warning) {
225 $login_warning = "success";
226 $login_info_person_id = $login_info->{person_id};
230 # overrides username login_info handler, which has to remain in place to make warnings (i should change this)
231 my $loginh = CXGN::Login->new({NO_REDIRECT=>1});
233 if(my $sp_person_id = $loginh->has_session()){
234 $login_info = $loginh->get_login_info();
235 $self->{validUser} = 1;
237 if($login_warning eq "success") {
240 $self->{validUser} = 1;
243 $self->{person_id} = $login_info->{person_id};
244 $self->{cookie_string} = $login_info->{cookie_string};
245 $self->{user_prefs} = $login_info->{user_prefs};
247 if($login_warning) { $login_warning .= "<br>" }
249 $self->{just_logged_in} = $just_logged_in;
250 $self->{login_warning} = $login_warning;
251 if($extra_notification) { $login_warning = $extra_notification }
253 if($self->{person_id}){
254 #makes sure that cookie and database user_pref string are current and agreeable on initial login or when loading any page. This is important when the user changes a preference, then jumps to a page in which UserPrefs ISN'T used, so the database will stil be updated with the new client cookie string.
255 my $prefh = CXGN::UserPrefs->new( $self->{dbh}, $self->{person_id} );
256 $self->{user_prefs} = $prefh->get_user_pref_string();
259 #logout is done last since we want to sync up the userprefs that were set before the logout button was clicked
266 return $login_warning;
270 ### CXGN::Page over-riding functions #############
271 sub get_system_message {
273 my $system_message='';
274 if(my $message_file=$self->{vhost_object}->get_conf('system_message_file'))
276 my $message_text=CXGN::Tools::File::file_contents($message_file);
277 $system_message.="<span class=\"developererrorbox\">$message_text</span><br />";
279 unless($self->{vhost_object}->get_conf('production_server'))
281 my $connection_test=CXGN::DB::Connection->new_no_connect();
282 $system_message.="<span class=\"developererrorbox\">Viewing the ".$connection_test->dbname." database on ".$connection_test->dbhost.", ".$connection_test->dbbranch." branch</span>\n";
284 else { die("This site is not ready to go live yet!");}
286 return $system_message;
291 # $self->{request}->send_http_header("text/html");
292 if($self->{content_title}){ $self->{page_title} .= ": " . $self->{content_title} }
294 my $system_message = $self->get_system_message();
295 my $login_warning = $self->get_login_info();
297 my $login_panel = $self->{page_object}->login_panel($self->{gene}, $self->{searchQuery});
299 my $html_head = $self->html_head();
301 my $user_bar = $self->{page_object}->user_bar
303 $self->{gene}, $self->{searchQuery}, $self->{just_logged_in}, $self->{person_id}, $self->{error404}
305 my $body_tag = "<body>";
307 my $notifier = CXGN::Page::Widgets::notifier();
309 if($self->{content_title} =~ /Home/i) { $body_tag = "<body onload='document.getElementById(\"query_focus\").focus()'>" }
315 <div id='userbar_container' style='width:100%'>
316 <table style='width:100%; margin-bottom:5px;'>
318 <td style='text-align:left'>
321 <td style='text-align:right'>
327 <span id='login_warning' style='color:#dd4444; font-size:1.05em'>$login_warning</span>
332 <span style='font-size:0.9em; color:#990000'>
333 <a href='/documents/howtojs.html' style='color:#993355'><b>Javascript</b></a> (and cookies) must be enabled for logging in, hotlists, and dynamic effects
341 <!--/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/| BEGIN PAGE CONTENT |/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/-->
348 ($self->{page_title
}, $self->{content_title
}, $self->{extra_head_stuff
}) = @_;
349 unless($self->{page_title
})
351 $self->{page_title
}=$self->{name
};
353 print $self->get_header();
358 my $extra_footer_stuff = shift;
359 print $self->get_footer($extra_footer_stuff);
364 my $extra_footer_stuff = shift;
365 $extra_footer_stuff||='';
369 <META HTTP-EQUIV="cache-control" CONTENT="no-store">
370 <META HTTP-EQUIV="cache-control" CONTENT="no-cache">
377 sub jsan_render_includes
{
379 return join("\n",map { qq|<script language
="JavaScript" src
="$_" type
="text/javascript"></script
>| } $self->_jsan->uris);
385 $self->jsan_use(qw
|CXGN
.Base CXGN
.UserPrefs CXGN
.Effects CXGN
.Hotlist CXGN
.secretary
|);
386 my $jsanh = $self->_jsan();
387 my $script_src = $self->jsan_render_includes();
388 my $pathex = $jsanh->_class_to_file("CXGN.Effects");
390 #my $script_src = $self->jsan_render_includes();
393 <head><title>$self->{page_title}</title>
395 <link rel="stylesheet" href="/documents/inc/secretary.css" TITLE="Secretary Default" TYPE="text/css" />
396 <link rel="icon" href="favicon.ico" type="image/x-icon" />
397 <link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
399 <META HTTP-EQUIV="cache-control" CONTENT="no-store" />
400 <META HTTP-EQUIV="cache-control" CONTENT="no-cache" />
402 <script language='javascript'>
404 JSAN.use = function () {};
406 //Global page variables for all scripts
407 var thispage = location.href;
408 var agi = '$self->{gene}';
409 var query = '$self->{searchQuery}';
410 var user_id = '$self->{person_id}';
411 var cookie_string = '$self->{cookie_string}';
412 var user_pref_string = '$self->{user_prefs}';
413 var just_logged_in = '$self->{just_logged_in}';
414 if(just_logged_in == '1') location.href = thispage;
418 <!--transitional script
420 <script language='javascript' src='/documents/inc/secretary.js'></script>
427 ## Secretary page elements ##################
434 while($self->{hotlist_button_ids
}->{"$agi:$i"}){
438 $self->{hotlist_button_ids
}->{"$agi:$enum"} = 1;
440 if(!$self->{hotlist_content
}) { $self->set_hotlist() }
441 if($self->{validUser
}){
443 foreach(@
{$self->{hotlist_content
}}){
444 if ($_ eq $agi) { $in_hotlist = 1 }
447 $buttonText .= <<HTML;
448 <img id='hotlistButton:$agi:$enum:imgAdd' src='/documents/img/secretary/hotlist_add.png' style='display:none' border=0 \>
449 <img id='hotlistButton:$agi:$enum:imgRemove' src='/documents/img/secretary/hotlist_remove.png' style='display:inline' border=0 \>
450 <a href='#' id='hotlistButton:$agi:$enum'
451 onclick='Hotlist.remove(this.id, \"$agi\"); return false;'
452 style='text-decoration:none'>Remove from Hotlist</a>
456 $buttonText .= <<HTML;
457 <img id='hotlistButton:$agi:$enum:imgAdd' src='/documents/img/secretary/hotlist_add.gif' style='display:inline' border=0 \>
458 <img id='hotlistButton:$agi:$enum:imgRemove' src='/documents/img/secretary/hotlist_remove.gif' style='display:none' border=0 \>
459 <a href='#' id='hotlistButton:$agi:$enum'
460 onclick='Hotlist.add(this.id, \"$agi\");return false;'
461 style='text-decoration:none'>Add to Hotlist</a>
464 $buttonText .= " <span id='hotlistWait:$agi:$enum' style='display:none'>...</span>";
467 $buttonText .= <<HTML
468 <img src='/documents/img/secretary/hotlist_add.gif' border=0 \>
469 <a href='#' style='text-decoration:none'
470 onMouseOver='document.getElementById("hotlistGuest:$agi:$enum").style.display = "inline";'
471 onMouseOut='document.getElementById("hotlistGuest:$agi:$enum").style.display = "none";'
472 onclick='show_login();'>Add to Hotlist</a>
474 <span id='hotlistGuest:$agi:$enum' style='display:none;color:#aa3333' > Login to use hotlists</span>
480 sub navigation_info
{
482 my $extraHREF = shift;
483 my $querysize = $self->{querySize
};
484 my $lb = $self->{leftBound
};
485 my $searchquery = $self->{searchQuery
};
486 my $referenceGene = $self->{referenceGene
};
487 my $physicalMode = $self->{physicalMode
};
488 my $prevQuery = $self->{prevQuery
};
489 my $prevLB = $self->{prevLB
};
493 if((defined $querysize) && ($querysize < 20 || ($lb + 19) > $querysize)){
496 my $navinfo = "<span style='font-size:1em'>Results <b>$lb</b> - <b>$rb</b> of ";
497 if(defined $querysize) {$navinfo .= "<b>$querysize</b><br>"}
498 else {$navinfo .= "<b>200+</b><br>"}
503 my %lowerbounds = ( 1=>10, 2=>10, 3=>10,4=>0,5=>10,M
=>0,C
=>0);
504 my %upperbounds = (1=>809, 2=>481, 3=>666, 4=>401, 5=>676, M
=>14, C
=>13);
505 my ($currentpos) = $searchquery =~ /AT[1-5MC]G(\d{1,3})/;
506 my ($refpos) = $referenceGene =~ /AT[1-5MC]G(\d{1,3})/;
507 my ($chromchar) = $searchquery =~ /AT([1-5MC])G/;
508 my $lower = $lowerbounds{$chromchar};
509 my $upper = $upperbounds{$chromchar};
510 $lower = int($lower / 5) * 5;
511 $currentpos = zeropadleft
(int(zeropadright
($currentpos)));
512 $refpos = zeropadleft
(int($refpos));
513 my $navinfo = "<span style='font-size:1em'><b>$querysize</b> genes at the prefix <b>AT$chromchar" . "G$currentpos</b>";
514 $navinfo .= " <b style='letter-spacing:0px'>(";
516 while ($i <= $upper){
518 if(abs($pos-$currentpos)<3 || (abs($upper-$currentpos)<3 && abs($upper-$pos)<5)) {
520 $navinfo .= "<span style='color:white;background-color:#000088'>|</span>";
522 elsif(abs($pos-$refpos)<3){
523 $navinfo .= "<a title='" . zeropadleft
($refpos) . "' href='query.pl?query=AT$chromchar" . "G" . zeropadleft
($refpos);
524 $navinfo .= "&referenceGene=$referenceGene&prevQ=$prevQuery&prevLB=${prevLB}$extraHREF'";
525 $navinfo .= " style='text-decoration:none; background-color:#00AA00;font-weight:bold;color:white'>|</a>";
527 elsif(abs($upper-$pos)<5){
528 $navinfo .= "<a title='" . zeropadleft
($upper) . "' href='query.pl?query=AT$chromchar" . "G" . zeropadleft
($upper);
529 $navinfo .= "&referenceGene=$referenceGene&prevQ=$prevQuery&prevLB=${prevLB}$extraHREF'";
530 $navinfo .= " style='text-decoration:none;font-weight:bold;'>|</a>";
533 $navinfo .= "<a title='" . zeropadleft
($pos) . "' href='query.pl?query=AT$chromchar" . "G" . zeropadleft
($pos);
534 $navinfo .= "&referenceGene=$referenceGene&prevQ=$prevQuery&prevLB=${prevLB}$extraHREF'";
535 $navinfo .= " style='font-weight:bold;text-decoration:none'>|</a>";
540 $navinfo .= ")</b></span>";
545 sub navigation_control
{
547 my $prevReplace = shift;
548 my $nextReplace = shift;
549 my $extraHREF = shift;
551 my $lb = $self->{leftBound
};
552 my $query = $self->{searchQuery
};
553 my $nocheck = $self->{noCheck
};
554 my $referenceGene = $self->{referenceGene
};
555 my $physicalMode = $self->{physicalMode
};
556 my $querysize = $self->{querySize
};
557 my $prevQuery = $self->{prevQuery
};
558 my $prevLB = $self->{prevLB
};
559 my $navbar = "<span class='navbar'>";
561 if(!$physicalMode){ #standard search-relevance navigation
565 if(defined $querysize){
566 $nextbound = $lb - 20;
567 if($nextbound != 1 || 1) {
568 $navbar .= "<a href='query.pl?query=$query&lb=1&qsize=$querysize&nocheck=$nocheck&prevQ=$prevQuery&prevLB=${prevLB}${extraHREF}'>« First</a> ";
571 $navbar .= "<span style='color:grey'><u>« First</u> </span>";
573 $navbar .= "<a href='query.pl?query=$query&lb=$nextbound&qsize=$querysize&nocheck=${nocheck}${extraHREF}'>‹ Prev</a> ";
576 $nextbound = $lb - 20;
577 if($nextbound != 1 || 1) { ##still deciding whether to grey-out if unnecessary
578 $navbar .= "<a href='query.pl?query=$query&lb=1&nocheck=${nocheck}${extraHREF}'>« First</a> ";
581 $navbar .= "<span style='color:grey'><u>« First</u> </span>";
583 $navbar .= "<a href='query.pl?query=$query&lb=$nextbound&nocheck=${nocheck}${extraHREF}'>‹ Prev</a> ";
588 $navbar .= "<span style='color:grey'><u>« First</u> <u>‹ Prev</u> </span>";
591 if((!defined $querysize) || (($querysize - $lb) >= 20)){
592 $nextbound = $lb + 20;
594 if(defined $querysize) {
595 $navbar .= "<a href='query.pl?query=$query&lb=$nextbound&qsize=$querysize&nocheck=${nocheck}${extraHREF}'>Next ›</a> ";
598 $navbar .= "<a href='query.pl?query=$query&lb=$nextbound&nocheck=${nocheck}${extraHREF}'>Next ›</a> "
600 if(!defined $querysize) {$lastbound = $lb + 200 }
601 elsif($querysize%20) {$lastbound = $querysize - $querysize%20 + 1 }
602 else {$lastbound = $querysize - 19 }
604 if(defined $querysize) {
605 if($lastbound != $nextbound || 1){
606 $navbar .= "<a href='query.pl?query=$query&lb=$lastbound&qsize=$querysize&nocheck=${nocheck}${extraHREF}'>Last »</a>";
609 $navbar .= "<span style='color:grey'><u>Last »</u></span>";
613 $navbar .= "<a href='query.pl?query=$query&lb=$lastbound&nocheck=${nocheck}${extraHREF}'>Jump »</a>";
617 $navbar .= "<span style='color:grey'><u>Next ›</u> <u>Last »</u></span>";
621 #gene pseudo-physical navigation
624 ###Locus Boundaries####found by ordering locii in database#################
642 my %lowerbounds = (1=>10, 2=>10, 3=>10,4=>0,5=>10,M
=>0,C
=>0);
643 my %upperbounds = (1=>809, 2=>481, 3=>666, 4=>401, 5=>676, M
=>14, C
=>13);
645 my ($chrom) = $query =~ /(AT[1-5MC]G)/;
646 my ($chromchar) = $query =~ /AT([1-5MC])G/;
647 my ($location) = $query =~ /AT[1-5MC]G(\d{1,3})/;
649 if(length($location)<1) { $location .= "000" }
650 elsif(length($location) == 1) { $location .= "00" }
651 elsif(length($location) ==2) { $location .= "0" }
652 $location = int($location);
653 my $jumpleft = $location - 10;
654 my $jumpright = $location + 10;
655 my $next = $location + 1;
656 my $prev = $location - 1;
658 if($location > ($lowerbounds{$chromchar}+10)) {
659 $navbar .= "<a href='query.pl?query=$chrom" . zeropadleft
($jumpleft) . "&referenceGene=$referenceGene&prevQ=$prevQuery&prevLB=${prevLB}$extraHREF'>« (x10)</a> ";
662 $navbar .= "<span style='color:grey'><u>« (x10)</u></span> ";
664 if($location > $lowerbounds{$chromchar}) {
665 $navbar .= "<a href='query.pl?query=";
667 $navbar .= $prevReplace;
670 $navbar .= $chrom . zeropadleft
($prev);
672 $navbar .= "&referenceGene=$referenceGene&prevQ=$prevQuery&prevLB=${prevLB}$extraHREF'>‹ Left</a> ";
675 $navbar .= "<span style='color:grey'><u>‹ Left</u></span> ";
677 if($location < $upperbounds{$chromchar}) {
678 $navbar .= "<a href='query.pl?query=";
680 $navbar .= $nextReplace;
683 $navbar .= $chrom . zeropadleft
($next);
685 $navbar .= "&referenceGene=$referenceGene&prevQ=$prevQuery&prevLB=${prevLB}$extraHREF'>Right ›</a> ";
688 $navbar .= "<span style='color:grey'><u>Right ›</u></span> ";
690 if($location < ($upperbounds{$chromchar}-10)) {
691 $navbar .= "<a href='query.pl?query=$chrom" . zeropadleft
($jumpright) . "&referenceGene=$referenceGene&prevQ=$prevQuery&prevLB=${prevLB}$extraHREF'>(x10) »</a> ";
694 $navbar .= "<span style='color:grey'><u>(x10) »</u></span>";
697 $navbar .= "</span>";
701 ## Helper routines ##########################
705 if(length($location)==1) {
706 return "00" . $location;
708 elsif(length($location)==2) {
709 return "0" . $location;
712 return "" . $location;
719 if(length($location)==1) {
720 return $location . "00";
722 elsif(length($location)==2) {
723 return $location . "0";
726 return $location . "";