Subversion Repositories CoffeeCatalog

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
9 - 1
#!/usr/bin/perl -w
2
 
3
use strict;
4
use WebDB;
5
use HTML::Tiny;
6
use CGI qw/param header start_html url uploadInfo/;
7
my $h = HTML::Tiny->new( mode => 'html' );
8
 
9
my %F;
10
 
11
 
12
my $pageTitle = "CC Manage Coffee";
13
my $homeURL = "/";
14
my $DBTable = "coffees";
15
my %FIELDS = (
16
	roaster   =>  [qw(Roaster     1		select			required)],
17
	coffee    =>  [qw(Coffee      2   text      required)],
18
	region    =>  [qw(Region      3		text			required)],
19
	source    =>  [qw(Source      4		select			required)],
20
	rating    =>  [qw(Rating      5		select)],
21
	note      =>  [qw(Notes       6		textarea)],
22
	logo      =>  [qw(Image       7		image)]
23
);
24
 
25
my %fieldDisplayName = map  { $_ => $FIELDS{$_}->[0]   } keys %FIELDS;
26
my %fieldType        = map  { $_ => $FIELDS{$_}->[2]   } keys %FIELDS;
27
my @requiredFields   = sort fieldOrder grep { defined $FIELDS{$_}->[3] } keys %FIELDS;
28
my @DBFields   = sort fieldOrder grep { $fieldType{$_} =~ /^(text|select)/ } keys %FIELDS;
29
my $primary = $DBFields[0];
30
my $secondary = $DBFields[1];
31
 
32
sub fieldOrder {
33
	$FIELDS{$a}->[1] <=> $FIELDS{$b}->[1];
34
}
35
 
36
sub saveForm {
37
  my $FTS = shift;
38
 
39
  my $dbh = WebDB::connect ();
40
  if ($FTS->{logo}) {
41
    use Image::Magick;
42
    my $mime_type = uploadInfo ($FTS->{logo})->{'Content-Type'};
43
    my ($full, $thumb) = read_image_file ($FTS->{logo});
44
    $dbh->do (
45
  		  "INSERT INTO $DBTable
46
		    (roaster,coffee,logo,thumbnail,region,note,mime_type,source,rating,date_added)
47
		    VALUES(?,?,?,?,?,?,?,?,?,now())
48
		    ON DUPLICATE KEY UPDATE logo = ?, thumbnail = ?, region = ?, note = ?, mime_type = ?, source = ?, rating = ?",
49
			   undef,
50
			   $FTS->{roaster}, $FTS->{coffee}, $full, $thumb, $FTS->{region}, $FTS->{note}, $mime_type, $FTS->{source}, $FTS->{rating},
51
			                                    $full, $thumb, $FTS->{region}, $FTS->{note}, $mime_type, $FTS->{source}, $FTS->{rating});
52
  } else {
53
    $dbh->do (
54
  		  "INSERT INTO $DBTable
55
		    (roaster,coffee,region,note,source,rating,date_added)
56
		    VALUES(?,?,?,?,?,?,now())
57
		    ON DUPLICATE KEY UPDATE region = ?, note = ?, source = ?, rating = ?",
58
			   undef,
59
			   $FTS->{roaster}, $FTS->{coffee}, $FTS->{region}, $FTS->{note}, $FTS->{source}, $FTS->{rating},
60
			                                    $FTS->{region}, $FTS->{note}, $FTS->{source}, $FTS->{rating});
61
  }
62
	$dbh->disconnect ();	 # Image was stored into database successfully.
63
}
64
 
65
sub select_rating {
66
  my $value = shift // "";
67
 
68
  return $h->select ({ name=>"rating" },
69
    [ map { $value eq $_ ?
70
              $h->option ({ value=>$_, selected=>[] }, $_) :
71
              $h->option ({ value=>$_ }, $_)
72
          } "", 1..5]);
73
}
74
 
75
sub select_source {
76
  my $value = shift // "";
77
 
78
  return $h->select ({ name=>"source" },
79
    [ map { $value eq $_ ?
80
              $h->option ({ value=>$_, selected=>[] }, $_) :
81
              $h->option ({ value=>$_ }, $_)
82
          } "", qw(Direct Fellow Retail SCG) ]);
83
}
84
 
85
sub select_roaster {
86
  my $value = shift // "";
87
  my @roasters;
88
 
89
  my $dbh = WebDB::connect ();
90
  @roasters = map { @$_ } $dbh->selectall_array ("select distinct roaster from roasters order by roaster");
91
  $dbh->disconnect ();
92
 
93
  return $h->select ({ name=>"roaster" },
94
    [ map { $value eq $_ ?
95
              $h->option ({ value=>$_, selected=>[] }, $_) :
96
              $h->option ({ value=>$_ }, $_)
97
          } "", @roasters ]);
98
}
99
 
100
 
101
 
102
print header (),
103
			start_html (-title => "Add / Update Coffee", -style => {'src' => "style.css"} );
104
 
105
print $h->div ({ class => "accent pageheader" }, [
106
  $h->h1 ($pageTitle),
107
  $h->div ({ class=>"sp0" }, [
108
    $h->div ({ class=>"spLeft" }, [
109
    ]),
110
    $h->div ({ class=>"spRight" }, [
111
      $h->input ({ type=>"button", value=>"Home", onClick=>"window.location.href='$homeURL'" }),
112
    ]),
113
  ]),
114
]);
115
 
116
my $choice = param ("choice") // "";
117
if ($choice eq "Save") {
118
	process_form ();
119
} elsif (defined (param ($primary)) and defined (param ($secondary))) {
120
  my $thing = param ($primary);
121
  my $thing2 = param ($secondary);
122
	display_form ({ $primary => $thing, $secondary => $thing2 }, $choice);
123
} else {
124
	display_form (); # blank form
125
}
126
 
127
print $h->close ("html");
128
 
129
sub display_form  {
130
  my $R = shift;
131
  my $view = shift // "";
132
	my $actionbutton;
133
 
134
	my ($I, $logo);
135
 
136
  if ($R) {
137
    # we're dealing with an existing thing.  Get the current values out of the DB...
138
    my $dbh = WebDB::connect ();
139
 
140
	  @F{@DBFields} = $dbh->selectrow_array (
141
                     "SELECT ". join (", ", @DBFields) ." FROM $DBTable WHERE $primary = ? and $secondary = ?",
142
                      undef, $R->{$primary}, $R->{$secondary});
143
	  $dbh->disconnect ();
144
 
145
	  my $i = "serve_image.pl" . sprintf ("?roaster=%s", $h->url_encode ($R->{$primary}));
146
	  $i .= sprintf ("&coffee=%s", $h->url_encode ($R->{$secondary}));
147
	  $I = $h->img ({ src => "$i;thumbnail=1", class=>"show", alt => $R->{$primary} });
148
	  $I .= $h->img ({ src => "$i", class=>"hide", alt => $R->{$primary} });
149
 
150
	  # did we find a record?
151
	  error ("Cannot find a database entry for '$R->{$primary}'") unless defined $F{$DBFields[0]};
152
 
153
    if ($view eq "Update") {
154
      # We'd like the update that thing, give the user a form...
155
      print $h->p ("Updating Coffee: $R->{$primary}, $R->{$secondary}...");
156
 
157
      foreach (@DBFields) {
158
        $F{$_} = formField ($_, $F{$_});
159
      }
160
      $logo = $I.'&nbsp;&nbsp;&nbsp;' . formField ('logo');
161
 
162
      $actionbutton = formField ("choice", "Save");
163
      $actionbutton .= formField ("Cancel");
164
    } else {
165
      # We're just looking at it...
166
      print $h->p ("Viewing Coffee: $R->{$primary}, $R->{$secondary}...");
167
      $logo = $I;
168
      $F{$DBFields[0]} .= $h->input ({ type=>"hidden", name=>$DBFields[0], value=> $F{$DBFields[0]} });
169
      $F{$DBFields[0]} .= $h->input ({ type=>"hidden", name=>$DBFields[1], value=> $F{$DBFields[1]} });
170
      $actionbutton = formField ("choice", "Update");
171
      if ($view eq "POSTSAVE") {
172
        $actionbutton .= formField ("Cancel", "Back", "POSTSAVE");
173
      } else {
174
        $actionbutton .= formField ("Cancel", "Back");
175
      }
176
    }
177
  } else {
178
    print $h->p ("Adding a new Coffee...");
179
 
180
    foreach (@DBFields) {
181
      $F{$_} = formField ($_);
182
    }
183
    $logo = formField ('logo');
184
 
185
    $actionbutton = formField ("choice", "Save");
186
    $actionbutton .= formField ("Cancel");
187
  }
188
 
189
 
190
	print $h->open ("form", { action => url (), method=>"POST", enctype=>"multipart/form-data" });
191
	print $h->div ({ class=>"sp0" },
192
	  $h->div ({ class=>"rTable" }, [ map ({
193
      $h->div ({ class=>"rTableRow" }, [
194
        $h->div ({ class=>"rTableCell right top" }, "$fieldDisplayName{$_}: "),
195
        $h->div ({ class=>"rTableCell" }, $F{$_})
196
      ])
197
      } @DBFields),
198
      $h->div ({ class=>"rTableRow" }, [
199
        $h->div ({ class=>"rTableCell right top" }, "$fieldDisplayName{'logo'}: "),
200
        $h->div ({ class=>"rTableCell" }, $logo)
201
      ]),
202
    ])
203
  );
204
 
205
  print $actionbutton;
206
  print $h->close ("form");
207
 
208
  printJavascript ();
209
 
210
}
211
 
