File: Plugin.pm

package info (click to toggle)
gbrowse 2.56%2Bdfsg-8
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 13,104 kB
  • sloc: perl: 50,765; sh: 227; sql: 62; makefile: 50; ansic: 27
file content (1088 lines) | stat: -rw-r--r-- 32,987 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
package Bio::Graphics::Browser2::Plugin;
# $Id$
# base class for plugins for the Generic Genome Browser

=head1 NAME

Bio::Graphics::Browser2::Plugin -- Base class for gbrowse plugins.

=head1 SYNOPSIS

 package Bio::Graphics::Browser2::Plugin::MyPlugin;
 use Bio::Graphics::Browser2::Plugin;
 use CGI ':standard';
 @ISA = 'Bio::Graphics::Browser2::Plugin';

 # called by gbrowse to return name of plugin for popup menu
 sub name        { 'Example Plugin' }

 # called by gbrowse to return the descriptive verb for popup menu
 sub verb        { 'Demonstrate' }

 # called by gbrowse to return description of plugin
 sub description { 'This is an example plugin' }

 # called by gbrowse to return type of plugin
 sub type        { 'annotator' }

 # called by gbrowse to configure default settings for plugin
 sub config_defaults {
     my $self = shift;
     return {foo => $value1,
             bar => $value2}
 }

 # called by gbrowse to reconfigure plugin settings based on CGI parameters
 sub reconfigure {
   my $self = shift;
   my $current = $self->configuration;
   $current->{foo} = $self->config_param('foo');
   $current->{bar} = $self->config_param('bar');
 }

 # called by gbrowse to create a <form> fragment for changing settings
 sub configure_form {
   my $self    = shift;
   my $current = $self->configuration;
   my $form = textfield(-name  => $self->config_name('foo'),
                        -value => $current->{foo})
              .
              textfield(-name  => $self->config_name('bar'),
                        -value => $current->{bar});
   return $form;
 }

 # called by gbrowse to annotate the DNA, returning features
 sub annotate {
    my $self     = shift;
    my ($segment,$coordinate_mapper)  = @_;
    my $config   = $self->configuration;
    my $feature_list = $self->new_feature_list;
    $feature_list->add_type('my_type' => {glyph => 'generic',
					  key   => 'my type',
					  bgcolor => 'green',
					  link    => 'http://www.google.com/search?q=$name'
					 }
			   );
    # do something with the sequence segment
    my @features = do_something();
    $feature_list->add_feature($_ => 'my_type') foreach @features;
    return $feature_list;
 }


=head1 DESCRIPTION

This is the base class for Generic Genome Browser plugins.  Plugins
are perl .pm files that are stored in the gbrowse.conf/plugins
directory.  Plugins are activated in the gbrowse.conf/ configuration
file by including them on the list indicated by the "plugins" setting:

 plugins = BatchDumper FastaDumper GFFDumper
	   OligoFinder RestrictionAnnotator

Site-specific plugins may be placed in one or more site-specific
directories and added to the plugin search path using the plugin_path
setting:

  plugin_path = /usr/local/gbrowse_plugins

GBrowse currently recognizes five distinct types of plugins:

=over 4

=item 1) dumpers

These plugins receive the genomic segment object and generate a dump
-- the output can be text, html or some other specialized
format. Example: GAME dumper.

=item 2) finders

These plugins accept input from the user and return a
list of genomic regions.  The main browser displays the found regions
and allows the user to select among them. Example: BLAST search.

=item 3) annotators 

These plugins receive the genomic segment object and either 1) return 
a list of features which are overlayed on top of the detailed view 
(Example: restriction site annotator) or 2) update the database with 
new or modified features and return nothing (Example: basic editor)

=item 4) trackfilters

These plugins can be used to reduce the complexity of sites that have
many tracks, by providing search and filtering functions for the track
table. When a trackfilter is active, its form-based user interface is
positioned directly above the tracks table, and changes to the for
cause the list of tracks to be updated dynamically.

=item 5) highlighters

These plugins will color-highlight features based on user-defined
attributes. For example, you could highlight all features that are in
the positive strand.

=item 6) filters

These plugins will filter features based on user-defined
attributes. Only features that match the attributes will be
displayed. For example, you could filter out RNA transcript features
based on their size, so that only features that are less than 50 bp in
length (e.g. short RNAs) are shown.

