--- loncom/interface/domainprefs.pm 2008/05/15 13:50:23 1.41.2.1 +++ loncom/interface/domainprefs.pm 2019/02/15 20:56:14 1.349 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Handler to set domain-wide configuration settings # -# $Id: domainprefs.pm,v 1.41.2.1 2008/05/15 13:50:23 raeburn Exp $ +# $Id: domainprefs.pm,v 1.349 2019/02/15 20:56:14 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -19,14 +19,141 @@ # # You should have received a copy of the GNU General Public License # along with LON-CAPA; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA# +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# # /home/httpd/html/adm/gpl.txt # # http://www.lon-capa.org/ # # ############################################################### -############################################################## +############################################################### + +=pod + +=head1 NAME + +Apache::domainprefs.pm + +=head1 SYNOPSIS + +Handles configuration of a LON-CAPA domain. + +This is part of the LearningOnline Network with CAPA project +described at http://www.lon-capa.org. + + +=head1 OVERVIEW + +Each institution using LON-CAPA will typically have a single domain designated +for use by individuals affiliated with the institution. Accordingly, each domain +may define a default set of logos and a color scheme which can be used to "brand" +the LON-CAPA instance. In addition, an institution will typically have a language +and timezone which are used for the majority of courses. + +LON-CAPA provides a mechanism to display and modify these defaults, as well as a +host of other domain-wide settings which determine the types of functionality +available to users and courses in the domain. + +There is also a mechanism to configure cataloging of courses in the domain, and +controls on the operation of automated processes which govern such things as +roster updates, user directory updates and processing of course requests. + +The domain coordination manual which is built dynamically on install/update of +LON-CAPA from the relevant help items provides more information about domain +configuration. + +Most of the domain settings are stored in the configuration.db GDBM file which is +housed on the primary library server for the domain in /home/httpd/lonUsers/$dom, +where $dom is the domain. The configuration.db stores settings in a number of +frozen hashes of hashes. In a few cases, domain information must be uploaded to +the domain as files (e.g., image files for logos etc., or plain text files for +bubblesheet formats). In this case the domainprefs.pm must be running in a user +session hosted on the primary library server in the domain, as these files are +stored in author space belonging to a special $dom-domainconfig user. + +domainprefs.pm in combination with lonconfigsettings.pm will retrieve and display +the current settings, and provides an interface to make modifications. + +=head1 SUBROUTINES + +=over + +=item print_quotas() + +Inputs: 4 + +$dom,$settings,$rowtotal,$action. + +$dom is the domain, $settings is a reference to a hash of current settings for +the current context, $rowtotal is a reference to the scalar used to record the +number of rows displayed on the page, and $action is the context (quotas, +requestcourses or requestauthor). + +The print_quotas routine was orginally created to display/store information +about default quota sizes for portfolio spaces for the different types of +institutional affiliation in the domain (e.g., Faculty, Staff, Student etc.), +but is now also used to manage availability of user tools: +i.e., blogs, aboutme page, and portfolios, and the course request tool, +used by course owners to request creation of a course, and to display/store +default quota sizes for Authoring Spaces. + +Outputs: 1 + +$datatable - HTML containing form elements which allow settings to be changed. + +In the case of course requests, radio buttons are displayed for each institutional +affiliate type (and also default, and _LC_adv) for each of the course types +(official, unofficial, community, textbook, placement, and lti). +In each case the radio buttons allow the selection of one of four values: + +0, approval, validate, autolimit=N (where N is blank, or a positive integer). +which have the following effects: + +0 + +=over + +- course requests are not allowed for this course types/affiliation + +=back + +approval + +=over + +- course requests must be approved by a Doman Coordinator in the +course's domain + +=back + +validate + +=over + +- an institutional validation (e.g., check requestor is instructor +of record) needs to be passed before the course will be created. The required +validation is in localenroll.pm on the primary library server for the course +domain. + +=back + +autolimit + +=over + +- course requests will be processed automatically up to a limit of +N requests for the course type for the particular requestor. +If N is undefined, there is no limit to the number of course requests +which a course owner may submit and have processed automatically. + +=back + +=item modify_quotas() + +=back + +=cut package Apache::domainprefs; @@ -36,9 +163,22 @@ use Apache::lonnet; use Apache::loncommon(); use Apache::lonhtmlcommon(); use Apache::lonlocal; -use LONCAPA(); +use Apache::lonmsg(); +use Apache::lonconfigsettings; +use Apache::lonuserutils(); +use Apache::loncoursequeueadmin(); +use LONCAPA qw(:DEFAULT :match); use LONCAPA::Enrollment; +use LONCAPA::lonauthcgi(); +use LONCAPA::SSL; use File::Copy; +use Locale::Language; +use DateTime::TimeZone; +use DateTime::Locale; +use Time::HiRes qw( sleep ); + +my $registered_cleanup; +my $modified_urls; sub handler { my $r=shift; @@ -48,6 +188,7 @@ sub handler { return OK; } + my $context = 'domain'; my $dom = $env{'request.role.domain'}; my $domdesc = &Apache::lonnet::domain($dom,'description'); if (&Apache::lonnet::allowed('mau',$dom)) { @@ -58,24 +199,70 @@ sub handler { "/adm/domainprefs:mau:0:0:Cannot modify domain settings"; return HTTP_NOT_ACCEPTABLE; } + + $registered_cleanup=0; + @{$modified_urls}=(); + &Apache::lonhtmlcommon::clear_breadcrumbs(); &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'}, - ['phase']); + ['phase','actions']); my $phase = 'pickactions'; if ( exists($env{'form.phase'}) ) { $phase = $env{'form.phase'}; } + my %servers = &Apache::lonnet::internet_dom_servers($dom); my %domconfig = &Apache::lonnet::get_dom('configuration',['login','rolecolors', - 'quotas','autoenroll','autoupdate','directorysrch', - 'usercreation','usermodification','contacts'],$dom); - my @prefs_order = ('rolecolors','login','quotas','autoenroll', - 'autoupdate','directorysrch','contacts', - 'usercreation','usermodification'); + 'quotas','autoenroll','autoupdate','autocreate', + 'directorysrch','usercreation','usermodification', + 'contacts','defaults','scantron','coursecategories', + 'serverstatuses','requestcourses','helpsettings', + 'coursedefaults','usersessions','loadbalancing', + 'requestauthor','selfenrollment','inststatus', + 'ltitools','ssl','trust','lti'],$dom); + my %encconfig = + &Apache::lonnet::get_dom('encconfig',['ltitools','lti'],$dom); + if (ref($domconfig{'ltitools'}) eq 'HASH') { + if (ref($encconfig{'ltitools'}) eq 'HASH') { + foreach my $id (keys(%{$domconfig{'ltitools'}})) { + if ((ref($domconfig{'ltitools'}{$id}) eq 'HASH') && + (ref($encconfig{'ltitools'}{$id}) eq 'HASH')) { + foreach my $item ('key','secret') { + $domconfig{'ltitools'}{$id}{$item} = $encconfig{'ltitools'}{$id}{$item}; + } + } + } + } + } + if (ref($domconfig{'lti'}) eq 'HASH') { + if (ref($encconfig{'lti'}) eq 'HASH') { + foreach my $id (keys(%{$domconfig{'lti'}})) { + if ((ref($domconfig{'lti'}{$id}) eq 'HASH') && + (ref($encconfig{'lti'}{$id}) eq 'HASH')) { + foreach my $item ('key','secret') { + $domconfig{'lti'}{$id}{$item} = $encconfig{'lti'}{$id}{$item}; + } + } + } + } + } + my @prefs_order = ('rolecolors','login','defaults','quotas','autoenroll', + 'autoupdate','autocreate','directorysrch','contacts', + 'usercreation','selfcreation','usermodification','scantron', + 'requestcourses','requestauthor','coursecategories', + 'serverstatuses','helpsettings','coursedefaults', + 'ltitools','selfenrollment','usersessions','ssl','trust','lti'); + my %existing; + if (ref($domconfig{'loadbalancing'}) eq 'HASH') { + %existing = %{$domconfig{'loadbalancing'}}; + } + if ((keys(%servers) > 1) || (keys(%existing) > 0)) { + push(@prefs_order,'loadbalancing'); + } my %prefs = ( 'rolecolors' => { text => 'Default color schemes', - help => 'Default_Color_Schemes', + help => 'Domain_Configuration_Color_Schemes', header => [{col1 => 'Student Settings', col2 => '',}, {col1 => 'Coordinator Settings', @@ -84,164 +271,396 @@ sub handler { col2 => '',}, {col1 => 'Administrator Settings', col2 => '',}], + print => \&print_rolecolors, + modify => \&modify_rolecolors, }, - 'login' => + 'login' => { text => 'Log-in page options', - help => 'Domain_Log-in_Page', - header => [{col1 => 'Item', - col2 => '',}], + help => 'Domain_Configuration_Login_Page', + header => [{col1 => 'Log-in Page Items', + col2 => '',}, + {col1 => 'Log-in Help', + col2 => 'Value'}, + {col1 => 'Custom HTML in document head', + col2 => 'Value'}], + print => \&print_login, + modify => \&modify_login, + }, + 'defaults' => + { text => 'Default authentication/language/timezone/portal/types', + help => 'Domain_Configuration_LangTZAuth', + header => [{col1 => 'Setting', + col2 => 'Value'}, + {col1 => 'Internal Authentication', + col2 => 'Value'}, + {col1 => 'Institutional user types', + col2 => 'Name displayed'}], + print => \&print_defaults, + modify => \&modify_defaults, }, 'quotas' => - { text => 'Default quotas for user portfolios', - help => 'Default_User_Quota', - header => [{col1 => 'User type', - col2 => 'Default quota'}], + { text => 'Blogs, personal web pages, webDAV/quotas, portfolios', + help => 'Domain_Configuration_Quotas', + header => [{col1 => 'User affiliation', + col2 => 'Available tools', + col3 => 'Quotas, MB; (Authoring requires role)',}], + print => \&print_quotas, + modify => \&modify_quotas, }, 'autoenroll' => { text => 'Auto-enrollment settings', - help => 'Domain_Auto_Enrollment', + help => 'Domain_Configuration_Auto_Enrollment', header => [{col1 => 'Configuration setting', col2 => 'Value(s)'}], + print => \&print_autoenroll, + modify => \&modify_autoenroll, }, 'autoupdate' => { text => 'Auto-update settings', - help => 'Domain_Auto_Update', + help => 'Domain_Configuration_Auto_Updates', header => [{col1 => 'Setting', col2 => 'Value',}, - {col1 => 'User Population', - col2 => 'Updataeable user data'}], + {col1 => 'Setting', + col2 => 'Affiliation'}, + {col1 => 'User population', + col2 => 'Updatable user data'}], + print => \&print_autoupdate, + modify => \&modify_autoupdate, + }, + 'autocreate' => + { text => 'Auto-course creation settings', + help => 'Domain_Configuration_Auto_Creation', + header => [{col1 => 'Configuration Setting', + col2 => 'Value',}], + print => \&print_autocreate, + modify => \&modify_autocreate, }, 'directorysrch' => - { text => 'Institutional directory searches', - help => 'Domain_Directory_Search', - header => [{col1 => 'Setting', + { text => 'Directory searches', + help => 'Domain_Configuration_InstDirectory_Search', + header => [{col1 => 'Institutional Directory Setting', + col2 => 'Value',}, + {col1 => 'LON-CAPA Directory Setting', col2 => 'Value',}], + print => \&print_directorysrch, + modify => \&modify_directorysrch, }, 'contacts' => - { text => 'Contact Information', - help => 'Domain_Contact_Information', - header => [{col1 => 'Setting', - col2 => 'Value',}], + { text => 'E-mail addresses and helpform', + help => 'Domain_Configuration_Contact_Info', + header => [{col1 => 'Default e-mail addresses', + col2 => 'Value',}, + {col1 => 'Recipient(s) for notifications', + col2 => 'Value',}, + {col1 => 'Nightly status check e-mail', + col2 => 'Settings',}, + {col1 => 'Ask helpdesk form settings', + col2 => 'Value',},], + print => \&print_contacts, + modify => \&modify_contacts, }, - 'usercreation' => { text => 'User creation', - help => 'Domain_User_Creation', - header => [{col1 => 'Format Rule Type', - col2 => 'Format Rules in force'}, + help => 'Domain_Configuration_User_Creation', + header => [{col1 => 'Format rule type', + col2 => 'Format rules in force'}, {col1 => 'User account creation', col2 => 'Usernames which may be created',}, {col1 => 'Context', - col2 => 'Assignable Authentication Types'}], + col2 => 'Assignable authentication types'}], + print => \&print_usercreation, + modify => \&modify_usercreation, + }, + 'selfcreation' => + { text => 'Users self-creating accounts', + help => 'Domain_Configuration_Self_Creation', + header => [{col1 => 'Self-creation with institutional username', + col2 => 'Enabled?'}, + {col1 => 'Institutional user type (login/SSO self-creation)', + col2 => 'Information user can enter'}, + {col1 => 'Self-creation with e-mail verification', + col2 => 'Settings'}], + print => \&print_selfcreation, + modify => \&modify_selfcreation, }, - 'usermodification' => + 'usermodification' => { text => 'User modification', - help => 'Domain_User_Modification', + help => 'Domain_Configuration_User_Modification', header => [{col1 => 'Target user has role', - col2 => 'User information updateable in author context'}, + col2 => 'User information updatable in author context'}, {col1 => 'Target user has role', - col2 => 'User information updateable in course context'}], + col2 => 'User information updatable in course context'}], + print => \&print_usermodification, + modify => \&modify_usermodification, + }, + 'scantron' => + { text => 'Bubblesheet format', + help => 'Domain_Configuration_Scantron_Format', + header => [ {col1 => 'Bubblesheet format file', + col2 => ''}, + {col1 => 'Bubblesheet data upload formats', + col2 => 'Settings'}], + print => \&print_scantron, + modify => \&modify_scantron, }, + 'requestcourses' => + {text => 'Request creation of courses', + help => 'Domain_Configuration_Request_Courses', + header => [{col1 => 'User affiliation', + col2 => 'Availability/Processing of requests',}, + {col1 => 'Setting', + col2 => 'Value'}, + {col1 => 'Available textbooks', + col2 => ''}, + {col1 => 'Available templates', + col2 => ''}, + {col1 => 'Validation (not official courses)', + col2 => 'Value'},], + print => \&print_quotas, + modify => \&modify_quotas, + }, + 'requestauthor' => + {text => 'Request Authoring Space', + help => 'Domain_Configuration_Request_Author', + header => [{col1 => 'User affiliation', + col2 => 'Availability/Processing of requests',}, + {col1 => 'Setting', + col2 => 'Value'}], + print => \&print_quotas, + modify => \&modify_quotas, + }, + 'coursecategories' => + { text => 'Cataloging of courses/communities', + help => 'Domain_Configuration_Cataloging_Courses', + header => [{col1 => 'Catalog type/availability', + col2 => '',}, + {col1 => 'Category settings for standard catalog', + col2 => '',}, + {col1 => 'Categories', + col2 => '', + }], + print => \&print_coursecategories, + modify => \&modify_coursecategories, + }, + 'serverstatuses' => + {text => 'Access to server status pages', + help => 'Domain_Configuration_Server_Status', + header => [{col1 => 'Status Page', + col2 => 'Other named users', + col3 => 'Specific IPs', + }], + print => \&print_serverstatuses, + modify => \&modify_serverstatuses, + }, + 'helpsettings' => + {text => 'Support settings', + help => 'Domain_Configuration_Help_Settings', + header => [{col1 => 'Help Page Settings (logged-in users)', + col2 => 'Value'}, + {col1 => 'Helpdesk Roles', + col2 => 'Settings'},], + print => \&print_helpsettings, + modify => \&modify_helpsettings, + }, + 'coursedefaults' => + {text => 'Course/Community defaults', + help => 'Domain_Configuration_Course_Defaults', + header => [{col1 => 'Defaults which can be overridden in each course by a CC', + col2 => 'Value',}, + {col1 => 'Defaults which can be overridden for each course by a DC', + col2 => 'Value',},], + print => \&print_coursedefaults, + modify => \&modify_coursedefaults, + }, + 'selfenrollment' => + {text => 'Self-enrollment in Course/Community', + help => 'Domain_Configuration_Selfenrollment', + header => [{col1 => 'Configuration Rights', + col2 => 'Configured by Course Personnel or Domain Coordinator?'}, + {col1 => 'Defaults', + col2 => 'Value'}, + {col1 => 'Self-enrollment validation (optional)', + col2 => 'Value'},], + print => \&print_selfenrollment, + modify => \&modify_selfenrollment, + }, + 'privacy' => + {text => 'User Privacy', + help => 'Domain_Configuration_User_Privacy', + header => [{col1 => 'Setting', + col2 => 'Value',}], + print => \&print_privacy, + modify => \&modify_privacy, + }, + 'usersessions' => + {text => 'User session hosting/offloading', + help => 'Domain_Configuration_User_Sessions', + header => [{col1 => 'Domain server', + col2 => 'Servers to offload sessions to when busy'}, + {col1 => 'Hosting of users from other domains', + col2 => 'Rules'}, + {col1 => "Hosting domain's own users elsewhere", + col2 => 'Rules'}], + print => \&print_usersessions, + modify => \&modify_usersessions, + }, + 'loadbalancing' => + {text => 'Dedicated Load Balancer(s)', + help => 'Domain_Configuration_Load_Balancing', + header => [{col1 => 'Balancers', + col2 => 'Default destinations', + col3 => 'User affiliation', + col4 => 'Overrides'}, + ], + print => \&print_loadbalancing, + modify => \&modify_loadbalancing, + }, + 'ltitools' => + {text => 'External Tools (LTI)', + help => 'Domain_Configuration_LTI_Tools', + header => [{col1 => 'Setting', + col2 => 'Value',}], + print => \&print_ltitools, + modify => \&modify_ltitools, + }, + 'ssl' => + {text => 'LON-CAPA Network (SSL)', + help => 'Domain_Configuration_Network_SSL', + header => [{col1 => 'Server', + col2 => 'Certificate Status'}, + {col1 => 'Connections to other servers', + col2 => 'Rules'}, + {col1 => 'Connections from other servers', + col2 => 'Rules'}, + {col1 => "Replicating domain's published content", + col2 => 'Rules'}], + print => \&print_ssl, + modify => \&modify_ssl, + }, + 'trust' => + {text => 'Trust Settings', + help => 'Domain_Configuration_Trust', + header => [{col1 => "Access to this domain's content by others", + col2 => 'Rules'}, + {col1 => "Access to other domain's content by this domain", + col2 => 'Rules'}, + {col1 => "Enrollment in this domain's courses by others", + col2 => 'Rules',}, + {col1 => "Co-author roles in this domain for others", + col2 => 'Rules',}, + {col1 => "Co-author roles for this domain's users elsewhere", + col2 => 'Rules',}, + {col1 => "Domain roles in this domain assignable to others", + col2 => 'Rules'}, + {col1 => "Course catalog for this domain displayed elsewhere", + col2 => 'Rules'}, + {col1 => "Requests for creation of courses in this domain by others", + col2 => 'Rules'}, + {col1 => "Users in other domains can send messages to this domain", + col2 => 'Rules'},], + print => \&print_trust, + modify => \&modify_trust, + }, + 'lti' => + {text => 'LTI Provider', + help => 'Domain_Configuration_LTI_Provider', + header => [{col1 => 'Setting', + col2 => 'Value',}], + print => \&print_lti, + modify => \&modify_lti, + }, ); + if (keys(%servers) > 1) { + $prefs{'login'} = { text => 'Log-in page options', + help => 'Domain_Configuration_Login_Page', + header => [{col1 => 'Log-in Service', + col2 => 'Server Setting',}, + {col1 => 'Log-in Page Items', + col2 => ''}, + {col1 => 'Log-in Help', + col2 => 'Value'}, + {col1 => 'Custom HTML in document head', + col2 => 'Value'}], + print => \&print_login, + modify => \&modify_login, + }; + } + my @roles = ('student','coordinator','author','admin'); my @actions = &Apache::loncommon::get_env_multiple('form.actions'); &Apache::lonhtmlcommon::add_breadcrumb ({href=>"javascript:changePage(document.$phase,'pickactions')", - text=>"Pick functionality"}); + text=>"Settings to display/modify"}); my $confname = $dom.'-domainconfig'; + if ($phase eq 'process') { - &Apache::lonhtmlcommon::add_breadcrumb - ({href=>"javascript:changePage(document.$phase,'display')", - text=>"Domain Configuration"}, - {href=>"javascript:changePage(document.$phase,'$phase')", - text=>"Updated"}); - &print_header($r,$phase); - foreach my $item (@prefs_order) { - if (grep(/^\Q$item\E$/,@actions)) { - $r->print('

'.&mt($prefs{$item}{'text'}).'

'. - &process_changes($r,$dom,$confname,$item, - \@roles,%domconfig)); - } - } - $r->print('

'); - &print_footer($r,$phase,'display','Back to configuration display', - \@actions); - $r->print('

'); + my $result = &Apache::lonconfigsettings::make_changes($r,$dom,$phase,$context,\@prefs_order, + \%prefs,\%domconfig,$confname,\@roles); + if ((ref($result) eq 'HASH') && (keys(%{$result}))) { + $r->rflush(); + &devalidate_remote_domconfs($dom,$result); + } } elsif ($phase eq 'display') { - &Apache::lonhtmlcommon::add_breadcrumb - ({href=>"javascript:changePage(document.$phase,'display')", - text=>"Domain Configuration"}); - &print_header($r,$phase); - if (@actions > 0) { - my $rowsum = 0; - my (%output,%rowtotal,@items); - my $halfway = @actions/2; - foreach my $item (@prefs_order) { - if (grep(/^\Q$item\E$/,@actions)) { - push(@items,$item); - ($output{$item},$rowtotal{$item}) = - &print_config_box($r,$dom,$confname,$phase, - $item,$prefs{$item}, - $domconfig{$item}); - $rowsum += $rowtotal{$item}; - } - } - my $colend; - my $halfway = $rowsum/2; - my $aggregate = 0; - my $sumleft = 0; - my $sumright = 0; - my $crossover; - for (my $i=0; $i<@items; $i++) { - $aggregate += $rowtotal{$items[$i]}; - if ($aggregate > $halfway) { - $crossover = $i; - last; - } - } - for (my $i=0; $i<$crossover; $i++) { - $sumleft += $rowtotal{$items[$i]}; - } - for (my $i=$crossover+1; $i<@items; $i++) { - $sumright += $rowtotal{$items[$i]}; - } - if ((@items > 1) && ($env{'form.numcols'} == 2)) { - my $sumdiff = $sumright - $sumleft; - if ($sumdiff > 0) { - $colend = $crossover + 1; - } else { - $colend = $crossover; - } - } else { - $colend = @items; - } - $r->print('

'); - for (my $i=0; $i<$colend; $i++) { - $r->print($output{$items[$i]}); - } - $r->print(''); - if ($colend < @items) { - for (my $i=$colend; $i<@items; $i++) { - $r->print($output{$items[$i]}); - } - } - $r->print('

'); - $r->print(&print_footer($r,$phase,'process','Save',\@actions)); - } else { - $r->print(''. - ''."\n". - ''.&mt('No settings chosen'). - ''); + my $js = &recaptcha_js(). + &toggle_display_js(); + if ((keys(%servers) > 1) || (keys(%existing) > 0)) { + my ($othertitle,$usertypes,$types) = + &Apache::loncommon::sorted_inst_types($dom); + $js .= &lonbalance_targets_js($dom,$types,\%servers, + $domconfig{'loadbalancing'}). + &new_spares_js(). + &common_domprefs_js(). + &Apache::loncommon::javascript_array_indexof(); + } + if (grep(/^requestcourses$/,@actions)) { + my $javascript_validations; + my $coursebrowserjs=&Apache::loncommon::coursebrowser_javascript($env{'request.role.domain'}); + $js .= < +$javascript_validations + +$coursebrowserjs +END + } + if (grep(/^selfcreation$/,@actions)) { + $js .= &selfcreate_javascript(); + } + if (grep(/^contacts$/,@actions)) { + $js .= &contacts_javascript(); + } + if (grep(/^scantron$/,@actions)) { + $js .= &scantron_javascript(); } - $r->print(''); - $r->print(&Apache::loncommon::end_page()); + &Apache::lonconfigsettings::display_settings($r,$dom,$phase,$context,\@prefs_order,\%prefs,\%domconfig,$confname,$js); } else { - if ($phase eq '') { - $phase = 'pickactions'; +# check if domconfig user exists for the domain. + my $servadm = $r->dir_config('lonAdmEMail'); + my ($configuserok,$author_ok,$switchserver) = + &config_check($dom,$confname,$servadm); + unless ($configuserok eq 'ok') { + &Apache::lonconfigsettings::print_header($r,$phase,$context); + $r->print(&mt('The domain configuration user "[_1]" has yet to be created.', + $confname). + '
' + ); + if ($switchserver) { + $r->print(&mt('Ordinarily, that domain configuration user is created when the ./UPDATE script is run to install LON-CAPA for the first time.'). + '
'. + &mt('However, that does not apply when new domains are added to a multi-domain server, and ./UPDATE has not been run recently.'). + '
'. + &mt('The "[_1]" user can be created automatically when a Domain Coordinator visits the web-based "Set domain configuration" screen, in a session hosted on the primary library server.',$confname). + '
'. + &mt('To do that now, use the following link: [_1]',$switchserver) + ); + } else { + $r->print(&mt('To create that user from the command line run the ./UPDATE script found in the top level directory of the extracted LON-CAPA tarball.'). + '
'. + &mt('Once that is done, you will be able to use the web-based "Set domain configuration" to configure the domain') + ); + } + $r->print(&Apache::loncommon::end_page()); + return OK; } - my %helphash; - &print_header($r,$phase); if (keys(%domconfig) == 0) { my $primarylibserv = &Apache::lonnet::domain($dom,'primary'); my @ids=&Apache::lonnet::current_machine_ids(); @@ -260,6 +679,7 @@ sub handler { } } if ($custom_img_count > 0) { + &Apache::lonconfigsettings::print_header($r,$phase,$context); my $switch_server = &check_switchserver($dom,$confname); $r->print( &mt('Domain configuration settings have yet to be saved for this domain via the web-based domain preferences interface.').'
'. @@ -269,70 +689,76 @@ sub handler { if ($switch_server) { $r->print($switch_server.' '.&mt('to primary library server for domain: [_1]',$dom)); } + $r->print(&Apache::loncommon::end_page()); return OK; } } } - $r->print('

'.&mt('Functionality to display/modify').'

'); - $r->print(''."\n".'

  '. - '

'); - my ($numitems,$midpoint,$seconddiv,$count); - $numitems = @prefs_order; - $midpoint = int($numitems/2); - if ($numitems%2) { - $midpoint ++; - } - $count = 0; - foreach my $item (@prefs_order) { - $r->print('

'); - $count ++; - if ((!$seconddiv) && ($count >= $midpoint)) { - $r->print('
'."\n".'
'."\n"); - $seconddiv = 1; - } - } - $r->print('

'. - &mt('Display options').'

'."\n". - '

'.&mt('Display using: ')."\n". - '  '. - ''. - &mt('two columns').'

'); - $r->print(&print_footer($r,$phase,'display','Go')); - $r->print(''); - $r->print(&Apache::loncommon::end_page()); + &Apache::lonconfigsettings::display_choices($r,$phase,$context,\@prefs_order,\%prefs); } return OK; } sub process_changes { - my ($r,$dom,$confname,$action,$roles,%domconfig) = @_; + my ($r,$dom,$confname,$action,$roles,$values,$lastactref) = @_; + my %domconfig; + if (ref($values) eq 'HASH') { + %domconfig = %{$values}; + } my $output; if ($action eq 'login') { - $output = &modify_login($r,$dom,$confname,%domconfig); + $output = &modify_login($r,$dom,$confname,$lastactref,%domconfig); } elsif ($action eq 'rolecolors') { $output = &modify_rolecolors($r,$dom,$confname,$roles, - %domconfig); + $lastactref,%domconfig); } elsif ($action eq 'quotas') { - $output = &modify_quotas($dom,%domconfig); + $output = &modify_quotas($r,$dom,$action,$lastactref,%domconfig); } elsif ($action eq 'autoenroll') { - $output = &modify_autoenroll($dom,%domconfig); + $output = &modify_autoenroll($dom,$lastactref,%domconfig); } elsif ($action eq 'autoupdate') { $output = &modify_autoupdate($dom,%domconfig); + } elsif ($action eq 'autocreate') { + $output = &modify_autocreate($dom,%domconfig); } elsif ($action eq 'directorysrch') { - $output = &modify_directorysrch($dom,%domconfig); + $output = &modify_directorysrch($dom,$lastactref,%domconfig); } elsif ($action eq 'usercreation') { $output = &modify_usercreation($dom,%domconfig); + } elsif ($action eq 'selfcreation') { + $output = &modify_selfcreation($dom,$lastactref,%domconfig); } elsif ($action eq 'usermodification') { $output = &modify_usermodification($dom,%domconfig); } elsif ($action eq 'contacts') { - $output = &modify_contacts($dom,%domconfig); + $output = &modify_contacts($dom,$lastactref,%domconfig); + } elsif ($action eq 'defaults') { + $output = &modify_defaults($dom,$lastactref,%domconfig); + } elsif ($action eq 'scantron') { + $output = &modify_scantron($r,$dom,$confname,$lastactref,%domconfig); + } elsif ($action eq 'coursecategories') { + $output = &modify_coursecategories($dom,$lastactref,%domconfig); + } elsif ($action eq 'serverstatuses') { + $output = &modify_serverstatuses($dom,%domconfig); + } elsif ($action eq 'requestcourses') { + $output = &modify_quotas($r,$dom,$action,$lastactref,%domconfig); + } elsif ($action eq 'requestauthor') { + $output = &modify_quotas($r,$dom,$action,$lastactref,%domconfig); + } elsif ($action eq 'helpsettings') { + $output = &modify_helpsettings($r,$dom,$confname,$lastactref,%domconfig); + } elsif ($action eq 'coursedefaults') { + $output = &modify_coursedefaults($dom,$lastactref,%domconfig); + } elsif ($action eq 'selfenrollment') { + $output = &modify_selfenrollment($dom,$lastactref,%domconfig) + } elsif ($action eq 'usersessions') { + $output = &modify_usersessions($dom,$lastactref,%domconfig); + } elsif ($action eq 'loadbalancing') { + $output = &modify_loadbalancing($dom,%domconfig); + } elsif ($action eq 'ltitools') { + $output = &modify_ltitools($r,$dom,$action,$lastactref,%domconfig); + } elsif ($action eq 'ssl') { + $output = &modify_ssl($dom,$lastactref,%domconfig); + } elsif ($action eq 'trust') { + $output = &modify_trust($dom,$lastactref,%domconfig); + } elsif ($action eq 'lti') { + $output = &modify_lti($r,$dom,$action,$lastactref,%domconfig); } return $output; } @@ -340,36 +766,84 @@ sub process_changes { sub print_config_box { my ($r,$dom,$confname,$phase,$action,$item,$settings) = @_; my $rowtotal = 0; - my $output = + my $output; + if ($action eq 'coursecategories') { + $output = &coursecategories_javascript($settings); + } elsif ($action eq 'defaults') { + $output = &defaults_javascript($settings); + } elsif ($action eq 'helpsettings') { + my (%privs,%levelscurrent); + my %full=(); + my %levels=( + course => {}, + domain => {}, + system => {}, + ); + my $context = 'domain'; + my $crstype = 'Course'; + my $formname = 'display'; + &Apache::lonuserutils::custom_role_privs(\%privs,\%full,\%levels,\%levelscurrent); + my @templateroles = &Apache::lonuserutils::custom_template_roles($context,$crstype); + $output = + &Apache::lonuserutils::custom_roledefs_js($context,$crstype,$formname,\%full, + \@templateroles); + } elsif ($action eq 'ltitools') { + $output .= <itools_javascript($settings); + } elsif ($action eq 'lti') { + $output .= <i_javascript($settings); + } + $output .= ' - '; -# -# FIXME - put the help link back in when the help files exist -# -# '); + '."\n". + ''; $rowtotal ++; - if (($action eq 'autoupdate') || ($action eq 'rolecolors') || - ($action eq 'usercreation') || ($action eq 'usermodification')) { - my $colspan = ($action eq 'rolecolors')?' colspan="2"':''; + my $numheaders = 1; + if (ref($item->{'header'}) eq 'ARRAY') { + $numheaders = scalar(@{$item->{'header'}}); + } + if ($numheaders > 1) { + my $colspan = ''; + my $rightcolspan = ''; + if (($action eq 'rolecolors') || ($action eq 'defaults') || + ($action eq 'directorysrch') || + (($action eq 'login') && ($numheaders < 4))) { + $colspan = ' colspan="2"'; + } + if ($action eq 'usersessions') { + $rightcolspan = ' colspan="3"'; + } $output .= ' + + + + + + + + + + + + + + + + + + + + + @@ -410,8 +1048,10 @@ sub print_config_box { '; + } elsif ($caller eq 'help') { + my ($defaulturl,$defaulttype,%url,%type,%lt,%langchoices); + my $switchserver = &check_switchserver($dom,$confname); + my $itemcount = 1; + $defaulturl = '/adm/loginproblems.html'; + $defaulttype = 'default'; + %lt = &Apache::lonlocal::texthash ( + del => 'Delete?', + rep => 'Replace:', + upl => 'Upload:', + default => 'Default', + custom => 'Custom', + ); + %langchoices = &Apache::lonlocal::texthash(&get_languages_hash()); + my @currlangs; + if (ref($settings) eq 'HASH') { + if (ref($settings->{'helpurl'}) eq 'HASH') { + foreach my $key (sort(keys(%{$settings->{'helpurl'}}))) { + next if ($settings->{'helpurl'}{$key} eq ''); + $url{$key} = $settings->{'helpurl'}{$key}.'?inhibitmenu=yes'; + $type{$key} = 'custom'; + unless ($key eq 'nolang') { + push(@currlangs,$key); + } + } + } elsif ($settings->{'helpurl'} ne '') { + $type{'nolang'} = 'custom'; + $url{'nolang'} = $settings->{'helpurl'}.'?inhibitmenu=yes'; + } + } + foreach my $lang ('nolang',sort(@currlangs)) { + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable .= ''; + if ($url{$lang} eq '') { + $url{$lang} = $defaulturl; + } + if ($type{$lang} eq '') { + $type{$lang} = $defaulttype; + } + $datatable .= ''."\n". + ''; + $itemcount ++; } - foreach my $item (@images) { - if ($designhash{$dom.'.login.'.$item} ne '') { - $designs{$item} = $designhash{$dom.'.login.'.$item}; - $is_custom{$item} = 1; + my @addlangs; + foreach my $lang (sort(keys(%langchoices))) { + next if ((grep(/^\Q$lang\E$/,@currlangs)) || ($lang eq 'x_chef')); + push(@addlangs,$lang); + } + if (@addlangs > 0) { + my %toadd; + map { $toadd{$_} = $langchoices{$_} ; } @addlangs; + $toadd{''} = &mt('Select'); + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable .= ''; + $itemcount ++; } - foreach my $item (@bgs) { - if ($designhash{$dom.'.login.'.$item} ne '') { - $designs{'bgs'}{$item} = $designhash{$dom.'.login.'.$item}; - $is_custom{$item} = 1; + $datatable .= &captcha_choice('login',$settings,$itemcount); + } elsif ($caller eq 'headtag') { + my %domservers = &Apache::lonnet::get_servers($dom); + my $choice = $choices{'headtag'}; + $css_class = ' class="LC_odd_row"'; + $datatable .= ''. + ''; } - my %alt_text = &Apache::lonlocal::texthash ( img => 'Log-in banner', - logo => 'Institution Logo', - domlogo => 'Domain Logo', - login => 'Login box'); - my $itemcount = 1; - my $css_class = $itemcount%2?' class="LC_odd_row"':''; - my $datatable = - ''. - ''; - $itemcount ++; - $css_class = $itemcount%2?' class="LC_odd_row"':''; - $datatable .= ''. - ''. - ''; - $itemcount ++; - $datatable .= &display_color_options($dom,$confname,$phase,'login',$itemcount,\%choices,\%is_custom,\%defaults,\%designs,\@images,\@bgs,\@links,\%alt_text,$rowtotal,\@logintext,$loginheader); - $datatable .= '
'.&mt($item->{text}). - ' 
'.&mt($item->{text}).' '. -# &Apache::loncommon::help_open_topic($item->{'help'}).'
'. + &mt($item->{text}).' '. + &Apache::loncommon::help_open_topic($item->{'help'}).'
- - + + '; - $rowtotal ++; - if ($action eq 'autoupdate') { - $output .= &print_autoupdate('top',$dom,$settings,\$rowtotal); - } elsif ($action eq 'usercreation') { - $output .= &print_usercreation('top',$dom,$settings,\$rowtotal); - } elsif ($action eq 'usermodification') { - $output .= &print_usermodification('top',$dom,$settings,\$rowtotal); - } else { + $rowtotal ++; + if (($action eq 'autoupdate') || ($action eq 'usercreation') || ($action eq 'selfcreation') || + ($action eq 'usermodification') || ($action eq 'defaults') || ($action eq 'coursedefaults') || + ($action eq 'selfenrollment') || ($action eq 'usersessions') || ($action eq 'ssl') || + ($action eq 'directorysrch') || ($action eq 'trust') || ($action eq 'helpsettings') || + ($action eq 'contacts')) { + $output .= $item->{'print'}->('top',$dom,$settings,\$rowtotal); + } elsif ($action eq 'coursecategories') { + $output .= $item->{'print'}->('top',$dom,$item,$settings,\$rowtotal); + } elsif ($action eq 'scantron') { + $output .= $item->{'print'}->($r,'top',$dom,$confname,$settings,\$rowtotal); + } elsif ($action eq 'login') { + if ($numheaders == 4) { + $colspan = ' colspan="2"'; + $output .= &print_login('service',$dom,$confname,$phase,$settings,\$rowtotal); + } else { + $output .= &print_login('page',$dom,$confname,$phase,$settings,\$rowtotal); + } + } elsif (($action eq 'requestcourses') || ($action eq 'requestauthor')) { + $output .= &print_quotas($dom,$settings,\$rowtotal,$action); + } elsif ($action eq 'rolecolors') { $output .= &print_rolecolors($phase,'student',$dom,$confname,$settings,\$rowtotal); } $output .= ' @@ -380,14 +854,52 @@ sub print_config_box { + '; + my @trusthdrs = qw(2 3 4 5 6 7); + my @prefixes = qw(enroll othcoau coaurem domroles catalog reqcrs); + for (my $i=0; $i<@trusthdrs; $i++) { + $output .= ' + + + '; + } + $output .= ' + + @@ -395,13 +907,139 @@ sub print_config_box { + + + + + '; + } else { + $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal); + } + } $rowtotal ++; - } elsif ($action eq 'usermodification') { - $output .= &print_usermodification('bottom',$dom,$settings,\$rowtotal); - } else { + } elsif (($action eq 'usermodification') || ($action eq 'coursedefaults') || + ($action eq 'defaults') || ($action eq 'directorysrch') || + ($action eq 'helpsettings')) { + $output .= $item->{'print'}->('bottom',$dom,$settings,\$rowtotal); + } elsif ($action eq 'scantron') { + $output .= $item->{'print'}->($r,'bottom',$dom,$confname,$settings,\$rowtotal); + } elsif ($action eq 'ssl') { + $output .= $item->{'print'}->('connto',$dom,$settings,\$rowtotal).' +
'.$item->{'header'}->[0]->{'col1'}.''.$item->{'header'}->[0]->{'col2'}.''.&mt($item->{'header'}->[0]->{'col1'}).''.&mt($item->{'header'}->[0]->{'col2'}).'
- - + + '; $rowtotal ++; - if ($action eq 'autoupdate') { - $output .= &print_autoupdate('bottom',$dom,$settings,\$rowtotal); - } elsif ($action eq 'usercreation') { - $output .= &print_usercreation('middle',$dom,$settings,\$rowtotal).' + if (($action eq 'autoupdate') || ($action eq 'usercreation') || + ($action eq 'selfcreation') || ($action eq 'selfenrollment') || + ($action eq 'usersessions') || ($action eq 'coursecategories') || + ($action eq 'trust') || ($action eq 'contacts') || ($action eq 'defaults')) { + if ($action eq 'coursecategories') { + $output .= &print_coursecategories('middle',$dom,$item,$settings,\$rowtotal); + $colspan = ' colspan="2"'; + } elsif ($action eq 'trust') { + $output .= $item->{'print'}->('shared',$dom,$settings,\$rowtotal); + } else { + $output .= $item->{'print'}->('middle',$dom,$settings,\$rowtotal); + } + if ($action eq 'trust') { + $output .= ' +
'.$item->{'header'}->[1]->{'col1'}.''.$item->{'header'}->[1]->{'col2'}.''.&mt($item->{'header'}->[1]->{'col1'}).''.&mt($item->{'header'}->[1]->{'col2'}).'
+
+ + + + '. + $item->{'print'}->($prefixes[$i],$dom,$settings,\$rowtotal).' +
'.&mt($item->{'header'}->[$trusthdrs[$i]]->{'col1'}).''.&mt($item->{'header'}->[$trusthdrs[$i]]->{'col2'}).'
+
+ + + + '. + $item->{'print'}->('bottom',$dom,$settings,\$rowtotal); + } else { + $output .= '
'.&mt($item->{'header'}->[8]->{'col1'}).''.&mt($item->{'header'}->[8]->{'col2'}).'
- - '. - &print_usercreation('bottom',$dom,$settings,\$rowtotal); + + + '."\n"; + if ($action eq 'coursecategories') { + $output .= &print_coursecategories('bottom',$dom,$item,$settings,\$rowtotal); + } elsif ($action eq 'contacts') { + $output .= $item->{'print'}->('lower',$dom,$settings,\$rowtotal).' + +
'.$item->{'header'}->[2]->{'col1'}.''.$item->{'header'}->[2]->{'col2'}.'
'.&mt($item->{'header'}->[2]->{'col1'}).''.&mt($item->{'header'}->[2]->{'col2'}).'
+
+ + + + '. + $item->{'print'}->('bottom',$dom,$settings,\$rowtotal).' +
'.&mt($item->{'header'}->[3]->{'col1'}).''.&mt($item->{'header'}->[3]->{'col2'}).'
+
+
+ + + + '. + $item->{'print'}->('connfrom',$dom,$settings,\$rowtotal).' +
'.&mt($item->{'header'}->[2]->{'col1'}).''.&mt($item->{'header'}->[2]->{'col2'}).'
+
+ + + + '. + $item->{'print'}->('bottom',$dom,$settings,\$rowtotal); + } elsif ($action eq 'login') { + if ($numheaders == 4) { + $output .= &print_login('page',$dom,$confname,$phase,$settings,\$rowtotal).' +
'.&mt($item->{'header'}->[3]->{'col1'}).''.&mt($item->{'header'}->[3]->{'col2'}).'
+
+ + + + '. + &print_login('help',$dom,$confname,$phase,$settings,\$rowtotal); + $rowtotal ++; + } else { + $output .= &print_login('help',$dom,$confname,$phase,$settings,\$rowtotal); + } + $output .= ' +
'.&mt($item->{'header'}->[2]->{'col1'}).''.&mt($item->{'header'}->[2]->{'col2'}).'
+
+ + '; + if ($numheaders == 4) { + $output .= ' + + + '; + } else { + $output .= ' + + + '; + } + $rowtotal ++; + $output .= &print_login('headtag',$dom,$confname,$phase,$settings,\$rowtotal); + } elsif ($action eq 'requestcourses') { + $output .= &print_requestmail($dom,$action,$settings,\$rowtotal); + $rowtotal ++; + $output .= &print_studentcode($settings,\$rowtotal).' +
'.&mt($item->{'header'}->[3]->{'col1'}).''.&mt($item->{'header'}->[3]->{'col2'}).'
'.&mt($item->{'header'}->[2]->{'col1'}).''.&mt($item->{'header'}->[2]->{'col2'}).'
+
+ + + + '. + &textbookcourses_javascript($settings). + &print_textbookcourses($dom,'textbooks',$settings,\$rowtotal).' +
'.&mt($item->{'header'}->[2]->{'col1'}).''.&mt($item->{'header'}->[2]->{'col2'}).'
+
+ + + + '. + &print_textbookcourses($dom,'templates',$settings,\$rowtotal).' +
'.&mt($item->{'header'}->[3]->{'col1'}).''.&mt($item->{'header'}->[3]->{'col2'}).'
+
+ + + + + '. + &print_validation_rows('requestcourses',$dom,$settings,\$rowtotal); + } elsif ($action eq 'requestauthor') { + $output .= &print_requestmail($dom,$action,$settings,\$rowtotal); + $rowtotal ++; + } elsif ($action eq 'rolecolors') { $output .= &print_rolecolors($phase,'coordinator',$dom,$confname,$settings,\$rowtotal).'
'.&mt($item->{'header'}->[4]->{'col1'}).''.&mt($item->{'header'}->[4]->{'col2'}).'
- - + + '. &print_rolecolors($phase,'author',$dom,$confname,$settings,\$rowtotal).'
'.$item->{'header'}->[2]->{'col1'}.''.$item->{'header'}->[2]->{'col2'}.''. + &mt($item->{'header'}->[2]->{'col1'}).''. + &mt($item->{'header'}->[2]->{'col2'}).'
@@ -421,8 +1061,8 @@ sub print_config_box {
- - + + '. &print_rolecolors($phase,'admin',$dom,$confname,$settings,\$rowtotal); $rowtotal += 2; @@ -433,27 +1073,54 @@ sub print_config_box { '; + return $datatable; + } elsif ($caller eq 'page') { + my %defaultchecked = ( + 'coursecatalog' => 'on', + 'helpdesk' => 'on', + 'adminmail' => 'off', + 'newuser' => 'off', + ); + my @toggles = ('coursecatalog','adminmail','helpdesk','newuser'); + my (%checkedon,%checkedoff); + foreach my $item (@toggles) { + if ($defaultchecked{$item} eq 'on') { + $checkedon{$item} = ' checked="checked" '; + $checkedoff{$item} = ' '; + } elsif ($defaultchecked{$item} eq 'off') { + $checkedoff{$item} = ' checked="checked" '; + $checkedon{$item} = ' '; + } + } + my @images = ('img','logo','domlogo','login'); + my @logintext = ('textcol','bgcol'); + my @bgs = ('pgbg','mainbg','sidebg'); + my @links = ('link','alink','vlink'); + my %designhash = &Apache::loncommon::get_domainconf($dom); + my %defaultdesign = %Apache::loncommon::defaultdesign; + my (%is_custom,%designs); + my %defaults = ( + font => $defaultdesign{'login.font'}, + ); foreach my $item (@images) { - if ($settings->{$item} ne '') { - $designs{$item} = $settings->{$item}; - $is_custom{$item} = 1; - } + $defaults{$item} = $defaultdesign{'login.'.$item}; + $defaults{'showlogo'}{$item} = 1; } - foreach my $item (@logintext) { - if ($settings->{$item} ne '') { - $designs{'logintext'}{$item} = $settings->{$item}; - $is_custom{$item} = 1; - } + foreach my $item (@bgs) { + $defaults{'bgs'}{$item} = $defaultdesign{'login.'.$item}; } - if ($settings->{'loginheader'} ne '') { - $loginheader = $settings->{'loginheader'}; + foreach my $item (@logintext) { + $defaults{'logintext'}{$item} = $defaultdesign{'login.'.$item}; } - if ($settings->{'font'} ne '') { - $designs{'font'} = $settings->{'font'}; - $is_custom{'font'} = 1; + foreach my $item (@links) { + $defaults{'links'}{$item} = $defaultdesign{'login.'.$item}; } - foreach my $item (@bgs) { - if ($settings->{$item} ne '') { - $designs{'bgs'}{$item} = $settings->{$item}; - $is_custom{$item} = 1; + if (ref($settings) eq 'HASH') { + foreach my $item (@toggles) { + if ($settings->{$item} eq '1') { + $checkedon{$item} = ' checked="checked" '; + $checkedoff{$item} = ' '; + } elsif ($settings->{$item} eq '0') { + $checkedoff{$item} = ' checked="checked" '; + $checkedon{$item} = ' '; + } } - } - foreach my $item (@links) { - if ($settings->{$item} ne '') { - $designs{'links'}{$item} = $settings->{$item}; - $is_custom{$item} = 1; + foreach my $item (@images) { + if (defined($settings->{$item})) { + $designs{$item} = $settings->{$item}; + $is_custom{$item} = 1; + } + if (defined($settings->{'showlogo'}{$item})) { + $designs{'showlogo'}{$item} = $settings->{'showlogo'}{$item}; + } + } + foreach my $item (@logintext) { + if ($settings->{$item} ne '') { + $designs{'logintext'}{$item} = $settings->{$item}; + $is_custom{$item} = 1; + } + } + if ($settings->{'font'} ne '') { + $designs{'font'} = $settings->{'font'}; + $is_custom{'font'} = 1; + } + foreach my $item (@bgs) { + if ($settings->{$item} ne '') { + $designs{'bgs'}{$item} = $settings->{$item}; + $is_custom{$item} = 1; + } + } + foreach my $item (@links) { + if ($settings->{$item} ne '') { + $designs{'links'}{$item} = $settings->{$item}; + $is_custom{$item} = 1; + } + } + } else { + if ($designhash{$dom.'.login.font'} ne '') { + $designs{'font'} = $designhash{$dom.'.login.font'}; + $is_custom{'font'} = 1; + } + foreach my $item (@images) { + if ($designhash{$dom.'.login.'.$item} ne '') { + $designs{$item} = $designhash{$dom.'.login.'.$item}; + $is_custom{$item} = 1; + } + } + foreach my $item (@bgs) { + if ($designhash{$dom.'.login.'.$item} ne '') { + $designs{'bgs'}{$item} = $designhash{$dom.'.login.'.$item}; + $is_custom{$item} = 1; + } + } + foreach my $item (@links) { + if ($designhash{$dom.'.login.'.$item} ne '') { + $designs{'links'}{$item} = $designhash{$dom.'.login.'.$item}; + $is_custom{$item} = 1; + } } } - } else { - if ($designhash{$dom.'.login.font'} ne '') { - $designs{'font'} = $designhash{$dom.'.login.font'}; - $is_custom{'font'} = 1; + my %alt_text = &Apache::lonlocal::texthash ( img => 'Log-in banner', + logo => 'Institution Logo', + domlogo => 'Domain Logo', + login => 'Login box'); + my $itemcount = 1; + foreach my $item (@toggles) { + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= + ''. + ''; + $itemcount ++; + } + $datatable .= &display_color_options($dom,$confname,$phase,'login',$itemcount,\%choices,\%is_custom,\%defaults,\%designs,\@images,\@bgs,\@links,\%alt_text,$rowtotal,\@logintext); + $datatable .= '
'.$item->{'header'}->[3]->{'col1'}.''.$item->{'header'}->[3]->{'col2'}.''.&mt($item->{'header'}->[3]->{'col1'}).''.&mt($item->{'header'}->[3]->{'col2'}).'
'; - if (($action eq 'login') || ($action eq 'directorysrch')) { + if ($action eq 'login') { $output .= ' - '; + '; + } elsif ($action eq 'serverstatuses') { + $output .= ' + '; + } else { $output .= ' - '; + '; } - $output .= ' - - '; - $rowtotal ++; - if ($action eq 'login') { - $output .= &print_login($dom,$confname,$phase,$settings,\$rowtotal); - } elsif ($action eq 'quotas') { - $output .= &print_quotas($dom,$settings,\$rowtotal); - } elsif ($action eq 'autoenroll') { - $output .= &print_autoenroll($dom,$settings,\$rowtotal); - } elsif ($action eq 'directorysrch') { - $output .= &print_directorysrch($dom,$settings,\$rowtotal); - } elsif ($action eq 'contacts') { - $output .= &print_contacts($dom,$settings,\$rowtotal); + if (defined($item->{'header'}->[0]->{'col3'})) { + $output .= ''; + if ($item->{'header'}->[0]->{'col3'}) { + if (defined($item->{'header'}->[0]->{'col4'})) { + $output .= ''; + } + if ($item->{'header'}->[0]->{'col4'}) { + $output .= ''; + $rowtotal ++; + if ($action eq 'quotas') { + $output .= &print_quotas($dom,$settings,\$rowtotal,$action); + } elsif (($action eq 'autoenroll') || ($action eq 'autocreate') || + ($action eq 'serverstatuses') || ($action eq 'loadbalancing') || + ($action eq 'ltitools') || ($action eq 'lti')) { + $output .= $item->{'print'}->($dom,$settings,\$rowtotal); } } $output .= ' @@ -464,249 +1131,379 @@ sub print_config_box { return ($output,$rowtotal); } -sub print_header { - my ($r,$phase) = @_; - my $alert = &mt('You must select at least one functionality type to display.'); - my $js = ' - -'; - my $additem; - if ($phase eq 'pickactions') { - my %loaditems = ( - 'onload' => "javascript:getViewportDims(document.$phase.width,document.$phase.height);setDisplayColumns();setFormElements(document.pickactions);", - ); - $additem = {'add_entries' => \%loaditems,}; - } else { - my %loaditems = ( - 'onload' => "javascript:getViewportDims(document.$phase.width,document.$phase.height);", - ); - $additem = {'add_entries' => \%loaditems,}; - } - $r->print(&Apache::loncommon::start_page('View/Modify Domain Settings', - $js,$additem)); - $r->print(&Apache::lonhtmlcommon::breadcrumbs('Domain Settings')); - $r->print(' - - - - - -'); - $r->print(''); - return; -} - -sub print_footer { - my ($r,$phase,$newphase,$button_text,$actions) = @_; - $button_text = &mt($button_text); - $r->print(''. - ''. - ''); - if (($phase eq 'display') || ($phase eq 'process')) { - if (ref($actions) eq 'ARRAY') { - foreach my $item (@{$actions}) { - $r->print('')."\n"; + $datatable .= ''. + ''; + my ($custom,$exempt); + if (ref($disallowed{$lonhost}) eq 'HASH') { + $custom = $disallowed{$lonhost}{'custompath'}; + $exempt = $disallowed{$lonhost}{'exempt'}; + } + $datatable .= ''. + ''. + ''; + } + $datatable .= '
'.$item->{'header'}->[0]->{'col1'}.''.&mt($item->{'header'}->[0]->{'col1'}).''.&mt($item->{'header'}->[0]->{'col1'}). + '
('.&mt('Automatic access for Dom. Coords.').')
'.$item->{'header'}->[0]->{'col1'}.''.&mt($item->{'header'}->[0]->{'col1'}).''.$item->{'header'}->[0]->{'col2'}.'
'. + &mt($item->{'header'}->[0]->{'col2'}); + if ($action eq 'serverstatuses') { + $output .= '
('.&mt('user1:domain1,user2:domain2 etc.').')'; + } + } else { + $output .= '
'. + &mt($item->{'header'}->[0]->{'col2'}); + } + $output .= ''. + &mt($item->{'header'}->[0]->{'col3'}); + } else { + $output .= ''. + &mt($item->{'header'}->[0]->{'col3'}); + } + if ($action eq 'serverstatuses') { + $output .= '
('.&mt('IP1,IP2 etc.').')'; + } + $output .= '
'. + &mt($item->{'header'}->[0]->{'col4'}); + } + $output .= '
'); - } - my $dest='"javascript:changePage(document.'.$phase.','."'$newphase'".')"'; - if ($phase eq 'process') { - $r->print('

'.$button_text.'

'); - } else { - my $onclick; - if ($phase eq 'display') { - $onclick = '"javascript:changePage(document.'.$phase.','."'$newphase'".')"'; - } else { - $onclick = '"javascript:changePage(document.'.$phase.','."'$newphase'".')"'; - } - $r->print('

'); - } - if ($phase eq 'process') { - $r->print(''.&Apache::loncommon::end_page()); - } - return; -} - -sub print_login { - my ($dom,$confname,$phase,$settings,$rowtotal) = @_; - my %choices = &login_choices(); - my ($catalogon,$catalogoff,$adminmailon,$adminmailoff,$loginheader); - $catalogon = ' checked="checked" '; - $adminmailoff = ' checked="checked" '; - $loginheader = 'image'; - my @images = ('img','logo','domlogo','login'); - my @logintext = ('textcol','bgcol'); - my @bgs = ('pgbg','mainbg','sidebg'); - my @links = ('link','alink','vlink'); - my %designhash = &Apache::loncommon::get_domainconf($dom); - my %defaultdesign = %Apache::loncommon::defaultdesign; - my (%is_custom,%designs); - my %defaults = ( - font => $defaultdesign{'login.font'}, - ); - foreach my $item (@images) { - $defaults{$item} = $defaultdesign{'login.'.$item}; - } - foreach my $item (@bgs) { - $defaults{'bgs'}{$item} = $defaultdesign{'login.'.$item}; - } - foreach my $item (@logintext) { - $defaults{'logintext'}{$item} = $defaultdesign{'login.'.$item}; - } - foreach my $item (@links) { - $defaults{'links'}{$item} = $defaultdesign{'login.'.$item}; - } - if (ref($settings) eq 'HASH') { - if ($settings->{'coursecatalog'} eq '0') { - $catalogoff = $catalogon; - $catalogon = ' '; - } - if ($settings->{'adminmail'} eq '1') { - $adminmailon = $adminmailoff; - $adminmailoff = ' '; - } + $datatable .= '
'.$choices{$item}. + ''. + ' 
'; + if ($lang eq 'nolang') { + $datatable .= &mt('Log-in help page if no specific language file: [_1]', + &Apache::loncommon::modal_link($url{$lang},$lt{$type{$lang}},600,500)); + } else { + $datatable .= &mt('Log-in help page for language: [_1] is [_2]', + $langchoices{$lang}, + &Apache::loncommon::modal_link($url{$lang},$lt{$type{$lang}},600,500)); + } + $datatable .= ''; + if ($type{$lang} eq 'custom') { + $datatable .= ' '.$lt{'rep'}.''; + } else { + $datatable .= $lt{'upl'}; + } + $datatable .='
'; + if ($switchserver) { + $datatable .= &mt('Upload to library server: [_1]',$switchserver); + } else { + $datatable .= ''; + } + $datatable .= '
'. + &mt('Add log-in help page for a specific language:').' '. + &Apache::loncommon::select_form('','loginhelpurl_add_lang',\%toadd). + ''.$lt{'upl'}.'
'; + if ($switchserver) { + $datatable .= &mt('Upload to library server: [_1]',$switchserver); + } else { + $datatable .= ''; } + $datatable .= '
'.$choice.''. + ''. + ''. + ''."\n"; + my (%currurls,%currexempt); + if (ref($settings) eq 'HASH') { + if (ref($settings->{'headtag'}) eq 'HASH') { + foreach my $lonhost (keys(%{$settings->{'headtag'}})) { + if (ref($settings->{'headtag'}{$lonhost}) eq 'HASH') { + $currurls{$lonhost} = $settings->{'headtag'}{$lonhost}{'url'}; + $currexempt{$lonhost} = $settings->{'headtag'}{$lonhost}{'exempt'}; + } + } } } - foreach my $item (@links) { - if ($designhash{$dom.'.login.'.$item} ne '') { - $designs{'links'}{$item} = $designhash{$dom.'.login.'.$item}; - $is_custom{$item} = 1; + my %lt = &Apache::lonlocal::texthash( + del => 'Delete?', + rep => 'Replace:', + upl => 'Upload:', + curr => 'View contents', + none => 'None', + ); + my $switchserver = &check_switchserver($dom,$confname); + foreach my $lonhost (sort(keys(%domservers))) { + my $exempt = &check_exempt_addresses($currexempt{$lonhost}); + $datatable .= ''; + if ($currurls{$lonhost}) { + $datatable .= ''. + ''; } + $datatable .= '
'.$choices{'hostid'}.''.$choices{'current'}.''.$choices{'action'}.''.$choices{'exempt'}.'
'.$domservers{$lonhost}.''.$lt{'curr'}.' '.$lt{'rep'}.''; + } else { + $datatable .= ''.$lt{'none'}.''.$lt{'upl'}; } + $datatable .='
'; + if ($switchserver) { + $datatable .= &mt('Upload to library server: [_1]',$switchserver); + } else { + $datatable .= ''; + } + $datatable .= '
'.$choices{'coursecatalog'}. - ''. - ' '. - '
'.$choices{'adminmail'}.''. - ' '. - '
'; return $datatable; } sub login_choices { my %choices = &Apache::lonlocal::texthash ( - coursecatalog => 'Display Course Catalog link?', - adminmail => "Display Administrator's E-mail Address?", - img => "Header", - logo => "Main Logo", - domlogo => "Domain Logo", - login => "Log-in Header", - textcol => "Text color", - bgcol => "Box color", - bgs => "Background colors", - links => "Link colors", - font => "Font color", - pgbg => "Page", - mainbg => "Main panel", - sidebg => "Side panel", - link => "Link", - alink => "Active link", - vlink => "Visited link", + coursecatalog => 'Display Course/Community Catalog link?', + adminmail => "Display Administrator's E-mail Address?", + helpdesk => 'Display "Contact Helpdesk" link', + disallowlogin => "Login page requests redirected", + hostid => "Server", + server => "Redirect to:", + serverpath => "Path", + custompath => "Custom", + exempt => "Exempt IP(s)", + directlogin => "No redirect", + newuser => "Link to create a user account", + img => "Header", + logo => "Main Logo", + domlogo => "Domain Logo", + login => "Log-in Header", + textcol => "Text color", + bgcol => "Box color", + bgs => "Background colors", + links => "Link colors", + font => "Font color", + pgbg => "Header", + mainbg => "Page", + sidebg => "Login box", + link => "Link", + alink => "Active link", + vlink => "Visited link", + headtag => "Custom markup", + action => "Action", + current => "Current", ); return %choices; } @@ -721,16 +1518,7 @@ sub print_rolecolors { my %designhash = &Apache::loncommon::get_domainconf($dom); my %defaultdesign = %Apache::loncommon::defaultdesign; my (%is_custom,%designs); - my %defaults = ( - img => $defaultdesign{$role.'.img'}, - font => $defaultdesign{$role.'.font'}, - ); - foreach my $item (@bgs) { - $defaults{'bgs'}{$item} = $defaultdesign{$role.'.'.$item}; - } - foreach my $item (@links) { - $defaults{'links'}{$item} = $defaultdesign{$role.'.'.$item}; - } + my %defaults = &role_defaults($role,\@bgs,\@links,\@images); if (ref($settings) eq 'HASH') { if (ref($settings->{$role}) eq 'HASH') { if ($settings->{$role}->{'img'} ne '') { @@ -741,6 +1529,10 @@ sub print_rolecolors { $designs{'font'} = $settings->{$role}->{'font'}; $is_custom{'font'} = 1; } + if ($settings->{$role}->{'fontmenu'} ne '') { + $designs{'fontmenu'} = $settings->{$role}->{'fontmenu'}; + $is_custom{'fontmenu'} = 1; + } foreach my $item (@bgs) { if ($settings->{$role}->{$item} ne '') { $designs{'bgs'}{$item} = $settings->{$role}->{$item}; @@ -759,6 +1551,10 @@ sub print_rolecolors { $designs{img} = $designhash{$dom.'.'.$role.'.img'}; $is_custom{'img'} = 1; } + if ($designhash{$dom.'.'.$role.'.fontmenu'} ne '') { + $designs{fontmenu} = $designhash{$dom.'.'.$role.'.fontmenu'}; + $is_custom{'fontmenu'} = 1; + } if ($designhash{$dom.'.'.$role.'.font'} ne '') { $designs{font} = $designhash{$dom.'.'.$role.'.font'}; $is_custom{'font'} = 1; @@ -783,43 +1579,103 @@ sub print_rolecolors { return $datatable; } +sub role_defaults { + my ($role,$bgs,$links,$images,$logintext) = @_; + my %defaults; + unless ((ref($bgs) eq 'ARRAY') && (ref($links) eq 'ARRAY') && (ref($images) eq 'ARRAY')) { + return %defaults; + } + my %defaultdesign = %Apache::loncommon::defaultdesign; + if ($role eq 'login') { + %defaults = ( + font => $defaultdesign{$role.'.font'}, + ); + if (ref($logintext) eq 'ARRAY') { + foreach my $item (@{$logintext}) { + $defaults{'logintext'}{$item} = $defaultdesign{$role.'.'.$item}; + } + } + foreach my $item (@{$images}) { + $defaults{'showlogo'}{$item} = 1; + } + } else { + %defaults = ( + img => $defaultdesign{$role.'.img'}, + font => $defaultdesign{$role.'.font'}, + fontmenu => $defaultdesign{$role.'.fontmenu'}, + ); + } + foreach my $item (@{$bgs}) { + $defaults{'bgs'}{$item} = $defaultdesign{$role.'.'.$item}; + } + foreach my $item (@{$links}) { + $defaults{'links'}{$item} = $defaultdesign{$role.'.'.$item}; + } + foreach my $item (@{$images}) { + $defaults{$item} = $defaultdesign{$role.'.'.$item}; + } + return %defaults; +} + sub display_color_options { my ($dom,$confname,$phase,$role,$itemcount,$choices,$is_custom,$defaults,$designs, - $images,$bgs,$links,$alt_text,$rowtotal,$logintext,$loginheader) = @_; + $images,$bgs,$links,$alt_text,$rowtotal,$logintext) = @_; + my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'}; my $css_class = $itemcount%2?' class="LC_odd_row"':''; my $datatable = ''. ''.$choices->{'font'}.''; if (!$is_custom->{'font'}) { - $datatable .= ''.&mt('Default in use:').' '.$defaults->{'font'}.''; + $datatable .= ''.&mt('Default in use:').' '.$defaults->{'font'}.''; } else { $datatable .= ' '; } - my $fontlink = &color_pick($phase,$role,'font',$choices->{'font'},$designs->{'font'}); + my $current_color = $designs->{'font'} ? $designs->{'font'} : $defaults->{'font'}; + $datatable .= ''. - ' '.$fontlink. - '    '. - ''; + ' '. + ' '; + unless ($role eq 'login') { + $datatable .= ''. + ''.$choices->{'fontmenu'}.''; + if (!$is_custom->{'fontmenu'}) { + $datatable .= ''.&mt('Default in use:').' '.$defaults->{'fontmenu'}.''; + } else { + $datatable .= ' '; + } + $current_color = $designs->{'fontmenu'} ? + $designs->{'fontmenu'} : $defaults->{'fontmenu'}; + $datatable .= ''. + ' '. + ' '; + } my $switchserver = &check_switchserver($dom,$confname); foreach my $img (@{$images}) { $itemcount ++; $css_class = $itemcount%2?' class="LC_odd_row"':''; $datatable .= ''. - ''.$choices->{$img}.''; + ''.$choices->{$img}; my ($imgfile,$img_import,$login_hdr_pick,$logincolors); + if ($role eq 'login') { + if ($img eq 'login') { + $login_hdr_pick = + &login_header_options($img,$role,$defaults,$is_custom,$choices); + $logincolors = + &login_text_colors($img,$role,$logintext,$phase,$choices, + $designs,$defaults); + } elsif ($img ne 'domlogo') { + $datatable.= &logo_display_options($img,$defaults,$designs); + } + } + $datatable .= ''; if ($designs->{$img} ne '') { $imgfile = $designs->{$img}; $img_import = ($imgfile =~ m{^/adm/}); } else { $imgfile = $defaults->{$img}; } - if ($img eq 'login') { - $login_hdr_pick = &login_header_options($img,$role,$defaults,$is_custom,$choices, - $loginheader); - $logincolors = - &login_text_colors($img,$role,$logintext,$phase,$choices,$designs); - } if ($imgfile) { my ($showfile,$fullsize); if ($imgfile =~ m-^(/res/\Q$dom\E/\Q$confname\E/\Q$img\E)/([^/]+)$-) { @@ -841,19 +1697,20 @@ sub display_color_options { $showfile = $imgfile; my $imgdir = $1; my $filename = $2; - if (-e "/home/httpd/html/$imgdir/tn-".$filename) { + if (-e "$londocroot/$imgdir/tn-".$filename) { $showfile = "/$imgdir/tn-".$filename; } else { - my $input = "/home/httpd/html".$imgfile; - my $output = '/home/httpd/html/'.$imgdir.'/tn-'.$filename; + my $input = $londocroot.$imgfile; + my $output = "$londocroot/$imgdir/tn-".$filename; if (!-e $output) { my ($width,$height) = &thumb_dimensions(); my ($fullwidth,$fullheight) = &check_dimensions($input); if ($fullwidth ne '' && $fullheight ne '') { if ($fullwidth > $width && $fullheight > $height) { my $size = $width.'x'.$height; - system("convert -sample $size $input $output"); - $showfile = '/'.$imgdir.'/tn-'.$filename; + my @args = ('convert','-sample',$size,$input,$output); + system({$args[0]} @args); + $showfile = "/$imgdir/tn-".$filename; } } } @@ -882,22 +1739,24 @@ sub display_color_options { } $datatable .= ''; if ($img eq 'login') { - $datatable .= $login_hdr_pick; - } + $datatable .= $login_hdr_pick; + } $datatable .= &image_changes($is_custom->{$img},$alt_text->{$img},$img_import, $showfile,$fullsize,$role,$img,$imgfile,$logincolors); } else { - $datatable .= '
'. - &mt('Upload:'); + $datatable .= ' '. + &mt('Upload:').'
'; } } else { - $datatable .= '
'. - &mt('Upload:'); + $datatable .= ' '. + &mt('Upload:').'
'; } if ($switchserver) { $datatable .= &mt('Upload to library server: [_1]',$switchserver); } else { - $datatable .=' '; + if ($img ne 'login') { # suppress file selection for Log-in header + $datatable .=' '; + } } $datatable .= ''; } @@ -908,7 +1767,7 @@ sub display_color_options { my $bgs_def; foreach my $item (@{$bgs}) { if (!$is_custom->{$item}) { - $bgs_def .= ''.$choices->{$item}.'    
'.$defaults->{'bgs'}{$item}.''; + $bgs_def .= ''.$choices->{$item}.'    
'.$defaults->{'bgs'}{$item}.''; } } if ($bgs_def) { @@ -918,13 +1777,14 @@ sub display_color_options { } $datatable .= ''. ''; + foreach my $item (@{$bgs}) { - my $link = &color_pick($phase,$role,$item,$choices->{$item},$designs->{'bgs'}{$item}); - $datatable .= ''; } $datatable .= '
'.$link; + $datatable .= ''.$choices->{$item}; + my $color = $designs->{'bgs'}{$item} ? $designs->{'bgs'}{$item} : $defaults->{'bgs'}{$item}; if ($designs->{'bgs'}{$item}) { - $datatable .= '    '; + $datatable .= ' '; } - $datatable .= '
'; @@ -935,7 +1795,7 @@ sub display_color_options { my $links_def; foreach my $item (@{$links}) { if (!$is_custom->{$item}) { - $links_def .= ''.$choices->{$item}.'
'.$defaults->{'links'}{$item}.''; + $links_def .= ''.$choices->{$item}.'
'.$defaults->{'links'}{$item}.''; } } if ($links_def) { @@ -946,34 +1806,49 @@ sub display_color_options { $datatable .= ''. ''; foreach my $item (@{$links}) { - $datatable .= ''; } $$rowtotal += $itemcount; return $datatable; } +sub logo_display_options { + my ($img,$defaults,$designs) = @_; + my $checkedon; + if (ref($defaults) eq 'HASH') { + if (ref($defaults->{'showlogo'}) eq 'HASH') { + if ($defaults->{'showlogo'}{$img}) { + $checkedon = 'checked="checked" '; + } + } + } + if (ref($designs) eq 'HASH') { + if (ref($designs->{'showlogo'}) eq 'HASH') { + if (defined($designs->{'showlogo'}{$img})) { + if ($designs->{'showlogo'}{$img} == 0) { + $checkedon = ''; + } elsif ($designs->{'showlogo'}{$img} == 1) { + $checkedon = 'checked="checked" '; + } + } + } + } + return '
'."\n"; +} + sub login_header_options { - my ($img,$role,$defaults,$is_custom,$choices,$loginheader) = @_; - my $image_checked = ' checked="checked" '; - my $text_checked = ' '; - if ($loginheader eq 'text') { - $image_checked = ' '; - $text_checked = ' checked="checked" '; - } - my $output = '   '. - '
'."\n"; + my ($img,$role,$defaults,$is_custom,$choices) = @_; + my $output = ''; if ((!$is_custom->{'textcol'}) || (!$is_custom->{'bgcol'})) { - $output .= &mt('Text default(s)').':
'; + $output .= &mt('Text default(s):').'
'; if (!$is_custom->{'textcol'}) { $output .= $choices->{'textcol'}.': '.$defaults->{'logintext'}{'textcol'}. '   '; @@ -990,17 +1865,13 @@ sub login_header_options { } sub login_text_colors { - my ($img,$role,$logintext,$phase,$choices,$designs) = @_; + my ($img,$role,$logintext,$phase,$choices,$designs,$defaults) = @_; my $color_menu = '
'."\n". - &color_pick($phase,$role,$item,$choices->{$item}, - $designs->{'links'}{$item}); + my $color = $designs->{'links'}{$item} ? $designs->{'links'}{$item} : $defaults->{'links'}{$item}; + $datatable .= ''.$choices->{$item}."\n"; if ($designs->{'links'}{$item}) { - $datatable.='    '; + $datatable.=' '; } - $datatable .= '
'; foreach my $item (@{$logintext}) { - my $link = &color_pick($phase,$role,$item,$choices->{$item},$designs->{'logintext'}{$item}); - $color_menu .= ''. - ''; + $color_menu .= ''; } $color_menu .= '
'.$link; - if ($designs->{'logintext'}{$item}) { - $color_menu .= '    '; - } - $color_menu .= '
 '.$choices->{$item}; + my $color = $designs->{'logintext'}{$item} ? $designs->{'logintext'}{$item} : $defaults->{'logintext'}{$item}; + $color_menu .= '

'; return $color_menu; @@ -1009,124 +1880,1141 @@ sub login_text_colors { sub image_changes { my ($is_custom,$alt_text,$img_import,$showfile,$fullsize,$role,$img,$imgfile,$logincolors) = @_; my $output; - if (!$is_custom) { - if ($img eq 'login') { - $output .= &mt('Default image:').'
'; + if ($img eq 'login') { + $output = ''.$logincolors; # suppress image for Log-in header + } elsif (!$is_custom) { + if ($img ne 'domlogo') { + $output = &mt('Default image:').'
'; } else { - $output .= &mt('Default in use:').'
'; + $output = &mt('Default in use:').'
'; } } - if ($img_import) { - $output .= ''; - } - $output .= ''.$alt_text.''; - if ($is_custom) { - $output .= ''.$logincolors.' '.&mt('Replace:').'
'; - } else { - $output .= ''.$logincolors.&mt('Upload:').'
'; - } - return $output; -} - -sub color_pick { - my ($phase,$role,$item,$desc,$curcol) = @_; - my $link = ''.$desc.''; - return $link; -} - -sub color_pick_js { - my $pjump_def = &Apache::lonhtmlcommon::pjump_javascript_definition(); - my $output = <<"ENDCOL"; - function pclose() { - parmwin=window.open("/adm/rat/empty.html","LONCAPAparms","height=350,width=350,scrollbars=no,menubar=no"); - parmwin.close(); - } - - $pjump_def - - function psub() { - pclose(); - if (document.parmform.pres_marker.value!='') { - if (document.parmform.pres_type.value!='') { - eval('document.display.'+ - document.parmform.pres_marker.value+ - '.value=document.parmform.pres_value.value;'); - } + if ($img ne 'login') { + if ($img_import) { + $output .= ''; + } + $output .= ''.$alt_text.''; + if ($is_custom) { + $output .= ''.$logincolors.' '.&mt('Replace:').'
'; } else { - document.parmform.pres_value.value=''; - document.parmform.pres_marker.value=''; + $output .= ''.$logincolors.&mt('Upload:').'
'; } } - - function get_id (span_id) { - if (document.getElementById) { - return document.getElementById(span_id); - } - if (document.all) { - return document.all[span_id]; - } - return false; - } - - function colchg_span (span_id_str,new_color_item) { - var span_ref = get_id(span_id_str); - if (span_ref.style) { span_ref = span_ref.style; } - span_ref.background = new_color_item.value; - span_ref.backgroundColor = new_color_item.value; - span_ref.bgColor = new_color_item.value; - } - -ENDCOL return $output; } sub print_quotas { - my ($dom,$settings,$rowtotal) = @_; - my $datatable; - my ($othertitle,$usertypes,$types) = &sorted_inst_types($dom); + my ($dom,$settings,$rowtotal,$action) = @_; + my $context; + if ($action eq 'quotas') { + $context = 'tools'; + } else { + $context = $action; + } + my ($datatable,$defaultquota,$authorquota,@usertools,@options,%validations); + my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); my $typecount = 0; - my $css_class; + my ($css_class,%titles); + if ($context eq 'requestcourses') { + @usertools = ('official','unofficial','community','textbook','placement','lti'); + @options =('norequest','approval','validate','autolimit'); + %validations = &Apache::lonnet::auto_courserequest_checks($dom); + %titles = &courserequest_titles(); + } elsif ($context eq 'requestauthor') { + @usertools = ('author'); + @options = ('norequest','approval','automatic'); + %titles = &authorrequest_titles(); + } else { + @usertools = ('aboutme','blog','webdav','portfolio'); + %titles = &tool_titles(); + } if (ref($types) eq 'ARRAY') { foreach my $type (@{$types}) { + my ($currdefquota,$currauthorquota); + unless (($context eq 'requestcourses') || + ($context eq 'requestauthor')) { + if (ref($settings) eq 'HASH') { + if (ref($settings->{defaultquota}) eq 'HASH') { + $currdefquota = $settings->{defaultquota}->{$type}; + } else { + $currdefquota = $settings->{$type}; + } + if (ref($settings->{authorquota}) eq 'HASH') { + $currauthorquota = $settings->{authorquota}->{$type}; + } + } + } if (defined($usertypes->{$type})) { $typecount ++; $css_class = $typecount%2?' class="LC_odd_row"':''; - $datatable .= ''. + $datatable .= ''. ''.$usertypes->{$type}.''. - ''. + ''; + if ($context eq 'requestcourses') { + $datatable .= ''; + } + my %cell; + foreach my $item (@usertools) { + if ($context eq 'requestcourses') { + my ($curroption,$currlimit); + if (ref($settings) eq 'HASH') { + if (ref($settings->{$item}) eq 'HASH') { + $curroption = $settings->{$item}->{$type}; + if ($curroption =~ /^autolimit=(\d*)$/) { + $currlimit = $1; + } + } + } + if (!$curroption) { + $curroption = 'norequest'; + } + $datatable .= ''; + foreach my $option (@options) { + my $val = $option; + if ($option eq 'norequest') { + $val = 0; + } + if ($option eq 'validate') { + my $canvalidate = 0; + if (ref($validations{$item}) eq 'HASH') { + if ($validations{$item}{$type}) { + $canvalidate = 1; + } + } + next if (!$canvalidate); + } + my $checked = ''; + if ($option eq $curroption) { + $checked = ' checked="checked"'; + } elsif ($option eq 'autolimit') { + if ($curroption =~ /^autolimit/) { + $checked = ' checked="checked"'; + } + } + $cell{$item} .= ''; + if ($option eq 'autolimit') { + $cell{$item} .= ' '; + } + $cell{$item} .= ' '; + if ($option eq 'autolimit') { + $cell{$item} .= $titles{'unlimited'}; + } + } + } elsif ($context eq 'requestauthor') { + my $curroption; + if (ref($settings) eq 'HASH') { + $curroption = $settings->{$type}; + } + if (!$curroption) { + $curroption = 'norequest'; + } + foreach my $option (@options) { + my $val = $option; + if ($option eq 'norequest') { + $val = 0; + } + my $checked = ''; + if ($option eq $curroption) { + $checked = ' checked="checked"'; + } + $datatable .= '  '; + } + } else { + my $checked = 'checked="checked" '; + if (ref($settings) eq 'HASH') { + if (ref($settings->{$item}) eq 'HASH') { + if ($settings->{$item}->{$type} == 0) { + $checked = ''; + } elsif ($settings->{$item}->{$type} == 1) { + $checked = 'checked="checked" '; + } + } + } + $datatable .= '  '; + } + } + if ($context eq 'requestcourses') { + $datatable .= ''; + foreach my $item (@usertools) { + $datatable .= ''; + } + $datatable .= '
'.$titles{$item}.'
'.$cell{$item}.'
'; + } + $datatable .= ''; + unless (($context eq 'requestcourses') || + ($context eq 'requestauthor')) { + $datatable .= + ''. + ''.&mt('Portfolio').': '. ' Mb'; + '" value="'.$currdefquota. + '" size="5" />
'.(' ' x 2). + ''.&mt('Authoring').': '. + ''; + } + $datatable .= ''; } } } - my $defaultquota = '20'; - if (ref($settings) eq 'HASH') { - if (defined($settings->{'default'})) { - $defaultquota = $settings->{'default'}; + unless (($context eq 'requestcourses') || ($context eq 'requestauthor')) { + $defaultquota = '20'; + $authorquota = '500'; + if (ref($settings) eq 'HASH') { + if (ref($settings->{'defaultquota'}) eq 'HASH') { + $defaultquota = $settings->{'defaultquota'}->{'default'}; + } elsif (defined($settings->{'default'})) { + $defaultquota = $settings->{'default'}; + } + if (ref($settings->{'authorquota'}) eq 'HASH') { + $authorquota = $settings->{'authorquota'}->{'default'}; + } } } $typecount ++; $css_class = $typecount%2?' class="LC_odd_row"':''; $datatable .= ''. ''.$othertitle.''. - ''. - ' Mb'; + ''; + if ($context eq 'requestcourses') { + $datatable .= ''; + } + my %defcell; + foreach my $item (@usertools) { + if ($context eq 'requestcourses') { + my ($curroption,$currlimit); + if (ref($settings) eq 'HASH') { + if (ref($settings->{$item}) eq 'HASH') { + $curroption = $settings->{$item}->{'default'}; + if ($curroption =~ /^autolimit=(\d*)$/) { + $currlimit = $1; + } + } + } + if (!$curroption) { + $curroption = 'norequest'; + } + $datatable .= ''; + foreach my $option (@options) { + my $val = $option; + if ($option eq 'norequest') { + $val = 0; + } + if ($option eq 'validate') { + my $canvalidate = 0; + if (ref($validations{$item}) eq 'HASH') { + if ($validations{$item}{'default'}) { + $canvalidate = 1; + } + } + next if (!$canvalidate); + } + my $checked = ''; + if ($option eq $curroption) { + $checked = ' checked="checked"'; + } elsif ($option eq 'autolimit') { + if ($curroption =~ /^autolimit/) { + $checked = ' checked="checked"'; + } + } + $defcell{$item} .= ''; + if ($option eq 'autolimit') { + $defcell{$item} .= ' '; + } + $defcell{$item} .= ' '; + if ($option eq 'autolimit') { + $defcell{$item} .= $titles{'unlimited'}; + } + } + } elsif ($context eq 'requestauthor') { + my $curroption; + if (ref($settings) eq 'HASH') { + $curroption = $settings->{'default'}; + } + if (!$curroption) { + $curroption = 'norequest'; + } + foreach my $option (@options) { + my $val = $option; + if ($option eq 'norequest') { + $val = 0; + } + my $checked = ''; + if ($option eq $curroption) { + $checked = ' checked="checked"'; + } + $datatable .= '  '; + } + } else { + my $checked = 'checked="checked" '; + if (ref($settings) eq 'HASH') { + if (ref($settings->{$item}) eq 'HASH') { + if ($settings->{$item}->{'default'} == 0) { + $checked = ''; + } elsif ($settings->{$item}->{'default'} == 1) { + $checked = 'checked="checked" '; + } + } + } + $datatable .= '  '; + } + } + if ($context eq 'requestcourses') { + $datatable .= ''; + foreach my $item (@usertools) { + $datatable .= ''; + } + $datatable .= '
'.$titles{$item}.'
'.$defcell{$item}.'
'; + } + $datatable .= ''; + unless (($context eq 'requestcourses') || ($context eq 'requestauthor')) { + $datatable .= ''. + ''.&mt('Portfolio').': '. + ''.(' ' x2). + ''.&mt('Authoring').': '. + ''; + } + $datatable .= ''; + $typecount ++; + $css_class = $typecount%2?' class="LC_odd_row"':''; + $datatable .= ''. + ''.&mt('LON-CAPA Advanced Users').'
'; + if ($context eq 'requestcourses') { + $datatable .= &mt('(overrides affiliation, if set)'). + ''. + ''. + ''; + } else { + $datatable .= &mt('(overrides affiliation, if checked)'). + ''. + ''; + my $checked = ''; + if ($curroption eq '') { + $checked = ' checked="checked"'; + } + $advcell{$item} .= '  '; + foreach my $option (@options) { + my $val = $option; + if ($option eq 'norequest') { + $val = 0; + } + if ($option eq 'validate') { + my $canvalidate = 0; + if (ref($validations{$item}) eq 'HASH') { + if ($validations{$item}{'_LC_adv'}) { + $canvalidate = 1; + } + } + next if (!$canvalidate); + } + my $checked = ''; + if ($val eq $curroption) { + $checked = ' checked="checked"'; + } elsif ($option eq 'autolimit') { + if ($curroption =~ /^autolimit/) { + $checked = ' checked="checked"'; + } + } + $advcell{$item} .= ''; + if ($option eq 'autolimit') { + $advcell{$item} .= ' '; + } + $advcell{$item} .= ' '; + if ($option eq 'autolimit') { + $advcell{$item} .= $titles{'unlimited'}; + } + } + } elsif ($context eq 'requestauthor') { + my $curroption; + if (ref($settings) eq 'HASH') { + $curroption = $settings->{'_LC_adv'}; + } + my $checked = ''; + if ($curroption eq '') { + $checked = ' checked="checked"'; + } + $datatable .= '  '; + foreach my $option (@options) { + my $val = $option; + if ($option eq 'norequest') { + $val = 0; + } + my $checked = ''; + if ($val eq $curroption) { + $checked = ' checked="checked"'; + } + $datatable .= '  '; + } + } else { + my $checked = 'checked="checked" '; + if (ref($settings) eq 'HASH') { + if (ref($settings->{$item}) eq 'HASH') { + if ($settings->{$item}->{'_LC_adv'} == 0) { + $checked = ''; + } elsif ($settings->{$item}->{'_LC_adv'} == 1) { + $checked = 'checked="checked" '; + } + } + } + $datatable .= '  '; + } + } + if ($context eq 'requestcourses') { + $datatable .= ''; + foreach my $item (@usertools) { + $datatable .= ''; + } + $datatable .= '
'. + '
'; + } + my %advcell; + foreach my $item (@usertools) { + if ($context eq 'requestcourses') { + my ($curroption,$currlimit); + if (ref($settings) eq 'HASH') { + if (ref($settings->{$item}) eq 'HASH') { + $curroption = $settings->{$item}->{'_LC_adv'}; + if ($curroption =~ /^autolimit=(\d*)$/) { + $currlimit = $1; + } + } + } + $datatable .= '
'.$titles{$item}.'
'.$advcell{$item}.'
'; + } + $datatable .= ''; $$rowtotal += $typecount; return $datatable; } +sub print_requestmail { + my ($dom,$action,$settings,$rowtotal,$customcss,$rowstyle) = @_; + my ($now,$datatable,%currapp); + $now = time; + if (ref($settings) eq 'HASH') { + if (ref($settings->{'notify'}) eq 'HASH') { + if ($settings->{'notify'}{'approval'} ne '') { + map {$currapp{$_}=1;} split(/,/,$settings->{'notify'}{'approval'}); + } + } + } + my $numinrow = 2; + my $css_class; + if ($$rowtotal%2) { + $css_class = 'LC_odd_row'; + } + if ($customcss) { + $css_class .= " $customcss"; + } + $css_class =~ s/^\s+//; + if ($css_class) { + $css_class = ' class="'.$css_class.'"'; + } + if ($rowstyle) { + $css_class .= ' style="'.$rowstyle.'"'; + } + my $text; + if ($action eq 'requestcourses') { + $text = &mt('Receive notification of course requests requiring approval'); + } elsif ($action eq 'requestauthor') { + $text = &mt('Receive notification of Authoring Space requests requiring approval'); + } else { + $text = &mt('Receive notification of queued requests for self-created user accounts requiring approval'); + } + $datatable = ''. + ' '.$text.''. + ' '; + my ($numdc,$table,$rows) = &active_dc_picker($dom,$numinrow,'checkbox', + $action.'notifyapproval',%currapp); + if ($numdc > 0) { + $datatable .= $table; + } else { + $datatable .= &mt('There are no active Domain Coordinators'); + } + $datatable .=''; + return $datatable; +} + +sub print_studentcode { + my ($settings,$rowtotal) = @_; + my $rownum = 0; + my ($output,%current); + my @crstypes = ('official','unofficial','community','textbook','placement','lti'); + if (ref($settings) eq 'HASH') { + if (ref($settings->{'uniquecode'}) eq 'HASH') { + foreach my $type (@crstypes) { + $current{$type} = $settings->{'uniquecode'}{$type}; + } + } + } + $output .= ''. + ''.&mt('Generate unique six character code as course identifier?').''. + ''; + foreach my $type (@crstypes) { + my $check = ' '; + if ($current{$type}) { + $check = ' checked="checked" '; + } + $output .= ''.(' 'x2).' '; + } + $output .= ''; + $$rowtotal ++; + return $output; +} + +sub print_textbookcourses { + my ($dom,$type,$settings,$rowtotal) = @_; + my $rownum = 0; + my $css_class; + my $itemcount = 1; + my $maxnum = 0; + my $bookshash; + if (ref($settings) eq 'HASH') { + $bookshash = $settings->{$type}; + } + my %ordered; + if (ref($bookshash) eq 'HASH') { + foreach my $item (keys(%{$bookshash})) { + if (ref($bookshash->{$item}) eq 'HASH') { + my $num = $bookshash->{$item}{'order'}; + $ordered{$num} = $item; + } + } + } + my $confname = $dom.'-domainconfig'; + my $switchserver = &check_switchserver($dom,$confname); + my $maxnum = scalar(keys(%ordered)); + my $datatable; + if (keys(%ordered)) { + my @items = sort { $a <=> $b } keys(%ordered); + for (my $i=0; $i<@items; $i++) { + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $key = $ordered{$items[$i]}; + my %coursehash=&Apache::lonnet::coursedescription($key); + my $coursetitle = $coursehash{'description'}; + my ($subject,$title,$author,$publisher,$image,$imgsrc,$cdom,$cnum); + if (ref($bookshash->{$key}) eq 'HASH') { + $subject = $bookshash->{$key}->{'subject'}; + $title = $bookshash->{$key}->{'title'}; + if ($type eq 'textbooks') { + $publisher = $bookshash->{$key}->{'publisher'}; + $author = $bookshash->{$key}->{'author'}; + $image = $bookshash->{$key}->{'image'}; + if ($image ne '') { + my ($path,$imagefile) = ($image =~ m{^(.+)/([^/]+)$}); + my $imagethumb = "$path/tn-".$imagefile; + $imgsrc = ''.&mt('Textbook image').''; + } + } + } + my $chgstr = ' onchange="javascript:reorderBooks(this.form,'."'$type".'_'."$key','$type'".');"'; + $datatable .= '' + .''.(' 'x2). + ''. + ''. + ''.&mt('Subject:').' '. + (' 'x2). + ''.&mt('Title:').' '; + if ($type eq 'textbooks') { + $datatable .= (' 'x2). + ''.&mt('Publisher:').' '. + (' 'x2). + ''.&mt('Author(s):').' '. + (' 'x2). + ''.&mt('Thumbnail:'); + if ($image) { + $datatable .= $imgsrc. + ' '. + ' '.&mt('Replace:').' '; + } + if ($switchserver) { + $datatable .= &mt('Upload to library server: [_1]',$switchserver); + } else { + $datatable .= ''; + } + } + $datatable .= ' '. + ''.&mt('LON-CAPA course:').' '. + $coursetitle.''."\n"; + $itemcount ++; + } + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $chgstr = ' onchange="javascript:reorderBooks(this.form,'."'$type"."_addbook_pos','$type'".');"'; + $datatable .= ''."\n". + ''."\n". + ' '."\n". + ''.&mt('Add').''."\n". + ''. + ''.&mt('Subject:').' '."\n". + (' 'x2). + ''.&mt('Title:').' '."\n". + (' 'x2); + if ($type eq 'textbooks') { + $datatable .= ''.&mt('Publisher:').' '."\n". + (' 'x2). + ''.&mt('Author(s):').' '."\n". + (' 'x2). + ''.&mt('Image:').' '; + if ($switchserver) { + $datatable .= &mt('Upload to library server: [_1]',$switchserver); + } else { + $datatable .= ''; + } + $datatable .= ''."\n"; + } + $datatable .= ''.&mt('LON-CAPA course:').' '. + &Apache::loncommon::select_dom_form($env{'request.role.domain'},$type.'_addbook_cdom'). + ''. + &Apache::loncommon::selectcourse_link + ('display',$type.'_addbook_cnum',$type.'_addbook_cdom',undef,undef,undef,'Course'). + ''."\n". + ''."\n"; + $itemcount ++; + return $datatable; +} + +sub textbookcourses_javascript { + my ($settings) = @_; + return unless(ref($settings) eq 'HASH'); + my (%ordered,%total,%jstext); + foreach my $type ('textbooks','templates') { + $total{$type} = 0; + if (ref($settings->{$type}) eq 'HASH') { + foreach my $item (keys(%{$settings->{$type}})) { + if (ref($settings->{$type}->{$item}) eq 'HASH') { + my $num = $settings->{$type}->{$item}{'order'}; + $ordered{$type}{$num} = $item; + } + } + $total{$type} = scalar(keys(%{$settings->{$type}})); + } + my @jsarray = (); + foreach my $item (sort {$a <=> $b } (keys(%{$ordered{$type}}))) { + push(@jsarray,$ordered{$type}{$item}); + } + $jstext{$type} = ' var '.$type.' = Array('."'".join("','",@jsarray)."'".');'."\n"; + } + return <<"ENDSCRIPT"; + + +ENDSCRIPT +} + +sub ltitools_javascript { + my ($settings) = @_; + my $togglejs = <itools_toggle_js(); + unless (ref($settings) eq 'HASH') { + return $togglejs; + } + my (%ordered,$total,%jstext); + $total = 0; + foreach my $item (keys(%{$settings})) { + if (ref($settings->{$item}) eq 'HASH') { + my $num = $settings->{$item}{'order'}; + $ordered{$num} = $item; + } + } + $total = scalar(keys(%{$settings})); + my @jsarray = (); + foreach my $item (sort {$a <=> $b } (keys(%ordered))) { + push(@jsarray,$ordered{$item}); + } + my $jstext = ' var ltitools = Array('."'".join("','",@jsarray)."'".');'."\n"; + return <<"ENDSCRIPT"; + + +$togglejs + +ENDSCRIPT +} + +sub ltitools_toggle_js { + return <<"ENDSCRIPT"; + + +ENDSCRIPT +} + +sub lti_javascript { + my ($settings) = @_; + my $togglejs = <i_toggle_js(); + unless (ref($settings) eq 'HASH') { + return $togglejs; + } + my (%ordered,$total,%jstext); + $total = 0; + foreach my $item (keys(%{$settings})) { + if (ref($settings->{$item}) eq 'HASH') { + my $num = $settings->{$item}{'order'}; + $ordered{$num} = $item; + } + } + $total = scalar(keys(%{$settings})); + my @jsarray = (); + foreach my $item (sort {$a <=> $b } (keys(%ordered))) { + push(@jsarray,$ordered{$item}); + } + my $jstext = ' var lti = Array('."'".join("','",@jsarray)."'".');'."\n"; + return <<"ENDSCRIPT"; + + +$togglejs + +ENDSCRIPT +} + +sub lti_toggle_js { + my %lcauthparmtext = &Apache::lonlocal::texthash ( + localauth => 'Local auth argument', + krb => 'Kerberos domain', + ); + return <<"ENDSCRIPT"; + + +ENDSCRIPT +} + sub print_autoenroll { my ($dom,$settings,$rowtotal) = @_; my $autorun = &Apache::lonnet::auto_run(undef,$dom), - my ($defdom,$runon,$runoff); + my ($defdom,$runon,$runoff,$coownerson,$coownersoff,$failsafe); if (ref($settings) eq 'HASH') { if (exists($settings->{'run'})) { if ($settings->{'run'} eq '0') { @@ -1145,9 +3033,24 @@ sub print_autoenroll { $runon = ' '; } } + if (exists($settings->{'co-owners'})) { + if ($settings->{'co-owners'} eq '0') { + $coownersoff = ' checked="checked" '; + $coownerson = ' '; + } else { + $coownerson = ' checked="checked" '; + $coownersoff = ' '; + } + } else { + $coownersoff = ' checked="checked" '; + $coownerson = ' '; + } if (exists($settings->{'sender_domain'})) { $defdom = $settings->{'sender_domain'}; } + if (exists($settings->{'autofailsafe'})) { + $failsafe = $settings->{'autofailsafe'}; + } } else { if ($autorun) { $runon = ' checked="checked" '; @@ -1175,8 +3078,20 @@ sub print_autoenroll { &mt('username').': '. '  '.&mt('domain'). - ': '.$domform.''; - $$rowtotal += 2; + ': '.$domform.''. + ''. + ''.&mt('Automatically assign co-ownership').''. + ' '. + ''. + ''. + ''.&mt('Failsafe for no drops when institutional data missing').''. + ''. + ''; + $$rowtotal += 4; return $datatable; } @@ -1218,9 +3133,17 @@ sub print_autoupdate { $classlistsoff.'value="0" />'.&mt('No').''. ''; $$rowtotal += 2; + } elsif ($position eq 'middle') { + my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); + my $numinrow = 3; + my $locknamesettings; + $datatable .= &insttypes_row($settings,$types,$usertypes, + $dom,$numinrow,$othertitle, + 'lockablenames',$rowtotal); + $$rowtotal ++; } else { - my ($othertitle,$usertypes,$types) = &sorted_inst_types($dom); - my @fields = ('lastname','firstname','middlename','gen', + my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); + my @fields = ('lastname','firstname','middlename','generation', 'permanentemail','id'); my %fieldtitles = &Apache::loncommon::personal_data_fieldtitles(); my $numrows = 0; @@ -1241,204 +3164,3631 @@ sub print_autoupdate { return $datatable; } -sub print_directorysrch { +sub print_autocreate { my ($dom,$settings,$rowtotal) = @_; - my $srchon = ' '; - my $srchoff = ' checked="checked" '; - my ($exacton,$containson,$beginson); - my $localon = ' '; - my $localoff = ' checked="checked" '; - if (ref($settings) eq 'HASH') { - if ($settings->{'available'} eq '1') { - $srchon = $srchoff; - $srchoff = ' '; - } - if ($settings->{'localonly'} eq '1') { - $localon = $localoff; - $localoff = ' '; - } - if (ref($settings->{'searchtypes'}) eq 'ARRAY') { - foreach my $type (@{$settings->{'searchtypes'}}) { - if ($type eq 'exact') { - $exacton = ' checked="checked" '; - } elsif ($type eq 'contains') { - $containson = ' checked="checked" '; - } elsif ($type eq 'begins') { - $beginson = ' checked="checked" '; + my (%createon,%createoff,%currhash); + my @types = ('xml','req'); + if (ref($settings) eq 'HASH') { + foreach my $item (@types) { + $createoff{$item} = ' checked="checked" '; + $createon{$item} = ' '; + if (exists($settings->{$item})) { + if ($settings->{$item}) { + $createon{$item} = ' checked="checked" '; + $createoff{$item} = ' '; } } - } else { - if ($settings->{'searchtypes'} eq 'exact') { - $exacton = ' checked="checked" '; - } elsif ($settings->{'searchtypes'} eq 'contains') { - $containson = ' checked="checked" '; - } elsif ($settings->{'searchtypes'} eq 'specify') { - $exacton = ' checked="checked" '; - $containson = ' checked="checked" '; - } + } + if ($settings->{'xmldc'} ne '') { + $currhash{$settings->{'xmldc'}} = 1; + } + } else { + foreach my $item (@types) { + $createoff{$item} = ' checked="checked" '; + $createon{$item} = ' '; } } - my ($searchtitles,$titleorder) = &sorted_searchtitles(); - my ($othertitle,$usertypes,$types) = &sorted_inst_types($dom); - - my $numinrow = 4; - my $cansrchrow = 0; + $$rowtotal += 2; + my $numinrow = 2; my $datatable=''. - ''.&mt('Directory search available?').''. + ''.&mt('Create pending official courses from XML files').''. ' '. - ''. - ''. - ''.&mt('Other domains can search?').''. + ''.&mt('Yes').' '. + ''. + ''. + ''.&mt('Create pending requests for official courses (if validated)').''. ' '. - ''. - ''; - $$rowtotal += 2; - if (ref($usertypes) eq 'HASH') { - if (keys(%{$usertypes}) > 0) { - $datatable .= &users_cansearch_row($settings,$types,$usertypes,$dom, - $numinrow,$othertitle); - $cansrchrow = 1; - } - } - if ($cansrchrow) { - $$rowtotal ++; - $datatable .= ''; + ''.&mt('Yes').' '. + ''; + my ($numdc,$dctable,$rows) = &active_dc_picker($dom,$numinrow,'radio', + 'autocreate_xmldc',%currhash); + $datatable .= ''; + if ($numdc > 1) { + $datatable .= &mt('Course creation processed as: (choose Dom. Coord.)'). + ''; } else { - $datatable .= ''; + $datatable .= &mt('Course creation processed as:'). + ''; } - $datatable .= ''.&mt('Supported search methods'). - ''; - foreach my $title (@{$titleorder}) { - if (defined($searchtitles->{$title})) { - my $check = ' '; - if (ref($settings) eq 'HASH') { - if (ref($settings->{'searchby'}) eq 'ARRAY') { - if (grep(/^\Q$title\E$/,@{$settings->{'searchby'}})) { - $check = ' checked="checked" '; + $datatable .= $dctable.''; + $$rowtotal += $rows; + return $datatable; +} + +sub print_directorysrch { + my ($position,$dom,$settings,$rowtotal) = @_; + my $datatable; + if ($position eq 'top') { + my $instsrchon = ' '; + my $instsrchoff = ' checked="checked" '; + my ($exacton,$containson,$beginson); + my $instlocalon = ' '; + my $instlocaloff = ' checked="checked" '; + if (ref($settings) eq 'HASH') { + if ($settings->{'available'} eq '1') { + $instsrchon = $instsrchoff; + $instsrchoff = ' '; + } + if ($settings->{'localonly'} eq '1') { + $instlocalon = $instlocaloff; + $instlocaloff = ' '; + } + if (ref($settings->{'searchtypes'}) eq 'ARRAY') { + foreach my $type (@{$settings->{'searchtypes'}}) { + if ($type eq 'exact') { + $exacton = ' checked="checked" '; + } elsif ($type eq 'contains') { + $containson = ' checked="checked" '; + } elsif ($type eq 'begins') { + $beginson = ' checked="checked" '; } } + } else { + if ($settings->{'searchtypes'} eq 'exact') { + $exacton = ' checked="checked" '; + } elsif ($settings->{'searchtypes'} eq 'contains') { + $containson = ' checked="checked" '; + } elsif ($settings->{'searchtypes'} eq 'specify') { + $exacton = ' checked="checked" '; + $containson = ' checked="checked" '; + } } - $datatable .= ''; } - } - $datatable .= '
'. - '
'; - $$rowtotal ++; - if ($cansrchrow) { - $datatable .= ''; + my ($searchtitles,$titleorder) = &sorted_searchtitles(); + my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); + + my $numinrow = 4; + my $cansrchrow = 0; + $datatable=''. + ''.&mt('Institutional directory search available?').''. + ' '. + ''. + ''. + ''.&mt('Other domains can search institution?').''. + ' '. + ''. + ''; + $$rowtotal += 2; + if (ref($usertypes) eq 'HASH') { + if (keys(%{$usertypes}) > 0) { + $datatable .= &insttypes_row($settings,$types,$usertypes,$dom, + $numinrow,$othertitle,'cansearch', + $rowtotal); + $cansrchrow = 1; + } + } + if ($cansrchrow) { + $$rowtotal ++; + $datatable .= ''; + } else { + $datatable .= ''; + } + $datatable .= ''.&mt('Supported search methods'). + ''; + foreach my $title (@{$titleorder}) { + if (defined($searchtitles->{$title})) { + my $check = ' '; + if (ref($settings) eq 'HASH') { + if (ref($settings->{'searchby'}) eq 'ARRAY') { + if (grep(/^\Q$title\E$/,@{$settings->{'searchby'}})) { + $check = ' checked="checked" '; + } + } + } + $datatable .= ''; + } + } + $datatable .= '
'. + '
'; + $$rowtotal ++; + if ($cansrchrow) { + $datatable .= ''; + } else { + $datatable .= ''; + } + $datatable .= ''.&mt('Search latitude').''. + ''. + ' '. + ' '. + ''; + $$rowtotal ++; } else { - $datatable .= ''; + my $domsrchon = ' checked="checked" '; + my $domsrchoff = ' '; + my $domlocalon = ' '; + my $domlocaloff = ' checked="checked" '; + if (ref($settings) eq 'HASH') { + if ($settings->{'lclocalonly'} eq '1') { + $domlocalon = $domlocaloff; + $domlocaloff = ' '; + } + if ($settings->{'lcavailable'} eq '0') { + $domsrchoff = $domsrchon; + $domsrchon = ' '; + } + } + $datatable=''. + ''.&mt('LON-CAPA directory search available?').''. + ' '. + ''. + ''. + ''.&mt('Other domains can search LON-CAPA domain?').''. + ' '. + ''. + ''; + $$rowtotal += 2; } - $datatable .= ''.&mt('Search latitude').''. - ''. - ' '. - ' '. - ''; - $$rowtotal ++; return $datatable; } sub print_contacts { - my ($dom,$settings,$rowtotal) = @_; + my ($position,$dom,$settings,$rowtotal) = @_; my $datatable; my @contacts = ('adminemail','supportemail'); - my (%checked,%to,%otheremails); - my @mailings = ('errormail','packagesmail','helpdeskmail'); - foreach my $type (@mailings) { - $otheremails{$type} = ''; - } - if (ref($settings) eq 'HASH') { - foreach my $item (@contacts) { - if (exists($settings->{$item})) { - $to{$item} = $settings->{$item}; + my (%checked,%to,%otheremails,%bccemails,%includestr,%includeloc,%currfield, + $maxsize,$fields,$fieldtitles,$fieldoptions,$possoptions,@mailings,%lonstatus); + if ($position eq 'top') { + if (ref($settings) eq 'HASH') { + foreach my $item (@contacts) { + if (exists($settings->{$item})) { + $to{$item} = $settings->{$item}; + } } } + } elsif ($position eq 'middle') { + @mailings = ('errormail','packagesmail','lonstatusmail','requestsmail', + 'updatesmail','idconflictsmail'); foreach my $type (@mailings) { - if (exists($settings->{$type})) { - if (ref($settings->{$type}) eq 'HASH') { - foreach my $item (@contacts) { - if ($settings->{$type}{$item}) { - $checked{$type}{$item} = ' checked="checked" '; + $otheremails{$type} = ''; + } + } elsif ($position eq 'lower') { + if (ref($settings) eq 'HASH') { + if (ref($settings->{'lonstatus'}) eq 'HASH') { + %lonstatus = %{$settings->{'lonstatus'}}; + } + } + } else { + @mailings = ('helpdeskmail','otherdomsmail'); + foreach my $type (@mailings) { + $otheremails{$type} = ''; + } + $bccemails{'helpdeskmail'} = ''; + $bccemails{'otherdomsmail'} = ''; + $includestr{'helpdeskmail'} = ''; + $includestr{'otherdomsmail'} = ''; + ($fields,$fieldtitles,$fieldoptions,$possoptions) = &helpform_fields(); + } + if (ref($settings) eq 'HASH') { + unless (($position eq 'top') || ($position eq 'lower')) { + foreach my $type (@mailings) { + if (exists($settings->{$type})) { + if (ref($settings->{$type}) eq 'HASH') { + foreach my $item (@contacts) { + if ($settings->{$type}{$item}) { + $checked{$type}{$item} = ' checked="checked" '; + } + } + $otheremails{$type} = $settings->{$type}{'others'}; + if (($type eq 'helpdeskmail') || ($type eq 'otherdomsmail')) { + $bccemails{$type} = $settings->{$type}{'bcc'}; + if ($settings->{$type}{'include'} ne '') { + ($includeloc{$type},$includestr{$type}) = split(/:/,$settings->{$type}{'include'},2); + $includestr{$type} = &unescape($includestr{$type}); + } } } - $otheremails{$type} = $settings->{$type}{'others'}; + } elsif ($type eq 'lonstatusmail') { + $checked{'lonstatusmail'}{'adminemail'} = ' checked="checked" '; + } + } + } + if ($position eq 'bottom') { + foreach my $type (@mailings) { + $bccemails{$type} = $settings->{$type}{'bcc'}; + if ($settings->{$type}{'include'} ne '') { + ($includeloc{$type},$includestr{$type}) = split(/:/,$settings->{$type}{'include'},2); + $includestr{$type} = &unescape($includestr{$type}); + } + } + if (ref($settings->{'helpform'}) eq 'HASH') { + if (ref($fields) eq 'ARRAY') { + foreach my $field (@{$fields}) { + $currfield{$field} = $settings->{'helpform'}{$field}; + } + } + if (exists($settings->{'helpform'}{'maxsize'})) { + $maxsize = $settings->{'helpform'}{'maxsize'}; + } else { + $maxsize = '1.0'; + } + } else { + if (ref($fields) eq 'ARRAY') { + foreach my $field (@{$fields}) { + $currfield{$field} = 'yes'; + } } + $maxsize = '1.0'; } } } else { - $to{'supportemail'} = $Apache::lonnet::perlvar{'lonSupportEMail'}; - $to{'adminemail'} = $Apache::lonnet::perlvar{'lonAdmEMail'}; - $checked{'errormail'}{'adminemail'} = ' checked="checked" '; - $checked{'packagesmail'}{'adminemail'} = ' checked="checked" '; - $checked{'helpdeskmail'}{'supportemail'} = ' checked="checked" '; + if ($position eq 'top') { + $to{'supportemail'} = $Apache::lonnet::perlvar{'lonSupportEMail'}; + $to{'adminemail'} = $Apache::lonnet::perlvar{'lonAdmEMail'}; + $checked{'errormail'}{'adminemail'} = ' checked="checked" '; + $checked{'packagesmail'}{'adminemail'} = ' checked="checked" '; + $checked{'lonstatusmail'}{'adminemail'} = ' checked="checked" '; + $checked{'requestsmail'}{'adminemail'} = ' checked="checked" '; + $checked{'updatesmail'}{'adminemail'} = ' checked="checked" '; + $checked{'idconflictsmail'}{'adminemail'} = ' checked="checked" '; + } elsif ($position eq 'bottom') { + $checked{'helpdeskmail'}{'supportemail'} = ' checked="checked" '; + $checked{'otherdomsmail'}{'supportemail'} = ' checked="checked" '; + if (ref($fields) eq 'ARRAY') { + foreach my $field (@{$fields}) { + $currfield{$field} = 'yes'; + } + } + $maxsize = '1.0'; + } } my ($titles,$short_titles) = &contact_titles(); my $rownum = 0; my $css_class; - foreach my $item (@contacts) { - if ($rownum%2) { - $css_class = ''; - } else { - $css_class = ' class="LC_odd_row" '; + if ($position eq 'top') { + foreach my $item (@contacts) { + $css_class = $rownum%2?' class="LC_odd_row"':''; + $datatable .= ''. + ''.$titles->{$item}. + ''. + ''; + $rownum ++; + } + } elsif ($position eq 'bottom') { + $css_class = $rownum%2?' class="LC_odd_row"':''; + $datatable .= ''. + ''.&mt('Extra helpdesk form fields:').'
'. + &mt('(e-mail, subject, and description always shown)'). + ''; + if ((ref($fields) eq 'ARRAY') && (ref($fieldtitles) eq 'HASH') && + (ref($fieldoptions) eq 'HASH') && (ref($possoptions) eq 'HASH')) { + $datatable .= ''; + foreach my $field (@{$fields}) { + $datatable .= ''. + ''; + } + $datatable .= '
'.&mt('Field').''.&mt('Status').'
'.$fieldtitles->{$field}; + if (($field eq 'screenshot') || ($field eq 'cc')) { + $datatable .= ' '.&mt('(logged-in users)'); + } + $datatable .=''; + my $clickaction; + if ($field eq 'screenshot') { + $clickaction = ' onclick="screenshotSize(this);"'; + } + if (ref($possoptions->{$field}) eq 'ARRAY') { + foreach my $option (@{$possoptions->{$field}}) { + my $checked; + if ($currfield{$field} eq $option) { + $checked = ' checked="checked"'; + } + $datatable .= ''.(' 'x2); + } + } + if ($field eq 'screenshot') { + my $display; + if ($currfield{$field} eq 'no') { + $display = ' style="display:none"'; + } + $datatable .= '
'.&mt('Maximum size for upload (MB)').''. + ''; + } + $datatable .= '
'; } - $datatable .= ''. - ''.$titles->{$item}. - ''. - ''; + $datatable .= ''."\n"; $rownum ++; } - foreach my $type (@mailings) { - if ($rownum%2) { - $css_class = ''; + unless (($position eq 'top') || ($position eq 'lower')) { + foreach my $type (@mailings) { + $css_class = $rownum%2?' class="LC_odd_row"':''; + $datatable .= ''. + ''. + $titles->{$type}.': '. + ''; + if (($type eq 'helpdeskmail') || ($type eq 'otherdomsmail')) { + $datatable .= '
'.&mt('E-mail recipient(s)').''; + } + $datatable .= ''; + foreach my $item (@contacts) { + $datatable .= ' '; + } + $datatable .= '
'.&mt('Others').':  '. + ''; + my %locchecked; + if (($type eq 'helpdeskmail') || ($type eq 'otherdomsmail')) { + foreach my $loc ('s','b') { + if ($includeloc{$type} eq $loc) { + $locchecked{$loc} = ' checked="checked"'; + last; + } + } + $datatable .= '
'.&mt('Bcc:').(' 'x6). + '
'. + '
'.&mt('Optional added text').''. + &mt('Text automatically added to e-mail:').' '. + '
'. + ''.&mt('Location:').' '. + ''. + (' 'x2). + ''. + '
'; + } + $datatable .= ''."\n"; + $rownum ++; + } + } + if ($position eq 'middle') { + my %choices; + my $corelink = &core_link_msu(); + $choices{'reporterrors'} = &mt('E-mail error reports to [_1]',$corelink); + $choices{'reportupdates'} = &mt('E-mail record of completed LON-CAPA updates to [_1]', + $corelink); + $choices{'reportstatus'} = &mt('E-mail status if errors above threshold to [_1]',$corelink); + my @toggles = ('reporterrors','reportupdates','reportstatus'); + my %defaultchecked = ('reporterrors' => 'on', + 'reportupdates' => 'on', + 'reportstatus' => 'on'); + (my $reports,$rownum) = &radiobutton_prefs($settings,\@toggles,\%defaultchecked, + \%choices,$rownum); + $datatable .= $reports; + } elsif ($position eq 'lower') { + $css_class = $rownum%2?' class="LC_odd_row"':''; + my ($threshold,$sysmail,%excluded,%weights); + my ($defaults,$names) = &Apache::loncommon::lon_status_items(); + if ($lonstatus{'threshold'} =~ /^\d+$/) { + $threshold = $lonstatus{'threshold'}; } else { - $css_class = ' class="LC_odd_row" '; + $threshold = $defaults->{'threshold'}; + } + if ($lonstatus{'sysmail'} =~ /^\d+$/) { + $sysmail = $lonstatus{'sysmail'}; + } else { + $sysmail = $defaults->{'sysmail'}; + } + if (ref($lonstatus{'weights'}) eq 'HASH') { + foreach my $type ('E','W','N','U') { + if ($lonstatus{'weights'}{$type} =~ /^\d+$/) { + $weights{$type} = $lonstatus{'weights'}{$type}; + } else { + $weights{$type} = $defaults->{$type}; + } + } + } else { + foreach my $type ('E','W','N','U') { + $weights{$type} = $defaults->{$type}; + } + } + if (ref($lonstatus{'excluded'}) eq 'ARRAY') { + if (@{$lonstatus{'excluded'}} > 0) { + map {$excluded{$_} = 1; } @{$lonstatus{'excluded'}}; + } } $datatable .= ''. - ''. - $titles->{$type}.': '. + ''. + $titles->{'errorthreshold'}. + ''. + ''; + $rownum ++; + $css_class = $rownum%2?' class="LC_odd_row"':''; + $datatable .= ''. ''. - ''; - foreach my $item (@contacts) { - $datatable .= ' '; - } - $datatable .= '
'.&mt('Others').':  '. - ''. - ''."\n"; + ''.$titles->{'errorweights'}. + ''; + foreach my $type ('E','W','N','U') { + $datatable .= ''; + } + $datatable .= '
'.$names->{$type}.'
'. + '
'; + $rownum ++; + $css_class = $rownum%2?' class="LC_odd_row"':''; + $datatable .= ''. + $titles->{'errorexcluded'}.''. + ''; + my $numinrow = 4; + my @ids = sort(values(%Apache::lonnet::serverhomeIDs)); + for (my $i=0; $i<@ids; $i++) { + my $rem = $i%($numinrow); + if ($rem == 0) { + if ($i > 0) { + $datatable .= ''; + } + $datatable .= ''; + } + my $check; + if ($excluded{$ids[$i]}) { + $check = ' checked="checked" '; + } + $datatable .= ''; + } + my $colsleft = $numinrow - @ids%($numinrow); + if ($colsleft > 1 ) { + $datatable .= ''; + } elsif ($colsleft == 1) { + $datatable .= ''; + } + $datatable .= '
'. + ''. + '  
'; $rownum ++; + $css_class = $rownum%2?' class="LC_odd_row"':''; + $datatable .= ''. + ''. + $titles->{'errorsysmail'}. + ''. + ''; + $rownum ++; + } elsif ($position eq 'bottom') { + my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); + my (@posstypes,%usertypeshash); + if (ref($types) eq 'ARRAY') { + @posstypes = @{$types}; + } + if (@posstypes) { + if (ref($usertypes) eq 'HASH') { + %usertypeshash = %{$usertypes}; + } + my @overridden; + my $numinrow = 4; + if (ref($settings) eq 'HASH') { + if (ref($settings->{'overrides'}) eq 'HASH') { + foreach my $key (sort(keys(%{$settings->{'overrides'}}))) { + if (ref($settings->{'overrides'}{$key}) eq 'HASH') { + push(@overridden,$key); + foreach my $item (@contacts) { + if ($settings->{'overrides'}{$key}{$item}) { + $checked{'override_'.$key}{$item} = ' checked="checked" '; + } + } + $otheremails{'override_'.$key} = $settings->{'overrides'}{$key}{'others'}; + $bccemails{'override_'.$key} = $settings->{'overrides'}{$key}{'bcc'}; + $includeloc{'override_'.$key} = ''; + $includestr{'override_'.$key} = ''; + if ($settings->{'overrides'}{$key}{'include'} ne '') { + ($includeloc{'override_'.$key},$includestr{'override_'.$key}) = + split(/:/,$settings->{'overrides'}{$key}{'include'},2); + $includestr{'override_'.$key} = &unescape($includestr{'override_'.$key}); + } + } + } + } + } + my $customclass = 'LC_helpdesk_override'; + my $optionsprefix = 'LC_options_helpdesk_'; + + my $onclicktypes = "toggleHelpdeskRow(this.form,'overrides','$customclass','$optionsprefix');"; + + $datatable .= &insttypes_row($settings,$types,$usertypes,$dom, + $numinrow,$othertitle,'overrides', + \$rownum,$onclicktypes,$customclass); + $rownum ++; + $usertypeshash{'default'} = $othertitle; + foreach my $status (@posstypes) { + my $css_class; + if ($rownum%2) { + $css_class = 'LC_odd_row '; + } + $css_class .= $customclass; + my $rowid = $optionsprefix.$status; + my $hidden = 1; + my $currstyle = 'display:none'; + if (grep(/^\Q$status\E$/,@overridden)) { + $currstyle = 'display:table-row'; + $hidden = 0; + } + my $key = 'override_'.$status; + $datatable .= &overridden_helpdesk($checked{$key},$otheremails{$key},$bccemails{$key}, + $includeloc{$key},$includestr{$key},$status,$rowid, + $usertypeshash{$status},$css_class,$currstyle, + \@contacts,$short_titles); + unless ($hidden) { + $rownum ++; + } + } + } } $$rowtotal += $rownum; return $datatable; } +sub core_link_msu { + return &Apache::loncommon::modal_link('http://loncapa.org/core.html', + &mt('LON-CAPA core group - MSU'),600,500); +} + +sub overridden_helpdesk { + my ($checked,$otheremails,$bccemails,$includeloc,$includestr,$type,$rowid, + $typetitle,$css_class,$rowstyle,$contacts,$short_titles) = @_; + my $class = 'LC_left_item'; + if ($css_class) { + $css_class = ' class="'.$css_class.'"'; + } + if ($rowid) { + $rowid = ' id="'.$rowid.'"'; + } + if ($rowstyle) { + $rowstyle = ' style="'.$rowstyle.'"'; + } + my ($output,$description); + $description = &mt('Helpdesk requests from: [_1] in this domain (overrides default)',"$typetitle"); + $output = ''. + "$description\n". + ''. + '
'.&mt('E-mail recipient(s)').''. + ''; + if (ref($contacts) eq 'ARRAY') { + foreach my $item (@{$contacts}) { + my $check; + if (ref($checked) eq 'HASH') { + $check = $checked->{$item}; + } + my $title; + if (ref($short_titles) eq 'HASH') { + $title = $short_titles->{$item}; + } + $output .= ' '; + } + } + $output .= '
'.&mt('Others').':  '. + ''; + my %locchecked; + foreach my $loc ('s','b') { + if ($includeloc eq $loc) { + $locchecked{$loc} = ' checked="checked"'; + last; + } + } + $output .= '
'.&mt('Bcc:').(' 'x6). + '
'. + '
'.&mt('Optional added text').''. + &mt('Text automatically added to e-mail:').' '. + '
'. + ''.&mt('Location:').' '. + ''. + (' 'x2). + ''. + '
'. + ''."\n"; + return $output; +} + +sub contacts_javascript { + return <<"ENDSCRIPT"; + + + +ENDSCRIPT +} + +sub print_helpsettings { + my ($position,$dom,$settings,$rowtotal) = @_; + my $confname = $dom.'-domainconfig'; + my $formname = 'display'; + my ($datatable,$itemcount); + if ($position eq 'top') { + $itemcount = 1; + my (%choices,%defaultchecked,@toggles); + $choices{'submitbugs'} = &mt('Display link to: [_1]?', + &Apache::loncommon::modal_link('http://bugs.loncapa.org', + &mt('LON-CAPA bug tracker'),600,500)); + %defaultchecked = ('submitbugs' => 'on'); + @toggles = ('submitbugs'); + ($datatable,$itemcount) = &radiobutton_prefs($settings,\@toggles,\%defaultchecked, + \%choices,$itemcount); + $$rowtotal ++; + } else { + my $css_class; + my %existing=&Apache::lonnet::dump('roles',$dom,$confname,'rolesdef_'); + my (%customroles,%ordered,%current); + if (ref($settings) eq 'HASH') { + if (ref($settings->{'adhoc'}) eq 'HASH') { + %current = %{$settings->{'adhoc'}}; + } + } + my $count = 0; + foreach my $key (sort(keys(%existing))) { + if ($key=~/^rolesdef\_(\w+)$/) { + my $rolename = $1; + my (%privs,$order); + ($privs{'system'},$privs{'domain'},$privs{'course'}) = split(/\_/,$existing{$key}); + $customroles{$rolename} = \%privs; + if (ref($current{$rolename}) eq 'HASH') { + $order = $current{$rolename}{'order'}; + } + if ($order eq '') { + $order = $count; + } + $ordered{$order} = $rolename; + $count++; + } + } + my $maxnum = scalar(keys(%ordered)); + my @roles_by_num = (); + foreach my $item (sort {$a <=> $b } (keys(%ordered))) { + push(@roles_by_num,$item); + } + my $context = 'domprefs'; + my $crstype = 'Course'; + my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); + my @accesstypes = ('all','dh','da','none'); + my ($numstatustypes,@jsarray); + if (ref($types) eq 'ARRAY') { + if (@{$types} > 0) { + $numstatustypes = scalar(@{$types}); + push(@accesstypes,'status'); + @jsarray = ('bystatus'); + } + } + my %domhelpdesk = &Apache::lonnet::get_active_domroles($dom,['dh','da']); + if (keys(%domhelpdesk)) { + push(@accesstypes,('inc','exc')); + push(@jsarray,('notinc','notexc')); + } + my $hiddenstr = join("','",@jsarray); + my $context = 'domprefs'; + my $crstype = 'Course'; + my $prefix = 'helproles_'; + my $add_class = 'LC_hidden'; + foreach my $num (@roles_by_num) { + my $role = $ordered{$num}; + my ($desc,$access,@statuses); + if (ref($current{$role}) eq 'HASH') { + $desc = $current{$role}{'desc'}; + $access = $current{$role}{'access'}; + if (ref($current{$role}{'insttypes'}) eq 'ARRAY') { + @statuses = @{$current{$role}{'insttypes'}}; + } + } + if ($desc eq '') { + $desc = $role; + } + my $identifier = 'custhelp'.$num; + my %full=(); + my %levels= ( + course => {}, + domain => {}, + system => {}, + ); + my %levelscurrent=( + course => {}, + domain => {}, + system => {}, + ); + &Apache::lonuserutils::custom_role_privs($customroles{$role},\%full,\%levels,\%levelscurrent); + my @templateroles = &Apache::lonuserutils::custom_template_roles($context,$crstype); + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $chgstr = ' onchange="javascript:reorderHelpRoles(this.form,'."'helproles_".$num."_pos'".');"'; + $datatable .= ''.$role.'
'. + ''.(' 'x2). + ''. + ''. + '
'.&mt('Role name').''. + &mt('Name shown to users:'). + ''. + '
'. + &helpdeskroles_access($dom,$prefix,$num,$add_class,$current{$role},\@accesstypes, + $othertitle,$usertypes,$types,\%domhelpdesk). + '
'. + ''.&mt('Role privileges').&adhocbutton($prefix,$num,'privs','show').''. + &Apache::lonuserutils::custom_role_table($crstype,\%full,\%levels, + \%levelscurrent,$identifier, + 'LC_hidden',$prefix.$num.'_privs'). + '
'; + $itemcount ++; + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $newcust = 'custhelp'.$count; + my (%privs,%levelscurrent); + my %full=(); + my %levels= ( + course => {}, + domain => {}, + system => {}, + ); + &Apache::lonuserutils::custom_role_privs(\%privs,\%full,\%levels,\%levelscurrent); + my @templateroles = &Apache::lonuserutils::custom_template_roles($context,$crstype); + my $chgstr = ' onchange="javascript:reorderHelpRoles(this.form,'."'helproles_".$count."_pos'".');"'; + $datatable .= ''. + '
'.&mt('Role name').''. + ''. + &mt('Internal name:'). + ''. + ''.(' 'x4). + ''. + &mt('Name shown to users:'). + ''. + '
'. + &helpdeskroles_access($dom,$prefix,$count,'',undef,\@accesstypes,$othertitle, + $usertypes,$types,\%domhelpdesk). + '
'.&mt('Role privileges').''. + &Apache::lonuserutils::custom_role_header($context,$crstype, + \@templateroles,$newcust). + &Apache::lonuserutils::custom_role_table('Course',\%full,\%levels, + \%levelscurrent,$newcust). + '
'. + &helpsettings_javascript(\@roles_by_num,$maxnum,$hiddenstr,$formname). + ''; + $count ++; + $$rowtotal += $count; + } + return $datatable; +} + +sub adhocbutton { + my ($prefix,$num,$field,$visibility) = @_; + my %lt = &Apache::lonlocal::texthash( + show => 'Show details', + hide => 'Hide details', + ); + return ''.(' 'x10). + ''.(' 'x2).''.(' 'x2); +} + +sub helpsettings_javascript { + my ($roles_by_num,$total,$hiddenstr,$formname) = @_; + return unless(ref($roles_by_num) eq 'ARRAY'); + my %html_js_lt = &Apache::lonlocal::texthash( + show => 'Show details', + hide => 'Hide details', + ); + &html_escape(\%html_js_lt); + my $jstext = ' var helproles = Array('."'".join("','",@{$roles_by_num})."'".');'."\n"; + return <<"ENDSCRIPT"; + + +ENDSCRIPT +} + +sub helpdeskroles_access { + my ($dom,$prefix,$num,$add_class,$current,$accesstypes,$othertitle, + $usertypes,$types,$domhelpdesk) = @_; + return unless ((ref($accesstypes) eq 'ARRAY') && (ref($domhelpdesk) eq 'HASH')); + my %lt = &Apache::lonlocal::texthash( + 'rou' => 'Role usage', + 'whi' => 'Which helpdesk personnel may use this role?', + 'all' => 'All with domain helpdesk or helpdesk assistant role', + 'dh' => 'All with domain helpdesk role', + 'da' => 'All with domain helpdesk assistant role', + 'none' => 'None', + 'status' => 'Determined based on institutional status', + 'inc' => 'Include all, but exclude specific personnel', + 'exc' => 'Exclude all, but include specific personnel', + ); + my %usecheck = ( + all => ' checked="checked"', + ); + my %displaydiv = ( + status => 'none', + inc => 'none', + exc => 'none', + priv => 'block', + ); + my $output; + if (ref($current) eq 'HASH') { + if (($current->{'access'} ne '') && ($current->{'access'} ne 'all')) { + if (grep(/^\Q$current->{access}\E$/,@{$accesstypes})) { + $usecheck{$current->{access}} = $usecheck{'all'}; + delete($usecheck{'all'}); + if ($current->{access} =~ /^(status|inc|exc)$/) { + my $access = $1; + $displaydiv{$access} = 'inline'; + } elsif ($current->{access} eq 'none') { + $displaydiv{'priv'} = 'none'; + } + } + } + } + $output = '
'.$lt{'rou'}.''. + '

'.$lt{'whi'}.'

'; + foreach my $access (@{$accesstypes}) { + $output .= '

'; + if ($access eq 'status') { + $output .= '

'. + &Apache::lonuserutils::adhoc_status_types($dom,$prefix,$num,$current->{$access}, + $othertitle,$usertypes,$types). + '
'; + } elsif (($access eq 'inc') && (keys(%{$domhelpdesk}) > 0)) { + $output .= '
'. + &Apache::lonuserutils::adhoc_staff($access,$prefix,$num,$current->{$access},$domhelpdesk). + '
'; + } elsif (($access eq 'exc') && (keys(%{$domhelpdesk}) > 0)) { + $output .= '
'. + &Apache::lonuserutils::adhoc_staff($access,$prefix,$num,$current->{$access},$domhelpdesk). + '
'; + } + $output .= '

'; + } + $output .= '
'; + return $output; +} + +sub radiobutton_prefs { + my ($settings,$toggles,$defaultchecked,$choices,$itemcount,$onclick, + $additional,$align) = @_; + return unless ((ref($toggles) eq 'ARRAY') && (ref($defaultchecked) eq 'HASH') && + (ref($choices) eq 'HASH')); + + my (%checkedon,%checkedoff,$datatable,$css_class); + + foreach my $item (@{$toggles}) { + if ($defaultchecked->{$item} eq 'on') { + $checkedon{$item} = ' checked="checked" '; + $checkedoff{$item} = ' '; + } elsif ($defaultchecked->{$item} eq 'off') { + $checkedoff{$item} = ' checked="checked" '; + $checkedon{$item} = ' '; + } + } + if (ref($settings) eq 'HASH') { + foreach my $item (@{$toggles}) { + if ($settings->{$item} eq '1') { + $checkedon{$item} = ' checked="checked" '; + $checkedoff{$item} = ' '; + } elsif ($settings->{$item} eq '0') { + $checkedoff{$item} = ' checked="checked" '; + $checkedon{$item} = ' '; + } + } + } + if ($onclick) { + $onclick = ' onclick="'.$onclick.'"'; + } + foreach my $item (@{$toggles}) { + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= + ''. + ''.$choices->{$item}. + ''; + if ($align eq 'left') { + $datatable .= ''; + } else { + $datatable .= ''; + } + $datatable .= + ''. + ' '. + ''.$additional. + ''. + ''; + $itemcount ++; + } + return ($datatable,$itemcount); +} + +sub print_ltitools { + my ($dom,$settings,$rowtotal) = @_; + my $rownum = 0; + my $css_class; + my $itemcount = 1; + my $maxnum = 0; + my %ordered; + if (ref($settings) eq 'HASH') { + foreach my $item (keys(%{$settings})) { + if (ref($settings->{$item}) eq 'HASH') { + my $num = $settings->{$item}{'order'}; + $ordered{$num} = $item; + } + } + } + my $confname = $dom.'-domainconfig'; + my $switchserver = &check_switchserver($dom,$confname); + my $maxnum = scalar(keys(%ordered)); + my $datatable; + my %lt = <itools_names(); + my @courseroles = ('cc','in','ta','ep','st'); + my @ltiroles = qw(Instructor ContentDeveloper TeachingAssistant Learner); + my @fields = ('fullname','firstname','lastname','email','roles','user'); + if (keys(%ordered)) { + my @items = sort { $a <=> $b } keys(%ordered); + for (my $i=0; $i<@items; $i++) { + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $item = $ordered{$items[$i]}; + my ($title,$key,$secret,$url,$lifetime,$imgsrc,%sigsel); + if (ref($settings->{$item}) eq 'HASH') { + $title = $settings->{$item}->{'title'}; + $url = $settings->{$item}->{'url'}; + $key = $settings->{$item}->{'key'}; + $secret = $settings->{$item}->{'secret'}; + $lifetime = $settings->{$item}->{'lifetime'}; + my $image = $settings->{$item}->{'image'}; + if ($image ne '') { + $imgsrc = ''.&mt('Tool Provider icon').''; + } + if ($settings->{$item}->{'sigmethod'} eq 'HMAC-256') { + $sigsel{'HMAC-256'} = ' selected="selected"'; + } else { + $sigsel{'HMAC-SHA1'} = ' selected="selected"'; + } + } + my $chgstr = ' onchange="javascript:reorderLTITools(this.form,'."'ltitools_".$item."'".');"'; + $datatable .= '' + .''.(' 'x2). + ''. + ''. + '
'.&mt('Required settings').''. + ''.$lt{'title'}.': '. + (' 'x2). + ''.$lt{'version'}.': '. + (' 'x2). + ''.$lt{'msgtype'}.': '. + (' 'x2). + ''.$lt{'sigmethod'}.':'. + '

'. + ''.$lt{'url'}.':'. + (' 'x2). + ''.$lt{'key'}.':'. + ' '. + (' 'x2). + ''.$lt{'lifetime'}.':'. + ' '. + (' 'x2). + ''.$lt{'secret'}.':'. + ''. + ''. + ''. + '
'. + '
'.&mt('Optional settings').''. + ''.&mt('Display target:'); + my %currdisp; + if (ref($settings->{$item}->{'display'}) eq 'HASH') { + if ($settings->{$item}->{'display'}->{'target'} eq 'window') { + $currdisp{'window'} = ' checked="checked"'; + } elsif ($settings->{$item}->{'display'}->{'target'} eq 'tab') { + $currdisp{'tab'} = ' checked="checked"'; + } else { + $currdisp{'iframe'} = ' checked="checked"'; + } + if ($settings->{$item}->{'display'}->{'width'} =~ /^(\d+)$/) { + $currdisp{'width'} = $1; + } + if ($settings->{$item}->{'display'}->{'height'} =~ /^(\d+)$/) { + $currdisp{'height'} = $1; + } + $currdisp{'linktext'} = $settings->{$item}->{'display'}->{'linktext'}; + $currdisp{'explanation'} = $settings->{$item}->{'display'}->{'explanation'}; + } else { + $currdisp{'iframe'} = ' checked="checked"'; + } + foreach my $disp ('iframe','tab','window') { + $datatable .= ''.(' 'x2); + } + $datatable .= (' 'x4); + foreach my $dimen ('width','height') { + $datatable .= ''. + (' 'x2); + } + $datatable .= '
'. + '
'.$lt{'linktext'}.'
'. + '
'. + '
'.$lt{'explanation'}.'
'. + '

'; + my %units = ( + 'passback' => 'days', + 'roster' => 'seconds', + ); + foreach my $extra ('passback','roster') { + my $validsty = 'none'; + my $currvalid; + my $checkedon = ''; + my $checkedoff = ' checked="checked"'; + if ($settings->{$item}->{$extra}) { + $checkedon = $checkedoff; + $checkedoff = ''; + $validsty = 'inline-block'; + if ($settings->{$item}->{$extra.'valid'} =~ /^\d+\.?\d*$/) { + $currvalid = $settings->{$item}->{$extra.'valid'}; + } + } + my $onclick = ' onclick="toggleLTITools(this.form,'."'$extra','$i'".');"'; + $datatable .= '
'.$lt{$extra}.' '. + ''.(' 'x2). + '
'. + '
'. + ''. + &mt("at least [_1] $units{$extra} after launch", + ''). + '
'; + } + $datatable .= ''.$lt{'icon'}.': '; + if ($imgsrc) { + $datatable .= $imgsrc. + ' '. + ' '.&mt('Replace:').' '; + } else { + $datatable .= '('.&mt('if larger than 21x21 pixels, image will be scaled').') '; + } + if ($switchserver) { + $datatable .= &mt('Upload to library server: [_1]',$switchserver); + } else { + $datatable .= ''; + } + $datatable .= '
'; + my (%checkedfields,%rolemaps,$userincdom); + if (ref($settings->{$item}) eq 'HASH') { + if (ref($settings->{$item}->{'fields'}) eq 'HASH') { + %checkedfields = %{$settings->{$item}->{'fields'}}; + } + $userincdom = $settings->{$item}->{'incdom'}; + if (ref($settings->{$item}->{'roles'}) eq 'HASH') { + %rolemaps = %{$settings->{$item}->{'roles'}}; + $checkedfields{'roles'} = 1; + } + } + $datatable .= '
'.&mt('User data sent on launch').''. + ''; + my $userfieldstyle = 'display:none;'; + my $seluserdom = ''; + my $unseluserdom = ' selected="selected"'; + foreach my $field (@fields) { + my ($checked,$onclick,$id,$spacer); + if ($checkedfields{$field}) { + $checked = ' checked="checked"'; + } + if ($field eq 'user') { + $id = ' id="ltitools_user_field_'.$i.'"'; + $onclick = ' onclick="toggleLTITools(this.form,'."'$field','$i'".')"'; + if ($checked) { + $userfieldstyle = 'display:inline-block'; + if ($userincdom) { + $seluserdom = $unseluserdom; + $unseluserdom = ''; + } + } + } else { + $spacer = (' ' x2); + } + $datatable .= ''.$spacer; + } + $datatable .= ''; + $datatable .= '
'. + ' : '. + '
'; + $datatable .= '
'. + '
'.&mt('Role mapping').''; + foreach my $role (@courseroles) { + my ($selected,$selectnone); + if (!$rolemaps{$role}) { + $selectnone = ' selected="selected"'; + } + $datatable .= ''; + } + $datatable .= '
'. + &Apache::lonnet::plaintext($role,'Course').'
'. + '
'; + my %courseconfig; + if (ref($settings->{$item}) eq 'HASH') { + if (ref($settings->{$item}->{'crsconf'}) eq 'HASH') { + %courseconfig = %{$settings->{$item}->{'crsconf'}}; + } + } + $datatable .= '
'.&mt('Configurable in course').''; + foreach my $item ('label','title','target','linktext','explanation','append') { + my $checked; + if ($courseconfig{$item}) { + $checked = ' checked="checked"'; + } + $datatable .= ''.(' ' x2)."\n"; + } + $datatable .= '
'. + '
'.&mt('Custom items sent on launch').''. + ''; + if (ref($settings->{$item}->{'custom'}) eq 'HASH') { + my %custom = %{$settings->{$item}->{'custom'}}; + if (keys(%custom) > 0) { + foreach my $key (sort(keys(%custom))) { + $datatable .= ''. + ''; + } + } + } + $datatable .= ''; + $datatable .= '
'.&mt('Action').''.&mt('Name').''.&mt('Value').'
'. + ''.$key.'
'. + ''. + '
'."\n"; + $itemcount ++; + } + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $chgstr = ' onchange="javascript:reorderLTITools(this.form,'."'ltitools_add_pos'".');"'; + $datatable .= ''."\n". + ''."\n". + ' '."\n". + ''.&mt('Add').''."\n". + ''. + '
'.&mt('Required settings').''. + ''.$lt{'title'}.': '."\n". + (' 'x2). + ''.$lt{'version'}.': '."\n". + (' 'x2). + ''.$lt{'msgtype'}.': '. + ''.$lt{'sigmethod'}.':'. + '
'. + ''.$lt{'url'}.': '."\n". + (' 'x2). + ''.$lt{'key'}.': '."\n". + (' 'x2). + ''.$lt{'lifetime'}.': '."\n". + (' 'x2). + ''.$lt{'secret'}.':'. + ' '."\n". + '
'. + '
'.&mt('Optional settings').''. + ''.&mt('Display target:'); + my %defaultdisp; + $defaultdisp{'iframe'} = ' checked="checked"'; + foreach my $disp ('iframe','tab','window') { + $datatable .= ''.(' 'x2); + } + $datatable .= (' 'x4); + foreach my $dimen ('width','height') { + $datatable .= ''. + (' 'x2); + } + $datatable .= '
'. + '
'.$lt{'linktext'}.'
'. + '
'. + '
'.$lt{'explanation'}.'
'. + ''. + '

'; + my %units = ( + 'passback' => 'days', + 'roster' => 'seconds', + ); + my %defaulttimes = ( + 'passback' => '7', + 'roster' => '300', + ); + foreach my $extra ('passback','roster') { + my $onclick = ' onclick="toggleLTITools(this.form,'."'$extra','add'".');"'; + $datatable .= '
'.$lt{$extra}.' '. + ''.(' 'x2).''. + '
'. + '
'; + } + $datatable .= ''.$lt{'icon'}.': '. + '('.&mt('if larger than 21x21 pixels, image will be scaled').') '; + if ($switchserver) { + $datatable .= &mt('Upload to library server: [_1]',$switchserver); + } else { + $datatable .= ''; + } + $datatable .= '
'. + '
'.&mt('User data sent on launch').''. + ''; + foreach my $field (@fields) { + my ($id,$onclick,$spacer); + if ($field eq 'user') { + $id = ' id="ltitools_user_field_add"'; + $onclick = ' onclick="toggleLTITools(this.form,'."'$field','add'".')"'; + } else { + $spacer = (' ' x2); + } + $datatable .= ''.$spacer; + } + $datatable .= ''. + '
'; + $datatable .= '
'.&mt('Role mapping').''; + foreach my $role (@courseroles) { + my ($checked,$checkednone); + $datatable .= ''; + } + $datatable .= '
'. + &Apache::lonnet::plaintext($role,'Course').'
'. + '
'. + '
'.&mt('Configurable in course').''; + foreach my $item ('label','title','target','linktext','explanation','append') { + $datatable .= ''.(' ' x2)."\n"; + } + $datatable .= '
'. + '
'.&mt('Custom items sent on launch').''. + ''. + ''. + '
'.&mt('Action').''.&mt('Name').''.&mt('Value').'
'. + ''. + '
'."\n". + ''."\n". + ''."\n"; + $itemcount ++; + return $datatable; +} + +sub ltitools_names { + my %lt = &Apache::lonlocal::texthash( + 'title' => 'Title', + 'version' => 'Version', + 'msgtype' => 'Message Type', + 'sigmethod' => 'Signature Method', + 'url' => 'URL', + 'key' => 'Key', + 'lifetime' => 'Nonce lifetime (s)', + 'secret' => 'Secret', + 'icon' => 'Icon', + 'user' => 'User', + 'fullname' => 'Full Name', + 'firstname' => 'First Name', + 'lastname' => 'Last Name', + 'email' => 'E-mail', + 'roles' => 'Role', + 'window' => 'Window', + 'tab' => 'Tab', + 'iframe' => 'iFrame', + 'height' => 'Height', + 'width' => 'Width', + 'linktext' => 'Default Link Text', + 'explanation' => 'Default Explanation', + 'passback' => 'Tool can return grades:', + 'roster' => 'Tool can retrieve roster:', + 'crstarget' => 'Display target', + 'crslabel' => 'Course label', + 'crstitle' => 'Course title', + 'crslinktext' => 'Link Text', + 'crsexplanation' => 'Explanation', + 'crsappend' => 'Provider URL', + ); + return %lt; +} + +sub print_lti { + my ($dom,$settings,$rowtotal) = @_; + my $itemcount = 1; + my $maxnum = 0; + my $css_class; + my %ordered; + if (ref($settings) eq 'HASH') { + foreach my $item (keys(%{$settings})) { + if (ref($settings->{$item}) eq 'HASH') { + my $num = $settings->{$item}{'order'}; + $ordered{$num} = $item; + } + } + } + my $maxnum = scalar(keys(%ordered)); + my $datatable; + my %lt = <i_names(); + if (keys(%ordered)) { + my @items = sort { $a <=> $b } keys(%ordered); + for (my $i=0; $i<@items; $i++) { + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $item = $ordered{$items[$i]}; + my ($key,$secret,$lifetime,$consumer,$requser,$current); + if (ref($settings->{$item}) eq 'HASH') { + $key = $settings->{$item}->{'key'}; + $secret = $settings->{$item}->{'secret'}; + $lifetime = $settings->{$item}->{'lifetime'}; + $consumer = $settings->{$item}->{'consumer'}; + $requser = $settings->{$item}->{'requser'}; + $current = $settings->{$item}; + } + my $onclickrequser = ' onclick="toggleLTI(this.form,'."'requser','$i'".');"'; + my %checkedrequser = ( + yes => ' checked="checked"', + no => '', + ); + if (!$requser) { + $checkedrequser{'no'} = $checkedrequser{'yes'}; + $checkedrequser{'yes'} = ''; + } + my $chgstr = ' onchange="javascript:reorderLTI(this.form,'."'lti_pos_".$item."'".');"'; + $datatable .= '' + .''.(' 'x2). + ''. + ''. + '
'.&mt('Required settings').''. + ''.$lt{'consumer'}. + ': '. + (' 'x2). + ''.$lt{'version'}.': '. + (' 'x2). + ''.$lt{'lifetime'}.':'. + (' 'x2). + ''.$lt{'requser'}.':'. + ' '."\n". + ''."\n". + '

'. + ''.$lt{'key'}. + ': '. + (' 'x2). + ''.$lt{'secret'}.':'. + ''. + ''. + ''. + '
'.<i_options($i,$current,$itemcount,%lt).''; + $itemcount ++; + } + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $chgstr = ' onchange="javascript:reorderLTI(this.form,'."'lti_pos_add'".');"'; + $datatable .= ''."\n". + ''."\n". + ' '."\n". + ''.&mt('Add').''."\n". + ''. + '
'.&mt('Required settings').''. + ''.$lt{'consumer'}. + ': '."\n". + (' 'x2). + ''.$lt{'version'}.': '."\n". + (' 'x2). + ''.$lt{'lifetime'}.': '."\n". + (' 'x2). + ''.$lt{'requser'}.':'. + ' '."\n". + ''."\n". + '

'. + ''.$lt{'key'}.': '."\n". + (' 'x2). + ''.$lt{'secret'}.':'. + ' '."\n". + '
'.<i_options('add',undef,$itemcount,%lt). + ''."\n". + ''."\n"; + $$rowtotal ++; + return $datatable;; +} + +sub lti_names { + my %lt = &Apache::lonlocal::texthash( + 'version' => 'LTI Version', + 'url' => 'URL', + 'key' => 'Key', + 'lifetime' => 'Nonce lifetime (s)', + 'consumer' => 'Consumer', + 'secret' => 'Secret', + 'requser' => "User's identity sent", + 'email' => 'Email address', + 'sourcedid' => 'User ID', + 'other' => 'Other', + 'passback' => 'Can return grades to Consumer:', + 'roster' => 'Can retrieve roster from Consumer:', + 'topmenu' => 'Display LON-CAPA page header', + 'inlinemenu'=> 'Display LON-CAPA inline menu', + ); + return %lt; +} + +sub lti_options { + my ($num,$current,$itemcount,%lt) = @_; + my (%checked,%rolemaps,$crssecsrc,$userfield,$cidfield); + $checked{'mapuser'}{'sourcedid'} = ' checked="checked"'; + $checked{'mapcrs'}{'course_offering_sourcedid'} = ' checked="checked"'; + $checked{'makecrs'}{'N'} = ' checked="checked"'; + $checked{'mapcrstype'} = {}; + $checked{'makeuser'} = {}; + $checked{'selfenroll'} = {}; + $checked{'crssec'} = {}; + $checked{'crssecsrc'} = {}; + $checked{'lcauth'} = {}; + $checked{'menuitem'} = {}; + if ($num eq 'add') { + $checked{'lcauth'}{'lti'} = ' checked="checked"'; + } + my $userfieldsty = 'none'; + my $crsfieldsty = 'none'; + my $crssecfieldsty = 'none'; + my $secsrcfieldsty = 'none'; + my $passbacksty = 'none'; + my $optionsty = 'block'; + my $lcauthparm; + my $lcauthparmstyle = 'display:none'; + my $lcauthparmtext; + my $menusty; + my $numinrow = 4; + my %menutitles = <imenu_titles(); + + if (ref($current) eq 'HASH') { + if (!$current->{'requser'}) { + $optionsty = 'none'; + } + if (($current->{'mapuser'} ne '') && ($current->{'mapuser'} ne 'lis_person_sourcedid')) { + $checked{'mapuser'}{'sourcedid'} = ''; + if ($current->{'mapuser'} eq 'lis_person_contact_email_primary') { + $checked{'mapuser'}{'email'} = ' checked="checked"'; + } else { + $checked{'mapuser'}{'other'} = ' checked="checked"'; + $userfield = $current->{'mapuser'}; + $userfieldsty = 'inline-block'; + } + } + if (($current->{'mapcrs'} ne '') && ($current->{'mapcrs'} ne 'course_offering_sourcedid')) { + $checked{'mapcrs'}{'course_offering_sourcedid'} = ''; + if ($current->{'mapcrs'} eq 'context_id') { + $checked{'mapcrs'}{'context_id'} = ' checked="checked"'; + } else { + $checked{'mapcrs'}{'other'} = ' checked="checked"'; + $cidfield = $current->{'mapcrs'}; + $crsfieldsty = 'inline-block'; + } + } + if (ref($current->{'mapcrstype'}) eq 'ARRAY') { + foreach my $type (@{$current->{'mapcrstype'}}) { + $checked{'mapcrstype'}{$type} = ' checked="checked"'; + } + } + if ($current->{'makecrs'}) { + $checked{'makecrs'}{'Y'} = ' checked="checked"'; + } + if (ref($current->{'makeuser'}) eq 'ARRAY') { + foreach my $role (@{$current->{'makeuser'}}) { + $checked{'makeuser'}{$role} = ' checked="checked"'; + } + } + if ($current->{'lcauth'} =~ /^(internal|localauth|krb4|krb5|lti)$/) { + $checked{'lcauth'}{$1} = ' checked="checked"'; + unless (($current->{'lcauth'} eq 'lti') || ($current->{'lcauth'} eq 'internal')) { + $lcauthparm = $current->{'lcauthparm'}; + $lcauthparmstyle = 'display:table-row'; + if ($current->{'lcauth'} eq 'localauth') { + $lcauthparmtext = &mt('Local auth argument'); + } else { + $lcauthparmtext = &mt('Kerberos domain'); + } + } + } + if (ref($current->{'selfenroll'}) eq 'ARRAY') { + foreach my $role (@{$current->{'selfenroll'}}) { + $checked{'selfenroll'}{$role} = ' checked="checked"'; + } + } + if (ref($current->{'maproles'}) eq 'HASH') { + %rolemaps = %{$current->{'maproles'}}; + } + if ($current->{'section'} ne '') { + $checked{'crssec'}{'Y'} = ' checked="checked"'; + $crssecfieldsty = 'inline-block'; + if ($current->{'section'} eq 'course_section_sourcedid') { + $checked{'crssecsrc'}{'sourcedid'} = ' checked="checked"'; + } else { + $checked{'crssecsrc'}{'other'} = ' checked="checked"'; + $crssecsrc = $current->{'section'}; + $secsrcfieldsty = 'inline-block'; + } + } else { + $checked{'crssec'}{'N'} = ' checked="checked"'; + } + if ($current->{'topmenu'}) { + $checked{'topmenu'}{'Y'} = ' checked="checked"'; + } else { + $checked{'topmenu'}{'N'} = ' checked="checked"'; + } + if ($current->{'inlinemenu'}) { + $checked{'inlinemenu'}{'Y'} = ' checked="checked"'; + } else { + $checked{'inlinemenu'}{'N'} = ' checked="checked"'; + } + if (($current->{'topmenu'}) || ($current->{'inlinemenu'})) { + $menusty = 'inline-block'; + if (ref($current->{'lcmenu'}) eq 'ARRAY') { + foreach my $item (@{$current->{'lcmenu'}}) { + if (exists($menutitles{$item})) { + $checked{'menuitem'}{$item} = ' checked="checked"'; + } + } + } + } else { + $menusty = 'none'; + } + } else { + $checked{'makecrs'}{'N'} = ' checked="checked"'; + $checked{'crssec'}{'N'} = ' checked="checked"'; + $checked{'topmenu'}{'N'} = ' checked="checked"'; + $checked{'inlinemenu'}{'Y'} = ' checked="checked"'; + $checked{'menuitem'}{'grades'} = ' checked="checked"'; + $menusty = 'inline-block'; + } + my @coursetypes = ('official','unofficial','community','textbook','placement','lti'); + my %coursetypetitles = &Apache::lonlocal::texthash ( + official => 'Official', + unofficial => 'Unofficial', + community => 'Community', + textbook => 'Textbook', + placement => 'Placement Test', + lti => 'LTI Provider', + ); + my @authtypes = ('internal','krb4','krb5','localauth'); + my %shortauth = ( + internal => 'int', + krb4 => 'krb4', + krb5 => 'krb5', + localauth => 'loc' + ); + my %authnames = &authtype_names(); + my @ltiroles = qw(Learner Instructor ContentDeveloper TeachingAssistant Mentor Member Manager Administrator); + my @lticourseroles = qw(Learner Instructor TeachingAssistant Mentor); + my @courseroles = ('cc','in','ta','ep','st'); + my $onclickuser = ' onclick="toggleLTI(this.form,'."'user','$num'".');"'; + my $onclickcrs = ' onclick="toggleLTI(this.form,'."'crs','$num'".');"'; + my $onclicksec = ' onclick="toggleLTI(this.form,'."'sec','$num'".');"'; + my $onclicksecsrc = ' onclick="toggleLTI(this.form,'."'secsrc','$num'".')"'; + my $onclicklcauth = ' onclick="toggleLTI(this.form,'."'lcauth','$num'".')"'; + my $onclickmenu = ' onclick="toggleLTI(this.form,'."'lcmenu','$num'".');"'; + my $output = '
'.&mt('Mapping users').''. + '
'.&mt('LON-CAPA username').': '; + foreach my $option ('sourcedid','email','other') { + $output .= ''. + ($option eq 'other' ? '' : (' 'x2) ); + } + $output .= '
'. + '
'. + '
'. + '
'.&mt('Mapping course roles').''; + foreach my $ltirole (@lticourseroles) { + my ($selected,$selectnone); + if ($rolemaps{$ltirole} eq '') { + $selectnone = ' selected="selected"'; + } + $output .= ''; + } + $output .= '
'.$ltirole.'
'. + '
'. + '
'.&mt('Roles which may create user accounts').''; + foreach my $ltirole (@ltiroles) { + $output .= '  '; + } + $output .= '
'. + '
'.&mt('New user accounts created for LTI users').''. + ''. + &modifiable_userdata_row('lti','instdata_'.$num,$current,$numinrow,$itemcount). + '
'. + ''. + ''. + ''. + ''. + '
LON-CAPA Authentication'; + foreach my $auth ('lti',@authtypes) { + my $authtext; + if ($auth eq 'lti') { + $authtext = &mt('None'); + } else { + $authtext = $authnames{$shortauth{$auth}}; + } + $output .= '  '; + } + $output .= '
'. + ''.$lcauthparmtext.''. + '
'. + '
'.&mt('Mapping courses').''. + '
'. + &mt('Unique course identifier').': '; + foreach my $option ('course_offering_sourcedid','context_id','other') { + $output .= ''. + ($option eq 'other' ? '' : (' 'x2) ); + } + $output .= '
'. + ''. + '
'. + ''.&mt('LON-CAPA course type(s)').': '; + foreach my $type (@coursetypes) { + $output .= ''. + (' 'x2); + } + $output .= '
'. + '
'.&mt('Creating courses').''. + ''.&mt('Course created (if absent) on Instructor access').': '. + ''.(' 'x2). + ''. + '
'. + '
'.&mt('Roles which may self-enroll').''; + foreach my $lticrsrole (@lticourseroles) { + $output .= '  '; + } + $output .= '
'. + '
'.&mt('Course options').''. + '
'.&mt('Assign users to sections').': '. + ''.(' 'x2). + '
'. + '
'. + ''.&mt('From').':'.(' 'x2). + '
'. + ''. + '
'; + my ($pb1p1chk,$pb1p0chk,$onclickpb); + foreach my $extra ('roster','passback') { + my $checkedon = ''; + my $checkedoff = ' checked="checked"'; + if ($extra eq 'passback') { + $pb1p1chk = ' checked="checked"'; + $pb1p0chk = ''; + $onclickpb = ' onclick="toggleLTI(this.form,'."'passback','$num'".');"'; + } else { + $onclickpb = ''; + } + if (ref($current) eq 'HASH') { + if (($current->{$extra})) { + $checkedon = $checkedoff; + $checkedoff = ''; + if ($extra eq 'passback') { + $passbacksty = 'inline-block'; + } + if ($current->{'passbackformat'} eq '1.0') { + $pb1p0chk = ' checked="checked"'; + $pb1p1chk = ''; + } + } + } + $output .= $lt{$extra}.' '. + ''.(' 'x2). + '
'; + } + $output .= '
'. + ''.&mt('Grade format'). + ''.(' 'x2). + '
'. + '
'.&mt('Course defaults (Course Coordinator can override)').''. + '
'.$lt{'topmenu'}.': '. + ''.(' 'x2). + '
'. + '
'. + '
'.$lt{'inlinemenu'}.': '. + ''.(' 'x2). + '
'; + $output .='
'. + '
'. + ''.&mt('Menu items').': '; + foreach my $type ('fullname','coursetitle','role','logout','grades') { + $output .= ''. + (' 'x2); + } + $output .= '
'; +# '
'.&mt('Assigning author roles').''; +# +# $output .= '
'. +# '
'.&mt('Assigning domain roles').''; + return $output; +} + +sub ltimenu_titles { + return &Apache::lonlocal::texthash( + fullname => 'Full name', + coursetitle => 'Course title', + role => 'Role', + logout => 'Logout', + grades => 'Grades', + ); +} + +sub print_coursedefaults { + my ($position,$dom,$settings,$rowtotal) = @_; + my ($css_class,$datatable,%checkedon,%checkedoff,%defaultchecked,@toggles); + my $itemcount = 1; + my %choices = &Apache::lonlocal::texthash ( + canuse_pdfforms => 'Course/Community users can create/upload PDF forms', + uploadquota => 'Default quota for files uploaded directly to course/community using Course Editor (MB)', + anonsurvey_threshold => 'Responder count needed before showing submissions for anonymous surveys', + coursecredits => 'Credits can be specified for courses', + uselcmath => 'Math preview uses LON-CAPA previewer (javascript) in place of DragMath (Java)', + usejsme => 'Molecule editor uses JSME (HTML5) in place of JME (Java)', + texengine => 'Default method to display mathematics', + postsubmit => 'Disable submit button/keypress following student submission', + canclone => "People who may clone a course (besides course's owner and coordinators)", + mysqltables => 'Lifetime (s) of "Temporary" MySQL tables (student performance data) on homeserver', + ); + my %staticdefaults = ( + anonsurvey_threshold => 10, + uploadquota => 500, + postsubmit => 60, + mysqltables => 172800, + ); + if ($position eq 'top') { + %defaultchecked = ( + 'canuse_pdfforms' => 'off', + 'uselcmath' => 'on', + 'usejsme' => 'on', + 'canclone' => 'none', + ); + @toggles = ('canuse_pdfforms','uselcmath','usejsme'); + my $deftex = $Apache::lonnet::deftex; + if (ref($settings) eq 'HASH') { + if ($settings->{'texengine'}) { + if ($settings->{'texengine'} =~ /^(MathJax|mimetex|tth)$/) { + $deftex = $settings->{'texengine'}; + } + } + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $mathdisp = ''. + ''.$choices{'texengine'}. + ''. + ''."\n"; + $itemcount ++; + ($datatable,$itemcount) = &radiobutton_prefs($settings,\@toggles,\%defaultchecked, + \%choices,$itemcount); + $datatable = $mathdisp.$datatable; + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= + ''. + ''.$choices{'canclone'}. + ''; + my $currcanclone = 'none'; + my $onclick; + my @cloneoptions = ('none','domain'); + my %clonetitles = ( + none => 'No additional course requesters', + domain => "Any course requester in course's domain", + instcode => 'Course requests for official courses ...', + ); + my (%codedefaults,@code_order,@posscodes); + if (&Apache::lonnet::auto_instcode_defaults($dom,\%codedefaults, + \@code_order) eq 'ok') { + if (@code_order > 0) { + push(@cloneoptions,'instcode'); + $onclick = ' onclick="toggleDisplay(this.form,'."'cloneinstcode'".');"'; + } + } + if (ref($settings) eq 'HASH') { + if ($settings->{'canclone'}) { + if (ref($settings->{'canclone'}) eq 'HASH') { + if (ref($settings->{'canclone'}{'instcode'}) eq 'ARRAY') { + if (@code_order > 0) { + $currcanclone = 'instcode'; + @posscodes = @{$settings->{'canclone'}{'instcode'}}; + } + } + } elsif ($settings->{'canclone'} eq 'domain') { + $currcanclone = $settings->{'canclone'}; + } + } + } + foreach my $option (@cloneoptions) { + my ($checked,$additional); + if ($currcanclone eq $option) { + $checked = ' checked="checked"'; + } + if ($option eq 'instcode') { + if (@code_order) { + my $show = 'none'; + if ($checked) { + $show = 'block'; + } + $additional = '
'. + &mt('Institutional codes for new and cloned course have identical:'). + '
'; + foreach my $item (@code_order) { + my $codechk; + if ($checked) { + if (grep(/^\Q$item\E$/,@posscodes)) { + $codechk = ' checked="checked"'; + } + } + $additional .= ''; + } + $additional .= (' 'x2).'('.&mt('check as many as needed').')
'; + } + } + $datatable .= + ' '.$additional.'
'; + } + $datatable .= ''. + ''; + $itemcount ++; + } else { + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + my ($currdefresponder,%defcredits,%curruploadquota,%deftimeout,%currmysql); + my $currusecredits = 0; + my $postsubmitclient = 1; + my @types = ('official','unofficial','community','textbook','placement'); + if (ref($settings) eq 'HASH') { + $currdefresponder = $settings->{'anonsurvey_threshold'}; + if (ref($settings->{'uploadquota'}) eq 'HASH') { + foreach my $type (keys(%{$settings->{'uploadquota'}})) { + $curruploadquota{$type} = $settings->{'uploadquota'}{$type}; + } + } + if (ref($settings->{'coursecredits'}) eq 'HASH') { + foreach my $type (@types) { + next if ($type eq 'community'); + $defcredits{$type} = $settings->{'coursecredits'}->{$type}; + if ($defcredits{$type} ne '') { + $currusecredits = 1; + } + } + } + if (ref($settings->{'postsubmit'}) eq 'HASH') { + if ($settings->{'postsubmit'}->{'client'} eq 'off') { + $postsubmitclient = 0; + foreach my $type (@types) { + $deftimeout{$type} = $staticdefaults{'postsubmit'}; + } + } else { + foreach my $type (@types) { + if (ref($settings->{'postsubmit'}->{'timeout'}) eq 'HASH') { + if ($settings->{'postsubmit'}->{'timeout'}->{$type} =~ /^\d+$/) { + $deftimeout{$type} = $settings->{'postsubmit'}->{'timeout'}->{$type}; + } else { + $deftimeout{$type} = $staticdefaults{'postsubmit'}; + } + } else { + $deftimeout{$type} = $staticdefaults{'postsubmit'}; + } + } + } + } else { + foreach my $type (@types) { + $deftimeout{$type} = $staticdefaults{'postsubmit'}; + } + } + if (ref($settings->{'mysqltables'}) eq 'HASH') { + foreach my $type (keys(%{$settings->{'mysqltables'}})) { + $currmysql{$type} = $settings->{'mysqltables'}{$type}; + } + } else { + foreach my $type (@types) { + $currmysql{$type} = $staticdefaults{'mysqltables'}; + } + } + } else { + foreach my $type (@types) { + $deftimeout{$type} = $staticdefaults{'postsubmit'}; + } + } + if (!$currdefresponder) { + $currdefresponder = $staticdefaults{'anonsurvey_threshold'}; + } elsif ($currdefresponder < 1) { + $currdefresponder = 1; + } + foreach my $type (@types) { + if ($curruploadquota{$type} eq '') { + $curruploadquota{$type} = $staticdefaults{'uploadquota'}; + } + } + $datatable .= + ''. + $choices{'anonsurvey_threshold'}. + ''. + ''. + ''. + ''."\n"; + $itemcount ++; + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable .= ''. + $choices{'uploadquota'}. + ''. + ''. + ''; + foreach my $type (@types) { + $datatable .= ''; + } + $datatable .= '
'.&mt($type).'
'. + '
'."\n"; + $itemcount ++; + my $onclick = "toggleDisplay(this.form,'credits');"; + my $display = 'none'; + if ($currusecredits) { + $display = 'block'; + } + my $additional = '
'. + ''.&mt('Default credits').'
'; + foreach my $type (@types) { + next if ($type eq 'community'); + $additional .= ''; + } + $additional .= '
'.&mt($type).'
'. + '
'."\n"; + %defaultchecked = ('coursecredits' => 'off'); + @toggles = ('coursecredits'); + my $current = { + 'coursecredits' => $currusecredits, + }; + (my $table,$itemcount) = + &radiobutton_prefs($current,\@toggles,\%defaultchecked, + \%choices,$itemcount,$onclick,$additional,'left'); + $datatable .= $table; + $onclick = "toggleDisplay(this.form,'studentsubmission');"; + my $display = 'none'; + if ($postsubmitclient) { + $display = 'block'; + } + $additional = '
'. + &mt('Number of seconds submit is disabled').'
'. + ''.&mt('Enter 0 to remain disabled until page reload.').'
'. + ''; + foreach my $type (@types) { + $additional .= ''; + } + $additional .= '
'.&mt($type).'
'. + '
'."\n"; + %defaultchecked = ('postsubmit' => 'on'); + @toggles = ('postsubmit'); + $current = { + 'postsubmit' => $postsubmitclient, + }; + ($table,$itemcount) = + &radiobutton_prefs($current,\@toggles,\%defaultchecked, + \%choices,$itemcount,$onclick,$additional,'left'); + $datatable .= $table; + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable .= ''. + $choices{'mysqltables'}. + ''. + ''. + ''; + foreach my $type (@types) { + $datatable .= ''; + } + $datatable .= '
'.&mt($type).'
'. + '
'."\n"; + $itemcount ++; + + } + $$rowtotal += $itemcount; + return $datatable; +} + +sub print_selfenrollment { + my ($position,$dom,$settings,$rowtotal) = @_; + my ($css_class,$datatable); + my $itemcount = 1; + my @types = ('official','unofficial','community','textbook','placement'); + if (($position eq 'top') || ($position eq 'middle')) { + my ($rowsref,$titlesref) = &Apache::lonuserutils::get_selfenroll_titles(); + my %descs = &Apache::lonuserutils::selfenroll_default_descs(); + my @rows; + my $key; + if ($position eq 'top') { + $key = 'admin'; + if (ref($rowsref) eq 'ARRAY') { + @rows = @{$rowsref}; + } + } elsif ($position eq 'middle') { + $key = 'default'; + @rows = ('types','registered','approval','limit'); + } + foreach my $row (@rows) { + if (defined($titlesref->{$row})) { + $itemcount ++; + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= ''. + ''.$titlesref->{$row}.''. + ''. + ''; + my (%current,%currentcap); + if (ref($settings) eq 'HASH') { + if (ref($settings->{$key}) eq 'HASH') { + foreach my $type (@types) { + if (ref($settings->{$key}->{$type}) eq 'HASH') { + $current{$type} = $settings->{$key}->{$type}->{$row}; + } + if (($row eq 'limit') && ($key eq 'default')) { + if (ref($settings->{$key}->{$type}) eq 'HASH') { + $currentcap{$type} = $settings->{$key}->{$type}->{'cap'}; + } + } + } + } + } + my %roles = ( + '0' => &Apache::lonnet::plaintext('dc'), + ); + + foreach my $type (@types) { + unless (($row eq 'registered') && ($key eq 'default')) { + $datatable .= ''; + } + } + unless (($row eq 'registered') && ($key eq 'default')) { + $datatable .= ''; + } + foreach my $type (@types) { + if ($type eq 'community') { + $roles{'1'} = &mt('Community personnel'); + } else { + $roles{'1'} = &mt('Course personnel'); + } + $datatable .= ''; + } + $datatable .= ''; + } + $datatable .= '
'.&mt($type).'
'; + if ($position eq 'top') { + my %checked; + if ($current{$type} eq '0') { + $checked{'0'} = ' checked="checked"'; + } else { + $checked{'1'} = ' checked="checked"'; + } + foreach my $role ('1','0') { + $datatable .= ' '; + } + } else { + if ($row eq 'types') { + my %checked; + if ($current{$type} =~ /^(all|dom)$/) { + $checked{$1} = ' checked="checked"'; + } else { + $checked{''} = ' checked="checked"'; + } + foreach my $val ('','dom','all') { + $datatable .= ' '; + } + } elsif ($row eq 'registered') { + my %checked; + if ($current{$type} eq '1') { + $checked{'1'} = ' checked="checked"'; + } else { + $checked{'0'} = ' checked="checked"'; + } + foreach my $val ('0','1') { + $datatable .= ' '; + } + } elsif ($row eq 'approval') { + my %checked; + if ($current{$type} =~ /^([12])$/) { + $checked{$1} = ' checked="checked"'; + } else { + $checked{'0'} = ' checked="checked"'; + } + for my $val (0..2) { + $datatable .= ' '; + } + } elsif ($row eq 'limit') { + my %checked; + if ($current{$type} =~ /^(allstudents|selfenrolled)$/) { + $checked{$1} = ' checked="checked"'; + } else { + $checked{'none'} = ' checked="checked"'; + } + my $cap; + if ($currentcap{$type} =~ /^\d+$/) { + $cap = $currentcap{$type}; + } + foreach my $val ('none','allstudents','selfenrolled') { + $datatable .= ' '; + } + $datatable .= '
'. + ''.&mt('Maximum allowed: '). + ''. + ''; + } + } + $datatable .= '
'; + } + } elsif ($position eq 'bottom') { + $datatable .= &print_validation_rows('selfenroll',$dom,$settings,\$itemcount); + } + $$rowtotal += $itemcount; + return $datatable; +} + +sub print_validation_rows { + my ($caller,$dom,$settings,$rowtotal) = @_; + my ($itemsref,$namesref,$fieldsref); + if ($caller eq 'selfenroll') { + ($itemsref,$namesref,$fieldsref) = &Apache::lonuserutils::selfenroll_validation_types(); + } elsif ($caller eq 'requestcourses') { + ($itemsref,$namesref,$fieldsref) = &Apache::loncoursequeueadmin::requestcourses_validation_types(); + } + my %currvalidation; + if (ref($settings) eq 'HASH') { + if (ref($settings->{'validation'}) eq 'HASH') { + %currvalidation = %{$settings->{'validation'}}; + } + } + my $datatable; + my $itemcount = 0; + foreach my $item (@{$itemsref}) { + my $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable .= ''. + $namesref->{$item}. + ''. + ''; + if (($item eq 'url') || ($item eq 'button')) { + $datatable .= ''. + ''; + } elsif ($item eq 'fields') { + my @currfields; + if (ref($currvalidation{$item}) eq 'ARRAY') { + @currfields = @{$currvalidation{$item}}; + } + foreach my $field (@{$fieldsref}) { + my $check = ''; + if (grep(/^\Q$field\E$/,@currfields)) { + $check = ' checked="checked"'; + } + $datatable .= ' '; + } + } elsif ($item eq 'markup') { + $datatable .= ''; + } + $datatable .= ''."\n"; + if (ref($rowtotal)) { + $itemcount ++; + } + } + if ($caller eq 'requestcourses') { + my %currhash; + if (ref($settings) eq 'HASH') { + if (ref($settings->{'validation'}) eq 'HASH') { + if ($settings->{'validation'}{'dc'} ne '') { + $currhash{$settings->{'validation'}{'dc'}} = 1; + } + } + } + my $numinrow = 2; + my ($numdc,$dctable,$rows) = &active_dc_picker($dom,$numinrow,'radio', + 'validationdc',%currhash); + my $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable .= ''; + if ($numdc > 1) { + $datatable .= &mt('Course creation processed as: (choose Dom. Coord.)'); + } else { + $datatable .= &mt('Course creation processed as: '); + } + $datatable .= ''.$dctable.''; + $itemcount ++; + } + if (ref($rowtotal)) { + $$rowtotal += $itemcount; + } + return $datatable; +} + +sub print_usersessions { + my ($position,$dom,$settings,$rowtotal) = @_; + my ($css_class,$datatable,$itemcount,%checked,%choices); + my (%by_ip,%by_location,@intdoms,@instdoms); + &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms); + + my @alldoms = &Apache::lonnet::all_domains(); + my %serverhomes = %Apache::lonnet::serverhomeIDs; + my %servers = &Apache::lonnet::internet_dom_servers($dom); + my %altids = &id_for_thisdom(%servers); + if ($position eq 'top') { + if (keys(%serverhomes) > 1) { + my %spareid = ¤t_offloads_to($dom,$settings,\%servers); + my $curroffloadnow; + if (ref($settings) eq 'HASH') { + if (ref($settings->{'offloadnow'}) eq 'HASH') { + $curroffloadnow = $settings->{'offloadnow'}; + } + } + $datatable .= &spares_row($dom,\%servers,\%spareid,\%serverhomes,\%altids,$curroffloadnow,$rowtotal); + } else { + $datatable .= ''. + &mt('Nothing to set here, as the cluster to which this domain belongs only contains one server.'). + ''; + } + } else { + my %titles = &usersession_titles(); + my ($prefix,@types); + if ($position eq 'bottom') { + $prefix = 'remote'; + @types = ('version','excludedomain','includedomain'); + } else { + $prefix = 'hosted'; + @types = ('excludedomain','includedomain'); + } + ($datatable,$itemcount) = &rules_by_location($settings,$prefix,\%by_location,\%by_ip,\@types,\%titles); + } + $$rowtotal += $itemcount; + return $datatable; +} + +sub rules_by_location { + my ($settings,$prefix,$by_location,$by_ip,$types,$titles) = @_; + my ($datatable,$itemcount,$css_class); + if (keys(%{$by_location}) == 0) { + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable = ''. + &mt('Nothing to set here, as the cluster to which this domain belongs only contains one institution.'). + ''; + $itemcount = 1; + } else { + $itemcount = 0; + my $numinrow = 5; + my (%current,%checkedon,%checkedoff); + my @locations = sort(keys(%{$by_location})); + foreach my $type (@{$types}) { + $checkedon{$type} = ''; + $checkedoff{$type} = ' checked="checked"'; + } + if (ref($settings) eq 'HASH') { + if (ref($settings->{$prefix}) eq 'HASH') { + foreach my $key (keys(%{$settings->{$prefix}})) { + $current{$key} = $settings->{$prefix}{$key}; + if ($key eq 'version') { + if ($current{$key} ne '') { + $checkedon{$key} = ' checked="checked"'; + $checkedoff{$key} = ''; + } + } elsif (ref($current{$key}) eq 'ARRAY') { + $checkedon{$key} = ' checked="checked"'; + $checkedoff{$key} = ''; + } + } + } + } + foreach my $type (@{$types}) { + next if ($type ne 'version' && !@locations); + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable .= ' + '.$titles->{$type}.'
+   +   + '; + if ($type eq 'version') { + my @lcversions = &Apache::lonnet::all_loncaparevs(); + my $selector = ' '; + $datatable .= &mt('remote server must be version: [_1] or later',$selector); + } else { + $datatable.= '
'.(' 'x2). + ''. + "\n". + '
'; + my $rem; + for (my $i=0; $i<@locations; $i++) { + my ($showloc,$value,$checkedtype); + if (ref($by_location->{$locations[$i]}) eq 'ARRAY') { + my $ip = $by_location->{$locations[$i]}->[0]; + if (ref($by_ip->{$ip}) eq 'ARRAY') { + $value = join(':',@{$by_ip->{$ip}}); + $showloc = join(', ',@{$by_ip->{$ip}}); + if (ref($current{$type}) eq 'ARRAY') { + foreach my $loc (@{$by_ip->{$ip}}) { + if (grep(/^\Q$loc\E$/,@{$current{$type}})) { + $checkedtype = ' checked="checked"'; + last; + } + } + } + } + } + $rem = $i%($numinrow); + if ($rem == 0) { + if ($i > 0) { + $datatable .= ''; + } + $datatable .= ''; + } + $datatable .= ''; + } + $rem = @locations%($numinrow); + my $colsleft = $numinrow - $rem; + if ($colsleft > 1 ) { + $datatable .= ''; + } elsif ($colsleft == 1) { + $datatable .= ''; + } + $datatable .= '
'. + ''. + '  
'; + } + $datatable .= ''; + $itemcount ++; + } + } + return ($datatable,$itemcount); +} + +sub print_ssl { + my ($position,$dom,$settings,$rowtotal) = @_; + my ($css_class,$datatable); + my $itemcount = 1; + if ($position eq 'top') { + my $primary_id = &Apache::lonnet::domain($dom,'primary'); + my $intdom = &Apache::lonnet::internet_dom($primary_id); + my $same_institution; + if ($intdom ne '') { + my $internet_names = &Apache::lonnet::get_internet_names($Apache::lonnet::perlvar{'lonHostID'}); + if (ref($internet_names) eq 'ARRAY') { + if (grep(/^\Q$intdom\E$/,@{$internet_names})) { + $same_institution = 1; + } + } + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable = ''; + if ($same_institution) { + my %domservers = &Apache::lonnet::get_servers($dom); + $datatable .= &LONCAPA::SSL::print_certstatus(\%domservers,'web','domprefs'); + } else { + $datatable .= &mt("You need to be logged into one of your own domain's servers to display information about the status of LON-CAPA SSL certificates."); + } + $datatable .= ''; + $itemcount ++; + } else { + my %titles = &ssl_titles(); + my (%by_ip,%by_location,@intdoms,@instdoms); + &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms); + my @alldoms = &Apache::lonnet::all_domains(); + my %serverhomes = %Apache::lonnet::serverhomeIDs; + my @domservers = &Apache::lonnet::get_servers($dom); + my %servers = &Apache::lonnet::internet_dom_servers($dom); + my %altids = &id_for_thisdom(%servers); + if (($position eq 'connto') || ($position eq 'connfrom')) { + my $legacy; + unless (ref($settings) eq 'HASH') { + my $name; + if ($position eq 'connto') { + $name = 'loncAllowInsecure'; + } else { + $name = 'londAllowInsecure'; + } + my $primarylibserv = &Apache::lonnet::domain($dom,'primary'); + my @ids=&Apache::lonnet::current_machine_ids(); + if (($primarylibserv ne '') && (!grep(/^\Q$primarylibserv\E$/,@ids))) { + my %what = ( + $name => 1, + ); + my ($result,$returnhash) = + &Apache::lonnet::get_remote_globals($primarylibserv,\%what); + if ($result eq 'ok') { + if (ref($returnhash) eq 'HASH') { + $legacy = $returnhash->{$name}; + } + } + } else { + $legacy = $Apache::lonnet::perlvar{$name}; + } + } + foreach my $type ('dom','intdom','other') { + my %checked; + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= ''.$titles{$type}.''. + ''; + my $skip; + if ($type eq 'dom') { + unless (keys(%servers) > 1) { + $datatable .= &mt('Nothing to set here, as there are no other servers/VMs'); + $skip = 1; + } + } + if ($type eq 'intdom') { + unless (@instdoms > 1) { + $datatable .= &mt('Nothing to set here, as there are no other domains for this institution'); + $skip = 1; + } + } elsif ($type eq 'other') { + if (keys(%by_location) == 0) { + $datatable .= &mt('Nothing to set here, as there are no other institutions'); + $skip = 1; + } + } + unless ($skip) { + $checked{'yes'} = ' checked="checked"'; + if (ref($settings) eq 'HASH') { + if (ref($settings->{$position}) eq 'HASH') { + if ($settings->{$position}->{$type} =~ /^(no|req)$/) { + $checked{$1} = $checked{'yes'}; + delete($checked{'yes'}); + } + } + } else { + if ($legacy == 0) { + $checked{'req'} = $checked{'yes'}; + delete($checked{'yes'}); + } + } + foreach my $option ('no','yes','req') { + $datatable .= ''.(' 'x2); + } + } + $datatable .= ''; + $itemcount ++; + } + } else { + my $prefix = 'replication'; + my @types = ('certreq','nocertreq'); + if (keys(%by_location) == 0) { + $datatable .= ''. + &mt('Nothing to set here, as there are no other institutions'). + ''; + $itemcount ++; + } else { + ($datatable,$itemcount) = + &rules_by_location($settings,$prefix,\%by_location,\%by_ip,\@types,\%titles); + } + } + } + $$rowtotal += $itemcount; + return $datatable; +} + +sub ssl_titles { + return &Apache::lonlocal::texthash ( + dom => 'LON-CAPA servers/VMs from same domain', + intdom => 'LON-CAPA servers/VMs from same "internet" domain', + other => 'External LON-CAPA servers/VMs', + connto => 'Connections to other servers', + connfrom => 'Connections from other servers', + replication => 'Replicating content to other institutions', + certreq => 'Client certificate required, but specific domains exempt', + nocertreq => 'No client certificate required, except for specific domains', + no => 'SSL not used', + yes => 'SSL Optional (used if available)', + req => 'SSL Required', + ); +} + +sub print_trust { + my ($prefix,$dom,$settings,$rowtotal) = @_; + my ($css_class,$datatable,%checked,%choices); + my (%by_ip,%by_location,@intdoms,@instdoms); + &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms); + my $itemcount = 1; + my %titles = &trust_titles(); + my @types = ('exc','inc'); + if ($prefix eq 'top') { + $prefix = 'content'; + } elsif ($prefix eq 'bottom') { + $prefix = 'msg'; + } + ($datatable,$itemcount) = &rules_by_location($settings,$prefix,\%by_location,\%by_ip,\@types,\%titles); + $$rowtotal += $itemcount; + return $datatable; +} + +sub trust_titles { + return &Apache::lonlocal::texthash( + content => "Access to this domain's content by others", + shared => "Access to other domain's content by this domain", + enroll => "Enrollment in this domain's courses by others", + othcoau => "Co-author roles in this domain for others", + coaurem => "Co-author roles for this domain's users elsewhere", + domroles => "Domain roles in this domain assignable to others", + catalog => "Course Catalog for this domain displayed elsewhere", + reqcrs => "Requests for creation of courses in this domain by others", + msg => "Users in other domains can send messages to this domain", + exc => "Allow all, but exclude specific domains", + inc => "Deny all, but include specific domains", + ); +} + +sub build_location_hashes { + my ($intdoms,$by_ip,$by_location,$instdoms) = @_; + return unless((ref($intdoms) eq 'ARRAY') && (ref($by_ip) eq 'HASH') && + (ref($by_location) eq 'HASH') && (ref($instdoms) eq 'ARRAY')); + my %iphost = &Apache::lonnet::get_iphost(); + my $primary_id = &Apache::lonnet::domain($env{'request.role.domain'},'primary'); + my $primary_ip = &Apache::lonnet::get_host_ip($primary_id); + if (ref($iphost{$primary_ip}) eq 'ARRAY') { + foreach my $id (@{$iphost{$primary_ip}}) { + my $intdom = &Apache::lonnet::internet_dom($id); + unless(grep(/^\Q$intdom\E$/,@{$intdoms})) { + push(@{$intdoms},$intdom); + } + } + } + foreach my $ip (keys(%iphost)) { + if (ref($iphost{$ip}) eq 'ARRAY') { + foreach my $id (@{$iphost{$ip}}) { + my $location = &Apache::lonnet::internet_dom($id); + if ($location) { + if (grep(/^\Q$location\E$/,@{$intdoms})) { + my $dom = &Apache::lonnet::host_domain($id); + unless (grep(/^\Q$dom\E/,@{$instdoms})) { + push(@{$instdoms},$dom); + } + next; + } + if (ref($by_ip->{$ip}) eq 'ARRAY') { + unless(grep(/^\Q$location\E$/,@{$by_ip->{$ip}})) { + push(@{$by_ip->{$ip}},$location); + } + } else { + $by_ip->{$ip} = [$location]; + } + } + } + } + } + foreach my $ip (sort(keys(%{$by_ip}))) { + if (ref($by_ip->{$ip}) eq 'ARRAY') { + @{$by_ip->{$ip}} = sort(@{$by_ip->{$ip}}); + my $first = $by_ip->{$ip}->[0]; + if (ref($by_location->{$first}) eq 'ARRAY') { + unless (grep(/^\Q$ip\E$/,@{$by_location->{$first}})) { + push(@{$by_location->{$first}},$ip); + } + } else { + $by_location->{$first} = [$ip]; + } + } + } + return; +} + +sub current_offloads_to { + my ($dom,$settings,$servers) = @_; + my (%spareid,%otherdomconfigs); + if (ref($servers) eq 'HASH') { + foreach my $lonhost (sort(keys(%{$servers}))) { + my $gotspares; + if (ref($settings) eq 'HASH') { + if (ref($settings->{'spares'}) eq 'HASH') { + if (ref($settings->{'spares'}{$lonhost}) eq 'HASH') { + $spareid{$lonhost}{'primary'} = $settings->{'spares'}{$lonhost}{'primary'}; + $spareid{$lonhost}{'default'} = $settings->{'spares'}{$lonhost}{'default'}; + $gotspares = 1; + } + } + } + unless ($gotspares) { + my $gotspares; + my $serverhomeID = + &Apache::lonnet::get_server_homeID($servers->{$lonhost}); + my $serverhomedom = + &Apache::lonnet::host_domain($serverhomeID); + if ($serverhomedom ne $dom) { + if (ref($otherdomconfigs{$serverhomedom} eq 'HASH')) { + if (ref($otherdomconfigs{$serverhomedom}{'usersessions'}) eq 'HASH') { + if (ref($otherdomconfigs{$serverhomedom}{'usersessions'}{'spares'}) eq 'HASH') { + $spareid{$lonhost}{'primary'} = $otherdomconfigs{$serverhomedom}{'usersessions'}{'spares'}{'primary'}; + $spareid{$lonhost}{'default'} = $otherdomconfigs{$serverhomedom}{'usersessions'}{'spares'}{'default'}; + $gotspares = 1; + } + } + } else { + $otherdomconfigs{$serverhomedom} = + &Apache::lonnet::get_dom('configuration',['usersessions'],$serverhomedom); + if (ref($otherdomconfigs{$serverhomedom}) eq 'HASH') { + if (ref($otherdomconfigs{$serverhomedom}{'usersessions'}) eq 'HASH') { + if (ref($otherdomconfigs{$serverhomedom}{'usersessions'}{'spares'}) eq 'HASH') { + if (ref($otherdomconfigs{$serverhomedom}{'usersessions'}{'spares'}{$lonhost}) eq 'HASH') { + $spareid{$lonhost}{'primary'} = $otherdomconfigs{$serverhomedom}{'usersessions'}{'spares'}{'primary'}; + $spareid{$lonhost}{'default'} = $otherdomconfigs{$serverhomedom}{'usersessions'}{'spares'}{'default'}; + $gotspares = 1; + } + } + } + } + } + } + } + unless ($gotspares) { + if ($lonhost eq $Apache::lonnet::perlvar{'lonHostID'}) { + $spareid{$lonhost}{'primary'} = $Apache::lonnet::spareid{'primary'}; + $spareid{$lonhost}{'default'} = $Apache::lonnet::spareid{'default'}; + } else { + my $server_hostname = &Apache::lonnet::hostname($lonhost); + my $server_homeID = &Apache::lonnet::get_server_homeID($server_hostname); + if ($server_homeID eq $Apache::lonnet::perlvar{'lonHostID'}) { + $spareid{$lonhost}{'primary'} = $Apache::lonnet::spareid{'primary'}; + $spareid{$lonhost}{'default'} = $Apache::lonnet::spareid{'default'}; + } else { + my %what = ( + spareid => 1, + ); + my ($result,$returnhash) = + &Apache::lonnet::get_remote_globals($lonhost,\%what); + if ($result eq 'ok') { + if (ref($returnhash) eq 'HASH') { + if (ref($returnhash->{'spareid'}) eq 'HASH') { + $spareid{$lonhost}{'primary'} = $returnhash->{'spareid'}->{'primary'}; + $spareid{$lonhost}{'default'} = $returnhash->{'spareid'}->{'default'}; + } + } + } + } + } + } + } + } + return %spareid; +} + +sub spares_row { + my ($dom,$servers,$spareid,$serverhomes,$altids,$curroffloadnow,$rowtotal) = @_; + my $css_class; + my $numinrow = 4; + my $itemcount = 1; + my $datatable; + my %typetitles = &sparestype_titles(); + if ((ref($servers) eq 'HASH') && (ref($spareid) eq 'HASH') && (ref($altids) eq 'HASH')) { + foreach my $server (sort(keys(%{$servers}))) { + my $serverhome = &Apache::lonnet::get_server_homeID($servers->{$server}); + my ($othercontrol,$serverdom); + if ($serverhome ne $server) { + $serverdom = &Apache::lonnet::host_domain($serverhome); + $othercontrol = &mt('Session offloading controlled by domain: [_1]',''.$serverdom.''); + } else { + $serverdom = &Apache::lonnet::host_domain($server); + if ($serverdom ne $dom) { + $othercontrol = &mt('Session offloading controlled by domain: [_1]',''.$serverdom.''); + } + } + next unless (ref($spareid->{$server}) eq 'HASH'); + my $checkednow; + if (ref($curroffloadnow) eq 'HASH') { + if ($curroffloadnow->{$server}) { + $checkednow = ' checked="checked"'; + } + } + $css_class = $itemcount%2 ? ' class="LC_odd_row"' : ''; + $datatable .= ' + + '. + &mt('[_1] when busy, offloads to:' + ,''.$server.'').'
'. + ''."\n". + ''. + "\n"; + my (%current,%canselect); + my @choices = + &possible_newspares($server,$spareid->{$server},$serverhomes,$altids); + foreach my $type ('primary','default') { + if (ref($spareid->{$server}) eq 'HASH') { + if (ref($spareid->{$server}{$type}) eq 'ARRAY') { + my @spares = @{$spareid->{$server}{$type}}; + if (@spares > 0) { + if ($othercontrol) { + $current{$type} = join(', ',@spares); + } else { + $current{$type} .= ''; + my $numspares = scalar(@spares); + for (my $i=0; $i<@spares; $i++) { + my $rem = $i%($numinrow); + if ($rem == 0) { + if ($i > 0) { + $current{$type} .= ''; + } + $current{$type} .= ''; + } + $current{$type} .= ''."\n"; + } + my $rem = @spares%($numinrow); + my $colsleft = $numinrow - $rem; + if ($colsleft > 1 ) { + $current{$type} .= ''; + } elsif ($colsleft == 1) { + $current{$type} .= ''."\n"; + } + $current{$type} .= '
'. + '  
'; + } + } + } + if ($current{$type} eq '') { + $current{$type} = &mt('None specified'); + } + if ($othercontrol) { + if ($type eq 'primary') { + $canselect{$type} = $othercontrol; + } + } else { + $canselect{$type} = + &mt('Add new [_1]'.$type.'[_2]:','','').' '. + ''."\n"; + } + } else { + $current{$type} = &mt('Could not be determined'); + if ($type eq 'primary') { + $canselect{$type} = $othercontrol; + } + } + if ($type eq 'default') { + $datatable .= ''; + } + $datatable .= ''.$typetitles{$type}.''."\n". + ''.$current{$type}.''."\n". + ''.$canselect{$type}.''."\n"; + } + $itemcount ++; + } + } + $$rowtotal += $itemcount; + return $datatable; +} + +sub possible_newspares { + my ($server,$currspares,$serverhomes,$altids) = @_; + my $serverhostname = &Apache::lonnet::hostname($server); + my %excluded; + if ($serverhostname ne '') { + %excluded = ( + $serverhostname => 1, + ); + } + if (ref($currspares) eq 'HASH') { + foreach my $type (keys(%{$currspares})) { + if (ref($currspares->{$type}) eq 'ARRAY') { + if (@{$currspares->{$type}} > 0) { + foreach my $curr (@{$currspares->{$type}}) { + my $hostname = &Apache::lonnet::hostname($curr); + $excluded{$hostname} = 1; + } + } + } + } + } + my @choices; + if ((ref($serverhomes) eq 'HASH') && (ref($altids) eq 'HASH')) { + if (keys(%{$serverhomes}) > 1) { + foreach my $name (sort(keys(%{$serverhomes}))) { + unless ($excluded{$name}) { + if (exists($altids->{$serverhomes->{$name}})) { + push(@choices,$altids->{$serverhomes->{$name}}); + } else { + push(@choices,$serverhomes->{$name}); + } + } + } + } + } + return sort(@choices); +} + +sub print_loadbalancing { + my ($dom,$settings,$rowtotal) = @_; + my $primary_id = &Apache::lonnet::domain($dom,'primary'); + my $intdom = &Apache::lonnet::internet_dom($primary_id); + my $numinrow = 1; + my $datatable; + my %servers = &Apache::lonnet::internet_dom_servers($dom); + my (%currbalancer,%currtargets,%currrules,%existing,%currcookies); + if (ref($settings) eq 'HASH') { + %existing = %{$settings}; + } + if ((keys(%servers) > 1) || (keys(%existing) > 0)) { + &get_loadbalancers_config(\%servers,\%existing,\%currbalancer, + \%currtargets,\%currrules,\%currcookies); + } else { + return; + } + my ($othertitle,$usertypes,$types) = + &Apache::loncommon::sorted_inst_types($dom); + my $rownum = 8; + if (ref($types) eq 'ARRAY') { + $rownum += scalar(@{$types}); + } + my @css_class = ('LC_odd_row','LC_even_row'); + my $balnum = 0; + my $islast; + my (@toshow,$disabledtext); + if (keys(%currbalancer) > 0) { + @toshow = sort(keys(%currbalancer)); + if (scalar(@toshow) < scalar(keys(%servers)) + 1) { + push(@toshow,''); + } + } else { + @toshow = (''); + $disabledtext = &mt('No existing load balancer'); + } + foreach my $lonhost (@toshow) { + if ($balnum == scalar(@toshow)-1) { + $islast = 1; + } else { + $islast = 0; + } + my $cssidx = $balnum%2; + my $targets_div_style = 'display: none'; + my $disabled_div_style = 'display: block'; + my $homedom_div_style = 'display: none'; + $datatable .= ''. + ''. + '

'; + if ($lonhost eq '') { + $datatable .= ''; + if (keys(%currbalancer) > 0) { + $datatable .= &mt('Add balancer:'); + } else { + $datatable .= &mt('Enable balancer:'); + } + $datatable .= ' '. + ''."\n". + ' '."\n"; + } else { + $datatable .= ''.$lonhost.'
'. + ''. + ''; + $targets_div_style = 'display: block'; + $disabled_div_style = 'display: none'; + if ($dom eq &Apache::lonnet::host_domain($lonhost)) { + $homedom_div_style = 'display: block'; + } + } + $datatable .= '

'. + '
'.$disabledtext.'
'."\n". + '
'.&mt('Offloads to:').'
'; + my ($numspares,@spares) = &count_servers($lonhost,%servers); + my @sparestypes = ('primary','default'); + my %typetitles = &sparestype_titles(); + my %hostherechecked = ( + no => ' checked="checked"', + ); + my %balcookiechecked = ( + no => ' checked="checked"', + ); + foreach my $sparetype (@sparestypes) { + my $targettable; + for (my $i=0; $i<$numspares; $i++) { + my $checked; + if (ref($currtargets{$lonhost}) eq 'HASH') { + if (ref($currtargets{$lonhost}{$sparetype}) eq 'ARRAY') { + if (grep(/^\Q$spares[$i]\E$/,@{$currtargets{$lonhost}{$sparetype}})) { + $checked = ' checked="checked"'; + } + } + } + my ($chkboxval,$disabled); + if (($lonhost ne '') && (exists($servers{$lonhost}))) { + $chkboxval = $spares[$i]; + } + if (exists($currbalancer{$spares[$i]})) { + $disabled = ' disabled="disabled"'; + } + $targettable .= + ''; + my $rem = $i%($numinrow); + if ($rem == 0) { + if (($i > 0) && ($i < $numspares-1)) { + $targettable .= ''; + } + if ($i < $numspares-1) { + $targettable .= ''; + } + } + } + if ($targettable ne '') { + my $rem = $numspares%($numinrow); + my $colsleft = $numinrow - $rem; + if ($colsleft > 1 ) { + $targettable .= ''. + ' '; + } elsif ($colsleft == 1) { + $targettable .= ' '; + } + $datatable .= ''.$typetitles{$sparetype}.'
'. + ''.$targettable.'

'; + } + $hostherechecked{$sparetype} = ''; + if (ref($currtargets{$lonhost}) eq 'HASH') { + if (ref($currtargets{$lonhost}{$sparetype}) eq 'ARRAY') { + if (grep(/^\Q$lonhost\E$/,@{$currtargets{$lonhost}{$sparetype}})) { + $hostherechecked{$sparetype} = ' checked="checked"'; + $hostherechecked{'no'} = ''; + } + } + } + } + if ($currcookies{$lonhost}) { + %balcookiechecked = ( + yes => ' checked="checked"', + ); + } + $datatable .= &mt('Hosting on balancer itself').'
'. + '
'; + foreach my $sparetype (@sparestypes) { + $datatable .= '
'; + } + $datatable .= &mt('Use balancer cookie').'
'. + '
'. + '
'. + '
'. + &loadbalancing_rules($dom,$intdom,$currrules{$lonhost}, + $othertitle,$usertypes,$types,\%servers, + \%currbalancer,$lonhost, + $targets_div_style,$homedom_div_style, + $css_class[$cssidx],$balnum,$islast); + $$rowtotal += $rownum; + $balnum ++; + } + $datatable .= ''; + return $datatable; +} + +sub get_loadbalancers_config { + my ($servers,$existing,$currbalancer,$currtargets,$currrules,$currcookies) = @_; + return unless ((ref($servers) eq 'HASH') && + (ref($existing) eq 'HASH') && (ref($currbalancer) eq 'HASH') && + (ref($currtargets) eq 'HASH') && (ref($currrules) eq 'HASH') && + (ref($currcookies) eq 'HASH')); + if (keys(%{$existing}) > 0) { + my $oldlonhost; + foreach my $key (sort(keys(%{$existing}))) { + if ($key eq 'lonhost') { + $oldlonhost = $existing->{'lonhost'}; + $currbalancer->{$oldlonhost} = 1; + } elsif ($key eq 'targets') { + if ($oldlonhost) { + $currtargets->{$oldlonhost} = $existing->{'targets'}; + } + } elsif ($key eq 'rules') { + if ($oldlonhost) { + $currrules->{$oldlonhost} = $existing->{'rules'}; + } + } elsif (ref($existing->{$key}) eq 'HASH') { + $currbalancer->{$key} = 1; + $currtargets->{$key} = $existing->{$key}{'targets'}; + $currrules->{$key} = $existing->{$key}{'rules'}; + if ($existing->{$key}{'cookie'}) { + $currcookies->{$key} = 1; + } + } + } + } else { + my ($balancerref,$targetsref) = + &Apache::lonnet::get_lonbalancer_config($servers); + if ((ref($balancerref) eq 'HASH') && (ref($targetsref) eq 'HASH')) { + foreach my $server (sort(keys(%{$balancerref}))) { + $currbalancer->{$server} = 1; + $currtargets->{$server} = $targetsref->{$server}; + } + } + } + return; +} + +sub loadbalancing_rules { + my ($dom,$intdom,$currrules,$othertitle,$usertypes,$types,$servers, + $currbalancer,$lonhost,$targets_div_style,$homedom_div_style, + $css_class,$balnum,$islast) = @_; + my $output; + my $num = 0; + my ($alltypes,$othertypes,$titles) = + &loadbalancing_titles($dom,$intdom,$usertypes,$types); + if ((ref($alltypes) eq 'ARRAY') && (ref($titles) eq 'HASH')) { + foreach my $type (@{$alltypes}) { + $num ++; + my $current; + if (ref($currrules) eq 'HASH') { + $current = $currrules->{$type}; + } + if (($type eq '_LC_external') || ($type eq '_LC_internetdom')) { + if ($dom ne &Apache::lonnet::host_domain($lonhost)) { + $current = ''; + } + } + $output .= &loadbalance_rule_row($type,$titles->{$type},$current, + $servers,$currbalancer,$lonhost,$dom, + $targets_div_style,$homedom_div_style, + $css_class,$balnum,$num,$islast); + } + } + return $output; +} + +sub loadbalancing_titles { + my ($dom,$intdom,$usertypes,$types) = @_; + my %othertypes = ( + '_LC_adv' => &mt('Advanced users from [_1]',$dom), + '_LC_author' => &mt('Users from [_1] with author role',$dom), + '_LC_internetdom' => &mt('Users not from [_1], but from [_2]',$dom,$intdom), + '_LC_external' => &mt('Users not from [_1]',$intdom), + '_LC_ipchangesso' => &mt('SSO users from [_1], with IP mismatch',$dom), + '_LC_ipchange' => &mt('Non-SSO users with IP mismatch'), + ); + my @alltypes = ('_LC_adv','_LC_author','_LC_internetdom','_LC_external','_LC_ipchangesso','_LC_ipchange'); + my @available; + if (ref($types) eq 'ARRAY') { + @available = @{$types}; + } + unless (grep(/^default$/,@available)) { + push(@available,'default'); + } + unshift(@alltypes,@available); + my %titles; + foreach my $type (@alltypes) { + if ($type =~ /^_LC_/) { + $titles{$type} = $othertypes{$type}; + } elsif ($type eq 'default') { + $titles{$type} = &mt('All users from [_1]',$dom); + if (ref($types) eq 'ARRAY') { + if (@{$types} > 0) { + $titles{$type} = &mt('Other users from [_1]',$dom); + } + } + } elsif (ref($usertypes) eq 'HASH') { + $titles{$type} = $usertypes->{$type}; + } + } + return (\@alltypes,\%othertypes,\%titles); +} + +sub loadbalance_rule_row { + my ($type,$title,$current,$servers,$currbalancer,$lonhost,$dom, + $targets_div_style,$homedom_div_style,$css_class,$balnum,$num,$islast) = @_; + my @rulenames; + my %ruletitles = &offloadtype_text(); + if (($type eq '_LC_ipchangesso') || ($type eq '_LC_ipchange')) { + @rulenames = ('balancer','offloadedto','specific'); + } else { + @rulenames = ('default','homeserver'); + if ($type eq '_LC_external') { + push(@rulenames,'externalbalancer'); + } else { + push(@rulenames,'specific'); + } + push(@rulenames,'none'); + } + my $style = $targets_div_style; + if (($type eq '_LC_external') || ($type eq '_LC_internetdom')) { + $style = $homedom_div_style; + } + my $space; + if ($islast && $num == 1) { + $space = '
 
'; + } + my $output = + ''.$space. + '
'.$title.'
'."\n". + ''.$space. + '
'."\n"; + for (my $i=0; $i<@rulenames; $i++) { + my $rule = $rulenames[$i]; + my ($checked,$extra); + if ($rulenames[$i] eq 'default') { + $rule = ''; + } + if ($rulenames[$i] eq 'specific') { + if (ref($servers) eq 'HASH') { + my $default; + if (($current ne '') && (exists($servers->{$current}))) { + $checked = ' checked="checked"'; + } + unless ($checked) { + $default = ' selected="selected"'; + } + $extra = + ': '; + } + } elsif ($rule eq $current) { + $checked = ' checked="checked"'; + } + $output .= ''.$extra.'
'."\n"; + } + $output .= '
'."\n"; + return $output; +} + +sub offloadtype_text { + my %ruletitles = &Apache::lonlocal::texthash ( + 'default' => 'Offloads to default destinations', + 'homeserver' => "Offloads to user's home server", + 'externalbalancer' => "Offloads to Load Balancer in user's domain", + 'specific' => 'Offloads to specific server', + 'none' => 'No offload', + 'balancer' => 'Session hosted on Load Balancer, after re-authentication', + 'offloadedto' => 'Session hosted on offload server, after re-authentication', + 'particular' => 'Session hosted (after re-auth) on server:', + ); + return %ruletitles; +} + +sub sparestype_titles { + my %typestitles = &Apache::lonlocal::texthash ( + 'primary' => 'primary', + 'default' => 'default', + ); + return %typestitles; +} + sub contact_titles { my %titles = &Apache::lonlocal::texthash ( - 'supportemail' => 'Support E-mail address', - 'adminemail' => 'Default Server Admin E-mail address', - 'errormail' => 'Error reports to be e-mailed to', - 'packagesmail' => 'Package update alerts to be e-mailed to', - 'helpdeskmail' => 'Helpdesk requests to be e-mailed to' + 'supportemail' => 'Support E-mail address', + 'adminemail' => 'Default Server Admin E-mail address', + 'errormail' => 'Error reports to be e-mailed to', + 'packagesmail' => 'Package update alerts to be e-mailed to', + 'helpdeskmail' => "Helpdesk requests from all users in this domain", + 'otherdomsmail' => 'Helpdesk requests from users in other (unconfigured) domains', + 'lonstatusmail' => 'E-mail from nightly status check (warnings/errors)', + 'requestsmail' => 'E-mail from course requests requiring approval', + 'updatesmail' => 'E-mail from nightly check of LON-CAPA module integrity/updates', + 'idconflictsmail' => 'E-mail from bi-nightly check for multiple users sharing same student/employee ID', + 'errorthreshold' => 'Error/warning threshold for status e-mail', + 'errorsysmail' => 'Error threshold for e-mail to core group', + 'errorweights' => 'Weights used to compute error count', + 'errorexcluded' => 'Servers with unsent updates excluded from count', ); my %short_titles = &Apache::lonlocal::texthash ( adminemail => 'Admin E-mail address', @@ -1447,6 +6797,84 @@ sub contact_titles { return (\%titles,\%short_titles); } +sub helpform_fields { + my %titles = &Apache::lonlocal::texthash ( + 'username' => 'Name', + 'user' => 'Username/domain', + 'phone' => 'Phone', + 'cc' => 'Cc e-mail', + 'course' => 'Course Details', + 'section' => 'Sections', + 'screenshot' => 'File upload', + ); + my @fields = ('username','phone','user','course','section','cc','screenshot'); + my %possoptions = ( + username => ['yes','no','req'], + phone => ['yes','no','req'], + user => ['yes','no'], + cc => ['yes','no'], + course => ['yes','no'], + section => ['yes','no'], + screenshot => ['yes','no'], + ); + my %fieldoptions = &Apache::lonlocal::texthash ( + 'yes' => 'Optional', + 'req' => 'Required', + 'no' => "Not shown", + ); + return (\@fields,\%titles,\%fieldoptions,\%possoptions); +} + +sub tool_titles { + my %titles = &Apache::lonlocal::texthash ( + aboutme => 'Personal web page', + blog => 'Blog', + webdav => 'WebDAV', + portfolio => 'Portfolio', + official => 'Official courses (with institutional codes)', + unofficial => 'Unofficial courses', + community => 'Communities', + textbook => 'Textbook courses', + placement => 'Placement tests', + ); + return %titles; +} + +sub courserequest_titles { + my %titles = &Apache::lonlocal::texthash ( + official => 'Official', + unofficial => 'Unofficial', + community => 'Communities', + textbook => 'Textbook', + placement => 'Placement tests', + lti => 'LTI Provider', + norequest => 'Not allowed', + approval => 'Approval by DC', + validate => 'With validation', + autolimit => 'Numerical limit', + unlimited => '(blank for unlimited)', + ); + return %titles; +} + +sub authorrequest_titles { + my %titles = &Apache::lonlocal::texthash ( + norequest => 'Not allowed', + approval => 'Approval by Dom. Coord.', + automatic => 'Automatic approval', + ); + return %titles; +} + +sub courserequest_conditions { + my %conditions = &Apache::lonlocal::texthash ( + approval => '(Processing of request subject to approval by Domain Coordinator).', + validate => '(Processing of request subject to institutional validation).', + ); + return %conditions; +} + + sub print_usercreation { my ($position,$dom,$settings,$rowtotal) = @_; my $numinrow = 4; @@ -1478,7 +6906,7 @@ sub print_usercreation { $rowcount ++; } } elsif ($position eq 'middle') { - my @creators = ('author','course'); + my @creators = ('author','course','requestcrs'); my ($rules,$ruleorder) = &Apache::lonnet::inst_userrules($dom,'username'); my %lt = &usercreation_types(); @@ -1510,7 +6938,7 @@ sub print_usercreation { } $datatable .= ''. ''.$lt{$item}. - ''; + ''; my @options = ('any'); if (ref($rules) eq 'HASH') { if (keys(%{$rules}) > 0) { @@ -1519,12 +6947,13 @@ sub print_usercreation { } push(@options,'none'); foreach my $option (@options) { + my $type = 'radio'; my $check = ' '; if ($checked{$item} eq $option) { $check = ' checked="checked" '; } $datatable .= '  '; } @@ -1532,7 +6961,7 @@ sub print_usercreation { } } else { my @contexts = ('author','course','domain'); - my @authtypes = ('int','krb4','krb5','loc'); + my @authtypes = ('int','krb4','krb5','loc','lti'); my %checked; if (ref($settings) eq 'HASH') { if (ref($settings->{'authtypes'}) eq 'HASH') { @@ -1581,19 +7010,611 @@ sub print_usercreation { return $datatable; } +sub print_selfcreation { + my ($position,$dom,$settings,$rowtotal) = @_; + my (@selfcreate,$createsettings,$processing,$emailoptions,$emailverified, + $emaildomain,$datatable); + if (ref($settings) eq 'HASH') { + if (ref($settings->{'cancreate'}) eq 'HASH') { + $createsettings = $settings->{'cancreate'}; + if (ref($createsettings) eq 'HASH') { + if (ref($createsettings->{'selfcreate'}) eq 'ARRAY') { + @selfcreate = @{$createsettings->{'selfcreate'}}; + } elsif ($createsettings->{'selfcreate'} ne '') { + if ($settings->{'cancreate'}{'selfcreate'} eq 'any') { + @selfcreate = ('email','login','sso'); + } elsif ($createsettings->{'selfcreate'} ne 'none') { + @selfcreate = ($createsettings->{'selfcreate'}); + } + } + if (ref($createsettings->{'selfcreateprocessing'}) eq 'HASH') { + $processing = $createsettings->{'selfcreateprocessing'}; + } + if (ref($createsettings->{'emailoptions'}) eq 'HASH') { + $emailoptions = $createsettings->{'emailoptions'}; + } + if (ref($createsettings->{'emailverified'}) eq 'HASH') { + $emailverified = $createsettings->{'emailverified'}; + } + if (ref($createsettings->{'emaildomain'}) eq 'HASH') { + $emaildomain = $createsettings->{'emaildomain'}; + } + } + } + } + my %radiohash; + my $numinrow = 4; + map { $radiohash{'cancreate_'.$_} = 1; } @selfcreate; + my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); + if ($position eq 'top') { + my %choices = &Apache::lonlocal::texthash ( + cancreate_login => 'Institutional Login', + cancreate_sso => 'Institutional Single Sign On', + ); + my @toggles = sort(keys(%choices)); + my %defaultchecked = ( + 'cancreate_login' => 'off', + 'cancreate_sso' => 'off', + ); + my ($onclick,$itemcount); + ($datatable,$itemcount) = &radiobutton_prefs(\%radiohash,\@toggles,\%defaultchecked, + \%choices,$itemcount,$onclick); + $$rowtotal += $itemcount; + + if (ref($usertypes) eq 'HASH') { + if (keys(%{$usertypes}) > 0) { + $datatable .= &insttypes_row($createsettings,$types,$usertypes, + $dom,$numinrow,$othertitle, + 'statustocreate',$rowtotal); + $$rowtotal ++; + } + } + my @fields = ('lastname','firstname','middlename','permanentemail','id','inststatus'); + my %fieldtitles = &Apache::loncommon::personal_data_fieldtitles(); + $fieldtitles{'inststatus'} = &mt('Institutional status'); + my $rem; + my $numperrow = 2; + my $css_class = $$rowtotal%2?' class="LC_odd_row"':''; + $datatable .= ''. + ''.&mt('Mapping of Shibboleth environment variable names to user data fields (SSO auth)').''. + ''."\n". + ''."\n"; + for (my $i=0; $i<@fields; $i++) { + $rem = $i%($numperrow); + if ($rem == 0) { + if ($i > 0) { + $datatable .= ''; + } + $datatable .= ''; + } + my $currval; + if (ref($createsettings) eq 'HASH') { + if (ref($createsettings->{'shibenv'}) eq 'HASH') { + $currval = $createsettings->{'shibenv'}{$fields[$i]}; + } + } + $datatable .= ''; + } + my $colsleft = $numperrow - $rem; + if ($colsleft > 1 ) { + $datatable .= ''; + } elsif ($colsleft == 1) { + $datatable .= ''; + } + $datatable .= '
'. + ''. + ' '. + $fieldtitles{$fields[$i]}.''. + '  
'; + $$rowtotal ++; + } elsif ($position eq 'middle') { + my %domconf = &Apache::lonnet::get_dom('configuration',['usermodification'],$dom); + my @posstypes; + if (ref($types) eq 'ARRAY') { + @posstypes = @{$types}; + } + unless (grep(/^default$/,@posstypes)) { + push(@posstypes,'default'); + } + my %usertypeshash; + if (ref($usertypes) eq 'HASH') { + %usertypeshash = %{$usertypes}; + } + $usertypeshash{'default'} = $othertitle; + foreach my $status (@posstypes) { + $datatable .= &modifiable_userdata_row('selfcreate',$status,$domconf{'usermodification'}, + $numinrow,$$rowtotal,\%usertypeshash); + $$rowtotal ++; + } + } else { + my %choices = &Apache::lonlocal::texthash ( + 'cancreate_email' => 'Non-institutional username (via e-mail verification)', + ); + my @toggles = sort(keys(%choices)); + my %defaultchecked = ( + 'cancreate_email' => 'off', + ); + my $customclass = 'LC_selfcreate_email'; + my $classprefix = 'LC_canmodify_emailusername_'; + my $optionsprefix = 'LC_options_emailusername_'; + my $display = 'none'; + my $rowstyle = 'display:none'; + if (grep(/^\Qemail\E$/,@selfcreate)) { + $display = 'block'; + $rowstyle = 'display:table-row'; + } + my $onclick = "toggleRows(this.form,'cancreate_email','selfassign','$customclass','$classprefix','$optionsprefix');"; + ($datatable,$$rowtotal) = &radiobutton_prefs(\%radiohash,\@toggles,\%defaultchecked, + \%choices,$$rowtotal,$onclick); + $datatable .= &print_requestmail($dom,'selfcreation',$createsettings,$rowtotal,$customclass, + $rowstyle); + $$rowtotal ++; + $datatable .= &captcha_choice('cancreate',$createsettings,$$rowtotal,$customclass, + $rowstyle); + $$rowtotal ++; + my (@ordered,@posstypes,%usertypeshash); + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom); + my ($emailrules,$emailruleorder) = + &Apache::lonnet::inst_userrules($dom,'email'); + my $primary_id = &Apache::lonnet::domain($dom,'primary'); + my $intdom = &Apache::lonnet::internet_dom($primary_id); + if (ref($types) eq 'ARRAY') { + @posstypes = @{$types}; + } + if (@posstypes) { + unless (grep(/^default$/,@posstypes)) { + push(@posstypes,'default'); + } + if (ref($usertypes) eq 'HASH') { + %usertypeshash = %{$usertypes}; + } + my $currassign; + if (ref($domdefaults{'inststatusguest'}) eq 'ARRAY') { + $currassign = { + selfassign => $domdefaults{'inststatusguest'}, + }; + @ordered = @{$domdefaults{'inststatusguest'}}; + } else { + $currassign = { selfassign => [] }; + } + my $onclicktypes = "toggleDataRow(this.form,'selfassign','$customclass','$optionsprefix',);". + "toggleDataRow(this.form,'selfassign','$customclass','$classprefix',1);"; + $datatable .= &insttypes_row($currassign,$types,$usertypes,$dom, + $numinrow,$othertitle,'selfassign', + $rowtotal,$onclicktypes,$customclass, + $rowstyle); + $$rowtotal ++; + $usertypeshash{'default'} = $othertitle; + foreach my $status (@posstypes) { + my $css_class; + if ($$rowtotal%2) { + $css_class = 'LC_odd_row '; + } + $css_class .= $customclass; + my $rowid = $optionsprefix.$status; + my $hidden = 1; + my $currstyle = 'display:none'; + if (grep(/^\Q$status\E$/,@ordered)) { + $currstyle = $rowstyle; + $hidden = 0; + } + $datatable .= &noninst_users($processing,$emailverified,$emailoptions,$emaildomain, + $emailrules,$emailruleorder,$settings,$status,$rowid, + $usertypeshash{$status},$css_class,$currstyle,$intdom); + unless ($hidden) { + $$rowtotal ++; + } + } + } else { + my $css_class; + if ($$rowtotal%2) { + $css_class = 'LC_odd_row '; + } + $css_class .= $customclass; + $usertypeshash{'default'} = $othertitle; + $datatable .= &noninst_users($processing,$emailverified,$emailoptions,$emaildomain, + $emailrules,$emailruleorder,$settings,'default','', + $othertitle,$css_class,$rowstyle,$intdom); + $$rowtotal ++; + } + my ($infofields,$infotitles) = &Apache::loncommon::emailusername_info(); + $numinrow = 1; + if (@posstypes) { + foreach my $status (@posstypes) { + my $rowid = $classprefix.$status; + my $datarowstyle = 'display:none'; + if (grep(/^\Q$status\E$/,@ordered)) { + $datarowstyle = $rowstyle; + } + $datatable .= &modifiable_userdata_row('cancreate','emailusername_'.$status,$settings, + $numinrow,$$rowtotal,\%usertypeshash,$infofields, + $infotitles,$rowid,$customclass,$datarowstyle); + unless ($datarowstyle eq 'display:none') { + $$rowtotal ++; + } + } + } else { + $datatable .= &modifiable_userdata_row('cancreate','emailusername_default',$settings, + $numinrow,$$rowtotal,\%usertypeshash,$infofields, + $infotitles,'',$customclass,$rowstyle); + } + } + return $datatable; +} + +sub selfcreate_javascript { + return <<"ENDSCRIPT"; + + + +ENDSCRIPT +} + +sub noninst_users { + my ($processing,$emailverified,$emailoptions,$emaildomain,$emailrules, + $emailruleorder,$settings,$type,$rowid,$typetitle,$css_class,$rowstyle,$intdom) = @_; + my $class = 'LC_left_item'; + if ($css_class) { + $css_class = ' class="'.$css_class.'"'; + } + if ($rowid) { + $rowid = ' id="'.$rowid.'"'; + } + if ($rowstyle) { + $rowstyle = ' style="'.$rowstyle.'"'; + } + my ($output,$description); + if ($type eq 'default') { + $description = &mt('Requests for: [_1]',$typetitle); + } else { + $description = &mt('Requests for: [_1] (status self-reported)',$typetitle); + } + $output = ''. + "$description\n". + ''. + ''; + my %headers = &Apache::lonlocal::texthash( + approve => 'Processing', + email => 'E-mail', + username => 'Username', + ); + foreach my $item ('approve','email','username') { + $output .= ''; + } + $output .= ''; + foreach my $item ('approve','email','username') { + $output .= ''."\n"; + } + $output .= "
'.$headers{$item}.'
'; + my (%choices,@options,$hashref,$defoption,$name,$onclick,$hascustom); + if ($item eq 'approve') { + %choices = &Apache::lonlocal::texthash ( + automatic => 'Automatically approved', + approval => 'Queued for approval', + ); + @options = ('automatic','approval'); + $hashref = $processing; + $defoption = 'automatic'; + $name = 'cancreate_emailprocess_'.$type; + } elsif ($item eq 'email') { + %choices = &Apache::lonlocal::texthash ( + any => 'Any e-mail', + inst => 'Institutional only', + noninst => 'Non-institutional only', + custom => 'Custom restrictions', + ); + @options = ('any','inst','noninst'); + my $showcustom; + if (ref($emailrules) eq 'HASH') { + if (keys(%{$emailrules}) > 0) { + push(@options,'custom'); + $showcustom = 'cancreate_emailrule'; + if (ref($settings) eq 'HASH') { + if (ref($settings->{'email_rule'}) eq 'ARRAY') { + foreach my $rule (@{$settings->{'email_rule'}}) { + if (exists($emailrules->{$rule})) { + $hascustom ++; + } + } + } elsif (ref($settings->{'email_rule'}) eq 'HASH') { + if (ref($settings->{'email_rule'}{$type}) eq 'ARRAY') { + foreach my $rule (@{$settings->{'email_rule'}{$type}}) { + if (exists($emailrules->{$rule})) { + $hascustom ++; + } + } + } + } + } + } + } + $onclick = ' onclick="toggleEmailOptions(this.form,'."'cancreate_emailoptions','$showcustom',". + "'cancreate_emaildomain','$type'".');"'; + $hashref = $emailoptions; + $defoption = 'any'; + $name = 'cancreate_emailoptions_'.$type; + } elsif ($item eq 'username') { + %choices = &Apache::lonlocal::texthash ( + all => 'Same as e-mail', + first => 'Omit @domain', + free => 'Free to choose', + ); + @options = ('all','first','free'); + $hashref = $emailverified; + $defoption = 'all'; + $name = 'cancreate_usernameoptions_'.$type; + } + foreach my $option (@options) { + my $checked; + if (ref($hashref) eq 'HASH') { + if ($type eq '') { + if (!exists($hashref->{'default'})) { + if ($option eq $defoption) { + $checked = ' checked="checked"'; + } + } else { + if ($hashref->{'default'} eq $option) { + $checked = ' checked="checked"'; + } + } + } else { + if (!exists($hashref->{$type})) { + if ($option eq $defoption) { + $checked = ' checked="checked"'; + } + } else { + if ($hashref->{$type} eq $option) { + $checked = ' checked="checked"'; + } + } + } + } elsif (($item eq 'email') && ($hascustom)) { + if ($option eq 'custom') { + $checked = ' checked="checked"'; + } + } elsif ($option eq $defoption) { + $checked = ' checked="checked"'; + } + $output .= '
'; + if ($item eq 'email') { + if ($option eq 'custom') { + my $id = 'cancreate_emailrule_'.$type; + my $display = 'none'; + if ($checked) { + $display = 'inline'; + } + my $numinrow = 2; + $output .= '
'. + ''.&mt('Disallow').''. + &user_formats_row('email',$settings,$emailrules, + $emailruleorder,$numinrow,'',$type); + '
'; + } elsif (($option eq 'inst') || ($option eq 'noninst')) { + my %text = &Apache::lonlocal::texthash ( + inst => 'must end:', + noninst => 'cannot end:', + ); + my $value; + if (ref($emaildomain) eq 'HASH') { + if (ref($emaildomain->{$type}) eq 'HASH') { + $value = $emaildomain->{$type}->{$option}; + } + } + if ($value eq '') { + $value = '@'.$intdom; + } + my $condition = 'cancreate_emaildomain_'.$option.'_'.$type; + my $display = 'none'; + if ($checked) { + $display = 'inline'; + } + $output .= '
'. + ''.$text{$option}.' '. + ''. + '
'; + } + } + } + $output .= '
\n"; + return $output; +} + +sub captcha_choice { + my ($context,$settings,$itemcount,$customcss,$rowstyle) = @_; + my ($keyentry,$currpub,$currpriv,%checked,$rowname,$pubtext,$privtext, + $vertext,$currver); + my %lt = &captcha_phrases(); + $keyentry = 'hidden'; + if ($context eq 'cancreate') { + $rowname = &mt('CAPTCHA validation'); + } elsif ($context eq 'login') { + $rowname = &mt('"Contact helpdesk" CAPTCHA validation'); + } + if (ref($settings) eq 'HASH') { + if ($settings->{'captcha'}) { + $checked{$settings->{'captcha'}} = ' checked="checked"'; + } else { + $checked{'original'} = ' checked="checked"'; + } + if ($settings->{'captcha'} eq 'recaptcha') { + $pubtext = $lt{'pub'}; + $privtext = $lt{'priv'}; + $keyentry = 'text'; + $vertext = $lt{'ver'}; + $currver = $settings->{'recaptchaversion'}; + if ($currver ne '2') { + $currver = 1; + } + } + if (ref($settings->{'recaptchakeys'}) eq 'HASH') { + $currpub = $settings->{'recaptchakeys'}{'public'}; + $currpriv = $settings->{'recaptchakeys'}{'private'}; + } + } else { + $checked{'original'} = ' checked="checked"'; + } + my $css_class; + if ($itemcount%2) { + $css_class = 'LC_odd_row'; + } + if ($customcss) { + $css_class .= " $customcss"; + } + $css_class =~ s/^\s+//; + if ($css_class) { + $css_class = ' class="'.$css_class.'"'; + } + if ($rowstyle) { + $css_class .= ' style="'.$rowstyle.'"'; + } + my $output = ''. + ''.$rowname.''."\n". + ''."\n". + '
'."\n"; + foreach my $option ('original','recaptcha','notused') { + $output .= ''; + unless ($option eq 'notused') { + $output .= (' 'x2)."\n"; + } + } +# +# Note: If reCAPTCHA is to be used for LON-CAPA servers in a domain, a domain coordinator should visit: +# https://www.google.com/recaptcha and generate a Public and Private key. For domains with multiple +# servers a single key pair will be used for all servers, so the internet domain (e.g., yourcollege.edu) +# specified for use with the key should be broad enough to accommodate all servers in the LON-CAPA domain. +# + $output .= '
'."\n". + ''.$pubtext.' '."\n". + '
'."\n". + ''.$privtext.' '."\n". + '
'. + ''.$vertext.' '."\n". + '
'. + '
'."\n". + ''; + return $output; +} + sub user_formats_row { - my ($type,$settings,$rules,$ruleorder,$numinrow,$rowcount) = @_; + my ($type,$settings,$rules,$ruleorder,$numinrow,$rowcount,$status) = @_; my $output; my %text = ( 'username' => 'new usernames', 'id' => 'IDs', ); - my $css_class = $rowcount%2?' class="LC_odd_row"':''; - $output = ''. - ''. - &mt("Format rules to check for $text{$type}: "). - ''. - ''; + unless ($type eq 'email') { + my $css_class = $rowcount%2?' class="LC_odd_row"':''; + $output = ''. + ''; + $output .= '
'. + &mt("Format rules to check for $text{$type}: "). + ''; + } my $rem; if (ref($ruleorder) eq 'ARRAY') { for (my $i=0; $i<@{$ruleorder}; $i++) { @@ -1611,25 +7632,41 @@ sub user_formats_row { if (grep(/^\Q$ruleorder->[$i]\E$/,@{$settings->{$type.'_rule'}})) { $check = ' checked="checked" '; } + } elsif ((ref($settings->{$type.'_rule'}) eq 'HASH') && ($status ne '')) { + if (ref($settings->{$type.'_rule'}->{$status}) eq 'ARRAY') { + if (grep(/^\Q$ruleorder->[$i]\E$/,@{$settings->{$type.'_rule'}->{$status}})) { + $check = ' checked="checked" '; + } + } } } + my $name = $type.'_rule'; + if ($type eq 'email') { + $name .= '_'.$status; + } $output .= ''; } } $rem = @{$ruleorder}%($numinrow); } - my $colsleft = $numinrow - $rem; + my $colsleft; + if ($rem) { + $colsleft = $numinrow - $rem; + } if ($colsleft > 1 ) { $output .= ''; } elsif ($colsleft == 1) { $output .= ''; } - $output .= '
'. ''. '  
'; + unless ($type eq 'email') { + $output .= ''; + } return $output; } @@ -1637,13 +7674,26 @@ sub usercreation_types { my %lt = &Apache::lonlocal::texthash ( author => 'When adding a co-author', course => 'When adding a user to a course', + requestcrs => 'When requesting a course', any => 'Any', official => 'Institutional only ', unofficial => 'Non-institutional only', none => 'None', ); return %lt; -} +} + +sub selfcreation_types { + my %lt = &Apache::lonlocal::texthash ( + selfcreate => 'User creates own account', + any => 'Any', + official => 'Institutional only ', + unofficial => 'Non-institutional only', + email => 'E-mail address', + login => 'Institutional Login', + sso => 'SSO', + ); +} sub authtype_names { my %lt = &Apache::lonlocal::texthash( @@ -1651,6 +7701,7 @@ sub authtype_names { krb4 => 'Kerberos 4', krb5 => 'Kerberos 5', loc => 'Local', + lti => 'LTI', ); return %lt; } @@ -1677,7 +7728,7 @@ sub print_usermodification { $$rowtotal ++; $rowcount ++; } - } else { + } elsif ($position eq 'bottom') { $context = 'course'; $rowcount = 0; foreach my $role ('st','ep','ta','in','cr') { @@ -1690,37 +7741,1376 @@ sub print_usermodification { return $datatable; } +sub print_defaults { + my ($position,$dom,$settings,$rowtotal) = @_; + my $rownum = 0; + my ($datatable,$css_class,$titles); + unless ($position eq 'bottom') { + $titles = &defaults_titles($dom); + } + if ($position eq 'top') { + my @items = ('auth_def','auth_arg_def','lang_def','timezone_def', + 'datelocale_def','portal_def'); + my %defaults; + if (ref($settings) eq 'HASH') { + %defaults = %{$settings}; + } else { + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); + foreach my $item (@items) { + $defaults{$item} = $domdefaults{$item}; + } + } + foreach my $item (@items) { + if ($rownum%2) { + $css_class = ''; + } else { + $css_class = ' class="LC_odd_row" '; + } + $datatable .= ''. + ''.$titles->{$item}. + ''; + if ($item eq 'auth_def') { + my @authtypes = ('internal','krb4','krb5','localauth','lti'); + my %shortauth = ( + internal => 'int', + krb4 => 'krb4', + krb5 => 'krb5', + localauth => 'loc', + lti => 'lti', + ); + my %authnames = &authtype_names(); + foreach my $auth (@authtypes) { + my $checked = ' '; + if ($defaults{$item} eq $auth) { + $checked = ' checked="checked" '; + } + $datatable .= '  '; + } + } elsif ($item eq 'timezone_def') { + my $includeempty = 1; + $datatable .= &Apache::loncommon::select_timezone($item,$defaults{$item},undef,$includeempty); + } elsif ($item eq 'datelocale_def') { + my $includeempty = 1; + $datatable .= &Apache::loncommon::select_datelocale($item,$defaults{$item},undef,$includeempty); + } elsif ($item eq 'lang_def') { + my $includeempty = 1; + $datatable .= &Apache::loncommon::select_language($item,$defaults{$item},$includeempty); + } else { + my $size; + if ($item eq 'portal_def') { + $size = ' size="25"'; + } + $datatable .= ''; + } + $datatable .= ''; + $rownum ++; + } + } elsif ($position eq 'middle') { + my @items = ('intauth_cost','intauth_check','intauth_switch'); + my %defaults; + if (ref($settings) eq 'HASH') { + %defaults = %{$settings}; + if ($defaults{'intauth_cost'} !~ /^\d+$/) { + $defaults{'intauth_cost'} = 10; + } + if ($defaults{'intauth_check'} !~ /^(0|1|2)$/) { + $defaults{'intauth_check'} = 0; + } + if ($defaults{'intauth_switch'} !~ /^(0|1|2)$/) { + $defaults{'intauth_switch'} = 0; + } + } else { + %defaults = ( + 'intauth_cost' => 10, + 'intauth_check' => 0, + 'intauth_switch' => 0, + ); + } + foreach my $item (@items) { + if ($rownum%2) { + $css_class = ''; + } else { + $css_class = ' class="LC_odd_row" '; + } + $datatable .= ''. + ''.$titles->{$item}. + ''; + if ($item eq 'intauth_switch') { + my @options = (0,1,2); + my %optiondesc = &Apache::lonlocal::texthash ( + 0 => 'No', + 1 => 'Yes', + 2 => 'Yes, and copy existing passwd file to passwd.bak file', + ); + $datatable .= ''; + foreach my $option (@options) { + my $checked = ' '; + if ($defaults{$item} eq $option) { + $checked = ' checked="checked"'; + } + $datatable .= ''; + } + $datatable .= '
'. + '
'; + } elsif ($item eq 'intauth_check') { + my @options = (0,1,2); + my %optiondesc = &Apache::lonlocal::texthash ( + 0 => 'No', + 1 => 'Yes, allow login then update passwd file using default cost (if higher)', + 2 => 'Yes, disallow login if stored cost is less than domain default', + ); + $datatable .= ''; + foreach my $option (@options) { + my $checked = ' '; + my $onclick; + if ($defaults{$item} eq $option) { + $checked = ' checked="checked"'; + } + if ($option == 2) { + $onclick = ' onclick="javascript:warnIntAuth(this);"'; + } + $datatable .= ''; + } + $datatable .= '
'. + '
'; + } else { + $datatable .= ''; + } + $datatable .= ''; + $rownum ++; + } + } else { + my %defaults; + if (ref($settings) eq 'HASH') { + if ((ref($settings->{'inststatusorder'}) eq 'ARRAY') && (ref($settings->{'inststatustypes'}) eq 'HASH')) { + my $maxnum = @{$settings->{'inststatusorder'}}; + for (my $i=0; $i<$maxnum; $i++) { + $css_class = $rownum%2?' class="LC_odd_row"':''; + my $item = $settings->{'inststatusorder'}->[$i]; + my $title = $settings->{'inststatustypes'}->{$item}; + my $chgstr = ' onchange="javascript:reorderTypes(this.form,'."'$item'".');"'; + $datatable .= ''. + ''. + ' '.&mt('Internal ID:').' '.$item.' '. + ''. + &mt('delete').''. + ''.&mt('Name displayed:'). + ''. + ''; + } + $css_class = $rownum%2?' class="LC_odd_row"':''; + my $chgstr = ' onchange="javascript:reorderTypes(this.form,'."'addinststatus_pos'".');"'; + $datatable .= ''. + ' '.&mt('Internal ID:'). + ''. + ' '.&mt('(new)'). + ''. + &mt('Name displayed:'). + ''. + ''."\n"; + $rownum ++; + } + } + } + $$rowtotal += $rownum; + return $datatable; +} + +sub get_languages_hash { + my %langchoices; + foreach my $id (&Apache::loncommon::languageids()) { + my $code = &Apache::loncommon::supportedlanguagecode($id); + if ($code ne '') { + $langchoices{$code} = &Apache::loncommon::plainlanguagedescription($id); + } + } + return %langchoices; +} + +sub defaults_titles { + my ($dom) = @_; + my %titles = &Apache::lonlocal::texthash ( + 'auth_def' => 'Default authentication type', + 'auth_arg_def' => 'Default authentication argument', + 'lang_def' => 'Default language', + 'timezone_def' => 'Default timezone', + 'datelocale_def' => 'Default locale for dates', + 'portal_def' => 'Portal/Default URL', + 'intauth_cost' => 'Encryption cost for bcrypt (positive integer)', + 'intauth_check' => 'Check bcrypt cost if authenticated', + 'intauth_switch' => 'Existing crypt-based switched to bcrypt on authentication', + ); + if ($dom) { + my $uprimary_id = &Apache::lonnet::domain($dom,'primary'); + my $uint_dom = &Apache::lonnet::internet_dom($uprimary_id); + my $protocol = $Apache::lonnet::protocol{$uprimary_id}; + $protocol = 'http' if ($protocol ne 'https'); + if ($uint_dom) { + $titles{'portal_def'} .= ' '.&mt('(for example: [_1])',$protocol.'://loncapa.'. + $uint_dom); + } + } + return (\%titles); +} + +sub print_scantron { + my ($r,$position,$dom,$confname,$settings,$rowtotal) = @_; + if ($position eq 'top') { + return &print_scantronformat($r,$dom,$confname,$settings,\$rowtotal); + } else { + return &print_scantronconfig($dom,$settings,\$rowtotal); + } +} + +sub scantron_javascript { + return <<"ENDSCRIPT"; + + + +ENDSCRIPT + +} + +sub print_scantronformat { + my ($r,$dom,$confname,$settings,$rowtotal) = @_; + my $itemcount = 1; + my ($datatable,$css_class,$scantronurl,$is_custom,%error,%scantronurls, + %confhash); + my $switchserver = &check_switchserver($dom,$confname); + my %lt = &Apache::lonlocal::texthash ( + default => 'Default bubblesheet format file error', + custom => 'Custom bubblesheet format file error', + ); + my %scantronfiles = ( + default => 'default.tab', + custom => 'custom.tab', + ); + foreach my $key (keys(%scantronfiles)) { + $scantronurls{$key} = '/res/'.$dom.'/'.$confname.'/scantron/' + .$scantronfiles{$key}; + } + my @defaultinfo = &Apache::lonnet::stat_file($scantronurls{'default'}); + if ((!@defaultinfo) || ($defaultinfo[0] eq 'no_such_dir')) { + if (!$switchserver) { + my $servadm = $r->dir_config('lonAdmEMail'); + my ($configuserok,$author_ok) = &config_check($dom,$confname,$servadm); + if ($configuserok eq 'ok') { + if ($author_ok eq 'ok') { + my %legacyfile = ( + default => $Apache::lonnet::perlvar{'lonTabDir'}.'/default_scantronformat.tab', + custom => $Apache::lonnet::perlvar{'lonTabDir'}.'/scantronformat.tab', + ); + my %md5chk; + foreach my $type (keys(%legacyfile)) { + ($md5chk{$type}) = split(/ /,`md5sum $legacyfile{$type}`); + chomp($md5chk{$type}); + } + if ($md5chk{'default'} ne $md5chk{'custom'}) { + foreach my $type (keys(%legacyfile)) { + ($scantronurls{$type},my $error) = + &legacy_scantronformat($r,$dom,$confname, + $type,$legacyfile{$type}, + $scantronurls{$type}, + $scantronfiles{$type}); + if ($error ne '') { + $error{$type} = $error; + } + } + if (keys(%error) == 0) { + $is_custom = 1; + $confhash{'scantron'}{'scantronformat'} = + $scantronurls{'custom'}; + my $putresult = + &Apache::lonnet::put_dom('configuration', + \%confhash,$dom); + if ($putresult ne 'ok') { + $error{'custom'} = + ''. + &mt('An error occurred updating the domain configuration: [_1]',$putresult).''; + } + } + } else { + ($scantronurls{'default'},my $error) = + &legacy_scantronformat($r,$dom,$confname, + 'default',$legacyfile{'default'}, + $scantronurls{'default'}, + $scantronfiles{'default'}); + if ($error eq '') { + $confhash{'scantron'}{'scantronformat'} = ''; + my $putresult = + &Apache::lonnet::put_dom('configuration', + \%confhash,$dom); + if ($putresult ne 'ok') { + $error{'default'} = + ''. + &mt('An error occurred updating the domain configuration: [_1]',$putresult).''; + } + } else { + $error{'default'} = $error; + } + } + } + } + } else { + $error{'default'} = &mt("Unable to copy default bubblesheet formatfile to domain's RES space: [_1]",$switchserver); + } + } + if (ref($settings) eq 'HASH') { + if ($settings->{'scantronformat'} eq "/res/$dom/$confname/scantron/custom.tab") { + my @info = &Apache::lonnet::stat_file($settings->{'scantronformat'}); + if ((!@info) || ($info[0] eq 'no_such_dir')) { + $scantronurl = ''; + } else { + $scantronurl = $settings->{'scantronformat'}; + } + $is_custom = 1; + } else { + $scantronurl = $scantronurls{'default'}; + } + } else { + if ($is_custom) { + $scantronurl = $scantronurls{'custom'}; + } else { + $scantronurl = $scantronurls{'default'}; + } + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $datatable .= ''; + if (!$is_custom) { + $datatable .= ''.&mt('Default in use:').'
'. + ''; + if ($scantronurl) { + $datatable .= &Apache::loncommon::modal_link($scantronurl,&mt('Default bubblesheet format file'),600,500, + undef,undef,undef,undef,'background-color:#ffffff'); + } else { + $datatable = &mt('File unavailable for display'); + } + $datatable .= ''; + if (keys(%error) == 0) { + $datatable .= ''; + if (!$switchserver) { + $datatable .= &mt('Upload:').'
'; + } + } else { + my $errorstr; + foreach my $key (sort(keys(%error))) { + $errorstr .= $lt{$key}.': '.$error{$key}.'
'; + } + $datatable .= ''.$errorstr; + } + } else { + if (keys(%error) > 0) { + my $errorstr; + foreach my $key (sort(keys(%error))) { + $errorstr .= $lt{$key}.': '.$error{$key}.'
'; + } + $datatable .= ''.$errorstr.' '; + } elsif ($scantronurl) { + my $link = &Apache::loncommon::modal_link($scantronurl,&mt('Custom bubblesheet format file'),600,500, + undef,undef,undef,undef,'background-color:#ffffff'); + $datatable .= ''. + $link. + ''. + ' '. + &mt('Replace:').'
'; + } + } + if (keys(%error) == 0) { + if ($switchserver) { + $datatable .= &mt('Upload to library server: [_1]',$switchserver); + } else { + $datatable .=' '. + ''; + } + } + $datatable .= ''; + $$rowtotal ++; + return $datatable; +} + +sub legacy_scantronformat { + my ($r,$dom,$confname,$file,$legacyfile,$newurl,$newfile) = @_; + my ($url,$error); + my @statinfo = &Apache::lonnet::stat_file($newurl); + if ((!@statinfo) || ($statinfo[0] eq 'no_such_dir')) { + (my $result,$url) = + &publishlogo($r,'copy',$legacyfile,$dom,$confname,'scantron', + '','',$newfile); + if ($result ne 'ok') { + $error = &mt("An error occurred publishing the [_1] bubblesheet format file in RES space. Error was: [_2].",$newfile,$result); + } + } + return ($url,$error); +} + +sub print_scantronconfig { + my ($dom,$settings,$rowtotal) = @_; + my $itemcount = 2; + my $is_checked = ' checked="checked"'; + my %optionson = ( + hdr => ' checked="checked"', + pad => ' checked="checked"', + rem => ' checked="checked"', + ); + my %optionsoff = ( + hdr => '', + pad => '', + rem => '', + ); + my $currcsvsty = 'none'; + my ($datatable,%csvfields,%checked,%onclick,%csvoptions); + my @fields = &scantroncsv_fields(); + my %titles = &scantronconfig_titles(); + if (ref($settings) eq 'HASH') { + if (ref($settings->{config}) eq 'HASH') { + if ($settings->{config}->{dat}) { + $checked{'dat'} = $is_checked; + } + if (ref($settings->{config}->{csv}) eq 'HASH') { + if (ref($settings->{config}->{csv}->{fields}) eq 'HASH') { + %csvfields = %{$settings->{config}->{csv}->{fields}}; + if (keys(%csvfields) > 0) { + $checked{'csv'} = $is_checked; + $currcsvsty = 'block'; + } + } + if (ref($settings->{config}->{csv}->{options}) eq 'HASH') { + %csvoptions = %{$settings->{config}->{csv}->{options}}; + foreach my $option (keys(%optionson)) { + unless ($csvoptions{$option}) { + $optionsoff{$option} = $optionson{$option}; + $optionson{$option} = ''; + } + } + } + } + } else { + $checked{'dat'} = $is_checked; + } + } else { + $checked{'dat'} = $is_checked; + } + $onclick{'csv'} = ' onclick="toggleScantron(this.form);"'; + my $css_class = $itemcount%2? ' class="LC_odd_row"':''; + $datatable = ''.&mt('Supported formats').''. + ''; + foreach my $item ('dat','csv') { + my $id; + if ($item eq 'csv') { + $id = 'id="scantronconfcsv" '; + } + $datatable .= ''.(' 'x3); + if ($item eq 'csv') { + $datatable .= '
'. + ''.&mt('CSV Column Mapping').''. + ''."\n"; + foreach my $col (@fields) { + my $selnone; + if ($csvfields{$col} eq '') { + $selnone = ' selected="selected"'; + } + $datatable .= ''. + ''; + } + $datatable .= '
'.&mt('Field').''.&mt('Location').'
'.$titles{$col}.'
'. + '
'. + ''.&mt('CSV Options').''; + foreach my $option ('hdr','pad','rem') { + $datatable .= ''.$titles{$option}.':'. + ''.(' 'x2)."\n". + '
'; + } + $datatable .= '
'; + $itemcount ++; + } + } + $datatable .= ''; + $$rowtotal ++; + return $datatable; +} + +sub scantronconfig_titles { + return &Apache::lonlocal::texthash( + dat => 'Standard format (.dat)', + csv => 'Comma separated values (.csv)', + hdr => 'Remove first line in file (contains column titles)', + pad => 'Prepend 0s to PaperID', + rem => 'Remove leading spaces (except Question Response columns)', + CODE => 'CODE', + ID => 'Student ID', + PaperID => 'Paper ID', + FirstName => 'First Name', + LastName => 'Last Name', + FirstQuestion => 'First Question Response', + Section => 'Section', + ); +} + +sub scantroncsv_fields { + return ('PaperID','LastName','FirstName','ID','Section','CODE','FirstQuestion'); +} + +sub print_coursecategories { + my ($position,$dom,$hdritem,$settings,$rowtotal) = @_; + my $datatable; + if ($position eq 'top') { + my (%checked); + my @catitems = ('unauth','auth'); + my @cattypes = ('std','domonly','codesrch','none'); + $checked{'unauth'} = 'std'; + $checked{'auth'} = 'std'; + if (ref($settings) eq 'HASH') { + foreach my $type (@cattypes) { + if ($type eq $settings->{'unauth'}) { + $checked{'unauth'} = $type; + } + if ($type eq $settings->{'auth'}) { + $checked{'auth'} = $type; + } + } + } + my %lt = &Apache::lonlocal::texthash ( + unauth => 'Catalog type for unauthenticated users', + auth => 'Catalog type for authenticated users', + none => 'No catalog', + std => 'Standard catalog', + domonly => 'Domain-only catalog', + codesrch => "Code search form", + ); + my $itemcount = 0; + foreach my $item (@catitems) { + my $css_class = $itemcount%2? ' class="LC_odd_row"':''; + $datatable .= ''. + ''.$lt{$item}.''. + ''; + foreach my $type (@cattypes) { + my $ischecked; + if ($checked{$item} eq $type) { + $ischecked=' checked="checked"'; + } + $datatable .= ' '; + } + $datatable .= ''; + $itemcount ++; + } + $$rowtotal += $itemcount; + } elsif ($position eq 'middle') { + my $toggle_cats_crs = ' '; + my $toggle_cats_dom = ' checked="checked" '; + my $can_cat_crs = ' '; + my $can_cat_dom = ' checked="checked" '; + my $toggle_catscomm_comm = ' '; + my $toggle_catscomm_dom = ' checked="checked" '; + my $can_catcomm_comm = ' '; + my $can_catcomm_dom = ' checked="checked" '; + my $toggle_catsplace_place = ' '; + my $toggle_catsplace_dom = ' checked="checked" '; + my $can_catplace_place = ' '; + my $can_catplace_dom = ' checked="checked" '; + + if (ref($settings) eq 'HASH') { + if ($settings->{'togglecats'} eq 'crs') { + $toggle_cats_crs = $toggle_cats_dom; + $toggle_cats_dom = ' '; + } + if ($settings->{'categorize'} eq 'crs') { + $can_cat_crs = $can_cat_dom; + $can_cat_dom = ' '; + } + if ($settings->{'togglecatscomm'} eq 'comm') { + $toggle_catscomm_comm = $toggle_catscomm_dom; + $toggle_catscomm_dom = ' '; + } + if ($settings->{'categorizecomm'} eq 'comm') { + $can_catcomm_comm = $can_catcomm_dom; + $can_catcomm_dom = ' '; + } + if ($settings->{'togglecatsplace'} eq 'place') { + $toggle_catsplace_place = $toggle_catsplace_dom; + $toggle_catsplace_dom = ' '; + } + if ($settings->{'categorizeplace'} eq 'place') { + $can_catplace_place = $can_catplace_dom; + $can_catplace_dom = ' '; + } + } + my %title = &Apache::lonlocal::texthash ( + togglecats => 'Show/Hide a course in catalog', + togglecatscomm => 'Show/Hide a community in catalog', + togglecatsplace => 'Show/Hide a placement test in catalog', + categorize => 'Assign a category to a course', + categorizecomm => 'Assign a category to a community', + categorizeplace => 'Assign a category to a placement test', + ); + my %level = &Apache::lonlocal::texthash ( + dom => 'Set in Domain', + crs => 'Set in Course', + comm => 'Set in Community', + place => 'Set in Placement Test', + ); + $datatable = ''. + ''.$title{'togglecats'}.''. + ' '. + ''. + ''. + ''.$title{'categorize'}.''. + ''. + ' '. + ''. + ''. + ''.$title{'togglecatscomm'}.''. + ' '. + ''. + ''. + ''.$title{'categorizecomm'}.''. + ''. + ' '. + ''. + ''. + ''.$title{'togglecatsplace'}.''. + ' '. + ''. + ''. + ''.$title{'categorizeplace'}.''. + ''. + ' '. + ''. + ''; + $$rowtotal += 6; + } else { + my $css_class; + my $itemcount = 1; + my $cathash; + if (ref($settings) eq 'HASH') { + $cathash = $settings->{'cats'}; + } + if (ref($cathash) eq 'HASH') { + my (@cats,@trails,%allitems,%idx,@jsarray); + &Apache::loncommon::extract_categories($cathash,\@cats,\@trails, + \%allitems,\%idx,\@jsarray); + my $maxdepth = scalar(@cats); + my $colattrib = ''; + if ($maxdepth > 2) { + $colattrib = ' colspan="2" '; + } + my @path; + if (@cats > 0) { + if (ref($cats[0]) eq 'ARRAY') { + my $numtop = @{$cats[0]}; + my $maxnum = $numtop; + my %default_names = ( + instcode => &mt('Official courses'), + communities => &mt('Communities'), + placement => &mt('Placement Tests'), + ); + + if ((!grep(/^instcode$/,@{$cats[0]})) || + ($cathash->{'instcode::0'} eq '') || + (!grep(/^communities$/,@{$cats[0]})) || + ($cathash->{'communities::0'} eq '') || + (!grep(/^placement$/,@{$cats[0]})) || + ($cathash->{'placement::0'} eq '')) { + $maxnum ++; + } + my $lastidx; + for (my $i=0; $i<$numtop; $i++) { + my $parent = $cats[0][$i]; + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $item = &escape($parent).'::0'; + my $chgstr = ' onchange="javascript:reorderCats(this.form,'."'','$item','$idx{$item}'".');"'; + $lastidx = $idx{$item}; + $datatable .= '' + .''; + if ($parent eq 'instcode' || $parent eq 'communities' || $parent eq 'placement') { + $datatable .= '' + .$default_names{$parent}.''; + if ($parent eq 'instcode') { + $datatable .= '
(' + .&mt('with institutional codes') + .')'; + } else { + $datatable .= '
'; + } + $datatable .= '' + .''; + if ($parent eq 'instcode') { + $datatable .= ' '; + } else { + $datatable .= '
' + .''; + } + $datatable .= ''; + if (($parent eq 'communities') || ($parent eq 'placement')) { + $datatable .= '
'; + } + $datatable .= ''; + } else { + $datatable .= $parent + .' '; + } + my $depth = 1; + push(@path,$parent); + $datatable .= &build_category_rows($itemcount,\@cats,$depth,$parent,\@path,\%idx); + pop(@path); + $datatable .= ''; + $itemcount ++; + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $chgstr = ' onchange="javascript:reorderCats(this.form,'."'','addcategory_pos','$lastidx'".');"'; + $datatable .= ''.&mt('Add category:').' ' + .'' + .''."\n"; + $itemcount ++; + foreach my $default ('instcode','communities','placement') { + if ((!grep(/^\Q$default\E$/,@{$cats[0]})) || ($cathash->{$default.'::0'} eq '')) { + $css_class = $itemcount%2?' class="LC_odd_row"':''; + my $chgstr = ' onchange="javascript:reorderCats(this.form,'."'','$default"."_pos','$lastidx'".');"'; + $datatable .= ''. + ''. + ''. + $default_names{$default}.''; + if ($default eq 'instcode') { + $datatable .= '
(' + .&mt('with institutional codes').')'; + } + $datatable .= '' + .' ' + .''; + } + } + } + } else { + $datatable .= &initialize_categories($itemcount); + } + } else { + $datatable .= ''.$hdritem->{'header'}->[1]->{'col2'}.'' + .&initialize_categories($itemcount); + } + $$rowtotal += $itemcount; + } + return $datatable; +} + +sub print_serverstatuses { + my ($dom,$settings,$rowtotal) = @_; + my $datatable; + my @pages = &serverstatus_pages(); + my (%namedaccess,%machineaccess); + foreach my $type (@pages) { + $namedaccess{$type} = ''; + $machineaccess{$type}= ''; + } + if (ref($settings) eq 'HASH') { + foreach my $type (@pages) { + if (exists($settings->{$type})) { + if (ref($settings->{$type}) eq 'HASH') { + foreach my $key (keys(%{$settings->{$type}})) { + if ($key eq 'namedusers') { + $namedaccess{$type} = $settings->{$type}->{$key}; + } elsif ($key eq 'machines') { + $machineaccess{$type} = $settings->{$type}->{$key}; + } + } + } + } + } + } + my $titles= &LONCAPA::lonauthcgi::serverstatus_titles(); + my $rownum = 0; + my $css_class; + foreach my $type (@pages) { + $rownum ++; + $css_class = $rownum%2?' class="LC_odd_row"':''; + $datatable .= ''. + ''. + $titles->{$type}.''. + ''. + ''. + ''. + ''. + ''. + ''."\n"; + } + $$rowtotal += $rownum; + return $datatable; +} + +sub serverstatus_pages { + return ('userstatus','lonstatus','loncron','server-status','codeversions', + 'checksums','clusterstatus','certstatus','metadata_keywords', + 'metadata_harvest','takeoffline','takeonline','showenv','toggledebug', + 'ping','domconf','uniquecodes','diskusage','coursecatalog'); +} + +sub defaults_javascript { + my ($settings) = @_; + my $intauthcheck = &mt('Warning: disallowing login for an authenticated user if the stored cost is less than the default will require a password reset by/for the user.'); + my $intauthcost = &mt('Warning: bcrypt encryption cost for internal authentication must be an integer.'); + &js_escape(\$intauthcheck); + &js_escape(\$intauthcost); + my $intauthjs = <<"ENDSCRIPT"; + +function warnIntAuth(field) { + if (field.name == 'intauth_check') { + if (field.value == '2') { + alert('$intauthcheck'); + } + } + if (field.name == 'intauth_cost') { + field.value.replace(/\s/g,''); + if (field.value != '') { + var regexdigit=/^\\d+\$/; + if (!regexdigit.test(field.value)) { + alert('$intauthcost'); + } + } + } + return; +} + +ENDSCRIPT + + if (ref($settings) ne 'HASH') { + return &Apache::lonhtmlcommon::scripttag($intauthjs); + } + if ((ref($settings->{'inststatusorder'}) eq 'ARRAY') && (ref($settings->{'inststatustypes'}) eq 'HASH')) { + my $maxnum = scalar(@{$settings->{'inststatusorder'}}); + if ($maxnum eq '') { + $maxnum = 0; + } + $maxnum ++; + my $jstext = ' var inststatuses = Array('."'".join("','",@{$settings->{'inststatusorder'}})."'".');'; + return <<"ENDSCRIPT"; + + +ENDSCRIPT + } else { + return &Apache::lonhtmlcommon::scripttag($intauthjs); + } +} + +sub coursecategories_javascript { + my ($settings) = @_; + my ($output,$jstext,$cathash); + if (ref($settings) eq 'HASH') { + $cathash = $settings->{'cats'}; + } + if (ref($cathash) eq 'HASH') { + my (@cats,@jsarray,%idx); + &Apache::loncommon::gather_categories($cathash,\@cats,\%idx,\@jsarray); + if (@jsarray > 0) { + $jstext = ' var categories = Array('.scalar(@jsarray).');'."\n"; + for (my $i=0; $i<@jsarray; $i++) { + if (ref($jsarray[$i]) eq 'ARRAY') { + my $catstr = join('","',@{$jsarray[$i]}); + $jstext .= ' categories['.$i.'] = Array("'.$catstr.'");'."\n"; + } + } + } + } else { + $jstext = ' var categories = Array(1);'."\n". + ' categories[0] = Array("instcode_pos");'."\n"; + } + my $instcode_reserved = &mt('The name: [_1] is a reserved category.','"instcode"'); + my $communities_reserved = &mt('The name: [_1] is a reserved category.','"communities"'); + my $placement_reserved = &mt('The name: [_1] is a reserved category.','"placement"'); + my $choose_again = "\n".&mt('Please use a different name for the new top level category.'); + &js_escape(\$instcode_reserved); + &js_escape(\$communities_reserved); + &js_escape(\$placement_reserved); + &js_escape(\$choose_again); + $output = <<"ENDSCRIPT"; + + +ENDSCRIPT + return $output; +} + +sub initialize_categories { + my ($itemcount) = @_; + my ($datatable,$css_class,$chgstr); + my %default_names = ( + instcode => 'Official courses (with institutional codes)', + communities => 'Communities', + placement => 'Placement Tests', + ); + my %selnum = ( + instcode => '0', + communities => '1', + placement => '2', + ); + my %selected; + foreach my $default ('instcode','communities','placement') { + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $chgstr = ' onchange="javascript:reorderCats(this.form,'."'','$default"."_pos','0'".');"'; + map { $selected{$selnum{$_}} = '' } keys(%selnum); + $selected{$selnum{$default}} = ' selected="selected"'; + $datatable .= '' + .' ' + .$default_names{$default} + .'' + .' '; + $itemcount ++; + } + $css_class = $itemcount%2?' class="LC_odd_row"':''; + $chgstr = ' onchange="javascript:reorderCats(this.form,'."'','addcategory_pos','0'".');"'; + $datatable .= '' + .' ' + .&mt('Add category').''.&mt('Name:') + .' ' + .''; + return $datatable; +} + +sub build_category_rows { + my ($itemcount,$cats,$depth,$parent,$path,$idx) = @_; + my ($text,$name,$item,$chgstr); + if (ref($cats) eq 'ARRAY') { + my $maxdepth = scalar(@{$cats}); + if (ref($cats->[$depth]) eq 'HASH') { + if (ref($cats->[$depth]{$parent}) eq 'ARRAY') { + my $numchildren = @{$cats->[$depth]{$parent}}; + my $css_class = $itemcount%2?' class="LC_odd_row"':''; + $text .= ''; + my ($idxnum,$parent_name,$parent_item); + my $higher = $depth - 1; + if ($higher == 0) { + $parent_name = &escape($parent).'::'.$higher; + } else { + if (ref($path) eq 'ARRAY') { + $parent_name = &escape($parent).':'.&escape($path->[-2]).':'.$higher; + } + } + $parent_item = 'addcategory_pos_'.$parent_name; + for (my $j=0; $j<=$numchildren; $j++) { + if ($j < $numchildren) { + $name = $cats->[$depth]{$parent}[$j]; + $item = &escape($name).':'.&escape($parent).':'.$depth; + $idxnum = $idx->{$item}; + } else { + $name = $parent_name; + $item = $parent_item; + } + $chgstr = ' onchange="javascript:reorderCats(this.form,'."'$parent_name','$item','$idxnum'".');"'; + $text .= ''; + } + $text .= '
 '; + if ($j < $numchildren) { + my $deeper = $depth+1; + $text .= $name.' ' + .''; + if(ref($path) eq 'ARRAY') { + push(@{$path},$name); + $text .= &build_category_rows($itemcount,$cats,$deeper,$name,$path,$idx); + pop(@{$path}); + } + } else { + $text .= &mt('Add subcategory:').' '; + } + $text .= '
'; + } else { + my $higher = $depth-1; + if ($higher == 0) { + $name = &escape($parent).'::'.$higher; + } else { + if (ref($path) eq 'ARRAY') { + $name = &escape($parent).':'.&escape($path->[-2]).':'.$higher; + } + } + my $colspan; + if ($parent ne 'instcode') { + $colspan = $maxdepth - $depth - 1; + $text .= ''.&mt('Add subcategory:').''; + } + } + } + } + return $text; +} + sub modifiable_userdata_row { - my ($context,$role,$settings,$numinrow,$rowcount) = @_; - my $rolename; - if ($role eq 'cr') { - $rolename = &mt('Custom role'); + my ($context,$item,$settings,$numinrow,$rowcount,$usertypes,$fieldsref,$titlesref, + $rowid,$customcss,$rowstyle) = @_; + my ($role,$rolename,$statustype); + $role = $item; + if ($context eq 'cancreate') { + if ($item =~ /^(emailusername)_(.+)$/) { + $role = $1; + $statustype = $2; + if (ref($usertypes) eq 'HASH') { + if ($usertypes->{$statustype}) { + $rolename = &mt('Data provided by [_1]',$usertypes->{$statustype}); + } else { + $rolename = &mt('Data provided by user'); + } + } + } + } elsif ($context eq 'selfcreate') { + if (ref($usertypes) eq 'HASH') { + $rolename = $usertypes->{$role}; + } else { + $rolename = $role; + } + } elsif ($context eq 'lti') { + $rolename = &mt('Institutional data used (if available)'); } else { - $rolename = &Apache::lonnet::plaintext($role); + if ($role eq 'cr') { + $rolename = &mt('Custom role'); + } else { + $rolename = &Apache::lonnet::plaintext($role); + } + } + my (@fields,%fieldtitles); + if (ref($fieldsref) eq 'ARRAY') { + @fields = @{$fieldsref}; + } else { + @fields = ('lastname','firstname','middlename','generation', + 'permanentemail','id'); + } + if ((ref($titlesref) eq 'HASH')) { + %fieldtitles = %{$titlesref}; + } else { + %fieldtitles = &Apache::loncommon::personal_data_fieldtitles(); } - my @fields = ('lastname','firstname','middlename','generation', - 'permanentemail','id'); - my %fieldtitles = &Apache::loncommon::personal_data_fieldtitles(); my $output; - my $css_class = $rowcount%2?' class="LC_odd_row"':''; - $output = ''. + my $css_class; + if ($rowcount%2) { + $css_class = 'LC_odd_row'; + } + if ($customcss) { + $css_class .= " $customcss"; + } + $css_class =~ s/^\s+//; + if ($css_class) { + $css_class = ' class="'.$css_class.'"'; + } + if ($rowstyle) { + $css_class .= ' style="'.$rowstyle.'"'; + } + if ($rowid) { + $rowid = ' id="'.$rowid.'"'; + } + $output = ''. ''.$rolename.''. ''; my $rem; my %checks; + my %current; if (ref($settings) eq 'HASH') { - if (ref($settings->{$context}) eq 'HASH') { + my $hashref; + if ($context eq 'lti') { + if (ref($settings) eq 'HASH') { + $hashref = $settings->{'instdata'}; + } + } elsif (ref($settings->{$context}) eq 'HASH') { if (ref($settings->{$context}->{$role}) eq 'HASH') { - foreach my $field (@fields) { - if ($settings->{$context}->{$role}->{$field}) { + $hashref = $settings->{'lti_instdata'}; + } + if ($role eq 'emailusername') { + if ($statustype) { + if (ref($settings->{$context}->{$role}->{$statustype}) eq 'HASH') { + $hashref = $settings->{$context}->{$role}->{$statustype}; + } + } + } + } + if (ref($hashref) eq 'HASH') { + foreach my $field (@fields) { + if ($hashref->{$field}) { + if ($role eq 'emailusername') { + $checks{$field} = $hashref->{$field}; + } else { $checks{$field} = ' checked="checked" '; } } } } } - for (my $i=0; $i<@fields; $i++) { - my $rem = $i%($numinrow); + + my $total = scalar(@fields); + for (my $i=0; $i<$total; $i++) { + $rem = $i%($numinrow); if ($rem == 0) { if ($i > 0) { $output .= ''; @@ -1728,24 +9118,51 @@ sub modifiable_userdata_row { $output .= ''; } my $check = ' '; - if (exists($checks{$fields[$i]})) { - $check = $checks{$fields[$i]} - } else { - if ($role eq 'st') { - if (ref($settings) ne 'HASH') { - $check = ' checked="checked" '; + unless ($role eq 'emailusername') { + if (exists($checks{$fields[$i]})) { + $check = $checks{$fields[$i]} + } elsif ($context ne 'lti') { + if ($role eq 'st') { + if (ref($settings) ne 'HASH') { + $check = ' checked="checked" '; + } } } } $output .= ''; - $rem = @fields%($numinrow); + ''; + my $prefix = 'canmodify'; + if ($role eq 'emailusername') { + unless ($checks{$fields[$i]} =~ /^(required|optional)$/) { + $checks{$fields[$i]} = 'omit'; + } + foreach my $option ('required','optional','omit') { + my $checked=''; + if ($checks{$fields[$i]} eq $option) { + $checked='checked="checked" '; + } + $output .= ''.(' ' x2); + } + $output .= ''.$fieldtitles{$fields[$i]}.''; + } else { + if ($context eq 'lti') { + $prefix = 'lti'; + } + $output .= ''; + } + $output .= ''; + } + $rem = $total%$numinrow; + my $colsleft; + if ($rem) { + $colsleft = $numinrow - $rem; } - my $colsleft = $numinrow - $rem; - if ($colsleft > 1 ) { + if ($colsleft > 1) { $output .= ''; } elsif ($colsleft == 1) { @@ -1755,11 +9172,44 @@ sub modifiable_userdata_row { return $output; } -sub users_cansearch_row { - my ($settings,$types,$usertypes,$dom,$numinrow,$othertitle) = @_; - my $output = ''. - ''; + return $output; } sub sorted_searchtitles { @@ -1897,55 +9351,429 @@ sub usertype_update_row { } sub modify_login { - my ($r,$dom,$confname,%domconfig) = @_; - my ($resulttext,$errors,$colchgtext,%changes,%colchanges); - my %title = ( coursecatalog => 'Display course catalog', - adminmail => 'Display administrator E-mail address', - loginheader => 'Log-in box header'); - my @offon = ('off','on'); - my %loginhash; + my ($r,$dom,$confname,$lastactref,%domconfig) = @_; + my ($resulttext,$errors,$colchgtext,%changes,%colchanges,%newfile,%newurl, + %curr_loginvia,%loginhash,@currlangs,@newlangs,$addedfile,%title,@offon); + %title = ( coursecatalog => 'Display course catalog', + adminmail => 'Display administrator E-mail address', + helpdesk => 'Display "Contact Helpdesk" link', + newuser => 'Link for visitors to create a user account', + loginheader => 'Log-in box header'); + @offon = ('off','on'); + if (ref($domconfig{login}) eq 'HASH') { + if (ref($domconfig{login}{loginvia}) eq 'HASH') { + foreach my $lonhost (keys(%{$domconfig{login}{loginvia}})) { + $curr_loginvia{$lonhost} = $domconfig{login}{loginvia}{$lonhost}; + } + } + } ($errors,%colchanges) = &modify_colors($r,$dom,$confname,['login'], \%domconfig,\%loginhash); - $loginhash{login}{coursecatalog} = $env{'form.coursecatalog'}; - $loginhash{login}{adminmail} = $env{'form.adminmail'}; + my @toggles = ('coursecatalog','adminmail','helpdesk','newuser'); + foreach my $item (@toggles) { + $loginhash{login}{$item} = $env{'form.'.$item}; + } $loginhash{login}{loginheader} = $env{'form.loginheader'}; if (ref($colchanges{'login'}) eq 'HASH') { $colchgtext = &display_colorchgs($dom,\%colchanges,['login'], \%loginhash); } + + my %servers = &Apache::lonnet::internet_dom_servers($dom); + my %domservers = &Apache::lonnet::get_servers($dom); + my @loginvia_attribs = ('serverpath','custompath','exempt'); + if (keys(%servers) > 1) { + foreach my $lonhost (keys(%servers)) { + next if ($env{'form.'.$lonhost.'_server'} eq $lonhost); + if (ref($curr_loginvia{$lonhost}) eq 'HASH') { + if ($env{'form.'.$lonhost.'_server'} eq $curr_loginvia{$lonhost}{'server'}) { + $loginhash{login}{loginvia}{$lonhost}{'server'} = $curr_loginvia{$lonhost}{'server'}; + } elsif ($curr_loginvia{$lonhost}{'server'} ne '') { + if (defined($servers{$env{'form.'.$lonhost.'_server'}})) { + $loginhash{login}{loginvia}{$lonhost}{'server'} = $env{'form.'.$lonhost.'_server'}; + $changes{'loginvia'}{$lonhost} = 1; + } else { + $loginhash{login}{loginvia}{$lonhost}{'server'} = ''; + $changes{'loginvia'}{$lonhost} = 1; + } + } else { + if (defined($servers{$env{'form.'.$lonhost.'_server'}})) { + $loginhash{login}{loginvia}{$lonhost}{'server'} = $env{'form.'.$lonhost.'_server'}; + $changes{'loginvia'}{$lonhost} = 1; + } + } + if ($loginhash{login}{loginvia}{$lonhost}{'server'} eq '') { + foreach my $item (@loginvia_attribs) { + $loginhash{login}{loginvia}{$lonhost}{$item} = ''; + } + } else { + foreach my $item (@loginvia_attribs) { + my $new = $env{'form.'.$lonhost.'_'.$item}; + if (($item eq 'serverpath') && ($new eq 'custom')) { + $env{'form.'.$lonhost.'_custompath'} =~ s/\s+//g; + if ($env{'form.'.$lonhost.'_custompath'} eq '') { + $new = '/'; + } + } + if (($item eq 'custompath') && + ($env{'form.'.$lonhost.'_serverpath'} ne 'custom')) { + $new = ''; + } + if ($new ne $curr_loginvia{$lonhost}{$item}) { + $changes{'loginvia'}{$lonhost} = 1; + } + if ($item eq 'exempt') { + $new = &check_exempt_addresses($new); + } + $loginhash{login}{loginvia}{$lonhost}{$item} = $new; + } + } + } else { + if (defined($servers{$env{'form.'.$lonhost.'_server'}})) { + $loginhash{login}{loginvia}{$lonhost}{'server'} = $env{'form.'.$lonhost.'_server'}; + $changes{'loginvia'}{$lonhost} = 1; + foreach my $item (@loginvia_attribs) { + my $new = $env{'form.'.$lonhost.'_'.$item}; + if (($item eq 'serverpath') && ($new eq 'custom')) { + if ($env{'form.'.$lonhost.'_custompath'} eq '') { + $new = '/'; + } + } + if (($item eq 'custompath') && + ($env{'form.'.$lonhost.'_serverpath'} ne 'custom')) { + $new = ''; + } + $loginhash{login}{loginvia}{$lonhost}{$item} = $new; + } + } + } + } + } + + my $servadm = $r->dir_config('lonAdmEMail'); + my %langchoices = &Apache::lonlocal::texthash(&get_languages_hash()); + if (ref($domconfig{'login'}) eq 'HASH') { + if (ref($domconfig{'login'}{'helpurl'}) eq 'HASH') { + foreach my $lang (sort(keys(%{$domconfig{'login'}{'helpurl'}}))) { + if ($lang eq 'nolang') { + push(@currlangs,$lang); + } elsif (defined($langchoices{$lang})) { + push(@currlangs,$lang); + } else { + next; + } + } + } + } + my @delurls = &Apache::loncommon::get_env_multiple('form.loginhelpurl_del'); + if (@currlangs > 0) { + foreach my $lang (@currlangs) { + if (grep(/^\Q$lang\E$/,@delurls)) { + $changes{'helpurl'}{$lang} = 1; + } elsif ($env{'form.loginhelpurl_'.$lang.'.filename'}) { + $changes{'helpurl'}{$lang} = 1; + $newfile{$lang} = $env{'form.loginhelpurl_'.$lang.'.filename'}; + push(@newlangs,$lang); + } else { + $loginhash{'login'}{'helpurl'}{$lang} = $domconfig{'login'}{'helpurl'}{$lang}; + } + } + } + unless (grep(/^nolang$/,@currlangs)) { + if ($env{'form.loginhelpurl_nolang.filename'}) { + $changes{'helpurl'}{'nolang'} = 1; + $newfile{'nolang'} = $env{'form.loginhelpurl_nolang.filename'}; + push(@newlangs,'nolang'); + } + } + if ($env{'form.loginhelpurl_add_lang'}) { + if ((defined($langchoices{$env{'form.loginhelpurl_add_lang'}})) && + ($env{'form.loginhelpurl_add_file.filename'})) { + $newfile{$env{'form.loginhelpurl_add_lang'}} = $env{'form.loginhelpurl_add_file.filename'}; + $addedfile = $env{'form.loginhelpurl_add_lang'}; + } + } + if ((@newlangs > 0) || ($addedfile)) { + my $error; + my ($configuserok,$author_ok,$switchserver) = &config_check($dom,$confname,$servadm); + if ($configuserok eq 'ok') { + if ($switchserver) { + $error = &mt("Upload of custom help file is not permitted to this server: [_1]",$switchserver); + } elsif ($author_ok eq 'ok') { + my @allnew = @newlangs; + if ($addedfile ne '') { + push(@allnew,$addedfile); + } + foreach my $lang (@allnew) { + my $formelem = 'loginhelpurl_'.$lang; + if ($lang eq $env{'form.loginhelpurl_add_lang'}) { + $formelem = 'loginhelpurl_add_file'; + } + (my $result,$newurl{$lang}) = &publishlogo($r,'upload',$formelem,$dom,$confname, + "help/$lang",'','',$newfile{$lang}); + if ($result eq 'ok') { + $loginhash{'login'}{'helpurl'}{$lang} = $newurl{$lang}; + $changes{'helpurl'}{$lang} = 1; + } else { + my $puberror = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$newfile{$lang},$result); + $errors .= '
  • '.$puberror.'
  • '; + if ((grep(/^\Q$lang\E$/,@currlangs)) && + (!grep(/^\Q$lang\E$/,@delurls))) { + $loginhash{'login'}{'helpurl'}{$lang} = $domconfig{'login'}{'helpurl'}{$lang}; + } + } + } + } else { + $error = &mt("Upload of custom log-in help file(s) failed because an author role could not be assigned to a Domain Configuration user ([_1]) in domain: [_2]. Error was: [_3].",$confname,$dom,$author_ok); + } + } else { + $error = &mt("Upload of custom log-in help file(s) failed because a Domain Configuration user ([_1]) could not be created in domain: [_2]. Error was: [_3].",$confname,$dom,$configuserok); + } + if ($error) { + &Apache::lonnet::logthis($error); + $errors .= '
  • '.$error.'
  • '; + } + } + + my (%currheadtagurls,%currexempt,@newhosts,%newheadtagurls,%possexempt); + if (ref($domconfig{'login'}) eq 'HASH') { + if (ref($domconfig{'login'}{'headtag'}) eq 'HASH') { + foreach my $lonhost (keys(%{$domconfig{'login'}{'headtag'}})) { + if ($domservers{$lonhost}) { + if (ref($domconfig{'login'}{'headtag'}{$lonhost}) eq 'HASH') { + $currheadtagurls{$lonhost} = $domconfig{'login'}{'headtag'}{$lonhost}{'url'}; + $currexempt{$lonhost} = $domconfig{'login'}{'headtag'}{$lonhost}{'exempt'}; + } + } + } + } + } + my @delheadtagurls = &Apache::loncommon::get_env_multiple('form.loginheadtag_del'); + foreach my $lonhost (sort(keys(%domservers))) { + if (grep(/^\Q$lonhost\E$/,@delheadtagurls)) { + $changes{'headtag'}{$lonhost} = 1; + } else { + if ($env{'form.loginheadtagexempt_'.$lonhost}) { + $possexempt{$lonhost} = &check_exempt_addresses($env{'form.loginheadtagexempt_'.$lonhost}); + } + if ($env{'form.loginheadtag_'.$lonhost.'.filename'}) { + push(@newhosts,$lonhost); + } elsif ($currheadtagurls{$lonhost}) { + $loginhash{'login'}{'headtag'}{$lonhost}{'url'} = $currheadtagurls{$lonhost}; + if ($currexempt{$lonhost}) { + if ((!exists($possexempt{$lonhost})) || ($possexempt{$lonhost} ne $currexempt{$lonhost})) { + $changes{'headtag'}{$lonhost} = 1; + } + } elsif ($possexempt{$lonhost}) { + $changes{'headtag'}{$lonhost} = 1; + } + if ($possexempt{$lonhost}) { + $loginhash{'login'}{'headtag'}{$lonhost}{'exempt'} = $possexempt{$lonhost}; + } + } + } + } + if (@newhosts) { + my $error; + my ($configuserok,$author_ok,$switchserver) = &config_check($dom,$confname,$servadm); + if ($configuserok eq 'ok') { + if ($switchserver) { + $error = &mt("Upload of custom markup is not permitted to this server: [_1]",$switchserver); + } elsif ($author_ok eq 'ok') { + foreach my $lonhost (@newhosts) { + my $formelem = 'loginheadtag_'.$lonhost; + (my $result,$newheadtagurls{$lonhost}) = &publishlogo($r,'upload',$formelem,$dom,$confname, + "login/headtag/$lonhost",'','', + $env{'form.loginheadtag_'.$lonhost.'.filename'}); + if ($result eq 'ok') { + $loginhash{'login'}{'headtag'}{$lonhost}{'url'} = $newheadtagurls{$lonhost}; + $changes{'headtag'}{$lonhost} = 1; + if ($possexempt{$lonhost}) { + $loginhash{'login'}{'headtag'}{$lonhost}{'exempt'} = $possexempt{$lonhost}; + } + } else { + my $puberror = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].", + $newheadtagurls{$lonhost},$result); + $errors .= '
  • '.$puberror.'
  • '; + if ((grep(/^\Q$lonhost\E$/,keys(%currheadtagurls))) && + (!grep(/^\Q$lonhost\E$/,@delheadtagurls))) { + $loginhash{'login'}{'headtag'}{$lonhost} = $currheadtagurls{$lonhost}; + } + } + } + } else { + $error = &mt("Upload of custom markup file(s) failed because an author role could not be assigned to a Domain Configuration user ([_1]) in domain: [_2]. Error was: [_3].",$confname,$dom,$author_ok); + } + } else { + $error = &mt("Upload of custom markup file(s) failed because a Domain Configuration user ([_1]) could not be created in domain: [_2]. Error was: [_3].",$confname,$dom,$configuserok); + } + if ($error) { + &Apache::lonnet::logthis($error); + $errors .= '
  • '.$error.'
  • '; + } + } + &process_captcha('login',\%changes,$loginhash{'login'},$domconfig{'login'}); + + my $defaulthelpfile = '/adm/loginproblems.html'; + my $defaulttext = &mt('Default in use'); + my $putresult = &Apache::lonnet::put_dom('configuration',\%loginhash, $dom); if ($putresult eq 'ok') { - if (($domconfig{'login'}{'coursecatalog'} eq '0') && - ($env{'form.coursecatalog'} eq '1')) { - $changes{'coursecatalog'} = 1; - } elsif (($domconfig{'login'}{'coursecatalog'} eq '' || - $domconfig{'login'}{'coursecatalog'} eq '1') && - ($env{'form.coursecatalog'} eq '0')) { - $changes{'coursecatalog'} = 1; - } - if (($domconfig{'login'}{'adminmail'} eq '1') && - ($env{'form.adminmail'} eq '0')) { - $changes{'adminmail'} = 1; - } elsif (($domconfig{'login'}{'adminmail'} eq '' || - $domconfig{'login'}{'adminmail'} eq '0') && - ($env{'form.adminmail'} eq '1')) { - $changes{'adminmail'} = 1; - } - if (($domconfig{'login'}{'loginheader'} eq 'text') && - ($env{'form.loginheader'} eq 'image')) { - $changes{'loginheader'} = 1; - } elsif (($domconfig{'login'}{'loginheader'} eq '' || - $domconfig{'login'}{'loginheader'} eq 'image') && - ($env{'form.loginheader'} eq 'text')) { - $changes{'loginheader'} = 1; + my @toggles = ('coursecatalog','adminmail','helpdesk','newuser'); + my %defaultchecked = ( + 'coursecatalog' => 'on', + 'helpdesk' => 'on', + 'adminmail' => 'off', + 'newuser' => 'off', + ); + if (ref($domconfig{'login'}) eq 'HASH') { + foreach my $item (@toggles) { + if ($defaultchecked{$item} eq 'on') { + if (($domconfig{'login'}{$item} eq '0') && + ($env{'form.'.$item} eq '1')) { + $changes{$item} = 1; + } elsif (($domconfig{'login'}{$item} eq '' || + $domconfig{'login'}{$item} eq '1') && + ($env{'form.'.$item} eq '0')) { + $changes{$item} = 1; + } + } elsif ($defaultchecked{$item} eq 'off') { + if (($domconfig{'login'}{$item} eq '1') && + ($env{'form.'.$item} eq '0')) { + $changes{$item} = 1; + } elsif (($domconfig{'login'}{$item} eq '' || + $domconfig{'login'}{$item} eq '0') && + ($env{'form.'.$item} eq '1')) { + $changes{$item} = 1; + } + } + } } if (keys(%changes) > 0 || $colchgtext) { &Apache::loncommon::devalidate_domconfig_cache($dom); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domainconfig'} = 1; + } $resulttext = &mt('Changes made:').'
      '; foreach my $item (sort(keys(%changes))) { - if ($item eq 'loginheader') { - $resulttext .= '
    • '.&mt("$title{$item} set to $env{'form.loginheader'}").'
    • '; + if ($item eq 'loginvia') { + if (ref($changes{$item}) eq 'HASH') { + $resulttext .= '
    • '.&mt('Log-in page availability:').'
        '; + foreach my $lonhost (sort(keys(%{$changes{$item}}))) { + if (defined($servers{$loginhash{login}{loginvia}{$lonhost}{'server'}})) { + if (ref($loginhash{login}{loginvia}{$lonhost}) eq 'HASH') { + my $protocol = $Apache::lonnet::protocol{$env{'form.'.$lonhost.'_server'}}; + $protocol = 'http' if ($protocol ne 'https'); + my $target = $protocol.'://'.$servers{$env{'form.'.$lonhost.'_server'}}; + + if ($loginhash{login}{loginvia}{$lonhost}{'serverpath'} eq 'custom') { + $target .= $loginhash{login}{loginvia}{$lonhost}{'custompath'}; + } else { + $target .= $loginhash{login}{loginvia}{$lonhost}{'serverpath'}; + } + $resulttext .= '
      • '.&mt('Server: [_1] log-in page redirects to [_2].',$servers{$lonhost},''.$target.''); + if ($loginhash{login}{loginvia}{$lonhost}{'exempt'} ne '') { + $resulttext .= ' '.&mt('No redirection for clients from following IPs:').' '.$loginhash{login}{loginvia}{$lonhost}{'exempt'}; + } + $resulttext .= '
      • '; + } else { + $resulttext .= '
      • '.&mt('Server: [_1] has standard log-in page.',$lonhost).'
      • '; + } + } else { + $resulttext .= '
      • '.&mt('Server: [_1] has standard log-in page.',$servers{$lonhost}).'
      • '; + } + } + $resulttext .= '
    • '; + } + } elsif ($item eq 'helpurl') { + if (ref($changes{$item}) eq 'HASH') { + foreach my $lang (sort(keys(%{$changes{$item}}))) { + if (grep(/^\Q$lang\E$/,@delurls)) { + my ($chg,$link); + $link = &Apache::loncommon::modal_link($defaulthelpfile,$defaulttext,600,500); + if ($lang eq 'nolang') { + $chg = &mt('custom log-in help file removed for no preferred language; [_1]',$link); + } else { + $chg = &mt('custom log-in help file removed for specific language: [_1]; [_2]',$langchoices{$lang},$link); + } + $resulttext .= '
    • '.$chg.'
    • '; + } else { + my $chg; + if ($lang eq 'nolang') { + $chg = &mt('custom log-in help file for no preferred language'); + } else { + $chg = &mt('custom log-in help file for specific language: [_1]',$langchoices{$lang}); + } + $resulttext .= '
    • '.&Apache::loncommon::modal_link( + $loginhash{'login'}{'helpurl'}{$lang}. + '?inhibitmenu=yes',$chg,600,500). + '
    • '; + } + } + } + } elsif ($item eq 'headtag') { + if (ref($changes{$item}) eq 'HASH') { + foreach my $lonhost (sort(keys(%{$changes{$item}}))) { + if (grep(/^\Q$lonhost\E$/,@delheadtagurls)) { + $resulttext .= '
    • '.&mt('custom markup file removed for [_1]',$domservers{$lonhost}).'
    • '; + } elsif (ref($loginhash{'login'}{'headtag'}{$lonhost}) eq 'HASH') { + $resulttext .= '
    • '.&mt('custom markup').' '.&mt('(for [_1])',$servers{$lonhost}).' '; + if ($possexempt{$lonhost}) { + $resulttext .= &mt('not included for client IP(s): [_1]',$possexempt{$lonhost}); + } else { + $resulttext .= &mt('included for any client IP'); + } + $resulttext .= '
    • '; + } + } + } + } elsif ($item eq 'captcha') { + if (ref($loginhash{'login'}) eq 'HASH') { + my $chgtxt; + if ($loginhash{'login'}{$item} eq 'notused') { + $chgtxt .= &mt('No CAPTCHA validation in use for helpdesk form.'); + } else { + my %captchas = &captcha_phrases(); + if ($captchas{$loginhash{'login'}{$item}}) { + $chgtxt .= &mt("Validation for helpdesk form set to $captchas{$loginhash{'login'}{$item}}."); + } else { + $chgtxt .= &mt('Validation for helpdesk form set to unknown type.'); + } + } + $resulttext .= '
    • '.$chgtxt.'
    • '; + } + } elsif ($item eq 'recaptchakeys') { + if (ref($loginhash{'login'}) eq 'HASH') { + my ($privkey,$pubkey); + if (ref($loginhash{'login'}{$item}) eq 'HASH') { + $pubkey = $loginhash{'login'}{$item}{'public'}; + $privkey = $loginhash{'login'}{$item}{'private'}; + } + my $chgtxt .= &mt('ReCAPTCHA keys changes').'
        '; + if (!$pubkey) { + $chgtxt .= '
      • '.&mt('Public key deleted').'
      • '; + } else { + $chgtxt .= '
      • '.&mt('Public key set to [_1]',$pubkey).'
      • '; + } + if (!$privkey) { + $chgtxt .= '
      • '.&mt('Private key deleted').'
      • '; + } else { + $chgtxt .= '
      • '.&mt('Private key set to [_1]',$privkey).'
      • '; + } + $chgtxt .= '
      '; + $resulttext .= '
    • '.$chgtxt.'
    • '; + } + } elsif ($item eq 'recaptchaversion') { + if (ref($loginhash{'login'}) eq 'HASH') { + if ($loginhash{'login'}{'captcha'} eq 'recaptcha') { + $resulttext .= '
    • '.&mt('ReCAPTCHA for helpdesk form set to version [_1]',$loginhash{'login'}{'recaptchaversion'}). + '
    • '; + } + } } else { $resulttext .= '
    • '.&mt("$title{$item} set to $offon[$env{'form.'.$item}]").'
    • '; } @@ -1965,13 +9793,36 @@ sub modify_login { return $resulttext; } +sub check_exempt_addresses { + my ($iplist) = @_; + $iplist =~ s/^\s+//; + $iplist =~ s/\s+$//; + my @poss_ips = split(/\s*[,:]\s*/,$iplist); + my (@okips,$new); + foreach my $ip (@poss_ips) { + if ($ip =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) { + if (($1 <= 255) && ($2 <= 255) && ($3 <= 255) && ($4 <= 255)) { + push(@okips,$ip); + } + } + } + if (@okips > 0) { + $new = join(',',@okips); + } else { + $new = ''; + } + return $new; +} + sub color_font_choices { my %choices = &Apache::lonlocal::texthash ( img => "Header", bgs => "Background colors", links => "Link colors", + images => "Images", font => "Font color", + fontmenu => "Font menu", pgbg => "Page", tabbg => "Header", sidebg => "Border", @@ -1983,9 +9834,14 @@ sub color_font_choices { } sub modify_rolecolors { - my ($r,$dom,$confname,$roles,%domconfig) = @_; + my ($r,$dom,$confname,$roles,$lastactref,%domconfig) = @_; my ($resulttext,%rolehash); $rolehash{'rolecolors'} = {}; + if (ref($domconfig{'rolecolors'}) ne 'HASH') { + if ($domconfig{'rolecolors'} eq '') { + $domconfig{'rolecolors'} = {}; + } + } my ($errors,%changes) = &modify_colors($r,$dom,$confname,$roles, $domconfig{'rolecolors'},$rolehash{'rolecolors'}); my $putresult = &Apache::lonnet::put_dom('configuration',\%rolehash, @@ -1993,6 +9849,9 @@ sub modify_rolecolors { if ($putresult eq 'ok') { if (keys(%changes) > 0) { &Apache::loncommon::devalidate_domconfig_cache($dom); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domainconfig'} = 1; + } $resulttext = &display_colorchgs($dom,\%changes,$roles, $rolehash{'rolecolors'}); } else { @@ -2018,6 +9877,7 @@ sub modify_colors { my @images; my $servadm = $r->dir_config('lonAdmEMail'); my $errors; + my %defaults; foreach my $role (@{$roles}) { if ($role eq 'login') { %choices = &login_choices(); @@ -2032,25 +9892,61 @@ sub modify_colors { @images = ('img'); @bgs = ('pgbg','tabbg','sidebg'); } - $confhash->{$role}{'font'} = $env{'form.'.$role.'_font'}; - foreach my $item (@bgs,@links,@logintext) { - $confhash->{$role}{$item} = $env{'form.'.$role.'_'.$item}; - } - my ($configuserok,$author_ok,$switchserver,%currroles); - my $uhome = &Apache::lonnet::homeserver($confname,$dom,1); - ($configuserok,%currroles) = &check_configuser($uhome,$dom, - $confname,$servadm); - if ($configuserok eq 'ok') { - $switchserver = &check_switchserver($dom,$confname); - if ($switchserver eq '') { - $author_ok = &check_authorstatus($dom,$confname,%currroles); + my %defaults = &role_defaults($role,\@bgs,\@links,\@images,\@logintext); + unless ($env{'form.'.$role.'_font'} eq $defaults{'font'}) { + $confhash->{$role}{'font'} = $env{'form.'.$role.'_font'}; + } + if ($role eq 'login') { + foreach my $item (@logintext) { + $env{'form.'.$role.'_'.$item} = lc($env{'form.'.$role.'_'.$item}); + if ($env{'form.'.$role.'_'.$item} =~ /^\w+/) { + $env{'form.'.$role.'_'.$item} = '#'.$env{'form.'.$role.'_'.$item}; + } + unless ($env{'form.'.$role.'_'.$item} eq lc($defaults{'logintext'}{$item})) { + $confhash->{$role}{$item} = $env{'form.'.$role.'_'.$item}; + } + } + } else { + $env{'form.'.$role.'_fontmenu'} = lc($env{'form.'.$role.'_fontmenu'}); + if ($env{'form.'.$role.'_fontmenu'} =~ /^\w+/) { + $env{'form.'.$role.'_fontmenu'} = '#'.$env{'form.'.$role.'_fontmenu'}; + } + unless($env{'form.'.$role.'_fontmenu'} eq lc($defaults{'fontmenu'})) { + $confhash->{$role}{'fontmenu'} = $env{'form.'.$role.'_fontmenu'}; + } + } + foreach my $item (@bgs) { + $env{'form.'.$role.'_'.$item} = lc($env{'form.'.$role.'_'.$item}); + if ($env{'form.'.$role.'_'.$item} =~ /^\w+/) { + $env{'form.'.$role.'_'.$item} = '#'.$env{'form.'.$role.'_'.$item}; + } + unless ($env{'form.'.$role.'_'.$item} eq lc($defaults{'bgs'}{$item})) { + $confhash->{$role}{$item} = $env{'form.'.$role.'_'.$item}; } } + foreach my $item (@links) { + $env{'form.'.$role.'_'.$item} = lc($env{'form.'.$role.'_'.$item}); + if ($env{'form.'.$role.'_'.$item} =~ /^\w+/) { + $env{'form.'.$role.'_'.$item} = '#'.$env{'form.'.$role.'_'.$item}; + } + unless ($env{'form.'.$role.'_'.$item} eq lc($defaults{'links'}{$item})) { + $confhash->{$role}{$item} = $env{'form.'.$role.'_'.$item}; + } + } + my ($configuserok,$author_ok,$switchserver) = + &config_check($dom,$confname,$servadm); my ($width,$height) = &thumb_dimensions(); if (ref($domconfig->{$role}) ne 'HASH') { $domconfig->{$role} = {}; } foreach my $img (@images) { + if (($role eq 'login') && (($img eq 'img') || ($img eq 'logo'))) { + if (defined($env{'form.login_showlogo_'.$img})) { + $confhash->{$role}{'showlogo'}{$img} = 1; + } else { + $confhash->{$role}{'showlogo'}{$img} = 0; + } + } if ( ! $env{'form.'.$role.'_'.$img.'.filename'} && !defined($domconfig->{$role}{$img}) && !$env{'form.'.$role.'_del_'.$img} @@ -2077,11 +9973,11 @@ sub modify_colors { $error = &mt("Upload of [_1] image for $role page(s) failed because an error occurred publishing the file in RES space. Error was: [_2].",$choices{img},$result); } } else { - $error = &mt("Upload of [_1] image for $role page(s) failed because an author role could not be assigned to a Domain Configuation user ([_2]) in domain: [_3]. Error was: [_4].",$choices{$img},$confname,$dom,$author_ok); + $error = &mt("Upload of [_1] image for $role page(s) failed because an author role could not be assigned to a Domain Configuration user ([_2]) in domain: [_3]. Error was: [_4].",$choices{$img},$confname,$dom,$author_ok); } } } else { - $error = &mt("Upload of [_1] image for $role page(s) failed because a Domain Configuation user ([_2]) could not be created in domain: [_3]. Error was: [_4].",$choices{$img},$confname,$dom,$configuserok); + $error = &mt("Upload of [_1] image for $role page(s) failed because a Domain Configuration user ([_2]) could not be created in domain: [_3]. Error was: [_4].",$choices{$img},$confname,$dom,$configuserok); } if ($error) { &Apache::lonnet::logthis($error); @@ -2125,7 +10021,19 @@ sub modify_colors { $changes{$role}{'images'}{$img} = 1; } } - } + if (($role eq 'login') && (($img eq 'logo') || ($img eq 'img'))) { + if (ref($domconfig->{'login'}{'showlogo'}) eq 'HASH') { + if ($confhash->{$role}{'showlogo'}{$img} ne + $domconfig->{$role}{'showlogo'}{$img}) { + $changes{$role}{'showlogo'}{$img} = 1; + } + } else { + if ($confhash->{$role}{'showlogo'}{$img} == 0) { + $changes{$role}{'showlogo'}{$img} = 1; + } + } + } + } if ($domconfig->{$role}{'font'} ne '') { if ($confhash->{$role}{'font'} ne $domconfig->{$role}{'font'}) { $changes{$role}{'font'} = 1; @@ -2135,6 +10043,17 @@ sub modify_colors { $changes{$role}{'font'} = 1; } } + if ($role ne 'login') { + if ($domconfig->{$role}{'fontmenu'} ne '') { + if ($confhash->{$role}{'fontmenu'} ne $domconfig->{$role}{'fontmenu'}) { + $changes{$role}{'fontmenu'} = 1; + } + } else { + if ($confhash->{$role}{'fontmenu'}) { + $changes{$role}{'fontmenu'} = 1; + } + } + } foreach my $item (@bgs) { if ($domconfig->{$role}{$item} ne '') { if ($confhash->{$role}{$item} ne $domconfig->{$role}{$item}) { @@ -2180,6 +10099,21 @@ sub modify_colors { return ($errors,%changes); } +sub config_check { + my ($dom,$confname,$servadm) = @_; + my ($configuserok,$author_ok,$switchserver,%currroles); + my $uhome = &Apache::lonnet::homeserver($confname,$dom,1); + ($configuserok,%currroles) = &check_configuser($uhome,$dom, + $confname,$servadm); + if ($configuserok eq 'ok') { + $switchserver = &check_switchserver($dom,$confname); + if ($switchserver eq '') { + $author_ok = &check_authorstatus($dom,$confname,%currroles); + } + } + return ($configuserok,$author_ok,$switchserver); +} + sub default_change_checker { my ($role,$images,$links,$bgs,$logintext,$confhash,$changes) = @_; foreach my $item (@{$links}) { @@ -2202,11 +10136,16 @@ sub default_change_checker { $confhash->{$role}{$img} = ''; $changes->{$role}{'images'}{$img} = 1; } + if ($role eq 'login') { + if ($confhash->{$role}{'showlogo'}{$img} == 0) { + $changes->{$role}{'showlogo'}{$img} = 1; + } + } } if ($confhash->{$role}{'font'}) { $changes->{$role}{'font'} = 1; } -} +} sub display_colorchgs { my ($dom,$changes,$roles,$confhash) = @_; @@ -2233,12 +10172,18 @@ sub display_colorchgs { $resulttext .= '
    • '.&mt($choices{$key}).':
        '; } foreach my $item (sort(keys(%{$changes->{$role}{$key}}))) { - if ($confhash->{$role}{$item} eq '') { + if (($role eq 'login') && ($key eq 'showlogo')) { + if ($confhash->{$role}{$key}{$item}) { + $resulttext .= '
      • '.&mt("$choices{$item} set to be displayed").'
      • '; + } else { + $resulttext .= '
      • '.&mt("$choices{$item} set to not be displayed").'
      • '; + } + } elsif ($confhash->{$role}{$item} eq '') { $resulttext .= '
      • '.&mt("$choices{$item} set to default").'
      • '; } else { my $newitem = $confhash->{$role}{$item}; if ($key eq 'images') { - $newitem = ''.$choices{$item}.''; + $newitem = ''.$choices{$item}.''; } $resulttext .= '
      • '.&mt("$choices{$item} set to [_1]",$newitem).'
      • '; } @@ -2311,7 +10256,7 @@ sub check_authorstatus { my $end = 0; $author_ok = &Apache::lonnet::assignrole($dom,$confname,'/'.$dom.'/', - 'au',$end,$start); + 'au',$end,$start,'','','domconfig'); } else { $author_ok = 'ok'; } @@ -2319,27 +10264,32 @@ sub check_authorstatus { } sub publishlogo { - my ($r,$action,$formname,$dom,$confname,$subdir,$thumbwidth,$thumbheight) = @_; - my ($output,$fname,$logourl); + my ($r,$action,$formname,$dom,$confname,$subdir,$thumbwidth,$thumbheight,$savefileas) = @_; + my ($output,$fname,$logourl,$madethumb); if ($action eq 'upload') { $fname=$env{'form.'.$formname.'.filename'}; chop($env{'form.'.$formname}); } else { ($fname) = ($formname =~ /([^\/]+)$/); } + if ($savefileas ne '') { + $fname = $savefileas; + } $fname=&Apache::lonnet::clean_filename($fname); # See if there is anything left unless ($fname) { return ('error: no uploaded file'); } $fname="$subdir/$fname"; - my $filepath='/home/'.$confname.'/public_html'; + my $docroot=$r->dir_config('lonDocRoot'); + my $filepath="$docroot/priv"; + my $relpath = "$dom/$confname"; my ($fnamepath,$file,$fetchthumb); $file=$fname; if ($fname=~m|/|) { ($fnamepath,$file) = ($fname =~ m|^(.*)/([^/]+)$|); } - my @parts=split(/\//,$filepath.'/'.$fnamepath); + my @parts=split(/\//,"$filepath/$relpath/$fnamepath"); my $count; - for ($count=4;$count<=$#parts;$count++) { + for ($count=5;$count<=$#parts;$count++) { $filepath.="/$parts[$count]"; if ((-e $filepath)!=1) { mkdir($filepath,02770); @@ -2349,25 +10299,25 @@ sub publishlogo { if ($file=~/\.(\w+)$/ && (&Apache::loncommon::fileembstyle($1) eq 'hdn')) { $output = - &mt('Invalid file extension ([_1]) - reserved for LONCAPA use.',$1); + &mt('Invalid file extension ([_1]) - reserved for internal use.',$1); } elsif ($file=~/\.(\w+)$/ && !defined(&Apache::loncommon::fileembstyle($1))) { $output = &mt('Unrecognized file extension ([_1]) - rename the file with a proper extension and re-upload.',$1); } elsif ($file=~/\.(\d+)\.(\w+)$/) { - $output = &mt('File name not allowed a rename the file to remove the number immediately before the file extension([_1]) and re-upload.',$2); + $output = &mt('Filename not allowed - rename the file to remove the number immediately before the file extension([_1]) and re-upload.',$2); } elsif (-d "$filepath/$file") { - $output = &mt('File name is a directory name - rename the file and re-upload'); + $output = &mt('Filename is a directory name - rename the file and re-upload'); } else { my $source = $filepath.'/'.$file; my $logfile; - if (!open($logfile,">>$source".'.log')) { - return (&mt('No write permission to Construction Space')); + if (!open($logfile,">>",$source.'.log')) { + return (&mt('No write permission to Authoring Space')); } print $logfile "\n================= Publish ".localtime()." ================\n". $env{'user.name'}.':'.$env{'user.domain'}."\n"; # Save the file - if (!open(FH,'>'.$source)) { + if (!open(FH,">",$source)) { &Apache::lonnet::logthis('Failed to create '.$source); return (&mt('Failed to create file')); } @@ -2386,7 +10336,6 @@ $env{'user.name'}.':'.$env{'user.domain' close(FH); chmod(0660, $source); # Permissions to rw-rw---. - my $docroot=$r->dir_config('lonDocRoot'); my $targetdir=$docroot.'/res/'.$dom.'/'.$confname .'/'.$fnamepath; my $copyfile=$targetdir.'/'.$file; @@ -2409,8 +10358,15 @@ $env{'user.name'}.':'.$env{'user.domain' if (copy($source,$copyfile)) { print $logfile "\nCopied original source to ".$copyfile."\n"; $output = 'ok'; - &write_metadata($dom,$confname,$formname,$targetdir,$file,$logfile); $logourl = '/res/'.$dom.'/'.$confname.'/'.$fname; + push(@{$modified_urls},[$copyfile,$source]); + my $metaoutput = + &write_metadata($dom,$confname,$formname,$targetdir,$file,$logfile); + unless ($registered_cleanup) { + my $handlers = $r->get_handlers('PerlCleanupHandler'); + $r->set_handlers('PerlCleanupHandler' => [\¬ifysubscribed,@{$handlers}]); + $registered_cleanup=1; + } } else { print $logfile "\nUnable to write ".$copyfile.':'.$!."\n"; $output = &mt('Failed to copy file to RES space').", $!"; @@ -2422,14 +10378,23 @@ $env{'user.name'}.':'.$env{'user.domain' if ($fullwidth ne '' && $fullheight ne '') { if ($fullwidth > $thumbwidth && $fullheight > $thumbheight) { my $thumbsize = $thumbwidth.'x'.$thumbheight; - system("convert -sample $thumbsize $inputfile $outfile"); + my @args = ('convert','-sample',$thumbsize,$inputfile,$outfile); + system({$args[0]} @args); chmod(0660, $filepath.'/tn-'.$file); if (-e $outfile) { my $copyfile=$targetdir.'/tn-'.$file; if (copy($outfile,$copyfile)) { print $logfile "\nCopied source to ".$copyfile."\n"; - &write_metadata($dom,$confname,$formname, - $targetdir,'tn-'.$file,$logfile); + my $thumb_metaoutput = + &write_metadata($dom,$confname,$formname, + $targetdir,'tn-'.$file,$logfile); + push(@{$modified_urls},[$copyfile,$outfile]); + unless ($registered_cleanup) { + my $handlers = $r->get_handlers('PerlCleanupHandler'); + $r->set_handlers('PerlCleanupHandler' => [\¬ifysubscribed,@{$handlers}]); + $registered_cleanup=1; + } + $madethumb = 1; } else { print $logfile "\nUnable to write ".$copyfile. ':'.$!."\n"; @@ -2442,7 +10407,7 @@ $env{'user.name'}.':'.$env{'user.domain' $output = $versionresult; } } - return ($output,$logourl); + return ($output,$logourl,$madethumb); } sub logo_versioning { @@ -2494,30 +10459,79 @@ sub write_metadata { { print $logfile "\nWrite metadata file for ".$targetdir.'/'.$file; my $mfh; - unless (open($mfh,'>'.$targetdir.'/'.$file.'.meta')) { + if (open($mfh,">",$targetdir.'/'.$file.'.meta')) { + foreach (sort(keys(%metadatafields))) { + unless ($_=~/\./) { + my $unikey=$_; + $unikey=~/^([A-Za-z]+)/; + my $tag=$1; + $tag=~tr/A-Z/a-z/; + print $mfh "\n\<$tag"; + foreach (split(/\,/,$metadatakeys{$unikey})) { + my $value=$metadatafields{$unikey.'.'.$_}; + $value=~s/\"/\'\'/g; + print $mfh ' '.$_.'="'.$value.'"'; + } + print $mfh '>'. + &HTML::Entities::encode($metadatafields{$unikey},'<>&"') + .''; + } + } + $output = 'ok'; + print $logfile "\nWrote metadata"; + close($mfh); + } else { + print $logfile "\nFailed to open metadata file"; $output = &mt('Could not write metadata'); } - foreach (sort keys %metadatafields) { - unless ($_=~/\./) { - my $unikey=$_; - $unikey=~/^([A-Za-z]+)/; - my $tag=$1; - $tag=~tr/A-Z/a-z/; - print $mfh "\n\<$tag"; - foreach (split(/\,/,$metadatakeys{$unikey})) { - my $value=$metadatafields{$unikey.'.'.$_}; - $value=~s/\"/\'\'/g; - print $mfh ' '.$_.'="'.$value.'"'; - } - print $mfh '>'. - &HTML::Entities::encode($metadatafields{$unikey},'<>&"') - .''; - } - } - $output = 'ok'; - print $logfile "\nWrote metadata"; - close($mfh); } + return $output; +} + +sub notifysubscribed { + foreach my $targetsource (@{$modified_urls}){ + next unless (ref($targetsource) eq 'ARRAY'); + my ($target,$source)=@{$targetsource}; + if ($source ne '') { + if (open(my $logfh,">>",$source.'.log')) { + print $logfh "\nCleanup phase: Notifications\n"; + my @subscribed=&subscribed_hosts($target); + foreach my $subhost (@subscribed) { + print $logfh "\nNotifying host ".$subhost.':'; + my $reply=&Apache::lonnet::critical('update:'.$target,$subhost); + print $logfh $reply; + } + my @subscribedmeta=&subscribed_hosts("$target.meta"); + foreach my $subhost (@subscribedmeta) { + print $logfh "\nNotifying host for metadata only ".$subhost.':'; + my $reply=&Apache::lonnet::critical('update:'.$target.'.meta', + $subhost); + print $logfh $reply; + } + print $logfh "\n============ Done ============\n"; + close($logfh); + } + } + } + return OK; +} + +sub subscribed_hosts { + my ($target) = @_; + my @subscribed; + if (open(my $fh,"<","$target.subscription")) { + while (my $subline=<$fh>) { + if ($subline =~ /^($match_lonid):/) { + my $host = $1; + if ($host ne $Apache::lonnet::perlvar{'lonHostID'}) { + unless (grep(/^\Q$host\E$/,@subscribed)) { + push(@subscribed,$host); + } + } + } + } + } + return @subscribed; } sub check_switchserver { @@ -2530,82 +10544,1914 @@ sub check_switchserver { my @ids=&Apache::lonnet::current_machine_ids(); foreach my $id (@ids) { if ($id eq $home) { $allowed=1; } } if (!$allowed) { - $switchserver=''.&mt('Switch Server').''; + $switchserver=''.&mt('Switch Server').''; } return $switchserver; } -sub javascript_set_colnums { - return < 1100) { - document.pickactions.numcols[1].checked = true; +sub modify_quotas { + my ($r,$dom,$action,$lastactref,%domconfig) = @_; + my ($context,@usertools,@options,%validations,%titles,%confhash,%toolshash, + %limithash,$toolregexp,%conditions,$resulttext,%changes,$confname,$configuserok, + $author_ok,$switchserver,$errors,$validationitemsref,$validationnamesref, + $validationfieldsref); + if ($action eq 'quotas') { + $context = 'tools'; } else { - document.pickactions.numcols[0].checked = true; + $context = $action; } -} -END -} - -sub modify_quotas { - my ($dom,%domconfig) = @_; - my ($resulttext,%changes); - my ($othertitle,$usertypes,$types) = &sorted_inst_types($dom); - my %formhash; + if ($context eq 'requestcourses') { + @usertools = ('official','unofficial','community','textbook','placement','lti'); + @options =('norequest','approval','validate','autolimit'); + %validations = &Apache::lonnet::auto_courserequest_checks($dom); + %titles = &courserequest_titles(); + $toolregexp = join('|',@usertools); + %conditions = &courserequest_conditions(); + $confname = $dom.'-domainconfig'; + my $servadm = $r->dir_config('lonAdmEMail'); + ($configuserok,$author_ok,$switchserver) = &config_check($dom,$confname,$servadm); + ($validationitemsref,$validationnamesref,$validationfieldsref) = + &Apache::loncoursequeueadmin::requestcourses_validation_types(); + } elsif ($context eq 'requestauthor') { + @usertools = ('author'); + %titles = &authorrequest_titles(); + } else { + @usertools = ('aboutme','blog','webdav','portfolio'); + %titles = &tool_titles(); + } + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); + my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); foreach my $key (keys(%env)) { - if ($key =~ /^form\.quota_(.+)$/) { - $formhash{$1} = $env{$key}; + if ($context eq 'requestcourses') { + if ($key =~ /^form\.crsreq_($toolregexp)_(.+)$/) { + my $item = $1; + my $type = $2; + if ($type =~ /^limit_(.+)/) { + $limithash{$item}{$1} = $env{$key}; + } else { + $confhash{$item}{$type} = $env{$key}; + } + } + } elsif ($context eq 'requestauthor') { + if ($key =~ /^\Qform.authorreq_\E(.+)$/) { + $confhash{$1} = $env{$key}; + } + } else { + if ($key =~ /^form\.quota_(.+)$/) { + $confhash{'defaultquota'}{$1} = $env{$key}; + } elsif ($key =~ /^form\.authorquota_(.+)$/) { + $confhash{'authorquota'}{$1} = $env{$key}; + } elsif ($key =~ /^form\.\Q$context\E_(.+)$/) { + @{$toolshash{$1}} = &Apache::loncommon::get_env_multiple($key); + } } } - $formhash{'default'} = $env{'form.defaultquota'}; - if (ref($domconfig{'quotas'}) eq 'HASH') { - foreach my $key (keys(%{$domconfig{'quotas'}})) { - if (exists($formhash{$key})) { - if ($formhash{$key} ne $domconfig{'quotas'}{$key}) { - $changes{$key} = 1; + if (($context eq 'requestcourses') || ($context eq 'requestauthor')) { + my @approvalnotify = &Apache::loncommon::get_env_multiple('form.'.$context.'notifyapproval'); + @approvalnotify = sort(@approvalnotify); + $confhash{'notify'}{'approval'} = join(',',@approvalnotify); + my @crstypes = ('official','unofficial','community','textbook','placement','lti'); + my @hasuniquecode = &Apache::loncommon::get_env_multiple('form.uniquecode'); + foreach my $type (@hasuniquecode) { + if (grep(/^\Q$type\E$/,@crstypes)) { + $confhash{'uniquecode'}{$type} = 1; + } + } + my (%newbook,%allpos); + if ($context eq 'requestcourses') { + foreach my $type ('textbooks','templates') { + @{$allpos{$type}} = (); + my $invalid; + if ($type eq 'textbooks') { + $invalid = &mt('Invalid LON-CAPA course for textbook'); + } else { + $invalid = &mt('Invalid LON-CAPA course for template'); + } + if ($env{'form.'.$type.'_addbook'}) { + if (($env{'form.'.$type.'_addbook_cnum'} =~ /^$match_courseid$/) && + ($env{'form.'.$type.'_addbook_cdom'} =~ /^$match_domain$/)) { + if (&Apache::lonnet::homeserver($env{'form.'.$type.'_addbook_cnum'}, + $env{'form.'.$type.'_addbook_cdom'}) eq 'no_host') { + $errors .= '
      • '.$invalid.'
      • '; + } else { + $newbook{$type} = $env{'form.'.$type.'_addbook_cdom'}.'_'.$env{'form.'.$type.'_addbook_cnum'}; + my $position = $env{'form.'.$type.'_addbook_pos'}; + $position =~ s/\D+//g; + if ($position ne '') { + $allpos{$type}[$position] = $newbook{$type}; + } + } + } else { + $errors .= '
      • '.$invalid.'
      • '; + } + } + } + } + if (ref($domconfig{$action}) eq 'HASH') { + if (ref($domconfig{$action}{'notify'}) eq 'HASH') { + if ($domconfig{$action}{'notify'}{'approval'} ne $confhash{'notify'}{'approval'}) { + $changes{'notify'}{'approval'} = 1; } } else { - $formhash{$key} = $domconfig{'quotas'}{$key}; + if ($confhash{'notify'}{'approval'}) { + $changes{'notify'}{'approval'} = 1; + } + } + if (ref($domconfig{$action}{'uniquecode'}) eq 'HASH') { + if (ref($confhash{'uniquecode'}) eq 'HASH') { + foreach my $crstype (keys(%{$domconfig{$action}{'uniquecode'}})) { + unless ($confhash{'uniquecode'}{$crstype}) { + $changes{'uniquecode'} = 1; + } + } + unless ($changes{'uniquecode'}) { + foreach my $crstype (keys(%{$confhash{'uniquecode'}})) { + unless ($domconfig{$action}{'uniquecode'}{$crstype}) { + $changes{'uniquecode'} = 1; + } + } + } + } else { + $changes{'uniquecode'} = 1; + } + } elsif (ref($confhash{'uniquecode'}) eq 'HASH') { + $changes{'uniquecode'} = 1; + } + if ($context eq 'requestcourses') { + foreach my $type ('textbooks','templates') { + if (ref($domconfig{$action}{$type}) eq 'HASH') { + my %deletions; + my @todelete = &Apache::loncommon::get_env_multiple('form.'.$type.'_del'); + if (@todelete) { + map { $deletions{$_} = 1; } @todelete; + } + my %imgdeletions; + my @todeleteimages = &Apache::loncommon::get_env_multiple('form.'.$type.'_image_del'); + if (@todeleteimages) { + map { $imgdeletions{$_} = 1; } @todeleteimages; + } + my $maxnum = $env{'form.'.$type.'_maxnum'}; + for (my $i=0; $i<=$maxnum; $i++) { + my $itemid = $env{'form.'.$type.'_id_'.$i}; + my ($key) = ($itemid =~ /^\Q$type\E_(\w+)$/); + if (ref($domconfig{$action}{$type}{$key}) eq 'HASH') { + if ($deletions{$key}) { + if ($domconfig{$action}{$type}{$key}{'image'}) { + #FIXME need to obsolete item in RES space + } + next; + } else { + my $newpos = $env{'form.'.$itemid}; + $newpos =~ s/\D+//g; + foreach my $item ('subject','title','publisher','author') { + next if ((($item eq 'author') || ($item eq 'publisher')) && + ($type eq 'templates')); + $confhash{$type}{$key}{$item} = $env{'form.'.$type.'_'.$item.'_'.$i}; + if ($domconfig{$action}{$type}{$key}{$item} ne $confhash{$type}{$key}{$item}) { + $changes{$type}{$key} = 1; + } + } + $allpos{$type}[$newpos] = $key; + } + if ($imgdeletions{$key}) { + $changes{$type}{$key} = 1; + #FIXME need to obsolete item in RES space + } elsif ($env{'form.'.$type.'_image_'.$i.'.filename'}) { + my ($cdom,$cnum) = split(/_/,$key); + if (&Apache::lonnet::homeserver($cnum,$cdom) eq 'no_host') { + $errors .= '
      • '.&mt('Image not saved: could not find textbook course').'
      • '; + } else { + my ($imgurl,$error) = &process_textbook_image($r,$dom,$confname,$type.'_image_'.$i, + $cdom,$cnum,$type,$configuserok, + $switchserver,$author_ok); + if ($imgurl) { + $confhash{$type}{$key}{'image'} = $imgurl; + $changes{$type}{$key} = 1; + } + if ($error) { + &Apache::lonnet::logthis($error); + $errors .= '
      • '.$error.'
      • '; + } + } + } elsif ($domconfig{$action}{$type}{$key}{'image'}) { + $confhash{$type}{$key}{'image'} = + $domconfig{$action}{$type}{$key}{'image'}; + } + } + } + } + } + } + } else { + if ($confhash{'notify'}{'approval'}) { + $changes{'notify'}{'approval'} = 1; + } + if (ref($confhash{'uniquecode'} eq 'HASH')) { + $changes{'uniquecode'} = 1; + } + } + if ($context eq 'requestcourses') { + foreach my $type ('textbooks','templates') { + if ($newbook{$type}) { + $changes{$type}{$newbook{$type}} = 1; + foreach my $item ('subject','title','publisher','author') { + next if ((($item eq 'author') || ($item eq 'publisher')) && + ($type eq 'template')); + $env{'form.'.$type.'_addbook_'.$item} =~ s/(`)/'/g; + if ($env{'form.'.$type.'_addbook_'.$item}) { + $confhash{$type}{$newbook{$type}}{$item} = $env{'form.'.$type.'_addbook_'.$item}; + } + } + if ($type eq 'textbooks') { + if ($env{'form.'.$type.'_addbook_image.filename'} ne '') { + my ($cdom,$cnum) = split(/_/,$newbook{$type}); + if (&Apache::lonnet::homeserver($cnum,$cdom) eq 'no_host') { + $errors .= '
      • '.&mt('Image not saved: could not find textbook course').'
      • '; + } else { + my ($imageurl,$error) = + &process_textbook_image($r,$dom,$confname,$type.'_addbook_image',$cdom,$cnum,$type, + $configuserok,$switchserver,$author_ok); + if ($imageurl) { + $confhash{$type}{$newbook{$type}}{'image'} = $imageurl; + } + if ($error) { + &Apache::lonnet::logthis($error); + $errors .= '
      • '.$error.'
      • '; + } + } + } + } + } + if (@{$allpos{$type}} > 0) { + my $idx = 0; + foreach my $item (@{$allpos{$type}}) { + if ($item ne '') { + $confhash{$type}{$item}{'order'} = $idx; + if (ref($domconfig{$action}) eq 'HASH') { + if (ref($domconfig{$action}{$type}) eq 'HASH') { + if (ref($domconfig{$action}{$type}{$item}) eq 'HASH') { + if ($domconfig{$action}{$type}{$item}{'order'} ne $idx) { + $changes{$type}{$item} = 1; + } + } + } + } + $idx ++; + } + } + } + } + if (ref($validationitemsref) eq 'ARRAY') { + foreach my $item (@{$validationitemsref}) { + if ($item eq 'fields') { + my @changed; + @{$confhash{'validation'}{$item}} = &Apache::loncommon::get_env_multiple('form.requestcourses_validation_'.$item); + if (@{$confhash{'validation'}{$item}} > 0) { + @{$confhash{'validation'}{$item}} = sort(@{$confhash{'validation'}{$item}}); + } + if (ref($domconfig{'requestcourses'}) eq 'HASH') { + if (ref($domconfig{'requestcourses'}{'validation'}) eq 'HASH') { + if (ref($domconfig{'requestcourses'}{'validation'}{$item}) eq 'ARRAY') { + @changed = &Apache::loncommon::compare_arrays($confhash{'validation'}{$item}, + $domconfig{'requestcourses'}{'validation'}{$item}); + } else { + @changed = @{$confhash{'validation'}{$item}}; + } + } else { + @changed = @{$confhash{'validation'}{$item}}; + } + } else { + @changed = @{$confhash{'validation'}{$item}}; + } + if (@changed) { + if ($confhash{'validation'}{$item}) { + $changes{'validation'}{$item} = join(', ',@{$confhash{'validation'}{$item}}); + } else { + $changes{'validation'}{$item} = &mt('None'); + } + } + } else { + $confhash{'validation'}{$item} = $env{'form.requestcourses_validation_'.$item}; + if ($item eq 'markup') { + if ($env{'form.requestcourses_validation_'.$item}) { + $env{'form.requestcourses_validation_'.$item} =~ s/[\n\r\f]+/\s/gs; + } + } + if (ref($domconfig{'requestcourses'}) eq 'HASH') { + if (ref($domconfig{'requestcourses'}{'validation'}) eq 'HASH') { + if ($domconfig{'requestcourses'}{'validation'}{$item} ne $confhash{'validation'}{$item}) { + $changes{'validation'}{$item} = $confhash{'validation'}{$item}; + } + } else { + if ($confhash{'validation'}{$item} ne '') { + $changes{'validation'}{$item} = $confhash{'validation'}{$item}; + } + } + } else { + if ($confhash{'validation'}{$item} ne '') { + $changes{'validation'}{$item} = $confhash{'validation'}{$item}; + } + } + } + } + } + if ($env{'form.validationdc'}) { + my $newval = $env{'form.validationdc'}; + my %domcoords = &Apache::lonnet::get_active_domroles($dom,['dc']); + if (exists($domcoords{$newval})) { + $confhash{'validation'}{'dc'} = $newval; + } + } + if (ref($confhash{'validation'}) eq 'HASH') { + if (ref($domconfig{'requestcourses'}) eq 'HASH') { + if (ref($domconfig{'requestcourses'}{'validation'}) eq 'HASH') { + if ($domconfig{'requestcourses'}{'validation'}{'dc'}) { + unless ($confhash{'validation'}{'dc'} eq $domconfig{'requestcourses'}{'validation'}{'dc'}) { + if ($confhash{'validation'}{'dc'} eq '') { + $changes{'validation'}{'dc'} = &mt('None'); + } else { + $changes{'validation'}{'dc'} = $confhash{'validation'}{'dc'}; + } + } + } elsif ($confhash{'validation'}{'dc'} ne '') { + $changes{'validation'}{'dc'} = $confhash{'validation'}{'dc'}; + } + } elsif ($confhash{'validation'}{'dc'} ne '') { + $changes{'validation'}{'dc'} = $confhash{'validation'}{'dc'}; + } + } elsif ($confhash{'validation'}{'dc'} ne '') { + $changes{'validation'}{'dc'} = $confhash{'validation'}{'dc'}; + } + } else { + if (ref($domconfig{'requestcourses'}) eq 'HASH') { + if (ref($domconfig{'requestcourses'}{'validation'}) eq 'HASH') { + if ($domconfig{'requestcourses'}{'validation'}{'dc'}) { + $changes{'validation'}{'dc'} = &mt('None'); + } + } + } } } + } else { + $confhash{'defaultquota'}{'default'} = $env{'form.defaultquota'}; + $confhash{'authorquota'}{'default'} = $env{'form.authorquota'}; } - foreach my $key (keys(%formhash)) { - if ($formhash{$key} ne '') { - if (!exists($domconfig{'quotas'}{$key})) { - $changes{$key} = 1; + foreach my $item (@usertools) { + foreach my $type (@{$types},'default','_LC_adv') { + my $unset; + if ($context eq 'requestcourses') { + $unset = '0'; + if ($type eq '_LC_adv') { + $unset = ''; + } + if ($confhash{$item}{$type} eq 'autolimit') { + $confhash{$item}{$type} .= '='; + unless ($limithash{$item}{$type} =~ /\D/) { + $confhash{$item}{$type} .= $limithash{$item}{$type}; + } + } + } elsif ($context eq 'requestauthor') { + $unset = '0'; + if ($type eq '_LC_adv') { + $unset = ''; + } + } else { + if (grep(/^\Q$type\E$/,@{$toolshash{$item}})) { + $confhash{$item}{$type} = 1; + } else { + $confhash{$item}{$type} = 0; + } + } + if (ref($domconfig{$action}) eq 'HASH') { + if ($action eq 'requestauthor') { + if ($domconfig{$action}{$type} ne $confhash{$type}) { + $changes{$type} = 1; + } + } elsif (ref($domconfig{$action}{$item}) eq 'HASH') { + if ($domconfig{$action}{$item}{$type} ne $confhash{$item}{$type}) { + $changes{$item}{$type} = 1; + } + } else { + if ($context eq 'requestcourses') { + if ($confhash{$item}{$type} ne $unset) { + $changes{$item}{$type} = 1; + } + } else { + if (!$confhash{$item}{$type}) { + $changes{$item}{$type} = 1; + } + } + } + } else { + if ($context eq 'requestcourses') { + if ($confhash{$item}{$type} ne $unset) { + $changes{$item}{$type} = 1; + } + } elsif ($context eq 'requestauthor') { + if ($confhash{$type} ne $unset) { + $changes{$type} = 1; + } + } else { + if (!$confhash{$item}{$type}) { + $changes{$item}{$type} = 1; + } + } } } } + unless (($context eq 'requestcourses') || ($context eq 'requestauthor')) { + if (ref($domconfig{'quotas'}) eq 'HASH') { + if (ref($domconfig{'quotas'}{'defaultquota'}) eq 'HASH') { + foreach my $key (keys(%{$domconfig{'quotas'}{'defaultquota'}})) { + if (exists($confhash{'defaultquota'}{$key})) { + if ($confhash{'defaultquota'}{$key} ne $domconfig{'quotas'}{'defaultquota'}{$key}) { + $changes{'defaultquota'}{$key} = 1; + } + } else { + $confhash{'defaultquota'}{$key} = $domconfig{'quotas'}{'defaultquota'}{$key}; + } + } + } else { + foreach my $key (keys(%{$domconfig{'quotas'}})) { + if (exists($confhash{'defaultquota'}{$key})) { + if ($confhash{'defaultquota'}{$key} ne $domconfig{'quotas'}{$key}) { + $changes{'defaultquota'}{$key} = 1; + } + } else { + $confhash{'defaultquota'}{$key} = $domconfig{'quotas'}{$key}; + } + } + } + if (ref($domconfig{'quotas'}{'authorquota'}) eq 'HASH') { + foreach my $key (keys(%{$domconfig{'quotas'}{'authorquota'}})) { + if (exists($confhash{'authorquota'}{$key})) { + if ($confhash{'authorquota'}{$key} ne $domconfig{'quotas'}{'authorquota'}{$key}) { + $changes{'authorquota'}{$key} = 1; + } + } else { + $confhash{'authorquota'}{$key} = $domconfig{'quotas'}{'authorquota'}{$key}; + } + } + } + } + if (ref($confhash{'defaultquota'}) eq 'HASH') { + foreach my $key (keys(%{$confhash{'defaultquota'}})) { + if (ref($domconfig{'quotas'}) eq 'HASH') { + if (ref($domconfig{'quotas'}{'defaultquota'}) eq 'HASH') { + if (!exists($domconfig{'quotas'}{'defaultquota'}{$key})) { + $changes{'defaultquota'}{$key} = 1; + } + } else { + if (!exists($domconfig{'quotas'}{$key})) { + $changes{'defaultquota'}{$key} = 1; + } + } + } else { + $changes{'defaultquota'}{$key} = 1; + } + } + } + if (ref($confhash{'authorquota'}) eq 'HASH') { + foreach my $key (keys(%{$confhash{'authorquota'}})) { + if (ref($domconfig{'quotas'}) eq 'HASH') { + if (ref($domconfig{'quotas'}{'authorquota'}) eq 'HASH') { + if (!exists($domconfig{'quotas'}{'authorquota'}{$key})) { + $changes{'authorquota'}{$key} = 1; + } + } else { + $changes{'authorquota'}{$key} = 1; + } + } else { + $changes{'authorquota'}{$key} = 1; + } + } + } + } + + if ($context eq 'requestauthor') { + $domdefaults{'requestauthor'} = \%confhash; + } else { + foreach my $key (keys(%confhash)) { + unless (($context eq 'requestcourses') && (($key eq 'textbooks') || ($key eq 'templates'))) { + $domdefaults{$key} = $confhash{$key}; + } + } + } + my %quotahash = ( - quotas => {%formhash}, + $action => { %confhash } ); my $putresult = &Apache::lonnet::put_dom('configuration',\%quotahash, $dom); if ($putresult eq 'ok') { if (keys(%changes) > 0) { + my $cachetime = 24*60*60; + &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domdefaults'} = 1; + } $resulttext = &mt('Changes made:').'
          '; - foreach my $type (@{$types},'default') { - if (defined($changes{$type})) { - my $typetitle = $usertypes->{$type}; - if ($type eq 'default') { - $typetitle = $othertitle; + unless (($context eq 'requestcourses') || + ($context eq 'requestauthor')) { + if (ref($changes{'defaultquota'}) eq 'HASH') { + $resulttext .= '
        • '.&mt('Portfolio default quotas').'
            '; + foreach my $type (@{$types},'default') { + if (defined($changes{'defaultquota'}{$type})) { + my $typetitle = $usertypes->{$type}; + if ($type eq 'default') { + $typetitle = $othertitle; + } + $resulttext .= '
          • '.&mt('[_1] set to [_2] MB',$typetitle,$confhash{'defaultquota'}{$type}).'
          • '; + } + } + $resulttext .= '
        • '; + } + if (ref($changes{'authorquota'}) eq 'HASH') { + $resulttext .= '
        • '.&mt('Authoring Space default quotas').'
            '; + foreach my $type (@{$types},'default') { + if (defined($changes{'authorquota'}{$type})) { + my $typetitle = $usertypes->{$type}; + if ($type eq 'default') { + $typetitle = $othertitle; + } + $resulttext .= '
          • '.&mt('[_1] set to [_2] MB',$typetitle,$confhash{'authorquota'}{$type}).'
          • '; + } + } + $resulttext .= '
        • '; + } + } + my %newenv; + foreach my $item (@usertools) { + my (%haschgs,%inconf); + if ($context eq 'requestauthor') { + %haschgs = %changes; + %inconf = %confhash; + } else { + if (ref($changes{$item}) eq 'HASH') { + %haschgs = %{$changes{$item}}; + } + if (ref($confhash{$item}) eq 'HASH') { + %inconf = %{$confhash{$item}}; + } + } + if (keys(%haschgs) > 0) { + my $newacc = + &Apache::lonnet::usertools_access($env{'user.name'}, + $env{'user.domain'}, + $item,'reload',$context); + if (($context eq 'requestcourses') || + ($context eq 'requestauthor')) { + if ($env{'environment.canrequest.'.$item} ne $newacc) { + $newenv{'environment.canrequest.'.$item} = $newacc; + } + } else { + if ($env{'environment.availabletools.'.$item} ne $newacc) { + $newenv{'environment.availabletools.'.$item} = $newacc; + } + } + unless ($context eq 'requestauthor') { + $resulttext .= '
        • '.$titles{$item}.'
            '; + } + foreach my $type (@{$types},'default','_LC_adv') { + if ($haschgs{$type}) { + my $typetitle = $usertypes->{$type}; + if ($type eq 'default') { + $typetitle = $othertitle; + } elsif ($type eq '_LC_adv') { + $typetitle = 'LON-CAPA Advanced Users'; + } + if ($inconf{$type}) { + if ($context eq 'requestcourses') { + my $cond; + if ($inconf{$type} =~ /^autolimit=(\d*)$/) { + if ($1 eq '') { + $cond = &mt('(Automatic processing of any request).'); + } else { + $cond = &mt('(Automatic processing of requests up to limit of [quant,_1,request] per user).',$1); + } + } else { + $cond = $conditions{$inconf{$type}}; + } + $resulttext .= '
          • '.&mt('Set to be available to [_1].',$typetitle).' '.$cond.'
          • '; + } elsif ($context eq 'requestauthor') { + $resulttext .= '
          • '.&mt('Set to "[_1]" for "[_2]".', + $titles{$inconf{$type}},$typetitle); + + } else { + $resulttext .= '
          • '.&mt('Set to be available to [_1]',$typetitle).'
          • '; + } + } else { + if ($type eq '_LC_adv') { + if ($inconf{$type} eq '0') { + $resulttext .= '
          • '.&mt('Set to be unavailable to [_1]',$typetitle).'
          • '; + } else { + $resulttext .= '
          • '.&mt('No override set for [_1]',$typetitle).'
          • '; + } + } else { + $resulttext .= '
          • '.&mt('Set to be unavailable to [_1]',$typetitle).'
          • '; + } + } + } + } + unless ($context eq 'requestauthor') { + $resulttext .= '
        • '; + } + } + } + if (($action eq 'requestcourses') || ($action eq 'requestauthor')) { + if (ref($changes{'notify'}) eq 'HASH') { + if ($changes{'notify'}{'approval'}) { + if (ref($confhash{'notify'}) eq 'HASH') { + if ($confhash{'notify'}{'approval'}) { + $resulttext .= '
        • '.&mt('Notification of requests requiring approval will be sent to: ').$confhash{'notify'}{'approval'}.'
        • '; + } else { + $resulttext .= '
        • '.&mt('No Domain Coordinators will receive notification of requests requiring approval.').'
        • '; + } + } + } + } + } + if ($action eq 'requestcourses') { + my @offon = ('off','on'); + if ($changes{'uniquecode'}) { + if (ref($confhash{'uniquecode'}) eq 'HASH') { + my $codestr = join(' ',map{ &mt($_); } sort(keys(%{$confhash{'uniquecode'}}))); + $resulttext .= '
        • '. + &mt('Generation of six character code as course identifier for distribution to students set to on for: [_1].',''.$codestr.''). + '
        • '; + } else { + $resulttext .= '
        • '.&mt('Generation of six character code as course identifier for distribution to students set to off.'). + '
        • '; + } + } + foreach my $type ('textbooks','templates') { + if (ref($changes{$type}) eq 'HASH') { + $resulttext .= '
        • '.&mt("Available $type updated").'
            '; + foreach my $key (sort(keys(%{$changes{$type}}))) { + my %coursehash = &Apache::lonnet::coursedescription($key); + my $coursetitle = $coursehash{'description'}; + my $position = $confhash{$type}{$key}{'order'} + 1; + $resulttext .= '
          • '; + foreach my $item ('subject','title','publisher','author') { + next if ((($item eq 'author') || ($item eq 'publisher')) && + ($type eq 'templates')); + my $name = $item.':'; + $name =~ s/^(\w)/\U$1/; + $resulttext .= &mt($name).' '.$confhash{$type}{$key}{$item}.'
            '; + } + $resulttext .= ' '.&mt('Order: [_1]',$position).'
            '; + if ($type eq 'textbooks') { + if ($confhash{$type}{$key}{'image'}) { + $resulttext .= ' '.&mt('Image: [_1]', + 'Textbook cover').'
            '; + } + } + $resulttext .= ' '.&mt('LON-CAPA Course: [_1]',$coursetitle).'
          • '; + } + $resulttext .= '
        • '; + } + } + if (ref($changes{'validation'}) eq 'HASH') { + if ((ref($validationitemsref) eq 'ARRAY') && (ref($validationnamesref) eq 'HASH')) { + $resulttext .= '
        • '.&mt('Validation of courses/communities updated').'
            '; + foreach my $item (@{$validationitemsref}) { + if (exists($changes{'validation'}{$item})) { + if ($item eq 'markup') { + $resulttext .= '
          • '.&mt('[_1] set to: [_2]',$validationnamesref->{$item}, + '
            '.$changes{'validation'}{$item}.'
            ').'
          • '; + } else { + $resulttext .= '
          • '.&mt('[_1] set to: [_2]',$validationnamesref->{$item}, + ''.$changes{'validation'}{$item}.'').'
          • '; + } + } + } + if (exists($changes{'validation'}{'dc'})) { + $resulttext .= '
          • '.&mt('Validated course requests identified as processed by: [_1]', + ''.$changes{'validation'}{'dc'}.'').'
          • '; + } } - $resulttext .= '
          • '.&mt('[_1] set to [_2] Mb',$typetitle,$formhash{$type}).'
          • '; } } $resulttext .= '
          '; + if (keys(%newenv)) { + &Apache::lonnet::appenv(\%newenv); + } } else { - $resulttext = &mt('No changes made to default quotas'); + if ($context eq 'requestcourses') { + $resulttext = &mt('No changes made to rights to request creation of courses.'); + } elsif ($context eq 'requestauthor') { + $resulttext = &mt('No changes made to rights to request author space.'); + } else { + $resulttext = &mt('No changes made to availability of personal information pages, blogs, portfolios or default quotas'); + } } } else { $resulttext = ''. &mt('An error occurred: [_1]',$putresult).''; } + if ($errors) { + $resulttext .= '

          '.&mt('The following errors occurred when modifying Textbook settings.'). + '

            '.$errors.'

          '; + } + return $resulttext; +} + +sub process_textbook_image { + my ($r,$dom,$confname,$caller,$cdom,$cnum,$type,$configuserok,$switchserver,$author_ok) = @_; + my $filename = $env{'form.'.$caller.'.filename'}; + my ($error,$url); + my ($width,$height) = (50,50); + if ($configuserok eq 'ok') { + if ($switchserver) { + $error = &mt('Upload of textbook image is not permitted to this server: [_1]', + $switchserver); + } elsif ($author_ok eq 'ok') { + my ($result,$imageurl) = + &publishlogo($r,'upload',$caller,$dom,$confname, + "$type/$cdom/$cnum/cover",$width,$height); + if ($result eq 'ok') { + $url = $imageurl; + } else { + $error = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$filename,$result); + } + } else { + $error = &mt("Upload of [_1] failed because an author role could not be assigned to a Domain Configuration user ([_2]) in domain: [_3]. Error was: [_4].",$filename,$confname,$dom,$author_ok); + } + } else { + $error = &mt("Upload of [_1] failed because a Domain Configuration user ([_2]) could not be created in domain: [_3]. Error was: [_4].",$filename,$confname,$dom,$configuserok); + } + return ($url,$error); +} + +sub modify_ltitools { + my ($r,$dom,$action,$lastactref,%domconfig) = @_; + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); + my ($newid,@allpos,%changes,%confhash,%encconfig,$errors,$resulttext); + my $confname = $dom.'-domainconfig'; + my $servadm = $r->dir_config('lonAdmEMail'); + my ($configuserok,$author_ok,$switchserver) = &config_check($dom,$confname,$servadm); + my (%posslti,%possfield); + my @courseroles = ('cc','in','ta','ep','st'); + my @ltiroles = qw(Instructor ContentDeveloper TeachingAssistant Learner); + map { $posslti{$_} = 1; } @ltiroles; + my @allfields = ('fullname','firstname','lastname','email','user','roles'); + map { $possfield{$_} = 1; } @allfields; + my %lt = <itools_names(); + if ($env{'form.ltitools_add'}) { + my $title = $env{'form.ltitools_add_title'}; + $title =~ s/(`)/'/g; + ($newid,my $error) = &get_ltitools_id($dom,$title); + if ($newid) { + my $position = $env{'form.ltitools_add_pos'}; + $position =~ s/\D+//g; + if ($position ne '') { + $allpos[$position] = $newid; + } + $changes{$newid} = 1; + foreach my $item ('title','url','key','secret','lifetime') { + $env{'form.ltitools_add_'.$item} =~ s/(`)/'/g; + if ($item eq 'lifetime') { + $env{'form.ltitools_add_'.$item} =~ s/[^\d.]//g; + } + if ($env{'form.ltitools_add_'.$item}) { + if (($item eq 'key') || ($item eq 'secret')) { + $encconfig{$newid}{$item} = $env{'form.ltitools_add_'.$item}; + } else { + $confhash{$newid}{$item} = $env{'form.ltitools_add_'.$item}; + } + } + } + if ($env{'form.ltitools_add_version'} eq 'LTI-1p0') { + $confhash{$newid}{'version'} = $env{'form.ltitools_add_version'}; + } + if ($env{'form.ltitools_add_msgtype'} eq 'basic-lti-launch-request') { + $confhash{$newid}{'msgtype'} = $env{'form.ltitools_add_msgtype'}; + } + if ($env{'form.ltitools_add_sigmethod'} eq 'HMAC-SHA256') { + $confhash{$newid}{'sigmethod'} = $env{'form.ltitools_add_sigmethod'}; + } else { + $confhash{$newid}{'sigmethod'} = 'HMAC-SHA1'; + } + foreach my $item ('width','height','linktext','explanation') { + $env{'form.ltitools_add_'.$item} =~ s/^\s+//; + $env{'form.ltitools_add_'.$item} =~ s/\s+$//; + if (($item eq 'width') || ($item eq 'height')) { + if ($env{'form.ltitools_add_'.$item} =~ /^\d+$/) { + $confhash{$newid}{'display'}{$item} = $env{'form.ltitools_add_'.$item}; + } + } else { + if ($env{'form.ltitools_add_'.$item} ne '') { + $confhash{$newid}{'display'}{$item} = $env{'form.ltitools_add_'.$item}; + } + } + } + if ($env{'form.ltitools_add_target'} eq 'window') { + $confhash{$newid}{'display'}{'target'} = $env{'form.ltitools_add_target'}; + } elsif ($env{'form.ltitools_add_target'} eq 'tab') { + $confhash{$newid}{'display'}{'target'} = $env{'form.ltitools_add_target'}; + } else { + $confhash{$newid}{'display'}{'target'} = 'iframe'; + } + foreach my $item ('passback','roster') { + if ($env{'form.ltitools_'.$item.'_add'}) { + $confhash{$newid}{$item} = 1; + if ($env{'form.ltitools_'.$item.'valid_add'} ne '') { + my $lifetime = $env{'form.ltitools_'.$item.'valid_add'}; + $lifetime =~ s/^\s+|\s+$//g; + if ($lifetime =~ /^\d+\.?\d*$/) { + $confhash{$newid}{$item.'valid'} = $lifetime; + } + } + } + } + if ($env{'form.ltitools_add_image.filename'} ne '') { + my ($imageurl,$error) = + &process_ltitools_image($r,$dom,$confname,'ltitools_add_image',$newid, + $configuserok,$switchserver,$author_ok); + if ($imageurl) { + $confhash{$newid}{'image'} = $imageurl; + } + if ($error) { + &Apache::lonnet::logthis($error); + $errors .= '
        • '.$error.'
        • '; + } + } + my @fields = &Apache::loncommon::get_env_multiple('form.ltitools_add_fields'); + foreach my $field (@fields) { + if ($possfield{$field}) { + if ($field eq 'roles') { + foreach my $role (@courseroles) { + my $choice = $env{'form.ltitools_add_roles_'.$role}; + if (($choice ne '') && ($posslti{$choice})) { + $confhash{$newid}{'roles'}{$role} = $choice; + if ($role eq 'cc') { + $confhash{$newid}{'roles'}{'co'} = $choice; + } + } + } + } else { + $confhash{$newid}{'fields'}{$field} = 1; + } + } + } + if (ref($confhash{$newid}{'fields'}) eq 'HASH') { + if ($confhash{$newid}{'fields'}{'user'}) { + if ($env{'form.ltitools_userincdom_add'}) { + $confhash{$newid}{'incdom'} = 1; + } + } + } + my @courseconfig = &Apache::loncommon::get_env_multiple('form.ltitools_courseconfig'); + foreach my $item (@courseconfig) { + $confhash{$newid}{'crsconf'}{$item} = 1; + } + if ($env{'form.ltitools_add_custom'}) { + my $name = $env{'form.ltitools_add_custom_name'}; + my $value = $env{'form.ltitools_add_custom_value'}; + $value =~ s/(`)/'/g; + $name =~ s/(`)/'/g; + $confhash{$newid}{'custom'}{$name} = $value; + } + } else { + my $error = &mt('Failed to acquire unique ID for new external tool'); + $errors .= '
        • '.$error.'
        • '; + } + } + if (ref($domconfig{$action}) eq 'HASH') { + my %deletions; + my @todelete = &Apache::loncommon::get_env_multiple('form.ltitools_del'); + if (@todelete) { + map { $deletions{$_} = 1; } @todelete; + } + my %customadds; + my @newcustom = &Apache::loncommon::get_env_multiple('form.ltitools_customadd'); + if (@newcustom) { + map { $customadds{$_} = 1; } @newcustom; + } + my %imgdeletions; + my @todeleteimages = &Apache::loncommon::get_env_multiple('form.ltitools_image_del'); + if (@todeleteimages) { + map { $imgdeletions{$_} = 1; } @todeleteimages; + } + my $maxnum = $env{'form.ltitools_maxnum'}; + for (my $i=0; $i<=$maxnum; $i++) { + my $itemid = $env{'form.ltitools_id_'.$i}; + $itemid =~ s/\D+//g; + if (ref($domconfig{$action}{$itemid}) eq 'HASH') { + if ($deletions{$itemid}) { + if ($domconfig{$action}{$itemid}{'image'}) { + #FIXME need to obsolete item in RES space + } + $changes{$itemid} = $domconfig{$action}{$itemid}{'title'}; + next; + } else { + my $newpos = $env{'form.ltitools_'.$itemid}; + $newpos =~ s/\D+//g; + foreach my $item ('title','url','lifetime') { + $confhash{$itemid}{$item} = $env{'form.ltitools_'.$item.'_'.$i}; + if ($domconfig{$action}{$itemid}{$item} ne $confhash{$itemid}{$item}) { + $changes{$itemid} = 1; + } + } + foreach my $item ('key','secret') { + $encconfig{$itemid}{$item} = $env{'form.ltitools_'.$item.'_'.$i}; + if ($domconfig{$action}{$itemid}{$item} ne $encconfig{$itemid}{$item}) { + $changes{$itemid} = 1; + } + } + if ($env{'form.ltitools_version_'.$i} eq 'LTI-1p0') { + $confhash{$itemid}{'version'} = $env{'form.ltitools_version_'.$i}; + } + if ($env{'form.ltitools_msgtype_'.$i} eq 'basic-lti-launch-request') { + $confhash{$itemid}{'msgtype'} = $env{'form.ltitools_msgtype_'.$i}; + } + if ($env{'form.ltitools_sigmethod_'.$i} eq 'HMAC-SHA256') { + $confhash{$itemid}{'sigmethod'} = $env{'form.ltitools_sigmethod_'.$i}; + } else { + $confhash{$itemid}{'sigmethod'} = 'HMAC-SHA1'; + } + if ($domconfig{$action}{$itemid}{'sigmethod'} eq '') { + if ($confhash{$itemid}{'sigmethod'} ne 'HMAC-SHA1') { + $changes{$itemid} = 1; + } + } elsif ($domconfig{$action}{$itemid}{'sigmethod'} ne $confhash{$itemid}{'sigmethod'}) { + $changes{$itemid} = 1; + } + foreach my $size ('width','height') { + $env{'form.ltitools_'.$size.'_'.$i} =~ s/^\s+//; + $env{'form.ltitools_'.$size.'_'.$i} =~ s/\s+$//; + if ($env{'form.ltitools_'.$size.'_'.$i} =~ /^\d+$/) { + $confhash{$itemid}{'display'}{$size} = $env{'form.ltitools_'.$size.'_'.$i}; + if (ref($domconfig{$action}{$itemid}{'display'}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'display'}{$size} ne $confhash{$itemid}{'display'}{$size}) { + $changes{$itemid} = 1; + } + } else { + $changes{$itemid} = 1; + } + } elsif (ref($domconfig{$action}{$itemid}{'display'}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'display'}{$size} ne '') { + $changes{$itemid} = 1; + } + } + } + foreach my $item ('linktext','explanation') { + $env{'form.ltitools_'.$item.'_'.$i} =~ s/^\s+//; + $env{'form.ltitools_'.$item.'_'.$i} =~ s/\s+$//; + if ($env{'form.ltitools_'.$item.'_'.$i} ne '') { + $confhash{$itemid}{'display'}{$item} = $env{'form.ltitools_'.$item.'_'.$i}; + if (ref($domconfig{$action}{$itemid}{'display'}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'display'}{$item} ne $confhash{$itemid}{'display'}{$item}) { + $changes{$itemid} = 1; + } + } else { + $changes{$itemid} = 1; + } + } elsif (ref($domconfig{$action}{$itemid}{'display'}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'display'}{$item} ne '') { + $changes{$itemid} = 1; + } + } + } + if ($env{'form.ltitools_target_'.$i} eq 'window') { + $confhash{$itemid}{'display'}{'target'} = $env{'form.ltitools_target_'.$i}; + } elsif ($env{'form.ltitools_target_'.$i} eq 'tab') { + $confhash{$itemid}{'display'}{'target'} = $env{'form.ltitools_target_'.$i}; + } else { + $confhash{$itemid}{'display'}{'target'} = 'iframe'; + } + if (ref($domconfig{$action}{$itemid}{'display'}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'display'}{'target'} ne $confhash{$itemid}{'display'}{'target'}) { + $changes{$itemid} = 1; + } + } else { + $changes{$itemid} = 1; + } + foreach my $extra ('passback','roster') { + if ($env{'form.ltitools_'.$extra.'_'.$i}) { + $confhash{$itemid}{$extra} = 1; + if ($env{'form.ltitools_'.$extra.'valid_'.$i} ne '') { + my $lifetime = $env{'form.ltitools_'.$extra.'valid_'.$i}; + $lifetime =~ s/^\s+|\s+$//g; + if ($lifetime =~ /^\d+\.?\d*$/) { + $confhash{$itemid}{$extra.'valid'} = $lifetime; + } + } + } + if ($domconfig{$action}{$itemid}{$extra} ne $confhash{$itemid}{$extra}) { + $changes{$itemid} = 1; + } + if ($domconfig{$action}{$itemid}{$extra.'valid'} ne $confhash{$itemid}{$extra.'valid'}) { + $changes{$itemid} = 1; + } + } + my @courseconfig = &Apache::loncommon::get_env_multiple('form.ltitools_courseconfig_'.$i); + foreach my $item ('label','title','target','linktext','explanation','append') { + if (grep(/^\Q$item\E$/,@courseconfig)) { + $confhash{$itemid}{'crsconf'}{$item} = 1; + if (ref($domconfig{$action}{$itemid}{'crsconf'}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'crsconf'}{$item} ne $confhash{$itemid}{'crsconf'}{$item}) { + $changes{$itemid} = 1; + } + } else { + $changes{$itemid} = 1; + } + } + } + my @fields = &Apache::loncommon::get_env_multiple('form.ltitools_fields_'.$i); + foreach my $field (@fields) { + if ($possfield{$field}) { + if ($field eq 'roles') { + foreach my $role (@courseroles) { + my $choice = $env{'form.ltitools_roles_'.$role.'_'.$i}; + if (($choice ne '') && ($posslti{$choice})) { + $confhash{$itemid}{'roles'}{$role} = $choice; + if ($role eq 'cc') { + $confhash{$itemid}{'roles'}{'co'} = $choice; + } + } + if (ref($domconfig{$action}{$itemid}{'roles'}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'roles'}{$role} ne $confhash{$itemid}{'roles'}{$role}) { + $changes{$itemid} = 1; + } + } elsif ($confhash{$itemid}{'roles'}{$role}) { + $changes{$itemid} = 1; + } + } + } else { + $confhash{$itemid}{'fields'}{$field} = 1; + if (ref($domconfig{$action}{$itemid}{'fields'}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'fields'}{$field} ne $confhash{$itemid}{'fields'}{$field}) { + $changes{$itemid} = 1; + } + } else { + $changes{$itemid} = 1; + } + } + } + } + if (ref($confhash{$itemid}{'fields'}) eq 'HASH') { + if ($confhash{$itemid}{'fields'}{'user'}) { + if ($env{'form.ltitools_userincdom_'.$i}) { + $confhash{$itemid}{'incdom'} = 1; + } + if ($domconfig{$action}{$itemid}{'incdom'} ne $confhash{$itemid}{'incdom'}) { + $changes{$itemid} = 1; + } + } + } + $allpos[$newpos] = $itemid; + } + if ($imgdeletions{$itemid}) { + $changes{$itemid} = 1; + #FIXME need to obsolete item in RES space + } elsif ($env{'form.ltitools_image_'.$i.'.filename'}) { + my ($imgurl,$error) = &process_ltitools_image($r,$dom,$confname,'ltitools_image_'.$i, + $itemid,$configuserok,$switchserver, + $author_ok); + if ($imgurl) { + $confhash{$itemid}{'image'} = $imgurl; + $changes{$itemid} = 1; + } + if ($error) { + &Apache::lonnet::logthis($error); + $errors .= '
        • '.$error.'
        • '; + } + } elsif ($domconfig{$action}{$itemid}{'image'}) { + $confhash{$itemid}{'image'} = + $domconfig{$action}{$itemid}{'image'}; + } + if ($customadds{$i}) { + my $name = $env{'form.ltitools_custom_name_'.$i}; + $name =~ s/(`)/'/g; + $name =~ s/^\s+//; + $name =~ s/\s+$//; + my $value = $env{'form.ltitools_custom_value_'.$i}; + $value =~ s/(`)/'/g; + $value =~ s/^\s+//; + $value =~ s/\s+$//; + if ($name ne '') { + $confhash{$itemid}{'custom'}{$name} = $value; + $changes{$itemid} = 1; + } + } + my %customdels; + my @customdeletions = &Apache::loncommon::get_env_multiple('form.ltitools_customdel_'.$i); + if (@customdeletions) { + $changes{$itemid} = 1; + } + map { $customdels{$_} = 1; } @customdeletions; + if (ref($domconfig{$action}{$itemid}{'custom'}) eq 'HASH') { + foreach my $key (keys(%{$domconfig{$action}{$itemid}{'custom'}})) { + unless ($customdels{$key}) { + if ($env{'form.ltitools_customval_'.$key.'_'.$i} ne '') { + $confhash{$itemid}{'custom'}{$key} = $env{'form.ltitools_customval_'.$key.'_'.$i}; + } + if ($domconfig{$action}{$itemid}{'custom'}{$key} ne $env{'form.ltitools_customval_'.$key.'_'.$i}) { + $changes{$itemid} = 1; + } + } + } + } + unless ($changes{$itemid}) { + foreach my $key (keys(%{$domconfig{$action}{$itemid}})) { + if (ref($domconfig{$action}{$itemid}{$key}) eq 'HASH') { + if (ref($confhash{$itemid}{$key}) eq 'HASH') { + foreach my $innerkey (keys(%{$domconfig{$action}{$itemid}{$key}})) { + unless (exists($confhash{$itemid}{$key}{$innerkey})) { + $changes{$itemid} = 1; + last; + } + } + } elsif (keys(%{$domconfig{$action}{$itemid}{$key}}) > 0) { + $changes{$itemid} = 1; + } + } + last if ($changes{$itemid}); + } + } + } + } + } + if (@allpos > 0) { + my $idx = 0; + foreach my $itemid (@allpos) { + if ($itemid ne '') { + $confhash{$itemid}{'order'} = $idx; + if (ref($domconfig{$action}) eq 'HASH') { + if (ref($domconfig{$action}{$itemid}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'order'} ne $idx) { + $changes{$itemid} = 1; + } + } + } + $idx ++; + } + } + } + my %ltitoolshash = ( + $action => { %confhash } + ); + my $putresult = &Apache::lonnet::put_dom('configuration',\%ltitoolshash, + $dom); + if ($putresult eq 'ok') { + my %ltienchash = ( + $action => { %encconfig } + ); + &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom); + if (keys(%changes) > 0) { + my $cachetime = 24*60*60; + my %ltiall = %confhash; + foreach my $id (keys(%ltiall)) { + if (ref($encconfig{$id}) eq 'HASH') { + foreach my $item ('key','secret') { + $ltiall{$id}{$item} = $encconfig{$id}{$item}; + } + } + } + &Apache::lonnet::do_cache_new('ltitools',$dom,\%ltiall,$cachetime); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'ltitools'} = 1; + } + $resulttext = &mt('Changes made:').'
            '; + my %bynum; + foreach my $itemid (sort(keys(%changes))) { + my $position = $confhash{$itemid}{'order'}; + $bynum{$position} = $itemid; + } + foreach my $pos (sort { $a <=> $b } keys(%bynum)) { + my $itemid = $bynum{$pos}; + if (ref($confhash{$itemid}) ne 'HASH') { + $resulttext .= '
          • '.&mt('Deleted: [_1]',$changes{$itemid}).'
          • '; + } else { + $resulttext .= '
          • '.$confhash{$itemid}{'title'}.''; + if ($confhash{$itemid}{'image'}) { + $resulttext .= ' '. + ''.&mt('Tool Provider icon').''; + } + $resulttext .= '
            • '; + my $position = $pos + 1; + $resulttext .= '
            • '.&mt('Order: [_1]',$position).'
            • '; + foreach my $item ('version','msgtype','sigmethod','url','lifetime') { + if ($confhash{$itemid}{$item} ne '') { + $resulttext .= '
            • '.$lt{$item}.': '.$confhash{$itemid}{$item}.'
            • '; + } + } + if ($encconfig{$itemid}{'key'} ne '') { + $resulttext .= '
            • '.$lt{'key'}.': '.$encconfig{$itemid}{'key'}.'
            • '; + } + if ($encconfig{$itemid}{'secret'} ne '') { + $resulttext .= '
            • '.$lt{'secret'}.': '; + my $num = length($encconfig{$itemid}{'secret'}); + $resulttext .= ('*'x$num).'
            • '; + } + $resulttext .= '
            • '.&mt('Configurable in course:'); + my @possconfig = ('label','title','target','linktext','explanation','append'); + my $numconfig = 0; + if (ref($confhash{$itemid}{'crsconf'}) eq 'HASH') { + foreach my $item (@possconfig) { + if ($confhash{$itemid}{'crsconf'}{$item}) { + $numconfig ++; + $resulttext .= ' "'.$lt{'crs'.$item}.'"'; + } + } + } + if (!$numconfig) { + $resulttext .= &mt('None'); + } + $resulttext .= '
            • '; + foreach my $item ('passback','roster') { + $resulttext .= '
            • '.$lt{$item}.' '; + if ($confhash{$itemid}{$item}) { + $resulttext .= &mt('Yes'); + if ($confhash{$itemid}{$item.'valid'}) { + if ($item eq 'passback') { + $resulttext .= ' '.&mt('valid for at least [quant,_1,day] after launch', + $confhash{$itemid}{$item.'valid'}); + } else { + $resulttext .= ' '.&mt('valid for at least [quant,_1,second] after launch', + $confhash{$itemid}{$item.'valid'}); + } + } + } else { + $resulttext .= &mt('No'); + } + $resulttext .= '
            • '; + } + if (ref($confhash{$itemid}{'display'}) eq 'HASH') { + my $displaylist; + if ($confhash{$itemid}{'display'}{'target'}) { + $displaylist = &mt('Display target').': '. + $confhash{$itemid}{'display'}{'target'}.','; + } + foreach my $size ('width','height') { + if ($confhash{$itemid}{'display'}{$size}) { + $displaylist .= (' 'x2).$lt{$size}.': '. + $confhash{$itemid}{'display'}{$size}.','; + } + } + if ($displaylist) { + $displaylist =~ s/,$//; + $resulttext .= '
            • '.$displaylist.'
            • '; + } + foreach my $item ('linktext','explanation') { + if ($confhash{$itemid}{'display'}{$item}) { + $resulttext .= '
            • '.$lt{$item}.': '.$confhash{$itemid}{'display'}{$item}.'
            • '; + } + } + } + if (ref($confhash{$itemid}{'fields'}) eq 'HASH') { + my $fieldlist; + foreach my $field (@allfields) { + if ($confhash{$itemid}{'fields'}{$field}) { + $fieldlist .= (' 'x2).$lt{$field}.','; + } + } + if ($fieldlist) { + $fieldlist =~ s/,$//; + if ($confhash{$itemid}{'fields'}{'user'}) { + if ($confhash{$itemid}{'incdom'}) { + $fieldlist .= ' ('.&mt('username:domain').')'; + } else { + $fieldlist .= ' ('.&mt('username').')'; + } + } + $resulttext .= '
            • '.&mt('Data sent').':'.$fieldlist.'
            • '; + } + } + if (ref($confhash{$itemid}{'roles'}) eq 'HASH') { + my $rolemaps; + foreach my $role (@courseroles) { + if ($confhash{$itemid}{'roles'}{$role}) { + $rolemaps .= (' 'x2).&Apache::lonnet::plaintext($role,'Course').'='. + $confhash{$itemid}{'roles'}{$role}.','; + } + } + if ($rolemaps) { + $rolemaps =~ s/,$//; + $resulttext .= '
            • '.&mt('Role mapping:').$rolemaps.'
            • '; + } + } + if (ref($confhash{$itemid}{'custom'}) eq 'HASH') { + my $customlist; + if (keys(%{$confhash{$itemid}{'custom'}})) { + foreach my $key (sort(keys(%{$confhash{$itemid}{'custom'}}))) { + $customlist .= $key.':'.$confhash{$itemid}{'custom'}{$key}.(' 'x2); + } + } + if ($customlist) { + $resulttext .= '
            • '.&mt('Custom items').': '.$customlist.'
            • '; + } + } + $resulttext .= '
            '; + } + } + $resulttext .= '
          '; + } else { + $resulttext = &mt('No changes made.'); + } + } else { + $errors .= '
        • '.&mt('Failed to save changes').'
        • '; + } + if ($errors) { + $resulttext .= &mt('The following errors occurred: ').'
            '. + $errors.'
          '; + } + return $resulttext; +} + +sub process_ltitools_image { + my ($r,$dom,$confname,$caller,$itemid,$configuserok,$switchserver,$author_ok) = @_; + my $filename = $env{'form.'.$caller.'.filename'}; + my ($error,$url); + my ($width,$height) = (21,21); + if ($configuserok eq 'ok') { + if ($switchserver) { + $error = &mt('Upload of Tool Provider (LTI) icon is not permitted to this server: [_1]', + $switchserver); + } elsif ($author_ok eq 'ok') { + my ($result,$imageurl,$madethumb) = + &publishlogo($r,'upload',$caller,$dom,$confname, + "ltitools/$itemid/icon",$width,$height); + if ($result eq 'ok') { + if ($madethumb) { + my ($path,$imagefile) = ($imageurl =~ m{^(.+)/([^/]+)$}); + my $imagethumb = "$path/tn-".$imagefile; + $url = $imagethumb; + } else { + $url = $imageurl; + } + } else { + $error = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$filename,$result); + } + } else { + $error = &mt("Upload of [_1] failed because an author role could not be assigned to a Domain Configuration user ([_2]) in domain: [_3]. Error was: [_4].",$filename,$confname,$dom,$author_ok); + } + } else { + $error = &mt("Upload of [_1] failed because a Domain Configuration user ([_2]) could not be created in domain: [_3]. Error was: [_4].",$filename,$confname,$dom,$configuserok); + } + return ($url,$error); +} + +sub get_ltitools_id { + my ($cdom,$title) = @_; + # get lock on ltitools db + my $lockhash = { + lock => $env{'user.name'}. + ':'.$env{'user.domain'}, + }; + my $tries = 0; + my $gotlock = &Apache::lonnet::newput_dom('ltitools',$lockhash,$cdom); + my ($id,$error); + + while (($gotlock ne 'ok') && ($tries<10)) { + $tries ++; + sleep (0.1); + $gotlock = &Apache::lonnet::newput_dom('ltitools',$lockhash,$cdom); + } + if ($gotlock eq 'ok') { + my %currids = &Apache::lonnet::dump_dom('ltitools',$cdom); + if ($currids{'lock'}) { + delete($currids{'lock'}); + if (keys(%currids)) { + my @curr = sort { $a <=> $b } keys(%currids); + if ($curr[-1] =~ /^\d+$/) { + $id = 1 + $curr[-1]; + } + } else { + $id = 1; + } + if ($id) { + unless (&Apache::lonnet::newput_dom('ltitools',{ $id => $title },$cdom) eq 'ok') { + $error = 'nostore'; + } + } else { + $error = 'nonumber'; + } + } + my $dellockoutcome = &Apache::lonnet::del_dom('ltitools',['lock'],$cdom); + } else { + $error = 'nolock'; + } + return ($id,$error); +} + +sub modify_lti { + my ($r,$dom,$action,$lastactref,%domconfig) = @_; + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); + my ($newid,@allpos,%changes,%confhash,%encconfig,$errors,$resulttext); + my (%posslti,%posslticrs,%posscrstype); + my @courseroles = ('cc','in','ta','ep','st'); + my @ltiroles = qw(Learner Instructor ContentDeveloper TeachingAssistant Mentor Member Manager Administrator); + my @lticourseroles = qw(Instructor TeachingAssistant Mentor Learner); + my @coursetypes = ('official','unofficial','community','textbook','placement'); + my %coursetypetitles = &Apache::lonlocal::texthash ( + official => 'Official', + unofficial => 'Unofficial', + community => 'Community', + textbook => 'Textbook', + placement => 'Placement Test', + ); + my %fieldtitles = &Apache::loncommon::personal_data_fieldtitles(); + my %lt = <i_names(); + map { $posslti{$_} = 1; } @ltiroles; + map { $posslticrs{$_} = 1; } @lticourseroles; + map { $posscrstype{$_} = 1; } @coursetypes; + + my %menutitles = <imenu_titles(); + + my (@items,%deletions,%itemids); + if ($env{'form.lti_add'}) { + my $consumer = $env{'form.lti_consumer_add'}; + $consumer =~ s/(`)/'/g; + ($newid,my $error) = &get_lti_id($dom,$consumer); + if ($newid) { + $itemids{'add'} = $newid; + push(@items,'add'); + $changes{$newid} = 1; + } else { + my $error = &mt('Failed to acquire unique ID for new LTI configuration'); + $errors .= '
        • '.$error.'
        • '; + } + } + if (ref($domconfig{$action}) eq 'HASH') { + my @todelete = &Apache::loncommon::get_env_multiple('form.lti_del'); + if (@todelete) { + map { $deletions{$_} = 1; } @todelete; + } + my $maxnum = $env{'form.lti_maxnum'}; + for (my $i=0; $i<=$maxnum; $i++) { + my $itemid = $env{'form.lti_id_'.$i}; + $itemid =~ s/\D+//g; + if (ref($domconfig{$action}{$itemid}) eq 'HASH') { + if ($deletions{$itemid}) { + $changes{$itemid} = $domconfig{$action}{$itemid}{'consumer'}; + } else { + push(@items,$i); + $itemids{$i} = $itemid; + } + } + } + } + foreach my $idx (@items) { + my $itemid = $itemids{$idx}; + next unless ($itemid); + my $position = $env{'form.lti_pos_'.$idx}; + $position =~ s/\D+//g; + if ($position ne '') { + $allpos[$position] = $itemid; + } + foreach my $item ('consumer','key','secret','lifetime','requser') { + my $formitem = 'form.lti_'.$item.'_'.$idx; + $env{$formitem} =~ s/(`)/'/g; + if ($item eq 'lifetime') { + $env{$formitem} =~ s/[^\d.]//g; + } + if ($env{$formitem} ne '') { + if (($item eq 'key') || ($item eq 'secret')) { + $encconfig{$itemid}{$item} = $env{$formitem}; + } else { + $confhash{$itemid}{$item} = $env{$formitem}; + unless (($idx eq 'add') || ($changes{$itemid})) { + if ($domconfig{$action}{$itemid}{$item} ne $confhash{$itemid}{$item}) { + $changes{$itemid} = 1; + } + } + } + } + } + if ($env{'form.lti_version_'.$idx} eq 'LTI-1p0') { + $confhash{$itemid}{'version'} = $env{'form.lti_version_'.$idx}; + } + if ($confhash{$itemid}{'requser'}) { + if ($env{'form.lti_mapuser_'.$idx} eq 'sourcedid') { + $confhash{$itemid}{'mapuser'} = 'lis_person_sourcedid'; + } elsif ($env{'form.lti_mapuser_'.$idx} eq 'email') { + $confhash{$itemid}{'mapuser'} = 'lis_person_contact_email_primary'; + } elsif ($env{'form.lti_mapuser_'.$idx} eq 'other') { + my $mapuser = $env{'form.lti_customuser_'.$idx}; + $mapuser =~ s/(`)/'/g; + $mapuser =~ s/^\s+|\s+$//g; + $confhash{$itemid}{'mapuser'} = $mapuser; + } + foreach my $ltirole (@lticourseroles) { + my $possrole = $env{'form.lti_maprole_'.$ltirole.'_'.$idx}; + if (grep(/^\Q$possrole\E$/,@courseroles)) { + $confhash{$itemid}{'maproles'}{$ltirole} = $possrole; + } + } + my @possmakeuser = &Apache::loncommon::get_env_multiple('form.lti_makeuser_'.$idx); + my @makeuser; + foreach my $ltirole (sort(@possmakeuser)) { + if ($posslti{$ltirole}) { + push(@makeuser,$ltirole); + } + } + $confhash{$itemid}{'makeuser'} = \@makeuser; + if (@makeuser) { + my $lcauth = $env{'form.lti_lcauth_'.$idx}; + if ($lcauth =~ /^(internal|krb4|krb5|localauth)$/) { + $confhash{$itemid}{'lcauth'} = $lcauth; + if ($lcauth ne 'internal') { + my $lcauthparm = $env{'form.lti_lcauthparm_'.$idx}; + $lcauthparm =~ s/^(\s+|\s+)$//g; + $lcauthparm =~ s/`//g; + if ($lcauthparm ne '') { + $confhash{$itemid}{'lcauthparm'} = $lcauthparm; + } + } + } else { + $confhash{$itemid}{'lcauth'} = 'lti'; + } + } + my @possinstdata = &Apache::loncommon::get_env_multiple('form.lti_instdata_'.$idx); + if (@possinstdata) { + foreach my $field (@possinstdata) { + if (exists($fieldtitles{$field})) { + push(@{$confhash{$itemid}{'instdata'}}); + } + } + } + if (($env{'form.lti_mapcrs_'.$idx} eq 'course_offering_sourcedid') || + ($env{'form.lti_mapcrs_'.$idx} eq 'context_id')) { + $confhash{$itemid}{'mapcrs'} = $env{'form.lti_mapcrs_'.$idx}; + } elsif ($env{'form.lti_mapcrs_'.$idx} eq 'other') { + my $mapcrs = $env{'form.lti_mapcrsfield_'.$idx}; + $mapcrs =~ s/(`)/'/g; + $mapcrs =~ s/^\s+|\s+$//g; + $confhash{$itemid}{'mapcrs'} = $mapcrs; + } + my @posstypes = &Apache::loncommon::get_env_multiple('form.lti_mapcrstype_'.$idx); + my @crstypes; + foreach my $type (sort(@posstypes)) { + if ($posscrstype{$type}) { + push(@crstypes,$type); + } + } + $confhash{$itemid}{'mapcrstype'} = \@crstypes; + if ($env{'form.lti_makecrs_'.$idx}) { + $confhash{$itemid}{'makecrs'} = 1; + } + my @possenroll = &Apache::loncommon::get_env_multiple('form.lti_selfenroll_'.$idx); + my @selfenroll; + foreach my $type (sort(@possenroll)) { + if ($posslticrs{$type}) { + push(@selfenroll,$type); + } + } + $confhash{$itemid}{'selfenroll'} = \@selfenroll; + if ($env{'form.lti_crssec_'.$idx}) { + if ($env{'form.lti_crssecsrc_'.$idx} eq 'course_section_sourcedid') { + $confhash{$itemid}{'section'} = $env{'form.lti_crssecsrc_'.$idx}; + } elsif ($env{'form.lti_crssecsrc_'.$idx} eq 'other') { + my $section = $env{'form.lti_customsection_'.$idx}; + $section =~ s/(`)/'/g; + $section =~ s/^\s+|\s+$//g; + if ($section ne '') { + $confhash{$itemid}{'section'} = $section; + } + } + } + foreach my $field ('passback','roster','topmenu','inlinemenu') { + if ($env{'form.lti_'.$field.'_'.$idx}) { + $confhash{$itemid}{$field} = 1; + } + } + if ($env{'form.lti_passback_'.$idx}) { + if ($env{'form.lti_passbackformat_'.$idx} eq '1.0') { + $confhash{$itemid}{'passbackformat'} = '1.0'; + } else { + $confhash{$itemid}{'passbackformat'} = '1.1'; + } + } + if ($env{'form.lti_topmenu_'.$idx} || $env{'form.lti_inlinemenu_'.$idx}) { + $confhash{$itemid}{lcmenu} = []; + my @possmenu = &Apache::loncommon::get_env_multiple('form.lti_menuitem_'.$idx); + foreach my $field (@possmenu) { + if (exists($menutitles{$field})) { + if ($field eq 'grades') { + next unless ($env{'form.lti_inlinemenu_'.$idx}); + } + push(@{$confhash{$itemid}{lcmenu}},$field); + } + } + } + unless (($idx eq 'add') || ($changes{$itemid})) { + foreach my $field ('mapuser','mapcrs','makecrs','section','passback','roster','lcauth','lcauthparm','topmenu','inlinemenu') { + if ($domconfig{$action}{$itemid}{$field} ne $confhash{$itemid}{$field}) { + $changes{$itemid} = 1; + } + } + unless ($changes{$itemid}) { + if ($domconfig{$action}{$itemid}{'passback'} eq $confhash{$itemid}{'passback'}) { + if ($domconfig{$action}{$itemid}{'passbackformat'} ne $confhash{$itemid}{'passbackformat'}) { + $changes{$itemid} = 1; + } + } + } + foreach my $field ('makeuser','mapcrstype','selfenroll','instdata','lcmenu') { + unless ($changes{$itemid}) { + if (ref($domconfig{$action}{$itemid}{$field}) eq 'ARRAY') { + if (ref($confhash{$itemid}{$field}) eq 'ARRAY') { + my @diffs = &Apache::loncommon::compare_arrays($domconfig{$action}{$itemid}{$field}, + $confhash{$itemid}{$field}); + if (@diffs) { + $changes{$itemid} = 1; + } + } elsif (@{$domconfig{$action}{$itemid}{$field}} > 0) { + $changes{$itemid} = 1; + } + } elsif (ref($confhash{$itemid}{$field}) eq 'ARRAY') { + if (@{$confhash{$itemid}{$field}} > 0) { + $changes{$itemid} = 1; + } + } + } + } + unless ($changes{$itemid}) { + if (ref($domconfig{$action}{$itemid}{'maproles'}) eq 'HASH') { + if (ref($confhash{$itemid}{'maproles'}) eq 'HASH') { + foreach my $ltirole (keys(%{$domconfig{$action}{$itemid}{'maproles'}})) { + if ($domconfig{$action}{$itemid}{'maproles'}{$ltirole} ne + $confhash{$itemid}{'maproles'}{$ltirole}) { + $changes{$itemid} = 1; + last; + } + } + unless ($changes{$itemid}) { + foreach my $ltirole (keys(%{$confhash{$itemid}{'maproles'}})) { + if ($confhash{$itemid}{'maproles'}{$ltirole} ne + $domconfig{$action}{$itemid}{'maproles'}{$ltirole}) { + $changes{$itemid} = 1; + last; + } + } + } + } elsif (keys(%{$domconfig{$action}{$itemid}{'maproles'}}) > 0) { + $changes{$itemid} = 1; + } + } elsif (ref($confhash{$itemid}{'maproles'}) eq 'HASH') { + unless ($changes{$itemid}) { + if (keys(%{$confhash{$itemid}{'maproles'}}) > 0) { + $changes{$itemid} = 1; + } + } + } + } + } + } + } + if (@allpos > 0) { + my $idx = 0; + foreach my $itemid (@allpos) { + if ($itemid ne '') { + $confhash{$itemid}{'order'} = $idx; + if (ref($domconfig{$action}) eq 'HASH') { + if (ref($domconfig{$action}{$itemid}) eq 'HASH') { + if ($domconfig{$action}{$itemid}{'order'} ne $idx) { + $changes{$itemid} = 1; + } + } + } + $idx ++; + } + } + } + my %ltihash = ( + $action => { %confhash } + ); + my $putresult = &Apache::lonnet::put_dom('configuration',\%ltihash, + $dom); + if ($putresult eq 'ok') { + my %ltienchash = ( + $action => { %encconfig } + ); + &Apache::lonnet::put_dom('encconfig',\%ltienchash,$dom); + if (keys(%changes) > 0) { + my $cachetime = 24*60*60; + my %ltiall = %confhash; + foreach my $id (keys(%ltiall)) { + if (ref($encconfig{$id}) eq 'HASH') { + foreach my $item ('key','secret') { + $ltiall{$id}{$item} = $encconfig{$id}{$item}; + } + } + } + &Apache::lonnet::do_cache_new('lti',$dom,\%ltiall,$cachetime); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'lti'} = 1; + } + $resulttext = &mt('Changes made:').'
            '; + my %bynum; + foreach my $itemid (sort(keys(%changes))) { + my $position = $confhash{$itemid}{'order'}; + $bynum{$position} = $itemid; + } + foreach my $pos (sort { $a <=> $b } keys(%bynum)) { + my $itemid = $bynum{$pos}; + if (ref($confhash{$itemid}) ne 'HASH') { + $resulttext .= '
          • '.&mt('Deleted: [_1]',$changes{$itemid}).'
          • '; + } else { + $resulttext .= '
          • '.$confhash{$itemid}{'consumer'}.'
            • '; + my $position = $pos + 1; + $resulttext .= '
            • '.&mt('Order: [_1]',$position).'
            • '; + foreach my $item ('version','lifetime') { + if ($confhash{$itemid}{$item} ne '') { + $resulttext .= '
            • '.$lt{$item}.': '.$confhash{$itemid}{$item}.'
            • '; + } + } + if ($encconfig{$itemid}{'key'} ne '') { + $resulttext .= '
            • '.$lt{'key'}.': '.$encconfig{$itemid}{'key'}.'
            • '; + } + if ($encconfig{$itemid}{'secret'} ne '') { + $resulttext .= '
            • '.$lt{'secret'}.': '; + my $num = length($encconfig{$itemid}{'secret'}); + $resulttext .= ('*'x$num).'
            • '; + } + if ($confhash{$itemid}{'requser'}) { + if ($confhash{$itemid}{'mapuser'}) { + my $shownmapuser; + if ($confhash{$itemid}{'mapuser'} eq 'lis_person_sourcedid') { + $shownmapuser = $lt{'sourcedid'}.' (lis_person_sourcedid)'; + } elsif ($confhash{$itemid}{'mapuser'} eq 'lis_person_contact_email_primary') { + $shownmapuser = $lt{'email'}.' (lis_person_contact_email_primary)'; + } else { + $shownmapuser = &mt('Other').' ('.$confhash{$itemid}{'mapuser'}.')'; + } + $resulttext .= '
            • '.&mt('LON-CAPA username').': '.$shownmapuser.'
            • '; + } + if (ref($confhash{$itemid}{'maproles'}) eq 'HASH') { + my $rolemaps; + foreach my $role (@ltiroles) { + if ($confhash{$itemid}{'maproles'}{$role}) { + $rolemaps .= (' 'x2).$role.'='. + &Apache::lonnet::plaintext($confhash{$itemid}{'maproles'}{$role}, + 'Course').','; + } + } + if ($rolemaps) { + $rolemaps =~ s/,$//; + $resulttext .= '
            • '.&mt('Role mapping:').$rolemaps.'
            • '; + } + } + if (ref($confhash{$itemid}{'makeuser'}) eq 'ARRAY') { + if (@{$confhash{$itemid}{'makeuser'}} > 0) { + $resulttext .= '
            • '.&mt('Following roles may create user accounts: [_1]', + join(', ',@{$confhash{$itemid}{'makeuser'}})).'
              '; + if ($confhash{$itemid}{'lcauth'} eq 'lti') { + $resulttext .= &mt('New users will only be able to authenticate via LTI').'
            • '; + } else { + $resulttext .= &mt('New users will be assigned LON-CAPA authentication: [_1]', + $confhash{$itemid}{'lcauth'}); + if ($confhash{$itemid}{'lcauth'} eq 'internal') { + $resulttext .= '; '.&mt('a randomly generated password will be created'); + } elsif ($confhash{$itemid}{'lcauth'} eq 'localauth') { + if ($confhash{$itemid}{'lcauthparm'} ne '') { + $resulttext .= ' '.&mt('with argument: [_1]',$confhash{$itemid}{'lcauthparm'}); + } + } else { + $resulttext .= '; '.&mt('Kerberos domain: [_1]',$confhash{$itemid}{'lcauthparm'}); + } + } + $resulttext .= ''; + } else { + $resulttext .= '
            • '.&mt('User account creation not permitted.').'
            • '; + } + } + if (ref($confhash{$itemid}{'instdata'}) eq 'ARRAY') { + if (@{$confhash{$itemid}{'instdata'}} > 0) { + $resulttext .= '
            • '.&mt('Institutional data will be used when creating a new user for: [_1]', + join(', ',map { $fieldtitles{$_}; } @{$confhash{$itemid}{'instdata'}})).'
            • '; + } else { + $resulttext .= '
            • '.&mt('No institutional data used when creating a new user.').'
            • '; + } + } + if ($confhash{$itemid}{'mapcrs'}) { + $resulttext .= '
            • '.&mt('Unique course identifier').': '.$confhash{$itemid}{'mapcrs'}.'
            • '; + } + if (ref($confhash{$itemid}{'mapcrstype'}) eq 'ARRAY') { + if (@{$confhash{$itemid}{'mapcrstype'}} > 0) { + $resulttext .= '
            • '.&mt('Mapping for the following LON-CAPA course types: [_1]', + join(', ',map { $coursetypetitles{$_}; } @coursetypes)). + '
            • '; + } else { + $resulttext .= '
            • '.&mt('No mapping to LON-CAPA courses').'
            • '; + } + } + if ($confhash{$itemid}{'makecrs'}) { + $resulttext .= '
            • '.&mt('Instructor may create course (if absent).').'
            • '; + } else { + $resulttext .= '
            • '.&mt('Instructor may not create course (if absent).').'
            • '; + } + if (ref($confhash{$itemid}{'selfenroll'}) eq 'ARRAY') { + if (@{$confhash{$itemid}{'selfenroll'}} > 0) { + $resulttext .= '
            • '.&mt('Self-enrollment for following roles: [_1]', + join(', ',@{$confhash{$itemid}{'selfenroll'}})). + '
            • '; + } else { + $resulttext .= '
            • '.&mt('Self-enrollment not permitted').'
            • '; + } + } + if ($confhash{$itemid}{'section'}) { + if ($confhash{$itemid}{'section'} eq 'course_section_sourcedid') { + $resulttext .= '
            • '.&mt('User section from standard field:'). + ' (course_section_sourcedid)'.'
            • '; + } else { + $resulttext .= '
            • '.&mt('User section from:').' '. + $confhash{$itemid}{'section'}.'
            • '; + } + } else { + $resulttext .= '
            • '.&mt('No section assignment').'
            • '; + } + foreach my $item ('passback','roster','topmenu','inlinemenu') { + $resulttext .= '
            • '.$lt{$item}.': '; + if ($confhash{$itemid}{$item}) { + $resulttext .= &mt('Yes'); + if ($item eq 'passback') { + if ($confhash{$itemid}{'passbackformat'} eq '1.0') { + $resulttext .= ' ('.&mt('Outcomes Extension (1.0)').')'; + } elsif ($confhash{$itemid}{'passbackformat'} eq '1.1') { + $resulttext .= ' ('.&mt('Outcomes Service (1.1)').')'; + } + } + } else { + $resulttext .= &mt('No'); + } + $resulttext .= '
            • '; + } + if (ref($confhash{$itemid}{'lcmenu'}) eq 'ARRAY') { + if (@{$confhash{$itemid}{'lcmenu'}} > 0) { + $resulttext .= '
            • '.&mt('Menu items:').' '. + join(', ', map { $menutitles{$_}; } (@{$confhash{$itemid}{'lcmenu'}})).'
            • '; + } else { + $resulttext .= '
            • '.&mt('No menu items displayed in header or online menu').'
            • '; + } + } + } + $resulttext .= '
            '; + } + } + $resulttext .= '
          '; + } else { + $resulttext = &mt('No changes made.'); + } + } else { + $errors .= '
        • '.&mt('Failed to save changes').'
        • '; + } + if ($errors) { + $resulttext .= &mt('The following errors occurred: ').'
            '. + $errors.'
          '; + } return $resulttext; } +sub get_lti_id { + my ($domain,$consumer) = @_; + # get lock on lti db + my $lockhash = { + lock => $env{'user.name'}. + ':'.$env{'user.domain'}, + }; + my $tries = 0; + my $gotlock = &Apache::lonnet::newput_dom('lti',$lockhash,$domain); + my ($id,$error); + + while (($gotlock ne 'ok') && ($tries<10)) { + $tries ++; + sleep (0.1); + $gotlock = &Apache::lonnet::newput_dom('lti',$lockhash,$domain); + } + if ($gotlock eq 'ok') { + my %currids = &Apache::lonnet::dump_dom('lti',$domain); + if ($currids{'lock'}) { + delete($currids{'lock'}); + if (keys(%currids)) { + my @curr = sort { $a <=> $b } keys(%currids); + if ($curr[-1] =~ /^\d+$/) { + $id = 1 + $curr[-1]; + } + } else { + $id = 1; + } + if ($id) { + unless (&Apache::lonnet::newput_dom('lti',{ $id => $consumer },$domain) eq 'ok') { + $error = 'nostore'; + } + } else { + $error = 'nonumber'; + } + } + my $dellockoutcome = &Apache::lonnet::del_dom('lti',['lock'],$domain); + } else { + $error = 'nolock'; + } + return ($id,$error); +} + sub modify_autoenroll { - my ($dom,%domconfig) = @_; + my ($dom,$lastactref,%domconfig) = @_; my ($resulttext,%changes); my %currautoenroll; if (ref($domconfig{'autoenroll'}) eq 'HASH') { @@ -2615,7 +12461,9 @@ sub modify_autoenroll { } my $autorun = &Apache::lonnet::auto_run(undef,$dom), my %title = ( run => 'Auto-enrollment active', - sender => 'Sender for notification messages'); + sender => 'Sender for notification messages', + coowners => 'Automatic assignment of co-ownership to instructors of record (institutional data)', + failsafe => 'Failsafe for no drops if institutional data missing for a section'); my @offon = ('off','on'); my $sender_uname = $env{'form.sender_uname'}; my $sender_domain = $env{'form.sender_domain'}; @@ -2624,11 +12472,18 @@ sub modify_autoenroll { } elsif ($sender_uname eq '') { $sender_domain = ''; } + my $coowners = $env{'form.autoassign_coowners'}; + my $failsafe = $env{'form.autoenroll_failsafe'}; + $failsafe =~ s{^\s+|\s+$}{}g; + if ($failsafe =~ /\D/) { + undef($failsafe); + } my %autoenrollhash = ( - autoenroll => { run => $env{'form.autoenroll_run'}, - sender_uname => $sender_uname, - sender_domain => $sender_domain, - + autoenroll => { 'run' => $env{'form.autoenroll_run'}, + 'sender_uname' => $sender_uname, + 'sender_domain' => $sender_domain, + 'co-owners' => $coowners, + 'autofailsafe' => $failsafe, } ); my $putresult = &Apache::lonnet::put_dom('configuration',\%autoenrollhash, @@ -2649,6 +12504,16 @@ sub modify_autoenroll { if ($currautoenroll{'sender_domain'} ne $sender_domain) { $changes{'sender'} = 1; } + if ($currautoenroll{'co-owners'} ne '') { + if ($currautoenroll{'co-owners'} ne $coowners) { + $changes{'coowners'} = 1; + } + } elsif ($coowners) { + $changes{'coowners'} = 1; + } + if ($currautoenroll{'autofailsafe'} ne $failsafe) { + $changes{'autofailsafe'} = 1; + } if (keys(%changes) > 0) { $resulttext = &mt('Changes made:').'
            '; if ($changes{'run'}) { @@ -2661,6 +12526,24 @@ sub modify_autoenroll { $resulttext .= '
          • '.&mt("$title{'sender'} set to [_1]",$sender_uname.':'.$sender_domain).'
          • '; } } + if ($changes{'coowners'}) { + $resulttext .= '
          • '.&mt("$title{'coowners'} set to $offon[$env{'form.autoassign_coowners'}]").'
          • '; + &Apache::loncommon::devalidate_domconfig_cache($dom); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domainconfig'} = 1; + } + } + if ($changes{'autofailsafe'}) { + if ($failsafe ne '') { + $resulttext .= '
          • '.&mt('Failsafe for no drops if institutional data missing for a section set to: [_1]',$failsafe).'
          • '; + } else { + $resulttext .= '
          • '.&mt('Failsafe for no drops if institutional data missing for a section: deleted'); + } + &Apache::lonnet::get_domain_defaults($dom,1); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domdefaults'} = 1; + } + } $resulttext .= '
          '; } else { $resulttext = &mt('No changes made to auto-enrollment settings'); @@ -2685,28 +12568,50 @@ sub modify_autoupdate { run => 'Auto-update:', classlists => 'Updates to user information in classlists?' ); - my ($othertitle,$usertypes,$types) = &sorted_inst_types($dom); + my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); my %fieldtitles = &Apache::lonlocal::texthash ( id => 'Student/Employee ID', permanentemail => 'E-mail address', lastname => 'Last Name', firstname => 'First Name', middlename => 'Middle Name', - gen => 'Generation', + generation => 'Generation', ); - my $othertitle = &mt('All users'); + $othertitle = &mt('All users'); if (keys(%{$usertypes}) > 0) { $othertitle = &mt('Other users'); } foreach my $key (keys(%env)) { if ($key =~ /^form\.updateable_(.+)_([^_]+)$/) { - push(@{$fields{$1}},$2); + my ($usertype,$item) = ($1,$2); + if (grep(/^\Q$item\E$/,keys(%fieldtitles))) { + if ($usertype eq 'default') { + push(@{$fields{$1}},$2); + } elsif (ref($types) eq 'ARRAY') { + if (grep(/^\Q$usertype\E$/,@{$types})) { + push(@{$fields{$1}},$2); + } + } + } + } + } + my @lockablenames = &Apache::loncommon::get_env_multiple('form.lockablenames'); + @lockablenames = sort(@lockablenames); + if (ref($currautoupdate{'lockablenames'}) eq 'ARRAY') { + my @changed = &Apache::loncommon::compare_arrays($currautoupdate{'lockablenames'},\@lockablenames); + if (@changed) { + $changes{'lockablenames'} = 1; + } + } else { + if (@lockablenames) { + $changes{'lockablenames'} = 1; } } my %updatehash = ( autoupdate => { run => $env{'form.autoupdate_run'}, classlists => $env{'form.classlists'}, fields => {%fields}, + lockablenames => \@lockablenames, } ); foreach my $key (keys(%currautoupdate)) { @@ -2724,9 +12629,11 @@ sub modify_autoupdate { foreach my $type (@{$currautoupdate{$key}{$item}}) { if (!exists($fields{$item})) { $change = 1; + last; } elsif (ref($fields{$item}) eq 'ARRAY') { if (!grep(/^\Q$type\E$/,@{$fields{$item}})) { $change = 1; + last; } } } @@ -2736,12 +12643,41 @@ sub modify_autoupdate { } } } + } elsif ($key eq 'lockablenames') { + if (ref($currautoupdate{$key}) eq 'ARRAY') { + my @changed = &Apache::loncommon::compare_arrays($currautoupdate{'lockablenames'},\@lockablenames); + if (@changed) { + $changes{'lockablenames'} = 1; + } + } else { + if (@lockablenames) { + $changes{'lockablenames'} = 1; + } + } + } + } + unless (grep(/^\Qlockablenames\E$/,keys(%currautoupdate))) { + if (@lockablenames) { + $changes{'lockablenames'} = 1; } } foreach my $item (@{$types},'default') { if (defined($fields{$item})) { if (ref($currautoupdate{'fields'}) eq 'HASH') { - if (!exists($currautoupdate{'fields'}{$item})) { + if (ref($currautoupdate{'fields'}{$item}) eq 'ARRAY') { + my $change = 0; + if (ref($fields{$item}) eq 'ARRAY') { + foreach my $type (@{$fields{$item}}) { + if (!grep(/^\Q$type\E$/,@{$currautoupdate{'fields'}{$item}})) { + $change = 1; + last; + } + } + } + if ($change) { + push(@{$changes{'fields'}},$item); + } + } else { push(@{$changes{'fields'}},$item); } } else { @@ -2755,7 +12691,17 @@ sub modify_autoupdate { if (keys(%changes) > 0) { $resulttext = &mt('Changes made:').'
            '; foreach my $key (sort(keys(%changes))) { - if (ref($changes{$key}) eq 'ARRAY') { + if ($key eq 'lockablenames') { + $resulttext .= '
          • '; + if (@lockablenames) { + $usertypes->{'default'} = $othertitle; + $resulttext .= &mt("User preference to disable replacement of user's name with institutional data (by auto-update), available for the following affiliations:").' '. + join(', ', map { $usertypes->{$_}; } @lockablenames).'
          • '; + } else { + $resulttext .= &mt("User preference to disable replacement of user's name with institutional data (by auto-update) is unavailable."); + } + $resulttext .= ''; + } elsif (ref($changes{$key}) eq 'ARRAY') { foreach my $item (@{$changes{$key}}) { my @newvalues; foreach my $type (@{$fields{$item}}) { @@ -2794,8 +12740,83 @@ sub modify_autoupdate { return $resulttext; } -sub modify_directorysrch { +sub modify_autocreate { my ($dom,%domconfig) = @_; + my ($resulttext,%changes,%currautocreate,%newvals,%autocreatehash); + if (ref($domconfig{'autocreate'}) eq 'HASH') { + foreach my $key (keys(%{$domconfig{'autocreate'}})) { + $currautocreate{$key} = $domconfig{'autocreate'}{$key}; + } + } + my %title= ( xml => 'Auto-creation of courses in XML course description files', + req => 'Auto-creation of validated requests for official courses', + xmldc => 'Identity of course creator of courses from XML files', + ); + my @types = ('xml','req'); + foreach my $item (@types) { + $newvals{$item} = $env{'form.autocreate_'.$item}; + $newvals{$item} =~ s/\D//g; + $newvals{$item} = 0 if ($newvals{$item} eq ''); + } + $newvals{'xmldc'} = $env{'form.autocreate_xmldc'}; + my %domcoords = &Apache::lonnet::get_active_domroles($dom,['dc']); + unless (exists($domcoords{$newvals{'xmldc'}})) { + $newvals{'xmldc'} = ''; + } + %autocreatehash = ( + autocreate => { xml => $newvals{'xml'}, + req => $newvals{'req'}, + } + ); + if ($newvals{'xmldc'} ne '') { + $autocreatehash{'autocreate'}{'xmldc'} = $newvals{'xmldc'}; + } + my $putresult = &Apache::lonnet::put_dom('configuration',\%autocreatehash, + $dom); + if ($putresult eq 'ok') { + my @items = @types; + if ($newvals{'xml'}) { + push(@items,'xmldc'); + } + foreach my $item (@items) { + if (exists($currautocreate{$item})) { + if ($currautocreate{$item} ne $newvals{$item}) { + $changes{$item} = 1; + } + } elsif ($newvals{$item}) { + $changes{$item} = 1; + } + } + if (keys(%changes) > 0) { + my @offon = ('off','on'); + $resulttext = &mt('Changes made:').'
              '; + foreach my $item (@types) { + if ($changes{$item}) { + my $newtxt = $offon[$newvals{$item}]; + $resulttext .= '
            • '. + &mt("$title{$item} set to [_1]$newtxt [_2]", + '',''). + '
            • '; + } + } + if ($changes{'xmldc'}) { + my ($dcname,$dcdom) = split(':',$newvals{'xmldc'}); + my $newtxt = &Apache::loncommon::plainname($dcname,$dcdom); + $resulttext .= '
            • '.&mt("$title{'xmldc'} set to [_1]",''.$newtxt.'').'
            • '; + } + $resulttext .= '
            '; + } else { + $resulttext = &mt('No changes made to auto-creation settings'); + } + } else { + $resulttext = ''. + &mt('An error occurred: [_1]',$putresult).''; + } + return $resulttext; +} + +sub modify_directorysrch { + my ($dom,$lastactref,%domconfig) = @_; my ($resulttext,%changes); my %currdirsrch; if (ref($domconfig{'directorysrch'}) eq 'HASH') { @@ -2803,8 +12824,10 @@ sub modify_directorysrch { $currdirsrch{$key} = $domconfig{'directorysrch'}{$key}; } } - my %title = ( available => 'Directory search available', - localonly => 'Other domains can search', + my %title = ( available => 'Institutional directory search available', + localonly => 'Other domains can search institution', + lcavailable => 'LON-CAPA directory search available', + lclocalonly => 'Other domains can search LON-CAPA domain', searchby => 'Search types', searchtypes => 'Search latitude'); my @offon = ('off','on'); @@ -2814,7 +12837,7 @@ sub modify_directorysrch { my @cansearch = &Apache::loncommon::get_env_multiple('form.cansearch'); my @searchby = &Apache::loncommon::get_env_multiple('form.searchby'); - my ($othertitle,$usertypes,$types) = &sorted_inst_types($dom); + my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); if (keys(%{$usertypes}) == 0) { @cansearch = ('default'); } else { @@ -2878,7 +12901,9 @@ sub modify_directorysrch { my %dirsrch_hash = ( directorysrch => { available => $env{'form.dirsrch_available'}, cansearch => \@cansearch, - localonly => $env{'form.dirsrch_localonly'}, + localonly => $env{'form.dirsrch_instlocalonly'}, + lclocalonly => $env{'form.dirsrch_domlocalonly'}, + lcavailable => $env{'form.dirsrch_domavailable'}, searchby => \@searchby, searchtypes => \@searchtypes, } @@ -2895,24 +12920,47 @@ sub modify_directorysrch { $changes{'available'} = 1; } } + if (exists($currdirsrch{'lcavailable'})) { + if ($currdirsrch{'lcavailable'} ne $env{'form.dirsrch_domavailable'}) { + $changes{'lcavailable'} = 1; + } + } else { + if ($env{'form.dirsrch_lcavailable'} eq '1') { + $changes{'lcavailable'} = 1; + } + } if (exists($currdirsrch{'localonly'})) { - if ($currdirsrch{'localonly'} ne $env{'form.dirsrch_localonly'}) { - $changes{'localonly'} = 1; - } + if ($currdirsrch{'localonly'} ne $env{'form.dirsrch_instlocalonly'}) { + $changes{'localonly'} = 1; + } } else { - if ($env{'form.dirsrch_localonly'} eq '1') { + if ($env{'form.dirsrch_instlocalonly'} eq '1') { $changes{'localonly'} = 1; } } + if (exists($currdirsrch{'lclocalonly'})) { + if ($currdirsrch{'lclocalonly'} ne $env{'form.dirsrch_domlocalonly'}) { + $changes{'lclocalonly'} = 1; + } + } else { + if ($env{'form.dirsrch_domlocalonly'} eq '1') { + $changes{'lclocalonly'} = 1; + } + } if (keys(%changes) > 0) { $resulttext = &mt('Changes made:').'
              '; if ($changes{'available'}) { $resulttext .= '
            • '.&mt("$title{'available'} set to: $offon[$env{'form.dirsrch_available'}]").'
            • '; } + if ($changes{'lcavailable'}) { + $resulttext .= '
            • '.&mt("$title{'lcavailable'} set to: $offon[$env{'form.dirsrch_domavailable'}]").'
            • '; + } if ($changes{'localonly'}) { - $resulttext .= '
            • '.&mt("$title{'localonly'} set to: $otherdoms[$env{'form.dirsrch_localonly'}]").'
            • '; + $resulttext .= '
            • '.&mt("$title{'localonly'} set to: $otherdoms[$env{'form.dirsrch_instlocalonly'}]").'
            • '; + } + if ($changes{'lclocalonly'}) { + $resulttext .= '
            • '.&mt("$title{'lclocalonly'} set to: $otherdoms[$env{'form.dirsrch_domlocalonly'}]").'
            • '; } - if (ref($changes{'cansearch'}) eq 'ARRAY') { my $chgtext; if (ref($usertypes) eq 'HASH') { @@ -2927,7 +12975,11 @@ sub modify_directorysrch { } else { $chgtext =~ s/\; $//; } - $resulttext .= '
            • '.&mt("Users from domain '[_1]' permitted to search the institutional directory set to: [_2]",$dom,$chgtext).'
            • '; + $resulttext .= + '
            • '. + &mt("Users from domain '[_1]' permitted to search the institutional directory set to: [_2]", + ''.$dom.'',$chgtext). + '
            • '; } } } @@ -2955,11 +13007,15 @@ sub modify_directorysrch { } } $chgtext =~ s/\; $//; - $resulttext .= '
            • '.&mt("$title{'searchtypes'} set to: \"[_1]\"",$chgtext).'
            • '; + $resulttext .= '
            • '.&mt($title{'searchtypes'}.' set to: "[_1]"',$chgtext).'
            • '; } $resulttext .= '
            '; + &Apache::lonnet::do_cache_new('directorysrch',$dom,$dirsrch_hash{'directorysrch'},3600); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'directorysrch'} = 1; + } } else { - $resulttext = &mt('No changes made to institution directory search settings'); + $resulttext = &mt('No changes made to directory search settings'); } } else { $resulttext = ''. @@ -2969,16 +13025,20 @@ sub modify_directorysrch { } sub modify_contacts { - my ($dom,%domconfig) = @_; + my ($dom,$lastactref,%domconfig) = @_; my ($resulttext,%currsetting,%newsetting,%changes,%contacts_hash); if (ref($domconfig{'contacts'}) eq 'HASH') { foreach my $key (keys(%{$domconfig{'contacts'}})) { $currsetting{$key} = $domconfig{'contacts'}{$key}; } } - my (%others,%to); + my (%others,%to,%bcc,%includestr,%includeloc); my @contacts = ('supportemail','adminemail'); - my @mailings = ('errormail','packagesmail','helpdeskmail'); + my @mailings = ('errormail','packagesmail','helpdeskmail','otherdomsmail', + 'lonstatusmail','requestsmail','updatesmail','idconflictsmail'); + my @toggles = ('reporterrors','reportupdates','reportstatus'); + my @lonstatus = ('threshold','sysmail','weights','excluded'); + my ($fields,$fieldtitles,$fieldoptions,$possoptions) = &helpform_fields(); foreach my $type (@mailings) { @{$newsetting{$type}} = &Apache::loncommon::get_env_multiple('form.'.$type); @@ -2988,14 +13048,120 @@ sub modify_contacts { } else { $contacts_hash{contacts}{$type}{$item} = 0; } - } + } $others{$type} = $env{'form.'.$type.'_others'}; $contacts_hash{contacts}{$type}{'others'} = $others{$type}; + if (($type eq 'helpdeskmail') || ($type eq 'otherdomsmail')) { + $bcc{$type} = $env{'form.'.$type.'_bcc'}; + $contacts_hash{contacts}{$type}{'bcc'} = $bcc{$type}; + if (($env{'form.'.$type.'_includestr'} ne '') && ($env{'form.'.$type.'_includeloc'} =~ /^s|b$/)) { + $includestr{$type} = $env{'form.'.$type.'_includestr'}; + $includeloc{$type} = $env{'form.'.$type.'_includeloc'}; + $contacts_hash{contacts}{$type}{'include'} = $includeloc{$type}.':'.&escape($includestr{$type}); + } + } } foreach my $item (@contacts) { $to{$item} = $env{'form.'.$item}; $contacts_hash{'contacts'}{$item} = $to{$item}; } + foreach my $item (@toggles) { + if ($env{'form.'.$item} =~ /^(0|1)$/) { + $contacts_hash{'contacts'}{$item} = $env{'form.'.$item}; + } + } + my ($lonstatus_defs,$lonstatus_names) = &Apache::loncommon::lon_status_items(); + foreach my $item (@lonstatus) { + if ($item eq 'excluded') { + my (%serverhomes,@excluded); + map { $serverhomes{$_} = 1; } values(%Apache::lonnet::serverhomeIDs); + my @possexcluded = &Apache::loncommon::get_env_multiple('form.errorexcluded'); + if (@possexcluded) { + foreach my $id (sort(@possexcluded)) { + if ($serverhomes{$id}) { + push(@excluded,$id); + } + } + } + if (@excluded) { + $contacts_hash{'contacts'}{'lonstatus'}{$item} = \@excluded; + } + } elsif ($item eq 'weights') { + foreach my $type ('E','W','N') { + $env{'form.error'.$item.'_'.$type} =~ s/^\s+|\s+$//g; + if ($env{'form.error'.$item.'_'.$type} =~ /^\d+$/) { + unless ($env{'form.error'.$item.'_'.$type} == $lonstatus_defs->{$type}) { + $contacts_hash{'contacts'}{'lonstatus'}{$item}{$type} = + $env{'form.error'.$item.'_'.$type}; + } + } + } + } elsif (($item eq 'threshold') || ($item eq 'sysmail')) { + $env{'form.error'.$item} =~ s/^\s+|\s+$//g; + if ($env{'form.error'.$item} =~ /^\d+$/) { + unless ($env{'form.error'.$item} == $lonstatus_defs->{$item}) { + $contacts_hash{'contacts'}{'lonstatus'}{$item} = $env{'form.error'.$item}; + } + } + } + } + if ((ref($fields) eq 'ARRAY') && (ref($possoptions) eq 'HASH')) { + foreach my $field (@{$fields}) { + if (ref($possoptions->{$field}) eq 'ARRAY') { + my $value = $env{'form.helpform_'.$field}; + $value =~ s/^\s+|\s+$//g; + if (grep(/^\Q$value\E$/,@{$possoptions->{$field}})) { + $contacts_hash{'contacts'}{'helpform'}{$field} = $value; + if ($field eq 'screenshot') { + $env{'form.helpform_maxsize'} =~ s/^\s+|\s+$//g; + if ($env{'form.helpform_maxsize'} =~ /^\d+\.?\d*$/) { + $contacts_hash{'contacts'}{'helpform'}{'maxsize'} = $env{'form.helpform_maxsize'}; + } + } + } + } + } + } + my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); + my (@statuses,%usertypeshash,@overrides); + if ((ref($types) eq 'ARRAY') && (@{$types} > 0)) { + @statuses = @{$types}; + if (ref($usertypes) eq 'HASH') { + %usertypeshash = %{$usertypes}; + } + } + if (@statuses) { + my @possoverrides = &Apache::loncommon::get_env_multiple('form.overrides'); + foreach my $type (@possoverrides) { + if (($type ne '') && (grep(/^\Q$type\E$/,@statuses))) { + push(@overrides,$type); + } + } + if (@overrides) { + foreach my $type (@overrides) { + my @standard = &Apache::loncommon::get_env_multiple('form.override_'.$type); + foreach my $item (@contacts) { + if (grep(/^\Q$item\E$/,@standard)) { + $contacts_hash{'contacts'}{'overrides'}{$type}{$item} = 1; + $newsetting{'override_'.$type}{$item} = 1; + } else { + $contacts_hash{'contacts'}{'overrides'}{$type}{$item} = 0; + $newsetting{'override_'.$type}{$item} = 0; + } + } + $contacts_hash{'contacts'}{'overrides'}{$type}{'others'} = $env{'form.override_'.$type.'_others'}; + $contacts_hash{'contacts'}{'overrides'}{$type}{'bcc'} = $env{'form.override_'.$type.'_bcc'}; + $newsetting{'override_'.$type}{'others'} = $env{'form.override_'.$type.'_others'}; + $newsetting{'override_'.$type}{'bcc'} = $env{'form.override_'.$type.'_bcc'}; + if (($env{'form.override_'.$type.'_includestr'} ne '') && ($env{'form.override_'.$type.'_includeloc'} =~ /^s|b$/)) { + $includestr{$type} = $env{'form.override_'.$type.'_includestr'}; + $includeloc{$type} = $env{'form.override_'.$type.'_includeloc'}; + $contacts_hash{'contacts'}{'overrides'}{$type}{'include'} = $includeloc{$type}.':'.&escape($includestr{$type}); + $newsetting{'override_'.$type}{'include'} = $contacts_hash{'contacts'}{'overrides'}{$type}{'include'}; + } + } + } + } if (keys(%currsetting) > 0) { foreach my $item (@contacts) { if ($to{$item} ne $currsetting{$item}) { @@ -3015,6 +13181,137 @@ sub modify_contacts { if ($others{$type} ne $currsetting{$type}{'others'}) { push(@{$changes{$type}},'others'); } + if (($type eq 'helpdeskmail') || ($type eq 'otherdomsmail')) { + if ($bcc{$type} ne $currsetting{$type}{'bcc'}) { + push(@{$changes{$type}},'bcc'); + } + my ($currloc,$currstr) = split(/:/,$currsetting{$type}{'include'},2); + if (($includeloc{$type} ne $currloc) || (&escape($includestr{$type}) ne $currstr)) { + push(@{$changes{$type}},'include'); + } + } + } + if (ref($fields) eq 'ARRAY') { + if (ref($currsetting{'helpform'}) eq 'HASH') { + foreach my $field (@{$fields}) { + if ($currsetting{'helpform'}{$field} ne $contacts_hash{'contacts'}{'helpform'}{$field}) { + push(@{$changes{'helpform'}},$field); + } + if (($field eq 'screenshot') && ($contacts_hash{'contacts'}{'helpform'}{'screenshot'} ne 'no')) { + if ($currsetting{'helpform'}{'maxsize'} ne $contacts_hash{'contacts'}{'helpform'}{'maxsize'}) { + push(@{$changes{'helpform'}},'maxsize'); + } + } + } + } else { + foreach my $field (@{$fields}) { + if ($contacts_hash{'contacts'}{'helpform'}{$field} ne 'yes') { + push(@{$changes{'helpform'}},$field); + } + if (($field eq 'screenshot') && ($contacts_hash{'contacts'}{'helpform'}{'screenshot'} ne 'no')) { + if ($contacts_hash{'contacts'}{'helpform'}{'maxsize'} != 1) { + push(@{$changes{'helpform'}},'maxsize'); + } + } + } + } + } + if (@statuses) { + if (ref($currsetting{'overrides'}) eq 'HASH') { + foreach my $key (keys(%{$currsetting{'overrides'}})) { + if (ref($currsetting{'overrides'}{$key}) eq 'HASH') { + if (ref($newsetting{'override_'.$key}) eq 'HASH') { + foreach my $item (@contacts,'bcc','others','include') { + if ($currsetting{'overrides'}{$key}{$item} ne $newsetting{'override_'.$key}{$item}) { + push(@{$changes{'overrides'}},$key); + last; + } + } + } else { + push(@{$changes{'overrides'}},$key); + } + } + } + foreach my $key (@overrides) { + unless (exists($currsetting{'overrides'}{$key})) { + push(@{$changes{'overrides'}},$key); + } + } + } else { + foreach my $key (@overrides) { + push(@{$changes{'overrides'}},$key); + } + } + } + if (ref($currsetting{'lonstatus'}) eq 'HASH') { + foreach my $key ('excluded','weights','threshold','sysmail') { + if ($key eq 'excluded') { + if ((ref($contacts_hash{contacts}{lonstatus}) eq 'HASH') && + (ref($contacts_hash{contacts}{lonstatus}{excluded}) eq 'ARRAY')) { + if ((ref($currsetting{'lonstatus'}{$key}) eq 'ARRAY') && + (@{$currsetting{'lonstatus'}{$key}})) { + my @diffs = + &Apache::loncommon::compare_arrays($contacts_hash{contacts}{lonstatus}{excluded}, + $currsetting{'lonstatus'}{$key}); + if (@diffs) { + push(@{$changes{'lonstatus'}},$key); + } + } elsif (@{$contacts_hash{contacts}{lonstatus}{excluded}}) { + push(@{$changes{'lonstatus'}},$key); + } + } elsif ((ref($currsetting{'lonstatus'}{$key}) eq 'ARRAY') && + (@{$currsetting{'lonstatus'}{$key}})) { + push(@{$changes{'lonstatus'}},$key); + } + } elsif ($key eq 'weights') { + if ((ref($contacts_hash{contacts}{lonstatus}) eq 'HASH') && + (ref($contacts_hash{contacts}{lonstatus}{$key}) eq 'HASH')) { + if (ref($currsetting{'lonstatus'}{$key}) eq 'HASH') { + foreach my $type ('E','W','N','U') { + unless ($contacts_hash{contacts}{lonstatus}{$key}{$type} eq + $currsetting{'lonstatus'}{$key}{$type}) { + push(@{$changes{'lonstatus'}},$key); + last; + } + } + } else { + foreach my $type ('E','W','N','U') { + if ($contacts_hash{contacts}{lonstatus}{$key}{$type} ne '') { + push(@{$changes{'lonstatus'}},$key); + last; + } + } + } + } elsif (ref($currsetting{'lonstatus'}{$key}) eq 'HASH') { + foreach my $type ('E','W','N','U') { + if ($currsetting{'lonstatus'}{$key}{$type} ne '') { + push(@{$changes{'lonstatus'}},$key); + last; + } + } + } + } elsif (($key eq 'threshold') || ($key eq 'sysmail')) { + if (ref($contacts_hash{contacts}{lonstatus}) eq 'HASH') { + if ($currsetting{'lonstatus'}{$key} =~ /^\d+$/) { + if ($currsetting{'lonstatus'}{$key} != $contacts_hash{contacts}{lonstatus}{$key}) { + push(@{$changes{'lonstatus'}},$key); + } + } elsif ($contacts_hash{contacts}{lonstatus}{$key} =~ /^\d+$/) { + push(@{$changes{'lonstatus'}},$key); + } + } elsif ($currsetting{'lonstatus'}{$key} =~ /^\d+$/) { + push(@{$changes{'lonstatus'}},$key); + } + } + } + } else { + if (ref($contacts_hash{contacts}{lonstatus}) eq 'HASH') { + foreach my $key ('excluded','weights','threshold','sysmail') { + if (exists($contacts_hash{contacts}{lonstatus}{$key})) { + push(@{$changes{'lonstatus'}},$key); + } + } + } } } else { my %default; @@ -3023,25 +13320,67 @@ sub modify_contacts { $default{'errormail'} = 'adminemail'; $default{'packagesmail'} = 'adminemail'; $default{'helpdeskmail'} = 'supportemail'; + $default{'otherdomsmail'} = 'supportemail'; + $default{'lonstatusmail'} = 'adminemail'; + $default{'requestsmail'} = 'adminemail'; + $default{'updatesmail'} = 'adminemail'; foreach my $item (@contacts) { if ($to{$item} ne $default{$item}) { - $changes{$item} = 1; - } + $changes{$item} = 1; + } } foreach my $type (@mailings) { if ((@{$newsetting{$type}} != 1) || ($newsetting{$type}[0] ne $default{$type})) { - push(@{$changes{$type}},@{$newsetting{$type}}); } if ($others{$type} ne '') { push(@{$changes{$type}},'others'); - } + } + if (($type eq 'helpdeskmail') || ($type eq 'otherdomsmail')) { + if ($bcc{$type} ne '') { + push(@{$changes{$type}},'bcc'); + } + if (($includeloc{$type} =~ /^b|s$/) && ($includestr{$type} ne '')) { + push(@{$changes{$type}},'include'); + } + } + } + if (ref($fields) eq 'ARRAY') { + foreach my $field (@{$fields}) { + if ($contacts_hash{'contacts'}{'helpform'}{$field} ne 'yes') { + push(@{$changes{'helpform'}},$field); + } + if (($field eq 'screenshot') && ($contacts_hash{'contacts'}{'helpform'}{'screenshot'} ne 'no')) { + if ($contacts_hash{'contacts'}{'helpform'}{'maxsize'} != 1) { + push(@{$changes{'helpform'}},'maxsize'); + } + } + } + } + if (ref($contacts_hash{contacts}{lonstatus}) eq 'HASH') { + foreach my $key ('excluded','weights','threshold','sysmail') { + if (exists($contacts_hash{contacts}{lonstatus}{$key})) { + push(@{$changes{'lonstatus'}},$key); + } + } + } + } + foreach my $item (@toggles) { + if (($env{'form.'.$item} == 1) && ($currsetting{$item} == 0)) { + $changes{$item} = 1; + } elsif ((!$env{'form.'.$item}) && + (($currsetting{$item} eq '') || ($currsetting{$item} == 1))) { + $changes{$item} = 1; } } my $putresult = &Apache::lonnet::put_dom('configuration',\%contacts_hash, $dom); if ($putresult eq 'ok') { if (keys(%changes) > 0) { + &Apache::loncommon::devalidate_domconfig_cache($dom); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domainconfig'} = 1; + } my ($titles,$short_titles) = &contact_titles(); $resulttext = &mt('Changes made:').'
              '; foreach my $item (@contacts) { @@ -3054,7 +13393,11 @@ sub modify_contacts { } foreach my $type (@mailings) { if (ref($changes{$type}) eq 'ARRAY') { - $resulttext .= '
            • '.$titles->{$type}.': '; + if (($type eq 'helpdeskmail') || ($type eq 'otherdomsmail')) { + $resulttext .= '
            • '.$titles->{$type}.' -- '.&mt('sent to').': '; + } else { + $resulttext .= '
            • '.$titles->{$type}.': '; + } my @text; foreach my $item (@{$newsetting{$type}}) { push(@text,$short_titles->{$item}); @@ -3062,13 +13405,206 @@ sub modify_contacts { if ($others{$type} ne '') { push(@text,$others{$type}); } - $resulttext .= ''. - join(', ',@text).'
            • '; + if (@text) { + $resulttext .= ''. + join(', ',@text).''; + } + if (($type eq 'helpdeskmail') || ($type eq 'otherdomsmail')) { + if ($bcc{$type} ne '') { + my $bcctext; + if (@text) { + $bcctext = ' '.&mt('with Bcc to'); + } else { + $bcctext = '(Bcc)'; + } + $resulttext .= $bcctext.': '.$bcc{$type}.''; + } elsif (!@text) { + $resulttext .= &mt('No one'); + } + if ($includestr{$type} ne '') { + if ($includeloc{$type} eq 'b') { + $resulttext .= '
              '.&mt('Text automatically added to e-mail body:').' '.$includestr{$type}; + } elsif ($includeloc{$type} eq 's') { + $resulttext .= '
              '.&mt('Text automatically added to e-mail subject:').' '.$includestr{$type}; + } + } + } elsif (!@text) { + $resulttext .= &mt('No recipients'); + } + $resulttext .= ''; + } + } + if (ref($changes{'overrides'}) eq 'ARRAY') { + my @deletions; + foreach my $type (@{$changes{'overrides'}}) { + if ($usertypeshash{$type}) { + if (grep(/^\Q$type\E/,@overrides)) { + $resulttext .= '
            • '.&mt("Overrides based on requester's affiliation set for [_1]", + $usertypeshash{$type}).'
              • '; + if (ref($newsetting{'override_'.$type}) eq 'HASH') { + my @text; + foreach my $item (@contacts) { + if ($newsetting{'override_'.$type}{$item}) { + push(@text,$short_titles->{$item}); + } + } + if ($newsetting{'override_'.$type}{'others'} ne '') { + push(@text,$newsetting{'override_'.$type}{'others'}); + } + + if (@text) { + $resulttext .= &mt('Helpdesk e-mail sent to: [_1]', + ''.join(', ',@text).''); + } + if ($newsetting{'override_'.$type}{'bcc'} ne '') { + my $bcctext; + if (@text) { + $bcctext = ' '.&mt('with Bcc to'); + } else { + $bcctext = '(Bcc)'; + } + $resulttext .= $bcctext.': '.$newsetting{'override_'.$type}{'bcc'}.''; + } elsif (!@text) { + $resulttext .= &mt('Helpdesk e-mail sent to no one'); + } + $resulttext .= '
              • '; + if ($newsetting{'override_'.$type}{'include'} ne '') { + my ($loc,$str) = split(/:/,$newsetting{'override_'.$type}{'include'}); + if ($loc eq 'b') { + $resulttext .= '
              • '.&mt('Text automatically added to e-mail body:').' '.&unescape($str).'
              • '; + } elsif ($loc eq 's') { + $resulttext .= '
              • '.&mt('Text automatically added to e-mail subject:').' '.&unescape($str).'
              • '; + } + } + } + $resulttext .= '
            • '; + } else { + push(@deletions,$usertypeshash{$type}); + } + } + } + if (@deletions) { + $resulttext .= '
            • '.&mt("Overrides based on requester's affiliation discontinued for: [_1]", + join(', ',@deletions)).'
            • '; + } + } + my @offon = ('off','on'); + my $corelink = &core_link_msu(); + if ($changes{'reporterrors'}) { + $resulttext .= '
            • '. + &mt('E-mail error reports to [_1] set to "'. + $offon[$env{'form.reporterrors'}].'".', + $corelink). + '
            • '; + } + if ($changes{'reportupdates'}) { + $resulttext .= '
            • '. + &mt('E-mail record of completed LON-CAPA updates to [_1] set to "'. + $offon[$env{'form.reportupdates'}].'".', + $corelink). + '
            • '; + } + if ($changes{'reportstatus'}) { + $resulttext .= '
            • '. + &mt('E-mail status if errors above threshold to [_1] set to "'. + $offon[$env{'form.reportstatus'}].'".', + $corelink). + '
            • '; + } + if (ref($changes{'lonstatus'}) eq 'ARRAY') { + $resulttext .= '
            • '. + &mt('Nightly status check e-mail settings').':
                '; + my (%defval,%use_def,%shown); + $defval{'threshold'} = $lonstatus_defs->{'threshold'}; + $defval{'sysmail'} = $lonstatus_defs->{'sysmail'}; + $defval{'weights'} = + join(', ',map { $lonstatus_names->{$_}.'='.$lonstatus_defs->{$_}; } ('E','W','N','U')); + $defval{'excluded'} = &mt('None'); + if (ref($contacts_hash{'contacts'}{'lonstatus'}) eq 'HASH') { + foreach my $item ('threshold','sysmail','weights','excluded') { + if (exists($contacts_hash{'contacts'}{'lonstatus'}{$item})) { + if (($item eq 'threshold') || ($item eq 'sysmail')) { + $shown{$item} = $contacts_hash{'contacts'}{'lonstatus'}{$item}; + } elsif ($item eq 'weights') { + if (ref($contacts_hash{'contacts'}{'lonstatus'}{$item}) eq 'HASH') { + foreach my $type ('E','W','N','U') { + $shown{$item} .= $lonstatus_names->{$type}.'='; + if (exists($contacts_hash{'contacts'}{'lonstatus'}{$item}{$type})) { + $shown{$item} .= $contacts_hash{'contacts'}{'lonstatus'}{$item}{$type}; + } else { + $shown{$item} .= $lonstatus_defs->{$type}; + } + $shown{$item} .= ', '; + } + $shown{$item} =~ s/, $//; + } else { + $shown{$item} = $defval{$item}; + } + } elsif ($item eq 'excluded') { + if (ref($contacts_hash{'contacts'}{'lonstatus'}{$item}) eq 'ARRAY') { + $shown{$item} = join(', ',@{$contacts_hash{'contacts'}{'lonstatus'}{$item}}); + } else { + $shown{$item} = $defval{$item}; + } + } + } else { + $shown{$item} = $defval{$item}; + } + } + } else { + foreach my $item ('threshold','weights','excluded','sysmail') { + $shown{$item} = $defval{$item}; + } + } + foreach my $item ('threshold','weights','excluded','sysmail') { + $resulttext .= '
              • '.&mt($titles->{'error'.$item}.' -- [_1]', + $shown{$item}).'
              • '; + } + $resulttext .= '
            • '; + } + if ((ref($changes{'helpform'}) eq 'ARRAY') && (ref($fields) eq 'ARRAY')) { + my (@optional,@required,@unused,$maxsizechg); + foreach my $field (@{$changes{'helpform'}}) { + if ($field eq 'maxsize') { + $maxsizechg = 1; + next; + } + if ($contacts_hash{'contacts'}{'helpform'}{$field} eq 'yes') { + push(@optional,$field); + } elsif ($contacts_hash{'contacts'}{'helpform'}{$field} eq 'no') { + push(@unused,$field); + } elsif ($contacts_hash{'contacts'}{'helpform'}{$field} eq 'req') { + push(@required,$field); + } + } + if (@optional) { + $resulttext .= '
            • '. + &mt('Help form fields changed to "Optional": [_1].', + ''.join(', ',map { $fieldtitles->{$_}; } @optional)).''. + '
            • '; + } + if (@required) { + $resulttext .= '
            • '. + &mt('Help form fields changed to "Required": [_1].', + ''.join(', ',map { $fieldtitles->{$_}; } @required)).''. + '
            • '; + } + if (@unused) { + $resulttext .= '
            • '. + &mt('Help form fields changed to "Not shown": [_1].', + ''.join(', ',map { $fieldtitles->{$_}; } @unused)).''. + '
            • '; + } + if ($maxsizechg) { + $resulttext .= '
            • '. + &mt('Max size for file uploaded to help form by logged-in user set to [_1] MB.', + $contacts_hash{'contacts'}{'helpform'}{'maxsize'}). + '
            • '; } } $resulttext .= '
            '; } else { - $resulttext = &mt('No changes made to contact information'); + $resulttext = &mt('No changes made to contacts and form settings'); } } else { $resulttext = ''. @@ -3079,19 +13615,30 @@ sub modify_contacts { sub modify_usercreation { my ($dom,%domconfig) = @_; - my ($resulttext,%curr_usercreation,%changes,%authallowed,%cancreate); + my ($resulttext,%curr_usercreation,%changes,%authallowed,%cancreate,%save_usercreate); + my $warningmsg; if (ref($domconfig{'usercreation'}) eq 'HASH') { foreach my $key (keys(%{$domconfig{'usercreation'}})) { - $curr_usercreation{$key} = $domconfig{'usercreation'}{$key}; + if ($key eq 'cancreate') { + if (ref($domconfig{'usercreation'}{$key}) eq 'HASH') { + foreach my $item (keys(%{$domconfig{'usercreation'}{$key}})) { + if (($item eq 'requestcrs') || ($item eq 'course') || ($item eq 'author')) { + $curr_usercreation{$key}{$item} = $domconfig{'usercreation'}{$key}{$item}; + } else { + $save_usercreate{$key}{$item} = $domconfig{'usercreation'}{$key}{$item}; + } + } + } + } elsif ($key eq 'email_rule') { + $save_usercreate{$key} = $domconfig{'usercreation'}{$key}; + } else { + $curr_usercreation{$key} = $domconfig{'usercreation'}{$key}; + } } } - my %title = &Apache::lonlocal::texthash ( - author => 'adding co-authors/assistant authors', - course => 'adding users to a course', - ); my @username_rule = &Apache::loncommon::get_env_multiple('form.username_rule'); my @id_rule = &Apache::loncommon::get_env_multiple('form.id_rule'); - my @contexts = ('author','course'); + my @contexts = ('author','course','requestcrs'); foreach my $item(@contexts) { $cancreate{$item} = $env{'form.can_createuser_'.$item}; } @@ -3099,11 +13646,11 @@ sub modify_usercreation { foreach my $item (@contexts) { if ($curr_usercreation{'cancreate'}{$item} ne $cancreate{$item}) { push(@{$changes{'cancreate'}},$item); - } + } } } elsif (ref($curr_usercreation{'cancreate'}) eq 'ARRAY') { foreach my $item (@contexts) { - if (grep(/^\Q$item\E$/,@{$curr_usercreation{'cancreate'}})) { + if (!grep(/^\Q$item\E$/,@{$curr_usercreation{'cancreate'}})) { if ($cancreate{$item} ne 'any') { push(@{$changes{'cancreate'}},$item); } @@ -3114,7 +13661,7 @@ sub modify_usercreation { } } } else { - foreach my $item ('author','course') { + foreach my $item (@contexts) { push(@{$changes{'cancreate'}},$item); } } @@ -3149,10 +13696,10 @@ sub modify_usercreation { push(@{$changes{'id_rule'}},@id_rule); } - my @contexts = ('author','course','domain'); - my @authtypes = ('int','krb4','krb5','loc'); + my @authen_contexts = ('author','course','domain'); + my @authtypes = ('int','krb4','krb5','loc','lti'); my %authhash; - foreach my $item (@contexts) { + foreach my $item (@authen_contexts) { my @authallowed = &Apache::loncommon::get_env_multiple('form.'.$item.'_auth'); foreach my $auth (@authtypes) { if (grep(/^\Q$auth\E$/,@authallowed)) { @@ -3163,7 +13710,7 @@ sub modify_usercreation { } } if (ref($curr_usercreation{'authtypes'}) eq 'HASH') { - foreach my $item (@contexts) { + foreach my $item (@authen_contexts) { if (ref($curr_usercreation{'authtypes'}{$item}) eq 'HASH') { foreach my $auth (@authtypes) { if ($authhash{$item}{$auth} ne $curr_usercreation{'authtypes'}{$item}{$auth}) { @@ -3174,37 +13721,40 @@ sub modify_usercreation { } } } else { - foreach my $item (@contexts) { + foreach my $item (@authen_contexts) { push(@{$changes{'authtypes'}},$item); } } + $save_usercreate{'cancreate'}{'course'} = $cancreate{'course'}; + $save_usercreate{'cancreate'}{'author'} = $cancreate{'author'}; + $save_usercreate{'cancreate'}{'requestcrs'} = $cancreate{'requestcrs'}; + $save_usercreate{'id_rule'} = \@id_rule; + $save_usercreate{'username_rule'} = \@username_rule, + $save_usercreate{'authtypes'} = \%authhash; + my %usercreation_hash = ( - usercreation => { - cancreate => \%cancreate, - username_rule => \@username_rule, - id_rule => \@id_rule, - authtypes => \%authhash, - } - ); + usercreation => \%save_usercreate, + ); my $putresult = &Apache::lonnet::put_dom('configuration',\%usercreation_hash, $dom); + if ($putresult eq 'ok') { if (keys(%changes) > 0) { $resulttext = &mt('Changes made:').'
              '; if (ref($changes{'cancreate'}) eq 'ARRAY') { my %lt = &usercreation_types(); foreach my $type (@{$changes{'cancreate'}}) { - my $chgtext; + my $chgtext = $lt{$type}.', '; if ($cancreate{$type} eq 'none') { - $chgtext = $lt{$type}.' '.&mt('creation of new users is not permitted, except by a Domain Coordinator.'); + $chgtext .= &mt('creation of new users is not permitted, except by a Domain Coordinator.'); } elsif ($cancreate{$type} eq 'any') { - $chgtext = $lt{$type}.' '.&mt('creation of new users is permitted for both institutional and non-institutional usernames.'); + $chgtext .= &mt('creation of new users is permitted for both institutional and non-institutional usernames.'); } elsif ($cancreate{$type} eq 'official') { - $chgtext = $lt{$type}.' '.&mt('creation of new users is only permitted for institutional usernames.',$lt{$type}); + $chgtext .= &mt('creation of new users is only permitted for institutional usernames.'); } elsif ($cancreate{$type} eq 'unofficial') { - $chgtext = $lt{$type}.' '.&mt('creation of new users is only permitted for non-institutional usernames.',$lt{$type}); + $chgtext .= &mt('creation of new users is only permitted for non-institutional usernames.'); } $resulttext .= '
            • '.$chgtext.'
            • '; } @@ -3253,7 +13803,11 @@ sub modify_usercreation { push(@allowed,$authname{$auth}); } } - $chgtext .= join(', ',@allowed).''; + if (@allowed > 0) { + $chgtext .= join(', ',@allowed).''; + } else { + $chgtext .= &mt('none').''; + } } $chgtext .= '
            '; $resulttext .= '
          • '.&mt('Authentication types available for assignment to new users').'
            '.$chgtext; @@ -3267,15 +13821,965 @@ sub modify_usercreation { $resulttext = ''. &mt('An error occurred: [_1]',$putresult).''; } + if ($warningmsg ne '') { + $resulttext .= '
            '.$warningmsg.'
            '; + } return $resulttext; } +sub modify_selfcreation { + my ($dom,$lastactref,%domconfig) = @_; + my ($resulttext,$warningmsg,%curr_usercreation,%curr_usermodify,%curr_inststatus,%changes,%cancreate); + my (%save_usercreate,%save_usermodify,%save_inststatus,@types,%usertypes); + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); + my ($othertitle,$usertypesref,$typesref) = &Apache::loncommon::sorted_inst_types($dom); + if (ref($typesref) eq 'ARRAY') { + @types = @{$typesref}; + } + if (ref($usertypesref) eq 'HASH') { + %usertypes = %{$usertypesref}; + } + $usertypes{'default'} = $othertitle; +# +# Retrieve current domain configuration for self-creation of usernames from $domconfig{'usercreation'}. +# + if (ref($domconfig{'usercreation'}) eq 'HASH') { + foreach my $key (keys(%{$domconfig{'usercreation'}})) { + if ($key eq 'cancreate') { + if (ref($domconfig{'usercreation'}{$key}) eq 'HASH') { + foreach my $item (keys(%{$domconfig{'usercreation'}{$key}})) { + if (($item eq 'selfcreate') || ($item eq 'statustocreate') || + ($item eq 'captcha') || ($item eq 'recaptchakeys') || + ($item eq 'recaptchaversion') || ($item eq 'notify') || + ($item eq 'emailusername') || ($item eq 'shibenv') || + ($item eq 'selfcreateprocessing') || ($item eq 'emailverified') || + ($item eq 'emailoptions') || ($item eq 'emaildomain')) { + $curr_usercreation{$key}{$item} = $domconfig{'usercreation'}{$key}{$item}; + } else { + $save_usercreate{$key}{$item} = $domconfig{'usercreation'}{$key}{$item}; + } + } + } + } elsif ($key eq 'email_rule') { + $curr_usercreation{$key} = $domconfig{'usercreation'}{$key}; + } else { + $save_usercreate{$key} = $domconfig{'usercreation'}{$key}; + } + } + } +# +# Retrieve current domain configuration for self-creation of usernames from $domconfig{'usermodification'}. +# + if (ref($domconfig{'usermodification'}) eq 'HASH') { + foreach my $key (keys(%{$domconfig{'usermodification'}})) { + if ($key eq 'selfcreate') { + $curr_usermodify{$key} = $domconfig{'usermodification'}{$key}; + } else { + $save_usermodify{$key} = $domconfig{'usermodification'}{$key}; + } + } + } +# +# Retrieve current domain configuration for institutional status types from $domconfig{'inststatus'}. +# + if (ref($domconfig{'inststatus'}) eq 'HASH') { + foreach my $key (keys(%{$domconfig{'inststatus'}})) { + if ($key eq 'inststatusguest') { + $curr_inststatus{$key} = $domconfig{'inststatus'}{$key}; + } else { + $save_inststatus{$key} = $domconfig{'inststatus'}{$key}; + } + } + } + + my @contexts = ('selfcreate'); + @{$cancreate{'selfcreate'}} = (); + %{$cancreate{'emailusername'}} = (); + if (@types) { + @{$cancreate{'statustocreate'}} = (); + } + %{$cancreate{'selfcreateprocessing'}} = (); + %{$cancreate{'shibenv'}} = (); + %{$cancreate{'emailverified'}} = (); + %{$cancreate{'emailoptions'}} = (); + %{$cancreate{'emaildomain'}} = (); + my %selfcreatetypes = ( + sso => 'users authenticated by institutional single sign on', + login => 'users authenticated by institutional log-in', + email => 'users verified by e-mail', + ); +# +# Populate $cancreate{'selfcreate'} array reference with types of user, for which self-creation of user accounts +# is permitted. +# + my ($emailrules,$emailruleorder) = &Apache::lonnet::inst_userrules($dom,'email'); + + my (@statuses,%email_rule); + foreach my $item ('login','sso','email') { + if ($item eq 'email') { + if ($env{'form.cancreate_email'}) { + if (@types) { + my @poss_statuses = &Apache::loncommon::get_env_multiple('form.selfassign'); + foreach my $status (@poss_statuses) { + if (grep(/^\Q$status\E$/,(@types,'default'))) { + push(@statuses,$status); + } + } + $save_inststatus{'inststatusguest'} = \@statuses; + } else { + push(@statuses,'default'); + } + if (@statuses) { + my %curr_rule; + if (ref($curr_usercreation{'email_rule'}) eq 'ARRAY') { + foreach my $type (@statuses) { + $curr_rule{$type} = $curr_usercreation{'email_rule'}; + } + } elsif (ref($curr_usercreation{'email_rule'}) eq 'HASH') { + foreach my $type (@statuses) { + $curr_rule{$type} = $curr_usercreation{'email_rule'}{$type}; + } + } + push(@{$cancreate{'selfcreate'}},'email'); + push(@contexts,('selfcreateprocessing','emailverified','emailoptions')); + my %curremaildom; + if (ref($curr_usercreation{'cancreate'}{'emaildomain'}) eq 'HASH') { + %curremaildom = %{$curr_usercreation{'cancreate'}{'emaildomain'}}; + } + foreach my $type (@statuses) { + if ($env{'form.cancreate_emailprocess_'.$type} =~ /^(?:approval|automatic)$/) { + $cancreate{'selfcreateprocessing'}{$type} = $env{'form.cancreate_emailprocess_'.$type}; + } + if ($env{'form.cancreate_usernameoptions_'.$type} =~ /^(?:all|first|free)$/) { + $cancreate{'emailverified'}{$type} = $env{'form.cancreate_usernameoptions_'.$type}; + } + if ($env{'form.cancreate_emailoptions_'.$type} =~ /^(any|inst|noninst|custom)$/) { +# +# Retrieve rules (if any) governing types of e-mail address which may be used to verify a username. +# + my $chosen = $1; + if (($chosen eq 'inst') || ($chosen eq 'noninst')) { + my $emaildom; + if ($env{'form.cancreate_emaildomain_'.$chosen.'_'.$type} =~ /^\@[^\@]+$/) { + $emaildom = $env{'form.cancreate_emaildomain_'.$chosen.'_'.$type}; + $cancreate{'emaildomain'}{$type}{$chosen} = $emaildom; + if (ref($curremaildom{$type}) eq 'HASH') { + if (exists($curremaildom{$type}{$chosen})) { + if ($curremaildom{$type}{$chosen} ne $emaildom) { + push(@{$changes{'cancreate'}},'emaildomain'); + } + } elsif ($emaildom ne '') { + push(@{$changes{'cancreate'}},'emaildomain'); + } + } elsif ($emaildom ne '') { + push(@{$changes{'cancreate'}},'emaildomain'); + } + } + $cancreate{'emailoptions'}{$type} = $env{'form.cancreate_emailoptions_'.$type}; + } elsif ($chosen eq 'custom') { + my @possemail_rules = &Apache::loncommon::get_env_multiple('form.email_rule_'.$type); + $email_rule{$type} = []; + if (ref($emailrules) eq 'HASH') { + foreach my $rule (@possemail_rules) { + if (exists($emailrules->{$rule})) { + push(@{$email_rule{$type}},$rule); + } + } + } + if (@{$email_rule{$type}}) { + $cancreate{'emailoptions'}{$type} = 'custom'; + if (ref($curr_rule{$type}) eq 'ARRAY') { + if (@{$curr_rule{$type}} > 0) { + foreach my $rule (@{$curr_rule{$type}}) { + if (!grep(/^\Q$rule\E$/,@{$email_rule{$type}})) { + push(@{$changes{'email_rule'}},$type); + } + } + } + foreach my $type (@{$email_rule{$type}}) { + if (!grep(/^\Q$type\E$/,@{$curr_rule{$type}})) { + push(@{$changes{'email_rule'}},$type); + } + } + } else { + push(@{$changes{'email_rule'}},$type); + } + } + } else { + $cancreate{'emailoptions'}{$type} = $env{'form.cancreate_emailoptions_'.$type}; + } + } + } + if (@types) { + if (ref($curr_inststatus{'inststatusguest'}) eq 'ARRAY') { + my @changed = &Apache::loncommon::compare_arrays(\@statuses,$curr_inststatus{'inststatusguest'}); + if (@changed) { + push(@{$changes{'inststatus'}},'inststatusguest'); + } + } else { + push(@{$changes{'inststatus'}},'inststatusguest'); + } + } + } else { + delete($env{'form.cancreate_email'}); + if (ref($curr_inststatus{'inststatusguest'}) eq 'ARRAY') { + if (@{$curr_inststatus{'inststatusguest'}} > 0) { + push(@{$changes{'inststatus'}},'inststatusguest'); + } + } + } + } else { + $save_inststatus{'inststatusguest'} = []; + if (ref($curr_inststatus{'inststatusguest'}) eq 'ARRAY') { + if (@{$curr_inststatus{'inststatusguest'}} > 0) { + push(@{$changes{'inststatus'}},'inststatusguest'); + } + } + } + } else { + if ($env{'form.cancreate_'.$item}) { + push(@{$cancreate{'selfcreate'}},$item); + } + } + } + my (%userinfo,%savecaptcha); + my ($infofields,$infotitles) = &Apache::loncommon::emailusername_info(); +# +# Populate $cancreate{'emailusername'}{$type} hash ref with information fields (if new user will provide data +# value set to one), if self-creation with e-mail address permitted, where $type is user type: faculty, staff, student etc. +# + + if ($env{'form.cancreate_email'}) { + push(@contexts,'emailusername'); + if (@statuses) { + foreach my $type (@statuses) { + if (ref($infofields) eq 'ARRAY') { + foreach my $field (@{$infofields}) { + if ($env{'form.canmodify_emailusername_'.$type.'_'.$field} =~ /^(required|optional)$/) { + $cancreate{'emailusername'}{$type}{$field} = $1; + } + } + } + } + } +# +# Populate $cancreate{'notify'} hash ref with names of Domain Coordinators who are to be notified of +# queued requests for self-creation of account verified by e-mail. +# + + my @approvalnotify = &Apache::loncommon::get_env_multiple('form.selfcreationnotifyapproval'); + @approvalnotify = sort(@approvalnotify); + $cancreate{'notify'}{'approval'} = join(',',@approvalnotify); + if (ref($curr_usercreation{'cancreate'}) eq 'HASH') { + if (ref($curr_usercreation{'cancreate'}{'notify'}) eq 'HASH') { + if ($curr_usercreation{'cancreate'}{'notify'}{'approval'} ne $cancreate{'notify'}{'approval'}) { + push(@{$changes{'cancreate'}},'notify'); + } + } else { + if ($cancreate{'notify'}{'approval'}) { + push(@{$changes{'cancreate'}},'notify'); + } + } + } elsif ($cancreate{'notify'}{'approval'}) { + push(@{$changes{'cancreate'}},'notify'); + } + + &process_captcha('cancreate',\%changes,\%savecaptcha,$curr_usercreation{'cancreate'}); + } +# +# Check if domain default is set appropriately, if self-creation of accounts is to be available for +# institutional log-in. +# + if (grep(/^login$/,@{$cancreate{'selfcreate'}})) { + if (!((($domdefaults{'auth_def'} =~/^krb/) && ($domdefaults{'auth_arg_def'} ne '')) || + ($domdefaults{'auth_def'} eq 'localauth'))) { + $warningmsg = &mt('Although account creation has been set to be available for institutional logins, currently default authentication in this domain has not been set to support this.').' '. + &mt('You need to set the default authentication type to Kerberos 4 or 5 (with a Kerberos domain specified), or to Local authentication, if the localauth module has been customized in your domain to authenticate institutional logins.'); + } + } + my @fields = ('lastname','firstname','middlename','generation', + 'permanentemail','id'); + my @shibfields = (@fields,'inststatus'); + my %fieldtitles = &Apache::loncommon::personal_data_fieldtitles(); +# +# Where usernames may created for institutional log-in and/or institutional single sign on: +# (a) populate $cancreate{'statustocreate'} array reference with institutional status types who +# may self-create accounts +# (b) populate $save_usermodify{'selfcreate'} hash reference with status types, and information fields +# which the user may supply, if institutional data is unavailable. +# + if (($env{'form.cancreate_login'}) || ($env{'form.cancreate_sso'})) { + if (@types) { + @{$cancreate{'statustocreate'}} = &Apache::loncommon::get_env_multiple('form.statustocreate'); + push(@contexts,'statustocreate'); + foreach my $type (@types) { + my @modifiable = &Apache::loncommon::get_env_multiple('form.canmodify_'.$type); + foreach my $field (@fields) { + if (grep(/^\Q$field\E$/,@modifiable)) { + $save_usermodify{'selfcreate'}{$type}{$field} = 1; + } else { + $save_usermodify{'selfcreate'}{$type}{$field} = 0; + } + } + } + if (ref($curr_usermodify{'selfcreate'}) eq 'HASH') { + foreach my $type (@types) { + if (ref($curr_usermodify{'selfcreate'}{$type}) eq 'HASH') { + foreach my $field (@fields) { + if ($save_usermodify{'selfcreate'}{$type}{$field} ne + $curr_usermodify{'selfcreate'}{$type}{$field}) { + push(@{$changes{'selfcreate'}},$type); + last; + } + } + } + } + } else { + foreach my $type (@types) { + push(@{$changes{'selfcreate'}},$type); + } + } + } + foreach my $field (@shibfields) { + if ($env{'form.shibenv_'.$field} ne '') { + $cancreate{'shibenv'}{$field} = $env{'form.shibenv_'.$field}; + } + } + if (ref($curr_usercreation{'cancreate'}) eq 'HASH') { + if (ref($curr_usercreation{'cancreate'}{'shibenv'}) eq 'HASH') { + foreach my $field (@shibfields) { + if ($env{'form.shibenv_'.$field} ne $curr_usercreation{'cancreate'}{'shibenv'}{$field}) { + push(@{$changes{'cancreate'}},'shibenv'); + } + } + } else { + foreach my $field (@shibfields) { + if ($env{'form.shibenv_'.$field}) { + push(@{$changes{'cancreate'}},'shibenv'); + last; + } + } + } + } + } + foreach my $item (@contexts) { + if (ref($curr_usercreation{'cancreate'}{$item}) eq 'ARRAY') { + foreach my $curr (@{$curr_usercreation{'cancreate'}{$item}}) { + if (ref($cancreate{$item}) eq 'ARRAY') { + if (!grep(/^$curr$/,@{$cancreate{$item}})) { + if (!grep(/^$item$/,@{$changes{'cancreate'}})) { + push(@{$changes{'cancreate'}},$item); + } + } + } + } + if (ref($cancreate{$item}) eq 'ARRAY') { + foreach my $type (@{$cancreate{$item}}) { + if (!grep(/^$type$/,@{$curr_usercreation{'cancreate'}{$item}})) { + if (!grep(/^$item$/,@{$changes{'cancreate'}})) { + push(@{$changes{'cancreate'}},$item); + } + } + } + } + } elsif (ref($curr_usercreation{'cancreate'}{$item}) eq 'HASH') { + if (ref($cancreate{$item}) eq 'HASH') { + foreach my $type (keys(%{$curr_usercreation{'cancreate'}{$item}})) { + if (ref($curr_usercreation{'cancreate'}{$item}{$type}) eq 'HASH') { + foreach my $field (keys(%{$curr_usercreation{'cancreate'}{$item}{$type}})) { + unless ($curr_usercreation{'cancreate'}{$item}{$type}{$field} eq $cancreate{$item}{$type}{$field}) { + if (!grep(/^$item$/,@{$changes{'cancreate'}})) { + push(@{$changes{'cancreate'}},$item); + } + } + } + } elsif (($item eq 'selfcreateprocessing') || ($item eq 'emailverified') || ($item eq 'emailoptions')) { + if ($cancreate{$item}{$type} ne $curr_usercreation{'cancreate'}{$item}{$type}) { + if (!grep(/^$item$/,@{$changes{'cancreate'}})) { + push(@{$changes{'cancreate'}},$item); + } + } + } + } + foreach my $type (keys(%{$cancreate{$item}})) { + if (ref($cancreate{$item}{$type}) eq 'HASH') { + foreach my $field (keys(%{$cancreate{$item}{$type}})) { + if (ref($curr_usercreation{'cancreate'}{$item}{$type}) eq 'HASH') { + unless ($curr_usercreation{'cancreate'}{$item}{$type}{$field} eq $cancreate{$item}{$type}{$field}) { + if (!grep(/^$item$/,@{$changes{'cancreate'}})) { + push(@{$changes{'cancreate'}},$item); + } + } + } else { + if (!grep(/^$item$/,@{$changes{'cancreate'}})) { + push(@{$changes{'cancreate'}},$item); + } + } + } + } elsif (($item eq 'selfcreateprocessing') || ($item eq 'emailverified') || ($item eq 'emailoptions')) { + if ($cancreate{$item}{$type} ne $curr_usercreation{'cancreate'}{$item}{$type}) { + if (!grep(/^$item$/,@{$changes{'cancreate'}})) { + push(@{$changes{'cancreate'}},$item); + } + } + } + } + } + } elsif ($curr_usercreation{'cancreate'}{$item}) { + if (ref($cancreate{$item}) eq 'ARRAY') { + if (!grep(/^\Q$curr_usercreation{'cancreate'}{$item}\E$/,@{$cancreate{$item}})) { + if (!grep(/^$item$/,@{$changes{'cancreate'}})) { + push(@{$changes{'cancreate'}},$item); + } + } + } + } elsif (($item eq 'selfcreateprocessing') || ($item eq 'emailverified') || ($item eq 'emailoptions')) { + if (ref($cancreate{$item}) eq 'HASH') { + if (!grep(/^$item$/,@{$changes{'cancreate'}})) { + push(@{$changes{'cancreate'}},$item); + } + } + } elsif ($item eq 'emailusername') { + if (ref($cancreate{$item}) eq 'HASH') { + foreach my $type (keys(%{$cancreate{$item}})) { + if (ref($cancreate{$item}{$type}) eq 'HASH') { + foreach my $field (keys(%{$cancreate{$item}{$type}})) { + if ($cancreate{$item}{$type}{$field}) { + if (!grep(/^$item$/,@{$changes{'cancreate'}})) { + push(@{$changes{'cancreate'}},$item); + } + last; + } + } + } + } + } + } + } +# +# Populate %save_usercreate hash with updates to self-creation configuration. +# + $save_usercreate{'cancreate'}{'captcha'} = $savecaptcha{'captcha'}; + $save_usercreate{'cancreate'}{'recaptchakeys'} = $savecaptcha{'recaptchakeys'}; + $save_usercreate{'cancreate'}{'recaptchaversion'} = $savecaptcha{'recaptchaversion'}; + $save_usercreate{'cancreate'}{'selfcreate'} = $cancreate{'selfcreate'}; + if (ref($cancreate{'notify'}) eq 'HASH') { + $save_usercreate{'cancreate'}{'notify'} = $cancreate{'notify'}; + } + if (ref($cancreate{'selfcreateprocessing'}) eq 'HASH') { + $save_usercreate{'cancreate'}{'selfcreateprocessing'} = $cancreate{'selfcreateprocessing'}; + } + if (ref($cancreate{'emailverified'}) eq 'HASH') { + $save_usercreate{'cancreate'}{'emailverified'} = $cancreate{'emailverified'}; + } + if (ref($cancreate{'emailoptions'}) eq 'HASH') { + $save_usercreate{'cancreate'}{'emailoptions'} = $cancreate{'emailoptions'}; + } + if (ref($cancreate{'emaildomain'}) eq 'HASH') { + $save_usercreate{'cancreate'}{'emaildomain'} = $cancreate{'emaildomain'}; + } + if (ref($cancreate{'statustocreate'}) eq 'ARRAY') { + $save_usercreate{'cancreate'}{'statustocreate'} = $cancreate{'statustocreate'}; + } + if (ref($cancreate{'shibenv'}) eq 'HASH') { + $save_usercreate{'cancreate'}{'shibenv'} = $cancreate{'shibenv'}; + } + $save_usercreate{'cancreate'}{'emailusername'} = $cancreate{'emailusername'}; + $save_usercreate{'email_rule'} = \%email_rule; + + my %userconfig_hash = ( + usercreation => \%save_usercreate, + usermodification => \%save_usermodify, + inststatus => \%save_inststatus, + ); + + my $putresult = &Apache::lonnet::put_dom('configuration',\%userconfig_hash, + $dom); +# +# Accumulate details of changes to domain configuration for self-creation of usernames in $resulttext +# + if ($putresult eq 'ok') { + if (keys(%changes) > 0) { + $resulttext = &mt('Changes made:').'
              '; + if (ref($changes{'cancreate'}) eq 'ARRAY') { + my %lt = &selfcreation_types(); + foreach my $type (@{$changes{'cancreate'}}) { + my $chgtext = ''; + if ($type eq 'selfcreate') { + if (@{$cancreate{$type}} == 0) { + $chgtext .= &mt('Self creation of a new user account is not permitted.'); + } else { + $chgtext .= &mt('Self-creation of a new account is permitted for:'). + '
                '; + foreach my $case (@{$cancreate{$type}}) { + $chgtext .= '
              • '.$selfcreatetypes{$case}.'
              • '; + } + $chgtext .= '
              '; + if (ref($cancreate{$type}) eq 'ARRAY') { + if (grep(/^(login|sso)$/,@{$cancreate{$type}})) { + if (ref($cancreate{'statustocreate'}) eq 'ARRAY') { + if (@{$cancreate{'statustocreate'}} == 0) { + $chgtext .= ''. + &mt("However, no institutional affiliations (including 'other') are currently permitted to create accounts via log-in or single sign-on."). + '
              '; + } + } + } + if (grep(/^email$/,@{$cancreate{$type}})) { + if (!@statuses) { + $chgtext .= ''. + &mt("However, e-mail verification is currently set to 'unavailable' for all user types (including 'other'), so self-creation of accounts is not possible for non-institutional log-in."). + '
              '; + + } + } + } + } + } elsif ($type eq 'shibenv') { + if (keys(%{$cancreate{$type}}) == 0) { + $chgtext .= &mt('Shibboleth-autheticated user does not use environment variables to set user information').'
              '; + } else { + $chgtext .= &mt('Shibboleth-autheticated user information set from environment variables, as follows:'). + '
                '; + foreach my $field (@shibfields) { + next if ($cancreate{$type}{$field} eq ''); + if ($field eq 'inststatus') { + $chgtext .= '
              • '.&mt('Institutional status').' -- '.$cancreate{$type}{$field}.'
              • '; + } else { + $chgtext .= '
              • '.$fieldtitles{$field}.' -- '.$cancreate{$type}{$field}.'
              • '; + } + } + $chgtext .= '
              '; + } + } elsif ($type eq 'statustocreate') { + if ((ref($cancreate{'selfcreate'}) eq 'ARRAY') && + (ref($cancreate{'statustocreate'}) eq 'ARRAY')) { + if (@{$cancreate{'selfcreate'}} > 0) { + if (@{$cancreate{'statustocreate'}} == 0) { + $chgtext .= &mt("Institutional affiliations permitted to create accounts set to 'None'."); + if (!grep(/^email$/,@{$cancreate{'selfcreate'}})) { + $chgtext .= '
              '. + ''. + &mt("However, no institutional affiliations (including 'other') are currently permitted to create accounts."). + ''; + } + } elsif (keys(%usertypes) > 0) { + if (grep(/^(login|sso)$/,@{$cancreate{'selfcreate'}})) { + $chgtext .= &mt('Creation of a new account for an institutional user is restricted to the following institutional affiliation(s):'); + } else { + $chgtext .= &mt('Institutional affiliations permitted to create accounts with institutional authentication were set as follows:'); + } + $chgtext .= '
                '; + foreach my $case (@{$cancreate{$type}}) { + if ($case eq 'default') { + $chgtext .= '
              • '.$othertitle.'
              • '; + } else { + $chgtext .= '
              • '.$usertypes{$case}.'
              • '; + } + } + $chgtext .= '
              '; + if (!grep(/^(login|sso)$/,@{$cancreate{'selfcreate'}})) { + $chgtext .= ''. + &mt('However, users authenticated by institutional login/single sign on are not currently permitted to create accounts.'). + ''; + } + } + } else { + if (@{$cancreate{$type}} == 0) { + $chgtext .= &mt("Institutional affiliations permitted to create accounts were set to 'none'."); + } else { + $chgtext .= &mt('Although institutional affiliations permitted to create accounts were changed, self creation of accounts is not currently permitted for any authentication types.'); + } + } + $chgtext .= '
              '; + } + } elsif ($type eq 'selfcreateprocessing') { + my %choices = &Apache::lonlocal::texthash ( + automatic => 'Automatic approval', + approval => 'Queued for approval', + ); + if (@types) { + if (@statuses) { + $chgtext .= &mt('Processing of requests to create account with e-mail verification set as follows:'). + '
                '; + foreach my $status (@statuses) { + if ($status eq 'default') { + $chgtext .= '
              • '.$othertitle.' -- '.$choices{$cancreate{'selfcreateprocessing'}{$status}}.'
              • '; + } else { + $chgtext .= '
              • '.$usertypes{$status}.' -- '.$choices{$cancreate{'selfcreateprocessing'}{$status}}.'
              • '; + } + } + $chgtext .= '
              '; + } + } else { + $chgtext .= &mt('Processing of requests to create account with e-mail verification set to: "[_1]"', + $choices{$cancreate{'selfcreateprocessing'}{'default'}}); + } + } elsif ($type eq 'emailverified') { + my %options = &Apache::lonlocal::texthash ( + all => 'Same as e-mail', + first => 'Omit @domain', + free => 'Free to choose', + ); + if (@types) { + if (@statuses) { + $chgtext .= &mt('For self-created accounts verified by e-mail address, username is set as follows:'). + '
                '; + foreach my $status (@statuses) { + if ($type eq 'default') { + $chgtext .= '
              • '.$othertitle.' -- '.$options{$cancreate{'emailverified'}{$status}}.'
              • '; + } else { + $chgtext .= '
              • '.$usertypes{$status}.' -- '.$options{$cancreate{'emailverified'}{$status}}.'
              • '; + } + } + $chgtext .= '
              '; + } + } else { + $chgtext .= &mt("For self-created accounts verified by e-mail address, user's username is: '[_1]'", + $options{$cancreate{'emailverified'}{'default'}}); + } + } elsif ($type eq 'emailoptions') { + my %options = &Apache::lonlocal::texthash ( + any => 'Any e-mail', + inst => 'Institutional only', + noninst => 'Non-institutional only', + custom => 'Custom restrictions', + ); + if (@types) { + if (@statuses) { + $chgtext .= &mt('For self-created accounts verified by e-mail address, requirements for e-mail address are as follows:'). + '
                '; + foreach my $status (@statuses) { + if ($type eq 'default') { + $chgtext .= '
              • '.$othertitle.' -- '.$options{$cancreate{'emailoptions'}{$status}}.'
              • '; + } else { + $chgtext .= '
              • '.$usertypes{$status}.' -- '.$options{$cancreate{'emailoptions'}{$status}}.'
              • '; + } + } + $chgtext .= '
              '; + } + } else { + if ($cancreate{'emailoptions'}{'default'} eq 'any') { + $chgtext .= &mt('For self-created accounts verified by e-mail address, any e-mail may be used'); + } else { + $chgtext .= &mt('For self-created accounts verified by e-mail address, e-mail restricted to: "[_1]"', + $options{$cancreate{'emailoptions'}{'default'}}); + } + } + } elsif ($type eq 'emaildomain') { + my $output; + if (@statuses) { + foreach my $type (@statuses) { + if (ref($cancreate{'emaildomain'}{$type}) eq 'HASH') { + if ($cancreate{'emailoptions'}{$type} eq 'inst') { + if ($type eq 'default') { + if ((ref($cancreate{'emaildomain'}{$type}) ne 'HASH') || + ($cancreate{'emaildomain'}{$type}{'inst'} eq '')) { + $output = '
            • '.$othertitle.' -- '.&mt('No restriction on e-mail domain').'
            • '; + } else { + $output = '
            • '.$othertitle.' -- '.&mt("User's e-mail address needs to end: [_1]", + $cancreate{'emaildomain'}{$type}{'inst'}).'
            • '; + } + } else { + if ((ref($cancreate{'emaildomain'}{$type}) ne 'HASH') || + ($cancreate{'emaildomain'}{$type}{'inst'} eq '')) { + $output = '
            • '.$usertypes{$type}.' -- '.&mt('No restriction on e-mail domain').'
            • '; + } else { + $output = '
            • '.$usertypes{$type}.' -- '.&mt("User's e-mail address needs to end: [_1]", + $cancreate{'emaildomain'}{$type}{'inst'}).'
            • '; + } + } + } elsif ($cancreate{'emailoptions'}{$type} eq 'noninst') { + if ($type eq 'default') { + if ((ref($cancreate{'emaildomain'}{$type}) ne 'HASH') || + ($cancreate{'emaildomain'}{$type}{'noninst'} eq '')) { + $output = '
            • '.$othertitle.' -- '.&mt('No restriction on e-mail domain').'
            • '; + } else { + $output = '
            • '.$othertitle.' -- '.&mt("User's e-mail address must not end: [_1]", + $cancreate{'emaildomain'}{$type}{'noninst'}).'
            • '; + } + } else { + if ((ref($cancreate{'emaildomain'}{$type}) ne 'HASH') || + ($cancreate{'emaildomain'}{$type}{'noninst'} eq '')) { + $output = '
            • '.$usertypes{$type}.' -- '.&mt('No restriction on e-mail domain').'
            • '; + } else { + $output = '
            • '.$usertypes{$type}.' -- '.&mt("User's e-mail address must not end: [_1]", + $cancreate{'emaildomain'}{$type}{'noninst'}).'
            • '; + } + } + } + } + } + } + if ($output ne '') { + $chgtext .= &mt('For self-created accounts verified by e-mail address:'). + '
                '.$output.'
              '; + } + } elsif ($type eq 'captcha') { + if ($savecaptcha{$type} eq 'notused') { + $chgtext .= &mt('No CAPTCHA validation in use for self-creation screen.'); + } else { + my %captchas = &captcha_phrases(); + if ($captchas{$savecaptcha{$type}}) { + $chgtext .= &mt("Validation for self-creation screen set to $captchas{$savecaptcha{$type}}."); + } else { + $chgtext .= &mt('Validation for self-creation screen set to unknown type.'); + } + } + } elsif ($type eq 'recaptchakeys') { + my ($privkey,$pubkey); + if (ref($savecaptcha{$type}) eq 'HASH') { + $pubkey = $savecaptcha{$type}{'public'}; + $privkey = $savecaptcha{$type}{'private'}; + } + $chgtext .= &mt('ReCAPTCHA keys changes').'
                '; + if (!$pubkey) { + $chgtext .= '
              • '.&mt('Public key deleted').'
              • '; + } else { + $chgtext .= '
              • '.&mt('Public key set to [_1]',$pubkey).'
              • '; + } + if (!$privkey) { + $chgtext .= '
              • '.&mt('Private key deleted').'
              • '; + } else { + $chgtext .= '
              • '.&mt('Private key set to [_1]',$pubkey).'
              • '; + } + $chgtext .= '
              '; + } elsif ($type eq 'recaptchaversion') { + if ($savecaptcha{'captcha'} eq 'recaptcha') { + $chgtext .= &mt('ReCAPTCHA set to version [_1]',$savecaptcha{$type}); + } + } elsif ($type eq 'emailusername') { + if (ref($cancreate{'emailusername'}) eq 'HASH') { + if (@statuses) { + foreach my $type (@statuses) { + if (ref($cancreate{'emailusername'}{$type}) eq 'HASH') { + if (keys(%{$cancreate{'emailusername'}{$type}}) > 0) { + $chgtext .= &mt('When self-creating account with e-mail verification, the following information will be provided by [_1]:',"'$usertypes{$type}'"). + '
                '; + foreach my $field (@{$infofields}) { + if ($cancreate{'emailusername'}{$type}{$field}) { + $chgtext .= '
              • '.$infotitles->{$field}.'
              • '; + } + } + $chgtext .= '
              '; + } else { + $chgtext .= &mt('When self creating account with e-mail verification, no information besides e-mail address will be provided by [_1].',"'$usertypes{$type}'").'
              '; + } + } else { + $chgtext .= &mt('When self creating account with e-mail verification, no information besides e-mail address will be provided by [_1].',"'$usertypes{$type}'").'
              '; + } + } + } + } + } elsif ($type eq 'notify') { + my $numapprove = 0; + if (ref($changes{'cancreate'}) eq 'ARRAY') { + if ((grep(/^notify$/,@{$changes{'cancreate'}})) && (ref($cancreate{'notify'}) eq 'HASH')) { + if ($cancreate{'notify'}{'approval'}) { + $chgtext .= &mt('Notification of username requests requiring approval will be sent to: ').$cancreate{'notify'}{'approval'}; + $numapprove ++; + } + } + } + unless ($numapprove) { + $chgtext .= &mt('No Domain Coordinators will receive notification of username requests requiring approval.'); + } + } + if ($chgtext) { + $resulttext .= '
            • '.$chgtext.'
            • '; + } + } + } + if ((ref($changes{'email_rule'}) eq 'ARRAY') && (@{$changes{'email_rule'}} > 0)) { + my ($emailrules,$emailruleorder) = + &Apache::lonnet::inst_userrules($dom,'email'); + foreach my $type (@{$changes{'email_rule'}}) { + if (ref($email_rule{$type}) eq 'ARRAY') { + my $chgtext = '
                '; + foreach my $rule (@{$email_rule{$type}}) { + if (ref($emailrules->{$rule}) eq 'HASH') { + $chgtext .= '
              • '.$emailrules->{$rule}{'name'}.'
              • '; + } + } + $chgtext .= '
              '; + my $typename; + if (@types) { + if ($type eq 'default') { + $typename = $othertitle; + } else { + $typename = $usertypes{$type}; + } + $chgtext .= &mt('(Affiliation: [_1])',$typename); + } + if (@{$email_rule{$type}} > 0) { + $resulttext .= '
            • '. + &mt('Accounts may not be created by users verified by e-mail, for e-mail addresses of the following types: ', + $usertypes{$type}). + $chgtext. + '
            • '; + } else { + $resulttext .= '
            • '. + &mt('There are now no restrictions on e-mail addresses which may be used for verification when a user requests an account.'). + '
            • '. + &mt('(Affiliation: [_1])',$typename); + } + } + } + } + if (ref($changes{'inststatus'}) eq 'ARRAY') { + if (ref($save_inststatus{'inststatusguest'}) eq 'ARRAY') { + if (@{$save_inststatus{'inststatusguest'}} > 0) { + my $chgtext = '
                '; + foreach my $type (@{$save_inststatus{'inststatusguest'}}) { + $chgtext .= '
              • '.$usertypes{$type}.'
              • '; + } + $chgtext .= '
              '; + $resulttext .= '
            • '. + &mt('A user will self-report one of the following affiliations when requesting an account verified by e-mail: '). + $chgtext. + '
            • '; + } else { + $resulttext .= '
            • '. + &mt('No affiliations available for self-reporting when requesting an account verified by e-mail.'). + '
            • '; + } + } + } + if (ref($changes{'selfcreate'}) eq 'ARRAY') { + $resulttext .= '
            • '.&mt('When self-creating institutional account:').'
                '; + my %fieldtitles = &Apache::loncommon::personal_data_fieldtitles(); + foreach my $type (@{$changes{'selfcreate'}}) { + my $typename = $type; + if (keys(%usertypes) > 0) { + if ($usertypes{$type} ne '') { + $typename = $usertypes{$type}; + } + } + my @modifiable; + $resulttext .= '
              • '. + &mt('Self-creation of account by users with status: [_1]', + ''.$typename.''). + ' - '.&mt('modifiable fields (if institutional data blank): '); + foreach my $field (@fields) { + if ($save_usermodify{'selfcreate'}{$type}{$field}) { + push(@modifiable,''.$fieldtitles{$field}.''); + } + } + if (@modifiable > 0) { + $resulttext .= join(', ',@modifiable); + } else { + $resulttext .= &mt('none'); + } + $resulttext .= '
              • '; + } + $resulttext .= '
            • '; + } + $resulttext .= '
            '; + my $cachetime = 24*60*60; + $domdefaults{'inststatusguest'} = $save_inststatus{'inststatusguest'}; + &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domdefaults'} = 1; + } + } else { + $resulttext = &mt('No changes made to self-creation settings'); + } + } else { + $resulttext = ''. + &mt('An error occurred: [_1]',$putresult).''; + } + if ($warningmsg ne '') { + $resulttext .= '
            '.$warningmsg.'
            '; + } + return $resulttext; +} + +sub process_captcha { + my ($container,$changes,$newsettings,$current) = @_; + return unless ((ref($changes) eq 'HASH') && (ref($newsettings) eq 'HASH') || (ref($current) eq 'HASH')); + $newsettings->{'captcha'} = $env{'form.'.$container.'_captcha'}; + unless ($newsettings->{'captcha'} eq 'recaptcha' || $newsettings->{'captcha'} eq 'notused') { + $newsettings->{'captcha'} = 'original'; + } + if ($current->{'captcha'} ne $newsettings->{'captcha'}) { + if ($container eq 'cancreate') { + if (ref($changes->{'cancreate'}) eq 'ARRAY') { + push(@{$changes->{'cancreate'}},'captcha'); + } elsif (!defined($changes->{'cancreate'})) { + $changes->{'cancreate'} = ['captcha']; + } + } else { + $changes->{'captcha'} = 1; + } + } + my ($newpub,$newpriv,$currpub,$currpriv,$newversion,$currversion); + if ($newsettings->{'captcha'} eq 'recaptcha') { + $newpub = $env{'form.'.$container.'_recaptchapub'}; + $newpriv = $env{'form.'.$container.'_recaptchapriv'}; + $newpub =~ s/[^\w\-]//g; + $newpriv =~ s/[^\w\-]//g; + $newsettings->{'recaptchakeys'} = { + public => $newpub, + private => $newpriv, + }; + $newversion = $env{'form.'.$container.'_recaptchaversion'}; + $newversion =~ s/\D//g; + if ($newversion ne '2') { + $newversion = 1; + } + $newsettings->{'recaptchaversion'} = $newversion; + } + if (ref($current->{'recaptchakeys'}) eq 'HASH') { + $currpub = $current->{'recaptchakeys'}{'public'}; + $currpriv = $current->{'recaptchakeys'}{'private'}; + unless ($newsettings->{'captcha'} eq 'recaptcha') { + $newsettings->{'recaptchakeys'} = { + public => '', + private => '', + } + } + } + if ($current->{'captcha'} eq 'recaptcha') { + $currversion = $current->{'recaptchaversion'}; + if ($currversion ne '2') { + $currversion = 1; + } + } + if ($currversion ne $newversion) { + if ($container eq 'cancreate') { + if (ref($changes->{'cancreate'}) eq 'ARRAY') { + push(@{$changes->{'cancreate'}},'recaptchaversion'); + } elsif (!defined($changes->{'cancreate'})) { + $changes->{'cancreate'} = ['recaptchaversion']; + } + } else { + $changes->{'recaptchaversion'} = 1; + } + } + if (($newpub ne $currpub) || ($newpriv ne $currpriv)) { + if ($container eq 'cancreate') { + if (ref($changes->{'cancreate'}) eq 'ARRAY') { + push(@{$changes->{'cancreate'}},'recaptchakeys'); + } elsif (!defined($changes->{'cancreate'})) { + $changes->{'cancreate'} = ['recaptchakeys']; + } + } else { + $changes->{'recaptchakeys'} = 1; + } + } + return; +} + sub modify_usermodification { my ($dom,%domconfig) = @_; - my ($resulttext,%curr_usermodification,%changes); + my ($resulttext,%curr_usermodification,%changes,%modifyhash); if (ref($domconfig{'usermodification'}) eq 'HASH') { foreach my $key (keys(%{$domconfig{'usermodification'}})) { - $curr_usermodification{$key} = $domconfig{'usermodification'}{$key}; + if ($key eq 'selfcreate') { + $modifyhash{$key} = $domconfig{'usermodification'}{$key}; + } else { + $curr_usermodification{$key} = $domconfig{'usermodification'}{$key}; + } } } my @contexts = ('author','course'); @@ -3290,7 +14794,6 @@ sub modify_usermodification { course => ['st','ep','ta','in','cr'], ); my %fieldtitles = &Apache::loncommon::personal_data_fieldtitles(); - my %modifyhash; foreach my $context (@contexts) { foreach my $role (@{$roles{$context}}) { my @modifiable = &Apache::loncommon::get_env_multiple('form.canmodify_'.$role); @@ -3370,4 +14873,3514 @@ sub modify_usermodification { return $resulttext; } +sub modify_defaults { + my ($dom,$lastactref,%domconfig) = @_; + my ($resulttext,$mailmsgtxt,%newvalues,%changes,@errors); + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); + my @items = ('auth_def','auth_arg_def','lang_def','timezone_def','datelocale_def', + 'portal_def','intauth_cost','intauth_check','intauth_switch'); + my @authtypes = ('internal','krb4','krb5','localauth','lti'); + foreach my $item (@items) { + $newvalues{$item} = $env{'form.'.$item}; + if ($item eq 'auth_def') { + if ($newvalues{$item} ne '') { + if (!grep(/^\Q$newvalues{$item}\E$/,@authtypes)) { + push(@errors,$item); + } + } + } elsif ($item eq 'lang_def') { + if ($newvalues{$item} ne '') { + if ($newvalues{$item} =~ /^(\w+)/) { + my $langcode = $1; + if ($langcode ne 'x_chef') { + if (code2language($langcode) eq '') { + push(@errors,$item); + } + } + } else { + push(@errors,$item); + } + } + } elsif ($item eq 'timezone_def') { + if ($newvalues{$item} ne '') { + if (!DateTime::TimeZone->is_valid_name($newvalues{$item})) { + push(@errors,$item); + } + } + } elsif ($item eq 'datelocale_def') { + if ($newvalues{$item} ne '') { + my @datelocale_ids = DateTime::Locale->ids(); + if (!grep(/^\Q$newvalues{$item}\E$/,@datelocale_ids)) { + push(@errors,$item); + } + } + } elsif ($item eq 'portal_def') { + if ($newvalues{$item} ne '') { + unless ($newvalues{$item} =~ /^https?\:\/\/(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])\/?$/) { + push(@errors,$item); + } + } + } elsif ($item eq 'intauth_cost') { + if ($newvalues{$item} ne '') { + if ($newvalues{$item} =~ /\D/) { + push(@errors,$item); + } + } + } elsif ($item eq 'intauth_check') { + if ($newvalues{$item} ne '') { + unless ($newvalues{$item} =~ /^(0|1|2)$/) { + push(@errors,$item); + } + } + } elsif ($item eq 'intauth_switch') { + if ($newvalues{$item} ne '') { + unless ($newvalues{$item} =~ /^(0|1|2)$/) { + push(@errors,$item); + } + } + } + if (grep(/^\Q$item\E$/,@errors)) { + $newvalues{$item} = $domdefaults{$item}; + } elsif ($domdefaults{$item} ne $newvalues{$item}) { + $changes{$item} = 1; + } + $domdefaults{$item} = $newvalues{$item}; + } + my %defaults_hash = ( + defaults => \%newvalues, + ); + my $title = &defaults_titles(); + + my $currinststatus; + if (ref($domconfig{'inststatus'}) eq 'HASH') { + $currinststatus = $domconfig{'inststatus'}; + } else { + my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); + $currinststatus = { + inststatustypes => $usertypes, + inststatusorder => $types, + inststatusguest => [], + }; + } + my @todelete = &Apache::loncommon::get_env_multiple('form.inststatus_delete'); + my @allpos; + my %alltypes; + my @inststatusguest; + if (ref($currinststatus->{'inststatusguest'}) eq 'ARRAY') { + foreach my $type (@{$currinststatus->{'inststatusguest'}}) { + unless (grep(/^\Q$type\E$/,@todelete)) { + push(@inststatusguest,$type); + } + } + } + my ($currtitles,$currorder); + if (ref($currinststatus) eq 'HASH') { + if (ref($currinststatus->{'inststatusorder'}) eq 'ARRAY') { + foreach my $type (@{$currinststatus->{'inststatusorder'}}) { + if (ref($currinststatus->{inststatustypes}) eq 'HASH') { + if ($currinststatus->{inststatustypes}->{$type} ne '') { + $currtitles .= $currinststatus->{inststatustypes}->{$type}.','; + } + } + unless (grep(/^\Q$type\E$/,@todelete)) { + my $position = $env{'form.inststatus_pos_'.$type}; + $position =~ s/\D+//g; + $allpos[$position] = $type; + $alltypes{$type} = $env{'form.inststatus_title_'.$type}; + $alltypes{$type} =~ s/`//g; + } + } + $currorder = join(',',@{$currinststatus->{'inststatusorder'}}); + $currtitles =~ s/,$//; + } + } + if ($env{'form.addinststatus'}) { + my $newtype = $env{'form.addinststatus'}; + $newtype =~ s/\W//g; + unless (exists($alltypes{$newtype})) { + $alltypes{$newtype} = $env{'form.addinststatus_title'}; + $alltypes{$newtype} =~ s/`//g; + my $position = $env{'form.addinststatus_pos'}; + $position =~ s/\D+//g; + if ($position ne '') { + $allpos[$position] = $newtype; + } + } + } + my @orderedstatus; + foreach my $type (@allpos) { + unless (($type eq '') || (grep(/^\Q$type\E$/,@orderedstatus))) { + push(@orderedstatus,$type); + } + } + foreach my $type (keys(%alltypes)) { + unless (grep(/^\Q$type\E$/,@orderedstatus)) { + delete($alltypes{$type}); + } + } + $defaults_hash{'inststatus'} = { + inststatustypes => \%alltypes, + inststatusorder => \@orderedstatus, + inststatusguest => \@inststatusguest, + }; + if (ref($defaults_hash{'inststatus'}) eq 'HASH') { + foreach my $item ('inststatustypes','inststatusorder','inststatusguest') { + $domdefaults{$item} = $defaults_hash{'inststatus'}{$item}; + } + } + if ($currorder ne join(',',@orderedstatus)) { + $changes{'inststatus'}{'inststatusorder'} = 1; + } + my $newtitles; + foreach my $item (@orderedstatus) { + $newtitles .= $alltypes{$item}.','; + } + $newtitles =~ s/,$//; + if ($currtitles ne $newtitles) { + $changes{'inststatus'}{'inststatustypes'} = 1; + } + my $putresult = &Apache::lonnet::put_dom('configuration',\%defaults_hash, + $dom); + if ($putresult eq 'ok') { + if (keys(%changes) > 0) { + $resulttext = &mt('Changes made:').'
              '; + my $version = &Apache::lonnet::get_server_loncaparev($dom); + my $mailmsgtext = "Changes made to domain settings in a LON-CAPA installation - domain: $dom (running version: $version) - dns_domain.tab needs to be updated with the following changes, to support legacy 2.4, 2.5 and 2.6 versions of LON-CAPA.\n\n"; + foreach my $item (sort(keys(%changes))) { + if ($item eq 'inststatus') { + if (ref($changes{'inststatus'}) eq 'HASH') { + if (@orderedstatus) { + $resulttext .= '
            • '.&mt('Institutional user status types set to:').' '; + foreach my $type (@orderedstatus) { + $resulttext .= $alltypes{$type}.', '; + } + $resulttext =~ s/, $//; + $resulttext .= '
            • '; + } else { + $resulttext .= '
            • '.&mt('Institutional user status types deleted').'
            • '; + } + } + } else { + my $value = $env{'form.'.$item}; + if ($value eq '') { + $value = &mt('none'); + } elsif ($item eq 'auth_def') { + my %authnames = &authtype_names(); + my %shortauth = ( + internal => 'int', + krb4 => 'krb4', + krb5 => 'krb5', + localauth => 'loc', + lti => 'lti', + ); + $value = $authnames{$shortauth{$value}}; + } elsif ($item eq 'intauth_switch') { + my %optiondesc = &Apache::lonlocal::texthash ( + 0 => 'No', + 1 => 'Yes', + 2 => 'Yes, and copy existing passwd file to passwd.bak file', + ); + if ($value =~ /^(0|1|2)$/) { + $value = $optiondesc{$value}; + } else { + $value = &mt('none -- defaults to No'); + } + } elsif ($item eq 'intauth_check') { + my %optiondesc = &Apache::lonlocal::texthash ( + 0 => 'No', + 1 => 'Yes, allow login then update passwd file using default cost (if higher)', + 2 => 'Yes, disallow login if stored cost is less than domain default', + ); + if ($value =~ /^(0|1|2)$/) { + $value = $optiondesc{$value}; + } else { + $value = &mt('none -- defaults to No'); + } + } + $resulttext .= '
            • '.&mt('[_1] set to "[_2]"',$title->{$item},$value).'
            • '; + $mailmsgtext .= "$title->{$item} set to $value\n"; + } + } + $resulttext .= '
            '; + $mailmsgtext .= "\n"; + my $cachetime = 24*60*60; + &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domdefaults'} = 1; + } + if ($changes{'auth_def'} || $changes{'auth_arg_def'} || $changes{'lang_def'} || $changes{'datelocale_def'}) { + my $notify = 1; + if (ref($domconfig{'contacts'}) eq 'HASH') { + if ($domconfig{'contacts'}{'reportupdates'} == 0) { + $notify = 0; + } + } + if ($notify) { + &Apache::lonmsg::sendemail('installrecord@loncapa.org', + "LON-CAPA Domain Settings Change - $dom", + $mailmsgtext); + } + } + } else { + $resulttext = &mt('No changes made to default authentication/language/timezone settings'); + } + } else { + $resulttext = ''. + &mt('An error occurred: [_1]',$putresult).''; + } + if (@errors > 0) { + $resulttext .= '
            '.&mt('The following were left unchanged because the values entered were invalid:'); + foreach my $item (@errors) { + $resulttext .= ' "'.$title->{$item}.'",'; + } + $resulttext =~ s/,$//; + } + return $resulttext; +} + +sub modify_scantron { + my ($r,$dom,$confname,$lastactref,%domconfig) = @_; + my ($resulttext,%confhash,%changes,$errors); + my $custom = 'custom.tab'; + my $default = 'default.tab'; + my $servadm = $r->dir_config('lonAdmEMail'); + my ($configuserok,$author_ok,$switchserver) = + &config_check($dom,$confname,$servadm); + if ($env{'form.scantronformat.filename'} ne '') { + my $error; + if ($configuserok eq 'ok') { + if ($switchserver) { + $error = &mt("Upload of bubblesheet format file is not permitted to this server: [_1]",$switchserver); + } else { + if ($author_ok eq 'ok') { + my ($result,$scantronurl) = + &publishlogo($r,'upload','scantronformat',$dom, + $confname,'scantron','','',$custom); + if ($result eq 'ok') { + $confhash{'scantron'}{'scantronformat'} = $scantronurl; + $changes{'scantronformat'} = 1; + } else { + $error = &mt("Upload of [_1] failed because an error occurred publishing the file in RES space. Error was: [_2].",$custom,$result); + } + } else { + $error = &mt("Upload of [_1] failed because an author role could not be assigned to a Domain Configuration user ([_2]) in domain: [_3]. Error was: [_4].",$custom,$confname,$dom,$author_ok); + } + } + } else { + $error = &mt("Upload of [_1] failed because a Domain Configuration user ([_2]) could not be created in domain: [_3]. Error was: [_4].",$custom,$confname,$dom,$configuserok); + } + if ($error) { + &Apache::lonnet::logthis($error); + $errors .= '
          • '.$error.'
          • '; + } + } + if (ref($domconfig{'scantron'}) eq 'HASH') { + if ($domconfig{'scantron'}{'scantronformat'} ne '') { + if ($env{'form.scantronformat_del'}) { + $confhash{'scantron'}{'scantronformat'} = ''; + $changes{'scantronformat'} = 1; + } else { + $confhash{'scantron'}{'scantronformat'} = $domconfig{'scantron'}{'scantronformat'}; + } + } + } + my @options = ('hdr','pad','rem'); + my @fields = &scantroncsv_fields(); + my %titles = &scantronconfig_titles(); + my @formats = &Apache::loncommon::get_env_multiple('form.scantronconfig'); + my ($newdat,$currdat,%newcol,%currcol); + if (grep(/^dat$/,@formats)) { + $confhash{'scantron'}{config}{dat} = 1; + $newdat = 1; + } else { + $newdat = 0; + } + if (grep(/^csv$/,@formats)) { + my %bynum; + foreach my $field (@fields) { + if ($env{'form.scantronconfig_csv_'.$field} =~ /^(\d+)$/) { + my $posscol = $1; + if (($posscol < 20) && (!$bynum{$posscol})) { + $confhash{'scantron'}{config}{csv}{fields}{$field} = $posscol; + $bynum{$posscol} = $field; + $newcol{$field} = $posscol; + } + } + } + if (keys(%newcol)) { + foreach my $option (@options) { + if ($env{'form.scantroncsv_'.$option}) { + $confhash{'scantron'}{config}{csv}{options}{$option} = 1; + } + } + } + } + $currdat = 1; + if (ref($domconfig{'scantron'}) eq 'HASH') { + if (ref($domconfig{'scantron'}{'config'}) eq 'HASH') { + unless (exists($domconfig{'scantron'}{'config'}{'dat'})) { + $currdat = 0; + } + if (ref($domconfig{'scantron'}{'config'}{'csv'}) eq 'HASH') { + if (ref($domconfig{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') { + %currcol = %{$domconfig{'scantron'}{'config'}{'csv'}{'fields'}}; + } + } + } + } + if ($currdat != $newdat) { + $changes{'config'} = 1; + } else { + foreach my $field (@fields) { + if ($currcol{$field} ne '') { + if ($currcol{$field} ne $newcol{$field}) { + $changes{'config'} = 1; + last; + } + } elsif ($newcol{$field} ne '') { + $changes{'config'} = 1; + last; + } + } + } + if (keys(%confhash) > 0) { + my $putresult = &Apache::lonnet::put_dom('configuration',\%confhash, + $dom); + if ($putresult eq 'ok') { + if (keys(%changes) > 0) { + if (ref($confhash{'scantron'}) eq 'HASH') { + $resulttext = &mt('Changes made:').'
              '; + if ($changes{'scantronformat'}) { + if ($confhash{'scantron'}{'scantronformat'} eq '') { + $resulttext .= '
            • '.&mt('[_1] bubblesheet format file removed; [_2] file will be used for courses in this domain.',$custom,$default).'
            • '; + } else { + $resulttext .= '
            • '.&mt('Custom bubblesheet format file ([_1]) uploaded for use with courses in this domain.',$custom).'
            • '; + } + } + if ($changes{'config'}) { + if (ref($confhash{'scantron'}{'config'}) eq 'HASH') { + if ($confhash{'scantron'}{'config'}{'dat'}) { + $resulttext .= '
            • '.&mt('Bubblesheet data upload formats includes .dat format').'
            • '; + } + if (ref($confhash{'scantron'}{'config'}{'csv'}) eq 'HASH') { + if (ref($confhash{'scantron'}{'config'}{'csv'}{'fields'}) eq 'HASH') { + if (keys(%{$confhash{'scantron'}{'config'}{'csv'}{'fields'}})) { + $resulttext .= '
            • '.&mt('Bubblesheet data upload formats includes .csv format, with following fields/column numbers supported:').'
                '; + foreach my $field (@fields) { + if ($confhash{'scantron'}{'config'}{'csv'}{'fields'}{$field} ne '') { + my $showcol = $confhash{'scantron'}{'config'}{'csv'}{'fields'}{$field} + 1; + $resulttext .= '
              • '.$titles{$field}.': '.$showcol.'
              • '; + } + } + $resulttext .= '
            • '; + if (ref($confhash{'scantron'}{'config'}{'csv'}{'options'}) eq 'HASH') { + if (keys(%{$confhash{'scantron'}{'config'}{'csv'}{'options'}})) { + $resulttext .= '
            • '.&mt('Bubblesheet data upload formats includes .csv format, with following options:').'
                '; + foreach my $option (@options) { + if ($confhash{'scantron'}{'config'}{'csv'}{'options'}{$option} ne '') { + $resulttext .= '
              • '.$titles{$option}.'
              • '; + } + } + $resulttext .= '
            • '; + } + } + } + } + } + } else { + $resulttext .= '
            • '.&mt('No bubblesheet data upload formats set -- will default to assuming .dat format').'
            • '; + } + } + $resulttext .= '
            '; + } else { + $resulttext = &mt('Changes made to bubblesheet format file.'); + } + &Apache::loncommon::devalidate_domconfig_cache($dom); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domainconfig'} = 1; + } + } else { + $resulttext = &mt('No changes made to bubblesheet format settings'); + } + } else { + $resulttext = ''. + &mt('An error occurred: [_1]',$putresult).''; + } + } else { + $resulttext = &mt('No changes made to bubblesheet format file'); + } + if ($errors) { + $resulttext .= &mt('The following errors occurred: ').'
              '. + $errors.'
            '; + } + return $resulttext; +} + +sub modify_coursecategories { + my ($dom,$lastactref,%domconfig) = @_; + my ($resulttext,%deletions,%reorderings,%needreordering,%adds,%changes,$errors, + $cathash); + my @deletecategory = &Apache::loncommon::get_env_multiple('form.deletecategory'); + my @catitems = ('unauth','auth'); + my @cattypes = ('std','domonly','codesrch','none'); + if (ref($domconfig{'coursecategories'}) eq 'HASH') { + $cathash = $domconfig{'coursecategories'}{'cats'}; + if ($domconfig{'coursecategories'}{'togglecats'} ne $env{'form.togglecats'}) { + $changes{'togglecats'} = 1; + $domconfig{'coursecategories'}{'togglecats'} = $env{'form.togglecats'}; + } + if ($domconfig{'coursecategories'}{'categorize'} ne $env{'form.categorize'}) { + $changes{'categorize'} = 1; + $domconfig{'coursecategories'}{'categorize'} = $env{'form.categorize'}; + } + if ($domconfig{'coursecategories'}{'togglecatscomm'} ne $env{'form.togglecatscomm'}) { + $changes{'togglecatscomm'} = 1; + $domconfig{'coursecategories'}{'togglecatscomm'} = $env{'form.togglecatscomm'}; + } + if ($domconfig{'coursecategories'}{'categorizecomm'} ne $env{'form.categorizecomm'}) { + $changes{'categorizecomm'} = 1; + $domconfig{'coursecategories'}{'categorizecomm'} = $env{'form.categorizecomm'}; + + } + if ($domconfig{'coursecategories'}{'togglecatsplace'} ne $env{'form.togglecatsplace'}) { + $changes{'togglecatsplace'} = 1; + $domconfig{'coursecategories'}{'togglecatsplace'} = $env{'form.togglecatsplace'}; + } + if ($domconfig{'coursecategories'}{'categorizeplace'} ne $env{'form.categorizeplace'}) { + $changes{'categorizeplace'} = 1; + $domconfig{'coursecategories'}{'categorizeplace'} = $env{'form.categorizeplace'}; + } + foreach my $item (@catitems) { + if (grep(/^\Q$env{'form.coursecat_'.$item}\E$/,@cattypes)) { + if ($domconfig{'coursecategories'}{$item} ne $env{'form.coursecat_'.$item}) { + $changes{$item} = 1; + $domconfig{'coursecategories'}{$item} = $env{'form.coursecat_'.$item}; + } + } + } + } else { + $changes{'togglecats'} = 1; + $changes{'categorize'} = 1; + $changes{'togglecatscomm'} = 1; + $changes{'categorizecomm'} = 1; + $changes{'togglecatsplace'} = 1; + $changes{'categorizeplace'} = 1; + $domconfig{'coursecategories'} = { + togglecats => $env{'form.togglecats'}, + categorize => $env{'form.categorize'}, + togglecatscomm => $env{'form.togglecatscomm'}, + categorizecomm => $env{'form.categorizecomm'}, + togglecatsplace => $env{'form.togglecatsplace'}, + categorizeplace => $env{'form.categorizeplace'}, + }; + foreach my $item (@catitems) { + if ($env{'form.coursecat_'.$item} ne 'std') { + $changes{$item} = 1; + } + if (grep(/^\Q$env{'form.coursecat_'.$item}\E$/,@cattypes)) { + $domconfig{'coursecategories'}{$item} = $env{'form.coursecat_'.$item}; + } + } + } + if (ref($cathash) eq 'HASH') { + if (($domconfig{'coursecategories'}{'cats'}{'instcode::0'} ne '') && ($env{'form.instcode'} == 0)) { + push (@deletecategory,'instcode::0'); + } + if (($domconfig{'coursecategories'}{'cats'}{'communities::0'} ne '') && ($env{'form.communities'} == 0)) { + push(@deletecategory,'communities::0'); + } + if (($domconfig{'coursecategories'}{'cats'}{'placement::0'} ne '') && ($env{'form.placement'} == 0)) { + push(@deletecategory,'placement::0'); + } + } + my (@predelcats,@predeltrails,%predelallitems,%sort_by_deltrail); + if (ref($cathash) eq 'HASH') { + if (@deletecategory > 0) { + #FIXME Need to remove category from all courses using a deleted category + &Apache::loncommon::extract_categories($cathash,\@predelcats,\@predeltrails,\%predelallitems); + foreach my $item (@deletecategory) { + if ($domconfig{'coursecategories'}{'cats'}{$item} ne '') { + delete($domconfig{'coursecategories'}{'cats'}{$item}); + $deletions{$item} = 1; + &recurse_cat_deletes($item,$cathash,\%deletions); + } + } + } + foreach my $item (keys(%{$cathash})) { + my ($cat,$container,$depth) = map { &unescape($_); } split(/:/,$item); + if ($cathash->{$item} ne $env{'form.'.$item}) { + $reorderings{$item} = 1; + $domconfig{'coursecategories'}{'cats'}{$item} = $env{'form.'.$item}; + } + if ($env{'form.addcategory_name_'.$item} ne '') { + my $newcat = $env{'form.addcategory_name_'.$item}; + my $newdepth = $depth+1; + my $newitem = &escape($newcat).':'.&escape($cat).':'.$newdepth; + $domconfig{'coursecategories'}{'cats'}{$newitem} = $env{'form.addcategory_pos_'.$item}; + $adds{$newitem} = 1; + } + if ($env{'form.subcat_'.$item} ne '') { + my $newcat = $env{'form.subcat_'.$item}; + my $newdepth = $depth+1; + my $newitem = &escape($newcat).':'.&escape($cat).':'.$newdepth; + $domconfig{'coursecategories'}{'cats'}{$newitem} = 0; + $adds{$newitem} = 1; + } + } + } + if ($env{'form.instcode'} eq '1') { + if (ref($cathash) eq 'HASH') { + my $newitem = 'instcode::0'; + if ($cathash->{$newitem} eq '') { + $domconfig{'coursecategories'}{'cats'}{$newitem} = $env{'form.instcode_pos'}; + $adds{$newitem} = 1; + } + } else { + my $newitem = 'instcode::0'; + $domconfig{'coursecategories'}{'cats'}{$newitem} = $env{'form.instcode_pos'}; + $adds{$newitem} = 1; + } + } + if ($env{'form.communities'} eq '1') { + if (ref($cathash) eq 'HASH') { + my $newitem = 'communities::0'; + if ($cathash->{$newitem} eq '') { + $domconfig{'coursecategories'}{'cats'}{$newitem} = $env{'form.communities_pos'}; + $adds{$newitem} = 1; + } + } else { + my $newitem = 'communities::0'; + $domconfig{'coursecategories'}{'cats'}{$newitem} = $env{'form.communities_pos'}; + $adds{$newitem} = 1; + } + } + if ($env{'form.placement'} eq '1') { + if (ref($cathash) eq 'HASH') { + my $newitem = 'placement::0'; + if ($cathash->{$newitem} eq '') { + $domconfig{'coursecategories'}{'cats'}{$newitem} = $env{'form.placement_pos'}; + $adds{$newitem} = 1; + } + } else { + my $newitem = 'placement::0'; + $domconfig{'coursecategories'}{'cats'}{$newitem} = $env{'form.placement_pos'}; + $adds{$newitem} = 1; + } + } + if ($env{'form.addcategory_name'} ne '') { + if (($env{'form.addcategory_name'} ne 'instcode') && + ($env{'form.addcategory_name'} ne 'communities') && + ($env{'form.addcategory_name'} ne 'placement')) { + my $newitem = &escape($env{'form.addcategory_name'}).'::0'; + $domconfig{'coursecategories'}{'cats'}{$newitem} = $env{'form.addcategory_pos'}; + $adds{$newitem} = 1; + } + } + my $putresult; + if ((keys(%deletions) > 0) || (keys(%reorderings) > 0) || (keys(%adds) > 0)) { + if (keys(%deletions) > 0) { + foreach my $key (keys(%deletions)) { + if ($predelallitems{$key} ne '') { + $sort_by_deltrail{$predelallitems{$key}} = $predeltrails[$predelallitems{$key}]; + } + } + } + my (@chkcats,@chktrails,%chkallitems); + &Apache::loncommon::extract_categories($domconfig{'coursecategories'}{'cats'},\@chkcats,\@chktrails,\%chkallitems); + if (ref($chkcats[0]) eq 'ARRAY') { + my $depth = 0; + my $chg = 0; + for (my $i=0; $i<@{$chkcats[0]}; $i++) { + my $name = $chkcats[0][$i]; + my $item; + if ($name eq '') { + $chg ++; + } else { + $item = &escape($name).'::0'; + if ($chg) { + $domconfig{'coursecategories'}{'cats'}{$item} -= $chg; + } + $depth ++; + &recurse_check(\@chkcats,$domconfig{'coursecategories'}{'cats'},$depth,$name); + $depth --; + } + } + } + } + if ((keys(%changes) > 0) || (keys(%deletions) > 0) || (keys(%reorderings) > 0) || (keys(%adds) > 0)) { + $putresult = &Apache::lonnet::put_dom('configuration',\%domconfig,$dom); + if ($putresult eq 'ok') { + my %title = ( + togglecats => 'Show/Hide a course in catalog', + categorize => 'Assign a category to a course', + togglecatscomm => 'Show/Hide a community in catalog', + categorizecomm => 'Assign a category to a community', + ); + my %level = ( + dom => 'set in Domain ("Modify Course/Community")', + crs => 'set in Course ("Course Configuration")', + comm => 'set in Community ("Community Configuration")', + none => 'No catalog', + std => 'Standard catalog', + domonly => 'Domain-only catalog', + codesrch => 'Code search form', + ); + $resulttext = &mt('Changes made:').'
              '; + if ($changes{'togglecats'}) { + $resulttext .= '
            • '.&mt("$title{'togglecats'} $level{$env{'form.togglecats'}}").'
            • '; + } + if ($changes{'categorize'}) { + $resulttext .= '
            • '.&mt("$title{'categorize'} $level{$env{'form.categorize'}}").'
            • '; + } + if ($changes{'togglecatscomm'}) { + $resulttext .= '
            • '.&mt("$title{'togglecatscomm'} $level{$env{'form.togglecatscomm'}}").'
            • '; + } + if ($changes{'categorizecomm'}) { + $resulttext .= '
            • '.&mt("$title{'categorizecomm'} $level{$env{'form.categorizecomm'}}").'
            • '; + } + if ($changes{'unauth'}) { + $resulttext .= '
            • '.&mt('Catalog type for unauthenticated users set to "'.$level{$env{'form.coursecat_unauth'}}.'"').'
            • '; + } + if ($changes{'auth'}) { + $resulttext .= '
            • '.&mt('Catalog type for authenticated users set to "'.$level{$env{'form.coursecat_auth'}}.'"').'
            • '; + } + if ((keys(%deletions) > 0) || (keys(%reorderings) > 0) || (keys(%adds) > 0)) { + my $cathash; + if (ref($domconfig{'coursecategories'}) eq 'HASH') { + $cathash = $domconfig{'coursecategories'}{'cats'}; + } else { + $cathash = {}; + } + my (@cats,@trails,%allitems); + &Apache::loncommon::extract_categories($cathash,\@cats,\@trails,\%allitems); + if (keys(%deletions) > 0) { + $resulttext .= '
            • '.&mt('Deleted categories:').'
                '; + foreach my $predeltrail (sort {$a <=> $b } (keys(%sort_by_deltrail))) { + $resulttext .= '
              • '.$predeltrails[$predeltrail].'
              • '; + } + $resulttext .= '
            • '; + } + if (keys(%reorderings) > 0) { + my %sort_by_trail; + $resulttext .= '
            • '.&mt('Reordered categories:').'
                '; + foreach my $key (keys(%reorderings)) { + if ($allitems{$key} ne '') { + $sort_by_trail{$allitems{$key}} = $trails[$allitems{$key}]; + } + } + foreach my $trail (sort {$a <=> $b } (keys(%sort_by_trail))) { + $resulttext .= '
              • '.$trails[$trail].'
              • '; + } + $resulttext .= '
            • '; + } + if (keys(%adds) > 0) { + my %sort_by_trail; + $resulttext .= '
            • '.&mt('Added categories:').'
                '; + foreach my $key (keys(%adds)) { + if ($allitems{$key} ne '') { + $sort_by_trail{$allitems{$key}} = $trails[$allitems{$key}]; + } + } + foreach my $trail (sort {$a <=> $b } (keys(%sort_by_trail))) { + $resulttext .= '
              • '.$trails[$trail].'
              • '; + } + $resulttext .= '
            • '; + } + } + $resulttext .= '
            '; + if ($changes{'unauth'} || $changes{'auth'}) { + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom); + if ($changes{'auth'}) { + $domdefaults{'catauth'} = $domconfig{'coursecategories'}{'auth'}; + } + if ($changes{'unauth'}) { + $domdefaults{'catunauth'} = $domconfig{'coursecategories'}{'unauth'}; + } + my $cachetime = 24*60*60; + &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domdefaults'} = 1; + } + } + } else { + $resulttext = ''. + &mt('An error occurred: [_1]',$putresult).''; + } + } else { + $resulttext = &mt('No changes made to course and community categories'); + } + return $resulttext; +} + +sub modify_serverstatuses { + my ($dom,%domconfig) = @_; + my ($resulttext,%changes,%currserverstatus,%newserverstatus); + if (ref($domconfig{'serverstatuses'}) eq 'HASH') { + %currserverstatus = %{$domconfig{'serverstatuses'}}; + } + my @pages = &serverstatus_pages(); + foreach my $type (@pages) { + $newserverstatus{$type}{'namedusers'} = ''; + $newserverstatus{$type}{'machines'} = ''; + if (defined($env{'form.'.$type.'_namedusers'})) { + my @users = split(/,/,$env{'form.'.$type.'_namedusers'}); + my @okusers; + foreach my $user (@users) { + my ($uname,$udom) = split(/:/,$user); + if (($udom =~ /^$match_domain$/) && + (&Apache::lonnet::domain($udom)) && + ($uname =~ /^$match_username$/)) { + if (!grep(/^\Q$user\E/,@okusers)) { + push(@okusers,$user); + } + } + } + if (@okusers > 0) { + @okusers = sort(@okusers); + $newserverstatus{$type}{'namedusers'} = join(',',@okusers); + } + } + if (defined($env{'form.'.$type.'_machines'})) { + my @machines = split(/,/,$env{'form.'.$type.'_machines'}); + my @okmachines; + foreach my $ip (@machines) { + my @parts = split(/\./,$ip); + next if (@parts < 4); + my $badip = 0; + for (my $i=0; $i<4; $i++) { + if (!(($parts[$i] >= 0) && ($parts[$i] <= 255))) { + $badip = 1; + last; + } + } + if (!$badip) { + push(@okmachines,$ip); + } + } + @okmachines = sort(@okmachines); + $newserverstatus{$type}{'machines'} = join(',',@okmachines); + } + } + my %serverstatushash = ( + serverstatuses => \%newserverstatus, + ); + foreach my $type (@pages) { + foreach my $setting ('namedusers','machines') { + my (@current,@new); + if (ref($currserverstatus{$type}) eq 'HASH') { + if ($currserverstatus{$type}{$setting} ne '') { + @current = split(/,/,$currserverstatus{$type}{$setting}); + } + } + if ($newserverstatus{$type}{$setting} ne '') { + @new = split(/,/,$newserverstatus{$type}{$setting}); + } + if (@current > 0) { + if (@new > 0) { + foreach my $item (@current) { + if (!grep(/^\Q$item\E$/,@new)) { + $changes{$type}{$setting} = 1; + last; + } + } + foreach my $item (@new) { + if (!grep(/^\Q$item\E$/,@current)) { + $changes{$type}{$setting} = 1; + last; + } + } + } else { + $changes{$type}{$setting} = 1; + } + } elsif (@new > 0) { + $changes{$type}{$setting} = 1; + } + } + } + if (keys(%changes) > 0) { + my $titles= &LONCAPA::lonauthcgi::serverstatus_titles(); + my $putresult = &Apache::lonnet::put_dom('configuration', + \%serverstatushash,$dom); + if ($putresult eq 'ok') { + $resulttext .= &mt('Changes made:').'
              '; + foreach my $type (@pages) { + if (ref($changes{$type}) eq 'HASH') { + $resulttext .= '
            • '.$titles->{$type}.'
                '; + if ($changes{$type}{'namedusers'}) { + if ($newserverstatus{$type}{'namedusers'} eq '') { + $resulttext .= '
              • '.&mt("Access terminated for all specific (named) users").'
              • '."\n"; + } else { + $resulttext .= '
              • '.&mt("Access available for the following specified users: ").$newserverstatus{$type}{'namedusers'}.'
              • '."\n"; + } + } + if ($changes{$type}{'machines'}) { + if ($newserverstatus{$type}{'machines'} eq '') { + $resulttext .= '
              • '.&mt("Access terminated for all specific IP addresses").'
              • '."\n"; + } else { + $resulttext .= '
              • '.&mt("Access available for the following specified IP addresses: ").$newserverstatus{$type}{'machines'}.'
              • '."\n"; + } + + } + $resulttext .= '
            • '; + } + } + $resulttext .= '
            '; + } else { + $resulttext = ''. + &mt('An error occurred saving access settings for server status pages: [_1].',$putresult).''; + + } + } else { + $resulttext = &mt('No changes made to access to server status pages'); + } + return $resulttext; +} + +sub modify_helpsettings { + my ($r,$dom,$confname,$lastactref,%domconfig) = @_; + my ($resulttext,$errors,%changes,%helphash); + my %defaultchecked = ('submitbugs' => 'on'); + my @offon = ('off','on'); + my @toggles = ('submitbugs'); + my %current = ('submitbugs' => '', + 'adhoc' => {}, + ); + if (ref($domconfig{'helpsettings'}) eq 'HASH') { + %current = %{$domconfig{'helpsettings'}}; + } + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); + foreach my $item (@toggles) { + if ($defaultchecked{$item} eq 'on') { + if ($current{$item} eq '') { + if ($env{'form.'.$item} eq '0') { + $changes{$item} = 1; + } + } elsif ($current{$item} ne $env{'form.'.$item}) { + $changes{$item} = 1; + } + } elsif ($defaultchecked{$item} eq 'off') { + if ($current{$item} eq '') { + if ($env{'form.'.$item} eq '1') { + $changes{$item} = 1; + } + } elsif ($current{$item} ne $env{'form.'.$item}) { + $changes{$item} = 1; + } + } + if (($env{'form.'.$item} eq '0') || ($env{'form.'.$item} eq '1')) { + $helphash{'helpsettings'}{$item} = $env{'form.'.$item}; + } + } + my $maxnum = $env{'form.helproles_maxnum'}; + my $confname = $dom.'-domainconfig'; + my %existing=&Apache::lonnet::dump('roles',$dom,$confname,'rolesdef_'); + my (@allpos,%newsettings,%changedprivs,$newrole); + my ($othertitle,$usertypes,$types) = &Apache::loncommon::sorted_inst_types($dom); + my @accesstypes = ('all','dh','da','none','status','inc','exc'); + my %domhelpdesk = &Apache::lonnet::get_active_domroles($dom,['dh','da']); + my %lt = &Apache::lonlocal::texthash( + s => 'system', + d => 'domain', + order => 'Display order', + access => 'Role usage', + all => 'All with domain helpdesk or helpdesk assistant role', + dh => 'All with domain helpdesk role', + da => 'All with domain helpdesk assistant role', + none => 'None', + status => 'Determined based on institutional status', + inc => 'Include all, but exclude specific personnel', + exc => 'Exclude all, but include specific personnel', + ); + for (my $num=0; $num<=$maxnum; $num++) { + my ($prefix,$identifier,$rolename,%curr); + if ($num == $maxnum) { + next unless ($env{'form.newcusthelp'} == $maxnum); + $identifier = 'custhelp'.$num; + $prefix = 'helproles_'.$num; + $rolename = $env{'form.custhelpname'.$num}; + $rolename=~s/[^A-Za-z0-9]//gs; + next if ($rolename eq ''); + next if (exists($existing{'rolesdef_'.$rolename})); + my %newprivs = &Apache::lonuserutils::custom_role_update($rolename,$identifier); + my $result = &Apache::lonnet::definerole($rolename,$newprivs{'s'},$newprivs{'d'}, + $newprivs{'c'},$confname,$dom); + if ($result ne 'ok') { + $errors .= '
          • '. + &mt('An error occurred storing the new custom role: [_1]', + $result).'
          • '; + next; + } else { + $changedprivs{$rolename} = \%newprivs; + $newrole = $rolename; + } + } else { + $prefix = 'helproles_'.$num; + $rolename = $env{'form.'.$prefix}; + next if ($rolename eq ''); + next unless (exists($existing{'rolesdef_'.$rolename})); + $identifier = 'custhelp'.$num; + my %newprivs = &Apache::lonuserutils::custom_role_update($rolename,$identifier); + my %currprivs; + ($currprivs{'s'},$currprivs{'d'},$currprivs{'c'}) = + split(/\_/,$existing{'rolesdef_'.$rolename}); + foreach my $level ('c','d','s') { + if ($newprivs{$level} ne $currprivs{$level}) { + my $result = &Apache::lonnet::definerole($rolename,$newprivs{'s'},$newprivs{'d'}, + $newprivs{'c'},$confname,$dom); + if ($result ne 'ok') { + $errors .= '
          • '. + &mt('An error occurred storing privileges for existing role [_1]: [_2]', + $rolename,$result).'
          • '; + } else { + $changedprivs{$rolename} = \%newprivs; + } + last; + } + } + if (ref($current{'adhoc'}) eq 'HASH') { + if (ref($current{'adhoc'}{$rolename}) eq 'HASH') { + %curr = %{$current{'adhoc'}{$rolename}}; + } + } + } + my $newpos = $env{'form.'.$prefix.'_pos'}; + $newpos =~ s/\D+//g; + $allpos[$newpos] = $rolename; + my $newdesc = $env{'form.'.$prefix.'_desc'}; + $helphash{'helpsettings'}{'adhoc'}{$rolename}{'desc'} = $newdesc; + if ($curr{'desc'}) { + if ($curr{'desc'} ne $newdesc) { + $changes{'customrole'}{$rolename}{'desc'} = 1; + $newsettings{$rolename}{'desc'} = $newdesc; + } + } elsif ($newdesc ne '') { + $changes{'customrole'}{$rolename}{'desc'} = 1; + $newsettings{$rolename}{'desc'} = $newdesc; + } + my $access = $env{'form.'.$prefix.'_access'}; + if (grep(/^\Q$access\E$/,@accesstypes)) { + $helphash{'helpsettings'}{'adhoc'}{$rolename}{'access'} = $access; + if ($access eq 'status') { + my @statuses = &Apache::loncommon::get_env_multiple('form.'.$prefix.'_status'); + if (scalar(@statuses) == 0) { + $helphash{'helpsettings'}{'adhoc'}{$rolename}{'access'} = 'none'; + } else { + my (@shownstatus,$numtypes); + $helphash{'helpsettings'}{'adhoc'}{$rolename}{$access} = []; + if (ref($types) eq 'ARRAY') { + $numtypes = scalar(@{$types}); + foreach my $type (sort(@statuses)) { + if ($type eq 'default') { + push(@{$helphash{'helpsettings'}{'adhoc'}{$rolename}{$access}},$type); + } elsif (grep(/^\Q$type\E$/,@{$types})) { + push(@{$helphash{'helpsettings'}{'adhoc'}{$rolename}{$access}},$type); + push(@shownstatus,$usertypes->{$type}); + } + } + } + if (grep(/^default$/,@statuses)) { + push(@shownstatus,$othertitle); + } + if (scalar(@shownstatus) == 1+$numtypes) { + $helphash{'helpsettings'}{'adhoc'}{$rolename}{'access'} = 'all'; + delete($helphash{'helpsettings'}{'adhoc'}{$rolename}{'status'}); + } else { + $newsettings{$rolename}{'status'} = join(' '.&mt('or').' ',@shownstatus); + if (ref($curr{'status'}) eq 'ARRAY') { + my @diffs = &Apache::loncommon::compare_arrays($helphash{'helpsettings'}{'adhoc'}{$rolename}{$access},$curr{$access}); + if (@diffs) { + $changes{'customrole'}{$rolename}{$access} = 1; + } + } elsif (@{$helphash{'helpsettings'}{'adhoc'}{$rolename}{$access}}) { + $changes{'customrole'}{$rolename}{$access} = 1; + } + } + } + } elsif (($access eq 'inc') || ($access eq 'exc')) { + my @personnel = &Apache::loncommon::get_env_multiple('form.'.$prefix.'_staff_'.$access); + my @newspecstaff; + $helphash{'helpsettings'}{'adhoc'}{$rolename}{$access} = []; + foreach my $person (sort(@personnel)) { + if ($domhelpdesk{$person}) { + push(@{$helphash{'helpsettings'}{'adhoc'}{$rolename}{$access}},$person); + } + } + if (ref($curr{$access}) eq 'ARRAY') { + my @diffs = &Apache::loncommon::compare_arrays($helphash{'helpsettings'}{'adhoc'}{$rolename}{$access},$curr{$access}); + if (@diffs) { + $changes{'customrole'}{$rolename}{$access} = 1; + } + } elsif (@{$helphash{'helpsettings'}{'adhoc'}{$rolename}{$access}}) { + $changes{'customrole'}{$rolename}{$access} = 1; + } + foreach my $person (@{$helphash{'helpsettings'}{'adhoc'}{$rolename}{$access}}) { + my ($uname,$udom) = split(/:/,$person); + push(@newspecstaff,&Apache::loncommon::aboutmewrapper(&Apache::loncommon::plainname($uname,$udom,'lastname'),$uname,$udom)); + } + $newsettings{$rolename}{$access} = join(', ',sort(@newspecstaff)); + } + } else { + $helphash{'helpsettings'}{'adhoc'}{$rolename}{'access'}= 'all'; + } + unless ($curr{'access'} eq $access) { + $changes{'customrole'}{$rolename}{'access'} = 1; + $newsettings{$rolename}{'access'} = $lt{$helphash{'helpsettings'}{'adhoc'}{$rolename}{'access'}}; + } + } + if (@allpos > 0) { + my $idx = 0; + foreach my $rolename (@allpos) { + if ($rolename ne '') { + $helphash{'helpsettings'}{'adhoc'}{$rolename}{'order'} = $idx; + if (ref($current{'adhoc'}) eq 'HASH') { + if (ref($current{'adhoc'}{$rolename}) eq 'HASH') { + if ($current{'adhoc'}{$rolename}{'order'} ne $idx) { + $changes{'customrole'}{$rolename}{'order'} = 1; + $newsettings{$rolename}{'order'} = $idx+1; + } + } + } + $idx ++; + } + } + } + my $putresult; + if (keys(%changes) > 0) { + $putresult = &Apache::lonnet::put_dom('configuration',\%helphash,$dom); + if ($putresult eq 'ok') { + if (ref($helphash{'helpsettings'}) eq 'HASH') { + $domdefaults{'submitbugs'} = $helphash{'helpsettings'}{'submitbugs'}; + if (ref($helphash{'helpsettings'}{'adhoc'}) eq 'HASH') { + $domdefaults{'adhocroles'} = $helphash{'helpsettings'}{'adhoc'}; + } + } + my $cachetime = 24*60*60; + &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domdefaults'} = 1; + } + } else { + $errors .= '
          • '. + &mt('An error occurred storing the settings: [_1]', + $putresult).'
          • '; + } + } + if ((keys(%changes) && ($putresult eq 'ok')) || (keys(%changedprivs))) { + $resulttext = &mt('Changes made:').'
              '; + my (%shownprivs,@levelorder); + @levelorder = ('c','d','s'); + if ((keys(%changes)) && ($putresult eq 'ok')) { + foreach my $item (sort(keys(%changes))) { + if ($item eq 'submitbugs') { + $resulttext .= '
            • '.&mt('Display link to: [_1] set to "'.$offon[$env{'form.'.$item}].'".', + &Apache::loncommon::modal_link('http://bugs.loncapa.org', + &mt('LON-CAPA bug tracker'),600,500)).'
            • '; + } elsif ($item eq 'customrole') { + if (ref($changes{'customrole'}) eq 'HASH') { + my @keyorder = ('order','desc','access','status','exc','inc'); + my %keytext = &Apache::lonlocal::texthash( + order => 'Order', + desc => 'Role description', + access => 'Role usage', + status => 'Allowed institutional types', + exc => 'Allowed personnel', + inc => 'Disallowed personnel', + ); + foreach my $role (sort(keys(%{$changes{'customrole'}}))) { + if (ref($changes{'customrole'}{$role}) eq 'HASH') { + if ($role eq $newrole) { + $resulttext .= '
            • '.&mt('New custom role added: [_1]', + $role).'
                '; + } else { + $resulttext .= '
              • '.&mt('Existing custom role modified: [_1]', + $role).'
                  '; + } + foreach my $key (@keyorder) { + if ($changes{'customrole'}{$role}{$key}) { + $resulttext .= '
                • '.&mt("[_1] set to: [_2]", + $keytext{$key},$newsettings{$role}{$key}). + '
                • '; + } + } + if (ref($changedprivs{$role}) eq 'HASH') { + $shownprivs{$role} = 1; + $resulttext .= '
                • '.&mt('Privileges set to :').'
                    '; + foreach my $level (@levelorder) { + foreach my $item (split(/\:/,$changedprivs{$role}{$level})) { + next if ($item eq ''); + my ($priv) = split(/\&/,$item,2); + if (&Apache::lonnet::plaintext($priv)) { + $resulttext .= '
                  • '.&Apache::lonnet::plaintext($priv); + unless ($level eq 'c') { + $resulttext .= ' ('.$lt{$level}.')'; + } + $resulttext .= '
                  • '; + } + } + } + $resulttext .= '
                  '; + } + $resulttext .= '
              • '; + } + } + } + } + } + } + if (keys(%changedprivs)) { + foreach my $role (sort(keys(%changedprivs))) { + unless ($shownprivs{$role}) { + $resulttext .= '
              • '.&mt('Existing custom role modified: [_1]', + $role).'
                  '. + '
                • '.&mt('Privileges set to :').'
                    '; + foreach my $level (@levelorder) { + foreach my $item (split(/\:/,$changedprivs{$role}{$level})) { + next if ($item eq ''); + my ($priv) = split(/\&/,$item,2); + if (&Apache::lonnet::plaintext($priv)) { + $resulttext .= '
                  • '.&Apache::lonnet::plaintext($priv); + unless ($level eq 'c') { + $resulttext .= ' ('.$lt{$level}.')'; + } + $resulttext .= '
                  • '; + } + } + } + $resulttext .= '
              • '; + } + } + } + $resulttext .= '
              '; + } else { + $resulttext = &mt('No changes made to help settings'); + } + if ($errors) { + $resulttext .= '
              '.&mt('The following errors occurred: ').'
                '. + $errors.'
              '; + } + return $resulttext; +} + +sub modify_coursedefaults { + my ($dom,$lastactref,%domconfig) = @_; + my ($resulttext,$errors,%changes,%defaultshash); + my %defaultchecked = ( + 'canuse_pdfforms' => 'off', + 'uselcmath' => 'on', + 'usejsme' => 'on' + ); + my @toggles = ('canuse_pdfforms','uselcmath','usejsme'); + my @numbers = ('anonsurvey_threshold','uploadquota_official','uploadquota_unofficial', + 'uploadquota_community','uploadquota_textbook','uploadquota_placement', + 'mysqltables_official','mysqltables_unofficial','mysqltables_community', + 'mysqltables_textbook','mysqltables_placement'); + my @types = ('official','unofficial','community','textbook','placement'); + my %staticdefaults = ( + anonsurvey_threshold => 10, + uploadquota => 500, + postsubmit => 60, + mysqltables => 172800, + ); + my %texoptions = ( + MathJax => 'MathJax', + mimetex => &mt('Convert to Images'), + tth => &mt('TeX to HTML'), + ); + $defaultshash{'coursedefaults'} = {}; + + if (ref($domconfig{'coursedefaults'}) ne 'HASH') { + if ($domconfig{'coursedefaults'} eq '') { + $domconfig{'coursedefaults'} = {}; + } + } + + if (ref($domconfig{'coursedefaults'}) eq 'HASH') { + foreach my $item (@toggles) { + if ($defaultchecked{$item} eq 'on') { + if (($domconfig{'coursedefaults'}{$item} eq '') && + ($env{'form.'.$item} eq '0')) { + $changes{$item} = 1; + } elsif ($domconfig{'coursedefaults'}{$item} ne $env{'form.'.$item}) { + $changes{$item} = 1; + } + } elsif ($defaultchecked{$item} eq 'off') { + if (($domconfig{'coursedefaults'}{$item} eq '') && + ($env{'form.'.$item} eq '1')) { + $changes{$item} = 1; + } elsif ($domconfig{'coursedefaults'}{$item} ne $env{'form.'.$item}) { + $changes{$item} = 1; + } + } + $defaultshash{'coursedefaults'}{$item} = $env{'form.'.$item}; + } + foreach my $item (@numbers) { + my ($currdef,$newdef); + $newdef = $env{'form.'.$item}; + if ($item eq 'anonsurvey_threshold') { + $currdef = $domconfig{'coursedefaults'}{$item}; + $newdef =~ s/\D//g; + if ($newdef eq '' || $newdef < 1) { + $newdef = 1; + } + $defaultshash{'coursedefaults'}{$item} = $newdef; + } else { + my ($setting,$type) = ($item =~ /^(uploadquota|mysqltables)_(\w+)$/); + if (ref($domconfig{'coursedefaults'}{$setting}) eq 'HASH') { + $currdef = $domconfig{'coursedefaults'}{$setting}{$type}; + } + $newdef =~ s/[^\w.\-]//g; + $defaultshash{'coursedefaults'}{$setting}{$type} = $newdef; + } + if ($currdef ne $newdef) { + if ($item eq 'anonsurvey_threshold') { + unless (($currdef eq '') && ($newdef == $staticdefaults{$item})) { + $changes{$item} = 1; + } + } elsif ($item =~ /^(uploadquota|mysqltables)_/) { + my $setting = $1; + unless (($currdef eq '') && ($newdef == $staticdefaults{$setting})) { + $changes{$setting} = 1; + } + } + } + } + my $texengine; + if ($env{'form.texengine'} =~ /^(MathJax|mimetex|tth)$/) { + $texengine = $env{'form.texengine'}; + my $currdef = $domconfig{'coursedefaults'}{'texengine'}; + if ($currdef eq '') { + unless ($texengine eq $Apache::lonnet::deftex) { + $changes{'texengine'} = 1; + } + } elsif ($currdef ne $texengine) { + $changes{'texengine'} = 1; + } + } + if ($texengine ne '') { + $defaultshash{'coursedefaults'}{'texengine'} = $texengine; + } + my $currclone = $domconfig{'coursedefaults'}{'canclone'}; + my @currclonecode; + if (ref($currclone) eq 'HASH') { + if (ref($currclone->{'instcode'}) eq 'ARRAY') { + @currclonecode = @{$currclone->{'instcode'}}; + } + } + my $newclone; + if ($env{'form.canclone'} =~ /^(none|domain|instcode)$/) { + $newclone = $env{'form.canclone'}; + } + if ($newclone eq 'instcode') { + my @newcodes = &Apache::loncommon::get_env_multiple('form.clonecode'); + my (%codedefaults,@code_order,@clonecode); + &Apache::lonnet::auto_instcode_defaults($dom,\%codedefaults, + \@code_order); + foreach my $item (@code_order) { + if (grep(/^\Q$item\E$/,@newcodes)) { + push(@clonecode,$item); + } + } + if (@clonecode) { + $defaultshash{'coursedefaults'}{'canclone'} = { $newclone => \@clonecode }; + my @diffs = &Apache::loncommon::compare_arrays(\@currclonecode,\@clonecode); + if (@diffs) { + $changes{'canclone'} = 1; + } + } else { + $newclone eq ''; + } + } elsif ($newclone ne '') { + $defaultshash{'coursedefaults'}{'canclone'} = $newclone; + } + if ($newclone ne $currclone) { + $changes{'canclone'} = 1; + } + my %credits; + foreach my $type (@types) { + unless ($type eq 'community') { + $credits{$type} = $env{'form.'.$type.'_credits'}; + $credits{$type} =~ s/[^\d.]+//g; + } + } + if ((ref($domconfig{'coursedefaults'}{'coursecredits'}) ne 'HASH') && + ($env{'form.coursecredits'} eq '1')) { + $changes{'coursecredits'} = 1; + foreach my $type (keys(%credits)) { + $defaultshash{'coursedefaults'}{'coursecredits'}{$type} = $credits{$type}; + } + } else { + if ($env{'form.coursecredits'} eq '1') { + foreach my $type (@types) { + unless ($type eq 'community') { + if ($domconfig{'coursedefaults'}{'coursecredits'}{$type} ne $credits{$type}) { + $changes{'coursecredits'} = 1; + } + $defaultshash{'coursedefaults'}{'coursecredits'}{$type} = $credits{$type}; + } + } + } elsif (ref($domconfig{'coursedefaults'}{'coursecredits'}) eq 'HASH') { + foreach my $type (@types) { + unless ($type eq 'community') { + if ($domconfig{'coursedefaults'}{'coursecredits'}{$type}) { + $changes{'coursecredits'} = 1; + last; + } + } + } + } + } + if ($env{'form.postsubmit'} eq '1') { + $defaultshash{'coursedefaults'}{'postsubmit'}{'client'} = 'on'; + my %currtimeout; + if (ref($domconfig{'coursedefaults'}{'postsubmit'}) eq 'HASH') { + if ($domconfig{'coursedefaults'}{'postsubmit'}{'client'} eq 'off') { + $changes{'postsubmit'} = 1; + } + if (ref($domconfig{'coursedefaults'}{'postsubmit'}{'timeout'}) eq 'HASH') { + %currtimeout = %{$domconfig{'coursedefaults'}{'postsubmit'}{'timeout'}}; + } + } else { + $changes{'postsubmit'} = 1; + } + foreach my $type (@types) { + my $timeout = $env{'form.'.$type.'_timeout'}; + $timeout =~ s/\D//g; + if ($timeout == $staticdefaults{'postsubmit'}) { + $timeout = ''; + } elsif (($timeout eq '') || ($timeout =~ /^0+$/)) { + $timeout = '0'; + } + unless ($timeout eq '') { + $defaultshash{'coursedefaults'}{'postsubmit'}{'timeout'}{$type} = $timeout; + } + if (exists($currtimeout{$type})) { + if ($timeout ne $currtimeout{$type}) { + $changes{'postsubmit'} = 1; + } + } elsif ($timeout ne '') { + $changes{'postsubmit'} = 1; + } + } + } else { + $defaultshash{'coursedefaults'}{'postsubmit'}{'client'} = 'off'; + if (ref($domconfig{'coursedefaults'}{'postsubmit'}) eq 'HASH') { + if ($domconfig{'coursedefaults'}{'postsubmit'}{'client'} eq 'on') { + $changes{'postsubmit'} = 1; + } + } else { + $changes{'postsubmit'} = 1; + } + } + } + my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash, + $dom); + if ($putresult eq 'ok') { + if (keys(%changes) > 0) { + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); + if (($changes{'canuse_pdfforms'}) || ($changes{'uploadquota'}) || ($changes{'postsubmit'}) || + ($changes{'coursecredits'}) || ($changes{'uselcmath'}) || ($changes{'usejsme'}) || + ($changes{'canclone'}) || ($changes{'mysqltables'}) || ($changes{'texengine'})) { + foreach my $item ('canuse_pdfforms','uselcmath','usejsme','texengine') { + if ($changes{$item}) { + $domdefaults{$item}=$defaultshash{'coursedefaults'}{$item}; + } + } + if ($changes{'coursecredits'}) { + if (ref($defaultshash{'coursedefaults'}{'coursecredits'}) eq 'HASH') { + foreach my $type (keys(%{$defaultshash{'coursedefaults'}{'coursecredits'}})) { + $domdefaults{$type.'credits'} = + $defaultshash{'coursedefaults'}{'coursecredits'}{$type}; + } + } + } + if ($changes{'postsubmit'}) { + if (ref($defaultshash{'coursedefaults'}{'postsubmit'}) eq 'HASH') { + $domdefaults{'postsubmit'} = $defaultshash{'coursedefaults'}{'postsubmit'}{'client'}; + if (ref($defaultshash{'coursedefaults'}{'postsubmit'}{'timeout'}) eq 'HASH') { + foreach my $type (keys(%{$defaultshash{'coursedefaults'}{'postsubmit'}{'timeout'}})) { + $domdefaults{$type.'postsubtimeout'} = + $defaultshash{'coursedefaults'}{'postsubmit'}{'timeout'}{$type}; + } + } + } + } + if ($changes{'uploadquota'}) { + if (ref($defaultshash{'coursedefaults'}{'uploadquota'}) eq 'HASH') { + foreach my $type (@types) { + $domdefaults{$type.'quota'}=$defaultshash{'coursedefaults'}{'uploadquota'}{$type}; + } + } + } + if ($changes{'canclone'}) { + if (ref($defaultshash{'coursedefaults'}{'canclone'}) eq 'HASH') { + if (ref($defaultshash{'coursedefaults'}{'canclone'}{'instcode'}) eq 'ARRAY') { + my @clonecodes = @{$defaultshash{'coursedefaults'}{'canclone'}{'instcode'}}; + if (@clonecodes) { + $domdefaults{'canclone'} = join('+',@clonecodes); + } + } + } else { + $domdefaults{'canclone'}=$defaultshash{'coursedefaults'}{'canclone'}; + } + } + my $cachetime = 24*60*60; + &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domdefaults'} = 1; + } + } + $resulttext = &mt('Changes made:').'
                '; + foreach my $item (sort(keys(%changes))) { + if ($item eq 'canuse_pdfforms') { + if ($env{'form.'.$item} eq '1') { + $resulttext .= '
              • '.&mt("Course/Community users can create/upload PDF forms set to 'on'").'
              • '; + } else { + $resulttext .= '
              • '.&mt('Course/Community users can create/upload PDF forms set to "off"').'
              • '; + } + } elsif ($item eq 'uselcmath') { + if ($env{'form.'.$item} eq '1') { + $resulttext .= '
              • '.&mt('Math preview uses LON-CAPA previewer (javascript), if supported by browser.').'
              • '; + } else { + $resulttext .= '
              • '.&mt('Math preview uses DragMath (Java), if supported by client OS.').'
              • '; + } + } elsif ($item eq 'usejsme') { + if ($env{'form.'.$item} eq '1') { + $resulttext .= '
              • '.&mt('Molecule editor uses JSME (HTML5), if supported by browser.').'
              • '; + } else { + $resulttext .= '
              • '.&mt('Molecule editor uses JME (Java), if supported by client OS.').'
              • '; + } + } elsif ($item eq 'texengine') { + if ($defaultshash{'coursedefaults'}{'texengine'} ne '') { + $resulttext .= '
              • '.&mt('Default method to display mathematics set to: "[_1]"', + $texoptions{$defaultshash{'coursedefaults'}{'texengine'}}).'
              • '; + } + } elsif ($item eq 'anonsurvey_threshold') { + $resulttext .= '
              • '.&mt('Responder count required for display of anonymous survey submissions set to [_1].',$defaultshash{'coursedefaults'}{'anonsurvey_threshold'}).'
              • '; + } elsif ($item eq 'uploadquota') { + if (ref($defaultshash{'coursedefaults'}{'uploadquota'}) eq 'HASH') { + $resulttext .= '
              • '.&mt('Default quota for content uploaded to a course/community via Course Editor set as follows:').'
                  '. + '
                • '.&mt('Official courses: [_1] MB',''.$defaultshash{'coursedefaults'}{'uploadquota'}{'official'}.'').'
                • '. + '
                • '.&mt('Unofficial courses: [_1] MB',''.$defaultshash{'coursedefaults'}{'uploadquota'}{'unofficial'}.'').'
                • '. + '
                • '.&mt('Textbook courses: [_1] MB',''.$defaultshash{'coursedefaults'}{'uploadquota'}{'textbook'}.'').'
                • '. + '
                • '.&mt('Placement tests: [_1] MB',''.$defaultshash{'coursedefaults'}{'uploadquota'}{'placement'}.'').'
                • '. + '
                • '.&mt('Communities: [_1] MB',''.$defaultshash{'coursedefaults'}{'uploadquota'}{'community'}.'').'
                • '. + '
                '. + '
              • '; + } else { + $resulttext .= '
              • '.&mt('Default quota for content uploaded via Course Editor remains default: [_1] MB',$staticdefaults{'uploadquota'}).'
              • '; + } + } elsif ($item eq 'mysqltables') { + if (ref($defaultshash{'coursedefaults'}{'mysqltables'}) eq 'HASH') { + $resulttext .= '
              • '.&mt('Lifetime of "Temporary" MySQL tables (student performance data) on homeserver').'
                  '. + '
                • '.&mt('Official courses: [_1] s',''.$defaultshash{'coursedefaults'}{'mysqltables'}{'official'}.'').'
                • '. + '
                • '.&mt('Unofficial courses: [_1] s',''.$defaultshash{'coursedefaults'}{'mysqltables'}{'unofficial'}.'').'
                • '. + '
                • '.&mt('Textbook courses: [_1] s',''.$defaultshash{'coursedefaults'}{'mysqltables'}{'textbook'}.'').'
                • '. + '
                • '.&mt('Placement tests: [_1] s',''.$defaultshash{'coursedefaults'}{'mysqltables'}{'placement'}.'').'
                • '. + '
                • '.&mt('Communities: [_1] s',''.$defaultshash{'coursedefaults'}{'mysqltables'}{'community'}.'').'
                • '. + '
                '. + '
              • '; + } else { + $resulttext .= '
              • '.&mt('Lifetime of "Temporary" MySQL tables (student performance data) on homeserver remains default: [_1] s',$staticdefaults{'uploadquota'}).'
              • '; + } + } elsif ($item eq 'postsubmit') { + if ($domdefaults{'postsubmit'} eq 'off') { + $resulttext .= '
              • '.&mt('Submit button(s) remain enabled on page after student makes submission.'); + } else { + $resulttext .= '
              • '.&mt('Submit button(s) disabled on page after student makes submission').'; '; + if (ref($defaultshash{'coursedefaults'}{'postsubmit'}) eq 'HASH') { + $resulttext .= &mt('durations:').'
                  '; + foreach my $type (@types) { + $resulttext .= '
                • '; + my $timeout; + if (ref($defaultshash{'coursedefaults'}{'postsubmit'}{'timeout'}) eq 'HASH') { + $timeout = $defaultshash{'coursedefaults'}{'postsubmit'}{'timeout'}{$type}; + } + my $display; + if ($timeout eq '0') { + $display = &mt('unlimited'); + } elsif ($timeout eq '') { + $display = &mt('[quant,_1,second] (default)',$staticdefaults{'postsubmit'}); + } else { + $display = &mt('[quant,_1,second]',$timeout); + } + if ($type eq 'community') { + $resulttext .= &mt('Communities'); + } elsif ($type eq 'official') { + $resulttext .= &mt('Official courses'); + } elsif ($type eq 'unofficial') { + $resulttext .= &mt('Unofficial courses'); + } elsif ($type eq 'textbook') { + $resulttext .= &mt('Textbook courses'); + } elsif ($type eq 'placement') { + $resulttext .= &mt('Placement tests'); + } + $resulttext .= ' -- '.$display.'
                • '; + } + $resulttext .= '
                '; + } + $resulttext .= '
              • '; + } + } elsif ($item eq 'coursecredits') { + if (ref($defaultshash{'coursedefaults'}{'coursecredits'}) eq 'HASH') { + if (($domdefaults{'officialcredits'} eq '') && + ($domdefaults{'unofficialcredits'} eq '') && + ($domdefaults{'textbookcredits'} eq '')) { + $resulttext .= '
              • '.&mt('Student credits not in use for courses in this domain').'
              • '; + } else { + $resulttext .= '
              • '.&mt('Student credits can be set per course by a Domain Coordinator, with the following defaults applying:').'
                  '. + '
                • '.&mt('Official courses: [_1]',$defaultshash{'coursedefaults'}{'coursecredits'}{'official'}).'
                • '. + '
                • '.&mt('Unofficial courses: [_1]',$defaultshash{'coursedefaults'}{'coursecredits'}{'unofficial'}).'
                • '. + '
                • '.&mt('Textbook courses: [_1]',$defaultshash{'coursedefaults'}{'coursecredits'}{'textbook'}).'
                • '. + '
                '. + '
              • '; + } + } else { + $resulttext .= '
              • '.&mt('Student credits not in use for courses in this domain').'
              • '; + } + } elsif ($item eq 'canclone') { + if (ref($defaultshash{'coursedefaults'}{'canclone'}) eq 'HASH') { + if (ref($defaultshash{'coursedefaults'}{'canclone'}{'instcode'}) eq 'ARRAY') { + my $clonecodes = join(' '.&mt('and').' ',@{$defaultshash{'coursedefaults'}{'canclone'}{'instcode'}}); + $resulttext .= '
              • '.&mt('By default, official courses can be cloned from existing courses with the same: [_1]',''.$clonecodes.'').'
              • '; + } + } elsif ($defaultshash{'coursedefaults'}{'canclone'} eq 'domain') { + $resulttext .= '
              • '.&mt('By default, a course requester can clone any course from his/her domain.').'
              • '; + } else { + $resulttext .= '
              • '.&mt('By default, only course owner and coordinators may clone a course.').'
              • '; + } + } + } + $resulttext .= '
              '; + } else { + $resulttext = &mt('No changes made to course defaults'); + } + } else { + $resulttext = ''. + &mt('An error occurred: [_1]',$putresult).''; + } + return $resulttext; +} + +sub modify_selfenrollment { + my ($dom,$lastactref,%domconfig) = @_; + my ($resulttext,$errors,%changes,%selfenrollhash,%ordered); + my @types = ('official','unofficial','community','textbook','placement'); + my %titles = &tool_titles(); + my %descs = &Apache::lonuserutils::selfenroll_default_descs(); + ($ordered{'admin'},my $titlesref) = &Apache::lonuserutils::get_selfenroll_titles(); + $ordered{'default'} = ['types','registered','approval','limit']; + + my (%roles,%shown,%toplevel); + $roles{'0'} = &Apache::lonnet::plaintext('dc'); + + if (ref($domconfig{'selfenrollment'}) ne 'HASH') { + if ($domconfig{'selfenrollment'} eq '') { + $domconfig{'selfenrollment'} = {}; + } + } + %toplevel = ( + admin => 'Configuration Rights', + default => 'Default settings', + validation => 'Validation of self-enrollment requests', + ); + my ($itemsref,$namesref,$fieldsref) = &Apache::lonuserutils::selfenroll_validation_types(); + + if (ref($ordered{'admin'}) eq 'ARRAY') { + foreach my $item (@{$ordered{'admin'}}) { + foreach my $type (@types) { + if ($env{'form.selfenrolladmin_'.$item.'_'.$type}) { + $selfenrollhash{'admin'}{$type}{$item} = 1; + } else { + $selfenrollhash{'admin'}{$type}{$item} = 0; + } + if (ref($domconfig{'selfenrollment'}{'admin'}) eq 'HASH') { + if (ref($domconfig{'selfenrollment'}{'admin'}{$type}) eq 'HASH') { + if ($selfenrollhash{'admin'}{$type}{$item} ne + $domconfig{'selfenrollment'}{'admin'}{$type}{$item}) { + push(@{$changes{'admin'}{$type}},$item); + } + } else { + if (!$selfenrollhash{'admin'}{$type}{$item}) { + push(@{$changes{'admin'}{$type}},$item); + } + } + } elsif (!$selfenrollhash{'admin'}{$type}{$item}) { + push(@{$changes{'admin'}{$type}},$item); + } + } + } + } + + foreach my $item (@{$ordered{'default'}}) { + foreach my $type (@types) { + my $value = $env{'form.selfenrolldefault_'.$item.'_'.$type}; + if ($item eq 'types') { + unless (($value eq 'all') || ($value eq 'dom')) { + $value = ''; + } + } elsif ($item eq 'registered') { + unless ($value eq '1') { + $value = 0; + } + } elsif ($item eq 'approval') { + unless ($value =~ /^[012]$/) { + $value = 0; + } + } else { + unless (($value eq 'allstudents') || ($value eq 'selfenrolled')) { + $value = 'none'; + } + } + $selfenrollhash{'default'}{$type}{$item} = $value; + if (ref($domconfig{'selfenrollment'}{'default'}) eq 'HASH') { + if (ref($domconfig{'selfenrollment'}{'default'}{$type}) eq 'HASH') { + if ($selfenrollhash{'default'}{$type}{$item} ne + $domconfig{'selfenrollment'}{'default'}{$type}{$item}) { + push(@{$changes{'default'}{$type}},$item); + } + } else { + push(@{$changes{'default'}{$type}},$item); + } + } else { + push(@{$changes{'default'}{$type}},$item); + } + if ($item eq 'limit') { + if (($value eq 'allstudents') || ($value eq 'selfenrolled')) { + $env{'form.selfenrolldefault_cap_'.$type} =~ s/\D//g; + if ($env{'form.selfenrolldefault_cap_'.$type} ne '') { + $selfenrollhash{'default'}{$type}{'cap'} = $env{'form.selfenrolldefault_cap_'.$type}; + } + } else { + $selfenrollhash{'default'}{$type}{'cap'} = ''; + } + if (ref($domconfig{'selfenrollment'}{'default'}{$type}) eq 'HASH') { + if ($selfenrollhash{'default'}{$type}{'cap'} ne + $domconfig{'selfenrollment'}{'admin'}{$type}{'cap'}) { + push(@{$changes{'default'}{$type}},'cap'); + } + } elsif ($selfenrollhash{'default'}{$type}{'cap'} ne '') { + push(@{$changes{'default'}{$type}},'cap'); + } + } + } + } + + foreach my $item (@{$itemsref}) { + if ($item eq 'fields') { + my @changed; + @{$selfenrollhash{'validation'}{$item}} = &Apache::loncommon::get_env_multiple('form.selfenroll_validation_'.$item); + if (@{$selfenrollhash{'validation'}{$item}} > 0) { + @{$selfenrollhash{'validation'}{$item}} = sort(@{$selfenrollhash{'validation'}{$item}}); + } + if (ref($domconfig{'selfenrollment'}{'validation'}) eq 'HASH') { + if (ref($domconfig{'selfenrollment'}{'validation'}{$item}) eq 'ARRAY') { + @changed = &Apache::loncommon::compare_arrays($selfenrollhash{'validation'}{$item}, + $domconfig{'selfenrollment'}{'validation'}{$item}); + } else { + @changed = @{$selfenrollhash{'validation'}{$item}}; + } + } else { + @changed = @{$selfenrollhash{'validation'}{$item}}; + } + if (@changed) { + if ($selfenrollhash{'validation'}{$item}) { + $changes{'validation'}{$item} = join(', ',@{$selfenrollhash{'validation'}{$item}}); + } else { + $changes{'validation'}{$item} = &mt('None'); + } + } + } else { + $selfenrollhash{'validation'}{$item} = $env{'form.selfenroll_validation_'.$item}; + if ($item eq 'markup') { + if ($env{'form.selfenroll_validation_'.$item}) { + $env{'form.selfenroll_validation_'.$item} =~ s/[\n\r\f]+/\s/gs; + } + } + if (ref($domconfig{'selfenrollment'}{'validation'}) eq 'HASH') { + if ($domconfig{'selfenrollment'}{'validation'}{$item} ne $selfenrollhash{'validation'}{$item}) { + $changes{'validation'}{$item} = $selfenrollhash{'validation'}{$item}; + } + } + } + } + + my $putresult = &Apache::lonnet::put_dom('configuration',{'selfenrollment' => \%selfenrollhash}, + $dom); + if ($putresult eq 'ok') { + if (keys(%changes) > 0) { + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); + $resulttext = &mt('Changes made:').'
                '; + foreach my $key ('admin','default','validation') { + if (ref($changes{$key}) eq 'HASH') { + $resulttext .= '
              • '.$toplevel{$key}.'
                  '; + if ($key eq 'validation') { + foreach my $item (@{$itemsref}) { + if (exists($changes{$key}{$item})) { + if ($item eq 'markup') { + $resulttext .= '
                • '.&mt('[_1] set to: [_2]',$namesref->{$item}, + '
                  '.$changes{$key}{$item}.'
                  ').'
                • '; + } else { + $resulttext .= '
                • '.&mt('[_1] set to: [_2]',$namesref->{$item}, + ''.$changes{$key}{$item}.'').'
                • '; + } + } + } + } else { + foreach my $type (@types) { + if ($type eq 'community') { + $roles{'1'} = &mt('Community personnel'); + } else { + $roles{'1'} = &mt('Course personnel'); + } + if (ref($changes{$key}{$type}) eq 'ARRAY') { + if (ref($selfenrollhash{$key}{$type}) eq 'HASH') { + if ($key eq 'admin') { + my @mgrdc = (); + if (ref($ordered{$key}) eq 'ARRAY') { + foreach my $item (@{$ordered{'admin'}}) { + if (ref($selfenrollhash{$key}{$type}) eq 'HASH') { + if ($selfenrollhash{$key}{$type}{$item} eq '0') { + push(@mgrdc,$item); + } + } + } + if (@mgrdc) { + $domdefaults{$type.'selfenrolladmdc'} = join(',',@mgrdc); + } else { + delete($domdefaults{$type.'selfenrolladmdc'}); + } + } + } else { + if (ref($ordered{$key}) eq 'ARRAY') { + foreach my $item (@{$ordered{$key}}) { + if (grep(/^\Q$item\E$/,@{$changes{$key}{$type}})) { + $domdefaults{$type.'selfenroll'.$item} = + $selfenrollhash{$key}{$type}{$item}; + } + } + } + } + } + $resulttext .= '
                • '.$titles{$type}.'
                    '; + foreach my $item (@{$ordered{$key}}) { + if (grep(/^\Q$item\E$/,@{$changes{$key}{$type}})) { + $resulttext .= '
                  • '; + if ($key eq 'admin') { + $resulttext .= &mt('[_1] -- management by: [_2]',$titlesref->{$item}, + ''.$roles{$selfenrollhash{'admin'}{$type}{$item}}.''); + } else { + $resulttext .= &mt('[_1] set to: [_2]',$titlesref->{$item}, + ''.$descs{$item}{$selfenrollhash{'default'}{$type}{$item}}.''); + } + $resulttext .= '
                  • '; + } + } + $resulttext .= '
                • '; + } + } + $resulttext .= '
              • '; + } + } + } + if ((exists($changes{'admin'})) || (exists($changes{'default'}))) { + my $cachetime = 24*60*60; + &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domdefaults'} = 1; + } + } + $resulttext .= '
              '; + } else { + $resulttext = &mt('No changes made to self-enrollment settings'); + } + } else { + $resulttext = ''. + &mt('An error occurred: [_1]',$putresult).''; + } + return $resulttext; +} + +sub modify_usersessions { + my ($dom,$lastactref,%domconfig) = @_; + my @hostingtypes = ('version','excludedomain','includedomain'); + my @offloadtypes = ('primary','default'); + my %types = ( + remote => \@hostingtypes, + hosted => \@hostingtypes, + spares => \@offloadtypes, + ); + my @prefixes = ('remote','hosted','spares'); + my @lcversions = &Apache::lonnet::all_loncaparevs(); + my (%by_ip,%by_location,@intdoms,@instdoms); + &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms); + my @locations = sort(keys(%by_location)); + my (%defaultshash,%changes); + foreach my $prefix (@prefixes) { + $defaultshash{'usersessions'}{$prefix} = {}; + } + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); + my $resulttext; + my %iphost = &Apache::lonnet::get_iphost(); + foreach my $prefix (@prefixes) { + next if ($prefix eq 'spares'); + foreach my $type (@{$types{$prefix}}) { + my $inuse = $env{'form.'.$prefix.'_'.$type.'_inuse'}; + if ($type eq 'version') { + my $value = $env{'form.'.$prefix.'_'.$type}; + my $okvalue; + if ($value ne '') { + if (grep(/^\Q$value\E$/,@lcversions)) { + $okvalue = $value; + } + } + if (ref($domconfig{'usersessions'}) eq 'HASH') { + if (ref($domconfig{'usersessions'}{$prefix}) eq 'HASH') { + if ($domconfig{'usersessions'}{$prefix}{$type} ne '') { + if ($inuse == 0) { + $changes{$prefix}{$type} = 1; + } else { + if ($okvalue ne $domconfig{'usersessions'}{$prefix}{$type}) { + $changes{$prefix}{$type} = 1; + } + if ($okvalue ne '') { + $defaultshash{'usersessions'}{$prefix}{$type} = $okvalue; + } + } + } else { + if (($inuse == 1) && ($okvalue ne '')) { + $defaultshash{'usersessions'}{$prefix}{$type} = $okvalue; + $changes{$prefix}{$type} = 1; + } + } + } else { + if (($inuse == 1) && ($okvalue ne '')) { + $defaultshash{'usersessions'}{$prefix}{$type} = $okvalue; + $changes{$prefix}{$type} = 1; + } + } + } else { + if (($inuse == 1) && ($okvalue ne '')) { + $defaultshash{'usersessions'}{$prefix}{$type} = $okvalue; + $changes{$prefix}{$type} = 1; + } + } + } else { + my @vals = &Apache::loncommon::get_env_multiple('form.'.$prefix.'_'.$type); + my @okvals; + foreach my $val (@vals) { + if ($val =~ /:/) { + my @items = split(/:/,$val); + foreach my $item (@items) { + if (ref($by_location{$item}) eq 'ARRAY') { + push(@okvals,$item); + } + } + } else { + if (ref($by_location{$val}) eq 'ARRAY') { + push(@okvals,$val); + } + } + } + @okvals = sort(@okvals); + if (ref($domconfig{'usersessions'}) eq 'HASH') { + if (ref($domconfig{'usersessions'}{$prefix}) eq 'HASH') { + if (ref($domconfig{'usersessions'}{$prefix}{$type}) eq 'ARRAY') { + if ($inuse == 0) { + $changes{$prefix}{$type} = 1; + } else { + $defaultshash{'usersessions'}{$prefix}{$type} = \@okvals; + my @changed = &Apache::loncommon::compare_arrays($domconfig{'usersessions'}{$prefix}{$type},$defaultshash{'usersessions'}{$prefix}{$type}); + if (@changed > 0) { + $changes{$prefix}{$type} = 1; + } + } + } else { + if ($inuse == 1) { + $defaultshash{'usersessions'}{$prefix}{$type} = \@okvals; + $changes{$prefix}{$type} = 1; + } + } + } else { + if ($inuse == 1) { + $defaultshash{'usersessions'}{$prefix}{$type} = \@okvals; + $changes{$prefix}{$type} = 1; + } + } + } else { + if ($inuse == 1) { + $defaultshash{'usersessions'}{$prefix}{$type} = \@okvals; + $changes{$prefix}{$type} = 1; + } + } + } + } + } + + my @alldoms = &Apache::lonnet::all_domains(); + my %servers = &Apache::lonnet::internet_dom_servers($dom); + my %spareid = ¤t_offloads_to($dom,$domconfig{'usersessions'},\%servers); + my $savespares; + + foreach my $lonhost (sort(keys(%servers))) { + my $serverhomeID = + &Apache::lonnet::get_server_homeID($servers{$lonhost}); + my $serverhostname = &Apache::lonnet::hostname($lonhost); + $defaultshash{'usersessions'}{'spares'}{$lonhost} = {}; + my %spareschg; + foreach my $type (@{$types{'spares'}}) { + my @okspares; + my @checked = &Apache::loncommon::get_env_multiple('form.spare_'.$type.'_'.$lonhost); + foreach my $server (@checked) { + if (&Apache::lonnet::hostname($server) ne '') { + unless (&Apache::lonnet::hostname($server) eq $serverhostname) { + unless (grep(/^\Q$server\E$/,@okspares)) { + push(@okspares,$server); + } + } + } + } + my $new = $env{'form.newspare_'.$type.'_'.$lonhost}; + my $newspare; + if (($new ne '') && (&Apache::lonnet::hostname($new))) { + unless (&Apache::lonnet::hostname($new) eq $serverhostname) { + $newspare = $new; + } + } + my @spares; + if (($newspare ne '') && (!grep(/^\Q$newspare\E$/,@okspares))) { + @spares = sort(@okspares,$newspare); + } else { + @spares = sort(@okspares); + } + $defaultshash{'usersessions'}{'spares'}{$lonhost}{$type} = \@spares; + if (ref($spareid{$lonhost}) eq 'HASH') { + if (ref($spareid{$lonhost}{$type}) eq 'ARRAY') { + my @diffs = &Apache::loncommon::compare_arrays($spareid{$lonhost}{$type},\@spares); + if (@diffs > 0) { + $spareschg{$type} = 1; + } + } + } + } + if (keys(%spareschg) > 0) { + $changes{'spares'}{$lonhost} = \%spareschg; + } + } + $defaultshash{'usersessions'}{'offloadnow'} = {}; + my @offloadnow = &Apache::loncommon::get_env_multiple('form.offloadnow'); + my @okoffload; + if (@offloadnow) { + foreach my $server (@offloadnow) { + if (&Apache::lonnet::hostname($server) ne '') { + unless (grep(/^\Q$server\E$/,@okoffload)) { + push(@okoffload,$server); + } + } + } + if (@okoffload) { + foreach my $lonhost (@okoffload) { + $defaultshash{'usersessions'}{'offloadnow'}{$lonhost} = 1; + } + } + } + if (ref($domconfig{'usersessions'}) eq 'HASH') { + if (ref($domconfig{'usersessions'}{'spares'}) eq 'HASH') { + if (ref($changes{'spares'}) eq 'HASH') { + if (keys(%{$changes{'spares'}}) > 0) { + $savespares = 1; + } + } + } else { + $savespares = 1; + } + if (ref($domconfig{'usersessions'}{'offloadnow'}) eq 'HASH') { + foreach my $lonhost (keys(%{$domconfig{'usersessions'}{'offloadnow'}})) { + unless ($defaultshash{'usersessions'}{'offloadnow'}{$lonhost}) { + $changes{'offloadnow'} = 1; + last; + } + } + unless ($changes{'offloadnow'}) { + foreach my $lonhost (keys(%{$defaultshash{'usersessions'}{'offloadnow'}})) { + unless ($domconfig{'usersessions'}{'offloadnow'}{$lonhost}) { + $changes{'offloadnow'} = 1; + last; + } + } + } + } elsif (@okoffload) { + $changes{'offloadnow'} = 1; + } + } elsif (@okoffload) { + $changes{'offloadnow'} = 1; + } + my $nochgmsg = &mt('No changes made to settings for user session hosting/offloading.'); + if ((keys(%changes) > 0) || ($savespares)) { + my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash, + $dom); + if ($putresult eq 'ok') { + if (ref($defaultshash{'usersessions'}) eq 'HASH') { + if (ref($defaultshash{'usersessions'}{'remote'}) eq 'HASH') { + $domdefaults{'remotesessions'} = $defaultshash{'usersessions'}{'remote'}; + } + if (ref($defaultshash{'usersessions'}{'hosted'}) eq 'HASH') { + $domdefaults{'hostedsessions'} = $defaultshash{'usersessions'}{'hosted'}; + } + if (ref($defaultshash{'usersessions'}{'offloadnow'}) eq 'HASH') { + $domdefaults{'offloadnow'} = $defaultshash{'usersessions'}{'offloadnow'}; + } + } + my $cachetime = 24*60*60; + &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); + &Apache::lonnet::do_cache_new('usersessions',$dom,$defaultshash{'usersessions'},3600); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domdefaults'} = 1; + $lastactref->{'usersessions'} = 1; + } + if (keys(%changes) > 0) { + my %lt = &usersession_titles(); + $resulttext = &mt('Changes made:').'
                '; + foreach my $prefix (@prefixes) { + if (ref($changes{$prefix}) eq 'HASH') { + $resulttext .= '
              • '.$lt{$prefix}.'
                  '; + if ($prefix eq 'spares') { + if (ref($changes{$prefix}) eq 'HASH') { + foreach my $lonhost (sort(keys(%{$changes{$prefix}}))) { + $resulttext .= '
                • '.$lonhost.' '; + my $lonhostdom = &Apache::lonnet::host_domain($lonhost); + my $cachekey = &escape('spares').':'.&escape($lonhostdom); + &Apache::lonnet::remote_devalidate_cache($lonhost,[$cachekey]); + if (ref($changes{$prefix}{$lonhost}) eq 'HASH') { + foreach my $type (@{$types{$prefix}}) { + if ($changes{$prefix}{$lonhost}{$type}) { + my $offloadto = &mt('None'); + if (ref($defaultshash{'usersessions'}{'spares'}{$lonhost}{$type}) eq 'ARRAY') { + if (@{$defaultshash{'usersessions'}{'spares'}{$lonhost}{$type}} > 0) { + $offloadto = join(', ',@{$defaultshash{'usersessions'}{'spares'}{$lonhost}{$type}}); + } + } + $resulttext .= &mt('[_1] set to: [_2].',''.$lt{$type}.'',$offloadto).(' 'x3); + } + } + } + $resulttext .= '
                • '; + } + } + } else { + foreach my $type (@{$types{$prefix}}) { + if (defined($changes{$prefix}{$type})) { + my ($newvalue,$notinuse); + if (ref($defaultshash{'usersessions'}) eq 'HASH') { + if (ref($defaultshash{'usersessions'}{$prefix})) { + if ($type eq 'version') { + $newvalue = $defaultshash{'usersessions'}{$prefix}{$type}; + } else { + if (ref($defaultshash{'usersessions'}{$prefix}{$type}) eq 'ARRAY') { + if (@{$defaultshash{'usersessions'}{$prefix}{$type}} > 0) { + $newvalue = join(', ',@{$defaultshash{'usersessions'}{$prefix}{$type}}); + } + } else { + $notinuse = 1; + } + } + } + } + if ($newvalue eq '') { + if ($type eq 'version') { + $resulttext .= '
                • '.&mt('[_1] set to: off',$lt{$type}).'
                • '; + } elsif ($notinuse) { + $resulttext .= '
                • '.&mt('[_1] set to: not in use',$lt{$type}).'
                • '; + } else { + $resulttext .= '
                • '.&mt('[_1] set to: none',$lt{$type}).'
                • '; + } + } else { + if ($type eq 'version') { + $newvalue .= ' '.&mt('(or later)'); + } + $resulttext .= '
                • '.&mt('[_1] set to: [_2].',$lt{$type},$newvalue).'
                • '; + } + } + } + } + $resulttext .= '
                '; + } + } + if ($changes{'offloadnow'}) { + if (ref($defaultshash{'usersessions'}{'offloadnow'}) eq 'HASH') { + if (keys(%{$defaultshash{'usersessions'}{'offloadnow'}}) > 0) { + $resulttext .= '
              • '.&mt('Switch active users on next access, for server(s):').'
                  '; + foreach my $lonhost (sort(keys(%{$defaultshash{'usersessions'}{'offloadnow'}}))) { + $resulttext .= '
                • '.$lonhost.'
                • '; + } + $resulttext .= '
                '; + } else { + $resulttext .= '
              • '.&mt('No servers now set to switch active users on next access.'); + } + } else { + $resulttext .= '
              • '.&mt('No servers now set to switch active users on next access.').'
              • '; + } + } + $resulttext .= '
              '; + } else { + $resulttext = $nochgmsg; + } + } else { + $resulttext = ''. + &mt('An error occurred: [_1]',$putresult).''; + } + } else { + $resulttext = $nochgmsg; + } + return $resulttext; +} + +sub modify_ssl { + my ($dom,$lastactref,%domconfig) = @_; + my (%by_ip,%by_location,@intdoms,@instdoms); + &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms); + my @locations = sort(keys(%by_location)); + my %servers = &Apache::lonnet::internet_dom_servers($dom); + my (%defaultshash,%changes); + my $action = 'ssl'; + my @prefixes = ('connto','connfrom','replication'); + foreach my $prefix (@prefixes) { + $defaultshash{$action}{$prefix} = {}; + } + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); + my $resulttext; + my %iphost = &Apache::lonnet::get_iphost(); + my @reptypes = ('certreq','nocertreq'); + my @connecttypes = ('dom','intdom','other'); + my %types = ( + connto => \@connecttypes, + connfrom => \@connecttypes, + replication => \@reptypes, + ); + foreach my $prefix (sort(keys(%types))) { + foreach my $type (@{$types{$prefix}}) { + if (($prefix eq 'connto') || ($prefix eq 'connfrom')) { + my $value = 'yes'; + if ($env{'form.'.$prefix.'_'.$type} =~ /^(no|req)$/) { + $value = $env{'form.'.$prefix.'_'.$type}; + } + if (ref($domconfig{$action}) eq 'HASH') { + if (ref($domconfig{$action}{$prefix}) eq 'HASH') { + if ($domconfig{$action}{$prefix}{$type} ne '') { + if ($value ne $domconfig{$action}{$prefix}{$type}) { + $changes{$prefix}{$type} = 1; + } + $defaultshash{$action}{$prefix}{$type} = $value; + } else { + $defaultshash{$action}{$prefix}{$type} = $value; + $changes{$prefix}{$type} = 1; + } + } else { + $defaultshash{$action}{$prefix}{$type} = $value; + $changes{$prefix}{$type} = 1; + } + } else { + $defaultshash{$action}{$prefix}{$type} = $value; + $changes{$prefix}{$type} = 1; + } + if (($type eq 'dom') && (keys(%servers) == 1)) { + delete($changes{$prefix}{$type}); + } elsif (($type eq 'intdom') && (@instdoms == 1)) { + delete($changes{$prefix}{$type}); + } elsif (($type eq 'other') && (keys(%by_location) == 0)) { + delete($changes{$prefix}{$type}); + } + } elsif ($prefix eq 'replication') { + if (@locations > 0) { + my $inuse = $env{'form.'.$prefix.'_'.$type.'_inuse'}; + my @vals = &Apache::loncommon::get_env_multiple('form.'.$prefix.'_'.$type); + my @okvals; + foreach my $val (@vals) { + if ($val =~ /:/) { + my @items = split(/:/,$val); + foreach my $item (@items) { + if (ref($by_location{$item}) eq 'ARRAY') { + push(@okvals,$item); + } + } + } else { + if (ref($by_location{$val}) eq 'ARRAY') { + push(@okvals,$val); + } + } + } + @okvals = sort(@okvals); + if (ref($domconfig{$action}) eq 'HASH') { + if (ref($domconfig{$action}{$prefix}) eq 'HASH') { + if (ref($domconfig{$action}{$prefix}{$type}) eq 'ARRAY') { + if ($inuse == 0) { + $changes{$prefix}{$type} = 1; + } else { + $defaultshash{$action}{$prefix}{$type} = \@okvals; + my @changed = &Apache::loncommon::compare_arrays($domconfig{$action}{$prefix}{$type},$defaultshash{$action}{$prefix}{$type}); + if (@changed > 0) { + $changes{$prefix}{$type} = 1; + } + } + } else { + if ($inuse == 1) { + $defaultshash{$action}{$prefix}{$type} = \@okvals; + $changes{$prefix}{$type} = 1; + } + } + } else { + if ($inuse == 1) { + $defaultshash{$action}{$prefix}{$type} = \@okvals; + $changes{$prefix}{$type} = 1; + } + } + } else { + if ($inuse == 1) { + $defaultshash{$action}{$prefix}{$type} = \@okvals; + $changes{$prefix}{$type} = 1; + } + } + } + } + } + } + if (keys(%changes)) { + foreach my $prefix (keys(%changes)) { + if (ref($changes{$prefix}) eq 'HASH') { + if (scalar(keys(%{$changes{$prefix}})) == 0) { + delete($changes{$prefix}); + } + } else { + delete($changes{$prefix}); + } + } + } + my $nochgmsg = &mt('No changes made to LON-CAPA SSL settings'); + if (keys(%changes) > 0) { + my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash, + $dom); + if ($putresult eq 'ok') { + if (ref($defaultshash{$action}) eq 'HASH') { + if (ref($defaultshash{$action}{'replication'}) eq 'HASH') { + $domdefaults{'replication'} = $defaultshash{$action}{'replication'}; + } + if (ref($defaultshash{$action}{'connto'}) eq 'HASH') { + $domdefaults{'connto'} = $defaultshash{$action}{'connto'}; + } + if (ref($defaultshash{$action}{'connfrom'}) eq 'HASH') { + $domdefaults{'connfrom'} = $defaultshash{$action}{'connfrom'}; + } + } + my $cachetime = 24*60*60; + &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domdefaults'} = 1; + } + if (keys(%changes) > 0) { + my %titles = &ssl_titles(); + $resulttext = &mt('Changes made:').'
                '; + foreach my $prefix (@prefixes) { + if (ref($changes{$prefix}) eq 'HASH') { + $resulttext .= '
              • '.$titles{$prefix}.'
                  '; + foreach my $type (@{$types{$prefix}}) { + if (defined($changes{$prefix}{$type})) { + my ($newvalue,$notinuse); + if (ref($defaultshash{$action}) eq 'HASH') { + if (ref($defaultshash{$action}{$prefix})) { + if (($prefix eq 'connto') || ($prefix eq 'connfrom')) { + $newvalue = $titles{$defaultshash{$action}{$prefix}{$type}}; + } else { + if (ref($defaultshash{$action}{$prefix}{$type}) eq 'ARRAY') { + if (@{$defaultshash{$action}{$prefix}{$type}} > 0) { + $newvalue = join(', ',@{$defaultshash{$action}{$prefix}{$type}}); + } + } else { + $notinuse = 1; + } + } + } + if ($notinuse) { + $resulttext .= '
                • '.&mt('[_1] set to: not in use',$titles{$type}).'
                • '; + } elsif ($newvalue eq '') { + $resulttext .= '
                • '.&mt('[_1] set to: none',$titles{$type}).'
                • '; + } else { + $resulttext .= '
                • '.&mt('[_1] set to: [_2].',$titles{$type},$newvalue).'
                • '; + } + } + } + } + $resulttext .= '
                '; + } + } + } else { + $resulttext = $nochgmsg; + } + } else { + $resulttext = ''. + &mt('An error occurred: [_1]',$putresult).''; + } + } else { + $resulttext = $nochgmsg; + } + return $resulttext; +} + +sub modify_trust { + my ($dom,$lastactref,%domconfig) = @_; + my (%by_ip,%by_location,@intdoms,@instdoms); + &build_location_hashes(\@intdoms,\%by_ip,\%by_location,\@instdoms); + my @locations = sort(keys(%by_location)); + my @prefixes = qw(content shared enroll othcoau coaurem domroles catalog reqcrs msg); + my @types = ('exc','inc'); + my (%defaultshash,%changes); + foreach my $prefix (@prefixes) { + $defaultshash{'trust'}{$prefix} = {}; + } + my %domdefaults = &Apache::lonnet::get_domain_defaults($dom,1); + my $resulttext; + foreach my $prefix (@prefixes) { + foreach my $type (@types) { + my $inuse = $env{'form.'.$prefix.'_'.$type.'_inuse'}; + my @vals = &Apache::loncommon::get_env_multiple('form.'.$prefix.'_'.$type); + my @okvals; + foreach my $val (@vals) { + if ($val =~ /:/) { + my @items = split(/:/,$val); + foreach my $item (@items) { + if (ref($by_location{$item}) eq 'ARRAY') { + push(@okvals,$item); + } + } + } else { + if (ref($by_location{$val}) eq 'ARRAY') { + push(@okvals,$val); + } + } + } + @okvals = sort(@okvals); + if (ref($domconfig{'trust'}) eq 'HASH') { + if (ref($domconfig{'trust'}{$prefix}) eq 'HASH') { + if (ref($domconfig{'trust'}{$prefix}{$type}) eq 'ARRAY') { + if ($inuse == 0) { + $changes{$prefix}{$type} = 1; + } else { + $defaultshash{'trust'}{$prefix}{$type} = \@okvals; + my @changed = &Apache::loncommon::compare_arrays($domconfig{'trust'}{$prefix}{$type},$defaultshash{'trust'}{$prefix}{$type}); + if (@changed > 0) { + $changes{$prefix}{$type} = 1; + } + } + } else { + if ($inuse == 1) { + $defaultshash{'trust'}{$prefix}{$type} = \@okvals; + $changes{$prefix}{$type} = 1; + } + } + } else { + if ($inuse == 1) { + $defaultshash{'trust'}{$prefix}{$type} = \@okvals; + $changes{$prefix}{$type} = 1; + } + } + } else { + if ($inuse == 1) { + $defaultshash{'trust'}{$prefix}{$type} = \@okvals; + $changes{$prefix}{$type} = 1; + } + } + } + } + my $nochgmsg = &mt('No changes made to trust settings.'); + if (keys(%changes) > 0) { + my $putresult = &Apache::lonnet::put_dom('configuration',\%defaultshash, + $dom); + if ($putresult eq 'ok') { + if (ref($defaultshash{'trust'}) eq 'HASH') { + foreach my $prefix (@prefixes) { + if (ref($defaultshash{'trust'}{$prefix}) eq 'HASH') { + $domdefaults{'trust'.$prefix} = $defaultshash{'trust'}{$prefix}; + } + } + } + my $cachetime = 24*60*60; + &Apache::lonnet::do_cache_new('domdefaults',$dom,\%domdefaults,$cachetime); + if (ref($lastactref) eq 'HASH') { + $lastactref->{'domdefaults'} = 1; + } + if (keys(%changes) > 0) { + my %lt = &trust_titles(); + $resulttext = &mt('Changes made:').'
                  '; + foreach my $prefix (@prefixes) { + if (ref($changes{$prefix}) eq 'HASH') { + $resulttext .= '
                • '.$lt{$prefix}.'
                    '; + foreach my $type (@types) { + if (defined($changes{$prefix}{$type})) { + my ($newvalue,$notinuse); + if (ref($defaultshash{'trust'}) eq 'HASH') { + if (ref($defaultshash{'trust'}{$prefix})) { + if (ref($defaultshash{'trust'}{$prefix}{$type}) eq 'ARRAY') { + if (@{$defaultshash{'trust'}{$prefix}{$type}} > 0) { + $newvalue = join(', ',@{$defaultshash{'trust'}{$prefix}{$type}}); + } + } else { + $notinuse = 1; + } + } + } + if ($notinuse) { + $resulttext .= '
                  • '.&mt('[_1] set to: not in use',$lt{$type}).'
                  • '; + } elsif ($newvalue eq '') { + $resulttext .= '
                  • '.&mt('[_1] set to: none',$lt{$type}).'
                  • '; + } else { + $resulttext .= '
                  • '.&mt('[_1] set to: [_2].',$lt{$type},$newvalue).'
                  • '; + } + } + } + $resulttext .= '
                  '; + } + } + $resulttext .= '
                '; + } else { + $resulttext = $nochgmsg; + } + } else { + $resulttext = ''. + &mt('An error occurred: [_1]',$putresult).''; + } + } else { + $resulttext = $nochgmsg; + } + return $resulttext; +} + +sub modify_loadbalancing { + my ($dom,%domconfig) = @_; + my $primary_id = &Apache::lonnet::domain($dom,'primary'); + my $intdom = &Apache::lonnet::internet_dom($primary_id); + my ($othertitle,$usertypes,$types) = + &Apache::loncommon::sorted_inst_types($dom); + my %servers = &Apache::lonnet::internet_dom_servers($dom); + my %libraryservers = &Apache::lonnet::get_servers($dom,'library'); + my @sparestypes = ('primary','default'); + my %typetitles = &sparestype_titles(); + my $resulttext; + my (%currbalancer,%currtargets,%currrules,%existing,%currcookies); + if (ref($domconfig{'loadbalancing'}) eq 'HASH') { + %existing = %{$domconfig{'loadbalancing'}}; + } + &get_loadbalancers_config(\%servers,\%existing,\%currbalancer, + \%currtargets,\%currrules,\%currcookies); + my ($saveloadbalancing,%defaultshash,%changes); + my ($alltypes,$othertypes,$titles) = + &loadbalancing_titles($dom,$intdom,$usertypes,$types); + my %ruletitles = &offloadtype_text(); + my @deletions = &Apache::loncommon::get_env_multiple('form.loadbalancing_delete'); + for (my $i=0; $i<$env{'form.loadbalancing_total'}; $i++) { + my $balancer = $env{'form.loadbalancing_lonhost_'.$i}; + if ($balancer eq '') { + next; + } + if (!exists($servers{$balancer})) { + if (exists($currbalancer{$balancer})) { + push(@{$changes{'delete'}},$balancer); + } + next; + } + if ((@deletions > 0) && (grep(/^\Q$i\E$/,@deletions))) { + push(@{$changes{'delete'}},$balancer); + next; + } + if (!exists($currbalancer{$balancer})) { + push(@{$changes{'add'}},$balancer); + } + $defaultshash{'loadbalancing'}{$balancer}{'targets'}{'primary'} = []; + $defaultshash{'loadbalancing'}{$balancer}{'targets'}{'default'} = []; + $defaultshash{'loadbalancing'}{$balancer}{'rules'} = {}; + unless (ref($domconfig{'loadbalancing'}) eq 'HASH') { + $saveloadbalancing = 1; + } + foreach my $sparetype (@sparestypes) { + my @targets = &Apache::loncommon::get_env_multiple('form.loadbalancing_target_'.$i.'_'.$sparetype); + my @offloadto; + foreach my $target (@targets) { + if (($servers{$target}) && ($target ne $balancer)) { + if ($sparetype eq 'default') { + if (ref($defaultshash{'loadbalancing'}{$balancer}{'targets'}{'primary'}) eq 'ARRAY') { + next if (grep(/^\Q$target\E$/,@{$defaultshash{'loadbalancing'}{$balancer}{'targets'}{'primary'}})); + } + } + unless(grep(/^\Q$target\E$/,@offloadto)) { + push(@offloadto,$target); + } + } + } + if ($env{'form.loadbalancing_target_'.$i.'_hosthere'} eq $sparetype) { + unless(grep(/^\Q$balancer\E$/,@offloadto)) { + push(@offloadto,$balancer); + } + } + $defaultshash{'loadbalancing'}{$balancer}{'targets'}{$sparetype} = \@offloadto; + } + if ($env{'form.loadbalancing_cookie_'.$i}) { + $defaultshash{'loadbalancing'}{$balancer}{'cookie'} = 1; + if (exists($currbalancer{$balancer})) { + unless ($currcookies{$balancer}) { + $changes{'curr'}{$balancer}{'cookie'} = 1; + } + } + } elsif (exists($currbalancer{$balancer})) { + if ($currcookies{$balancer}) { + $changes{'curr'}{$balancer}{'cookie'} = 1; + } + } + if (ref($currtargets{$balancer}) eq 'HASH') { + foreach my $sparetype (@sparestypes) { + if (ref($currtargets{$balancer}{$sparetype}) eq 'ARRAY') { + my @targetdiffs = &Apache::loncommon::compare_arrays($currtargets{$balancer}{$sparetype},$defaultshash{'loadbalancing'}{$balancer}{'targets'}{$sparetype}); + if (@targetdiffs > 0) { + $changes{'curr'}{$balancer}{'targets'} = 1; + } + } elsif (ref($defaultshash{'loadbalancing'}{$balancer}{'targets'}{$sparetype}) eq 'ARRAY') { + if (@{$defaultshash{'loadbalancing'}{$balancer}{'targets'}{$sparetype}} > 0) { + $changes{'curr'}{$balancer}{'targets'} = 1; + } + } + } + } else { + if (ref($defaultshash{'loadbalancing'}{$balancer}) eq 'HASH') { + foreach my $sparetype (@sparestypes) { + if (ref($defaultshash{'loadbalancing'}{$balancer}{'targets'}{$sparetype}) eq 'ARRAY') { + if (@{$defaultshash{'loadbalancing'}{$balancer}{'targets'}{$sparetype}} > 0) { + $changes{'curr'}{$balancer}{'targets'} = 1; + } + } + } + } + } + my $ishomedom; + if (&Apache::lonnet::host_domain($balancer) eq $dom) { + $ishomedom = 1; + } + if (ref($alltypes) eq 'ARRAY') { + foreach my $type (@{$alltypes}) { + my $rule; + unless ((($type eq '_LC_external') || ($type eq '_LC_internetdom')) && + (!$ishomedom)) { + $rule = $env{'form.loadbalancing_rules_'.$i.'_'.$type}; + } + if ($rule eq 'specific') { + my $specifiedhost = $env{'form.loadbalancing_singleserver_'.$i.'_'.$type}; + if (exists($servers{$specifiedhost})) { + $rule = $specifiedhost; + } + } + $defaultshash{'loadbalancing'}{$balancer}{'rules'}{$type} = $rule; + if (ref($currrules{$balancer}) eq 'HASH') { + if ($rule ne $currrules{$balancer}{$type}) { + $changes{'curr'}{$balancer}{'rules'}{$type} = 1; + } + } elsif ($rule ne '') { + $changes{'curr'}{$balancer}{'rules'}{$type} = 1; + } + } + } + } + my $nochgmsg = &mt('No changes made to Load Balancer settings.'); + if ((keys(%changes) > 0) || ($saveloadbalancing)) { + unless (ref($defaultshash{'loadbalancing'}) eq 'HASH') { + $defaultshash{'loadbalancing'} = {}; + } + my $putresult = &Apache::lonnet::put_dom('configuration', + \%defaultshash,$dom); + if ($putresult eq 'ok') { + if (keys(%changes) > 0) { + my %toupdate; + if (ref($changes{'delete'}) eq 'ARRAY') { + foreach my $balancer (sort(@{$changes{'delete'}})) { + $resulttext .= '
              • '.&mt('Load Balancing discontinued for: [_1]',$balancer).'
              • '; + $toupdate{$balancer} = 1; + } + } + if (ref($changes{'add'}) eq 'ARRAY') { + foreach my $balancer (sort(@{$changes{'add'}})) { + $resulttext .= '
              • '.&mt('Load Balancing enabled for: [_1]',$balancer); + $toupdate{$balancer} = 1; + } + } + if (ref($changes{'curr'}) eq 'HASH') { + foreach my $balancer (sort(keys(%{$changes{'curr'}}))) { + $toupdate{$balancer} = 1; + if (ref($changes{'curr'}{$balancer}) eq 'HASH') { + if ($changes{'curr'}{$balancer}{'targets'}) { + my %offloadstr; + foreach my $sparetype (@sparestypes) { + if (ref($defaultshash{'loadbalancing'}{$balancer}{'targets'}{$sparetype}) eq 'ARRAY') { + if (@{$defaultshash{'loadbalancing'}{$balancer}{'targets'}{$sparetype}} > 0) { + $offloadstr{$sparetype} = join(', ',@{$defaultshash{'loadbalancing'}{$balancer}{'targets'}{$sparetype}}); + } + } + } + if (keys(%offloadstr) == 0) { + $resulttext .= '
              • '.&mt("Servers to which Load Balance server offloads set to 'None', by default").'
              • '; + } else { + my $showoffload; + foreach my $sparetype (@sparestypes) { + $showoffload .= ''.$typetitles{$sparetype}.': '; + if (defined($offloadstr{$sparetype})) { + $showoffload .= $offloadstr{$sparetype}; + } else { + $showoffload .= &mt('None'); + } + $showoffload .= (' 'x3); + } + $resulttext .= '
              • '.&mt('By default, Load Balancer: [_1] set to offload to - [_2]',$balancer,$showoffload).'
              • '; + } + } + } + if (ref($changes{'curr'}{$balancer}{'rules'}) eq 'HASH') { + if ((ref($alltypes) eq 'ARRAY') && (ref($titles) eq 'HASH')) { + foreach my $type (@{$alltypes}) { + if ($changes{'curr'}{$balancer}{'rules'}{$type}) { + my $rule = $defaultshash{'loadbalancing'}{$balancer}{'rules'}{$type}; + my $balancetext; + if ($rule eq '') { + $balancetext = $ruletitles{'default'}; + } elsif (($rule eq 'homeserver') || ($rule eq 'externalbalancer') || + ($type eq '_LC_ipchange') || ($type eq '_LC_ipchangesso')) { + if (($type eq '_LC_ipchange') || ($type eq '_LC_ipchangesso')) { + foreach my $sparetype (@sparestypes) { + if (ref($defaultshash{'loadbalancing'}{$balancer}{'targets'}{$sparetype}) eq 'ARRAY') { + map { $toupdate{$_} = 1; } (@{$defaultshash{'loadbalancing'}{$balancer}{'targets'}{$sparetype}}); + } + } + foreach my $item (@{$alltypes}) { + next if ($item =~ /^_LC_ipchange/); + my $hasrule = $defaultshash{'loadbalancing'}{$balancer}{'rules'}{$item}; + if ($hasrule eq 'homeserver') { + map { $toupdate{$_} = 1; } (keys(%libraryservers)); + } else { + unless (($hasrule eq 'default') || ($hasrule eq 'none') || ($hasrule eq 'externalbalancer')) { + if ($servers{$hasrule}) { + $toupdate{$hasrule} = 1; + } + } + } + } + if (($rule eq 'balancer') || ($rule eq 'offloadedto')) { + $balancetext = $ruletitles{$rule}; + } else { + my $receiver = $defaultshash{'loadbalancing'}{$balancer}{'rules'}{$type}; + $balancetext = $ruletitles{'particular'}.' '.$receiver; + if ($receiver) { + $toupdate{$receiver}; + } + } + } else { + $balancetext = $ruletitles{$rule}; + } + } else { + $balancetext = &mt('offload to [_1]',$defaultshash{'loadbalancing'}{$balancer}{'rules'}{$type}); + } + $resulttext .= '
              • '.&mt('Load Balancer: [_1] -- balancing for [_2] set to - "[_3]"',$balancer,$titles->{$type},$balancetext).'
              • '; + } + } + } + } + if ($changes{'curr'}{$balancer}{'cookie'}) { + $resulttext .= '
              • '.&mt('Load Balancer: [_1] -- cookie use enabled', + $balancer).'
              • '; + } + if (keys(%toupdate)) { + my %thismachine; + my $updatedhere; + my $cachetime = 60*60*24; + map { $thismachine{$_} = 1; } &Apache::lonnet::current_machine_ids(); + foreach my $lonhost (keys(%toupdate)) { + if ($thismachine{$lonhost}) { + unless ($updatedhere) { + &Apache::lonnet::do_cache_new('loadbalancing',$dom, + $defaultshash{'loadbalancing'}, + $cachetime); + $updatedhere = 1; + } + } else { + my $cachekey = &escape('loadbalancing').':'.&escape($dom); + &Apache::lonnet::remote_devalidate_cache($lonhost,[$cachekey]); + } + } + } + } + } + if ($resulttext ne '') { + $resulttext = &mt('Changes made:').'
                  '.$resulttext.'
                '; + } else { + $resulttext = $nochgmsg; + } + } else { + $resulttext = $nochgmsg; + } + } else { + $resulttext = ''. + &mt('An error occurred: [_1]',$putresult).''; + } + } else { + $resulttext = $nochgmsg; + } + return $resulttext; +} + +sub recurse_check { + my ($chkcats,$categories,$depth,$name) = @_; + if (ref($chkcats->[$depth]{$name}) eq 'ARRAY') { + my $chg = 0; + for (my $j=0; $j<@{$chkcats->[$depth]{$name}}; $j++) { + my $category = $chkcats->[$depth]{$name}[$j]; + my $item; + if ($category eq '') { + $chg ++; + } else { + my $deeper = $depth + 1; + $item = &escape($category).':'.&escape($name).':'.$depth; + if ($chg) { + $categories->{$item} -= $chg; + } + &recurse_check($chkcats,$categories,$deeper,$category); + $deeper --; + } + } + } + return; +} + +sub recurse_cat_deletes { + my ($item,$coursecategories,$deletions) = @_; + my ($deleted,$container,$depth) = map { &unescape($_); } split(/:/,$item); + my $subdepth = $depth + 1; + if (ref($coursecategories) eq 'HASH') { + foreach my $subitem (keys(%{$coursecategories})) { + my ($child,$parent,$itemdepth) = map { &unescape($_); } split(/:/,$subitem); + if (($parent eq $deleted) && ($itemdepth == $subdepth)) { + delete($coursecategories->{$subitem}); + $deletions->{$subitem} = 1; + &recurse_cat_deletes($subitem,$coursecategories,$deletions); + } + } + } + return; +} + +sub active_dc_picker { + my ($dom,$numinrow,$inputtype,$name,%currhash) = @_; + my %domcoords = &Apache::lonnet::get_active_domroles($dom,['dc']); + my @domcoord = keys(%domcoords); + if (keys(%currhash)) { + foreach my $dc (keys(%currhash)) { + unless (exists($domcoords{$dc})) { + push(@domcoord,$dc); + } + } + } + @domcoord = sort(@domcoord); + my $numdcs = scalar(@domcoord); + my $rows = 0; + my $table; + if ($numdcs > 1) { + $table = '
    '. - ''. ' 
    '.&mt('Users allowed to search').' ('.$dom.')'. - ''; +sub insttypes_row { + my ($settings,$types,$usertypes,$dom,$numinrow,$othertitle,$context,$rowtotal,$onclick, + $customcss,$rowstyle) = @_; + my %lt = &Apache::lonlocal::texthash ( + cansearch => 'Users allowed to search', + statustocreate => 'Institutional affiliation(s) able to create own account (login/SSO)', + lockablenames => 'User preference to lock name', + selfassign => 'Self-reportable affiliations', + overrides => "Override domain's helpdesk settings based on requester's affiliation", + ); + my $showdom; + if ($context eq 'cansearch') { + $showdom = ' ('.$dom.')'; + } + my $class = 'LC_left_item'; + if ($context eq 'statustocreate') { + $class = 'LC_right_item'; + } + my $css_class; + if ($$rowtotal%2) { + $css_class = 'LC_odd_row'; + } + if ($customcss) { + $css_class .= ' '.$customcss; + } + $css_class =~ s/^\s+//; + if ($css_class) { + $css_class = ' class="'.$css_class.'"'; + } + if ($rowstyle) { + $css_class .= ' style="'.$rowstyle.'"'; + } + if ($onclick) { + $onclick = 'onclick="'.$onclick.'" '; + } + my $output = ''. + ''; - return $output; -} - -sub sorted_inst_types { - my ($dom) = @_; - my ($usertypes,$order) = &Apache::lonnet::retrieve_inst_usertypes($dom); - my $othertitle = &mt('All users'); - my @types; - if (ref($order) eq 'ARRAY') { - @types = @{$order}; - } - if (@types == 0) { - if (ref($usertypes) eq 'HASH') { - @types = sort(keys(%{$usertypes})); + if ($colsleft > 1) { + $output .= '
    '.$lt{$context}.$showdom. + ''; my $rem; if (ref($types) eq 'ARRAY') { for (my $i=0; $i<@{$types}; $i++) { @@ -1772,58 +9222,62 @@ sub users_cansearch_row { $output .= ''; } my $check = ' '; - if (ref($settings->{'cansearch'}) eq 'ARRAY') { - if (grep(/^\Q$types->[$i]\E$/,@{$settings->{'cansearch'}})) { + if (ref($settings) eq 'HASH') { + if (ref($settings->{$context}) eq 'ARRAY') { + if (grep(/^\Q$types->[$i]\E$/,@{$settings->{$context}})) { + $check = ' checked="checked" '; + } + } elsif (ref($settings->{$context}) eq 'HASH') { + if (ref($settings->{$context}->{$types->[$i]}) eq 'HASH') { + $check = ' checked="checked" '; + } + } elsif ($context eq 'statustocreate') { $check = ' checked="checked" '; } } $output .= ''; } } - $rem = @{$types}%($numinrow); } my $colsleft = $numinrow - $rem; - if ($colsleft > 1) { - $output .= ''; } - } - $output .= ''. - '
    '. ''; + if ($context eq 'overrides') { + if ($colsleft > 1) { + $output .= ''; + } else { + $output .= ''; + } + $output .= ' '; } else { - $output .= ''; - } - my $defcheck = ' '; - if (ref($settings->{'cansearch'}) eq 'ARRAY') { - if (grep(/^default$/,@{$settings->{'cansearch'}})) { - $defcheck = ' checked="checked" '; + if ($rem == 0) { + $output .= '
    '; + } else { + $output .= ''; } + my $defcheck = ' '; + if (ref($settings) eq 'HASH') { + if (ref($settings->{$context}) eq 'ARRAY') { + if (grep(/^default$/,@{$settings->{$context}})) { + $defcheck = ' checked="checked" '; + } + } elsif ($context eq 'statustocreate') { + $defcheck = ' checked="checked" '; + } + } + $output .= ''; } - if (keys(%{$usertypes}) > 0) { - $othertitle = &mt('Other users'); - } - return ($othertitle,$usertypes,\@types); + $output .= '
    '; + for (my $i=0; $i<@domcoord; $i++) { + my $rem = $i%($numinrow); + if ($rem == 0) { + if ($i > 0) { + $table .= ''; + } + $table .= ''; + $rows ++; + } + my $check = ''; + if ($inputtype eq 'radio') { + if (keys(%currhash) == 0) { + if (!$i) { + $check = ' checked="checked"'; + } + } elsif (exists($currhash{$domcoord[$i]})) { + $check = ' checked="checked"'; + } + } else { + if (exists($currhash{$domcoord[$i]})) { + $check = ' checked="checked"'; + } + } + if ($i == @domcoord - 1) { + my $colsleft = $numinrow - $rem; + if ($colsleft > 1) { + $table .= ''; + } + $table .= '
    '; + } else { + $table .= ''; + } + } else { + $table .= ''; + } + my ($dcname,$dcdom) = split(':',$domcoord[$i]); + my $user = &Apache::loncommon::plainname($dcname,$dcdom); + $table .= '
    '; + } elsif ($numdcs == 1) { + my ($dcname,$dcdom) = split(':',$domcoord[0]); + my $user = &Apache::loncommon::plainname($dcname,$dcdom); + if ($inputtype eq 'radio') { + $table = ''.$user; + if ($user ne $dcname.':'.$dcdom) { + $table .= ' ('.$dcname.':'.$dcdom.')'; + } + } else { + my $check; + if (exists($currhash{$domcoord[0]})) { + $check = ' checked="checked"'; + } + $table = ''; + $rows ++; + } + } + return ($numdcs,$table,$rows); +} + +sub usersession_titles { + return &Apache::lonlocal::texthash( + hosted => 'Hosting of sessions for users from other domains on servers in this domain', + remote => 'Hosting of sessions for users in this domain on servers in other domains', + spares => 'Servers offloaded to, when busy', + version => 'LON-CAPA version requirement', + excludedomain => 'Allow all, but exclude specific domains', + includedomain => 'Deny all, but include specific domains', + primary => 'Primary (checked first)', + default => 'Default', + ); +} + +sub id_for_thisdom { + my (%servers) = @_; + my %altids; + foreach my $server (keys(%servers)) { + my $serverhome = &Apache::lonnet::get_server_homeID($servers{$server}); + if ($serverhome ne $server) { + $altids{$serverhome} = $server; + } + } + return %altids; +} + +sub count_servers { + my ($currbalancer,%servers) = @_; + my (@spares,$numspares); + foreach my $lonhost (sort(keys(%servers))) { + next if ($currbalancer eq $lonhost); + push(@spares,$lonhost); + } + if ($currbalancer) { + $numspares = scalar(@spares); + } else { + $numspares = scalar(@spares) - 1; + } + return ($numspares,@spares); +} + +sub lonbalance_targets_js { + my ($dom,$types,$servers,$settings) = @_; + my $select = &mt('Select'); + my ($alltargets,$allishome,$allinsttypes,@alltypes); + if (ref($servers) eq 'HASH') { + $alltargets = join("','",sort(keys(%{$servers}))); + my @homedoms; + foreach my $server (sort(keys(%{$servers}))) { + if (&Apache::lonnet::host_domain($server) eq $dom) { + push(@homedoms,'1'); + } else { + push(@homedoms,'0'); + } + } + $allishome = join("','",@homedoms); + } + if (ref($types) eq 'ARRAY') { + if (@{$types} > 0) { + @alltypes = @{$types}; + } + } + push(@alltypes,'default','_LC_adv','_LC_author','_LC_internetdom','_LC_external'); + $allinsttypes = join("','",@alltypes); + my (%currbalancer,%currtargets,%currrules,%existing,%currcookies); + if (ref($settings) eq 'HASH') { + %existing = %{$settings}; + } + &get_loadbalancers_config($servers,\%existing,\%currbalancer, + \%currtargets,\%currrules,\%currcookies); + my $balancers = join("','",sort(keys(%currbalancer))); + return <<"END"; + + + +END +} + +sub new_spares_js { + my @sparestypes = ('primary','default'); + my $types = join("','",@sparestypes); + my $select = &mt('Select'); + return <<"END"; + + + +END + +} + +sub common_domprefs_js { + return <<"END"; + + + +END + +} + +sub recaptcha_js { + my %lt = &captcha_phrases(); + return <<"END"; + + + +END + +} + +sub toggle_display_js { + return <<"END"; + + + +END + +} + +sub captcha_phrases { + return &Apache::lonlocal::texthash ( + priv => 'Private key', + pub => 'Public key', + original => 'original (CAPTCHA)', + recaptcha => 'successor (ReCAPTCHA)', + notused => 'unused', + ver => 'ReCAPTCHA version (1 or 2)', + ); +} + +sub devalidate_remote_domconfs { + my ($dom,$cachekeys) = @_; + return unless (ref($cachekeys) eq 'HASH'); + my %servers = &Apache::lonnet::internet_dom_servers($dom); + my %thismachine; + map { $thismachine{$_} = 1; } &Apache::lonnet::current_machine_ids(); + my @posscached = ('domainconfig','domdefaults','ltitools','usersessions','directorysrch'); + if (keys(%servers)) { + foreach my $server (keys(%servers)) { + next if ($thismachine{$server}); + my @cached; + foreach my $name (@posscached) { + if ($cachekeys->{$name}) { + push(@cached,&escape($name).':'.&escape($dom)); + } + } + if (@cached) { + &Apache::lonnet::remote_devalidate_cache($server,\@cached); + } + } + } + return; +} + 1;