1 # Vend::Data - Interchange databases
3 # Copyright (C) 2002-2009 Interchange Development Group
4 # Copyright (C) 1996-2002 Red Hat, Inc.
6 # This program was originally based on Vend 0.2 and 0.3
7 # Copyright 1995 by Andrew M. Wilcox <amw@wilcoxsolutions.com>
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation and modified by the Interchange license;
12 # either version 2 of the License, or (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public
20 # License along with this program; if not, write to the Free
21 # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
47 product_code_exists_ref
48 product_code_exists_tag
59 @EXPORT_OK = qw(update_productbase column_index);
62 no warnings qw(uninitialized numeric);
65 use Vend::Interpolate;
66 use Vend::Table::Common qw(import_ascii_delimited);
68 File::Basename::fileparse_set_fstype($^O);
73 require Vend::Table::DBI;
74 require Vend::Table::DBI_CompositeKey;
79 require Vend::Table::LDAP;
83 require Vend::Table::GDBM;
85 if($Global::DB_File) {
86 require Vend::Table::DB_File;
88 require Vend::Table::InMemory;
89 require Vend::Table::Shadow;
92 my ($Products, $Item_price);
94 sub instant_database {
96 return undef unless $file =~ /\.(txt|asc)$/;
97 my $dir = File::Basename::dirname($file);
98 my $fname = File::Basename::basename($file);
102 $Vend::Database{$dbname}
103 and return $Vend::Database{$dbname}->ref();
104 $Vend::WriteDatabase{$file} and $Vend::WriteDatabase{$dbname} = 1;
105 if( file_name_is_absolute($_[0]) ) {
107 "Instant database (%s): no absolute file names.",
116 "Instant database (%s): no file found.",
122 return $Vend::Database{$dbname} = import_database({
127 Class => 'TRANSIENT',
128 EXPORT_ON_CLOSE => 1,
132 sub database_exists_ref {
134 return $_[0]->ref() if ref $_[0];
135 return $Vend::Interpolate::Db{$_[0]}
136 if $Vend::Interpolate::Db{$_[0]};
137 $Vend::Database{$_[0]}
138 and return $Vend::Database{$_[0]}->ref();
139 return instant_database(@_);
142 sub database_key_exists {
144 return $db->record_exists($key);
147 sub product_code_exists_ref {
148 my ($code, $base) = @_;
152 return undef unless $ref = $Vend::Productbase{$base};
153 return $ref->ref() if $ref->record_exists($code);
157 foreach $ref (@Vend::Productbase) {
158 return ($return = $ref) if $ref->record_exists($code);
163 sub product_code_exists_tag {
164 my ($code, $base) = @_;
166 return undef unless $Vend::Productbase{$base};
167 return $base if $Vend::Productbase{$base}->record_exists($code);
171 foreach my $ref (@Vend::Productbase) {
172 return $Vend::Basefinder{$ref} if $ref->record_exists($code);
178 return tie_database() if $_[0] || $Global::AcrossLocks;
182 sub update_productbase {
185 return unless defined $Vend::Productbase{$_[0]};
187 undef @Vend::Productbase;
188 for(@{$Vend::Cfg->{ProductFiles}}) {
189 unless ($Vend::Database{$_}) {
190 die "$_ not a database, cannot use as products file\n";
192 $Vend::Productbase{$_} = $Vend::Database{$_};
193 $Vend::Basefinder{$Vend::Database{$_}} = $_;
194 push @Vend::Productbase, $Vend::Database{$_};
195 $Vend::OnlyProducts = $_;
198 undef $Vend::OnlyProducts if scalar @Vend::Productbase > 1;
200 $Products = $Vend::Productbase[0];
201 #::logError("Productbase: '@Vend::Productbase' --> " . uneval(\%Vend::Basefinder));
206 my ($code, $q, $base) = @_;
208 $base = $Vend::Basefinder{$base}
215 mv_ib => $base || undef,
221 sub product_category {
222 my ($code, $base) = @_;
223 return "" unless $base = product_code_exists_ref($code, $base || undef);
224 return database_field($base, $code, $Vend::Cfg->{CategoryField});
227 sub product_description {
228 my ($code, $base) = @_;
229 return "" unless $base = product_code_exists_ref($code, $base || undef);
230 return database_field($base, $code, $Vend::Cfg->{DescriptionField});
234 my ($db, $key, $field_name, $foreign) = @_;
235 #::logDebug("database_field: " . uneval_it(\@_));
236 $db = database_exists_ref($db) or return undef;
237 return '' unless defined $db->test_column($field_name);
238 $key = $db->foreign($key, $foreign) if $foreign;
239 return '' unless $db->test_record($key);
240 return $db->field($key, $field_name);
245 $db = database_exists_ref($db) or return undef;
246 return '' unless $db->test_record($key);
247 return $db->row_hash($key);
250 sub increment_field {
251 my ($db, $key, $field_name, $adder) = @_;
253 return undef unless $db->test_record($key);
254 return undef unless defined $db->test_column($field_name);
255 #::logDebug(__PACKAGE__ . "increment_field: " . uneval_it(\@_));
256 return $db->inc_field($key, $field_name, $adder);
260 my($base, $method, @args) = @_;
262 my $db = ref $base ? $base : $Vend::Database{$base};
270 my ($table, $type, $options, $text) = @_;
271 #::logDebug("Called import_text: table=$table type=$type opt=" . Data::Dumper::Dumper($options) . " text=$text");
272 my ($delimiter, $record_delim) = find_delimiter($type);
273 my $db = $Vend::Database{$table}
274 or die ("Non-existent table '$table'.\n");
278 @columns = ($db->columns());
280 if($options->{'continue'}) {
281 $options->{CONTINUE} = uc $options->{'continue'};
282 $options->{NOTES_SEPARATOR} = uc $options->{separator}
283 if defined $options->{separator};
286 my $sub = sub { return $db };
288 my $fn = $Vend::Cfg->{ScratchDir} . "/import.$$.$now";
292 if($delimiter eq 'CSV') {
294 $add .= join '","', @columns;
296 $text = "$add\n$text";
299 $options->{field_names} = \@columns;
300 $options->{delimiter} = $options->{DELIMITER} = $delimiter;
303 if($options->{file}) {
304 $fn = $options->{file};
305 Vend::File::allowed_file($fn)
306 or die ::errmsg("No absolute file names like '%s' allowed.\n", $fn);
309 # data is already in memory, do not create a temporary file
310 $options->{scalar_ref} = 1;
315 local($/) = $record_delim if defined $record_delim;
317 $options->{Object} = $db;
319 ## This is where the actual import happens
320 Vend::Table::Common::import_ascii_delimited($fn, $options);
323 unlink $fn unless $options->{'file'} or $options->{scalar_ref};
328 my ($db, $key, $field_name, $value, $append, $foreign) = @_;
330 $db = database_exists_ref($db);
331 return undef unless defined $db->test_column($field_name);
333 $key = $db->foreign($key, $foreign)
336 # Create it if it doesn't exist
337 unless ($db->record_exists($key)) {
341 $value = $db->field($key, $field_name) . $value;
343 return $db->set_field($key, $field_name, $value);
348 my $db = product_code_exists_ref($code) or return;
349 return $db->row($code);
352 sub product_row_hash {
354 my $db = product_code_exists_ref($code) or return;
355 return $db->row_hash($code);
359 my ($field_name, $code, $base) = @_;
360 #::logDebug("product_field: name=$field_name code=$code base=$base");
361 return database_field($Vend::OnlyProducts, $code, $field_name)
362 if $Vend::OnlyProducts;
363 #::logDebug("product_field: onlyproducts=$Vend::OnlyProducts");
365 $db = product_code_exists_ref($code, $base || undef)
367 #::logDebug("product_field: exists db=$db");
368 return "" unless defined $db->test_column($field_name);
369 return $db->field($code, $field_name);
374 my ($field_name, $code, $emptyok) = @_;
375 #::logDebug("product_field: name=$field_name code=$code base=$base");
377 for(@{$Vend::Cfg->{ProductFiles}}) {
378 my $db = database_exists_ref($_)
380 next unless defined $db->test_column($field_name);
381 $result = database_field($db, $code, $field_name);
382 last if $emptyok or length($result);
412 my ($field_name) = @_;
413 $Products = $Products->ref();
414 return undef unless defined $Products->test_column($field_name);
415 return $Products->column_index($field_name);
419 my ($field_name) = @_;
420 return defined $Products->test_column($field_name);
423 sub db_column_exists {
424 my ($db,$field_name) = @_;
425 return defined $db->test_column($field_name);
431 while( ($name) = each %Vend::Database ) {
432 $Vend::Database{$name}->close_table()
433 unless defined $Vend::Cfg->{SaveDatabase}{$name};
434 delete $Vend::Database{$name};
436 undef %Vend::Table::DBI::DBI_connect_bad;
437 undef %Vend::TransactionDatabase;
438 undef %Vend::WriteDatabase;
439 undef %Vend::Basefinder;
440 undef $Vend::VarDatabase;
444 my $db = $_[0] || $Products;
445 return $db->ref() if $db;
451 # Read in the shipping file.
453 *read_shipping = \&Vend::Interpolate::read_shipping;
455 # Read in the sales tax file.
459 return unless $Vend::Cfg->{SalesTax};
460 return if $Vend::Cfg->{SalesTax} eq 'multi';
461 my $file = $Vend::Cfg->{Special}{'salestax.asc'};
462 $file = Vend::File::catfile($Vend::Cfg->{ProductDir}, "salestax.asc")
465 $Vend::Cfg->{SalesTaxTable} = {};
467 my @lines = split /\n/, readfile($file);
470 ($code, $percent) = split(/\s+/, $_, 2);
471 $Vend::Cfg->{SalesTaxTable}->{"\U$code"} = $percent;
474 if(not defined $Vend::Cfg->{SalesTaxTable}->{DEFAULT}) {
475 $Vend::Cfg->{SalesTaxTable}->{DEFAULT} = 0;
483 3 => ["\n%%\n", "\n%%%\n"],
490 LINE => ["\n", "\n\n"],
491 '%%%' => ["\n%%\n", "\n%%%\n"],
492 '%%' => ["\n%%\n", "\n%%%\n"],
496 "\t" => ["\t", "\n"],
498 "\n%%\n" => ["\n%%\n", "\n%%%\n"],
505 return @{$Delimiter{$type}}
506 if defined $Delimiter{$type};
517 or die errmsg("Cannot open database text source file %s: %s\n", $fn, $!);
523 if(! $tried_plain and $_) {
528 if (/^\Q$char\E+$/) {
529 ($fdelim, $rdelim) = ($char, "\n");
533 $tried_plain++ or next;
544 $type = 'CSV' if $fdelim eq ',';
545 if($type and defined $Delimiter{$type}) {
546 ($fdelim, $rdelim) = @{$Delimiter{$type}};
548 return ($fdelim, $rdelim);
551 use vars '%db_config';
555 'DBI_CompositeKey' => {
559 Class Vend::Table::DBI_CompositeKey
566 Class Vend::Table::DBI
572 Class Vend::Table::Shadow
579 Class Vend::Table::InMemory
587 Class Vend::Table::InMemory
595 Class Vend::Table::GDBM
603 Class Vend::Table::DB_File
611 Class Vend::Table::SDBM
613 FileExtensions => [ qw/dir pag/ ],
620 Class Vend::Table::LDAP
628 if($Global::Database) {
629 copyref($Global::Database, $Vend::Cfg->{Database});
632 my @tables = keys %{$Vend::Cfg->{Database}};
639 foreach $name (@tables) {
640 $data = $Vend::Cfg->{Database}{$name} || {};
641 if(! $redone and $data->{MIRROR}) {
642 #::logDebug("mirror database $name, delaying");
644 push @delayed, $name;
647 if(! $data->{name}) {
650 if( $data->{type} > 6 or $data->{HOT} or $data->{IMPORT_ONCE} ) {
652 $Vend::Database{$name} = import_database($data);
655 my $msg = "table '%s' failed: %s";
656 $msg = errmsg($msg, $name, $@);
661 if($data->{GUESS_NUMERIC}) {
662 my $dir = $data->{DIR} || $Vend::Cfg->{ProductDir};
663 my $fn = Vend::Util::catfile( $dir, $data->{file} );
664 my @fields = grep /\S/, split /\s+/, readfile("$fn.numeric");
665 $data->{NUMERIC} = {};
667 $data->{NUMERIC}{$_} = 1;
670 my $class = $db_config{$data->{Class}}->{Class};
671 $Vend::Database{$name} = new $class ($data);
675 # So mirrors will not happen until after mirror source
684 update_productbase();
689 while (($name,$data) = each %{$Vend::Cfg->{Database}}) {
690 if (defined $Vend::Cfg->{SaveDatabase}{$name}) {
691 $Vend::Database{$name} = $Vend::Cfg->{SaveDatabase}{$name};
694 my $class = $db_config{$data->{Class}}->{Class};
696 $Vend::Database{$name} =
700 logGlobal("Error creating dummy database for $name: $@");
703 update_productbase();
708 sub create_empty_txt {
709 my ($obj, $database_txt, $delimiter, $record_delim) = @_;
710 return if -f $database_txt;
711 return unless $obj->{CREATE_EMPTY_TXT};
713 if($obj->{CREATE_EMPTY_TXT} =~ /[\s,]\w/) {
714 $ary = [ grep /\S/, split /[\s,]+/, $obj->{CREATE_EMPTY_TXT} ];
719 unless (ref($ary) eq 'ARRAY') {
720 logError("Cannot create text file with no database NAME parameter and no field names in CREATE_EMPTY_TXT");
724 $record_delim ||= "\n";
725 my $line = join $obj->{DELIMITER}, @$ary;
726 $line .= $record_delim;
727 Vend::Util::writefile($database_txt, $line);
732 sub import_database {
733 my ($obj, $dummy) = @_;
736 my $database = $obj->{'file'};
737 my $type = $obj->{'type'};
738 my $name = $obj->{'name'};
740 #my @caller = caller();
741 #::logDebug ("enter import_database: dummy=$dummy");
742 #::logDebug("opening table table=$database config=" . uneval($obj) . " caller=@caller");
744 #::logDebug ("database=$database type=$type name=$name obj=" . uneval($obj));
745 #::logDebug ("database=$database type=$type name=$name obj=" . uneval($obj)) if $obj->{HOT};
748 return $Vend::Cfg->{SaveDatabase}->{$name}
749 if defined $Vend::Cfg->{SaveDatabase}->{$name};
751 my ($delimiter, $record_delim, $change_delimiter, $cacheable);
752 my ($base,$path,$tail,$dir,$database_txt);
754 die "import_database: No database name!\n"
759 my $new_database_dbm;
765 my $no_import = defined $Vend::Cfg->{NoImport}->{$name} || $obj->{NO_IMPORT};
767 if (defined $Vend::ForceImport{$name}) {
769 delete $Vend::ForceImport{$name};
772 $base = $obj->{'name'};
773 $dir = $obj->{DIR} if defined $obj->{DIR};
775 if ($obj->{OrigClass}) {
776 my $ref = $db_config{$obj->{OrigClass} || $Global::Default_database};
777 $class_config = {%$ref};
778 $class_config->{Class} = $db_config{$obj->{Class}}->{Class};
779 $class_config->{OrigClass} = $obj->{OrigClass};
781 $class_config = $db_config{$obj->{Class} || $Global::Default_database};
784 #::logDebug ("params=$database_txt path='$path' base='$base' tail='$tail' dir='$dir'") if $type == 9;
789 last IMPORT if $no_import and $obj->{DIR};
790 #::logDebug ("no_import_check: once=$obj->{IMPORT_ONCE} dir=$obj->{DIR}");
791 last IMPORT if defined $obj->{IMPORT_ONCE} and $obj->{DIR};
792 #::logDebug ("first no_import_check: passed") if $type == 9;
794 $database_txt = $database;
796 ($base,$path,$tail) = fileparse $database_txt, '\.[^/.]+$';
798 if(Vend::Util::file_name_is_absolute($database_txt)) {
799 unless (allowed_file($database_txt)) {
801 "Security violation, trying to import %s",
805 die "Security violation.\n";
810 $dir = $obj->{DIR} || $Vend::Cfg->{ProductDir} || $Global::ConfigDir;
811 $database_txt = Vend::Util::catfile($dir,$database_txt);
816 $obj->{ObjectType} = $class_config->{Class};
818 my $dot = $obj->{HIDE_AUTO_FILES} ? '.' : '';
820 $obj->{AUTO_NUMBER_FILE} = Vend::File::make_absolute_file(
821 $obj->{AUTO_NUMBER_FILE} || "$dir/$dot$obj->{name}.autonumber"
824 if($class_config->{Extension}) {
825 $database_dbm = Vend::Util::catfile(
828 $class_config->{Extension}
830 $new_database_dbm = Vend::Util::catfile(
833 $class_config->{Extension}
837 if($class_config->{TableExtension}) {
838 $table_name = $database_dbm;
839 $new_table_name = $new_database_dbm;
842 $table_name = $new_table_name = $base;
845 $cacheable = $class_config->{Cacheable} || undef;
847 if ($class_config->{RestrictedImport}) {
848 $obj->{db_file_extended} = $database_dbm;
850 $Vend::Cfg->{NoImportExternal}
852 or (! $obj->{CREATE_EMPTY_TXT} and ! -f $database_txt)
858 open(Vend::Data::TMP, ">$new_database_dbm");
859 print Vend::Data::TMP "\n";
860 close(Vend::Data::TMP);
865 if($obj->{Mirror_complete}) {
869 #::logDebug ("table $new_table_name: undeffing $database_dbm, hot=$obj->{HOT}");
875 last IMPORT if $no_import;
876 #::logDebug ("moving to import") if $type == 9;
878 $change_delimiter = $obj->{DELIMITER} if defined $obj->{DELIMITER};
883 ! defined $database_dbm
884 or ! -e $database_dbm
886 or ($txt_time = file_modification_time($database_txt, $obj->{PRELOAD}))
888 ($dbm_time = file_modification_time($database_dbm))
891 warn "Importing $obj->{'name'} table from $database_txt\n"
894 $type = 1 unless $type;
895 ($delimiter, $record_delim) = find_delimiter($change_delimiter || $type);
898 ($delimiter, $record_delim) = auto_delimiter($database_txt);
901 $obj->{delimiter} = $obj->{DELIMITER} = $delimiter;
905 local($/) = $record_delim if defined $record_delim;
907 if($obj->{CREATE_EMPTY_TXT}) {
908 create_empty_txt($obj, $database_txt, $delimiter, $record_delim);
912 $db = Vend::Table::Common::import_from_ic_db(
919 $db = Vend::Table::Common::import_ascii_delimited(
927 if(defined $database_dbm) {
928 $db->close_table() if defined $db;
930 unlink $database_dbm if $Global::Windows;
931 if($class_config->{FileExtensions}) {
932 open(TOUCH, ">>$database_dbm")
933 or die "Couldn't freshen $database_dbm: $_";
935 or die "Couldn't freshen $database_dbm: $_";
936 for(@{$class_config->{FileExtensions}}) {
937 my ($old, $new) = ("$new_database_dbm.$_", "$database_dbm.$_");
940 "Couldn't move '$old' to '$new': $!\n";
944 rename($new_database_dbm, $database_dbm)
945 or die "Couldn't move '$new_database_dbm' to '$database_dbm': $!\n";
949 elsif ($obj->{AUTO_EXPORT} and $dbm_time > $txt_time) {
950 $obj->{export_now} = 1;
957 if($obj->{WRITE_CONTROL}) {
958 if($obj->{READ_ONLY}) {
959 $obj->{Read_only} = 1;
961 elsif($obj->{WRITE_ALWAYS}) {
962 $obj->{Read_only} = 0;
964 elsif($obj->{WRITE_CATALOG}) {
965 $obj->{Read_only} = $obj->{WRITE_CATALOG}{$Vend::Cat}
966 ? (! defined $Vend::WriteDatabase{$name})
969 elsif(! defined $obj->{WRITE_TAGGED} or $obj->{WRITE_TAGGED}) {
970 $obj->{Read_only} = ! defined $Vend::WriteDatabase{$name};
974 $obj->{Read_only} = ! defined $Vend::WriteDatabase{$name}
975 if $class_config->{Tagged_write};
978 $obj->{Transactions} = 1 if $Vend::TransactionDatabase{$name};
980 if($class_config->{Extension}) {
982 $obj->{db_file} = $table_name unless $obj->{db_file};
983 $obj->{db_text} = $database_txt unless $obj->{db_text};
985 #::logDebug("ready to try opening db $table_name") if ! $db;
988 if (exists $Vend::Interpolate::Db{$class_config->{Class}}) {
989 $db = $Vend::Interpolate::Db{$table_name}->open_table( $obj, $obj->{db_file} );
991 die errmsg("no access for database %s", $table_name);
995 $db = $class_config->{Class}->open_table( $obj, $obj->{db_file} );
997 $obj->{NAME} = $db->[$Vend::Table::Common::COLUMN_INDEX]
998 unless defined $obj->{NAME};
999 #::logDebug("didn't die but no db") if ! $db;
1002 #::logDebug("db=$db, \$\!='$!' \$\@='$@' (" . length($@) . ")\n") if ! $db;
1004 #::logDebug("Dieing of $@");
1005 die $@ unless $no_import;
1006 die $@ if $tried_import++;
1007 if(! -f $database_dbm) {
1008 $Vend::ForceImport{$obj->{name}} = 1;
1009 return import_database($obj);
1013 undef $tried_import;
1014 #::logDebug("Opening $obj->{name}: RO=$obj->{Read_only} WC=$obj->{WRITE_CONTROL} WA=$obj->{WRITE_ALWAYS}");
1017 if(defined $cacheable) {
1018 $Vend::Cfg->{SaveDatabase}->{$name} = $db;
1021 $Vend::Basefinder{$db} = $name;
1026 sub index_database {
1027 my($dbname, $opt) = @_;
1029 return undef unless defined $dbname;
1032 $db = database_exists_ref($dbname)
1034 logError("Vend::Data export: non-existent database %s", $dbname);
1040 my $ext = $opt->{extension} || 'idx';
1042 my $db_fn = $db->config('db_file');
1043 my $bx_fn = $opt->{basefile} || $db->config('db_text');
1044 my $ix_fn = "$bx_fn.$ext";
1045 my $type = $opt->{type} || $db->config('type');
1048 # "dbname=$dbname db_fn=$db_fn bx_fn=$bx_fn ix_fn=$ix_fn\n" .
1049 # "options: " . uneval($opt) . "\n"
1054 file_modification_time($db_fn)
1056 file_modification_time($bx_fn) )
1058 export_database($dbname, $bx_fn, $type);
1061 return if $opt->{export_only};
1065 file_modification_time($ix_fn)
1067 file_modification_time($bx_fn) )
1069 # We didn't need to index if got here
1073 if(! $opt->{spec}) {
1074 $opt->{fn} = $opt->{fn} || $opt->{fields} || $opt->{col} || $opt->{columns};
1075 my $key = $db->config('KEY');
1076 my @fields = grep $_ ne $key, split /[\0,\s]+/, $opt->{fn};
1077 my $sort = join ",", @fields;
1079 logError(errmsg("index attempted on table '%s' with no fields, no search spec", $dbname));
1082 $opt->{spec} = <<EOF;
1089 my $scan = Vend::Interpolate::escape_scan($opt->{spec});
1090 $scan =~ s:^scan/::;
1094 mv_search_file => $bx_fn,
1097 Vend::Scan::find_search_params($c, $scan);
1099 $c->{mv_matchlimit} = 100000
1100 unless defined $c->{mv_matchlimit};
1101 my $f_delim = $c->{mv_return_delim} || "\t";
1102 my $r_delim = $c->{mv_record_delim} || "\n";
1105 if($c->{mv_return_fields}) {
1106 @fn = split /\s*[\0,]+\s*/, $c->{mv_return_fields};
1109 #::logDebug( "search options: " . uneval($c) . "\n");
1111 open(Vend::Data::INDEX, "+<$ix_fn") or
1112 open(Vend::Data::INDEX, "+>$ix_fn") or
1113 die "Couldn't open $ix_fn: $!\n";
1114 lockfile(\*Vend::Data::INDEX, 1, 1)
1115 or die "Couldn't exclusive lock $ix_fn: $!\n";
1116 open(Vend::Data::INDEX, "+>$ix_fn") or
1117 die "Couldn't write $ix_fn: $!\n";
1121 print INDEX join $f_delim, @fn;
1122 print INDEX $r_delim;
1125 my $ref = Vend::Scan::perform_search($c);
1127 print INDEX join $f_delim, @$_;
1128 print INDEX $r_delim;
1131 unlockfile(\*Vend::Data::INDEX)
1132 or die "Couldn't unlock $ix_fn: $!\n";
1133 close(Vend::Data::INDEX)
1134 or die "Couldn't close $ix_fn: $!\n";
1135 return 1 if $opt->{show_status};
1139 sub export_database {
1140 my($db, $file, $type, $opt) = @_;
1141 return undef unless defined $db;
1143 my (@data, $field, $delete);
1145 $field = $opt->{field} if $opt->{field};
1146 $delete = $opt->{delete} if $opt->{delete};
1148 $db = database_exists_ref($db)
1150 logError("Vend::Data export: non-existent database %s" , $db);
1156 if ($Vend::Cfg->{NoExportExternal} and !$opt->{force}) {
1157 # Skip export only for "external" tables (currently SQL and LDAP),
1158 # just like NoImportExternal does
1159 my $class = $db->config('Class');
1160 my $class_config = $db_config{$class || $Global::Default_database};
1161 return 1 if $class_config->{RestrictedImport};
1164 my $table_name = $db->config('name');
1166 return 1 if $Vend::Cfg->{NoExport}{$table_name} and !$opt->{force};
1169 if($qual = $opt->{where}) {
1173 my $val = $db->quote($qual->{$_}, $_);
1174 push @out, "$_ = $val";
1176 $qual = 'WHERE ' . join(" AND ", @out);
1178 elsif($qual !~ /^\s*where\s+/i) {
1179 $qual = "WHERE $qual";
1184 if("\U$type" eq 'NOTES') {
1189 my ($delim, $record_delim) = find_delimiter($type || $db->config('type'));
1190 $delim or ($delim, $record_delim) = find_delimiter($db->config('DELIMITER'));
1191 $delim or ($delim, $record_delim) = find_delimiter('TAB');
1193 $file = $file || $db->config('file');
1194 my $dir = $db->config('DIR');
1196 $file = Vend::Util::catfile( $dir, $file)
1197 unless Vend::Util::file_name_is_absolute($file);
1199 my @cols = $db->columns();
1201 my ($notouch, $nuke);
1202 if ($field and ! $delete) {
1203 #::logDebug("Trying for add field=$field delete=$delete");
1204 if($db->column_exists($field)) {
1206 "Can't define column '%s' twice in table '%s'",
1212 logError("Adding column %s to table %s" , $field, $table_name);
1217 #::logDebug("Trying for delete field=$field delete=$delete");
1218 if(! $db->column_exists($field)) {
1220 "Can't delete non-existent column '%s' in table '%s'",
1226 logError("Deleting column %s from table %s" , $field, $table_name);
1231 unless ($_ eq $field) {
1237 logError("Deleting field %s" , $_ );
1244 open(EXPORT, "+<$file") or
1245 open(EXPORT, "+>$file") or
1246 die "Couldn't open $file: $!\n";
1247 lockfile(\*EXPORT, 1, 1)
1248 or die "Couldn't exclusive lock $file: $!\n";
1249 open(EXPORT, "+>$file") or
1250 die "Couldn't write $file: $!\n";
1252 #::logDebug("EXPORT_SORT=" . $db->config('EXPORT_SORT'));
1253 if($opt->{sort} ||= $db->config('EXPORT_SORT')) {
1254 #::logDebug("Found EXPORT_SORT=$opt->{sort}");
1255 my ($sort_field, $sort_option) = split /:/, $opt->{sort};
1256 #::logDebug("Found sort_field=$sort_field sort_option=$sort_option");
1257 $db->sort_each($sort_field, $sort_option);
1260 if($delim eq 'CSV') {
1263 print EXPORT join $delim, @cols;
1264 print EXPORT qq%"\n%;
1265 while( (undef, @data) = $db->each_record($qual) ) {
1267 splice(@data, $nuke, 1) if defined $nuke;
1268 $tempdata = join $delim, @data;
1269 $tempdata =~ tr/\n/\r/;
1270 print EXPORT $tempdata;
1271 print EXPORT qq%"\n%;
1274 elsif ($delim eq "\n" and $notes || $db->config('CONTINUE') eq 'NOTES') {
1278 if($db->config('CONTINUE') eq 'NOTES') {
1279 $sep = $db->config('NOTES_SEPARATOR');
1284 $sep = $opt->{notes_separator} || "\f";
1285 $nf = $opt->{notes_field} || 'notes_field';
1286 for( my $i = 0; $i < @cols; $i++ ) {
1287 next unless $cols[$i] eq $nf;
1291 $nf_col = scalar @cols if ! defined $nf_col;
1292 splice(@cols, $nf_col, 1);
1294 print EXPORT join "\n", @cols;
1295 print EXPORT "\n$nf $sep\n\n";
1297 while( (undef, @data) = $db->each_record($qual) ) {
1298 splice(@data, $nuke, 1) if defined $nuke;
1299 my $nd = splice(@data, $nf_col, 1);
1300 # Yes, we don't want the last field yet. 8-)
1301 for($i = 0; $i < $#data; $i++) {
1302 next if $data[$i] eq '';
1303 $data[$i] =~ tr/\n/\r/;
1305 "$cols[$i]: $data[$i]\n" unless $data[$i] eq '';
1307 print EXPORT "\n$nd\n$sep\n";
1310 elsif($record_delim eq "\n") {
1311 print EXPORT join $delim, @cols;
1312 print EXPORT $record_delim;
1313 my $detab = ($delim eq "\t") ? 1 : 0;
1315 while( (undef, @data) = $db->each_record($qual) ) {
1316 splice(@data, $nuke, 1);
1317 if ($detab) { s/\t/ /g for @data; }
1318 $tempdata = join $delim, @data;
1319 $tempdata =~ s/\r?\n/\r/g;
1320 print EXPORT $tempdata, $record_delim;
1324 while( (undef, @data) = $db->each_record($qual) ) {
1325 if ($detab) { s/\t/ /g for @data; }
1326 $tempdata = join $delim, @data;
1327 $tempdata =~ s/\r?\n/\r/g;
1328 print EXPORT $tempdata, $record_delim;
1333 print EXPORT join $delim, @cols;
1334 print EXPORT $record_delim;
1335 my $detab = ($delim eq "\t" or $record_delim eq "\t") ? 1 : 0;
1336 while( (undef, @data) = $db->each_record($qual) ) {
1337 splice(@data, $nuke, 1) if defined $nuke;
1338 if ($detab) { s/\t/ /g for @data; }
1339 print EXPORT join($delim, @data);
1340 print EXPORT $record_delim;
1343 unlockfile(\*EXPORT)
1344 or die "Couldn't unlock $file: $!\n";
1346 or die "Couldn't close $file: $!\n";
1347 if(defined $notouch) {
1348 my $f = $db->config('db_file_extended');
1354 if (my $subs = $db->config('POSTEXPORT')) {
1355 # Make a copy of the options once to hand off to each sub.
1356 my $options = { %$opt, delim => $delim, record_delim => $record_delim };
1357 for my $name (@$subs) {
1358 my $sub = $Vend::Cfg->{Sub}{$name} || $Global::GlobalSub->{$name}
1360 logError("Unknown POSTEXPORT sub '%s' on database '%s'.", $name, $db->name);
1363 $sub->($db->name, $file, $options)
1364 or logError("Failed call to POSTEXPORT sub '%s' on database '%s'!", $name, $db->name);
1374 return if not defined $opt_remap;
1376 if($opt_remap and $record) {
1380 while (($k, $v) = each %opt_map) {
1381 next unless defined $record->{$v};
1382 $rec{$k} = $record->{$v};
1385 delete @{$record}{@del};
1386 @{$record}{keys %rec} = (values %rec);
1388 elsif($::Variable->{MV_OPTION_TABLE_MAP}) {
1389 $opt_remap = $::Variable->{MV_OPTION_TABLE_MAP};
1390 $opt_remap =~ s/^\s+//;
1391 $opt_remap =~ s/\s+$//;
1392 map { m{(.*?)=(.*)} and $opt_map{$2} = $1} split /[\0,\s]+/, $opt_remap;
1394 remap_options($record);
1403 my ($item, $raw) = @_;
1405 return $raw if $raw =~ /^[\d.]*$/;
1412 if($raw =~ /^\[\B/ and $raw =~ /\]$/) {
1413 my $ref = Vend::Interpolate::tag_calc($raw);
1414 @p = ref $ref ? @{$ref} : $ref;
1417 @p = Text::ParseWords::shellwords($raw);
1419 if(scalar @p > ($::Limit->{chained_cost_levels} || 64)) {
1420 logError('Too many chained cost levels for item ' . uneval($item) );
1424 #::logDebug("chain_cost item = " . uneval ($item) . "\np=" . uneval(\@p) );
1425 my ($chain, $percent);
1429 foreach $price (@p) {
1430 next if ! length($price);
1431 if($its++ > ($::Limit->{chained_cost_levels} || 64)) {
1432 logError('Too many chained cost levels for item ' . uneval($item) );
1438 $passed_key = $price;
1442 if ($price =~ s/^;//) {
1445 $price =~ s/,$// and $chain = 1;
1446 if ($price =~ /^ \( \s* (.*) \s* \) \s* $/x) {
1450 if ($price =~ s/^([^-+\d.].*)//s) {
1452 if($mod =~ s/^\$(\d|$)/$1/) {
1453 $price = $item->{mv_price} || $mod;
1454 if($price =~ /^\s*free\s*$/i) {
1460 elsif($mod =~ /^(\w*):([^:]*)(?::(\S*))?$/) {
1461 my ($table, $field, $key) = ($1, $2, $3);
1462 #::logDebug("field begins as '$field'");
1463 $field = $Vend::Cfg->{PriceDefault} if ! $field;
1465 (! $key and $key = $passed_key)
1467 (! $field and $field = $passed_key)
1469 (! $table and $table = $passed_key);
1472 $table = $item->{mv_ib} || $Vend::Cfg->{ProductFiles}[0]
1474 if($key and defined $item->{$key}) {
1475 $key = $item->{$key};
1478 if($field =~ /,/ || $field =~ /\.\./) {
1479 my (@tmp) = split /,/, $field;
1481 if (/(.+?)(\d+)\.\.+.+?(\d+)/) {
1482 push @breaks, map { "$1$_" } $2 .. $3;
1490 #::logDebug("price breaks: " . join(',', @breaks));
1493 $attribute = shift @breaks if $breaks[0] !~ /\d/;
1494 if (! $attribute || ! $item->{$attribute}) {
1495 $quantity = $item->{quantity};
1499 $regex = $item->{$attribute}
1500 unless $item->{$attribute} =~ /^[\d.]+$/;
1501 $quantity = Vend::Util::tag_nitems(
1504 qualifier => $attribute,
1505 compare => $regex || undef,
1510 $field = shift @breaks;
1513 redo CHAIN if $quantity < $test;
1515 my $t = $table || $item->{mv_ib} || $Vend::Cfg->{ProductFiles}[0];
1516 my $k = $key || $item->{code};
1517 my $row = database_row($t, $k);
1518 #::logDebug("database reference to price breaks found table=$t key=$k row=" . ::uneval($row));
1519 redo CHAIN if ref $row ne 'HASH';
1522 $keep = $row->{$field}
1523 if length($row->{$field}) && $row->{$field} != 0;
1525 next unless exists $row->{$_};
1528 last if $test > $quantity;
1530 $keep = $row->{$field} if $row->{$field} != 0;
1532 #::logDebug("price=$keep") if $keep;
1533 $price = $keep if $keep;
1536 $price = database_field(
1537 ($table || $item->{mv_ib} || $Vend::Cfg->{ProductFiles}[0]),
1538 ($key || $item->{code}),
1541 #::logDebug("database reference found table=$table field=$field key=$key|$item->{$key}|$item->{code} price=$price");
1544 elsif ($mod =~ s/(\w+)=(.*)//) {
1546 my(@args) = split /:/, $2;
1547 my $sub = # $intrinsic_price{$tag} ||
1548 $Vend::Cfg->{Sub}{$tag} || $Global::GlobalSub->{$tag};
1553 my($k, $v) = split /=/, $_;
1558 $i{passed_key} = $passed_key if $passed_key;
1561 $price = $sub->(\%i);
1564 $price = Vend::Tags->$tag(\%i);
1568 elsif ($mod =~ s/^[&]//) {
1569 $Vend::Interpolate::item = $item;
1570 $Vend::Interpolate::s = $final;
1571 $Vend::Interpolate::q = $item->{quantity};
1572 $price = Vend::Interpolate::tag_calc($mod);
1573 undef $Vend::Interpolate::item;
1576 elsif ($mod =~ s/^=([\d.]*)=([^=]+)//) {
1578 my ($attribute, $table, $field, $key) = split /:/, $2;
1580 $key = $field ? $item->{$attribute} : $item->{code}
1582 $price = database_field( ( $table ||
1584 $Vend::Cfg->{ProductFiles}[0]),
1586 ($field || $item->{$attribute})
1591 #::logDebug("before option_cost price=$price final=$final");
1593 ($p, $f) = Vend::Options::option_cost($item, $table, $final);
1594 $final = $f if defined $f;
1596 #::logDebug("option_cost returned p=$p f=$f, price=$price final=$final");
1600 elsif($mod =~ /^\s*[[_]+/) {
1601 $::Scratch->{mv_item_object} = $Vend::Interpolate::item = $item;
1602 $Vend::Interpolate::s = $final;
1603 $Vend::Interpolate::q = $item->{quantity};
1604 $price = Vend::Interpolate::interpolate_html($mod);
1605 undef $::Scratch->{mv_item_object};
1606 undef $Vend::Interpolate::item;
1609 elsif($mod =~ s/^>>+//) {
1610 # This can point to a new mode for shipping
1620 elsif($price =~ s/%$//) {
1621 $price = $final * ($price / 100);
1623 elsif($price =~ s/\s*\*$//) {
1627 $final += $price if $price;
1628 last if ($final and !$chain);
1631 #::logDebug("chain_cost intermediate '$final'");
1633 #::logDebug("chain_cost returning '$final'");
1639 my($item, $quantity, $noformat) = @_;
1641 #::logDebug("item_price initial call: " . (ref $item ? $item->{code} : $item));
1643 return $item->{mv_cache_price}
1644 if ! $quantity and defined $item->{mv_cache_price};
1646 $item = { 'code' => $item } unless ref $item;
1647 $item->{quantity} = 1 if ! defined $item->{quantity};
1649 if( ! $item->{mv_ib}
1650 and $Vend::Cfg->{AutoModifier}
1651 and $item->{mv_ib} = product_code_exists_tag($item->{code})
1654 foreach my $i (@{$Vend::Cfg->{AutoModifier}}) {
1656 my ($table,$key,$foreign) = split /:+/, $i, 3;
1659 ($attr, $table) = split /\s*=\s*/, $table, 2;
1662 if(! $key and ! $foreign) {
1664 $item->{$attr} = item_common($item, $table);
1670 $table = $item->{mv_ib};
1674 $table ||= $Vend::Cfg->{ProductFiles}[0];
1676 my $select = $foreign ? $item->{$foreign} : $item->{code};
1677 $select ||= $item->{code};
1679 #::logDebug("attr=$attr table=$table key=$key select=$select foreign=$foreign");
1680 $item->{$attr} = ::tag_data($table, $key, $select);
1681 #::logDebug("item->$attr=$item->{$attr}");
1683 #::logDebug("item=" . ::uneval($item));
1690 if ($item->{mv_mp}) {
1691 return 0 if $item->{mv_si};
1693 my $mv_mp = $item->{mv_mi}
1695 logError("Bad modular item %s: ", uneval_it($item));
1698 for(@$Vend::Items) {
1699 next unless $_->{mv_si} and $_->{mv_mi} eq $mv_mp;
1700 #::logDebug("pushing item $_->{code}, mv_mi=$item->{mv_mi}, mv_mp=$item->{mv_mp}, mv_si=$item->{mv_si}");
1710 if ($Vend::Cfg->{PriceField}) {
1712 if (not $base = $item->{mv_ib}) {
1713 $base = product_code_exists_tag($item->{code})
1714 or ($Vend::Cfg->{OnFly} && 'mv_fly')
1717 $price = database_field($base, $item->{code}, $Vend::Cfg->{PriceField});
1720 #::logDebug("price for item before chain $item->{code}=$price PriceField=$Vend::Cfg->{PriceField}");
1721 $price = chain_cost($item,$price || $Vend::Cfg->{CommonAdjust});
1722 if($Vend::Cfg->{PriceDivide} == 0) {
1723 my $msg = "Locale %s PriceDivide non-numeric or zero [%s].";
1724 $msg .= " Possibly bad locale data.",
1727 $::Scratch->{mv_currency} || $::Scratch->{mv_locale},
1728 $Vend::Cfg->{PriceDivide},
1730 $Vend::Cfg->{PriceDivide} = 1;
1732 $price = $price / $Vend::Cfg->{PriceDivide};
1734 $item->{mv_cache_price} = $price
1735 if ! $quantity and exists $item->{mv_cache_price};
1736 #::logDebug("price for item $item->{code}=$price item=" . ::uneval_it($item)) if $price != 0;
1738 } while ($item = shift @items);
1739 #::logDebug("#### final price for item $master->{code}=$final item=" . ::uneval($master)) if $master;
1740 $master->{mv_cache_price} = $final
1741 if $master and ! $quantity and exists $master->{mv_cache_price};
1742 #::logDebug("final price in item $master->{code} is $master->{mv_cache_price}") if $master;
1748 my $base = $Vend::Database{$item->{mv_ib}} || $Products;
1749 return database_field($base, $item->{code}, $Vend::Cfg->{CategoryField});
1752 sub item_description {
1754 my $base = $Vend::Database{$item->{mv_ib}} || $Products;
1755 return database_field($base, $item->{code}, $Vend::Cfg->{DescriptionField});
1759 my ($item, $field, $emptyok) = @_;
1760 my $base = $item->{mv_ib};
1763 foreach my $code ($item->{code}, $item->{mv_sku}) {
1764 next if ! length($code);
1765 for my $dbname ($base, @{$Vend::Cfg->{ProductFiles}} ) {
1767 next if $seen{$dbname}++;
1768 my $db = database_exists_ref($dbname)
1770 last unless defined $db->test_column($field);
1771 $res = database_field($db, $code, $field);
1772 return $res if $emptyok or length($res);
1778 my ($item, $field) = @_;
1779 my $base = $Vend::Database{$item->{mv_ib}} || $Products;
1780 my $res = database_field($base, $item->{code}, $field);
1781 return $res if length($res);
1782 return database_field($base, $item->{mv_sku}, $field);
1786 item_price($_[0]) * ($_[0]->{quantity} || 0);
1790 my ($base, $thing) = @_;
1791 return ($base, $thing) unless $thing =~ /^(\w+):+(.*)/;
1795 # Security handled before this in update_data
1796 $Vend::WriteDatabase{$t} = 1;
1798 my $db = database_exists_ref($t);
1799 return undef unless $db;
1800 return ($db->ref(), $c);
1803 ## Update the user-entered fields.
1806 my @cgi_keys = keys %CGI::values;
1807 # Update a database record
1808 # Check to see if this is allowed
1809 #::logDebug("mv_data_enable=$::Scratch->{mv_data_enable}");
1810 if(! $::Scratch->{mv_data_enable}) {
1812 "Attempted database update without permission, table=%s key=%s.",
1813 $CGI::values{mv_data_table},
1814 $CGI::values{$CGI::values{mv_data_key}},
1818 unless (defined $CGI::values{mv_data_table} and
1819 defined $CGI::values{mv_data_key} ) {
1820 logError("Attempted database operation without table, fields, or key.\n" .
1824 $CGI::values{mv_data_table},
1825 $CGI::values{mv_data_fields},
1826 $CGI::values{mv_data_key},
1832 my $function = lc (delete $CGI::values{mv_data_function});
1833 if($function eq 'delete' and ! delete $CGI::values{mv_data_verify}) {
1834 logError("update_data: DELETE without VERIFY, abort");
1837 my $table = $CGI::values{mv_data_table};
1838 my $prikey = $CGI::values{mv_data_key};
1839 my $decode = is_yes($CGI::values{mv_data_decode});
1842 #::logDebug("data_enable=$::Scratch->{mv_data_enable}, checking");
1843 if($::Scratch->{mv_data_enable} =~ /^(\w+):(.*?):/s) {
1844 # check for single key and possible set of columns
1847 my $en_key = $::Scratch->{mv_data_enable_key};
1848 #::logDebug("en_table=$en_table en_col=$en_col, en_key=$en_key, checking");
1849 if( $en_table ne $table
1851 ($en_key and $CGI::values{$prikey} ne $en_key)
1854 logError("Attempted database operation without permission:\n" .
1855 "Permission: '%s' (key='$en_key')\n" .
1859 $::Scratch->{mv_data_enable},
1860 $CGI::values{mv_data_table},
1861 $CGI::values{mv_data_fields},
1862 $CGI::values{$CGI::values{mv_data_key}},
1869 $Vend::WriteDatabase{$table} = 1;
1871 my $base_db = database_exists_ref($table)
1872 or die "Not a defined database '$table': $!\n";
1873 $base_db = $base_db->ref();
1875 my @fields = grep $_ && $_ ne $prikey,
1876 split /[\s\0,]+/, $CGI::values{mv_data_fields};
1877 unshift(@fields, $prikey);
1879 my @file_fields = split /[\s\0,]+/, $CGI::values{mv_data_file_field};
1880 my @file_paths = split /\0/, $CGI::values{mv_data_file_path};
1881 my @file_name_from = split /\0/, $CGI::values{mv_data_file_name_from};
1882 my @file_oldfiles = split /\0/, $CGI::values{mv_data_file_oldfile};
1885 $en_col =~ s/^\s+//;
1886 $en_col =~ s/\s+$//;
1888 @col_present{ grep /\S/, split /[\s\0,]+/, $en_col } = ();
1889 $col_present{$prikey} = 1;
1890 for(@fields, $CGI::values{mv_blob_field}, $CGI::values{mv_blob_pointer}) {
1892 next if exists $col_present{$_};
1893 next if /:/ and $::Scratch->{mv_data_enable} =~ / $_ /;
1894 logError("Attempted database operation without permission:\n" .
1895 "Permission: '%s'\n" .
1899 $::Scratch->{mv_data_enable},
1900 $CGI::values{mv_data_table},
1901 $CGI::values{mv_data_fields},
1902 $CGI::values{$CGI::values{mv_data_key}},
1907 $function = 'update' unless $function;
1916 my $multi = $CGI::values{$prikey} =~ tr/\0/\0/;
1919 my ($minname, $maxname);
1921 while (($key, $value) = each %CGI::values) {
1922 next unless defined $data{$key};
1923 if($CGI::values{"mv_data_prep_$key"}) {
1924 $value = Vend::Interpolate::filter_value(
1925 $CGI::values{"mv_data_prep_$key"},
1929 $count = (@{$data{$key}} = split /\0/, $value, -1);
1930 $max = $count, $maxname = $key if $count > $max;
1931 $min = $count, $minname = $key if $count < $min;
1934 if( $multi and ($max - $min) > 1 and ! $CGI::values{mv_data_force}) {
1935 logError("probable bad form -- number of values min=%s (%s) max=%s (%s)", $min, $minname, $max, $maxname);
1940 #::logDebug("function=$function auto_number=" . $base_db->config('_Auto_number'));
1941 if ($CGI::values{mv_data_auto_number}) {
1943 my $ref = $data{$prikey};
1944 while (scalar @$ref < $max) {
1947 $base_db->config('AUTO_NUMBER', '000001')
1948 if ! $base_db->config('_Auto_number');
1949 $CGI::values{mv_data_return_key} = $prikey
1950 unless $CGI::values{mv_data_return_key};
1952 elsif($function eq 'insert' and $base_db->config('_Auto_number') ) {
1955 #::logDebug("autonumber=$autonumber");
1957 my $multikey = $base_db->config('COMPOSITE_KEY');
1960 my $Tag = new Vend::Tags;
1963 if($Vend::Session->{logged_in} and $Vend::admin) {
1965 return $Tag->if_mm('files', shift);
1968 elsif($Vend::Session->{logged_in} and ! $Vend::admin) {
1971 return 1 if $::Scratch->{$file} == 1;
1972 return $Tag->userdb(
1973 function => 'check_file_acl',
1980 $acl_func = sub { return $::Scratch->{shift(@_)} == 1 }
1983 for (my $i = 0; $i < @file_fields; $i++) {
1984 my $nm = $file_fields[$i];
1986 next if $nm =~ /__\d+$/;
1990 if($nm =~ m{^(\d+)_} and $CGI::values{$nm}) {
1993 $sneakdata{$nm}->[0] = $CGI::values{$nm};
1994 for(qw/ mv_data_file_name_to_ mv_data_file_size_to_ /) {
1996 my $fld = $CGI::values{$t}
1999 $sneakdata{$fld}->[0] = $CGI::values{$fld};
2001 $dref = $sneakdata{$nm};
2002 $dmain = \%sneakdata;
2009 unless (length($dref->[0])) {
2010 # no need for a file update
2011 if($file_oldfiles[$i]) {
2012 $dref->[0] = $file_oldfiles[$i];
2017 # remove path components
2018 $dref->[0] =~ s:.*/::;
2019 $dref->[0] =~ s:.*\\::;
2021 if(my $switch = $file_name_from[$i]) {
2023 if($data{$switch} and $new = $data{$switch}->[0]) {
2024 my $ext = $dref->[0];
2025 if($ext =~ s/.*\.//) {
2026 $dref->[0] = join '.', $new, $ext;
2034 if (length ($file_paths[$i])) {
2036 $outfile = join('/', $file_paths[$i], $dref->[0]);
2040 $ok = $acl_func->($outfile);
2044 $ok = $acl_func->($file_paths[$i]);
2049 logError ("ACL function failed on '%s': %s", $outfile, $@);
2052 logError ("Not allowed to upload \"%s\"", $outfile);
2057 Vend::Interpolate::tag_value_extended(
2064 logError("%s is not a file (does form allow file upload?).", $dref->[0]);
2067 Vend::Interpolate::tag_value_extended(
2070 outfile => $outfile,
2071 umask => $::Scratch->{mv_create_umask} || '022',
2072 auto_create_dir => $::Scratch->{mv_auto_create_dir},
2077 logError("failed to write %s: %s", $outfile, $!);
2082 # preparing to dump file contents into database column
2083 if(my $nfield = $CGI::values{"mv_data_file_name_to_$nm"}) {
2084 $dmain->{"$ntag$nfield"}->[0] = $dmain->{$nm}->[0];
2087 = Vend::Interpolate::tag_value_extended ($nm,
2088 {file_contents => 1});
2089 if(my $sfield = $CGI::values{"mv_data_file_size_to_$nm"}) {
2090 $dmain->{"$ntag$sfield"}->[0] = length $dmain->{$nm}->[0];
2096 if (not defined $data{$prikey}) {
2097 logError("No key '%s' in field specifier %s", $prikey, 'mv_data_fields');
2100 elsif ( ! @{$data{$prikey}}) {
2102 @{$data{$prikey}} = map { '' } @{ $data{$fields[1]} };
2105 logError("No key '%s' found for function='%s' table='%s'",
2106 $prikey, $function, $CGI::values{mv_data_table},
2124 # Fields to set in database despite mv_blob_only
2127 if($CGI::values{mv_blob_field} and $CGI::values{mv_blob_nick}) {
2128 #::logDebug("update_data: blob processing enabled");
2129 $blob_field = $CGI::values{mv_blob_field};
2130 $blob_nick = $CGI::values{mv_blob_nick};
2131 $blob_ptr = $CGI::values{mv_blob_pointer};
2134 map { ($_, 1) } split /[\s,\0]+/, $CGI::values{mv_blob_exception};
2136 if( ! $base_db->column_exists($blob_field) ) {
2139 logError("No blob field '%s' found for table='%s', skipping blob save.",
2140 $CGI::values{mv_blob_field}, $CGI::values{mv_data_table},
2143 elsif ($MVSAFE::Safe) {
2144 $safe = $Vend::Interpolate::ready_safe;
2147 $safe = new Vend::Safe;
2149 $base_db->column_exists($blob_ptr)
2151 #::logDebug("update_data: blob safe object=$safe");
2155 my $multiqual = $CGI::values{mv_data_multiple_qual} || $prikey;
2156 if($CGI::values{mv_data_multiple}) {
2157 my $re = qr/^\d+_$prikey$/;
2158 @multis = grep $_ =~ $re, @cgi_keys;
2162 @multis = sort { $a <=> $b } @multis;
2165 #::logDebug("update_data:db=$base_db key=$prikey VALUES=" . ::uneval(\%CGI::values));
2166 #::logDebug("update_data:db=$base_db key=$prikey data=" . ::uneval(\%data));
2169 for($i = 0; $i < @{$data{$prikey}}; $i++) {
2170 #::logDebug("iteration of update_data:db=$base_db key=$prikey data=" . ::uneval(\%data));
2173 next unless (length($value = $data{$_}->[$i]) || $CGI::values{mv_update_empty} );
2176 HTML::Entities::decode($value) if $decode;
2178 if($CGI::values{"mv_data_filter_$_"}) {
2179 $value = Vend::Interpolate::filter_value(
2180 $CGI::values{"mv_data_filter_$_"},
2185 $select_key = $value if $_ eq $prikey;
2186 not defined $value and $value = '';
2190 if(! length($select_key) ) {
2191 next if defined $CGI::values{mv_update_empty_key}
2192 and ! $CGI::values{mv_update_empty_key};
2195 if($function eq 'delete') {
2196 $base_db->delete_record($select_key);
2200 $key = $data{$prikey}->[$i];
2201 if(! length($key) and ! $autonumber) {
2202 ## KEY IS possibly SET HERE
2203 $key = $base_db->set_row($key);
2205 push(@rows_set, $key);
2207 # allow form submissions to go to database and to mail
2208 if ($CGI::values{mv_data_email}) {
2210 [ errmsg("### Form Submission from %s", $key), $blob_nick, ],
2223 my $string = $base_db->field($key, $blob_field);
2224 #::logDebug("update_data: blob string=$string");
2225 $blob = $safe->reval($string);
2226 #::logDebug("update_data: blob object=$blob");
2227 $blob = {} unless ref($blob) eq 'HASH';
2229 my @keys = split /::/, $blob_nick;
2231 unless ( ref($brec->{$_}) eq 'HASH') {
2234 $brec = $brec->{$_};
2237 while($field = shift @k) {
2239 next if $field eq $prikey and ! $multikey;
2242 # We are going to set the field unless it is only for
2243 # storing in a blob (and possibly emailing)
2245 if ($CGI::values{mv_blob_only} and ! $blob_exception{$field}) {
2246 #::logDebug("$field not storing, only blob");
2250 #::logDebug("storing d=$d $field blob_only=$CGI::values{mv_blob_only}");
2251 ($d, $f) = set_db($base_db, $field);
2252 #::logDebug("storing table=$table d=$d f=$f key=$key");
2254 if(! $value and ! length($value)) {
2255 $value = $CGI::values{"mv_data_undef:$field"} ? undef : '';
2258 if(! defined $qd->{$d}) {
2261 $qv->{$d} = [$value];
2264 push @{$qf->{$d}}, $f;
2265 push @{$qv->{$d}}, $value;
2267 #$d->set_field($key, $f, $value);
2270 push(@email_rows, [$f, $value])
2271 if $CGI::values{mv_data_email};
2272 #::logDebug("update_data:db=$d key=$key field=$f value=$value");
2273 $brec->{$f} = $value if $brec;
2276 my $dml = { dml => 'upsert' };
2277 $dml->{dml} = $function
2278 if $::Pragma->{dml} eq 'strict'
2279 || $function eq 'insert' && $::Pragma->{dml} eq 'preserve';
2282 #::logDebug("update_data: Getting ready to set_slice");
2283 my $k = $multikey ? undef : $key;
2284 $qret = $qd->{$_}->set_slice([$dml, $k], $qf->{$_}, $qv->{$_});
2285 $rows_set[$i] = $qret unless $rows_set[$i];
2287 if($blob && $rows_set[$i]) {
2288 $brec->{mv_data_fields} = join " ", @fields;
2289 my $string = uneval_it($blob);
2290 #::logDebug("update_data: blob saving string=$string");
2291 $base_db->set_field($key, $blob_field, $string);
2293 $base_db->set_field($key, $blob_ptr, $blob_nick);
2298 [ errmsg("### END FORM SUBMISSION %s", $key), $blob_nick, ]
2300 if $CGI::values{mv_data_email};
2304 if(my $new = shift(@multis)) {
2305 last SETDATA unless length $CGI::values{"${new}_$multiqual"};
2307 my $t = $new . "_$_";
2308 if($sneakdata{$t}) {
2309 $data{$_} = delete $sneakdata{$t};
2312 $data{$_} = [ $CGI::values{$_} = $CGI::values{$t} ];
2320 if($CGI::values{mv_data_return_key}) {
2321 my @keys = split /\0/, $CGI::values{mv_data_return_key};
2323 $CGI::values{$_} = join("\0", @rows_set);
2327 if($CGI::values{mv_auto_export}) {
2328 Vend::Data::export_database($table);
2331 if($CGI::values{mv_data_email}) {
2332 push @email_rows, [ 'mv_data_fields', \@fields ];
2333 Vend::Interpolate::tag_mail('', { log_error => 1 }, \@email_rows);
2336 # Allow setting in one then returning to another
2337 if($CGI::values{mv_return_table}) {
2338 $CGI::values{mv_data_table} = $CGI::values{mv_return_table};
2341 my @reloads = grep /^mv_data_table__\d+$/, keys %CGI::values;
2343 @reloads = map { m/.*__(\d+)$/; $1 } @reloads;
2344 @reloads = sort { $a <=> $b } @reloads;
2345 my $new = shift @reloads;
2346 my $this = qr{__$new$};
2347 my $some = qr{__\d+$};
2348 #::logDebug("Reloading, new=$new this=$this some=$some");
2352 push(@death_row, $_), next unless $_ =~ $some;
2356 $cgiset{$k} = delete $CGI::values{$_};
2364 for(my $i = 0; $i < @file_fields; $i++) {
2365 push(@file_death, $i), next unless $file_fields[$i] =~ $some;
2366 if($file_fields[$i] =~ $this) {
2367 my $k = $file_fields[$i];
2369 $filemove{$file_fields[$i]} = $k;
2374 while (defined($i = pop @file_death)) {
2375 splice @file_fields, $i, 1;
2376 splice @file_paths, $i, 1;
2377 splice @file_oldfiles, $i, 1;
2381 if(my $new = $filemove{$_}) {
2387 while(my ($k,$v) = each %filemove) {
2388 $CGI::file{$v} = delete $CGI::file{$k};
2392 $::Scratch->{mv_data_enable} = delete $::Scratch->{"mv_data_enable__$new"};
2393 delete $::Scratch->{mv_data_enable_key};
2396 next unless /^mv_(data|blob|update)_/ or $data{$_}; # Reprieve!
2397 delete $CGI::values{$_};
2400 @CGI::values{keys %cgiset} = values %cgiset;
2401 $CGI::values{mv_data_file_field} = join "\0", @file_fields;
2402 $CGI::values{mv_data_file_path} = join "\0", @file_paths;
2403 $CGI::values{mv_data_file_oldfiles} = join "\0", @file_oldfiles;
2404 #::logDebug("Reloading, function=$CGI::values{mv_data_function}");