=back
	
All plug-ins inherit from Bio::Graphics::Browser2::Plugin, which
defines reasonable (but uninteresting) defaults for each of the
methods.  Specific behavior is then implemented by selectively
overriding certain methods.

The best way to understand how this works is to look at the source
code for some working plugins.  Examples provided with the gbrowse
distribution include:

=over 4

=item GFFDumper.pm

A simple dumper which produces GFF format output representing the
features of the currently-selected segment.

=item FastaDumper.pm

A more complex dumper that illustrates how to create and manage
persistent user-modifiable settings.

=item SequenceDumper.pm

Another dumper that shows how plugins interact with the Bio::SeqIO
system.

=item OligoFinder.pm

A finder that searches for short oligos in the entire database.  (Only
works with Bio::DB::GFF databases.)

=item RestrictionAnnotator.pm

An annotator that finds restriction sites in the currently selected
region of the genome.  It creates a new track for each type of
restriction site selected.

=item RandomGene.pm

An example annotator that generates random gene-like structures in the
currently displayed region of the genome.  It's intended as a template
for front-ends to gene prediction programs.

=item AttributeHiliter.pm

An example feature hiliter that works with Bio::DB::GFF and
Bio::SeqFeature::Store databases.

=item FilterTest.pm

An example feature filter that filters based on strand of the feature.

=item SimpleTrackFinder.pm

An example track filter that filters tracks based on their name.

=item SourceTrackFinder.pm

Another example track filter that filters tracks based on the contents
of their "track source" and "data source" options.

=back

=head1 METHODS

The remainder of this document describes the methods available to the
programmer.

=head2 INITIALIZATION

The initialization methods establish the human-readable name,
description, and basic operating parameters of the plugin.  They
should be overridden in each plugin you write.

=over 4

=item $name = $self->name()

Return a short human-readable name for the plugin.  This will be
displayed to the user in a menu using one of the following forms:

    Dump <name>
    Find <name>
    Annotate <name>
    plugin_defined_verb <name>

=item $description = $self->description()

This method returns a longer description for the plugin.  The text may
contain HTML tags, and should describe what the plugin does and who
wrote it.  This text is displayed when the user presses the "About..."
button.

=item $verb = $self->verb()

This method returns a verb to be used in the plugin popup menu
in cases where the main three don't fit.  This method should
be set return whitespace or an empty string (not undefined) 
if you do not want a descriptive verb for the menu

=item $suppress_title = $self->suppress_title()

The purpose of this methods is to suppress the 'Configure...'
or 'Find...' title that is printed at the top of the page when the 
plugin is loaded.  It will return false unless overridden by a plugin where
this behaviour is desired.

=item $type = $self->type()

This tells gbrowse what the plugin's type is.  It must return one of
the scripts "dumper," "finder,", "annotator" as described in the
introduction to this documentation.  If the method is not overridden,
type() will return "dumper."

=item $self->init()

This method is called before any methods are invoked and allows the
plugin to do any run-time initialization it needs.  The default is to
do nothing.  Ordinarily this method does not need to be implemented.

=back

=head2 ACCESS TO THE ENVIRONMENT

The following methods give the plugin access to the environment,
including the gbrowse page settings, the sequence features database,
and the plugin's own configuration settings.

These methods do not generally need to be overridden.

=over 4

=item $config = $self->configuration()

Call this method to retrieve the persistent configuration for this
plugin.  The configuration is a hashref containing the default
configuration settings established by config_defaults(), possibly
modified by the user.  Due to cookie limitations, the values of the
hashref must be scalars or array references.

See CONFIGURATION METHODS for instructions on how to create and
maintain the plugin's persistent configuration information.

=item $renderer = $self->renderer

This method returns a copy of the Render object, which provides access
to the internal workings of the page layout engine. You will need to 
troll the Bio::Graphics::Browser2::Render source code to understand
how to use this object.

=item $database = $self->database

This method returns a copy of the default database.  Depending on the
data source chosen by the gbrowse administrator, this may be a
Bio::DB::GFF database, a Bio::DB::Das::Chado database, a Bio::Das
database, a Bio::DB::Das::BioSQL database, or any of the other
Das-like databases that gbrowse supports.

=item @dbs   = $self->all_databases

