Parent Directory
|
Revision Log
added fixes to PGalias.pm and PGcore.pm related to using $self-> in contexts where the binding was not as expected (e.g. in 'blah'. $self->{foobar} .'blah' ) Other minor fixes and improvements.
1 ################################################################################ 2 # WeBWorK Online Homework Delivery System 3 # Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ 4 # $CVSHeader: pg/lib/PGloadfiles.pm,v 1.1 2010/05/14 11:39:02 gage Exp $ 5 # 6 # This program is free software; you can redistribute it and/or modify it under 7 # the terms of either: (a) the GNU General Public License as published by the 8 # Free Software Foundation; either version 2, or (at your option) any later 9 # version, or (b) the "Artistic License" which comes with this package. 10 # 11 # This program is distributed in the hope that it will be useful, but WITHOUT 12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 # FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the 14 # Artistic License for more details. 15 ################################################################################ 16 17 =head2 18 19 The module name spaces loaded in dangerousMacros are: 20 21 PGrandom (if not previously loaded) 22 WWPlot 23 Fun 24 Label 25 Circle 26 27 in addition the subroutine &evaluate_units is shared from the module Units. 28 29 =cut 30 31 # BEGIN { 32 # be_strict(); # an alias for use strict. This means that all global variable must contain main:: as a prefix. 33 # 34 # } 35 # 36 # # ^variable my $debugON 37 # my $debugON = 0; 38 # 39 # # grab read only variables from the current safe compartment 40 # 41 # # ^variable my $macrosPath 42 # my ($macrosPath, 43 # # ^variable my $pwd 44 # $pwd, 45 # # ^variable my $appletPath 46 # $appletPath, 47 # # ^variable my $server_root_url 48 # $server_root_url, 49 # # ^variable my $templateDirectory 50 # $templateDirectory, 51 # # ^variable my $scriptDirectory 52 # $scriptDirectory, 53 # # ^variable my $externalTTHPath 54 # $externalTTHPath, 55 # ); 56 # 57 # # ^function _dangerousMacros_init 58 # # ^uses %envir 59 # # ^uses $macrosPath 60 # # ^uses $pwd 61 # # ^uses $appletPath 62 # # ^uses $server_root_url 63 # # ^uses $templateDirectory 64 # # ^uses $scriptDirectory 65 # # ^uses $externalTTHPath 66 # # ^uses $debugON 67 # sub _dangerousMacros_init { #use envir instead of local variables? 68 # # will allow easy addition of new directories -- is this too liberal? do some pg directories need to be protected? 69 # $macrosPath = eval('$main::envir{pgDirectories}{macrosPath}'); 70 # # will allow easy addition of new directories -- is this too liberal? do some pg directories need to be protected? 71 # $pwd = eval('$main::envir{fileName}'); $pwd =~ s!/[^/]*$!!; 72 # $appletPath = eval('$main::envir{pgDirectories}{appletPath}'); 73 # $server_root_url = eval('$main::envir{server_root_url}'); 74 # 75 # $templateDirectory = eval('$main::envir{templateDirectory}'); 76 # $scriptDirectory = eval('$main::envir{scriptDirectory}'); 77 # $externalTTHPath = eval('$main::envir{externalTTHPath}'); 78 # $pwd = $templateDirectory.$pwd unless substr($pwd,0,1) eq '/'; 79 # $pwd =~ s!/tmpEdit/!/!; 80 # warn "dangerousmacros initialized" if $debugON; 81 # warn eval(q! "dangerousmacros.pl externalTTHPath is ".$main::externalTTHPath;!) if $debugON; 82 # warn eval(q! "dangerousmacros.pl: The envir variable $main::{envir} is".join(" ",%main::envir)!) if $debugON; 83 # } 84 # 85 # # ^function _dangerousMacros_export 86 # sub _dangerousMacros_export { 87 # my @EXPORT= ( 88 # '&_dangerousMacros_init', 89 # '&alias', 90 # '&compile_file', 91 # '&insertGraph', 92 # '&loadMacros', 93 # '&HEADER_TEXT', 94 # '&sourceAlias', 95 # '&tth', 96 # ); 97 # @EXPORT; 98 # } 99 100 101 =head2 loadMacros 102 103 loadMacros(@macroFiles) 104 105 loadMacros takes a list of file names and evaluates the contents of each file. 106 This is used to load macros which define and augment the PG language. The macro 107 files are searched for in the directories specified by the array referenced by 108 $macrosPath, which by default is the current course's macros directory followed 109 by WeBWorK's pg/macros directory. The latter is where the default behaviour of 110 the PG language is defined. The default path is set in the global.conf file. 111 112 Macro files named PG.pl, IO.pl, or dangerousMacros.pl will be loaded with no 113 opcode restrictions, hence any code in those files will be able to execute 114 privileged operations. This is true no matter which macro directory the file is 115 in. For example, if $macrosPath contains the path to a problem library macros 116 directory which contains a PG.pl file, this file will be loaded and allowed to 117 engage in privileged behavior. 118 119 =head3 Overloading macro files 120 121 An individual course can modify the PG language, for that course only, by 122 duplicating one of the macro files in the system-wide macros directory and 123 placing this file in the macros directory for the course. The new file in the 124 course's macros directory will now be used instead of the file in the 125 system-wide macros directory. 126 127 The new file in the course macros directory can by modified by adding macros or 128 modifying existing macros. 129 130 =head3 Modifying existing macros 131 132 I<Modifying macros is for users with some experience.> 133 134 Modifying existing macros might break other standard macros or problems which 135 depend on the unmodified behavior of these macors so do this with great caution. 136 In addition problems which use new macros defined in these files or which depend 137 on the modified behavior of existing macros will not work in other courses 138 unless the macros are also transferred to the new course. It helps to document 139 the problems by indicating any special macros which the problems require. 140 141 There is no facility for modifying or overloading a single macro. The entire 142 file containing the macro must be overloaded. 143 144 Modifications to files in the course macros directory affect only that course, 145 they will not interfere with the normal behavior of WeBWorK in other courses. 146 147 =cut 148 149 our $debugON =0; 150 151 package PGloadfiles; 152 use strict; 153 use Exporter; 154 use PGcore; 155 use WeBWorK::PG::Translator; 156 use WeBWorK::PG::IO; 157 158 our @ISA = qw ( PGcore ) ; # look up features in PGcore -- in this case we want the environment. 159 160 161 162 163 # Global variables used 164 # ${main::macrosPath} 165 # Global macros used 166 # None 167 168 # Because of the need to use the directory variables it is tricky to define this 169 # in translate.pl since, as currently written, the directories are not available 170 # at that time. Perhaps if I rewrite translate as an object that method will work. 171 172 # The only difficulty with defining loadMacros inside the Safe compartment is that 173 # the error reporting does not work with syntax errors. 174 # A kludge using require works around this problem 175 176 # our ($macrosPath, 177 # # ^variable my $pwd 178 # $pwd, 179 # # ^variable my $appletPath 180 # $appletPath, 181 # # ^variable my $server_root_url 182 # $server_root_url, 183 # # ^variable my $templateDirectory 184 # $templateDirectory, 185 # # ^variable my $scriptDirectory 186 # $scriptDirectory, 187 # # ^variable my $externalTTHPath 188 # $externalTTHPath, 189 # ); 190 # sub _dangerousMacros_init { #use envir instead of local variables? 191 # # will allow easy addition of new directories -- is this too liberal? do some pg directories need to be protected? 192 # $macrosPath = eval('$main::envir{pgDirectories}{macrosPath}'); 193 # # will allow easy addition of new directories -- is this too liberal? do some pg directories need to be protected? 194 # $pwd = eval('$main::envir{fileName}'); $pwd =~ s!/[^/]*$!!; 195 # $appletPath = eval('$main::envir{pgDirectories}{appletPath}'); 196 # $server_root_url = eval('$main::envir{server_root_url}'); 197 # 198 # $templateDirectory = eval('$main::envir{templateDirectory}'); 199 # $scriptDirectory = eval('$main::envir{scriptDirectory}'); 200 # $externalTTHPath = eval('$main::envir{externalTTHPath}'); 201 # $pwd = $templateDirectory.$pwd unless substr($pwd,0,1) eq '/'; 202 # $pwd =~ s!/tmpEdit/!/!; 203 # warn "dangerousmacros initialized" if $debugON; 204 # warn eval(q! "dangerousmacros.pl externalTTHPath is ".$main::externalTTHPath;!) if $debugON; 205 # warn eval(q! "dangerousmacros.pl: The envir variable $main::{envir} is".join(" ",%main::envir)!) if $debugON; 206 # } 207 # new 208 # Create one loadfiles object per question (and per PGcore object) 209 # Process macro files 210 # Keep list of macro files processed. 211 sub new { 212 my $class = shift; 213 my $envir = shift; #pointer to environment hash 214 warn "PGloadmacros must be called with an environment" unless ref($envir) eq 'HASH'; 215 my $self = { 216 envir => $envir, 217 macroFileList => {}, # records macros used in compilation 218 pgFileName => '', # current pg file being processed 219 server_root_url => '', # how do we find this? 220 macrosPath => '', 221 pwd => '', # current directory -- defined in initialize 222 }; 223 bless $self, $class; 224 $self->initialize; 225 #$self->check_parameters; 226 return $self; 227 } 228 sub initialize { 229 my $self = shift; 230 my $templateDirectory = $self->{envir}->{templateDirectory}; 231 my $pwd = $self->{envir}->{fileName}; 232 $pwd =~ s!/[^/]*$!!; 233 $pwd = $templateDirectory.$pwd unless substr($pwd,0,1) eq '/'; 234 $pwd =~ s!/tmpEdit/!/!; 235 $self->{pwd} = $pwd; 236 $self->{macrosPath} = $self->{envir}->{pgDirectories}->{macrosPath}; 237 238 } 239 240 sub PG_restricted_eval { 241 my $self = shift; 242 WeBWorK::PG::Translator::PG_restricted_eval(@_); 243 } 244 sub PG_macro_file_eval { 245 my $self = shift; 246 WeBWorK::PG::Translator::PG_macro_file_eval(@_); 247 } 248 249 250 # ^function loadMacros 251 # ^uses time_it 252 # ^uses $debugON 253 # ^uses $externalTTHPath 254 # ^uses findMacroFile 255 sub loadMacros { 256 my $self = shift; 257 my @files = @_; 258 my $fileName; 259 my $macrosPath = $self->{envir}->{macrosPath}; 260 eval {main::time_it("begin load macros");}; 261 ############################################################################### 262 # At this point the directories have been defined from %envir and we can define 263 # the directories for this file 264 ############################################################################### 265 266 # special case inits 267 # foreach my $file ('PG.pl','dangerousMacros.pl','IO.pl') { 268 # my $macro_file_name = $file; 269 # $macro_file_name =~s/\.pl//; # trim off the extension 270 # $macro_file_name =~s/\.pg//; # sometimes the extension is .pg (e.g. CAPA files) 271 # my $init_subroutine_name = "_${macro_file_name}_init"; 272 # my $init_subroutine = eval { \&{$init_subroutine_name} }; 273 # use strict; 274 # my $macro_file_loaded = defined($init_subroutine); 275 # warn "dangerousMacros: macro init $init_subroutine_name defined |$init_subroutine| |$macro_file_loaded|" if $debugON; 276 # if ( defined($init_subroutine) && defined( &{$init_subroutine} ) ) { 277 # 278 # warn "dangerousMacros: initializing $macro_file_name" if $debugON; 279 # &$init_subroutine(); 280 # } 281 # } 282 #FIXME -- how to check that things are defined properly 283 # unless (defined( &DOCUMENT)){ 284 # warn "WARNING::Please make sure that the DOCUMENT() statement comes before<BR>\n" . 285 # " the loadMacros() statement in the problem template.<p>" . 286 # " The externalTTHPath variable |$externalTTHPath| was\n". 287 # " not defined which usually indicates the problem above.<br>\n"; 288 # 289 # } 290 #warn "running load macros"; 291 292 while (@files) { 293 $fileName = shift @files; 294 next if ($fileName =~ /^PG.pl$/) ; # the PG.pl macro package is already loaded. 295 296 my $macro_file_name = $fileName; 297 $macro_file_name =~s/\.pl//; # trim off the extension 298 $macro_file_name =~s/\.pg//; # sometimes the extension is .pg (e.g. CAPA files) 299 my $init_subroutine_name = "_${macro_file_name}_init"; 300 $init_subroutine_name =~ s![^a-zA-Z0-9_]!_!g; # remove dangerous chars 301 302 ############################################################################### 303 # For some reason the "no stict" which works on webwork-db doesn't work on 304 # webwork. For this reason the constuction &{$init_subroutine_name} 305 # was abandoned and replaced by eval. This is considerably more dangerous 306 # since one could hide something nasty in a file name. 307 # Keep an eye on this ??? 308 # webwork-db used perl 5.6.1 and webwork used perl 5.6.0 309 ############################################################################### 310 311 # compile initialization subroutine. (5.6.0 version) 312 313 314 # eval( q{ \$init_subroutine = \\&main::}.$init_subroutine_name); 315 # warn "dangerousMacros: failed to compile $init_subroutine_name. $@" if $@; 316 317 318 ############################################################################### 319 #compile initialization subroutine. (5.6.1 version) also works with 5.6.0 320 321 # no strict; 322 my $init_subroutine = eval { \&{'main::'.$init_subroutine_name} }; 323 # use strict; 324 325 ############################################################################### 326 327 # macros are searched for in the directories listed in the $macrosPath array reference. 328 329 my $macro_file_loaded = defined($init_subroutine) && defined(&$init_subroutine); 330 warn "dangerousMacros: macro init $init_subroutine_name defined |$init_subroutine| |$macro_file_loaded|" if $debugON; 331 unless ($macro_file_loaded) { 332 warn "loadMacros: loading macro file $fileName\n" if $debugON; 333 my $filePath = $self->findMacroFile($fileName); 334 #### (check for renamed files here?) #### 335 if ($filePath) { 336 $self->compile_file($filePath); 337 warn "loadMacros is compiling $filePath\n" if $debugON; 338 } 339 else { 340 die "Can't locate macro file |$fileName| via path: |".join("|, |",@{$macrosPath})."|"; 341 } 342 } 343 ############################################################################### 344 # Try again to define the initialization subroutine. (5.6.0 version) 345 346 # eval( q{ \$init_subroutine = \\&main::}.$init_subroutine_name ); 347 # warn "dangerousMacros: failed to compile $init_subroutine_name. $@" if $@; 348 # $init_subroutine = $temp::rf_init_subroutine; 349 ############################################################################### 350 # Try again to define the initialization subroutine. (5.6.1 version) also works with 5.6.0 351 352 # no strict; 353 $init_subroutine = eval { \&{'main::'.$init_subroutine_name} }; 354 # use strict; 355 ############################################################################### 356 #warn "loadMacros: defining \$temp::rf_init_subroutine ",$temp::rf_init_subroutine; 357 $macro_file_loaded = defined($init_subroutine) && defined(&$init_subroutine); 358 warn "dangerousMacros: macro init $init_subroutine_name defined |$init_subroutine| |$macro_file_loaded|" if $debugON; 359 360 if ( defined($init_subroutine) && defined( &{$init_subroutine} ) ) { 361 warn "dangerousMacros: initializing $macro_file_name" if $debugON; 362 &$init_subroutine(); 363 } 364 #warn "main:: contains <br>\n $macro_file_name ".join("<br>\n $macro_file_name ", %main::); 365 } 366 #arn "files loaded:", join(" ", keys %{ $self->{macroFileList} }); 367 eval{main::time_it("end load macros");}; 368 } 369 370 371 # ^function findMacroFile 372 # ^uses $macrosPath 373 # ^uses $pwd 374 sub findMacroFile { 375 my $self = shift; 376 my $macroFileName = shift; 377 my $macroFilePath; 378 my $pwd = $self->{pwd}; 379 foreach my $dir (@{$self->{macrosPath} } ) { 380 $macroFilePath = "$dir/$macroFileName"; 381 $macroFilePath =~ s!^\.\.?/!$pwd/!; 382 return $macroFilePath if (-r $macroFilePath); 383 } 384 return; # no file found 385 } 386 # errors in compiling macros is not always being reported. 387 # ^function compile_file 388 # ^uses @__eval__ 389 # ^uses PG_restricted_eval 390 # ^uses $__files__ 391 sub compile_file { 392 my $self = shift; 393 my $filePath = shift; 394 warn "loading $filePath" if $debugON; 395 local(*MACROFILE); 396 local($/); 397 $/ = undef; # allows us to treat the file as a single line 398 open(MACROFILE, "<$filePath") || die "Cannot open file: $filePath"; 399 my $string = <MACROFILE>; 400 #warn "compiling $string"; 401 my ($result,$error,$fullerror) = $self->PG_macro_file_eval($string); 402 #eval ('$main::__files__->{pop @main::__eval__} = $filePath'); 403 if ($error) { # the $fullerror report has formatting and is never empty 404 # this is now handled by PG_errorMessage() in the PG translator 405 #$fullerror =~ s/\(eval \d+\)/ $filePath\n/; # attempt to insert file name instead of eval number 406 die "Error detected while loading $filePath:\n$fullerror"; 407 408 } 409 $self->{macroFileList}->{$filePath} =1; 410 close(MACROFILE); 411 412 } 413 414 415 416 417 418 419 =head2 sourceAlias 420 421 sourceAlias($path_to_PG_file); 422 423 Returns a relative URL to the F<source.pl> script, which may be installed in a 424 course's F<html> directory to allow formatted viewing of the problem source. 425 426 =cut 427 428 # ^function sourceAlias 429 # ^uses PG_restricted_eval 430 # ^uses %envir 431 # ^uses $envir{inputs_ref} 432 # ^uses $envir{psvn} 433 # ^uses $envir{probNum} 434 # ^uses $envir{displayMode} 435 # ^uses $envir{courseName} 436 # ^uses $envir{sessionKey} 437 sub sourceAlias { 438 my $self = shift; 439 my $path_to_file = shift; 440 my $envir = PG_restricted_eval(q!\%main::envir!); 441 my $user = $envir->{inputs_ref}->{user}; 442 $user = " " unless defined($user); 443 my $out = 'source.pl?probSetKey=' . $envir->{psvn}. 444 '&probNum=' . $envir->{probNum} . 445 '&Mode=' . $envir->{displayMode} . 446 '&course=' . $envir->{courseName} . 447 '&user=' . $user . 448 '&displayPath=' . $path_to_file . 449 '&key=' . $envir->{sessionKey}; 450 451 $out; 452 } 453 454 455 1;
aubreyja at gmail dot com | ViewVC Help |
Powered by ViewVC 1.0.9 |