Subversion Repositories CoffeeCatalog

Rev

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