# The LearningOnline Network with CAPA # Metadata display handler # # $Id: lonmeta.pm,v 1.208 2007/05/15 20:21:17 albertel Exp $ # # Copyright Michigan State University Board of Trustees # # This file is part of the LearningOnline Network with CAPA (LON-CAPA). # # LON-CAPA is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # LON-CAPA is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with LON-CAPA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # /home/httpd/html/adm/gpl.txt # # http://www.lon-capa.org/ package Apache::lonmeta; use strict; use LONCAPA::lonmetadata(); use Apache::Constants qw(:common); use Apache::lonnet; use Apache::loncommon(); use Apache::lonhtmlcommon(); use Apache::lonmsg; use Apache::lonpublisher; use Apache::lonlocal; use Apache::lonmysql; use Apache::lonmsg; use LONCAPA qw(:DEFAULT :match); ############################################################ ############################################################ ## ## &get_dynamic_metadata_from_sql($url) ## ## Queries sql database for dynamic metdata ## Returns a hash of hashes, with keys of urls which match $url ## Returned fields are given below. ## ## Examples: ## ## %DynamicMetadata = &Apache::lonmeta::get_dynmaic_metadata_from_sql ## ('/res/msu/korte/'); ## ## $DynamicMetadata{'/res/msu/korte/example.problem'}->{$field} ## ############################################################ ############################################################ sub get_dynamic_metadata_from_sql { my ($url) = shift(); my ($authordom,$author)=($url=~m{^/res/($match_domain)/($match_username)/}); if (! defined($authordom)) { $authordom = shift(); } if (! defined($author)) { $author = shift(); } if (! defined($authordom) || ! defined($author)) { return (); } my $query = 'SELECT * FROM metadata WHERE url LIKE "'.$url.'%"'; my $server = &Apache::lonnet::homeserver($author,$authordom); my $reply = &Apache::lonnet::metadata_query($query,undef,undef, ,[$server]); return () if (! defined($reply) || ref($reply) ne 'HASH'); my $filename = $reply->{$server}; if (! defined($filename) || $filename =~ /^error/) { return (); } my $max_time = time + 10; # wait 10 seconds for results at most my %ReturnHash; # # Look for results my $finished = 0; while (! $finished && time < $max_time) { my $datafile=$Apache::lonnet::perlvar{'lonDaemons'}.'/tmp/'.$filename; if (! -e "$datafile.end") { next; } my $fh; if (!($fh=Apache::File->new($datafile))) { next; } while (my $result = <$fh>) { chomp($result); next if (! $result); my %hash=&LONCAPA::lonmetadata::metadata_col_to_hash('metadata', map { &unescape($_) } split(/\,/,$result)); foreach my $key (keys(%hash)) { $ReturnHash{$hash{'url'}}->{$key}=$hash{$key}; } } $finished = 1; } # return %ReturnHash; } # Fetch and evaluate dynamic metadata sub dynamicmeta { my $url=&Apache::lonnet::declutter(shift); $url=~s/\.meta$//; my ($adomain,$aauthor)=($url=~/^($match_domain)\/($match_username)\//); my $regexp=$url; $regexp=~s/(\W)/\\$1/g; $regexp='___'.$regexp.'___'; my %evaldata=&Apache::lonnet::dump('nohist_resevaldata',$adomain, $aauthor,$regexp); my %DynamicData = &LONCAPA::lonmetadata::process_reseval_data(\%evaldata); my %Data = &LONCAPA::lonmetadata::process_dynamic_metadata($url, \%DynamicData); # # Deal with 'count' separately $Data{'count'} = &access_count($url,$aauthor,$adomain); # # Debugging code I will probably need later if (0) { &Apache::lonnet::logthis('Dynamic Metadata'); while(my($k,$v)=each(%Data)){ &Apache::lonnet::logthis(' "'.$k.'"=>"'.$v.'"'); } &Apache::lonnet::logthis('-------------------'); } return %Data; } sub access_count { my ($src,$author,$adomain) = @_; my %countdata=&Apache::lonnet::dump('nohist_accesscount',$adomain, $author,$src); if (! exists($countdata{$src})) { return &mt('Not Available'); } else { return $countdata{$src}; } } # Try to make an alt tag if there is none sub alttag { my ($base,$src)=@_; my $fullpath=&Apache::lonnet::hreflocation($base,$src); my $alttag=&Apache::lonnet::metadata($fullpath,'title').' '. &Apache::lonnet::metadata($fullpath,'subject').' '. &Apache::lonnet::metadata($fullpath,'abstract'); $alttag=~s/\s+/ /gs; $alttag=~s/\"//gs; $alttag=~s/\'//gs; $alttag=~s/\s+$//gs; $alttag=~s/^\s+//gs; if ($alttag) { return $alttag; } else { return &mt('No information available'); } } # Author display sub authordisplay { my ($aname,$adom)=@_; return &Apache::loncommon::aboutmewrapper (&Apache::loncommon::plainname($aname,$adom), $aname,$adom,'preview').' ['.$aname.':'.$adom.']'; } # Pretty display sub evalgraph { my $value=shift; if (! $value) { return ''; } my $val=int($value*10.+0.5)-10; my $output=''; if ($val>=20) { $output.=''; } else { $output.=''. ''; } $output.=''; if ($val>20) { $output.=''. ''; } else { $output.=''; } $output.='
          ('.sprintf("%5.2f",$value).')
