* Add enclair_db option to UserDB.pm. Allows logging of enclair password
[interchange.git] / lib / Vend / Options / Simple.pm
1 # Vend::Options::Simple - Interchange Simple product options
2 #
3 # $Id: Simple.pm,v 1.9 2007-08-09 13:40:55 pajamian Exp $
4 #
5 # Copyright (C) 2002-2007 Interchange Development Group <interchange@icdevgroup.org>
6 # Copyright (C) 2002-2003 Mike Heins <mikeh@perusion.net>
7
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public
19 # License along with this program; if not, write to the Free
20 # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
21 # MA  02110-1301  USA.
22 #
23
24 package Vend::Options::Simple;
25
26 $VERSION = substr(q$Revision: 1.9 $, 10);
27
28 =head1 NAME
29
30 Vend::Options::Simple - Interchange Simple Options Support
31
32 =head1 SYNOPSIS
33
34     [item-options]
35  
36         or
37  
38     [price code=SKU]
39  
40 =head1 PREREQUISITES
41
42 Vend::Options
43
44 =head1 DESCRIPTION
45
46 The Vend::Options::Simple module implements simple product options for
47 Interchange. It is compatible with Interchange 4.8.x simple options.
48
49 If the Interchange Variable MV_OPTION_TABLE is not set, it defaults
50 to "options", which combines options for Simple, Matrix, and
51 Modular into that one table. This goes along with foundation and
52 construct demos up until Interchange 4.9.8.
53
54 The "options" table remains the default for simple options.
55
56 =head1 AUTHORS
57
58 Mike Heins <mikeh@perusion.net>
59
60 =head1 CREDITS
61
62 Jon Jensen <jon@swelter.net>
63
64 =cut
65
66 use Vend::Util;
67 use Vend::Data;
68 use Vend::Interpolate;
69 use Vend::Options;
70 use strict;
71
72 use vars qw/%Default/;
73
74 %Default = ( 
75         option_template => '{LABEL} {PRICE?}({NEGATIVE?}subtract{/NEGATIVE?}{NEGATIVE:}add{/NEGATIVE:} {ABSOLUTE}) {/PRICE?}'
76 );
77
78 my $Admin_page;
79
80 sub price_options {
81         my ($item, $table, $final, $loc) = @_;
82
83         $loc ||= $Vend::Cfg->{Options_repository}{Simple} || {};
84         my $map = $loc->{map} || {};
85
86         my $db = database_exists_ref($table || $loc->{table} || 'options');
87         if(! $db) {
88                 logOnce('Non-existent price option table %s', $table);
89                 return;
90         }
91
92         my $tname = $db->name();
93         my $sku = $item->{code};
94
95 #::logDebug("Simple module price_options found enabled record");
96         my $fsel = $map->{sku} || 'sku';
97         my $rsel = $db->quote($sku, $fsel);
98         my @rf;
99         for(qw/o_group price/) {
100                 push @rf, ($map->{$_} || $_);
101         }
102
103         my $q = "SELECT " . join (",", @rf) . " FROM $tname where $fsel = $rsel and $rf[1] <> ''";
104 #::logDebug("Simple module price_options query=$q");
105         my $ary = $db->query($q); 
106         return if ! $ary->[0];
107         my $ref;
108         my $price = 0;
109         my $f;
110
111         foreach $ref (@$ary) {
112 #::logDebug("checking option " . uneval_it($ref));
113                 next unless defined $item->{$ref->[0]};
114                 next unless length($ref->[1]);
115                 $ref->[1] =~ s/^\s+//;
116                 $ref->[1] =~ s/\s+$//;
117                 $ref->[1] =~ s/==/=:/g;
118                 my %info = split /\s*[=,]\s*/, $ref->[1];
119                 if(defined $info{ $item->{$ref->[0]} } ) {
120                         my $atom = $info{ $item->{$ref->[0]} };
121                         if($atom =~ s/^://) {
122                                 $f = $atom;
123                                 next;
124                         }
125                         elsif ($atom =~ s/\%$//) {
126                                 $f = $final if ! defined $f;
127                                 $f += ($atom * $final / 100);
128                         }
129                         else {
130                                 $price += $atom;
131                         }
132                 }
133         }
134 #::logDebug("price_options returning price=$price f=$f");
135         return ($price, $f);
136 }
137
138 sub display_options {
139         my ($item, $opt, $loc) = @_;
140 #::logDebug("Simple options, item=" . ::uneval($item) . "\nopt=" . ::uneval($opt));
141 #::logDebug("Simple options by module, old");
142
143         $loc ||= $Vend::Cfg->{Options_repository}{Simple} || {};
144         my $map = $loc->{map} || {};
145
146         my $sku = $item->{code};
147
148         my $db;
149         my $tab;
150         if(not $db = $opt->{options_db}) {
151                 $tab = $opt->{table} ||= $loc->{table} 
152                                                          ||= $::Variable->{MV_OPTION_TABLE}
153                                                          ||= 'options';
154                 $db = database_exists_ref($tab)
155                         or do {
156                                 logOnce(
157                                                 "Simple options: unable to find table %s for item %s",
158                                                 $tab,
159                                                 $sku,
160                                         );
161                                 return undef;
162                         };
163         }
164
165         my $tname = $db->name();
166
167         my @rf;
168         my @out;
169         my $out;
170
171         use constant CODE   => 0;
172         use constant GROUP  => 1;
173         use constant VALUE  => 2;
174         use constant LABEL  => 3;
175         use constant WIDGET => 4;
176         use constant PRICE  => 5;
177         use constant HEIGHT => 6;
178         use constant WIDTH  => 7;
179
180         for(qw/code o_group o_value o_label o_widget price o_height o_width/) {
181                 push @rf, ($map->{$_} || $_);
182         }
183
184         my $fsel = $map->{sku} || 'sku';
185         my $rsel = $db->quote($sku, $fsel);
186         
187         my $q = "SELECT " . join (",", @rf) . " FROM $tname where $fsel = $rsel";
188
189         if(my $rsort = find_sort($opt, $db, $loc)) {
190                 $q .= $rsort;
191         }
192 #::logDebug("tag_options simple query: $q");
193
194         my $ary = $db->query($q)
195                 or return; 
196 #::logDebug("tag_options simple ary: " . ::uneval($ary));
197 #::logDebug("tag_options item=" . ::uneval($item));
198
199         my $ishash = defined $item->{mv_ip} ? 1 : 0;
200         my $ref;
201
202         $opt->{option_template} ||= $loc->{option_template};
203
204         foreach $ref (@$ary) {
205                 # skip unless o_value
206                 next unless $ref->[VALUE];
207 #::logDebug("tag_options attribute=" . GROUP);
208
209                 if ($opt->{label}) {
210                         $ref->[LABEL] = "<b>$ref->[LABEL]</b>" if $opt->{bold};
211                         push @out, $ref->[LABEL];
212                 }
213                 my $precursor = $opt->{report}
214                                           ? "$ref->[GROUP]$opt->{separator}"
215                                           : qq{<input type="hidden" name="mv_item_option" value="$ref->[GROUP]">};
216
217                 my $passed = $ref->[VALUE];
218                 if($opt->{blank_label}) {
219                         $passed = "=$opt->{blank_label}, $passed";
220                 }
221                 push @out, $precursor . Vend::Interpolate::tag_accessories(
222                                                 $sku,
223                                                 '',
224                                                 { 
225                                                         attribute => $ref->[GROUP],
226                                                         default => undef,
227                                                         extra => $opt->{extra},
228                                                         item => $item,
229                                                         js => $opt->{js},
230                                                         name => $ishash ? undef : "mv_order_$ref->[GROUP]",
231                                                         option_template => $opt->{option_template},
232                                                         passed => $passed,
233                                                         price => $opt->{price},
234                                                         price_data => $ref->[PRICE],
235                                                         height => $opt->{height} || $ref->[HEIGHT],
236                                                         width  => $opt->{width} || $ref->[WIDTH],
237                                                         type => $opt->{type} || $ref->[WIDGET] || 'select',
238                                                 },
239                                                 $item || undef,
240                                         );
241         }
242         if($opt->{td}) {
243                 for(@out) {
244                         $out .= "<td>$_</td>";
245                 }
246         }
247         else {
248                 $opt->{joiner} = "<br$Vend::Xtrailer>" if ! $opt->{joiner};
249                 $out .= join $opt->{joiner}, @out;
250         }
251 #::logDebug("display_options out size=" . length($out));
252         return $out;
253 }
254
255 sub admin_page {
256         my $item = shift;
257         my $opt = shift;
258         my $page = $Tag->file('include/Options/Simple') || $Admin_page;
259         Vend::Util::parse_locale(\$page);
260         return interpolate_html($page);
261 }
262
263 $Admin_page = <<'EoAdminPage';
264 [update values]
265 [if cgi ui_clone_options]
266 [and cgi ui_clone_id]
267 [perl interpolate=1 tables="[cgi mv_data_table]"]
268         my $db = $Db{[cgi mv_data_table]}
269                 or return;
270         my ($k,$v);
271         $db->clone_row($CGI->{ui_clone_id}, $CGI->{sku});
272         $db->clone_set('sku', $CGI->{ui_clone_id}, $CGI->{sku});
273         return;
274 [/perl]
275 [/if]
276
277 [if cgi sku]
278     [tag flag write]options[/tag]
279     [perl tables="options __UI_ITEM_TABLES__"]
280         my $otab = 'options';
281         my $odb = $Db{$otab};
282
283         foreach(sort keys %{$CGI}) {
284             next unless /^opt_group_(.*)/;
285             my $key = $1;
286
287             my $name = $CGI->{"opt_group_$key"};
288             my $value = $CGI->{"opt_value_$key"};
289             my $label = $CGI->{"opt_label_$key"};
290
291             next unless $name && $value;
292
293             unless($key) { $key = $CGI->{sku}."-$name"; }
294
295             my @value = split("\r\n",$value);
296
297             my %seen = ();
298             my $hasdefault = 0;
299
300             my($left,$right);
301             map {
302                 my $default = 0;
303                 s/[,\r\n]//g;
304                 if(s/\*//g) { $default = 1; $hasdefault = 1; }
305
306                 if($v) {
307                     if(/=/) {
308                         ($left,$right) = split('=',$_);
309                     } else {
310                         $right = $_;
311                         $left = substr($right,0,3);
312                     }
313
314                     while($seen{$left}++) { $left++; }
315
316                     $_ = join('=',$left,$right);
317                     if($default) { $_ .= "*"; }
318                 }
319             } @value;
320
321             my $value = join(",\n",@value);
322
323             $key =~ s/_/-/g; # javascript won't handle form names with '-'
324
325             $odb->set_field($key,'sku',$CGI->{sku});
326             $odb->set_field($key,'o_group',$name);
327             $odb->set_field($key,'o_value',$value);
328             $odb->set_field($key,'o_widget','select');
329             $odb->set_field($key,'o_label',$label);
330         }
331
332         return '';
333     [/perl]
334 [/if]
335
336
337 <form action="[area @@MV_PAGE@@]" method="post">
338 [if scratch ui_failure]
339 <p>
340 <blockquote>
341 <font color="__CONTRAST__">[scratch ui_failure][set ui_failure][/set]</font>
342 </blockquote>
343 <p>
344 &nbsp;
345 [/if]
346 [if scratch ui_message]
347 <p>
348 <blockquote>
349 <font color="__CONTRAST__">[scratch ui_message][set ui_message][/set]</font>
350 </blockquote>
351 <p>
352 &nbsp;
353 [/if]
354 <input type="hidden" name="sku"           value="[cgi item_id]">
355 <input type="hidden" name="ui_page_title" value="[cgi ui_page_title]">
356 <input type="hidden" name="ui_page_title" value="[cgi ui_page_banner]">
357 <input type="hidden" name="ui_return_to"  value="@@MV_PAGE@@">
358 <input type="hidden" name="mv_action"     value="back">
359
360 <table border="0"><tr><td valign="top">
361
362 [query list=1 sql="select * from options where sku='[filter op=sql interpolate=1][cgi item_id][/filter]' and o_group is not null"]
363 [list]
364 [if-sql-data options o_group]
365 [calc] $Scratch->{mod_code} = q{[sql-code]}; $Scratch->{mod_code} =~ s/-/_/g; return;[/calc]
366 <table border="0" cellspacing="0" cellpadding="3" bgcolor="[sql-alternate 2]__UI_T_ROW_EVEN__[else]__UI_T_ROW_ODD__[/else][/sql-alternate]">
367 <tr><td valign="center">Name: <input type="text" size="20" name="opt_group_[scratch mod_code]" value="[filter entities][sql-param o_group][/filter]">
368
369 <a href="[area href='@@MV_PAGE@@'
370                form='deleterecords=1
371                      ui_delete_id=[sql-code]
372                      item_id=[cgi item_id]
373                      mv_data_table=options
374                      mv_click=db_maintenance
375                      mv_action=back
376                      mv_nextpage=@@MV_PAGE@@
377                     '
378          ]"><img src="delete.gif" alt="[L]Delete[/L]" align="center" border="0"></a>
379 <br>[L]Label[/L]: <input type="text" size="20" name="opt_label_[scratch mod_code]" value="[filter entities][sql-param o_label][/filter]">
380 <input type="hidden" name="reset_[scratch mod_code]" value="[filter entities][sql-param o_label][/filter]">
381 <script><!--
382 document.write('<br><input type="checkbox" [sql-calc]q{[sql-param o_label]} eq q{[sql-param o_group]} ? "CHECKED" : undef;[/sql-calc]\n' +
383 '       onClick="if (this.checked) { this.form.opt_label_[scratch mod_code].value = this.form.opt_group_[scratch mod_code].value; } else { this.form.opt_label_[scratch mod_code].value = this.form.reset_[scratch mod_code].value; }">\n' +
384 '<font size=2>[L]Set label to name[/L]</font>');
385 // -->
386 </script>
387 </td></tr>
388 [tmp o_value][perl]
389     my @vals = split(',',q{[sql-param o_value]});
390     map { s/[\r\n]//g; } @vals;
391     return join("\n",@vals);
392 [/perl][/tmp]
393
394 <tr><td>
395 <textarea rows="5" cols="30" name="opt_value_[scratch mod_code]">[scratch o_value]</textarea><br>
396 [page href="admin/flex_editor"
397                 form="
398                         mv_data_table=options
399                         item_id=[sql-code]
400                         ui_return_to=admin/item_option
401                         ui_return_to=item_id=[cgi item_id]
402                         ui_data_fields=code o_widget o_width o_height
403                 "]Widget type edit</a>
404 </td></tr>
405 </table>
406 [/if-sql-data]
407 [/list]
408 [/query]
409
410 <br><br><br>
411 [button text="[L]Commit Changes[/L]"]
412
413 </td><td><pre>                          </pre></td><td valign="top">
414
415 <b>[L]Create a new option[/L]:</b><br>
416 [L]Name[/L]: <input type="text" size="20" name="opt_group_" value="">
417 <br>[L]Label[/L]: <input type="text" size="20" name="opt_label_">
418 <script><!--
419 document.write('<br><input type="checkbox"\n' +
420 '       onClick="if (this.checked) { this.form.opt_label_.value = this.form.opt_group_.value; } else { this.form.opt_label_.value = \'\'; }">\n' +
421 '<font size="2">[L]Set label to name[/L]</font>');
422 // -->
423 </script>
424 <br>
425 <textarea rows="5" cols="30" name="opt_value_"></textarea>
426 <br>
427 [button text="[L]Create option[/L]"]
428 <br><br>
429
430 <hr>
431
432 <br><br><b>[L]Clone an existing option set[/L]:</b><br>
433
434 [query
435         list=1
436         prefix=clone
437         sql="select DISTINCT sku from [cgi mv_data_table]"
438         more=1]
439 <select name="ui_clone_id">
440 <option value=""> --
441 [list]
442 [if-clone-data options o_enable]
443 <option value="[clone-code]">[clone-filter 20][clone-description][/clone-filter]
444 [/if-clone-data]
445 [/list]
446 </select>[more-list]<br>[more]<br>[/more-list][/query]&nbsp;[button text="[L]Clone options[/L]"]<br>
447 </form>
448
449 </td></tr></table>
450
451 EoAdminPage
452
453 1;