# Copyright 2002-2007 Interchange Development Group and others # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. See the LICENSE file for details. UserTag tree Order table master subordinate start UserTag tree addAttr UserTag tree attrAlias sub subordinate UserTag tree hasEndTag UserTag tree Version 1.12 UserTag tree Routine <{file}) { my $delim = $opt->{delimiter} || "\t"; my $s = $opt->{subordinate} || 'code'; my $l = $opt->{level_field} || 'msort'; $delim = qr/$delim/; my @lines = split /\n/, readfile($opt->{file}); my $hdr = shift @lines; my @fields = split $delim, $hdr; my $i = 1; for(@lines) { my $ref = {}; @{$ref}{@fields} = split $delim, $_; $ref->{$s} = $i++; push @passed, $ref; push @start, $ref if $ref->{$l} == 0; } $nodb = 1; } my $db; unless($nodb) { $db = ::database_exists_ref($table) or return error_opt($opt, "Database %s doesn't exist", $table); $db->column_exists($parent) or return error_opt($opt, "Parent column %s doesn't exist", $parent); $db->column_exists($sub) or return error_opt($opt, "Subordinate column %s doesn't exist", $sub); } my $basewhere; WHEREBASE: { my @keys; my @things; if($opt->{multiple_start}) { @keys = split /[\0,\s]+/, $start_item; } else { @keys = $start_item; } unless($nodb) { for(@keys) { push @things, "$parent = " . $db->quote($_, $parent); } } $basewhere = join " OR ", @things; } my @outline = (1); if(defined $opt->{outline}) { $opt->{outline} =~ s/[^a-zA-Z0-9]+//g; @outline = split //, $opt->{outline}; @outline = (qw/1 A 1 a 1 a/) if scalar @outline < 2; } my $mult = ( int($opt->{spacing}) || 10 ); my $keyfield; $keyfield = $db->config('KEY') unless $nodb; $opt->{code_field} = $keyfield if ! $opt->{code_field}; my $sort = ''; if($opt->{sort}) { $sort .= ' '; $sort .= 'ORDER BY ' unless $opt->{sort} =~ /^\s*order\s+by\s+/i; my @sort; @sort = ref $opt->{sort} ? @{$opt->{sort}} : ( $opt->{sort} ); for(@sort) { s/\s*[=:]\s*([rnxf]).*//; $_ .= " DESC" if $1 eq 'r'; } $sort .= join ", ", @sort; undef $opt->{sort}; } my $where = ''; unless($nodb) { if( my $f = $db->config('HIDE_FIELD')) { $where .= " AND $f <> 1"; } } if($opt->{where}) { $where .= " AND ($opt->{where})"; } my $qb = "SELECT * FROM $table WHERE $basewhere$where$sort"; #::logDebug("tree tag initial query=$qb"); my $ary; if($nodb) { $ary = \@start; } else { $ary = $db->query( { hashref => 1, sql => $qb, }); } my $memo; if( $opt->{memo} ) { $memo = ($::Scratch->{$opt->{memo}} ||= {}); my $toggle; if($opt->{toggle} and $toggle = $CGI::values{$opt->{toggle}}) { $memo->{$toggle} = ! $memo->{$toggle}; } } if($opt->{collapse} and $CGI::values{$opt->{collapse}}) { $memo = {}; delete $::Scratch->{$opt->{memo}} if $opt->{memo}; } my $explode; if($opt->{full} or $opt->{explode} and $CGI::values{$opt->{explode}}) { $explode = 1; } my $enable; my $qsub; my $donemsg; my $dbh; $dbh = $db->dbh() unless $nodb; my $qs_query = "SELECT * FROM $table WHERE $parent = ?$where$sort"; if($nodb) { my $l = $opt->{level_field} || 'msort'; #::logDebug("setting up nodb qsub level=$l"); $qsub = sub { my $key = shift; #::logDebug("Looking for key=$key"); return if $key < 1; my $base = $passed[$key - 1]->{$l} + 1; #::logDebug("Base level=$base, firstone = $passed[$key]{$l}"); my @out; for(my $i = $key; $passed[$i]{$l} >= $base ; $i++ ) { push @out, $passed[$i] if $passed[$i]{$l} == $base; } return unless @out; return \@out; }; } elsif($dbh and $db->config('Class') eq 'DBI') { my $sth = $dbh->prepare($qs_query) or die errmsg( "tree failed to prepare query: %s\nError was: %s", $qs_query, $DBI::errstr, ); $qsub = sub { #::logDebug("executing query sub DBI style"); # while ! $donemsg++; my $parm = shift; my @ary; $sth->execute($parm) or die errmsg( "tree failed to prepare query for '%s': %s\nError was: %s", $parm, $qs_query, $DBI::errstr, ); while(my $ref = $sth->fetchrow_hashref()) { push @ary, { %$ref }; } return unless @ary; return \@ary; }; } else { $qsub = sub { my $parm = shift; #::logDebug("executing query sub regular style"); # while ! $donemsg++; $parm = $db->quote($parm, $parent); my $q = $qs_query; $q =~ s/\s\?\s/ $parm /; $db->query( { hashref => 1, sql => $q }); }; } $memo = {} if ! $memo; my $count = 0; my $stop_sub; #::logDebug("tree-list: valid parent=$parent sub=$sub start=$start_item mult=$mult"); my @ary_stack = ( $ary ); # Stacks the rows my @above_stack = { $start_item => 1 }; # Holds the previous levels my @inc_stack = ($outline[0]); # Holds the increment characters my @rows; my $row; ARY: for (;;) { #::logDebug("next ary"); my $ary = pop(@ary_stack) or last ARY; my $above = pop(@above_stack); my $level = scalar(@ary_stack); my $increment = pop(@inc_stack); ROW: for(;;) { #::logDebug("next row level=$level increment=$increment"); my $prev = $row; $row = shift @$ary or ($prev and $prev->{mv_last} = 1), last ROW; $row->{mv_level} = $level; $row->{mv_spacing} = $level * $mult; $row->{mv_spacer} = $opt->{spacer} x $row->{mv_spacing} if $opt->{spacer}; $row->{mv_increment} = $increment++; $row->{mv_ip} = $count++; push(@rows, $row); my $code = $row->{$keyfield}; $row->{mv_toggled} = 1 if $memo->{$code}; #::logDebug("next row sub=$sub=$row->{$sub}"); my $next = $row->{$sub} or next ROW; my $stop; $row->{mv_children} = 1 if ($opt->{stop} and ! $row->{ $opt->{stop} } ) or ($opt->{continue} and $row->{ $opt->{continue} }) or ($opt->{autodetect}); $stop = 1 if ! $explode and ! $memo->{$code}; #::logDebug("next row sub=$sub=$next stop=$stop explode=$explode memo=$memo->{$code}"); if($above->{$next} and ($opt->{autodetect} or ! $stop) ) { my $fmt = <{$parent}, $next); if(! $opt->{pedantic}) { error_opt($opt, $msg); next ROW; } else { $opt->{log_error} = 1 unless $opt->{show_error}; return error_opt($opt, $msg); } } my $a; if ($opt->{autodetect} or ! $stop) { #::logDebug("next=$next row query=$q"); $a = $qsub->($next); $above->{$next} = 1 if $a and scalar @{$a}; } if($opt->{autodetect}) { $row->{mv_children} = $a ? scalar(@$a) : 0; } if (! $stop) { push(@ary_stack, $ary); push(@above_stack, $above); push(@inc_stack, $increment); $level++; $increment = defined $outline[$level] ? $outline[$level] : 1; $ary = $a; } } # END ROW #::logDebug("last row"); } # END ARY $opt->{object} = { mv_results => \@rows }; #::logDebug("last ary, results =" . ::uneval(\@rows)); return labeled_list($opt, $text, $opt->{object}); } EOR