'; return $output; } sub diffgraph { my $value=shift; if (! $value) { return ''; } my $val=int(40.0*$value+0.5); my @colors=('#FF9933','#EEAA33','#DDBB33','#CCCC33', '#BBDD33','#CCCC33','#DDBB33','#EEAA33'); my $output=''; for (my $i=0;$i<8;$i++) { if ($val>$i*5) { $output.=''; } else { $output.=''; } } $output.='
   ('.sprintf("%3.2f",$value).')
'; return $output; } # The field names sub fieldnames { my $file_type=shift; my %fields = ('title' => 'Title', 'author' =>'Author(s)', 'authorspace' => 'Author Space', 'modifyinguser' => 'Last Modifying User', 'subject' => 'Subject', 'standards' => 'Standards', 'keywords' => 'Keyword(s)', 'notes' => 'Notes', 'abstract' => 'Abstract', 'lowestgradelevel' => 'Lowest Grade Level', 'highestgradelevel' => 'Highest Grade Level'); if ( !defined($file_type) || ($file_type ne 'portfolio' && $file_type ne 'groups') ) { %fields = (%fields, 'domain' => 'Domain', 'mime' => 'MIME Type', 'language' => 'Language', 'creationdate' => 'Creation Date', 'lastrevisiondate' => 'Last Revision Date', 'owner' => 'Publisher/Owner', 'copyright' => 'Copyright/Distribution', 'customdistributionfile' => 'Custom Distribution File', 'sourceavail' => 'Source Available', 'sourcerights' => 'Source Custom Distribution File', 'obsolete' => 'Obsolete', 'obsoletereplacement' => 'Suggested Replacement for Obsolete File', 'count' => 'Network-wide number of accesses (hits)', 'course' => 'Network-wide number of courses using resource', 'course_list' => 'Network-wide courses using resource', 'sequsage' => 'Number of resources using or importing resource', 'sequsage_list' => 'Resources using or importing resource', 'goto' => 'Number of resources that follow this resource in maps', 'goto_list' => 'Resources that follow this resource in maps', 'comefrom' => 'Number of resources that lead up to this resource in maps', 'comefrom_list' => 'Resources that lead up to this resource in maps', 'clear' => 'Material presented in clear way', 'depth' => 'Material covered with sufficient depth', 'helpful' => 'Material is helpful', 'correct' => 'Material appears to be correct', 'technical' => 'Resource is technically correct', 'avetries' => 'Average number of tries till solved', 'stdno' => 'Statistics calculated for number of students', 'difficulty' => 'Degree of difficulty', 'disc' => 'Degree of discrimination', 'dependencies' => 'Resources used by this resource', ); } return &Apache::lonlocal::texthash(%fields); } sub portfolio_linked_path { my ($path,$group,$port_path) = @_; my $start = 'portfolio'; if ($group) { $start = "groups/$group/".$start; } my %anchor_fields = ( 'selectfile' => $start, 'currentpath' => '/' ); my $result = &Apache::portfolio::make_anchor($port_path,\%anchor_fields,$start); my $fullpath = '/'; my (undef,@tree) = split('/',$path); my $filename = pop(@tree); foreach my $dir (@tree) { $fullpath .= $dir.'/'; $result .= '/'; my %anchor_fields = ( 'selectfile' => $dir, 'currentpath' => $fullpath ); $result .= &Apache::portfolio::make_anchor($port_path,\%anchor_fields,$dir); } $result .= "/$filename"; return $result; } sub get_port_path_and_group { my ($uri)=@_; my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; my ($port_path,$group); if ($uri =~ m{^/editupload/\Q$cdom\E/\Q$cnum\E/groups/}) { $group = (split('/',$uri))[5]; $port_path = '/adm/coursegrp_portfolio'; } else { $port_path = '/adm/portfolio'; } if ($env{'form.group'} ne $group) { $env{'form.group'} = $group; } return ($port_path,$group); } sub portfolio_display_uri { my ($uri,$as_links)=@_; my ($port_path,$group) = &get_port_path_and_group($uri); $uri =~ s|.*/(portfolio/.*)$|$1|; my ($res_uri,$meta_uri) = ($uri,$uri); if ($uri =~ /\.meta$/) { $res_uri =~ s/\.meta//; } else { $meta_uri .= '.meta'; } my ($path) = ($res_uri =~ m|^portfolio(.*/)[^/]*$|); if ($as_links) { $res_uri = &portfolio_linked_path($res_uri,$group,$port_path); $meta_uri = &portfolio_linked_path($meta_uri,$group,$port_path); } return ($res_uri,$meta_uri,$path); } sub pre_select_course { my ($r,$uri) = @_; my $output; my $fn=&Apache::lonnet::filelocation('',$uri); my ($res_uri,$meta_uri,$path) = &portfolio_display_uri($uri); %Apache::lonpublisher::metadatafields=(); %Apache::lonpublisher::metadatakeys=(); my $result=&Apache::lonnet::getfile($fn); if ($result == -1){ $r->print(&mt('Creating new file [_1]'),$meta_uri); } else { &Apache::lonpublisher::metaeval($result); } $r->print('
'); $r->print('

'.&mt('If you would like to associate this resource ([_1]) with a current or previous course, please select one from the list below, otherwise select, \'None\'',''.$res_uri.'').'

'); $output = &select_course(); $r->print($output.'
'); $r->print(''); $r->print(''); $r->print('
'); my ($port_path,$group) = &get_port_path_and_group($uri); my $group_input; if ($group) { $group_input = ''; } $r->print('

