added sol100 and chado cvterm pages to validate_all.t
[sgn.git] / lib / CXGN / Page / Form / SimpleFormPage.pm
blob91542b17863df4ba4a6fe5832c7d6bbb02f3b41c
3 =head1 NAME
5 SimpleFormPage.pm -- an abstract class that implements a simple webpage that can be use to add, view and modify information in the database.
7 =head1 DESCRIPTION
9 SimpleFormPage.pm works with the CXGN::Page::Form classes and user-defined database classes that have to follow certain guidelines for this class to work properly (in essence, the database classes need to follow the rules for the CXGN::Page::Form framework. For more information, see the documentation of the L<CXGN::Page::Form> classes).
11 SimpleFormPage.pm implements a simple authentication for the calls that modify database content. The function check_modify_privileges should return 1 if the current user had edit/delete privileges, otherwise it should return 0. The default implementation returns 1 for the owner and any logged in curators, 0 for all others.
13 SimpleFormPage contains a number of pre-populated accessors for often used CXGN features, such as CXGN::Page (get_page()), CXGN::DB::Connection (get_dbh()), and CXGN::Login (get_user()).
15 When creating a derived class, you need to override the following functions:
17 C<sub define_object()>
19 C<sub generate_form()>
21 If you want to create a page layout different from the default layout, you can also override:
23 C<sub display_page()>
25 =head1 AUTHOR(S)
27 Lukas Mueller (lam87@cornell.edu)
30 =head1 FUNCTIONS AND INTERFACES
32 The following is a list of object functions. Some of these functions are used internally, and some need to be overridden in child classes. See the description above and the individual functions for more details.
34 =cut
36 use strict;
37 use Carp;
39 use CXGN::Tools::Text qw | sanitize_string |;
40 use CXGN::Page;
41 use CXGN::Page::Form::Static;
42 use CXGN::Page::Form::Editable;
43 use CXGN::Page::Form::ConfirmStore;
45 use CXGN::Login;
46 use CXGN::DB::Connection;
48 use CXGN::People;
49 use CXGN::People::Person;
51 package CXGN::Page::Form::SimpleFormPage;
53 use CXGN::Page::FormattingHelpers qw / page_title_html blue_section_html /;
55 =head2 new
57 Usage: my $s = CXGN::Page::SimpleFormPageSubClass->new();
58 Desc: This class needs to be subclassed and certain functions
59 overridden (the functions are listed above). The constructor
60 should not be overridden or called from the overridden
61 constructor.
62 Ret:
63 Args:
64 Side Effects:
65 Example:
67 =cut
69 sub new {
70 my $class = shift;
71 my $schema= shift;
72 my $self = bless {}, $class;
74 my $primary_key = shift;
76 $self->set_page(CXGN::Page->new("SGN", "Lukas"));
77 my $dbh=$self->get_page()->get_dbh(); # reuse the dbh from the Page object
78 $self->set_dbh($dbh);
79 $dbh->add_search_path("$schema");
81 $self->set_login(CXGN::Login->new($self->get_dbh()));
82 $self->get_page->{request}->no_cache(1);
83 my %args = $self->get_page()->cgi_params(); #multi-valued parameters have values in a string, delimited by \0
85 # sanitize the inputs, we don't want to end up like bobby tables school.
87 foreach my $k (keys (%args)) {
88 $args{$k} = CXGN::Tools::Text::sanitize_string($args{$k});
91 $self->set_args(%args);
93 $self->define_object();
95 $self->set_action($args{action});
97 if (!$self->get_action()) {
98 $self->set_action("view");
101 if (!$self->get_object_id() && $self->get_action()!~/new|store|confirm_store/) {
102 $self->get_page()->message_page("No identifier provided to display data of this page for action view.");
104 else {
105 if ($self->get_action()!~/new|view|edit|store|delete|confirm_delete|confirm_store/) {
106 $self->get_page()->error_page("Undefined input. Cannot proceed. Sorry.\n");
110 if ($self->get_action() eq "view") {
111 $self->view();
113 elsif ($self->get_action() eq "edit") {
114 $self->edit();
116 elsif ($self->get_action() eq "new") {
117 $self->set_object_id(0);
118 my %args = $self->get_args();
119 $args{$self->get_primary_key()}=0;
120 $self->set_args(%args);
121 $self->add();
123 elsif ($self->get_action() eq "store") {
125 $self->store();
127 elsif ($self->get_action() eq "confirm_delete") {
128 my $id = $args{$self->get_primary_key()};
129 if (!$id) {
130 $self->get_page()->error_page("need an id for deleting");
132 $self->delete_dialog("Delete", "Object",
133 $self->get_primary_key(),
134 $id,
135 "<a href=\"".$self->get_script_name()."?".$self->get_primary_key()."=".$id."&amp;action=view\">Go back to detail page without deleting</a>");
138 elsif ($self->get_action() eq "delete") {
139 $self->delete();
141 elsif ($self->get_action() eq "confirm_store") {
143 $self->confirm_store();
145 return $self;
148 =head2 check_modify_privileges
150 Usage: if ($s->check_modifiy_privileges) { ...}
151 Desc: checks if the currently logged in user has sufficient
152 privileges for the current action. This can be overridden
153 if the default privileges are undesirable. The default is:
154 logged-in curators can do everything
155 logged-in owners can do everything
156 logged-in users can only view
157 non-logged-in users can only view
159 Ret: true if the user has sufficient privileges for the action.
160 Args:
161 Side Effects:
162 Example:
164 =cut
166 sub check_modify_privileges {
167 my $self = shift;
169 # implement quite strict access controls by default
171 my $person_id = $self->get_login()->verify_session();
172 my $user = CXGN::People::Person->new($self->get_dbh(), $person_id);
173 my $user_id = $user->get_sp_person_id();
174 if ($user->get_user_type() eq 'curator') {
175 return 0;
177 if ($user->get_user_type() !~ /submitter|sequencer|curator/) {
178 $self->get_page()->message_page("You must have an account of type submitter to be able to submit data. Please contact SGN to change your account type.");
181 my @owners = $self->get_owners();
182 if ((@owners) && (!(grep { $_ =~ /^$user_id$/ } @owners) )) {
183 # check the owner only if the action is not new
185 #my $owner_id = $self->get_object_owner();
186 # if (($owner_id) && ($owner_id != $user_id))
190 $self->get_page()->message_page("You do not have rights to modify this database entry because you do not own it. [$user_id, @owners]");
192 else {
193 return 0;
197 # override to check privileges for edit, store, delete.
198 # return 0 for allow, 1 for not allow.
199 return 0;
203 =head2 define_object
205 Usage:
206 Desc: define the object that this simple form page operates on.
207 The object needs to implement L<CXGN::DB::ModifiableI>
208 Ret:
209 Args:
210 Side Effects:
211 Example:
213 =cut
215 sub define_object {
216 my $self = shift;
218 # in the subclass, instantiate your object here and call
219 $self->set_object();
220 $self->set_object_id();
221 $self->set_primary_key();
225 # edit is an internally used function to show the editable form.
228 sub edit {
229 my $self = shift;
230 $self->check_modify_privileges();
231 $self->generate_form();
232 $self->display_page();
235 # add is an internally used function to generate an 'emtpy' form.
238 sub add {
239 my $self = shift;
240 $self->check_modify_privileges();
241 $self->generate_form();
242 $self->display_page();
246 =head2 store
248 Usage: $s->store()
249 Desc: stores the form into the database. In this default implementation,
250 which can but should not overridden, first, the privileges are checked
251 and all the form fields are validated for correctness. If this fails,
252 the input form is shown again with an appropriate error message.
253 Else, the store is issued for the object (set using set_object).
254 If this succeeds, the page is re-directed to the same page, but with
255 the 'view' action parameter.
256 Ret:
257 Args:
258 Side Effects:
259 Example:
261 =cut
264 sub store {
265 my $self = shift;
266 my $dont_show_form = shift;
268 $self->check_modify_privileges();
270 # for the store we need a properly formatted form so that we can
271 # use its validate and store functions.
273 $self->generate_form();
275 # validate the form
276 my %errors = $self->get_form()->validate($self->get_args());
277 if (!%errors)
280 #give the user the opportunity to modify, add or remove form parameters before committing them
281 #(this needs to be done after validate() because that assumes it'll have the parameters given on the form as displayed)
282 $self->validate_parameters_before_store();
284 # the form validated. Now let's check if it passes the uniqueness
285 # constraints.
286 # Assume that the database accessor class inherits from
287 # CXGN::DB::Modifiable and thus has a exists_in_database
288 # function - but don't make it a requirement. If it doesn't -- never mind.
290 # if ($self->get_object()->can("exists_in_database")) {
291 # if ($self->get_object()->exists_in_database()) {
292 # $self->get_page()->message_page("Some fields in this object must be unique in the database.
293 # Please modify your input and try to submit again.");
297 # eval
299 #print STDERR "**** about to call the get_form->store() ****";
300 $self->get_form()->store($self->get_args());
302 #give the user the opportunity to do anything related to the form after storing
303 $self->process_parameters_after_store();
304 # };
305 # if ($@) {
306 # $self->get_page()->message_page("An error occurred while attempting to store data. Please verify your input and try again. $@");
309 # was it an insert? get the insert id
311 if (!$self->get_object_id()) {
312 my $id = $self->get_form()->get_insert_id();
313 $self->set_object_id($id);
315 my %args = $self->get_args();
317 # if we are supposed to show the form, redirect to the
318 # script this script with the appropriate parameters,
319 # the object_id, and the action = view.
321 if (!$dont_show_form) {
322 my $url =
323 $self->get_script_name()."?".
324 $self->get_primary_key()."=".
325 $self->get_object_id()."&action=view";
327 $self->get_page()->client_redirect($url);
331 else {
332 # if there was an error, re-display the same page.
333 # the errors will be displayed on the page for each
334 # field that did not validate.
336 $self->display_page();
337 exit();
343 =head2 view
345 Description: Displays the page in the 'view' mode.
347 =cut
349 sub view {
350 my $self = shift;
352 # make sure we get a static form.
354 $self->set_action("view");
356 $self->generate_form();
357 $self->display_page();
360 =head2 get_user()
362 Usage:
363 Desc: returns a CXGN::People::Person object
364 that represents the currently logged in user
365 or an empty user if the page is being viewed
366 without login.
367 Ret:
368 Args:
369 Side Effects:
370 Example:
372 =cut
374 sub get_user {
375 my $self=shift;
376 my $person_id = $self->get_login()->has_session();
377 return CXGN::People::Person->new($self->get_dbh(), $person_id);
380 =head2 generate_form
382 Usage: $s->generate_form()
383 Desc: this method needs to be overridden. In this method,
384 a new form must be generated (as a CXGN::Page::Form object)
385 which can be instantiated either as a Static or Editable
386 version.
387 Ret:
388 Args:
389 Side Effects:
390 Example:
392 =cut
394 sub generate_form {
395 my $self = shift;
396 warn "Please subclass 'generate_form' function!\n";
399 =head2 delete_dialog
401 Desc: Displays a verification dialog for a delete action.
403 =cut
405 sub delete_dialog {
406 my $self = shift;
407 $self->check_modify_privileges();
408 my $title = shift;
409 my $object_name = shift;
410 my $field_name = shift;
411 my $object_id = shift;
412 my $back_link = shift;
414 $self->get_page()->header();
416 page_title_html("$title");
417 print qq {
418 <form>
419 Delete database object $object_name (id=$object_id)?
420 <input type="hidden" name="action" value="delete" />
421 <input type="hidden" name="$field_name" value="$object_id" />
422 <input type="submit" value="Delete" />
423 </form>
425 $back_link
429 $self->get_page()->footer();
433 =head2 delete
435 Usage:
436 Desc: actually performs the delete in the database.
437 Database objects should implement the delete as a
438 obsoletion. See L<CXGN::DB::ModifiableI>
439 Ret:
440 Args:
441 Side Effects:
442 Example:
444 =cut
446 sub delete {
447 my $self = shift;
448 warn "Override 'delete' function in derived class\n";
450 $self->get_page()->message_page("Deleting is not implemented for this object");
454 =head2 confirm_store
456 Desc: displays a confirmation dialog for before certain store
457 operations.
459 =cut
461 sub confirm_store {
463 my $self=shift;
464 $self->get_page()->header();
467 page_title_html("Confirm store");
468 print qq {
469 <form>
470 Store object in the database?
471 <input type="hidden" name="action" value="store" />
473 <input type="submit" value="Store" />
474 </form>
476 <a href="javascript:history.back(1)">Go back without storing the object</a>
480 $self->get_page()->footer();
484 =head2 display_page
486 Usage: $s->display_page()
487 Desc: can be overridden in the subclass. In the default
488 implementation, displays the header and footer, the
489 appropriate edit links and the form in table format,
490 in the appropriate Editable or Static form.
491 Ret:
492 Args:
493 Side Effects:
494 Example:
496 =cut
498 sub display_page {
499 my $self=shift;
501 $self->get_page()->header();
502 print $self->get_edit_links();
503 $self->get_form()->as_table();
505 $self->get_page()->footer();
510 =head2 get_page, set_page
512 Usage: $p = $s->get_page();
513 Desc: get the page object of the current page.
514 the simple form page automatically initializes
515 this property.
516 Ret:
517 Args:
518 Side Effects:
519 Example:
521 =cut
523 sub get_page {
524 my $self=shift;
525 return $self->{page};
529 sub set_page {
530 my $self=shift;
531 $self->{page}=shift;
534 =head2 get_dbh
536 Usage: $dbh = $s->get_dbh();
537 Desc: get the dbh connection of this simple form page.
538 the simple form page constructor initializes this
539 property
540 Ret:
541 Args:
542 Side Effects:
543 Example:
545 =cut
547 sub get_dbh {
548 my $self=shift;
549 return $self->{dbh};
553 sub set_dbh {
554 my $self=shift;
555 $self->{dbh}=shift;
558 =head2 get_login
560 Usage: my $l = $s->get_login()
561 Desc: get the login object of the simple form page.
562 the simple form page constructor initializes this
563 property.
564 Ret:
565 Args:
566 Side Effects:
567 Example:
569 =cut
571 sub get_login {
572 my $self=shift;
573 return $self->{login};
577 sub set_login {
578 my $self=shift;
579 $self->{login}=shift;
582 =head2 get_args
584 Usage: my %args = $s ->get_args()
585 Desc: gets the page arguments as a hash. The
586 simple form page constructor initializes
587 this property.
588 Ret:
589 Args:
590 Side Effects:
591 Example:
593 =cut
595 sub get_args {
596 my $self=shift;
597 if (!$self->{args}) { %{$self->{args}} = (); }
598 return %{$self->{args}};
602 sub set_args {
603 my $self=shift;
604 %{$self->{args}}=@_;
608 =head2 get_object, set_object
610 Usage: $s->define_object(CXGN::Phenome::Locus->new($self->get_dbh(), $self->get_object_id())
611 Desc: sets the object that this simple form page operates on.
612 this needs to be called in the define_object method, which must
613 be overridden. see define_object for more info.
614 Ret:
615 Args:
616 Side Effects:
617 Example:
619 =cut
621 sub get_object {
622 my $self=shift;
623 return $self->{object};
627 sub set_object {
628 my $self=shift;
629 $self->{object}=shift;
632 =head2 get_owners
634 Usage: my @owners = $self->get_owners()
635 Desc: find the owner(s) of your object
636 most tables have a single owner (the field sp_person_id)
637 and this accessor will return an array with one element.
638 However, if the object has multiple owners
639 (such as 'Locus' - group of owners is defined in locus_owner table)
640 this function will need to be overriden (see CXGN/Phenome/Locus.pm)
641 Ret: an array
642 Args: none
643 Side Effects:
644 Example:
646 =cut
648 sub get_owners {
649 my $self=shift;
650 return @{$self->{owners}};
654 sub set_owners {
655 my $self=shift;
656 @{$self->{owners}}=@_;
659 =head2 get_action, set_action
661 Usage: my $action = $s ->get_action()
662 Desc: the current action of the simple form page.
663 can be either edit, save, view, etc.
664 the action is set by the constructor, so the
665 setter should never be called.
666 Ret:
667 Args:
668 Side Effects:
669 Example:
671 =cut
673 sub get_action {
674 my $self=shift;
675 return $self->{action};
679 sub set_action {
680 my $self=shift;
681 $self->{action}=shift;
685 =head2 get_primary_key, set_primary_key
687 Usage: $s->set_primary_key("read_id")
688 Desc: sets the name of the primary key of the data at hand.
689 Ret:
690 Args:
691 Side Effects:
692 Example:
694 =cut
696 sub get_primary_key {
697 my $self=shift;
698 return $self->{primary_key};
703 sub set_primary_key {
704 my $self=shift;
705 $self->{primary_key}=shift;
708 =head2 get_script_name, set_script_name
710 Usage: $s->get_script_name()
711 Desc: gets the name of the script that uses the simple form page.
712 useful for constructing links.
713 Ret:
714 Args:
715 Side Effects:
716 Example:
718 =cut
720 sub get_script_name {
721 my $self=shift;
722 if (!exists($self->{script_name})) {
723 #return CXGN::Apache::Request::page_name();
724 return $ENV{SCRIPT_NAME};
726 else {
727 return $self->{script_name};
732 sub set_script_name {
733 my $self=shift;
734 $self->{script_name}=shift;
737 =head2 get_object_id, set_object_id
739 Usage: my $id = $s->get_object_id()
740 Desc: get/set the primary id for the object at hand
741 Ret:
742 Args:
743 Side Effects:
744 Example:
746 =cut
748 sub get_object_id {
749 my $self=shift;
750 return $self->{object_id};
754 sub set_object_id {
755 my $self=shift;
756 $self->{object_id}=shift;
759 =head2 get_form, set_form
761 Usage: my $f = $s->get_form()
762 Desc: get the form object associated with the simple
763 form page. The form is initialized automatically to the
764 appropriate subclass, either L<CXGN::Page::Form::Static> or
765 L<CXGN::Page::Form::Editable>, depending on the action,
766 according to the form object defined in the generate_form
767 method, which needs to be overridden in the subclass.
768 Ret:
769 Args:
770 Side Effects:
771 Example:
773 =cut
775 sub get_form {
776 my $self=shift;
778 return $self->{form};
782 sub set_form {
783 my $self=shift;
784 $self->{form}=shift;
787 =head2 init_form
789 Usage: $s->init_form()
790 Desc: initializes the correct form subclass, either
791 CXGN::Page::Form::Editable or CXGN::Page::Form::Static,
792 or CXGN::Page::Form::ConfirmStore.
793 Ret:
794 Args:
795 Side Effects:
796 Example:
798 =cut
800 sub init_form {
801 my $self = shift;
803 if ($self->get_action() =~/edit|^store|new/) {
804 $self->set_form( CXGN::Page::Form::Editable -> new() );
806 }elsif ($self->get_action() =~/confirm_store/) {
807 $self->set_form( CXGN::Page::Form::ConfirmStore->new() ) ;
809 }else {
810 $self->set_form( CXGN::Page::Form::Static -> new() );
815 =head2 get_request, set_request
817 Usage: my $r = $s->get_request()
818 Desc: returns the apache request object for the
819 simple form page.
820 Ret:
821 Args:
822 Side Effects:
823 Example:
825 =cut
827 sub get_request {
828 my $self=shift;
829 return $self->{request};
833 sub set_request {
834 my $self=shift;
835 $self->{request}=shift;
838 =head2 validate_parameters_before_store
840 Desc: Allow for custom validation of the form as a whole,
841 and in perl rather than javascript; parameters can be
842 removed, modified or added as necessary before the database
843 is touched. Meant to be overridden.
845 Complements ElementI::validate(), which simply allows for
846 checking format of individual fields.
847 validate_parameters_before_store() is called after all
848 Elements have validate()d themselves.
850 To avoid letting the store go through, create an error or
851 message page and call exit().
853 Ret: none
855 =cut
857 sub validate_parameters_before_store {
860 =head2 process_parameters_after_store
862 Desc: Allow for custom postprocessing, eg logging to disk, sending
863 e-mail confirmations, or changing the template of the form
864 before it\'s viewed again. Meant to be overridden.
865 Ret: none
867 =cut
869 sub process_parameters_after_store {
872 =head2 get_edit_links
874 Usage: my $edit_links = $s->get_edit_links()
875 Desc: gets a string corresponding to the edit links to be placed
876 on the page, as appropriate for the current action. The edit
877 link, for example, is replaced with a 'cancel edit' in edit mode
878 etc.
879 Ret:
880 Args:
881 Side Effects:
882 Example:
884 =cut
887 sub get_edit_links {
888 my $self =shift;
889 my $form_name = shift;
890 return $self->get_new_link_html($form_name)." ".
891 $self->get_edit_link_html($form_name)." ".$self->get_delete_link_html($form_name);
895 =head2 function get_new_link_html
897 Synopsis:
898 Description: Creates an appropriate 'new' link for the edit links.
900 =cut
902 sub get_new_link_html {
903 my $self = shift;
904 my $form_name = shift;
906 my $script_name = $self->get_script_name();
907 my $primary_key = $self->get_primary_key();
908 my $object_id = $self->get_object_id();
910 my $new_link = qq { <a href="$script_name?action=new&amp;form=$form_name">[New]</a> };
911 if ($self->get_action() eq "edit") {
912 $new_link = qq { <span class="ghosted">[New]</span> };
914 if ($self->get_action() eq "new") {
915 $new_link = qq { <a onClick="history.go(-1)">[Cancel]</a> };
917 return $new_link;
920 =head2 function get_edit_link_html
922 Description: Creates an appropriate 'edit' link for the edit links.
924 =cut
926 sub get_edit_link_html {
927 my $self = shift;
928 my $form_name = shift;
930 my $edit_link = "";
931 my $script_name = $self->get_script_name();
932 my $primary_key = $self->get_primary_key();
933 my $object_id = $self->get_object_id();
935 my $user_id= $self->get_user()->get_sp_person_id();
936 my @owners= $self->get_owners();
937 if (($self->get_user()->get_user_type() eq "curator") || grep{/^$user_id$/} @owners ) {
938 $edit_link = qq { <a href="$script_name?action=edit&amp;form=$form_name&amp;$primary_key=$object_id">[Edit]</a> };
940 }else {
941 $edit_link = qq { <span class="ghosted">[Edit]</span> };
945 if ($self->get_action() eq "edit") {
946 $edit_link = qq { <a href="$script_name?action=view&amp;form=$form_name&amp;$primary_key=$object_id">[Cancel Edit]</a> };
949 if ($self->get_action() eq "new") {
950 $edit_link = qq { <span class="ghosted">[Edit]</span> };
953 return $edit_link;
956 =head2 function get_delete_link_html
958 Description: Generates and appropriate 'delete' link for the edit links.
960 =cut
962 sub get_delete_link_html {
963 my $self = shift;
964 my $form_name = shift;
966 my $delete_link = "";
967 my $script_name = $self->get_script_name();
968 my $primary_key = $self->get_primary_key();
969 my $object_id = $self->get_object_id();
970 my $user_id= $self->get_user()->get_sp_person_id();
971 my @owners= $self->get_owners();
972 if (($self->get_user()->get_user_type() eq "curator") || grep{/^$user_id$/} @owners ) {
974 $delete_link = qq { <a href="$script_name?action=confirm_delete&amp;form=$form_name&amp;$primary_key=$object_id">[Delete]</a> };
976 }else {
977 $delete_link = qq { <span class="ghosted">[Delete]</span> };
980 if ($self->get_action() eq "edit") {
981 $delete_link = qq { <span class="ghosted">[Delete]</span> };
984 if ($self->get_action() eq "new") {
985 $delete_link = qq { <span class="ghosted">[Delete]</span> };
987 return $delete_link;
991 return 1;