This method returns copies of all the databases defined for this data
source. Most useful for Finder plugins.

=item @track_names = $self->selected_tracks

This method returns the list of track names that the user currently
has turned on.  Track names are the internal names identified in
gbrowse configuration file stanzas, for example "ORFs" in the
01.yeast.conf example file.

=item @feature_types = $self->selected_features

This method returns the list of feature types that the user currently
has turned on.  Feature types are the feature identifiers indicated by
the "feature" setting in each track in the gbrowse configuration file,
for example "ORF:sgd" in the 01.yeast.conf [ORFs] track.

=item $gbrowse_settings = $self->page_settings

This method returns a big hash containing the current gbrowse
persistent user settings.  These settings are documented in the
gbrowse executable source code.  You will not ordinarily need to
access the contents of this hash, and you should *not* change its
values.

=item $browser_config = $self->browser_config

This method returns a copy of the Bio::Graphics::Browser2::DataSource
object that drives gbrowse.  This object allows you to interrogate
(and change!)  the values set in the current gbrowse configuration
file.

=item $value = $self->setting('setting name')

The recommended use for this object is to recover plugin-specific
settings from the gbrowse configuration file.  These can be defined by
the gbrowse administrator by placing the following type of stanza into
the gbrowse config file:

  [GOSearch:plugin]
  traverse_isa = 1
  use_server   = http://amigo.geneontology.org

"GOSearch" is the package name of the plugin, and the ":plugin" part
of the stanza name tells gbrowse that this is a plugin-private
configuration section.

You can now access these settings from within the plugin by using the
following idiom:

   my $traverse_isa   = $self->setting('traverse_isa');
   my $server         = $self->setting('use_server');

This facility is intended to be used for any settings that should not
be changed by the end user.  Persistent user preferences should be
stored in the hash returned by configuration().

If your plugin inherits from another one, then the inheritance path will
be searched for settings. For example, if the GOSearch plugin inherits
from the OntologySearch plugin, then setting() will search first for a stanza named
"GOSearch:plugin" and then for "OntologySearch:plugin".

=item $search = $self->db_search

This method returns a Bio::Graphics::Browser2::RegionSearch object,
which you can use to search all local and remote databases. The
interface is this:

 $features = $search->search_features(\%args);

where \%args are the various arguments (e.g. -type, -seq_id, -name)
passed to the dbadaptors' search_features() method. Alternatively:

 $features = $search->search_features('keyword string')

which implements GBrowse's heuristic search.

The result is an array reference of features found.

The search object also has a get_seq_stream() method that accepts the
same arguments as search_features() but returns an iterator. The
iterator implements a next_seq() method.

=item $language = $self->language

This method returns the current I18n language file. You can use this
to make translations with the tr() method:

  print $self->language->tr('WELCOME');

=item $segments = $self->segments 

This method returns the current segments in use by gbrowse.  The active
segments are set from within gbrowse

 $plugin->segments(\@segments);

The active segments can then be retrieved from within the plugin.  This is 
useful in cases where segment-specific information is required by plugin methods
that are not passed a segment object.


=item $config_path   = $self->config_path

This method returns the path to the directory in which gbrowse stores
its configuration files.  This is very useful for storing
plugin-specific configuration files.  See the sourcecode of
RestrictionAnnotator for an example of this.

=item $feature_file  = $self->new_feature_file

This method creates a new Bio::Graphics::FeatureFile for use by
annotators.  The annotate() method must invoke this method, configure
the resulting feature file, and then add one or more
Bio::Graphics::Feature objects to it.

This method is equivalent to calling
Bio::Graphics::FeatureFile->new(-smart_features=>1), where the
-smart_features argument allows features to be turned into imagemap
links.

=back

=head2 METHODS TO BE IMPLEMENTED IN DUMPERS

All plugins that act as feature dumpers should override one or more of
the methods described in this section.

=over 4

=item $self->dump($segment)

Given a Bio::Das::SegmentI object, produce some output from its
sequence and/or features.  This can be used to dump something as
simple as a FASTA file, or as complex as a motif analysis performed on
the sequence.

As described in L<Bio::Das::SegmentI>, the segment object represents
the region of the genome currently on display in the gbrowse "detail"
panel.  You may call its seq() method to return the sequence as a
string, or its features() method to return a list of all features that
have been annotated onto this segment of the genome.