'. ''. $group_input. ''. '
'); return; } sub select_course { my $output=$/; my $current_restriction= $Apache::lonpublisher::metadatafields{'courserestricted'}; my $selected = ($current_restriction eq 'none' ? 'selected="selected"' : ''); if ($current_restriction =~ /^course\.($match_domain\_$match_courseid)$/) { my $assoc_crs = $1; my $added_metadata_fields = &Apache::lonparmset::get_added_meta_fieldnames($assoc_crs); if (ref($added_metadata_fields) eq 'HASH') { if (keys(%{$added_metadata_fields}) > 0) { my $transfernotes; foreach my $field_name (keys(%{$added_metadata_fields})) { my $value = $Apache::lonpublisher::metadatafields{$field_name}; if ($value) { $transfernotes .= &Apache::loncommon::start_data_table_row(). ''. $field_name.''.$value.''. &Apache::loncommon::end_data_table_row(); } } if ($transfernotes ne '') { my %courseinfo = &Apache::lonnet::coursedescription($assoc_crs,{'one_time' => 1}); my $assoc_crs_description = $courseinfo{'description'}; $output .= &mt('This resource is currently associated with a course ([_1]) which includes added metadata fields specific to the course.',$assoc_crs_description).'
'."\n". &mt('You can choose to transfer data from the added fields to the "Notes" field if you are planning to change the course association.').'

