--- loncom/interface/lonpreferences.pm	2019/08/21 22:41:13	1.235
+++ loncom/interface/lonpreferences.pm	2025/03/05 05:24:42	1.247
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # Preferences
 #
-# $Id: lonpreferences.pm,v 1.235 2019/08/21 22:41:13 raeburn Exp $
+# $Id: lonpreferences.pm,v 1.247 2025/03/05 05:24:42 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -31,7 +31,6 @@
 package Apache::lonpreferences;
 
 use strict;
-use LONCAPA;
 use Apache::Constants qw(:common);
 use Apache::File;
 use Apache::loncommon();
@@ -40,6 +39,7 @@ use Apache::lonlocal;
 use Apache::lonnet;
 use LONCAPA::lonauthcgi();
 use LONCAPA();
+use DateTime::TimeZone();
 
 ################################################################
 #                       Handler subroutines                    #
@@ -491,24 +491,24 @@ sub screennamechanger {
                   text => 'Change Screen Name'});
     $r->print(Apache::loncommon::start_page('Personal Data'));
     $r->print(Apache::lonhtmlcommon::breadcrumbs('Change Screen Name'));
-    $r->print('<p>'
+    $r->print('<div class="LC_landmark" role="main"><p>'
              .&mt('Change the name that is displayed in your posts.')
              .'</p>'
     );
     $r->print('<form name="prefs" action="/adm/preferences" method="post">'
              .'<input type="hidden" name="action" value="verify_and_change_screenname" />'
              .&Apache::lonhtmlcommon::start_pick_box()
-             .&Apache::lonhtmlcommon::row_title(&mt('Screenname').' '.&mt('(shown if you post anonymously)'))
-             .'<input type="text" size="20" value="'.$screenname.'" name="screenname" />'
+             .&Apache::lonhtmlcommon::row_title('<label for="screenname">'.&mt('Screenname').'</label> '.&mt('(shown if you post anonymously)'))
+             .'<input type="text" size="20" value="'.$screenname.'" name="screenname" id="screenname" />'
              .&Apache::lonhtmlcommon::row_closure()
-             .&Apache::lonhtmlcommon::row_title(&mt('Nickname').' '.&mt('(shown if you post non-anonymously)'))
-             .'<input type="text" size="20" value="'.$nickname.'" name="nickname" />'
+             .&Apache::lonhtmlcommon::row_title('<label for="nickname">'.&mt('Nickname').'</label> '.&mt('(shown if you post non-anonymously)'))
+             .'<input type="text" size="20" value="'.$nickname.'" name="nickname" id="nickname" />'
              .&Apache::lonhtmlcommon::row_closure()
-             .&Apache::lonhtmlcommon::row_title()
+             .&Apache::lonhtmlcommon::row_title('<span class="LC_visually_hidden">'.&mt('Submit').':</span>','','','',1)
              .'<input type="submit" value="'.&mt('Save').'" />'
              .&Apache::lonhtmlcommon::row_closure(1)
              .&Apache::lonhtmlcommon::end_pick_box()
-             .'</form>'
+             .'</form></div>'
     );
 }
 