212
sub process_form  {
213
  my %FORM;
214
  foreach (keys %FIELDS) {
215
  	if ($fieldType{$_} =~ /^text/) {
216
  		$FORM{$_} = WebDB::trim param ($_) // "";
217
  	} else {
218
	  	$FORM{$_} = param ($_) // "";
219
  	}
220
  }
221
 
222
  	 # check for required fields
223
	my @errors = ();
224
	foreach (@requiredFields) {
225
		push @errors, "$fieldDisplayName{$_} is missing." if $FORM{$_} eq "";
226
	}
227
 
228
  if (@errors)	 {
229
    print $h->div ({ class=>"error" }, [
230
  	  $h->p ("The following errors occurred:"),
231
  	  $h->ul ($h->li (@errors)),
232
  	  $h->p ("Please click your Browser's Back button to\n"
233
  	  	   . "return to the previous page and correct the problem.")
234
  	]);
235
  	return;
236
  }	 # Form was okay;  get image type and contents and create new record.
237
 
238
  saveForm (\%FORM);
239
 
240
	print $h->p ({ class=>"success" }, "Coffee successfully saved.");
241
 
242
  display_form ({ roaster=>$FORM{roaster}, coffee=>$FORM{coffee} }, "POSTSAVE");
243
}
244
 
245
sub read_image_file {
246
	my $fh = shift;             # filename/file handle
247
	my $img = new Image::Magick;
248
	my $fullimg = new Image::Magick;
249
	my ($full, $thumb);
250
	my $err;
251
	# read full-size image directly from upload file
252
	(read ($fh, $full, (stat ($fh))[7]) == (stat ($fh))[7])
253
          or error ("Can't read image file: $!");
254
	# produce thumbnail from full-size image
255
	$err = $img->BlobToImage ($full);
256
	error ("Can't convert image data: $err") if $err;
257
	$err = $fullimg->BlobToImage ($full);
258
	error ("Can't convert image data: $err") if $err;
259
 
260
 	if ($fullimg->Get("width") > 300 or $fullimg->Get("height") > 300) {
261
	  $err = $fullimg->Scale (geometry => "300x300");
262
	  error ("Can't scale image file: $err") if $err;
263
 	}
264
 
265
	$err = $img->Scale (geometry => "64x64");
266
	error ("Can't scale image file: $err") if $err;
267
	$thumb = $img->ImageToBlob ();
268
	$full  = $fullimg->ImageToBlob ();
269
	return ($full, $thumb);
270
}
271
 
272
sub error {
273
	my $msg = shift;
274
	print $h->p ({ class=>"error" }, "Error: $msg");
275
  print $h->close("html");
276
	exit (0);
277
}
278
 
279
sub formField {
280
	my $name  = shift;
281
	my $value = shift // '';
282
	my $context = shift // '';
283
	my $type = $fieldType{$name} // "button";
284
 
285
	if ($type eq "image") {
286
		return $h->input ({
287
        name => $name,
288
        class => "inputfile",
289
        type => "file",
290
        id   => "file",
291
        size => 60
292
      }) . $h->label ({ for=>"file", class=>"top" }, $h->span ("Choose File..."));
293
 
294
	} elsif ($type eq "button") {
295
		if ($name eq "Cancel") {
296
		  if ($context eq "POSTSAVE") {
297
		    return $h->input ({ type=>"button", value => $value ne '' ? $value : "Cancel" , onClick=>"window.location.href = \"coffees.pl\"; return false;" })
298
		  } else {
299
		    return $h->input ({ type=>"button", value => $value ne '' ? $value : "Cancel" , onClick=>"history.back(); return false;" })
300
		  }
301
		} else {
302
			return $h->input ({ type=>"submit", value => $value, name=>$name })
303
		}
304
 
305
	} elsif ($type eq "textarea") {
306
	  return $h->tag ("textarea", {
307
	    name => $name,
308
	    override => 1,
309
			cols => 30,
310
			rows => 4
311
	  }, $value);
312
 
313
  } elsif ($type eq "select") {
314
    no strict;
315
    return &{"select_".$name} ($value);
316
	}	else {
317
	  return $h->input ({
318
	    name => $name,
319
	    type => $type,
320
	    value => $value,
321
	    required => [],
322
	    override => 1,
323
	    size => 30
324
	  });
325
	}
326
}
327
 
328
sub printJavascript {
329
    print<<JSCRIPT;
330
 
331
 
332
<SCRIPT language="JavaScript">
333
<!--
334
 
335
var inputs = document.querySelectorAll( '.inputfile' );
336
Array.prototype.forEach.call( inputs, function( input )
337
{
338
	var label	 = input.nextElementSibling,
339
		labelVal = label.innerHTML;
340
 
341
	input.addEventListener( 'change', function( e )
342
	{
343
		var fileName = e.target.value.split( '\\\\' ).pop();
344
		if( fileName )
345
			label.querySelector( 'span' ).innerHTML = fileName;
346
		else
347
			label.innerHTML = labelVal;
348
	});
349
});
350
 
351
//-->
352
</SCRIPT>
353
 
354
 
355
JSCRIPT
356
 
357
}
358