At the time that dump() is called, gbrowse will already have set up
the HTTP header and performed other initialization.  The dump() method
merely needs to begin printing output using the appropriate MIME
type.  By default, the MIME type is text/plain, but this can be
changed with the mime_type() method described next.  

The following trivial example shows a dump() method that prints the
name and length of the segment:

  sub dump {
     my $self = shift;
     my $segment = shift;
     print "name   = ",$segment->seq_id,"\n";
     print "length = ",$segment->length,"\n";
  }

=item $type = $self->mime_type

Return the MIME type of the information produced by the plugin.  By
default, this method returns "text/plain".  Override it to return
another MIME type, such as "text/xml".

=back

=head2 METHODS TO BE IMPLEMENTED IN FINDERS

All finder plugins will need to override one or more of the methods
described in this section.

=over 4

=item $features = $self->find($segment);

The find() method will be passed a Bio::Das::SegmentI segment object,
as described earlier for the dump() method.  Your code should search
the segment for features of interest, and return a two element
list. The first element should be an arrayref of Bio::SeqFeatureI
objects (see L<Bio::SeqFeatureI>), or an empty list if nothing was
found. These synthetic feature objects should indicate the position,
name and type of the features found. The second element of the
returned list should be a (possibly shortened) version of the search
string for display in informational messages.

Depending on the type of find you are performing, you might search the
preexisting features on the segment for matches, or create your own
features from scratch in the way that the annotator plugins do.  You
may choose to ignore the passed segment and perform the search on the
entire database, which you can obtain using the database() method
call.

To create features from scratch I suggest you use either
Bio::Graphics::Feature, or Bio::SeqFeature::Generic to generate the
features.  See their respective manual pages for details, and the
OligoFinder.pm plugin for an example of how to do this.

If the plugin requires user input before it can perform its task,
find() should return undef.  Gbrowse will invoke configure_form()
followed by reconfigure() in order to prompt the user for input.  If
nothing is found, the plugin should return an empty list.  The
following is an example of how to prompt the user for input -- in this
case, a gene ontology term:

  sub find {
     my $self = shift;
     my $segment  = shift;  # we ignore this!
     my $config   = $self->configuration;
     my $query    = $config->{query} or return undef;  # PROMPT FOR INPUT
     my $search   = $self->db_search;
     my @features = $search->features(-attributes=>{GO_Term => $query});
     return (\@features,$query); 
  }

  sub configure_form {
     my $self = shift;
     return "Enter a GO Term: "
            . textfield(-name=>$self->config_name('query'));
  }

  sub reconfigure {
     my $self = shift;
     my $config = $self->configuration;
     $config->{query} = $self->config_param('query');
  }

See the sections below for more description of the configure_form()
and reconfigure() methods.  

NOTE: If you need to use auxiliary files like BLAST files, you can
store the location of those files in the gbrowse .conf file under the
stanza [YourPlugin:plugin]:
   
   [YourPlugin:plugin]
   blast_path = /usr/local/blast/databases

   sub find {
      my $self = shift;
      my $segment = shift;  # ignored
      my $blast_path = $self->browser_config->plugin_setting('blast_path');
      # etc etc etc  
   }

=item $features = $self->auto_find($search_string)

If the plugin has an "auto_find" method, then the method will be
invoked whenever the user types a string into GBrowse's search
box. The plugin may search any of the current data source's databases
(which you can get using $self->all_databases), or its own databases.

Return an arrayref containing the features found. Return an empty
arrayref to indicate that no features were found. Return undef to
indicate that the plugin declines to perform the search, in which case
GBrowse will default to its own search algorithm.

You may also choose to merge your search results with GBrowse's. To do
this, you can initiate the default search by calling:

 $default_features 
    = $self->db_search->search_features({-search_term => 'searchterm'})

Then do what you need to do to merge your customized search with the
default terms.

=back

=head2 METHODS TO BE IMPLEMENTED IN ANNOTATORS

All annotator plugins will need to override the method described in
this section.

=over 4

=item $feature_file = $plugin->annotate($segment[,$coordinate_mapper])

