X-Git-Url: http://git.freeside.biz/gitweb/?a=blobdiff_plain;ds=sidebyside;f=httemplate%2Felements%2Fselect-tiered.html;fp=httemplate%2Felements%2Fselect-tiered.html;h=35f9e5a60f4e328f1dd9bff4af790fef5cd304ee;hb=825422583d2d510c0f552c513bef4cc1ac921950;hp=0000000000000000000000000000000000000000;hpb=a7edb84851d2238c7ec449483f5bd34e40813ccd;p=freeside.git diff --git a/httemplate/elements/select-tiered.html b/httemplate/elements/select-tiered.html new file mode 100644 index 000000000..35f9e5a60 --- /dev/null +++ b/httemplate/elements/select-tiered.html @@ -0,0 +1,191 @@ +<%doc> +Usage: + +<& /elements/select-tiered.html, + tiers => [ + { table => 'table1', ... }, # most select-table options are supported + { table => 'table2', ..., link_col = 't2num' }, # foreign key in table1 + ], + prefix => '', # to avoid name conflicts + curr_value => 42, # in the last table + field => 'fieldname', # NAME attribute of the last element +&> + +This creates a group of SELECT elements (similar to select-table.html) for +drill-down navigation of data with one-to-many relationships. + +'tiers' is required, and must be an arrayref of hashes, each describing one +tier of selection (from most general to most specific). Each tier can +contain the following: +- table, select, addl_from, hashref, extra_sql: as in FS::Record::qsearch. +- records, an arrayref of exact records. Either this or "table" must be + provided. +- field: the NAME attribute of the select element. Optional. +- name_col: the column/method name to obtain the record's text label in the + select element. +- value_col: the column/method name to obtain the record's value, which is + sent on form submission. Defaults to the primary key. +- link_col: the column/method name to associate the record to the value_col + of a record in the previous table's value_col. (That is, the foreign key.) +- empty_label: the label to use for an option with the logical meaning of + "all of these" and a value of ''. +- curr_value: the currently selected value. This will constrain the current + values of preceding tiers. +- multiple: set to true for a multiple-style selector. This should work but + isn't fully tested. +- after: an HTML string to be inserted after the select element, before + the next one. By default there's nothing between them. + +For convenience, "curr_value" and "field" can be passed as part of the +main argument list, and will be applied to the last tier. + + +% $i = 0; +% foreach my $tier (@$tiers) { +% my $onchange; +% $onchange="onchange='${pre}select_change(this, $i)'" +% if $i < scalar(@$tiers) - 1; + +<% $tier->{after} %> +% } #foreach $tier + +<%init> +my %opt = @_; +my $pre = $opt{prefix} || ''; +my $tiers = $opt{tiers} or die "no tiers defined"; + +my $i; +for( $i = 0; $i < @$tiers; $i++ ) { + my $tier = $tiers->[$i]; + my $key = $tier->{value_col}; + my $name_col = $tier->{name_col}; + if ( !exists($tier->{records}) ) { + # minor false laziness w/ select-table + my $dbdef_table = dbdef->table($tier->{table}) + or die "can't find dbdef for ".$tier->{table}." table\n"; + $key ||= $dbdef_table->primary_key; + my $hashref = $tier->{hashref} || {}; + my $select = $tier->{select} || '*'; + # we don't yet support agent_virt + $tier->{records} = [ qsearch({ + 'select' => $select, # the real magic + 'table' => $tier->{table}, + 'addl_from' => $tier->{addl_from}, + 'hashref' => $hashref, + 'extra_sql' => $tier->{extra_sql}, + }) ]; + } + + # set up options + my %children_of; + if ( $i == 0 ) { + $children_of{''} = { + map { $_->$key => $_->$name_col } @{ $tier->{records} } + }; + } + else { + my $link_col = $tier->{link_col} + or die "no link_col in '".$tier->{table}."' tier\n"; + # %children_of maps the option values in the previous tier + # to hashes of their linked options in this tier. + foreach my $rec (@{ $tier->{records} }) { + $children_of{ $rec->$link_col } ||= {}; + $children_of{ $rec->$link_col }->{ $rec->$key } = $rec->$name_col; + } + } + + if ( defined $tier->{empty_label} ) { + foreach my $key (keys %children_of) { + # only create "all" options if there are multiple choices + if ( scalar(keys %{ $children_of{$key} }) > 1 ) { + $children_of{$key}->{''} = $tier->{empty_label}; + } + } + } + $tier->{by_key} = \%children_of; +} + +$i = scalar(@$tiers) - 1; +$tiers->[$i]->{curr_value} ||= $opt{curr_value}; +$tiers->[$i]->{field} ||= $opt{field}; + +# We expect the usual case to be $opt{curr_value}, i.e. +# current value in the last tier. So trace it backward. +while($i >= 1) { + my $curr_value = $tiers->[$i]->{curr_value}; + last if !defined($curr_value); + + my $tier = $tiers->[$i]; + foreach my $key ( %{ $tier->{by_key} } ) { + my $options = $tier->{by_key}->{$key}; + if ( exists( $options->{$curr_value} ) ) { + warn "tier $i curr_value ($curr_value) found under key $key\n"; + $tiers->[$i-1]->{curr_value} = $key; + last; + } + } + $i--; +} + +my $tiers_by_key = [ map { $_->{by_key} } @$tiers ]; +my $curr_values = [ map { $_->{curr_value} || '' } @$tiers ]; +