1 # Vend::Table::Editor - Swiss-army-knife table editor for Interchange
3 # $Id: Editor.pm,v 1.93 2009-03-20 18:59:35 mheins Exp $
5 # Copyright (C) 2002-2008 Interchange Development Group
6 # Copyright (C) 2002 Mike Heins <mike@perusion.net>
8 # This program was originally based on Vend 0.2 and 0.3
9 # Copyright 1995 by Andrew M. Wilcox <amw@wilcoxsolutions.com>
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public
22 # License along with this program; if not, write to the Free
23 # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
26 package Vend::Table::Editor;
28 use vars qw($VERSION);
29 $VERSION = substr(q$Revision: 1.93 $, 10);
32 use Vend::Interpolate;
35 @EXPORT_OK = qw/meta_record expand_values tabbed_display display/;
37 no warnings qw(uninitialized numeric);
41 Vend::Table::Editor -- Interchange do-all HTML table editor
45 [table-editor OPTIONS]
47 [table-editor OPTIONS] TEMPLATE [/table-editor]
51 The [table-editor] tag produces an HTML form that edits a database
52 table or collects values for a "wizard". It is extremely configurable
53 as to display and characteristics of the widgets used to collect the
56 The widget types are based on the Interchange C<[display ...]> UserTag,
57 which in turn is heavily based on the ITL core C<[accessories ...]> tag.
59 The C<simplest> form of C<[table-editor]> is:
61 [table-editor table=foo]
63 A page which contains only that tag will edit the table C<foo>, where
64 C<foo> is the name of an Interchange table to edit. If no C<foo> table
65 is C<defined>, then nothing will be displayed.
67 If the C<mv_metadata> entry "foo" is present, it is used as the
68 definition for table display, including the fields to edit and labels
69 for sections of the form. If C<ui_data_fields> is defined, this
70 cancels fetch of the view and any breaks and labels must be
71 defined with C<ui_break_before> and C<ui_break_before_label>. More
72 on the view concept later.
74 A simple "wizard" can be made with:
78 ui_wizard_fields="foo bar"
80 mv_prevpage=wizard_intro
83 The purpose of a "wizard" is to collect values from the user and
84 place them in the $Values array. A next page value (option mv_nextpage)
85 must be defined to give a destination; if mv_prevpage is defined then
86 a "Back" button is presented to allow paging backward in the wizard.
90 my $Tag = new Vend::Tags;
94 use vars qw/%Display_type %Display_options %Style_sheet/;
99 $opt->{cell_span} = 3;
104 $opt->{break_cell_first_style} ||= 'border-top: 1px solid #999999';
107 adjust_width => sub {
109 $opt->{adjust_cell_class} ||= $opt->{widget_cell_class};
110 if($opt->{table_width} and $opt->{table_width} =~ /^\s*(\d+)(\w*)\s*$/) {
113 $opt->{help_cell_style} ||= 'width: ' . int($wid * .35) . $type;
116 $opt->{help_cell_style} ||= 'width: 400';
126 <td$opt->{label_cell_extra}>
127 {BLABEL}{LABEL}{ELABEL}{META_STRING}
129 <td$opt->{data_cell_extra}\{COLSPAN}>
130 <table cellspacing="0" cellmargin="0" width="100%">
132 <td$opt->{widget_cell_extra}>
135 <td$opt->{help_cell_extra}>{TKEY}{HELP?}<i>{HELP}</i>{/HELP?}{HELP_URL?}<br><a href="{HELP_URL}">$opt->{help_anchor}</a>{/HELP_URL?}</td>
154 $opt->{break_template} ||= <<EOF;
155 <tr$opt->{break_row_extra}><td colspan="$span" $opt->{break_cell_extra}\{FIRST?} style="$opt->{break_cell_first_style}"{/FIRST?}>{ROW}</td></tr>
158 <td$opt->{label_cell_extra}>
159 {BLABEL}{LABEL}{ELABEL}
161 <td$opt->{data_cell_extra}\{COLSPAN}>
162 <table cellspacing="0" cellmargin="0" width="100%">
164 <td$opt->{widget_cell_extra}>
167 <td$opt->{help_cell_extra}>{TKEY}{HELP?}<i>{HELP}</i>{/HELP?}{HELP_URL?}<br><a href="{HELP_URL}">$opt->{help_anchor}</a>{/HELP_URL?}</td>
168 <td align="right">{META_STRING}</td>
177 text_js_help => sub {
180 <td$opt->{label_cell_extra}>
181 {BLABEL}{LABEL}{ELABEL}
183 <td$opt->{data_cell_extra}\{COLSPAN} nowrap>{WIDGET}{HELP_EITHER?} <a href="{HELP_URL?}{HELP_URL}{/HELP_URL?}{HELP_URL:}javascript:alert('{HELP}'); void(0){/HELP_URL:}" title="{HELP}">$opt->{help_anchor}</a>{/HELP_EITHER?} {META_URL?}<a href="{META_URL}">$opt->{meta_anchor}</a>{/META_URL?}
189 three_column => sub {
192 <td$opt->{label_cell_extra}>
193 {BLABEL}{LABEL}{ELABEL}
195 <td$opt->{data_cell_extra}\{COLSPAN} nowrap>
198 <td>{HELP_EITHER?} <a href="{HELP_URL}" title="{HELP}">$opt->{help_anchor}</a>{/HELP_EITHER?} {META_URL?}<a href="{META_URL}">$opt->{meta_anchor}</a>{/META_URL?}
205 #::logDebug("calling simple_row display");
208 <td$opt->{label_cell_extra}>
209 {BLABEL}{LABEL}{ELABEL}
211 <td$opt->{data_cell_extra}\{COLSPAN} nowrap>{WIDGET}{HELP_EITHER?} <a href="{HELP_URL}" title="{HELP}">$opt->{help_anchor}</a>{/HELP_EITHER?} {META_URL?}<a href="{META_URL}">$opt->{meta_anchor}</a>{/META_URL?}
221 <td colspan="2"$opt->{help_cell_extra}>
226 {/HELP?} <td colspan="2"$opt->{label_cell_extra}>
231 <td colspan="2"$opt->{widget_cell_extra}>
238 adjust_width => sub {
241 $opt->{break_template} ||= <<EOF;
243 <tr$opt->{break_row_extra}><td colspan="$span" $opt->{break_cell_extra}>{ROW}</td></tr>
246 <td$opt->{label_cell_extra}>
247 {BLABEL}{LABEL}{ELABEL}
249 <td$opt->{data_cell_extra}\{COLSPAN}>
250 <table cellspacing="0" cellmargin="0" width="100%">
252 <td$opt->{widget_cell_extra}>
255 <td$opt->{help_cell_extra}>{TKEY}{HELP?}{HELP}{/HELP?}{HELP:} {/HELP:}{HELP_URL?}<br><a href="{HELP_URL}">$opt->{help_anchor}</a>{/HELP_URL?}</td>
256 <td align="right">{META_STRING}</td>
267 $opt->{break_template} ||= <<EOF;
269 <tr$opt->{break_row_extra}><td colspan="$span" $opt->{break_cell_extra}>{ROW}</td></tr>
272 <td$opt->{label_cell_extra}>
273 {BLABEL}{LABEL}{ELABEL}
275 <td$opt->{data_cell_extra}\{COLSPAN}>
276 <table cellspacing="0" cellmargin="0" width="100%">
278 <td$opt->{widget_cell_extra}>
281 <td$opt->{help_cell_extra}>{TKEY}{HELP?}{HELP}{/HELP?}{HELP:} {/HELP:}{HELP_URL?}<br><a href="{HELP_URL}">$opt->{help_anchor}</a>{/HELP_URL?}</td>
282 <td align="right">{META_STRING}</td>
290 simple_help_below => sub {
293 <td$opt->{label_cell_extra}>
294 {BLABEL}{LABEL}{ELABEL}
296 <td$opt->{data_cell_extra}\{COLSPAN}>
298 {HELP_EITHER?}<br$Trailer>{/HELP_EITHER?}
299 {HELP}{HELP_URL?}<br$Trailer><a href="{HELP_URL}">$opt->{help_anchor}</a>{/HELP_URL?}
300 {META_URL?}<a href="{META_URL}">$opt->{meta_anchor}</a>{/META_URL?}
309 simple_icon_help => sub {
311 $opt->{help_icon} ||= '/icons/small/unknown.gif';
313 <td$opt->{label_cell_extra}>
314 {BLABEL}{LABEL}{ELABEL}
316 <td$opt->{data_cell_extra}\{COLSPAN}>
319 <td style="padding-left: 3px">
323 {HELP_EITHER?} <a href="{HELP_URL?}{HELP_URL}{/HELP_URL?}{HELP_URL:}javascript:alert('{HELP}'); void(0){/HELP_URL:}" title="{HELP}"><img src="$opt->{help_icon}" border="0"></a>{/HELP_EITHER?} {META_URL?}<a href="{META_URL}">$opt->{meta_anchor}</a>{/META_URL?}
346 $Display_type{$dt_map{$_}} = $Display_type{$_}
347 if $Display_type{$_};
348 $Display_options{$dt_map{$_}} = $Display_options{$_}
349 if $Display_options{$_};
354 <style type="text/css">
356 background-color: #CCCCCC;
362 background-color: #E6E6E6;;
364 font-family: Arial, Helvetica, sans-serif;
368 A.rhead:active,A.rhead:hover {
371 text-decoration: underline;
374 A.rhead:link,A.rhead:visited {
377 text-decoration:none;
381 background-color: #E6E6E6;
383 font-family: Arial, Helvetica, sans-serif;
390 background-color: #999999;
395 background-color: #999999;;
401 A.rmarq:active,A.rmarq:link,A.rmarq:visited {
405 text-decoration:none;
412 text-decoration: underline;
417 font-family: Arial, Helvetica, sans-serif;
423 background-color: #FFFFFF;
424 border: 1px solid #CCCCCC;
428 background-color: #EAF1FB;
430 font-family: Arial, Helvetica, sans-serif;
435 A.rowalt:hover,A.rowalt:hover,A.rownorm:active,A.rownorm:hover {
437 font-family: Arial, Helvetica, sans-serif;
439 text-decoration: underline;
442 A.rowalt:link,A.rowalt:visited,A.rownorm:link,A.rownorm:visited {
444 font-family: Arial, Helvetica, sans-serif;
446 text-decoration:none;
450 background-color: #FFFFFF;
452 font-family: Arial, Helvetica, sans-serif;
458 background-color: #FFFFFF;
460 font-family: Arial, Helvetica, sans-serif;
467 background-color: #CCCCCC;
471 background-color: #E6E6E6;
473 font-family: Arial, Helvetica, sans-serif;
479 background-color: #999999;
485 background-color: #FFFFFF;
487 font-family: Arial, Helvetica, sans-serif;
494 background-color: #808080;;
496 font-family: Verdana, Arial, Helvetica, sans-serif;
501 A.rtitle:active,A.rtitle:link,A.rtitle:visited {
505 text-decoration:none;
511 font-family: Verdana, Arial, Helvetica, sans-serif;
517 font-family: Verdana, Arial, Helvetica, sans-serif;
523 font-family: Verdana, Arial, Helvetica, sans-serif;
530 background-color: #FFFFFF;
535 background-color: #999999;
540 background-color: #EEEEEE;
541 border-left: 1px solid #999999;
547 border-bottom: 1px solid #CCCCCC;
548 border-right: 1px solid #CCCCCC;
549 border-top: 1px solid #CCCCCC;
569 font-family: Arial, Helvetica, sans-serif;
575 border-bottom: 1px solid #CCCCCC;
576 border-left: 1px solid #CCCCCC;
577 border-top: 1px solid #CCCCCC;
586 border-bottom: 1px solid #CCCCCC;
587 border-top: 1px solid #CCCCCC;
599 A:link.ctitle,A:visited.ctitle {
603 text-decoration: none
606 A:hover.ctitle,A:active.ctitle {
610 text-decoration: underline
620 vertical-align: center
629 return $val unless $val =~ /\[/;
630 $val =~ s/\[cgi\s+([^\[]+)\]/$CGI::values{$1}/ig;
631 $val =~ s/\[var\s+([^\[]+)\]/$::Variable->{$1}/ig;
632 $val =~ s/\[value\s+([^\[]+)\]/$::Values->{$1}/ig;
637 my ($type,$opt) = @_;
638 my $meta = meta_record("_widget::$type", $opt->{view}, $opt->{meta_table}, 1);
639 return $meta if $meta;
640 my $w = $Vend::Cfg->{CodeDef}{Widget};
641 if($w and $w->{Widget}{$type}) {
643 return undef unless $string = $w->{ExtraMeta}{$type};
644 return get_option_hash($string);
647 $w = $Global::CodeDef->{Widget};
648 if($w and $w->{Widget}{$type}) {
650 return undef unless $string = $w->{ExtraMeta}{$type};
651 return get_option_hash($string);
654 return $Vend::Form::ExtraMeta{$type};
658 my ($item, $view, $mdb, $extended_only, $overlay) = @_;
660 #::logDebug("meta_record: item=$item view=$view mdb=$mdb");
661 return undef unless $item;
665 $mtable = $mdb || $::Variable->{UI_META_TABLE} || 'mv_metadata';
666 #::logDebug("meta_record mtable=$mtable");
667 $mdb = database_exists_ref($mtable)
670 #::logDebug("meta_record has an item=$item and mdb=$mdb");
674 my $mkey = $view ? "${view}::$item" : $item;
676 if( ref ($mdb) eq 'HASH') {
680 $record = $mdb->row_hash($mkey);
681 #::logDebug("used mkey=$mkey to select record=$record");
684 $record ||= $mdb->row_hash($item) if $view and $mdb;
685 #::logDebug("meta_record record=$record");
687 return undef if ! $record;
689 # Get additional settings from extended field, which is a serialized
692 if(! $record->{extended}) {
693 return undef if $extended_only;
697 $hash = get_option_hash($record->{extended});
698 $record = {} if $extended_only;
699 if(ref $hash eq 'HASH') {
700 @$record{keys %$hash} = values %$hash;
704 return undef if $extended_only;
708 # Allow view settings to be placed in the extended area
709 if($view and $hash and $hash->{view}) {
710 my $view_hash = $record->{view}{$view};
712 and @$record{keys %$view_hash} = values %$view_hash;
715 # Allow overlay of certain settings
716 if($overlay and $record->{overlay}) {
717 my $ol_hash = $record->{overlay}{$overlay};
718 Vend::Util::copyref($ol_hash, $record) if $ol_hash;
720 #::logDebug("return meta_record=" . ::uneval($record) );
724 my $base_entry_value;
727 my ($table,$column,$key,$opt) = @_;
729 if( ref($opt) ne 'HASH' ) {
730 $opt = get_option_hash($opt);
733 my $template = $opt->{type} eq 'hidden' ? '' : $opt->{template};
735 if($opt->{override}) {
736 $opt->{value} = defined $opt->{default} ? $opt->{default} : '';
739 if(! defined $opt->{value} and $table and $column and length($key)) {
740 $opt->{value} = tag_data($table, $column, $key);
741 $opt->{already_got_data} = 1;
747 my $no_meta = $opt->{ui_no_meta_display};
750 ## No meta display wanted
751 last METALOOK if $no_meta;
752 ## No meta display possible
753 $table and $column or $opt->{meta}
756 ## We get a metarecord directly, though why it would be here
757 ## and not in options I don't know
758 if($opt->{meta} and ref($opt->{meta}) eq 'HASH') {
759 $record = $opt->{meta};
763 $mtab = $opt->{meta_table} || $::Variable->{UI_META_TABLE} || 'mv_metadata'
765 my $meta = Vend::Data::database_exists_ref($mtab)
767 ::logError("non-existent meta table: %s", $mtab);
772 my $view = $opt->{view} || $opt->{arbitrary};
774 ## This is intended to trigger on the first access
775 if($table eq $mtab and $column eq $meta->config('KEY')) {
776 if($view and $opt->{value} !~ /::.+::/) {
777 $base_entry_value = ($opt->{value} =~ /^([^:]+)::(\w+)$/)
782 $base_entry_value = $opt->{value} =~ /(\w+)::/
788 my (@tries) = "${table}::$column";
789 unshift @tries, "${table}::${column}::$key"
790 if length($key) and $opt->{specific};
792 my $sess = $Vend::Session->{mv_metadata} || {};
794 push @tries, { type => $opt->{type} }
795 if $opt->{type} || $opt->{label};
797 for my $metakey (@tries) {
798 ## In case we were passed a meta record
799 last if $record = $sess->{$metakey} and ref $record;
800 $record = meta_record($metakey, $view, $meta)
808 last METAMAKE if $no_meta;
813 ## Here we allow override with the display tag, even with views and
851 delete $record->{$_} if ! length($record->{$_});
852 next unless defined $opt->{$_};
853 $record->{$_} = $opt->{$_};
857 if($record->{type_empty} and length($opt->{value}) == 0) {
858 $record->{type} = $record->{type_empty};
861 $record->{type} ||= $opt->{default_widget};
864 $record->{name} ||= $column;
865 #::logDebug("record now=" . ::uneval($record));
867 if($record->{options} and $record->{options} =~ /^[\w:,]+$/) {
868 #::logDebug("checking options");
870 my $passed = $record->{options};
872 if($passed eq 'tables') {
873 my @tables = $Tag->list_databases();
874 $record->{passed} = join (',', "=--none--", @tables);
876 elsif($passed =~ /^(?:filters|\s*codedef:+(\w+)(:+(\w+))?\s*)$/i) {
877 my $tag = $1 || 'filters';
879 $record->{passed} = Vend::Util::codedef_options($tag, $mod);
881 elsif($passed =~ /^columns(::(\w*))?\s*$/) {
883 my $tname = $2 || $record->{db} || $table;
884 if ($total eq '::' and $base_entry_value) {
885 $tname = $base_entry_value;
887 $record->{passed} = join ",",
889 $Tag->db_columns($tname),
892 elsif($passed =~ /^keys(::(\w+))?\s*$/) {
893 my $tname = $2 || $record->{db} || $table;
894 $record->{passed} = join ",",
896 $Tag->list_keys($tname),
902 #::logDebug("checking for custom widget");
903 if ($record->{type} =~ s/^custom\s+//s) {
904 my $wid = lc $record->{type};
906 $record->{attribute} ||= $column;
907 $record->{table} ||= $mtab;
908 $record->{rows} ||= $record->{height};
909 $record->{cols} ||= $record->{width};
910 $record->{field} ||= 'options';
911 $record->{name} ||= $column;
913 $w = $Tag->$wid($record->{name}, $opt->{value}, $record, $opt);
916 ::logError("error using custom widget %s: %s", $wid, $@);
921 $opt->{restrict_allow} ||= $record->{restrict_allow};
922 #::logDebug("formatting prepend/append/lookup_query name=$opt->{name} restrict_allow=$opt->{restrict_allow}");
923 for(qw/append prepend lookup_query/) {
924 next unless $record->{$_};
925 if($opt->{restrict_allow}) {
926 $record->{$_} = $Tag->restrict({
928 enable => $opt->{restrict_allow},
929 disable => $opt->{restrict_deny},
930 body => $record->{$_},
934 $record->{$_} = expand_values($record->{$_});
936 $record->{$_} = Vend::Util::resolve_links($record->{$_});
937 $record->{$_} =~ s/_UI_VALUE_/$opt->{value}/g;
938 $record->{$_} =~ /_UI_URL_VALUE_/
940 my $tmp = $opt->{value};
941 $tmp =~ s/(\W)/sprintf '%%%02x', ord($1)/eg;
942 $record->{$_} =~ s/_UI_URL_VALUE_/$tmp/g;
944 $record->{$_} =~ s/_UI_TABLE_/$table/g;
945 $record->{$_} =~ s/_UI_COLUMN_/$column/g;
946 $record->{$_} =~ s/_UI_KEY_/$key/g;
950 my $r = get_option_hash(delete $opt->{opts});
951 for my $k (keys %$r) {
952 $record->{$k} = $r->{$k};
957 #::logDebug("overriding defaults");
958 #::logDebug("passed=$record->{passed}") if $record->{debug};
960 attribute => $column,
961 cols => $opt->{cols} || $record->{width},
962 passed => $record->{options},
963 rows => $opt->{rows} || $record->{height},
964 value => $opt->{value},
965 applylocale => $opt->{applylocale},
968 while( my ($k, $v) = each %things) {
969 next if length $record->{$k};
970 next unless defined $v;
974 #::logDebug("calling Vend::Form with record=" . ::uneval($record));
975 if($record->{save_defaults}) {
976 my $sd = $Vend::Session->{meta_defaults} ||= {};
977 $sd = $sd->{"${table}::$column"} ||= {};
978 while (my ($k,$v) = each %$record) {
979 next if ref($v) eq 'CODE';
984 $w = Vend::Form::display($record);
985 if($record->{filter}) {
986 $w .= qq{<input type="hidden" name="ui_filter:$record->{name}" value="};
987 $w .= $record->{filter};
993 my $text = $opt->{value};
994 my $iname = $opt->{name} || $column;
996 # Count lines for textarea
998 $count = $text =~ s/(\r\n|\r|\n)/$1/g;
1000 HTML::Entities::encode($text, $ESCAPE_CHARS::std);
1004 $count = 20 if $count > 20;
1006 <textarea name="$iname" cols="60" rows="$count">$text</textarea>
1009 elsif ($text =~ /^\d+$/) {
1010 my $cur = length($text);
1011 $size = $cur > 8 ? $cur + 1 : 8;
1017 <input name="$iname" size="$size" value="$text">
1021 my $array_return = wantarray;
1023 #::logDebug("widget=$w");
1025 # don't output label if widget is hidden form variable only
1026 # and not an array type
1027 undef $template if $w =~ /^\s*<input\s[^>]*type\s*=\W*hidden\b[^>]*>\s*$/i;
1029 return $w unless $template || $opt->{return_hash} || $array_return;
1031 if($template and $template !~ /\s/) {
1038 <table cellspacing="0" cellmargin="0"><tr><td>\$WIDGET\$</td><td>\$HELP\${HELP_URL}<br$Vend::Xtrailer><a href="\$HELP_URL\$">help</a>{/HELP_URL}</td></tr></table>
1044 $record->{label} ||= $column;
1048 HELP => $opt->{applylocale}
1049 ? errmsg($record->{help})
1051 META_URL => $opt->{meta_url},
1052 HELP_URL => $record->{help_url},
1053 LABEL => $opt->{applylocale}
1054 ? errmsg($record->{label})
1057 #::logDebug("passed meta_url=$opt->{meta_url}");
1058 $sub{HELP_EITHER} = $sub{HELP} || $sub{HELP_URL};
1060 if($opt->{return_hash}) {
1062 $sub{RECORD} = $record;
1065 elsif($array_return) {
1066 return ($w, $sub{LABEL}, $sub{HELP}, $record->{help_url});
1069 # Strip the {TAG} {/TAG} pairs if nothing there
1070 $template =~ s#{([A-Z_]+)}(.*?){/\1}#$sub{$1} ? $2: '' #ges;
1072 $sub{HELP_URL} ||= 'javascript:void(0)';
1073 $template =~ s/\$([A-Z_]+)\$/$sub{$1}/g;
1074 #::logDebug("substituted template is: $template");
1079 sub tabbed_display {
1080 my ($tit, $cont, $opt) = @_;
1085 $opt->{tab_bgcolor_template} ||= '#xxxxxx';
1086 $opt->{tab_height} ||= '20'; $opt->{tab_width} ||= '120';
1087 $opt->{panel_id} ||= 'mvpan';
1088 $opt->{tab_horiz_offset} ||= '10';
1089 $opt->{tab_vert_offset} ||= '8';
1091 $opt->{tab_style} ||= q{
1093 font-family: sans-serif;
1097 border-color:#999999;
1098 border-style:outset;
1099 border-bottom-style:none;
1101 if($opt->{ui_style}) {
1102 $opt->{panel_style} ||= q{
1108 $opt->{panel_style} ||= q{
1109 font-family: sans-serif;
1113 border-color:#999999;
1114 border-style:outset;
1116 $opt->{panel_height} ||= '600';
1117 $opt->{panel_width} ||= '800';
1118 $width_height = <<EOF;
1119 width: $opt->{panel_width}px;
1120 height: $opt->{panel_height}px;
1124 $opt->{panel_shade} ||= 'e';
1126 my @chars = reverse(0 .. 9, 'a' .. $opt->{panel_shade});
1127 my $id = $opt->{panel_id};
1128 my $vpf = $id . '_';
1129 my $num_panels = scalar(@$cont);
1130 my $tabs_per_row = int( $opt->{panel_width} / $opt->{tab_width}) || 1;
1131 my $num_rows = POSIX::ceil( $num_panels / $tabs_per_row);
1132 my $width = $opt->{panel_width};
1133 my $height = $opt->{tab_height} * $num_rows + $opt->{panel_height};
1137 if($opt->{ui_style}) {
1144 * ($opt->{tab_height} - $opt->{tab_vert_offset})
1145 + $opt->{tab_vert_offset};
1146 $int1 = $panel_y - 2;
1147 $int2 = $opt->{tab_height} * $num_rows;
1149 for(my $i = 0; $i < $num_panels; $i++) {
1150 my $c = $opt->{tab_bgcolor_template} || '#xxxxxx';
1151 $c =~ s/x/$chars[$i] || $opt->{panel_shade}/eg;
1154 my $cArray = qq{var ${vpf}colors = ['} . join("','", @colors) . qq{'];};
1155 #::logDebug("num rows=$num_rows");
1157 <script language="JavaScript">
1159 var ${vpf}panelID = "$id"
1160 var ${vpf}numDiv = $num_panels;
1161 var ${vpf}numRows = $num_rows;
1162 var ${vpf}tabsPerRow = $tabs_per_row;
1163 var ${vpf}numLocations = ${vpf}numRows * ${vpf}tabsPerRow
1164 var ${vpf}tabWidth = $opt->{tab_width}
1165 var ${vpf}tabHeight = $opt->{tab_height}
1166 var ${vpf}vOffset = $opt->{tab_vert_offset};
1167 var ${vpf}hOffset = $opt->{tab_horiz_offset};
1170 var ${vpf}uptabs = new Array;
1171 var ${vpf}dntabs = new Array;
1172 var ${vpf}divLocation = new Array(${vpf}numLocations)
1173 var ${vpf}newLocation = new Array(${vpf}numLocations)
1174 for(var i=0; i<${vpf}numLocations; ++i) {
1175 ${vpf}divLocation[i] = i
1176 ${vpf}newLocation[i] = i
1179 function ${vpf}getDiv(s,i) {
1181 if (document.layers) {
1182 div = document.layers[${vpf}panelID].layers[panelID+s+i]
1183 } else if (document.all && !document.getElementById) {
1184 div = document.all[${vpf}panelID+s+i]
1186 div = document.getElementById(${vpf}panelID+s+i)
1191 function ${vpf}setZIndex(div, zIndex) {
1192 if (document.layers) div.style = div;
1193 div.style.zIndex = zIndex
1196 function ${vpf}updatePosition(div, newPos) {
1197 ${vpf}newClip=${vpf}tabHeight*(Math.floor(newPos/${vpf}tabsPerRow)+1)
1198 if (document.layers) {
1200 div.clip.bottom=${vpf}newClip; // clip off bottom
1202 div.style.clip="rect(0 auto "+${vpf}newClip+" 0)"
1204 div.style.top = (${vpf}numRows-(Math.floor(newPos/${vpf}tabsPerRow) + 1)) * (${vpf}tabHeight-${vpf}vOffset)
1205 div.style.left = (newPos % ${vpf}tabsPerRow) * ${vpf}tabWidth + (${vpf}hOffset * (Math.floor(newPos / ${vpf}tabsPerRow)))
1208 function ${vpf}tripTab(n) {
1209 // n is the ID of the division that was clicked
1210 // firstTab is the location of the first tab in the selected row
1212 for(var i = 0; i < ${vpf}dntabs.length; i++) {
1213 el = document.getElementById('${vpf}td' + i);
1214 if(el != undefined) {
1215 el.innerHTML = ${vpf}dntabs[ i ];
1216 el.style.backgroundColor = '#B4B0AA';
1219 el = document.getElementById('${vpf}td' + n);
1220 el.innerHTML = ${vpf}uptabs[ n ];
1221 el.style.backgroundColor = '#D4D0C8';
1222 // Set tab positions & zIndex
1225 for(var i=0; i<${vpf}numDiv; ++i) {
1226 var loc = ${vpf}newLocation[i]
1227 var div = ${vpf}getDiv("panel",i)
1229 ${vpf}setZIndex(div, ${vpf}numLocations +1);
1230 div.style.display = 'block';
1231 div.style.backgroundColor = ${vpf}colors[0];
1234 ${vpf}setZIndex(div, ${vpf}numLocations - loc)
1235 div.style.display = 'none';
1236 div.style.backgroundColor = ${vpf}colors[j++];
1238 ${vpf}divLocation[i] = loc
1242 function ${vpf}selectTab(n) {
1243 // n is the ID of the division that was clicked
1244 // firstTab is the location of the first tab in the selected row
1245 var firstTab = Math.floor(${vpf}divLocation[n] / ${vpf}tabsPerRow) * ${vpf}tabsPerRow
1246 // newLoc is its new location
1247 for(var i=0; i<${vpf}numDiv; ++i) {
1248 // loc is the current location of the tab
1249 var loc = ${vpf}divLocation[i]
1250 // If in the selected row
1251 if(loc >= firstTab && loc < (firstTab + ${vpf}tabsPerRow)) ${vpf}newLocation[i] = (loc - firstTab)
1252 else if(loc < ${vpf}tabsPerRow) ${vpf}newLocation[i] = firstTab+(loc % ${vpf}tabsPerRow)
1253 else ${vpf}newLocation[i] = loc
1255 // Set tab positions & zIndex
1258 for(var i=0; i<${vpf}numDiv; ++i) {
1259 var loc = ${vpf}newLocation[i]
1260 var div = ${vpf}getDiv("panel",i)
1261 var tdiv = ${vpf}getDiv("tab",i)
1263 ${vpf}setZIndex(div, ${vpf}numLocations +1);
1264 div.style.display = 'block';
1265 tdiv.style.backgroundColor = ${vpf}colors[0];
1266 div.style.backgroundColor = ${vpf}colors[0];
1269 ${vpf}setZIndex(div, ${vpf}numLocations - loc)
1270 div.style.display = 'none';
1271 tdiv.style.backgroundColor = ${vpf}colors[j];
1272 div.style.backgroundColor = ${vpf}colors[j++];
1274 ${vpf}divLocation[i] = loc
1275 ${vpf}updatePosition(tdiv, loc)
1276 if(i == n) ${vpf}setZIndex(tdiv, ${vpf}numLocations +1)
1277 else ${vpf}setZIndex(tdiv,${vpf}numLocations - loc)
1283 <style type="text/css">
1287 width:$opt->{tab_width}px;
1296 width: $opt->{panel_width}px;
1297 height: $opt->{panel_height}px;
1308 my $ibase = $Tag->image({
1311 secure => $Vend::admin && $::Variable->{UI_SECURE},
1313 $opt->{clear_image} ||= 'bg.gif';
1314 my $clear = "$ibase/$opt->{clear_image}";
1317 for(my $i = 0; $i < $num_panels; $i++) {
1318 my $zi = $num_panels - $i;
1320 my $left = (($i % $tabs_per_row)
1322 + ($opt->{tab_horiz_offset}
1323 * (int($i / $tabs_per_row))));
1324 my $top = ( $num_rows - (int($i / $tabs_per_row) + 1))
1325 - ($opt->{tab_height} - $opt->{tab_vert_offset});
1326 my $cliprect = $opt->{tab_height} * (int($i / $tabs_per_row) + 1);
1328 <div id="${id}panel$i"
1331 background-color: $colors[$i];
1334 $opt->{panel_prepend}
1336 $opt->{panel_append}
1339 if($opt->{ui_style}) {
1341 <td class="subtabdown" id="${vpf}td$i">
1344 $dntabs[$i] = <<EOF;
1345 <table width="100%" border="0" cellspacing="0" cellpadding="0">
1347 <td class="subtabdownleft"><a href="javascript:${vpf}tripTab($i,1)"><img src="$clear" width="16" height="16" border="0"></a></td>
1348 <td nowrap class="subtabdownfill"><a href="javascript:${vpf}tripTab($i,1)" class="subtablink">$tit->[$i]</a></td>
1349 <td class="subtabdownright"><a href="javascript:${vpf}tripTab($i,1)"><img src="$clear" width="16" height="16" border="0"></a></td>
1352 <td colspan="3" class="darkshade"><img src="$clear" height="1"></td>
1355 <td colspan="3" class="lightshade"><img src="$clear" height="1"></td>
1362 $uptabs[$i] = <<EOF;
1363 <table width="100%" border="0" cellspacing="0" cellpadding="0">
1365 <td class="subtableft"><a href="javascript:${vpf}tripTab($i,1)"><img src="$clear" width="16" height="16" border="0"></a></td>
1366 <td nowrap class="subtabfill"><a href="javascript:${vpf}tripTab($i,1)" class="subtablink">$tit->[$i]</a></td>
1367 <td class="subtabright"><a href="javascript:${vpf}tripTab($i,1)"><img src="$clear" width="16" height="16" border="0"></a></td>
1370 <td colspan="3" class="subtabfilllwr"><img src="$clear" height="1"></td>
1380 onclick="${vpf}selectTab($i)"
1385 background-color: $colors[$i];
1390 clip:rect(0 auto $cliprect 0);
1398 my $start_index = $opt->{start_at_index} || 0;
1401 $Tag->output_to('third_tabs', { name => 'third_tabs' }, $s2);
1406 left: 0; top: 0; width: 100%; height: 100%;
1415 for(my $i = 0; $i < @dntabs; $i++) {
1416 $out .= "${vpf}uptabs[ $i ] = ";
1417 $out .= $Tag->jsq($uptabs[$i]);
1419 $out .= "${vpf}dntabs[ $i ] = ";
1420 $out .= $Tag->jsq($dntabs[$i]);
1424 ${vpf}tripTab($start_index);
1431 ${vpf}selectTab($start_index);
1452 return 'TABLE_STD' . ++$tcount_all;
1456 my ($tag, $string) = @_;
1457 #::logDebug("calling add_exclude tag='$tag' string='$string'");
1458 return unless $string =~ /\S/;
1459 $exclude{$tag} ||= ' ';
1460 $exclude{$tag} .= "$string ";
1466 my $exclude = shift @_;
1469 $tag = "COLUMN_$tag";
1471 #::logDebug("$tag content length=" . length($value));
1473 if(exists $outhash{$tag}) {
1475 $col =~ s/^COLUMN_//;
1476 my $msg = errmsg("Column '%s' defined twice, skipping second.", $col);
1477 Vend::Tags->warnings($msg);
1481 $outhash{$tag} = $value;
1484 $alias{$tag} ||= [];
1485 push @{$alias{$tag}}, @others;
1488 my $ctl = $controls[$ctl_index] ||= [];
1489 add_exclude($tag, $exclude) if $exclude;
1491 return unless length($value);
1499 $alias{$tag} ||= [];
1500 push @{$alias{$tag}}, @_;
1507 my $exclude = shift @_;
1510 die "duplicate tag settor $tag" if exists $outhash{$tag};
1511 $outhash{$tag} = $value;
1513 #::logDebug("$tag exclude=$exclude, content length=" . length($value));
1516 $alias{$tag} ||= [];
1517 push @{$alias{$tag}}, @others;
1520 add_exclude($tag, $exclude) if $exclude =~ /\S/;
1522 return unless length($value);
1526 sub resolve_exclude {
1528 while(my ($k, $v) = each %exclude) {
1530 my @things = grep /\S/ && ! $seen{$_}++, split /\s+/, $v;
1531 #::logDebug("examining $k for $v");
1532 for my $thing (@things) {
1533 if($thing =~ s/^[^A-Z]//) {
1534 #::logDebug("examining $v for $thing!=$exc->{$thing}");
1535 $outhash{$k} = '' unless $exc->{$thing};
1538 #::logDebug("examining $v for $thing=$exc->{$thing}");
1539 $outhash{$k} = '' if $exc->{$thing};
1546 undef $base_entry_value;
1549 Vend::Interpolate::init_calc() if ! $Vend::Calc_initialized;
1560 my %o_default_length = (
1561 border_cell_class => 'cborder',
1562 widget_cell_class => 'cwidget',
1563 label_cell_class => 'clabel',
1564 data_cell_class => 'cdata',
1565 help_cell_class => 'chelp',
1566 break_cell_class_first => 'cbreakfirst',
1567 break_cell_class => 'cbreak',
1568 spacer_row_class => 'rspacer',
1569 break_row_class => 'rbreak',
1570 title_row_class => 'rmarq',
1571 data_row_class => 'rnorm',
1572 ok_button_style => 'font-weight: bold; width: 40px; text-align: center',
1575 my %o_default_var = (qw/
1576 color_fail UI_CONTRAST
1577 color_success UI_C_SUCCESS
1580 my %o_default_defined = (
1581 mv_update_empty => 1,
1582 restrict_allow => 'page area var',
1587 wizard_next => 'return',
1588 help_anchor => 'help',
1589 wizard_cancel => 'back',
1591 color_success => '#00FF00',
1592 color_fail => '#FF0000',
1595 clear_image => 'bg.gif',
1596 table_width => '100%',
1599 # Build maps for ui_te_* option pass
1630 push @hmap, [ qr/ui_te_$_:/, $_ ];
1633 my %ignore_cgi = qw/
1648 if($CGI::values{save_cgi}) {
1649 @k = split /\0/, $CGI::values{save_cgi};
1652 @k = grep ! $ignore_cgi{$_}, keys %CGI::values;
1655 # Can be an array because of produce_hidden
1656 $ref->{save_cgi} = \@k;
1659 $ref->{$_} = $CGI::values{$_};
1664 sub produce_hidden {
1665 my ($key, $val) = @_;
1666 return unless length $val;
1667 my @p; # pieces of var
1669 if(ref($val) eq 'ARRAY') {
1673 @p = split /\0/, $val;
1677 push @o, qq{<input type="hidden" name="$key" value="$_">\n};
1682 sub resolve_options {
1683 my ($opt, $CGI, $data) = @_;
1685 # This may be passed by the caller, but is normally from the form
1687 $CGI ||= \%CGI::values;
1689 my $table = $opt->{mv_data_table};
1690 my $key = $opt->{item_id};
1692 $table = $CGI->{mv_data_table}
1693 if ! $table and $opt->{cgi} and $CGI->{mv_data_table};
1695 $opt->{table} = $opt->{mv_data_table} = $table;
1697 # First we see if something has created a big options munge
1699 if($opt->{all_opts}) {
1700 #::logDebug("all_opts being brought in...=" . ::uneval($opt->{all_opts}));
1701 if(ref($opt->{all_opts}) eq 'HASH') {
1702 #::logDebug("all_opts being brought in...");
1703 my $o = $opt->{all_opts};
1705 $opt->{$_} = $o->{$_};
1709 my $o = meta_record($opt->{all_opts});
1710 #::logDebug("all_opts being brought in text, o=$o");
1713 $opt->{$_} = $o->{$_};
1717 logError("%s: improper option %s, must be %s, was %s.",
1721 ref $opt->{all_opts},
1725 #::logDebug("options now=" . ::uneval($opt));
1758 include_form_interpolate
1806 tab_bgcolor_template
1822 ui_break_before_label
1844 next if ! defined $CGI->{$_};
1845 $opt->{$_} = $CGI->{$_};
1847 my @cgi = keys %{$CGI};
1848 foreach my $row (@hmap) {
1849 my @keys = grep $_ =~ $row->[0], @cgi;
1852 and $opt->{$row->[1]}{$1} = $CGI->{$_};
1857 #::logDebug("no_meta_display=$opt->{ui_no_meta_display}");
1859 if($opt->{no_table_meta} || $opt->{ui_no_meta_display}) {
1863 $tmeta = meta_record($table, $opt->{ui_meta_view}) || {};
1866 $opt->{view_from} ||= $tmeta->{view_from};
1869 if($opt->{no_base_meta} || $opt->{ui_no_meta_display}) {
1873 $baseopt = meta_record('table-editor') || {};
1874 delete $baseopt->{extended};
1877 if( ! $opt->{ui_meta_view}
1878 and $opt->{view_from}
1880 and ! $opt->{ui_no_meta_display}
1881 and $opt->{ui_meta_view} = $data->{$opt->{view_from}}
1884 $tmeta = meta_record($table, $opt->{ui_meta_view}) || {};
1887 # This section checks the passed options and converts them from
1888 # strings to refs if necessary
1923 next if ref $opt->{$_};
1924 ($opt->{$_} = {}, next) if ! $opt->{$_};
1926 my $string = $opt->{$_};
1927 $string =~ s/^\s+//gm;
1928 $string =~ s/\s+$//gm;
1929 while($string =~ m/^(.+?)=\s*(.+)/mg) {
1936 for(grep length($baseopt->{$_}), @mapdirect) {
1937 #::logDebug("checking baseopt->{$_}, baseopt=$baseopt->{$_} tmeta=$tmeta->{$_}");
1938 $tmeta->{$_} = $baseopt->{$_} unless length $tmeta->{$_};
1941 for(grep defined $tmeta->{$_}, @mapdirect) {
1942 #::logDebug("checking tmeta->{$_}, tmeta=$tmeta->{$_} opt=$opt->{$_}");
1943 #::logDebug("opt->{$_} is " . (defined $opt->{$_} ? 'defined' : 'undefined'));
1944 $opt->{$_} = $tmeta->{$_} unless defined $opt->{$_};
1956 next if ! defined $CGI->{$_};
1957 $opt->{$_} = $CGI->{$_};
1961 if($opt->{wizard}) {
1962 $opt->{noexport} = 1;
1963 $opt->{next_text} = 'Next -->' unless $opt->{next_text};
1964 $opt->{back_text} = '<-- Back' unless $opt->{back_text};
1967 $opt->{next_text} = "Ok" unless $opt->{next_text};
1969 $opt->{cancel_text} = 'Cancel' unless $opt->{cancel_text};
1971 for(qw/ next_text cancel_text back_text/ ) {
1972 $opt->{$_} = errmsg($opt->{$_});
1975 if($opt->{wizard} || $opt->{notable} and ! $opt->{table}) {
1976 $opt->{table} = 'mv_null';
1977 $Vend::Database{mv_null} =
1981 [ 'code', 'value' ],
1982 [ 'code' => 0, 'value' => 1 ],
1985 ], 'Vend::Table::InMemory';
1988 # resolve form defaults
1990 while( my ($k, $v) = each %o_default_var) {
1991 $opt->{$k} ||= $::Variable->{$v};
1994 while( my ($k, $v) = each %o_default_length) {
1995 $opt->{$k} = $v if ! length($opt->{$k});
1998 while( my ($k, $v) = each %o_default_defined) {
1999 $opt->{$k} = $v if ! defined($opt->{$k});
2002 while( my ($k, $v) = each %o_default) {
2006 if (! $opt->{inner_table_width}) {
2007 if ($opt->{table_width} =~ /^\d+$/) {
2008 $opt->{inner_table_width} = $opt->{table_width} - 2;
2010 elsif($opt->{table_width} =~ /\%/) {
2011 $opt->{inner_table_width} = '100%';
2014 $opt->{inner_table_width} = $opt->{table_width};
2018 if (! $opt->{inner_table_height}) {
2019 if ($opt->{table_height} =~ /^\d+$/) {
2020 $opt->{inner_table_height} = $opt->{table_height} - 2;
2022 elsif($opt->{table_height} =~ /\%/) {
2023 $opt->{inner_table_height} = '100%';
2026 $opt->{inner_table_height} = $opt->{table_height};
2030 if(! $opt->{left_width}) {
2031 if($opt->{table_width} eq '100%') {
2032 $opt->{left_width} = 150;
2035 $opt->{left_width} = '30%';
2039 if(my $dt = $opt->{display_type}) {
2040 my $sub = $Display_options{$dt};
2041 $sub and ref($sub) eq 'CODE' and $sub->($opt);
2044 # init the row styles
2045 foreach my $rtype (qw/data break combo spacer title/) {
2046 my $mainp = $rtype . '_row_extra';
2048 for my $ptype (qw/class style align valign width/) {
2049 my $parm = $rtype . '_row_' . $ptype;
2050 $opt->{$parm} ||= $tmeta->{$parm};
2051 if(defined $opt->{$parm}) {
2052 $thing .= qq{ $ptype="$opt->{$parm}"};
2055 $opt->{$mainp} ||= $tmeta->{$mainp};
2056 if($opt->{$mainp}) {
2057 $thing .= " " . $opt->{$mainp};
2059 $opt->{$mainp} = $thing;
2062 # Init the cell styles
2064 for my $ctype (qw/label data widget help break/) {
2065 my $mainp = $ctype . '_cell_extra';
2067 for my $ptype (qw/class style align valign width/) {
2068 my $parm = $ctype . '_cell_' . $ptype;
2069 $opt->{$parm} ||= $tmeta->{$parm};
2070 if(defined $opt->{$parm}) {
2071 $thing .= qq{ $ptype="$opt->{$parm}"};
2074 $opt->{$mainp} ||= $tmeta->{$mainp};
2075 if($opt->{$mainp}) {
2076 $thing .= " " . $opt->{$mainp};
2078 $opt->{$mainp} = $thing;
2081 # Init the button styles
2083 for my $ctype (qw/ok next back cancel delete reset/) {
2084 my $mainp = $ctype . '_button_extra';
2086 for my $ptype (qw/class style/) {
2087 my $parm = $ctype . '_button_' . $ptype;
2088 $opt->{$parm} ||= $tmeta->{$parm};
2089 if(defined $opt->{$parm}) {
2090 $thing .= qq{ $ptype="$opt->{$parm}"};
2093 $opt->{$mainp} ||= $tmeta->{$mainp};
2094 if($opt->{$mainp}) {
2095 $thing .= " " . $opt->{$mainp};
2097 $opt->{$mainp} = $thing;
2100 $opt->{ui_data_fields} ||= $opt->{ui_wizard_fields};
2102 ###############################################################
2103 # Get the field display information including breaks and labels
2104 ###############################################################
2105 if( ! $opt->{ui_data_fields} and ! $opt->{ui_data_fields_all}) {
2106 $opt->{ui_data_fields} = $tmeta->{ui_data_fields} || $tmeta->{options};
2108 #::logDebug("fields were=$opt->{ui_data_fields}");
2110 $opt->{ui_data_fields} =~ s/\r\n/\n/g;
2111 $opt->{ui_data_fields} =~ s/\r/\n/g;
2112 $opt->{ui_data_fields} =~ s/^[ \t]+//mg;
2113 $opt->{ui_data_fields} =~ s/[ \t]+$//mg;
2115 if($opt->{ui_data_fields} =~ /\n\n/) {
2118 my $fstring = "\n\n$opt->{ui_data_fields}";
2119 while ($fstring =~ s/\n+(?:\n[ \t]*=(.*?)(\*?))?\n+[ \t]*(\w[:.\w]+)/\n$3/) {
2121 $opt->{start_at} ||= $3 if $2;
2122 push @break_labels, "$3=$1" if $1;
2124 $opt->{ui_break_before} = join(" ", @breaks)
2125 if ! $opt->{ui_break_before};
2126 $opt->{ui_break_before_label} = join(",", @break_labels)
2127 if ! $opt->{ui_break_before_label};
2128 while($fstring =~ s/\n(.*)[ \t]*\*/\n$1/) {
2129 $opt->{focus_at} = $1;
2131 $opt->{ui_data_fields} = $fstring;
2134 $opt->{ui_data_fields} ||= $opt->{mv_data_fields};
2135 $opt->{ui_data_fields} =~ s/^[\s,\0]+//;
2136 $opt->{ui_data_fields} =~ s/[\s,\0]+$//;
2137 #::logDebug("fields now=$opt->{ui_data_fields}");
2139 #### This code is also in main editor routine, change there too!
2140 my $cells_per_span = $opt->{cell_span} || 2;
2143 ## Visual field layout
2144 if($opt->{ui_data_fields} =~ /[\w:.]+[ \t,]+\w+.*\n\w+/) {
2145 my $cs = $opt->{colspan} ||= {};
2146 my @things = split /\n/, $opt->{ui_data_fields};
2150 my @cols = split /[\s\0,]+/, $_;
2151 my $cnt = scalar(@cols);
2152 $max = $cnt if $cnt > $max;
2155 $opt->{across} = $max;
2157 my $cnt = scalar(@$_);
2159 my $name = $_->[-1];
2160 $cs->{$name} = (($max - $cnt) * $cells_per_span) + 1;
2165 #### This code is also in main editor routine, change there too!
2166 my $rowdiv = $opt->{across} || 1;
2168 my $span = $rowdiv * $cells_per_span;
2171 # Make standard fixed rows
2172 $opt->{spacer_row} = <<EOF;
2173 <tr$opt->{spacer_row_extra}>
2174 <td colspan="$span" $opt->{spacer_row_extra}><img src="$opt->{clear_image}" width="1" height="$opt->{spacer_height}" alt="x"></td>
2178 $opt->{mv_nextpage} = $Global::Variable->{MV_PAGE}
2179 if ! $opt->{mv_nextpage};
2181 $opt->{form_extra} =~ s/^\s*/ /
2182 if $opt->{form_extra};
2183 $opt->{form_extra} ||= '';
2185 $opt->{form_extra} .= qq{ name="$opt->{form_name}"}
2186 if $opt->{form_name};
2188 $opt->{form_extra} .= qq{ target="$opt->{form_target}"}
2189 if $opt->{form_target};
2191 $opt->{enctype} = $opt->{file_upload} ? ' enctype="multipart/form-data"' : '';
2194 # UserTag table-editor Order mv_data_table item_id
2195 # UserTag table-editor addAttr
2196 # UserTag table-editor AttrAlias clone ui_clone_id
2197 # UserTag table-editor AttrAlias table mv_data_table
2198 # UserTag table-editor AttrAlias fields ui_data_fields
2199 # UserTag table-editor AttrAlias mv_data_fields ui_data_fields
2200 # UserTag table-editor AttrAlias key item_id
2201 # UserTag table-editor AttrAlias view ui_meta_view
2202 # UserTag table-editor AttrAlias profile ui_profile
2203 # UserTag table-editor AttrAlias email_fields ui_display_only
2204 # UserTag table-editor hasEndTag
2205 # UserTag table-editor MapRoutine Vend::Table::Editor::editor
2208 my ($table, $key, $opt, $overall_template) = @_;
2209 show_times("begin table editor call item_id=$key") if $Global::ShowTimes;
2211 #::logDebug("overall_template=$overall_template\nin=$opt->{overall_template}");
2219 my $hidden = $opt->{hidden} ||= {};
2221 #::logDebug("key at beginning: $key");
2222 $opt->{mv_data_table} = $table if $table;
2223 $opt->{table} = $opt->{mv_data_table};
2224 $opt->{ui_meta_view} ||= $CGI->{ui_meta_view} if $opt->{cgi};
2226 $key ||= $opt->{item_id};
2229 $key ||= $CGI->{item_id};
2230 unless($opt->{ui_multi_key} = $CGI->{ui_multi_key}) {
2231 $opt->{item_id_left} ||= $CGI::values{item_id_left};
2232 $opt->{ui_sequence_edit} ||= $CGI::values{ui_sequence_edit};
2236 if($opt->{ui_sequence_edit} and ! $opt->{ui_multi_key}) {
2237 delete $opt->{ui_sequence_edit};
2238 my $left = delete $opt->{item_id_left};
2241 #::logDebug("No key, getting from $left");
2242 if($left =~ s/(.*?)[\0,]// ) {
2243 $key = $opt->{item_id} = $1;
2244 $hidden->{item_id_left} = $left;
2245 $hidden->{ui_sequence_edit} = 1;
2248 $key = $opt->{item_id} = $left;
2250 #::logDebug("No key, left now $left");
2253 #::logDebug("Key, leaving left $left");
2254 $hidden->{item_id_left} = $left;
2255 $hidden->{ui_sequence_edit} = 1;
2259 $opt->{item_id} = $key;
2261 $pass_return_to = save_cgi() if $hidden->{ui_sequence_edit};
2268 ## Try and sneak a peek at the data so we can determine views and
2269 ## maybe some other stuff -- we definitely need table/key or a
2271 unless($opt->{notable}) {
2273 my $tab = $table || $opt->{mv_data_table} || $CGI->{mv_data_table};
2274 my $key = $opt->{item_id} || $CGI->{item_id};
2275 $db = database_exists_ref($tab);
2278 $multikey = $db->config('COMPOSITE_KEY');
2279 if($multikey and $key !~ /\0/) {
2280 $key =~ s/-_NULL_-/\0/g;
2282 if($opt->{ui_clone_id} and $db->record_exists($opt->{ui_clone_id})) {
2283 $data = $db->row_hash($opt->{ui_clone_id});
2285 elsif ($key and $db->record_exists($key)) {
2286 $data = $db->row_hash($key);
2290 if(! $exists and $multikey) {
2293 my @inits = split /\0/, $key;
2294 for(@{$db->config('_Key_columns')}) {
2295 $data->{$_} = shift @inits;
2302 my $regin = $opt->{all_opts} ? 1 : 0;
2304 resolve_options($opt, undef, $data);
2306 $Trailer = $opt->{xhtml} ? '/' : '';
2308 ## Must reset these in case they get set from all_opts.
2309 $hidden = $opt->{hidden};
2311 $overall_template = $opt->{overall_template}
2312 if $opt->{overall_template};
2314 $table = $opt->{table};
2315 $key = $opt->{item_id};
2316 if($opt->{save_meta}) {
2317 $::Scratch->{$opt->{save_meta}} = uneval($opt);
2319 #::logDebug("key after resolve_options: $key");
2321 #::logDebug("cell_span=$opt->{cell_span}");
2322 #### This code is also in resolve_options routine, change there too!
2323 my $rowdiv = $opt->{across} || 1;
2324 my $cells_per_span = $opt->{cell_span} || 2;
2326 my $span = $rowdiv * $cells_per_span;
2329 my $oddspan = $span - 1;
2330 my $def = $opt->{default_ref} || $::Values;
2332 my $append = $opt->{append};
2333 my $check = $opt->{check};
2334 my $class = $opt->{class} || {};
2335 my $database = $opt->{database};
2336 my $default = $opt->{default};
2337 my $disabled = $opt->{disabled};
2338 my $error = $opt->{error};
2339 my $extra = $opt->{extra};
2340 my $field = $opt->{field};
2341 my $filter = $opt->{filter};
2342 my $form = $opt->{form};
2343 my $height = $opt->{height};
2344 my $help = $opt->{help};
2345 my $help_url = $opt->{help_url};
2346 my $label = $opt->{label};
2347 my $wid_href = $opt->{wid_href};
2348 my $lookup = $opt->{lookup};
2349 my $lookup_query = $opt->{lookup_query};
2350 my $meta = $opt->{meta};
2351 my $js_check = $opt->{js_check};
2352 my $maxlength = $opt->{maxlength};
2353 my $opts = $opt->{opts};
2354 my $options = $opt->{options};
2355 my $outboard = $opt->{outboard};
2356 my $override = $opt->{override};
2357 my $passed = $opt->{passed};
2358 my $pre_filter = $opt->{pre_filter};
2359 my $prepend = $opt->{prepend};
2360 my $template = $opt->{template};
2361 my $widget = $opt->{widget};
2362 my $width = $opt->{width};
2363 my $colspan = $opt->{colspan} || {};
2365 my $blabel = $opt->{blabel};
2366 my $elabel = $opt->{elabel};
2368 my $hidden_all = $opt->{hidden_all} ||= {};
2369 #::logDebug("hidden_all=" . ::uneval($hidden_all));
2374 if($pass_return_to) {
2375 delete $::Scratch->{$opt->{next_text}};
2377 elsif (! $opt->{wizard} and ! $opt->{nosave}) {
2378 $ntext = $Tag->return_to('click', 1);
2379 $ctext = $ntext . "\nmv_todo=back";
2382 if($opt->{action_click}) {
2384 mv_todo=$opt->{wizard_next}
2385 ui_wizard_action=Next
2386 mv_click=$opt->{action_click}
2391 mv_todo=$opt->{wizard_next}
2392 ui_wizard_action=Next
2393 mv_click=ui_override_next
2396 $::Scratch->{$opt->{next_text}} = $ntext;
2398 my $hidgo = $opt->{mv_cancelpage} || $opt->{hidden}{ui_return_to} || $CGI->{return_to};
2399 $hidgo =~ s/\0.*//s;
2400 $ctext = $::Scratch->{$opt->{cancel_text}} = <<EOF;
2402 ui_wizard_action=Cancel
2404 mv_todo=$opt->{wizard_cancel}
2406 if($opt->{mv_prevpage}) {
2407 $btext = $::Scratch->{$opt->{back_text}} = <<EOF;
2409 ui_wizard_action=Back
2410 mv_nextpage=$opt->{mv_prevpage}
2411 mv_todo=$opt->{wizard_next}
2415 delete $opt->{back_text};
2419 for(qw/next_text back_text cancel_text/) {
2420 $opt->{"orig_$_"} = $opt->{$_};
2423 $::Scratch->{$opt->{next_text}} = $ntext if $ntext;
2424 $::Scratch->{$opt->{cancel_text}} = $ctext if $ctext;
2425 $::Scratch->{$opt->{back_text}} = $btext if $btext;
2427 $opt->{next_text} = HTML::Entities::encode($opt->{next_text}, $ESCAPE_CHARS::std);
2428 $opt->{back_text} = HTML::Entities::encode($opt->{back_text}, $ESCAPE_CHARS::std);
2429 $opt->{cancel_text} = HTML::Entities::encode($opt->{cancel_text}, $ESCAPE_CHARS::std);
2431 $::Scratch->{$opt->{next_text}} = $ntext if $ntext;
2432 $::Scratch->{$opt->{cancel_text}} = $ctext if $ctext;
2433 $::Scratch->{$opt->{back_text}} = $btext if $btext;
2435 undef $opt->{tabbed} if $::Scratch->{ui_old_browser};
2436 undef $opt->{auto_secure} if $opt->{cgi};
2438 ### Build the error checking
2439 my $error_show_var = 1;
2441 if($opt->{ui_profile} or $check) {
2442 $Tag->error( { all => 1 } )
2443 unless $CGI->{mv_form_profile} or $opt->{keep_errors};
2444 my $prof = $opt->{ui_profile} || "&update=yes\n";
2445 if ($prof =~ s/^\*//) {
2446 # special notation ui_profile="*whatever" means
2447 # use automatic checklist-related profile
2449 $prof = $::Scratch->{"profile_$name"} || "&update=yes\n";
2451 $prof =~ s/^\s*(\w+)[\s=]+required\b/$1=mandatory/mg;
2452 for (grep /\S/, split /\n/, $prof) {
2453 if (/^\s*(\w+)\s*=(.+)$/) {
2454 my $k = $1; my $v = $2;
2458 $error_show_var = 0 if $v =~ /\S /;
2461 $prof = '&calc delete $Values->{step_'
2466 $opt->{ui_profile_success} = "&set=step_$name 1";
2469 my $success = $opt->{ui_profile_success};
2470 # make sure profile so far ends with a newline so we can add more
2471 $prof .= "\n" unless $prof =~ /\n\s*\z/;
2473 while ( my($k, $v) = each %$check ) {
2474 next unless length $v;
2480 $v =~ s/^required\b/mandatory/mg;
2481 unless ($v =~ /^\&/m) {
2482 $error_show_var = 0 if $v =~ /\S /;
2484 $v =~ s/\n/\n&and\n/g;
2490 for (@_ = grep /\S/, split /[\s,]+/, $check) {
2492 $prof .= "$_=mandatory\n";
2496 ## Enable individual widget checks
2497 $::Scratch->{mv_individual_profile} = 1;
2499 ## Call the profile in the form
2500 $opt->{hidden}{mv_form_profile} = 'ui_profile';
2501 my $fail = $opt->{mv_failpage} || $Global::Variable->{MV_PAGE};
2503 # watch out for early interpolation here!
2504 $::Scratch->{ui_profile} = <<EOF;
2506 #Debug("cancel='$opt->{orig_cancel_text}' back='$opt->{orig_back_text}' click=\$CGI->{mv_click}");
2507 my \@clicks = split /\\0/, \$CGI->{mv_click};
2509 for( qq{$opt->{orig_cancel_text}}, qq{$opt->{orig_back_text}}) {
2510 #Debug("compare is '\$_'");
2514 #Debug("click is '\$_'");
2515 return if \$_ eq \$cancel;
2518 # the following should already be interpolated by the table-editor tag
2519 # before going into scratch ui_profile
2525 mv_form_profile=mandatory
2526 &set=mv_todo $opt->{action}
2530 $opt->{blabel} = '<span style="font-weight: normal">';
2531 $opt->{elabel} = '</span>';
2532 $mlabel = ($opt->{message_label} || ' '
2533 . errmsg('<b>Bold</b> fields are required'));
2534 $have_errors = $Tag->error( {
2536 show_var => $error_show_var,
2538 joiner => "<br$Vend::Xtrailer>",
2541 if($opt->{all_errors} and $have_errors) {
2542 my $title = $opt->{all_errors_title} || errmsg('Errors');
2543 my $style = $opt->{all_errors_style} || "color: $opt->{color_fail}";
2545 title => $opt->{all_errors_title} || errmsg('Errors'),
2546 style => $opt->{all_errors_style} || "color: $opt->{color_fail}",
2547 errors => $have_errors,
2549 my $tpl = $opt->{all_errors_template} || <<EOF;
2551 <blockquote style="{STYLE}">{ERRORS}</blockquote>
2554 $mlabel .= tag_attr_list($tpl, \%hash, 'uc');
2558 ### end build of error checking
2562 $::Scratch->{ui_error} .= "<BR>\n" if $::Scratch->{ui_error};
2563 $::Scratch->{ui_error} .= ::errmsg(@_);
2567 unless($opt->{notable}) {
2569 $db = database_exists_ref($table)
2570 or return $die->("table-editor: bad table '%s'", $table);
2573 $opt->{ui_data_fields} =~ s/[,\0\s]+/ /g;
2575 if($opt->{ui_wizard_fields}) {
2576 $opt->{ui_display_only} = $opt->{ui_data_fields};
2579 if(! $opt->{ui_data_fields}) {
2580 if( $opt->{notable}) {
2581 ::logError("table_editor: no place to get fields!");
2585 $opt->{ui_data_fields} = join " ", $db->columns();
2590 if($opt->{notable}) {
2591 $keycol = $opt->{ui_data_key_name};
2594 $keycol = $opt->{ui_data_key_name} || $db->config('KEY');
2597 ###############################################################
2602 my (@cols, %colseen);
2604 for (split /[,\0\s]/, $opt->{ui_data_fields}) {
2605 next if $colseen{$_}++;
2609 $opt->{ui_data_fields} = join " ", @cols;
2611 $linecount = scalar @cols;
2614 my $url = $Tag->area('ui');
2617 if($opt->{ui_new_item} and ! $opt->{notable}) {
2618 if( ! $db->config('_Auto_number') and ! $db->config('AUTO_SEQUENCE')) {
2619 $db->config('AUTO_NUMBER', '000001');
2620 $key = $db->autonumber($key);
2624 $opt->{mv_data_auto_number} = 1;
2625 $key_message = errmsg('(new key will be assigned if left blank)');
2629 if($opt->{notable}) {
2632 elsif($opt->{ui_clone_id} and $db->record_exists($opt->{ui_clone_id})) {
2633 $data = $db->row_hash($opt->{ui_clone_id})
2635 return $die->('table-editor: row_hash function failed for %s.', $key);
2636 $data->{$keycol} = $key;
2638 elsif ($db->record_exists($key)) {
2639 $data = $db->row_hash($key);
2643 if ($opt->{reload} and $have_errors) {
2646 $data->{$_} = $CGI->{$_}
2647 if defined $CGI->{$_};
2658 if($opt->{mailto} and $opt->{mv_blob_field}) {
2659 $opt->{hidden}{mv_blob_only} = 1;
2660 $opt->{hidden}{mv_blob_nick}
2661 = $opt->{mv_blob_nick}
2662 || POSIX::strftime("%Y%m%d%H%M%S", localtime());
2664 elsif($opt->{mv_blob_field}) {
2665 #::logDebug("checking blob");
2668 $blob_pointer = $data->{$opt->{mv_blob_pointer}}
2669 if $opt->{mv_blob_pointer};
2670 $blob_pointer ||= $opt->{mv_blob_nick};
2675 unless ( $db->column_exists($opt->{mv_blob_field}) ) {
2676 push @errors, ::errmsg(
2677 "blob field %s not in database.",
2678 $opt->{mv_blob_field},
2683 my $bstring = $data->{$opt->{mv_blob_field}};
2685 #::logDebug("blob: bstring=$bstring");
2689 if(length $bstring) {
2690 $blob = $Vend::Interpolate::safe_safe->reval($bstring);
2692 push @errors, ::errmsg("error reading blob data: %s", $@);
2695 #::logDebug("blob evals to " . ::uneval_it($blob));
2697 if(ref($blob) !~ /HASH/) {
2698 push @errors, ::errmsg("blob data not a storage book.");
2707 my @labels = keys %$blob;
2708 unshift @labels, '';
2711 for my $k (keys %$hidden_all) {
2712 my $v = $hidden_all->{$k};
2713 if(ref($v) eq 'ARRAY') {
2715 $extra .= "\n$k=$_";
2719 $extra .= "\n$k=$v";
2723 for my $key (@labels) {
2727 $ref = $blob->{$key};
2728 $lab = $ref->{$opt->{mv_blob_label} || 'name'};
2732 $lab = '--' . errmsg('none') . '--';
2737 $wid_data{$key} = "$key=$key - $lab";
2739 $url_data{$key} = $Tag->page( {
2740 href => $Global::Variable->{MV_PAGE},
2742 item_id=$opt->{item_id}
2743 mv_blob_nick=$key$extra
2746 $url_data{$key} .= "$key - $lab</a><br$Trailer>";
2749 $wid_data{$key} = $key;
2751 $url_data{$key} = $Tag->page( {
2752 href => $Global::Variable->{MV_PAGE},
2754 item_id=$opt->{item_id}
2755 mv_blob_nick=$key$extra
2758 $url_data{$key} .= "$key</a>";
2761 #::logDebug("wid_data is " . ::uneval_it(\%wid_data));
2762 $opt->{mv_blob_title} = "Stored settings"
2763 if ! $opt->{mv_blob_title};
2764 $opt->{mv_blob_title} = errmsg($opt->{mv_blob_title});
2766 $::Scratch->{Load} = <<EOF;
2767 [return-to type=click stack=1 page="$Global::Variable->{MV_PAGE}"]
2769 [perl]Log("tried to go to $Global::Variable->{MV_PAGE}"); return[/perl]
2772 #::logDebug("blob_pointer=$blob_pointer blob_nick=$opt->{mv_blob_nick}");
2776 if( $opt->{mv_blob_nick} ) {
2777 $lfrom_msg = $opt->{mv_blob_nick};
2780 $lfrom_msg = errmsg("current values");
2782 $lfrom_msg = errmsg("loaded from %s", $lfrom_msg);
2783 $loaded_from = <<EOF;
2784 <i>($lfrom_msg)</i><br>
2787 $loaded_from .= errmsg("Load from") . ":<blockquote>";
2788 $loaded_from .= join (" ", @url_data{ sort keys %url_data });
2789 $loaded_from .= "</blockquote>";
2794 if( $opt->{mv_blob_only} and $opt->{mv_blob_nick}) {
2795 $checked = ' CHECKED';
2796 $set = $opt->{mv_blob_nick};
2799 unless ($opt->{nosave}) {
2800 $blob_widget = display(undef, undef, undef, {
2801 name => 'mv_blob_nick',
2802 type => $opt->{ui_blob_widget} || 'combo',
2803 filter => 'nullselect',
2804 value => $opt->{mv_blob_nick},
2805 passed => join (",", @wid_data{ sort keys %wid_data }) || 'default',
2807 my $msg1 = errmsg('Save to');
2808 my $msg2 = errmsg('Save to book only');
2809 for (\$msg1, \$msg2) {
2810 $$_ =~ s/ / /g;
2812 $blob_widget = <<EOF unless $opt->{ui_blob_hidden};
2813 <b>$msg1:</b> $blob_widget
2814 <input type="checkbox" name="mv_blob_only" class="$opt->{widget_class}" value="1"$checked> $msg2</small>
2818 $blob_widget = <<EOF unless $opt->{ui_blob_hidden};
2819 <tr$opt->{data_row_extra}>
2820 <td width="$opt->{left_width}"$opt->{label_cell_extra}>
2821 <small>$opt->{mv_blob_title}<br>
2824 <td$opt->{data_cell_extra}>
2830 <td colspan="$span"$opt->{border_cell_extra}><img src="$opt->{clear_image}" width="1" height="$opt->{border_height}" alt="x"></td>
2834 if($opt->{mv_blob_nick}) {
2835 delete $opt->{force_defaults};
2836 my @keys = split /::/, $opt->{mv_blob_nick};
2837 my $ref = $blob->{shift @keys};
2842 $ref = $prior->{$_};
2844 last DOBLOB unless ref $ref;
2847 $data->{$_} = $ref->{$_};
2854 #::logDebug("data is: " . ::uneval($data));
2855 $data = { $keycol => $key }
2858 if(! $opt->{mv_data_function}) {
2859 $opt->{mv_data_function} = $exists ? 'update' : 'insert';
2862 my $url_base = $opt->{secure} ? $Vend::Cfg->{SecureURL} : $Vend::Cfg->{VendURL};
2864 if(! $opt->{href}) {
2865 $opt->{href} = $opt->{mv_nextpage};
2866 $opt->{hidden}{mv_ui} = 1
2867 if $Vend::admin and ! defined $opt->{hidden}{mv_ui};
2868 $opt->{hidden}{mv_action} = $opt->{action};
2871 $opt->{href} = "$url_base/$opt->{href}"
2872 if $opt->{href} !~ m{^(https?:|)/};
2874 $opt->{method} = $opt->{get} ? 'GET' : 'POST';
2876 my $wo = $opt->{widgets_only};
2881 if($opt->{reparse} and ! $opt->{promiscuous}) {
2882 $restrict_begin = qq{[restrict allow="$opt->{restrict_allow}"]};
2883 $restrict_end = '[/restrict]';
2888 chunk ttag(), $restrict_begin;
2890 chunk 'FORM_BEGIN', 'OUTPUT_MAP', <<EOF;
2891 <form method="$opt->{method}" action="$opt->{href}"$opt->{enctype}$opt->{form_extra}>
2894 my $prescript_marker = $#out;
2896 $hidden->{mv_click} = $opt->{process_filter};
2897 $hidden->{mv_todo} = $opt->{action};
2898 $hidden->{mv_nextpage} = $opt->{mv_nextpage};
2899 $hidden->{mv_data_table} = $table;
2900 $hidden->{mv_data_key} = $keycol;
2902 $hidden->{mv_return_table} = $CGI->{mv_return_table} || $table;
2905 $hidden->{mv_return_table} = $table;
2908 chunk 'HIDDEN_ALWAYS', 'OUTPUT_MAP', <<EOF;
2909 <input type="hidden" name="mv_session_id" value="$Vend::Session->{id}">
2910 <input type="hidden" name="mv_click" value="process_filter">
2928 for my $k (@opt_set) {
2929 $opt->{hidden}{$k} = $opt->{$k};
2932 if($pass_return_to) {
2933 while( my($k, $v) = each %$pass_return_to) {
2934 next if defined $opt->{hidden}{$k};
2935 $opt->{hidden}{$k} = $pass_return_to->{$k};
2939 if($opt->{mailto}) {
2940 $opt->{mailto} =~ s/\s+/ /g;
2941 $::Scratch->{mv_email_enable} = $opt->{mailto};
2942 $opt->{hidden}{mv_data_email} = 1;
2945 $Vend::Session->{ui_return_stack} ||= [];
2948 if($opt->{cgi} and ! $pass_return_to) {
2949 my $r_ary = $Vend::Session->{ui_return_stack};
2951 #::logDebug("ready to maybe push/pop return-to from stack, stack = " . ::uneval($r_ary));
2952 if($CGI::values{ui_return_stack}++) {
2953 push @$r_ary, $CGI::values{ui_return_to};
2954 $CGI::values{ui_return_to} = $r_ary->[0];
2956 elsif ($CGI::values{ui_return_to}) {
2957 @$r_ary = ( $CGI::values{ui_return_to} );
2959 chunk 'RETURN_TO', '', $Tag->return_to(); # unless $wo;
2960 #::logDebug("return-to stack = " . ::uneval($r_ary));
2963 if(ref $opt->{hidden} or ref $opt->{hidden_all}) {
2967 while ( ($hk, $hv) = each %$hidden ) {
2969 ##if new item, get mv_nextpage from radio buttons
2971 if($opt->{ui_new_item}){
2972 next if $hk =~ /mv_nextpage/;
2973 push @o, produce_hidden($hk, $hv);
2976 push @o, produce_hidden($hk, $hv);
2979 while ( ($hk, $hv) = each %$hidden_all ) {
2980 push @o, produce_hidden($hk, $hv);
2982 chunk 'HIDDEN_USER', 'OUTPUT_MAP', join("", @o); # unless $wo;
2985 if($opt->{tabbed}) {
2986 $opt->{table_width} ||= ($opt->{panel_width} || 800) + 10;
2987 $opt->{table_height} ||= ($opt->{panel_height} || 600) + 10;
2988 $opt->{inner_table_width} ||= ($opt->{panel_width} || 800);
2989 $opt->{inner_table_height} ||= ($opt->{panel_height} || 600);
2991 chunk ttag(), <<EOF; # unless $wo;
2992 <table class="touter" border="0" cellspacing="0" cellpadding="0" width="$opt->{table_width}" height="$opt->{table_height}">
2996 <table class="tinner" width="$opt->{inner_table_width}" height="$opt->{inner_table_height}" cellspacing="0" cellmargin="0" cellpadding="2" align="center" border="0">
2998 chunk ttag(), 'NO_TOP OUTPUT_MAP', <<EOF; # unless $opt->{no_top} or $wo;
3000 <td colspan="$span"$opt->{border_cell_extra}><img src="$opt->{clear_image}" width="1" height="$opt->{border_height}" alt="x"></td>
3004 if ($opt->{intro_text}) {
3005 #::logDebug("intro_text=$opt->{intro_text}");
3006 chunk ttag(), <<EOF;
3007 <tr $opt->{spacer_row_extra}>
3008 <td colspan="$span" $opt->{spacer_cell_extra}>$opt->{intro_text}</td>
3010 <tr $opt->{title_row_extra}>
3011 <td colspan="$span" $opt->{title_cell_extra}>$::Scratch->{page_title}</td>
3016 $opt->{top_buttons_rows} = 5 unless defined $opt->{top_buttons_rows};
3019 my $extra_ok = $blob_widget
3020 || $opt->{output_map}
3021 || $linecount >= $opt->{top_buttons_rows}
3022 || defined $opt->{include_form}
3025 $mlabel ||= ' ';
3026 if ($extra_ok and ! $opt->{no_top} and ! $opt->{nosave}) {
3027 if($opt->{back_text}) {
3028 chunk ttag(), 'OUTPUT_MAP', <<EOF; # unless $wo;
3029 <tr$opt->{data_row_extra}>
3030 <td$opt->{label_cell_extra}> </td>
3031 <td align="left" colspan="$oddspan"$opt->{data_cell_extra}>
3033 chunk 'COMBINED_BUTTONS_TOP', 'BOTTOM_BUTTONS OUTPUT_MAP', <<EOF;
3034 <input type="submit" name="mv_click" value="$opt->{back_text}"$opt->{back_button_extra}> <input type="submit" name="mv_click" value="$opt->{cancel_text}"$opt->{cancel_button_extra}> <b><input type="submit" name="mv_click" value="$opt->{next_text}"$opt->{next_button_extra}></b>
3037 chunk 'MLABEL', 'OUTPUT_MAP', 'MESSAGES', $mlabel;
3038 chunk ttag(), <<EOF;
3044 elsif ($opt->{wizard}) {
3045 chunk ttag(), 'NO_TOP OUTPUT_MAP', <<EOF;
3046 <tr$opt->{data_row_extra}>
3047 <td$opt->{label_cell_extra}> </td>
3048 <td align="left" colspan="$oddspan"$opt->{data_cell_extra}>
3050 chunk 'WIZARD_BUTTONS_TOP', 'BOTTOM_BUTTONS NO_TOP OUTPUT_MAP', <<EOF;
3051 <input type="submit" name="mv_click" value="$opt->{cancel_text}"$opt->{cancel_button_extra}> <b><input type="submit" name="mv_click" value="$opt->{next_text}"$opt->{next_button_extra}></b>
3054 chunk 'MLABEL', 'NO_TOP OUTPUT_MAP', 'MESSAGES', $mlabel;
3055 chunk ttag(), 'NO_TOP OUTPUT_MAP', <<EOF;
3062 chunk ttag(), 'BOTTOM_BUTTONS NO_TOP OUTPUT_MAP', <<EOF;
3063 <tr$opt->{data_row_extra}>
3064 <td$opt->{label_cell_extra}> </td>
3065 <td align="left" colspan="$oddspan"$opt->{data_cell_extra}>
3068 chunk 'OK_TOP', 'NO_TOP OUTPUT_MAP', <<EOF;
3069 <input type="submit" name="mv_click" value="$opt->{next_text}"$opt->{ok_button_extra}>
3071 chunk 'CANCEL_TOP', 'NOCANCEL BOTTOM_BUTTONS NO_TOP OUTPUT_MAP', <<EOF;
3073 <input type="submit" name="mv_click" value="$opt->{cancel_text}"$opt->{cancel_button_extra}>
3076 if($opt->{show_reset}) {
3077 chunk 'RESET_TOP', 'BOTTOM_BUTTONS NO_TOP OUTPUT_MAP', <<EOF;
3079 <input type="reset"$opt->{reset_button_extra}>
3083 chunk 'MLABEL', 'BOTTOM_BUTTONS OUTPUT_MAP', $mlabel;
3084 chunk ttag(), 'BOTTOM_BUTTONS NO_TOP OUTPUT_MAP', <<EOF;
3092 chunk 'BLOB_WIDGET', $blob_widget; # unless $wo;
3096 if($opt->{ui_new_item} and $opt->{ui_clone_tables}) {
3099 my @tables = split /[\s\0,]+/, $opt->{ui_clone_tables};
3108 for(@tables, @sets) {
3109 $tab_checked{$_} = 1 if s/\*$//;
3112 @tables = grep ! $seen{$_}++ && defined $Vend::Cfg->{Database}{$_}, @tables;
3116 [flag type=write table="_TABLES_"]
3117 [perl tables="_TABLES_"]
3118 delete $::Scratch->{clone_tables};
3119 return if ! $CGI->{ui_clone_id};
3120 return if ! $CGI->{ui_clone_tables};
3121 my $id = $CGI->{ui_clone_id};
3123 my $out = "Cloning id=$id...";
3125 my $new = $CGI->{$CGI->{mv_data_key}}
3127 $out .= ("clone $id: no mv_data_key '$CGI->{mv_data_key}'");
3128 $::Scratch->{ui_message} = $out;
3134 Log("cannot clone multiple keys '$new'.");
3139 my @possible = qw/_TABLES_/;
3140 @possible{@possible} = @possible;
3141 my @tables = grep /\S/, split /[\s,\0]+/, $CGI->{ui_clone_tables};
3142 my @sets = grep /:/, @tables;
3143 @tables = grep $_ !~ /:/, @tables;
3145 next unless $possible{$_};
3146 my $db = $Db{$_} || Vend::Data::database_exists_ref($_);
3149 my $res = $db->clone_row($id, $new);
3151 $out .= "cloned $id to to $new in table $_<BR>\n";
3154 $out .= "FAILED clone of $id to to $new in table $_<BR>\n";
3158 my ($t, $col) = split /:/, $_;
3159 my $db = $Db{$t} || Vend::Data::database_exists_ref($t) or next;
3160 my $res = $db->clone_set($col, $id, $new);
3162 $out .= "cloned $col=$id to to $col=$new in table $t<BR>\n";
3165 $out .= "FAILED clone of $col=$id to to $col=$new in table $t<BR>\n";
3168 $::Scratch->{ui_message} = $out;
3173 @tables = grep $Tag->if_mm( { table => "$_=i" } ), @tables;
3176 my $db = Vend::Data::database_exists_ref($_)
3178 next unless $db->record_exists($opt->{ui_clone_id});
3179 my $checked = $tab_checked{$_} ? ' CHECKED' : '';
3181 <input type="checkbox" name="ui_clone_tables" value="$_"$checked> clone to <b>$_</b><br>
3185 my ($t, $col) = split /:/, $_;
3186 my $checked = $tab_checked{$_} ? ' CHECKED' : '';
3188 <input type="checkbox" name="ui_clone_tables" value="$_"$checked> clone entries of <b>$t</b> matching on <b>$col</b><br>
3192 my $tabs = join " ", @tables;
3193 $set =~ s/_TABLES_/$tabs/g;
3194 $::Scratch->{clone_tables} = $set;
3195 chunk ttag(), <<EOF; # unless $wo;
3197 <td colspan="$span"$opt->{border_cell_extra}>
3199 chunk 'CLONE_TABLES', <<EOF;
3200 $tabform<input type="hidden" name="mv_check" value="clone_tables">
3201 <input type="hidden" name="ui_clone_id" value="$opt->{ui_clone_id}">
3203 chunk ttag(), <<EOF; # unless $wo;
3209 chunk_alias 'TOP_OF_FORM', qw/ FORM_BEGIN /;
3210 chunk_alias 'TOP_BUTTONS', qw/
3211 COMBINED_BUTTONS_TOP
3220 if($opt->{ui_break_before}) {
3221 my @tmp = grep /\S/, split /[\s,\0]+/, $opt->{ui_break_before};
3222 @break{@tmp} = @tmp;
3223 if($opt->{ui_break_before_label}) {
3224 @tmp = grep /\S/, split /\s*[,\0]\s*/, $opt->{ui_break_before_label};
3226 my ($br, $lab) = split /\s*=\s*/, $_, 2;
3227 $break_label{$br} = $lab;
3231 if(!$db and ! $opt->{notable}) {
3232 return "<tr><td>Broken table '$table'</td></tr>";
3235 my $passed_fields = $opt->{ui_data_fields};
3244 if($opt->{notable}) {
3245 @cols = split /[\s,\0]+/, $passed_fields;
3249 while($passed_fields =~ s/(\w+[.:]+\S+)//) {
3250 push @extra_cols, $1;
3253 my @do = grep /\S/, split /[\0,\s]+/, $opt->{ui_display_only};
3255 #::logDebug("display_only: $_");
3256 $email_cols{$_} = 1 if $opt->{mailto};
3257 $display_only{$_} = 1;
3258 push @extra_cols, $_;
3261 @dbcols = split /\s+/, $Tag->db_columns( {
3263 columns => $passed_fields,
3267 if($opt->{ui_data_fields}) {
3268 for(@dbcols, @extra_cols) {
3269 unless (/^(\w+)([.:]+)(\S+)/) {
3283 next unless $Tag->db_columns( { name => $t, columns => $c, });
3287 @cols = grep $ok_col{$_}, split /\s+/, $opt->{ui_data_fields};
3290 $keycol = $cols[0] if ! $keycol;
3292 if($opt->{defaults}) {
3293 if($opt->{force_defaults}) {
3294 $default->{$_} = $def->{$_} for @cols;
3296 elsif($opt->{wizard}) {
3298 $default->{$_} = $def->{$_} if defined $def->{$_};
3303 next if defined $default->{$_};
3304 next unless defined $def->{$_};
3305 $default->{$_} = $def->{$_};
3310 my $super = $Tag->if_mm('super');
3314 my @data_enable = ($opt->{mv_blob_pointer}, $opt->{mv_blob_field});
3317 if($opt->{left_width} and ! $opt->{label_cell_width}) {
3318 $opt->{label_cell_extra} .= qq{ width="$opt->{left_width}"};
3322 if($super and ! $opt->{no_meta}) {
3323 $show_meta = defined $def->{ui_meta_force}
3324 ? $def->{ui_meta_force}
3325 : $::Variable->{UI_META_LINK};
3329 if(! $opt->{row_template} and ! $opt->{simple_row}) {
3330 $opt->{meta_prepend} = qq{<br$Trailer><font size="1">}
3331 unless defined $opt->{meta_prepend};
3333 $opt->{meta_append} = '</font>'
3334 unless defined $opt->{meta_append};
3337 $opt->{meta_prepend} ||= '';
3338 $opt->{meta_append} ||= '';
3340 $opt->{meta_title} ||= errmsg('Edit field meta display info, table %s, column %s');
3341 $opt->{meta_title_specific} ||= errmsg('Item-specific meta edit, table %s, column %s, key %s');
3342 $opt->{meta_image_specific} ||= errmsg('specmeta.png');
3343 $opt->{meta_image} ||= errmsg('meta.png');
3344 $opt->{meta_image_extra} ||= 'border="0"';
3345 $opt->{meta_anchor_specific} ||= errmsg('item-specific meta');
3346 $opt->{meta_anchor} ||= errmsg('meta');
3347 $opt->{meta_anchor_specific} ||= errmsg('item-specific meta');
3348 $opt->{meta_extra} = " $opt->{meta_extra}"
3349 if $opt->{meta_extra};
3350 $opt->{meta_extra} ||= "";
3351 $opt->{meta_extra} .= qq{ class="$opt->{meta_class}"}
3352 if $opt->{meta_class};
3353 $opt->{meta_extra} .= qq{ style="$opt->{meta_style}"}
3354 if $opt->{meta_style};
3357 my $row_template = convert_old_template($opt->{row_template});
3359 #::logDebug("display_type='$opt->{display_type}' row_template length=" . length($row_template));
3361 if(! $row_template) {
3362 $opt->{display_type} = 'simple_row' if $opt->{simple_row};
3363 $opt->{display_type} ||= 'image_meta' if $opt->{image_meta};
3364 my $dt = $opt->{display_type} ||= 'default';
3368 #::logDebug("display_type=$dt");
3369 my $sub = $Display_type{$dt};
3370 if(ref($sub) eq 'CODE') {
3371 $row_template = $sub->($opt, $span);
3374 ::logError("table-editor: display_type '%s' sub not found", $dt);
3375 $row_template = $Display_type{default}->($opt, $span);
3379 $row_template =~ s/~OPT:(\w+)~/$opt->{$1}/g;
3380 $row_template =~ s/~([A-Z]+)_EXTRA~/$opt->{"\L$1\E_extra"} || $opt->{"\L$1\E_cell_extra"}/g;
3382 $opt->{row_template} = $row_template;
3384 $opt->{combo_template} ||= <<EOF;
3385 <tr$opt->{combo_row_extra}><td> {LABEL} </td><td>{WIDGET}</td></tr>
3388 $opt->{break_template} ||= <<EOF;
3389 <tr$opt->{break_row_extra}><td colspan="$span" $opt->{break_cell_extra}\{FIRST?} style="$opt->{break_cell_first_style}"{/FIRST?}>{ROW}</td></tr>
3395 if(my $jsc = $opt->{js_changed}) {
3397 and $jsc = qq{onChange="$jsc} . q{('$$KEY$$','$$COL$$');"};
3398 foreach my $c (@cols) {
3399 next if $extra->{$c} =~ /\bonchange\s*=/i;
3401 $tpl .= $extra->{$c} if length $extra->{$c};
3402 $tpl =~ s/\$\$KEY\$\$/$key/g;
3403 $tpl =~ s/\$\$COL\$\$/$c/g;
3404 if ($extra->{$c} and $extra->{$c} =~ /\bonchange\s*=/i) {
3405 $tpl =~ s/onChange="//;
3407 $extra->{$c} =~ s/\b(onchange\s*=\s*["'])/$1$tpl/i;
3410 $extra->{$c} = $tpl;
3417 if($opt->{link_table} and $key) {
3418 #::logDebug("In link table routines...");
3433 if(ref($opt->{link_table}) eq 'ARRAY') {
3434 @ltable = @{$opt->{link_table}};
3435 @lfields = @{$opt->{link_fields}};
3436 @lview = @{$opt->{link_view}};
3437 @lkey = @{$opt->{link_key}};
3438 @llab = @{$opt->{link_label}};
3439 @ltpl = @{$opt->{link_template}};
3440 @lnb = @{$opt->{link_no_blank}};
3441 @lrq = @{$opt->{link_row_qual}};
3442 @lra = @{$opt->{link_auto_number}};
3443 @lrb = @{$opt->{link_rows_blank}};
3444 @lba = @{$opt->{link_blank_auto}};
3445 @lbefore = @{$opt->{link_before}};
3446 @lsort = @{$opt->{link_sort}};
3449 @ltable = $opt->{link_table};
3450 @lfields = $opt->{link_fields};
3451 @lview = $opt->{link_view};
3452 @lkey = $opt->{link_key};
3453 @llab = $opt->{link_label};
3454 @ltpl = $opt->{link_template};
3455 @lnb = $opt->{link_no_blank};
3456 @lrq = $opt->{link_row_qual};
3457 @lra = $opt->{link_auto_number};
3458 @lrb = $opt->{link_rows_blank};
3459 @lba = $opt->{link_blank_auto};
3460 @lbefore = $opt->{link_before};
3461 @lsort = $opt->{link_sort};
3463 while(my $lt = shift @ltable) {
3464 my $lf = shift @lfields;
3465 my $lv = shift @lview;
3466 my $lk = shift @lkey;
3467 my $ll = shift @llab;
3468 my $lb = shift @lbefore;
3469 my $lnb = shift @lnb;
3470 my $ls = shift @lsort;
3471 my $lrq = shift @lrq;
3472 my $lra = shift @lra;
3473 my $lrb = shift @lrb;
3474 my $lba = shift @lba;
3478 $ll ||= errmsg("Settings in table %s linked by %s", $lt, $lk);
3482 my $ldb = database_exists_ref($lt)
3484 logError("Bad table editor link table: %s", $lt);
3488 my $lmeta = $Tag->meta_record($lt, $lv);
3489 $lf ||= $lmeta->{spread_fields};
3491 my $l_pkey = $ldb->config('KEY');
3495 my @f = grep /\w/, split /[\s,\0]+/, $lf;
3496 @f = grep $_ ne $lk && $_ ne $l_pkey, @f;
3503 <input type="hidden" name="mv_data_auto_number__$tcount" value="$lra">
3504 <input type="hidden" name="mv_data_function__$tcount" value="insert">
3508 ## Have to produce two field lists -- one for
3509 ## in link_blank_auto mode (no link_key for row_edit)
3510 ## and one with all when not in link_blank_auto
3512 my @cf = grep /\S/, split /[\s,\0]+/, $lf;
3513 @cf = grep $_ ne $l_pkey, @cf;
3514 $lf = join " ", @cf;
3516 unshift @cf, $lk if $lba;
3517 my $df = join " ", @cf;
3519 my $lextra = $opt->{link_extra} || '';
3520 $lextra = " $lextra" if $lextra;
3522 my @lout = q{<table cellspacing="0" cellpadding="1">};
3523 push @lout, qq{<tr><td$lextra>
3524 <input type="hidden" name="mv_data_table__$tcount" value="$lt">
3525 <input type="hidden" name="mv_data_fields__$tcount" value="$df">
3526 <input type="hidden" name="mv_data_multiple__$tcount" value="1">
3527 <input type="hidden" name="mv_data_key__$tcount" value="$l_pkey">
3528 <input type="hidden" name="mv_data_multiple_qual__$tcount" value="$lrq">
3531 push @lout, $Tag->row_edit({ table => $lt, columns => $lf });
3532 push @lout, '</tr>';
3534 my $tname = $ldb->name();
3537 $lfor = $ldb->quote($key, $lk);
3538 my $q = "SELECT $l_pkey FROM $tname WHERE $lk = $lfor";
3539 $q .= " ORDER BY $ls" if $ls;
3540 my $ary = $ldb->query($q);
3543 my $pp = $rcount ? "${rcount}_" : '';
3544 my $hid = qq{<input type="hidden" name="$pp${l_pkey}__$tcount" value="};
3545 $hid .= HTML::Entities::encode($rk);
3547 push @lout, qq{<tr><td$lextra>$rk$hid</td>};
3549 my $hid = qq{<input type="hidden" name="$pp${lk}__$tcount" value="};
3550 $hid .= HTML::Entities::encode($k);
3552 push @lout, qq{<td$lextra>$k$hid</td>};
3557 extra => $opt->{link_extra},
3563 push @lout, $Tag->row_edit(\%o);
3564 push @lout, "</tr>";
3567 if($lba and $lrq eq $lk || $lrq eq $l_pkey) {
3568 my $colcount = scalar(@cf) + 1;
3569 push @lout, qq{<td colspan="$colcount">Link row qualifier must be different than link_key and primary code when in auto mode.</td>};
3574 my $start_ptr = 999000;
3576 for(0 .. $opt->{link_rows_blank}) {
3580 extra => $opt->{link_extra},
3581 pointer => $start_ptr,
3585 my $ktype = $lba ? 'hidden' : 'text';
3586 push @lout, qq{<tr><td$lextra>};
3587 push @lout, qq{<input size="8" type="$ktype" name="${start_ptr}_${l_pkey}__$tcount" value="">};
3588 push @lout, '(auto)' if $lba;
3589 push @lout, '</td>';
3591 my $hid = qq{<input type="hidden" name="${start_ptr}_${lk}__$tcount" value="};
3592 $hid .= HTML::Entities::encode($k);
3594 push @lout, qq{<td$lextra>$k$hid</td>};
3596 push @lout, $Tag->row_edit(\%o);
3597 push @lout, '</tr>';
3601 push @lout, "</table>";
3602 $whash->{LABEL} = $ll;
3603 $whash->{WIDGET} = join "", @lout;
3607 $murl = $Tag->area({
3608 href => 'admin/db_metaconfig_spread',
3614 $whash->{META_URL} = $murl;
3615 $whash->{META_STRING} = qq{<a href="$murl"$opt->{meta_extra}>};
3616 $whash->{META_STRING} .= errmsg('meta') . '</a>';
3620 $link_row{$lt} = $whash;
3622 $link_before{$lb} = $lt;
3624 my $mde_key = "mv_data_enable__$tcount";
3625 $::Scratch->{$mde_key} = "$lt:" . join(",", $l_pkey, @cf) . ':';
3627 #::logDebug("Made link_table...whash=$whash");
3631 if($opt->{include_form}) {
3632 #::logDebug("In link table routines...");
3637 if(ref($opt->{include_form}) eq 'ARRAY') {
3638 @icells = @{delete $opt->{include_form}};
3639 @ibefore = @{delete $opt->{include_before} || []};
3642 @icells = delete $opt->{include_form};
3643 @ibefore = delete $opt->{include_before};
3646 $opt->{include_before} = {};
3647 while(my $it = shift @icells) {
3648 my $ib = shift @ibefore;
3652 #::logDebug("Made include_before=$it");
3654 $opt->{include_before}{$ib} = $it;
3656 elsif($it =~ /^\s*<tr\b/i) {
3657 push @includes, $it;
3660 push @includes, "<tr>$it</tr>";
3663 $opt->{include_form} = join "\n", @includes if @includes;
3666 if($opt->{tabbed}) {
3667 my $ph = $opt->{panel_height} || '600';
3668 my $pw = $opt->{panel_width} || '800';
3669 my $th = $opt->{tab_height} || '30';
3671 my $extra = qq{ width="$pw" height="$oh" valign="top"};
3672 chunk ttag(), qq{<tr><td colspan="$span"$extra>\n};
3675 #::logDebug("include_before: " . uneval($opt->{include_before}));
3681 ## Find out what our errors are
3682 if($CGI->{mv_form_profile} eq 'ui_profile' and $Vend::Session->{errors}) {
3683 for(keys %{$Vend::Session->{errors}}) {
3686 $reload = 1 unless $opt->{no_reload};
3692 for(qw/callback_prescript callback_postscript/) {
3693 next unless $opt->{$_};
3694 next if ref($opt->{$_}) eq 'CODE';
3696 name => errmsg('table-editor'),
3697 set => errmsg('%s is not a code reference', $_),
3701 my $callback_prescript = $opt->{callback_prescript} || sub {
3702 push @prescript, @_;
3704 my $callback_postscript = $opt->{callback_postscript} || sub {
3705 push @postscript, @_;
3709 if(my $sheet = $opt->{style_sheet}) {
3712 if($Style_sheet{$sheet}) {
3713 push @prescript, $Style_sheet{$sheet};
3715 elsif($sheet =~ /\s/) {
3717 if($sheet !~ /^<\w+/) {
3719 push @prescript, q{<style type="text/css">}
3721 push @prescript, $sheet;
3722 push @prescript, q{</style>} if $pre_put;
3726 "%s: style sheet %s not found, using default",
3727 errmsg('table-editor'),
3730 push @prescript, $Style_sheet{default};
3734 foreach my $col (@cols) {
3739 if($col eq $keycol) {
3740 if($opt->{ui_hide_key}) {
3741 my $kval = $key || $override->{$col} || $default->{$col};
3743 qq{<input type="hidden" name="$col" value="$kval">};
3745 $titles[$ctl_index] = $break_label{$col};
3749 elsif ($opt->{ui_new_item}) {
3750 $tkey_message = $key_message;
3755 my $do = $display_only{$col};
3760 if($col =~ /(\w+):+([^:]+)(?::+(\S+))?/) {
3765 and $serialize = $c;
3767 $do = 1 if $disabled->{$c};
3768 push @ext_enable, ("$t:$c" . $k ? ":$k" : '')
3776 and $serialize = $c;
3777 $do = 1 if $disabled->{$c};
3778 push @data_enable, $col
3779 unless $do and ! $opt->{mailto};
3784 $currval = $data->{$col} if defined $data->{$col};
3785 if ($opt->{force_defaults} or defined $override->{$c} ) {
3786 $currval = $override->{$c};
3788 #::logDebug("hit override for $col,currval=$currval");
3790 elsif (defined $CGI->{"ui_preload:$t:$c"} ) {
3791 $currval = delete $CGI->{"ui_preload:$t:$c"};
3793 #::logDebug("hit preload for $col,currval=$currval");
3795 elsif( ($do && ! $currval) or $col =~ /:/) {
3799 for( $override, $data, $default) {
3800 next unless defined $_->{$check};
3806 $k = defined $key ? $key : $refkey;
3808 $currval = tag_data($t, $c, $k) if defined $k;
3809 #::logDebug("hit display_only for $col, t=$t, c=$c, k=$k, currval=$currval");
3811 elsif (defined $default->{$c} and ! length($data->{$c}) ) {
3812 $currval = $default->{$c};
3814 #::logDebug("hit preload for $col,currval=$currval");
3817 #::logDebug("hit data->col for $col, t=$t, c=$c, k=$k, currval=$currval");
3818 $currval = length($data->{$col}) ? $data->{$col} : '';
3822 if($reload and defined $CGI::values{$col}) {
3823 $currval = $CGI::values{$col};
3828 #::logDebug("serialize=$serialize");
3829 if($serialize{$col}) {
3830 push @{$serialize{$col}}, $serialize;
3835 my ($tt, $tc) = split /:+/, $col;
3836 $sd = tag_data($tt, $tc, $k);
3839 $sd = $data->{$col} || $default->{$col};
3841 #::logDebug("serial_data=$sd");
3842 $serial_data{$col} = $sd;
3843 $opt->{hidden}{$col} = $data->{$col};
3844 $serialize{$col} = [$serialize];
3848 #::logDebug("fetching serial_data for $col hk=$hk data=$serial_data{$col}");
3849 $currval = dotted_hash($serial_data{$col}, $hk);
3850 #::logDebug("fetched hk=$hk value=$currval");
3852 $namecol = $c = $serialize;
3854 if($reload and defined $CGI::values{$namecol}) {
3855 $currval = $CGI::values{$namecol};
3859 $namecol = $col unless $namecol;
3861 #::logDebug("display_only=$do col=$c");
3862 $widget->{$c} = 'value'
3863 if $do and ! ($disabled->{$c} || $opt->{wizard} || $opt->{mailto});
3865 if (! length $currval and defined $default->{$c}) {
3866 $currval = $default->{$c};
3869 $template->{$c} ||= $row_template;
3875 std_label => '$LABEL$',
3878 if($opt->{all_errors}) {
3880 $parm->{text} = $opt->{error_template} || <<EOF;
3881 <font color="$opt->{color_fail}">\$LABEL\$ (%s)</font>
3882 [else]{REQUIRED <b>}{LABEL}{REQUIRED </b>}[/else]
3885 $err_string = $Tag->error($parm);
3886 if($template->{$c} !~ /{ERROR\??}/) {
3887 $template->{$c} =~ s/{LABEL}/$err_string/g
3889 $template->{$c} =~ s/\$LABEL\$/{LABEL}/g;
3893 my $meta_string = '';
3895 my $meta_url_specific;
3897 # Get global variables
3898 my $base = $::Variable->{UI_BASE}
3899 || $Global::Variable->{UI_BASE} || 'admin';
3900 my $page = $Global::Variable->{MV_PAGE};
3901 my $id = $t . "::$c";
3902 $id = $opt->{ui_meta_view} . "::$id"
3903 if $opt->{ui_meta_view} and $opt->{ui_meta_view} ne 'metaconfig';
3907 ui_return_to=item_id=$opt->{item_id}
3908 ui_return_to=ui_meta_view=$opt->{ui_meta_view}
3909 ui_return_to=mv_return_table=$t
3910 mv_return_table=$table
3911 ui_return_stack=$CGI->{ui_return_stack}
3914 $meta_url = $Tag->area({
3915 href => "$base/meta_editor",
3921 my $meta_specific = '';
3922 if($opt->{ui_meta_specific}) {
3923 $meta_url_specific = $Tag->area({
3924 href => "$base/meta_editor",
3926 item_id=${t}::${c}::$key
3930 $meta_specific = <<EOF;
3931 <br$Trailer><a href="$meta_url_specific"$opt->{meta_extra} tabindex="9999">$opt->{meta_anchor_specific}</a>
3935 $opt->{meta_append} = '</font>'
3936 unless defined $opt->{meta_append};
3937 if($opt->{image_meta}) {
3938 #::logDebug("meta-title=$opt->{meta_title}");
3939 my $title = errmsg($opt->{meta_title}, $t, $c);
3940 $meta_string = <<EOF;
3941 <a href="$meta_url"$opt->{meta_extra} tabindex="9999"><img src="$opt->{meta_image}" title="$title" $opt->{meta_image_extra}></a>
3943 if($meta_specific) {
3944 $title = errmsg($opt->{meta_title_specific}, $t, $c, $key);
3945 $meta_string .= <<EOF;
3946 <a href="$meta_url_specific"$opt->{meta_extra} tabindex="9999"><img src="$opt->{meta_image_specific}" title="$title" $opt->{meta_image_extra}></a>
3951 $meta_string = <<EOF;
3952 $opt->{meta_prepend}<a href="$meta_url"$opt->{meta_extra} tabindex="9999">$opt->{meta_anchor}</a>
3953 $meta_specific$opt->{meta_append}
3958 $class->{$c} ||= $opt->{widget_class};
3960 #::logDebug("col=$c currval=$currval widget=$widget->{$c} label=$label->{$c}");
3961 my $display = display($t, $c, $key, {
3962 append => $append->{$c},
3964 arbitrary => $opt->{ui_meta_view},
3965 callback_prescript => $callback_prescript,
3966 callback_postscript => $callback_postscript,
3967 class => $class->{$c},
3969 db => $database->{$c},
3970 default => $currval,
3971 default_widget => $opt->{default_widget},
3972 disabled => $disabled->{$c},
3973 extra => $extra->{$c},
3975 field => $field->{$c},
3976 filter => $filter->{$c},
3977 form => $form->{$c},
3978 form_name => $opt->{form_name},
3979 height => $height->{$c},
3980 help => $help->{$c},
3981 help_url => $help_url->{$c},
3982 href => $wid_href->{$c},
3983 js_check => $js_check->{$c},
3985 label => $label->{$c},
3986 lookup => $lookup->{$c},
3987 lookup_query => $lookup_query->{$c},
3988 maxlength => $maxlength->{$c},
3989 meta => $meta->{$c},
3990 meta_url => $meta_url,
3991 meta_url_specific => $meta_url_specific,
3993 options => $options->{$c},
3994 outboard => $outboard->{$c},
3995 override => $overridden,
3996 opts => $opts->{$c},
3997 passed => $passed->{$c},
3998 pre_filter => $pre_filter->{$c},
3999 prepend => $prepend->{$c},
4001 restrict_allow => $opt->{restrict_allow},
4002 specific => $opt->{ui_meta_specific},
4004 type => $widget->{$c},
4005 ui_no_meta_display => $opt->{ui_no_meta_display},
4006 width => $width->{$c},
4008 #::logDebug("finished display of col=$c");
4011 if ($display->{WIDGET} =~ /^\s*<input\s[^>]*type\s*=\W*hidden\b[^>]*>\s*$/is) {
4012 push @extra_hidden, $display->{WIDGET};
4015 $display->{TEMPLATE} = $template->{$c};
4016 $display->{META_STRING} = $meta_string;
4017 $display->{TKEY} = $tkey_message;
4018 $display->{BLABEL} = $blabel;
4019 $display->{ELABEL} = $elabel;
4020 $display->{COLSPAN} = qq{ colspan="$colspan->{$namecol}"}
4021 if $colspan->{$namecol};
4022 $display->{ERROR} = $err_string;
4025 if ($break{$namecol}) {
4026 #::logDebug("breaking on $namecol, control index=$ctl_index");
4027 if(@controls == 0 and @titles == 0) {
4028 $titles[0] = $break_label{$namecol};
4030 elsif(@titles == 0) {
4031 $titles[1] = $break_label{$namecol};
4035 push @titles, $break_label{$namecol};
4039 if($link_before{$col}) {
4040 col_chunk "_SPREAD_$link_before{$col}",
4041 delete $link_row{$link_before{$col}};
4043 if($opt->{include_before} and $opt->{include_before}{$col}) {
4044 #::logDebug("include_before: $col $opt->{include_before}{$col}");
4045 my $chunk = delete $opt->{include_before}{$col};
4046 if($opt->{include_form_interpolate}) {
4047 $Vend::Interpolate::Tmp->{table_editor_data} = $data;
4048 #::logDebug("data to include=" . ::uneval($data));
4049 $chunk = interpolate_html($chunk);
4051 elsif($opt->{include_form_expand}) {
4052 $chunk = expand_values($chunk);
4053 #::logDebug("include_before: expanded values on $col $chunk");
4055 my $h = { ROW => $chunk };
4056 $h->{TEMPLATE} = $opt->{whole_template} || '<tr>{ROW}</tr>';
4057 col_chunk "_INCLUDE_$col", $h;
4059 $ctl_index++ if $update_ctl;
4060 if($opt->{start_at} and $opt->{start_at} eq $namecol) {
4061 $opt->{start_at_index} = $ctl_index;
4062 #::logDebug("set start_at_index to $ctl_index");
4064 #::logDebug("control index now=$ctl_index");
4065 col_chunk $namecol, $display;
4068 for(sort keys %link_row) {
4069 #::logDebug("chunking link_table to _SPREAD_$_");
4070 col_chunk "_SPREAD_$_", delete $link_row{$_};
4073 my $firstout = scalar(@out);
4075 if($opt->{tabbed}) {
4076 chunk ttag(), qq{</td></tr>\n};
4079 while($rowcount % $rowdiv) {
4080 chunk ttag(), '<td colspan="$cells_per_span"> </td>'; # unless $wo;
4084 $::Scratch->{mv_data_enable} = '';
4085 if($opt->{auto_secure}) {
4086 $::Scratch->{mv_data_enable} .= "$table:" . join(",", @data_enable) . ':';
4087 $::Scratch->{mv_data_enable} .= $opt->{item_id};
4088 $::Scratch->{mv_data_enable_key} = $opt->{item_id};
4091 $::Scratch->{mv_data_enable} .= " " . join(" ", @ext_enable) . " ";
4093 #::logDebug("setting mv_data_enable to $::Scratch->{mv_data_enable}");
4094 my @serial = keys %serialize;
4098 #::logDebug("$_ serial_data=$serial_data{$_}");
4099 $serial_data{$_} = uneval($serial_data{$_})
4100 if is_hash($serial_data{$_});
4101 $serial_data{$_} =~ s/\&/&/g;
4102 $serial_data{$_} =~ s/"/"/g;
4103 push @o, qq{<input type="hidden" name="$_" value="$serial_data{$_}">}; # unless $wo;
4104 push @serial_fields, @{$serialize{$_}};
4107 if(! $wo and @serial_fields) {
4108 push @o, qq{<input type="hidden" name="ui_serial_fields" value="};
4109 push @o, join " ", @serial_fields;
4111 chunk 'HIDDEN_SERIAL', 'OUTPUT_MAP', join("", @o);
4115 ### Here the user can include some extra stuff in the form....
4117 if($opt->{include_form}) {
4118 my $chunk = delete $opt->{include_form};
4119 if($opt->{include_form_interpolate}) {
4120 $chunk = interpolate_html($chunk);
4122 elsif($opt->{include_form_expand}) {
4123 $chunk = expand_values($chunk);
4125 col_chunk '_INCLUDE_FORM',
4128 TEMPLATE => $opt->{whole_template} || '<tr>{ROW}</tr>',
4131 ### END USER INCLUDE
4133 unless ($opt->{mailto} and $opt->{mv_blob_only}) {
4134 @cols = grep ! $display_only{$_} && ! $disabled->{$_}, @cols;
4136 $passed_fields = join " ", @cols;
4138 my ($beghid, $endhid) = split m{</td>}i, $opt->{spacer_row}, 2;
4139 $endhid = "</td>$endhid" if $endhid;
4140 chunk ttag(), 'OUTPUT_MAP', $beghid;
4141 chunk 'HIDDEN_EXTRA', 'OUTPUT_MAP', qq{<input type="hidden" name="mv_data_fields" value="$passed_fields">@extra_hidden};
4142 chunk ttag(), 'OUTPUT_MAP', $endhid;
4145 last SAVEWIDGETS if $wo || $opt->{nosave};
4146 #::logDebug("in SAVEWIDGETS");
4147 chunk ttag(), 'OUTPUT_MAP', <<EOF;
4148 <tr$opt->{data_row_extra}>
4149 <td$opt->{label_cell_extra}> </td>
4150 <td align="left" colspan="$oddspan"$opt->{data_cell_extra}>
4153 if($opt->{back_text}) {
4155 chunk 'COMBINED_BUTTONS_BOTTOM', 'OUTPUT_MAP', <<EOF;
4156 <input type="submit" name="mv_click" value="$opt->{back_text}"$opt->{back_button_extra}> <input type="submit" name="mv_click" value="$opt->{cancel_text}"$opt->{cancel_button_extra}> <b><input type="submit" name="mv_click" value="$opt->{next_text}"$opt->{next_button_extra}></b>
4159 elsif($opt->{wizard}) {
4160 chunk 'WIZARD_BUTTONS_BOTTOM', 'OUTPUT_MAP', <<EOF;
4161 <input type="submit" name="mv_click" value="$opt->{cancel_text}"$opt->{cancel_button_extra}> <b><input type="submit" name="mv_click" value="$opt->{next_text}"$opt->{next_button_extra}></b>
4165 chunk 'OK_BOTTOM', 'OUTPUT_MAP', <<EOF;
4166 <input type="submit" name="mv_click" value="$opt->{next_text}"$opt->{ok_button_extra}>
4169 chunk 'CANCEL_BOTTOM', 'NOCANCEL OUTPUT_MAP', <<EOF;
4170 <input type="submit" name="mv_click" value="$opt->{cancel_text}"$opt->{cancel_button_extra}>
4173 chunk 'RESET_BOTTOM', 'OUTPUT_MAP', qq{ <input type="reset"$opt->{reset_button_extra}>}
4174 if $opt->{show_reset};
4178 if($exists and ! $opt->{nodelete} and $Tag->if_mm('tables', "$table=d")) {
4179 my $key_display = join '/', split /\0/, $key;
4180 my $extra = $Tag->return_to( { type => 'click', tablehack => 1 });
4181 my $page = $CGI->{ui_return_to};
4183 my $url = $Tag->area( {
4188 mv_data_table=$table
4189 mv_click=db_maintenance
4194 my $delstr = errmsg('Delete');
4195 my $delmsg = errmsg('Are you sure you want to delete %s?', $key_display);
4196 if($opt->{output_map} or $opt->{button_delete}) {
4197 chunk 'DELETE_BUTTON', 'NOSAVE OUTPUT_MAP', <<EOF;
4201 onClick="if(confirm('$delmsg')) { location='$url' }"
4202 title="Delete $key_display"
4203 value="$delstr"$opt->{delete_button_extra}>
4207 chunk 'DELETE_BUTTON', 'NOSAVE OUTPUT_MAP', <<EOF; # if ! $opt->{nosave};
4208 <br><br><a onClick="return confirm('$delmsg')" href="$url"><img src="delete.gif" alt="Delete $key_display" border="0"></a> $delstr
4214 if(! $opt->{notable} and $Tag->if_mm('tables', "$table=x") and ! $db->config('LARGE') ) {
4215 my $checked = ' CHECKED';
4216 my $msg = errmsg("Automatically export to text file");
4218 if defined $opt->{mv_auto_export} and ! $opt->{mv_auto_export};
4219 my $autoexpstr = errmsg('Auto-export');
4220 chunk 'AUTO_EXPORT', 'NOEXPORT NOSAVE OUTPUT_MAP', <<EOF;
4224 <input type="checkbox" class="$opt->{widget_class}" title="$msg" name="mv_auto_export" value="$table"$checked><span class="$opt->{widget_class}" title="$msg"> $autoexpstr</span>
4229 if($opt->{ui_new_item}) {
4230 my $aa_msg = l('Add another item');
4231 my $rt_msg = l('Return to table select');
4232 chunk 'DO_ANOTHER', 'OUTPUT_MAP', $opt->{ui_do_another} || <<EOF;
4235 <input type="radio" class="$opt->{widget_class}" name="mv_nextpage" value="admin/flex_select" CHECKED>
4237 <input type="radio" class="$opt->{widget_class}" name="mv_nextpage" value="admin/flex_editor">
4243 chunk_alias 'HIDDEN_FIELDS', qw/
4249 chunk_alias 'BOTTOM_BUTTONS', qw/
4250 WIZARD_BUTTONS_BOTTOM
4251 COMBINED_BUTTONS_BOTTOM
4257 chunk_alias 'EXTRA_BUTTONS', qw/
4261 chunk ttag(), 'OUTPUT_MAP', <<EOF;
4271 $message .= '<p>Errors:';
4272 $message .= qq{<font color="$opt->{color_fail}">};
4273 $message .= '<blockquote>';
4274 $message .= join "<br>", @errors;
4275 $message .= '</blockquote></font>';
4278 $message .= '<p>Messages:';
4279 $message .= qq{<font color="$opt->{color_success}">};
4280 $message .= '<blockquote>';
4281 $message .= join "<br>", @messages;
4282 $message .= '</blockquote></font>';
4284 $Tag->error( { all => 1 } );
4286 chunk ttag(), 'NO_BOTTOM _MESSAGE', <<EOF;
4288 <td colspan=$span$opt->{border_cell_extra}>
4291 chunk 'MESSAGE_TEXT', 'NO_BOTTOM', $message; # unless $wo or ($opt->{no_bottom} and ! $message);
4293 chunk ttag(), 'NO_BOTTOM _MESSAGE', <<EOF;
4298 #::logDebug("tcount=$tcount_all, prior to closing table");
4299 chunk ttag(), <<EOF; # unless $wo;
4301 <td colspan="$span"$opt->{border_cell_extra}><img src="$opt->{clear_image}" width="1" height="$opt->{border_height}" alt="x"></td>
4307 my $end_script = '';
4308 if( $opt->{start_at} || $opt->{focus_at}
4312 $widget->{$opt->{start_at}} !~ /radio|check/i
4315 my $foc = $opt->{focus_at} || $opt->{start_at};
4316 $end_script = <<EOF;
4318 document.$opt->{form_name}.$foc.focus();
4323 if($opt->{adjust_cell_class}) {
4324 $end_script .= <<EOF;
4326 var mytags=document.getElementsByTagName("td");
4330 var type = '$opt->{adjust_cell_class}';
4331 for(var i = 0; i < mytags.length; i++) {
4332 if(mytags[i].getAttribute('class') != type)
4334 var wid = mytags[i].offsetWidth;
4335 var span = mytags[i].getAttribute('colspan');
4336 if(span < 2 && mytags[i].getAttribute('class') == type && wid >= max) {
4341 if(max > 500 && (max / 2) > nextmax)
4343 for(var i = 0; i < mytags.length; i++) {
4344 if(mytags[i].getAttribute('class') != type)
4346 if(mytags[i].getAttribute('colspan') > 1)
4348 mytags[i].setAttribute('width', max);
4355 my $end = $outhash{FORM_BEGIN};
4356 $outhash{FORM_BEGIN} = join "\n", $end, @prescript;
4359 unshift @postscript, qq{</form>$end_script};
4361 chunk 'FORM_END', 'OUTPUT_MAP', join("\n", @postscript);
4363 chunk ttag(), $restrict_end;
4365 chunk_alias 'BOTTOM_OF_FORM', qw/ FORM_END /;
4380 $ehash{$_} = $opt->{lc $_} ? 1 : 0;
4383 $ehash{MESSAGE} = length($message) ? 1 : 0;
4385 #::logDebug("exclude is " . uneval(\%exclude));
4387 if($opt->{output_map}) {
4388 $opt->{output_map} =~ s/^\s+//;
4389 $opt->{output_map} =~ s/\s+$//;
4391 my @map = split /[\s,=\0]+/, $opt->{output_map};
4393 for(my $i = 0; $i <= @map; $i += 2) {
4394 $map{ uc $map[$i] } = lc $map[$i + 1];
4399 TOP_OF_FORM top_of_form
4400 BOTTOM_OF_FORM bottom_of_form
4401 HIDDEN_FIELDS hidden_fields
4402 TOP_BUTTONS top_buttons
4403 BOTTOM_BUTTONS bottom_buttons
4404 EXTRA_BUTTONS extra_buttons
4408 while(my($al, $to) = each %map) {
4409 #::logDebug("outputting alias $al to output $to");
4410 my $ary = $alias{$al} || [];
4411 #::logDebug("alias $al means " . join(" ", @$ary));
4412 my $string = join("", @outhash{@$ary});
4413 #::logDebug("alias $al string is $string");
4414 $Tag->output_to($to, { name => $to}, $string );
4418 resolve_exclude(\%ehash);
4421 return (map { @$_ } @controls) if wantarray;
4422 return join "", map { @$_ } @controls;
4424 show_times("end table editor call item_id=$key") if $Global::ShowTimes;
4427 if($overall_template =~ /\S/) {
4430 logDebug("must have chunk {$item} defined in overall template.");
4431 logError("must have chunk {%s} defined in overall template.", $item);
4435 if($opt->{fields_template_only}) {
4436 my $tstart = '<table';
4437 for my $p (qw/height width cellspacing cellmargin cellpadding class style/) {
4438 my $tag = "table_$p";
4439 next unless length $opt->{$tag} and $opt->{$tag} =~ /\S/;
4440 my $val = HTML::Entities::encode($opt->{$tag});
4441 $tstart .= qq{ $p="$val"};
4444 $overall_template = qq({TOP_OF_FORM}
4447 <tr><td colspan="$span">{TOP_BUTTONS}</td></tr>
4448 <tr><td colspan="$span">$overall_template</td></tr>
4449 <tr><td colspan="$span">{BOTTOM_BUTTONS}</td></tr>
4455 unless($opt->{incomplete_form_ok}) {
4456 $overall_template =~ /{TOP_OF_FORM}/
4457 or return $death->('TOP_OF_FORM');
4458 $overall_template =~ /{HIDDEN_FIELDS}/
4459 or return $death->('HIDDEN_FIELDS');
4460 $overall_template =~ /{BOTTOM_OF_FORM}/
4461 or return $death->('BOTTOM_OF_FORM');
4464 while($overall_template =~ m/\{((?:_INCLUDE_|COLUMN_|_SPREAD_).*?)\}/g) {
4467 my $thing = delete $outhash{$name};
4468 #::logDebug("Got to column replace $name, thing=$thing");
4470 $overall_template =~ s/\{$name\}/$thing->{ROW}/;
4472 elsif($name =~ s/__WIDGET$//) {
4473 $thing = delete $outhash{$name};
4474 my $lab = "${name}__LABEL";
4475 my $help = "${name}__HELP";
4476 #::logDebug("Got to widget replace $name, thing=$thing");
4477 $overall_template =~ s/\{$lab\}/$thing->{LABEL}/;
4478 $overall_template =~ s/\{$help\}/$thing->{HELP}/;
4479 $overall_template =~ s/\{$orig\}/$thing->{WIDGET}/;
4482 $overall_template =~ s!\{$name\}!
4483 tag_attr_list($thing->{TEMPLATE}, $thing)
4487 while($overall_template =~ m/\{([A-Z_]+)\}/g) {
4489 my $thing = delete $outhash{$name};
4490 #::logDebug("Got to random replace $name, thing=$thing");
4491 next if ! $thing and $alias{$name};
4492 $overall_template =~ s/\{$name\}/$thing/;
4494 while($overall_template =~ m/\{([A-Z_]+)\}/g) {
4496 my $thing = delete $alias{$name};
4497 #::logDebug("Got to alias replace $name, thing=$thing");
4498 $overall_template =~ s/\{$name\}/join "", @outhash{@$thing}/e;
4501 if($opt->{tabbed}) {
4504 push @tabcont, create_rows($opt, $_);
4506 $opt->{panel_table_extra} ||= 'width="100%" cellpadding="3" cellspacing="1"';
4507 $opt->{panel_table_extra} =~ s/^/ /;
4508 $opt->{panel_prepend} ||= "<table$opt->{panel_table_extra}>";
4509 $opt->{panel_append} ||= '</table>';
4510 push @put, tabbed_display(\@titles,\@tabcont,$opt);
4514 for(my $i = 0; $i < @controls; $i++) {
4515 push @put, tag_attr_list(
4516 $opt->{break_template},
4517 { FIRST => ! $first++, ROW => $titles[$i] },
4520 push @put, create_rows($opt, $controls[$i]);
4523 $overall_template =~ s/{:REST}/join "\n", @put/e;
4524 #::logDebug("overall_template:\n$overall_template");
4525 return $overall_template;
4528 for(my $i = 0; $i < $firstout; $i++) {
4529 #::logDebug("$out[$i] content length=" . length($outhash{$out[$i]} ));
4530 push @put, $outhash{$out[$i]};
4533 if($opt->{tabbed}) {
4534 #::logDebug("In tabbed display...controls=" . scalar(@controls) . ", titles=" . scalar(@titles));
4537 push @tabcont, create_rows($opt, $_);
4539 $opt->{panel_table_extra} ||= 'width="100%" cellpadding="3" cellspacing="1"';
4540 $opt->{panel_table_extra} =~ s/^/ /;
4541 $opt->{panel_prepend} ||= "<table$opt->{panel_table_extra}>";
4542 $opt->{panel_append} ||= '</table>';
4543 push @put, tabbed_display(\@titles,\@tabcont,$opt);
4546 #::logDebug("titles=" . uneval(\@titles) . "\ncontrols=" . uneval(\@controls));
4548 for(my $i = 0; $i < @controls; $i++) {
4549 push @put, tag_attr_list(
4550 $opt->{break_template},
4551 { FIRST => ! $first++, ROW => $titles[$i] },
4554 push @put, create_rows($opt, $controls[$i]);
4558 for(my $i = $firstout; $i < @out; $i++) {
4559 #::logDebug("$out[$i] content length=" . length($outhash{$out[$i]} ));
4560 push @put, @outhash{$out[$i]};
4562 return join "", @put;
4565 sub convert_old_template {
4567 $string =~ s/\$WIDGET\$/{WIDGET}/g
4569 $string =~ s!\{HELP_URL\}(.*)\{/HELP_URL\}!{HELP_URL?}$1\{/HELP_URL?}!gs;
4570 $string =~ s/\$HELP\$/{HELP}/g;
4571 $string =~ s/\$HELP_URL\$/{HELP_URL}/g;
4572 $string =~ s/\~META\~/{META_STRING}/g;
4573 $string =~ s/\$LABEL\$/{LABEL}/g;
4574 $string =~ s/\~ERROR\~/{LABEL}/g;
4575 $string =~ s/\~TKEY\~/{TKEY}/g;
4576 $string =~ s/\~BLABEL\~/{BLABEL}/g;
4577 $string =~ s/\~ELABEL\~/{ELABEL}/g;
4582 my ($opt, $columns) = @_;
4585 my $rowdiv = $opt->{across} || 1;
4586 my $cells_per_span = $opt->{cell_span} || 2;
4588 my $span = $rowdiv * $cells_per_span;
4589 my $oddspan = $span - 1;
4590 my $colspan = $opt->{colspan};
4591 #::logDebug("colspan=" . ::uneval($colspan));
4595 for my $c (@$columns) {
4597 $colname =~ s/^COLUMN_//;
4598 #::logDebug("doing column $c name=$colname");
4599 # If doesn't exist, was brought in before.
4600 my $ref = delete $outhash{$c}
4603 #::logDebug("outputting ROW $c=$ref->{ROW}");
4604 my $tpl = $ref->{TEMPLATE} || $opt->{combo_template};
4605 push @out, tag_attr_list($tpl, $ref);
4610 $w .= "<tr$opt->{data_row_extra}>\n" unless $rowcount++ % $rowdiv;
4611 if(my $s = $colspan->{$colname}) {
4612 #::logDebug("found colspan=$s (ref=$ref->{COLSPAN}) for $colname");
4613 my $extra = ($s - 1) / $cells_per_span;
4614 $rowcount += $extra;
4616 $w .= tag_attr_list($ref->{TEMPLATE}, $ref);
4617 $w .= "</tr>" unless $rowcount % $rowdiv;
4621 if($rowcount % $rowdiv) {
4623 while($rowcount % $rowdiv) {
4624 $w .= '<td colspan="$cells_per_span"> </td>';
4630 return join "\n", @out;