The annotate() method will be invoked with a Bio::Das::SegmentI
segment representing the region of the genome currently on view in the
gbrowse detail panel.  The method should first call its own
new_feature_list() to create a Bio::Graphics::FeatureFile feature set
object, and define one or more feature types to added to the feature
set.  The method should then create one or more Bio::Graphics::Feature
objects and add them to the feature set using add_feature.

The reason that annotate() returns a Bio::Graphics::FeatureFile rather
than an array of features the way that find() does is because
Bio::Graphics::FeatureFile also allows you to set up how the features
will be rendered; you can define tracks, assign different feature
types to different tracks, and assign each feature type a glyph,
color, and other options.

The annotate() function will also be passed a coordinate_mapper
variable.  This is a code ref to a function that will transform
coordinates from relative to absolute coordinates.  The function takes
a reference sequence name and a list of [$start,$end] coordinate
pairs, and returns a similar function result, except that the sequence
name and coordinates are all in absolute coordinate space.  Currently
there are no plugins that make use of this facility.

See L<Bio::Graphics::FeatureFile> for details, and the
RestrictionAnnotator.pm plugin for an example.

=back

=head2 METHODS TO BE IMPLEMENTED IN TRACKFILTERS

=over

=item @track_names = $plugin->filter_tracks($tracks,$source)

Given a list of track names and a Bio::Graphics::Browser2::DataSource
object, identify the track names to display and return them as a
list. The tracks are passed as a reference to a list of all possible
track names.

To make the form interactive, you may wish to pepper the plugin's
configuration form methods with calls to the javascript routine
doPluginUpdate(). This causes GBrowse to update the plugin's
configuration and refresh the tracks table as a side effect.

=item @terms = $plugin->hilite_terms

Returns a list of terms to hilight in the tracks table, or empty if none.

=back

=head2 METHODS TO BE IMPLEMENTED IN FEATURE HILITERS

=over 4

=item $color = $self->highlight($feature)

This method is passed a feature. It returns a color name (or any
Bio::Graphics color string) to highlight the feature with that color,
or undef if the feature should not be highlighted at all.

=back

=head2 METHODS TO BE IMPLEMENTED IN FEATURE FILTERS

=over 4

=item ($filter,$newkey) = $self->filter($track_label,$key)

This method is passed a track label and the original key of the
label. It is expected to return a two-element list consisting of a
coderef and a new key for the track. The coderef should be a
subroutine that takes a feature as its single argument and returns
true to include the feature in the track and false to exclude the
feature from the track.

  sub {
      my $feature = shift;
      return do_something() ? 1 : 0;
  }

The filter() method should return an updated string for the track key
to indicate that the track is being filtered. This is to inform the
user that the track is not showing all possible features.

The filter() method should return an empty list if it does not wish to
install or filter, or to remove a filter that was previously
installed.


=back

=head2 PERSISTENT CONFIGURATION METHODS

The following methods can be called to retrieve data about the
environment in which the plugin is running.  These methods are also
used by gbrowse to change the plugin state.

=over 4

=item $config = $self->config_defaults()

This method will be called once at plugin startup time to give the
plugin a chance to set up its default configuration state.  If you
implement this method you should return the configuration as a hash
reference in which the values of the hash are either scalar values or
array references.  The contents of this hash will be placed in a
CGI::Session.

You will wish to implement this method if the plugin has
user-modifiable settings.

NOTE ON FILEHANDLES: You are not allowed to permanently store a
filehandle in the persistent configuration data structure because the
session-handling code will try to serialize and store the filehandle,
which is not allowed by the default serializer. If you must store a
filehandle in the configuration data structure, be sure to delete it
within the annotate(), find() or dump() methods once you are finished
using it.

=item $self->configure_form()

This method will be called when the user presses the "Configure
plugin" button.  You should return the HTML for a fill-out form that
allows the user to change the current settings.  The HTML should
contain the contents of an HTML <form> section, but B<not> the actual
<form> and </form> tags.  These tags, along with the Submit and Cancel
buttons, will be added automatically.  Typically you will build up the
HTML to return using a series of .= append operations.

It is highly recommended that you use the CGI module to generate the
fill-out form.  In order to avoid clashing with other parts of
gbrowse, plugin fill-out forms must respect a namespacing convention
in which the name of each form field is preceded by the plugin package
name and a dot.  The package name is the last component of the
plugin's package; for example "GoSearch" is the package name for
Bio::Graphics::Browser2::Plugin::GoSearch. To represent the "query"
field of the plugin named "GOSearch", the text field must be named
"GOSearch.query".