@@ -627,7 +627,7 @@ sub icon_options {
 }
 
 sub icon_previews {
-     my %icon_text = (
+     my %icon_text = &Apache::lonlocal::texthash (
                       annotate => 'Notes',
                       wishlist => 'Stored Links',
                       catalog  => 'Info',
@@ -636,12 +636,12 @@ sub icon_previews {
                       printout => 'Print',
                      );
     my %inlinetools = (
-        printout => "s&8&3&prt.png&$icon_text{'printout'}&printout[_1]&gopost('/adm/printout',currentURL)&Prepare a printable document",
-        wishlist => "s&9&1&wishlist-link.png&$icon_text{'wishlist'}&wishlistlink[_2]&set_wishlistlink()&Save a link for this resource in your personal Stored Links repository",
-        evaluate => "s&8&1&eval.png&$icon_text{'evaluate'}&this[_1]&gopost('/adm/evaluate',currentURL,1)&Provide my evaluation of this resource",
-        feedback => "s&8&2&fdbk.png&$icon_text{'feedback'}&discuss[_1]&gopost('/adm/feedback',currentURL,1)&Provide feedback messages or contribute to the course discussion about this resource",
-        annotate => "s&9&3&anot.png&$icon_text{'annotate'}&tations[_1]&annotate()&Make notes and annotations about this resource",
-        catalog  => "s&6&3&catalog.png&$icon_text{'catalog'}&info[_1]&catalog_info()&Show Metadata",
+        printout => "s&8&3&prt.png&$icon_text{'printout'}&printout[_1]&gopost('/adm/printout',currentURL)&".&mt('Prepare a printable document'),
+        wishlist => "s&9&1&wishlist-link.png&$icon_text{'wishlist'}&wishlistlink[_2]&set_wishlistlink()&".&mt('Save a link for this resource in your personal Stored Links repository'),
+        evaluate => "s&8&1&eval.png&$icon_text{'evaluate'}&this[_1]&gopost('/adm/evaluate',currentURL,1)&".&mt('Provide my evaluation of this resource'),
+        feedback => "s&8&2&fdbk.png&$icon_text{'feedback'}&discuss[_1]&gopost('/adm/feedback',currentURL,1)&".&mt('Provide feedback messages or contribute to the course discussion about this resource'),
+        annotate => "s&9&3&anot.png&$icon_text{'annotate'}&tations[_1]&annotate()&".&mt('Make notes and annotations about this resource'),
+        catalog  => "s&6&3&catalog.png&$icon_text{'catalog'}&info[_1]&catalog_info()&".&mt('Show Metadata'),
     );
     my @toolsorder = qw(annotate wishlist evaluate feedback printout catalog);
     return (\%inlinetools,\@toolsorder);
@@ -864,6 +864,7 @@ sub msgforwardchanger {
     my $validatescript = &Apache::lonhtmlcommon::javascript_valid_email();
     my $jscript = qq|
 <script type="text/javascript" language="JavaScript">
+// <![CDATA[
 function validate() {
     for (var i=0; i<document.prefs.numnotify.value; i++) {
         var checkaddress = 0;
@@ -916,6 +917,8 @@ function modify_address(adnum) {
 } 
 
 $validatescript
+
+// ]]>
 </script>
 |;
     $r->print(<<ENDMSG);
@@ -1189,7 +1192,8 @@ sub colorschanger {
     my $resetbuttondesc = &mt('Reset All Colors to Default');
     my $colorchooser=&Apache::lonhtmlcommon::color_picker();
     $r->print('<script type="text/javascript" language="JavaScript">
-' . $colorchooser . '
+// <![CDATA[' ."\n". $colorchooser . "\n". '
+// ]]>
 </script>
 ');
     $r->print(<<ENDCOL);
@@ -1265,19 +1269,15 @@ sub passwordchanger {
     # This function is a bit of a mess....
     # Passwords are encrypted using londes.js (DES encryption)
     $errormessage = ($errormessage || '');
-    my ($user,$domain,$currentpass);
+    my ($user,$domain,$currentpass,$clientip);
+    $clientip = &Apache::lonnet::get_requestor_ip($r);
     &Apache::lonhtmlcommon::add_breadcrumb(
 		{ href => '/adm/preferences?action=changepass',
                   text => 'Change Password'});
     unless ($caller eq 'reset_by_email') {
         $r->print(Apache::loncommon::start_page('Personal Data'));
-        $r->print(Apache::lonhtmlcommon::breadcrumbs('Change Password'));
-    }
-    my ($blocked,$blocktext) =
-        &Apache::loncommon::blocking_status('passwd');
-    if ($blocked) {
-        $r->print('<p class="LC_warning">'.$blocktext.'</p>');
-        return;
+        $r->print(Apache::lonhtmlcommon::breadcrumbs('Change Password').
+                  '<div class="LC_landmark" role="main">');
     }
     if ((!defined($caller)) || ($caller eq 'preferences')) {
         $user = $env{'user.name'};
@@ -1285,6 +1285,12 @@ sub passwordchanger {
         if (!defined($caller)) {
             $caller = 'preferences';
         }
+        my ($blocked,$blocktext) =
+            &Apache::loncommon::blocking_status('passwd',$clientip);
+        if ($blocked) {
+            $r->print('<p class="LC_warning">'.$blocktext.'</p>');
+            return;
+        }
     } elsif ($caller eq 'reset_by_email') {
         my %data = &Apache::lonnet::tmpget($mailtoken);
         if (keys(%data) == 0) {
@@ -1301,6 +1307,12 @@ sub passwordchanger {
                 $user = $data{'username'};
                 $domain = $data{'domain'};
                 $currentpass = $data{'temppasswd'};
+                my ($blocked,$blocktext) =
+                    &Apache::loncommon::blocking_status('passwd',$clientip,$user,$domain);
+                if ($blocked) {
+                    $r->print('<p class="LC_warning">'.$blocktext.'</p>');
+                    return;
+                }
             } else {
                 $r->print(
                     '<p class="LC_warning">'
@@ -1360,11 +1372,11 @@ sub passwordchanger {
 	my $jsh=Apache::File->new($include."/londes.js");
 	$r->print(<$jsh>);
     }
-    $r->print(&jscript_send($caller,$extrafields));
+    $r->print(&jscript_send($caller,$domain,$currentauth,$extrafields));
     $r->print(<<ENDFORM);
 $errormessage
 
-<p>
+<p></p>
 <!-- We separate the forms into 'server' and 'client' in order to
      ensure that unencrypted passwords will not be sent out by a
      crappy browser -->
@@ -1377,11 +1389,105 @@ ENDFORM
 }
 
 sub jscript_send {
-    my ($caller,$extrafields) = @_;
+    my ($caller,$domain,$currentauth,$extrafields) = @_;
+    my ($min,$max,$rulestr,$numrules);
+    $min = $Apache::lonnet::passwdmin;
+    my %js_lt = &Apache::lonlocal::texthash(
+              uc => 'New password needs at least one upper case letter',
+              lc => 'New password needs at least one lower case letter',
+              num => 'New password needs at least one number',
+              spec => 'New password needs at least one non-alphanumeric',
+              blank1 => 'Empty Password field',
+              blank2 => 'Empty Confirm Password field',
+              mismatch => 'Contents of Password and Confirm Password fields must match',
+              fail => 'Please fix the following:',
+    );
+    &js_escape(\%js_lt);
+    if ($currentauth eq 'internal:') {
+        if ($domain ne '') {
+            my %passwdconf = &Apache::lonnet::get_passwdconf($domain);
+            if (keys(%passwdconf)) {
+                if ($passwdconf{min}) {
+                    $min = $passwdconf{min};
+                }
+                if ($passwdconf{max}) {
+                    $max = $passwdconf{max};
+                    $js_lt{'long'} = &js_escape(&mt('Maximum password length: [_1]',$max));
+                }
+                if (ref($passwdconf{chars}) eq 'ARRAY') {
+                    if (@{$passwdconf{chars}}) {
+                        $rulestr =  join('","',@{$passwdconf{chars}});
+                        $numrules = scalar(@{$passwdconf{chars}});
+                    }
+                }
+            }
+        }
+    }
+    $js_lt{'short'} = &js_escape(&mt('Minimum password length: [_1]',$min));
+
+    my $passwdcheck = <<"ENDJS";
+        var errors = new Array();
+        var min = parseInt("$min") || 0;
+        var currauth = "$currentauth";
+        if (this.document.client.elements.newpass_1.value == '') {
+            errors.push("$js_lt{'blank1'}");
+        }
+        if (this.document.client.elements.newpass_2.value == '') {
+            errors.push("$js_lt{'blank2'}");
+        }
+        if (errors.length == 0) {
+            if (this.document.client.elements.newpass_1.value !=  this.document.client.elements.newpass_2.value) {
+                errors.push("$js_lt{'mismatch'}");
+            }
+            var posspass = this.document.client.elements.newpass_1.value;
+            if (min > 0) {
+                if (posspass.length < min) {
+                    errors.push("$js_lt{'short'}");
+                }
+            }
+            if (currauth == 'internal:') {
+                var max = parseInt("$max") || 0;
+                if (max > 0) {
+                    if (posspass.length > max) {
+                        errors.push("$js_lt{'long'}");
+                    }
+                }
+                var numrules = parseInt("$numrules") || 0;
+                if (numrules > 0) {
+                    var rules = new Array("$rulestr");
+                    for (var i=0; i<rules.length; i++) {
+                        if (rules[i] == 'uc') {
+                            if (!posspass.match(/[A-Z]/)) {
+                                errors.push("$js_lt{'uc'}");
+                            }
+                        } else if (rules[i] == 'lc') {
+                            if (!posspass.match(/[a-z]/)) {
+                                errors.push("$js_lt{'lc'}");
+                            }
+                        } else if (rules[i] == 'num') {
+                            if (!posspass.match(/\\d/)) {
+                                errors.push("$js_lt{'num'}");
+                            }
+                        } else if (rules[i] == 'spec') {
+                            var pattern = /^[!@#$%^&*()_+\\-=\\[\\]{};':"\\\|,.<a>\\/?]/;
+                            if (!posspass.match(pattern)) {
+                                errors.push("$js_lt{'spec'}");
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        if (errors.length > 0) {
+            alert("$js_lt{'fail'}"+"\\n\\n"+errors.join("\\n"));
+            return;
+        }
+ENDJS
     my $output = qq|
 <script type="text/javascript" language="JavaScript">
-
+// <![CDATA[
     function send() {
+$passwdcheck
         uextkey=this.document.client.elements.ukey_cpass.value;
         lextkey=this.document.client.elements.lkey_cpass.value;
         initkeys();
@@ -1418,6 +1524,7 @@ sub jscript_send {
         this.document.pserver.submit();
     }
 
+// ]]>
 </script>
 |;
 }
@@ -1456,16 +1563,16 @@ sub client_form {
     } else {
         $output .= &Apache::lonhtmlcommon::row_title(
                        '<label for="currentpass">'.$lt{'currentpass'}.'</label>')
-                  .'<input type="password" name="currentpass" size="20"/>'
+                  .'<input type="password" name="currentpass" id="currentpass" size="20" />'
                   .&Apache::lonhtmlcommon::row_closure();
     }
     $output .= &Apache::lonhtmlcommon::row_title(
                    '<label for="newpass_1">'.$lt{'newpass'}.'</label>')
-              .'<input type="password" name="newpass_1" size="20" />'
+              .'<input type="password" name="newpass_1" id="newpass_1" size="20" />'
               .&Apache::lonhtmlcommon::row_closure()
               .&Apache::lonhtmlcommon::row_title(
                    '<label for="newpass_2">'.$lt{'confirmpass'}.'</label>')
-              .'<input type="password" name="newpass_2" size="20" />'
+              .'<input type="password" name="newpass_2" id="newpass_2" size="20" />'
               .&Apache::lonhtmlcommon::row_closure(1)
               .&Apache::lonhtmlcommon::end_pick_box();
     if ($caller eq 'reset_by_email') {
@@ -1480,7 +1587,6 @@ sub client_form {
 <input type="hidden" name="ukey_npass2" value="$hexkey->{'ukey_npass2'}" />
 <input type="hidden" name="lkey_npass2" value="$hexkey->{'lkey_npass2'}" />
 </form>
-</p>
 |;
     return $output;
 }
@@ -1522,14 +1628,8 @@ sub server_form {
 }
 
 sub verify_and_change_password {
-    my ($r,$caller,$mailtoken,$ended) = @_;
-    my ($user,$domain,$homeserver);
-    my ($blocked,$blocktext) =
-        &Apache::loncommon::blocking_status('passwd');
-    if ($blocked) {
-        $r->print('<p class="LC_warning">'.$blocktext.'</p>');
-        return;
-    }
+    my ($r,$caller,$mailtoken,$timelimit,$extrafields,$ended) = @_;
+    my ($user,$domain,$homeserver,$clientip);
     if ($caller eq 'reset_by_email') {
         $user       = $env{'form.uname'};
         $domain     = $env{'form.udom'};
@@ -1538,20 +1638,31 @@ sub verify_and_change_password {
             if ($homeserver eq 'no_host') {
         &passwordchanger($r,"<p>\n<span class='LC_error'>".
                          &mt("Invalid username and/or domain")."</span>\n</p>",
-                         $caller,$mailtoken);
-                return 1;
+                         $caller,$mailtoken,$timelimit,$extrafields);
+                return 'no_host';
             }
         } else {
             &passwordchanger($r,"<p>\n<span class='LC_error'>".
                              &mt("Username and domain were blank")."</span>\n</p>",
-                             $caller,$mailtoken);
-            return 1;
+                             $caller,$mailtoken,$timelimit,$extrafields);
+            return 'missingdata';
         }
     } else {
         $user       = $env{'user.name'};
         $domain     = $env{'user.domain'};
         $homeserver = $env{'user.home'};
     }
+    $clientip = &Apache::lonnet::get_requestor_ip($r);
+    my ($blocked,$blocktext) =
+        &Apache::loncommon::blocking_status('passwd',$clientip,$user,$domain);
+    if ($blocked) {
+        $r->print('<p class="LC_warning">'.$blocktext.'</p>');
+        if ($caller eq 'reset_by_email') {
+            return 'blocked';
+        } else {
+            return;
+        }
+    }
     my $currentauth=&Apache::lonnet::queryauthenticate($user,$domain);
     # Check for authentication types that allow changing of the password.
     if ($currentauth !~ /^(unix|internal):/) {
@@ -1559,8 +1670,8 @@ sub verify_and_change_password {
             &passwordchanger($r,"<p>\n<span class='LC_error'>".
                              &mt("Authentication type for this user can not be changed by this mechanism").
                              "</span>\n</p>",
-                              $caller,$mailtoken);
-            return 1;
+                              $caller,$mailtoken,$timelimit,$extrafields);
+            return 'otherauth';
         } else {
             return;
         }
@@ -1576,8 +1687,12 @@ sub verify_and_change_password {
 	    defined($newpass2)    ){
 	&passwordchanger($r,"<p>\n<span class='LC_error'>".
 			 &mt("One or more password fields were blank").
-                         "</span>\n</p>",$caller,$mailtoken);
-	return;
+                         "</span>\n</p>",$caller,$mailtoken,$timelimit,$extrafields);
+        if ($caller eq 'reset_by_email') {
+            return 'missingdata';
+        } else {
+            return;
+        }
     }
     # Get the keys
     my $lonhost = $r->dir_config('lonHostID');
@@ -1595,7 +1710,11 @@ sub verify_and_change_password {
 </p>
 ENDERROR
         # Probably should log an error here
-        return 1;
+        if ($caller eq 'reset_by_email') {
+            return 'internalerror';
+        } else {
+            return;
+        }
     }
     my ($ckey,$n1key,$n2key)=split(/&/,$tmpinfo);
     #
@@ -1609,31 +1728,39 @@ ENDERROR
             &passwordchanger($r,
                          '<span class="LC_error">'.
                          &mt('Could not verify current authentication.').'  '.
-                         &mt('Please try again.').'</span>',$caller,$mailtoken);
-            return 1;
+                         &mt('Please try again.').'</span>',$caller,$mailtoken,$timelimit,$extrafields);
+            return 'emptydata';
         }
         if ($currentpass ne $data{'temppasswd'}) {
             &passwordchanger($r,
                          '<span class="LC_error">'.
                          &mt('Could not verify current authentication.').'  '.
-                         &mt('Please try again.').'</span>',$caller,$mailtoken);
-            return 1;
+                         &mt('Please try again.').'</span>',$caller,$mailtoken,$timelimit,$extrafields);
+            return 'missingtemp';
         }
     }
     if ($newpass1 ne $newpass2) {
 	&passwordchanger($r,
 			 '<span class="LC_warning">'.
 			 &mt('The new passwords you entered do not match.').'  '.
-			 &mt('Please try again.').'</span>',$caller,$mailtoken);
-	return 1;
+			 &mt('Please try again.').'</span>',$caller,$mailtoken,$timelimit,$extrafields);
+        if ($caller eq 'reset_by_email') {
+            return 'mismatch';
+        } else {
+            return;
+        }
     }
     if ($currentauth eq 'unix:') {
         if (length($newpass1) < 7) {
             &passwordchanger($r,
                              '<span class="LC_warning">'.
                              &mt('Passwords must be a minimum of 7 characters long.').'  '.
-                             &mt('Please try again.').'</span>',$caller,$mailtoken);
-            return 1;
+                             &mt('Please try again.').'</span>',$caller,$mailtoken,$timelimit,$extrafields);
+            if ($caller eq 'reset_by_email') {
+                return 'length';
+            } else {
+                return;
+            }
         }
     } else {
         my $warning = &Apache::loncommon::check_passwd_rules($domain,$newpass1);
@@ -1641,8 +1768,12 @@ ENDERROR
             &passwordchanger($r,'<span class="LC_warning">'.
                             $warning.
                             &mt('Please try again.').'</span>',
-                            $caller,$mailtoken);
-            return 1;
+                            $caller,$mailtoken,$timelimit,$extrafields);
+            if ($caller eq 'reset_by_email') {
+                return 'rules';
+            } else {
+                return;
+            }
         }
     }
     #
@@ -1662,8 +1793,12 @@ ENDERROR
 ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_\`abcdefghijklmnopqrstuvwxyz{|}~
 </pre></span>
 ENDERROR
-        &passwordchanger($r,$errormessage,$caller,$mailtoken);
-        return 1;
+        &passwordchanger($r,$errormessage,$caller,$mailtoken,$timelimit,$extrafields);
+        if ($caller eq 'reset_by_email') {
+            return 'badchars';
+        } else {
+            return;
+        }
     }
     # 
     # Change the password (finally)
@@ -1686,7 +1821,7 @@ ENDERROR
 	# error error: run in circles, scream and shout
         if ($caller eq 'reset_by_email') {
             if (!$result) {
-                return 1;
+                return 'error';
             } else {
                 return $result;
             }
@@ -1948,9 +2083,117 @@ sub author_space_settings {
     my $domain     = $env{'user.domain'};
     my %author_roles = &Apache::lonnet::get_my_roles($user,$domain,'userroles','',['au','ca','aa']);
     if (keys(%author_roles) > 0) {
-            $r->print(Apache::loncommon::start_page('Authoring Space Settings'));
-            $r->print(Apache::lonhtmlcommon::breadcrumbs('Authoring Space Settings'));
-            my %userenv = &Apache::lonnet::get('environment',['nocodemirror']);
+        my ($showdomdefs,$js,$args,@items);
+        my $returnurl = &HTML::Entities::encode($env{'form.returnurl'},'"<>&\'');
+        if (&expanded_authoring_settings()) {
+            @items = ('nocodemirror');
+            if (&daxe_permitted(\%author_roles)) {
+                push(@items,'daxecollapse');
+            }
+            push(@items,('copyright','sourceavail'));
+            $showdomdefs = 1;
+            $js = &toggle_options_js();
+            my $onload;
+            foreach my $item (@items) {
+                $onload .= "javascript:toggleOptions(document.prefs,'$item','user_$item');"
+            }
+            $args = { 'add_entries' => { 'onload' => $onload } };
+        }
+        $r->print(Apache::loncommon::start_page('Authoring Space Settings',$js,$args));
+        $r->print(Apache::lonhtmlcommon::breadcrumbs('Authoring Space Settings'));
+        if ($showdomdefs) {
+            my %userenv = &Apache::lonnet::get('environment',\@items);
+            my %domdefs = &Apache::lonnet::get_domain_defaults($domain);
+            my %staticdefaults = (
+                            'nocodemirror'  => '0',
+                            'daxecollapse'  => '0',
+                            'copyright'     => 'default',
+                            'sourceavail'   => 'closed',
+            );
+            my %lt = &authoring_settings_text();
+            my %titles = &authoring_settings_titles();
+            $r->print("<h3>$lt{'auss'}</h3>".
+                      '<form name="prefs" action="/adm/preferences" method="post">'."\n".
+                      '<input type="submit" value="'.$lt{'save'}.'" /><br /><hr />'."\n".
+                      '<input type="hidden" name="returnurl" value="'.$returnurl.'" />'."\n".
+                      '<input type="hidden" name="action" value="change_authoring_settings" />'."\n");
+            foreach my $item (@items) {
+                my ($domdef,$checkeddom,$checkeduser,$domdefdisplay,$divsty,$userelem);
+                $checkeddom = ' checked="checked"';
+                $divsty = 'display:none';
+                if (exists($domdefs{$item})) {
+                    $domdef = $domdefs{$item};
+                } else {
+                    $domdef = $staticdefaults{$item};
+                }
+                if ($item eq 'copyright') {
+                    $domdefdisplay = &Apache::loncommon::copyrightdescription($domdef);
+                    $userelem = &selectbox('userchoice_'.$item,$userenv{$item},'',
+                                           \&Apache::loncommon::copyrightdescription,
+                                           (grep !/^priv|custom$/,(&Apache::loncommon::copyrightids)));
+                } elsif ($item eq 'sourceavail') {
+                    $domdefdisplay = &Apache::loncommon::source_copyrightdescription($domdef);
+                    $userelem = &selectbox('userchoice_'.$item,$userenv{$item},'',
+                                           \&Apache::loncommon::source_copyrightdescription,
+                                           (&Apache::loncommon::source_copyrightids));
+                } elsif (($item eq 'nocodemirror') || ($item eq 'daxecollapse')) {
+                    if ($domdef) {
+                        if ($item eq 'daxecollapse') {
+                            $domdefdisplay = $lt{'coll'};
+                        } else {
+                            $domdefdisplay = $lt{'yes'};
+                        }
+                    } else {
+                        if ($item eq 'daxecollapse') {
+                            $domdefdisplay = $lt{'expa'};
+                        } else {
+                            $domdefdisplay = $lt{'no'};
+                        }
+                    }
+                    my (%checked,%text);
+                    $checked{'no'} = ' checked="checked"';
+                    if ($userenv{$item} eq 'yes') {
+                        $checked{'yes'} = $checked{'no'};
+                        $checked{'no'} = '';
+                    }
+                    if ($item eq 'daxecollapse') {
+                        %text = (
+                                 yes => $lt{'coll'},
+                                 no  => $lt{'expa'},
+                               );
+                    } else {
+                        %text = (
+                                 yes => $lt{'yes'},
+                                 no  => $lt{'no'},
+                               );
+                    }
+                    $userelem = '<span class="LC_nobreak">';
+                    foreach my $choice ('yes','no') {
+                        $userelem .= '<label><input type="radio" name="userchoice_'.$item.'" value="'.$choice.'"'.
+                                     $checked{$choice}.' />'.$text{$choice}.'</label>&nbsp;&nbsp;&nbsp;';
+                    }
+                    $userelem .= '</span>';
+                }
+                if ($userenv{$item} ne '') {
+                    $checkeduser = $checkeddom;
+                    $checkeddom = '';
+                    $divsty = 'display:inline-block';
+                }
+                $r->print(<<"END");
+<h4><span class="LC_nobreak">$titles{$item}</span></h4>
+<p class="LC_nobreak">$lt{'curd'}: <span style="font-style:italic">$domdefdisplay</span></p>
+<p class="LC_nobreak">
+<label><input type="radio" name="$item" value="dom" onclick="toggleOptions(this.form,'$item','user_$item');"$checkeddom />$lt{'used'}</label>&nbsp;&nbsp;&nbsp;
+<label><input type="radio" name="$item" value="user" onclick="toggleOptions(this.form,'$item','user_$item');"$checkeduser />$lt{'usyo'}</label></p>
+<fieldset id="user_$item" style="$divsty">
+<legend style="font-weight:normal;font-style:italic;">$lt{'ousv'}</legend>
+$userelem
+</fieldset><br /><hr />
+END
+            }
+            $r->print('<input type="submit" value="'.$lt{'save'}.'" />'.
+                      '</form>'."\n");
+        } else {
             my $constchecked='';
             if ($env{'environment.nocodemirror'}) {
                $constchecked=' checked="checked"';
@@ -1958,7 +2201,6 @@ sub author_space_settings {
             my $text=&mt('By default, CodeMirror an editor with advanced functionality for editing code is activated for authors.');
             my $cmoff=&mt('Deactivate CodeMirror. This can improve performance on slow computers and accessibility.');
             my $change=&mt('Save');
-            my $returnurl = &HTML::Entities::encode($env{'form.returnurl'},'"<>&\'');
             $r->print(<<ENDSCREEN);
         <form name="prefs" action="/adm/preferences" method="post">
         <input type="hidden" name="returnurl" value="$returnurl" />
@@ -1968,6 +2210,7 @@ sub author_space_settings {
         <input type="submit" value="$change" />
         </form>
 ENDSCREEN
+        }
     }
 }
 
@@ -1977,6 +2220,8 @@ sub change_authoring_settings {
     my $domain     = $env{'user.domain'};
     my %author_roles = &Apache::lonnet::get_my_roles($user,$domain,'userroles','',['au','ca','aa']);
     if (keys(%author_roles) > 0) {
+        my $message;
+        if (!&expanded_authoring_settings()) {
             my %ausettings=('environment.nocodemirror' => '');
             if ($env{'form.cmoff'}) { $ausettings{'environment.nocodemirror'}='yes'; }
             &Apache::lonnet::put('environment',\%ausettings);
@@ -1987,25 +2232,195 @@ sub change_authoring_settings {
             } else {
                 $status=&mt('off');
             }
-            my $message=&Apache::lonhtmlcommon::confirm_success(&mt('Set [_1] to [_2]','<i>'.&mt('Deactivate CodeMirror in Authoring Space').'</i>','<tt>'.$status.'</tt>'));
+            $message=&Apache::lonhtmlcommon::confirm_success(&mt('Set [_1] to [_2]','<i>'.&mt('Deactivate CodeMirror in Authoring Space').'</i>','<tt>'.$status.'</tt>'));
             $message=&Apache::loncommon::confirmwrapper($message);
-            if ($env{'form.returnurl'}) {
-                &do_redirect($r,$env{'form.returnurl'},$message);
+        } else {
+            my @items = ('nocodemirror');
+            if (&daxe_permitted(\%author_roles)) {
+                push(@items,'daxecollapse');
+            }
+            push(@items,('copyright','sourceavail'));
+            my %oldsettings = &Apache::lonnet::get('environment',\@items);
+            my %domdefs = &Apache::lonnet::get_domain_defaults($domain);
+            my %lt = &authoring_settings_text();
+            my %titles = &authoring_settings_titles();
+            my ($result,%newsettings,%changes,@delete,@unchanged,@delerrors,@adderrors);
+            foreach my $item (@items) {
+                if ($env{'form.'.$item} eq 'dom') {
+                    if ($oldsettings{$item} eq '') {
+                        push(@unchanged,$item);
+                    } else {
+                        push(@delete,$item);
+                    }
+                } elsif ($env{'form.'.$item} eq 'user') {
+                    my $newval = $env{'form.userchoice_'.$item};
+                    my @possibles;
+                    if (($item eq 'nocodemirror') || ($item eq 'daxecollapse')) {
+                        if ($newval =~ /^yes|no$/) {
+                            $newsettings{$item} = $newval;
+                        }
+                    } elsif ($item eq 'copyright') {
+                        @possibles = (grep !/^priv|custom$/,(&Apache::loncommon::copyrightids));
+                        if (grep(/^\Q$newval\E$/,@possibles)) {
+                            $newsettings{$item} = $newval;
+                        }
+                    } elsif ($item eq 'sourceavail') {
+                        @possibles = (&Apache::loncommon::source_copyrightids);
+                        if (grep(/^\Q$newval\E$/,@possibles)) {
+                            $newsettings{$item} = $newval;
+                        }
+                    }
+                    if ($oldsettings{$item} eq $newsettings{$item}) {
+                        push(@unchanged,$item);
+                    } else {
+                        $changes{$item} = $newsettings{$item};
+                    }
+                }
+            }
+            if (@delete) {
+                if (&Apache::lonnet::del('environment',\@delete) eq 'ok') {
+                    foreach my $key (@delete) {
+                        &Apache::lonnet::delenv('environment.'.$key);
+                    }
+                } else {
+                    @delerrors = @delete;
+                }
+            }
+            if (keys(%changes)) {
+                if (&Apache::lonnet::put('environment',\%changes) eq 'ok') {
+                    my %newenvhash;
+                    map {$newenvhash{'environment.'.$_} = $changes{$_}; } (keys(%changes));
+                    &Apache::lonnet::appenv(\%newenvhash);
+                } else {
+                    foreach my $item (@items) {
+                        if (exists($changes{$item})) {
+                            push(@adderrors,$item);
+                        }
+                    }
+                }
+            }
+            if (@adderrors) {
+                $result = &mt('An error occurred when saving user-specific settings for').': '.
+                          join(', ', map { $titles{$_} } @adderrors);
+                $message = &Apache::loncommon::confirmwrapper(&Apache::lonhtmlcommon::confirm_success($result,1));
+            } elsif (keys(%changes)) {
+                $result = &mt('User-specific settings saved:').'<ul>';
+                foreach my $item (@items) {
+                    next unless (exists($changes{$item}));
+                    my $value = $changes{$item};
+                    if ($item eq 'nocodemirror') {
+                        $value = $lt{$changes{$item}};
+                    } elsif ($item eq 'daxecollapse') {
+                        if ($value eq 'yes') {
+                            $value = $lt{'coll'};
+                        } else {
+                            $value = $lt{'expa'};
+                        }
+                    } elsif ($item eq 'copyright') {
+                        $value = &Apache::loncommon::copyrightdescription($changes{$item});
+                    } elsif ($item eq 'sourceavail') {
+                        $value = &Apache::loncommon::source_copyrightdescription($changes{$item});
+                    }
+                    $result .= '<li>'.
+                               &mt('[_1] set to [_2]',
+                                   $titles{$item},
+                                   '<span style="font-style:italic">'.$value.'</span>').
+                               '</li>';
+                }
+                $result .= '</ul>';
+                $message = &Apache::loncommon::confirmwrapper(&Apache::lonhtmlcommon::confirm_success($result));
+            }
+            if (@delerrors) {
+                $result = &mt('An error occurred when deleting user-specific settings for').':<ul><li>'.
+                          join('</li><li>', map { $titles{$_} } @delerrors).'</li></ul>';
+                $message .= &Apache::loncommon::confirmwrapper(&Apache::lonhtmlcommon::confirm_success($result,1));
+            } elsif (@delete) {
+                $result = &mt('Set use of domain default for').':<ul><li>'.
+                          join('</li><li>', map { $titles{$_} } @delete).'</li></ul>';
+                $message .= &Apache::loncommon::confirmwrapper(&Apache::lonhtmlcommon::confirm_success($result));
+            }
+            if (@unchanged) {
+                $result = &mt('No changes made for').':<ul><li>'.
+                          join('</li><li>', map { $titles{$_} } @unchanged).'</li></ul>';
+                $message .= &Apache::loncommon::confirmwrapper(&Apache::lonhtmlcommon::confirm_success($result));
+            }
+        }
+        if ($env{'form.returnurl'}) {
+            &do_redirect($r,$env{'form.returnurl'},$message);
+        } else {
+            &print_main_menu($r,$message);
+        }
+    }
+}
+
+sub authoring_settings_text {
+    return &Apache::lonlocal::texthash(
+               'auss' => 'Authoring Space Settings',
+               'used' => 'Use domain default',
+               'usyo' => 'Use your own user-specific setting',
+               'curd' => 'Current domain default is',
+               'ousv' => 'Own user-specific value',
+               'save' => 'Save',
+               'yes'  => 'Deactivated',
+               'no'   => 'Activated',
+               'expa' => 'Start Expanded',
+               'coll' => 'Start Collapsed',
+     );
+}
+
+sub authoring_settings_titles {
+    return &Apache::lonlocal::texthash(
+               'nocodemirror' => 'CodeMirror for EditXML editor',
+               'daxecollapse' => 'Daxe editor: collapsible standard LON-CAPA menus',
+               'copyright'    => 'Default Copyright/Distribution in new metadata file',
+               'sourceavail'  => 'Default Source Available in new metadata file',
+    );
+}
+
+sub expanded_authoring_settings {
+    my $reqdmajor = 2;
+    my $reqdminor = 12;
+    my $loncaparev = &Apache::lonnet::get_server_loncaparev($env{'user.domain'},$env{'user.home'});
+    my ($major,$minor) = ($loncaparev =~ /^\'?(\d+)\.(\d+)\.[\w.\-]+\'?$/);
+    unless (($major eq '' && $minor eq '') ||
+            ($reqdmajor > $major) || (($reqdmajor == $major) && ($reqdminor > $minor))) {
+        return 1;
+    }
+    return;
+}
+
+sub daxe_permitted {
+    my ($aurolesref) = @_;
+    my $hasdaxe;
+    if (ref($aurolesref) eq 'HASH') {
+        my %editors;
+        foreach my $key (keys(%{$aurolesref})) {
+            if ($key =~ /^:$LONCAPA::match_domain:au$/) {
+                if (exists($env{'environment.editors'})) {
+                    if (grep(/^daxe$/,split(/,/,$env{'environment.editors'}))) {
+                        $hasdaxe = 1;
+                        last;
+                    }
+                }
             } else {
-                &print_main_menu($r,$message);
+                my ($auname,$audom) = ($key =~ /^($LONCAPA::match_username):($LONCAPA::match_domain):(ca|aa)$/);
+                if (exists($env{"environment.internal.editors./$audom/$auname"})) {
+                    if (grep(/^daxe$/,split(/,/,$env{"environment.internal.editors./$audom/$auname"}))) {
+                        $hasdaxe = 1;
+                        last;
+                    }
+                }
             }
+        }
     }
+    return $hasdaxe;
 }
 
 sub lockednameschanger {
     my $r = shift;
-    &Apache::lonhtmlcommon::add_breadcrumb(
-            {   href => '/adm/preferences?action=changelockednames',
-                text => 'Automatic name changes'});
-    $r->print(Apache::loncommon::start_page('Automatic name changes'));
-    $r->print(Apache::lonhtmlcommon::breadcrumbs('Allow/disallow name updates'));
     my %userenv = &Apache::lonnet::get('environment',['lockedname']);
     my $lockedname='';
+    my $ended;
     if (&can_toggle_namelocking()) {
         if ($userenv{'lockedname'}) {
             $lockedname = ' checked="checked"';
@@ -2023,6 +2438,11 @@ sub lockednameschanger {
             }
         }
         if (keys(%updateable)) {
+            &Apache::lonhtmlcommon::add_breadcrumb(
+                {   href => '/adm/preferences?action=changelockednames',
+                    text => 'Automatic name changes'});
+            $r->print(Apache::loncommon::start_page('Automatic name changes'));
+            $r->print(Apache::lonhtmlcommon::breadcrumbs('Allow/disallow name updates'));
             my %longnames = &Apache::lonlocal::texthash (
                                 firstname  => 'First Name',
                                 middlename => 'Middle Name',
@@ -2049,11 +2469,14 @@ ENDSCREEN
         } else {
             my $message = &mt('Based on your institutional affiliation no name information is automatically updated for your LON-CAPA account.');
             &print_main_menu($r,$message);
+            $ended = 1;
         }
     } else {
         my $message = &mt('You are not permitted to set a user preference for automatic name updates for your LON-CAPA account.');
         &print_main_menu($r,$message);
+        $ended = 1;
     }
+    return $ended;
 }
 
 sub verify_and_change_lockednames {
@@ -2084,6 +2507,90 @@ sub verify_and_change_lockednames {
     &print_main_menu($r,$message);
 }
 
+sub timezonechanger {
+    my $r = shift;
+    my $uname = $env{'user.name'};
+    my $udom = $env{'user.domain'};
+    if (&Apache::lonnet::usertools_access($uname,$udom,'timezone')) {
+        my $js = &toggle_options_js();
+        my %loaditems = (
+                           onload => "javascript:toggleOptions(document.prefs,'settimezone','LC_timezone_selector');",
+                        );
+        my $args = { 'add_entries' => \%loaditems };
+        &Apache::lonhtmlcommon::add_breadcrumb(
+                {   href => '/adm/preferences?action=',
+                    text => 'Set Your Time Zone'});
+        $r->print(Apache::loncommon::start_page('Set Your Time Zone',$js,$args));
+        $r->print(Apache::lonhtmlcommon::breadcrumbs('Set Your Time Zone'));
+        my %userenv = &Apache::lonnet::get('environment',['timezone']);
+        my $timezone = $userenv{'timezone'};
+        my %lt = &Apache::lonlocal::texthash(
+                     lctz  => 'Use Time Zone set by LON-CAPA',
+                     owntz => 'Use Time Zone set by you',
+                     save  => 'Save',
+        );
+        my (%checked,$tzsty);
+        if ($userenv{'timezone'} ne '') {
+            $checked{'owntz'} = ' checked="checked"';
+            $tzsty = 'inline-block';
+        } else {
+            $checked{'lctz'} = ' checked="checked"';
+            $tzsty = 'none';
+        }
+        my $onclick = ' onclick="javascript:toggleOptions(this.form,'."'settimezone','LC_timezone_selector'".');"';
+        my $selector = &Apache::loncommon::select_timezone('timezone',$timezone,undef,1);
+        $r->print(<<"END");
+<form name="prefs" action="/adm/preferences" method="post">
+<input type="hidden" name="action" value="verify_and_change_timezone" />
+<span class="LC_nobreak">
+<label><input type="radio" name="settimezone" value="0"$checked{'lctz'}$onclick />
+$lt{'lctz'}</label>&nbsp;&nbsp;&nbsp;&nbsp;
+<label><input type="radio" name="settimezone" value="1"$checked{'owntz'}$onclick />
+$lt{'owntz'}</label><div style="display:$tzsty" id="LC_timezone_selector">&nbsp;
+$selector
+</div></span><br /><br />
+<input type="submit" value="$lt{'save'}" />
+</form>
+END
+    }
+    return;
+}
+
+sub verify_and_change_timezone {
+    my $r = shift;
+    my $currtimezone = $env{'environment.timezone'};
+    my $newtimezone;
+    if ($env{'form.settimezone'}) {
+        $newtimezone = $env{'form.timezone'};
+        if (DateTime::TimeZone->is_valid_name($env{'form.timezone'})) {
+            $newtimezone = $env{'form.timezone'};
+        }
+    }
+    my $message='';
+    if ($newtimezone) {
+        if ($newtimezone eq $currtimezone) {
+            $message = &mt('Time Zone settings unchanged');
+        } else {
+            &Apache::lonnet::put('environment',{'timezone' => $newtimezone});
+            &Apache::lonnet::appenv({'environment.timezone' => $newtimezone});
+            $message=&Apache::lonhtmlcommon::confirm_success(
+                &mt('Set [_1] to [_2]',
+                    '<i>'.&mt('Your Time Zone').'</i>',
+                    '<tt>"'.$newtimezone.'"</tt>.')).
+               '<br />';
+        }
+    } elsif ($currtimezone) {
+        &Apache::lonnet::del('environment',['timezone']);
+        &Apache::lonnet::delenv('environment.timezone');
+        $message=&Apache::lonhtmlcommon::confirm_success(&mt('Time Zone now set by LON-CAPA'));
+    } else {
+        $message = &mt('Time Zone settings unchanged');
+    }
+    $message=&Apache::loncommon::confirmwrapper($message);
+    &print_main_menu($r,$message);
+    return;
+}
+
 sub print_main_menu {
     my ($r, $message) = @_;
     # Determine current authentication method
@@ -2098,6 +2605,11 @@ my %permissions;
 if (&Apache::lonnet::usertools_access($user,$domain,'aboutme')) {
     $permissions{'aboutme'} = 'F';
 }
+if (&Apache::lonnet::usertools_access($user,$domain,'timezone')) {
+    $permissions{'timezone'} = 'F';
+}
+my %author_roles = &Apache::lonnet::get_my_roles($user,$domain,'userroles','',['au']);
+my %author_coauthor_roles = &Apache::lonnet::get_my_roles($user,$domain,'userroles','',['au','ca','aa']);
 my @menu=
     ({	categorytitle=>'Personal Data',
 	items =>[
@@ -2106,6 +2618,7 @@ my @menu=
 		permission => $permissions{'aboutme'},
 		#help => 'Prefs_About_Me',
 		icon => 'system-users.png',
+                alttext => 'About Me Icon',
 		linktitle => 'Edit information about yourself that should be displayed on your public profile.'
 	    },
 	    {	linktext => 'Screen Name',
@@ -2113,6 +2626,7 @@ my @menu=
 		permission => 'F',
 		#help => 'Prefs_Screen_Name_Nickname',
 		icon => 'preferences-desktop-font.png',
+                alttext => 'Nickname Icon',
 		linktitle => 'Change the name that is displayed in your posts.'
 	    },
 		]
@@ -2124,6 +2638,7 @@ my @menu=
 		permission => 'F',
 		#help => 'Prefs_Language',
 		icon => 'preferences-desktop-locale.png',
+                alttext => 'Language Icon',
 		linktitle => 'Choose the default language for this user.'
 	    },
 	    {	linktext => $role.' Page',
@@ -2131,6 +2646,7 @@ my @menu=
 		permission => 'F',
 		#help => '',
 		icon => 'role_hotlist.png',
+                alttext => 'Switch Role Icon',
 		linktitle => 'Configure the roles hotlist.'
 	    },
 	    {	linktext => 'Math display settings',
@@ -2138,8 +2654,18 @@ my @menu=
 		permission => 'F',
 		#help => '',
 		icon => 'dismath.png',
+                alttext => 'Math Icon',
 		linktitle => 'Change how math is displayed.'
 	    },
+            {
+                linktext => 'Time Zone',
+                url => '/adm/preferences?action=changetimezone',
+                permission => $permissions{'timezone'},
+                #help => '',
+                icon => 'timezone.png',
+                alttext => 'Clock Icon',
+                linktitle => 'Set your time zone.',
+             }
 		]
     },
     {	categorytitle=>'Page Display Settings',
@@ -2149,6 +2675,7 @@ my @menu=
 		permission => 'F',
 		#help => 'Change_Colors',
 		icon => 'preferences-desktop-theme.png',
+                alttext => 'Colors Icon',
 		linktitle => 'Change LON-CAPA default colors.'
 	    },
             {   linktext => 'Menu Display',
@@ -2156,6 +2683,7 @@ my @menu=
                 permission => 'F',
                 #help => '',
                 icon => 'preferences-system-windows.png',
+                alttext => 'Menus Icon',
                 linktitle => 'Change whether the menus are displayed with icons or icons and text.'
             }
 		]
@@ -2167,6 +2695,7 @@ my @menu=
 		permission => 'F',
 		#help => 'Prefs_Messages',
 		icon => 'mail-reply-all.png',
+                alttext => 'Notifications Icon',
 		linktitle => 'Change messageforwarding or notifications settings.'
 	    },
 	    {	linktext => 'Discussion Display',
@@ -2174,10 +2703,40 @@ my @menu=
 		permission => 'F',
 		#help => 'Change_Discussion_Display',
 		icon => 'chat.png',
+                alttext => 'Discussions Icon', 
 		linktitle => 'Set display preferences for discussion posts for both discussion boards and individual resources in all your courses.'
 	    },
 		]
     },
+    );
+if (keys(%author_coauthor_roles) > 0) {
+    push(@menu,
+    {    categorytitle=>'Authoring Settings',
+         items => [
+             {
+                  linktext => 'Authoring Space Configuration',
+                  url => '/adm/preferences?action=authorsettings',
+                  permission => 'F',
+                  icon => 'codemirror.png',
+                  alttext => 'Coding Icon',
+                  linktitle => 'Settings for your authoring space.',
+             },
+                  ]
+    },
+    );
+    if (keys(%author_roles) > 0) {
+        push(@{ $menu[4]->{items} }, {
+        linktext => 'Restrict Domain Coordinator Access',
+        url => '/adm/preferences?action=changedomcoord',
+        permission => 'F',
+        #help => '',
+        icon => 'system-lock-screen.png',
+        alttext => 'Lock Icon',
+        linktitle => 'Restrict domain coordinator access.',
+        });
+    }
+}
+push(@menu,
     {	categorytitle=>'Other',
 	items =>[
 	    {	linktext => 'Register Response Devices (&quot;Clickers&quot;)',
@@ -2185,19 +2744,21 @@ my @menu=
 		permission => 'F',
 		#help => '',
 		icon => 'network-workgroup.png',
+                alttext => 'Clicker Icon',
 		linktitle => 'Register your clicker.'
 	    },
 		]
     },
-    );
+);
 
     if ($currentauth =~ /^(unix|internal):/) {
-push(@{ $menu[0]->{items} }, {
+        push(@{ $menu[0]->{items} }, {
 	linktext => 'Password',
 	url => '/adm/preferences?action=changepass',
 	permission => 'F',
 	#help => 'Change_Password',
 	icon => 'emblem-readonly.png',
+        alttext => 'Secure Icon', 
 	linktitle => 'Change your password.',
 	});
     }
@@ -2209,62 +2770,42 @@ push(@{ $menu[0]->{items} }, {
         permission => 'F',
         #help => '',
         icon => 'system-lock-screen.png',
+        alttext => 'Screen Lock Icon',
         linktitle => 'Allow/disallow propagation of name changes from institutional directory service',
         });
     }
 
-    my %author_roles = &Apache::lonnet::get_my_roles($user,$domain,'userroles','',['au']);
-    if (keys(%author_roles) > 0) {
-push(@{ $menu[4]->{items} }, {
-	linktext => 'Restrict Domain Coordinator Access',
-	url => '/adm/preferences?action=changedomcoord',
-	permission => 'F',
-	#help => '',
-	icon => 'system-lock-screen.png',
-	linktitle => 'Restrict domain coordinator access.',
-	});
-    }
-
     if (&Apache::lonnet::allowed('whn',$env{'request.course.id'})
 	|| &Apache::lonnet::allowed('whn',$env{'request.course.id'}.'/'
 				    .$env{'request.course.sec'})) {
-push(@{ $menu[4]->{items} }, {
+push(@{ $menu[-1]->{items} }, {
 	linktext => 'Course Initialization',
 	url => '/adm/preferences?action=changecourseinit',
 	permission => 'F',
 	#help => '',
 	icon => 'course_ini.png',
+        alttext => 'Course Launch Icon',
 	linktitle => 'Set the default page to be displayed when you select a course role.',
 	});
 
     }
 
-    my %author_coauthor_roles = &Apache::lonnet::get_my_roles($user,$domain,'userroles','',['au','ca','aa']);
-    if (keys(%author_coauthor_roles) > 0) {
-        push(@{ $menu[4]->{items} }, {
-            linktext => 'Authoring Space Configuration',
-            url => '/adm/preferences?action=authorsettings',
-            permission => 'F',
-            icon => 'codemirror.png',
-            linktitle => 'Settings for your authoring space.',
-        });
-    }
-
     if (&can_toggle_debug()) {
-push(@{ $menu[4]->{items} }, {
+push(@{ $menu[-1]->{items} }, {
 	linktext => 'Toggle Debug Messages (Currently '.($env{'user.debug'} ? 'on)' : 'off)'),
 	url => '/adm/preferences?action=debugtoggle',
 	permission => 'F',
 	#help => '',
 	icon => 'blog.png',
+        alttext => 'Debugging Icon', 
 	linktitle => 'Toggle Debug Messages.',
 	});
     }
 
     $r->print(&Apache::loncommon::start_page('My Space'));
     $r->print(Apache::lonhtmlcommon::breadcrumbs('Change Preferences'));
-    $r->print($message);
-    $r->print(Apache::lonhtmlcommon::generate_menu(@menu));
+    $r->print('<div class="LC_landmark" role="main">'."\n".$message);
+    $r->print(Apache::lonhtmlcommon::generate_menu(@menu)."\n".'</div>');
     $r->print(Apache::loncommon::end_page());
 }
 
@@ -2317,8 +2858,9 @@ sub handler {
         $ended = 1;
     }elsif($env{'form.action'} eq 'changepass'){
         &passwordchanger($r);
+        $r->print('</div>');
     }elsif($env{'form.action'} eq 'verify_and_change_pass'){
-        &verify_and_change_password($r,'preferences','',\$ended);
+        &verify_and_change_password($r,'preferences','','','',\$ended);
     }elsif($env{'form.action'} eq 'changescreenname'){
         &screennamechanger($r);
     }elsif($env{'form.action'} eq 'verify_and_change_screenname'){
@@ -2394,10 +2936,14 @@ sub handler {
 	&print_main_menu($r);
         $ended = 1;
     } elsif ($env{'form.action'} eq 'changelockednames') {
-        &lockednameschanger($r);
+        $ended = &lockednameschanger($r);
     } elsif ($env{'form.action'} eq 'verify_and_change_lockednames') {
         &verify_and_change_lockednames($r);
         $ended = 1;
+    } elsif ($env{'form.action'} eq 'changetimezone') {
+        &timezonechanger($r);
+    } elsif ($env{'form.action'} eq 'verify_and_change_timezone') {
+        &verify_and_change_timezone($r);
     }
 
     # Properly end the HTML page of all preference pages
@@ -2479,13 +3025,66 @@ sub updateable_userinfo {
 sub do_redirect {
     my ($r,$url,$msg) = @_;
     $r->print(
-        &Apache::loncommon::start_page('Switching Server ...',undef,
-                                       {'redirect'       => [0.5,$url]}).
+        &Apache::loncommon::start_page('Loading ...',undef,
+                                       {'redirect'       => [2,$url]}).
         '<div style="padding:0;clear:both;margin:0;border:0"></div>'."\n".
         "$msg\n".
         &Apache::loncommon::end_page());
     return;
 }
 
+sub toggle_options_js {
+    return <<"ENDSCRIPT";
+<script type="text/javascript">
+// <![CDATA[
+function toggleOptions(form,radioname,divid) {
+    var num = form.elements[radioname].length;
+    if (num) {
+        var setvis = '';
+        var onvalue = 'user';
+        if (radioname == 'settimezone') {
+            onvalue = '1';
+        }
+        for (var i=0; i<num; i++) {
+            if (form.elements[radioname][i].checked) {
+                if (form.elements[radioname][i].value == onvalue) {
+                    if (document.getElementById(divid)) {
+                        document.getElementById(divid).style.display = 'inline-block';
+                    }
+                    setvis = 1;
+                }
+                break;
+            }
+        }
+        if (!setvis) {
+            if (document.getElementById(divid)) {
+                document.getElementById(divid).style.display = 'none';
+            }
+        }
+    }
+    return;
+}
+// ]]>
+</script>
+ENDSCRIPT
+}
+
+sub selectbox {
+    my ($name,$value,$readonly,$functionref,@idlist)=@_;
+    my $selout = '<select name="'.$name.'">';
+    foreach my $id (@idlist) {
+        $selout.='<option value="'.$id.'"';
+        if ($id eq $value) {
+            $selout.=' selected="selected"';
+        }
+        if ($readonly) {
+            $selout .= ' disabled="disabled"';
+        }
+        $selout.='>'.&{$functionref}($id).'</option>';
+    }
+    $selout.='</select>';
+    return $selout;
+}
+
 1;
 __END__