1 |
# Gedafe, the Generic Database Frontend |
2 |
# copyright (c) 2000-2003 ETH Zurich |
3 |
# see http://isg.ee.ethz.ch/tools/gedafe/ |
4 |
|
5 |
# released under the GNU General Public License |
6 |
|
7 |
package Gedafe::GUI; |
8 |
|
9 |
use strict; |
10 |
|
11 |
use Gedafe::Global qw(%g); |
12 |
use Gedafe::DB qw( |
13 |
DB_GetNumRecords |
14 |
DB_FetchList |
15 |
DB_GetRecord |
16 |
DB_AddRecord |
17 |
DB_UpdateRecord |
18 |
DB_GetCombo |
19 |
DB_DeleteRecord |
20 |
DB_GetDefault |
21 |
DB_ParseWidget |
22 |
DB_ID2HID |
23 |
DB_HID2ID |
24 |
DB_RawField |
25 |
DB_DumpTable |
26 |
); |
27 |
|
28 |
use Gedafe::Util qw( |
29 |
ConnectToTicketsDaemon |
30 |
MakeURL |
31 |
MyURL |
32 |
Template |
33 |
DropUnique |
34 |
UniqueFormStart |
35 |
FormStart |
36 |
UniqueFormEnd |
37 |
FormEnd |
38 |
NextRefresh |
39 |
); |
40 |
|
41 |
use POSIX; |
42 |
|
43 |
use vars qw(@ISA @EXPORT_OK); |
44 |
require Exporter; |
45 |
@ISA = qw(Exporter); |
46 |
@EXPORT_OK = qw( |
47 |
GUI_Entry |
48 |
GUI_List |
49 |
GUI_CheckFormID |
50 |
GUI_PostEdit |
51 |
GUI_Edit |
52 |
GUI_Delete |
53 |
GUI_Export |
54 |
GUI_DumpTable |
55 |
GUI_Pearl |
56 |
GUI_WidgetRead |
57 |
); |
58 |
|
59 |
sub GUI_AppletParam($$); |
60 |
sub GUI_CheckFormID($$); |
61 |
sub GUI_Delete($$$); |
62 |
sub GUI_DeleteLink($$$$); |
63 |
sub GUI_DumpTable($$$); |
64 |
sub GUI_Edit($$$); |
65 |
sub GUI_EditLink($$$$); |
66 |
sub GUI_Edit_Error($$$$$$); |
67 |
sub GUI_Entry($$$); |
68 |
sub GUI_Export($$$); |
69 |
sub GUI_ExportData($$); |
70 |
sub GUI_FilterFirst($$$$); |
71 |
sub GUI_Footer($); |
72 |
sub GUI_HTMLMarkup($); |
73 |
sub GUI_Hash2Str($); |
74 |
sub GUI_Header($$); |
75 |
sub GUI_InitTemplateArgs($$); |
76 |
sub GUI_List($$$); |
77 |
sub GUI_ListButtons($$$$); |
78 |
sub GUI_ListTable($$$); |
79 |
sub GUI_MakeCombo($$$$); |
80 |
sub GUI_MakeISearch($$$$$$); |
81 |
sub GUI_PostEdit($$$); |
82 |
sub GUI_Search($$$); |
83 |
sub GUI_Str2Hash($$); |
84 |
sub GUI_URL_Decode($); |
85 |
sub GUI_URL_Encode($); |
86 |
sub GUI_WidgetRead($$$); |
87 |
sub GUI_WidgetWrite($$$$); |
88 |
sub GUI_WidgetWrite_Date($$$); |
89 |
|
90 |
my %numeric_types = ( |
91 |
time => 1, |
92 |
timestamp => 1, |
93 |
int2 => 1, |
94 |
int4 => 1, |
95 |
int8 => 1, |
96 |
numeric => 1, |
97 |
float4 => 1, |
98 |
float8 => 1, |
99 |
); |
100 |
|
101 |
# setup for GUI_Export |
102 |
my ($csv, @exp_fmt_choices, %exp_fmt_choices); |
103 |
BEGIN { |
104 |
eval { |
105 |
# load modules necessary for formatting exported data: |
106 |
# if they don't load (aren't installed), no biggie- will fallback |
107 |
require Text::CSV_XS; |
108 |
$csv = Text::CSV_XS->new({binary => 1}); |
109 |
push(@exp_fmt_choices, 'csv'); |
110 |
$exp_fmt_choices{'csv'} = "Comma-separated (CSV)"; |
111 |
}; |
112 |
# add default (built-in) export format: tab-separated |
113 |
push(@exp_fmt_choices, 'tsv'); |
114 |
$exp_fmt_choices{'tsv'} = "Tab-separated (TSV)"; |
115 |
} |
116 |
|
117 |
sub GUI_HTMLMarkup($) |
118 |
{ |
119 |
my $str = shift; |
120 |
|
121 |
# e-mail addresses |
122 |
my $emaddrchars = '\w.:/~-'; |
123 |
$str =~ s,([^\@$emaddrchars]|\A)([$emaddrchars]+\@(?:[\w-]+\.)+[\w-]+)([^\@$emaddrchars]|\Z),$1<A HREF="mailto:$2">$2</A>$3,gi; |
124 |
|
125 |
my $urlchars = '\w.:/?~%&=\@\#-'; |
126 |
|
127 |
# http addresses with explicit "http://" or "https://" |
128 |
$str =~ s,([^$urlchars]|\A)(https?://[$urlchars]+)([^$urlchars]|\Z),$1<A HREF="$2" TARGET="refwindow">$2</A>$3,gi; |
129 |
|
130 |
# http addresses beginning with "www." |
131 |
$str =~ s,([^$urlchars]|\A)(www\.[\w.:/?~%&=\#-]+)([^$urlchars]|\Z),$1<A HREF="http://$2" TARGET="refwindow">$2</A>$3,gi; |
132 |
|
133 |
# http addresses ending in a common top-level domain |
134 |
my $tlds = 'ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|fi|fj|fk|fm|fo|fr|ga|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|yu|za|zm|zw|aero|biz|com|coop|info|museum|name|org|pro|gov|edu|mil|int'; |
135 |
|
136 |
$str =~ s,([^$urlchars]|\A)([\w.-]+\.)($tlds)(:\d+)?(/[\w./?~%&=\#-]*)?([^$urlchars]|\Z),$1<A HREF="http://$2$3$4$5" TARGET="refwindow">$2$3$4$5</A>$6,gi; |
137 |
|
138 |
return $str; |
139 |
} |
140 |
|
141 |
sub GUI_InitTemplateArgs($$) |
142 |
{ |
143 |
my ($s, $args) = @_; |
144 |
my $q = $s->{cgi}; |
145 |
|
146 |
my $refresh = NextRefresh(); |
147 |
|
148 |
$args->{DATABASE_DESC}=$g{db_database}{desc}; |
149 |
|
150 |
$args->{DOCUMENTATION_URL}=$g{conf}{documentation_url}; |
151 |
$args->{THEME}=$q->url_param('theme'); |
152 |
|
153 |
my $stripped_url = MakeURL($s->{url}, { |
154 |
filterfirst_button => '', |
155 |
search_button => '', |
156 |
}); |
157 |
$args->{BOOKMARK_URL}=MakeURL($stripped_url, { |
158 |
refresh => '', |
159 |
}); |
160 |
$args->{REFRESH_URL}=MakeURL($stripped_url, { |
161 |
refresh => $refresh, |
162 |
}); |
163 |
|
164 |
my $logout_url = MakeURL($stripped_url, { |
165 |
logout => 1, |
166 |
refresh => '', |
167 |
}); |
168 |
$args->{LOGOUT_URL}=$logout_url; |
169 |
|
170 |
my $entry_url = MakeURL($stripped_url, { |
171 |
id => '', |
172 |
action => '', |
173 |
orderby => '', |
174 |
table => '', |
175 |
offset => '', |
176 |
filterfirst => '', |
177 |
combo_filterfirst => '', |
178 |
descending => '', |
179 |
search_field => '', |
180 |
search_value => '', |
181 |
reedit_action => '', |
182 |
reedit_data => '', |
183 |
pearl=>'', |
184 |
}); |
185 |
$args->{ENTRY_URL}=$entry_url; |
186 |
$args->{REFRESH_ENTRY_URL}=MakeURL($entry_url, { |
187 |
refresh => $refresh, |
188 |
}); |
189 |
} |
190 |
|
191 |
sub GUI_Header($$) |
192 |
{ |
193 |
my ($s, $args) = @_; |
194 |
|
195 |
$args->{ELEMENT}='header'; |
196 |
print Template($args); |
197 |
|
198 |
$args->{ELEMENT}='header_table'; |
199 |
my $user = $args->{USER}; |
200 |
|
201 |
my $save_table = $args->{TABLE}; |
202 |
|
203 |
foreach my $t (@{$g{db_tables_list}}) { |
204 |
next if $g{db_tables}{$t}{hide}; |
205 |
next if $g{db_tables}{$t}{report}; |
206 |
if(defined $g{db_tables}{$t}{acls}{$user} and |
207 |
$g{db_tables}{$t}{acls}{$user} !~ /r/) { next; } |
208 |
my $desc = $g{db_tables}{$t}{desc}; |
209 |
$desc =~ s/ / /g; |
210 |
$args->{TABLE_TABLE}=$t; |
211 |
$args->{TABLE_DESC}=$desc; |
212 |
$args->{TABLE_URL}=MakeURL($args->{REFRESH_ENTRY_URL}, { |
213 |
action => 'list', |
214 |
table => $t, |
215 |
}); |
216 |
print Template($args); |
217 |
} |
218 |
delete $args->{TABLE_DESC}; |
219 |
delete $args->{TABLE_URL}; |
220 |
$args->{TABLE} = $save_table; |
221 |
|
222 |
$args->{ELEMENT}='header2'; |
223 |
print Template($args); |
224 |
|
225 |
delete $args->{ELEMENT}; |
226 |
|
227 |
$s->{header_sent}=1; |
228 |
} |
229 |
|
230 |
sub GUI_Footer($) |
231 |
{ |
232 |
my ($args) = @_; |
233 |
$args->{ELEMENT}='footer'; |
234 |
print Template($args); |
235 |
delete $args->{ELEMENT}; |
236 |
} |
237 |
|
238 |
|
239 |
sub GUI_Edit_Error($$$$$$) |
240 |
{ |
241 |
my ($s, $user, $str, $form_url, $data, $action) = @_; |
242 |
my $q = $s->{cgi}; |
243 |
|
244 |
my %template_args = ( |
245 |
PAGE => 'edit_error', |
246 |
USER => $user, |
247 |
TITLE => 'Database Error', |
248 |
ERROR => $str, |
249 |
REEDIT_URL => MakeURL($form_url, { |
250 |
action => 'reedit', |
251 |
reedit_action => $action, |
252 |
reedit_data => $data, |
253 |
}), |
254 |
); |
255 |
|
256 |
GUI_InitTemplateArgs($s, \%template_args); |
257 |
GUI_Header($s, \%template_args); |
258 |
|
259 |
$template_args{ELEMENT}='edit_error'; |
260 |
print Template(\%template_args); |
261 |
|
262 |
GUI_Footer(\%template_args); |
263 |
exit; |
264 |
} |
265 |
|
266 |
sub GUI_CheckFormID($$) |
267 |
{ |
268 |
my ($s, $user) = @_; |
269 |
my $q = $s->{cgi}; |
270 |
|
271 |
my $next_url = $q->param('next_url'); |
272 |
my %template_args = ( |
273 |
PAGE => 'doubleform', |
274 |
USER => $user, |
275 |
TITLE => "Duplicate Form", |
276 |
NEXT_URL => $next_url, |
277 |
); |
278 |
|
279 |
if(!DropUnique($s, $q->param('form_id'))) { |
280 |
print $q->header; |
281 |
GUI_InitTemplateArgs($s, \%template_args); |
282 |
GUI_Header($s, \%template_args); |
283 |
$template_args{ELEMENT}='doubleform'; |
284 |
print Template(\%template_args); |
285 |
GUI_Footer(\%template_args); |
286 |
exit; |
287 |
} |
288 |
} |
289 |
|
290 |
sub GUI_Entry($$$) |
291 |
{ |
292 |
my ($s, $user, $dbh) = @_; |
293 |
my $q = $s->{cgi}; |
294 |
|
295 |
my $refresh = NextRefresh(); |
296 |
|
297 |
my %template_args = ( |
298 |
USER => $user, |
299 |
TITLE => 'Entry', |
300 |
PAGE => 'entry', |
301 |
); |
302 |
|
303 |
GUI_InitTemplateArgs($s, \%template_args); |
304 |
GUI_Header($s, \%template_args); |
305 |
|
306 |
$template_args{ELEMENT}='tables_list_header', |
307 |
print Template(\%template_args); |
308 |
|
309 |
$template_args{ELEMENT}='entrytable'; |
310 |
foreach my $t (@{$g{db_tables_list}}) { |
311 |
next if $g{db_tables}{$t}{hide}; |
312 |
next if $g{db_tables}{$t}{report}; |
313 |
if(defined $g{db_tables}{$t}{acls}{$user} and |
314 |
$g{db_tables}{$t}{acls}{$user} !~ /r/) { next; } |
315 |
my $desc = $g{db_tables}{$t}{desc}; |
316 |
$desc =~ s/ / /g; |
317 |
$template_args{TABLE_DESC}=$desc; |
318 |
$template_args{TABLE_URL}= MakeURL($s->{url}, { |
319 |
action => 'list', |
320 |
table => $t, |
321 |
refresh => $refresh, |
322 |
}); |
323 |
print Template(\%template_args); |
324 |
} |
325 |
delete $template_args{TABLE_DESC}; |
326 |
delete $template_args{TABLE_URL}; |
327 |
|
328 |
$template_args{ELEMENT}='reports_list_header'; |
329 |
print Template(\%template_args); |
330 |
|
331 |
$template_args{ELEMENT}='entrytable'; |
332 |
foreach my $t (@{$g{db_tables_list}}) { |
333 |
next if $g{db_tables}{$t}{hide}; |
334 |
next unless $g{db_tables}{$t}{report}; |
335 |
if(defined $g{db_tables}{$t}{acls}{$user} and |
336 |
$g{db_tables}{$t}{acls}{$user} !~ /r/) { next; } |
337 |
my $desc = $g{db_tables}{$t}{desc}; |
338 |
$desc =~ s/ / /g; |
339 |
$template_args{TABLE_DESC}=$desc; |
340 |
$template_args{TABLE_URL}= MakeURL($s->{url}, { |
341 |
action => 'list', |
342 |
table => $t, |
343 |
refresh => $refresh, |
344 |
}); |
345 |
$template_args{REPORT}=1; |
346 |
print Template(\%template_args); |
347 |
} |
348 |
|
349 |
if(defined $g{pearls} and scalar %{$g{pearls}}) { |
350 |
$template_args{ELEMENT}='pearls_list_header'; |
351 |
print Template(\%template_args); |
352 |
|
353 |
$template_args{ELEMENT}='entrytable'; |
354 |
foreach my $t (sort {$a cmp $b} keys %{$g{pearls}}) { |
355 |
if (ref $g{pearls}{$t}) { |
356 |
@template_args{qw(TABLE_DESC TABLE_INFO)}=($g{pearls}{$t}->info); |
357 |
$template_args{TABLE_URL}= MakeURL($s->{url}, { |
358 |
action => 'configpearl', |
359 |
pearl=> $t, |
360 |
table => undef, |
361 |
refresh => $refresh, |
362 |
}); |
363 |
} else { |
364 |
$template_args{REPORT}=1; |
365 |
@template_args{qw(TABLE_DESC TABLE_INFO)}=($t,$g{pearls}{$t}); |
366 |
$template_args{TABLE_URL}= MakeURL($s->{url}, { |
367 |
action => 'entry', |
368 |
pearl=> $t, |
369 |
table => undef, |
370 |
refresh => $refresh, |
371 |
}); |
372 |
$template_args{REPORT}=1; |
373 |
} |
374 |
print Template(\%template_args); |
375 |
} |
376 |
} |
377 |
|
378 |
GUI_Footer(\%template_args); |
379 |
} |
380 |
|
381 |
sub GUI_FilterFirst($$$$) |
382 |
{ |
383 |
my $s = shift; |
384 |
my $q = $s->{cgi}; |
385 |
my $dbh = shift; |
386 |
my $view = shift; |
387 |
my $template_args = shift; |
388 |
my $myurl = MyURL($q); |
389 |
my $ff_field = $g{db_tables}{$view}{meta}{filterfirst}; |
390 |
my $ff_value = $q->url_param('filterfirst') || |
391 |
$q->url_param('combo_filterfirst') || ''; |
392 |
|
393 |
defined $ff_field or return undef; |
394 |
|
395 |
my $ff_ref = $g{db_fields}{$view}{$ff_field}{reference}; |
396 |
my $ff_combo_name = "${ff_ref}_combo" unless not defined $ff_ref; |
397 |
|
398 |
if(!defined $ff_ref or !defined $g{db_tables}{$ff_combo_name}) { |
399 |
die "combo not found for $ff_field (reference: ${ff_ref})"; |
400 |
} |
401 |
|
402 |
my $ff_combo = GUI_MakeCombo($dbh, $ff_combo_name, "combo_filterfirst", $ff_value); |
403 |
|
404 |
# ID->HID: if referenced table has a hid, assume the hid is shown in |
405 |
# this view and search that instead. In Gedafe 1.0 mode, the |
406 |
# combo-boxes always referenced the hid, so it is already done |
407 |
if($g{conf}{gedafe_compat} ne '1.0') { |
408 |
if(defined $g{db_fields}{$ff_ref}{"${ff_ref}_hid"}) { |
409 |
$ff_value = DB_ID2HID($dbh,$ff_ref,$ff_value); |
410 |
} |
411 |
} |
412 |
|
413 |
my $ff_hidden = ''; |
414 |
foreach($q->url_param) { |
415 |
next if /^filterfirst/; |
416 |
next if /button$/; |
417 |
$ff_hidden .= "<INPUT TYPE=\"hidden\" NAME=\"$_\" VALUE=\"".$q->url_param($_)."\">\n"; |
418 |
} |
419 |
$template_args->{ELEMENT}='filterfirst'; |
420 |
$template_args->{FILTERFIRST_FIELD}=$ff_field; |
421 |
$template_args->{FILTERFIRST_FIELD_DESC}=$g{db_fields}{$view}{$ff_field}{desc}; |
422 |
$template_args->{FILTERFIRST_COMBO}=$ff_combo; |
423 |
$template_args->{FILTERFIRST_HIDDEN}=$ff_hidden; |
424 |
$template_args->{FILTERFIRST_ACTION}=$q->url; |
425 |
print Template($template_args); |
426 |
delete $template_args->{ELEMENT}; |
427 |
delete $template_args->{FILTERFIRST_FIELD}; |
428 |
delete $template_args->{FILTERFIRST_FIELD_DESC}; |
429 |
delete $template_args->{FILTERFIRST_COMBO}; |
430 |
delete $template_args->{FILTERFIRST_HIDDEN}; |
431 |
delete $template_args->{FILTERFIRST_ACTION}; |
432 |
|
433 |
if($ff_value eq '') { $ff_value = undef; } |
434 |
|
435 |
return ($ff_field, $ff_value); |
436 |
} |
437 |
|
438 |
sub GUI_Search($$$) |
439 |
{ |
440 |
my $s = shift; |
441 |
my $q = $s->{cgi}; |
442 |
my $view = shift; |
443 |
my $template_args = shift; |
444 |
my $search_field = $q->url_param('search_field') || ''; |
445 |
my $search_value = $q->url_param('search_value') || ''; |
446 |
|
447 |
$search_field =~ s/^\s*//; $search_field =~ s/\s*$//; |
448 |
$search_value =~ s/^\s*//; $search_value =~ s/\s*$//; |
449 |
my $fields = $g{db_fields}{$view}; |
450 |
my $search_combo = "<SELECT name=\"search_field\" SIZE=\"1\">\n"; |
451 |
foreach(@{$g{db_fields_list}{$view}}) { |
452 |
next if /${view}_id/; |
453 |
if(/^$search_field$/) { |
454 |
$search_combo .= "<OPTION SELECTED VALUE=\"$_\">$fields->{$_}{desc}</OPTION>\n"; |
455 |
} |
456 |
else { |
457 |
$search_combo .= "<OPTION VALUE=\"$_\">$fields->{$_}{desc}</OPTION>\n"; |
458 |
} |
459 |
} |
460 |
$search_combo .= "</SELECT>\n"; |
461 |
my $search_hidden = ''; |
462 |
foreach($q->url_param) { |
463 |
next if /^search/; |
464 |
next if /^button/; |
465 |
next if /^offset$/; |
466 |
$search_hidden .= "<INPUT TYPE=\"hidden\" NAME=\"$_\" VALUE=\"".$q->url_param($_)."\">\n"; |
467 |
} |
468 |
$template_args->{ELEMENT} = 'search'; |
469 |
$template_args->{SEARCH_ACTION} = $q->url; |
470 |
$template_args->{SEARCH_COMBO} = $search_combo; |
471 |
$template_args->{SEARCH_HIDDEN} = $search_hidden; |
472 |
$template_args->{SEARCH_VALUE} = $search_value; |
473 |
$template_args->{SEARCH_SHOWALL} = MakeURL(MyURL($q), { search_value=>'', search_button=>'', search_field=>'' }); |
474 |
print Template($template_args); |
475 |
delete $template_args->{ELEMENT}; |
476 |
delete $template_args->{SEARCH_ACTION}; |
477 |
delete $template_args->{SEARCH_COMBO}; |
478 |
delete $template_args->{SEARCH_HIDDEN}; |
479 |
delete $template_args->{SEARCH_VALUE}; |
480 |
delete $template_args->{SEARCH_SHOWALL}; |
481 |
|
482 |
# search date = TODAY |
483 |
if($search_field ne '') { |
484 |
if($g{db_fields}{$view}{$search_field}{type} eq 'date') { |
485 |
if($search_value =~ /^today$/i) { |
486 |
$search_value = POSIX::strftime("%Y-%m-%d", localtime); |
487 |
} |
488 |
elsif($search_value =~ /^yesterday$/i) { |
489 |
my $time = time; |
490 |
$time -= 3600 * 24; |
491 |
$search_value = POSIX::strftime("%Y-%m-%d", localtime($time)); |
492 |
} |
493 |
} |
494 |
} |
495 |
|
496 |
return ($search_field, $search_value); |
497 |
} |
498 |
|
499 |
sub GUI_EditLink($$$$) |
500 |
{ |
501 |
my ($s, $template_args, $list, $row) = @_; |
502 |
my $edit_url; |
503 |
$edit_url = MakeURL($s->{url}, { |
504 |
action=>'edit', |
505 |
id=>$row->[0], |
506 |
refresh=>NextRefresh, |
507 |
}); |
508 |
$template_args->{ELEMENT}='td_edit'; |
509 |
$template_args->{EDIT_URL}=$edit_url; |
510 |
print Template($template_args); |
511 |
delete $template_args->{EDIT_URL}; |
512 |
} |
513 |
|
514 |
sub GUI_DeleteLink($$$$) |
515 |
{ |
516 |
my ($s, $template_args, $list, $row) = @_; |
517 |
my $delete_url; |
518 |
$delete_url = MakeURL($s->{url}, { |
519 |
action=>'delete', |
520 |
id=>$row->[0], |
521 |
refresh=>NextRefresh, |
522 |
}); |
523 |
$template_args->{ELEMENT}='td_delete'; |
524 |
$template_args->{DELETE_URL}=$delete_url; |
525 |
print Template($template_args); |
526 |
delete $template_args->{DELETE_URL}; |
527 |
} |
528 |
|
529 |
sub GUI_ListTable($$$) |
530 |
{ |
531 |
my ($s, $list, $page) = @_; |
532 |
|
533 |
# user can edit only if they have sql UPDATE privilege, and |
534 |
# this table is a real table, not a report (view) |
535 |
my $can_edit = ($list->{acl} =~ /w/ and !$list->{is_report}); |
536 |
my $can_delete = $can_edit; |
537 |
|
538 |
my %template_args = ( |
539 |
USER => $s->{user}, |
540 |
PAGE => $page, |
541 |
URL => $s->{url}, |
542 |
TABLE => $list->{spec}{view}, |
543 |
TITLE => "$g{db_tables}{$list->{spec}{table}}{desc}", |
544 |
ORDERBY => $list->{spec}{orderby}, |
545 |
); |
546 |
|
547 |
# <TABLE> |
548 |
$template_args{ELEMENT}='table'; |
549 |
|
550 |
# total number of records in result set |
551 |
$template_args{NUM_RECORDS} = $list->{totalrecords} |
552 |
if $g{conf}{show_row_count}; |
553 |
|
554 |
print Template(\%template_args); |
555 |
$s->{in_table}=1; # die will put a </TABLE> |
556 |
|
557 |
# header |
558 |
$template_args{ELEMENT}='tr'; |
559 |
print Template(\%template_args); |
560 |
for my $c (@{$list->{columns}}) { |
561 |
next if $c->{hide_list}; |
562 |
my $sort_url; |
563 |
if($list->{spec}{orderby} eq $c->{field}) { |
564 |
my $d = $list->{spec}{descending} ? '' : 1; |
565 |
$sort_url = MakeURL($s->{url}, { descending => $d }); |
566 |
} |
567 |
else { |
568 |
$sort_url = MakeURL($s->{url}, { orderby => "$c->{field}", descending=>'' }); |
569 |
} |
570 |
|
571 |
$template_args{ELEMENT}='th'; |
572 |
$template_args{DATA}=$c->{desc}; |
573 |
$template_args{FIELD}=$c->{field}; |
574 |
$template_args{SORT_URL}=$sort_url; |
575 |
print Template(\%template_args); |
576 |
} |
577 |
delete $template_args{DATA}; |
578 |
delete $template_args{FIELD}; |
579 |
delete $template_args{SORT_URL}; |
580 |
if($can_edit) { |
581 |
$template_args{ELEMENT}='th_edit'; |
582 |
print Template(\%template_args); |
583 |
} |
584 |
if($can_delete) { |
585 |
$template_args{ELEMENT}='th_delete'; |
586 |
print Template(\%template_args); |
587 |
} |
588 |
$template_args{ELEMENT}='xtr'; |
589 |
print Template(\%template_args); |
590 |
|
591 |
# data |
592 |
$list->{displayed_recs} = 0; |
593 |
for my $row (@{$list->{data}}) { |
594 |
$list->{displayed_recs}++; |
595 |
if($list->{displayed_recs}%2) { $template_args{EVENROW}=1; } |
596 |
else { $template_args{ODDROW}=1; } |
597 |
|
598 |
$template_args{ELEMENT}='tr'; |
599 |
print Template(\%template_args); |
600 |
my $column_number = 0; |
601 |
for my $d (@{$row->[1]}) { |
602 |
my $c = $list->{columns}[$column_number]; |
603 |
$column_number++; |
604 |
next if $c->{hide_list}; |
605 |
if($c->{type} eq 'bytea' && $d ne ' '){ |
606 |
my $bloburl = MakeURL($s->{url}, { |
607 |
table => $list->{spec}{view}, |
608 |
action => 'dumpblob', |
609 |
id => $row->[0], |
610 |
field => $c->{field}, |
611 |
}); |
612 |
$d = qq{<A HREF="$bloburl" TARGET="_blank">$d</A>}; |
613 |
} |
614 |
my $align = $c->{align}; |
615 |
defined $align or $align = $numeric_types{$c->{type}} ? |
616 |
'"RIGHT" NOWRAP' : '"LEFT"' unless defined $align; |
617 |
$template_args{ALIGN}=$align; |
618 |
$template_args{ELEMENT}='td'; |
619 |
$template_args{DATA}=$d; |
620 |
$template_args{MARKUP}=GUI_HTMLMarkup($d) if $d and $c->{markup}; |
621 |
print Template(\%template_args); |
622 |
} |
623 |
delete $template_args{DATA}; |
624 |
delete $template_args{ALIGN}; |
625 |
delete $template_args{MARKUP}; |
626 |
|
627 |
$template_args{ID} = $row->[0]; |
628 |
GUI_EditLink($s, \%template_args, $list, $row) if $can_edit; |
629 |
GUI_DeleteLink($s, \%template_args, $list, $row) if $can_delete; |
630 |
delete $template_args{ID}; |
631 |
|
632 |
$template_args{ELEMENT}='xtr'; |
633 |
print Template(\%template_args); |
634 |
|
635 |
if($list->{displayed_recs}%2) {delete $template_args{EVENROW};} |
636 |
else {delete $template_args{ODDROW};} |
637 |
} |
638 |
|
639 |
# </TABLE> |
640 |
$template_args{ELEMENT}='xtable'; |
641 |
print Template(\%template_args); |
642 |
$s->{in_table}=0; |
643 |
} |
644 |
|
645 |
sub GUI_ListButtons($$$$) |
646 |
{ |
647 |
my ($s, $list, $page, $position) = @_; |
648 |
|
649 |
my %template_args = ( |
650 |
USER => $s->{user}, |
651 |
PAGE => $page, |
652 |
URL => $s->{url}, |
653 |
TABLE => $list->{spec}{view}, |
654 |
TITLE => "$g{db_tables}{$list->{spec}{table}}{desc}", |
655 |
TOP => $position eq 'top', |
656 |
BOTTOM => $position eq 'bottom', |
657 |
IS_REPORT => $list->{is_report} |
658 |
); |
659 |
|
660 |
my $next_refresh = NextRefresh; |
661 |
|
662 |
my $nextoffset = $list->{spec}{offset}+$list->{spec}{limit}; |
663 |
my $prevoffset = $list->{spec}{offset}-$list->{spec}{limit}; |
664 |
$prevoffset > 0 or $prevoffset = ''; |
665 |
|
666 |
my $can_add = ($list->{acl} =~ /a/); |
667 |
my $add_url = $can_add ? MakeURL($s->{url}, { |
668 |
action => 'add', |
669 |
refresh => $next_refresh, |
670 |
}) : undef; |
671 |
|
672 |
my $prev_url = $list->{spec}{offset} != 0 ? MakeURL($s->{url}, |
673 |
{ offset => $prevoffset }) : undef; |
674 |
my $next_url = $list->{end} ? undef : |
675 |
MakeURL($s->{url}, { offset => $nextoffset }); |
676 |
|
677 |
$template_args{ELEMENT}='buttons'; |
678 |
$template_args{ADD_URL}=$add_url; |
679 |
$template_args{PREV_URL}=$prev_url; |
680 |
$template_args{NEXT_URL}=$next_url; |
681 |
|
682 |
# calculate correct offset for last page of results |
683 |
if ($g{conf}{show_row_count}) { |
684 |
my $totalrecs = $template_args{NUM_RECORDS} = $list->{totalrecords}; |
685 |
my $lastoffset = |
686 |
($totalrecs % $list->{spec}{limit} == 0 |
687 |
? $totalrecs - $list->{spec}{limit} |
688 |
: $totalrecs - ($totalrecs % $list->{spec}{limit})); |
689 |
my $first_url = |
690 |
($prev_url && $prevoffset ne '' |
691 |
? MakeURL($s->{url}, { offset => '' }) : undef); |
692 |
my $last_url = |
693 |
($next_url && $nextoffset != $lastoffset |
694 |
? MakeURL($s->{url}, { offset => $lastoffset }) : undef); |
695 |
$template_args{START_RECNUM}= |
696 |
($list->{spec}{offset}+1 > $totalrecs |
697 |
? $totalrecs |
698 |
: $list->{spec}{offset}+1); |
699 |
$template_args{END_RECNUM}= |
700 |
($list->{spec}{offset}+$#{$list->{data}}+1 > $totalrecs |
701 |
? $totalrecs |
702 |
: $list->{spec}{offset}+$#{$list->{data}}+1); |
703 |
$template_args{FIRST_URL}=$first_url; |
704 |
$template_args{LAST_URL}=$last_url; |
705 |
} |
706 |
|
707 |
print Template(\%template_args); |
708 |
} |
709 |
|
710 |
sub GUI_List($$$) |
711 |
{ |
712 |
my ($s, $user, $dbh) = @_; |
713 |
my $q = $s->{cgi}; |
714 |
my $table = $q->url_param('table'); |
715 |
|
716 |
my %template_args = ( |
717 |
USER => $user, |
718 |
PAGE => 'list', |
719 |
URL => $s->{url}, |
720 |
TABLE => $table, |
721 |
TITLE => "$g{db_tables}{$table}{desc}", |
722 |
EXPORT_AS_CHOICE => $#exp_fmt_choices > 0, |
723 |
EXPORT_CHOICES => "<SELECT NAME=\"export_format\">\n".join("\n", map { "<OPTION VALUE=\"$_\">$exp_fmt_choices{$_}</OPTION>" } @exp_fmt_choices).'</SELECT>', |
724 |
EXPORT_URL => MakeURL(MyURL($q), { action => 'export' }), |
725 |
); |
726 |
|
727 |
# header |
728 |
GUI_InitTemplateArgs($s, \%template_args); |
729 |
GUI_Header($s, \%template_args); |
730 |
|
731 |
# build list-spec |
732 |
my %spec = ( |
733 |
table => $table, |
734 |
view => defined $g{db_tables}{"${table}_list"} ? |
735 |
"${table}_list" : $table, |
736 |
offset => $q->url_param('offset') || 0, |
737 |
limit => $q->url_param('list_rows') || $g{conf}{list_rows}, |
738 |
orderby => $q->url_param('orderby') || '', |
739 |
descending => $q->url_param('descending') || 0, |
740 |
); |
741 |
|
742 |
# filterfirst |
743 |
($spec{filter_field}, $spec{filter_value}) = |
744 |
GUI_FilterFirst($s, $dbh, $spec{view}, \%template_args); |
745 |
|
746 |
# search |
747 |
($spec{search_field}, $spec{search_value}) = |
748 |
GUI_Search($s, $spec{view}, \%template_args); |
749 |
|
750 |
# fetch list |
751 |
my $list = DB_FetchList($s, \%spec); |
752 |
|
753 |
# get total number of records for this search set |
754 |
$list->{totalrecords} = DB_GetNumRecords($s, \%spec) |
755 |
if $g{conf}{show_row_count}; |
756 |
|
757 |
# is it a report (read-only)? |
758 |
$list->{is_report} = 1 if $g{db_tables}{$table}{report}; |
759 |
|
760 |
my $list_buttons = $g{conf}{list_buttons} || 'both'; |
761 |
|
762 |
# top buttons |
763 |
if($list_buttons eq 'top' or $list_buttons eq 'both'){ |
764 |
GUI_ListButtons($s, $list, 'list', 'top'); |
765 |
} |
766 |
|
767 |
# display table |
768 |
GUI_ListTable($s, $list, 'list'); |
769 |
|
770 |
# bottom buttons |
771 |
if($list_buttons eq 'bottom' or $list_buttons eq 'both'){ |
772 |
GUI_ListButtons($s, $list, 'list', 'bottom'); |
773 |
} |
774 |
delete $list->{displayed_recs}; |
775 |
delete $list->{totalrecords} if $g{conf}{show_row_count}; |
776 |
|
777 |
# footer |
778 |
GUI_Footer(\%template_args); |
779 |
} |
780 |
|
781 |
sub GUI_ExportData($$) |
782 |
{ |
783 |
my ($s, $list) = @_; |
784 |
my $q = $s->{cgi}; |
785 |
|
786 |
# decide what export format to use: 'csv' only if Text::CSV_XS loaded |
787 |
my $exp_fmt = ref $csv && $q->param('export_format') eq 'csv' ? 'csv' : 'tsv'; |
788 |
|
789 |
# print HTTP Content-type header |
790 |
if ($exp_fmt eq 'csv') { |
791 |
print $q->header(-type=>'text/csv', |
792 |
-attachment=>$list->{spec}{table}.'.csv', |
793 |
-expires=>'-1d'); |
794 |
} else { |
795 |
print $q->header(-type=>'text/tab-separated-values', |
796 |
-attachment=>$list->{spec}{table}.'.tsv', |
797 |
-expires=>'-1d'); |
798 |
} |
799 |
|
800 |
# fields |
801 |
my $fields = $g{db_fields}{$list->{spec}{view}}; |
802 |
if ($exp_fmt eq 'csv') { |
803 |
my $status = $csv->combine(map {$fields->{$_}{desc}} @{$list->{fields}}); |
804 |
print $csv->string(). "\n"; |
805 |
} else { |
806 |
print join("\t", map {$fields->{$_}{desc}} @{$list->{fields}})."\n"; |
807 |
} |
808 |
|
809 |
# data |
810 |
for my $row (@{$list->{data}}) { |
811 |
# if correct module is loaded and user selected 'CSV' |
812 |
if ($exp_fmt eq 'csv') { |
813 |
my $status = $csv->combine(@{$row->[1]}); |
814 |
print $csv->string() . "\n"; |
815 |
} else { |
816 |
print join("\t", map { |
817 |
my $str = defined $_ ? $_ : ''; |
818 |
$str=~s/\t/ /g; |
819 |
$str=~s/\n/\r/g; |
820 |
$str; |
821 |
} @{$row->[1]})."\n"; |
822 |
} |
823 |
} |
824 |
} |
825 |
|
826 |
sub GUI_Export($$$) |
827 |
{ |
828 |
my ($s, $user, $dbh) = @_; |
829 |
my $q = $s->{cgi}; |
830 |
my $table = $q->url_param('table'); |
831 |
|
832 |
my %template_args = ( |
833 |
USER => $user, |
834 |
PAGE => 'export', |
835 |
URL => $s->{url}, |
836 |
TABLE => $table, |
837 |
TITLE => "$g{db_tables}{$table}{desc}", |
838 |
); |
839 |
|
840 |
# build list-spec |
841 |
my %spec = ( |
842 |
table => $table, |
843 |
view => defined $g{db_tables}{"${table}_list"} ? |
844 |
"${table}_list" : $table, |
845 |
orderby => $q->url_param('orderby') || '', |
846 |
descending => $q->url_param('descending') || 0, |
847 |
export => 1, |
848 |
); |
849 |
|
850 |
# get search params |
851 |
$spec{search_field} = $q->url_param('search_field') || ''; |
852 |
$spec{search_value} = $q->url_param('search_value') || ''; |
853 |
$spec{search_field} =~ s/^\s*//; $spec{search_field} =~ s/\s*$//; |
854 |
$spec{search_value} =~ s/^\s*//; $spec{search_value} =~ s/\s*$//; |
855 |
|
856 |
# fetch list |
857 |
my $list = DB_FetchList($s, \%spec); |
858 |
|
859 |
GUI_ExportData($s, $list); |
860 |
} |
861 |
|
862 |
# CGI.pm already encodes/decodes parameters, but we want to do it ourselves |
863 |
# since we need to differentiate for example in reedit_data between a comma |
864 |
# as value and a comma as separator. Therefore we use the escape '!' instead |
865 |
# of '%'. |
866 |
sub GUI_URL_Encode($) |
867 |
{ |
868 |
my ($str) = @_; |
869 |
defined $str or $str = ''; |
870 |
$str =~ s/!/gedafe_PROTECTED_eXclamatiOn/g; |
871 |
$str =~ s/\W/'!'.sprintf('%2X',ord($&))/eg; |
872 |
$str =~ s/gedafe_PROTECTED_eXclamatiOn/'!'.sprintf('%2X',ord('!'))/eg; |
873 |
return $str; |
874 |
} |
875 |
|
876 |
sub GUI_URL_Decode($) |
877 |
{ |
878 |
my ($str) = @_; |
879 |
$str =~ s/!([0-9a-fA-F]{2})/pack("c",hex($1))/ge; |
880 |
return $str; |
881 |
} |
882 |
|
883 |
sub GUI_Hash2Str($) |
884 |
{ |
885 |
my ($record) = @_; |
886 |
my @data = (); |
887 |
for my $f (keys %$record) { |
888 |
my $d = GUI_URL_Encode($record->{$f}); |
889 |
push @data, "$f:$d"; |
890 |
} |
891 |
return join(',', @data); |
892 |
} |
893 |
|
894 |
sub GUI_Str2Hash($$) |
895 |
{ |
896 |
my ($str, $hash) = @_; |
897 |
for my $s (split(/,/, $str)) { |
898 |
if($s =~ /^(.*?):(.*)$/) { |
899 |
$hash->{$1} = GUI_URL_Decode($2); |
900 |
} |
901 |
} |
902 |
} |
903 |
|
904 |
sub GUI_WidgetRead($$$) |
905 |
{ |
906 |
my ($s, $input_name, $widget) = @_; |
907 |
my $q = $s->{cgi}; |
908 |
my $dbh = $s->{dbh}; |
909 |
|
910 |
my ($w, $warg) = DB_ParseWidget($widget); |
911 |
|
912 |
my $value = $q->param($input_name); |
913 |
|
914 |
if($w eq 'file'){ |
915 |
my $file = $value; |
916 |
my $deletefile = $q->param("file_delete_$input_name"); |
917 |
if($deletefile) { |
918 |
$value=""; |
919 |
} |
920 |
else { |
921 |
if($file) { |
922 |
my $filename = scalar $file; |
923 |
$filename =~ s/ /_/g; |
924 |
$filename =~ /([\w\d\.]+$)/; |
925 |
$filename = $1; |
926 |
my $mimetype = $q->uploadInfo($file)->{'Content-Type'}; |
927 |
my $blob=$filename.' '.$mimetype.'#'; |
928 |
my $buffer; |
929 |
while(read($file,$buffer,1024)){ |
930 |
$blob .=$buffer; |
931 |
} |
932 |
#note that value is set to a reference to the large blob |
933 |
$value=\$blob; |
934 |
} |
935 |
else { |
936 |
#when we are here the file field has not been set |
937 |
$value = undef; |
938 |
} |
939 |
} |
940 |
} |
941 |
if($w eq 'hid' or $w eq 'hidcombo' or $w eq 'hidisearch') { |
942 |
if(defined $value and $value !~ /^\s*$/) { |
943 |
$value=DB_HID2ID($dbh,$warg->{'ref'},$value); |
944 |
} |
945 |
} |
946 |
# if it's a combo and no value was specified in the text field... |
947 |
if($w eq 'idcombo' or $w eq 'hidcombo' or $w eq 'combo') { |
948 |
if(not defined $value or $value =~ /^\s*$/) { |
949 |
$value = $q->param("${input_name}_combo"); |
950 |
if($w eq 'hidcombo' and $g{conf}{gedafe_compat} eq '1.0') |
951 |
{ |
952 |
# hidcombos in 1.0 had to put the HID as key... |
953 |
$value = DB_HID2ID($dbh,$warg->{'ref'},$value); |
954 |
} |
955 |
} |
956 |
} |
957 |
|
958 |
return $value; |
959 |
} |
960 |
|
961 |
sub GUI_PostEdit($$$) |
962 |
{ |
963 |
my ($s, $user, $dbh) = @_; |
964 |
my $q = $s->{cgi}; |
965 |
my $action = $q->param('post_action'); |
966 |
if(not defined $action) { return; } |
967 |
|
968 |
if(defined $q->param('button_cancel')) { return; } |
969 |
|
970 |
my $table = $q->url_param('table'); |
971 |
|
972 |
## delete |
973 |
if($action eq 'delete') { |
974 |
if(!DB_DeleteRecord($dbh,$table,$q->param('id'))) { |
975 |
my %template_args = ( |
976 |
PAGE => 'db_error', |
977 |
USER => $user, |
978 |
TITLE => 'Database Error' |
979 |
); |
980 |
GUI_InitTemplateArgs($s, \%template_args); |
981 |
GUI_Header($s, \%template_args); |
982 |
$template_args{ELEMENT}='db_error'; |
983 |
$template_args{ERROR}=$g{db_error}; |
984 |
$template_args{NEXT_URL}=MyURL($q); |
985 |
print Template(\%template_args); |
986 |
GUI_Footer(\%template_args); |
987 |
exit; |
988 |
} |
989 |
} |
990 |
|
991 |
|
992 |
## add or edit: |
993 |
my %record; |
994 |
if($action eq 'add' || $action eq 'edit'){ |
995 |
for my $field (@{$g{db_fields_list}{$table}}) { |
996 |
my $value = GUI_WidgetRead($s, "field_$field", $g{db_fields}{$table}{$field}{widget}); |
997 |
if(defined $value) { |
998 |
$record{$field} = $value; |
999 |
} |
1000 |
} |
1001 |
} |
1002 |
if($action eq 'add') { |
1003 |
if(!DB_AddRecord($dbh,$table,\%record)) { |
1004 |
my $data = GUI_Hash2Str(\%record); |
1005 |
GUI_Edit_Error($s, $user, $g{db_error}, |
1006 |
$q->param('form_url'), $data, $action); |
1007 |
} |
1008 |
} |
1009 |
elsif($action eq 'edit') { |
1010 |
$record{id} = $q->param('id'); |
1011 |
if(!DB_UpdateRecord($dbh,$table,\%record)) { |
1012 |
my $data = GUI_Hash2Str(\%record); |
1013 |
GUI_Edit_Error($s, $user, $g{db_error}, |
1014 |
$q->param('form_url'), $data, $action); |
1015 |
} |
1016 |
} |
1017 |
} |
1018 |
|
1019 |
sub GUI_Edit($$$) |
1020 |
{ |
1021 |
my ($s, $user, $dbh) = @_; |
1022 |
my $q = $s->{cgi}; |
1023 |
my $action = $q->url_param('action'); |
1024 |
my $table = $q->url_param('table'); |
1025 |
my $id = $q->url_param('id'); |
1026 |
|
1027 |
my $reedit = undef; |
1028 |
if($action eq 'reedit') { |
1029 |
$reedit = 1; |
1030 |
$action = $q->url_param('reedit_action'); |
1031 |
} |
1032 |
|
1033 |
if(not exists $g{db_tables}{$table}) { |
1034 |
die "Error: no such table ($table)"; |
1035 |
} |
1036 |
|
1037 |
my $title = $g{db_tables}{$table}{desc}; |
1038 |
$title =~ s/s\s*$//; # very rough :-) |
1039 |
$title =~ s/^. //; |
1040 |
my %template_args = ( |
1041 |
USER => $user, |
1042 |
PAGE => 'edit', |
1043 |
TABLE => $table, |
1044 |
TITLE => $action eq 'add' ? "New $title" : "Edit $title", |
1045 |
ACTION => $action, |
1046 |
ID => $id, |
1047 |
REEDIT => $reedit, |
1048 |
); |
1049 |
|
1050 |
my $form_url = MakeURL($s->{url}, { refresh => NextRefresh() }); |
1051 |
my $next_url; |
1052 |
my $cancel_url = MakeURL($form_url, { |
1053 |
action => 'list', |
1054 |
id => '', |
1055 |
reedit_action => '', |
1056 |
reedit_data => '', |
1057 |
}); |
1058 |
if($action eq 'add') { |
1059 |
$next_url = MakeURL($form_url, { |
1060 |
action => $action, |
1061 |
reedit_action => '', |
1062 |
reedit_data => '', |
1063 |
}); |
1064 |
} |
1065 |
else { |
1066 |
$next_url = $cancel_url; |
1067 |
} |
1068 |
|
1069 |
GUI_InitTemplateArgs($s, \%template_args); |
1070 |
GUI_Header($s, \%template_args); |
1071 |
|
1072 |
# FORM |
1073 |
UniqueFormStart($s, $next_url); |
1074 |
print "<INPUT TYPE=\"hidden\" NAME=\"post_action\" VALUE=\"$action\">\n"; |
1075 |
|
1076 |
# Initialise values |
1077 |
my $fields = $g{db_fields}{$table}; |
1078 |
my @fields_list = @{$g{db_fields_list}{$table}}; |
1079 |
my %values = (); |
1080 |
if($reedit) { |
1081 |
GUI_Str2Hash($q->param('reedit_data'), \%values); |
1082 |
} |
1083 |
elsif($action eq 'edit') { |
1084 |
my %record = (); |
1085 |
DB_GetRecord($dbh,$table,$id,\%values); |
1086 |
} |
1087 |
elsif($action eq 'add') { |
1088 |
# take filterfirst value if set |
1089 |
my $ff_field = $g{db_tables}{$table}{meta}{filterfirst}; |
1090 |
my $ff_value = $q->url_param('filterfirst') || $q->url_param('combo_filterfirst') || ''; |
1091 |
if(defined $ff_value and defined $ff_field) { |
1092 |
if(defined $g{db_fields}{$table}{$ff_field}{ref_hid}) { |
1093 |
# convert ID reference to HID |
1094 |
$values{$ff_field} = DB_ID2HID($dbh, $g{db_fields}{$table}{$ff_field}{reference}, $ff_value); |
1095 |
} |
1096 |
} |
1097 |
# copy fields from previous add form |
1098 |
for my $field (@fields_list) { |
1099 |
if($g{db_fields}{$table}{$field}{copy}) { |
1100 |
# FIXME: find out how this works with files. |
1101 |
# I think we don't want to copy a file. |
1102 |
my $v = GUI_WidgetRead($s, "field_$field", $g{db_fields}{$table}{$field}{widget}); |
1103 |
$values{$field} = $v if defined $v; |
1104 |
} |
1105 |
} |
1106 |
} |
1107 |
|
1108 |
if($action eq 'edit') { |
1109 |
print "<INPUT TYPE=\"hidden\" NAME=\"id\" VALUE=\"$id\">\n"; |
1110 |
} |
1111 |
|
1112 |
# Fields |
1113 |
$template_args{ELEMENT} = 'editform_header'; |
1114 |
print Template(\%template_args); |
1115 |
|
1116 |
my $field; |
1117 |
my $n=0; |
1118 |
foreach $field (@fields_list) { |
1119 |
if($field eq "${table}_id") { next; } |
1120 |
|
1121 |
my $value = exists $values{$field} ? $values{$field} : ''; |
1122 |
# get default from DB |
1123 |
if(not defined $value or $value eq '') { |
1124 |
$value = DB_GetDefault($dbh,$table,$field); |
1125 |
} |
1126 |
|
1127 |
my $inputelem = GUI_WidgetWrite($s, "field_$field", $fields->{$field}{widget},$value); |
1128 |
|
1129 |
$template_args{ELEMENT} = 'editfield'; |
1130 |
$template_args{FIELD} = $field; |
1131 |
$template_args{LABEL} = $fields->{$field}{desc}; |
1132 |
$template_args{INPUT} = $inputelem, |
1133 |
$template_args{TWOCOL} = $n%2; |
1134 |
print Template(\%template_args); |
1135 |
$n++; |
1136 |
} |
1137 |
delete $template_args{FIELD}; |
1138 |
delete $template_args{LABEL}; |
1139 |
delete $template_args{INPUT}; |
1140 |
|
1141 |
# Fields |
1142 |
$template_args{ELEMENT} = 'editform_footer'; |
1143 |
print Template(\%template_args); |
1144 |
|
1145 |
# Buttons |
1146 |
$template_args{ELEMENT} = 'buttons'; |
1147 |
$template_args{CANCEL_URL} = $cancel_url; |
1148 |
print Template(\%template_args); |
1149 |
|
1150 |
UniqueFormEnd($s, $form_url, $next_url); |
1151 |
GUI_Footer(\%template_args); |
1152 |
} |
1153 |
|
1154 |
sub GUI_Pearl($) |
1155 |
{ |
1156 |
my $s = shift; |
1157 |
my $dbh = $s->{dbh}; |
1158 |
my $user = $s->{user}; |
1159 |
my $q = $s->{cgi}; |
1160 |
my $pearl = $q->url_param('pearl'); |
1161 |
|
1162 |
if(not exists $g{pearls}{$pearl}) { |
1163 |
die "Error: pearl named $pearl is known"; |
1164 |
} |
1165 |
my $p = $g{pearls}{$pearl}; |
1166 |
|
1167 |
my $title = ($p->info())[0]; |
1168 |
my %template_args = ( |
1169 |
USER => $user, |
1170 |
PAGE => 'edit', |
1171 |
TABLE => '', |
1172 |
TITLE => "Configure $title", |
1173 |
BUTTON_LABEL => 'Run Report', |
1174 |
); |
1175 |
|
1176 |
my $form_url = MakeURL(MyURL($q),{}); |
1177 |
my $cancel_url = MakeURL($form_url, { |
1178 |
action => 'entry'}); |
1179 |
|
1180 |
my $next_url = $form_url; |
1181 |
|
1182 |
GUI_InitTemplateArgs($s, \%template_args); |
1183 |
GUI_Header($s, \%template_args); |
1184 |
|
1185 |
# FORM |
1186 |
FormStart($s, $next_url); |
1187 |
print "<INPUT TYPE=\"hidden\" NAME=\"action\" VALUE=\"runpearl\">\n"; |
1188 |
print "<INPUT TYPE=\"hidden\" NAME=\"pearl\" VALUE=\"$pearl\">\n"; |
1189 |
# Fields |
1190 |
$template_args{ELEMENT} = 'editform_header'; |
1191 |
print Template(\%template_args); |
1192 |
|
1193 |
my $field; |
1194 |
my $n; |
1195 |
for (@{$g{pearls}{$pearl}->template()}){ |
1196 |
my ($field,$label,$widget,$value,$test) = @$_; |
1197 |
my $inputelem = GUI_WidgetWrite($s,"field_$field",$widget,$value); |
1198 |
|
1199 |
$template_args{ELEMENT} = 'editfield'; |
1200 |
$template_args{FIELD} = $field; |
1201 |
$template_args{LABEL} = $label; |
1202 |
$template_args{INPUT} = $inputelem, |
1203 |
$template_args{TWOCOL} = $n%2; |
1204 |
print Template(\%template_args); |
1205 |
$n++; |
1206 |
} |
1207 |
delete $template_args{FIELD}; |
1208 |
delete $template_args{LABEL}; |
1209 |
delete $template_args{INPUT}; |
1210 |
|
1211 |
# Fields |
1212 |
$template_args{ELEMENT} = 'editform_footer'; |
1213 |
print Template(\%template_args); |
1214 |
|
1215 |
# Buttons |
1216 |
$template_args{ELEMENT} = 'buttons'; |
1217 |
$template_args{CANCEL_URL} = $cancel_url; |
1218 |
print Template(\%template_args); |
1219 |
|
1220 |
FormEnd($s); |
1221 |
GUI_Footer(\%template_args); |
1222 |
} |
1223 |
|
1224 |
sub GUI_MakeCombo($$$$) |
1225 |
{ |
1226 |
my ($dbh, $combo_view, $name, $value) = @_; |
1227 |
|
1228 |
$value =~ s/^\s+//; |
1229 |
$value =~ s/\s+$//; |
1230 |
|
1231 |
my $str; |
1232 |
|
1233 |
my @combo; |
1234 |
if(not defined DB_GetCombo($dbh,$combo_view,\@combo)) { |
1235 |
return undef; |
1236 |
} |
1237 |
|
1238 |
$str = "<SELECT SIZE=\"1\" name=\"$name\">\n"; |
1239 |
# the empty option must not be empty! else the MORE ... disapears off screen |
1240 |
$str .= "<OPTION VALUE=\"\">Make your Choice ...</OPTION>\n"; |
1241 |
foreach(@combo) { |
1242 |
my $id = $_->[0]; |
1243 |
$id=~s/^\s+//; $id=~s/\s+$//; |
1244 |
#my $text = "$_->[0] -- $_->[1]"; |
1245 |
my $text = $_->[1]; |
1246 |
if($value eq $id) { |
1247 |
$str .= "<OPTION SELECTED VALUE=\"$id\">$text</OPTION>\n"; |
1248 |
} |
1249 |
else { |
1250 |
$str .= "<OPTION VALUE=\"$id\">$text</OPTION>\n"; |
1251 |
} |
1252 |
} |
1253 |
$str .= "</SELECT>\n"; |
1254 |
return $str; |
1255 |
} |
1256 |
|
1257 |
sub GUI_MakeISearch($$$$$$) |
1258 |
{ |
1259 |
my $ref_target = shift; |
1260 |
my $input_name = shift; |
1261 |
my $ticket = shift; |
1262 |
my $myurl = shift; |
1263 |
my $value = shift; |
1264 |
my $hidisearch = shift; |
1265 |
|
1266 |
$value =~ s/^\s+//; |
1267 |
$value =~ s/\s+$//; |
1268 |
|
1269 |
|
1270 |
my $targeturl = MakeURL($myurl,{action=>'dumptable',table=>$ref_target,ticket=>$ticket}); |
1271 |
|
1272 |
my $html; |
1273 |
$html .= "<input type=\"button\" onclick=\""; |
1274 |
$html .= "document.editform.$input_name.value=document.isearch_$input_name.getID('$value')"; |
1275 |
$html .= ";\" value=\"I-Search\"> "; |
1276 |
$html .= "<applet id=\"isearch_$input_name\" name=\"isearch_$input_name\""; |
1277 |
$html .= ' code="ISearch.class" width="70" height="20" archive="'.$g{conf}{isearch}.'">'."\n"; |
1278 |
$html .= GUI_AppletParam("url",$targeturl); |
1279 |
if($hidisearch){ |
1280 |
$html .= GUI_AppletParam("hid","true"); |
1281 |
} |
1282 |
$html .= "</applet>\n"; |
1283 |
|
1284 |
return $html |
1285 |
} |
1286 |
|
1287 |
sub GUI_AppletParam($$){ |
1288 |
my $name=shift; |
1289 |
my $value=shift; |
1290 |
return "<param name=\"$name\" value=\"$value\">\n"; |
1291 |
} |
1292 |
|
1293 |
|
1294 |
|
1295 |
sub GUI_WidgetWrite($$$$) |
1296 |
{ |
1297 |
my ($s, $input_name, $widget, $value) = @_; |
1298 |
|
1299 |
my $q = $s->{cgi}; |
1300 |
my $dbh = $s->{dbh}; |
1301 |
my $myurl = MyURL($q); |
1302 |
|
1303 |
if(not defined $value) { $value = ''; } |
1304 |
|
1305 |
my ($w, $warg) = DB_ParseWidget($widget); |
1306 |
|
1307 |
my $escval = $value; |
1308 |
$escval =~ s/\"/"/g; |
1309 |
|
1310 |
if($w eq 'readonly') { |
1311 |
return $value || ' '; |
1312 |
} |
1313 |
elsif($w eq 'text') { |
1314 |
my $size = defined $warg->{size} ? $warg->{size} : '20'; |
1315 |
return "<INPUT TYPE=\"text\" NAME=\"$input_name\" SIZE=\"$size\" VALUE=\"".$escval."\">"; |
1316 |
} |
1317 |
elsif($w eq 'area') { |
1318 |
my $rows = defined $warg->{rows} ? $warg->{rows} : '4'; |
1319 |
my $cols = defined $warg->{cols} ? $warg->{cols} : '60'; |
1320 |
return "<TEXTAREA NAME=\"$input_name\" ROWS=\"$rows\" COLS=\"$cols\" WRAP=\"virtual\">".$value."</TEXTAREA>"; |
1321 |
} |
1322 |
elsif($w eq 'varchar') { |
1323 |
my $size = defined $warg->{size} ? $warg->{size} : '20'; |
1324 |
my $maxlength = defined $warg->{maxlength} ? $warg->{maxlength} : '100'; |
1325 |
return "<INPUT TYPE=\"text\" NAME=\"$input_name\" SIZE=\"$size\" MAXLENGTH=\"$maxlength\" VALUE=\"$escval\">"; |
1326 |
} |
1327 |
elsif($w eq 'checkbox') { |
1328 |
return "<INPUT TYPE=\"checkbox\" NAME=\"$input_name\" VALUE=\"1\"".($value ? 'CHECKED' : '').">"; |
1329 |
} |
1330 |
elsif($w eq 'hid') { |
1331 |
return "<INPUT TYPE=\"text\" NAME=\"$input_name\" SIZE=\"10\" VALUE=\"".$escval."\">"; |
1332 |
} |
1333 |
elsif($w eq 'isearch' or $w eq 'hidisearch') { |
1334 |
my $out; |
1335 |
my $hidisearch; |
1336 |
$hidisearch = 0; |
1337 |
if($w eq 'hidisearch') { |
1338 |
# replace value with HID if 'hidcombo' |
1339 |
$value = DB_ID2HID($dbh,$warg->{'ref'},$value); |
1340 |
$hidisearch=1; |
1341 |
} |
1342 |
|
1343 |
my $combo = GUI_MakeISearch($warg->{'ref'}, $input_name, |
1344 |
$s->{ticket_value}, $myurl, $value, $hidisearch); |
1345 |
|
1346 |
$out.="<INPUT TYPE=\"text\" NAME=\"$input_name\" SIZE=10"; |
1347 |
$out .= " VALUE=\"$value\""; |
1348 |
$out .= ">\n$combo"; |
1349 |
return $out; |
1350 |
} |
1351 |
elsif($w eq 'idcombo' or $w eq 'hidcombo') { |
1352 |
my $out; |
1353 |
my $combo; |
1354 |
|
1355 |
if($g{conf}{gedafe_compat} eq '1.0') { |
1356 |
$value = DB_ID2HID($dbh,$warg->{'ref'},$value) if $w eq 'hidcombo'; |
1357 |
$combo = GUI_MakeCombo($dbh, $warg->{'combo'}, "${input_name}_combo", $value); |
1358 |
} |
1359 |
else { |
1360 |
$combo = GUI_MakeCombo($dbh, $warg->{'combo'}, "${input_name}_combo", $value); |
1361 |
$value = DB_ID2HID($dbh,$warg->{'ref'},$value) if $w eq 'hidcombo'; |
1362 |
} |
1363 |
$out .= "<INPUT TYPE=\"text\" NAME=\"$input_name\" SIZE=10"; |
1364 |
if($combo !~ /SELECTED/ and defined $value) { |
1365 |
$out .= " VALUE=\"$value\""; |
1366 |
} |
1367 |
$out .= ">\n$combo"; |
1368 |
return $out; |
1369 |
} |
1370 |
elsif($w eq 'combo') { |
1371 |
return GUI_MakeCombo($dbh, $warg->{'combo'}, "${input_name}_combo", $value); |
1372 |
} |
1373 |
elsif($w eq 'file'){ |
1374 |
my $filename = $value ne '' ? $value : "(none)"; |
1375 |
my $out = "Current file: <b>$filename</b>"; |
1376 |
if($value ne ''){ |
1377 |
$out .= "<br>Delete file?: <INPUT TYPE=\"checkbox\" NAME=\"file_delete_$input_name\">"; |
1378 |
} |
1379 |
$out .= "<br>Enter filename to update.<br><INPUT TYPE=\"file\" NAME=\"$input_name\">"; |
1380 |
return $out; |
1381 |
} |
1382 |
elsif($w eq 'date') { |
1383 |
return GUI_WidgetWrite_Date($input_name, $warg, $value); |
1384 |
} |
1385 |
|
1386 |
return "Unknown widget: $w"; |
1387 |
} |
1388 |
|
1389 |
sub GUI_WidgetWrite_Date($$$) |
1390 |
{ |
1391 |
my ($input_name, $warg, $value) = @_; |
1392 |
my ($value_y, $value_m, $value_d) = (0, 0, 0); |
1393 |
my $escval = $value; $escval =~ s/\"/"/g; |
1394 |
|
1395 |
my @months; |
1396 |
if($warg->{short}) { |
1397 |
@months = ("Jan", "Feb", "Mar", "Apr", "May", "Jun", |
1398 |
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"); |
1399 |
} |
1400 |
else { |
1401 |
@months = ("January", "February", "March", "April", |
1402 |
"May", "June", "July", "August", "September", |
1403 |
"October", "November", "December"); |
1404 |
} |
1405 |
|
1406 |
my $yearselect = "<option>(year)</option>\n"; |
1407 |
my $dayselect = "<option>(day)</option>\n"; |
1408 |
my $monthselect = "<option>(month)</option>\n"; |
1409 |
|
1410 |
if($value =~ /(\d+)-(\d+)-(\d+)/) { |
1411 |
($value_y, $value_m, $value_d) = ($1, $2, $3); |
1412 |
} |
1413 |
|
1414 |
for my $y (($warg->{from})..($warg->{to})) { |
1415 |
if ($y == $value_y) { |
1416 |
$yearselect .= "<option selected>$y</option>\n"; |
1417 |
} |
1418 |
else { |
1419 |
$yearselect .= "<option>$y</option>\n"; |
1420 |
} |
1421 |
} |
1422 |
for my $m (0..11) { |
1423 |
if ($m+1 == $value_m) { |
1424 |
$monthselect .= "<option selected>$months[$m]</option>\n"; |
1425 |
} |
1426 |
else { |
1427 |
$monthselect .= "<option>$months[$m]</option>\n"; |
1428 |
} |
1429 |
} |
1430 |
for my $d (1..31) { |
1431 |
if ($d == $value_d) { |
1432 |
$dayselect .= "<option selected>$d</option>\n"; |
1433 |
} |
1434 |
else { |
1435 |
$dayselect .= "<option>$d</option>\n"; |
1436 |
} |
1437 |
} |
1438 |
|
1439 |
my $yearinput = $input_name.'_1'; |
1440 |
my $monthinput = $input_name.'_2'; |
1441 |
my $dayinput = $input_name.'_3'; |
1442 |
my $functionname = $input_name.'_validate'; |
1443 |
my $out =<<end; |
1444 |
<SCRIPT LANGUAGE="JavaScript"> |
1445 |
<!-- |
1446 |
function $functionname(){ |
1447 |
var leap = 0; |
1448 |
|
1449 |
//get variables from form |
1450 |
var year = document.editform.$yearinput.selectedIndex; |
1451 |
var month = document.editform.$monthinput.selectedIndex; |
1452 |
var day = document.editform.$dayinput.selectedIndex; |
1453 |
|
1454 |
// if year month or date are on the (first) empty field |
1455 |
// then the date is invalid. Clear the field to reflect that. |
1456 |
if(year == 0 || month == 0 || day == 0){ |
1457 |
document.editform.$input_name.value = ""; |
1458 |
return; |
1459 |
} |
1460 |
|
1461 |
year = $warg->{from} + year - 1; |
1462 |
|
1463 |
// is this a leap year? |
1464 |
if ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))){ |
1465 |
leap = 1; |
1466 |
} |
1467 |
|
1468 |
|
1469 |
//update form to reflect corretions on date |
1470 |
if ((month == 2) && (leap == 1) && (day > 29)){ |
1471 |
document.editform.$dayinput.selectedIndex = 29; |
1472 |
day = 29; |
1473 |
} |
1474 |
|
1475 |
if ((month == 2) && (leap != 1) && (day > 28)){ |
1476 |
document.editform.$dayinput.selectedIndex = 28; |
1477 |
day = 28; |
1478 |
} |
1479 |
|
1480 |
if ((day > 30) && ((month == 4) || (month == 6) || (month == 9) || (month == 11))){ |
1481 |
document.editform.$dayinput.selectedIndex = 30; |
1482 |
day = 30; |
1483 |
} |
1484 |
|
1485 |
|
1486 |
var date = year + "-" + month + "-" + day; |
1487 |
|
1488 |
|
1489 |
document.editform.$input_name.value = date; |
1490 |
} |
1491 |
// --> |
1492 |
</script> |
1493 |
|
1494 |
<select NAME="$yearinput" onChange="$functionname()"> |
1495 |
$yearselect</select> |
1496 |
|
1497 |
<select NAME="$monthinput" onChange="$functionname()"> |
1498 |
$monthselect</select> |
1499 |
|
1500 |
<select NAME="$dayinput" onChange="$functionname()"> |
1501 |
$dayselect</select> |
1502 |
|
1503 |
<input TYPE="hidden" NAME="$input_name" VALUE="$escval"> |
1504 |
end |
1505 |
return $out; |
1506 |
} |
1507 |
|
1508 |
sub GUI_Delete($$$) |
1509 |
{ |
1510 |
my ($s, $user, $dbh) = @_; |
1511 |
my $q = $s->{cgi}; |
1512 |
my $table = $q->url_param('table'); |
1513 |
my $id = $q->url_param('id'); |
1514 |
my $next_url = MakeURL($s->{url}, { action=>'list', id=>'' }); |
1515 |
|
1516 |
my %template_args = ( |
1517 |
PAGE => 'delete', |
1518 |
USER => $user, |
1519 |
TITLE => "Delete Record", |
1520 |
TABLE => $table, |
1521 |
ID => $id, |
1522 |
NEXT_URL => $next_url, |
1523 |
); |
1524 |
|
1525 |
GUI_InitTemplateArgs($s, \%template_args); |
1526 |
GUI_Header($s, \%template_args); |
1527 |
UniqueFormStart($s, $next_url); |
1528 |
|
1529 |
$template_args{ELEMENT}='delete'; |
1530 |
print Template(\%template_args); |
1531 |
|
1532 |
print "<INPUT TYPE=\"hidden\" NAME=\"post_action\" VALUE=\"delete\">\n"; |
1533 |
print "<INPUT TYPE=\"hidden\" NAME=\"id\" VALUE=\"$id\">\n"; |
1534 |
UniqueFormEnd($s, $next_url, $next_url); |
1535 |
GUI_Footer(\%template_args); |
1536 |
} |
1537 |
|
1538 |
sub GUI_DumpTable($$$){ |
1539 |
my $s = shift; |
1540 |
my $q = $s->{cgi}; |
1541 |
my $user = shift; |
1542 |
my $dbh = shift; |
1543 |
my $myurl = MyURL($q); |
1544 |
my $table = $q->url_param('table'); |
1545 |
|
1546 |
my %atribs; |
1547 |
foreach($q->param) { |
1548 |
if(/^field_(.*)/) { |
1549 |
$atribs{$1} = $q->param($_); |
1550 |
} |
1551 |
} |
1552 |
my $data; |
1553 |
my $first = 1; |
1554 |
|
1555 |
my $view = defined $g{db_tables}{"${table}_list"} ? |
1556 |
"${table}_list" : $table; |
1557 |
|
1558 |
my @fields_list = @{$g{db_fields_list}{$view}}; |
1559 |
for (@fields_list){ |
1560 |
if(not $first){ |
1561 |
$data.="\t"; |
1562 |
} |
1563 |
$first = 0; |
1564 |
$data.=$_; |
1565 |
} |
1566 |
$data.="\n"; |
1567 |
|
1568 |
$data .= DB_DumpTable($dbh,$table,\%atribs); |
1569 |
print $data; |
1570 |
} |
1571 |
|
1572 |
1; |
1573 |
# Emacs Configuration |
1574 |
# |
1575 |
# Local Variables: |
1576 |
# mode: cperl |
1577 |
# eval: (cperl-set-style "BSD") |
1578 |
# cperl-indent-level: 8 |
1579 |
# mode: flyspell |
1580 |
# mode: flyspell-prog |
1581 |
# End: |
1582 |
# |
1583 |
|
1584 |
# vi: tw=0 sw=8 |