1 # Evan, 1 / 15 / 07: combines contact-info.pl and research-info.pl
3 #if a user is logged in, look at the logged-in user's info;
4 #else, can call with an id parameter to get a read-only view of any user's info
5 #(if no login and no 'id=', no good)
7 #(we don't want to encourage users to go directly to this page to edit their own info;
8 # they should go through top-level.pl, so they'll already be logged in when they get here)
13 my $contact_info_page = new SolPeoplePersonalInfoPage
();
15 package SolPeoplePersonalInfoPage
;
18 use CXGN
::Page
::FormattingHelpers qw
/page_title_html/;
22 use CXGN
::Phenome
::Locus
;
24 use base qw
| CXGN
::Page
::Form
::SimpleFormPage
|;
28 my $person_id = shift;
29 my $self = $class->SUPER::new
(); #arguments are parsed from the Page here
30 my (undef, undef, $this_script_filename) = File
::Spec
->splitpath($0); #$0 contains current filename with some path or other prepended
31 $self->set_script_name($this_script_filename);
37 # call set_object_id, set_object and set_primary_key here
38 my %args = $self->get_args();
39 $self->set_object_id($args{sp_person_id
});
40 $self->set_object(new CXGN
::People
::Person
($self->get_dbh(), $args{sp_person_id
}));
41 $self->set_primary_key("sp_person_id");
42 $self->set_owners($self->get_object()->get_sp_person_id());
45 #specified in SimpleFormPage
46 sub check_modify_privileges
{
49 # implement quite strict access controls by default
51 my $person_id = $self->get_login()->has_session();
52 my $user = CXGN
::People
::Person
->new($self->get_dbh(), $person_id);
53 my $user_id = $user->get_sp_person_id();
54 if ($user->get_user_type() eq 'curator') {
58 # check the owner only if the action is not new
60 my @owners= $self->get_owners();
63 if (!(grep {/^$user_id$/} @owners ))
65 $self->get_page()->message_page("You do not have rights to modify this database entry because you do not own it. [$user_id, @owners]");
72 # override to check privileges for edit, store, delete.
73 # return 0 for allow, 1 for not allow.
78 #specified in SimpleFormPage
79 sub validate_parameters_before_store
82 my %args = $self->get_args();
84 my ($keywords, $format, $research_interests, $organism_codes_str, $unlisted_organisms_str) =
85 @args{qw(keywords format research_interests organisms unlisted_organisms)};
86 $format = "auto" unless $format eq "html";
88 #after checking for legal values, do the actual storing of existing organism IDs here
89 my @organism_codes = map {$_ =~ tr/[0-9]//cds; $_} split(/\0/, $organism_codes_str);
90 foreach my $c ( @organism_codes )
92 my $test = new CXGN
::People
::Organism
($self->get_dbh(), $c);
95 $self->get_page()->message_page("Organism code \"$_\" is not defined in the database.\n");
98 my %selected_organisms = map {$_ => 1} @organism_codes; #ids of only those organisms selected with the multiselect appear in the hash
99 my $person = $self->get_object();
100 foreach my $o ($person->get_organisms())
103 if($selected_organisms{$o->get_sp_organism_id()})
109 #deal with unlisted organisms
110 my @unlisted_organisms;
111 foreach (split(/;/, $unlisted_organisms_str))
113 if(m/^\s*((\S.*\S)|\S)\s*$/) #remove leading and trailing spaces
115 push @unlisted_organisms, $1;
118 my $unlisted_organisms = join("\0", @unlisted_organisms);
120 # Quick, dirty hack method of defenestrating disallowed HTML tags in
121 # research interest statements: replace allowed tags with a marker string,
122 # then delete all tokens of the form "<[^>]+>", then replace the marker string
123 # with the original tag.
125 # Using the marker string allows us to "save" the information about the tag
126 # that is allowed -- changing it to a token that won't match the "<[^>]+>"
127 # pattern. Using a random marker string prevents a user learning (through a
128 # website error or something that reveals a static marker string) the marker
129 # and trying to insert illegal tags by typing the marker into their statement
130 # manually. This is further guarded against by only recognizing marker strings
131 # with allowed tags encoded. (Yes, I am paranoid)
133 # The main problem with this is if someone happens to use both a literal "<"
134 # and a matching ">" in their research statement -- if that happens, the
135 # text flanked by these will be deleted -- they'll need to use < and >
137 my $tag_marker = $self->get_page()->tempname();
138 my $formatted_interests = $research_interests;
139 $formatted_interests =~ s/<(\/{0,1})([pbi]|br)>/$1-$2-$tag_marker/g
;
140 $formatted_interests =~ s/\<[^>]+\>//g;
141 $formatted_interests =~ s/(\/{0,1})-([pbi]|br)-$tag_marker/<$1$2>/g
;
143 # This is stored separately in the database, without tags, for full-text searching.
144 $research_interests =~ s/\<[^>]+\>//g;
146 if ($format eq "auto")
148 $formatted_interests =~ s/\r{0,1}\n\r{0,1}\n/<\/p><p
>/g
;
149 $formatted_interests = "<p>$formatted_interests</p>";
152 $args{keywords
} = HTML
::Entities
::encode_entities
($keywords);
153 $args{format
} = $format;
154 $args{research_interests
} = ($format eq "auto") ? HTML
::Entities
::encode_entities
($research_interests) : $formatted_interests;
155 $args{unlisted_organisms
} = $unlisted_organisms;
156 $self->set_args(%args); #necessary if you want to affect the arguments that will be stored
163 my %args = $self->get_args();
164 my $person = $self->get_object();
167 my $form = $self->get_form();
168 if ($form->is_editable()) {
169 $form->set_submit_method('post'); #avoid long URLs caused by research interests
172 my ($displayed_first_name, $displayed_last_name) = ($person->get_first_name(), $person->get_last_name());
173 #i added some html into empty name fields asking people to update their blank entries in the database.
174 #if we encounter one of these, do not fill in the field for their name with the html! --john
175 if($displayed_first_name=~/^</){$displayed_first_name='';}
176 if($displayed_last_name=~/^</){$displayed_last_name='';}
178 my @organisms = $person->get_organisms();
179 my @organism_ids = map {$_->get_organism_id()} @organisms;
180 my @organism_names = map {$_->get_organism_name()} @organisms;
181 my @organism_selections = map {$_->is_selected() ?
'1' : '0'} @organisms;
183 #element IDs for the source elements for the preview panes
184 $self->{html_source_element_id
} = 'html_source';
185 $self->{organisms_source_element_id
} = 'organisms_source';
187 my $default_field_length = 22;
188 if($form->is_editable()) {
189 $form->add_label(display_name
=> "",
190 field_name
=> "lbl1",
191 contents
=> "If you would like to enter or update information at this time but do not want it displayed publically, check this option.");
193 $form->add_checkbox(display_name
=> "Censor contact information from directory search and public display",
194 field_name
=> "censor",
196 selected
=> $person->get_censored(),
198 getter
=> "get_censored",
199 setter
=> "set_censor");
201 $form->add_label(display_name
=> "",
202 field_name
=> "lbl2",
203 contents
=> "The first and last names you enter here will be displayed in the SGN directory (if not censored using the box above)."
204 . " Your username for this interface is not displayed.");
206 $form->add_select(display_name
=> "Salutation",
207 field_name
=> "salutation",
208 contents
=> $person->get_salutation(),
210 getter
=> "get_salutation",
211 setter
=> "set_salutation",
212 select_list_ref
=> [qw(None Prof. Dr. Mr. Ms. Mrs.)],
213 select_id_list_ref
=> ['', qw(Prof. Dr. Mr. Ms. Mrs.)]);
214 $form->add_field(display_name
=> "First name",
215 field_name
=> "first_name",
216 contents
=> $displayed_first_name,
217 length => $default_field_length,
219 getter
=> "get_first_name",
220 setter
=> "set_first_name",
221 validate
=> "string");
222 $form->add_field(display_name
=> "Last name",
223 field_name
=> "last_name",
224 contents
=> $displayed_last_name,
225 length => $default_field_length,
227 getter
=> "get_last_name",
228 setter
=> "set_last_name",
229 validate
=> "string");
230 $form->add_field(display_name
=> "Organization",
231 field_name
=> "organization",
232 contents
=> $person->get_organization(),
235 getter
=> "get_organization",
236 setter
=> "set_organization");
237 $form->add_textarea(display_name
=> "Address",
238 field_name
=> "address",
239 contents
=> $person->get_address(),
243 getter
=> "get_address",
244 setter
=> "set_address");
245 $form->add_field(display_name
=> "Country",
246 field_name
=> "country",
247 contents
=> $person->get_country(),
248 length => $default_field_length,
250 getter
=> "get_country",
251 setter
=> "set_country");
252 if($form->is_editable()) {
253 $form->add_label(display_name
=> "",
254 field_name
=> "lbl3",
255 contents
=> "If you wish to be contacted via telephone or fax by interested parties who found your directory entry on SGN, please enter"
256 . " telephone numbers here. These numbers will be displayed publically to all SGN users who view your entry.");
258 $form->add_field(display_name
=> "Phone",
259 field_name
=> "phone",
260 contents
=> $person->get_phone_number(),
261 length => $default_field_length,
263 getter
=> "get_phone_number",
264 setter
=> "set_phone_number");
265 $form->add_field(display_name
=> "Fax",
267 contents
=> $person->get_fax(),
268 length => $default_field_length,
271 setter
=> "set_fax");
272 if($form->is_editable())
274 $form->add_label(display_name
=> "",
275 field_name
=> "lbl4",
276 contents
=> "If you would like to be contacted via email by interested parties who found your directory entry on SGN, please enter"
277 . " an address here. This address will be displayed publically to all SGN users who view your entry.");
279 $form->add_field(display_name
=> "Contact E-mail",
280 field_name
=> "contact_email",
281 contents
=> $person->get_contact_email(),
282 length => $default_field_length,
284 getter
=> "get_contact_email",
285 setter
=> "set_contact_email");
286 $form->add_field(display_name
=> "Website",
287 field_name
=> "webpage",
288 contents
=> $person->get_webpage(),
289 length => $default_field_length,
291 getter
=> "get_webpage",
292 setter
=> "set_webpage");
293 if($form->is_editable())
295 $form->add_label(display_name
=> "",
296 field_name
=> "lbl5",
297 contents
=> "Enter research keywords (for searching) and a fuller explanation of your research interests (for searching and displaying) below.");
299 $form->add_field(display_name
=> "Keywords",
300 field_name
=> "keywords",
301 contents
=> $person->get_research_keywords(),
304 getter
=> 'get_research_keywords',
305 setter
=> 'set_research_keywords');
306 if($form->is_editable())
308 $form->add_label(display_name
=> "",
309 field_name
=> "lbl6",
310 contents
=> "Please separate terms with a <b>semicolon</b>. Do not use quotation marks or other punctuation. (Example: fruit development;"
311 . " host pathogen interaction; drought tolerance)");
313 $form->add_multiselect(display_name
=> "Research Organisms",
314 field_name
=> "organisms",
315 choices
=> \
@organism_ids,
316 labels
=> \
@organism_names,
317 contents
=> \
@organism_selections,
319 getter
=> 'get_organism_id_string');
320 if($form->is_editable())
322 $form->add_label(display_name
=> "",
323 field_name
=> "lbl7",
324 contents
=> "You may select multiple organisms by clicking on more than one while holding the control key down. If an organism is not listed"
325 . " in the selection box, please specify your additional organisms by their official Latin names in the text entry box below, in"
326 . " a <b>semicolon</b>-separated list.");
327 #the contents will be edited by the pre-storing validation function; notice this field never needs to be gotten, only sotten
328 $form->add_field(display_name
=> "Unlisted Organisms",
329 field_name
=> "unlisted_organisms",
330 id
=> $self->{organisms_source_element_id
},
334 setter
=> 'add_organisms');
337 if($form->is_editable()) {
338 $form->add_radio_list(display_name
=> "",
339 field_name
=> "format",
340 id_prefix
=> "format_",
341 choices
=> ["auto", "html"],
342 labels
=> ["Auto-format (entry is plain text)", "Use HTML markup tags"],
343 contents
=> $person->get_user_format(),
345 getter
=> 'get_user_format',
346 setter
=> 'set_user_format');
348 $form->add_label(display_name
=> "",
349 field_name
=> "lbl8",
350 contents
=> "For auto-formatted plain text entries, separate paragraphs by a single empty line. For HTML entries, please use only paragraph"
351 . " tags (<p>), bold and italic tags (<b>, <i>), and break tags (<br>). All other tags will be removed.");
353 $form->add_textarea(display_name
=> "Interests",
354 field_name
=> "research_interests",
355 id
=> $self->{html_source_element_id
},
356 contents
=> $person->get_research_interests(),
360 getter
=> 'get_research_interests',
361 setter
=> 'set_research_interests');
362 if($form->get_action() =~ /edit|store/)
364 $form->add_label(display_name
=> "",
365 field_name
=> "lbl9",
366 contents
=> "The preview feature below (requires JavaScript) shows your current interests statement if you've selected HTML format. Also,"
367 . " if you need to define new organisms, the preview will show how the website is parsing the text you've entered above.");
370 #for allowing the form to make changes
371 if($form->is_editable())
373 $form->add_hidden(display_name
=> "ID", field_name
=> "sp_person_id", contents
=> $person->get_sp_person_id());
374 $form->add_hidden(display_name
=> "Action", field_name
=> "action", contents
=> "store");
377 if($form->is_editable())
379 $form->set_reset_button_text("Clear");
380 $form->set_submit_button_text("Submit Changes");
382 if($self->get_action() =~ /view|edit/)
384 $form->from_database();
387 elsif($self->get_action() =~ /store/)
389 $form->from_request($self->get_args());
394 #return an HTML string for a toolbar with other possible actions for the form
395 sub get_actions_toolbar
398 my $user_id= $self->get_user()->get_sp_person_id();
399 my @owners= $self->get_owners();
401 my $script_name = $self->get_script_name();
402 my $user_is_owner = (grep {/^$user_id$/} @owners);
403 my %args = $self->get_args();
404 my $sp_person_id = $args{sp_person_id
};
406 my ($login_user_id, $user_type) = CXGN
::Login
->new($self->get_dbh())->has_session();
408 if($user_is_owner || $user_type eq 'curator')
410 $home = qq(<a href
="top-level.pl?sp_person_id=$sp_person_id">[Directory Update Home
]</a
> 
; 
;);
414 $home = qq(<span
class="ghosted">[Directory Update Home
]</span
>);
417 if($self->get_action() eq "edit")
419 if($user_is_owner || $user_type eq 'curator')
421 return $home . qq(<a href
="$script_name?action=view&sp_person_id=$sp_person_id">[Cancel Edit
]</a
>);
425 return $home . qq(<span
class="ghosted">[Cancel Edit
]</span
>);
428 elsif($self->get_action() eq "view")
430 if($user_is_owner || $user_type eq 'curator')
432 return $home . qq(<a href
="$script_name?action=edit&sp_person_id=$sp_person_id">[Edit
]</a
>);
436 return $home . qq(<span
class="ghosted">[Edit
]</span
>);
439 elsif($self->get_action() eq "store") {}
445 my $person = $self->get_object();
446 my $page = $self->get_page();
447 #SimpleFormPage takes care of some unknown action strings, but we don't handle the full set of actions it supports
448 if($self->get_action() !~ /^view|edit|store$/)
450 $page->message_page("Illegal parameter: action '" . $self->get_action() . "' is not supported by " . $self->get_script_name());
454 $page->add_style(text
=> ".subtitle {font-weight: bold; text-align: center}");
455 $page->add_style(text
=> ".invisible {display: none}");
456 $page->jsan_use("CXGN.DynamicPreviewPane", "MochiKit.DOM", "MochiKit.Signal"); #include javascript modules
457 $page->header("Sol People: contact and research info");
458 print page_title_html
("Personal info for " . $person->get_first_name() . " " . $person->get_last_name());
459 print $self->get_actions_toolbar() . "<hr />\n";
460 print $self->get_form()->as_table_string();
463 if($self->get_action() =~ /^edit$/) {
464 #show the preview panes
465 my $html_preview_pane_parent_id = 'html_preview_pane_parent';
466 my $organisms_preview_pane_parent_id = 'organisms_preview_pane_parent';
469 <div class="subtitle">Interests and Organisms Preview</div>
470 <div id="all_html_preview_stuff">
471 Your research interests will be displayed on SGN\'s website as shown in the box below (the box will not be displayed).
472 <div id="$html_preview_pane_parent_id"></div>
474 <br />You have entered the following currently unlisted organisms. Please check that these names are spelled correctly.
475 Once committed, these names cannot be changed by this interface.
476 If you accidentally save an incorrect organism name, please contact us via <a href="mailto:sgn-feedback\@sgn.cornell.edu">sgn-feedback\@sgn.cornell.edu</a>.
477 <div id="$organisms_preview_pane_parent_id"></div>
478 <script type="text/javascript">
479 DynamicPreviewPane.createPreviewPane('html', '$self->{html_source_element_id}', '$html_preview_pane_parent_id');
480 DynamicPreviewPane.createPreviewPane('list(;)', '$self->{organisms_source_element_id}', '$organisms_preview_pane_parent_id');
482 //show or hide the interests preview pane based on user format selection
483 function makeVisible() {MochiKit.DOM.removeElementClass(this, "invisible");}
484 function makeInvisible() {MochiKit.DOM.addElementClass(this, "invisible");}
485 var previewPaneSection = MochiKit.DOM.getElement("all_html_preview_stuff");
486 MochiKit.Signal.connect("format_auto", "onclick", previewPaneSection, makeInvisible);
487 MochiKit.Signal.connect("format_html", "onclick", previewPaneSection, makeVisible);
489 if(MochiKit.DOM.getElement("format_auto").checked) MochiKit.DOM.addElementClass("all_html_preview_stuff", "invisible");
494 my ($locus_annotations, $more_annotations) ;
495 my @annotated_loci = ();
497 if ($self->get_action() !~ /^edit$/) {
498 my $person_id= $person->get_sp_person_id();
499 @annotated_loci = CXGN
::Phenome
::Locus
::get_locus_ids_by_editor
($self->get_dbh(), $person_id);
502 my $max = @annotated_loci;
503 if (@annotated_loci>24) {
504 $more = @annotated_loci-24;
509 for (my $i=0; $i<$top; $i++) {
510 my $locus = CXGN
::Phenome
::Locus
->new($self->get_dbh(), $annotated_loci[$i]);
511 my $symbol = $locus->get_locus_symbol();
512 my $locus_id = $locus->get_locus_id();
515 if ($locus_id && $symbol) { #deanx jan23 2008
517 $locus_annotations .= qq | <a href
="/phenome/locus_display.pl?locus_id=$locus_id&action=view">$symbol</a
> | }
519 $more_annotations .= qq { <a href
="/phenome/locus_display.pl?locus_id=$locus_id&action=view">$symbol</a
> };
525 $locus_annotations .= qq|<br
><b
>and <a href
="/search/locus_search.pl?w8e4_editor=$person_id">$more more
</a></b
><br
/>|;
531 if (@annotated_loci) {
533 print "Locus editor assignments: \n";
534 print $locus_annotations;
537 my $pop_list = $self->owner_populations();
539 print "<hr />\n $pop_list";
545 sub owner_populations
{
547 my $sp_person_id = $self->get_object_id();
548 my @pops = CXGN
::Phenome
::Population
->my_populations($sp_person_id);
549 my $pop_list = 'Populations:<br/>';
552 foreach my $pops (@pops) {
553 my $pop_name = $pops->get_name();
554 my $pop_id = $pops->get_population_id();
555 #my $is_public = $pops->get_privacy_status();
556 #if ($is_public) {$is_public = 'is publicly available';}
557 #if (!$is_public) {$is_public = 'is not publicly available yet';}
558 $pop_list .= qq |<a href
="/phenome/population.pl?population_id=$pop_id">$pop_name</a><br/>|;