'. &Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_header_row(). 'Copy to notes?'."\n". 'Field name'."\n". 'Values'."\n". &Apache::loncommon::end_data_table_header_row(). $transfernotes. &Apache::loncommon::end_data_table().'
'; } } } } $output .= '
'; return ($output); } # Pretty printing of metadata field sub prettyprint { my ($type,$value,$target,$prefix,$form,$noformat)=@_; # $target,$prefix,$form are optional and for filecrumbs only if (! defined($value)) { return ' '; } # Title if ($type eq 'title') { return ''.$value.''; } # Dates if (($type eq 'creationdate') || ($type eq 'lastrevisiondate')) { return ($value?&Apache::lonlocal::locallocaltime( &Apache::lonmysql::unsqltime($value)): &mt('not available')); } # Language if ($type eq 'language') { return &Apache::loncommon::languagedescription($value); } # Copyright if ($type eq 'copyright') { return &Apache::loncommon::copyrightdescription($value); } # Copyright if ($type eq 'sourceavail') { return &Apache::loncommon::source_copyrightdescription($value); } # MIME if ($type eq 'mime') { return ' '. &Apache::loncommon::filedescription($value); } # Person if (($type eq 'author') || ($type eq 'owner') || ($type eq 'modifyinguser') || ($type eq 'authorspace')) { $value=~s/($match_username)(\:|\@)($match_domain)/&authordisplay($1,$3)/gse; return $value; } # Gradelevel if (($type eq 'lowestgradelevel') || ($type eq 'highestgradelevel')) { return &Apache::loncommon::gradeleveldescription($value); } # Only for advance users below if (! $env{'user.adv'}) { return '- '.&mt('not displayed').' -'; } # File if (($type eq 'customdistributionfile') || ($type eq 'obsoletereplacement') || ($type eq 'goto_list') || ($type eq 'comefrom_list') || ($type eq 'sequsage_list') || ($type eq 'dependencies')) { return ''; } # Evaluations if (($type eq 'clear') || ($type eq 'depth') || ($type eq 'helpful') || ($type eq 'correct') || ($type eq 'technical')) { return &evalgraph($value); } # Difficulty if ($type eq 'difficulty' || $type eq 'disc') { return &diffgraph($value); } # List of courses if ($type=~/\_list/) { my @Courses = split(/\s*\,\s*/,$value); my $Str=''; } # No pretty print found return $value; } # Pretty input of metadata field sub direct { return shift; } sub selectbox { my ($name,$value,$functionref,@idlist)=@_; if (! defined($functionref)) { $functionref=\&direct; } my $selout=''; } sub relatedfield { my ($show,$relatedsearchflag,$relatedsep,$fieldname,$relatedvalue)=@_; if (! $relatedsearchflag) { return ''; } if (! defined($relatedsep)) { $relatedsep=' '; } if (! $show) { return $relatedsep.' '; } return $relatedsep.''; } sub prettyinput { my ($type,$value,$fieldname,$formname, $relatedsearchflag,$relatedsep,$relatedvalue,$size,$course_key)=@_; if (! defined($size)) { $size = 80; } my $output; if (defined($course_key) && exists($env{$course_key.'.metadata.'.$type.'.options'})) { my $stu_add; my $only_one; my %meta_options; my @cur_values_inst; my $cur_values_stu; my $values = $env{$course_key.'.metadata.'.$type.'.values'}; if ($env{$course_key.'.metadata.'.$type.'.options'} =~ m/stuadd/) { $stu_add = 'true'; } if ($env{$course_key.'.metadata.'.$type.'.options'} =~ m/onlyone/) { $only_one = 'true'; } # need to take instructor values out of list where instructor and student # values may be mixed. if ($values) { foreach my $item (split(/,/,$values)) { $item =~ s/^\s+//; $meta_options{$item} = $item; } foreach my $item (split(/,/,$value)) { $item =~ s/^\s+//; if ($meta_options{$item}) { push(@cur_values_inst,$item); } else { if ($item ne '') { $cur_values_stu .= $item.','; } } } $cur_values_stu =~ s/,$//; my @key_order = sort(keys(%meta_options)); unshift(@key_order,''); $meta_options{''} = 'Not specified'; $meta_options{'select_form_order'} = \@key_order; } else { $cur_values_stu = $value; } if ($type eq 'courserestricted') { return (&select_course()); # return (''); } if (($type eq 'keywords') || ($type eq 'subject') || ($type eq 'author')||($type eq 'notes') || ($type eq 'abstract')|| ($type eq 'title')|| ($type eq 'standards') || (exists($env{$course_key.'.metadata.'.$type.'.added'}))) { if ($values) { if ($only_one) { $output .= (&Apache::loncommon::select_form($cur_values_inst[0],'new_'.$type,%meta_options)); } else { $output .= (&Apache::loncommon::multiple_select_form('new_'.$type,\@cur_values_inst,undef,\%meta_options)); } } if ($stu_add) { $output .= ''. &relatedfield(1,$relatedsearchflag,$relatedsep,$fieldname, $relatedvalue); } return ($output); } if (($type eq 'lowestgradelevel') || ($type eq 'highestgradelevel')) { return &Apache::loncommon::select_level_form($value,$fieldname). &relatedfield(0,$relatedsearchflag,$relatedsep); } return(); } # Language if ($type eq 'language') { return &selectbox($fieldname, $value, \&Apache::loncommon::languagedescription, (&Apache::loncommon::languageids)). &relatedfield(0,$relatedsearchflag,$relatedsep); } # Copyright if ($type eq 'copyright') { return &selectbox($fieldname, $value, \&Apache::loncommon::copyrightdescription, (&Apache::loncommon::copyrightids)). &relatedfield(0,$relatedsearchflag,$relatedsep); } # Source Copyright if ($type eq 'sourceavail') { return &selectbox($fieldname, $value, \&Apache::loncommon::source_copyrightdescription, (&Apache::loncommon::source_copyrightids)). &relatedfield(0,$relatedsearchflag,$relatedsep); } # Gradelevels if (($type eq 'lowestgradelevel') || ($type eq 'highestgradelevel')) { return &Apache::loncommon::select_level_form($value,$fieldname). &relatedfield(0,$relatedsearchflag,$relatedsep); } # Obsolete if ($type eq 'obsolete') { return ''. &relatedfield(0,$relatedsearchflag,$relatedsep); } # Obsolete replacement file if ($type eq 'obsoletereplacement') { return '".&mt('Select').''. &relatedfield(0,$relatedsearchflag,$relatedsep); } # Customdistribution file if ($type eq 'customdistributionfile') { return '".&mt('Select').''. &relatedfield(0,$relatedsearchflag,$relatedsep); } # Source Customdistribution file if ($type eq 'sourcerights') { return '".&mt('Select').''. &relatedfield(0,$relatedsearchflag,$relatedsep); } if ($type eq 'courserestricted') { return (&select_course()); #return (''); } # Dates if (($type eq 'creationdate') || ($type eq 'lastrevisiondate')) { return &Apache::lonhtmlcommon::date_setter($formname,$fieldname,$value). &relatedfield(0,$relatedsearchflag,$relatedsep); } # No pretty input found $value=~s/^\s+//gs; $value=~s/\s+$//gs; $value=~s/\s+/ /gs; $value=~s/\"/\"\;/gs; return ''. &relatedfield(1,$relatedsearchflag,$relatedsep,$fieldname, $relatedvalue); } # Main Handler sub handler { my $r=shift; &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, ['currentpath','changecourse']); my $uri=$r->uri; # # Set document type &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; return OK if $r->header_only; my ($resdomain,$resuser)= (&Apache::lonnet::declutter($uri)=~/^($match_domain)\/($match_username)\//); if ($uri=~m:/adm/bombs/(.*)$:) { $r->print(&Apache::loncommon::start_page('Error Messages')); # Looking for all bombs? &report_bombs($r,$uri); } elsif ($uri=~m|^/editupload/[^/]+/[^/]+/portfolio/|) { ($resdomain,$resuser)= (&Apache::lonnet::declutter($uri)=~m|^($match_domain)/($match_name)/portfolio|); $r->print(&Apache::loncommon::start_page('Edit Portfolio File Catalog Information', undef, {'domain' => $resdomain,})); if ($env{'form.store'}) { &present_editable_metadata($r,$uri,'portfolio'); } else { my $fn=&Apache::lonnet::filelocation('',$uri); %Apache::lonpublisher::metadatafields=(); %Apache::lonpublisher::metadatakeys=(); my $result=&Apache::lonnet::getfile($fn); &Apache::lonpublisher::metaeval($result); if ((!$Apache::lonpublisher::metadatafields{'courserestricted'}) || ($env{'form.changecourse'} eq 'true')) { &pre_select_course($r,$uri); } else { &present_editable_metadata($r,$uri,'portfolio'); } } } elsif ($uri=~m|^/editupload/[^/]+/[^/]+/groups/|) { $r->print(&Apache::loncommon::start_page('Edit Group Portfolio File Catalog Information', undef, {'domain' => $resdomain,})); &present_editable_metadata($r,$uri,'groups'); } elsif ($uri=~m|^/~|) { # Construction space $r->print(&Apache::loncommon::start_page('Edit Catalog Information', "\n".'', {'domain' => $resdomain,})); &present_editable_metadata($r,$uri); } else { $r->print(&Apache::loncommon::start_page('Catalog Information', undef, {'domain' => $resdomain,})); &present_uneditable_metadata($r,$uri); } $r->print(&Apache::loncommon::end_page()); return OK; } ##################################################### ##################################################### ### ### ### Report Bombs ### ### ### ##################################################### ##################################################### sub report_bombs { my ($r,$uri) = @_; # Set document type $uri =~ s:/adm/bombs/::; $uri = &Apache::lonnet::declutter($uri); $r->print('

'.&Apache::lonnet::clutter($uri).'

