--- loncom/interface/lonparmset.pm 2006/06/26 22:22:48 1.318 +++ loncom/interface/lonparmset.pm 2008/10/10 10:54:03 1.411 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Handler to set parameters for assessments # -# $Id: lonparmset.pm,v 1.318 2006/06/26 22:22:48 albertel Exp $ +# $Id: lonparmset.pm,v 1.411 2008/10/10 10:54:03 bisitz Exp $ # # Copyright Michigan State University Board of Trustees # @@ -65,18 +65,8 @@ use Apache::lonlocal; use Apache::lonnavmaps; use Apache::longroup; use Apache::lonrss; -use LONCAPA; +use LONCAPA qw(:DEFAULT :match); -# --- Caches local to lonparmset - -my $parmhashid; -my %parmhash; -my $symbsid; -my %symbs; -my $rulesid; -my %rules; - -# --- end local caches ################################################## ################################################## @@ -119,10 +109,11 @@ sub parmval { sub parmval_by_symb { my ($what,$symb,$def,$uname,$udom,$csec,$cgroup,$courseopt)=@_; -# load caches - &cacheparmhash(); - my $useropt=&Apache::lonnet::get_userresdata($uname,$udom); + my $useropt; + if ($uname ne '' && $udom ne '') { + $useropt = &Apache::lonnet::get_userresdata($uname,$udom); + } my $result=''; my @outpar=(); @@ -164,7 +155,7 @@ sub parmval_by_symb { # ------------------------------------------------------ third, check map parms - my $thisparm=$parmhash{$symbparm}; + my $thisparm=&parmhash($symbparm); if (defined($thisparm)) { $outpar[11]=$thisparm; $result=11; } if (defined($$courseopt{$courselevelr})) { @@ -173,7 +164,7 @@ sub parmval_by_symb { } # ------------------------------------------------------ fourth, back to course - if (defined($csec)) { + if ($csec ne '') { if (defined($$courseopt{$seclevel})) { $outpar[9]=$$courseopt{$seclevel}; $result=9; @@ -189,7 +180,7 @@ sub parmval_by_symb { } } # ------------------------------------------------------ fifth, check course group - if (defined($cgroup)) { + if ($cgroup ne '') { if (defined($$courseopt{$grplevel})) { $outpar[6]=$$courseopt{$grplevel}; $result=6; @@ -206,7 +197,7 @@ sub parmval_by_symb { # ---------------------------------------------------------- fifth, check user - if (defined($uname)) { + if ($uname ne '') { if (defined($$useropt{$courselevel})) { $outpar[3]=$$useropt{$courselevel}; $result=3; @@ -225,60 +216,90 @@ sub parmval_by_symb { return ($result,@outpar); } -sub resetparmhash { - $parmhashid=''; -} -sub cacheparmhash { - if ($parmhashid eq $env{'request.course.fn'}) { return; } - my %parmhashfile; - if (tie(%parmhashfile,'GDBM_File', - $env{'request.course.fn'}.'_parms.db',&GDBM_READER(),0640)) { - %parmhash=%parmhashfile; - untie %parmhashfile; - $parmhashid=$env{'request.course.fn'}; - } -} -sub resetsymbcache { - $symbsid=''; +# --- Caches local to lonparmset + + +sub reset_caches { + &resetparmhash(); + &resetsymbcache(); + &resetrulescache(); } -sub symbcache { - my $id=shift; - if ($symbsid ne $env{'request.course.id'}) { - %symbs=(); +{ + my $parmhashid; + my %parmhash; + sub resetparmhash { + undef($parmhashid); + undef(%parmhash); } - unless ($symbs{$id}) { - my $navmap = Apache::lonnavmaps::navmap->new(); - if ($id=~/\./) { - my $resource=$navmap->getById($id); - $symbs{$id}=$resource->symb(); - } else { - my $resource=$navmap->getByMapPc($id); - $symbs{$id}=&Apache::lonnet::declutter($resource->src()); + + sub cacheparmhash { + if ($parmhashid eq $env{'request.course.fn'}) { return; } + my %parmhashfile; + if (tie(%parmhashfile,'GDBM_File', + $env{'request.course.fn'}.'_parms.db',&GDBM_READER(),0640)) { + %parmhash=%parmhashfile; + untie(%parmhashfile); + $parmhashid=$env{'request.course.fn'}; } - $symbsid=$env{'request.course.id'}; } - return $symbs{$id}; -} - -sub resetrulescache { - $rulesid=''; -} + + sub parmhash { + my ($id) = @_; + &cacheparmhash(); + return $parmhash{$id}; + } + } + +{ + my $symbsid; + my %symbs; + sub resetsymbcache { + undef($symbsid); + undef(%symbs); + } + + sub symbcache { + my $id=shift; + if ($symbsid ne $env{'request.course.id'}) { + undef(%symbs); + } + if (!$symbs{$id}) { + my $navmap = Apache::lonnavmaps::navmap->new(); + if ($id=~/\./) { + my $resource=$navmap->getById($id); + $symbs{$id}=$resource->symb(); + } else { + my $resource=$navmap->getByMapPc($id); + $symbs{$id}=&Apache::lonnet::declutter($resource->src()); + } + $symbsid=$env{'request.course.id'}; + } + return $symbs{$id}; + } + } -sub rulescache { - my $id=shift; - if ($rulesid ne $env{'request.course.id'}) { - %rules=(); +{ + my $rulesid; + my %rules; + sub resetrulescache { + undef($rulesid); + undef(%rules); } - unless (defined($rules{$id})) { - my $dom = $env{'course.'.$env{'request.course.id'}.'.domain'}; - my $crs = $env{'course.'.$env{'request.course.id'}.'.num'}; - %rules=&Apache::lonnet::dump('parmdefactions',$dom,$crs); - $rulesid=$env{'request.course.id'}; + + sub rulescache { + my $id=shift; + if ($rulesid ne $env{'request.course.id'} + && !defined($rules{$id})) { + my $dom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my $crs = $env{'course.'.$env{'request.course.id'}.'.num'}; + %rules=&Apache::lonnet::dump('parmdefactions',$dom,$crs); + $rulesid=$env{'request.course.id'}; + } + return $rules{$id}; } - return $rules{$id}; } sub preset_defaults { @@ -344,7 +365,6 @@ sub storeparm { # - new type # - username # - userdomain - my %recstack; sub storeparm_by_symb { my ($symb,$spnam,$snum,$nval,$ntype,$uname,$udom,$csec,$recflag,$cgroup)=@_; @@ -508,21 +528,26 @@ sub storeparm_by_symb_inner { Format a value for output. -Inputs: $value, $type +Inputs: $value, $type, $editable Returns: $value, formatted for output. If $type indicates it is a date, localtime($value) is returned. +$editable will return an icon to click on =cut ################################################## ################################################## sub valout { - my ($value,$type)=@_; + my ($value,$type,$editable)=@_; my $result = ''; # Values of zero are valid. if (! $value && $value ne '0') { - $result = ' '; + if ($editable) { + $result = '*'; + } else { + $result=' '; + } } else { if ($type eq 'date_interval') { my ($sec,$min,$hour,$mday,$mon,$year)=gmtime($value); @@ -548,9 +573,11 @@ sub valout { } $result=~s/\s+$//; } elsif (&isdateparm($type)) { - $result = localtime($value).&date_sanity_info($value); + $result = &Apache::lonlocal::locallocaltime($value). + &date_sanity_info($value); } else { $result = $value; + $result = &HTML::Entities::encode($result,'"<>&'); } } return $result; @@ -587,10 +614,16 @@ sub plink { my ($parmname)=((split(/\&/,$marker))[1]=~/\_([^\_]+)$/); my ($hour,$min,$sec,$val)=&preset_defaults($parmname); unless (defined($winvalue)) { $winvalue=$val; } + my $valout = &valout($value,$type,1); + foreach my $item (\$type, \$dis, \$winvalue, \$marker, \$return, \$call, + \$hour, \$min, \$sec) { + $$item = &HTML::Entities::encode($$item,'"<>&'); + $$item =~ s/\'/\\\'/g; + } return '
\n".&mt("Full Name").": ". $name{'firstname'}.' '.$name{'middlename'}.' ' .$name{'lastname'}.' '.$name{'generation'}. - "\n".&mt('ID').": ".$name{'id'}.'
'; + "\n".&mt('ID').": ".$name{'id'}.'
'; } @usersgroups = &Apache::lonnet::get_users_groups( $udom,$uname,$env{'request.course.id'}); @@ -1557,7 +1626,8 @@ sub assessparms { foreach ('tolerance','date_default','date_start','date_end', 'date_interval','int','float','string') { $r->print(''); + &HTML::Entities::encode($env{'form.recent_'.$_},'"&<>'). + '" name="recent_'.$_.'" />'); } if (!$pssymb) { @@ -1634,7 +1704,7 @@ sub assessparms { ); $r->print(<$lt{'pie'} -$lt{'csv'}($csuname $lt{'at'} $csudom) +$lt{'csv'}($csuname $lt{'at'} $csudom) $lt{'ic'}$lt{'rl'} $lt{'ic'} @@ -1735,8 +1805,8 @@ ENDTABLEHEADFOUR "$title"); + "', 'metadatafile', '450', '500', 'no', 'yes');\"". + " target=\"_self\">$title"); if ($thistitle) { $r->print(' ('.$thistitle.')'); @@ -1836,29 +1906,30 @@ ENDTABLEHEADFOUR #---------------------------------------------------- print header information my $foldermap=&mt($maptitle=~/^uploaded/?'Folder':'Map'); my $showtitle=$maptitles{$maptitle}.($maptitle!~/^uploaded/?' ['.$maptitle.']':''); - $r->print(< -Set Defaults for All Resources in $foldermap -$showtitle -Specifically for -ENDMAPONE + my $tmp=""; if ($uname) { my $person=&Apache::loncommon::plainname($uname,$udom); - $r->print(&mt("User")." $uname \($person\) ". - &mt('in')." \n"); + $tmp.=&mt("User")." $uname \($person\) ". + &mt('in')." \n"; } else { - $r->print("".&mt('all').' '.&mt('users in')." \n"); + $tmp.="".&mt('all').' '.&mt('users in')." \n"; } if ($cgroup) { - $r->print(&mt("Group")." $cgroup". - " ".&mt('of')." \n"); + $tmp.=&mt("Group")." $cgroup". + " ".&mt('of')." \n"; $csec = ''; } elsif ($csec) { - $r->print(&mt("Section")." $csec". - " ".&mt('of')." \n"); + $tmp.=&mt("Section")." $csec". + " ".&mt('of')." \n"; } - $r->print("$coursename"); - $r->print("\n"); + $r->print('' + .&mt('Set Defaults for All Resources in [_1]Specifically for [_2][_3]' + ,$foldermap.''.$showtitle.'' + ,$tmp + ,''.$coursename.'' + ) + ."\n" + ); #---------------------------------------------------------------- print table $r->print(''); $r->print(''.&mt('Parameter Name').''); @@ -1871,7 +1942,7 @@ ENDMAPONE \%type,\%display,$defbgone,$defbgtwo,$defbgthree, $parmlev,$uname,$udom,$csec,$cgroup); } - $r->print(""); + $r->print(""); } # end each map } # end of $parmlev eq map #--------------------------------- Entry for parm level general (Course level) @@ -1981,6 +2052,8 @@ sub crsenv { my $dom = $env{'course.'.$env{'request.course.id'}.'.domain'}; my $crs = $env{'course.'.$env{'request.course.id'}.'.num'}; + my (%crsinfo,$chome); + # # Go through list of changes foreach (keys %env) { @@ -2000,7 +2073,7 @@ sub crsenv { ('environment', {'top level map backup '.$bkuptime => $tmp[1] }, $dom,$crs). - ''; + ''; } # # Deal with modified default spreadsheets @@ -2028,6 +2101,29 @@ sub crsenv { if ($name =~ /^default_enrollment_(start|end)_date$/) { $value=&Apache::lonhtmlcommon::get_date_from_form($name.'_value'); } + # + # Deal with the emails + if ($name =~ /\.email$/) { + foreach my $specifier (split(',',$value)) { + my ($user,$sections_or_groups)= + ($specifier=~/^([^\(]+)\(([^\)]+)\)/); + if (!$sections_or_groups) { + $user = $specifier; + } + my ($name,$domain) = split(':',$user); + if (!defined($user) || !defined($domain)) { + $setoutput.= ' '. + &mt("Invalid email address specified, address must be of the form username:domain what was specified was ([_1])",$user). + ''; + undef($value); + } elsif (&Apache::lonnet::homeserver($user,$domain) eq 'no_host') { + $setoutput.= ' '. + &mt("Invalid email address specified, user [_1] is unknown.",$name). + ''; + undef($value); + } + } + } # Get existing cloners my @oldcloner = (); if ($name eq 'cloners') { @@ -2041,40 +2137,89 @@ sub crsenv { # # Let the user know we made the changes if ($name && defined($value)) { - my $failed_cloners; + my %failed_cloners; if ($name eq 'cloners') { $value =~ s/\s//g; $value =~ s/^,//; $value =~ s/,$//; # check requested clones are valid users. - $failed_cloners = &check_cloners(\$value,\@oldcloner); + %failed_cloners = &check_cloners(\$value,\@oldcloner); } my $put_result = &Apache::lonnet::put('environment', {$name=>$value},$dom,$crs); if ($put_result eq 'ok') { - $setoutput.=&mt('Set').' '.$name.' '.&mt('to').' '.$value.'.'; + $setoutput.=&mt('Set').' '.$name.' '.&mt('to').' '; + if ($name =~ /^default_enrollment_(start|end)_date$/) { + $setoutput .= &Apache::lonlocal::locallocaltime($value); + } elsif ($name eq 'categories') { + $setoutput .= $env{'form.categories_display'}; + } else { + $setoutput .= $value; + } + $setoutput .= '.'; if ($name eq 'cloners') { &change_clone($value,\@oldcloner); } - # Flush the course logs so course description is immediately updated + # Update environment and nohist_courseids.db + if (($name eq 'description') || ($name eq 'cloners') || + ($name eq 'hidefromcat') || ($name eq 'categories')) { + if ($chome eq '') { + %crsinfo = + &Apache::lonnet::courseiddump($dom,'.',1,'.','.', + $crs,undef,undef,'.'); + $chome = &Apache::lonnet::homeserver($crs,$dom); + } + } if ($name eq 'description' && defined($value)) { - &Apache::lonnet::flushcourselogs(); + &Apache::lonnet::appenv({'course.'.$env{'request.course.id'}.'.description' => $value}); + if (ref($crsinfo{$env{'request.course.id'}}) eq 'HASH') { + $crsinfo{$env{'request.course.id'}}{'description'} = $value; + my $putresult = + &Apache::lonnet::courseidput($dom,\%crsinfo, + $chome,'notime'); + } + } + if (($name eq 'cloners') || ($name eq 'hidefromcat') || ($name eq 'categories')) { + if (ref($crsinfo{$env{'request.course.id'}}) eq 'HASH') { + &Apache::lonnet::appenv({'course.'.$env{'request.course.id'}.'.'.$name => $value}); + $crsinfo{$env{'request.course.id'}}{$name} = $value; + my $putresult = + &Apache::lonnet::courseidput($dom,\%crsinfo, + $chome,'notime'); + } } } else { $setoutput.=&mt('Unable to set').' '.$name.' '.&mt('to'). ' '.$value.' '.&mt('due to').' '.$put_result.'.'; } - if (($name eq 'cloners') && ($failed_cloners)) { - $setoutput.= &mt('Unable to include').' - '.$failed_cloners.', '. - &mt('reason').' - '.&mt('LON-CAPA user(s) do(es) not exist'). - '.'.&mt('Please '). - ' '. - &mt('add the user(s)').', '. - &mt('and then return to the '). - ''. - &mt('Course Parameters page').' '. - &mt('to add the new user(s) to the list of possible cloners'). - '.'; + if (($name eq 'cloners') && (keys(%failed_cloners) > 0)) { + $setoutput.= &mt('Unable to include').': '; + my @fails; + my $num = 0; + if (defined($failed_cloners{'format'})) { + $fails[$num] .= ''.$failed_cloners{'format'}. + ', '.&mt('reason').' - '. + &mt('Invalid format'); + $num ++; + } + if (defined($failed_cloners{'domain'})) { + $fails[$num] .= ''.$failed_cloners{'domain'}. + ', '.&mt('reason').' - '. + &mt('Domain does not exist'); + $num ++; + } + if (defined($failed_cloners{'newuser'})) { + $fails[$num] .= ''.$failed_cloners{'newuser'}. ', '.&mt('reason').' - '. + &mt('LON-CAPA user(s) do(es) not exist.'). + '.'.&mt('Please '). + ' '. + &mt('add the user(s)').', '. + &mt('and then return to the '). + ''. + &mt('Course Parameters page').' '. + &mt('to add the new user(s) to the list of possible cloners'); + } + $setoutput .= join('; ',@fails).'.'; } } } @@ -2093,65 +2238,75 @@ sub crsenv { my $SelectStyleFile=&mt('Select Style File'); my $SelectSpreadsheetFile=&mt('Select Spreadsheet File'); my $output=''; + my $can_categorize; if (! exists($values{'con_lost'})) { my %descriptions= - ('url' => ''.&mt('Top Level Map').' '. + ('url' => ''.&mt('Top Level Map').''. '". &mt('Select Map').' '. - &mt('Modification may make assessment data inaccessible'). + &mt('Modification may make assessment data inaccessible!'). '', 'description' => ''.&mt('Course Description').'', 'courseid' => ''.&mt('Course ID or number'). ''. - '('.&mt('internal').', '.&mt('optional').')', - 'cloners' => ''.&mt('Users allowed to clone course').'(user:domain,user:domain)'.&mt('Users with active Course Coordinator role in the course automatically have the right to clone it, and can be omitted from list.'), + '('.&mt('internal, optional').')', + 'cloners' => ''.&mt('Users allowed to clone course').'' + .'("'.&mt('user:domain,user:domain,*:domain').'")' + .&mt('Users with active Course Coordinator role in this course are permitted to clone and need not be included.').'' + .&mt('Use [_1] to allow course to be cloned by anyone in the specified domain.','"*:domain"').'' + .&mt('Use [_1] to allow unrestricted cloning in all domains.','"*"'), 'grading' => ''.&mt('Grading').''. - '"standard", "external", or "spreadsheet" '.&Apache::loncommon::help_open_topic('GradingOptions'), - 'default_xml_style' => ''.&mt('Default XML Style File').' '. + &mt('[_1], [_2], or [_3]','"standard"','"external"','"spreadsheet"').&Apache::loncommon::help_open_topic('GradingOptions'), + 'task_grading' => ''.&mt('Bridge Task Grading').'' + .&mt('Instructors and TAs in sections, when grading bridge tasks, should be allowed to grade other sections.').'' + .'('.&mt('[_1]: they are allowed (this is the default). [_2]: no, they can only grade their own section.','"any"','"section"').')', + 'default_xml_style' => ''.&mt('Default XML Style File').''. '$SelectStyleFile", - 'question.email' => ''.&mt('Feedback Addresses for Resource Content Question'). - '(user:domain,'. - 'user:domain(section;section;...;*;...),...)', - 'comment.email' => ''.&mt('Feedback Addresses for Course Content Comments').''. - '(user:domain,user:domain(section;section;...;*;...),...)', - 'policy.email' => ''.&mt('Feedback Addresses for Course Policy').''. - '(user:domain,user:domain(section;section;...;*;...),...)', - 'hideemptyrows' => ''.&mt('Hide Empty Rows in Spreadsheets').''. - '('.&mt('"[_1]" for default hiding','yes').')', - 'pageseparators' => ''.&mt('Visibly Separate Items on Pages').''. - '('.&mt('"[_1]" for visible separation','yes').', '. - &mt('changes will not show until next login').')', - 'student_classlist_view' => ''.&mt('Allow students to view classlist.').''.&mt('("all":students can view all sections,"section":students can only view their own section.blank or "disabled" prevents student view.'), - - 'plc.roles.denied'=> ''.&mt('Disallow live chatroom use for Roles'). - '"st": '. - &mt('student').', "ta": '. - 'TA, "in": '. - &mt('instructor').';'.&mt('role,role,...').') '. - Apache::loncommon::help_open_topic("Course_Disable_Discussion"), + ",'sty')\">$SelectStyleFile", + 'question.email' => ''.&mt('Feedback Addresses for Resource Content Question').'' + .'("'.&mt('user:domain,user:domain(section;section;...;*;...),...').'")', + 'question.email.text' => ''.&mt('Custom Text for Resource Content Question Option in Feedback').'', + 'comment.email' => ''.&mt('Feedback Addresses for Course Content Comments').'' + .'("'.&mt('user:domain,user:domain(section;section;...;*;...),...').'")', + 'comment.email.text' => ''.&mt('Custom Text for Course Content Option in Feedback').'', + 'policy.email' => ''.&mt('Feedback Addresses for Course Policy').'' + .'("'.&mt('user:domain,user:domain(section;section;...;*;...),...').'")', + 'policy.email.text' => ''.&mt('Custom Text for Course Policy Option in Feedback').'', + 'hideemptyrows' => ''.&mt('Hide Empty Rows in Spreadsheets').'' + .'('.&mt('[_1] for default hiding','"yes"').')', + 'pageseparators' => ''.&mt('Visibly Separate Items on Pages').'' + .'('.&mt('[_1] for visible separation.','"yes"').' ' + .&mt('Changes will not show until next login.').')', + 'student_classlist_view' => ''.&mt('Allow students to view classlist.').'' + .'('.&mt('[_1]: students can view all sections. [_2]: students can only view their own section. blank or [_3] prevents student view.','"all"','"section"','"disabled"').')', + 'student_classlist_portfiles' => ''.&mt('Include link to accessible portfolio files').'' + .'('.&mt('[_1] for link to each a listing of each student\'s files.','"yes"').')', + 'student_classlist_opt_in' => ''.&mt("Student's agreement needed for listing in student-viewable roster").'' + .'('.&mt('[_1] to require students to opt-in to listing in the roster (on the roster page).','"yes"').')', + 'plc.roles.denied'=> ''.&mt('Disallow live chatroom use for Roles').'' + .'('.&mt('[_1]: student, [_2]: TA, [_3]: instructor','"st"','"ta"','"in"').')' + .'("'.&mt('role,role,...').'") ' + .Apache::loncommon::help_open_topic("Course_Disable_Discussion"), 'plc.users.denied' => ''.&mt('Disallow live chatroom use for Users').''. - '(user:domain,user:domain,...)', + '("'.&mt('user:domain,user:domain,...').'")', - 'pch.roles.denied'=> ''.&mt('Disallow Resource Discussion for Roles'). - '"st": '. - 'student, "ta": '. - 'TA, "in": '. - 'instructor;role,role,...) '. - Apache::loncommon::help_open_topic("Course_Disable_Discussion"), + 'pch.roles.denied'=> ''.&mt('Disallow Resource Discussion for Roles').'' + .'('.&mt('[_1]: student, [_2]: TA, [_3]: instructor','"st"','"ta"','"in"') + .'("'.&mt('role,role,...').'") ' + .Apache::loncommon::help_open_topic("Course_Disable_Discussion"), 'pch.users.denied' => ''.&mt('Disallow Resource Discussion for Users').''. - '(user:domain,user:domain,...)', + '("'.&mt('user:domain,user:domain,...').'")', 'spreadsheet_default_classcalc' => ''.&mt('Default Course Spreadsheet').' '. '$SelectSpreadsheetFile", 'spreadsheet_default_studentcalc' - => ''.&mt('Default Student Spreadsheet').' '. + => ''.&mt('Default Student Spreadsheet').''. '$SelectSpreadsheetFile", @@ -2162,44 +2317,45 @@ sub crsenv { ",'spreadsheet')\">$SelectSpreadsheetFile", 'allow_limited_html_in_feedback' => ''.&mt('Allow limited HTML in discussion posts').''. - '('.&mt('Set value to "[_1]" to allow',"yes").')', + '('.&mt('Set value to [_1] to allow.','"yes"').')', 'allow_discussion_post_editing' - => ''.&mt('Allow users with specified roles to edit/delete their own discussion posts').'"st": '. - &mt('student').', "ta": '. - 'TA, "in": '. - &mt('instructor').'; ('.&mt('role:section,role:section,..., e.g., st:001,st:002,in,cc would permit students in sections 001 and 002 and instructors in any section, and course coordinators to edit their own posts.').')'. - '('.&mt('or set value to "[_1]" to allow all roles',"yes").')', + => ''.&mt('Allow users with specified roles to edit/delete their own discussion posts').'' + .'('.&mt('[_1]: student, [_2]: TA, [_3]: instructor','"st"','"ta"','"in"').')' + .'('.&mt('Set value to [_1] to allow all roles.','"yes"').')' + .'("'.&mt('role:section,role:section,...').'")' + .'('.&mt('Example: "st:001,st:002,in,cc" would permit students in sections 001 and 002 and instructors in any section, and course coordinators to edit their own posts.').')', 'rndseed' - => ''.&mt('Randomization algorithm used').' '. - ''.&mt('Modifying this will make problems').' '. - &mt('have different numbers and answers').'', + => ''.&mt('Randomization algorithm used').'' + .'' + .&mt('Modifying this will make problems have different numbers and answers!') + .'', 'receiptalg' => ''.&mt('Receipt algorithm used').' '. &mt('This controls how receipt numbers are generated.'), 'suppress_tries' => ''.&mt('Suppress number of tries in printing').''. - ' ('.&mt('"[_1]" to suppress, anything else to not suppress','yes').')', + ' ('.&mt('[_1] to suppress, anything else to not suppress','"yes"').')', 'problem_stream_switch' => ''.&mt('Allow problems to be split over pages').''. - ' ('.&mt('"[_1]" if allowed, anything else if not','yes').')', + ' ('.&mt('[_1] if allowed, anything else if not','"yes"').')', 'default_paper_size' => ''.&mt('Default paper type').''. ' ('.&mt('supported types').': Letter [8 1/2x11 in], Legal [8 1/2x14 in],'. ' Tabloid [11x17 in], Executive [7 1/2x10 in], A2 [420x594 mm],'. ' A3 [297x420 mm], A4 [210x297 mm], A5 [148x210 mm], A6 [105x148 mm])', - 'anonymous_quiz' - => ''.&mt('Anonymous quiz/exam').''. - ' ('.&mt('yes').' '.&mt('to avoid print students names').' )', + 'print_header_format' + => ' '.&mt('Print header format').'' + .&mt('Substitutions:[_1]: student name, [_2]: course id, [_3]: assignment note. Numbers after the % limit the field size.','"%n"','"%c"','"%a"'), 'default_enrollment_start_date' => ''.&mt('Default beginning date for student access.').'', 'default_enrollment_end_date' => ''.&mt('Default ending date for student access.').'', - 'nothideprivileged' => ''.&mt('Privileged users that should not be hidden on staff listings').''. - '(user:domain,user:domain,...)', + 'nothideprivileged' => ''.&mt('Privileged users that should not be hidden on staff listings').'' + .'("'.&mt('user:domain,user:domain,*:domain').'")', 'languages' => ''.&mt('Languages used').'', 'disable_receipt_display' => ''.&mt('Disable display of problem receipts').''. ' ('.&mt('"[_1]" to disable, anything else if not','yes').')', 'task_messages' - => ''.&mt('Send message to student when clicking Done on Tasks. [_1] to send a message only to student, [_2] to send message to student and add record to user information page for instructors. Leave blank to disable.','only_student','student_and_user_notes_screen').'', + => ''.&mt('Send message to student when clicking Done on Tasks').' ('.&mt('[_1] to send a message only to student, [_2] to send message to student and add record to user information page for instructors. Leave blank to disable.','"only_student"','"student_and_user_notes_screen"').')', 'disablesigfigs' => ''.&mt('Disable checking of Significant Figures').''. ' ('.&mt('"[_1]" to disable, anything else if not','yes').')', @@ -2209,24 +2365,57 @@ sub crsenv { 'externalsyllabus' => ''.&mt('URL of Syllabus (not using internal handler)').'', 'tthoptions' - => ''.&mt('Default set of options to pass to tth/m when converting tex').'' - ); - my @Display_Order = ('url','description','courseid','cloners','grading', + => ''.&mt('Default set of options to pass to tth/m when converting tex').'', + + 'texengine' + => ''.&mt('Force all students in the course to use a specific math rendering engine.').'' + .'('.&mt('[_1], [_2] (Convert to Images), [_3] (TeX to HTML), or blank for student\'s preference','"jsMath"','"mimetex"','"tth"').')', + 'timezone' + => ''.&mt('Timezone in which the course takes place').'', + + 'suppress_embed_prompt' + => ''.&mt('Suppress prompt to upload items referenced in a web page being uploaded to portfolio, when current role is student.').''. + ' ('.&mt('[_1] to suppress, anything else to not suppress','"yes"').')', + 'hidefromcat' + => ''.&mt('Exclude from course catalog').''. + ' ('.&mt('[_1] to exclude, anything else to include - included if assigned an institutional code, or manually catagorized','"yes"').')', + 'categories' + => ''.&mt('Categorize course').' '. + &mt('Display Categories').'', + 'datelocale' + => ''.&mt('Locale used for course calendar').'', + ); + my @Display_Order = ('url','description','courseid','cloners'); + (my $can_toggle_cat,$can_categorize) = &can_modify_catsettings($dom); + if ($can_toggle_cat) { + push(@Display_Order,'hidefromcat'); + } + if ($can_categorize) { + push(@Display_Order,'categories'); + } + push (@Display_Order,('grading', 'externalsyllabus', 'default_xml_style','pageseparators', - 'question.email','comment.email','policy.email', + 'question.email','question.email.text','comment.email', + 'comment.email.text','policy.email','policy.email.text', 'student_classlist_view', + 'student_classlist_opt_in', + 'student_classlist_portfiles', 'plc.roles.denied','plc.users.denied', 'pch.roles.denied','pch.users.denied', 'allow_limited_html_in_feedback', 'allow_discussion_post_editing', 'languages', + 'timezone', + 'datelocale', 'nothideprivileged', 'rndseed', 'receiptalg', 'problem_stream_switch', 'suppress_tries', + 'suppress_embed_prompt', 'default_paper_size', + 'print_header_format', 'disable_receipt_display', 'spreadsheet_default_classcalc', 'spreadsheet_default_studentcalc', @@ -2235,12 +2424,14 @@ sub crsenv { 'default_enrollment_start_date', 'default_enrollment_end_date', 'tthoptions', + 'texengine', 'disablesigfigs', 'disableexampointprint', - 'task_messages' - ); + 'task_messages','task_grading')); foreach my $parameter (sort(keys(%values))) { - unless (($parameter =~ m/^internal\./)||($parameter =~ m/^metadata\./)) { + unless (($parameter =~ m/^internal\./)||($parameter =~ m/^metadata\./) || + ($parameter =~ m/^selfenroll_/) || ($parameter =~ /_selfenroll$/) + || ($parameter eq 'type')) { if (! $descriptions{$parameter}) { $descriptions{$parameter}=$parameter; push(@Display_Order,$parameter); @@ -2263,6 +2454,39 @@ sub crsenv { $values{$parameter}, $onchange). ''; + } elsif ($parameter eq 'timezone') { + my $includeempty = 1; + my $timezone = &Apache::lonlocal::gettimezone(); + $output .= ''. + &Apache::loncommon::select_timezone($parameter.'_value', + $timezone, + $onchange,$includeempty).''; + } elsif ($parameter eq 'datelocale') { + my $includeempty = 1; + my $locale_obj = &Apache::lonlocal::getdatelocale(); + my $currdatelocale; + if (ref($locale_obj)) { + $currdatelocale = $locale_obj->id(); + } + $output .= ''. + &Apache::loncommon::select_datelocale($parameter.'_value', + $currdatelocale, + $onchange,$includeempty).''; + } elsif ($parameter eq 'categories') { + my $catdisplay; + if ($values{'categories'} ne '') { + my @curritems = split(/\&/,$values{'categories'}); + foreach my $item (@curritems) { + my ($name,$parent,$pos) = split(/:/,$item); + $catdisplay .= &unescape($name).'&'; + } + $catdisplay =~ s/\&$//; + } + $output .= ''. + ''. + ''; } else { $output .= ''. &Apache::lonhtmlcommon::textbox($parameter.'_value', @@ -2289,21 +2513,33 @@ sub crsenv { my %lt=&Apache::lonlocal::texthash( 'par' => 'Parameter', 'val' => 'Value', - 'set' => 'Set', - 'sce' => 'Set Course Environment' + 'set' => 'Set?', + 'sav' => 'Save' ); my $Parameter=&mt('Parameter'); my $Value=&mt('Value'); my $Set=&mt('Set'); - my $browse_js= - ''; - + my ($jscript,$categorize_js); + my $browse_js = &Apache::loncommon::browser_and_searcher_javascript('parmset'); + if ($can_categorize) { + $categorize_js = <'."\n". + $browse_js."\n".$categorize_js."\n".''; my $start_page = - &Apache::loncommon::start_page('Set Course Environment Parameters', - $browse_js); + &Apache::loncommon::start_page('Set Course Environment', + $jscript); my $end_page = &Apache::loncommon::end_page(); my $end_table=&Apache::loncommon::end_data_table(); @@ -2312,17 +2548,110 @@ $start_page $breadcrumbs $setoutput + $start_table $start_header_row -$lt{'par'}$lt{'val'}$lt{'set'}? +$lt{'par'}$lt{'val'}$lt{'set'} $end_header_row $output $end_table - + $end_page ENDENV } + +sub can_modify_catsettings { + my ($dom) = @_; + my %domconf = &Apache::lonnet::get_dom('configuration',['coursecategories'],$dom); + my ($can_toggle_cat,$can_categorize); + if (ref($domconf{'coursecategories'}) eq 'HASH') { + if ($domconf{'coursecategories'}{'togglecats'} eq 'crs') { + $can_toggle_cat = 1; + } + if ($domconf{'coursecategories'}{'categorize'} eq 'crs') { + $can_categorize = 1; + } + } + return ($can_toggle_cat,$can_categorize); +} + +sub assign_course_categories { + my ($r) = @_; + my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + my $hascats = 0; + my $cathash; + my %domconf = &Apache::lonnet::get_dom('configuration',['coursecategories'],$cdom); + if (ref($domconf{'coursecategories'}) eq 'HASH') { + $cathash = $domconf{'coursecategories'}{'cats'}; + if (ref($cathash) eq 'HASH') { + $hascats = 1; + } + } + my $catwin_js; + if ($hascats) { + my $alert = &mt('Use \"Save\" in the main window to save course categories'); + $catwin_js = < + +function updateCategories() { + var newcategories = ''; + var unescapedcats = ''; + if (document.chgcats.usecategory.length) { + for (var i=0; i 0) { + newcategories = newcategories.slice(0,-1); + } + if (unescapedcats.length > 0) { + unescapedcats = unescapedcats.slice(0,-3); + } + } else { + if (document.chgcats.usecategory.checked == true) { + newcategories = document.chgcats.usecategory.value; + unescapedcats = document.chgcats.catname.value; + } + } + opener.document.envform.categories_value.value = newcategories; + opener.document.envform.categories_display.value = unescapedcats; + opener.document.envform.categories_setparmval.checked = true; + alert("$alert"); + self.close(); + return; +} + + +ENDSCRIPT + } else { + my $onload; + } + my $start_page = + &Apache::loncommon::start_page('Course Categories',$catwin_js, + {'only_body' => 1,}); + my $end_page = &Apache::loncommon::end_page(); + my $categoriesform = ''.&mt('Categorize Course').''; + if ($hascats) { + my %currsettings = + &Apache::lonnet::get('environment',['hidefromcat','categories'],$cdom,$cnum); + $categoriesform .= &mt('Assign one or more categories to this course.').''. + ''."\n" + .&Apache::loncommon::assign_categories_table($cathash, + $currsettings{'categories'})."\n" + .''; + } else { + $categoriesform .= &mt('No categories defined for this domain'); + } + $r->print($start_page.$categoriesform.$end_page); + return; +} + ################################################## # Overview mode ################################################## @@ -2355,8 +2684,7 @@ sub readdata { my $classlist=&Apache::loncoursedata::get_classlist(); foreach (keys %$classlist) { - # the following undefs are for 'domain', and 'username' respectively. - if ($_=~/^(\w+)\:(\w+)$/) { + if ($_=~/^($match_username)\:($match_domain)$/) { my ($tuname,$tudom)=($1,$2); my $useropt=&Apache::lonnet::get_userresdata($tuname,$tudom); foreach my $userkey (keys %{$useropt}) { @@ -2392,20 +2720,32 @@ sub storedata { if ($tuname) { $tkey=~s/\.\[useropt\:$tuname\:$tudom\]\./\./; } - if ($cmd eq 'set') { - my $data=$env{$_}; - my $typeof=$env{'form.typeof_'.$thiskey}; - if ($$olddata{$thiskey} ne $data) { + if ($cmd eq 'set' || $cmd eq 'datepointer' || $cmd eq 'dateinterval') { + my ($data, $typeof, $text); + if ($cmd eq 'set') { + $data=$env{$_}; + $typeof=$env{'form.typeof_'.$thiskey}; + $text = &mt('Saved modified parameter for'); + } elsif ($cmd eq 'datepointer') { + $data=&Apache::lonhtmlcommon::get_date_from_form($env{$_}); + $typeof=$env{'form.typeof_'.$thiskey}; + $text = &mt('Saved modified date for'); + } elsif ($cmd eq 'dateinterval') { + $data=&get_date_interval_from_form($thiskey); + $typeof=$env{'form.typeof_'.$thiskey}; + $text = &mt('Saved modified date for'); + } + if (defined($data) and $$olddata{$thiskey} ne $data) { if ($tuname) { if (&Apache::lonnet::put('resourcedata',{$tkey=>$data, $tkey.'.type' => $typeof}, $tudom,$tuname) eq 'ok') { &log_parmset({$tkey=>$data,$tkey.'.type' => $typeof},0,$tuname,$tudom); - $r->print(''.&mt('Stored modified parameter for').' '. + $r->print(''.$text.' '. &Apache::loncommon::plainname($tuname,$tudom)); } else { $r->print(''. - &mt('Error storing parameters').''); + &mt('Error saving parameters').''); } &Apache::lonnet::devalidateuserresdata($tuname,$tudom); } else { @@ -2424,27 +2764,7 @@ sub storedata { } &Apache::lonnet::devalidateuserresdata($tuname,$tudom); } else { - push (@deldata,$thiskey); - } - } elsif ($cmd eq 'datepointer') { - my $data=&Apache::lonhtmlcommon::get_date_from_form($env{$_}); - my $typeof=$env{'form.typeof_'.$thiskey}; - if (defined($data) and $$olddata{$thiskey} ne $data) { - if ($tuname) { - if (&Apache::lonnet::put('resourcedata',{$tkey=>$data, - $tkey.'.type' => $typeof}, - $tudom,$tuname) eq 'ok') { - &log_parmset({$tkey=>$data,$tkey.'.type' => $typeof},0,$tuname,$tudom); - $r->print(''.&mt('Stored modified date for').' '.&Apache::loncommon::plainname($tuname,$tudom)); - } else { - $r->print(''. - &mt('Error storing parameters').''); - } - &Apache::lonnet::devalidateuserresdata($tuname,$tudom); - } else { - $newdata{$thiskey}=$data; - $newdata{$thiskey.'.type'}=$typeof; - } + push (@deldata,$thiskey,$thiskey.'.type'); } } } @@ -2467,10 +2787,10 @@ sub storedata { if ($putentries) { if (&Apache::lonnet::put('resourcedata',\%newdata,$dom,$crs) eq 'ok') { &log_parmset(\%newdata,0); - $r->print(''.&mt('Stored [_1] parameter(s)',$putentries/2).''); + $r->print(''.&mt('Saved [_1] parameter(s)',$putentries/2).''); } else { $r->print(''. - &mt('Error storing parameters').''); + &mt('Error saving parameters').''); } &Apache::lonnet::devalidatecourseresdata($crs,$dom); } @@ -2478,7 +2798,27 @@ sub storedata { sub extractuser { my $key=shift; - return ($key=~/^$env{'request.course.id'}.\[useropt\:(\w+)\:(\w+)\]\./); + return ($key=~/^$env{'request.course.id'}.\[useropt\:($match_username)\:($match_domain)\]\./); +} + +sub parse_listdata_key { + my ($key,$listdata) = @_; + # split into student/section affected, and + # the realm (folder/resource part and parameter + my ($student,$realm) = + ($key=~/^\Q$env{'request.course.id'}\E\.\[([^\.]+)\]\.(.+)$/); + # if course wide student would be undefined + if (!defined($student)) { + ($realm)=($key=~/^\Q$env{'request.course.id'}\E\.(.+)$/); + } + # strip off the .type if it's not the Question type parameter + if ($realm=~/\.type$/ && !exists($listdata->{$key.'.type'})) { + $realm=~s/\.type//; + } + # split into resource+part and parameter name + my ($res, $parm) = ($realm=~/^(.*)\.(.*)$/); + ($res, my $part) = ($res =~/^(.*)\.(.*)$/); + return ($student,$res,$part,$parm); } sub listdata { @@ -2492,40 +2832,48 @@ sub listdata { $tableopen=0; my $foundkeys=0; my %keyorder=&standardkeyorder(); + foreach my $thiskey (sort { + my ($astudent,$ares,$apart,$aparm) = &parse_listdata_key($a,$listdata); + my ($bstudent,$bres,$bpart,$bparm) = &parse_listdata_key($b,$listdata); + + # get the numerical order for the param + $aparm=$keyorder{'parameter_0_'.$aparm}; + $bparm=$keyorder{'parameter_0_'.$bparm}; + + my $result=0; + if ($sortorder eq 'realmstudent') { - my ($astudent,$arealm)=($a=~/^\Q$env{'request.course.id'}\E\.\[([^\.]+)\]\.(.+)\.[^\.]+$/); - my ($bstudent,$brealm)=($b=~/^\Q$env{'request.course.id'}\E\.\[([^\.]+)\]\.(.+)\.[^\.]+$/); - if (!defined($astudent)) { - ($arealm)=($a=~/^\Q$env{'request.course.id'}\E\.(.+)$/); - } - if (!defined($bstudent)) { - ($brealm)=($b=~/^\Q$env{'request.course.id'}\E\.(.+)$/); - } - $arealm=~s/\.type//; - my ($ares, $aparm) = ($arealm=~/^(.*)\.(.*)$/); - $aparm=$keyorder{'parameter_0_'.$aparm}; - $brealm=~s/\.type//; - my ($bres, $bparm) = ($brealm=~/^(.*)\.(.*)$/); - $bparm=$keyorder{'parameter_0_'.$bparm}; - if ($ares eq $bres) { - if (defined($aparm) && defined($bparm)) { - ($aparm <=> $bparm); - } elsif (defined($aparm)) { - -1; - } elsif (defined($bparm)) { - 1; - } else { - ($arealm cmp $brealm) || ($astudent cmp $bstudent); - } - } else { - ($arealm cmp $brealm) || ($astudent cmp $bstudent); + if ($ares ne $bres ) { + $result = ($ares cmp $bres); + } elsif ($astudent ne $bstudent) { + $result = ($astudent cmp $bstudent); + } elsif ($apart ne $bpart ) { + $result = ($apart cmp $bpart); } } else { - $a cmp $b; + if ($astudent ne $bstudent) { + $result = ($astudent cmp $bstudent); + } elsif ($ares ne $bres ) { + $result = ($ares cmp $bres); + } elsif ($apart ne $bpart ) { + $result = ($apart cmp $bpart); + } + } + + if (!$result) { + if (defined($aparm) && defined($bparm)) { + $result = ($aparm <=> $bparm); + } elsif (defined($aparm)) { + $result = -1; + } elsif (defined($bparm)) { + $result = 1; + } } + + $result; } keys %{$listdata}) { - + if ($$listdata{$thiskey.'.type'}) { my $thistype=$$listdata{$thiskey.'.type'}; if ($$resourcedata{$thiskey.'.type'}) { @@ -2536,7 +2884,7 @@ sub listdata { my $section=&mt('All Students'); if ($middle=~/^\[(.*)\]/) { my $issection=$1; - if ($issection=~/^useropt\:(\w+)\:(\w+)/) { + if ($issection=~/^useropt\:($match_username)\:($match_domain)/) { $section=&mt('User').": ".&Apache::loncommon::plainname($1,$2); } else { $section=&mt('Group/Section').': '.$issection; @@ -2581,15 +2929,6 @@ sub listdata { $oldpart=$part; } # -# Preset defaults? -# - my ($hour,$min,$sec,$val)=('','','',''); - unless ($$resourcedata{$thiskey}) { - my ($parmname)=($thiskey=~/\.(\w+)$/); - ($hour,$min,$sec,$val)=&preset_defaults($parmname); - } - -# # Ready to print # $r->print(&tablestart(). @@ -2605,38 +2944,20 @@ sub listdata { &Apache::lonhtmlcommon::date_setter('parmform', $jskey, $$resourcedata{$thiskey}, - '',1,'','',$hour,$min,$sec). + '',1,'',''). ''. +(($$resourcedata{$thiskey}!=0)?''. +&mt('Shift all dates based on this date').'':''). &date_sanity_info($$resourcedata{$thiskey}) ); - } elsif ($thistype eq 'string_yesno') { - my $showval; - if (defined($$resourcedata{$thiskey})) { - $showval=$$resourcedata{$thiskey}; - } else { - $showval=$val; - } - $r->print('print(' checked="checked"'); - } - $r->print(' />'.&mt('Yes').' '); - $r->print('print(' checked="checked"'); - } - $r->print(' />'.&mt('No').''); + } elsif ($thistype eq 'date_interval') { + $r->print(&date_interval_selector($thiskey, + $$resourcedata{$thiskey})); + } elsif ($thistype =~ m/^string/) { + $r->print(&string_selector($thistype,$thiskey, + $$resourcedata{$thiskey})); } else { - my $showval; - if (defined($$resourcedata{$thiskey})) { - $showval=$$resourcedata{$thiskey}; - } else { - $showval=$val; - } - $r->print(''); + $r->print(&default_selector($thiskey,$$resourcedata{$thiskey})); } $r->print(''); @@ -2646,6 +2967,117 @@ sub listdata { return $foundkeys; } + +sub date_interval_selector { + my ($thiskey, $showval) = @_; + my $result; + foreach my $which (['days', 86400, 31], + ['hours', 3600, 23], + ['minutes', 60, 59], + ['seconds', 1, 59]) { + my ($name, $factor, $max) = @{ $which }; + my $amount = int($showval/$factor); + $showval %= $factor; + my %select = ((map {$_ => $_} (0..$max)), + 'select_form_order' => [0..$max]); + $result .= &Apache::loncommon::select_form($amount,$name.'_'.$thiskey, + %select); + $result .= ' '.&mt($name); + } + $result .= ''; + return $result; + +} + +sub get_date_interval_from_form { + my ($key) = @_; + my $seconds = 0; + foreach my $which (['days', 86400], + ['hours', 3600], + ['minutes', 60], + ['seconds', 1]) { + my ($name, $factor) = @{ $which }; + if (defined($env{'form.'.$name.'_'.$key})) { + $seconds += $env{'form.'.$name.'_'.$key} * $factor; + } + } + return $seconds; +} + + +sub default_selector { + my ($thiskey, $showval) = @_; + return ''; +} + +my %strings = + ( + 'string_yesno' + => [[ 'yes', 'Yes' ], + [ 'no', 'No' ]], + 'string_problemstatus' + => [[ 'yes', 'Yes' ], + [ 'answer', 'Yes, and show correct answer if they exceed the maximum number of tries.' ], + [ 'no', 'No, don\'t show correct/incorrect feedback.' ], + [ 'no_feedback_ever', 'No, show no feedback at all.' ]], + ); + + +sub string_selector { + my ($thistype, $thiskey, $showval) = @_; + + if (!exists($strings{$thistype})) { + return &default_selector($thiskey,$showval); + } + + my $result; + foreach my $possibilities (@{ $strings{$thistype} }) { + my ($name, $description) = @{ $possibilities }; + $result .= ' '; + } + return $result; +} + +# +# Shift all start and end dates by $shift +# + +sub dateshift { + my ($shift)=@_; + my $dom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my $crs = $env{'course.'.$env{'request.course.id'}.'.num'}; + my %data=&Apache::lonnet::dump('resourcedata',$dom,$crs); +# ugly retro fix for broken version of types + foreach my $key (keys %data) { + if ($key=~/\wtype$/) { + my $newkey=$key; + $newkey=~s/type$/\.type/; + $data{$newkey}=$data{$key}; + delete $data{$key}; + } + } + my %storecontent=(); +# go through all parameters and look for dates + foreach my $key (keys %data) { + if ($data{$key.'.type'}=~/^date_(start|end)$/) { + my $newdate=$data{$key}+$shift; + $storecontent{$key}=$newdate; + } + } + my $reply=&Apache::lonnet::cput + ('resourcedata',\%storecontent,$dom,$crs); + if ($reply eq 'ok') { + &log_parmset(\%storecontent); + } + &Apache::lonnet::devalidatecourseresdata($crs,$dom); + return $reply; +} + sub newoverview { my ($r) = @_; @@ -2687,8 +3119,8 @@ ENDOVER my @selected_sections = &Apache::loncommon::get_env_multiple('form.Section'); @selected_sections = ('all') if (! @selected_sections); - foreach (@selected_sections) { - if ($_ eq 'all') { + foreach my $sec (@selected_sections) { + if ($sec eq 'all') { @selected_sections = ('all'); } } @@ -2708,6 +3140,9 @@ ENDOVER \%mapp, \%symbp,\%maptitles,\%uris, \%keyorder,\%defkeytype); + if (grep {$_ eq 'all'} (@psprt)) { + @psprt = keys(%allparts); + } # Menu to select levels, etc $r->print(' @@ -2764,7 +3199,7 @@ ENDOVER &listdata($r,$resourcedata,$listdata,$sortorder); } $r->print(&tableend(). - ((($env{'form.store'}) || ($env{'form.dis'}))?'':''). + ((($env{'form.store'}) || ($env{'form.dis'}))?'':''). ''.&Apache::loncommon::end_page()); } @@ -2837,9 +3272,176 @@ ENDOVER &Apache::loncommon::end_page()); } +sub clean_parameters { + my ($r) = @_; + my $dom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my $crs = $env{'course.'.$env{'request.course.id'}.'.num'}; + + my $start_page=&Apache::loncommon::start_page('Clean Parameters'); + my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs('Clean'); + $r->print(< +ENDOVER +# Store modified + + &storedata($r,$crs,$dom); + +# Read modified data + + my $resourcedata=&readdata($crs,$dom); + +# List data + + $r->print(''. + &mt('These parameters refer to resources that do not exist.'). + ''. + ''.''. + ''); + $r->print(&Apache::loncommon::start_data_table(). + ''. + ''.&mt('Delete').''. + ''.&mt('Parameter').''. + ''); + foreach my $thiskey (sort(keys(%{$resourcedata}))) { + next if (!exists($resourcedata->{$thiskey.'.type'}) + && $thiskey=~/\.type$/); + my %data = &parse_key($thiskey); + if (1) { #exists($data{'realm_exists'}) + #&& !$data{'realm_exists'}) { + $r->print(&Apache::loncommon::start_data_table_row(). + ''. + '' ); + + $r->print(''); + my $display_value = $resourcedata->{$thiskey}; + if (&isdateparm($resourcedata->{$thiskey.'.type'})) { + $display_value = + &Apache::lonlocal::locallocaltime($display_value); + } + $r->print(&mt('Parameter: "[_1]" with value: "[_2]"', + &standard_parameter_names($data{'parameter_name'}), + $resourcedata->{$thiskey})); + $r->print(''); + if ($data{'scope_type'} eq 'all') { + $r->print(&mt('All users')); + } elsif ($data{'scope_type'} eq 'user') { + $r->print(&mt('User: [_1]',join(':',@{$data{'scope'}}))); + } elsif ($data{'scope_type'} eq 'section') { + $r->print(&mt('Section: [_1]',$data{'scope'})); + } elsif ($data{'scope_type'} eq 'group') { + $r->print(&mt('Group: [_1]',$data{'scope'})); + } + $r->print(''); + if ($data{'realm_type'} eq 'all') { + $r->print(&mt('All Resources')); + } elsif ($data{'realm_type'} eq 'folder') { + $r->print(&mt('Folder: [_1]'),$data{'realm'}); + } elsif ($data{'realm_type'} eq 'symb') { + my ($map,$resid,$url) = + &Apache::lonnet::decode_symb($data{'realm'}); + $r->print(&mt('Resource: [_1] with ID: [_2] in folder [_3]', + $url,$resid,$map)); + } + $r->print(' '.&mt('Part: [_1]',$data{'parameter_part'})); + $r->print(''); + + } + } + $r->print(&Apache::loncommon::end_data_table().''. + ''. + ''. + &Apache::loncommon::end_page()); +} + +sub date_shift_one { + my ($r) = @_; + my $dom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my $crs = $env{'course.'.$env{'request.course.id'}.'.num'}; + + my $start_page=&Apache::loncommon::start_page('Shift Dates'); + my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs('Shift'); + $r->print(<print(''. + ''.&mt('Currently set date:').''. + &Apache::lonlocal::locallocaltime($env{'form.timebase'}).''. + ''.&mt('Shifted date:').''. + &Apache::lonhtmlcommon::date_setter('shiftform', + 'timeshifted', + $env{'form.timebase'},, + ''). + ''. + ''. + ''. + ''); + $r->print(&Apache::loncommon::end_page()); +} + +sub date_shift_two { + my ($r) = @_; + my $dom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my $crs = $env{'course.'.$env{'request.course.id'}.'.num'}; + my $start_page=&Apache::loncommon::start_page('Shift Dates'); + my $breadcrumbs = &Apache::lonhtmlcommon::breadcrumbs('Shift'); + $r->print(<print(&mt('Shifting all dates such that [_1] becomes [_2]', + &Apache::lonlocal::locallocaltime($env{'form.timebase'}), + &Apache::lonlocal::locallocaltime($timeshifted))); + my $delta=$timeshifted-$env{'form.timebase'}; + &dateshift($delta); + $r->print(&Apache::loncommon::end_page()); +} + +sub parse_key { + my ($key) = @_; + my %data; + my ($middle,$part,$name)= + ($key=~/^$env{'request.course.id'}\.(?:(.+)\.)*([\w\s]+)\.(\w+)$/); + $data{'scope_type'} = 'all'; + if ($middle=~/^\[(.*)\]/) { + $data{'scope'} = $1; + if ($data{'scope'}=~/^useropt\:($match_username)\:($match_domain)/) { + $data{'scope_type'} = 'user'; + $data{'scope'} = [$1,$2]; + } else { + #FIXME check for group scope + $data{'scope_type'} = 'section'; + } + $middle=~s/^\[(.*)\]//; + } + $middle=~s/\.+$//; + $middle=~s/^\.+//; + $data{'realm_type'}='all'; + if ($middle=~/^(.+)\_\_\_\(all\)$/) { + $data{'realm'} = $1; + $data{'realm_type'} = 'folder'; + $data{'realm_title'} = &Apache::lonnet::gettitle($data{'realm'}); + ($data{'realm_exists'}) = &Apache::lonnet::is_on_map($data{'realm'}); + } elsif ($middle) { + $data{'realm'} = $middle; + $data{'realm_type'} = 'symb'; + $data{'realm_title'} = &Apache::lonnet::gettitle($data{'realm'}); + my ($map,$resid,$url) = &Apache::lonnet::decode_symb($data{'realm'}); + $data{'realm_exists'} = &Apache::lonnet::symbverify($data{'realm'},$url); + } + + $data{'parameter_part'} = $part; + $data{'parameter_name'} = $name; + + return %data; +} + ################################################## ################################################## - + =pod =item check_cloners @@ -2869,48 +3471,63 @@ where $action is add or drop, and $clone user for whom cloning ability is to be changed in course. =cut - + ################################################## ################################################## sub extract_cloners { my ($clonelist,$allowclone) = @_; if ($clonelist =~ /,/) { - @{$allowclone} = split/,/,$clonelist; + @{$allowclone} = split(/,/,$clonelist); } else { $$allowclone[0] = $clonelist; } } - sub check_cloners { my ($clonelist,$oldcloner) = @_; - my ($clean_clonelist,$disallowed); + my ($clean_clonelist,%disallowed); my @allowclone = (); &extract_cloners($$clonelist,\@allowclone); foreach my $currclone (@allowclone) { - if (!grep/^$currclone$/,@$oldcloner) { - my ($uname,$udom) = split/:/,$currclone; - if ($uname && $udom) { - if (&Apache::lonnet::homeserver($uname,$udom) eq 'no_host') { - $disallowed .= $currclone.','; + if (!grep(/^\Q$currclone\E$/,@$oldcloner)) { + if ($currclone eq '*') { + $clean_clonelist .= $currclone.','; + } else { + my ($uname,$udom) = split(/:/,$currclone); + if ($uname eq '*') { + if ($udom =~ /^$match_domain$/) { + if (!&Apache::lonnet::domain($udom)) { + $disallowed{'domain'} .= $currclone.','; + } else { + $clean_clonelist .= $currclone.','; + } + } else { + $disallowed{'format'} .= $currclone.','; + } + } elsif ($currclone !~/^($match_username)\:($match_domain)$/) { + $disallowed{'format'} .= $currclone.','; } else { - $clean_clonelist .= $currclone.','; + if (&Apache::lonnet::homeserver($uname,$udom) eq 'no_host') { + $disallowed{'newuser'} .= $currclone.','; + } else { + $clean_clonelist .= $currclone.','; + } } } } else { $clean_clonelist .= $currclone.','; } } - if ($disallowed) { - $disallowed =~ s/,$//; + foreach my $key (keys(%disallowed)) { + $disallowed{$key} =~ s/,$//; } if ($clean_clonelist) { $clean_clonelist =~ s/,$//; } $$clonelist = $clean_clonelist; - return $disallowed; -} + return %disallowed; +} sub change_clone { my ($clonelist,$oldcloner) = @_; @@ -2923,43 +3540,47 @@ sub change_clone { my @allowclone; &extract_cloners($clonelist,\@allowclone); foreach my $currclone (@allowclone) { - if (!grep/^$currclone$/,@$oldcloner) { - ($uname,$udom) = split/:/,$currclone; - if ($uname && $udom) { - unless (&Apache::lonnet::homeserver($uname,$udom) eq 'no_host') { - my %currclonecrs = &Apache::lonnet::dump('environment',$udom,$uname,'cloneable'); - if ($currclonecrs{'cloneable'} !~ /\Q$clone_crs\E/) { - if ($currclonecrs{'cloneable'} eq '') { - $currclonecrs{'cloneable'} = $clone_crs; - } else { - $currclonecrs{'cloneable'} .= ','.$clone_crs; + if (!grep(/^$currclone$/,@$oldcloner)) { + if ($currclone ne '*') { + ($uname,$udom) = split(/:/,$currclone); + if ($uname && $udom && $uname ne '*') { + if (&Apache::lonnet::homeserver($uname,$udom) ne 'no_host') { + my %currclonecrs = &Apache::lonnet::dump('environment',$udom,$uname,'cloneable'); + if ($currclonecrs{'cloneable'} !~ /\Q$clone_crs\E/) { + if ($currclonecrs{'cloneable'} eq '') { + $currclonecrs{'cloneable'} = $clone_crs; + } else { + $currclonecrs{'cloneable'} .= ','.$clone_crs; + } + &Apache::lonnet::put('environment',\%currclonecrs,$udom,$uname); } - &Apache::lonnet::put('environment',\%currclonecrs,$udom,$uname); } } } } } foreach my $oldclone (@$oldcloner) { - if (!grep/^$oldclone$/,@allowclone) { - ($uname,$udom) = split/:/,$oldclone; - if ($uname && $udom) { - unless (&Apache::lonnet::homeserver($uname,$udom) eq 'no_host') { - my %currclonecrs = &Apache::lonnet::dump('environment',$udom,$uname,'cloneable'); - my %newclonecrs = (); - if ($currclonecrs{'cloneable'} =~ /\Q$clone_crs\E/) { - if ($currclonecrs{'cloneable'} =~ /,/) { - my @currclonecrs = split/,/,$currclonecrs{'cloneable'}; - foreach (@currclonecrs) { - unless ($_ eq $clone_crs) { - $newclonecrs{'cloneable'} .= $_.','; + if (!grep(/^\Q$oldclone\E$/,@allowclone)) { + if ($oldclone ne '*') { + ($uname,$udom) = split(/:/,$oldclone); + if ($uname && $udom && $uname ne '*' ) { + if (&Apache::lonnet::homeserver($uname,$udom) ne 'no_host') { + my %currclonecrs = &Apache::lonnet::dump('environment',$udom,$uname,'cloneable'); + my %newclonecrs = (); + if ($currclonecrs{'cloneable'} =~ /\Q$clone_crs\E/) { + if ($currclonecrs{'cloneable'} =~ /,/) { + my @currclonecrs = split/,/,$currclonecrs{'cloneable'}; + foreach my $crs (@currclonecrs) { + if ($crs ne $clone_crs) { + $newclonecrs{'cloneable'} .= $crs.','; + } } + $newclonecrs{'cloneable'} =~ s/,$//; + } else { + $newclonecrs{'cloneable'} = ''; } - $newclonecrs{'cloneable'} =~ s/,$//; - } else { - $newclonecrs{'cloneable'} = ''; + &Apache::lonnet::put('environment',\%newclonecrs,$udom,$uname); } - &Apache::lonnet::put('environment',\%newclonecrs,$udom,$uname); } } } @@ -2998,12 +3619,15 @@ ENDMAINFORMHEAD my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; my $vgr = &Apache::lonnet::allowed('vgr',$env{'request.course.id'}); + my $mgr = &Apache::lonnet::allowed('mgr',$env{'request.course.id'}); my @menu = - ( - { text => 'Set Course Environment Parameters', + ( { divider=>'Settings for Your Course', + }, + { text => 'Set Course Environment', action => 'crsenv', permission => $parm_permission, + help => 'Course_Environment', }, { text => 'Set Portfolio Metadata', action => 'setrestrictmeta', @@ -3013,29 +3637,39 @@ ENDMAINFORMHEAD url => '/adm/slotrequest?command=showslots', permission => $vgr, }, - { divider => 1, + { text => 'Reset Student Access Times', + url => '/adm/helper/resettimes.helper', + permission => $mgr, + }, + + { text => 'Set Parameter Setting Default Actions', + action => 'setdefaults', + permission => $parm_permission, + }, + { divider => 'New and Existing Parameter Settings for Your Resources', }, { text => 'Set/Modify Resource Parameters - Helper Mode', url => '/adm/helper/parameter.helper', permission => $parm_permission, + help => 'Parameter_Helper', }, - { text => 'Modify Resource Parameters - Overview Mode', - action => 'setoverview', - permission => $parm_permission, - }, - { text => 'Set Resource Parameters - Overview Mode', + { text => 'Set/Modify Resource Parameters - Overview Mode', action => 'newoverview', permission => $parm_permission, + help => 'Parameter_Overview', }, { text => 'Set/Modify Resource Parameters - Table Mode', action => 'settable', permission => $parm_permission, - help => 'Cascading_Parameters', + help => 'Table_Mode', }, - { text => 'Set Parameter Setting Default Actions', - action => 'setdefaults', + { divider => 'Existing Parameter Settings for Your Resources', + }, + { text => 'Modify Resource Parameters - Overview Mode', + action => 'setoverview', permission => $parm_permission, - }, + help => 'Parameter_Overview', + }, { text => 'Parameter Change Log and Course Blog Posting/User Notification', action => 'parameterchangelog', permission => $parm_permission, @@ -3044,7 +3678,7 @@ ENDMAINFORMHEAD my $menu_html = ''; foreach my $menu_item (@menu) { if ($menu_item->{'divider'}) { - $menu_html .= ''; + $menu_html .= ''.&mt($menu_item->{'divider'}).''; next; } next if (! $menu_item->{'permission'}); @@ -3068,36 +3702,200 @@ ENDMAINFORMHEAD } ### Set portfolio metadata sub output_row { - my ($r, $field_name, $field_text) = @_; + my ($r, $field_name, $field_text, $added_flag) = @_; my $output; my $options=$env{'course.'.$env{'request.course.id'}.'.metadata.'.$field_name.'.options'}; my $values=$env{'course.'.$env{'request.course.id'}.'.metadata.'.$field_name.'.values'}; - unless (defined($options)) { + if (!defined($options)) { $options = 'active,stuadd'; $values = ''; } - $output.=''.$field_text.':'; - $output.=''; - - my @options= ( ['active', 'Show to student'], - ['onlyone','Student may select only one choice'], - ['stuadd', 'Student may type choices']); - foreach my $opt (@options) { - my $checked = ($options =~ m/$opt->[0]/) ? ' checked="checked" ' : '' ; - $output.=(' 'x5).''. - &mt($opt->[1]).' '; + if (!($options =~ /deleted/)) { + my @options= ( ['active', 'Show to student'], + ['stuadd', 'Provide text area for students to type catalog information'], + ['choices','Provide choices for students to select from']); +# ['onlyone','Student may select only one choice']); + if ($added_flag) { + push @options,['deleted', 'Delete Metadata Field']; + } + $output = &Apache::loncommon::start_data_table_row(); + $output .= ''.$field_text.':'; + $output .= &Apache::loncommon::end_data_table_row(); + foreach my $opt (@options) { + my $checked = ($options =~ m/$opt->[0]/) ? ' checked="checked" ' : '' ; + $output .= &Apache::loncommon::continue_data_table_row(); + $output .= ''.(' ' x 5).' + '. + &mt($opt->[1]).' '; + $output .= &Apache::loncommon::end_data_table_row(); + } + $output .= &Apache::loncommon::continue_data_table_row(); + $output .= ''.(' ' x 10).''; + $output .= &Apache::loncommon::end_data_table_row(); + my $multiple_checked; + my $single_checked; + if ($options =~ m/onlyone/) { + $multiple_checked = ""; + $single_checked = " CHECKED "; + } else { + $multiple_checked = " CHECKED "; + $single_checked = ""; + } + $output .= &Apache::loncommon::continue_data_table_row(); + $output .= ''.(' ' x 10).' + + Student may select multiple choices from list'; + $output .= &Apache::loncommon::end_data_table_row(); + $output .= &Apache::loncommon::continue_data_table_row(); + $output .= ''.(' ' x 10).' + + Student may select only one choice from list'; + $output .= &Apache::loncommon::end_data_table_row(); } return ($output); } - +sub order_meta_fields { + my ($r)=@_; + my $idx = 1; + my $dom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my $crs = $env{'course.'.$env{'request.course.id'}.'.num'}; + $r->print(&Apache::loncommon::start_page('Order Metadata Fields')); + &Apache::lonhtmlcommon::add_breadcrumb + ({href=>"/adm/parmset?action=setrestrictmeta", + text=>"Restrict Metadata"}, + {text=>"Order Metadata"}); + $r->print(&Apache::lonhtmlcommon::breadcrumbs('Order Metadata')); + if ($env{'form.storeorder'}) { + my $newpos = $env{'form.newpos'} - 1; + my $currentpos = $env{'form.currentpos'} - 1; + my @neworder = (); + my @oldorder = split /,/,$env{'course.'.$env{'request.course.id'}.'.metadata.addedorder'}; + my $i; + if ($newpos > $currentpos) { + # moving stuff up + for ($i=0;$i<$currentpos;$i++) { + $neworder[$i]=$oldorder[$i]; + } + for ($i=$currentpos;$i<$newpos;$i++) { + $neworder[$i]=$oldorder[$i+1]; + } + $neworder[$newpos]=$oldorder[$currentpos]; + for ($i=$newpos+1;$i<=$#oldorder;$i++) { + $neworder[$i]=$oldorder[$i]; + } + } else { + # moving stuff down + for ($i=0;$i<$newpos;$i++) { + $neworder[$i]=$oldorder[$i]; + } + $neworder[$newpos]=$oldorder[$currentpos]; + for ($i=$newpos+1;$i<$currentpos+1;$i++) { + $neworder[$i]=$oldorder[$i-1]; + } + for ($i=$currentpos+1;$i<=$#oldorder;$i++) { + $neworder[$i]=$oldorder[$i]; + } + } + my $ordered_fields = join ",", @neworder; + my $put_result = &Apache::lonnet::put('environment', + {'metadata.addedorder'=>$ordered_fields},$dom,$crs); + &Apache::lonnet::appenv({'course.'.$env{'request.course.id'}.'.metadata.addedorder' => $ordered_fields}); + } + my $fields = &get_added_meta_fieldnames($env{'request.course.id'}); + my $ordered_fields; + my @fields_in_order = split /,/,$env{'course.'.$env{'request.course.id'}.'.metadata.addedorder'}; + if (!@fields_in_order) { + # no order found, pick sorted order then create metadata.addedorder key. + foreach my $key (sort keys %$fields) { + push @fields_in_order, $key; + $ordered_fields = join ",", @fields_in_order; + } + my $put_result = &Apache::lonnet::put('environment', + {'metadata.addedorder'=>$ordered_fields},$dom,$crs); + } + $r->print(''); + my $num_fields = scalar(@fields_in_order); + foreach my $key (@fields_in_order) { + $r->print(''); + $r->print(''); + $r->print(''); + for (my $i = 1;$i le $num_fields;$i ++) { + if ($i eq $idx) { + $r->print('('.$i.')'); + } else { + $r->print(''.$i.''); + } + } + $r->print(''); + $r->print(''); + $r->print(''); + $r->print(''); + $r->print($$fields{$key}.''); + $idx ++; + } + $r->print(''); + return 'ok'; +} +sub continue { + my $output; + $output .= ''; + $output .= ''; + $output .= ''; + return ($output); +} +sub addmetafield { + my ($r)=@_; + $r->print(&Apache::loncommon::start_page('Add Metadata Field')); + $r->print(&Apache::lonhtmlcommon::breadcrumbs('Add Metadata Field')); + my $dom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my $crs = $env{'course.'.$env{'request.course.id'}.'.num'}; + if (exists($env{'form.undelete'})) { + my @meta_fields = &Apache::loncommon::get_env_multiple('form.undeletefield'); + foreach my $meta_field(@meta_fields) { + my $options = $env{'course.'.$env{'request.course.id'}.'.metadata.'.$meta_field.'.options'}; + $options =~ s/deleted//; + $options =~ s/,,/,/; + my $put_result = &Apache::lonnet::put('environment', + {'metadata.'.$meta_field.'.options'=>$options},$dom,$crs); + + $r->print('Undeleted Metadata Field '.$env{'course.'.$env{'request.course.id'}.'.metadata.'.$meta_field.'.added'}." with result ".$put_result.''); + } + $r->print(&continue()); + } elsif (exists($env{'form.fieldname'})) { + my $meta_field = $env{'form.fieldname'}; + my $display_field = $env{'form.fieldname'}; + $meta_field =~ s/\W/_/g; + $meta_field =~ tr/A-Z/a-z/; + my $put_result = &Apache::lonnet::put('environment', + {'metadata.'.$meta_field.'.values'=>"", + 'metadata.'.$meta_field.'.added'=>"$display_field", + 'metadata.'.$meta_field.'.options'=>""},$dom,$crs); + $r->print('Added new Metadata Field '.$env{'form.fieldname'}." with result ".$put_result.''); + $r->print(&continue()); + } else { + my $fields = &get_deleted_meta_fieldnames($env{'request.course.id'}); + if ($fields) { + $r->print('You may undelete previously deleted fields.Check those you wish to undelete and click Undelete.'); + $r->print(''); + foreach my $key(keys(%$fields)) { + $r->print(''.$$fields{$key}.'print(''); + $r->print(''); + } + $r->print('Or you may enter a new metadata field name.print(''); + $r->print(''); + } + $r->print(''); +} sub setrestrictmeta { my ($r)=@_; my $next_meta; my $output; my $item_num; my $put_result; - $r->print(&Apache::loncommon::start_page('Restrict Metadata')); $r->print(&Apache::lonhtmlcommon::breadcrumbs('Restrict Metadata')); my $dom = $env{'course.'.$env{'request.course.id'}.'.domain'}; @@ -3115,12 +3913,18 @@ sub setrestrictmeta { if ($env{'form.'.$meta_field.'_stuadd'}) { $options.='stuadd,'; } - if ($env{'form.'.$meta_field.'_onlyone'}) { + if ($env{'form.'.$meta_field.'_choices'}) { + $options.='choices,'; + } + if ($env{'form.'.$meta_field.'_onlyone'} eq 'single') { $options.='onlyone,'; } if ($env{'form.'.$meta_field.'_active'}) { $options.='active,'; } + if ($env{'form.'.$meta_field.'_deleted'}) { + $options.='deleted,'; + } my $name = $save_field; $put_result = &Apache::lonnet::put('environment', {'metadata.'.$meta_field.'.options'=>$options, @@ -3132,25 +3936,70 @@ sub setrestrictmeta { } &Apache::lonnet::coursedescription($env{'request.course.id'}, {'freshen_cache' => 1}); + # Get the default metadata fields my %metadata_fields = &Apache::lonmeta::fieldnames('portfolio'); + # Now get possible added metadata fields + my $added_metadata_fields = &get_added_meta_fieldnames($env{'request.course.id'}); + my $row_alt = 1; + $output .= &Apache::loncommon::start_data_table(); foreach my $field (sort(keys(%metadata_fields))) { - &Apache::lonnet::logthis ($field); if ($field ne 'courserestricted') { + $row_alt = $row_alt ? 0 : 1; $output.= &output_row($r, $field, $metadata_fields{$field}); } } + my $buttons = (< + + + + + + + +ENDButtons + my $added_flag = 1; + foreach my $field (sort(keys(%$added_metadata_fields))) { + $row_alt = $row_alt ? 0 : 1; + $output.= &output_row($r, $field, $$added_metadata_fields{$field},$added_flag, $row_alt); + } + $output .= &Apache::loncommon::end_data_table(); $r->print(< - $output - + $buttons ENDenv $r->print(&Apache::loncommon::end_page()); return 'ok'; } ################################################## - +sub get_added_meta_fieldnames { + my ($cid) = @_; + my %fields; + foreach my $key(%env) { + if ($key =~ m/\Q$cid\E\.metadata\.(.+)\.added$/) { + my $field_name = $1; + my ($display_field_name) = $env{$key}; + $fields{$field_name} = $display_field_name; + } + } + return \%fields; +} +sub get_deleted_meta_fieldnames { + my ($cid) = @_; + my %fields; + foreach my $key(%env) { + if ($key =~ m/\Q$cid\E\.metadata\.(.+)\.added$/) { + my $field_name = $1; + if ($env{'course.'.$env{'request.course.id'}.'.metadata.'.$field_name.'.options'} =~ m/deleted/) { + my ($display_field_name) = $env{$key}; + $fields{$field_name} = $display_field_name; + } + } + } + return \%fields; +} sub defaultsetter { my ($r) = @_; @@ -3306,20 +4155,22 @@ ENDYESNO } $r->print(&Apache::loncommon::end_data_table(). "\n\n". + &mt('Save Rules')."' />\n". &Apache::loncommon::end_page()); return; } sub components { - my ($key,$uname,$udom,$exeuser,$exedomain)=@_; - my $typeflag=0; - if ($key=~/\.type$/) { + my ($key,$uname,$udom,$exeuser,$exedomain,$typeflag)=@_; + + if ($typeflag) { $key=~s/\.type$//; - $typeflag=1; } + + my ($middle,$part,$name)= + ($key=~/^$env{'request.course.id'}\.(?:(.+)\.)*([\w\s]+)\.(\w+)$/); my $issection; - my ($middle,$part,$name)=($key=~/^$env{'request.course.id'}\.(?:(.+)\.)*([\w\s]+)\.(\w+)$/); + my $section=&mt('All Students'); if ($middle=~/^\[(.*)\]/) { $issection=$1; @@ -3343,31 +4194,33 @@ sub components { $realmdescription=&mt('resource').' '.&Apache::lonnet::gettitle($middle); } my $what=$part.'.'.$name; - return ($realm,$section,$name,$part,$typeflag, + return ($realm,$section,$name,$part, $what,$middle,$uname,$udom,$issection,$realmdescription); } +my %standard_parms; +sub load_parameter_names { + open(my $config,"<$Apache::lonnet::perlvar{'lonTabDir'}/packages.tab"); + while (my $configline=<$config>) { + if ($configline !~ /\S/ || $configline=~/^\#/) { next; } + chomp($configline); + my ($short,$plain)=split(/:/,$configline); + my (undef,$name,$type)=split(/\&/,$short,3); + if ($type eq 'display') { + $standard_parms{$name} = $plain; + } + } + close($config); + $standard_parms{'int_pos'} = 'Positive Integer'; + $standard_parms{'int_zero_pos'} = 'Positive Integer or Zero'; + %standard_parms=&Apache::lonlocal::texthash(%standard_parms); +} + sub standard_parameter_names { my ($name)=@_; - my %standard_parms=&Apache::lonlocal::texthash('duedate' => 'Due Date', - 'answerdate' => 'Answer Date', - 'opendate' => 'Open Date', - 'maxtries' => 'Max. Number of Tries', - 'weight' => 'Weight', - 'date_start' => 'Starting Date', - 'date_end' => 'Ending Date', - 'interval' => 'Time Interval Length', - 'tol' => 'Numerical Tolerance', - 'sig' => 'Significant Digits', - 'contentopen' => 'Content Opening Date', - 'contentclose' => 'Content Closing Date', - 'discussend' => 'End of Discussion Time', - 'discusshide' => 'Discussion Hidden', - 'problemstatus' => 'Problem Status Visible', - 'int_pos' => 'Positive Integer', - 'int_zero_pos' => 'Positive Integer or Zero', - 'hinttries' => 'Number of Tries till Hints appear', - 'numbubbles' => 'Number of Bubbles in Exam Mode'); + if (!%standard_parms) { + &load_parameter_names(); + } if ($standard_parms{$name}) { return $standard_parms{$name}; } else { @@ -3382,31 +4235,27 @@ sub standard_parameter_names { sub parm_change_log { my ($r)=@_; - &startpage($r); + $r->print(&Apache::loncommon::start_page('Parameter Change Log')); + $r->print(&Apache::lonhtmlcommon::breadcrumbs('Parameter Change Log')); + my %parmlog=&Apache::lonnet::dump('nohist_parameterlog', $env{'course.'.$env{'request.course.id'}.'.domain'}, $env{'course.'.$env{'request.course.id'}.'.num'}); if ((keys(%parmlog))[0]=~/^error\:/) { undef(%parmlog); } - $r->print(' - '); + $r->print(''); my %saveable_parameters = ('show' => 'scalar',); &Apache::loncommon::store_course_settings('parameter_log', \%saveable_parameters); &Apache::loncommon::restore_course_settings('parameter_log', \%saveable_parameters); - if (!$env{'form.show'}) { $env{'form.show'}=10; } - - my $countselect = - &Apache::lonmeta::selectbox('show',$env{'form.show'},undef, - (&mt('all'),10,20,50,100,1000,10000)); - - $r->print(''.&mt('[_1] Records',$countselect).''. - ''); + $r->print(&Apache::loncommon::display_filter(). + ''.&Apache::lonhtmlcommon::checkbox('includetypes',$env{'form.includetypes'},'1'). + ' '.&mt('Include parameter types').''. + ''); my $courseopt=&Apache::lonnet::get_courseresdata($env{'course.'.$env{'request.course.id'}.'.num'}, $env{'course.'.$env{'request.course.id'}.'.domain'}); @@ -3415,9 +4264,27 @@ sub parm_change_log { &mt('Parameter').''.&mt('Part').''.&mt('New Value').''.&mt('Announce').''. &Apache::loncommon::end_data_table_header_row()); my $shown=0; - foreach my $id (sort { $parmlog{$b}{'exe_time'}<=>$parmlog{$a}{'exe_time'} } (keys(%parmlog))) { + my $folder=''; + if ($env{'form.displayfilter'} eq 'currentfolder') { + my $last=''; + if (tie(my %hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db', + &GDBM_READER(),0640)) { + $last=$hash{'last_known'}; + untie(%hash); + } + if ($last) { ($folder) = &Apache::lonnet::decode_symb($last); } + } + foreach my $id (sort + { + if ($parmlog{$b}{'exe_time'} ne $parmlog{$a}{'exe_time'}) { + return $parmlog{$b}{'exe_time'} <=>$parmlog{$a}{'exe_time'} + } + my $aid = (split('00000',$a))[-1]; + my $bid = (split('00000',$b))[-1]; + return $bid<=>$aid; + } (keys(%parmlog))) { my @changes=keys(%{$parmlog{$id}{'logentry'}}); - my $count=$#changes+1; + my $count = 0; my $time = &Apache::lonlocal::locallocaltime($parmlog{$id}{'exe_time'}); my $plainname = @@ -3436,71 +4303,99 @@ sub parm_change_log { $parmlog{$id}{'exe_udom'}); } my $row_start=&Apache::loncommon::start_data_table_row(); - $r->print($row_start.''.$time.' - '.$about_me_link. - ''.$parmlog{$id}{'exe_uname'}. - ':'.$parmlog{$id}{'exe_udom'}.''. - $send_msg_link.''); my $makenewrow=0; my %istype=(); + my $output; foreach my $changed (reverse(sort(@changes))) { - my $value=$parmlog{$id}{'logentry'}->{$changed}; - my ($realm,$section,$parmname,$part,$typeflag,$what,$middle,$uname,$udom,$issection,$realmdescription)= - &components($changed,$parmlog{$id}{'uname'},$parmlog{$id}{'udom'}); - if ($typeflag) { $istype{$parmname}=$value; } - if ($makenewrow) { $r->print($row_start); } else { $makenewrow=1; } - $r->print(''.$realm.''.$section.''. + my $value=$parmlog{$id}{'logentry'}{$changed}; + my $typeflag = ($changed =~/\.type$/ && + !exists($parmlog{$id}{'logentry'}{$changed.'.type'})); + my ($realm,$section,$parmname,$part,$what,$middle,$uname,$udom,$issection,$realmdescription)= + &components($changed,$parmlog{$id}{'uname'},$parmlog{$id}{'udom'},undef,undef,$typeflag); + if ($env{'form.displayfilter'} eq 'currentfolder') { + if ($folder) { + if ($middle!~/^\Q$folder\E/) { next; } + } + } + if ($typeflag) { + $istype{$parmname}=$value; + if (!$env{'form.includetypes'}) { next; } + } + $count++; + if ($makenewrow) { + $output .= $row_start; + } else { + $makenewrow=1; + } + $output .=''.$realm.''.$section.''. &standard_parameter_names($parmname).''. - ($part?&mt('Part: [_1]',$part):&mt('All Parts')).''); + ($part?&mt('Part: [_1]',$part):&mt('All Parts')).''; my $stillactive=0; - if ($parmlog{$id}{'deleteflag'}) { - $r->print(&mt('Deleted')); + if ($parmlog{$id}{'delflag'}) { + $output .= &mt('Deleted'); } else { if ($typeflag) { - $r->print(&mt('Type: [_1]',&standard_parameter_names($value))); + $output .= &mt('Type: [_1]',&standard_parameter_names($value)); } else { my ($level,@all)=&parmval_by_symb($what,$middle,&Apache::lonnet::metadata($middle,$what), $uname,$udom,$issection,$issection,$courseopt); if (&isdateparm($istype{$parmname})) { - $r->print(&Apache::lonlocal::locallocaltime($value)); + $output .= &Apache::lonlocal::locallocaltime($value); } else { - $r->print($value); + $output .= $value; } if ($value ne $all[$level]) { - $r->print(''.&mt('Not active anymore').''); + $output .= ''.&mt('Not active anymore').''; } else { $stillactive=1; } } } - $r->print(''); + $output .= ''; if ($stillactive) { my $title=&mt('Changed [_1]',&standard_parameter_names($parmname)); my $description=&mt('Changed [_1] for [_2] to [_3]',&standard_parameter_names($parmname),$realmdescription, (&isdateparm($istype{$parmname})?&Apache::lonlocal::locallocaltime($value):$value)); if (($uname) && ($udom)) { - $r->print(''. - &Apache::loncommon::messagewrapper('Notify User',$uname,$udom,$title,$description). - ''); + $output .= + &Apache::loncommon::messagewrapper('Notify User', + $uname,$udom,$title, + $description); } else { - $r->print(''. - &Apache::lonrss::course_blog_link($id,$title,$description). - ''); + $output .= + &Apache::lonrss::course_blog_link($id,$title, + $description); } - } else { - $r->print(' '); } - $r->print(&Apache::loncommon::end_data_table_row()); + $output .= ''.&Apache::loncommon::end_data_table_row(); + } + if ($env{'form.displayfilter'} eq 'containing') { + my $wholeentry=$about_me_link.':'. + $parmlog{$id}{'exe_uname'}.':'.$parmlog{$id}{'exe_udom'}.':'. + $output; + if ($wholeentry!~/\Q$env{'form.containingphrase'}\E/i) { next; } + } + if ($count) { + $r->print($row_start.''.$time.' + '.$about_me_link. + ''.$parmlog{$id}{'exe_uname'}. + ':'.$parmlog{$id}{'exe_udom'}.''. + $send_msg_link.''.$output); + $shown++; } - $shown++; if (!($env{'form.show'} eq &mt('all') || $shown<=$env{'form.show'})) { last; } } $r->print(&Apache::loncommon::end_data_table()); - $r->print(''); $r->print(&Apache::loncommon::end_page()); } +sub check_for_course_info { + my $navmap = Apache::lonnavmaps::navmap->new(); + return 1 if ($navmap); + return 0; +} + ################################################## ################################################## @@ -3511,14 +4406,15 @@ sub parm_change_log { Main handler. Calls &assessparms and &crsenv subroutines. =cut + ################################################## ################################################## -# use Data::Dumper; - sub handler { my $r=shift; + &reset_caches(); + if ($r->header_only) { &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; @@ -3529,31 +4425,30 @@ sub handler { 'pres_marker', 'pres_value', 'pres_type', - 'udom','uname','symb','serial']); + 'udom','uname','symb','serial','timebase']); &Apache::lonhtmlcommon::clear_breadcrumbs(); &Apache::lonhtmlcommon::add_breadcrumb({href=>"/adm/parmset", text=>"Parameter Manager", faq=>10, - bug=>'Instructor Interface'}); + bug=>'Instructor Interface', + help => 'Parameter_Manager'}); # ----------------------------------------------------- Needs to be in a course my $parm_permission = (&Apache::lonnet::allowed('opa',$env{'request.course.id'}) || &Apache::lonnet::allowed('opa',$env{'request.course.id'}.'/'. $env{'request.course.sec'})); - if ($env{'request.course.id'} && $parm_permission) { + my $exists = &check_for_course_info(); + + if ($env{'request.course.id'} && $parm_permission && $exists) { # Start Page &Apache::loncommon::content_type($r,'text/html'); $r->send_http_header; - # id numbers can change on re-ordering of folders - - &resetsymbcache(); - # # Main switch on form.action and form.state, as appropriate # @@ -3561,6 +4456,8 @@ sub handler { # the table mode if ((($env{'form.command'} eq 'set') && ($env{'form.url'}) && (!$env{'form.dis'})) || ($env{'form.symb'})) { + &Apache::lonhtmlcommon::add_breadcrumb({help=>'Problem_Parameters', + text=>"Problem Parameters"}); &assessparms($r); } elsif (! exists($env{'form.action'})) { @@ -3575,6 +4472,14 @@ sub handler { &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/parmset?action=setoverview', text=>"Overview Mode"}); &overview($r); + } elsif ($env{'form.action'} eq 'addmetadata' && $parm_permission) { + &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/parmset?action=addmetadata', + text=>"Add Metadata Field"}); + &addmetafield($r); + } elsif ($env{'form.action'} eq 'ordermetadata' && $parm_permission) { + &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/parmset?action=addmetadata', + text=>"Add Metadata Field"}); + &order_meta_fields($r); } elsif ($env{'form.action'} eq 'setrestrictmeta' && $parm_permission) { &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/parmset?action=setrestrictmeta', text=>"Restrict Metadata"}); @@ -3596,13 +4501,34 @@ sub handler { &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/parmset?action=settable', text=>"Parameter Change Log"}); &parm_change_log($r); - } + } elsif ($env{'form.action'} eq 'cleanparameters' && $parm_permission) { + &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/parmset?action=cleanparameters', + text=>"Clean Parameters"}); + &clean_parameters($r); + } elsif ($env{'form.action'} eq 'dateshift1' && $parm_permission) { + &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/parmset?action=dateshift1&timebase='.$env{'form.timebase'}, + text=>"Shifting Dates"}); + &date_shift_one($r); + } elsif ($env{'form.action'} eq 'dateshift2' && $parm_permission) { + &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/parmset?action=dateshift1&timebase='.$env{'form.timebase'}, + text=>"Shifting Dates"}); + &date_shift_two($r); + } elsif ($env{'form.action'} eq 'categorizecourse' && $parm_permission) { + &assign_course_categories($r); + } } else { # ----------------------------- Not in a course, or not allowed to modify parms - $env{'user.error.msg'}= - "/adm/parmset:opa:0:0:Cannot modify assessment parameters"; + if ($exists) { + $env{'user.error.msg'}= + "/adm/parmset:opa:0:0:Cannot modify assessment parameters"; + } else { + $env{'user.error.msg'}= + "/adm/parmset::0:1:Course environment gone, reinitialize the course"; + } return HTTP_NOT_ACCEPTABLE; } + &reset_caches(); + return OK; }
'. + ''. + '
$output - + $buttons