File:
[LON-CAPA] /
loncom /
interface /
lonpdfupload.pm
Revision
1.16:
download - view:
text,
annotated -
select for diffs
Thu Mar 18 14:50:15 2010 UTC (14 years, 4 months ago) by
raeburn
Branches:
MAIN
CVS tags:
HEAD
- Ensure user is in course context.
- use File::MMagic to confirm a PDF file was uploaded.
- Breadcrumb trail for two step process: 1. upload form, 2. processing result.
- Check permission from canuse_pdfforms course config.
- if none set, default to default for domain of course.
1: # The LearningOnline Network with CAPA
2: # PDF Form Upload Handler
3: #
4: # $Id: lonpdfupload.pm,v 1.16 2010/03/18 14:50:15 raeburn Exp $
5: #
6: # Copyright Michigan State University Board of Trustees
7: #
8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
9: #
10: # LON-CAPA is free software; you can redistribute it and/or modify
11: # it under the terms of the GNU General Public License as published by
12: # the Free Software Foundation; either version 2 of the License, or
13: # (at your option) any later version.
14: #
15: # LON-CAPA is distributed in the hope that it will be useful,
16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18: # GNU General Public License for more details.
19: #
20: # You should have received a copy of the GNU General Public License
21: # along with LON-CAPA; if not, write to the Free Software
22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23: #
24: # /home/httpd/html/adm/gpl.txt
25: #
26: # http://www.lon-capa.org/
27: #
28: package Apache::lonpdfupload;
29:
30: use lib '/home/httpd/lib/perl';
31: use Apache::Constants qw(:common :http);
32: use Apache::lonnet;
33: use Apache::lonhtmlcommon();
34: use Apache::loncommon();
35: use Apache::lonlocal;
36: use File::MMagic;
37: use CAM::PDF;
38:
39: use strict;
40:
41: sub handler() {
42: my $r = shift;
43: &Apache::loncommon::content_type($r,'text/html');
44: $r->send_http_header;
45: return OK if $r->header_only;
46:
47: # Needs to be in a course
48: if (!$env{'request.course.fn'}) {
49: # Not in a course
50: $env{'user.error.msg'}="/adm/pdfupload:bre:0:0:Cannot upload PDF forms unless in a course";
51: return HTTP_NOT_ACCEPTABLE;
52: }
53:
54: # Breadcrumbs
55: my $brcrum = [{'href' => '/adm/pdfupload',
56: 'text' => 'Upload PDF Form'}];
57: if ($env{'form.Uploaded'} && $env{'form.file'}) {
58: push(@{$brcrum},{'href' => '',
59: 'text' => 'PDF upload result'});
60: }
61:
62: $r->print(&Apache::loncommon::start_page('Upload PDF Form',
63: undef,
64: {'bread_crumbs' => $brcrum,})
65: );
66:
67: if ($env{'request.course.id'}) {
68: my $permission = $env{'course.'.$env{'request.course.id'}.'.canuse_pdfforms'};
69: if ($permission eq '') {
70: my %domdefs = &Apache::lonnet::get_domain_defaults($env{'course.'.$env{'request.course.id'}.'.domain'});
71: $permission = $domdefs{'canuse_pdfforms'};
72: }
73: unless ($permission) {
74: $r->print('<p class="LC_warning">'.
75: &mt('Upload of PDF forms is not permitted for this course.').
76: '</p>'.
77: &Apache::loncommon::end_page());
78: return OK;
79: }
80: } else {
81: $r->print('<p class="LC_warning">'.
82: &mt('Could not determine identity of this course. you may need to [_1]re-select[_2] the course.','<a href="/adm/roles">','</a>').
83: '</p>'.
84: &Apache::loncommon::end_page());
85: return OK;
86: }
87:
88: # if a file was upload
89: if($env{'form.Uploaded'} && $env{'form.file'}) {
90: my $mm = new File::MMagic;
91: my $mime_type = $mm->checktype_contents($env{'form.file'});
92: if ($mime_type eq 'application/pdf') {
93: $r->print(&processPDF);
94: } else {
95: $r->print('<p class="LC_error">'
96: .&mt("The uploaded file does not appear to be a PDF file.")
97: .'</p>');
98: }
99: } else {
100: # print upload form
101: $r->print(&get_javascripts);
102: $r->print(&get_uploadform);
103: }
104:
105: #link to course-content
106: $r->print('<hr />'
107: .'<p>'."\n"
108: .'<a href="/adm/navmaps">'."\n"
109: .&mt('Course Contents')."\n"
110: .'</a>'."\n"
111: .'</p>'."\n"
112: );
113:
114: #&dumpenv($r); #debug -> prints the environment
115: $r->print(&Apache::loncommon::end_page());
116: return OK;
117: }
118:
119: sub get_javascripts() {
120:
121: my $message = &mt('Please choose a PDF-File.');
122:
123: # simple test if the upload ends with ".pdf"
124: # it's only for giving a message to the user
125: my $result .= <<END
126: <script type="text/javascript">
127: function checkFilename(form) {
128: var fileExt = form.file.value;
129: fileExt = fileExt.match(/[.]pdf\$/g);
130: if(fileExt) {
131: return true;
132: }
133: alert("$message");
134: return false;
135: }
136: </script>
137: END
138: ;
139: return $result;
140: }
141:
142:
143: sub get_uploadform() {
144:
145: my %lt = &Apache::lonlocal::texthash(
146: 'title' => 'Upload a PDF Form with filled Form Fields',
147: 'chFile' => 'File',
148: 'submit' => 'Upload',
149: );
150:
151: my $result =
152: '<br />'
153: .'<form method="post" enctype="multipart/form-data" onsubmit="return checkFilename(this);">'
154: .'<input type="hidden" name="type" value="upload" />'
155: .&Apache::lonhtmlcommon::start_pick_box()
156: .&Apache::lonhtmlcommon::row_headline()
157: .'<h2>'.$lt{'title'}.'</h2>'
158: .&Apache::lonhtmlcommon::row_closure()
159: .&Apache::lonhtmlcommon::row_title($lt{'chFile'})
160: .'<input type="file" name="file" id="filename" />'
161: .&Apache::lonhtmlcommon::row_closure(1)
162: .&Apache::lonhtmlcommon::end_pick_box()
163: .'<p>'
164: .'<input type="submit" name="Uploaded" value="'.$lt{'submit'}.'" />'
165: .'</p>'
166: .'</form>'
167: .'<br />';
168:
169: return $result;
170: }
171:
172: sub processPDF {
173: my $result = (); # message for Browser
174: my @pdfdata = (); # answers from PDF-Forms
175:
176: @pdfdata = &get_pdf_data(); # get answers from PDF-Form
177:
178: if (scalar @pdfdata) {
179: &grade_pdf(@pdfdata);
180: } else {
181: $result .= '<p class="LC_error">'
182: .&mt("Can't find any valid PDF formfields.")
183: .'</p>';
184: }
185: }
186:
187: sub get_pdf_data() {
188: my @data = ();
189: my $pdf = CAM::PDF->new($env{'form.file'});
190:
191: my @formFields = $pdf->getFormFieldList(); #get names of formfields
192:
193: foreach my $field (@formFields) {
194: my $dict = $pdf->getFormFieldDict($pdf->getFormField($field)); # get formfield dictonary
195:
196: #
197: # this is necessary because CAM::PDF has a problem with formfieldnames which include a
198: # dot in fieldnames. So a fieldname like "i.am.aFormfield" will offer three fieldnames "i", "i.am"
199: # and "i.am.aFormfield". The fragmentary names keep no values and will be ignored.
200: if($dict->{'V'}) {
201: push(@data, $field."?". $dict->{'V'}{'value'}); #binding fieldname with value
202: }
203: }
204: return @data;
205: }
206:
207: sub grade_pdf {
208: my $result = ();
209: my @pdfdata = @_;
210:
211: my $meta = ();
212: my %grades = ();
213: my %problems = ();
214:
215: my $debug = ();
216:
217: $debug .= "Found: ". scalar @pdfdata." Entries \n";
218:
219: foreach my $entry (sort(@pdfdata)) {
220: if ($entry =~ /^meta.*/) {
221: $debug .= 'found: metadata -> '.$entry . "<br />";
222: my ($label, $value) = ($entry =~ /^([^?]*)\?(.*)/);
223: my ($domain, $user) = split('&', $value);
224: $user =~ s/(.*)\n/$1/; #TODO is that equals to chomp?
225:
226: if($user ne $env{'user.name'} or $domain ne $env{'user.domain'}) {
227: return '<p class="LC_error">'
228: .&mt('Wrong username ([_1]) found in PDF file. Expected username: [_2]'
229: ,$user.':'.$domain
230: ,$env{'user.domain'}.':'.$env{'user.name'})
231: .'</p>';
232: }
233:
234: } elsif($entry =~ /^upload.*/) {
235: $debug .= 'found: a problem -> '.$entry;
236: my ($label, $value) = ($entry =~ /^([^?]*)\?(.*)/);
237: my ($symb, $part, $type, $HWVAL) = split('&', $label);
238: my ($map,$id,$resource)=&Apache::lonnet::decode_symb($symb);
239: $value =~ s/(.*)\n/$1/;
240:
241: #filter incorrect radiobuttons (Bug in CABAReT Stage)
242: if($type eq 'radiobuttonresponse' && $value eq 'Off' ) {
243: next;
244: }
245:
246: my $submit = $part;
247: $submit =~ s/part_(.*)/submit_$1/;
248: if($problems{$symb.$part}) {
249: $problems{$symb.$part}{$HWVAL} = $value;
250: } else {
251: $problems{$symb.$part} = { 'resource' => $resource,
252: 'symb' => $symb,
253: 'submitted' => $part,
254: $submit => 'Answer',
255: $HWVAL => $value};
256: }
257: } else {
258: $debug .= 'found: -> '.$entry;
259: next;
260: }
261: }
262: #$result .= $debug;
263:
264: $result .= '<h2>'.&mt('Results of PDF Form problems').'</h2>';
265: $result .= &Apache::loncommon::start_data_table()
266: .&Apache::loncommon::start_data_table_header_row()
267: .'<th>'.&mt('Problem Name').'</th>'
268: .'<th>'.&mt('Grading').'</th>'
269: .&Apache::loncommon::start_data_table_header_row()
270: .&Apache::loncommon::end_data_table_header_row();
271:
272: foreach my $key (sort (keys %problems)) {
273: my %problem = %{$problems{$key}};
274: my ($problemname, $grade) = &grade_problem(%problem);
275:
276: $result .= &Apache::loncommon::start_data_table_row();
277: $result .= "<td>$problemname</td><td class='";
278: if($grade eq "EXACT_ANS" || $grade eq "APPROX_ANS") {
279: $result .= "LC_answer_correct";
280: } else {
281: $result .= "LC_answer_charged_try";
282: }
283: $grade = &parse_grade_answer($grade);
284: $result .= "'>$grade</span></td>";
285: $result .= &Apache::loncommon::end_data_table_row();
286: }
287: $result .= &Apache::loncommon::end_data_table();
288:
289:
290: return $result;
291: }
292:
293: sub grade_problem {
294: my %problem = @_;
295: my ($title, $part) = ();
296:
297: &Apache::loncommon::ssi_with_retries('/res/'.$problem{'resource'}, 5, %problem);
298:
299: $title = &Apache::lonnet::gettitle($problem{'symb'});
300: $part = $problem{submitted};
301: $part =~ s/part_(.*)/$1/;
302: unless($part eq '0') {
303: #add information about part number
304: $title .= " - Part $part";
305: }
306:
307: my %problemhash = &Apache::lonnet::restore($problem{'symb'});
308: my $grade = $problemhash{"resource.$part.award"};
309:
310: return ($title, $grade);
311: }
312:
313: sub parse_grade_answer {
314: my ($shortcut) = @_;
315: my %answerhash = ('EXACT_ANS' => &mt('You are correct.'),
316: 'APPROX_ANS' => &mt('You are correct.'),
317: 'INCORRECT' => &mt('You are incorrect'),
318: );
319:
320: foreach my $key (keys %answerhash) {
321: if($shortcut eq $key) {
322: return $answerhash{$shortcut};
323: }
324: }
325: return &mt('See course contents for further information.');
326:
327: }
328:
329:
330: sub dumpenv {
331: my $r = shift;
332:
333: $r->print ("<br />-------------------<br />");
334: foreach my $key (sort (keys %env)) {
335: $r->print ("<br />$key -> $env{$key}");
336: }
337: $r->print ("<br />-------------------<br />");
338: $r->print ("<br />-------------------<br />");
339: foreach my $key (sort (keys %ENV)) {
340: $r->print ("<br />$key -> $ENV{$key}");
341: }
342: $r->print ("<br />-------------------<br />");
343:
344: }
345:
346: 1;
347: __END__
348:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>