'); my ($domain,$author)=($uri=~/^($match_domain)\/($match_username)\//); if (&Apache::loncacc::constructaccess('/~'.$author.'/',$domain)) { if ($env{'form.clearbombs'}) { &Apache::lonmsg::clear_author_res_msg($uri); } my $clear=&mt('Clear all Messages in Subdirectory'); $r->print(< ENDCLEAR my %brokenurls = &Apache::lonmsg::all_url_author_res_msg($author,$domain); foreach (sort(keys(%brokenurls))) { if ($_=~/^\Q$uri\E/) { $r->print (''.$_.''. &Apache::lonmsg::retrieve_author_res_msg($_). '
'); } } } else { $r->print(&mt('Not authorized')); } return; } ##################################################### ##################################################### ### ### ### Uneditable Metadata Display ### ### ### ##################################################### ##################################################### sub present_uneditable_metadata { my ($r,$uri) = @_; # my $uploaded = ($uri =~ m|/uploaded/|); my %content=(); # Read file foreach (split(/\,/,&Apache::lonnet::metadata($uri,'keys'))) { $content{$_}=&Apache::lonnet::metadata($uri,$_); } # Render Output # displayed url my ($thisversion)=($uri=~/\.(\d+)\.(\w+)\.meta$/); $uri=~s/\.meta$//; my $disuri=&Apache::lonnet::clutter_with_no_wrapper($uri); # version my $versiondisplay=''; if (!$uploaded) { my $currentversion=&Apache::lonnet::getversion($disuri); if ($thisversion) { $versiondisplay=&mt('Version').': '.$thisversion. ' ('.&mt('most recent version').': '. ($currentversion>0 ? $currentversion : &mt('information not available')).')'; } else { $versiondisplay='Version: '.$currentversion; } } # crumbify displayed URL uri target prefix form size $disuri=&Apache::lonhtmlcommon::crumbs($disuri,undef, undef, undef,'+1'); $disuri =~ s:
::g; # obsolete my $obsolete=$content{'obsolete'}; my $obsoletewarning=''; if (($obsolete) && ($env{'user.adv'})) { $obsoletewarning='

'. &mt('This resource has been marked obsolete by the author(s)'). '

'; } # my %lt=&fieldnames(); my $table=''; my $title = $content{'title'}; if (! defined($title)) { $title = 'Untitled Resource'; } my @fields; if ($uploaded) { @fields = ('title','author','subject','keywords','notes','abstract', 'lowestgradelevel','highestgradelevel','standards','mime', 'owner'); } else { @fields = ('title', 'author', 'subject', 'keywords', 'notes', 'abstract', 'lowestgradelevel', 'highestgradelevel', 'standards', 'mime', 'language', 'creationdate', 'lastrevisiondate', 'owner', 'copyright', 'customdistributionfile', 'sourceavail', 'sourcerights', 'obsolete', 'obsoletereplacement'); } foreach my $field (@fields) { $table.=''.$lt{$field}. ''. &prettyprint($field,$content{$field}).''; delete($content{$field}); } # $r->print(<$title

$disuri
$obsoletewarning $versiondisplay