To make this easier to do right, the Plugin module provides a method
named config_name() which will add the prefix for you.  Here is how
to use it with the "query" example:

   $html .= textfield(-name  => $self->config_name('query'));

=item $self->reconfigure()

If you implement a configure_form() method, you must also implement a
reconfigure() method.  This method is called after the user submits
the form and should be used to integrate the form values with the
current configuration.

Remember that the form fields are namespaced.  You may recover them
using the CGI param() method by preceding them with the proper prefix.
To make this easier to manage, this module provides a config_param()
method that manages the namespaces transparently.

Here is a working example:

  sub reconfigure {
      my $self = shift;
      my $current_configuration = $self->configuration;
      $current_configuration->{query} = $self->config_param('query');
  }

All this does is to retrieve the current configuration by calling the
configuration() method.  The value of the "query" key is then replaced
by a fill-out form parameter named "query", using config_param()
instead of the more familiar CGI module's param() function.

=back

=cut


use strict;
use Bio::Graphics::Browser2;
use Data::Dumper;
use Digest::MD5 'md5_hex';
use CGI qw(url header p);

$Data::Dumper::Sortkeys = 1;

use vars '$VERSION','@ISA','@EXPORT';
$VERSION = '0.30';

# currently doesn't inherit
@ISA = ();

# currently doesn't export
@EXPORT = ();

sub new {
  my $class = shift;
  return bless {},$class;
}

# initialize other globals
sub init {
  my $self = shift;
  # do nothing
}

sub name {
  my $self = shift;
  return "generic";
}

sub id {
    my $self = shift;
    my $class = ref($self) || $self;
    my ($id)  = $class =~ /(\w+)$/;
    return $id;
}

# return nothing unless the plugin overides this method 
sub verb {
  my $self = shift;
  return '';
}

# return nothing unless the plugin overrides this method
sub suppress_title {
  my $self = shift;
  return '';
}

sub description {
  my $self = shift;
  my $language = $self->language;
  return p($self->language->tr('PLUGIN_BASE_CLASS_DESC'));
}

sub type {
  my $self = shift;
  return 'dumper';
}

sub mime_type {
  return 'text/plain';
}

sub config_defaults {
  my $self = shift;
  return;  # no configuration
}

sub configuration {
  my $self = shift;
  my $d = $self->{g_config};
  if (@_) {
    $self->{g_config} = shift;
  }
  $d;
}

sub renderer {
    my $self = shift;
    my $d    = $self->{render};
    $self->{render} = shift if @_;
    $d;
}

sub configure_form {
  return;
}

sub reconfigure {
  my $self = shift;
  # do nothing
}

sub authenticate {
    my $self = shift;
    # do nothing
}

sub authentication_hint {
    my $self = shift;
    # return nothing
}

# get/store database
sub database {
  my $self = shift;
  my $d = $self->{g_database};
  $self->{g_database} = shift if @_;
  $d;
}

sub all_databases {
    my $self = shift;
    my $source       = $self->browser_config;
    my @db_labels    = $source->databases;
    return map {$source->open_database($_)} @db_labels;
}

# get/store language
sub language {
  my $self = shift;
  my $d = $self->{g_language};
  $self->{g_language} = shift if @_;
  $d;
}

# get/store configuration file
# it's a Bio::Graphics::Browser file
sub browser_config {
  my $self = shift;
  my $d = $self->{g_config_file};
  $self->{g_config_file} = shift if @_;
  $d;
}

# get/store page settings
# it's a big hash as described in the notes of the gbrowse executable
sub page_settings {
  my $self = shift;
  my $d = $self->{g_page_settings};
  $self->{g_page_settings} = shift if @_;
  $d;
}

# get/store configuration directory path
sub config_path {
  my $self = shift;
  my $d = $self->{g_config_path};
  $self->{g_config_path} = shift if @_;
  $d;
}

# get/store the current segments
sub segments {
  my $self = shift;
  my $d = $self->{segments};
  $self->{segments} = shift if @_;
  $d;
}

# get/store a RegionSearch object (see Bio::Graphics::Browser2::RegionSearch)
sub db_search {
  my $self = shift;
  my $d = $self->{db_search};
  $self->{db_search} = shift if @_;
  $d;
}

