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
;
23 use Digest
::MD5 qw
| md5_hex
|;
25 use base qw
| CXGN
::Page
::Form
::SimpleFormPage
|;
29 my $person_id = shift;
30 my $self = $class->SUPER::new
(); #arguments are parsed from the Page here
31 my (undef, undef, $this_script_filename) = File
::Spec
->splitpath($0); #$0 contains current filename with some path or other prepended
32 $self->set_script_name($this_script_filename);
38 # call set_object_id, set_object and set_primary_key here
39 my %args = $self->get_args();
40 $self->set_object_id($args{sp_person_id
});
41 $self->set_object(new CXGN
::People
::Person
($self->get_dbh(), $args{sp_person_id
}));
42 $self->set_primary_key("sp_person_id");
43 $self->set_owners($self->get_object()->get_sp_person_id());
46 #specified in SimpleFormPage
47 sub check_modify_privileges
{
50 # implement quite strict access controls by default
52 my $person_id = $self->get_login()->has_session();
53 my $user = CXGN
::People
::Person
->new($self->get_dbh(), $person_id);
54 my $user_id = $user->get_sp_person_id();
55 if ($user->get_user_type() eq 'curator') {
59 # check the owner only if the action is not new
61 my @owners= $self->get_owners();
64 if (!(grep {/^$user_id$/} @owners ))
66 $self->get_page()->message_page("You do not have rights to modify this database entry because you do not own it. [$user_id, @owners]");
73 # override to check privileges for edit, store, delete.
74 # return 0 for allow, 1 for not allow.
79 #specified in SimpleFormPage
80 sub validate_parameters_before_store
83 my %args = $self->get_args();
85 my ($keywords, $format, $research_interests, $organism_codes_str, $unlisted_organisms_str) =
86 @args{qw(keywords format research_interests organisms unlisted_organisms)};
87 $format = "auto" unless $format eq "html";
89 #after checking for legal values, do the actual storing of existing organism IDs here
90 my @organism_codes = map {$_ =~ tr/[0-9]//cds; $_} split(/\0/, $organism_codes_str);
91 foreach my $c ( @organism_codes )
93 my $test = new CXGN
::People
::Organism
($self->get_dbh(), $c);
96 $self->get_page()->message_page("Organism code \"$_\" is not defined in the database.\n");
99 my %selected_organisms = map {$_ => 1} @organism_codes; #ids of only those organisms selected with the multiselect appear in the hash
100 my $person = $self->get_object();
101 foreach my $o ($person->get_organisms())
104 if($selected_organisms{$o->get_sp_organism_id()})
110 #deal with unlisted organisms
111 my @unlisted_organisms;
112 foreach (split(/;/, $unlisted_organisms_str))
114 if(m/^\s*((\S.*\S)|\S)\s*$/) #remove leading and trailing spaces
116 push @unlisted_organisms, $1;
119 my $unlisted_organisms = join("\0", @unlisted_organisms);
121 # Quick, dirty hack method of defenestrating disallowed HTML tags in
122 # research interest statements: replace allowed tags with a marker string,
123 # then delete all tokens of the form "<[^>]+>", then replace the marker string
124 # with the original tag.
126 # Using the marker string allows us to "save" the information about the tag
127 # that is allowed -- changing it to a token that won't match the "<[^>]+>"
128 # pattern. Using a random marker string prevents a user learning (through a
129 # website error or something that reveals a static marker string) the marker
130 # and trying to insert illegal tags by typing the marker into their statement
131 # manually. This is further guarded against by only recognizing marker strings
132 # with allowed tags encoded. (Yes, I am paranoid)
134 # The main problem with this is if someone happens to use both a literal "<"
135 # and a matching ">" in their research statement -- if that happens, the
136 # text flanked by these will be deleted -- they'll need to use < and >
138 my $tag_marker = $self->get_page()->tempname();
139 my $formatted_interests = $research_interests;
140 $formatted_interests =~ s/<(\/{0,1})([pbi]|br)>/$1-$2-$tag_marker/g
;
141 $formatted_interests =~ s/\<[^>]+\>//g;
142 $formatted_interests =~ s/(\/{0,1})-([pbi]|br)-$tag_marker/<$1$2>/g
;
144 # This is stored separately in the database, without tags, for full-text searching.
145 $research_interests =~ s/\<[^>]+\>//g;
147 if ($format eq "auto")
149 $formatted_interests =~ s/\r{0,1}\n\r{0,1}\n/<\/p><p
>/g
;
150 $formatted_interests = "<p>$formatted_interests</p>";
153 $args{keywords
} = HTML
::Entities
::encode_entities
($keywords);
154 $args{format
} = $format;
155 $args{research_interests
} = ($format eq "auto") ? HTML
::Entities
::encode_entities
($research_interests) : $formatted_interests;
156 $args{unlisted_organisms
} = $unlisted_organisms;
157 $self->set_args(%args); #necessary if you want to affect the arguments that will be stored
164 my %args = $self->get_args();
165 my $person = $self->get_object();
168 my $form = $self->get_form();
169 if ($form->is_editable()) {
170 $form->set_submit_method('post'); #avoid long URLs caused by research interests
173 my ($displayed_first_name, $displayed_last_name) = ($person->get_first_name(), $person->get_last_name());
174 #i added some html into empty name fields asking people to update their blank entries in the database.
175 #if we encounter one of these, do not fill in the field for their name with the html! --john
176 if($displayed_first_name=~/^</){$displayed_first_name='';}
177 if($displayed_last_name=~/^</){$displayed_last_name='';}
179 my @organisms = $person->get_organisms();
180 my @organism_ids = map {$_->get_organism_id()} @organisms;
181 my @organism_names = map {$_->get_organism_name()} @organisms;
182 my @organism_selections = map {$_->is_selected() ?
'1' : '0'} @organisms;
184 #element IDs for the source elements for the preview panes
185 $self->{html_source_element_id
} = 'html_source';
186 $self->{organisms_source_element_id
} = 'organisms_source';
188 my $default_field_length = 22;
189 if($form->is_editable()) {
190 $form->add_label(display_name
=> "",
191 field_name
=> "lbl1",
192 contents
=> "If you would like to enter or update information at this time but do not want it displayed publically, check this option.");
194 $form->add_checkbox(display_name
=> "Censor contact information from directory search and public display",
195 field_name
=> "censor",
197 selected
=> $person->get_censored(),
199 getter
=> "get_censored",
200 setter
=> "set_censor");
202 $form->add_label(display_name
=> "",
203 field_name
=> "lbl2",
204 contents
=> "The first and last names you enter here will be displayed in the SGN directory (if not censored using the box above)."
205 . " Your username for this interface is not displayed.");
207 $form->add_select(display_name
=> "Salutation",
208 field_name
=> "salutation",
209 contents
=> $person->get_salutation(),
211 getter
=> "get_salutation",
212 setter
=> "set_salutation",
213 select_list_ref
=> [qw(None Prof. Dr. Mr. Ms. Mrs.)],
214 select_id_list_ref
=> ['', qw(Prof. Dr. Mr. Ms. Mrs.)]);
215 $form->add_field(display_name
=> "First name",
216 field_name
=> "first_name",
217 contents
=> $displayed_first_name,
218 length => $default_field_length,
220 getter
=> "get_first_name",
221 setter
=> "set_first_name",
222 validate
=> "string");
223 $form->add_field(display_name
=> "Last name",
224 field_name
=> "last_name",
225 contents
=> $displayed_last_name,
226 length => $default_field_length,
228 getter
=> "get_last_name",
229 setter
=> "set_last_name",
230 validate
=> "string");
231 $form->add_field(display_name
=> "Organization",
232 field_name
=> "organization",
233 contents
=> $person->get_organization(),
236 getter
=> "get_organization",
237 setter
=> "set_organization");
238 $form->add_textarea(display_name
=> "Address",
239 field_name
=> "address",
240 contents
=> $person->get_address(),
244 getter
=> "get_address",
245 setter
=> "set_address");
246 $form->add_field(display_name
=> "Country",
247 field_name
=> "country",
248 contents
=> $person->get_country(),
249 length => $default_field_length,
251 getter
=> "get_country",
252 setter
=> "set_country");
253 if($form->is_editable()) {
254 $form->add_label(display_name
=> "",
255 field_name
=> "lbl3",
256 contents
=> "If you wish to be contacted via telephone or fax by interested parties who found your directory entry on SGN, please enter"
257 . " telephone numbers here. These numbers will be displayed publically to all SGN users who view your entry.");
259 $form->add_field(display_name
=> "Phone",
260 field_name
=> "phone",
261 contents
=> $person->get_phone_number(),
262 length => $default_field_length,
264 getter
=> "get_phone_number",
265 setter
=> "set_phone_number");
266 $form->add_field(display_name
=> "Fax",
268 contents
=> $person->get_fax(),
269 length => $default_field_length,
272 setter
=> "set_fax");
273 if($form->is_editable())
275 $form->add_label(display_name
=> "",
276 field_name
=> "lbl4",
277 contents
=> "If you would like to be contacted via email by interested parties who found your directory entry on SGN, please enter"
278 . " an address here. This address will be displayed publically to all SGN users who view your entry.");
280 $form->add_field(display_name
=> "Contact E-mail",
281 field_name
=> "contact_email",
282 contents
=> $person->get_contact_email(),
283 length => $default_field_length,
285 getter
=> "get_contact_email",
286 setter
=> "set_contact_email");
287 $form->add_field(display_name
=> "Website",
288 field_name
=> "webpage",
289 contents
=> $person->get_webpage(),
290 length => $default_field_length,
292 getter
=> "get_webpage",
293 setter
=> "set_webpage");
294 if($form->is_editable())
296 $form->add_label(display_name
=> "",
297 field_name
=> "lbl5",
298 contents
=> "Enter research keywords (for searching) and a fuller explanation of your research interests (for searching and displaying) below.");
300 $form->add_field(display_name
=> "Keywords",
301 field_name
=> "keywords",
302 contents
=> $person->get_research_keywords(),
305 getter
=> 'get_research_keywords',
306 setter
=> 'set_research_keywords');
307 if($form->is_editable())
309 $form->add_label(display_name
=> "",
310 field_name
=> "lbl6",
311 contents
=> "Please separate terms with a <b>semicolon</b>. Do not use quotation marks or other punctuation. (Example: fruit development;"
312 . " host pathogen interaction; drought tolerance)");
314 $form->add_multiselect(display_name
=> "Research Organisms",
315 field_name
=> "organisms",
316 choices
=> \
@organism_ids,
317 labels
=> \
@organism_names,
318 contents
=> \
@organism_selections,
320 getter
=> 'get_organism_id_string');
321 if($form->is_editable())
323 $form->add_label(display_name
=> "",
324 field_name
=> "lbl7",
325 contents
=> "You may select multiple organisms by clicking on more than one while holding the control key down. If an organism is not listed"
326 . " in the selection box, please specify your additional organisms by their official Latin names in the text entry box below, in"
327 . " a <b>semicolon</b>-separated list.");
328 #the contents will be edited by the pre-storing validation function; notice this field never needs to be gotten, only sotten
329 $form->add_field(display_name
=> "Unlisted Organisms",
330 field_name
=> "unlisted_organisms",
331 id
=> $self->{organisms_source_element_id
},
335 setter
=> 'add_organisms');
338 if($form->is_editable()) {
339 $form->add_radio_list(display_name
=> "",
340 field_name
=> "format",
341 id_prefix
=> "format_",
342 choices
=> ["auto", "html"],
343 labels
=> ["Auto-format (entry is plain text)", "Use HTML markup tags"],
344 contents
=> $person->get_user_format(),
346 getter
=> 'get_user_format',
347 setter
=> 'set_user_format');
349 $form->add_label(display_name
=> "",
350 field_name
=> "lbl8",
351 contents
=> "For auto-formatted plain text entries, separate paragraphs by a single empty line. For HTML entries, please use only paragraph"
352 . " tags (<p>), bold and italic tags (<b>, <i>), and break tags (<br>). All other tags will be removed.");
354 $form->add_textarea(display_name
=> "Interests",
355 field_name
=> "research_interests",
356 id
=> $self->{html_source_element_id
},
357 contents
=> $person->get_research_interests(),
361 getter
=> 'get_research_interests',
362 setter
=> 'set_research_interests');
363 if($form->get_action() =~ /edit|store/)
365 $form->add_label(display_name
=> "",
366 field_name
=> "lbl9",
367 contents
=> "The preview feature below (requires JavaScript) shows your current interests statement if you've selected HTML format. Also,"
368 . " if you need to define new organisms, the preview will show how the website is parsing the text you've entered above.");
371 #for allowing the form to make changes
372 if($form->is_editable())
374 $form->add_hidden(display_name
=> "ID", field_name
=> "sp_person_id", contents
=> $person->get_sp_person_id());
375 $form->add_hidden(display_name
=> "Action", field_name
=> "action", contents
=> "store");
378 if($form->is_editable())
380 $form->set_reset_button_text("Clear");
381 $form->set_submit_button_text("Submit Changes");
383 if($self->get_action() =~ /view|edit/)
385 $form->from_database();
388 elsif($self->get_action() =~ /store/)
390 $form->from_request($self->get_args());
395 #return an HTML string for a toolbar with other possible actions for the form
396 sub get_actions_toolbar
399 my $user_id= $self->get_user()->get_sp_person_id();
400 my @owners= $self->get_owners();
402 my $script_name = $self->get_script_name();
403 my $user_is_owner = (grep {/^$user_id$/} @owners);
404 my %args = $self->get_args();
405 my $sp_person_id = $args{sp_person_id
};
407 my ($login_user_id, $user_type) = CXGN
::Login
->new($self->get_dbh())->has_session();
409 if($user_is_owner || $user_type eq 'curator')
411 $home = qq(<a href
="top-level.pl?sp_person_id=$sp_person_id">[Directory Update Home
]</a
> 
; 
;);
415 $home = qq(<span
class="ghosted">[Directory Update Home
]</span
>);
418 if($self->get_action() eq "edit")
420 if($user_is_owner || $user_type eq 'curator')
422 return $home . qq(<a href
="$script_name?action=view&sp_person_id=$sp_person_id">[Cancel Edit
]</a
>);
426 return $home . qq(<span
class="ghosted">[Cancel Edit
]</span
>);
429 elsif($self->get_action() eq "view")
431 if($user_is_owner || $user_type eq 'curator')
433 return $home . qq(<a href
="$script_name?action=edit&sp_person_id=$sp_person_id">[Edit
]</a
>);
437 return $home . qq(<span
class="ghosted">[Edit
]</span
>);
440 elsif($self->get_action() eq "store") {}
446 my $person = $self->get_object();
447 my $page = $self->get_page();
448 #SimpleFormPage takes care of some unknown action strings, but we don't handle the full set of actions it supports
449 if($self->get_action() !~ /^view|edit|store$/)
451 $page->message_page("Illegal parameter: action '" . $self->get_action() . "' is not supported by " . $self->get_script_name());
455 $page->add_style(text
=> ".subtitle {font-weight: bold; text-align: center}");
456 $page->add_style(text
=> ".invisible {display: none}");
457 $page->jsan_use("CXGN.DynamicPreviewPane", "MochiKit.DOM", "MochiKit.Signal"); #include javascript modules
458 $page->header("Sol People: contact and research info");
460 print qq{<table
><tr valign
="middle"><td width
="670" align
="center" valign
="middle" ><br
/>};
462 print page_title_html
("Personal info for " . $person->get_first_name() . " " . $person->get_last_name());
467 print qq { <a href
="https://gravatar.com/" class="footer" ><img src
="http://gravatar.com/avatar/}. md5_hex(lc($person->get_contact_email())). qq {?d=mm" /></a
>};
468 print "</td></tr></table>";
472 print $self->get_actions_toolbar() . "<hr />\n";
473 print $self->get_form()->as_table_string();
476 if($self->get_action() =~ /^edit$/) {
477 #show the preview panes
478 my $html_preview_pane_parent_id = 'html_preview_pane_parent';
479 my $organisms_preview_pane_parent_id = 'organisms_preview_pane_parent';
482 <div class="subtitle">Interests and Organisms Preview</div>
483 <div id="all_html_preview_stuff">
484 Your research interests will be displayed on SGN\'s website as shown in the box below (the box will not be displayed).
485 <div id="$html_preview_pane_parent_id"></div>
487 <br />You have entered the following currently unlisted organisms. Please check that these names are spelled correctly.
488 Once committed, these names cannot be changed by this interface.
489 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>.
490 <div id="$organisms_preview_pane_parent_id"></div>
491 <script type="text/javascript">
492 DynamicPreviewPane.createPreviewPane('html', '$self->{html_source_element_id}', '$html_preview_pane_parent_id');
493 DynamicPreviewPane.createPreviewPane('list(;)', '$self->{organisms_source_element_id}', '$organisms_preview_pane_parent_id');
495 //show or hide the interests preview pane based on user format selection
496 function makeVisible() {MochiKit.DOM.removeElementClass(this, "invisible");}
497 function makeInvisible() {MochiKit.DOM.addElementClass(this, "invisible");}
498 var previewPaneSection = MochiKit.DOM.getElement("all_html_preview_stuff");
499 MochiKit.Signal.connect("format_auto", "onclick", previewPaneSection, makeInvisible);
500 MochiKit.Signal.connect("format_html", "onclick", previewPaneSection, makeVisible);
502 if(MochiKit.DOM.getElement("format_auto").checked) MochiKit.DOM.addElementClass("all_html_preview_stuff", "invisible");
507 my ($locus_annotations, $more_annotations) ;
508 my @annotated_loci = ();
510 if ($self->get_action() !~ /^edit$/) {
511 my $person_id= $person->get_sp_person_id();
512 @annotated_loci = CXGN
::Phenome
::Locus
::get_locus_ids_by_editor
($self->get_dbh(), $person_id);
515 my $max = @annotated_loci;
516 if (@annotated_loci>24) {
517 $more = @annotated_loci-24;
522 for (my $i=0; $i<$top; $i++) {
523 my $locus = CXGN
::Phenome
::Locus
->new($self->get_dbh(), $annotated_loci[$i]);
524 my $symbol = $locus->get_locus_symbol();
525 my $locus_id = $locus->get_locus_id();
528 if ($locus_id && $symbol) { #deanx jan23 2008
530 $locus_annotations .= qq | <a href
="/locus/$locus_id/view">$symbol</a
> | }
532 $more_annotations .= qq { <a href
="/locus/$locus_id/view">$symbol</a
> };
538 $locus_annotations .= qq|<br
><b
>and <a href
="/search/locus">$more more
</a></b
><br
/>|;
544 if (@annotated_loci) {
546 print "Locus editor assignments: \n";
547 print $locus_annotations;
550 my $pop_list = $self->owner_populations();
552 print "<hr />\n $pop_list";
558 sub owner_populations
{
560 my $sp_person_id = $self->get_object_id();
561 my @pops = CXGN
::Phenome
::Population
->my_populations($sp_person_id);
562 my $pop_list = 'Populations:<br/>';
565 foreach my $pops (@pops) {
566 my $pop_name = $pops->get_name();
567 my $pop_id = $pops->get_population_id();
568 #my $is_public = $pops->get_privacy_status();
569 #if ($is_public) {$is_public = 'is publicly available';}
570 #if (!$is_public) {$is_public = 'is not publicly available yet';}
571 $pop_list .= qq |<a href
="/phenome/population.pl?population_id=$pop_id">$pop_name</a><br/>|;