You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@vcl.apache.org by jf...@apache.org on 2014/09/11 18:01:49 UTC

svn commit: r1624325 [9/13] - in /vcl/trunk/web: ./ .ht-inc/ .ht-inc/authmethods/ css/ js/ js/resources/

Modified: vcl/trunk/web/.ht-inc/statistics.php
URL: http://svn.apache.org/viewvc/vcl/trunk/web/.ht-inc/statistics.php?rev=1624325&r1=1624324&r2=1624325&view=diff
==============================================================================
--- vcl/trunk/web/.ht-inc/statistics.php (original)
+++ vcl/trunk/web/.ht-inc/statistics.php Thu Sep 11 16:01:48 2014
@@ -267,7 +267,7 @@ function viewStatistics() {
 	$nows = 0;
 	$futures = 0;
 	$notavailable = 0;
-	$loadtimes = array("2less" => 0, "2more" => 0);
+	$loadtimes = array("2less" => 0, "2to6" => 0, "6to8" => 0, "8more" => 0);
 	$ending = array("deleted" => 0,
 	                "released" => 0,
 	                "failed" => 0,
@@ -280,20 +280,29 @@ function viewStatistics() {
 	$imageusers = array();
 	$imagehours = array();
 	$imageload2less = array();
-	$imageload2more = array();
+	$imageload2to6 = array();
+	$imageload6to8 = array();
+	$imageload8more = array();
 	$imagefails = array();
 	$lengths = array("30min" => 0,
 	                 "1hour" => 0,
 	                 "2hours" => 0,
 	                 "4hours" => 0,
-	                 "4hrsplus" => 0);
+	                 "6hours" => 0,
+	                 "8hours" => 0,
+	                 "10hours" => 0,
+	                 "10hrsplus" => 0);
 	$totalhours = 0;
 	$osusers = array();
 	while($row = mysql_fetch_assoc($qh)) {
 		if(! array_key_exists($row["prettyname"], $imageload2less))
 			$imageload2less[$row["prettyname"]] = 0;
-		if(! array_key_exists($row["prettyname"], $imageload2more))
-			$imageload2more[$row["prettyname"]] = 0;
+		if(! array_key_exists($row["prettyname"], $imageload2to6))
+			$imageload2to6[$row["prettyname"]] = 0;
+		if(! array_key_exists($row["prettyname"], $imageload6to8))
+			$imageload6to8[$row["prettyname"]] = 0;
+		if(! array_key_exists($row["prettyname"], $imageload8more))
+			$imageload8more[$row["prettyname"]] = 0;
 
 		# notavailable
 		if($row["wasavailable"] == 0) {
@@ -303,15 +312,22 @@ function viewStatistics() {
 			$totalreservations++;
 
 			# load times
-			if($row['loadtime'] < 120) {
+			if($row['loadtime'] <= 120) {
 				$loadtimes['2less']++;
 				# imageload2less
 				$imageload2less[$row['prettyname']]++;
 			}
+			elseif( ($row['loadtime'] > 120) && ($row['loadtime'] <= 360) ) {
+				$loadtimes['2to6']++;
+				$imageload2to6[$row['prettyname']]++;
+			}
+			elseif( ($row['loadtime'] > 360) && ($row['loadtime'] <= 480) ) {
+				$loadtimes['6to8']++;
+				$imageload6to8[$row['prettyname']]++;
+			}
 			else {
-				$loadtimes['2more']++;
-				# imageload2more
-				$imageload2more[$row['prettyname']]++;
+				$loadtimes['8more']++;
+				$imageload8more[$row['prettyname']]++;
 			}
 		}
 
@@ -349,8 +365,14 @@ function viewStatistics() {
 			$lengths["2hours"]++;
 		elseif($length <= 14400)
 			$lengths["4hours"]++;
+		elseif($length <= 21600)
+			$lengths["6hours"]++;
+		elseif($length <= 28800)
+			$lengths["8hours"]++;
+		elseif($length <= 36000)
+			$lengths["10hours"]++;
 		else
-			$lengths["4hrsplus"]++;
+			$lengths["10hrsplus"]++;
 
 		# imagehours
 		if(! array_key_exists($row["prettyname"], $imagehours))
@@ -397,9 +419,14 @@ function viewStatistics() {
 	print _("    <TH align=right>Load times &lt; 2 minutes:</TH>\n");
 	print "    <TD>{$loadtimes['2less']}</TD>\n";
 	print "  </TR>\n";
+	print _("    <TH align=right>Load times 2-6 minutes:</TH>\n");
+	print "    <TD>{$loadtimes['2to6']}</TD>\n";
+	print "  </TR>\n";
+	print _("    <TH align=right>Load times 6-8 minutes:</TH>\n");
+	print "    <TD>{$loadtimes['6to8']}</TD>\n";
 	print "  <TR>\n";
-	print _("    <TH align=right>Load times &gt;= 2 minutes:</TH>\n");
-	print "    <TD>{$loadtimes['2more']}</TD>\n";
+	print _("    <TH align=right>Load times &gt;= 8 minutes:</TH>\n");
+	print "    <TD>{$loadtimes['8more']}</TD>\n";
 	print "  </TR>\n";
 	print "  <TR>\n";
 	print _("    <TH align=right>Total Unique Users:</TH>\n");
@@ -419,8 +446,10 @@ function viewStatistics() {
 	print _("    <TH>Reservations</TH>\n");
 	print _("    <TH>Unique Users</TH>\n");
 	print _("    <TH>Hours Used</TH>\n");
-	print _("    <TH>&lt; 2 min load time</TH>\n");
-	print _("    <TH>&gt;= 2 min load time</TH>\n");
+	print _("    <TH>&lt; 2 min wait</TH>\n");
+	print _("    <TH>2-6 min wait</TH>\n");
+	print _("    <TH>6-8 min wait</TH>\n");
+	print _("    <TH>&gt;= 8 min wait</TH>\n");
 	print _("    <TH>Failures</TH>\n");
 	print "  </TR>\n";
 	foreach($imagecount as $key => $value) {
@@ -433,7 +462,9 @@ function viewStatistics() {
 		else
 			print "    <TD align=center>" . (int)$imagehours[$key] . "</TD>\n";
 		print "    <TD align=center>{$imageload2less[$key]}</TD>\n";
-		print "    <TD align=center>{$imageload2more[$key]}</TD>\n";
+		print "    <TD align=center>{$imageload2to6[$key]}</TD>\n";
+		print "    <TD align=center>{$imageload6to8[$key]}</TD>\n";
+		print "    <TD align=center>{$imageload8more[$key]}</TD>\n";
 		if($imagefails[$key]) {
 			$percent = $imagefails[$key] * 100 / $value;
 			if($percent < 1)
@@ -468,8 +499,20 @@ function viewStatistics() {
 	print "    <TD>" . $lengths["4hours"] . "</TD>\n";
 	print "  </TR>\n";
 	print "  <TR>\n";
-	print _("    <TH align=right>&gt; 4 Hours:</TH>\n");
-	print "    <TD>" . $lengths["4hrsplus"] . "</TD>\n";
+	print _("    <TH align=right>4 Hours - 6 Hours:</TH>\n");
+	print "    <TD>" . $lengths["6hours"] . "</TD>\n";
+	print "  </TR>\n";
+	print "  <TR>\n";
+	print _("    <TH align=right>6 Hours - 8 Hours:</TH>\n");
+	print "    <TD>" . $lengths["8hours"] . "</TD>\n";
+	print "  </TR>\n";
+	print "  <TR>\n";
+	print _("    <TH align=right>8 Hours - 10 Hours:</TH>\n");
+	print "    <TD>" . $lengths["10hours"] . "</TD>\n";
+	print "  </TR>\n";
+	print "  <TR>\n";
+	print _("    <TH align=right>&gt; 10 Hours:</TH>\n");
+	print "    <TD>" . $lengths["10hrsplus"] . "</TD>\n";
 	print "  </TR>\n";
 	print "</TABLE>\n";
 

Added: vcl/trunk/web/.ht-inc/storebackend.php
URL: http://svn.apache.org/viewvc/vcl/trunk/web/.ht-inc/storebackend.php?rev=1624325&view=auto
==============================================================================
--- vcl/trunk/web/.ht-inc/storebackend.php (added)
+++ vcl/trunk/web/.ht-inc/storebackend.php Thu Sep 11 16:01:48 2014
@@ -0,0 +1,409 @@
+<?php
+/*
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+*/
+
+/**
+ * \file
+ */
+
+require_once('.ht-inc/resource.php');
+
+$RESTresponsecodes = array(
+	200 => 'OK',  
+	201 => 'Created',  
+	202 => 'Accepted',  
+	203 => 'Non-Authoritative Information',  
+	204 => 'No Content',  
+	205 => 'Reset Content',  
+	206 => 'Partial Content',  
+	300 => 'Multiple Choices',  
+	301 => 'Moved Permanently',  
+	302 => 'Found',  
+	303 => 'See Other',  
+	304 => 'Not Modified',  
+	305 => 'Use Proxy',  
+	307 => 'Temporary Redirect',  
+	400 => 'Bad Request',  
+	401 => 'Unauthorized',  
+	402 => 'Payment Required',  
+	403 => 'Forbidden',  
+	404 => 'Not Found',  
+	405 => 'Method Not Allowed',  
+	406 => 'Not Acceptable',  
+	407 => 'Proxy Authentication Required',  
+	408 => 'Request Timeout',  
+	409 => 'Conflict',  
+	410 => 'Gone',  
+	411 => 'Length Required',  
+	412 => 'Precondition Failed',  
+	413 => 'Request Entity Too Large',  
+	414 => 'Request-URI Too Long',  
+	415 => 'Unsupported Media Type',  
+	416 => 'Requested Range Not Satisfiable',  
+	417 => 'Expectation Failed',  
+);
+
+/*function testDojoREST() {
+	#$images = getImages(0, 1997);
+	#$a = dataToJSON($images[1997], 1);
+	#printArray($images[1997]);
+	#printArray($a);
+
+	#$obj = new Config();
+	#$data = $obj->getData($obj->defaultGetDataArgs);
+	#printArray($data);
+
+
+
+	print resourceStore('config', 1, 'datastore', 1);
+
+	print "<select dojoType=\"dijit.form.FilteringSelect\" id=\"deployimage\" ";
+	print "style=\"width: 400px\" required=\"true\" searchAttr=\"name\" ";
+	print "query=\"{deleted: 0}\" queryExpr=\".*\${0}.*\" ";
+	print "highlightMatch=\"all\" autoComplete=\"false\" store=\"datastore\">\n";
+	print "</select><br>\n";
+
+	print "<button id=\"testbtn\" dojoType=\"dijit.form.Button\">\n";
+	print "  Toggle Deleted Flag\n";
+	print "  <script type=\"dojo/method\" event=\"onClick\">\n";
+	print <<<END
+var id = dijit.byId('deployimage').get('value');
+console.log('id to delete: ' + id);
+storedatastore.remove(id);
+console.log('here');
+END;
+	#print "    showNewResDlg();\n";
+	print "  </script>\n";
+	print "</button><br><br>\n";
+}*/
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn resourceStore($type, $detail, $jsid, $datawrapper=0)
+///
+/// \param $type - type of resource
+/// \param $detail - whether to use RESTresourceDetail or RESTresourceBasic
+/// \param $jsid - javascript id for store
+/// \param $datawrapper - (optional, default=0) set to 1 to wrap JsonRest
+/// store with a data ObjectStore
+///
+/// \return html
+///
+/// \brief generates HTML for a REST based resource store
+///
+////////////////////////////////////////////////////////////////////////////////
+function resourceStore($type, $detail, $jsid, $datawrapper=0) {
+	$h  =	"<div dojoType=\"dojo.store.JsonRest\" target=\"" . BASEURL;
+	if($detail)
+		$h .= "/index.php/RESTresourceDetail/$type/\" ";
+	else
+		$h .= "/index.php/RESTresourceBasic/$type/\" ";
+	if($datawrapper == 1)
+		$h .= "jsid=\"store$jsid\"></div>\n";
+	else
+		$h .= "jsid=\"$jsid\"></div>\n";
+	if($datawrapper == 1) {
+		$h .= "<div dojoType=\"dojo.data.ObjectStore\" objectStore=\"store$jsid\" ";
+		$h .= "jsid=\"$jsid\"></div>\n";
+	}
+	return $h;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn RESTresourceBasic()
+///
+/// \brief sends REST formatted basic information for requested resource type
+///
+////////////////////////////////////////////////////////////////////////////////
+function RESTresourceBasic() {
+	$type = validatetype(processRESTarg(1, ARG_STRING));
+	$deleted = processInputVar('deleted', ARG_NUMERIC, 0);
+	if($deleted != 0 && $deleted != 1)
+		$deleted = 0;
+	$name = processInputVar('name', ARG_STRING, '*');
+	if(is_null($type)) {
+		RESTresponse(404, "invalid resource type");
+		return;
+	}
+	$subid = processRESTarg(2, ARG_NUMERIC, 0);
+	if($type == 'image')
+		$resources = getUserResources(array("imageAdmin", "imageCheckOut"),
+		                              array('available'), 0, $deleted);
+	elseif($type == 'computer')
+		$resources = getUserResources(array("computerAdmin"), array("administer"),
+		                              0, $deleted);
+	elseif($type == 'config')
+		$resources = getUserResources(array("configAdmin"), array('available'),
+		                              0, $deleted);
+	#elseif ...
+	if($name != '*') {
+		# TODO
+		print '/*';
+		print "name: |$name| ";
+		foreach($resources[$type] as $id => $resname) {
+			print "img: |$resname| ";
+			if(! preg_match("/^$name$/i", $resname))
+				unset($resources[$type][$id]);
+		}
+		print '*/';
+	}
+	if($subid == 0) {
+		sendREST(dataToJSON($resources[$type]));
+		return;
+	}
+	elseif(! array_key_exists($subid, $resources[$type])) {
+		RESTresponse(404, "specified resource does not exist");
+		return;
+	}
+	sendREST(array('id' => (int)$subid, 'name' => $resources[$type][$subid]));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn RESTresourceDetail
+///
+/// \brief sends REST formatted detailed information for requested resource type
+///
+////////////////////////////////////////////////////////////////////////////////
+function RESTresourceDetail() {
+	$type = validatetype(processRESTarg(1, ARG_STRING));
+	$deleted = processInputVar('deleted', ARG_NUMERIC, 0);
+	if($deleted != 0 && $deleted != 1)
+		$deleted = 0;
+	$name = processInputVar('name', ARG_STRING, '*');
+	$tmp = processInputVar('prettyname', ARG_STRING, '*');
+	if($name == '*' && $tmp != '*')
+		$name = $tmp;
+	if(is_null($type)) {
+		RESTresponse(404, "invalid resource type");
+		return;
+	}
+	$subid = processRESTarg(2, ARG_NUMERIC, 0);
+	if($type == 'image')
+		$resources = getUserResources(array("imageAdmin", "imageCheckOut"),
+		                              array('available'), 0, $deleted);
+	elseif($type == 'computer')
+		$resources = getUserResources(array("computerAdmin"), array("administer"),
+		                              0, $deleted);
+	elseif($type == 'config')
+		$resources = getUserResources(array("configAdmin"), array('available'),
+		                              0, $deleted);
+	# TODO
+	#elseif ...
+	if($subid && ! array_key_exists($subid, $resources[$type])) {
+		RESTresponse(404, "specified resource does not exist");
+		printArray($resources[$type]);
+		return;
+	}
+	if($_SERVER['REQUEST_METHOD'] == 'DELETE') {
+		if(RESTdeleteResource($type, $subid))
+			RESTresponse(204);
+		else
+			RESTresponse(404, "specified resource does not exist 2");
+		return;
+	}
+	if($type == 'image') {
+		$items = getImages($deleted, $subid);
+		$data = array();
+		foreach(array_keys($resources[$type]) as $id) {
+			if($name != '*' &&
+			   ! preg_match("/^$name$/i", $items[$id]['prettyname']))
+				continue;
+			if(array_key_exists($id, $items))
+				$data[$id] = $items[$id];
+		}
+	}
+	elseif($type == 'computer')
+		$data = getComputers(1, 0, $subid);
+	elseif($type == 'config') {
+		$cluster = processInputVar('cluster', ARG_NUMERIC, -1);
+		$cfg = new Config();
+		$items = $cfg->getData($cfg->defaultGetDataArgs);
+		$data = array();
+		foreach(array_keys($resources[$type]) as $id) {
+			if($name != '*' &&
+			   ! preg_match("/^$name$/i", $items[$id]['name']))
+				continue;
+			if(array_key_exists($id, $items)) {
+				if($cluster == -1 ||
+				   ($cluster == 0 && $items[$id]['configtype'] != 'Cluster') ||
+				   ($cluster == 1 && $items[$id]['configtype'] == 'Cluster'))
+					$data[$id] = $items[$id];
+			}
+		}
+	}
+	#elseif ...
+	if($subid == 0) {
+		sendREST(dataToJSON($data));
+		return;
+	}
+	sendREST(dataToJSON($data[$subid], 1));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn RESTdeleteResource($type, $subid)
+///
+/// \param $type - resource type
+/// \param $subid - id of resource (from that resource's table, not the general
+/// resource table)
+///
+/// \return 1 on success, 0 on failure
+///
+/// \brief toggles delete flag for a resource or deletes it if that type does
+/// not handle being flagged as deleted
+///
+////////////////////////////////////////////////////////////////////////////////
+function RESTdeleteResource($type, $subid) {
+	switch($type) {
+		case 'image':
+			$obj = new Image();
+			break;
+		# TODO
+	}
+	$_SESSION['userresources'] = array();
+	return $obj->toggleDeleteResource($subid);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn processRESTarg($index, $type, $defaultvalue=NULL)
+///
+/// \param $index - index in URL path
+/// \param $type - tag type:\n
+/// \b ARG_NUMERIC - numeric\n
+/// \b ARG_STRING - string\n
+/// \b ARG_MULTINUMERIC - an array of numbers
+/// \param $defaultvalue - (optional, defaults to NULL) default value for the
+/// variable
+///
+/// \return sanitized value
+///
+/// \brief wrapper for processInputData
+///
+////////////////////////////////////////////////////////////////////////////////
+function processRESTarg($index, $type, $defaultvalue=NULL) {
+	if(! array_key_exists("PATH_INFO", $_SERVER))
+		return $defaultvalue;
+	$pathdata = explode("/", $_SERVER["PATH_INFO"]);
+	if(! array_key_exists($index + 1, $pathdata))
+		return $defaultvalue;
+	return processInputData($pathdata[$index + 1], $type, $defaultvalue);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn validatetype($type)
+///
+/// \param $type - resource type
+///
+/// \return $type if valid, NULL if not
+///
+/// \brief validates that $type is a know resource type
+///
+////////////////////////////////////////////////////////////////////////////////
+function validatetype($type) {
+	$types = getTypes('resources');
+	if(! in_array($type, $types['resources']))
+		return NULL;
+	return $type;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn RESTresponse($code, $reason="")
+///
+/// \param $code - HTTP response code
+/// \param $reason - (optional, default='') reason for given response; sets
+/// X-Status-Reason response header
+///
+/// \brief sets the HTTP response header and reason for response if $reason is
+/// not empty
+///
+////////////////////////////////////////////////////////////////////////////////
+function RESTresponse($code, $reason="") {
+	global $RESTresponsecodes;
+	$response = "HTTP/1.1 $code {$RESTresponsecodes[$code]}";
+	header($response);
+	if($reason != "")
+		header("X-Status-Reason: $reason");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn sendREST($arr)
+///
+/// \param $arr - array of data
+///
+/// \brief sends $arr as JSON formatted data
+///
+////////////////////////////////////////////////////////////////////////////////
+function sendREST($arr) {
+	sendJSON($arr, '', 1);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+///
+/// \fn dataToJSON($arr, $rec=0)
+///
+/// \param $arr - array of data
+/// \param $rec - (optional, default=0) 
+///
+/// \return array
+///
+/// \brief formats an array for better encoding to JSON
+///
+////////////////////////////////////////////////////////////////////////////////
+function dataToJSON($arr, $rec=0) {
+	$jarr = array();
+	foreach($arr as $key => $val) {
+		if(is_array($val)) {
+			if(empty($val)) {
+				$jarr[$key] = $val;
+				continue;
+			}
+			$tmp = array_keys($val);
+			if(is_array($val[$tmp[0]])) {
+				#printArray($val[$tmp[0]]);
+				$jarr[$key] = dataToJSON($val, 1);
+			}
+			else {
+				if(is_numeric($key)) {
+					#print "key: $key - rec: $rec<br>\n";
+					#printArray($val);
+					$jarr[] = dataToJSON($val, 1);
+				}
+				else {
+					$test = array_slice($val, 0);
+					if($test === $val)
+						$jarr[$key] = $val;
+					else
+						$jarr[$key] = dataToJSON($val);
+				}
+			}
+			#$jarr[] = $val;
+			#$jarr[$key] = dataToJSON($val, 1);
+		}
+		elseif($rec)
+			$jarr[$key] = $val;
+		else
+			$jarr[] = array('id' => $key, 'name' => $val);
+	}
+	return $jarr;
+}
+?>

Modified: vcl/trunk/web/.ht-inc/userpreferences.php
URL: http://svn.apache.org/viewvc/vcl/trunk/web/.ht-inc/userpreferences.php?rev=1624325&r1=1624324&r2=1624325&view=diff
==============================================================================
--- vcl/trunk/web/.ht-inc/userpreferences.php (original)
+++ vcl/trunk/web/.ht-inc/userpreferences.php Thu Sep 11 16:01:48 2014
@@ -251,7 +251,8 @@ function userpreferences() {
 	print "      <div id=uiprefs class=shown>\n";
 	print "      <fieldset>\n";
 	print _("      <legend>General Preferences</legend>\n");
-	print "      <FORM action=\"" . BASEURL . SCRIPT . "\" method=post>\n";
+	print "      <FORM action=\"" . BASEURL . SCRIPT . "\" method=post ";
+	print "onsubmit=\"return validatePublicKeys();\">\n";
 	$cdata = array();
 	if($user['showallgroups']) {
 		$selected['affiliation'] = '';
@@ -283,6 +284,51 @@ function userpreferences() {
 	print "      <INPUT type=radio id=r4 name=emailnotify value=1 ";
 	print "{$selected['disabled']}" . _("><label for=r4>Disabled");
 	print "</label></p>\n";
+
+	###########################
+	# temporary
+	if(! array_key_exists('usepublickeys', $user)) {
+		$user['usepublickeys'] = 0;
+		$_SESSION['user']['usepublickeys'] = 0;
+		$user['sshpublickeys'] = '';
+		$_SESSION['user']['sshpublickeys'] = '';
+	}
+	# end temporary
+	###########################
+
+	if($user['usepublickeys']) {
+		$selected['enabled'] = 'checked';
+		$selected['disabled'] = '';
+	}
+	else {
+		$selected['enabled'] = '';
+		$selected['disabled'] = 'checked';
+	}
+	print "      <p>" . _("Use public key authentication for SSH logins:") . "<br>\n";
+	print "      <INPUT type=radio id=r5 name=pubkeyauth value=2 ";
+	print "{$selected['enabled']} onclick=\"togglePubKeys(1);\"><label for=r5>";
+	print _("Enabled") .  "</label><br>\n";
+	print "      <INPUT type=radio id=r6 name=pubkeyauth value=1 ";
+	print "{$selected['disabled']} onclick=\"togglePubKeys(0);\"><label for=r6>";
+	print _("Disabled") . "</label><br><br>\n";
+	print "      " . _("Public keys:") . "<br>\n";
+	print "      <div style=\"width: 300px;\" id=\"pubkeyerr\" ";
+	print "class=\"hidden\">";
+	print "<font color=\"red\"><em>\n      ";
+	print _("Public keys can only contain letters, numbers, spaces, and these characters: + / @ . =");
+	print "</em></font></div>\n";
+	print "      <textarea id=\"pubkeys\" dojoType=\"dijit.form.Textarea\" ";
+	print "name=\"pubkeys\" style=\"width: 27em;\"";
+	if(! $user['usepublickeys'])
+		print " disabled=\"disabled\"";
+	print ">{$user['sshpublickeys']}</textarea><br>\n";
+	print "<strong>NOTE</strong>: Images using network storage (such as AFS) may not<br>\n";
+	print "work well with public key authentication. In some cases,<br>\n";
+	print "you may still be prompted for a password. In other cases,<br>\n";
+	print "you may need to run additional commands after logging in<br>\n";
+	print "to gain access to the network storage.<br>\n";
+	print "      </p>\n";
+
 	$cont = addContinuationsEntry('submitgeneralprefs', $cdata, SECINDAY, 1, 0);
 	print "      <INPUT type=hidden name=continuation value=\"$cont\">\n";
 	print _("      <INPUT type=submit value=\"Submit General Preferences\">\n");
@@ -331,7 +377,7 @@ function confirmUserPrefs($type) {
 	else
 		$serial = _("Yes");
 
-	print "<DIV align=center>\n";
+	print "<div align=center>\n";
 	if($type == 0) {
 		print _("<H2>Personal Information</H2>\n");
 		print _("<H3>Submit the following changes?</H3>\n");
@@ -395,7 +441,7 @@ function confirmUserPrefs($type) {
 	print "    </TD>\n";
 	print "  </TR>\n";
 	print "</table>\n";
-    print "</DIV>\n";
+	print "</div>\n";
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -455,6 +501,8 @@ function submitGeneralPreferences() {
 	global $user, $HTMLheader, $printedHTMLheader, $mode;
 	$groupview = getContinuationVar('groupview', processInputVar('groupview', ARG_STRING));
 	$emailnotify = processInputVar('emailnotify', ARG_NUMERIC);
+	$pubkeyauth = processInputVar('pubkeyauth', ARG_NUMERIC);
+	$pubkeys = processInputVar('pubkeys', ARG_STRING);
 	if($groupview != 'affiliation' && $groupview != 'allgroups') {
 		$printedHTMLheader = 1;
 		print $HTMLheader;
@@ -467,6 +515,12 @@ function submitGeneralPreferences() {
 		userpreferences();
 		return;
 	}
+	if($pubkeyauth != 1 && $pubkeyauth != 2) {
+		$printedHTMLheader = 1;
+		print $HTMLheader;
+		userpreferences();
+		return;
+	}
 	if(($groupview == 'allgroups' && $user['showallgroups'] == 0) ||
 	   ($groupview == 'affiliation' && $user['showallgroups'] == 1)) {
 		if($groupview == 'allgroups')
@@ -486,6 +540,23 @@ function submitGeneralPreferences() {
 		$_SESSION['user']['emailnotices'] = $newval;
 		$user['emailnotices'] = $newval;
 	}
+	if(($user['usepublickeys'] == 1 && $pubkeyauth == 1) ||
+	   ($user['usepublickeys'] == 0 && $pubkeyauth == 2)) {
+		$newval = $pubkeyauth - 1;
+		$query = "UPDATE user SET usepublickeys = $newval WHERE id = {$user['id']}";
+		doQuery($query);
+		$_SESSION['user']['usepublickeys'] = $newval;
+		$user['usepublickeys'] = $newval;
+	}
+	if($pubkeyauth == 2 && preg_match('|^[-a-zA-Z0-9\+/ @=\.]*$|', $pubkeys)) {
+		if(get_magic_quotes_gpc())
+			$pubkeys = stripslashes($pubkeys);
+		$_pubkeys = mysql_real_escape_string($pubkeys);
+		$query = "UPDATE user SET sshpublickeys = '$_pubkeys' WHERE id = {$user['id']}";
+		doQuery($query);
+		$_SESSION['user']['sshpublickeys'] = htmlspecialchars($pubkeys);
+		$user['sshpublickeys'] = htmlspecialchars($pubkeys);
+	}
 	print $HTMLheader;
 	$printedHTMLheader = 1;
 	$mode = 'submituserprefs';
@@ -512,7 +583,7 @@ function processUserPrefsInput($checks=1
 	$return = array();
 
 	$defaultres = $user["width"] . 'x' . $user["height"];
-	$return["preferredname"] = processInputVar("preferredname" , ARG_STRING);
+	$return["preferredname"] = processInputVar("preferredname" , ARG_STRING, $user["preferredname"]);
 	$return["resolution"] = processInputVar("resolution" , ARG_STRING, $defaultres);
 	$return["bpp"] = processInputVar("bpp" , ARG_NUMERIC, $user["bpp"]);
 	$return["audiomode"] = processInputVar("audiomode" , ARG_STRING, $user["audiomode"]);
@@ -577,6 +648,22 @@ function show(id) {
 		id = 'rdpfile';
 	document.getElementById(id).className = "shown";
 }
+function validatePublicKeys() {
+	var data = dijit.byId('pubkeys').value;
+	var patt = /^[-a-zA-Z0-9\+/ @=\.\\n]{0,65535}$/;
+	if(! patt.test(data)) {
+		dojo.removeClass('pubkeyerr', 'hidden');
+		return false;
+	}
+	dojo.addClass('pubkeyerr', 'hidden');
+	return true;
+}
+function togglePubKeys(mode) {
+	if(mode)
+		dijit.byId('pubkeys').set('disabled', false);
+	else
+		dijit.byId('pubkeys').set('disabled', true);
+}
 show("personal");
 document.getElementById("preflinks").className = "shown";
 document.getElementById("status").className = "visible";