* Add enclair_db option to UserDB.pm. Allows logging of enclair password
[interchange.git] / code / Widget / country_select.widget
1 # Copyright 2005-2009 Interchange Development Group and others
2
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.  See the LICENSE file for details.
7
8 CodeDef state_select  Widget  1
9 CodeDef state_select  Description State (needs country_select)
10 CodeDef state_select  Help Requires country_select widget for country to work properly
11 CodeDef state_select  Routine <<EOR
12 sub {
13         my $opt = shift;
14         my $sel = $opt->{state_element};
15         if(! $sel) {
16                 my $n = $opt->{name};
17                 my $pre = '';
18                 if($n =~ /^([a-z]_)/) {
19                         $pre = $1;
20                 }
21                 $sel = "${pre}state_widget_container";
22         }
23         $opt->{type} = 'hidden';
24         my $wid = Vend::Form::display($opt);
25         return qq{$wid<span id="$sel"></span>};
26 }
27 EOR
28
29 CodeDef state_select ExtraMeta <<EOM
30 {
31         _order => [ qw/
32                         state_element
33                 /],
34         state_element => {
35                 label => 'State element ID',
36                 help => 'The CSS ID of the span containing the dynamic widget. The default is usually good (state_widget_container or b_state_widget_container)',
37                 widget => 'text_30',
38         },
39 }
40 EOM
41
42 CodeDef country_select  Widget  1
43 CodeDef country_select  Description Country
44 CodeDef country_select  Help Requires state_select widget for state to work properly
45 CodeDef country_select  Routine <<EOR
46 sub {
47     my ($opt) = @_;
48         my $name = $opt->{name} ||= 'country';
49
50         use vars qw/$Tag/;
51
52         my $sel = $opt->{state_element};
53
54         my $pre = '';
55
56         if(! $sel) {
57                 my $n = $opt->{name};
58                 if($n =~ /^([a-z]_)/) {
59                         $pre = $1;
60                 }
61                 $sel = "${pre}state_widget_container";
62         }
63
64         my $svar = $opt->{state_var} || $opt->{state_variable} || "${pre}state";
65         my $svar_in = $svar . '_cs_in';
66         my $size = $opt->{state_size} || $opt->{cols} || $opt->{width} || '16';
67         my $ctab = $opt->{country_table} || 'country';
68         $opt->{state_style} ||= 'font-style: italic; font-size: smaller';
69
70         my $die = sub {
71                 my ($msg, @arg) = @_;
72                 $msg = errmsg($msg) if @arg;
73                 $Tag->error({ name => 'country_select widget', set => $msg});
74                 ::logError($msg);
75                 return undef;
76         };
77
78         my $pr; 
79         my $ap;
80
81         my $stab = $opt->{state_table} || 'state';
82
83         my $csort = $opt->{country_sort} || ($opt->{no_region} ? 'name' : 'sorder,name');
84         my $ssort = $opt->{state_sort} || 'country,name';
85
86         my @csort = grep /\w/, split /[\s,\0]+/, $csort;
87
88         my $csort_sub = sub {
89                 for(@csort) {
90                         my $so = $a->{$_} cmp $b->{$_};
91                         return $so if $so;
92                 }
93                 return 0;
94         };
95
96         my @ssort = grep /\w/, split /[\s,\0]+/, $ssort;
97
98         my $ssort_sub = sub {
99                 for(@ssort) {
100                         my $so = $a->{$_} cmp $b->{$_};
101                         return $so if $so;
102                 }
103                 return 0;
104         };
105
106         my $cdb = dbref($ctab) or return $die->('country table %s not found', $ctab);
107         my $sdb = dbref($stab) or return $die->('state table %s not found', $stab);
108         $ctab = $cdb->name();
109         $stab = $sdb->name();
110         my $cq = "select * from $ctab";
111         my $sq = "select * from $stab";
112         my $cary = $cdb->query({ sql => $cq, hashref => 1});
113         my $sary = $sdb->query({ sql => $sq, hashref => 1});
114
115         @csort = grep exists($cary->[0]{$_}), @csort;
116         @ssort = grep exists($sary->[0]{$_}), @ssort;
117
118         @$cary = sort $csort_sub @$cary;
119         @$sary = sort $ssort_sub @$sary;
120
121         if($opt->{only_with_shipping}) {
122                 @$cary = grep $_->{shipmodes} =~ /\w/, @$cary;
123         }
124
125         my %states;
126
127         for my $s (@$sary) {
128                 my $c = $s->{country};
129                 push @{$states{$c} ||= []}, [ $s->{state}, $s->{name} ];
130         }
131
132         my @copts;
133         my %no_state;
134
135         my $v_formv = "${pre}formv";
136         my $v_csval = "${pre}csval";
137         my $v_f = "${pre}f";
138         my $v_no_state = "${pre}no_state";
139         my $v_state_tary = "${pre}state_tary";
140         my $v_state_vary = "${pre}state_vary";
141
142         my $prev;
143         for my $c (@$cary) {
144                 if($c->{no_state}) {
145                         $no_state{$c->{code}} = 1;
146                 }
147                 if(! $opt->{no_region} and $c->{region} and $c->{region} ne $prev) {
148                         push @copts, ["~~" . $c->{region} . "~~"];
149                         $prev = $c->{region};
150                 }
151                 push @copts, [ $c->{code}, $c->{name} ];
152         }
153
154         my @pre;
155         push @pre, <<EOF;
156 <script>
157         var $v_formv;
158         var $v_no_state = new Array;
159 EOF
160
161         for(keys %no_state) {
162                 push @pre, "$v_no_state\['$_'] = 1";
163         }
164
165         push @pre, <<EOF;
166         var $v_state_vary = new Array;
167         var $v_state_tary = new Array;
168 EOF
169
170         for(keys %states) {
171                 my $sa = $states{$_};
172                 my @sv;
173                 my @st;
174                 my %seen;
175                 @$sa = grep !$seen{$_->[0]}++, @$sa;
176                 for my $e (@$sa) {
177                         push @sv, $e->[0];
178                         push @st, $e->[1];
179                 }
180
181                 for(@sv) { s/'/\\'/g; }
182                 for(@st) { s/'/\\'/g; }
183
184                 my $string = "$v_state_vary\['$_'] = ['";
185                 $string .= join "','", '', @sv;
186                 $string .= "'];";
187                 push @pre, $string;
188                 $string = "$v_state_tary\['$_'] = ['";
189                 $string .= join "','", errmsg('--select state--'), @st;
190                 $string .= "'];";
191                 push @pre, $string;
192         }
193
194         my $cvar = $opt->{name};
195         $cvar =~ s/\W+/_/g;
196
197         my $extra = $opt->{state_extra} ? " $opt->{state_extra}" : '';
198         my $state_js = $opt->{state_js} ? "; $opt->{state_js}" : '';
199         my $country_js = $opt->{country_js} ? "; $opt->{country_js}" : '';
200         for ($state_js, $country_js) { s|\bthis\.form\b|$v_formv|g }
201
202         push @pre, <<EOF;
203         function ${cvar}_widget_adjust_state (cel,sval) {
204                 var sbox = document.getElementById('$sel');
205                 var country = cel.value;
206
207                 if(! $v_formv) {
208                         $v_formv=cel.form;
209                 }
210
211                 if(! sval) {
212                         if($v_formv.$svar && $v_formv.$svar.value)
213                                 sval = $v_formv.$svar.value;
214                         else sval = '';
215                 }
216
217                 if(! sbox) return;
218                 if($v_no_state\[country]) {
219                         sbox.innerHTML = '<span style="$opt->{state_style}">No state required</span>';
220                         $v_formv.$svar.value = '';
221                         return;
222                 }
223                 var svary = $v_state_vary\[country];
224                 if(! svary) {
225                         var val = '';
226                         sbox.innerHTML = '<input type="text" size="$size" name="$svar_in" id="$svar_in" value="' + sval + '" onChange="$v_formv.$svar.value = this.value"$extra>';
227                         $v_formv.$svar.value=sval;
228
229                         return;
230                 }
231                 var stary = $v_state_tary\[country];
232
233                 var str = '<select name="$svar_in" id="$svar_in" onChange="$v_formv.$svar.value = this.value$state_js"$extra>';
234                 for(var i = 0; i < svary.length; i++) {
235                         str += '<option value="' + svary[i] + '"';
236                         if(svary[i] == sval)
237                                 str += ' SELECTED';
238                         str += '>';
239                         str += stary[i];
240                 }
241                 str += '</select>';
242                 sbox.innerHTML = str;
243
244                 return;
245         }
246 </script>
247 EOF
248
249         my $sval = $CGI::values{$svar} || $::Values->{$svar};
250         $sval = HTML::Entities::encode($sval, $ESCAPE_CHARS::std);
251         $sval = $Tag->jsq($sval) || "''";
252         my $fname = $opt->{form_name} || 'nevairbe';
253
254         my $prepend = join "\n", @pre;
255
256         if(my $sub = $opt->{callback_prescript}) {
257                 $sub->($prepend);
258         }
259         else {
260                 $opt->{prepend} = '' unless defined $opt->{prepend};
261                 $opt->{prepend} .= "\n" if length $opt->{prepend};
262                 $opt->{prepend} .= $prepend;
263         }
264
265
266         my $append = <<EOF;
267 <script>
268         var $v_f = document.$fname;
269         var $v_csval = $sval;
270         if(!$v_f) {
271                 for(var i = 0; i < document.forms.length; i++) {
272                         $v_f = document.forms[i];
273                         if($v_f.$opt->{name}) {
274                                 if($v_f.$svar && $v_f.$svar.value) 
275                                         $v_csval = $v_f.$svar.value;
276                                 ${cvar}_widget_adjust_state($v_f.$opt->{name}, $v_csval);
277                                 break;
278                         }
279                 }
280         }
281         $v_formv = $v_f;
282         if($v_formv.$svar) {
283                 csval = $v_formv.$svar.value;
284         }
285         ${cvar}_widget_adjust_state($v_formv.$opt->{name}, $v_csval);
286
287 </script>
288 EOF
289
290         if(my $sub = $opt->{callback_postscript}) {
291                 $sub->($append);
292         }
293         else {
294                 $opt->{append} = '' unless defined $opt->{append};
295                 $opt->{append} .= "\n" if length $opt->{append};
296                 $opt->{append} .= $append;
297         }
298
299         $opt->{js} = qq{ onLoad="${cvar}_widget_adjust_state(this)" onChange="${cvar}_widget_adjust_state(this)$country_js"};
300         my @out;
301         #push @out, '<xmp>';
302         #push @out, ::uneval(\%states);
303         #push @out, '</xmp>';
304         
305         $opt->{type} = 'select';
306         push @out, Vend::Form::display($opt, {}, \@copts);
307
308         return join "\n", @out;
309 }
310 EOR
311
312 CodeDef country_select ExtraMeta <<EOM
313 {
314         _order => [ qw/
315                         state_var
316                         state_style
317                         state_class
318                         country_sort
319                         no_region
320                         only_with_shipping
321                         form_name
322                         country_table
323                         state_table
324                         state_element
325                         state_js
326                 /],
327         state_var => {
328                 label => 'State variable',
329                 help => 'default is <i>state</i>, might use <i>b_state</i> instead',
330                 widget => 'text_16',
331         },
332         state_class => {
333                 label => 'CSS class for state',
334                 help => 'Modify look of state text',
335                 widget => 'text_20',
336         },
337         state_style => {
338                 label => 'CSS style for state',
339                 help => 'Modify look of state text',
340                 widget => 'text_60',
341         },
342         no_region => {
343                 label => 'Region sort',
344                 help => 'Controls country groupings',
345                 options => '=Region sort, 1=No region sort',
346                 widget => 'select',
347         },
348         only_with_shipping => {
349                 label => 'Only with shipping',
350                 help => 'Only show countries that have value in shipmodes',
351                 options => '=All countries, 1=Only with shipping',
352                 widget => 'select',
353         },
354         country_sort => {
355                 label => 'Country sort order',
356                 help => 'Should be "name" if no region sort, "sorder,name" with region',
357                 widget => 'text_16',
358         },
359         country_table => {
360                 label => 'Country table',
361                 help => 'default is usually good (country)',
362                 widget => 'text_16',
363         },
364         state_sort => {
365                 label => 'State sort order',
366                 help => 'Default of <i>country,name</i> is usually OK',
367                 widget => 'text_16',
368         },
369         state_table => {
370                 label => 'State table',
371                 help => 'default is usually good (state)',
372                 widget => 'text_16',
373         },
374         state_element => {
375                 label => 'State element ID',
376                 help => 'The CSS ID of the span containing the dynamic widget. The default is usually good (state_widget_container or b_state_widget_container)',
377                 widget => 'text_30',
378         },
379         state_js => {
380                 label => 'State javascript',
381                 help => 'Runs specified javascript under onChange. E.g. "state_js=check_tax(this.form)"',
382                 widget => 'text_20',
383         },
384 }
385 EOM