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