1 # Vend::Options::Simple - Interchange Simple product options
3 # $Id: Simple.pm,v 1.9 2007-08-09 13:40:55 pajamian Exp $
5 # Copyright (C) 2002-2007 Interchange Development Group <interchange@icdevgroup.org>
6 # Copyright (C) 2002-2003 Mike Heins <mikeh@perusion.net>
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.
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.
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,
24 package Vend::Options::Simple;
26 $VERSION = substr(q$Revision: 1.9 $, 10);
30 Vend::Options::Simple - Interchange Simple Options Support
46 The Vend::Options::Simple module implements simple product options for
47 Interchange. It is compatible with Interchange 4.8.x simple options.
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.
54 The "options" table remains the default for simple options.
58 Mike Heins <mikeh@perusion.net>
62 Jon Jensen <jon@swelter.net>
68 use Vend::Interpolate;
72 use vars qw/%Default/;
75 option_template => '{LABEL} {PRICE?}({NEGATIVE?}subtract{/NEGATIVE?}{NEGATIVE:}add{/NEGATIVE:} {ABSOLUTE}) {/PRICE?}'
81 my ($item, $table, $final, $loc) = @_;
83 $loc ||= $Vend::Cfg->{Options_repository}{Simple} || {};
84 my $map = $loc->{map} || {};
86 my $db = database_exists_ref($table || $loc->{table} || 'options');
88 logOnce('Non-existent price option table %s', $table);
92 my $tname = $db->name();
93 my $sku = $item->{code};
95 #::logDebug("Simple module price_options found enabled record");
96 my $fsel = $map->{sku} || 'sku';
97 my $rsel = $db->quote($sku, $fsel);
99 for(qw/o_group price/) {
100 push @rf, ($map->{$_} || $_);
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];
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/^://) {
125 elsif ($atom =~ s/\%$//) {
126 $f = $final if ! defined $f;
127 $f += ($atom * $final / 100);
134 #::logDebug("price_options returning price=$price f=$f");
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");
143 $loc ||= $Vend::Cfg->{Options_repository}{Simple} || {};
144 my $map = $loc->{map} || {};
146 my $sku = $item->{code};
150 if(not $db = $opt->{options_db}) {
151 $tab = $opt->{table} ||= $loc->{table}
152 ||= $::Variable->{MV_OPTION_TABLE}
154 $db = database_exists_ref($tab)
157 "Simple options: unable to find table %s for item %s",
165 my $tname = $db->name();
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;
180 for(qw/code o_group o_value o_label o_widget price o_height o_width/) {
181 push @rf, ($map->{$_} || $_);
184 my $fsel = $map->{sku} || 'sku';
185 my $rsel = $db->quote($sku, $fsel);
187 my $q = "SELECT " . join (",", @rf) . " FROM $tname where $fsel = $rsel";
189 if(my $rsort = find_sort($opt, $db, $loc)) {
192 #::logDebug("tag_options simple query: $q");
194 my $ary = $db->query($q)
196 #::logDebug("tag_options simple ary: " . ::uneval($ary));
197 #::logDebug("tag_options item=" . ::uneval($item));
199 my $ishash = defined $item->{mv_ip} ? 1 : 0;
202 $opt->{option_template} ||= $loc->{option_template};
204 foreach $ref (@$ary) {
205 # skip unless o_value
206 next unless $ref->[VALUE];
207 #::logDebug("tag_options attribute=" . GROUP);
210 $ref->[LABEL] = "<b>$ref->[LABEL]</b>" if $opt->{bold};
211 push @out, $ref->[LABEL];
213 my $precursor = $opt->{report}
214 ? "$ref->[GROUP]$opt->{separator}"
215 : qq{<input type="hidden" name="mv_item_option" value="$ref->[GROUP]">};
217 my $passed = $ref->[VALUE];
218 if($opt->{blank_label}) {
219 $passed = "=$opt->{blank_label}, $passed";
221 push @out, $precursor . Vend::Interpolate::tag_accessories(
225 attribute => $ref->[GROUP],
227 extra => $opt->{extra},
230 name => $ishash ? undef : "mv_order_$ref->[GROUP]",
231 option_template => $opt->{option_template},
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',
244 $out .= "<td>$_</td>";
248 $opt->{joiner} = "<br$Vend::Xtrailer>" if ! $opt->{joiner};
249 $out .= join $opt->{joiner}, @out;
251 #::logDebug("display_options out size=" . length($out));
258 my $page = $Tag->file('include/Options/Simple') || $Admin_page;
259 Vend::Util::parse_locale(\$page);
260 return interpolate_html($page);
263 $Admin_page = <<'EoAdminPage';
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]}
271 $db->clone_row($CGI->{ui_clone_id}, $CGI->{sku});
272 $db->clone_set('sku', $CGI->{ui_clone_id}, $CGI->{sku});
278 [tag flag write]options[/tag]
279 [perl tables="options __UI_ITEM_TABLES__"]
280 my $otab = 'options';
281 my $odb = $Db{$otab};
283 foreach(sort keys %{$CGI}) {
284 next unless /^opt_group_(.*)/;
287 my $name = $CGI->{"opt_group_$key"};
288 my $value = $CGI->{"opt_value_$key"};
289 my $label = $CGI->{"opt_label_$key"};
291 next unless $name && $value;
293 unless($key) { $key = $CGI->{sku}."-$name"; }
295 my @value = split("\r\n",$value);
304 if(s/\*//g) { $default = 1; $hasdefault = 1; }
308 ($left,$right) = split('=',$_);
311 $left = substr($right,0,3);
314 while($seen{$left}++) { $left++; }
316 $_ = join('=',$left,$right);
317 if($default) { $_ .= "*"; }
321 my $value = join(",\n",@value);
323 $key =~ s/_/-/g; # javascript won't handle form names with '-'
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);
337 <form action="[area @@MV_PAGE@@]" method="post">
338 [if scratch ui_failure]
341 <font color="__CONTRAST__">[scratch ui_failure][set ui_failure][/set]</font>
346 [if scratch ui_message]
349 <font color="__CONTRAST__">[scratch ui_message][set ui_message][/set]</font>
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">
360 <table border="0"><tr><td valign="top">
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"]
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]">
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
376 mv_nextpage=@@MV_PAGE@@
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]">
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>');
389 my @vals = split(',',q{[sql-param o_value]});
390 map { s/[\r\n]//g; } @vals;
391 return join("\n",@vals);
395 <textarea rows="5" cols="30" name="opt_value_[scratch mod_code]">[scratch o_value]</textarea><br>
396 [page href="admin/flex_editor"
398 mv_data_table=options
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>
411 [button text="[L]Commit Changes[/L]"]
413 </td><td><pre> </pre></td><td valign="top">
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_">
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>');
425 <textarea rows="5" cols="30" name="opt_value_"></textarea>
427 [button text="[L]Create option[/L]"]
432 <br><br><b>[L]Clone an existing option set[/L]:</b><br>
437 sql="select DISTINCT sku from [cgi mv_data_table]"
439 <select name="ui_clone_id">
442 [if-clone-data options o_enable]
443 <option value="[clone-code]">[clone-filter 20][clone-description][/clone-filter]
446 </select>[more-list]<br>[more]<br>[/more-list][/query] [button text="[L]Clone options[/L]"]<br>