diff --git a/lib/PGalias.pm b/lib/PGalias.pm index 6b4ec6e028..26593aec8a 100644 --- a/lib/PGalias.pm +++ b/lib/PGalias.pm @@ -14,682 +14,378 @@ ################################################################################ package PGalias; +use parent PGcore; # This is so that a PGalias object can call the PGcore warning_message and debug_message methods. + use strict; -use Exporter; +use warnings; + use UUID::Tiny ':std'; use PGcore; use PGresource; -our @ISA = qw ( PGcore ); # look up features in PGcore -- in this case we want the environment. - -=head2 new - - Create one alias object per question (and per PGcore object since there is a unique PGcore per question.) - Check that information is intact - Construct unique id stub seeds -- the id stub seed is for this PGalias object which is - attached to all the resource files (except equations) for this question. - Maintain list of links to external resources - -=cut - sub new { - my $class = shift; - my $envir = shift; #pointer to environment hash - my %options = @_; - warn "PGlias must be called with an environment" unless ref($envir) =~ /HASH/; - my $self = { - envir => $envir, - search_list => [ { url => 'foo', dir => '.' } ], # for subclasses -> list of url/directories to search - resource_list => {}, - %options, - - }; - bless $self, $class; - $self->initialize; - $self->check_parameters; - return $self; -} - -sub add_resource { - my $self = shift; - my ($aux_file_id, $resource) = @_; - if (ref($resource) =~ /PGresource/) { - $self->{resource_list}->{$aux_file_id} = $resource; - #$self->debug_message("$aux_file_id resource added"); - } else { - $self->warning_message("$aux_file_id does not refer to a a valid resource $resource"); - } -} + my ($class, $envir, %options) = @_; + warn 'PGlias must be called with an environment' unless ref($envir) =~ /HASH/; + my $self = bless { envir => $envir, resource_list => {}, %options }, $class; -sub get_resource { - my $self = shift; - my $aux_file_id = shift; - $self->{resource_list}->{$aux_file_id}; -} - -# methods -# make_alias -- outputs url and does what needs to be done -# normalize paths (remove extra precursors to the path) -# search directories for item -# make_links -- in those cases where links need to be made -# create_files -- e.g. when printing hardcopy -# dispatcher -- decides what needs to be done based on displayMode and file type -# alias_for_html -# alias_for_image_in_html image includes gif, png, jpg, swf, svg, flv?? ogg??, js -# alias_for_image_in_tex - -sub initialize { - my $self = shift; - my $envir = $self->{envir}; - - $self->{pgFileName} = $envir->{probFileName} // ''; + $self->{probFileName} = $envir->{probFileName} // ''; $self->{htmlDirectory} = $envir->{htmlDirectory}; $self->{htmlURL} = $envir->{htmlURL}; $self->{tempDirectory} = $envir->{tempDirectory}; $self->{templateDirectory} = $envir->{templateDirectory}; $self->{tempURL} = $envir->{tempURL}; - $self->{psvn} = $envir->{psvn}; $self->{displayMode} = $envir->{displayMode}; - $self->{problemSeed} = $envir->{problemSeed}; - $self->{problemUUID} = $envir->{problemUUID} // 0; - # Find auxiliary files even when the main file is in templates/tmpEdit - # FIXME: This shouldn't be done here. Instead the front end should pass in the problem source with the file name. + # Find auxiliary files even when the main file is in templates/tmpEdit. + # FIXME: This shouldn't be done here. Instead the front end should pass in the problem source with the file name. # The other instance of this in PGloadfiles.pm needs to be removed. - $self->{pgFileName} =~ s!(^|/)tmpEdit/!$1!; - - $self->{ext} = ''; + $self->{probFileName} =~ s!(^|/)tmpEdit/!$1!; - # Create an ID which is unique for the given psvn, problemSeed, and problemUUID. - # It is the responsibility of the caller to pass in a problemUUID that will provide the required uniqueness. - # That could include a course name, a student login name, etc. + # Create an ID which is unique for the given psvn, problemSeed, and problemUUID. It is the responsibility of the + # caller to pass in a problemUUID that will provide the required uniqueness. That could include a course name, a + # student login name, etc. $self->{unique_id_stub} = create_uuid_as_string(UUID_V3, UUID_NS_URL, - join('-', $self->{psvn}, $self->{problemSeed}, $self->{problemUUID})); -} + join('-', $envir->{psvn} // (), $envir->{problemSeed}, $envir->{problemUUID} // ())); + + # Check the parameters. + $self->warning_message('The displayMode is not defined') unless $self->{displayMode}; + $self->warning_message('The htmlDirectory is not defined.') unless $self->{htmlDirectory}; + $self->warning_message('The htmlURL is not defined.') unless $self->{htmlURL}; + $self->warning_message('The tempURL is not defined.') unless $self->{tempURL}; -sub check_parameters { - my $self = shift; + return $self; +} - # Problem specific data - $self->warning_message('The current problem set version number (psvn) is not defined') - unless defined $self->{psvn}; - $self->warning_message('The displayMode is not defined') unless $self->{displayMode}; +# This cache's auxiliary files within a single PG problem. +sub add_resource { + my ($self, $aux_file_id, $resource) = @_; + if (ref($resource) =~ /PGresource/) { + $self->{resource_list}{$aux_file_id} = $resource; + } else { + $self->warning_message(qq{"$aux_file_id" does not refer to a valid resource.}); + } + return; +} - # required directory addresses (and URL address) - warn 'htmlDirectory is not defined.' unless $self->{htmlDirectory}; - warn 'htmlURL is not defined.' unless $self->{htmlURL}; - warn 'tempURL is not defined.' unless $self->{tempURL}; +sub get_resource { + my ($self, $aux_file_id) = @_; + return $self->{resource_list}{$aux_file_id}; } sub make_resource_object { - my $self = shift; - my $aux_file_id = shift; - my $ext = shift; - my $resource = PGresource->new( - $self, #parent alias of resource + my ($self, $aux_file_id, $ext) = @_; + return PGresource->new( + $self, # parent alias of resource $aux_file_id, # resource file name $ext, # resource type - WARNING_messages => $self->{WARNING_messages}, #connect warning message channels + WARNING_messages => $self->{WARNING_messages}, # connect warning message channels DEBUG_messages => $self->{DEBUG_messages}, ); - return $resource; } -=head2 make_alias - -This is the workhorse of the PGalias module. It's front end is alias() in PG.pl. - -make_alias magically takes a name of an external resource ( html file, png file, etc.) -and creates full directory addresses and uri's appropriate to the current displayMode. -It also does any necessary conversions behind the scenes. - -Returns the uri of the resource. - -=cut - sub make_alias { - my $self = shift; - my $aux_file_id = shift; - #$self->debug_message("make alias for file $aux_file_id"); - $self->warning_message("Empty string used as input into the function alias") unless $aux_file_id; - - my $displayMode = $self->{displayMode}; - - # $adr_output is a url in HTML mode - # and a complete directory path in TEX mode. - my $adr_output; - my $ext = ''; - -####################################################################### - # determine file type - # determine display mode - # dispatch -####################################################################### - # determine extension, if there is one - # if extension exists use the value for $ext - # files without extensions are flagged with errors. - # The extension is retained as part of aux_file_id - - #$self->debug_message("This auxiliary file id is $aux_file_id" ); + my ($self, $aux_file_id) = @_; + $self->warning_message('Empty string used as input into the function alias') unless $aux_file_id; + + # Determine the file extension, if there is one. Files without extensions are flagged with errors. + my $ext; if ($aux_file_id =~ m/\.([^\.]+)$/) { $ext = $1; } else { - $self->warning_message("The file name $aux_file_id did not have an extension.
" - . "Every file name used as an argument to alias must have an extension.
" - . "The permissable extensions are .jpg, .pdf, .gif, .png, .mpg, .mp4, .ogg, .webm and .html .
"); - $ext = undef; - return undef; #quit; + $self->warning_message(qq{The file name "$aux_file_id" does not have an extension. } + . 'Every file name used as an argument to alias must have an extension. The permissable extensions are ' + . '.gif, .jpg, .png, .svg, .pdf, .mp4, .mpg, .ogg, .webm, .css, .js, .nb, .csv, .tgz, and .html.'); + return; } - # $self->debug_message("This auxiliary file id is $aux_file_id of type $ext" ); - -################################################################### - # Create resource object -################################################################### - #$self->debug_message("creating resource with id $aux_file_id"); - - ################################################################### - # This section checks to see if a resource exists (in this question) - # for this particular aux_file_id. - # If so, we simply return the appropriate uri for the file. - # The displayMode will be the same throughout the processing of the .pg file - # This effectively cache's auxiliary files within a single PG question. - ################################################################### - unless (defined $self->get_resource($aux_file_id)) { - $self->add_resource( - $aux_file_id, - $self->make_resource_object( - $aux_file_id, # resource file name - $ext # resource type - ) - - ); + # Checks to see if a resource exists for this particular aux_file_id. + # If not, then create one. Otherwise, return the URI for the existing resource. + unless (defined $self->get_resource($aux_file_id)) { + $self->add_resource($aux_file_id, $self->make_resource_object($aux_file_id, $ext)); } else { - #$self->debug_message( "found existing resource_object $aux_file_id"); - return $self->get_resource($aux_file_id)->uri(); + return $self->get_resource($aux_file_id)->uri; } - ################################################################### + + # $output_location is a URL in HTML mode and a complete directory path in TeX mode. + my $output_location; if ($ext eq 'html') { - $adr_output = $self->alias_for_html($aux_file_id, $ext); - } elsif ($ext =~ /^(gif|jpg|png|svg|pdf|mp4|mpg|ogg|webm|css|js|nb|tgz)$/) { - if ($displayMode =~ /^HTML/ or $displayMode eq 'PTX') { - $adr_output = $self->alias_for_html($aux_file_id, $ext); - } elsif ($displayMode eq 'TeX') { - ################################################################################ - # .gif FILES in TeX mode - ################################################################################ - $adr_output = $self->alias_for_tex($aux_file_id, $ext); + $output_location = $self->alias_for_html($aux_file_id, $ext); + } elsif ($ext =~ /^(gif|jpg|png|svg|pdf|mp4|mpg|ogg|webm|css|js|nb|csv|tgz)$/) { + if ($self->{displayMode} =~ /^HTML/ or $self->{displayMode} eq 'PTX') { + $output_location = $self->alias_for_html($aux_file_id, $ext); + } elsif ($self->{displayMode} eq 'TeX') { + $output_location = $self->alias_for_tex($aux_file_id, $ext); } else { - die "Error in alias: PGalias.pm: unrecognizable displayMode = $displayMode"; + $self->warning_message("Error creating resource alias. Unrecognizable displayMode: $self->{displayMode}"); } - # } elsif ($ext eq 'svg') { - # if ($displayMode =~/HTML/) { - # $self->warning_message("The image $aux_file_id of type $ext cannot yet be displayed in HTML mode"); - # # svg images need an embed tag not an image tag -- need to modify image for this also - # # an alternative (not desirable) is to convert svg to png - # } elsif ($displayMode eq 'TeX') { - # $self->warning_message("The image $aux_file_id of type $ext cannot yet be displayed in TeX mode"); - # } else { - # die "Error in alias: PGalias.pm: unrecognizable displayMode = $displayMode"; - # } - - } else { # $ext is not recognized - ################################################################################ - # FILES with unrecognized file extensions in any display modes - ################################################################################ - - warn "Error in the macro alias. Alias does not understand how to process files with extension $ext. - (Path to problem file is " . $self->{pgFileName} . ") "; + } else { + # $ext is not recognized + $self->warning_message(qq{Error creating resource alias. Files with extension "$ext" are not allowed.\n} + . qq{(Path to problem file is "$self->{probFileName}".)}); } - $self->warning_message( - "The macro alias was unable to form a URL for the auxiliary file |$aux_file_id| used in this problem.") - unless $adr_output; + $self->warning_message(qq{Unable to form a URL for the auxiliary file "$aux_file_id" used in this problem.}) + unless $output_location; - # $adr_output is a url in HTML modes - # and a complete path in TEX mode. - my $resource_object = $self->get_resource($aux_file_id); - # TEXT(alias() ) is expecting only a single item not an array - # so the code immediately below for adding extra information to alias is a bad idea. - #return (wantarray) ? ($adr_output, $resource_object): $adr_output; - # Instead we'll implement a get_resource() command in PGcore and PG - return ($adr_output); + return $output_location; } sub alias_for_html { - my $self = shift; #handed alias object - my $aux_file_id = shift; #handed the name of the resource object - # case 1: aux_file_id is complete or relative path to file - # case 2: aux_file_id is file name alone relative to the templates directory. - my $ext = shift; - #$self->debug_message("handling $aux_file_id of type $ext"); -####################### - # gather needed data and declare it locally -####################### - my $htmlURL = $self->{htmlURL}; - my $htmlDirectory = $self->{htmlDirectory}; - my $pgFileName = $self->{pgFileName}; - my $tempURL = $self->{tempURL}; - my $tempDirectory = $self->{tempDirectory}; - my $templateDirectory = $self->{templateDirectory}; - -####################### - # retrieve PGresponse resource object -####################### - my ($resource_uri); - my $resource_object = $self->get_resource($aux_file_id); - # $self->debug_message( "\nresource for $aux_file_id is ", ref($resource_object), $resource_object ); + my ($self, $aux_file_id, $ext) = @_; -############################################## - # Find complete path to the original files -############################################## + my $resource_object = $self->get_resource($aux_file_id); - # get the directories that might contain html files - my $dirPath = ''; - if ($ext eq 'html') { - $dirPath = 'htmlPath'; - } else { - $dirPath = 'imagesPath'; + if ($aux_file_id =~ /https?:/) { + # External URL. + $resource_object->uri($aux_file_id); + return $resource_object->uri; # External URLs need no further processing. } - my @aux_files_directories = @{ $self->{envir}->{$dirPath} }; - if ($pgFileName) { + # Get the directories that might contain auxiliary files. + my @aux_files_directories = @{ $self->{envir}{ $ext eq 'html' ? 'htmlPath' : 'imagesPath' } }; + if ($self->{probFileName}) { # Replace "." with the current pg problem file directory. - my $current_pg_directory = $self->directoryFromPath($pgFileName); - $current_pg_directory = $self->{templateDirectory} . $current_pg_directory; - @aux_files_directories = map { $_ eq '.' ? $current_pg_directory : $_ } @aux_files_directories; + @aux_files_directories = + map { $_ eq '.' ? $self->{templateDirectory} . $self->directoryFromPath($self->{probFileName}) : $_ } + @aux_files_directories; } else { @aux_files_directories = grep { $_ ne '.' } @aux_files_directories; } - # Find complete path to the original file - my $file_path; - if ($aux_file_id =~ /https?:/) { #external link_file - $resource_object->uri($aux_file_id); #no unique id is needed -- external link doc - $resource_object->{copy_link}->{type} = 'external'; - $resource_object->{uri}{is_accessible} = 1; # Assume a url is accessible. - return $resource_object->uri; # external links need no further processing - } elsif ($aux_file_id =~ m|^/|) { - $file_path = $aux_file_id; - } else { - $file_path = $self->find_file_in_directories($aux_file_id, \@aux_files_directories); + # Find the complete path to the original file. + my $file_path = + $aux_file_id =~ m|^/| ? $aux_file_id : $self->find_file_in_directories($aux_file_id, \@aux_files_directories); + + unless ($file_path) { + $self->warning_message(qq{Unable to find file "$aux_file_id".}); + return; } - # $self->debug_message("file path is $file_path"); - -##################### Case1: we've got a full pathname to a file in either the temp directory or the htmlDirectory -##################### Case2: we assume the file is in the same directory as the problem source file -##################### Case3: the file could have an external url - -############################################## - # store the complete path to the original file - # calculate the uri (which is a url suitable for the browser relative to the current site) - # store the uri. - # record status of the resource -############################################## - if ($file_path =~ m|^$tempDirectory|) { #case: file is stored in the course temporary directory - $resource_uri = $file_path; - $resource_uri =~ s|$tempDirectory|$tempURL|; - $resource_object->uri($resource_uri); #no unique id is needed -- public doc + + # Store the complete path to the original file, and calculate and store the URI (which is a URL suitable for the + # browser relative to the current site). + if ($file_path =~ m|^$self->{tempDirectory}|) { + # File is in the course temporary directory. + $resource_object->uri($file_path =~ s|$self->{tempDirectory}|$self->{tempURL}|r); $resource_object->path($file_path); - $resource_object->{copy_link}->{type} = 'orig'; - $resource_object->{path}->{is_complete} = (-r $resource_object->path); - } elsif ($file_path =~ m|^$htmlDirectory|) { #case: file is under the course html directory - $resource_uri = $file_path; - $resource_uri =~ s|$htmlDirectory|$htmlURL|; - $resource_object->uri($resource_uri); + } elsif ($file_path =~ m|^$self->{htmlDirectory}|) { + # File is in the course html directory. + $resource_object->uri($file_path =~ s|$self->{htmlDirectory}|$self->{htmlURL}|r); $resource_object->path($file_path); - $resource_object->{copy_link}->{type} = 'orig'; - $resource_object->{path}->{is_complete} = (-r $resource_object->path); - #################################################### - # one can add more public locations such as the site htdocs directory here in the elsif chain - #################################################### - } else { #case: resource is in a directory which is not public - # most often this is the directory containing the .pg file - # these files require a link to the temp Directory - # $self->debug_message("source file path ", $sourceFilePath); + } else { + # Resource is in a directory which is not public. + # Most often this is the directory containing the .pg file. + # These files need to be linked to from the public html temporary directory. $resource_object->path($file_path); - $resource_object->{copy_link}->{type} = 'link'; - $resource_object->{path}->{is_complete} = 0; - $resource_object->{uri}->{is_complete} = 0; - warn "$ext not defined" unless $ext; + $self->warning_message("File extension for resource $file_path is not defined") unless $ext; $resource_object->create_unique_id($ext); - # notice the resource uri is not yet defined -- we have to make the link first - } -############################################## - # Create links for objects of "link" type. - # between private directories such as myCourse/template - # and public directories (such as wwtmp/courseName or myCourse/html - # The location of the links depends on the type and location of the file -############################################## - # create_link_to_tmp_file() - #input: resource object, ext, (html) (tempURL), - #return: uri - if ($resource_object->{copy_link}->{type} eq 'link') { - # this creates a link from the original file to an alias in the tmp/html directory - # and places information about the path and the uri in the PGresponse object $resource_object - my $subdir = ''; - if ($ext eq 'html') { - $subdir = 'html'; - } else { - $subdir = 'images'; - } - $self->create_link_to_tmp_file(resource => $resource_object, subdir => $subdir); + # Create a link from the original file to an alias in the temporary public html directory. + $self->create_link_to_tmp_file($resource_object, $ext eq 'html' ? 'html' : 'images'); } -################################################################################ - # Return full url to image file (resource_id) -################################################################################ - $resource_object->uri(); # return the uri of the resource -- in this case the URL for the file in the temp directory + # Return the URI of the resource. + return $resource_object->uri; } -################################################################################ -# alias for image in tex mode -################################################################################ - sub alias_for_tex { - my $self = shift; #handed alias object - my $aux_file_id = shift; #handed the name of the resource object - # case 1: aux_file_id is complete or relative path to file - # case 2: aux_file_id is file name alone relative to the templates directory. - my $ext = shift; - - my $from_file_type = $ext; - my $to_file_type = "png"; # needed for conversion cases - - my $convert_fileQ = ( - $ext eq 'gif' # gif files need to be converted - # or $ext eq 'pdf' # other image types for tex - # or $ext eq 'jpg' - # or $ext eq 'svg' - # or $ext eq 'html' - ) ? 1 : 0; # does this file need conversion - - my $link_fileQ = 0; # does this file need to be linked? - my $targetDirectory = ($ext eq 'html') ? 'html' : 'images'; # subdirectory of tmp directory - -####################### - # gather needed data and declare it locally -####################### - my $htmlURL = $self->{htmlURL}; - my $htmlDirectory = $self->{htmlDirectory}; - my $pgFileName = $self->{pgFileName}; - my $tempURL = $self->{tempURL}; - my $tempDirectory = $self->{tempDirectory}; - my $templateDirectory = $self->{templateDirectory}; - -####################### - # retrieve PGresponse resource object -####################### - my ($resource_uri); - my $resource_object = $self->get_resource($aux_file_id); - #warn ( "\nresource for $aux_file_id is ", ref($resource_object), $resource_object ); + my ($self, $aux_file_id, $ext) = @_; -############################################## - # Find complete path to the original files -############################################## + my $resource_object = $self->get_resource($aux_file_id); - # get the directories that might contain html files - my $dirPath = ''; - if ($ext eq 'html') { - $dirPath = 'htmlPath'; - } else { - $dirPath = 'imagesPath'; + if ($aux_file_id =~ /https?:/) { + # External URL. + $resource_object->uri($aux_file_id); + return $resource_object->uri; # External URLs need no further processing. } - my @aux_files_directories = @{ $self->{envir}->{$dirPath} }; - if ($pgFileName) { + # Get the directories that might contain auxiliary files. + my @aux_files_directories = @{ $self->{envir}{ $ext eq 'html' ? 'htmlPath' : 'imagesPath' } }; + if ($self->{probFileName}) { # Replace "." with the current pg problem file directory. - my $current_pg_directory = $self->directoryFromPath($pgFileName); + my $current_pg_directory = $self->directoryFromPath($self->{probFileName}); $current_pg_directory = $self->{templateDirectory} . $current_pg_directory; @aux_files_directories = map { $_ eq '.' ? $current_pg_directory : $_ } @aux_files_directories; } else { @aux_files_directories = grep { $_ ne '.' } @aux_files_directories; } - # Find complete path to the original file - my $file_path; - if ($aux_file_id =~ /https?:/) { # external link_file - $resource_object->uri($aux_file_id); #no unique id is needed -- external link doc - $resource_object->{copy_link}->{type} = 'external'; - $resource_object->{uri}{is_accessible} = 1; # Assume a url is accessible. - return $resource_object->uri; # external links need no further processing - } elsif ($aux_file_id =~ m|^/|) { - $file_path = $aux_file_id; - } else { - $file_path = $self->find_file_in_directories($aux_file_id, \@aux_files_directories); + # Find complete path to the original file. + my $file_path = + $aux_file_id =~ m|^/| ? $aux_file_id : $self->find_file_in_directories($aux_file_id, \@aux_files_directories); + + unless ($file_path) { + $self->warning_message(qq{Unable to find "$aux_file_id" in any of the allowed auxiliary file directories.}); + return; } - #warn ("file path is $file_path"); - -##################### Case1: we've got a full pathname to a file in either the temp directory or the htmlDirectory -##################### Case2: we assume the file is in the same directory as the problem source file -##################### Case3: the file could have an external url - -############################################## - # store the complete path to the original file - # calculate the uri (which is a url suitable for the browser relative to the current site) - # store the uri. - # record status of the resource -############################################## - - if ($file_path =~ m|^$tempDirectory|) { #case: file is stored in the course temporary directory - - my $sourceFilePath = $file_path; - $resource_object->path($sourceFilePath); - $resource_object->{path}->{is_complete} = 1; - #warn("tempDir filePath ",$resource_object->path, "\n"); - # Gif files always need to be converted to png files for inclusion in pdflatex documents. - - $resource_object->{convert}->{needed} = $convert_fileQ; - $resource_object->{convert}->{from_path} = $sourceFilePath; - $resource_object->{convert}->{from_type} = $from_file_type; - $resource_object->{convert}->{to_path} = ''; #define later - $resource_object->{convert}->{to_type} = $to_file_type; - } elsif ($file_path =~ m|^$htmlDirectory|) { #case: file is under the course html directory - - my $sourceFilePath = $aux_file_id; - $resource_object->path($sourceFilePath); - $resource_object->{path}->{is_complete} = 1; - - $resource_object->{convert}->{needed} = $convert_fileQ; - $resource_object->{convert}->{from_path} = $sourceFilePath; - $resource_object->{convert}->{from_type} = $from_file_type; - $resource_object->{convert}->{to_path} = ''; #define later - $resource_object->{convert}->{to_type} = $to_file_type; - #warn ("htmlDir filePath ",$resource_object->path, "\n"); + # Store the complete path to the original file. + if ($file_path =~ m|^$self->{tempDirectory}|) { + # File is in the course temporary directory. + $resource_object->path($file_path); + } elsif ($file_path =~ m|^$self->{htmlDirectory}|) { + # File is in the course html directory. + $resource_object->path($aux_file_id); } else { - $resource_object->path($file_path); - $resource_object->{path}->{is_complete} = (-r $resource_object->path); - - $resource_object->{convert}->{needed} = $convert_fileQ; - $resource_object->{convert}->{from_path} = $resource_object->path(); - $resource_object->{convert}->{from_type} = $from_file_type; - $resource_object->{convert}->{to_path} = ''; #define later - $resource_object->{convert}->{to_type} = $to_file_type; - #warn ("templateDir filePath ",$resource_object->path, "\n"); - # notice the resource uri is not yet defined -- we have to make the link first } -################################################################################ - # Convert images to .png files if needed -################################################################################ - if ($resource_object->{convert}->{needed}) { #convert .gif to .png - - $self->convert_file_to_png_for_tex( - resource => $resource_object, - targetDirectory => $targetDirectory - ); - } else { # no conversion needed - $resource_object->uri($resource_object->path()); #path and uri are the same in this case. - $resource_object->{uri}->{is_complete} = 1; - $resource_object->{uri}->{is_accessible} = (-r $resource_object->uri()); + if ($ext eq 'gif' || $ext eq 'svg') { + # Convert gif and svg files to png files. + $self->convert_file_to_png_for_tex($resource_object, $ext eq 'html' ? 'html' : 'images'); + } else { + # Path and URI are the same in this case. + $resource_object->uri($resource_object->path); } -################################################################################ - # Don't need to create aliases in this case because nothing is being served over the web -################################################################################ - # Return full path to image file (resource_id) -################################################################################ - #warn ("final filePath ", $resource_object->uri(), "\n"); - #warn "file is a accessible ", $resource_object->{uri}->{is_accessible},"\n"; - # returns a file path - ($resource_object->{uri}->{is_accessible} == 1) ? $resource_object->uri() : ""; + # An alias is not needed in this case because nothing is being served over the web. + # Return the full path to the image file. + return $resource_object->uri && -r $resource_object->uri ? $resource_object->uri : ''; } -############################################################################ -# Utility for creating link from original file to alias in publically accessible temp directory -############################################################################ sub create_link_to_tmp_file { - my $self = shift; - my %args = @_; - my $resource_object = $args{resource}; - # warn "resource_object =", ref($resource_object); - my $unique_id = $resource_object->{unique_id}; - my $ext = $resource_object->{type}; - my $subdir = $args{subdir}; - my $link = "$subdir/$unique_id"; - ################# - # construct resource uri - ################# - my $resource_uri = $self->{tempURL}; - $resource_uri =~ s|/$||; #remove trailing slash, if any - $resource_uri = "$resource_uri/$link"; - ################# - # insure that linkPath exists and all intermediate directories have been created - ################# + my ($self, $resource_object, $subdir) = @_; + + my $ext = $resource_object->{type}; + my $link = "$subdir/$resource_object->{unique_id}"; + + # Insure that link path exists and all intermediate directories have been created. my $linkPath = $self->surePathToTmpFile($link); - if (-e $resource_object->path()) { - # if resource file exists - ################# - # destroy the old link. - ################# + if (-e $resource_object->path) { if (-e $linkPath) { - unlink($linkPath) || $self->warning_message("Unable to unlink alias file at |$linkPath|"); + # Destroy the old link. + unlink($linkPath) or $self->warning_message(qq{Unable to unlink alias file at "$linkPath".}); } - ################# - # create new link. - # create uri to this link - ################# - if (symlink($resource_object->path(), $linkPath)) { #create the symlink - $resource_object->{path}->{is_accessible} = 1; - $resource_object->{copy_link}->{link_to_path} = $linkPath; - $resource_object->{path}->{is_accessible} = (-r $linkPath); - - $resource_object->uri($resource_uri); - $resource_object->{uri}{is_accessible} = 1; # Assume a url is accessible. - $resource_object->{path}->{is_complete} = 1; - $resource_object->{uri}->{is_complete} = 1; + + # Create a new link, and the URI to this link. + if (symlink($resource_object->path, $linkPath)) { + $resource_object->uri(($self->{tempURL} =~ s|/$||r) . "/$link"); } else { $self->warning_message( - "The macro alias cannot create a link from |$linkPath| to |" . $resource_object->path() . "|
"); + qq{The macro alias cannot create a link from "$linkPath" to "} . $resource_object->path . '"'); } } else { - # if the resource file doesn't exist - my $message = ($resource_object->path()) ? " at |" . $resource_object->path() . "|" : " anywhere"; - $self->warning_message( - "The macro alias cannot find the file: |" . ($resource_object->fileName) . '|' . $message); - $resource_object->{path}->{is_accessible} = 0; - $resource_object->{uri}->{is_accessible} = 0; - # we should delete the resource object in this case? + $self->warning_message('Cannot find the file: "' + . $resource_object->fileName . '" ' + . ($resource_object->path ? ' at "' . $resource_object->path . '"' : ' anywhere')); } + return; } -############################################################################ -# Utility for converting .gif files to .png for tex -############################################################################ - sub convert_file_to_png_for_tex { - my $self = shift; - my %args = @_; - my $resource_object = $args{resource}; - my $targetDirectory = $args{targetDirectory}; - my $conversion_command = WeBWorK::PG::IO::externalCommand('gif2png'); - ################################################################################ - # Create path to new .png file - # Create new .png file - # We may not have permission to do this in the template directory - # so we create the file in the course temp directory. - ################################################################################ - my $ext = $resource_object->{type}; - $resource_object->create_unique_id($ext); - my $unique_id = $resource_object->{unique_id}; - $unique_id =~ s|\.[^/\.]*$|.png|; - my $link = "$targetDirectory/$unique_id"; - my $targetFilePath = $self->surePathToTmpFile($link); - $resource_object->{convert}->{to_path} = $targetFilePath; - $self->debug_message("target filePath ", $targetFilePath, "\n"); - my $sourceFilePath = $resource_object->{convert}->{from_path}; - $self->debug_message("convert filePath ", $sourceFilePath, "\n"); - # conversion_command is imported into this subroutine from the config files. - #$self->debug_message("cat $sourceFilePath | $conversion_command > $targetFilePath"); - my $returnCode = system "cat $sourceFilePath | $conversion_command > $targetFilePath"; - #$resource_object->debug_message( "FILE path $targetFilePath created =", -e $targetFilePath ); - #$resource_object->debug_message( "return Code $returnCode from cat $sourceFilePath | $command > $targetFilePath"); - if ($returnCode or not -e $targetFilePath) { + my ($self, $resource_object, $target_directory) = @_; + + $resource_object->create_unique_id($resource_object->{type}); + my $targetFilePath = + $self->surePathToTmpFile("$target_directory/" . ($resource_object->{unique_id} =~ s|\.[^/\.]*$|.png|r)); + $self->debug_message('target filePath ', $targetFilePath, "\n"); + my $sourceFilePath = $resource_object->path; + $self->debug_message('convert filePath ', $sourceFilePath, "\n"); + + my $conversion_command = WeBWorK::PG::IO::externalCommand('convert'); + my $returnCode = system "$conversion_command '${sourceFilePath}[0]' $targetFilePath"; + if ($returnCode || !-e $targetFilePath) { $resource_object->warning_message( - "returnCode $returnCode: failed to convert $sourceFilePath to $targetFilePath using gif->png with $conversion_command: $!" - ); + qq{Failed to convert "$sourceFilePath" to "$targetFilePath" using "$conversion_command": $!}); } - $resource_object->uri($resource_object->{convert}->{to_path}); - $resource_object->{uri}->{is_complete} = 1; - $resource_object->{uri}->{is_accessible} = (-r $resource_object->uri()); -} -################################################ - -# More resource search macros - -################################################ + $resource_object->uri($targetFilePath); -# -# Look for a macro file in the directories specified in the macros path -# - -# ^variable my $macrosPath -our ( - $macrosPath, - # ^variable my $pwd - $pwd, -); - -# ^function findMacroFile -# ^uses $macrosPath -# ^uses $pwd -sub findMacroFile { - my $self = shift; - my $fileName = shift; - my $filePath; - foreach my $dir (@{$macrosPath}) { - $filePath = "$dir/$fileName"; - $filePath =~ s!^\.\.?/!$pwd/!; # defined for PGloadFiles but not here - #FIXME? where is $pwd defined? why did it want to replace ../ with current directory - return $filePath if (-r $filePath); - } - return; # no file found + return; } sub find_file_in_directories { - my $self = shift; - my $file_name = shift; - my $directories = shift; - my $file_path; - foreach my $dir (@$directories) { - $dir =~ s|/$||; # remove final / if present - $file_path = "$dir/$file_name"; - return $file_path if (-r $file_path); + my ($self, $file_name, $directories) = @_; + for my $dir (@$directories) { + $dir =~ s|/$||; # Remove final / if present. + my $file_path = "$dir/$file_name"; + return $file_path if -r $file_path; } - return; # no file found -} - -# This is a stub for deprecated problems that call this method. Some of the Geogebra problems that do so actually work -# even though this method fails. -sub findAppletCodebase { - return ''; + return; # No file found. } 1; + +=head1 NAME + +PGalias - Create aliases for auxiliary resources. + +=head2 new + +Usage: C<< PGalias->new($envir, %options) >> + +The C constructor. The C<$envir> hash containing the problem +environment is required. The C<%options> can contain C and +C which should be array references. These are passed on to all +C objects constructed for each problem resource and are used by both +modules to store warning and debug messages. + +One C object is created for each C object (which is unique for +each problem). This object is used to construct unique ids for problem +resources and maintain a list of the resources used by a problem. A +unique_id_stub is generated for this C object which is the basis for +the unique ids generated for resource files (except equation images for the +"images" display mode) used by the problem. + +=head2 get_resource + +Usage: C<< $pgAlias->get_resource($aux_file_id) >> + +Returns the C object corresponding to C<$aux_file_id>. + +=head2 make_alias + +Usage: C<< $pgAlias->make_alias($aux_file_id) >> + +This is the workhorse of the C module. Its front end is C in +L. + +C takes the name of an auxiliary resource (html file, png file, +etc.) and creates a file name or URL appropriate to the current display mode. +It also does any necessary conversions behind the scenes. + +It returns the URL of the resource if the display mode is HTML or PTX, and the +full file path if the display mode is TeX. + +=head2 alias_for_html + +Usage: C<< $pgAlias->alias_for_html($aux_file_id, $ext) >> + +Returns the URL alias for the resource identified by C<$aux_file_id> with the +file name extension C<$ext>. + +=head2 alias_for_tex + +Usage: C<< $pgAlias->alias_for_tex($aux_file_id, $ext) >> + +Returns the full file path alias for the resource identified by C<$aux_file_id> +with the file name extension C<$ext>. + +=head2 create_link_to_tmp_file + +Usage: C<< $pgAlias->create_link_to_tmp_file($resource_object, $subdir) >> + +Creates a symbolic link in the subdirectory C<$subdir> of the publicly +accessible temporary directory to the file (usually in a course's templates +directory) represented by the C referenced by C<$resource_object>. +The link name is the file unique id alias. + +=head2 convert_file_to_png_for_tex + +Usage: C<< $pgAlias->convert_file_to_png_for_tex($resource_object, $target_directory) >> + +Converts a "gif" or "svg" file to a "png" file. The "png" file is saved in +C<$target_directory> and the file name is the unique id alias for the +C referenced by C<$resource_object>. + +=head2 find_file_in_directories + +Usage: C<< $pgAlias->find_file_in_directories($file_name, $directories) >> + +Finds the first directory in the array of directory names referenced to by +C<$directories> that contains a readable file named C<$file_name>, and returns +the full path of that file. + +=cut diff --git a/lib/PGcore.pm b/lib/PGcore.pm index 75f0d15eec..ade9a9fb8c 100755 --- a/lib/PGcore.pm +++ b/lib/PGcore.pm @@ -782,8 +782,7 @@ sub get_debug_messages { sub warning_message { my ($self, @str) = @_; - # Mark the start of each message. - push @{ $self->{WARNING_messages} }, '------', @str; + push @{ $self->{WARNING_messages} }, @str; } sub get_warning_messages { diff --git a/lib/PGloadfiles.pm b/lib/PGloadfiles.pm index f410c8eeea..9cf76e944e 100644 --- a/lib/PGloadfiles.pm +++ b/lib/PGloadfiles.pm @@ -84,7 +84,6 @@ sub new { my $self = { envir => $envir, macroFileList => {}, # records macros used in compilation - pgFileName => '', # current pg file being processed macrosPath => '', pwd => '', # current directory -- defined in initialize }; diff --git a/lib/PGresource.pm b/lib/PGresource.pm index 4494392d5e..bdac8a51ef 100644 --- a/lib/PGresource.pm +++ b/lib/PGresource.pm @@ -14,110 +14,137 @@ ################################################################################ package PGresource; +use parent PGcore; # This is so that a PGresource object can call the PGcore warning_message and debug_message methods. + use strict; -use Exporter; +use warnings; + use Scalar::Util; use UUID::Tiny ':std'; -use PGcore; -our @ISA = qw( PGcore ); sub new { - my $class = shift; - my $parent_alias = shift; - my $id = shift; - my $type = shift; - my %options = @_; - $type =~ s/^\.//; # remove initial period if included in type. - my $self = { - - id => $id, #auxiliary file name - parent_alias => $parent_alias, - type => $type, # gif eps pdf html pg (macro: pl) (applets: java js fla geogebra (ggb) swf ) - parent_file_id => $parent_alias->{pgFileName}, # file id for the file requesting the resource - - path => { - content => undef, # complete file path to resource - is_complete => 0, - is_accessible => 0, - }, - uri => { - content => undef, # usually url path to resource - is_complete => 0, - is_accessible => 0, - }, - return_uri => '', - recorded_uri => '', - convert => { - needed => 0, - from_type => undef, - from_path => undef, - to_type => undef, - to_path => undef, - }, - copy_link => { - type => undef, # copy or link or orig (original file, no copy or link needed) - link_to_path => undef, # the path of the alias - copy_to_path => undef, # the path of the duplicate file - }, - cache_info => {}, - unique_id => undef, - %options, - }; - bless $self, $class; + my ($class, $parent_alias, $id, $type, %options) = @_; + warn "PGresource must be called with a PGalias parent object." + unless ref($parent_alias) =~ /PGalias/; + + my $self = bless { + id => $id, # auxiliary file name + parent_alias => $parent_alias, + type => $type =~ s/^\.//r, # file extension + probFileName => $parent_alias->{probFileName}, + unique_id => undef, + path => undef, # complete file path to resource + uri => undef, # URL path (or complete file path for TeX) to resource + %options + }, $class; + Scalar::Util::weaken($self->{parent_alias}); - $self->warning_message("PGresource must be called with an alias object") unless ref($parent_alias) =~ /PGalias/; - $self->warning_message("PGresource must be called with a name") unless $id; - $self->warning_message("PGresource must be called with a type") unless $type; - # $self->warning_message( "Test warning message from resource object"); - # Use this to check if the warning and debug channels have been hooked up to PGcore and PGalias correctly. - return $self; -} -sub uri { - my $self = shift; - my $uri = shift; - $self->{uri}->{content} = $uri if $uri; - $self->{uri}->{content}; -} + $self->warning_message("PGresource must be called with a name.") unless $id; + $self->warning_message("PGresource must be called with a type.") unless $type; -sub path { - my $self = shift; - my $url = shift; - $self->{path}->{content} = $url if $url; - $self->{path}->{content}; + # Use this to check if the warning and debug channels have been hooked up to PGcore and PGalias correctly. + #$self->warning_message("Test warning message from resource object"); + #$self->debug_message("Test debug message from resource object"); + + return $self; } sub create_unique_id { - my $self = shift; - my $ext = shift; + my ($self, $ext) = @_; my $fileName = $self->fileName; + if ($self->{unique_id}) { $self->warning_message("unique id already exists for $fileName."); return $self->{unique_id}; } - $self->warning_message("auxiliary file $fileName missing resource path ") unless $self->path; - $self->warning_message("auxiliary file $fileName missing problem psvn") unless $self->{parent_alias}->{psvn}; - $self->warning_message("auxiliary file $fileName missing unique_id_stub") - unless $self->{parent_alias}->{unique_id_stub}; - my $unique_id_seed = $self->path() . $self->{parent_file_id} . $self->{id}; + + $self->warning_message(qq{Auxiliary file "$fileName" missing resource path.}) unless $self->path; + $self->warning_message(qq{Auxiliary file "$fileName" missing unique_id_stub.}) + unless $self->{parent_alias}{unique_id_stub}; + + my $unique_id_seed = $self->path . $self->{probFileName} . $self->{id}; $self->{unique_id} = - $self->{parent_alias}->{unique_id_stub} . '___' . create_uuid_as_string(UUID_V3, UUID_NS_URL, $unique_id_seed); + $self->{parent_alias}{unique_id_stub} . '___' . create_uuid_as_string(UUID_V3, UUID_NS_URL, $unique_id_seed); $self->{unique_id} .= ".$ext" if $ext; - $self->{unique_id}; + return $self->{unique_id}; +} + +sub uri { + my ($self, $uri) = @_; + $self->{uri} = $uri if $uri; + return $self->{uri}; +} + +sub path { + my ($self, $path) = @_; + $self->{path} = $path if $path; + return $self->{path}; } sub unique_id { - my $self = shift; - my $unique_id = shift; - $self->{unique_id} = $unique_id if $unique_id; - $self->{unique_id}; + my $self = shift; + return $self->{unique_id}; } sub fileName { - my $self = shift; - my $fileName = shift; + my ($self, $fileName) = @_; $self->{id} = $fileName if $fileName; - $self->{id}; + return $self->{id}; } + 1; + +=head1 NAME + +PGresource - Store information for an auxiliary resource. + +=head2 new + +Usage: C<< PGresource->new($parent_alias, $id, $type, %options) >> + +The C constructor. The C<$parent_alias>, C<$id>, and C<$type> +parameters are required. The C<$parent_alias> must be the parent C +object that calls this constructor (and this is the only situation where this +object should be constructed). The C<$id> should be the file name (or external +URL) of the auxiliary resource to be represented by this C object. +The C<$type> should be the file extension. The C<%options> should contain +C and C which should be array references. +These are used to store warning and debug messages. + +=head2 create_unique_id + +Usage: C<< $pgResource->create_unique_id($ext) >> + +This is the primary method of the C module. This generates a unique +id for the auxiliary resource that it represents. That id takes into account the +unique id stub of the parent C object, the full path to the resource, +the problem file name, and the resource file name. + +=head2 uri + +Usage: C<< $pgResource->uri($uri) >> + +Get or set the URI of the resource. + +=head2 path + +Usage: C<< $pgResource->path($path) >> + +Get or set the path of the resource. + +=head2 unique_id + +Usage: C<< $pgResource->unique_id >> + +Get the unique id of the resource. Note that the unique id is set by calling +C. + +=head2 fileName + +Usage: C<< $pgResource->fileName($fileName) >> + +Get or set the file name (or id) of the resource. + +=cut diff --git a/macros/PG.pl b/macros/PG.pl index 9ec9de88cb..2037f1de8a 100644 --- a/macros/PG.pl +++ b/macros/PG.pl @@ -1486,11 +1486,13 @@ sub ENDDOCUMENT { } sub alias { - $PG->{PG_alias}->make_alias(@_); + my $aux_file_id = shift; + return $PG->{PG_alias}->make_alias($aux_file_id); } sub get_resource { - $PG->{PG_alias}->get_resource(@_); + my $aux_file_id = shift; + return $PG->{PG_alias}->get_resource($aux_file_id); } sub maketext { @@ -1502,21 +1504,17 @@ sub insertGraph { } sub findMacroFile { - $PG->{PG_alias}->findMacroFile(@_); -} - -sub findAppletCodebase { - my $appletName = shift; - my $url = eval { $PG->{PG_alias}->findAppletCodebase($appletName) }; - # warn is already trapped under the old system - $PG->warning_message("While using findAppletCodebase to search for applet$appletName: $@") if $@; - $url; + $PG->{PG_loadMacros}->findMacroFile(@_); } sub loadMacros { $PG->{PG_loadMacros}->loadMacros(@_); } +# This is a stub for deprecated problems that call this method. Some of the GeoGebra +# problems that do so actually work even though this method does nothing. +sub findAppletCodebase { return ''; } + ## Problem Grader Subroutines #####################################