| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Maypole::Model::CDBI::AsForm; |
|
2
|
1
|
|
|
1
|
|
7315
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
35
|
|
|
3
|
|
|
|
|
|
|
|
|
4
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
31
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
7
|
use base 'Exporter'; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
90
|
|
|
7
|
1
|
|
|
1
|
|
2768
|
use Data::Dumper; |
|
|
1
|
|
|
|
|
13869
|
|
|
|
1
|
|
|
|
|
385
|
|
|
8
|
1
|
|
|
1
|
|
4876
|
use Class::DBI::Plugin::Type (); |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
use HTML::Element; |
|
10
|
|
|
|
|
|
|
use Carp qw/cluck/; |
|
11
|
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our $OLD_STYLE = 0; |
|
13
|
|
|
|
|
|
|
our @EXPORT = |
|
14
|
|
|
|
|
|
|
qw( |
|
15
|
|
|
|
|
|
|
to_cgi to_field foreign_input_delimiter search_inputs unselect_element |
|
16
|
|
|
|
|
|
|
_field_from_how _field_from_relationship _field_from_column |
|
17
|
|
|
|
|
|
|
_to_textarea _to_textfield _to_select _select_guts |
|
18
|
|
|
|
|
|
|
_to_foreign_inputs _to_enum_select _to_bool_select |
|
19
|
|
|
|
|
|
|
_to_hidden _to_link_hidden _rename_foreign_input _to_readonly |
|
20
|
|
|
|
|
|
|
_options_from_objects _options_from_arrays _options_from_hashes |
|
21
|
|
|
|
|
|
|
_options_from_array _options_from_hash |
|
22
|
|
|
|
|
|
|
); |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
our $VERSION = '.97'; |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=head1 NAME |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
Maypole::Model:CDBI::AsForm - Produce HTML form elements for database columns |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
package Music::CD; |
|
33
|
|
|
|
|
|
|
use Maypole::Model::CDBI::AsForm; |
|
34
|
|
|
|
|
|
|
use base 'Class::DBI'; |
|
35
|
|
|
|
|
|
|
use CGI; |
|
36
|
|
|
|
|
|
|
... |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
sub create_or_edit { |
|
39
|
|
|
|
|
|
|
my $self = shift; |
|
40
|
|
|
|
|
|
|
my %cgi_field = $self->to_cgi; |
|
41
|
|
|
|
|
|
|
return start_form, |
|
42
|
|
|
|
|
|
|
(map { "$_: ". $cgi_field{$_}->as_HTML." " } |
|
43
|
|
|
|
|
|
|
$class->Columns), |
|
44
|
|
|
|
|
|
|
end_form; |
|
45
|
|
|
|
|
|
|
} |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
. . . |
|
49
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
# Somewhere else in a Maypole application about beer... |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
$beer->to_field('brewery', 'textfield', { |
|
56
|
|
|
|
|
|
|
name => 'brewery_id', value => $beer->brewery, |
|
57
|
|
|
|
|
|
|
# however, no need to set value since $beer is object |
|
58
|
|
|
|
|
|
|
}); |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
# Rate a beer |
|
61
|
|
|
|
|
|
|
$beer->to_field(rating => select => { |
|
62
|
|
|
|
|
|
|
items => [1 , 2, 3, 4, 5], |
|
63
|
|
|
|
|
|
|
}); |
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
# Select a Brewery to visit in the UK |
|
66
|
|
|
|
|
|
|
Brewery->to_field(brewery_id => { |
|
67
|
|
|
|
|
|
|
items => [ Brewery->search_like(location => 'UK') ], |
|
68
|
|
|
|
|
|
|
}); |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
# Make a select for a boolean field |
|
71
|
|
|
|
|
|
|
$Pub->to_field('open' , { items => [ {'Open' => 1, 'Closed' => 0 } ] }); |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
$beer->to_field('brewery', { |
|
74
|
|
|
|
|
|
|
selected => $beer->brewery, # again not necessary since caller is obj. |
|
75
|
|
|
|
|
|
|
}); |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
$beer->to_field('brewery', 'link_hidden', {r => $r, uri => 'www.maypole.perl.org/brewery/view/'.$beer->brewery}); |
|
79
|
|
|
|
|
|
|
# an html link that is also a hidden input to the object. R is required to |
|
80
|
|
|
|
|
|
|
# make the uri unless you pass a uri |
|
81
|
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
##################################################### |
|
85
|
|
|
|
|
|
|
# Templates Usage |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
... |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
[% classmetadata.colnames.$col %] : |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
[% object.to_field(col).as_XML %] |
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
. . . |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
Brewery : |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
[% object.to_field('brewery', { selected => 23} ).as_XML %] |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
. . . |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
##################################################### |
|
115
|
|
|
|
|
|
|
# Advanced Usage |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
# has_many select |
|
118
|
|
|
|
|
|
|
package Job; |
|
119
|
|
|
|
|
|
|
__PACKAGE__->has_a('job_employer' => 'Employer'); |
|
120
|
|
|
|
|
|
|
__PACKAGE__->has_a('contact' => 'Contact') |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
package Contact; |
|
123
|
|
|
|
|
|
|
__PACKAGE__->has_a('cont_employer' => 'Employer'); |
|
124
|
|
|
|
|
|
|
__PACKAGE__->has_many('jobs' => 'Job', |
|
125
|
|
|
|
|
|
|
{ join => { job_employer => 'cont_employer' }, |
|
126
|
|
|
|
|
|
|
constraint => { 'finshed' => 0 }, |
|
127
|
|
|
|
|
|
|
order_by => "created ASC", |
|
128
|
|
|
|
|
|
|
} |
|
129
|
|
|
|
|
|
|
); |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
package Employer; |
|
132
|
|
|
|
|
|
|
__PACKAGE__->has_many('jobs' => 'Job',); |
|
133
|
|
|
|
|
|
|
__PACKAGE__->has_many('contacts' => 'Contact', |
|
134
|
|
|
|
|
|
|
order_by => 'name DESC', |
|
135
|
|
|
|
|
|
|
); |
|
136
|
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
# Choose some jobs to add to a contact (has multiple attribute). |
|
139
|
|
|
|
|
|
|
my $job_sel = Contact->to_field('jobs'); # Uses constraint and order by |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
# Choose a job from $contact->jobs |
|
143
|
|
|
|
|
|
|
my $job_sel = $contact->to_field('jobs'); |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
1; |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
This module helps to generate HTML forms for creating new database rows |
|
153
|
|
|
|
|
|
|
or editing existing rows. It maps column names in a database table to |
|
154
|
|
|
|
|
|
|
HTML form elements which fit the schema. Large text fields are turned |
|
155
|
|
|
|
|
|
|
into textareas, and fields with a has-a relationship to other |
|
156
|
|
|
|
|
|
|
C tables are turned into select drop-downs populated with |
|
157
|
|
|
|
|
|
|
objects from the joined class. |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
=head1 ARGUMENTS HASH |
|
161
|
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
This provides a convenient way to tweak AsForm's behavior in exceptional or |
|
163
|
|
|
|
|
|
|
not so exceptional instances. Below describes the arguments hash and |
|
164
|
|
|
|
|
|
|
example usages. |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
$beer->to_field($col, $how, $args); |
|
168
|
|
|
|
|
|
|
$beer->to_field($col, $args); |
|
169
|
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
Not all _to_* methods pay attention to all arguments. For example, '_to_textfield' does not look in $args->{'items'} at all. |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=over |
|
173
|
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=item name -- the name the element will have , this trumps the derived name. |
|
175
|
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
$beer->to_field('brewery', 'readonly', { |
|
177
|
|
|
|
|
|
|
name => 'brewery_id' |
|
178
|
|
|
|
|
|
|
}); |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=item value -- the initial value the element will have, trumps derived value |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
$beer->to_field('brewery', 'textfield', { |
|
183
|
|
|
|
|
|
|
name => 'brewery_id', value => $beer->brewery, |
|
184
|
|
|
|
|
|
|
# however, no need to set value since $beer is object |
|
185
|
|
|
|
|
|
|
}); |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
=item items -- array of items generally used to make select box options |
|
188
|
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
Can be array of objects, hashes, arrays, or strings, or just a hash. |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
# Rate a beer |
|
192
|
|
|
|
|
|
|
$beer->to_field(rating => select => { |
|
193
|
|
|
|
|
|
|
items => [1 , 2, 3, 4, 5], |
|
194
|
|
|
|
|
|
|
}); |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# Select a Brewery to visit in the UK |
|
197
|
|
|
|
|
|
|
Brewery->to_field(brewery_id => { |
|
198
|
|
|
|
|
|
|
items => [ Brewery->search_like(location => 'UK') ], |
|
199
|
|
|
|
|
|
|
}); |
|
200
|
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
# Make a select for a boolean field |
|
202
|
|
|
|
|
|
|
$Pub->to_field('open' , { items => [ {'Open' => 1, 'Closed' => 0 } ] }); |
|
203
|
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
=item selected -- something representing which item is selected in a select box |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
$beer->to_field('brewery', { |
|
207
|
|
|
|
|
|
|
selected => $beer->brewery, # again not necessary since caller is obj. |
|
208
|
|
|
|
|
|
|
}); |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
Can be an simple scalar id, an object, or an array of either |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
=item class -- the class for which the input being made for field pertains to. |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
This in almost always derived in cases where it may be difficult to derive, -- |
|
215
|
|
|
|
|
|
|
# Select beers to serve on handpump |
|
216
|
|
|
|
|
|
|
Pub->to_field(handpumps => select => { |
|
217
|
|
|
|
|
|
|
class => 'Beer', order_by => 'name ASC', multiple => 1, |
|
218
|
|
|
|
|
|
|
}); |
|
219
|
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=item column_type -- a string representing column type |
|
221
|
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
$pub->to_field('open', 'bool_select', { |
|
223
|
|
|
|
|
|
|
column_type => "bool('Closed', 'Open'), |
|
224
|
|
|
|
|
|
|
}); |
|
225
|
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=item column_nullable -- flag saying if column is nullable or not |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
Generally this can be set to get or not get a null/empty option added to |
|
229
|
|
|
|
|
|
|
a select box. AsForm attempts to call "$class->column_nullable" to set this |
|
230
|
|
|
|
|
|
|
and it defaults to true if there is no shuch method. |
|
231
|
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
$beer->to_field('brewery', { column_nullable => 1 }); |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
=item r or request -- the Mapyole request object |
|
235
|
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=item uri -- uri for a link , used in methods such as _to_link_hidden |
|
237
|
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
$beer->to_field('brewery', 'link_hidden', |
|
239
|
|
|
|
|
|
|
{r => $r, uri => 'www.maypole.perl.org/brewery/view/'.$beer->brewery}); |
|
240
|
|
|
|
|
|
|
# an html link that is also a hidden input to the object. R is required to |
|
241
|
|
|
|
|
|
|
# make the uri unless you pass a uri |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
=item order_by, constraint, join |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
These are used in making select boxes. order_by is a simple order by clause |
|
246
|
|
|
|
|
|
|
and constraint and join are hashes used to limit the rows selected. The |
|
247
|
|
|
|
|
|
|
difference is that join uses methods of the object and constraint uses |
|
248
|
|
|
|
|
|
|
static values. You can also specify these in the relationship definitions. |
|
249
|
|
|
|
|
|
|
See the relationships documentation of how to set arbitrayr meta info. |
|
250
|
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
BeerDB::LondonBeer->has_a('brewery', 'BeerDB::Brewery', |
|
252
|
|
|
|
|
|
|
order_by => 'brewery_name ASC', |
|
253
|
|
|
|
|
|
|
constraint => {location => 'London'}, |
|
254
|
|
|
|
|
|
|
'join' => {'brewery_tablecolumn => 'beer_obj_column'}, |
|
255
|
|
|
|
|
|
|
); |
|
256
|
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
=item no_hidden_constraints -- |
|
258
|
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
Tell AsForm not to make hidden inputs for relationship constraints. It does |
|
260
|
|
|
|
|
|
|
this sometimes when making foreign inputs. However, i think it should not |
|
261
|
|
|
|
|
|
|
do this and that the FromCGI 's _create_related method should do it. |
|
262
|
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
=back |
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
=head2 to_cgi |
|
266
|
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
$self->to_cgi([@columns, $args]); |
|
268
|
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
This returns a hash mapping all the column names to HTML::Element objects |
|
270
|
|
|
|
|
|
|
representing form widgets. It takes two opitonal arguments -- a list of |
|
271
|
|
|
|
|
|
|
columns and a hashref of hashes of arguments for each column. If called with an object like for editing, the inputs will have the object's values. |
|
272
|
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
$self->to_cgi(); # uses $self->columns; # most used |
|
274
|
|
|
|
|
|
|
$self->to_cgi(qw/brewery style rating/); # sometimes |
|
275
|
|
|
|
|
|
|
# and on rare occassions this is desireable if you have a lot of fields |
|
276
|
|
|
|
|
|
|
# and dont want to call to_field a bunch of times just to tweak one or |
|
277
|
|
|
|
|
|
|
# two of them. |
|
278
|
|
|
|
|
|
|
$self->to_cgi(@cols, {brewery => { |
|
279
|
|
|
|
|
|
|
how => 'textfield' # too big for select |
|
280
|
|
|
|
|
|
|
}, |
|
281
|
|
|
|
|
|
|
style => { |
|
282
|
|
|
|
|
|
|
column_nullable => 0, |
|
283
|
|
|
|
|
|
|
how => 'select', |
|
284
|
|
|
|
|
|
|
items => ['Ale', 'Lager'] |
|
285
|
|
|
|
|
|
|
} |
|
286
|
|
|
|
|
|
|
}); |
|
287
|
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
=cut |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
sub to_cgi { |
|
291
|
|
|
|
|
|
|
my ($class, @columns) = @_; |
|
292
|
|
|
|
|
|
|
my $args = {}; |
|
293
|
|
|
|
|
|
|
if (not @columns) { |
|
294
|
|
|
|
|
|
|
@columns = $class->columns; |
|
295
|
|
|
|
|
|
|
# Eventually after stabalization, we could add display_columns |
|
296
|
|
|
|
|
|
|
#keys map { $_ => 1 } ($class->display_columns, $class->columns); |
|
297
|
|
|
|
|
|
|
} else { |
|
298
|
|
|
|
|
|
|
if ( ref $columns[-1] eq 'HASH' ) { |
|
299
|
|
|
|
|
|
|
$args = pop @columns; |
|
300
|
|
|
|
|
|
|
} |
|
301
|
|
|
|
|
|
|
} |
|
302
|
|
|
|
|
|
|
map { $_ => $class->to_field($_, $args->{$_}) } @columns; |
|
303
|
|
|
|
|
|
|
} |
|
304
|
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
=head2 to_field($field [, $how][, $args]) |
|
306
|
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
This maps an individual column to a form element. The C argument |
|
308
|
|
|
|
|
|
|
can be used to force the field type into any you want. All that you need |
|
309
|
|
|
|
|
|
|
is a method named "_to_$how" in your class. Your class inherits many from |
|
310
|
|
|
|
|
|
|
AsForm already. |
|
311
|
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
If C is specified but the class cannot call the method it maps to, |
|
313
|
|
|
|
|
|
|
then AsForm will issue a warning and the default input will be made. |
|
314
|
|
|
|
|
|
|
You can write your own "_to_$how" methods and AsForm comes with many. |
|
315
|
|
|
|
|
|
|
See C. You can also pass this argument in $args->{how}. |
|
316
|
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
=cut |
|
319
|
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
sub to_field { |
|
321
|
|
|
|
|
|
|
my ($self, $field, $how, $args) = @_; |
|
322
|
|
|
|
|
|
|
if (ref $how) { $args = $how; $how = ''; } |
|
323
|
|
|
|
|
|
|
unless ($how) { $how = $args->{how} || ''; } |
|
324
|
|
|
|
|
|
|
#warn "In to_field field is $field how is $how. args ar e" . Dumper($args) . " \n"; |
|
325
|
|
|
|
|
|
|
# Set sensible default value |
|
326
|
|
|
|
|
|
|
if ($field and not defined $args->{default}) { |
|
327
|
|
|
|
|
|
|
my $def = $self->column_default($field) ; |
|
328
|
|
|
|
|
|
|
# exclude defaults we don't want actually put as value for input |
|
329
|
|
|
|
|
|
|
if (defined $def) { |
|
330
|
|
|
|
|
|
|
$def = $def =~ /(^0000-00-00.*$|^0[0]*$|^0\.00$|CURRENT_TIMESTAMP|NULL)/i ? '' : $def ; |
|
331
|
|
|
|
|
|
|
$args->{default} = $def; |
|
332
|
|
|
|
|
|
|
} |
|
333
|
|
|
|
|
|
|
} |
|
334
|
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
return $self->_field_from_how($field, $how, $args) || |
|
336
|
|
|
|
|
|
|
$self->_field_from_relationship($field, $args) || |
|
337
|
|
|
|
|
|
|
$self->_field_from_column($field, $args) || |
|
338
|
|
|
|
|
|
|
$self->_to_textfield($field, $args); |
|
339
|
|
|
|
|
|
|
} |
|
340
|
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
=head2 search_inputs |
|
343
|
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
my $cgi = $class->search_inputs ([$args]); # optional $args |
|
345
|
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
Returns hash or hashref of search inputs elements for a class making sure the |
|
347
|
|
|
|
|
|
|
inputs are empty of any initial values. |
|
348
|
|
|
|
|
|
|
You can specify what columns you want inputs for in |
|
349
|
|
|
|
|
|
|
$args->{columns} or |
|
350
|
|
|
|
|
|
|
by the method "search_columns". The default is "display_columns". |
|
351
|
|
|
|
|
|
|
If you want to te search on columns in related classes you can do that by |
|
352
|
|
|
|
|
|
|
specifying a one element hashref in place of the column name where |
|
353
|
|
|
|
|
|
|
the key is the related "column" (has_a or has_many method for example) and |
|
354
|
|
|
|
|
|
|
the value is a list ref of columns to search on in the related class. |
|
355
|
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
Example: |
|
357
|
|
|
|
|
|
|
sub BeerDB::Beer::search_columns { |
|
358
|
|
|
|
|
|
|
return ( 'name' , 'rating', { brewery => [ 'name', 'location'] } ); |
|
359
|
|
|
|
|
|
|
} |
|
360
|
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
# Now foreign inputs are made for Brewery name and location and the |
|
362
|
|
|
|
|
|
|
# there will be no name clashing and processing can be automated. |
|
363
|
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
=cut |
|
365
|
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
sub search_inputs { |
|
368
|
|
|
|
|
|
|
my ($class, $args) = @_; |
|
369
|
|
|
|
|
|
|
$class = ref $class || $class; |
|
370
|
|
|
|
|
|
|
#my $accssr_class = { $class->accessor_classes }; |
|
371
|
|
|
|
|
|
|
my %cgi; |
|
372
|
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
$args->{columns} ||= $class->can('search_columns') ?[$class->search_columns] : [$class->display_columns]; |
|
374
|
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
foreach my $field ( @{ $args->{columns} } ) { |
|
376
|
|
|
|
|
|
|
my $base_args = { |
|
377
|
|
|
|
|
|
|
no_hidden_constraints => 1, |
|
378
|
|
|
|
|
|
|
column_nullable => 1, # empty option on select boxes |
|
379
|
|
|
|
|
|
|
value => '', |
|
380
|
|
|
|
|
|
|
}; |
|
381
|
|
|
|
|
|
|
if ( ref $field eq "HASH" ) { # foreign search fields |
|
382
|
|
|
|
|
|
|
my ($accssr, $cols) = each %$field; |
|
383
|
|
|
|
|
|
|
$base_args->{columns} = $cols; |
|
384
|
|
|
|
|
|
|
unless ( @$cols ) { |
|
385
|
|
|
|
|
|
|
# default to search fields for related |
|
386
|
|
|
|
|
|
|
#$cols = $accssr_class->{$accssr}->search_columns; |
|
387
|
|
|
|
|
|
|
die ("$class search_fields error: Must specify at least one column to search in the foreign object named '$accssr'"); |
|
388
|
|
|
|
|
|
|
} |
|
389
|
|
|
|
|
|
|
my $fcgi = $class->to_field($accssr, 'foreign_inputs', $base_args); |
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
# unset the default values for a select box |
|
392
|
|
|
|
|
|
|
foreach (keys %$fcgi) { |
|
393
|
|
|
|
|
|
|
my $el = $fcgi->{$_}; |
|
394
|
|
|
|
|
|
|
if ($el->tag eq 'select') { |
|
395
|
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
$class->unselect_element($el); |
|
397
|
|
|
|
|
|
|
my ($first, @content) = $el->content_list; |
|
398
|
|
|
|
|
|
|
my @fc = $first->content_list; |
|
399
|
|
|
|
|
|
|
my $val = $first ? $first->attr('value') : undef; |
|
400
|
|
|
|
|
|
|
if ($first and (@fc > 0 or (defined $val and $val ne '')) ) { # something ( $first->attr('value') ne '' or |
|
401
|
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
# push an empty option on stactk |
|
403
|
|
|
|
|
|
|
$el->unshift_content(HTML::Element->new('option')); |
|
404
|
|
|
|
|
|
|
} |
|
405
|
|
|
|
|
|
|
} |
|
406
|
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
} |
|
408
|
|
|
|
|
|
|
$cgi{$accssr} = $fcgi; |
|
409
|
|
|
|
|
|
|
delete $base_args->{columns}; |
|
410
|
|
|
|
|
|
|
} else { |
|
411
|
|
|
|
|
|
|
$cgi{$field} = $class->to_field($field, $base_args); #{no_select => $args->{no_select}{$field} }); |
|
412
|
|
|
|
|
|
|
my $el = $cgi{$field}; |
|
413
|
|
|
|
|
|
|
if ($el->tag eq 'select') { |
|
414
|
|
|
|
|
|
|
$class->unselect_element($el); |
|
415
|
|
|
|
|
|
|
my ($first, @content) = $el->content_list; |
|
416
|
|
|
|
|
|
|
if ($first and $first->content_list) { # something |
|
417
|
|
|
|
|
|
|
#(defined $first->attr('value') or $first->attr('value') ne '')) |
|
418
|
|
|
|
|
|
|
# push an empty option on stactk |
|
419
|
|
|
|
|
|
|
$el->unshift_content(HTML::Element->new('option')); |
|
420
|
|
|
|
|
|
|
} |
|
421
|
|
|
|
|
|
|
} |
|
422
|
|
|
|
|
|
|
} |
|
423
|
|
|
|
|
|
|
} |
|
424
|
|
|
|
|
|
|
return \%cgi; |
|
425
|
|
|
|
|
|
|
} |
|
426
|
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
=head2 unselect_element |
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
unselect any selected elements in a HTML::Element select list widget |
|
433
|
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
=cut |
|
435
|
|
|
|
|
|
|
sub unselect_element { |
|
436
|
|
|
|
|
|
|
my ($self, $el) = @_; |
|
437
|
|
|
|
|
|
|
if (ref $el && $el->can('tag') && $el->tag eq 'select') { |
|
438
|
|
|
|
|
|
|
foreach my $opt ($el->content_list) { |
|
439
|
|
|
|
|
|
|
$opt->attr('selected', undef) if $opt->attr('selected'); |
|
440
|
|
|
|
|
|
|
} |
|
441
|
|
|
|
|
|
|
} |
|
442
|
|
|
|
|
|
|
} |
|
443
|
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
=head2 _field_from_how($field, $how,$args) |
|
445
|
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
Returns an input element based the "how" parameter or nothing at all. |
|
447
|
|
|
|
|
|
|
Override at will. |
|
448
|
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
=cut |
|
450
|
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
sub _field_from_how { |
|
452
|
|
|
|
|
|
|
my ($self, $field, $how, $args) = @_; |
|
453
|
|
|
|
|
|
|
return unless $how; |
|
454
|
|
|
|
|
|
|
$args ||= {}; |
|
455
|
|
|
|
|
|
|
no strict 'refs'; |
|
456
|
|
|
|
|
|
|
my $meth = "_to_$how"; |
|
457
|
|
|
|
|
|
|
if (not $self->can($meth)) { |
|
458
|
|
|
|
|
|
|
warn "Class can not $meth"; |
|
459
|
|
|
|
|
|
|
return; |
|
460
|
|
|
|
|
|
|
} |
|
461
|
|
|
|
|
|
|
return $self->$meth($field, $args); |
|
462
|
|
|
|
|
|
|
} |
|
463
|
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
=head2 _field_from_relationship($field, $args) |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
Returns an input based on the relationship associated with the field or nothing. |
|
467
|
|
|
|
|
|
|
Override at will. |
|
468
|
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
For has_a it will give select box |
|
470
|
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
=cut |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
sub _field_from_relationship { |
|
474
|
|
|
|
|
|
|
my ($self, $field, $args) = @_; |
|
475
|
|
|
|
|
|
|
return unless $field; |
|
476
|
|
|
|
|
|
|
my $rel_meta = $self->related_meta('r',$field) || return; |
|
477
|
|
|
|
|
|
|
my $rel_name = $rel_meta->{name}; |
|
478
|
|
|
|
|
|
|
my $fclass = $rel_meta->foreign_class; |
|
479
|
|
|
|
|
|
|
my $fclass_is_cdbi = $fclass ? $fclass->isa('Class::DBI') : 0; |
|
480
|
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
# maybe has_a select |
|
482
|
|
|
|
|
|
|
if ($rel_meta->{name} eq 'has_a' and $fclass_is_cdbi) { |
|
483
|
|
|
|
|
|
|
# This condictions allows for trumping of the has_a args |
|
484
|
|
|
|
|
|
|
if (not $rel_meta->{args}{no_select} and not $args->{no_select}) { |
|
485
|
|
|
|
|
|
|
$args->{class} = $fclass; |
|
486
|
|
|
|
|
|
|
return $self->_to_select($field, $args); |
|
487
|
|
|
|
|
|
|
} |
|
488
|
|
|
|
|
|
|
return; |
|
489
|
|
|
|
|
|
|
} |
|
490
|
|
|
|
|
|
|
# maybe has many select |
|
491
|
|
|
|
|
|
|
if ($rel_meta->{name} eq 'has_many' and $fclass_is_cdbi and ref $self) { |
|
492
|
|
|
|
|
|
|
# This condictions allows for trumping of the has_a args |
|
493
|
|
|
|
|
|
|
if (not $rel_meta->{args}{no_select} and not $args->{no_select}) { |
|
494
|
|
|
|
|
|
|
$args->{class} = $fclass; |
|
495
|
|
|
|
|
|
|
my @itms = $self->$field; # need list not iterator |
|
496
|
|
|
|
|
|
|
$args->{items} = \@itms; |
|
497
|
|
|
|
|
|
|
return $self->_to_select($field, $args); |
|
498
|
|
|
|
|
|
|
} |
|
499
|
|
|
|
|
|
|
return; |
|
500
|
|
|
|
|
|
|
} |
|
501
|
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
# maybe foreign inputs |
|
503
|
|
|
|
|
|
|
my %local_cols = map { $_ => 1 } $self->columns; # includes is_a cols |
|
504
|
|
|
|
|
|
|
if ($fclass_is_cdbi and (not $local_cols{$field} or $rel_name eq 'has_own')) { |
|
505
|
|
|
|
|
|
|
$args->{related_meta} = $rel_meta; # suspect faster to set these args |
|
506
|
|
|
|
|
|
|
return $self->_to_foreign_inputs($field, $args); |
|
507
|
|
|
|
|
|
|
} |
|
508
|
|
|
|
|
|
|
return; |
|
509
|
|
|
|
|
|
|
} |
|
510
|
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
=head2 _field_from_column($field, $args) |
|
512
|
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
Returns an input based on the column's characteristics, namely type, or nothing. |
|
514
|
|
|
|
|
|
|
Override at will. |
|
515
|
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
=cut |
|
517
|
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
sub _field_from_column { |
|
519
|
|
|
|
|
|
|
my ($self, $field, $args) = @_; |
|
520
|
|
|
|
|
|
|
# this class and pk are default class and field at this point |
|
521
|
|
|
|
|
|
|
my $class = $args->{class} || $self; |
|
522
|
|
|
|
|
|
|
$class = ref $class || $class; |
|
523
|
|
|
|
|
|
|
$field ||= ($class->primary_columns)[0]; # TODO |
|
524
|
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
# Get column type |
|
526
|
|
|
|
|
|
|
unless ($args->{column_type}) { |
|
527
|
|
|
|
|
|
|
if ($class->can('column_type')) { |
|
528
|
|
|
|
|
|
|
$args->{column_type} = $class->column_type($field); |
|
529
|
|
|
|
|
|
|
} else { |
|
530
|
|
|
|
|
|
|
# Right, have some of this |
|
531
|
|
|
|
|
|
|
eval "package $class; Class::DBI::Plugin::Type->import()"; |
|
532
|
|
|
|
|
|
|
$args->{column_type} = $class->column_type($field); |
|
533
|
|
|
|
|
|
|
} |
|
534
|
|
|
|
|
|
|
} |
|
535
|
|
|
|
|
|
|
my $type = $args->{column_type}; |
|
536
|
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
return $self->_to_textfield($field, $args) |
|
538
|
|
|
|
|
|
|
if $type and $type =~ /^(VAR)?CHAR/i; #common type |
|
539
|
|
|
|
|
|
|
return $self->_to_textarea($field, $args) |
|
540
|
|
|
|
|
|
|
if $type and $type =~ /^(TEXT|BLOB)$/i; |
|
541
|
|
|
|
|
|
|
return $self->_to_enum_select($field, $args) |
|
542
|
|
|
|
|
|
|
if $type and $type =~ /^ENUM\((.*?)\)$/i; |
|
543
|
|
|
|
|
|
|
return $self->_to_bool_select($field, $args) |
|
544
|
|
|
|
|
|
|
if $type and $type =~ /^BOOL/i; |
|
545
|
|
|
|
|
|
|
return $self->_to_readonly($field, $args) |
|
546
|
|
|
|
|
|
|
if $type and $type =~ /^readonly$/i; |
|
547
|
|
|
|
|
|
|
return; |
|
548
|
|
|
|
|
|
|
} |
|
549
|
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
sub _to_textarea { |
|
552
|
|
|
|
|
|
|
my ($self, $col, $args) = @_; |
|
553
|
|
|
|
|
|
|
my $class = $args->{class} || $self; |
|
554
|
|
|
|
|
|
|
$class = ref $class || $class; |
|
555
|
|
|
|
|
|
|
$col ||= ($class->primary_columns)[0]; # TODO |
|
556
|
|
|
|
|
|
|
# pjs added default |
|
557
|
|
|
|
|
|
|
$args ||= {}; |
|
558
|
|
|
|
|
|
|
my $val = $args->{value}; |
|
559
|
|
|
|
|
|
|
|
|
560
|
|
|
|
|
|
|
unless (defined $val) { |
|
561
|
|
|
|
|
|
|
if (ref $self) { |
|
562
|
|
|
|
|
|
|
$val = $self->$col; |
|
563
|
|
|
|
|
|
|
} else { |
|
564
|
|
|
|
|
|
|
$val = $args->{default}; |
|
565
|
|
|
|
|
|
|
$val = '' unless defined $val; |
|
566
|
|
|
|
|
|
|
} |
|
567
|
|
|
|
|
|
|
} |
|
568
|
|
|
|
|
|
|
my ($rows, $cols) = _box($val); |
|
569
|
|
|
|
|
|
|
$rows = $args->{rows} if $args->{rows}; |
|
570
|
|
|
|
|
|
|
$cols = $args->{cols} if $args->{cols};; |
|
571
|
|
|
|
|
|
|
my $name = $args->{name} || $col; |
|
572
|
|
|
|
|
|
|
my $a = |
|
573
|
|
|
|
|
|
|
HTML::Element->new("textarea", name => $name, rows => $rows, cols => $cols); |
|
574
|
|
|
|
|
|
|
$a->push_content($val); |
|
575
|
|
|
|
|
|
|
$OLD_STYLE && return $a->as_HTML; |
|
576
|
|
|
|
|
|
|
$a; |
|
577
|
|
|
|
|
|
|
} |
|
578
|
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
sub _to_textfield { |
|
580
|
|
|
|
|
|
|
my ($self, $col, $args ) = @_; |
|
581
|
|
|
|
|
|
|
use Carp qw/confess/; |
|
582
|
|
|
|
|
|
|
confess "No col passed to _to_textfield" unless $col; |
|
583
|
|
|
|
|
|
|
$args ||= {}; |
|
584
|
|
|
|
|
|
|
my $val = $args->{value}; |
|
585
|
|
|
|
|
|
|
my $name = $args->{name} || $col; |
|
586
|
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
unless (defined $val) { |
|
588
|
|
|
|
|
|
|
if (ref $self) { |
|
589
|
|
|
|
|
|
|
# Case where column inflates. |
|
590
|
|
|
|
|
|
|
# Input would get stringification which could be not good. |
|
591
|
|
|
|
|
|
|
# as in the case of Time::Piece objects |
|
592
|
|
|
|
|
|
|
$val = $self->can($col) ? $self->$col : ''; # in case it is a virtual column |
|
593
|
|
|
|
|
|
|
if (ref $val) { |
|
594
|
|
|
|
|
|
|
if (my $meta = $self->related_meta('',$col)) { |
|
595
|
|
|
|
|
|
|
if (my $code = $meta->{args}{deflate4edit} || $meta->{args}{deflate} ) { |
|
596
|
|
|
|
|
|
|
$val = ref $code ? &$code($val) : $val->$code; |
|
597
|
|
|
|
|
|
|
} elsif ( $val->isa('Class::DBI') ) { |
|
598
|
|
|
|
|
|
|
$val = $val->id; |
|
599
|
|
|
|
|
|
|
} else { |
|
600
|
|
|
|
|
|
|
#warn "No deflate4edit code defined for $val of type " . |
|
601
|
|
|
|
|
|
|
#ref $val . ". Using the stringified value in textfield.."; |
|
602
|
|
|
|
|
|
|
} |
|
603
|
|
|
|
|
|
|
} else { |
|
604
|
|
|
|
|
|
|
$val = $val->id if $val->isa("Class::DBI"); |
|
605
|
|
|
|
|
|
|
} |
|
606
|
|
|
|
|
|
|
} |
|
607
|
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
} else { |
|
609
|
|
|
|
|
|
|
$val = $args->{default}; |
|
610
|
|
|
|
|
|
|
$val = '' unless defined $val; |
|
611
|
|
|
|
|
|
|
} |
|
612
|
|
|
|
|
|
|
} |
|
613
|
|
|
|
|
|
|
my $a; |
|
614
|
|
|
|
|
|
|
# THIS If section is neccessary or you end up with "value" for a vaiue |
|
615
|
|
|
|
|
|
|
# if val is |
|
616
|
|
|
|
|
|
|
$val = '' unless defined $val; |
|
617
|
|
|
|
|
|
|
$a = HTML::Element->new("input", type => "text", name => $name, value =>$val); |
|
618
|
|
|
|
|
|
|
$OLD_STYLE && return $a->as_HTML; |
|
619
|
|
|
|
|
|
|
$a; |
|
620
|
|
|
|
|
|
|
} |
|
621
|
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
=head2 recognized arguments |
|
623
|
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
selected => $object|$id, |
|
625
|
|
|
|
|
|
|
name => $name, |
|
626
|
|
|
|
|
|
|
value => $value, |
|
627
|
|
|
|
|
|
|
where => SQL 'WHERE' clause, |
|
628
|
|
|
|
|
|
|
order_by => SQL 'ORDER BY' clause, |
|
629
|
|
|
|
|
|
|
constraint => hash of constraints to search |
|
630
|
|
|
|
|
|
|
limit => SQL 'LIMIT' clause, |
|
631
|
|
|
|
|
|
|
items => [ @items_of_same_type_to_select_from ], |
|
632
|
|
|
|
|
|
|
class => $class_we_are_selecting_from |
|
633
|
|
|
|
|
|
|
stringify => $stringify_coderef|$method_name |
|
634
|
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
=head2 1. a select box out of a has_a or has_many related class. |
|
637
|
|
|
|
|
|
|
# For has_a the default behavior is to make a select box of every element in |
|
638
|
|
|
|
|
|
|
# related class and you choose one. |
|
639
|
|
|
|
|
|
|
#Or explicitly you can create one and pass options like where and order |
|
640
|
|
|
|
|
|
|
BeerDB::Beer->to_field('brewery','select', {where => "location = 'Germany'"); |
|
641
|
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
# For has_many the default is to get a multiple select box with all objects. |
|
643
|
|
|
|
|
|
|
# If called as an object method, the objects existing ones will be selected. |
|
644
|
|
|
|
|
|
|
Brewery::BeerDB->to_field('beers','select', {where => "rating > 5"}); |
|
645
|
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
=head2 2. a select box for objects of arbitrary class -- say BeerDB::Beer for fun. |
|
648
|
|
|
|
|
|
|
# general |
|
649
|
|
|
|
|
|
|
BeerDB::Beer->to_field('', 'select', $options) |
|
650
|
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
BeerDB::Beer->to_field('', 'select'); # Select box of all the rows in class |
|
652
|
|
|
|
|
|
|
# with PK as ID, $Class->to_field() same. |
|
653
|
|
|
|
|
|
|
BeerDB::Beer->to_field('','select',{ where => "rating > 3 AND class like 'Ale'", order_by => 'rating DESC, beer_id ASC' , limit => 10}); |
|
654
|
|
|
|
|
|
|
# specify exact where clause |
|
655
|
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
=head2 3. If you already have a list of objects to select from -- |
|
657
|
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
BeerDB:;Beer->to_field($col, 'select' , {items => $objects}); |
|
659
|
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
# 3. a select box for arbitrary set of objects |
|
661
|
|
|
|
|
|
|
# Pass array ref of objects as first arg rather than field |
|
662
|
|
|
|
|
|
|
$any_class_or_obj->to_field([BeerDB::Beer->search(favorite => 1)], 'select',); |
|
663
|
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
=cut |
|
666
|
|
|
|
|
|
|
|
|
667
|
|
|
|
|
|
|
sub _to_select { |
|
668
|
|
|
|
|
|
|
my ($self, $col, $args) = @_; |
|
669
|
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
$args ||= {}; |
|
671
|
|
|
|
|
|
|
# Do we have items already ? Go no further. |
|
672
|
|
|
|
|
|
|
if ($args->{items} and ref $args->{items}) { |
|
673
|
|
|
|
|
|
|
my $a = $self->_select_guts($col, $args); |
|
674
|
|
|
|
|
|
|
$OLD_STYLE && return $a->as_HTML; |
|
675
|
|
|
|
|
|
|
if ($args->{multiple}) { |
|
676
|
|
|
|
|
|
|
$a->attr('multiple', 'multiple'); |
|
677
|
|
|
|
|
|
|
} |
|
678
|
|
|
|
|
|
|
return $a; |
|
679
|
|
|
|
|
|
|
} |
|
680
|
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
# Proceed with work |
|
682
|
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
my $rel_meta; |
|
684
|
|
|
|
|
|
|
if (not $col) { |
|
685
|
|
|
|
|
|
|
unless ($args->{class}) { |
|
686
|
|
|
|
|
|
|
$args->{class} = ref $self || $self; |
|
687
|
|
|
|
|
|
|
# object selected if called with one |
|
688
|
|
|
|
|
|
|
$args->{selected} = { $self->id => 1} |
|
689
|
|
|
|
|
|
|
if not $args->{selected} and ref $self; |
|
690
|
|
|
|
|
|
|
} |
|
691
|
|
|
|
|
|
|
$col = $args->{class}->primary_column; |
|
692
|
|
|
|
|
|
|
$args->{name} ||= $col; |
|
693
|
|
|
|
|
|
|
} |
|
694
|
|
|
|
|
|
|
# Related Class maybe ? |
|
695
|
|
|
|
|
|
|
elsif ($rel_meta = $self->related_meta('r:)', $col) ) { |
|
696
|
|
|
|
|
|
|
$args->{class} = $rel_meta->{foreign_class}; |
|
697
|
|
|
|
|
|
|
# related objects pre selected if object |
|
698
|
|
|
|
|
|
|
# "Has many" -- Issues: |
|
699
|
|
|
|
|
|
|
# 1) want to select one or many from list if self is an object |
|
700
|
|
|
|
|
|
|
# Thats about all we can do really, |
|
701
|
|
|
|
|
|
|
# 2) except for mapping which is TODO and would |
|
702
|
|
|
|
|
|
|
# do something like add to and take away from list of permissions for |
|
703
|
|
|
|
|
|
|
# example. |
|
704
|
|
|
|
|
|
|
|
|
705
|
|
|
|
|
|
|
# Hasmany select one from list if ref self |
|
706
|
|
|
|
|
|
|
if ($rel_meta->{name} =~ /has_many/i and ref $self) { |
|
707
|
|
|
|
|
|
|
my @itms = $self->$col; # need list not iterator |
|
708
|
|
|
|
|
|
|
$args->{items} = \@itms; |
|
709
|
|
|
|
|
|
|
my $a = $self->_select_guts($col, $args); |
|
710
|
|
|
|
|
|
|
$OLD_STYLE && return $a->as_HTML; |
|
711
|
|
|
|
|
|
|
return $a; |
|
712
|
|
|
|
|
|
|
} else { |
|
713
|
|
|
|
|
|
|
$args->{selected} ||= [ $self->$col ] if ref $self; |
|
714
|
|
|
|
|
|
|
#warn "selected is " . Dumper($args->{selected}); |
|
715
|
|
|
|
|
|
|
my $c = $rel_meta->{args}{constraint} || {}; |
|
716
|
|
|
|
|
|
|
my $j = $rel_meta->{args}{join} || {}; |
|
717
|
|
|
|
|
|
|
my @join ; |
|
718
|
|
|
|
|
|
|
if (ref $self) { |
|
719
|
|
|
|
|
|
|
@join = map { $_ ." = ". $self->_attr($_) } keys %$j; |
|
720
|
|
|
|
|
|
|
} |
|
721
|
|
|
|
|
|
|
my @constr= map { "$_ = '$c->{$_}'"} keys %$c; |
|
722
|
|
|
|
|
|
|
$args->{where} ||= join (' AND ', (@join, @constr)); |
|
723
|
|
|
|
|
|
|
$args->{order_by} ||= $rel_meta->{args}{order_by}; |
|
724
|
|
|
|
|
|
|
$args->{limit} ||= $rel_meta->{args}{limit}; |
|
725
|
|
|
|
|
|
|
} |
|
726
|
|
|
|
|
|
|
} |
|
727
|
|
|
|
|
|
|
|
|
728
|
|
|
|
|
|
|
# Set arguments |
|
729
|
|
|
|
|
|
|
unless ( defined $args->{column_nullable} ) { |
|
730
|
|
|
|
|
|
|
$args->{column_nullable} = $self->can('column_nullable') ? |
|
731
|
|
|
|
|
|
|
$self->column_nullable($col) : 1; |
|
732
|
|
|
|
|
|
|
} |
|
733
|
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
# Get items to select from |
|
735
|
|
|
|
|
|
|
my $items = _select_items($args); # array of hashrefs |
|
736
|
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
# Turn items into objects if related |
|
738
|
|
|
|
|
|
|
if ($rel_meta and not $args->{no_construct}) { |
|
739
|
|
|
|
|
|
|
my @objs = (); |
|
740
|
|
|
|
|
|
|
push @objs, $rel_meta->{foreign_class}->construct($_) foreach @$items; |
|
741
|
|
|
|
|
|
|
$args->{items} = \@objs; |
|
742
|
|
|
|
|
|
|
} else { |
|
743
|
|
|
|
|
|
|
$args->{items} = $items; |
|
744
|
|
|
|
|
|
|
} |
|
745
|
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
# Make select HTML element |
|
747
|
|
|
|
|
|
|
$a = $self->_select_guts($col, $args); |
|
748
|
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
if ($args->{multiple}) { |
|
750
|
|
|
|
|
|
|
$a->attr('multiple', 'multiple'); |
|
751
|
|
|
|
|
|
|
} |
|
752
|
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
# Return |
|
754
|
|
|
|
|
|
|
$OLD_STYLE && return $a->as_HTML; |
|
755
|
|
|
|
|
|
|
$a; |
|
756
|
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
} |
|
758
|
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
|
|
760
|
|
|
|
|
|
|
############## |
|
761
|
|
|
|
|
|
|
# Function # |
|
762
|
|
|
|
|
|
|
# ############# |
|
763
|
|
|
|
|
|
|
# returns the intersection of list refs a and b |
|
764
|
|
|
|
|
|
|
sub _list_intersect { |
|
765
|
|
|
|
|
|
|
my ($a, $b) = @_; |
|
766
|
|
|
|
|
|
|
my %isect; my %union; |
|
767
|
|
|
|
|
|
|
foreach my $e (@$a, @$b) { |
|
768
|
|
|
|
|
|
|
$union{$e}++ && $isect{$e}++; |
|
769
|
|
|
|
|
|
|
} |
|
770
|
|
|
|
|
|
|
return %isect; |
|
771
|
|
|
|
|
|
|
} |
|
772
|
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
############ |
|
774
|
|
|
|
|
|
|
# FUNCTION # |
|
775
|
|
|
|
|
|
|
############ |
|
776
|
|
|
|
|
|
|
# Get Items returns array of hashrefs |
|
777
|
|
|
|
|
|
|
sub _select_items { |
|
778
|
|
|
|
|
|
|
my $args = shift; |
|
779
|
|
|
|
|
|
|
my $fclass = $args->{class}; |
|
780
|
|
|
|
|
|
|
my @disp_cols = @{$args->{columns} || []}; |
|
781
|
|
|
|
|
|
|
@disp_cols = $fclass->columns('SelectBox') unless @disp_cols; |
|
782
|
|
|
|
|
|
|
@disp_cols = $fclass->columns('Stringify')unless @disp_cols; |
|
783
|
|
|
|
|
|
|
@disp_cols = $fclass->_essential unless @disp_cols; |
|
784
|
|
|
|
|
|
|
unshift @disp_cols, $fclass->columns('Primary'); |
|
785
|
|
|
|
|
|
|
#my %isect = _list_intersect(\@pks, \@disp_cols); |
|
786
|
|
|
|
|
|
|
#foreach (@pks) { push @sel_cols, $_ unless $isect{$_}; } |
|
787
|
|
|
|
|
|
|
#push @sel_cols, @disp_cols; |
|
788
|
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
#warn "in select items. args are : " . Dumper($args); |
|
790
|
|
|
|
|
|
|
my $distinct = ''; |
|
791
|
|
|
|
|
|
|
if ($args->{'distinct'}) { |
|
792
|
|
|
|
|
|
|
$distinct = 'DISTINCT '; |
|
793
|
|
|
|
|
|
|
} |
|
794
|
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
my $sql = "SELECT $distinct" . join( ', ', @disp_cols) . |
|
796
|
|
|
|
|
|
|
" FROM " . $fclass->table; |
|
797
|
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
$sql .= " WHERE " . $args->{where} if $args->{where}; |
|
799
|
|
|
|
|
|
|
$sql .= " ORDER BY " . $args->{order_by} if $args->{order_by}; |
|
800
|
|
|
|
|
|
|
$sql .= " LIMIT " . $args->{limit} if $args->{limit}; |
|
801
|
|
|
|
|
|
|
#warn "_select_items sql is : $sql"; |
|
802
|
|
|
|
|
|
|
|
|
803
|
|
|
|
|
|
|
my $sth = $fclass->db_Main->prepare($sql); |
|
804
|
|
|
|
|
|
|
$sth->execute; |
|
805
|
|
|
|
|
|
|
my @data; |
|
806
|
|
|
|
|
|
|
while ( my $d = $sth->fetchrow_hashref ) { |
|
807
|
|
|
|
|
|
|
push @data, $d; |
|
808
|
|
|
|
|
|
|
} |
|
809
|
|
|
|
|
|
|
return \@data; |
|
810
|
|
|
|
|
|
|
} |
|
811
|
|
|
|
|
|
|
|
|
812
|
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
# Makes a readonly input box out of column's value |
|
814
|
|
|
|
|
|
|
# No args makes object to readonly |
|
815
|
|
|
|
|
|
|
sub _to_readonly { |
|
816
|
|
|
|
|
|
|
my ($self, $col, $args) = @_; |
|
817
|
|
|
|
|
|
|
my $val = $args->{value}; |
|
818
|
|
|
|
|
|
|
if (not defined $val ) { # object to readonly |
|
819
|
|
|
|
|
|
|
$self->_croak("AsForm: To readonly field called as class method without a value") unless ref $self; |
|
820
|
|
|
|
|
|
|
$val = $self->id; |
|
821
|
|
|
|
|
|
|
$col = $self->primary_column; |
|
822
|
|
|
|
|
|
|
} |
|
823
|
|
|
|
|
|
|
my $a = HTML::Element->new('input', 'type' => 'text', readonly => '1', |
|
824
|
|
|
|
|
|
|
'name' => $col, 'value'=>$val); |
|
825
|
|
|
|
|
|
|
$OLD_STYLE && return $a->as_HTML; |
|
826
|
|
|
|
|
|
|
$a; |
|
827
|
|
|
|
|
|
|
} |
|
828
|
|
|
|
|
|
|
|
|
829
|
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
=head2 _to_enum_select |
|
831
|
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
Returns a select box for the an enum column type. |
|
833
|
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
=cut |
|
835
|
|
|
|
|
|
|
|
|
836
|
|
|
|
|
|
|
sub _to_enum_select { |
|
837
|
|
|
|
|
|
|
my ($self, $col, $args) = @_; |
|
838
|
|
|
|
|
|
|
my $type = $args->{column_type}; |
|
839
|
|
|
|
|
|
|
$type =~ /ENUM\((.*?)\)/i; |
|
840
|
|
|
|
|
|
|
(my $enum = $1) =~ s/'//g; |
|
841
|
|
|
|
|
|
|
my @enum_vals = split /\s*,\s*/, $enum; |
|
842
|
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
# determine which is pre selected |
|
844
|
|
|
|
|
|
|
my $selected = eval { $self->$col }; |
|
845
|
|
|
|
|
|
|
$selected = $args->{default} unless defined $selected; |
|
846
|
|
|
|
|
|
|
$selected = $enum_vals[0] unless defined $selected; |
|
847
|
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
my $a = HTML::Element->new("select", name => $col); |
|
849
|
|
|
|
|
|
|
for ( @enum_vals ) { |
|
850
|
|
|
|
|
|
|
my $sel = HTML::Element->new("option", value => $_); |
|
851
|
|
|
|
|
|
|
$sel->attr("selected" => "selected") if $_ eq $selected ; |
|
852
|
|
|
|
|
|
|
$sel->push_content($_); |
|
853
|
|
|
|
|
|
|
$a->push_content($sel); |
|
854
|
|
|
|
|
|
|
} |
|
855
|
|
|
|
|
|
|
$OLD_STYLE && return $a->as_HTML; |
|
856
|
|
|
|
|
|
|
$a; |
|
857
|
|
|
|
|
|
|
} |
|
858
|
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
|
|
860
|
|
|
|
|
|
|
=head2 _to_bool_select |
|
861
|
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
Returns a "No/Yes" select box for a boolean column type. |
|
863
|
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
=cut |
|
865
|
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
# TODO fix this mess with args |
|
867
|
|
|
|
|
|
|
sub _to_bool_select { |
|
868
|
|
|
|
|
|
|
my ($self, $col, $args) = @_; |
|
869
|
|
|
|
|
|
|
my $type = $args->{column_type}; |
|
870
|
|
|
|
|
|
|
my @bool_text = ('No', 'Yes'); |
|
871
|
|
|
|
|
|
|
if ($type =~ /BOOL\((.+?)\)/i) { |
|
872
|
|
|
|
|
|
|
(my $bool = $1) =~ s/'//g; |
|
873
|
|
|
|
|
|
|
@bool_text = split /,/, $bool; |
|
874
|
|
|
|
|
|
|
} |
|
875
|
|
|
|
|
|
|
|
|
876
|
|
|
|
|
|
|
# get selected |
|
877
|
|
|
|
|
|
|
my $selected = $args->{value} if defined $args->{value}; |
|
878
|
|
|
|
|
|
|
$selected = $args->{selected} unless defined $selected; |
|
879
|
|
|
|
|
|
|
$selected = ref $self ? eval {$self->$col;} : $args->{default} |
|
880
|
|
|
|
|
|
|
unless (defined $selected); |
|
881
|
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
my $a = HTML::Element->new("select", name => $col); |
|
883
|
|
|
|
|
|
|
if ($args->{column_nullable} || $args->{value} eq '') { |
|
884
|
|
|
|
|
|
|
my $null = HTML::Element->new("option"); |
|
885
|
|
|
|
|
|
|
$null->attr('selected', 'selected') if $args->{value} eq ''; |
|
886
|
|
|
|
|
|
|
$a->push_content( $null ); |
|
887
|
|
|
|
|
|
|
} |
|
888
|
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
my ($opt0, $opt1) = ( HTML::Element->new("option", value => 0), |
|
890
|
|
|
|
|
|
|
HTML::Element->new("option", value => 1) ); |
|
891
|
|
|
|
|
|
|
$opt0->push_content($bool_text[0]); |
|
892
|
|
|
|
|
|
|
$opt1->push_content($bool_text[1]); |
|
893
|
|
|
|
|
|
|
unless ($selected eq '') { |
|
894
|
|
|
|
|
|
|
$opt0->attr("selected" => "selected") if not $selected; |
|
895
|
|
|
|
|
|
|
$opt1->attr("selected" => "selected") if $selected; |
|
896
|
|
|
|
|
|
|
} |
|
897
|
|
|
|
|
|
|
$a->push_content($opt0, $opt1); |
|
898
|
|
|
|
|
|
|
$OLD_STYLE && return $a->as_HTML; |
|
899
|
|
|
|
|
|
|
$a; |
|
900
|
|
|
|
|
|
|
} |
|
901
|
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
=head2 _to_hidden($field, $args) |
|
903
|
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
This makes a hidden html element input. It uses the "name" and "value" |
|
905
|
|
|
|
|
|
|
arguments. If one or both are not there, it will look for an object in |
|
906
|
|
|
|
|
|
|
"items->[0]" or the caller. Then it will use $field or the primary key for |
|
907
|
|
|
|
|
|
|
name and the value of the column by the derived name. |
|
908
|
|
|
|
|
|
|
|
|
909
|
|
|
|
|
|
|
=cut |
|
910
|
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
sub _to_hidden { |
|
912
|
|
|
|
|
|
|
my ($self, $field, $args) = @_; |
|
913
|
|
|
|
|
|
|
$args ||= {}; |
|
914
|
|
|
|
|
|
|
my ($name, $value) = ($args->{'name'}, $args->{value}); |
|
915
|
|
|
|
|
|
|
$name = $field unless defined $name; |
|
916
|
|
|
|
|
|
|
if (! defined $name and !defined $value) { # check for objects |
|
917
|
|
|
|
|
|
|
my $obj = $args->{items}->[0] || $self; |
|
918
|
|
|
|
|
|
|
unless (ref $obj) { |
|
919
|
|
|
|
|
|
|
die "_to_hidden cannot determine a value. It was passed a value argument or items object or called with an object."; |
|
920
|
|
|
|
|
|
|
} |
|
921
|
|
|
|
|
|
|
$name = $obj->primary_column->name unless $name; |
|
922
|
|
|
|
|
|
|
$value = $obj->$name unless $value; |
|
923
|
|
|
|
|
|
|
} |
|
924
|
|
|
|
|
|
|
|
|
925
|
|
|
|
|
|
|
return HTML::Element->new('input', 'type' => 'hidden', |
|
926
|
|
|
|
|
|
|
'name' => $name, 'value'=>$value); |
|
927
|
|
|
|
|
|
|
} |
|
928
|
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
=head2 _to_link_hidden($col, $args) |
|
930
|
|
|
|
|
|
|
|
|
931
|
|
|
|
|
|
|
Makes a link with a hidden input with the id of $obj as the value and name. |
|
932
|
|
|
|
|
|
|
Name defaults to the objects primary key. The object defaults to self. |
|
933
|
|
|
|
|
|
|
|
|
934
|
|
|
|
|
|
|
=cut |
|
935
|
|
|
|
|
|
|
|
|
936
|
|
|
|
|
|
|
sub _to_link_hidden { |
|
937
|
|
|
|
|
|
|
my ($self, $accessor, $args) = @_; |
|
938
|
|
|
|
|
|
|
my $r = eval {$self->controller} || $args->{r} || ''; |
|
939
|
|
|
|
|
|
|
my $uri = $args->{uri} || ''; |
|
940
|
|
|
|
|
|
|
$self->_croak("_to_link_hidden cant get uri. No Maypole Request class (\$r) or uri arg. Need one or other.") |
|
941
|
|
|
|
|
|
|
unless $r; |
|
942
|
|
|
|
|
|
|
my ($obj, $name); |
|
943
|
|
|
|
|
|
|
if (ref $self) { # hidding linking self |
|
944
|
|
|
|
|
|
|
$obj = $self; |
|
945
|
|
|
|
|
|
|
$name = $args->{name} || $obj->primary_column->name; |
|
946
|
|
|
|
|
|
|
} elsif ($obj = $args->{items}->[0]) { |
|
947
|
|
|
|
|
|
|
$name = $args->{name} || $accessor || $obj->primary_column->name; |
|
948
|
|
|
|
|
|
|
# TODO use meta data above maybe |
|
949
|
|
|
|
|
|
|
} else { # hiding linking related object with id in args |
|
950
|
|
|
|
|
|
|
$obj = $self->related_class($r, $accessor)->retrieve($args->{id}); |
|
951
|
|
|
|
|
|
|
$name = $args->{name} || $accessor ; #$obj->primary_column->name; |
|
952
|
|
|
|
|
|
|
# TODO use meta data above maybe |
|
953
|
|
|
|
|
|
|
} |
|
954
|
|
|
|
|
|
|
$self->_croak("_to_link_hidden has no object") unless ref $obj; |
|
955
|
|
|
|
|
|
|
my $href = $uri || $r->config->{uri_base} . "/". $obj->table."/view/".$obj->id; |
|
956
|
|
|
|
|
|
|
my $a = HTML::Element->new('a', 'href' => $href); |
|
957
|
|
|
|
|
|
|
$a->push_content("$obj"); |
|
958
|
|
|
|
|
|
|
$a->push_content($self->to_field('blahfooey', 'hidden', {name => $name, value => $obj->id} )); |
|
959
|
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
$OLD_STYLE && return $a->as_HTML; |
|
961
|
|
|
|
|
|
|
return $a; |
|
962
|
|
|
|
|
|
|
} |
|
963
|
|
|
|
|
|
|
|
|
964
|
|
|
|
|
|
|
=head2 _to_foreign_inputs |
|
965
|
|
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
Creates inputs for a foreign class, usually related to the calling class or |
|
967
|
|
|
|
|
|
|
object. In names them so they do not clash with other names and so they |
|
968
|
|
|
|
|
|
|
can be processed generically. See _rename_foreign_inputs below and |
|
969
|
|
|
|
|
|
|
Maypole::Model::CDBI::FromCGI::classify_foreign_inputs. |
|
970
|
|
|
|
|
|
|
|
|
971
|
|
|
|
|
|
|
Arguments this recognizes are : |
|
972
|
|
|
|
|
|
|
|
|
973
|
|
|
|
|
|
|
related_meta -- if you have this, great, othervise it will determine or die |
|
974
|
|
|
|
|
|
|
columns -- list of columns to make inputs for |
|
975
|
|
|
|
|
|
|
request (r) -- TODO the Maypole request so we can see what action |
|
976
|
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
=cut |
|
978
|
|
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
sub _to_foreign_inputs { |
|
980
|
|
|
|
|
|
|
my ($self, $accssr, $args) = @_; |
|
981
|
|
|
|
|
|
|
my $rel_meta = $args->{related_meta} || $self->related_meta('r',$accssr); |
|
982
|
|
|
|
|
|
|
my $fields = $args->{columns}; |
|
983
|
|
|
|
|
|
|
if (!$rel_meta) { |
|
984
|
|
|
|
|
|
|
$self->_carp( "[_to_foreign_inputs] No relationship for accessor $accssr"); |
|
985
|
|
|
|
|
|
|
return; |
|
986
|
|
|
|
|
|
|
} |
|
987
|
|
|
|
|
|
|
|
|
988
|
|
|
|
|
|
|
my $rel_type = $rel_meta->{name}; |
|
989
|
|
|
|
|
|
|
my $classORobj = ref $self && ref $self->$accssr ? $self->$accssr : $rel_meta->{foreign_class}; |
|
990
|
|
|
|
|
|
|
|
|
991
|
|
|
|
|
|
|
unless ($fields) { |
|
992
|
|
|
|
|
|
|
$fields = $classORobj->can('display_columns') ? |
|
993
|
|
|
|
|
|
|
[$classORobj->display_columns] : [$classORobj->columns]; |
|
994
|
|
|
|
|
|
|
} |
|
995
|
|
|
|
|
|
|
|
|
996
|
|
|
|
|
|
|
# Ignore our fkey in them to prevent infinite recursion |
|
997
|
|
|
|
|
|
|
my $me = eval {$rel_meta->{args}{foreign_key}} || |
|
998
|
|
|
|
|
|
|
eval {$rel_meta->{args}{foreign_column}} |
|
999
|
|
|
|
|
|
|
|| ''; # what uses foreign_column has_many or might_have |
|
1000
|
|
|
|
|
|
|
my $constrained = $rel_meta->{args}{constraint}; |
|
1001
|
|
|
|
|
|
|
my %inputs; |
|
1002
|
|
|
|
|
|
|
foreach ( @$fields ) { |
|
1003
|
|
|
|
|
|
|
next if $constrained->{$_} || ($_ eq $me); # don't display constrained |
|
1004
|
|
|
|
|
|
|
$inputs{$_} = $classORobj->to_field($_); |
|
1005
|
|
|
|
|
|
|
} |
|
1006
|
|
|
|
|
|
|
|
|
1007
|
|
|
|
|
|
|
# Make hidden inputs for constrained columns unless we are editing object |
|
1008
|
|
|
|
|
|
|
# TODO -- is this right thing to do? |
|
1009
|
|
|
|
|
|
|
unless (ref $classORobj || $args->{no_hidden_constraints}) { |
|
1010
|
|
|
|
|
|
|
foreach ( keys %$constrained ) { |
|
1011
|
|
|
|
|
|
|
$inputs{$_} = $classORobj->to_field('blahfooey', 'hidden', |
|
1012
|
|
|
|
|
|
|
{ name => $_, value => $constrained->{$_}} ); |
|
1013
|
|
|
|
|
|
|
} |
|
1014
|
|
|
|
|
|
|
} |
|
1015
|
|
|
|
|
|
|
$self->_rename_foreign_input($accssr, \%inputs); |
|
1016
|
|
|
|
|
|
|
return \%inputs; |
|
1017
|
|
|
|
|
|
|
} |
|
1018
|
|
|
|
|
|
|
|
|
1019
|
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
=head2 _hash_selected |
|
1021
|
|
|
|
|
|
|
|
|
1022
|
|
|
|
|
|
|
*Function* to make sense out of the "selected" argument which has values of the |
|
1023
|
|
|
|
|
|
|
options that should be selected by default when making a select box. It |
|
1024
|
|
|
|
|
|
|
can be in a number formats. This method returns a map of which options to |
|
1025
|
|
|
|
|
|
|
select with the values being the keys in the map ( {val1 => 1, val2 = 1} ). |
|
1026
|
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
Currently this method handles the following formats for the "selected" argument |
|
1028
|
|
|
|
|
|
|
and in the following ways |
|
1029
|
|
|
|
|
|
|
|
|
1030
|
|
|
|
|
|
|
Object -- uses the id method to get the value |
|
1031
|
|
|
|
|
|
|
Scalar -- assumes it *is* the value |
|
1032
|
|
|
|
|
|
|
Array ref of objects -- same as Object |
|
1033
|
|
|
|
|
|
|
Arrays of data -- uses the 0th element in each |
|
1034
|
|
|
|
|
|
|
Hashes of data -- uses key named 'id' |
|
1035
|
|
|
|
|
|
|
|
|
1036
|
|
|
|
|
|
|
=cut |
|
1037
|
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
############ |
|
1039
|
|
|
|
|
|
|
# FUNCTION # |
|
1040
|
|
|
|
|
|
|
############ |
|
1041
|
|
|
|
|
|
|
|
|
1042
|
|
|
|
|
|
|
sub _hash_selected { |
|
1043
|
|
|
|
|
|
|
my ($args) = shift; |
|
1044
|
|
|
|
|
|
|
my $selected = $args->{value} || $args->{selected}; |
|
1045
|
|
|
|
|
|
|
my $type = ref $selected; |
|
1046
|
|
|
|
|
|
|
return $selected unless $selected and $type ne 'HASH'; |
|
1047
|
|
|
|
|
|
|
|
|
1048
|
|
|
|
|
|
|
# Single Object |
|
1049
|
|
|
|
|
|
|
if ($type and $type ne 'ARRAY') { |
|
1050
|
|
|
|
|
|
|
my $id = $selected->id; |
|
1051
|
|
|
|
|
|
|
$id =~ s/^0*//; |
|
1052
|
|
|
|
|
|
|
return {$id => 1}; |
|
1053
|
|
|
|
|
|
|
} |
|
1054
|
|
|
|
|
|
|
# Single Scalar id |
|
1055
|
|
|
|
|
|
|
elsif (not $type) { |
|
1056
|
|
|
|
|
|
|
return { $selected => 1}; |
|
1057
|
|
|
|
|
|
|
} |
|
1058
|
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
# Array of objs, arrays, hashes, or just scalalrs. |
|
1060
|
|
|
|
|
|
|
elsif ($type eq 'ARRAY') { |
|
1061
|
|
|
|
|
|
|
my %hashed; |
|
1062
|
|
|
|
|
|
|
my $ltype = ref $selected->[0]; |
|
1063
|
|
|
|
|
|
|
# Objects |
|
1064
|
|
|
|
|
|
|
if ($ltype and $ltype ne 'ARRAY') { |
|
1065
|
|
|
|
|
|
|
%hashed = map { $_->id => 1 } @$selected; |
|
1066
|
|
|
|
|
|
|
} |
|
1067
|
|
|
|
|
|
|
# Arrays of data with id first |
|
1068
|
|
|
|
|
|
|
elsif ($ltype and $ltype eq 'ARRAY') { |
|
1069
|
|
|
|
|
|
|
%hashed = map { $_->[0] => 1 } @$selected; |
|
1070
|
|
|
|
|
|
|
} |
|
1071
|
|
|
|
|
|
|
# Hashes using pk or id key |
|
1072
|
|
|
|
|
|
|
elsif ($ltype and $ltype eq 'HASH') { |
|
1073
|
|
|
|
|
|
|
my $pk = $args->{class}->primary_column || 'id'; |
|
1074
|
|
|
|
|
|
|
%hashed = map { $_->{$pk} => 1 } @$selected; |
|
1075
|
|
|
|
|
|
|
} |
|
1076
|
|
|
|
|
|
|
# Just Scalars |
|
1077
|
|
|
|
|
|
|
else { |
|
1078
|
|
|
|
|
|
|
%hashed = map { $_ => 1 } @$selected; |
|
1079
|
|
|
|
|
|
|
} |
|
1080
|
|
|
|
|
|
|
return \%hashed; |
|
1081
|
|
|
|
|
|
|
} else { |
|
1082
|
|
|
|
|
|
|
warn "AsForm Could not hash the selected argument: $selected"; |
|
1083
|
|
|
|
|
|
|
} |
|
1084
|
|
|
|
|
|
|
return; |
|
1085
|
|
|
|
|
|
|
} |
|
1086
|
|
|
|
|
|
|
|
|
1087
|
|
|
|
|
|
|
|
|
1088
|
|
|
|
|
|
|
|
|
1089
|
|
|
|
|
|
|
=head2 _select_guts |
|
1090
|
|
|
|
|
|
|
|
|
1091
|
|
|
|
|
|
|
Internal api method to make the actual select box form elements. |
|
1092
|
|
|
|
|
|
|
the data. |
|
1093
|
|
|
|
|
|
|
|
|
1094
|
|
|
|
|
|
|
Items to make options out of can be |
|
1095
|
|
|
|
|
|
|
Hash, Array, |
|
1096
|
|
|
|
|
|
|
Array of CDBI objects. |
|
1097
|
|
|
|
|
|
|
Array of scalars , |
|
1098
|
|
|
|
|
|
|
Array or Array refs with cols from class, |
|
1099
|
|
|
|
|
|
|
Array of hashes |
|
1100
|
|
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
=cut |
|
1102
|
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
sub _select_guts { |
|
1104
|
|
|
|
|
|
|
my ($self, $col, $args) = @_; #$nullable, $selected_id, $values) = @_; |
|
1105
|
|
|
|
|
|
|
|
|
1106
|
|
|
|
|
|
|
$args->{selected} = _hash_selected($args) if defined $args->{selected}; |
|
1107
|
|
|
|
|
|
|
my $name = $args->{name} || $col; |
|
1108
|
|
|
|
|
|
|
my $a = HTML::Element->new('select', name => $name); |
|
1109
|
|
|
|
|
|
|
$a->attr( %{$args->{attr}} ) if $args->{attr}; |
|
1110
|
|
|
|
|
|
|
|
|
1111
|
|
|
|
|
|
|
if ($args->{column_nullable}) { |
|
1112
|
|
|
|
|
|
|
my $null_element = HTML::Element->new('option', value => ''); |
|
1113
|
|
|
|
|
|
|
$null_element->attr(selected => 'selected') |
|
1114
|
|
|
|
|
|
|
if ($args->{selected}{'null'}); |
|
1115
|
|
|
|
|
|
|
$a->push_content($null_element); |
|
1116
|
|
|
|
|
|
|
} |
|
1117
|
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
my $items = $args->{items}; |
|
1119
|
|
|
|
|
|
|
my $type = ref $items; |
|
1120
|
|
|
|
|
|
|
my $proto = eval { ref $items->[0]; } || ""; |
|
1121
|
|
|
|
|
|
|
my $optgroups = $args->{optgroups} || ''; |
|
1122
|
|
|
|
|
|
|
|
|
1123
|
|
|
|
|
|
|
# Array of hashes, one for each optgroup |
|
1124
|
|
|
|
|
|
|
if ($optgroups) { |
|
1125
|
|
|
|
|
|
|
my $i = 0; |
|
1126
|
|
|
|
|
|
|
foreach (@$optgroups) { |
|
1127
|
|
|
|
|
|
|
my $ogrp= HTML::Element->new('optgroup', label => $_); |
|
1128
|
|
|
|
|
|
|
$ogrp->push_content($self->_options_from_hash($items->[$i], $args)); |
|
1129
|
|
|
|
|
|
|
$a->push_content($ogrp); |
|
1130
|
|
|
|
|
|
|
$i++; |
|
1131
|
|
|
|
|
|
|
} |
|
1132
|
|
|
|
|
|
|
} |
|
1133
|
|
|
|
|
|
|
|
|
1134
|
|
|
|
|
|
|
# Single Hash |
|
1135
|
|
|
|
|
|
|
elsif ($type eq 'HASH') { |
|
1136
|
|
|
|
|
|
|
$a->push_content($self->_options_from_hash($items, $args)); |
|
1137
|
|
|
|
|
|
|
} |
|
1138
|
|
|
|
|
|
|
# Single Array |
|
1139
|
|
|
|
|
|
|
elsif ( $type eq 'ARRAY' and not ref $items->[0] ) { |
|
1140
|
|
|
|
|
|
|
$a->push_content($self->_options_from_array($items, $args)); |
|
1141
|
|
|
|
|
|
|
} |
|
1142
|
|
|
|
|
|
|
# Array of Objects |
|
1143
|
|
|
|
|
|
|
elsif ( $type eq 'ARRAY' and $proto !~ /ARRAY|HASH/i ) { |
|
1144
|
|
|
|
|
|
|
# make select of objects |
|
1145
|
|
|
|
|
|
|
$a->push_content($self->_options_from_objects($items, $args)); |
|
1146
|
|
|
|
|
|
|
} |
|
1147
|
|
|
|
|
|
|
# Array of Arrays |
|
1148
|
|
|
|
|
|
|
elsif ( $type eq 'ARRAY' and $proto eq 'ARRAY' ) { |
|
1149
|
|
|
|
|
|
|
$a->push_content($self->_options_from_arrays($items, $args)); |
|
1150
|
|
|
|
|
|
|
} |
|
1151
|
|
|
|
|
|
|
# Array of Hashes |
|
1152
|
|
|
|
|
|
|
elsif ( $type eq 'ARRAY' and $proto eq 'HASH' ) { |
|
1153
|
|
|
|
|
|
|
$a->push_content($self->_options_from_hashes($items, $args)); |
|
1154
|
|
|
|
|
|
|
} else { |
|
1155
|
|
|
|
|
|
|
die "You passed a weird type of data structure to me. Here it is: " . |
|
1156
|
|
|
|
|
|
|
Dumper($items ); |
|
1157
|
|
|
|
|
|
|
} |
|
1158
|
|
|
|
|
|
|
|
|
1159
|
|
|
|
|
|
|
return $a; |
|
1160
|
|
|
|
|
|
|
|
|
1161
|
|
|
|
|
|
|
|
|
1162
|
|
|
|
|
|
|
} |
|
1163
|
|
|
|
|
|
|
|
|
1164
|
|
|
|
|
|
|
=head2 _options_from_objects ( $objects, $args); |
|
1165
|
|
|
|
|
|
|
|
|
1166
|
|
|
|
|
|
|
Private method to makes a options out of objects. It attempts to call each |
|
1167
|
|
|
|
|
|
|
objects stringify method specified in $args->{stringify} as the content. Otherwise the default stringification prevails. |
|
1168
|
|
|
|
|
|
|
|
|
1169
|
|
|
|
|
|
|
*Note only single primary keys supported |
|
1170
|
|
|
|
|
|
|
|
|
1171
|
|
|
|
|
|
|
=cut |
|
1172
|
|
|
|
|
|
|
sub _options_from_objects { |
|
1173
|
|
|
|
|
|
|
my ($self, $items, $args) = @_; |
|
1174
|
|
|
|
|
|
|
my $selected = $args->{selected} || {}; |
|
1175
|
|
|
|
|
|
|
|
|
1176
|
|
|
|
|
|
|
my @res; |
|
1177
|
|
|
|
|
|
|
for my $object (@$items) { |
|
1178
|
|
|
|
|
|
|
my $stringify = $args->{stringify}; |
|
1179
|
|
|
|
|
|
|
if ($object->can('stringify_column') ) { |
|
1180
|
|
|
|
|
|
|
$stringify ||= $object->stringify_column if ($object->stringify_column && $object->can($object->stringify_column)); |
|
1181
|
|
|
|
|
|
|
} |
|
1182
|
|
|
|
|
|
|
my $id = $object->id; |
|
1183
|
|
|
|
|
|
|
my $opt = HTML::Element->new("option", value => $id); |
|
1184
|
|
|
|
|
|
|
$id =~ s/^0*//; # leading zeros no good in hash key |
|
1185
|
|
|
|
|
|
|
$opt->attr(selected => "selected") if $selected->{$id}; |
|
1186
|
|
|
|
|
|
|
my $content = $stringify ? $object->$stringify : "$object"; |
|
1187
|
|
|
|
|
|
|
$opt->push_content($content); |
|
1188
|
|
|
|
|
|
|
push @res, $opt; |
|
1189
|
|
|
|
|
|
|
} |
|
1190
|
|
|
|
|
|
|
return @res; |
|
1191
|
|
|
|
|
|
|
} |
|
1192
|
|
|
|
|
|
|
|
|
1193
|
|
|
|
|
|
|
sub _options_from_arrays { |
|
1194
|
|
|
|
|
|
|
my ($self, $items, $args) = @_; |
|
1195
|
|
|
|
|
|
|
my $selected = $args->{selected} || {}; |
|
1196
|
|
|
|
|
|
|
my @res; |
|
1197
|
|
|
|
|
|
|
my $class = $args->{class} || ''; |
|
1198
|
|
|
|
|
|
|
my $stringify = $args->{stringify}; |
|
1199
|
|
|
|
|
|
|
$stringify ||= $self->stringify_column if ($self->can('stringify_column')); |
|
1200
|
|
|
|
|
|
|
for my $item (@$items) { |
|
1201
|
|
|
|
|
|
|
my @pks; # for future multiple key support |
|
1202
|
|
|
|
|
|
|
push @pks, shift @$item foreach $class->columns('Primary'); |
|
1203
|
|
|
|
|
|
|
my $id = $pks[0]; |
|
1204
|
|
|
|
|
|
|
$id =~ s/^0+//; # In case zerofill is on . |
|
1205
|
|
|
|
|
|
|
my $val = defined $id ? $id : ''; |
|
1206
|
|
|
|
|
|
|
my $opt = HTML::Element->new("option", value =>$val); |
|
1207
|
|
|
|
|
|
|
$opt->attr(selected => "selected") if $selected->{$id}; |
|
1208
|
|
|
|
|
|
|
my $content = ($class and $stringify and $class->can($stringify)) ? |
|
1209
|
|
|
|
|
|
|
$class->$stringify($_) : |
|
1210
|
|
|
|
|
|
|
join( '/', map { $_ if $_; }@{$item} ); |
|
1211
|
|
|
|
|
|
|
$opt->push_content( $content ); |
|
1212
|
|
|
|
|
|
|
push @res, $opt; |
|
1213
|
|
|
|
|
|
|
} |
|
1214
|
|
|
|
|
|
|
return @res; |
|
1215
|
|
|
|
|
|
|
} |
|
1216
|
|
|
|
|
|
|
|
|
1217
|
|
|
|
|
|
|
|
|
1218
|
|
|
|
|
|
|
sub _options_from_array { |
|
1219
|
|
|
|
|
|
|
my ($self, $items, $args) = @_; |
|
1220
|
|
|
|
|
|
|
my $selected = $args->{selected} || {}; |
|
1221
|
|
|
|
|
|
|
my @res; |
|
1222
|
|
|
|
|
|
|
for (@$items) { |
|
1223
|
|
|
|
|
|
|
my $val = defined $_ ? $_ : ''; |
|
1224
|
|
|
|
|
|
|
my $opt = HTML::Element->new("option", value => $val); |
|
1225
|
|
|
|
|
|
|
#$opt->attr(selected => "selected") if $selected =~/^$id$/; |
|
1226
|
|
|
|
|
|
|
$opt->attr(selected => "selected") if $selected->{$_}; |
|
1227
|
|
|
|
|
|
|
$opt->push_content( $_ ); |
|
1228
|
|
|
|
|
|
|
push @res, $opt; |
|
1229
|
|
|
|
|
|
|
} |
|
1230
|
|
|
|
|
|
|
return @res; |
|
1231
|
|
|
|
|
|
|
} |
|
1232
|
|
|
|
|
|
|
|
|
1233
|
|
|
|
|
|
|
sub _options_from_hash { |
|
1234
|
|
|
|
|
|
|
my ($self, $items, $args) = @_; |
|
1235
|
|
|
|
|
|
|
my $selected = $args->{selected} || {}; |
|
1236
|
|
|
|
|
|
|
my @res; |
|
1237
|
|
|
|
|
|
|
|
|
1238
|
|
|
|
|
|
|
my @values = values %$items; |
|
1239
|
|
|
|
|
|
|
# hash Key is the option content and the hash value is option value |
|
1240
|
|
|
|
|
|
|
for (sort keys %$items) { |
|
1241
|
|
|
|
|
|
|
my $val = defined $items->{$_} ? $items->{$_} : ''; |
|
1242
|
|
|
|
|
|
|
my $opt = HTML::Element->new("option", value => $val); |
|
1243
|
|
|
|
|
|
|
$opt->attr(selected => "selected") if $selected->{$items->{$_}}; |
|
1244
|
|
|
|
|
|
|
$opt->push_content( $_ ); |
|
1245
|
|
|
|
|
|
|
push @res, $opt; |
|
1246
|
|
|
|
|
|
|
} |
|
1247
|
|
|
|
|
|
|
return @res; |
|
1248
|
|
|
|
|
|
|
} |
|
1249
|
|
|
|
|
|
|
|
|
1250
|
|
|
|
|
|
|
|
|
1251
|
|
|
|
|
|
|
sub _options_from_hashes { |
|
1252
|
|
|
|
|
|
|
my ($self, $items, $args) = @_; |
|
1253
|
|
|
|
|
|
|
my $selected = $args->{selected} || {}; |
|
1254
|
|
|
|
|
|
|
my $pk = eval {$args->{class}->primary_column} || 'id'; |
|
1255
|
|
|
|
|
|
|
my $fclass = $args->{class} || ''; |
|
1256
|
|
|
|
|
|
|
my $stringify = $args->{stringify}; |
|
1257
|
|
|
|
|
|
|
$stringify ||= $self->stringify_column if ( $self->can('stringify_column') ); |
|
1258
|
|
|
|
|
|
|
my @res; |
|
1259
|
|
|
|
|
|
|
for my $item (@$items) { |
|
1260
|
|
|
|
|
|
|
my $val = defined $item->{$pk} ? $item->{$pk} : ''; |
|
1261
|
|
|
|
|
|
|
my $opt = HTML::Element->new("option", value => $val); |
|
1262
|
|
|
|
|
|
|
$opt->attr(selected => "selected") if $selected->{$val}; |
|
1263
|
|
|
|
|
|
|
my $content; |
|
1264
|
|
|
|
|
|
|
if ($fclass and $stringify and $fclass->can($stringify)) { |
|
1265
|
|
|
|
|
|
|
$content = bless ($item,$fclass)->$stringify(); |
|
1266
|
|
|
|
|
|
|
} elsif ( $stringify ) { |
|
1267
|
|
|
|
|
|
|
$content = $item->{$stringify}; |
|
1268
|
|
|
|
|
|
|
} else { |
|
1269
|
|
|
|
|
|
|
$content = join(' ', map {$item->{$_} } keys %$item); |
|
1270
|
|
|
|
|
|
|
} |
|
1271
|
|
|
|
|
|
|
|
|
1272
|
|
|
|
|
|
|
$opt->push_content( $content ); |
|
1273
|
|
|
|
|
|
|
push @res, $opt; |
|
1274
|
|
|
|
|
|
|
} |
|
1275
|
|
|
|
|
|
|
return @res; |
|
1276
|
|
|
|
|
|
|
} |
|
1277
|
|
|
|
|
|
|
|
|
1278
|
|
|
|
|
|
|
|
|
1279
|
|
|
|
|
|
|
=head2 _to_checkbox |
|
1280
|
|
|
|
|
|
|
|
|
1281
|
|
|
|
|
|
|
Makes a checkbox element -- TODO |
|
1282
|
|
|
|
|
|
|
|
|
1283
|
|
|
|
|
|
|
=cut |
|
1284
|
|
|
|
|
|
|
# |
|
1285
|
|
|
|
|
|
|
# checkboxes: if no data in hand (ie called as class method), replace |
|
1286
|
|
|
|
|
|
|
# with a radio button, in order to allow this field to be left |
|
1287
|
|
|
|
|
|
|
# unspecified in search / add forms. |
|
1288
|
|
|
|
|
|
|
# |
|
1289
|
|
|
|
|
|
|
# Not tested |
|
1290
|
|
|
|
|
|
|
# TODO -- make this general checkboxse |
|
1291
|
|
|
|
|
|
|
# |
|
1292
|
|
|
|
|
|
|
# |
|
1293
|
|
|
|
|
|
|
sub _to_checkbox { |
|
1294
|
|
|
|
|
|
|
my ($self, $col, $args) = @_; |
|
1295
|
|
|
|
|
|
|
my $nullable = eval {self->column_nullable($col)} || 0; |
|
1296
|
|
|
|
|
|
|
return $self->_to_radio($col) if !ref($self) || $nullable; |
|
1297
|
|
|
|
|
|
|
my $value = $self->$col; |
|
1298
|
|
|
|
|
|
|
my $a = HTML::Element->new("input", type=> "checkbox", name => $col); |
|
1299
|
|
|
|
|
|
|
$a->attr("checked" => 'true') if $value eq 'Y'; |
|
1300
|
|
|
|
|
|
|
return $a; |
|
1301
|
|
|
|
|
|
|
} |
|
1302
|
|
|
|
|
|
|
|
|
1303
|
|
|
|
|
|
|
=head2 _to_radio |
|
1304
|
|
|
|
|
|
|
|
|
1305
|
|
|
|
|
|
|
Makes a radio button element -- TODO |
|
1306
|
|
|
|
|
|
|
|
|
1307
|
|
|
|
|
|
|
=cut |
|
1308
|
|
|
|
|
|
|
# TODO -- make this general radio butons |
|
1309
|
|
|
|
|
|
|
# |
|
1310
|
|
|
|
|
|
|
sub _to_radio { |
|
1311
|
|
|
|
|
|
|
my ($self, $col) = @_; |
|
1312
|
|
|
|
|
|
|
my $value = ref $self && $self->$col || ''; |
|
1313
|
|
|
|
|
|
|
my $nullable = eval {self->column_nullable($col)} || 0; |
|
1314
|
|
|
|
|
|
|
my $a = HTML::Element->new("span"); |
|
1315
|
|
|
|
|
|
|
my $ry = HTML::Element->new("input", type=> "radio", name=>$col, value=>'Y' ); |
|
1316
|
|
|
|
|
|
|
my $rn = HTML::Element->new("input", type=> "radio", name=>$col, value=>'N' ); |
|
1317
|
|
|
|
|
|
|
my $ru = HTML::Element->new("input", type=> "radio", name=>$col, value=>'' ) if $nullable; |
|
1318
|
|
|
|
|
|
|
$ry->push_content('Yes'); $rn->push_content('No'); |
|
1319
|
|
|
|
|
|
|
$ru->push_content('n/a') if $nullable; |
|
1320
|
|
|
|
|
|
|
if ($value eq 'Y') { |
|
1321
|
|
|
|
|
|
|
$ry->attr("checked" => 'true'); |
|
1322
|
|
|
|
|
|
|
} elsif ($value eq 'N') { |
|
1323
|
|
|
|
|
|
|
$rn->attr("checked" => 'true'); |
|
1324
|
|
|
|
|
|
|
} elsif ($nullable) { |
|
1325
|
|
|
|
|
|
|
$ru->attr("checked" => 'true'); |
|
1326
|
|
|
|
|
|
|
} |
|
1327
|
|
|
|
|
|
|
$a->push_content($ry, $rn); |
|
1328
|
|
|
|
|
|
|
$a->push_content($ru) if $nullable; |
|
1329
|
|
|
|
|
|
|
return $a; |
|
1330
|
|
|
|
|
|
|
} |
|
1331
|
|
|
|
|
|
|
|
|
1332
|
|
|
|
|
|
|
|
|
1333
|
|
|
|
|
|
|
|
|
1334
|
|
|
|
|
|
|
############################ HELPER METHODS ###################### |
|
1335
|
|
|
|
|
|
|
################################################################## |
|
1336
|
|
|
|
|
|
|
|
|
1337
|
|
|
|
|
|
|
=head2 _rename_foreign_input |
|
1338
|
|
|
|
|
|
|
|
|
1339
|
|
|
|
|
|
|
_rename_foreign_input($html_el_or_hash_of_them); # changes made by reference |
|
1340
|
|
|
|
|
|
|
|
|
1341
|
|
|
|
|
|
|
Recursively renames the foreign inputs made by _to_foreign_inputs so they |
|
1342
|
|
|
|
|
|
|
can be processed generically. It uses foreign_input_delimiter. |
|
1343
|
|
|
|
|
|
|
|
|
1344
|
|
|
|
|
|
|
So if an Employee is a Person who has_many Addresses and you call and the |
|
1345
|
|
|
|
|
|
|
method 'foreign_input_delimiter' returns '__AF__' then |
|
1346
|
|
|
|
|
|
|
|
|
1347
|
|
|
|
|
|
|
Employee->to_field("person"); |
|
1348
|
|
|
|
|
|
|
|
|
1349
|
|
|
|
|
|
|
will get inputs for the Person as well as their Address (by default, |
|
1350
|
|
|
|
|
|
|
override _field_from_relationship to change logic) named like this: |
|
1351
|
|
|
|
|
|
|
|
|
1352
|
|
|
|
|
|
|
person__AF__address__AF__street |
|
1353
|
|
|
|
|
|
|
person__AF__address__AF__city |
|
1354
|
|
|
|
|
|
|
person__AF__address__AF__state |
|
1355
|
|
|
|
|
|
|
person__AF__address__AF__zip |
|
1356
|
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
And the processor would know to create this address, put the address id in |
|
1358
|
|
|
|
|
|
|
person->{address} data slot, insert the person and put the person id in the employee->{person} data slot and then insert the employee with that data. |
|
1359
|
|
|
|
|
|
|
|
|
1360
|
|
|
|
|
|
|
=cut |
|
1361
|
|
|
|
|
|
|
|
|
1362
|
|
|
|
|
|
|
sub _rename_foreign_input { |
|
1363
|
|
|
|
|
|
|
my ($self, $accssr, $element) = @_; |
|
1364
|
|
|
|
|
|
|
my $del = $self->foreign_input_delimiter; |
|
1365
|
|
|
|
|
|
|
|
|
1366
|
|
|
|
|
|
|
if ( ref $element ne 'HASH' ) { |
|
1367
|
|
|
|
|
|
|
# my $new_name = $accssr . "__AF__" . $input->attr('name'); |
|
1368
|
|
|
|
|
|
|
$element->attr( name => $accssr . $del . $element->attr('name')); |
|
1369
|
|
|
|
|
|
|
} else { |
|
1370
|
|
|
|
|
|
|
$self->_rename_foreign_input($accssr, $element->{$_}) |
|
1371
|
|
|
|
|
|
|
foreach (keys %$element); |
|
1372
|
|
|
|
|
|
|
} |
|
1373
|
|
|
|
|
|
|
} |
|
1374
|
|
|
|
|
|
|
|
|
1375
|
|
|
|
|
|
|
=head2 foreign_input_delimiter |
|
1376
|
|
|
|
|
|
|
|
|
1377
|
|
|
|
|
|
|
This tells AsForm what to use to delmit forieign input names. This is important |
|
1378
|
|
|
|
|
|
|
to avoid name clashes as well as automating processing of forms. |
|
1379
|
|
|
|
|
|
|
|
|
1380
|
|
|
|
|
|
|
=cut |
|
1381
|
|
|
|
|
|
|
|
|
1382
|
|
|
|
|
|
|
sub foreign_input_delimiter { '__AF__' }; |
|
1383
|
|
|
|
|
|
|
|
|
1384
|
|
|
|
|
|
|
=head2 _box($value) |
|
1385
|
|
|
|
|
|
|
|
|
1386
|
|
|
|
|
|
|
This functions computes the dimensions of a textarea based on the value |
|
1387
|
|
|
|
|
|
|
or the defaults. |
|
1388
|
|
|
|
|
|
|
|
|
1389
|
|
|
|
|
|
|
=cut |
|
1390
|
|
|
|
|
|
|
|
|
1391
|
|
|
|
|
|
|
sub _box { |
|
1392
|
|
|
|
|
|
|
my ($min_rows, $max_rows, $min_cols, $max_cols) = (2 => 50, 20 => 100); |
|
1393
|
|
|
|
|
|
|
my $text = shift; |
|
1394
|
|
|
|
|
|
|
if ($text) { |
|
1395
|
|
|
|
|
|
|
my @rows = split /^/, $text; |
|
1396
|
|
|
|
|
|
|
my $cols = $min_cols; |
|
1397
|
|
|
|
|
|
|
my $chars = 0; |
|
1398
|
|
|
|
|
|
|
for (@rows) { |
|
1399
|
|
|
|
|
|
|
my $len = length $_; |
|
1400
|
|
|
|
|
|
|
$chars += $len; |
|
1401
|
|
|
|
|
|
|
$cols = $len if $len > $cols; |
|
1402
|
|
|
|
|
|
|
$cols = $max_cols if $cols > $max_cols; |
|
1403
|
|
|
|
|
|
|
} |
|
1404
|
|
|
|
|
|
|
my $rows = @rows; |
|
1405
|
|
|
|
|
|
|
$rows = int($chars/$cols) + 1 if $chars/$cols > $rows; |
|
1406
|
|
|
|
|
|
|
$rows = $min_rows if $rows < $min_rows; |
|
1407
|
|
|
|
|
|
|
$rows = $max_rows if $rows > $max_rows; |
|
1408
|
|
|
|
|
|
|
($rows, $cols) |
|
1409
|
|
|
|
|
|
|
} else { |
|
1410
|
|
|
|
|
|
|
($min_rows, $min_cols); |
|
1411
|
|
|
|
|
|
|
} |
|
1412
|
|
|
|
|
|
|
} |
|
1413
|
|
|
|
|
|
|
|
|
1414
|
|
|
|
|
|
|
|
|
1415
|
|
|
|
|
|
|
1; |
|
1416
|
|
|
|
|
|
|
|
|
1417
|
|
|
|
|
|
|
|
|
1418
|
|
|
|
|
|
|
=head1 CHANGES |
|
1419
|
|
|
|
|
|
|
|
|
1420
|
|
|
|
|
|
|
1.0 |
|
1421
|
|
|
|
|
|
|
15-07-2004 -- Initial version |
|
1422
|
|
|
|
|
|
|
=head1 MAINTAINER |
|
1423
|
|
|
|
|
|
|
|
|
1424
|
|
|
|
|
|
|
Maypole Developers |
|
1425
|
|
|
|
|
|
|
|
|
1426
|
|
|
|
|
|
|
=head1 AUTHORS |
|
1427
|
|
|
|
|
|
|
|
|
1428
|
|
|
|
|
|
|
Peter Speltz, Aaron Trevena |
|
1429
|
|
|
|
|
|
|
|
|
1430
|
|
|
|
|
|
|
=head1 AUTHORS EMERITUS |
|
1431
|
|
|
|
|
|
|
|
|
1432
|
|
|
|
|
|
|
Simon Cozens, Tony Bowden |
|
1433
|
|
|
|
|
|
|
|
|
1434
|
|
|
|
|
|
|
=head1 TODO |
|
1435
|
|
|
|
|
|
|
|
|
1436
|
|
|
|
|
|
|
Testing - lots |
|
1437
|
|
|
|
|
|
|
checkbox generalization |
|
1438
|
|
|
|
|
|
|
radio generalization |
|
1439
|
|
|
|
|
|
|
Make link_hidden use standard make_url stuff when it gets in Maypole |
|
1440
|
|
|
|
|
|
|
How do you tell AF --" I want a has_many select box for this every time so, |
|
1441
|
|
|
|
|
|
|
when you call "to_field($this_hasmany)" you get a select box |
|
1442
|
|
|
|
|
|
|
|
|
1443
|
|
|
|
|
|
|
=head1 BUGS and QUERIES |
|
1444
|
|
|
|
|
|
|
|
|
1445
|
|
|
|
|
|
|
Please direct all correspondence regarding this module to: |
|
1446
|
|
|
|
|
|
|
Maypole list. |
|
1447
|
|
|
|
|
|
|
|
|
1448
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
|
1449
|
|
|
|
|
|
|
|
|
1450
|
|
|
|
|
|
|
Copyright 2003-2004 by Simon Cozens / Tony Bowden |
|
1451
|
|
|
|
|
|
|
|
|
1452
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify |
|
1453
|
|
|
|
|
|
|
it under the same terms as Perl itself. |
|
1454
|
|
|
|
|
|
|
|
|
1455
|
|
|
|
|
|
|
=head1 SEE ALSO |
|
1456
|
|
|
|
|
|
|
|
|
1457
|
|
|
|
|
|
|
L, L, L. |
|
1458
|
|
|
|
|
|
|
|
|
1459
|
|
|
|
|
|
|
=cut |
|
1460
|
|
|
|
|
|
|
|