Correct typo
[interchange.git] / code / UI_Tag / flex_select.coretag
1 # Copyright 2002-2007 Interchange Development Group and others
2
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.  See the LICENSE file for details.
7
8 # $Id: flex_select.coretag,v 1.18 2009-05-01 16:02:50 mheins Exp $
9
10 UserTag flex-select Order      table
11 UserTag flex-select addAttr
12 UserTag flex-select attrAlias  ml height
13 UserTag flex-select hasEndTag
14 UserTag flex-select Version    $Revision: 1.18 $
15 UserTag flex-select Routine    <<EOR
16 use vars qw/$CGI $Tmp $Tag/;
17 my @fs_more = qw/
18         help_name
19         icon_name
20         page_banner
21         page_title
22         ui_break_before
23         ui_description_fields
24         ui_flex_description
25         ui_flex_key
26         ui_show_fields
27         ui_sort_field
28         ui_sort_option
29 /;
30 sub flex_select_init {
31         my ($table, $opt) = @_;
32
33         my @warnings;
34         my @errors;
35
36 #::logDebug("Entering flex_select init");
37         if($CGI->{mv_more_ip}) {
38                 for(@fs_more) {
39                         $CGI->{$_} = $::Values->{$_};
40                 }
41         }
42
43         if($CGI->{mv_return_table}) {
44                 my $rt = delete $CGI->{mv_return_table};
45                 $rt =~ s/^\0+//;
46                 $rt =~ s/\0.*//;
47                 $CGI->{mv_data_table} = $rt if $rt;
48         }
49
50         my $bounce_url;
51         $::Scratch->{ui_class} = $CGI->{ui_class}
52                 if $CGI->{ui_class} &&  $CGI->{ui_class} =~ /^\w+$/;
53
54         if($CGI->{ui_text_like}) {
55                 ($CGI->{mv_like_field}, $CGI->{mv_like_spec}) = split /=+/, $CGI->{ui_text_like}, 2;
56         }
57
58         if($opt->{sql_query}) {
59                 my $spec;
60                 eval {
61                         ($table) = Vend::Scan::sql_statement($opt->{sql_query}, { table_only => 1});
62                 };
63                 if($@) {
64                         $Tag->error( {
65                                                 set => errmsg(
66                                                                         "flex-select -- bad query %s: %s",
67                                                                         $opt->{sql_query},
68                                                                         $@,
69                                                                 ),
70                                                 name => 'flex_select',
71                                                 });
72                         return undef;
73                 }
74         }
75
76         if($table =~ s/\.(txt|asc)$/_$1/) {
77                 $table =~ s:.*/::;
78         }
79         my $db = database_exists_ref($table);
80
81         $Tmp->{flex_select} ||= {};
82         my $ts = $Tmp->{flex_select}{$table} = {};
83
84         if(! $db) {
85                 $Tag->error({
86                                                 name => 'flex_select',
87                                                 set =>  errmsg('no %s database', $table),
88                                         });
89                 my $url = $Tag->area( {
90                                                         href => $::Variable->{UI_ERROR_PAGE} || 'admin/error',
91                                                         secure => $::Variable->{UI_SECURE},
92                                                 });
93 #::logDebug("delivering error url=$url");
94                 $Tag->deliver( { location => $url });
95                 return;
96         }
97
98         if( $::Variable->{UI_LARGE_TABLE} =~ /\b$table\b/ or $db->config('LARGE') ) {
99                 $ts->{large} = 1;
100         }
101
102         if( $db->config('COMPOSITE_KEY') ) {
103                 $ts->{multikey} = 1;
104                 $ts->{key_columns} = $db->config('_Key_columns');
105         }
106
107         DELETE: {
108                 last DELETE unless $CGI->{item_id};
109                 last DELETE unless delete $CGI->{deleterecords};
110                 unless ($Tag->if_mm('tables', '=d')) {
111                         $Tag->error({
112                                                         name => 'flex_select',
113                                                         set => errmsg("no permission to delete records"),
114                                                 });
115                         last DELETE;
116                 };
117
118                 $Vend::Cfg->{NoSearch} = '';
119
120                 my @ids = split /\0/, $CGI->{item_id};
121                 for(grep $_, @ids) {
122                         if($db->delete_record($_)) {
123                                 push @warnings, errmsg("Deleted record %s", $_);
124                         }
125                         else {
126                                 push @errors, $db->errstr();
127                         }
128                 }
129         }
130
131         SEQUENCE: {
132                 my $dest = $CGI->{ui_sequence_destination} || '__UI_BASE__/flex_editor';
133 #::logDebug("Entering flex_select sequence edit stuff");
134                 last SEQUENCE unless $CGI->{ui_sequence_edit};
135 #::logDebug("doing flex_select sequence edit stuff");
136                 my $doit;
137                 if($CGI->{item_id_left} =~ s/^(.*?)[\0]//) {
138                         $CGI->{ui_sequence_edit} = 1;
139                         $CGI->{item_id} = $1;
140                         $doit = 1;
141                 }
142                 elsif ($CGI->{item_id_left}) {
143                         $CGI->{item_id} = delete $CGI->{item_id_left};
144                         delete $CGI->{ui_sequence_edit};
145                         $doit = 1;
146                 }
147                 else {
148                         delete $CGI->{item_id};
149                         delete $CGI->{ui_sequence_edit};
150                 }
151                 last SEQUENCE unless $doit;
152                 my $url = $Tag->area( {
153                                                                         href => $dest,
154                                                                         form => qq{
155                                                                                 mv_data_table=$CGI->{mv_data_table}
156                                                                                 item_id=$CGI->{item_id}
157                                                                                 item_id_left=$CGI->{item_id_left}
158                                                                                 ui_sequence_edit=$CGI->{ui_sequence_edit}
159                                                                         },
160                                                                 });
161 #::logDebug("flex_select sequence developed URL=$url");
162                 $Tag->deliver( { location => $url } );
163                 return;
164         }
165
166         $ts->{table_meta} = $Tag->meta_record($table, $CGI->{ui_meta_view}) || {};
167         my $tm = $ts->{table_meta};
168         
169         my $extra;
170         if($tm->{name}) {
171                 $extra .= "<b>$tm->{name}</br>";
172         }
173         if($ts->{help_url}) {
174                 $extra .= qq{&nbsp;&nbsp;&nbsp;<small><a href="$ts->{help_url}">};
175                 $extra .= errmsg('help');
176                 $extra .= "</a></small>";
177         }
178         if($ts->{help}) {
179                 $extra .= "<blockquote>$ts->{help}</blockquote>";
180         }
181         $::Scratch->{page_banner} ||= $::Scratch->{page_title};
182         $::Scratch->{page_banner} .= $extra;
183
184         for(@errors) {
185                 $Tag->error({ name => 'flex_select', set => $_ });
186         }
187         for(@warnings) {
188                 $Tag->warnings($_);
189         }
190         return;
191 }
192
193 sub {
194         my ($table, $opt, $body) = @_;
195
196 #::logDebug("Entering flex_select");
197         my $CGI = \%CGI::values;
198
199         $table ||= $CGI->{mv_data_table};
200
201         ## Do the initialization
202         if($opt->{init}) {
203                 return flex_select_init($table, $opt);
204         }
205
206         my $filter;
207         if(ref($opt->{filter}) eq 'HASH') {
208                 $filter = $opt->{filter};
209         }
210         $filter ||= {};
211
212         my $spec;
213         my $stmt;
214         my $q;
215         if($opt->{sql_query}) {
216                 $q = $opt->{sql_query};
217                 if($CGI->{ui_sort_field} =~ s/^(\w+)(:[rfn]+)?$/$1/) {
218                         my $field = $1;
219                         my $opt = $2 || $CGI->{ui_sort_option};
220                         $field .= ' DESC', $CGI->{ui_sort_option} = 'r' if $opt =~ /r/i;
221                         $q =~ s/
222                                                 \s+ORDER\s+BY
223                                                 \s+(\w+(\s+desc\w*)?)
224                                                 (\s*,\s*\w+(\s+desc\w*)?)*
225                                                 (\s*$|\s+LIMIT\s+\d+(?:\s*,\s*\d+)?)
226                                    / ORDER BY $field$5/ix
227                         or
228                                 $q =~ s/(\s+LIMIT\s+\d+(?:\s*,\s*\d+)?)/ ORDER BY $field$1/ix
229                                 or $q .= " ORDER BY $field";
230                 }
231
232                 eval {
233                         ($spec) = Vend::Scan::sql_statement($q);
234                 };
235                 if($@ || ! $spec->{rt}) {
236                         $Tag->error( {
237                                                 set => errmsg("flex-select -- bad query %s: %s", $q, $@),
238                                                 name => 'flex_select',
239                                                 });
240                         return undef;
241                 }
242                 $table = $spec->{rt}->[0];
243         }
244
245         my $ref = dbref($table)
246                 or do {
247                         my $msg = errmsg("%s: table '%s' does not exist", 'flex_select', $table);
248                         logError($msg);
249                         $Tag->error({ name => 'flex_select', set => $msg });
250                         return undef;
251                 };
252         my $ts = $Tmp->{flex_select}{$table} ||= {};
253         my $meta = $ts->{table_meta} ||= $Tag->meta_record($table, $CGI->{ui_meta_view});
254
255 #::logDebug("flex_select table=$table");
256         if($meta->{sql_query}) {
257                 $q = $meta->{sql_query};
258                 if($CGI->{ui_sort_field} =~ s/^(\w+)(:[rfn]+)?$/$1/) {
259                         my $field = $1;
260                         my $opt = $2 || $CGI->{ui_sort_option};
261                         $field .= ' DESC', $CGI->{ui_sort_option} = 'r' if $opt =~ /r/i;
262                         $q =~ s/
263                                                 \s+ORDER\s+BY
264                                                 \s+(\w+(\s+desc\w*)?)
265                                                 (\s*,\s*\w+(\s+desc\w*)?)*
266                                                 (\s*$|\s+LIMIT\s+\d+(?:\s*,\s*\d+)?)
267                                    / ORDER BY $field$5/ix
268                         or
269                                 $q =~ s/(\s+LIMIT\s+\d+(?:\s*,\s*\d+)?)/ ORDER BY $field$1/ix
270                                 or $q .= " ORDER BY $field";
271                 }
272
273                 eval {
274                         ($spec) = Vend::Scan::sql_statement($q);
275                 };
276                 if($@ or ! $spec->{rt}) {
277                         $Tag->error( {
278                                                 set => errmsg("flex-select -- bad query %s: %s", $q, $@),
279                                                 name => 'flex_select',
280                                                 });
281                         return undef;
282                 }
283                 $table = $spec->{rt}->[0];
284         }
285
286         if( $table ne $ref->config('name')) {
287                 ## Probably transient database
288                 $CGI->{mv_data_table_real} = $table = $ref->config('name');
289         }
290
291         my @labels;          ## Locally set labels in ui_show_fields
292         my @views;           ## Locally set view data in ui_show_fields
293         my @filter_show;     ## Locally set filters in ui_show_fields
294         my @calcs;           ## Data calculation code (if any) from fs_data_calc
295         my @redirect;        ## A column with a different metadata from standard
296         my @extras;          ## A column with a different metadata from standard
297         my @style;           ## Style for data cell, only have to read once
298         my @link_page;       ## Locally set filters in ui_show_fields
299         my @link_parm;       ## Locally set filters in ui_show_fields
300         my @link_parm_extra; ## Locally set filters in ui_show_fields
301         my @link_anchor;     ## Locally set filters in ui_show_fields
302         my $filters_done;    ## Tells us we are done with filters
303
304         if(my $show = $CGI->{ui_show_fields} ||= $meta->{ui_show_fields} || $meta->{field}) {
305                 my $i = 0;
306                 if($show =~ s/[\r\n]+/\n/g) {
307                         $show =~ s/^\s+//;
308                         $show =~ s/\s+$//;
309                         my @f = split /\n/, $show;
310                         my @c;
311                         for(@f) {
312                                 s/^\s+//;
313                                 s/\s+$//;
314                                 if(s/\s*\((.+)\)\s*$//)  {
315                                         $filter_show[$i] = $1;
316                                 }
317                                 
318                                 if(/^(\w+)-(\w+)$/) {
319                                         push @c, $1;
320                                         $redirect[$i] = $2;
321                                 }
322                                 elsif(/^(\w+)(?:-([^=]+))?(?:=(.*))?/) {
323                                         push @c, $1;
324                                         $views[$i] = $2 if $2;
325                                         $labels[$i] = $3;
326                                 }
327                                 else {
328                                         push @c, $_;
329                                 }
330                                 $i++;
331                         }
332                         $show = join ",", @c;
333                 }
334                 else {
335                         $show =~ s/(\w+)(?:\((.*?)\))?/ ($filter_show[$i++] = $2), $1/eg;
336                         $show =~ s/[\0,\s]+/,/g;
337                 }
338                 $CGI->{ui_description_fields} = $show;
339                 $filters_done = 1;
340         }
341
342         if($spec) {
343 #::logDebug("flex_select spec=$spec");
344                 if($spec->{rf} and $spec->{rf}[0] ne '*') {
345                         my @c;
346                         my $header;
347                         for(my $i = 0; $i < @{$spec->{rf}}; $i++) {
348                                 if($spec->{hf}[$i]) {
349                                         $header++;
350                                         push @c, $spec->{rf}[$i] . '=' . $spec->{hf}[$i];
351                                 }
352                                 else {
353                                         push @c, $spec->{rf}[$i];
354                                 }
355                         }
356                         if($header) {
357                                 $CGI->{ui_show_fields} = join "\n", @c;
358                         }
359                         else {
360                                 $CGI->{ui_show_fields} = join " ", @c;
361                         }
362                 }
363                 if($spec->{tf} and $spec->{tf}[0]) {
364                         $CGI->{ui_sort_field} = join ",", @{$spec->{tf}};
365                         $CGI->{ui_sort_option} = join ",", @{$spec->{to}};
366                 }
367                 $CGI->{ui_list_size} = $spec->{ml} if $spec->{ml};
368         }
369
370         $meta ||= {};
371
372         if($CGI->{ui_flex_key}) {
373                 $ts->{keypos} = $CGI->{ui_flex_key};
374         }
375         else {
376                 $ts->{keypos} = $ref->config('KEY_INDEX');
377         }
378
379         $ts->{keyname} = $ref->config('KEY');
380         $ts->{owner_field} = $ref->config('OWNER_FIELD') || $::Scratch->{ui_owner};
381
382         if($CGI->{ui_exact_record}) {
383 #::logDebug("found exact record input");
384                 undef $CGI->{mv_like_field};
385                 my $id = $CGI->{mv_like_spec};
386                 $id =~ s/\0.*//s;
387                 my $url = $Tag->area({
388                                                                 href => 'admin/flex_editor',
389                                                                 form => qq{
390                                                                         mv_data_table=$CGI->{mv_data_table}
391                                                                         item_id=$id
392                                                                         ui_meta_view=$CGI->{ui_meta_view}
393                                                                 },
394                                                         });
395
396                 $Tag->deliver({ location => $url });
397 #::logDebug("deliver=$url");
398                 return;
399         }
400
401         my $sf;
402         if($sf = $CGI->{ui_sort_field} and $sf =~ s/^(\w+)([,\s\0]+.*)?$/$1/) {
403                 my $fmeta;
404                 $fmeta = $Tag->meta_record("${table}::$sf", $CGI->{ui_meta_view})
405                         and do {
406                                 $CGI->{ui_more_alpha} = $fmeta->{ui_more_alpha}
407                                         if length($fmeta->{ui_more_alpha});
408                                 if (! $CGI->{ui_sort_option} and length($fmeta->{ui_sort_option}) ) {
409                                         my $o = $fmeta->{ui_sort_option};
410                                         if($CGI->{ui_sort_option} =~ /r/) {
411                                                 $o =~ s/^([^r]+)$/$1r/
412                                                         or $o =~ s/r//;
413                                         }
414                                         $CGI->{ui_sort_option} = $o;
415                                 }
416                         };
417         }
418
419         for(qw/ui_more_alpha ui_more_decade ui_meta_specific/) {
420                 $CGI->{$_} = $meta->{$_} unless defined $CGI->{$_};
421         }
422         $Vend::Cfg->{NoSearch} = '';
423         my $out_message = '';
424         my $ui_text_qualification = $CGI->{ui_text_qualification};
425
426         if ($ui_text_qualification and $CGI->{ui_text_qualification} =~ /[<!=>\^]/ ) {
427                 if($ts->{owner_field}) {
428                         $CGI->{ui_text_qualification} = <<EOF;
429 co=1
430 st=db
431 sf=$ts->{owner_field}
432 se=$Vend::username
433 op=eq
434 nu=0
435 os=0
436 su=0
437 bs=0
438 EOF
439                 }
440                 else {
441                         $CGI->{ui_text_qualification} = "co=1\n";
442                 }
443
444                 my @entries = split /\s+(and|or)\s+/i,  $ui_text_qualification;
445                 my $or;
446                 for(@entries) {
447                         if(/^or$/i) {
448                                 $or = 1;
449                                 $CGI->{ui_text_qualification} .= "os=1\n";
450                                 next;
451                         }
452                         elsif(/^and$/i) {
453                                 $or = 0;
454                                 $CGI->{ui_text_qualification} .= "os=0\n";
455                                 next;
456                         }
457                         my ($f, $op, $s) = split /\s*([<=!>\^]+)\s*/, $_, 2;
458                         $op = "eq" if $op eq "==";
459                         $op = "rm" if $op eq "=";
460                         if($op eq '^') {
461                                 $op = 'rm';
462                                 $CGI->{ui_text_qualification} .= "bs=1\nsu=1\n";
463                         }
464                         else {
465                                 $CGI->{ui_text_qualification} .= "bs=0\nsu=0\n";
466                         }
467                         my $ms = defined $CGI->{mv_min_string} ? $CGI->{mv_min_string} : 1;
468                         if(length($s) > $ms) {
469                                 $CGI->{ui_text_qualification} .= "se=$s\nsf=$f\nop=$op\n";
470                         }
471                         else {
472                                 $CGI->{ui_text_qualification} .= "se=.\nsf=$f\nop=rn\n";
473                         }
474                         if($op =~ /[<>]/ and $s =~ /^[\d.]+$/) {
475                                 $CGI->{ui_text_qualification} .= "nu=1\n";
476                         }
477                         else {
478                                 $CGI->{ui_text_qualification} .= "nu=0\n";
479                         }
480                 }
481                 if(defined $or) {
482                         $CGI->{ui_text_qualification} .= $or ? "os=1\n" : "os=0\n";
483                 }
484
485                 $out_message = errmsg('Entries matching "%s"', $ui_text_qualification);
486         }
487         elsif ($ui_text_qualification) {
488                 $CGI->{ui_text_qualification} = "se=$CGI->{ui_text_qualification}";
489                 $out_message = errmsg('Entries matching "%s"', $ui_text_qualification);
490                 if($ts->{owner_field}) {
491                         $CGI->{ui_text_qualification} = <<EOF;
492 co=1
493 sf=$ts->{owner_field}
494 se=$Vend::username
495 op=eq
496 sf=:*
497 se=$CGI->{ui_text_qualification}
498 EOF
499                 }
500         }
501         elsif ( $CGI->{mv_like_field} ) {
502                 my @f = split /\0/, $CGI->{mv_like_field};
503                 my @s = split /\0/, $CGI->{mv_like_spec};
504                 my @q = 'ra=yes';
505                 my $found;
506                 for(my $i = 0; $i < @f; $i++) {
507                         next unless length $s[$i];
508                         $found++;
509                         push @q, "lf=$f[$i]";
510                         push @q, "ls=$s[$i]";
511                 }
512                 if($found) {
513                         $CGI->{ui_text_qualification} = join "\n", @q;
514                         my @out;
515                         for(@q) {
516                                 my $thing = $_;
517                                 $thing =~ s/^ls=/mv_like_spec=/;
518                                 $thing =~ s/^lf=/mv_like_field=/;
519                                 push @out, $thing; 
520                         }
521                         $ts->{like_recall} = join "\n", @out;
522                 }
523                 else       { $CGI->{ui_text_qualification} = "" }
524         }
525         elsif($ts->{owner_field}) {
526                 $CGI->{ui_text_qualification} = <<EOF;
527 co=1
528 sf=$ts->{owner_field}
529 se=$Vend::username
530 op=eq
531 EOF
532         }
533         elsif ($ts->{large}) {
534                 my $keylabel = $Tag->display({
535                                                         table => $table,
536                                                         name => 'item_id',
537                                                         column => $ts->{keyname},
538                                                         template => 1,
539                                                 });
540                 $ts->{like_spec} = $CGI->{mv_more_ip} ? 0 : 1;
541                 $CGI->{ui_text_qualification} = "";
542         }
543         else {
544                 $CGI->{ui_text_qualification} = "ra=yes";
545         }
546
547         if($meta->{ui_sort_combined} =~ /\S/) {
548                 $meta->{ui_sort_field} = $meta->{ui_sort_combined};
549                 $meta->{ui_sort_option} = '';
550         }
551
552         $CGI->{ui_sort_field}   ||= $meta->{ui_sort_field}
553                                                         ||  $meta->{lookup}
554                                                         ||  $ts->{keyname};
555         $CGI->{ui_sort_option}  ||= $meta->{ui_sort_option};
556         $CGI->{ui_sort_option}  =~ s/[\0,\s]+//g;
557         $CGI->{ui_list_size} = $opt->{height} || $meta->{height}
558                 if ! $CGI->{ui_list_size};
559
560         if(! $CGI->{ui_show_fields} ) {
561                 $CGI->{ui_show_fields} = 
562                         $CGI->{ui_description_fields}
563                                 = join ",", $ref->columns();
564         }
565         else {
566                 my $i = 0;
567                 my $show = $CGI->{ui_show_fields};
568                 if($filters_done) {
569                         # do nothing
570                 }
571                 else {
572                         if($show =~ s/[\r\n]+/\n/g) {
573                                 $show =~ s/^\s+//;
574                                 $show =~ s/\s+$//;
575                                 my @f = split /\n/, $show;
576                                 my @c;
577                                 for(@f) {
578                                         s/^\s+//;
579                                         s/\s+$//;
580                                         if(s/\s*\((.+)\)\s*$//)  {
581                                                 $filter_show[$i] = $1;
582                                         }
583                                         
584                                         if(/^(\w+)-(\w+)$/) {
585                                                 push @c, $1;
586                                                 $redirect[$i] = $2;
587                                         }
588                                         elsif(/^(\w+)(?:-([^=]+))?(?:=(.*))?/) {
589                                                 push @c, $1;
590                                                 $views[$i] = $2 if $2;
591                                                 $labels[$i] = $3;
592                                         }
593                                         else {
594                                                 push @c, $_;
595                                         }
596                                         $i++;
597                                 }
598                                 $show = join ",", @c;
599                         }
600                         else {
601                                 $show =~ s/(\w+)(?:\((.*?)\))?/ ($filter_show[$i++] = $2), $1/eg;
602                                 $show =~ s/[\0,\s]+/,/g;
603                         }
604                         $CGI->{ui_description_fields} = $show;
605                 }
606         }
607
608         my @cols = split /,/, $CGI->{ui_description_fields};
609
610         @cols = grep $ref->column_exists($_), @cols
611                 unless $spec;
612
613         my %limit_field;
614
615         $CGI->{ui_limit_fields} =~ s/[\0,\s]+/ /g;
616         $CGI->{ui_limit_fields} =~ s/^\s+//;
617         $CGI->{ui_limit_fields} =~ s/\s+$//;
618
619         my (@limit_field) = split " ", $CGI->{ui_limit_fields};
620
621         if(@limit_field) {
622                 @limit_field{@limit_field} = ();
623                 @cols = grep ! exists($limit_field{$_}), @cols;
624         }
625
626         unshift(@cols, $ts->{keyname})
627                 if $cols[0] ne $ts->{keyname};
628
629         $CGI->{ui_description_fields} = join ",", @cols;
630
631         unless ($CGI->{ui_sort_option}) { 
632                  $CGI->{ui_sort_option} = 'n'
633                                 if $ref->numeric($CGI->{ui_sort_field}); 
634         } 
635
636         my $fi = $CGI->{mv_data_table_real} || $CGI->{mv_data_table};
637         $ts->{sparams} = ($ts->{like_spec} || $spec) ? '' : <<EOF;
638
639         fi=$fi
640         st=db
641         $CGI->{ui_text_qualification}
642         su=1
643         ma=$CGI->{ui_more_alpha}
644         md=$CGI->{ui_more_decade}
645         ml=$CGI->{ui_list_size}
646         tf=$CGI->{ui_sort_field}
647         to=$CGI->{ui_sort_option}
648         rf=$CGI->{ui_description_fields}
649         nh=1
650
651 EOF
652         $::Scratch->{page_banner} .= $out_message;
653         $::Scratch->{page_title} .= $out_message;
654
655         my %output;
656 ### Header determination
657
658         my @refkeys = grep ref($opt->{$_}) eq 'HASH', keys %$opt;
659
660         my %default = (
661                 data_cell_class   => '',
662                 data_cell_style   => '',
663                 data_row_class_even   => 'rownorm',
664                 data_row_class_odd   => 'rowalt',
665                 data_row_style_even   => '',
666                 data_row_style_odd   => '',
667                 form_method => 'GET',
668                 explicit_edit => '',
669                 explicit_edit_page => '',
670                 explicit_edit_form => '',
671                 explicit_edit_anchor => '',
672                 no_code_link => '',
673                 group_image   => 'smindex.gif',
674                 group_class   => 'rhead',
675                 group_spacing   => 2,
676                 group_padding   => 0,
677                 group_width   => '100%',
678                 header_link_class   => 'rhead',
679                 header_cell_class   => 'rhead',
680                 header_cell_style   => '',
681                 header_row_class   => 'rhead',
682                 header_row_style   => '',
683                 mv_action => 'back',
684                 meta_image => errmsg('meta.png'),
685                 label => "flex_select_$table",
686                 no_checkbox => 0,
687                 radio_box => 0,
688                 user_merge => 0,
689                 check_uncheck_all => 0,
690                 number_list => 0,
691                 table_border  => 0,
692                 table_class   => 'rseparator',
693                 table_padding => 0,
694                 table_spacing => 1,
695                 table_style   => '',
696                 table_width   => '100%',
697         );
698
699         for(keys %default) {
700                 next if defined $opt->{$_};
701                 if(length $meta->{$_}) {
702                         $opt->{$_} = $meta->{$_};
703                 }
704                 else {
705                         $opt->{$_} = $default{$_};
706                 }
707         }
708
709         $opt->{ui_style} = 1 unless defined $opt->{ui_style};
710         $opt->{no_checkbox} = 1 if $ts->{multikey};
711
712         my $show_meta;
713         my $meta_anchor;
714         if($Tag->if_mm('super') and ! $opt->{no_meta}) {
715                 $show_meta = defined $::Values->{ui_meta_force}
716                                         ? $::Values->{ui_meta_force}
717                                         : $::Variable->{UI_META_SELECT};
718                 if($opt->{meta_image}) {
719                         $meta_anchor = qq{<img src="$opt->{meta_image}" border=0>};
720                 }
721                 else {
722                         $meta_anchor = 'M';
723                 }
724         }
725
726         $opt->{form_name} ||= "fs_$table";
727
728         $output{TOP_OF_TABLE} = <<EOF;
729 <table width="$opt->{table_width}" border="$opt->{table_border}" cellpadding="$opt->{table_padding}" cellspacing="$opt->{table_spacing}" class="$opt->{table_class}">
730 EOF
731
732         my $cwp = $Global::Variable->{MV_PAGE};
733         $opt->{form_href} ||= $CGI->{ui_searchpage} || $cwp;
734         $opt->{form_extra} ||= '';
735         $opt->{form_extra} .= qq{ name="$opt->{form_name}"} if $opt->{form_name};
736         $opt->{form_extra} =~ s/^\s*/ /;
737         my $action = $Tag->process({href => $opt->{form_href}});
738
739         $output{TOP_OF_FORM} = <<EOF;
740 <form action="$action" method="$opt->{form_method}"$opt->{form_extra}>
741 <input type=hidden name=mv_data_table    value="$table">
742 <input type=hidden name=mv_action        value="$opt->{mv_action}">
743 <input type=hidden name=mv_click         value="warn_me_main_form">
744 <input type=hidden name=mv_form_profile  value="$opt->{mv_form_profile}">
745 <input type=hidden name=mv_session_id    value="$Vend::SessionID">
746 EOF
747
748         ### What the heck is going on here?
749         if($CGI->{ui_meta_view}) {
750                 $output{TOP_OF_FORM} .= <<EOF;
751 <input type=hidden name=ui_meta_view         value="$CGI->{ui_meta_view}">
752 EOF
753                 $output{TOP_OF_FORM} .= $Tag->return_to();
754         }
755         else {
756                 $output{TOP_OF_FORM} .= <<EOF;
757         <!-- got no return-to -->
758 <input type=hidden name=ui_meta_specific value="$CGI->{ui_meta_specific}">
759 <input type=hidden name=ui_page_title    value="$CGI->{ui_page_title}">
760 <input type=hidden name=ui_page_banner   value="$CGI->{ui_page_banner}">
761 <input type=hidden name=ui_limit_fields  value="$CGI->{ui_limit_fields}">
762 <input type=hidden name=ui_show_fields   value="$CGI->{ui_show_fields}">
763 <input type=hidden name=ui_return_to     value="$cwp">
764 <input type=hidden name=ui_return_to     value="mv_data_table=$table">
765 EOF
766         }
767
768         my $cc = $ts->{column_meta} ||= {};
769         my $mview = $CGI->{ui_meta_view};
770
771         my $cmeta = sub {
772                 my $col = shift;
773                 return $cc->{$col} if $cc->{$col};
774                 my $m = $Tag->meta_record("${table}::$col", $mview);
775                 for(@refkeys) {
776                         $m->{$_} = $opt->{$_}{$col} if exists $opt->{$_}{$col};
777                 }
778                 $cc->{$col} = $m;
779                 return $m;
780         };
781
782         my $header_cell_style = sub {
783                                 my $col = shift;
784                                 my $m = $cmeta->($col);
785 #::logDebug("meta for header=" . ::uneval($m));
786                                 my $stuff = '';
787                                 for(qw/ class style align valign /) {
788                                         my $tag = "header_cell_$_";
789                                         my $thing;
790                                         if(ref $opt->{$tag}) {
791                                                 $thing = $opt->{$tag}{$col} || $m->{$tag} || $opt->{"all_$tag"}
792                                                         or next;
793                                         }
794                                         else {
795                                                 $thing = $m->{$tag} || $opt->{$tag}
796                                                         or next;
797                                         }
798                                         encode_entities($thing);
799                                         $stuff .= qq{ $_="$thing"};
800                                 }
801                                 return $stuff;
802                         };
803
804         my $data_cell_style = sub {
805                                 my $col = shift;
806                                 my $m = $cmeta->($col);
807                                 my $stuff = '';
808                                 for(qw/ class style align valign /) {
809                                         my $tag = "data_cell_$_";
810                                         my $thing;
811                                         if(ref $opt->{$tag}) {
812                                                 $thing = $opt->{$tag}{$col} || $m->{$tag} || $opt->{"all_$tag"}
813                                                         or next;
814                                         }
815                                         else {
816                                                 $thing = $m->{$tag} || $opt->{$tag}
817                                                         or next;
818                                         }
819                                         encode_entities($thing);
820                                         $stuff .= qq{ $_="$thing"};
821                                 }
822                                 return $stuff;
823                         };
824
825         my @head;
826         my $rc = $opt->{header_row_class};
827         push @head, "<tr ";
828         push @head, qq( class=$opt->{header_row_class}) if $opt->{header_row_class};
829         push @head, qq( style=$opt->{header_row_style}) if $opt->{header_row_style};
830         push @head, ">\n";
831         if(! $opt->{no_checkbox}) {
832                 push @head, "   <td class=rhead>&nbsp;</td>" 
833         }
834         if($opt->{radio_box}) {
835                 push @head, "   <td class=rhead>&nbsp;</td>" 
836         }
837         if($opt->{number_list}) {
838                 push @head, "   <td class=rhead align=right>#&nbsp;</td>" ;
839         }
840         if($opt->{explicit_edit}) {
841                 push @head, "   <td class=rhead>&nbsp;</td>" 
842         }
843
844         my $return = <<EOF;
845 ui_return_to=$cwp
846 ui_return_to=ui_meta_view=$opt->{ui_meta_view}
847 ui_return_to=mv_return_table=$table
848 mv_return_table=$table
849 ui_return_stack=$CGI->{ui_return_stack}
850 start_at=extended.ui_more_alpha
851 EOF
852
853         my %mkey;
854         if($ts->{multikey}) {
855                 for(@{$ts->{key_columns}}) {
856                         $mkey{$_} = 1;
857                 }
858         }
859
860         my @mcol;
861
862         my $idx = 0;
863         foreach my $col (@cols) {
864                 my $mcol = $col;
865                 if($redirect[$idx]) {
866                         $mcol .= "-$redirect[$idx]";
867                 }
868                 my $td_extra = $header_cell_style->($mcol);
869
870                 ## $cc is set in header_cell_class 
871                 my $m = $cc->{$mcol};
872
873                 if($mkey{$col}) {
874                         push @mcol, $idx - 1;
875                 }
876
877                 push @head, <<EOF;
878 <td$td_extra>
879 <table align="left" class="$opt->{group_class}" cellspacing=$opt->{group_spacing} cellpadding=$opt->{group_padding} width="$opt->{group_width}">
880     <tr>
881 EOF
882                 unless($opt->{no_group} || $m->{fs_no_group}) {
883                         my $u = $Tag->area({
884                                                                 href => 'admin/flex_group',
885                                                                 form => qq(
886                                                                                         mv_data_table=$table
887                                                                                         ui_meta_view=$mview
888                                                                                         from_page=$Global::Variable->{MV_PAGE}
889                                                                                         substr=$m->{fs_group_substring}
890                                                                                         mv_arg=$col
891                                                                                 ),
892                                                         });
893                         my $msg = errmsg('Select group by %s', $col);
894
895                         push @head, <<EOF;
896       <td align="right" valign="center" width=1>
897                 <a href="$u" title="$msg"><img src="$opt->{group_image}" border=0></a>
898       </td>
899 EOF
900
901                 }
902
903                 my $o = '';
904                 my $msg;
905                 my $rmsg;
906                 if($o = $m->{ui_sort_option}) {
907                         my @m;
908                         $msg = "sort by %s (%s)";
909
910                         if($CGI->{ui_sort_field} eq $col) {
911                                 if($CGI->{ui_sort_option} =~ /r/) {
912                                         $o =~ s/r//;
913                                 }
914                                 else {
915                                         $o .= "r";
916                                 }
917                         }
918                         push @m, errmsg('reverse') if $o =~ /r/;
919                         push @m, errmsg('case insensitive') if $o =~ /f/;
920                         push @m, errmsg('numeric') if $o =~ /n/;
921                         $rmsg = join ", ", @m;
922                 }
923                 else {
924                         if ($CGI->{ui_sort_field} eq $col and $CGI->{ui_sort_option} !~ /r/) {
925                                 $o .= 'r';
926                                 $msg = "sort by %s (%s)";
927                                 $rmsg = errmsg('reverse');
928                         }
929                         else {
930                                 $msg = "sort by %s";
931                         }
932                         $o .= 'n' if $ref->numeric($col);
933                 }
934                 my $sort_msg = errmsg($msg, $col, $rmsg);
935                 my $url = $Tag->area( {
936                                                                 href => $cwp,
937                                                                 form => qq(
938                                                                         $ts->{like_recall}
939                                                                         ui_text_qualification=$ui_text_qualification
940                                                                         mv_data_table=$table
941                                                                         ui_meta_view=$mview
942                                                                         ui_sort_field=$col
943                                                                         ui_sort_option=$o
944                                                                         ui_more_alpha=$m->{ui_more_alpha}
945                                                                 ),
946                                                         });
947
948                 my $lab = $labels[$idx] || $m->{label} || $col;
949
950                 # Set up some stuff for the data cells;
951                 $style[$idx] = $data_cell_style->($mcol);
952
953                 $filter_show[$idx] = $filter->{$mcol} if $filter->{$mcol};
954                 $filter_show[$idx] ||= $m->{fs_display_filter} || 'encode_entities';
955                 $filter_show[$idx] .= ' encode_entities'
956                          unless $filter_show[$idx] =~ /\b(?:encode_)?entities\b/;
957                 $style[$idx] .= " $1" while $filter_show[$idx] =~ s/(v?align=\w+)//i;
958
959                 if($views[$idx]) {
960                         my ($page, $parm, $l) = split /:/, $views[$idx];
961                         $m->{fs_link_page} = $page;
962
963                         $parm ||= 'item_id';
964                         my @p = split /[\s,\0]+/, $parm;
965                         my $arg = shift @p;
966                         $m->{fs_link_parm} = $arg;
967                         $m->{fs_link_parm_extra} = join ",", @p;
968                         $m->{fs_link_anchor} = $l;
969                 }
970
971                 if($m->{fs_link_page}) {
972                         $link_page[$idx]                = $m->{fs_link_page};
973                         $link_parm[$idx]                = $m->{fs_link_parm};
974                         if($m->{fs_link_parm_extra}) {
975                                 my @p = grep /\S/, split /[\s,\0]+/, $m->{fs_link_parm_extra};
976                                 $link_parm_extra[$idx]  = \@p;
977                         }
978                         $link_anchor[$idx]      = $m->{fs_link_anchor};
979                 }
980
981                 if(my $prog = $m->{fs_data_calc}) {
982 #::logDebug("looking at calcs=$prog");
983                         $prog =~ s/^\s+//;
984                         $prog =~ s/\s+$//;
985                         if($prog =~ /^\w+$/) {
986                                 $calcs[$idx] = $Vend::Cfg->{Sub}{$prog} || $Global::GlobalSub->{$prog};
987                         }
988                         else {
989                                 $prog =~ s/^\[(calc|perl)(.*?)\]//;
990                                 $prog =~ s{\[/(calc|perl)\]$}{};
991                                 $calcs[$idx] = $prog;
992                         }
993                         if($m->{fs_data_tables}) {
994                                 tag_perl($m->{fs_data_tables}, {});
995                         }
996                 }
997
998                 push @head, <<EOF;
999           <td$td_extra>
1000                 <a href="$url" class=$opt->{header_link_class} title="$sort_msg">$lab</a>
1001       </td>
1002 EOF
1003
1004                 if($show_meta) {
1005                         my $u = $Tag->area({ href=>'admin/meta_editor',
1006                                                                  form => qq(
1007                                                                  item_id=${table}::$mcol
1008                                                                  ui_meta_view=$mview
1009                                                                  $return),
1010                                                                 });
1011                         my $tit = errmsg(
1012                                                         "Edit header meta information for %s::%s",
1013                                                         $table,
1014                                                         $col,
1015                                                 );
1016                         push @head, <<EOF;
1017 <td width=1>
1018 <a href="$u" title="$tit">$meta_anchor</a>
1019 </td>
1020 EOF
1021
1022                 }
1023
1024                 push @head, <<EOF;
1025     </tr>
1026     </table>    
1027         </td>
1028 EOF
1029
1030                 $idx++;
1031         }
1032         push @head, "</tr>";
1033
1034         shift @mcol;
1035
1036         my $ncols = $idx;
1037         $ncols += $opt->{explicit_edit} if $opt->{explicit_edit} > 0;
1038         $ncols++ if $opt->{number_list};
1039         $ncols++ if $opt->{radio_box};
1040         $ncols++ unless $opt->{no_checkbox};
1041
1042         $output{HEADER_AREA} = join "", @head;
1043 ### Row output
1044
1045         my $cb_width = $opt->{checkbox_width} || '30';
1046         my $cb_name = $opt->{checkbox_name} || 'item_id';
1047         my $rb_name = $opt->{radiobox_name} || 'item_radio';
1048         my $edit_page = $opt->{edit_page} || 'admin/flex_editor';
1049         my $edit_parm = $opt->{edit_parm} || 'item_id';
1050         my $edit_extra = <<EOF;
1051 mv_data_table=$table
1052 ui_page_title=$CGI->{ui_page_title}
1053 ui_meta_view=$mview
1054 ui_page_banner=$CGI->{ui_page_banner}
1055 ui_meta_specific=$CGI->{ui_meta_specific}
1056 EOF
1057
1058         
1059         my @rows;
1060
1061         if($ts->{like_spec}) {
1062                 ## Do nothing
1063         }
1064         elsif($body =~ /\S/) {
1065                 my $o = { 
1066                                         label           => $opt->{label},
1067                                         list_prefix     => 'flex',
1068                                         prefix          => 'flex',
1069                                         more            => 1,
1070                                         search          => $ts->{sparams},
1071                                 };
1072                 push @rows, tag_loop_list($o);
1073         }
1074         else {
1075                 my $ary;
1076                 my $search;
1077                 my $params;
1078                 my $c;
1079 #::logDebug("MM=$CGI->{MM}($CGI::values{MM}) mv_more_matches=$CGI->{mv_more_matches}($CGI::values{mv_more_matches})");
1080                 if($CGI->{mv_more_ip}) {
1081                         $search = $::Instance->{SearchObject}{$opt->{label}};
1082                         $search ||= $::Instance->{SearchObject}{''};
1083                         $search ||= perform_search();
1084                         $ary = [ splice(
1085                                                 @{$search->{mv_results}},
1086                                                 $search->{mv_first_match},
1087                                                 $search->{mv_matchlimit},
1088                                                 )] ;
1089 #::logDebug("search first_match=$search->{mv_first_match} length=$search->{mv_matchlimit}");
1090 #::logDebug("Found search=" . ::uneval($search));
1091                 }
1092                 elsif($q) {
1093                         my $db = dbref($table);
1094                         my $o = {
1095                                 ma              => $CGI->{ui_more_alpha},
1096                                 md              => $CGI->{ui_more_decade},
1097                                 ml              => $CGI->{ui_list_size},
1098                                 more    => 1,
1099                                 table   => $fi,
1100                                 query   => $q,
1101                         };
1102                         $ary = $db->query($o);
1103                 }
1104                 else {
1105 #::logDebug("In new search");
1106                         $params = escape_scan($ts->{sparams});
1107                         $c = { mv_search_immediate => 1, mv_search_label => $opt->{label} };
1108                         Vend::Scan::find_search_params($c, $params);
1109                         $search = Vend::Scan::perform_search($c);
1110                         $ary = $search->{mv_results};
1111                 }
1112
1113                 finish_search($search) if $search;
1114                 
1115                 $search ||= {};
1116
1117                 if($CGI->{ui_return_to} and ! $CGI->{ui_return_stack}) {
1118                         $edit_extra .= $Tag->return_to('formlink');     
1119                 }
1120                 else {
1121                         $edit_extra .= "ui_return_to=$cwp";
1122                 }
1123
1124                 my $ee_extra;
1125                 if($opt->{explicit_edit}) {
1126                         $ee_extra = '';
1127                         for(qw/ class style width align valign /) {
1128                                 my $v = $opt->{"explicit_edit_$_"}
1129                                         or next;
1130                                 $ee_extra .= qq{ $_="$v"};
1131                         }
1132                         $ee_extra ||= ' width=30';
1133                 }
1134 #::logDebug("explicit_edit=$opt->{explicit_edit} no_code_link=$opt->{no_code_link}");
1135                 my $j = $search->{mv_first_match} || 0;
1136                 foreach my $line (@$ary) {
1137                         my $code = shift (@$line);
1138                         my $ecode = encode_entities($code);
1139                         my $rc = $j++ % 2
1140                                         ? $opt->{data_row_class_even}
1141                                         : $opt->{data_row_class_odd};
1142                         my $out = qq{<tr class="$rc">\n};
1143
1144                         my $code_pre; my $code_post;
1145                         my $ep_string = '';
1146                         if($opt->{no_code_link} and ! $opt->{explicit_edit}) {
1147                                 $code_pre = $code_post = '';
1148                         }
1149                         else {
1150                                 my @what;
1151                                 push @what, "$edit_parm=$code";
1152                                 if($ts->{multikey}) {
1153                                         unshift @what, 'ui_multi_key=1';
1154                                         for(@mcol) {
1155                                                 push @what, "$edit_parm=$line->[$_]";
1156                                         }
1157
1158                                 }
1159
1160                                 $ep_string = join "\n", @what, $edit_extra;
1161
1162                                 my $edit_url = $Tag->area({
1163                                                                         href => $edit_page,
1164                                                                         form => $ep_string,
1165                                                                 });
1166                                 my $msg = errmsg('edit %s', $ecode);
1167                                 $code_pre = qq{<a href="$edit_url" title="$msg">};
1168                                 $code_post = qq{</a>};
1169                         }
1170
1171                         unless($opt->{no_checkbox}) {
1172                                 $out .= <<EOF;
1173 <td width="$cb_width"><input type=checkbox name=$cb_name value="$ecode"></td>
1174 EOF
1175                         }
1176                         if($opt->{radio_box}) {
1177                                 $out .= <<EOF;
1178 <td width="$cb_width"><input type=radio name=$rb_name value="$ecode"></td>
1179 EOF
1180                         }
1181
1182                         if($opt->{number_list}) {
1183                                 $out .= qq{<td align=right>&nbsp;$j&nbsp;</td>};
1184                         }
1185
1186                         if($opt->{explicit_edit}) {
1187                                 for (qw/ explicit_edit_anchor explicit_edit_page explicit_edit_form /) {
1188                                         $opt->{$_} =~ s/^\s+//;
1189                                         $opt->{$_} =~ s/\s+$//;
1190                                 }
1191                                 my @forms   = grep /\w/, split /[ \t]*[\r\n|][ \t]*/, $opt->{explicit_edit_form};
1192                                 my @anchors = grep /\w/, split /[ \t]*[\r\n|][ \t]*/, $opt->{explicit_edit_anchor};
1193                                 my @pages   = grep /\w/, split /[ \t]*[\r\n|][ \t]*/, $opt->{explicit_edit_page};
1194                                 $out .= qq{<td$ee_extra>};
1195                                 while (my $page = shift @pages) {
1196                                         my $form = shift(@forms);
1197                                         my $edit_anchor = shift(@anchors) || errmsg('edit record');
1198                                         $edit_anchor =~ s/ /&nbsp;/g;
1199                                         if($form) {
1200                                                 $form .= $ecode;
1201                                         }
1202                                         my $url = $Tag->area({
1203                                                                                 href => $page || $edit_page,
1204                                                                                 form => $form || $ep_string,
1205                                                                         });
1206                                         my $msg = errmsg('process %s', $ecode);
1207                                         my $pre = qq{<a href="$url" title="$msg">};
1208                                         $out .= qq{&nbsp;$pre$edit_anchor$code_post&nbsp;};
1209                                 }
1210                                 $out .= qq{</td>};
1211                                 if($opt->{no_code_link}) {
1212                                         $code_post = $code_pre = '';
1213                                 }
1214                         }
1215
1216 #::logDebug("keyname=$ts->{keyname}");
1217                         $out .= "<td" . $data_cell_style->($ts->{keyname}) . ">";
1218                         $ecode = '';
1219                         if ($calcs[0]) {
1220                                 my %item;
1221                                 @item{@cols} = ($code, @$line);
1222                                 if(ref($calcs[0]) eq 'CODE') {
1223                                         $ecode = $calcs[0]->(\%item);
1224                                 }
1225                                 else {
1226                                         $Vend::Interpolate::item = \%item;
1227                                         $ecode = tag_calc($calcs[0]);
1228                                 }
1229                         }
1230                         if ($filter_show[0]) {
1231                                 $ecode = $code unless $ecode;
1232                                 $ecode = $Tag->filter($filter_show[0], $ecode, $cols[0]);
1233                                 $ecode =~ s/\[/&#91;/g;
1234                         }
1235                         $ecode = encode_entities($code) unless $ecode;
1236                         $out .= "$code_pre$ecode$code_post</td>";
1237                         my $i = 1;
1238                         for my $v (@$line) {
1239                                 my $extra = $style[$i];
1240                                 my $pre = '';
1241                                 my $post = '';
1242                                 my $lab;
1243
1244                                 if($link_page[$i]) {
1245                                         my $opt = { $link_parm[$i] => $v, form => 'auto' };
1246                                         if(my $p = $link_parm_extra[$i]) {
1247                                                 for(@$p) {
1248                                                         $opt->{$_} = $CGI->{$_};
1249                                                 }
1250                                         }
1251                                         $opt->{href} = $link_page[$i];
1252
1253                                         $lab = $link_anchor[$i];
1254                                         $lab =~ s/^\s+//;
1255                                         my $url = $Tag->area($opt);
1256                                         my $ev = encode_entities($v);
1257                                         $pre = qq{<a href="$url" title="$ev">};
1258                                         $post = '</a>';
1259                                 }
1260
1261                                 if($calcs[$i]) {
1262 #::logDebug("found a calc");
1263                                         my %item;
1264                                         @item{@cols} = ($code, @$line);
1265                                         if(ref($calcs[$i]) eq 'CODE') {
1266                                                 $lab = $calcs[$i]->(\%item);
1267                                         }
1268                                         else {
1269                                                 $Vend::Interpolate::item = \%item;
1270                                                 $lab = tag_calc($calcs[$i]);
1271                                         }
1272                                 }
1273
1274                                 $lab ||= $v;
1275
1276                                 $lab = $Tag->filter($filter_show[$i], $lab, $cols[$i]);
1277
1278                                 $lab =~ s/\[/&#91;/g;
1279                                 $out .= "<td$extra>$pre$lab$post</td>";
1280
1281                                 $i++;
1282                         }
1283                         $out .= "</tr>\n";
1284                         push @rows, $out;
1285                 }
1286
1287                 unless(@rows) {
1288                         my $nomsg = errmsg('No records');
1289                         push @rows, qq{<tr><td colspan=$ncols><blockquote>$nomsg.</blockquote></td></tr>};
1290                 }
1291                 else {
1292                         my $mmsg = errmsg($opt->{more_message} ||= 'More rows');
1293                         $opt->{more_list} ||= <<EOF;
1294 <tr>
1295 <td colspan={NCOLS} align=center>
1296 $mmsg: [decade-next][/decade-next] [more] [decade-prev][/decade-prev]
1297 </td>
1298 </tr>
1299 EOF
1300                         $opt->{more_list} =~ s/\{NCOLS\}/$ncols/g;
1301                         my $override = { mv_data_table => $table, ui_meta_view => $mview };
1302                         my @forms;
1303                         my @formparms = qw/ mv_data_table ui_meta_view ui_meta_specific /;
1304                         for(@formparms) {
1305                                 my $thing = $override->{$_} || $CGI->{$_};
1306                                 next unless length $thing;
1307                                 push @forms, "$_=$thing";
1308                         }
1309                         my $o = {
1310                                 object => $search,
1311                                 label => $opt->{label},
1312                                 form => join("\n", @forms),
1313                         };
1314                         $output{MORE_LIST} = tag_more_list(
1315                                                                                 $opt->{next_anchor},
1316                                                                                 $opt->{prev_anchor},
1317                                                                                 $opt->{page_anchor},
1318                                                                                 $opt->{more_border},
1319                                                                                 $opt->{more_border_selected},
1320                                                                                 $o,
1321                                                                                 $opt->{more_list},
1322                                                                         );
1323                 }
1324         }
1325
1326         $output{BOTTOM_OF_TABLE} = '</table>';
1327         $output{BOTTOM_OF_FORM} = '</form>';
1328         my $calc_sequence = <<'EOF';
1329 ui_sequence_edit=[calc]
1330         $CGI->{item_id_left} = $CGI->{item_id};
1331         $CGI->{item_id_left} =~ s/\0+/,/g;
1332         if($CGI->{item_id_left} =~ s/^(.*?),//) {
1333                 $CGI->{item_id} = $1;
1334                 return 1;
1335         }
1336         else {
1337                 delete $CGI->{item_id_left};
1338                 return '';
1339         }
1340 [/calc]
1341 EOF
1342         $calc_sequence .= "mv_nextpage=$edit_page\nmv_todo=return";
1343         my $ebutton = $Tag->button(     
1344                                                         {
1345                                                                 text => errmsg('Edit checked records in sequence'),
1346                                                                 extra => $opt->{edit_button_extra} || ' class=s2',
1347                                                         },
1348                                                         $calc_sequence,
1349                                                 );
1350         my $mbutton = '';
1351         my $dbutton = '';
1352         if($Tag->if_mm({ function => 'tables', table => "$table=d"}) ) {
1353                 $opt->{confirm} ||= "Are you sure you want to delete the checked records?";
1354                 my $dtext = qq{
1355 [flag type=write table=$table]
1356 deleterecords=1
1357 mv_click=db_maintenance};
1358                 $dbutton = '&nbsp;';
1359                 $dbutton .= $Tag->button(       
1360                                                         {
1361                                                                 text => errmsg('Delete checked records'),
1362                                                                 extra => $opt->{edit_button_extra} || ' class=s2',
1363                                                                 confirm => errmsg($opt->{confirm}),
1364                                                         },
1365                                                         $dtext,
1366                                                 );
1367                 
1368                 if($opt->{user_merge}) {
1369                         $opt->{confirm_merge} ||= "Are you sure you want to merge the checked users?";
1370                         $mbutton = '&nbsp;';
1371                         $mbutton .= $Tag->button(       
1372                                                                 {
1373                                                                         text => errmsg('Merge checked users'),
1374                                                                         extra => $opt->{merge_button_extra} || ' class=s2',
1375                                                                         confirm => errmsg($opt->{confirm_merge}),
1376                                                                 },
1377                                                                 '[user-merge]',
1378                                                         );
1379                                 
1380                 }
1381         }
1382         my $cboxes = '';
1383
1384         if($meta->{check_uncheck_all}) {
1385                 my $uc_msg = errmsg('Uncheck all');
1386                 my $ch_msg = errmsg('Check all');
1387                 $ch_msg =~ s/\s/&nbsp;/g;
1388                 $uc_msg =~ s/\s/&nbsp;/g;
1389                 $cboxes = <<EOF;
1390 <a href="javascript:checkAll(document.$opt->{form_name}, '$cb_name')">
1391 $ch_msg
1392 </a>&nbsp;&nbsp;
1393 <a href="javascript:checkAll(document.$opt->{form_name}, '$cb_name', 1)">
1394 $uc_msg
1395 </a>&nbsp;&nbsp;
1396 EOF
1397                 $cboxes =~ s/\n//g;
1398         }
1399
1400         if(! $opt->{no_checkbox} and ! $ts->{like_spec}) {
1401                 unless($opt->{no_top} || $opt->{bottom_buttons}) {
1402                         $output{TOP_BUTTONS} = $cboxes;
1403                         $output{TOP_BUTTONS} .= $ebutton;
1404                         if($mbutton) {
1405                                 $output{TOP_BUTTONS} .= '&nbsp;' x 4;
1406                                 $output{TOP_BUTTONS} .= $mbutton;
1407                         }
1408                         if($dbutton) {
1409                                 $output{TOP_BUTTONS} .= '&nbsp;' x 4;
1410                                 $output{TOP_BUTTONS} .= $dbutton;
1411                         }
1412                 }
1413
1414                 unless($opt->{no_bottom} || $opt->{top_buttons}) {
1415                         $output{BOTTOM_BUTTONS} = $cboxes;
1416                         $output{BOTTOM_BUTTONS} .= $ebutton;
1417                         if($mbutton) {
1418                                 $output{BOTTOM_BUTTONS} .= '&nbsp;' x 4;
1419                                 $output{BOTTOM_BUTTONS} .= $mbutton;
1420                         }
1421                         if($dbutton) {
1422                                 $output{BOTTOM_BUTTONS} .= '&nbsp;' x 4;
1423                                 $output{BOTTOM_BUTTONS} .= $dbutton;
1424                         }
1425                 }
1426         }
1427
1428         my %map = qw/
1429                         TOP_OF_FORM                     top_of_form
1430                         BOTTOM_OF_FORM          bottom_of_form
1431                         HIDDEN_FIELDS       hidden_fields
1432                         TOP_BUTTONS         top_buttons
1433                         BOTTOM_BUTTONS          bottom_buttons
1434                         EXTRA_BUTTONS           extra_buttons
1435                 /;
1436
1437         my @areas = qw/
1438                                         TOP_OF_TABLE
1439                                         TOP_OF_FORM
1440                                         HIDDEN_FIELDS
1441                                         TOP_BUTTONS 
1442                                         HEADER_AREA
1443                                         MAIN_BODY
1444                                         MORE_LIST
1445                                         BOTTOM_BUTTONS
1446                                         EXTRA_BUTTONS
1447                                         BOTTOM_OF_FORM
1448                                         BOTTOM_OF_TABLE
1449                                 /;
1450         if($ts->{like_spec}) {
1451                 push @rows, <<EOF;
1452         <tr>
1453         <td>&nbsp;</td>
1454         <td colspan="$ncols" align=left>
1455         [L]Check the box for exact record and enter the record id/key.[/L]
1456         [L]Or enter a query by example to select a set of records.[/L]
1457         [L]Each input will match on the <i>beginning</i> text in the field.[/L]
1458         <p>
1459         <small><input type=checkbox name=ui_exact_record value=1 class=s3> Edit exact record in key column</small>
1460         <br>
1461         &nbsp;
1462         </td>
1463         </tr>
1464         <tr>
1465         <td>&nbsp;</td>
1466         [loop list="[cgi ui_description_fields]"]
1467         <td>
1468                 <input type=hidden name=mv_like_field value="[loop-code]">
1469                 <input type=text name=mv_like_spec size=10>
1470         </td>
1471         [/loop]
1472         </tr>
1473         <tr>
1474         <td>&nbsp;</td>
1475         <td colspan="$ncols" align=left>
1476         &nbsp;
1477         <br>
1478         &nbsp;
1479         <br>
1480         <input type=submit value="[L]Find[/L]">
1481         </td>
1482         </tr>
1483 EOF
1484         }
1485
1486         $output{MAIN_BODY} = join "", @rows;
1487
1488         my @out;
1489         for(@areas) {
1490                 next unless $output{$_};
1491                 if($opt->{ui_style} and $map{$_}) {
1492                         my $op = $map{$_};
1493                         $Tag->output_to($op, { name => $op }, $output{$_} );
1494                 }
1495                 else {
1496                         push @out, $output{$_};
1497                 }
1498         }
1499         return join "", @out;
1500 }
1501 EOR