$table
ENDHEAD if (!$uploaded && $env{'user.adv'}) { &print_dynamic_metadata($r,$uri,\%content); } return; } sub print_dynamic_metadata { my ($r,$uri,$content) = @_; # my %content = %$content; my %lt=&fieldnames(); # my $description = 'Dynamic Metadata (updated periodically)'; $r->print('

'.&mt($description).'

'. &mt('Processing')); $r->rflush(); my %items=&fieldnames(); my %dynmeta=&dynamicmeta($uri); # # General Access and Usage Statistics if (exists($dynmeta{'count'}) || exists($dynmeta{'sequsage'}) || exists($dynmeta{'comefrom'}) || exists($dynmeta{'goto'}) || exists($dynmeta{'course'})) { $r->print('

'.&mt('Access and Usage Statistics').'

'. ''); foreach ('count', 'sequsage','sequsage_list', 'comefrom','comefrom_list', 'goto','goto_list', 'course','course_list') { $r->print(''. '\n"); } $r->print('
'.$lt{$_}.''. &prettyprint($_,$dynmeta{$_})."
'); } else { $r->print('

'.&mt('No Access or Usages Statistics are available for this resource.').'

'); } # # Assessment statistics if ($uri=~/\.(problem|exam|quiz|assess|survey|form)$/) { if (exists($dynmeta{'stdno'}) || exists($dynmeta{'avetries'}) || exists($dynmeta{'difficulty'}) || exists($dynmeta{'disc'})) { # This is an assessment, print assessment data $r->print('

'. &mt('Overall Assessment Statistical Data'). '

'. ''); $r->print(''. ''."\n"); foreach ('avetries','difficulty','disc') { $r->print(''. ''."\n"); } $r->print('
'.$lt{'stdno'}.''. &prettyprint('stdno',$dynmeta{'stdno'}). '
'.$lt{$_}.''. &prettyprint($_,sprintf('%5.2f',$dynmeta{$_})). '
'); } if (exists($dynmeta{'stats'})) { # # New assessment statistics $r->print('

'. &mt('Recent Detailed Assessment Statistical Data'). '

'); my $table = ''. ''. ''. ''. ''. ''. ''. ''. ''. ''.$/; foreach my $identifier (sort(keys(%{$dynmeta{'stats'}}))) { my $data = $dynmeta{'stats'}->{$identifier}; my $course = $data->{'course'}; my %courseinfo = &Apache::lonnet::coursedescription($course, {'one_time' => 1}); if (! exists($courseinfo{'num'}) || $courseinfo{'num'} eq '') { &Apache::lonnet::logthis('lookup for '.$course.' failed'); next; } $table .= ''; $table .= ''; $table .= ''; $table .= ''; foreach ('avetries','difficulty','disc') { $table .= ''; } $table .= ''; $table .= ''.$/; } $table .= '
'.&mt('Course').''.&mt('Section(s)').''.&mt('Num Student').'s'.&mt('Mean Tries').''.&mt('Degree of Difficulty').''.&mt('Degree of Discrimination').''.&mt('Time of computation').'
'.$courseinfo{'description'}.''.$data->{'sections'}.''.$data->{'stdno'}.''; if (exists($data->{$_})) { $table .= sprintf('%.2f',$data->{$_}).' '; } else { $table .= ''; } $table .= ''. &Apache::lonlocal::locallocaltime($data->{'timestamp'}). '
'.$/; $r->print($table); } else { $r->print('No new dynamic data found.'); } } else { $r->print('

'. &mt('No Assessment Statistical Data is available for this resource'). '

'); } # # if (exists($dynmeta{'clear'}) || exists($dynmeta{'depth'}) || exists($dynmeta{'helpful'}) || exists($dynmeta{'correct'}) || exists($dynmeta{'technical'})){ $r->print('

'.&mt('Evaluation Data').'

'. ''); foreach ('clear','depth','helpful','correct','technical') { $r->print(''. '\n"); } $r->print('
'.$lt{$_}.''. &prettyprint($_,$dynmeta{$_})."
'); } else { $r->print('

'.&mt('No Evaluation Data is available for this resource.').'

'); } $uri=~/^\/res\/($match_domain)\/($match_username)\//; if ((($env{'user.domain'} eq $1) && ($env{'user.name'} eq $2)) || ($env{'user.role.ca./'.$1.'/'.$2})) { if (exists($dynmeta{'comments'})) { $r->print('

'.&mt('Evaluation Comments').' ('. &mt('visible to author and co-authors only'). ')

'. '
'.$dynmeta{'comments'}.'
'); } else { $r->print('

'.&mt('There are no Evaluation Comments on this resource.').'

'); } my $bombs = &Apache::lonmsg::retrieve_author_res_msg($uri); if (defined($bombs) && $bombs ne '') { $r->print('

'.&mt('Error Messages').' ('. &mt('visible to author and co-authors only').')'. '

'.$bombs); } else { $r->print('

'.&mt('There are currently no Error Messages for this resource.').'

'); } } # # All other stuff $r->print('

'. &mt('Additional Metadata (non-standard, parameters, exports)'). '

'); foreach (sort(keys(%content))) { my $name=$_; if ($name!~/\.display$/) { my $display=&Apache::lonnet::metadata($uri, $name.'.display'); if (! $display) { $display=$name; }; my $otherinfo=''; foreach ('name','part','type','default') { if (defined(&Apache::lonnet::metadata($uri, $name.'.'.$_))) { $otherinfo.=' '.$_.'='. &Apache::lonnet::metadata($uri, $name.'.'.$_).'; '; } } $r->print('\n"); } } $r->print("
'.$display.''.$content{$name}); if ($otherinfo) { $r->print(' ('.$otherinfo.')'); } $r->print("
"); return; } ##################################################### ##################################################### ### ### ### Editable metadata display ### ### ### ##################################################### ##################################################### sub present_editable_metadata { my ($r,$uri,$file_type) = @_; # Construction Space Call # Header my $disuri=$uri; my $fn=&Apache::lonnet::filelocation('',$uri); $disuri=~s{^/\~}{/priv/}; $disuri=~s/\.meta$//; my $meta_uri = $disuri; my $path; if ($disuri =~ m|/portfolio/|) { ($disuri, $meta_uri, $path) = &portfolio_display_uri($disuri,1); } my $target=$uri; $target=~s{^/\~}{/res/$env{'request.role.domain'}/}; $target=~s/\.meta$//; my $bombs=&Apache::lonmsg::retrieve_author_res_msg($target); if ($bombs) { my $showdel=1; if ($env{'form.delmsg'}) { if (&Apache::lonmsg::del_url_author_res_msg($target) eq 'ok') { $bombs=&mt('Messages deleted.'); $showdel=0; } else { $bombs=&mt('Error deleting messages'); } } if ($env{'form.clearmsg'}) { my $cleardir=$target; $cleardir=~s/\/[^\/]+$/\//; if (&Apache::lonmsg::clear_author_res_msg($cleardir) eq 'ok') { $bombs=&mt('Messages cleared.'); $showdel=0; } else { $bombs=&mt('Error clearing messages'); } } my $del=&mt('Delete Messages for this Resource'); my $clear=&mt('Clear all Messages in Subdirectory'); my $goback=&mt('Back to Source File'); $r->print(<$disuri
ENDBOMBS if ($showdel) { $r->print(< ENDDEL } else { $r->print('

'.$goback.'

'); if ($env{'form.clearmsg'}) { my ($diruri) = ($disuri =~ m{(.*/)[^/]*}); $r->print('

'. &mt('Back To Directory').'

'); } } $r->print('
'.$bombs); } else { my $displayfile='Catalog Information for '.$disuri; if ($disuri=~/\/default$/) { my $dir=$disuri; $dir=~s/default$//; $displayfile= &mt('Default Cataloging Information for Directory').' '. $dir; } %Apache::lonpublisher::metadatafields=(); %Apache::lonpublisher::metadatakeys=(); my $result=&Apache::lonnet::getfile($fn); if ($result == -1){ $r->print(&mt('Creating new file [_1]'),$meta_uri); } else { &Apache::lonpublisher::metaeval($result); } if ($env{'form.new_courserestricted'}) { my $new_assoc_course = $env{'form.new_courserestricted'}; my $prev_courserestricted = $Apache::lonpublisher::metadatafields{'courserestricted'}; if (($prev_courserestricted) && ($prev_courserestricted ne $new_assoc_course)) { my $transfers = []; foreach my $key (keys(%env)) { if ($key =~ /^form\.transfer_(.+)$/) { push(@{$transfers},$1); } } if (@{$transfers} > 0) { &store_transferred_addedfields($fn,$uri,$transfers); } } } $r->print(<$displayfile ENDEDIT my %lt=&fieldnames($file_type); my $output; my @fields; my $added_metadata_fields; my @added_order; if ($file_type eq 'groups') { $Apache::lonpublisher::metadatafields{'courserestricted'}= 'course.'.$env{'request.course.id'}; } if ((! $Apache::lonpublisher::metadatafields{'courserestricted'}) && (! $env{'form.new_courserestricted'}) && (! $file_type eq 'groups')) { $Apache::lonpublisher::metadatafields{'courserestricted'}= 'none'; } elsif ($env{'form.new_courserestricted'}) { $Apache::lonpublisher::metadatafields{'courserestricted'}= $env{'form.new_courserestricted'}; } if ($file_type eq 'portfolio' || $file_type eq 'groups') { if(exists ($env{$Apache::lonpublisher::metadatafields{'courserestricted'}.'.metadata.fieldlist'})) { # retrieve fieldnames (in order) from the course restricted list @fields = (split(/,/,$env{$Apache::lonpublisher::metadatafields{'courserestricted'}.'.metadata.fieldlist'})); } else { # no saved field list, use default list @fields = ('author','title','subject','keywords','abstract', 'notes','lowestgradelevel', 'highestgradelevel','standards'); if ($Apache::lonpublisher::metadatafields{'courserestricted'} =~ /^course\.($match_domain\_$match_courseid)$/) { my $assoc_crs = $1; $added_metadata_fields = &Apache::lonparmset::get_added_meta_fieldnames($assoc_crs); if ($env{'course.'.$assoc_crs.'.metadata.addedorder'}) { @added_order = split(/,/,$env{'course.'.$assoc_crs.'.metadata.addedorder'}); } $env{$Apache::lonpublisher::metadatafields{'courserestricted'}.'.metadata.fieldlist'} = join(",",@fields); } } } else { @fields = ('author','title','subject','keywords','abstract','notes', 'copyright','customdistributionfile','language', 'standards', 'lowestgradelevel','highestgradelevel','sourceavail','sourcerights', 'obsolete','obsoletereplacement'); } if (! $Apache::lonpublisher::metadatafields{'copyright'}) { $Apache::lonpublisher::metadatafields{'copyright'}= 'default'; } if (($file_type eq 'portfolio') || ($file_type eq 'groups')) { if (! $Apache::lonpublisher::metadatafields{'mime'}) { ($Apache::lonpublisher::metadatafields{'mime'}) = ( $target=~/\.(\w+)$/ ); } if (! $Apache::lonpublisher::metadatafields{'owner'}) { $Apache::lonpublisher::metadatafields{'owner'} = $env{'user.name'}.':'.$env{'user.domain'}; } if ($Apache::lonpublisher::metadatafields{'courserestricted'} ne 'none') { if ($file_type eq 'portfolio') { $r->print(&mt('Associated with course [_1]', ''. $env{$Apache::lonpublisher::metadatafields{'courserestricted'}. ".description"}. '').'
'); } else { $r->print(&mt('Associated with course [_1]', ''. $env{$Apache::lonpublisher::metadatafields{'courserestricted'}. ".description"}.'').'
'); } } else { $r->print('This resource is not associated with a course.
'); } } if (@added_order) { foreach my $field_name (@added_order) { push(@fields,$field_name); $lt{$field_name} = $$added_metadata_fields{$field_name}; } } else { foreach my $field_name (keys(%$added_metadata_fields)) { push(@fields,$field_name); $lt{$field_name} = $$added_metadata_fields{$field_name}; } } $output .= &Apache::loncommon::start_data_table(); my $row_alt = 1; foreach my $field_name (@fields) { if (defined($env{'form.new_'.$field_name})) { my @values = &Apache::loncommon::get_env_multiple('form.new_'.$field_name); my $newvalue = ''; foreach my $item (@values) { if ($item ne '') { $newvalue .= $item.','; } } $newvalue =~ s/,$//; $Apache::lonpublisher::metadatafields{$field_name}=$newvalue; } if ($Apache::lonpublisher::metadatafields{'courserestricted'} ne 'none' && exists($env{$Apache::lonpublisher::metadatafields{'courserestricted'}.'.metadata.'.$field_name.'.options'})) { # handle restrictions here if ((($env{$Apache::lonpublisher::metadatafields{'courserestricted'}.'.metadata.'.$field_name.'.options'} =~ m/active/) || ($field_name eq 'courserestricted'))&& (!($env{$Apache::lonpublisher::metadatafields{'courserestricted'}.'.metadata.'.$field_name.'.options'} =~ m/deleted/))){ $output .= "\n".&Apache::loncommon::start_data_table_row(); $output .= (' '. &prettyinput($field_name, $Apache::lonpublisher::metadatafields{$field_name}, 'new_'.$field_name,'defaultmeta', undef,undef,undef,undef, $Apache::lonpublisher::metadatafields{'courserestricted'}).''); $output .= &Apache::loncommon::end_data_table_row(); } } else { $output.=(&Apache::loncommon::start_data_table_row().' '. &prettyinput($field_name, $Apache::lonpublisher::metadatafields{$field_name}, 'new_'.$field_name,'defaultmeta').''.&Apache::loncommon::end_data_table_row()); } } $output .= &Apache::loncommon::end_data_table(); if ($env{'form.store'}) { my ($outcome,$result) = &store_metadata($fn,$uri,'store'); $r->print($result); } $r->print($output.'
'); if ($file_type eq 'portfolio' || $file_type eq 'groups') { my ($port_path,$group) = &get_port_path_and_group($uri); if ($group ne '') { $r->print(''); } $r->print(''); $r->print('

'); if ($group ne '') { $r->print(''); } $r->print(''. ''); } } $r->print('
'); return; } sub store_metadata { my ($fn,$uri,$caller) = @_; my $mfh; my $formname='store'; my ($file_content,$output,$outcome); if (&Apache::loncommon::get_env_multiple('form.new_keywords')) { $Apache::lonpublisher::metadatafields{'keywords'} = join (',', &Apache::loncommon::get_env_multiple('form.new_keywords')); } foreach my $field (sort(keys(%Apache::lonpublisher::metadatafields))) { next if ($field =~ /\./); my $unikey=$field; $unikey=~/^([A-Za-z_]+)/; my $tag=$1; $tag=~tr/A-Z/a-z/; $file_content.= "\n\<$tag"; foreach my $key (split(/\,/,$Apache::lonpublisher::metadatakeys{$unikey})) { my $value = $Apache::lonpublisher::metadatafields{$unikey.'.'.$key}; $value=~s/\"/\'\'/g; $file_content.=' '.$key.'="'.$value.'"' ; } $file_content.= '>'. &HTML::Entities::encode ($Apache::lonpublisher::metadatafields{$unikey},'<>&"'). ''; } if ($fn =~ m|^$Apache::lonnet::perlvar{'lonDocRoot'}/userfiles|) { my ($path, $new_fn); if ($fn =~ m|$match_name/groups/\w+/portfolio/|) { ($path, $new_fn) = ($fn =~ m|/(groups/\w+/portfolio.*)/([^/]*)$|); } else { ($path, $new_fn) = ($fn =~ m|/(portfolio.*)/([^/]*)$|); } ($outcome,my $result) = &store_portfolio_metadata($formname,$file_content, $path,$new_fn,$uri,$caller); $output .= $result; } else { if (! ($mfh=Apache::File->new('>'.$fn))) { $output .= '

'; if ($caller eq 'transfer') { $output .= &mt('Could not transfer data in added fields to notes'); } else { $output .= &mt('Could not write metadata'); } $output .= ', '.&mt('FAIL').'

'; $outcome = 'fail'; } else { print $mfh ($file_content); close($mfh); &update_metadata_table($uri); $output .= '

'; if ($caller eq 'transfer') { $output .= &mt('Transferred data in added fields to notes'); } else { $output .= &mt('Wrote Metadata'); } $output .= ' '.&Apache::lonlocal::locallocaltime(time). '

'; $outcome = 'ok'; } } return ($outcome,$output); } sub store_transferred_addedfields { my ($fn,$uri,$transfers) = @_; foreach my $item (@{$transfers}) { $Apache::lonpublisher::metadatafields{'notes'} .= ' '.$item.' = '.$Apache::lonpublisher::metadatafields{$item}; } my ($outcome,$output) = &store_metadata($fn,$uri,'transfer'); if ($outcome eq 'ok') { foreach my $item (@{$transfers}) { delete($Apache::lonpublisher::metadatafields{$item}); } } } sub store_portfolio_metadata { my ($formname,$content,$path,$new_fn,$uri,$caller) = @_; my ($outcome,$output); $env{'form.'.$formname}=$content."\n"; $env{'form.'.$formname.'.filename'}=$new_fn; my $result =&Apache::lonnet::userfileupload($formname,'',$path); if ($result =~ /(error|notfound)/) { $output = '

'; if ($caller eq 'transfer') { $output .= &mt('Could not transfer data in added fields to notes'); } else { $output .= &mt('Could not write metadata'); } $output .= ', '.&mt('FAIL').'

'; $outcome = 'fail'; } else { &update_metadata_table($uri); $output = '

'; if ($caller eq 'transfer') { $output .= &mt('Transferred data in added fields to notes'); } else { $output .= &mt('Wrote Metadata'); } $output .= ' '.&Apache::lonlocal::locallocaltime(time). '

'; $outcome = 'ok'; } return ($outcome,$output); } sub update_metadata_table { my ($uri) = @_; my ($type,$udom,$uname,$file_name,$group) = &Apache::lonnet::parse_portfolio_url($uri); $file_name =~ s/\.meta$//; my $current_permissions = &Apache::lonnet::get_portfile_permissions($udom,$uname); my %access_controls = &Apache::lonnet::get_access_controls($current_permissions,$group, $file_name); my $access_hash = $access_controls{$file_name}; my $available = 0; if (ref($access_hash) eq 'HASH') { foreach my $key (keys(%{$access_hash})) { my ($num,$scope,$end,$start) = ($key =~ /^([^:]+):([a-z]+)_(\d*)_?(\d*)$/); if ($scope eq 'public' || $scope eq 'guest') { $available = 1; last; } } } if ($available) { my $result = &Apache::lonnet::update_portfolio_table($uname,$udom, $file_name,'portfolio_metadata',$group,'update'); } } 1; __END__