# just dump out the name of the thing
sub dump {
  my $self    = shift;
  my $segment = shift;
  my $language = $self->language;
  print header('text/plain');
  print $self->language->tr('PLUGIN_BASE_CLASS_DUMP');
}

sub find {
  my $self = shift;
  my $segment = shift;
  return ();
}

sub annotate {
  my $self = shift;
  my $segment = shift;
  my $coordinate_mapper = shift;
  # do nothing
  return;
}

sub filter_tracks {
    my $self = shift;
    my ($tracks,$source) = @_;
    return @$tracks;  # pass 'em all through
}

sub hilite_terms {
    my $self = shift;
    return;
}

sub pkg {
  my $self  = shift;
  my $class = ref $self or return;
  $class =~ /(\w+)$/    or return;
  return $1;
}

sub config_param {
  my $self = shift;
  my $pkg  = $self->pkg;
  unless (@_) {
    my @result;
    foreach (CGI::param()) {
      next unless /^$pkg\.(.+)/;
      push @result,$1;
    }
    return @result;
  }
  CGI::param($self->config_name(shift()));
}

sub config_name {
  my $self = shift;
  my $name = shift;
  my $pkg = $self->pkg;
  return "$pkg.$name";
}

sub selected_tracks {
  my $self = shift;
  my $page_settings = $self->page_settings;
  return grep {!/^_/ && $page_settings->{features}{$_}{visible}} @{$page_settings->{tracks}};
}

sub selected_features {
  my $self = shift;
  my $conf   = $self->browser_config;
  my @tracks = $self->selected_tracks;
  return map {$conf->config->label2type($_)} @tracks;
}

# called by annotators when they need to create a new list of features
sub new_feature_list {
  my $self     = shift;
  return Bio::Graphics::FeatureFile->new(-smart_features=>1,
		-safe => 1);
}

# install the plugin but do not show it in the "Reports & Analysis" menu
# off by default
sub hide {}

# front end to $self->browser_config->plugin_setting
sub setting {
    my $self = shift;
    my $setting_name = shift;

    eval "use Class::ISA";
    
    my $config  = $self->browser_config;
    my $globals = $config->globals;

    my @classes = Class::ISA->can('self_and_super_path') 
	        ? Class::ISA::self_and_super_path(ref $self)
		: ref $self;
    for (@classes) {
	my ($last_name)   = /(\w+)$/;
	my $option_name   = "${last_name}:plugin";
	if ($setting_name) {
	    my $result = $config->setting($option_name => $setting_name) 
		     || $globals->setting($option_name=>$setting_name);
	    return $result if defined $result;
	} else {
	    my @options = $config->label_options($option_name);
	    @options    = $globals->label_options($option_name) unless @options;
	    return @options if @options;
	}
    }
    return;
}

sub config_hash {
  return md5_hex( Dumper( shift->configuration ) );
}

# A list of all extra scripts required by a plugin, defaults to nothing.
sub scripts {
  my $self = shift;
  return;
}

# A list of all additional stylesheets required by a plugin, defaults to nothing.
sub stylesheets {
  my $self = shift;
  return;
}


# A list of all Javascript commands to be run once the page or tab is loaded (in either the body's onLoad section, or the callback event from the tab's rendering animation).
# Should return a hash, key is the section which contains the elements plus "_select", value is the function (e.g. "track_page_select", "some.function();").
# Will accet "main_page_select", "track_page_select", "custom_track_page_select", "settings_page_select" and "body" as keys.
sub onLoads {
  my $self = shift;
  return;
}

sub DESTROY {
    my $self = shift;
#    warn "$self is destroyed";
}


1;

__END__


=head1 SEE ALSO

L<Bio::Graphics::Browser>

=head1 AUTHOR

Lincoln Stein E<lt>lstein@cshl.orgE<gt>.

Copyright (c) 2003 Cold Spring Harbor Laboratory

This package and its accompanying libraries is free software; you can
redistribute it and/or modify it under the terms of the GPL (either
version 1, or at your option, any later version) or the Artistic
License 2.0.  Refer to LICENSE for the full license text. In addition,
please see DISCLAIMER.txt for disclaimers of warranty.

=cut