--- loncom/interface/lonparmset.pm 2002/06/14 20:14:30 1.52
+++ loncom/interface/lonparmset.pm 2016/07/15 22:24:37 1.562
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Handler to set parameters for assessments
#
-# $Id: lonparmset.pm,v 1.52 2002/06/14 20:14:30 www Exp $
+# $Id: lonparmset.pm,v 1.562 2016/07/15 22:24:37 damieng Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -25,201 +25,1056 @@
#
# http://www.lon-capa.org/
#
-# (Handler to resolve ambiguous file locations
-#
-# (TeX Content Handler
-#
-# YEAR=2000
-# 05/29/00,05/30,10/11 Gerd Kortemeyer)
-#
-# 10/11,10/12,10/16 Gerd Kortemeyer)
-#
-# 11/20,11/21,11/22,11/23,11/24,11/25,11/27,11/28,
-# 12/08,12/12,
-# YEAR=2001
-# 16/01/01,02/08,03/20,03/23,03/24,03/26,05/09,
-# 07/05,07/06,08/08,08/09,09/01,09/21 Gerd Kortemeyer
-# 12/17 Scott Harrison
-# 12/19 Guy Albertelli
-# 12/26,12/27 Gerd Kortemeyer
-#
-###
+###################################################################
+###################################################################
+
+=pod
+
+=head1 NAME
+
+lonparmset - Handler to set parameters for assessments and course
+
+=head1 SYNOPSIS
+
+lonparmset provides an interface to setting course parameters.
+
+It contains all the code for the "Content and Problem Settings" UI, except
+for the helpers parameter.helper and resettimes.helper, and lonhelper.pm,
+and lonblockingmenu.pm.
+
+=head1 DESCRIPTION
+
+This module sets coursewide and assessment parameters.
+
+=head1 INTERNAL SUBROUTINES
+
+=over
+
+=item parmval()
+
+Figure out a cascading parameter.
+
+Inputs: $what - a parameter spec (incluse part info and name I.E. 0.weight)
+ $id - a bighash Id number
+ $def - the resource's default value 'stupid emacs
+
+Returns: A list, the first item is the index into the remaining list of items of parm values that is the active one, the list consists of parm values at the 18 possible levels
+
+18 - General Course
+17 - Map or Folder level in course (recursive)
+16 - Map or Folder level in course (non-recursive)
+15 - resource default
+14 - map default
+13 - resource level in course
+12 - General for section
+11 - Map or Folder level for section (recursive)
+10 - Map or Folder level for section (non-recursive)
+9 - resource level in section
+8 - General for group
+7 - Map or Folder level for group (recursive)
+6 - Map or Folder level for group (non-recursive)
+5 - resource level in group
+4 - General for specific student
+3 - Map or Folder level for specific student (recursive)
+2 - Map or Folder level for specific student (non-recursive)
+1 - resource level for specific student
+
+=item parmval_by_symb()
+
+=item reset_caches()
+
+=item cacheparmhash()
+
+=item parmhash()
+
+=item symbcache()
+
+=item preset_defaults()
+
+=item date_sanity_info()
+
+=item storeparm()
+
+Store a parameter by symb
+
+ Takes
+ - symb
+ - name of parameter
+ - level
+ - new value
+ - new type
+ - username
+ - userdomain
+
+=item log_parmset()
+
+=item storeparm_by_symb_inner()
+
+=item valout()
+
+Format a value for output.
+
+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
+
+=item plink()
+
+Produces a link anchor.
+
+Inputs: $type,$dis,$value,$marker,$return,$call
+
+Returns: scalar with html code for a link which will envoke the
+javascript function 'pjump'.
+
+=item page_js()
+
+=item startpage()
+
+=item print_row()
+
+=item print_td()
+
+=item print_usergroups()
+
+=item parm_control_group()
+
+=item extractResourceInformation() :
+
+ extractResourceInformation extracts lots of information about all of the the course's resources into a variety of hashes.
+
+Input: See list below
+
+=over 4
+
+=item * B : Current username
+
+=item * B : Domain of current user.
+
+=item * B : Course
+
+=back
+
+Outputs: See list below:
+
+=over 4
+
+=item * B (out) : An array that will contain all of the ids in the course.
+
+=item * B(out) : hash, id->type, where "type" contains the extension of the file, thus, I.
+
+=item * B (out) : hash, id->key list, will contain a comma separated list of the meta-data keys available for the given id
+
+=item * B (out) : hash, name of parameter->display value (what is the display value?)
+
+=item * B (out) : hash, part identification->text representation of part, where the text representation is "[Part $part]"
+
+=item * B (out) : hash, ???
+
+=item * B : ??
+
+=item * B : hash, id->full sym?
+
+=item * B
+
+=item * B
+
+=item * B
+
+=item * B
+
+=back
+
+=item isdateparm()
+
+=item parmmenu()
+
+=item partmenu()
+
+=item usermenu()
+
+=item displaymenu()
+
+=item mapmenu()
+
+=item levelmenu()
+
+=item sectionmenu()
+
+=item keysplit()
+
+=item keysinorder()
+
+=item keysinorder_bytype()
+
+=item keysindisplayorder()
+
+=item standardkeyorder()
+
+=item assessparms() :
+
+Show assessment data and parameters. This is a large routine that should
+be simplified and shortened... someday.
+
+Inputs: $r - the Apache request object.
+
+Returns: nothing
+
+Variables used (guessed by Jeremy):
+
+=over
+
+=item * B: ParameterS CATegories? ends up a list of the types of parameters that exist, e.g., tol, weight, acc, opendate, duedate, answerdate, sig, maxtries, type.
+
+=item * B: ParameterS PaRTs? a list of the parts of a problem that we are displaying? Used to display only selected parts?
+
+=item * B<@catmarker> contains list of all possible parameters including part #s
+
+=item * B<$fullkeyp> contains the full part/id # for the extraction of proper parameters
+
+=item * B<$tempkeyp> contains part 0 only (no ids - ie, subparts)
+ When storing information, store as part 0
+ When requesting information, request from full part
+
+=back
+
+=item tablestart()
+
+=item tableend()
+
+=item extractuser()
+
+=item parse_listdata_key()
+
+=item listdata()
+
+=item date_interval_selector()
+
+=item get_date_interval_from_form()
+
+=item default_selector()
+
+=item string_selector()
+
+=item dateshift()
+
+=item newoverview()
+
+=item secgroup_lister()
+
+=item overview()
+
+=item clean_parameters()
+
+=item date_shift_one()
+
+=item date_shift_two()
+
+=item parse_key()
+
+=item header()
+
+Output html header for page
+
+=item print_main_menu()
+
+=item output_row()
+
+Set portfolio metadata
+
+=item order_meta_fields()
+
+=item addmetafield()
+
+=item setrestrictmeta()
+
+=item get_added_meta_fieldnames()
+
+=item get_deleted_meta_fieldnames()
+
+=item defaultsetter()
+
+=item components()
+
+=item load_parameter_names()
+
+=item parm_change_log()
+
+=item handler() :
+
+Main handler. Calls &assessparms subroutine.
+
+=back
+
+=cut
+
+###################################################################
+###################################################################
package Apache::lonparmset;
use strict;
use Apache::lonnet;
use Apache::Constants qw(:common :http REDIRECT);
+use Apache::lonhtmlcommon();
use Apache::loncommon;
use GDBM_File;
+use Apache::lonhomework;
+use Apache::lonxml;
+use Apache::lonlocal;
+use Apache::lonnavmaps;
+use Apache::longroup;
+use Apache::lonrss;
+use HTML::Entities;
+use LONCAPA qw(:DEFAULT :match);
+
+
+##################################################
+# CONTENT AND PROBLEM SETTINGS HTML PAGE HEADER/FOOTER
+##################################################
+
+# Page header
+#
+# @param {Apache2::RequestRec} $r - Apache request object
+# @param {string} $mode - selected tab, 'parmset' for course and problem settings, or 'coursepref' for course settings
+# @param {string} $crstype - course type ('Community' for community settings)
+sub startSettingsScreen {
+ my ($r,$mode,$crstype)=@_;
+
+ my $tabtext = &mt('Course Settings');
+ if ($crstype eq 'Community') {
+ $tabtext = &mt('Community Settings');
+ }
+ $r->print("\n".'
');
+}
-my %courseopt;
-my %useropt;
-my %parmhash;
-
-my @ids;
-my %symbp;
-my %mapp;
-my %typep;
-my %keyp;
-
-my $uname;
-my $udom;
-my $uhome;
-my $csec;
-# -------------------------------------------- Figure out a cascading parameter
+##################################################
+# TABLE MODE
+# (parmval is also used for the log of parameter changes)
+##################################################
+# Calls parmval_by_symb, getting the symb from $id (the big hash resource id) with &symbcache.
+#
+# @param {string} $what - part info and parameter name separated by a dot, e.g. '0.weight'
+# @param {string} $id - big hash resource id
+# @param {string} $def - the resource's default value for this parameter
+# @param {string} $uname - user name
+# @param {string} $udom - user domain
+# @param {string} $csec - section name
+# @param {string} $cgroup - group name
+# @param {hash reference} $courseopt - course parameters hash (result of lonnet::get_courseresdata, dump of course's resourcedata.db)
+# @returns {Array}
sub parmval {
- my ($what,$id,$def)=@_;
+ my ($what,$id,$def,$uname,$udom,$csec,$cgroup,$courseopt)=@_;
+ return &parmval_by_symb($what,&symbcache($id),$def,$uname,$udom,$csec,
+ $cgroup,$courseopt);
+}
+
+# Returns an array containing
+# - the most specific level that is defined for that parameter (integer)
+# - an array with the level as index and the parameter value as value (when defined)
+# (level 1 is the most specific and will have precedence)
+#
+# @param {string} $what - part info and parameter name separated by a dot, e.g. '0.weight'
+# @param {string} $symb - resource symb
+# @param {string} $def - the resource's default value for this parameter
+# @param {string} $uname - user name
+# @param {string} $udom - user domain
+# @param {string} $csec - section name
+# @param {string} $cgroup - group name
+# @param {hash reference} $courseopt - course parameters hash (result of lonnet::get_courseresdata, dump of course's resourcedata.db)
+# @returns {Array}
+sub parmval_by_symb {
+ my ($what,$symb,$def,$uname,$udom,$csec,$cgroup,$courseopt)=@_;
+
+ my $useropt;
+ if ($uname ne '' && $udom ne '') {
+ $useropt = &Apache::lonnet::get_userresdata($uname,$udom);
+ }
+
my $result='';
my @outpar=();
# ----------------------------------------------------- Cascading lookup scheme
+ my $map=(&Apache::lonnet::decode_symb($symb))[0];
+ $map = &Apache::lonnet::deversion($map);
+
+ # NOTE: some of that code looks redondant with code in lonnavmaps::parmval_real,
+ # any change should be reflected there.
+
+ my $symbparm=$symb.'.'.$what;
+ my $recurseparm=$map.'___(rec).'.$what;
+ my $mapparm=$map.'___(all).'.$what;
+
+ my $grplevel=$env{'request.course.id'}.'.['.$cgroup.'].'.$what;
+ my $grplevelr=$env{'request.course.id'}.'.['.$cgroup.'].'.$symbparm;
+ my $grpleveli=$env{'request.course.id'}.'.['.$cgroup.'].'.$recurseparm;
+ my $grplevelm=$env{'request.course.id'}.'.['.$cgroup.'].'.$mapparm;
+
+ my $seclevel=$env{'request.course.id'}.'.['.$csec.'].'.$what;
+ my $seclevelr=$env{'request.course.id'}.'.['.$csec.'].'.$symbparm;
+ my $secleveli=$env{'request.course.id'}.'.['.$csec.'].'.$recurseparm;
+ my $seclevelm=$env{'request.course.id'}.'.['.$csec.'].'.$mapparm;
+
+ my $courselevel=$env{'request.course.id'}.'.'.$what;
+ my $courselevelr=$env{'request.course.id'}.'.'.$symbparm;
+ my $courseleveli=$env{'request.course.id'}.'.'.$recurseparm;
+ my $courselevelm=$env{'request.course.id'}.'.'.$mapparm;
+
+
+# --------------------------------------------------------- first, check course
+
+# 18 - General Course
+ if (defined($$courseopt{$courselevel})) {
+ $outpar[18]=$$courseopt{$courselevel};
+ $result=18;
+ }
- my $symbparm=$symbp{$id}.'.'.$what;
- my $mapparm=$mapp{$id}.'___(all).'.$what;
+# 17 - Map or Folder level in course (recursive)
+ if (defined($$courseopt{$courseleveli})) {
+ $outpar[17]=$$courseopt{$courseleveli};
+ $result=17;
+ }
+
+# 16 - Map or Folder level in course (non-recursive)
+ if (defined($$courseopt{$courselevelm})) {
+ $outpar[16]=$$courseopt{$courselevelm};
+ $result=16;
+ }
+
+# ------------------------------------------------------- second, check default
- my $seclevel=$ENV{'request.course.id'}.'.['.$csec.'].'.$what;
- my $seclevelr=$ENV{'request.course.id'}.'.['.$csec.'].'.$symbparm;
- my $seclevelm=$ENV{'request.course.id'}.'.['.$csec.'].'.$mapparm;
+# 15 - resource default
+ if (defined($def)) { $outpar[15]=$def; $result=15; }
- my $courselevel=$ENV{'request.course.id'}.'.'.$what;
- my $courselevelr=$ENV{'request.course.id'}.'.'.$symbparm;
- my $courselevelm=$ENV{'request.course.id'}.'.'.$mapparm;
+# ------------------------------------------------------ third, check map parms
-# -------------------------------------------------------- first, check default
+
+# 14 - map default
+ my $thisparm=&parmhash($symbparm);
+ if (defined($thisparm)) { $outpar[14]=$thisparm; $result=14; }
+
+# 13 - resource level in course
+ if (defined($$courseopt{$courselevelr})) {
+ $outpar[13]=$$courseopt{$courselevelr};
+ $result=13;
+ }
+
+# ------------------------------------------------------ fourth, back to course
+ if ($csec ne '') {
+# 12 - General for section
+ if (defined($$courseopt{$seclevel})) {
+ $outpar[12]=$$courseopt{$seclevel};
+ $result=12;
+ }
+# 11 - Map or Folder level for section (recursive)
+ if (defined($$courseopt{$secleveli})) {
+ $outpar[11]=$$courseopt{$secleveli};
+ $result=11;
+ }
+# 10 - Map or Folder level for section (non-recursive)
+ if (defined($$courseopt{$seclevelm})) {
+ $outpar[10]=$$courseopt{$seclevelm};
+ $result=10;
+ }
+# 9 - resource level in section
+ if (defined($$courseopt{$seclevelr})) {
+ $outpar[9]=$$courseopt{$seclevelr};
+ $result=9;
+ }
+ }
+# ------------------------------------------------------ fifth, check course group
+ if ($cgroup ne '') {
+# 8 - General for group
+ if (defined($$courseopt{$grplevel})) {
+ $outpar[8]=$$courseopt{$grplevel};
+ $result=8;
+ }
+# 7 - Map or Folder level for group (recursive)
+ if (defined($$courseopt{$grpleveli})) {
+ $outpar[7]=$$courseopt{$grpleveli};
+ $result=7;
+ }
+# 6 - Map or Folder level for group (non-recursive)
+ if (defined($$courseopt{$grplevelm})) {
+ $outpar[6]=$$courseopt{$grplevelm};
+ $result=6;
+ }
+# 5 - resource level in group
+ if (defined($$courseopt{$grplevelr})) {
+ $outpar[5]=$$courseopt{$grplevelr};
+ $result=5;
+ }
+ }
- if ($def) { $outpar[11]=$def; $result=11; }
+# ---------------------------------------------------------- sixth, check user
-# ----------------------------------------------------- second, check map parms
+ if ($uname ne '') {
+# 4 - General for specific student
+ if (defined($$useropt{$courselevel})) {
+ $outpar[4]=$$useropt{$courselevel};
+ $result=4;
+ }
- my $thisparm=$parmhash{$symbparm};
- if ($thisparm) { $outpar[10]=$thisparm; $result=10; }
+# 3 - Map or Folder level for specific student (recursive)
+ if (defined($$useropt{$courseleveli})) {
+ $outpar[3]=$$useropt{$courseleveli};
+ $result=3;
+ }
-# --------------------------------------------------------- third, check course
+# 2 - Map or Folder level for specific student (non-recursive)
+ if (defined($$useropt{$courselevelm})) {
+ $outpar[2]=$$useropt{$courselevelm};
+ $result=2;
+ }
- if ($courseopt{$courselevel}) {
- $outpar[9]=$courseopt{$courselevel};
- $result=9;
+# 1 - resource level for specific student
+ if (defined($$useropt{$courselevelr})) {
+ $outpar[1]=$$useropt{$courselevelr};
+ $result=1;
+ }
}
+ return ($result,@outpar);
+}
+
+
- if ($courseopt{$courselevelm}) {
- $outpar[8]=$courseopt{$courselevelm};
- $result=8;
+# --- Caches local to lonparmset
+
+
+# Reset lonparmset caches (called at the beginning and end of the handler).
+sub reset_caches {
+ &resetparmhash();
+ &resetsymbcache();
+ &resetrulescache();
+}
+
+# cache for map parameters, stored temporarily in $env{'request.course.fn'}_parms.db
+# (these parameters come from param elements in .sequence files created with the advanced RAT)
+{
+ my $parmhashid; # course identifier, to initialize the cache only once for a course
+ my %parmhash; # the parameter cache
+ # reset map parameter hash
+ sub resetparmhash {
+ undef($parmhashid);
+ undef(%parmhash);
}
- if ($courseopt{$courselevelr}) {
- $outpar[7]=$courseopt{$courselevelr};
- $result=7;
+ # dump the _parms.db database into %parmhash
+ 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'};
+ }
}
- if ($csec) {
- if ($courseopt{$seclevel}) {
- $outpar[6]=$courseopt{$seclevel};
- $result=6;
- }
- if ($courseopt{$seclevelm}) {
- $outpar[5]=$courseopt{$seclevelm};
- $result=5;
- }
+ # returns a parameter value for an identifier symb.parts.parameter, using the map parameter cache
+ sub parmhash {
+ my ($id) = @_;
+ &cacheparmhash();
+ return $parmhash{$id};
+ }
+}
- if ($courseopt{$seclevelr}) {
- $outpar[4]=$courseopt{$seclevelr};
- $result=4;
- }
+# cache big hash id -> symb, using lonnavmaps to find association
+{
+ my $symbsid; # course identifier, to initialize the cache only once for a course
+ my %symbs; # hash id->symb
+ # reset the id->symb cache
+ sub resetsymbcache {
+ undef($symbsid);
+ undef(%symbs);
}
-# ---------------------------------------------------------- fourth, check user
+ # returns the symb corresponding to a big hash id (using lonnavmaps and a cache)
+ 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};
+ }
+}
- if ($uname) {
- if ($useropt{$courselevel}) {
- $outpar[3]=$useropt{$courselevel};
- $result=3;
- }
-
- if ($useropt{$courselevelm}) {
- $outpar[2]=$useropt{$courselevelm};
- $result=2;
- }
-
- if ($useropt{$courselevelr}) {
- $outpar[1]=$useropt{$courselevelr};
- $result=1;
- }
+# cache for parameter default actions (stored in parmdefactions.db)
+{
+ my $rulesid; # course identifier, to initialize the cache only once for a course
+ my %rules; # parameter default actions hash
+ sub resetrulescache {
+ undef($rulesid);
+ undef(%rules);
}
- return ($result,@outpar);
+ # returns the value for a given key in the parameter default action hash
+ 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};
+ }
+}
+
+
+# Returns the values of the parameter type default action
+# "default value when manually setting".
+# If none is defined, ('','','','','') is returned.
+#
+# @param {string} $type - parameter type
+# @returns {Array} - (hours, min, sec, value)
+sub preset_defaults {
+ my $type=shift;
+ if (&rulescache($type.'_action') eq 'default') {
+ # yes, there is something
+ return (&rulescache($type.'_hours'),
+ &rulescache($type.'_min'),
+ &rulescache($type.'_sec'),
+ &rulescache($type.'_value'));
+ } else {
+ # nothing there or something else
+ return ('','','','','');
+ }
+}
+
+
+# Checks that a date is after enrollment start date and before
+# enrollment end date.
+# Returns HTML with a warning if it is not, or the empty string otherwise.
+# This is used by both overview and table modes.
+#
+# @param {integer} $checkdate - the date to check.
+# @returns {string} - HTML possibly containing a localized warning message.
+sub date_sanity_info {
+ my $checkdate=shift;
+ unless ($checkdate) { return ''; }
+ my $result='';
+ my $crsprefix='course.'.$env{'request.course.id'}.'.';
+ if ($env{$crsprefix.'default_enrollment_end_date'}) {
+ if ($checkdate>$env{$crsprefix.'default_enrollment_end_date'}) {
+ $result.='
'
+ .&mt('After course enrollment end!')
+ .'
';
+ }
+ }
+ if ($env{$crsprefix.'default_enrollment_start_date'}) {
+ if ($checkdate<$env{$crsprefix.'default_enrollment_start_date'}) {
+ $result.='
'
+ .&mt('Before course enrollment start!')
+ .'
';
+ }
+ }
+# Preparation for additional warnings about dates in the past/future.
+# An improved, more context sensitive version is recommended,
+# e.g. warn for due and answer dates which are defined before the corresponding open date, etc.
+# if ($checkdate
'
+ .''
+ );
+ } # end of $parmlev eq general
+ }
+ $r->print('');
+ &endSettingsScreen($r);
+ $r->print(&Apache::loncommon::end_page());
+} # end sub assessparms
+
+
+
+##################################################
+# OVERVIEW MODE
+##################################################
+my $tableopen;
+
+sub tablestart {
+ my ($readonly) = @_;
+ if ($tableopen) {
+ return '';
+ } else {
+ $tableopen=1;
+ my $output = &Apache::loncommon::start_data_table().'