![]() ![]() | ![]() |
- apparently it is resizable not resizeable
1: # 2: # $Id: lonspreadsheet.pm,v 1.29 2003/11/11 20:27:15 albertel Exp $ 3: # 4: # Copyright Michigan State University Board of Trustees 5: # 6: # This file is part of the LearningOnline Network with CAPA (LON-CAPA). 7: # 8: # LON-CAPA is free software; you can redistribute it and/or modify 9: # it under the terms of the GNU General Public License as published by 10: # the Free Software Foundation; either version 2 of the License, or 11: # (at your option) any later version. 12: # 13: # LON-CAPA is distributed in the hope that it will be useful, 14: # but WITHOUT ANY WARRANTY; without even the implied warranty of 15: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16: # GNU General Public License for more details. 17: # 18: # You should have received a copy of the GNU General Public License 19: # along with LON-CAPA; if not, write to the Free Software 20: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21: # 22: # /home/httpd/html/adm/gpl.txt 23: # 24: # http://www.lon-capa.org/ 25: # 26: # The LearningOnline Network with CAPA 27: # Spreadsheet/Grades Display Handler 28: # 29: # POD required stuff: 30: 31: =head1 NAME 32: 33: lonspreadsheet 34: 35: =head1 SYNOPSIS 36: 37: Spreadsheet interface to internal LON-CAPA data 38: 39: =head1 DESCRIPTION 40: 41: Lonspreadsheet provides course coordinators the ability to manage their 42: students grades online. The students are able to view their own grades, but 43: not the grades of their peers. The spreadsheet is highly customizable, 44: offering the ability to use Perl code to manipulate data, as well as many 45: built-in functions. 46: 47: =head2 Functions available to user of lonspreadsheet 48: 49: =over 4 50: 51: =cut 52: 53: 54: package Apache::lonspreadsheet; 55: 56: use strict; 57: use warnings FATAL=>'all'; 58: no warnings 'uninitialized'; 59: use Apache::classcalc(); 60: use Apache::studentcalc(); 61: use Apache::assesscalc(); 62: use Apache::Constants qw(:common :http); 63: use Apache::lonnet; 64: use Apache::lonhtmlcommon; 65: use Apache::lonlocal; 66: use Apache::loncoursedata(); 67: use HTML::Entities(); 68: 69: ## 70: ## HTML utility subroutines really should go in lonhtmlcommon 71: ## 72: 73: sub textfield { 74: my ($title,$name,$value)=@_; 75: return "\n<p><b>$title:</b><br />". 76: '<input type=text name="'.$name.'" size=80 value="'.$value.'" />'; 77: } 78: 79: sub hiddenfield { 80: my ($name,$value)=@_; 81: return '<input type=hidden name="'.$name.'" value="'.$value.'" />'."\n"; 82: } 83: 84: sub selectbox { 85: my ($title,$name,$value,%options)=@_; 86: my $selout="\n<p><b>$title:</b><br />".'<select name="'.$name.'">'; 87: foreach (sort keys(%options)) { 88: $selout.='<option value="'.$_.'"'; 89: if ($_ eq $value) { $selout.=' selected'; } 90: $selout.='>'.&mt($options{$_}).'</option>'; 91: } 92: return $selout.'</select>'; 93: } 94: 95: sub file_dialogs { 96: my ($spreadsheet) = @_; 97: my $bgcolor = "#FFFFFF"; 98: my $sheettype = $spreadsheet->{'type'}; 99: my $result = ''; 100: my $message = ''; 101: ## 102: ## Deal with saving the spreadsheet 103: if ((exists($ENV{'form.save'}) || exists($ENV{'form.makedefault'})) && 104: exists($ENV{'form.savefilename'})) { 105: $spreadsheet->filename($ENV{'form.savefilename'}); 106: my $save_status = $spreadsheet->save(); 107: if ($save_status ne 'ok') { 108: $message .= "An error occurred while saving the spreadsheet". 109: "There error is:".$save_status; 110: } else { 111: $message .= "Spreadsheet saved as ".$spreadsheet->filename(); 112: } 113: } elsif (exists($ENV{'form.newformula'}) && 114: exists($ENV{'form.cell'}) && 115: $ENV{'form.cell'} ne '' ) { 116: ## 117: ## Make any requested modifications to the spreadsheet 118: $spreadsheet->modify_cell($ENV{'form.cell'}, 119: $ENV{'form.newformula'}); 120: $spreadsheet->save_tmp(); 121: # output that we are dealing with a temporary file 122: $result .=&hiddenfield('workcopy',$sheettype); 123: if ($ENV{'form.newformula'} !~ /^\s*$/) { 124: $message .='<table><tr>'. 125: '<td valign="top"><pre>'.&mt('Cell').' '.$ENV{'form.cell'}.' = </pre></td>'. 126: '<td><pre>'.$ENV{'form.newformula'}."</pre></td></tr></table>\n"; 127: } else { 128: $message .= &mt('Deleted contents of cell').' '.$ENV{'form.cell'}.'.'; 129: } 130: } 131: ## 132: ## Editing code 133: $result .=&hiddenfield('cell',''). 134: &hiddenfield('newformula',''); 135: ## 136: ## Create the save and load dialogs 137: my $filename = $spreadsheet->filename(); 138: my $truefilename = $filename; 139: if ($spreadsheet->is_default()) { 140: $filename = 'Default'; 141: } 142: my $save_dialog = '<nobr>'. 143: '<input type="submit" name="save" value="'.&mt('Save as').'" /> '. 144: '<input type="text" name="savefilename" size="30" value="'. 145: $truefilename.'" />'. 146: '</nobr>'; 147: my $makedefault_dialog = '<input type="submit" name="makedefault" '. 148: 'value="'.&mt('Save as & Make This Sheet the Default').'"/>'; 149: # 150: my $link = '<a href="javascript:openbrowser'. 151: "('sheet','loadfilename','spreadsheet')\">".&mt('Select Spreadsheet File')."</a>"; 152: my $load=&mt('Load:'); 153: my $load_dialog = <<END; 154: <table bgcolor="$bgcolor"> 155: <tr><td><input type="submit" name="load" value="$load" /></td> 156: <td><nobr> 157: <input type="text" name="loadfilename" size="20" value="$filename" /> 158: $link</nobr> 159: </td></tr> 160: <tr><td> </td><td> 161: <select name="fileselect" onchange="document.sheet.loadfilename.value=document.sheet.fileselect.value" > 162: END 163: my $default_filename_set = 0; 164: foreach my $sheetfilename ($spreadsheet->othersheets()) { 165: $load_dialog .= ' <option value="'.$sheetfilename.'"'; 166: if ($filename eq $sheetfilename) { 167: $load_dialog .= ' selected'; 168: $default_filename_set = 1; 169: } 170: $load_dialog .= '>'.$sheetfilename."</option>\n"; 171: } 172: $load_dialog .= "</select>\n</td><td> </td></tr>\n</table>\n"; 173: # 174: $result .=<<END; 175: <!-- 176: <fieldset title="File Dialogs" > 177: <legend>File Dialogs</legend> 178: --> 179: <!-- load / save dialogs --> 180: <table cellspacing="2"> 181: <tr> 182: <td>$load_dialog</td> 183: <td> 184: <table bgcolor="$bgcolor"> 185: <tr><td>$save_dialog</td></tr> 186: <tr><td align="center">$makedefault_dialog</td></tr> 187: </table> 188: </td> 189: </tr> 190: </table> 191: <!-- 192: </fieldset> 193: --> 194: END 195: return ($result,$message); 196: } 197: 198: sub handler { 199: my $r=shift; 200: # 201: # Overload checking 202: # 203: # Check this server 204: my $loaderror=&Apache::lonnet::overloaderror($r); 205: if ($loaderror) { return $loaderror; } 206: # Check the course homeserver 207: $loaderror= &Apache::lonnet::overloaderror($r, 208: $ENV{'course.'.$ENV{'request.course.id'}.'.home'}); 209: # if ($loaderror) { return $loaderror; } 210: # 211: # HTML Header 212: # 213: if ($r->header_only) { 214: &Apache::loncommon::content_type($r,'text/html'); 215: $r->send_http_header; 216: return OK; 217: } 218: # 219: # Roles Checking 220: # 221: # Needs to be in a course 222: if (! $ENV{'request.course.fn'}) { 223: # Not in a course, or not allowed to modify parms 224: $ENV{'user.error.msg'}= 225: $r->uri.":opa:0:0:Cannot modify spreadsheet"; 226: return HTTP_NOT_ACCEPTABLE; 227: } 228: my $courseid = $ENV{'request.course.id'}; 229: # 230: # Do not allow students to continue if standard grading is in effect. 231: if ($ENV{'request.role'} =~ /^st\./) { 232: if ($ENV{'course.'.$courseid.'.grading'} eq 'standard') { 233: return HTTP_NOT_ACCEPTABLE; 234: } 235: } 236: # 237: # Get query string for limited number of parameters 238: # 239: &Apache::loncommon::get_unprocessed_cgi 240: ($ENV{'QUERY_STRING'},['sname','sdomain','usymb','filename','recalc']); 241: # 242: # Deal with restricted student permissions 243: # 244: if ($ENV{'request.role'} =~ /^st\./) { 245: delete $ENV{'form.cell'} if (exists($ENV{'form.cell'})); 246: delete $ENV{'form.newformula'} if (exists($ENV{'form.newformula'})); 247: } 248: # 249: # Determine basic information about the spreadsheet 250: my ($sheettype) = ($r->uri=~/\/(\w+)$/); 251: # 252: my $symb = undef; 253: $symb = $ENV{'form.usymb'} if (exists($ENV{'form.usymb'})); 254: my $name = $ENV{'user.name'}; 255: my $domain = $ENV{'user.domain'}; 256: if (exists($ENV{'form.sname'})) { 257: $name = $ENV{'form.sname'}; 258: $domain = $ENV{'form.sdomain'}; 259: } 260: ## 261: ## Check permissions 262: my $allowed_to_edit = &Apache::lonnet::allowed('mgr', 263: $ENV{'request.course.id'}); 264: # Only those instructors/tas/whatevers with complete access 265: # (not section restricted) are able to modify spreadsheets. 266: my $allowed_to_view = &Apache::lonnet::allowed('vgr', 267: $ENV{'request.course.id'}); 268: if (! $allowed_to_view) { 269: $allowed_to_view = &Apache::lonnet::allowed('vgr', 270: $ENV{'request.course.id'}.'/'.$ENV{'request.course.sec'}); 271: # Those who are restricted by section are allowed to view. 272: # The routines in lonstatistics which decide which students' 273: # will be shown take care of the restriction by section. 274: } 275: # 276: # Only those able to view others grades will be allowed to continue 277: # if they are not requesting their own. 278: if ($sheettype eq 'classcalc') { 279: if (! $allowed_to_view) { 280: $ENV{'user.error.msg'}= 281: $r->uri.":vgr:0:0:Access Permission Denied"; 282: return HTTP_NOT_ACCEPTABLE; 283: } 284: } 285: if ((($name ne $ENV{'user.name'} ) || 286: ($domain ne $ENV{'user.domain'})) && $sheettype ne 'classcalc') { 287: # Check that the student is in their section? 288: if (exists($ENV{'request.course.sec'}) && 289: $ENV{'request.course.sec'} ne '' ) { 290: my $stu_sec = &Apache::lonnet::usection($domain,$name, 291: $ENV{'request.course.id'}); 292: if ($stu_sec ne $ENV{'request.course.sec'}) { 293: $ENV{'user.error.msg'}= 294: $r->uri.":vgr:0:0:Requested student not in your section."; 295: return HTTP_NOT_ACCEPTABLE; 296: } 297: } 298: } 299: 300: # 301: # Open page, try to prevent browser cache. 302: # 303: &Apache::loncommon::content_type($r,'text/html'); 304: &Apache::loncommon::no_cache($r); 305: $r->send_http_header; 306: 307: # 308: # Header.... 309: # 310: $r->print('<html><head><title>LON-CAPA Spreadsheet</title>'); 311: my $nothing = &Apache::lonhtmlcommon::javascript_nothing(); 312: ## 313: ## Spit out the javascript required for editing 314: ## 315: if ($allowed_to_edit) { 316: my %lt=( 317: 'ce' => 'Cell', 318: 'ac' => 'Accept', 319: 'dc' => 'Discard Changes' 320: ); 321: my $extra_javascript = 322: &Apache::loncommon::browser_and_searcher_javascript(); 323: $r->print(<<ENDSCRIPT); 324: <script type="text/javascript"> 325: //<!-- 326: $extra_javascript 327: 328: var editwin; 329: 330: function celledit(cellname,cellformula) { 331: var edit_text = ''; 332: // cellformula may contain less-than and greater-than symbols, so 333: // we need to escape them? 334: edit_text +='<html><head><title>Cell Edit Window</title></head><body>'; 335: edit_text += '<form name="editwinform">'; 336: edit_text += '<center><h3>$lt{'ce'} '+cellname+'</h3>'; 337: edit_text += '<textarea name="newformula" cols="60" rows="12"'; 338: edit_text += ' wrap="off" >'+cellformula+'</textarea>'; 339: edit_text += '</br>'; 340: edit_text += '<input type="button" name="accept" value="$lt{'ac'}"'; 341: edit_text += ' onClick=\\\'javascript:'; 342: edit_text += 'opener.document.sheet.cell.value='; 343: edit_text += '"'+cellname+'";'; 344: edit_text += 'opener.document.sheet.newformula.value='; 345: edit_text += 'document.editwinform.newformula.value;'; 346: edit_text += 'opener.document.sheet.submit();'; 347: edit_text += 'self.close()\\\' />'; 348: edit_text += ' '; 349: edit_text += '<input type="button" name="abort" '; 350: edit_text += 'value="$lt{'dc'}"'; 351: edit_text += ' onClick="javascript:self.close()" />'; 352: edit_text += '</center></body></html>'; 353: 354: if (editwin != null && !(editwin.closed) ) { 355: editwin.close(); 356: } 357: 358: editwin = window.open($nothing,'CellEditWin','height=280,width=480,scrollbars=no,resizable=yes,alwaysRaised=yes,dependent=yes',true); 359: editwin.document.write(edit_text); 360: } 361: //--> 362: </script> 363: ENDSCRIPT 364: } 365: $r->print('</head>'.&Apache::loncommon::bodytag('Grades Spreadsheet'). 366: '<form action="'.$r->uri.'" name="sheet" method="post">'); 367: $r->print(&hiddenfield('sname' ,$ENV{'form.sname'}). 368: &hiddenfield('sdomain',$ENV{'form.sdomain'}). 369: &hiddenfield('usymb' ,$ENV{'form.usymb'})); 370: $r->rflush(); 371: ## 372: ## Determine the filename to use 373: my $filename = undef; 374: if ($allowed_to_edit) { 375: $filename = $ENV{'form.filename'} if (exists($ENV{'form.filename'})); 376: # 377: if (exists($ENV{'form.load'}) && exists($ENV{'form.loadfilename'})) { 378: $filename = $ENV{'form.loadfilename'}; 379: $ENV{'form.workcopy'} = 'no'; 380: } 381: } 382: ## 383: ## Take care of "backdoor" spreadsheet expiration / recalc stuff 384: if ($allowed_to_edit && exists($ENV{'form.recalc'})) { 385: if (exists($ENV{'form.recalc'})) { 386: &Apache::loncoursedata::delete_caches($ENV{'requres.course.id'}); 387: } 388: if ($ENV{'form.recalc'} eq 'ilovewastingtime') { 389: &Apache::lonnet::logthis('ilovewastingtime'); 390: # expire ALL spreadsheets 391: &Apache::lonnet::expirespread('','','studentcalc'); 392: &Apache::lonnet::expirespread('','','assesscalc'); 393: } elsif ($ENV{'form.recalc'} =~ /^symb:/) { 394: # expire for all students on this symb 395: my ($symb) = ($ENV{'form.recalc'} =~ /^symb:(.*)$/); 396: &Apache::lonnet::logthis('symb = '.$symb); 397: &Apache::lonnet::expirespread('','','assesscalc',$symb); 398: &Apache::lonnet::expirespread('','','studentcalc'); 399: } elsif ($ENV{'form.recalc'} =~ /^student:/) { 400: # expire all assessment spreadsheets for this user 401: my ($sname,$sdom) = ($ENV{'form.recalc'}=~/^student:(.*):(.*)$/); 402: &Apache::lonnet::logthis('student = '.$sname.':'.$sdom); 403: if (defined($sname) && defined($sdom)) { 404: &Apache::lonnet::expirespread($sname,$sdom,'assesscalc'); 405: &Apache::lonnet::expirespread($sname,$sdom,'studentcalc'); 406: } 407: } 408: } 409: ## 410: ## Make the spreadsheet 411: &Apache::Spreadsheet::initialize_spreadsheet_package(); 412: my $spreadsheet = undef; 413: if ($sheettype eq 'classcalc') { 414: $spreadsheet = Apache::classcalc->new($name,$domain,$filename,undef); 415: } elsif ($sheettype eq 'studentcalc') { 416: $spreadsheet = Apache::studentcalc->new($name,$domain,$filename,undef); 417: } elsif ($sheettype eq 'assesscalc' && 418: defined($symb) && 419: $allowed_to_edit) { 420: $spreadsheet = Apache::assesscalc->new($name,$domain,$filename,$symb); 421: } else { 422: return HTTP_NOT_ACCEPTABLE; 423: } 424: if (! defined($spreadsheet)) { 425: # error error - run in circles, scream and shout 426: return; 427: } 428: $spreadsheet->initialize(); 429: # 430: # Output selector 431: ## 432: ## Editing/loading/saving 433: if ($allowed_to_edit) { 434: my ($html,$action_message) = &file_dialogs($spreadsheet); 435: if ($ENV{'form.makedefault'}) { 436: $spreadsheet->make_default(); 437: if ($action_message) { 438: $action_message .= '<br />'; 439: } 440: $action_message .= &mt('Made this spreadsheet the default'); 441: if ($sheettype eq 'classcalc') { 442: $action_message .= ' '.&mt('for the course'); 443: } elsif ($sheettype eq 'studentcalc') { 444: $action_message .= ' '.&mt('for all students'); 445: } elsif ($sheettype eq 'assesscalc') { 446: $action_message .= ' '.&mt('for all assessments'); 447: } 448: $action_message .= '.'; 449: } 450: $r->print('<table><tr><td>'.$spreadsheet->html_header().'</td>'. 451: '<td valign="bottom">'.$html."</td></tr></table>\n"); 452: if ($action_message ne '') { 453: $r->print(<<END); 454: <table> 455: <tr><td valign="top"><b>Last Action:</b></td> 456: <td> </td> 457: <td>$action_message</td> 458: </tr> 459: </table> 460: END 461: } 462: $r->rflush(); 463: } else { 464: $r->print('<table><tr><td>'.$spreadsheet->html_header(). 465: "</td></tr></table>\n"); 466: } 467: $r->rflush(); 468: # 469: $r->print("<table><tr>"); 470: if ($sheettype eq 'classcalc') { 471: $r->print('<td><input type="submit" value="'. 472: &mt('Generate Spreadsheet').'" />'. 473: '</td>'); 474: } 475: if ($allowed_to_view) { 476: $r->print('<td>'. 477: &Apache::loncommon::help_open_topic("Spreadsheet_About", 478: 'Spreadsheet Help'). 479: '</td>'); 480: } 481: if ($allowed_to_edit) { 482: $r->print('<td>'. 483: &Apache::loncommon::help_open_topic("Spreadsheet_Editing", 484: 'Editing Help'). 485: '</td>'); 486: } 487: $r->print('</tr></table>'); 488: # 489: # Keep track of the filename 490: $r->print(&hiddenfield('filename',$filename)); 491: # 492: # Keep track of the number of times we have been called, sort of. 493: $r->print(&hiddenfield('not_first_run','whatever')); 494: # 495: if (exists($ENV{'form.not_first_run'}) || $sheettype ne 'classcalc') { 496: $r->print($spreadsheet->get_html_title()); 497: if ($allowed_to_view || $allowed_to_edit) { 498: $r->print($spreadsheet->parent_link()); 499: } 500: $r->rflush(); 501: $spreadsheet->display($r); 502: } 503: $r->print('</form></body></html>'); 504: return OK; 505: } 506: 507: 1; 508: 509: __END__ 510: