Noel Dieschburg 13 роки тому
батько
коміт
a955b9e777
100 змінених файлів з 9828 додано та 10 видалено
  1. 13 0
      .hgignore
  2. 13 0
      custompages/README
  3. 73 0
      custompages/language.inc.php
  4. 49 0
      documentation/changelog.html
  5. 4 3
      main/admin/access_url_add_courses_to_url.php
  6. 246 0
      main/admin/add_users_to_session.php
  7. 10 0
      main/admin/filler.php
  8. 10 1
      main/admin/group_edit.php
  9. 3 0
      main/admin/group_list.php
  10. 1 0
      main/admin/languages.php
  11. 14 2
      main/admin/special_exports.php
  12. 105 0
      main/admin/user_add.php
  13. 20 0
      main/auth/cas/cas_var.inc.php
  14. 1610 0
      main/auth/cas/lib/CAS.php
  15. 218 0
      main/auth/cas/lib/CAS/PGTStorage/pgt-db.php
  16. 276 0
      main/auth/cas/lib/CAS/PGTStorage/pgt-file.php
  17. 215 0
      main/auth/cas/lib/CAS/PGTStorage/pgt-main.php
  18. 2700 0
      main/auth/cas/lib/CAS/client.php
  19. 499 0
      main/auth/cas/lib/CAS/domxml-php4-to-php5.php
  20. 27 0
      main/auth/cas/lib/CAS/languages/catalan.php
  21. 27 0
      main/auth/cas/lib/CAS/languages/english.php
  22. 28 0
      main/auth/cas/lib/CAS/languages/french.php
  23. 27 0
      main/auth/cas/lib/CAS/languages/german.php
  24. 27 0
      main/auth/cas/lib/CAS/languages/greek.php
  25. 27 0
      main/auth/cas/lib/CAS/languages/japanese.php
  26. 24 0
      main/auth/cas/lib/CAS/languages/languages.php
  27. 27 0
      main/auth/cas/lib/CAS/languages/spanish.php
  28. 25 0
      main/auth/cas/logincas.php
  29. 32 0
      main/auth/conditional_login/complete_phone_number.php
  30. 32 0
      main/auth/conditional_login/conditional_login.php
  31. 93 0
      main/auth/courses.php
  32. 74 1
      main/auth/inscription.php
  33. 50 2
      main/auth/ldap/authldap.php
  34. 1 1
      main/auth/lost_password.lib.php
  35. 538 0
      main/course_progress/index.php
  36. BIN
      main/css/academica/default.css
  37. BIN
      main/css/academica/images/dataTable/back_disabled.jpg
  38. BIN
      main/css/academica/images/dataTable/back_enabled.jpg
  39. BIN
      main/css/academica/images/dataTable/favicon.ico
  40. BIN
      main/css/academica/images/dataTable/forward_disabled.jpg
  41. BIN
      main/css/academica/images/dataTable/forward_enabled.jpg
  42. BIN
      main/css/academica/images/dataTable/sort_asc.png
  43. BIN
      main/css/academica/images/dataTable/sort_asc_disabled.png
  44. BIN
      main/css/academica/images/dataTable/sort_both.png
  45. BIN
      main/css/academica/images/dataTable/sort_desc.png
  46. BIN
      main/css/academica/images/dataTable/sort_desc_disabled.png
  47. 538 0
      main/css/academica/learnpath.css
  48. BIN
      main/css/baby_orange/default.css
  49. BIN
      main/css/baby_orange/images/dataTable/back_disabled.jpg
  50. BIN
      main/css/baby_orange/images/dataTable/back_enabled.jpg
  51. BIN
      main/css/baby_orange/images/dataTable/favicon.ico
  52. BIN
      main/css/baby_orange/images/dataTable/forward_disabled.jpg
  53. BIN
      main/css/baby_orange/images/dataTable/forward_enabled.jpg
  54. BIN
      main/css/baby_orange/images/dataTable/sort_asc.png
  55. BIN
      main/css/baby_orange/images/dataTable/sort_asc_disabled.png
  56. BIN
      main/css/baby_orange/images/dataTable/sort_both.png
  57. BIN
      main/css/baby_orange/images/dataTable/sort_desc.png
  58. BIN
      main/css/baby_orange/images/dataTable/sort_desc_disabled.png
  59. 538 0
      main/css/baby_orange/learnpath.css
  60. BIN
      main/css/blue_lagoon/default.css
  61. BIN
      main/css/blue_lagoon/images/dataTable/back_disabled.jpg
  62. BIN
      main/css/blue_lagoon/images/dataTable/back_enabled.jpg
  63. BIN
      main/css/blue_lagoon/images/dataTable/favicon.ico
  64. BIN
      main/css/blue_lagoon/images/dataTable/forward_disabled.jpg
  65. BIN
      main/css/blue_lagoon/images/dataTable/forward_enabled.jpg
  66. BIN
      main/css/blue_lagoon/images/dataTable/sort_asc.png
  67. BIN
      main/css/blue_lagoon/images/dataTable/sort_asc_disabled.png
  68. BIN
      main/css/blue_lagoon/images/dataTable/sort_both.png
  69. BIN
      main/css/blue_lagoon/images/dataTable/sort_desc.png
  70. BIN
      main/css/blue_lagoon/images/dataTable/sort_desc_disabled.png
  71. 538 0
      main/css/blue_lagoon/learnpath.css
  72. BIN
      main/css/chamilo/default.css
  73. BIN
      main/css/chamilo/images/dataTable/back_disabled.jpg
  74. BIN
      main/css/chamilo/images/dataTable/back_enabled.jpg
  75. BIN
      main/css/chamilo/images/dataTable/favicon.ico
  76. BIN
      main/css/chamilo/images/dataTable/forward_disabled.jpg
  77. BIN
      main/css/chamilo/images/dataTable/forward_enabled.jpg
  78. BIN
      main/css/chamilo/images/dataTable/sort_asc.png
  79. BIN
      main/css/chamilo/images/dataTable/sort_asc_disabled.png
  80. BIN
      main/css/chamilo/images/dataTable/sort_both.png
  81. BIN
      main/css/chamilo/images/dataTable/sort_desc.png
  82. BIN
      main/css/chamilo/images/dataTable/sort_desc_disabled.png
  83. 538 0
      main/css/chamilo/learnpath.css
  84. BIN
      main/css/chamilo_electric_blue/default.css
  85. BIN
      main/css/chamilo_electric_blue/images/dataTable/back_disabled.jpg
  86. BIN
      main/css/chamilo_electric_blue/images/dataTable/back_enabled.jpg
  87. BIN
      main/css/chamilo_electric_blue/images/dataTable/favicon.ico
  88. BIN
      main/css/chamilo_electric_blue/images/dataTable/forward_disabled.jpg
  89. BIN
      main/css/chamilo_electric_blue/images/dataTable/forward_enabled.jpg
  90. BIN
      main/css/chamilo_electric_blue/images/dataTable/sort_asc.png
  91. BIN
      main/css/chamilo_electric_blue/images/dataTable/sort_asc_disabled.png
  92. BIN
      main/css/chamilo_electric_blue/images/dataTable/sort_both.png
  93. BIN
      main/css/chamilo_electric_blue/images/dataTable/sort_desc.png
  94. BIN
      main/css/chamilo_electric_blue/images/dataTable/sort_desc_disabled.png
  95. 538 0
      main/css/chamilo_electric_blue/images/header-logo.png
  96. BIN
      main/css/chamilo_green/default.css
  97. BIN
      main/css/chamilo_green/images/dataTable/back_disabled.jpg
  98. BIN
      main/css/chamilo_green/images/dataTable/back_enabled.jpg
  99. BIN
      main/css/chamilo_green/images/dataTable/favicon.ico
  100. BIN
      main/css/chamilo_green/images/dataTable/forward_disabled.jpg

+ 13 - 0
.hgignore

@@ -0,0 +1,13 @@
+<?php
+// HTTP404 page with neat styling
+// 2011, Jean-Karim Bockstael <jeankarim@cblue.be>
+// ErrorDocument 404 /404/
+
+$language_file = array('document', 'index');
+require_once('../main/inc/global.inc.php');
+
+$msg = get_lang('FileNotFound');
+Display::display_header($msg);
+Display::display_error_message($msg);
+Display::display_footer();
+?>

+ 13 - 0
custompages/README

@@ -0,0 +1,13 @@
+CustomPages looks for alternatives in this directory, and displays them if present. The user-provided custom pages must exactly be named as such :
+
+- index-logged.php for the general landing page before login
+- index-unlogged.php for the general landing page when already logged-in
+- registration.php for the registration form
+- registration-feedback.php for the registration success feedback
+- lostpassword.php for the password recovery form
+- lostpassword-feedback.php for the password recovery feedback page
+
+Important note :
+If you pull this feature from a repo, not through an upgrade or install process, you have to exectute the following SQL statements in the main database or this option won't appear in the admin interface :
+	INSERT INTO settings_options (variable, value, display_text) VALUES ('use_custom_pages', 'true', 'Yes'), ('use_custom_pages', 'false', 'No');
+	INSERT INTO settings_current (variable, type, category, selected_value, title, comment, scope) VALUES ('use_custom_pages','radio','Platform','false','UseCustomPages','UseCustomPagesComment', 'platform');

+ 73 - 0
custompages/language.inc.php

@@ -0,0 +1,73 @@
+<?php
+// CustomPages : Browser language detection
+// Include this file in your custom page if you want to set the language variable of the Chamilo session to the best pick according to the visitor's browser's options.
+// 2011, Jean-Karim Bockstael, CBlue <jeankarim@cblue.be>
+
+// This requires the Chamilo system to be initialized
+// (note that it's easier to do the following include in the parent page)
+// require_once('path/to/main/inc/global.inc.php');
+
+// Returns the best match between available languages and visitor preferences
+// return the best match as 2-chars code, null when none match
+function get_preferred_language($available_langs) {
+    // Parsing the Accept-languages HTTP header
+    $langs = array();
+    foreach (explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']) as $httplang) {
+        $rawlang = explode(';q=', $httplang);
+        if (strpos($rawlang[0], '-') !== FALSE) {
+            // We ignore the locale part, as in en-GB vs en-US
+            $rawlang[0] = substr($rawlang[0], 0, strpos($rawlang[0], '-'));
+        }
+        if (count($rawlang) == 1) {
+            $rawlang[1] = 1.0; // The absence of weighting means a weight of 1 (max)
+        }
+        $langs[$rawlang[1]][] = $rawlang[0];
+    }
+    krsort($langs, SORT_NUMERIC);
+    // Choosing the best match
+    foreach($langs as $weight => $codes) {
+		foreach ($codes as $code) {
+        	if (in_array($code, $available_langs)) {
+            	return $code;
+        	}
+		}
+    }
+    // No match
+    return null;
+}
+
+// Wrapper function for the get_lang function
+// use this if you want to avoid translation caching issues
+function cp_get_lang($variable) {
+	return get_lang($variable, null, $_SESSION['user_language_choice']);
+}
+
+// Note that Chamilo languages are expressed as full english names, not 2-characters codes
+// e.g. 'english' instead of 'en', 'french' instead of 'fr', ...
+// We need a matching array. Note the value for the null key, which is the default language.
+// Also note that this is an example matchin array, not all languages are present.
+$chamilo_langs = array(null => 'english', 'en' => 'english', 'fr' => 'french', 'es' => 'spanish');
+
+// Which of these can we actually pick from ?
+$available_langs = array('en','fr');
+
+// Which language files will we need ?
+$language_file = array('courses', 'index', 'registration', 'admin','userInfo');
+
+// Let's find out which language to serve to this particular browser
+$lang_match = $chamilo_langs[get_preferred_language($available_langs)];
+
+// Chamilo overrides this parameters at some places, e.g. in the logout link
+if (isset($_REQUEST['language']) && !empty($_REQUEST['language']) && in_array($_REQUEST['language'], $chamilo_langs)) {
+	$lang_match = $_REQUEST['language'];
+}
+
+// Maybe a language had already been selected, we should honor this
+if (isset($_SESSION['user_language_choice']) && in_array($_SESSION['user_language_choice'], $chamilo_langs)) {
+	$lang_match = $_SESSION['user_language_choice'];
+}
+
+// We need to set the relevant session variables to the best match, to use Chamilo's i18n lib.
+$_user['language'] = $lang_match;
+$_SESSION['user_language_choice'] = $lang_match;
+?>

+ 49 - 0
documentation/changelog.html

@@ -31,6 +31,8 @@ require_once api_get_path(LIBRARY_PATH).'groupmanager.lib.php';
 require_once api_get_path(LIBRARY_PATH).'formvalidator/FormValidator.class.php';
 require_once 'main/chat/chat_functions.lib.php';
 
+require_once api_get_path(LIBRARY_PATH).'custompages.lib.php';
+
 $loginFailed = isset($_GET['loginFailed']) ? true : isset($loginFailed);
 $setting_show_also_closed_courses = api_get_setting('show_closed_courses') == 'true';
 
@@ -123,7 +125,53 @@ else {
     // Only if login form was not sent because if the form is sent the user was already on the page.
     event_open();
 }
+//checks user status and redirect him through custom page if setting is enabled
+
+$user_status = (isset($_SESSION['_user']['user_id'])) ? $_user['status'] : null;
+
+if ( $user_status == STUDENT && !api_get_setting('student_page_after_login') == '' ){
+	$redirect_url = html_entity_decode(api_get_setting('student_page_after_login'));
+	if ($redirect_url[0] == "/") {
+		$redirect_url = substr(api_get_path(WEB_PATH), 0, -1).$redirect_url;
+	}
+    header('Location: '.$redirect_url);
+      exit();
+}
+if ( $user_status == COURSEMANAGER && !api_get_setting('teacher_page_after_login') == '' ){
+	$redirect_url = html_entity_decode(api_get_setting('teacher_page_after_login'));
+	if ($redirect_url[0] == "/") {
+		$redirect_url = substr(api_get_path(WEB_PATH), 0, -1).$redirect_url;
+	}
+    header('Location: '.$redirect_url);
+      exit();
+}
+if ( $user_status == DRH && !api_get_setting('DRH_page_after_login') == '' ){
+	$redirect_url = html_entity_decode(api_get_setting('DRH_page_after_login'));
+	if ($redirect_url[0] == "/") {
+		$redirect_url = substr(api_get_path(WEB_PATH), 0, -1).$redirect_url;
+	}
+    header('Location: '.$redirect_url);
+      exit();
+}
+if ( $user_status == SESSIONADMIN && !api_get_setting('sessionadmin_page_after_login') == '' ){
+	$redirect_url = html_entity_decode(api_get_setting('sessionadmin_page_after_login'));
+	if ($redirect_url[0] == "/") {
+		$redirect_url = substr(api_get_path(WEB_PATH), 0, -1).$redirect_url;
+	}
+    header('Location: '.$redirect_url);
+      exit();
+}
+
 
+// Custom pages
+if (api_get_setting('use_custom_pages') == 'true') {
+	if (api_get_user_id()) {
+		CustomPages::displayPage('index-logged');
+	}
+	else {
+		CustomPages::displayPage('index-unlogged');
+	}
+}
 // The header.
 /*$header_title = get_lang('Homepage');
 //$sitename = api_get_setting('siteName');
@@ -134,6 +182,7 @@ $header_title = null;
 if (!api_is_anonymous()) {
     $header_title = " ";
 }
+
 Display::display_header($header_title);
 
 /* MAIN CODE */

+ 4 - 3
main/admin/access_url_add_courses_to_url.php

@@ -5,7 +5,7 @@
 */
 
 // name of the language file that needs to be included
-$language_file=array('admin','registration');
+$language_file=array('admin','registration','userInfo');
 
 // resetting the course id
 $cidReset=true;
@@ -57,7 +57,7 @@ function search_users($needle,$type,$relation_type) {
 		// get user_id from relation type and group id
 	    $sql = "SELECT user_id FROM $tbl_group_rel_user
 				WHERE group_id = '$group_id'
-				AND relation_type IN (".GROUP_USER_PERMISSION_ADMIN.",".GROUP_USER_PERMISSION_READER.",".GROUP_USER_PERMISSION_PENDING_INVITATION.",".GROUP_USER_PERMISSION_MODERATOR.") ";
+				AND relation_type IN (".GROUP_USER_PERMISSION_ADMIN.",".GROUP_USER_PERMISSION_READER.",".GROUP_USER_PERMISSION_PENDING_INVITATION.",".GROUP_USER_PERMISSION_MODERATOR.", ".GROUP_USER_PERMISSION_HRM.") ";
 		$res = Database::query($sql);
 		$user_ids = array();
 		if (Database::num_rows($res) > 0) {
@@ -322,7 +322,7 @@ if ($ajax_search) {
 		// get user_id from relation type and group id
 		$sql = "SELECT user_id FROM $tbl_group_rel_user
 				WHERE group_id = $id
-				AND relation_type IN (".GROUP_USER_PERMISSION_ADMIN.",".GROUP_USER_PERMISSION_READER.",".GROUP_USER_PERMISSION_PENDING_INVITATION.",".GROUP_USER_PERMISSION_MODERATOR.") ";
+				AND relation_type IN (".GROUP_USER_PERMISSION_ADMIN.",".GROUP_USER_PERMISSION_READER.",".GROUP_USER_PERMISSION_PENDING_INVITATION.",".GROUP_USER_PERMISSION_MODERATOR.", ".GROUP_USER_PERMISSION_HRM.") ";
 		$res = Database::query($sql);
 		$user_ids = array();
 		if (Database::num_rows($res) > 0) {
@@ -401,6 +401,7 @@ if ($add_type == 'multiple') {
 <option value="<?php echo GROUP_USER_PERMISSION_ADMIN ?>" <?php echo ((isset($_POST['relation']) && $_POST['relation']==GROUP_USER_PERMISSION_ADMIN)?'selected=selected':'') ?> > <?php echo get_lang('Admin') ?></option>
 <option value="<?php echo GROUP_USER_PERMISSION_PENDING_INVITATION ?>" <?php echo ((isset($_POST['relation']) && $_POST['relation']==GROUP_USER_PERMISSION_PENDING_INVITATION)?'selected=selected':'') ?> > <?php echo get_lang('Reader') ?></option>
 <option value="<?php echo GROUP_USER_PERMISSION_MODERATOR ?>" <?php echo ((isset($_POST['relation']) && $_POST['relation']==GROUP_USER_PERMISSION_MODERATOR)?'selected=selected':'') ?> > <?php echo get_lang('Moderator') ?></option>
+<option value="<?php echo GROUP_USER_PERMISSION_HRM ?>" <?php echo ((isset($_POST['relation']) && $_POST['relation']==GROUP_USER_PERMISSION_HRM)?'selected=selected':'') ?> > <?php echo get_lang('HumanResourcesManager') ?></option>
 </select>
 
 <?php

+ 246 - 0
main/admin/add_users_to_session.php

@@ -0,0 +1,246 @@
+<?php
+
+// name of the language file that needs to be included
+$language_file = array('admin','events');
+$cidReset = true;
+require_once '../inc/global.inc.php';
+$this_section = SECTION_PLATFORM_ADMIN;
+api_protect_admin_script();
+
+$interbreadcrumb[] = array ('url' => 'index.php', 'name' => get_lang('PlatformAdmin'));
+$tool_name = get_lang('events_title');
+
+$action = isset($_POST['action'])?$_POST['action']:null;
+$eventId = isset($_POST['eventId'])?$_POST['eventId']:null;
+$eventUsers = isset($_POST['eventUsers'])?$_POST['eventUsers']:null;
+$eventMessage = isset($_POST['eventMessage'])?$_POST['eventMessage']:null;
+$eventSubject = isset($_POST['eventSubject'])?$_POST['eventSubject']:null;
+
+if($action == 'modEventType') {
+	if($eventUsers) {
+		$users = explode(';',$eventUsers);
+	}
+	else {
+		$users = array();
+	}
+	
+	eventType_mod($eventId,$users,$eventMessage,$eventSubject);
+	// echo mysql_error();
+	header('location: event_type.php');
+	exit;
+}
+
+$ets = eventType_getAll();
+
+
+$ajaxPath = api_get_path(WEB_CODE_PATH).'inc/ajax/events.ajax.php';
+$htmlHeadXtra[] = '<script src="../inc/lib/javascript/jquery.js" type="text/javascript" language="javascript"></script>';
+
+Display::display_header($tool_name);
+
+?>
+
+<script language="javascript">
+	var usersList;
+	var eventTypes = <?php print json_encode($ets) ?>;
+
+	$(document).ready(function(){
+		ajax({action:"getUsers"},function(data) {
+				usersList = data;
+			}
+		);
+		
+		// ajax({action:"getEventTypes"},function(data) {
+				// eventTypes = data;
+				// showEventTypes(data);
+			// }
+		// );
+	});
+
+	function ajax(params,func) {
+		$.ajax({
+				url: "<?php echo $ajaxPath ?>",
+				type: "POST",
+				data: params,
+				success: func
+			}
+		);
+	}
+	
+	function refreshUsersList() {
+		removeAllOption($('#usersList'));
+		$.each(usersList,function(ind,item) {
+				addOption($('#usersList'),item.user_id,item.firstname + ' '+item.lastname);
+			}
+		);
+	}
+	
+	// function showEventTypes(data) {
+		// $.each(data,function(ind,item) {
+				// addOption($('#eventList'),item.id,item.name);
+			// }
+		// );
+	// }
+	
+	function getCurrentEventTypeInd() {
+		var ind=false;
+		$.each(eventTypes,function(i,item)
+			{
+				if(item.id == $('#eventList option:selected').first().attr('value')) {
+					ind=i;
+					return false;
+				}
+			}	
+		)
+		
+		return ind;
+	}
+	
+	function showEventType() {
+		eInd = getCurrentEventTypeInd();
+		
+		$('#eventId').attr('value',eventTypes[eInd].id);
+		$('#eventName').attr('value',eventTypes[eInd].name);
+		$('#eventNameTitle').text(eventTypes[eInd].nameLangVar);
+		$('#eventMessage').text(eventTypes[eInd].message);
+		$('#eventSubject').attr('value',eventTypes[eInd].subject);
+		$('#descLangVar').text(eventTypes[eInd].descLangVar);
+		
		
+		ajax({action:"getEventTypeUsers","id":eventTypes[eInd].id},function(data) {
+				removeAllOption($('#usersSubList'));
+				
+				refreshUsersList();
+				
+				usersIds = new Array();
+				
+				$.each(data,function(ind,item) {
+					addOption($('#usersSubList'),item.user_id,item.firstname + ' '+item.lastname);
+					usersIds[ind] = item.value;
+					removeOption($('#usersList'),item.user_id);
+				});
+				
+				$('#eventUsers').attr('value',usersIds.join(';'));
+			}
+		);
+	}
+	
+	function submitForm() {
+		if($('#eventId')) {
+			usersIds = new Array();
+			
+			$('#usersSubList option').each(function(ind,item)
+				{
+					usersIds[ind] = item.value;
+				}
+			);
+			
+			$('#eventUsers').attr('value',usersIds.join(';'));
+			
+			return true;
+		}
+		
+		return false;
+	}
+	
+	function addOption(select,value,text) {
+		select.append('<option value="'+value+'">'+text+'</option>');
+	}
+	
+	function removeOption(select,value) {
+		select.find('option[value='+value+']').remove();
+	}
+	
+	function removeAllOption(select) {
+		select.find('option').remove();
+	}
+	
+	function moveUsers(src,dest) {
+		src.find('option:selected').each(function(index,opt) {
+			text = opt.text;
+			val = opt.value;
+			
+			addOption(dest,val,text);
+			removeOption(src,val);
+		});
+	}
+</script>
+
+<h3><?php print get_lang('events_title') ?></h3>
+
+<table id="" width="90%">
+	<tr>
+		<td width="5%">
+			<h4><?php print get_lang('events_listTitle'); ?></h4>
+		</td>
+		<td width="5%">
+			<h4><?php print get_lang('events_userListTile'); ?></h4>
+		</td>
+		<td width="5%">
+			&nbsp;
+		</td>
+		<td width="5%">
+			<h4><?php print get_lang('events_userSubListTile'); ?></h4>
+		</td>
+	</tr>
+	<tr>
+		<td>
+			<select multiple="1" id="eventList" onChange="showEventType()">
+				<?php
+					
+					foreach($ets as $et) {
+						print '<option value="'.$et['id'].'">'.$et['nameLangVar'].'</option>';
+					}
+					
+				?>
+			</select>
+		</td>
+		<td>
+			<select multiple="1" id="usersList"></select>
+		</td>
+		<td valign="middle">
+			<button class="arrowr" onclick='moveUsers($("#usersList"),$("#usersSubList")); return false;'></button>
+			<br />
+			<button class="arrowl" onclick='moveUsers($("#usersSubList"),$("#usersList")); return false;'></button>
+		</td>
+		<td>
+			<select multiple="1" id="usersSubList"></select>
+		</td>
+	</tr>
+</table>
+
+<br />
+
+<h2 id="eventNameTitle"></h2>
+
+<form method="POST" onSubmit="return submitForm(); ">
+	<input type="hidden" name="action" value="modEventType" />
+	<input type="hidden" name="eventId" id="eventId" />
+	<input type="hidden" name="eventUsers" id="eventUsers" />
+	<input type="hidden" id="eventName" />
+	
+	<br />
+	
+	<div id="descLangVar">
+	</div>
+	<br />
+	
+	<label for="eventSubject"><h4><?php print get_lang('events_labelSubject'); ?></h4></label>
+	<input type="text" id="eventSubject" name="eventSubject" />
+	<br /><br />
+	<label for="eventMessage"><h4><?php print get_lang('events_labelMessage'); ?></h4></label>
+	<textarea cols="100" rows="10" name="eventMessage" id="eventMessage">
+	
+	</textarea>
+
+<br /><br />
+
+<input type="submit" value="<?php print get_lang('events_btnMod'); ?>" />
+
+</form>
+
+
+<?php
+
+Display :: display_footer();
+
+?>

+ 10 - 0
main/admin/filler.php

@@ -76,6 +76,13 @@ $form->addElement('file', 'picture', get_lang('AddPicture'));
 $allowed_picture_types = array ('jpg', 'jpeg', 'png', 'gif');
 $form->addRule('picture', get_lang('OnlyImagesAllowed').' ('.implode(',', $allowed_picture_types).')', 'filetype', $allowed_picture_types);
 
+//Group Parentship
+$groups = array();
+$groups[0] = get_lang('NoParentship');
+$groups = $groups + GroupPortalManager::get_groups_list($group_id);
+$group_data['parent_group'] = GroupPortalManager::get_parent_group($group_id);
+$form->addElement('select', 'parent_group', get_lang('GroupParentship'), $groups, array());
+
 // Status
 $status = array();
 $status[GROUP_PERMISSION_OPEN] 		= get_lang('Open');
@@ -105,8 +112,11 @@ if( $form->validate()) {
 		$url 			= $values['url'];
 		$status 		= intval($values['visibility']);
 		$picture 		= $_FILES['picture'];
+    $parent_group_id = intval($values['parent_group']); 
 
 		$group_id = GroupPortalManager::add($name, $description, $url, $status);
+    GroupPortalManager::set_parent_group($group_id,$parent_group_id);
+
 
 		if (!empty($picture['name'])) {
 			$picture_uri = GroupPortalManager::update_group_picture($group_id, $_FILES['picture']['name'], $_FILES['picture']['tmp_name']);

+ 10 - 1
main/admin/group_edit.php

@@ -78,12 +78,18 @@ if (strlen($group_data['picture_uri']) > 0) {
 	$form->addElement('checkbox', 'delete_picture', '', get_lang('DelImage'));
 }
 
+//Group Parentship
+$groups = array();
+$groups = GroupPortalManager::get_groups_list($group_id);
+$groups[0] = get_lang('NoParentship');
+$group_data['parent_group'] = GroupPortalManager::get_parent_group($group_id);
+$form->addElement('select', 'parent_group', get_lang('GroupParentship'), $groups, array());
+
 
 // Status
 $status = array();
 $status[GROUP_PERMISSION_OPEN] 		= get_lang('Open');
 $status[GROUP_PERMISSION_CLOSED]	= get_lang('Closed');
-
 $form->addElement('select', 'visibility', get_lang('GroupPermissions'), $status, array());
 
 // Submit button
@@ -111,8 +117,11 @@ if ( $form->validate()) {
 	$description	= $group['description'];
 	$url 			= $group['url'];
 	$status 		= intval($group['visibility']);
+  $parent_group_id = intval($group['parent_group']); 
 
 	GroupPortalManager::update($group_id, $name, $description, $url, $status, $picture_uri);
+  GroupPortalManager::set_parent_group($group_id,$parent_group_id);
+
 	$tok = Security::get_token();
 	header('Location: group_list.php?action=show_message&message='.urlencode(get_lang('GroupUpdated')).'&sec_token='.$tok);
 	exit();

+ 3 - 0
main/admin/group_list.php

@@ -159,6 +159,9 @@ if (api_is_platform_admin()) { ?>
         }       
         
     ?>
+    <?php if (api_get_setting('activate_send_event_by_mail') === 'true'): ?>
+	    <li><a href="event_type.php"><?php print  get_lang('events_title'); ?></a></li>
+    <?php endif ?>
     </ul>
 </div>
 

+ 1 - 0
main/admin/languages.php

@@ -528,6 +528,7 @@ if (!empty($_GET['category']) && !in_array($_GET['category'], array('Plugins', '
 Display :: display_header($tool_name);
 
 
+$action_images['cas'] = 'loginmanager.gif';
 
 // The action images.
 $action_images['platform']      = 'platform.png';

+ 14 - 2
main/admin/special_exports.php

@@ -23,6 +23,7 @@ require_once api_get_path(LIBRARY_PATH).'formvalidator/FormValidator.class.php';
 require_once api_get_path(LIBRARY_PATH).'system_announcements.lib.php';
 require_once api_get_path(LIBRARY_PATH).'WCAG/WCAG_rendering.php';
 require_once api_get_path(LIBRARY_PATH).'mail.lib.inc.php';
+require_once api_get_path(LIBRARY_PATH).'group_portal_manager.lib.php';
 
 // Setting the section (for the tabs).
 $this_section=SECTION_PLATFORM_ADMIN;
@@ -123,6 +124,8 @@ if (isset ($_GET['action']) && $_GET['action'] == 'edit') {
     $values['visible_guest'] 	= $announcement->visible_guest ;
     $values['lang'] 			= $announcement->lang;
     $values['action']			= 'edit';
+  $groups = SystemAnnouncementManager :: get_announcement_groups($announcement->id);
+  $values['group'] = isset($groups[0]['group_id']) ? $groups[0]['group_id'] : 0;
     $action_todo = true;
 }
 
@@ -155,6 +158,12 @@ if ($action_todo) {
     $form->addElement('checkbox', 'visible_guest', null, get_lang('Guest'));
     
     $form->addElement('hidden', 'id');
+
+  $group_list = GroupPortalManager::get_groups_list();
+  $group_list[0]  = get_lang('AllGroups');
+	$form->addElement('select', 'group',get_lang('AnnouncementForGroup'),$group_list);
+  $values['group'] = isset($values['group']) ? $values['group'] : '0';
+
     $form->addElement('checkbox', 'send_mail', get_lang('SendMail'));    
 
     if (isset($_REQUEST['action']) && $_REQUEST['action']=='add') {
@@ -193,7 +202,9 @@ if ($action_todo) {
         }
         switch ($values['action']) {
             case 'add':
-                if (SystemAnnouncementManager::add_announcement($values['title'], $values['content'], $values['start'], $values['end'], $values['visible_teacher'], $values['visible_student'], $values['visible_guest'], $values['lang'], $values['send_mail'], $values['add_to_calendar'])) {
+          $announcement_id = SystemAnnouncementManager::add_announcement($values['title'],$values['content'],$values['start'],$values['end'],$values['visible_teacher'],$values['visible_student'],$values['visible_guest'], $values['lang'],$values['send_mail'],  $values['add_to_calendar']);
+          if ($announcement_id !== false )  {
+          SystemAnnouncementManager::announcement_for_groups($announcement_id, array($values['group']));
                     Display :: display_confirmation_message(get_lang('AnnouncementAdded'));
                 } else {
                     $show_announcement_list = false;
@@ -202,6 +213,7 @@ if ($action_todo) {
                 break;
             case 'edit':
                 if (SystemAnnouncementManager::update_announcement($values['id'], $values['title'], $values['content'], $values['start'], $values['end'], $values['visible_teacher'], $values['visible_student'], $values['visible_guest'], $values['lang'], $values['send_mail'])) {
+          SystemAnnouncementManager::announcement_for_groups($values['id'], array($values['group']));
                     Display :: display_confirmation_message(get_lang('AnnouncementUpdated'));
                 } else {
                     $show_announcement_list = false;
@@ -260,4 +272,4 @@ if ($show_announcement_list) {
     $table->display();
 }
 /* FOOTER */
-Display :: display_footer();
+Display :: display_footer();

+ 105 - 0
main/admin/user_add.php

@@ -0,0 +1,105 @@
+<?php
+/*Written by Noel Dieschburg <noel@cblue.be> for the paris5 university
+
+* Checks if the user is already logged in via the cas system
+* Gets all the info via the ldap module (ldap has to work)
+
+*/
+require_once(api_get_path(SYS_PATH).'main/auth/cas/cas_var.inc.php');
+require_once(api_get_path(SYS_PATH).'main/auth/ldap/authldap.php');
+
+/**
+* checks if the user already get a session
+* @return the user login if the user already has a session ,false otherwise
+**/
+
+
+function cas_is_authenticated()
+{
+	global $cas_auth_ver, $cas_auth_server, $cas_auth_port, $cas_auth_uri; 
+	global $PHPCAS_CLIENT;
+	global $logout;
+
+
+	if (!is_object($PHPCAS_CLIENT) ) 
+	{
+		phpCAS::client($cas_auth_ver,$cas_auth_server,$cas_auth_port,$cas_auth_uri);
+//		die("phpCAS::client($cas_auth_ver,$cas_auth_server,$cas_auth_port,$cas_auth_uri);");
+		phpCAS::setNoCasServerValidation();
+	}
+	$auth = phpCAS::checkAuthentication(); 
+  
+	if ($auth) {
+		$login= trim(phpCAS::getUser());
+		/*
+		   Get user  attributes. Here are the attributes for crdp platform
+		   sn => name
+		   ENTPersonMailInterne => mail
+		   ENTPersonAlias => login
+		   ENTPersonProfils => profil
+		   givenName => first name
+		 */
+		/*$user=phpCAS::getAttributes();
+		$firstName = trim($user['givenName']);
+		$lastName = trim($user['sn']);
+		$login = trim($user['ENTPersonAlias']);
+		$profil = trim($user['ENTPersonProfils']);
+		$email = trim($user['ENTPersonMailInterne']);
+		$satus=5;
+		switch ($profil){
+			case 'admin_etab':
+				$status=3; //Session admin
+				break;
+			case 'admin_sie':
+				$status=3; //Session admin
+				break;
+			case 'National_3':
+				$status=1; // Teacher
+				break;
+			case 'National_1':
+				$status=5; // Student
+				break;
+			default:
+				$status=5; // Student
+		}*/
+		//If the user is in the dokeos database and we are ,not in a logout request, we upgrade his infomration by ldap
+		if (! $logout){
+			$user_table = Database::get_main_table(TABLE_MAIN_USER);
+			$sql = "SELECT user_id, username, password, auth_source, active, expiration_date ".
+				"FROM $user_table ".
+				"WHERE username = '$login' ";
+
+			$result = api_sql_query($sql,__FILE__,__LINE__);
+			if(mysql_num_rows($result) == 0) { 
+				require_once(api_get_path(SYS_PATH).'main/inc/lib/usermanager.lib.php');
+				$rnumber=rand(0,256000);
+				UserManager::create_user($firstName, $lastName, $status, $email, $login, md5('casplaceholder'.$rnumber), $official_code='',$language='',$phone='',$picture_uri='',$auth_source = PLATFORM_AUTH_SOURCE);
+			}
+			else {
+				$user = mysql_fetch_assoc($result);
+				$user_id = intval($user['user_id']);
+				//echo "deb : $status";
+				UserManager::update_user ($user_id, $firstname, $lastname, $login, null, null, $email, $status, '', '', '', '', 1, null, 0, null,'') ;
+
+			}
+		}
+		return($login);
+	}
+	else 
+	{ 
+		return(false);
+	}
+}
+
+/**
+* Logs out the user of the cas 
+* The user MUST be logged in with cas to use this function 
+**/ 
+function cas_logout()
+{
+	
+	//phpCAS::logoutWithRedirectService("fmc.univ-paris5.fr");		
+	phpCAS::logoutWithRedirectService(api_get_path(WEB_PATH));		
+}
+
+?>

+ 20 - 0
main/auth/cas/cas_var.inc.php

@@ -0,0 +1,20 @@
+<?
+/* This file contains all the configuration variable for the cas module
+ * In the future, these will be in the database
+*/
+require_once('lib/CAS.php');
+define("CAS_VERSION_2_0",'2.0');
+define("SAML_VERSION_1_1", 'S1'); 
+
+global $cas_auth_ver, $cas_auth_server, $cas_auth_port, $cas_auth_uri; 
+
+$cas_auth_server = api_get_setting('cas_server');
+$cas_auth_uri = api_get_setting('cas_server_uri');
+$cas_auth_port = intval(api_get_setting('cas_port'));
+
+$cas_auth_uri = api_get_setting('cas_server_uri');
+if ( ! is_string($cas_auth_uri)) $cas_auth_uri = ''; 
+	
+$cas_auth_ver = '2.0';
+//$cas_auth_ver = SAML_VERSION_1_1;
+?>

+ 1610 - 0
main/auth/cas/lib/CAS.php

@@ -0,0 +1,1610 @@
+<?php
+
+/*
+ * Copyright © 2003-2010, The ESUP-Portail consortium & the JA-SIG Collaborative.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright notice,
+ *       this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright notice,
+ *       this list of conditions and the following disclaimer in the documentation
+ *       and/or other materials provided with the distribution.
+ *     * Neither the name of the ESUP-Portail consortium & the JA-SIG
+ *       Collaborative nor the names of its contributors may be used to endorse or
+ *       promote products derived from this software without specific prior
+ *       written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+//
+// hack by Vangelis Haniotakis to handle the absence of $_SERVER['REQUEST_URI'] in IIS
+//
+if (!$_SERVER['REQUEST_URI']) {
+	$_SERVER['REQUEST_URI'] = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['QUERY_STRING'];
+}
+
+//
+// another one by Vangelis Haniotakis also to make phpCAS work with PHP5
+//
+if (version_compare(PHP_VERSION, '5', '>=')) {
+	require_once (dirname(__FILE__) . '/CAS/domxml-php4-to-php5.php');
+}
+
+/**
+ * @file CAS/CAS.php
+ * Interface class of the phpCAS library
+ *
+ * @ingroup public
+ */
+
+// ########################################################################
+//  CONSTANTS
+// ########################################################################
+
+// ------------------------------------------------------------------------
+//  CAS VERSIONS
+// ------------------------------------------------------------------------
+
+/**
+ * phpCAS version. accessible for the user by phpCAS::getVersion().
+ */
+define('PHPCAS_VERSION', '1.1.1');
+
+// ------------------------------------------------------------------------
+//  CAS VERSIONS
+// ------------------------------------------------------------------------
+/**
+ * @addtogroup public
+ * @{
+ */
+
+/**
+ * CAS version 1.0
+ */
+define("CAS_VERSION_1_0", '1.0');
+/*!
+ * CAS version 2.0
+ */
+define("CAS_VERSION_2_0", '2.0');
+
+// ------------------------------------------------------------------------
+//  SAML defines
+// ------------------------------------------------------------------------
+
+/**
+ * SAML protocol
+ */
+define("SAML_VERSION_1_1", 'S1');
+
+/**
+ * XML header for SAML POST
+ */
+define("SAML_XML_HEADER", '<?xml version="1.0" encoding="UTF-8"?>');
+
+/**
+ * SOAP envelope for SAML POST
+ */
+define("SAML_SOAP_ENV", '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Header/>');
+
+/**
+ * SOAP body for SAML POST
+ */
+define("SAML_SOAP_BODY", '<SOAP-ENV:Body>');
+
+/**
+ * SAMLP request
+ */
+define("SAMLP_REQUEST", '<samlp:Request xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol"  MajorVersion="1" MinorVersion="1" RequestID="_192.168.16.51.1024506224022" IssueInstant="2002-06-19T17:03:44.022Z">');
+define("SAMLP_REQUEST_CLOSE", '</samlp:Request>');
+
+/**
+ * SAMLP artifact tag (for the ticket)
+ */
+define("SAML_ASSERTION_ARTIFACT", '<samlp:AssertionArtifact>');
+
+/**
+ * SAMLP close
+ */
+define("SAML_ASSERTION_ARTIFACT_CLOSE", '</samlp:AssertionArtifact>');
+
+/**
+ * SOAP body close
+ */
+define("SAML_SOAP_BODY_CLOSE", '</SOAP-ENV:Body>');
+
+/**
+ * SOAP envelope close
+ */
+define("SAML_SOAP_ENV_CLOSE", '</SOAP-ENV:Envelope>');
+
+/**
+ * SAML Attributes
+ */
+define("SAML_ATTRIBUTES", 'SAMLATTRIBS');
+
+/** @} */
+/**
+ * @addtogroup publicPGTStorage
+ * @{
+ */
+// ------------------------------------------------------------------------
+//  FILE PGT STORAGE
+// ------------------------------------------------------------------------
+/**
+ * Default path used when storing PGT's to file
+ */
+define("CAS_PGT_STORAGE_FILE_DEFAULT_PATH", '/tmp');
+/**
+ * phpCAS::setPGTStorageFile()'s 2nd parameter to write plain text files
+ */
+define("CAS_PGT_STORAGE_FILE_FORMAT_PLAIN", 'plain');
+/**
+ * phpCAS::setPGTStorageFile()'s 2nd parameter to write xml files
+ */
+define("CAS_PGT_STORAGE_FILE_FORMAT_XML", 'xml');
+/**
+ * Default format used when storing PGT's to file
+ */
+define("CAS_PGT_STORAGE_FILE_DEFAULT_FORMAT", CAS_PGT_STORAGE_FILE_FORMAT_PLAIN);
+// ------------------------------------------------------------------------
+//  DATABASE PGT STORAGE
+// ------------------------------------------------------------------------
+/**
+ * default database type when storing PGT's to database
+ */
+define("CAS_PGT_STORAGE_DB_DEFAULT_DATABASE_TYPE", 'mysql');
+/**
+ * default host when storing PGT's to database
+ */
+define("CAS_PGT_STORAGE_DB_DEFAULT_HOSTNAME", 'localhost');
+/**
+ * default port when storing PGT's to database
+ */
+define("CAS_PGT_STORAGE_DB_DEFAULT_PORT", '');
+/**
+ * default database when storing PGT's to database
+ */
+define("CAS_PGT_STORAGE_DB_DEFAULT_DATABASE", 'phpCAS');
+/**
+ * default table when storing PGT's to database
+ */
+define("CAS_PGT_STORAGE_DB_DEFAULT_TABLE", 'pgt');
+
+/** @} */
+// ------------------------------------------------------------------------
+// SERVICE ACCESS ERRORS
+// ------------------------------------------------------------------------
+/**
+ * @addtogroup publicServices
+ * @{
+ */
+
+/**
+ * phpCAS::service() error code on success
+ */
+define("PHPCAS_SERVICE_OK", 0);
+/**
+ * phpCAS::service() error code when the PT could not retrieve because
+ * the CAS server did not respond.
+ */
+define("PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE", 1);
+/**
+ * phpCAS::service() error code when the PT could not retrieve because
+ * the response of the CAS server was ill-formed.
+ */
+define("PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE", 2);
+/**
+ * phpCAS::service() error code when the PT could not retrieve because
+ * the CAS server did not want to.
+ */
+define("PHPCAS_SERVICE_PT_FAILURE", 3);
+/**
+ * phpCAS::service() error code when the service was not available.
+ */
+define("PHPCAS_SERVICE_NOT AVAILABLE", 4);
+
+/** @} */
+// ------------------------------------------------------------------------
+//  LANGUAGES
+// ------------------------------------------------------------------------
+/**
+ * @addtogroup publicLang
+ * @{
+ */
+
+define("PHPCAS_LANG_ENGLISH", 'english');
+define("PHPCAS_LANG_FRENCH", 'french');
+define("PHPCAS_LANG_GREEK", 'greek');
+define("PHPCAS_LANG_GERMAN", 'german');
+define("PHPCAS_LANG_JAPANESE", 'japanese');
+define("PHPCAS_LANG_SPANISH", 'spanish');
+define("PHPCAS_LANG_CATALAN", 'catalan');
+
+/** @} */
+
+/**
+ * @addtogroup internalLang
+ * @{
+ */
+
+/**
+ * phpCAS default language (when phpCAS::setLang() is not used)
+ */
+define("PHPCAS_LANG_DEFAULT", PHPCAS_LANG_ENGLISH);
+
+/** @} */
+// ------------------------------------------------------------------------
+//  DEBUG
+// ------------------------------------------------------------------------
+/**
+ * @addtogroup publicDebug
+ * @{
+ */
+
+/**
+ * The default directory for the debug file under Unix.
+ */
+define('DEFAULT_DEBUG_DIR', '/tmp/');
+
+/** @} */
+// ------------------------------------------------------------------------
+//  MISC
+// ------------------------------------------------------------------------
+/**
+ * @addtogroup internalMisc
+ * @{
+ */
+
+/**
+ * This global variable is used by the interface class phpCAS.
+ *
+ * @hideinitializer
+ */
+$GLOBALS['PHPCAS_CLIENT'] = null;
+
+/**
+ * This global variable is used to store where the initializer is called from 
+ * (to print a comprehensive error in case of multiple calls).
+ *
+ * @hideinitializer
+ */
+$GLOBALS['PHPCAS_INIT_CALL'] = array (
+	'done' => FALSE,
+	'file' => '?',
+	'line' => -1,
+	'method' => '?'
+);
+
+/**
+ * This global variable is used to store where the method checking
+ * the authentication is called from (to print comprehensive errors)
+ *
+ * @hideinitializer
+ */
+$GLOBALS['PHPCAS_AUTH_CHECK_CALL'] = array (
+	'done' => FALSE,
+	'file' => '?',
+	'line' => -1,
+	'method' => '?',
+	'result' => FALSE
+);
+
+/**
+ * This global variable is used to store phpCAS debug mode.
+ *
+ * @hideinitializer
+ */
+$GLOBALS['PHPCAS_DEBUG'] = array (
+	'filename' => '/tmp/cas.log',
+	'indent' => 0,
+	'unique_id' => ''
+);
+
+/** @} */
+
+// ########################################################################
+//  CLIENT CLASS
+// ########################################################################
+
+// include client class
+include_once (dirname(__FILE__) . '/CAS/client.php');
+
+// ########################################################################
+//  INTERFACE CLASS
+// ########################################################################
+
+/**
+ * @class phpCAS
+ * The phpCAS class is a simple container for the phpCAS library. It provides CAS
+ * authentication for web applications written in PHP.
+ *
+ * @ingroup public
+ * @author Pascal Aubry <pascal.aubry at univ-rennes1.fr>
+ *
+ * \internal All its methods access the same object ($PHPCAS_CLIENT, declared 
+ * at the end of CAS/client.php).
+ */
+
+class phpCAS {
+
+	// ########################################################################
+	//  INITIALIZATION
+	// ########################################################################
+
+	/**
+	 * @addtogroup publicInit
+	 * @{
+	 */
+
+	/**
+	 * phpCAS client initializer.
+	 * @note Only one of the phpCAS::client() and phpCAS::proxy functions should be
+	 * called, only once, and before all other methods (except phpCAS::getVersion()
+	 * and phpCAS::setDebug()).
+	 *
+	 * @param $server_version the version of the CAS server
+	 * @param $server_hostname the hostname of the CAS server
+	 * @param $server_port the port the CAS server is running on
+	 * @param $server_uri the URI the CAS server is responding on
+	 * @param $start_session Have phpCAS start PHP sessions (default true)
+	 *
+	 * @return a newly created CASClient object
+	 */
+	function client($server_version, $server_hostname, $server_port, $server_uri, $start_session = true) {
+		global $PHPCAS_CLIENT, $PHPCAS_INIT_CALL;
+
+		phpCAS :: traceBegin();
+		if (is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error($PHPCAS_INIT_CALL['method'] . '() has already been called (at ' . $PHPCAS_INIT_CALL['file'] . ':' . $PHPCAS_INIT_CALL['line'] . ')');
+		}
+		if (gettype($server_version) != 'string') {
+			phpCAS :: error('type mismatched for parameter $server_version (should be `string\')');
+		}
+		if (gettype($server_hostname) != 'string') {
+			phpCAS :: error('type mismatched for parameter $server_hostname (should be `string\')');
+		}
+		if (gettype($server_port) != 'integer') {
+			phpCAS :: error('type mismatched for parameter $server_port (should be `integer\')');
+		}
+		if (gettype($server_uri) != 'string') {
+			phpCAS :: error('type mismatched for parameter $server_uri (should be `string\')');
+		}
+
+		// store where the initializer is called from
+		$dbg = phpCAS :: backtrace();
+		$PHPCAS_INIT_CALL = array (
+			'done' => TRUE,
+			'file' => $dbg[0]['file'],
+			'line' => $dbg[0]['line'],
+			'method' => __CLASS__ . '::' . __FUNCTION__
+		);
+
+		// initialize the global object $PHPCAS_CLIENT
+		$PHPCAS_CLIENT = new CASClient($server_version, FALSE /*proxy*/
+		, $server_hostname, $server_port, $server_uri, $start_session);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * phpCAS proxy initializer.
+	 * @note Only one of the phpCAS::client() and phpCAS::proxy functions should be
+	 * called, only once, and before all other methods (except phpCAS::getVersion()
+	 * and phpCAS::setDebug()).
+	 *
+	 * @param $server_version the version of the CAS server
+	 * @param $server_hostname the hostname of the CAS server
+	 * @param $server_port the port the CAS server is running on
+	 * @param $server_uri the URI the CAS server is responding on
+	 * @param $start_session Have phpCAS start PHP sessions (default true)
+	 *
+	 * @return a newly created CASClient object
+	 */
+	function proxy($server_version, $server_hostname, $server_port, $server_uri, $start_session = true) {
+		global $PHPCAS_CLIENT, $PHPCAS_INIT_CALL;
+
+		phpCAS :: traceBegin();
+		if (is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error($PHPCAS_INIT_CALL['method'] . '() has already been called (at ' . $PHPCAS_INIT_CALL['file'] . ':' . $PHPCAS_INIT_CALL['line'] . ')');
+		}
+		if (gettype($server_version) != 'string') {
+			phpCAS :: error('type mismatched for parameter $server_version (should be `string\')');
+		}
+		if (gettype($server_hostname) != 'string') {
+			phpCAS :: error('type mismatched for parameter $server_hostname (should be `string\')');
+		}
+		if (gettype($server_port) != 'integer') {
+			phpCAS :: error('type mismatched for parameter $server_port (should be `integer\')');
+		}
+		if (gettype($server_uri) != 'string') {
+			phpCAS :: error('type mismatched for parameter $server_uri (should be `string\')');
+		}
+
+		// store where the initialzer is called from
+		$dbg = phpCAS :: backtrace();
+		$PHPCAS_INIT_CALL = array (
+			'done' => TRUE,
+			'file' => $dbg[0]['file'],
+			'line' => $dbg[0]['line'],
+			'method' => __CLASS__ . '::' . __FUNCTION__
+		);
+
+		// initialize the global object $PHPCAS_CLIENT
+		$PHPCAS_CLIENT = new CASClient($server_version, TRUE /*proxy*/
+		, $server_hostname, $server_port, $server_uri, $start_session);
+		phpCAS :: traceEnd();
+	}
+
+	/** @} */
+	// ########################################################################
+	//  DEBUGGING
+	// ########################################################################
+
+	/**
+	 * @addtogroup publicDebug
+	 * @{
+	 */
+
+	/**
+	 * Set/unset debug mode
+	 *
+	 * @param $filename the name of the file used for logging, or FALSE to stop debugging.
+	 */
+	function setDebug($filename = '') {
+		global $PHPCAS_DEBUG;
+
+		if ($filename != FALSE && gettype($filename) != 'string') {
+			phpCAS :: error('type mismatched for parameter $dbg (should be FALSE or the name of the log file)');
+		}
+
+		if (empty ($filename)) {
+			if (preg_match('/^Win.*/', getenv('OS'))) {
+				if (isset ($_ENV['TMP'])) {
+					$debugDir = $_ENV['TMP'] . '/';
+				} else
+					if (isset ($_ENV['TEMP'])) {
+						$debugDir = $_ENV['TEMP'] . '/';
+					} else {
+						$debugDir = '';
+					}
+			} else {
+				$debugDir = DEFAULT_DEBUG_DIR;
+			}
+			$filename = $debugDir . 'phpCAS.log';
+		}
+
+		if (empty ($PHPCAS_DEBUG['unique_id'])) {
+			$PHPCAS_DEBUG['unique_id'] = substr(strtoupper(md5(uniqid(''))), 0, 4);
+		}
+
+		$PHPCAS_DEBUG['filename'] = $filename;
+
+		phpCAS :: trace('START ******************');
+	}
+
+	/** @} */
+	/**
+	 * @addtogroup internalDebug
+	 * @{
+	 */
+
+	/**
+	 * This method is a wrapper for debug_backtrace() that is not available 
+	 * in all PHP versions (>= 4.3.0 only)
+	 */
+	function backtrace() {
+		if (function_exists('debug_backtrace')) {
+			return debug_backtrace();
+		} else {
+			// poor man's hack ... but it does work ...
+			return array ();
+		}
+	}
+
+	/**
+	 * Logs a string in debug mode.
+	 *
+	 * @param $str the string to write
+	 *
+	 * @private
+	 */
+	function log($str) {
+		$indent_str = ".";
+		global $PHPCAS_DEBUG;
+
+		if ($PHPCAS_DEBUG['filename']) {
+			for ($i = 0; $i < $PHPCAS_DEBUG['indent']; $i++) {
+				$indent_str .= '|    ';
+			}
+			error_log($PHPCAS_DEBUG['unique_id'] . ' ' . $indent_str . $str . "\n", 3, $PHPCAS_DEBUG['filename']);
+		}
+
+	}
+
+	/**
+	 * This method is used by interface methods to print an error and where the function
+	 * was originally called from.
+	 *
+	 * @param $msg the message to print
+	 *
+	 * @private
+	 */
+	function error($msg) {
+		$dbg = phpCAS :: backtrace();
+		$function = '?';
+		$file = '?';
+		$line = '?';
+		if (is_array($dbg)) {
+			for ($i = 1; $i < sizeof($dbg); $i++) {
+				if (is_array($dbg[$i])) {
+					if ($dbg[$i]['class'] == __CLASS__) {
+						$function = $dbg[$i]['function'];
+						$file = $dbg[$i]['file'];
+						$line = $dbg[$i]['line'];
+					}
+				}
+			}
+		}
+		echo "<br />\n<b>phpCAS error</b>: <font color=\"FF0000\"><b>" . __CLASS__ . "::" . $function . '(): ' . htmlentities($msg) . "</b></font> in <b>" . $file . "</b> on line <b>" . $line . "</b><br />\n";
+		phpCAS :: trace($msg);
+		phpCAS :: traceExit();
+		exit ();
+	}
+
+	/**
+	 * This method is used to log something in debug mode.
+	 */
+	function trace($str) {
+		$dbg = phpCAS :: backtrace();
+		phpCAS :: log($str . ' [' . basename($dbg[1]['file']) . ':' . $dbg[1]['line'] . ']');
+	}
+
+	/**
+	 * This method is used to indicate the start of the execution of a function in debug mode.
+	 */
+	function traceBegin() {
+		global $PHPCAS_DEBUG;
+
+		$dbg = phpCAS :: backtrace();
+		$str = '=> ';
+		if (!empty ($dbg[2]['class'])) {
+			$str .= $dbg[2]['class'] . '::';
+		}
+		$str .= $dbg[2]['function'] . '(';
+		if (is_array($dbg[2]['args'])) {
+			foreach ($dbg[2]['args'] as $index => $arg) {
+				if ($index != 0) {
+					$str .= ', ';
+				}
+				$str .= str_replace("\n", "", var_export($arg, TRUE));
+			}
+		}
+		$str .= ') [' . basename($dbg[2]['file']) . ':' . $dbg[2]['line'] . ']';
+		phpCAS :: log($str);
+		$PHPCAS_DEBUG['indent']++;
+	}
+
+	/**
+	 * This method is used to indicate the end of the execution of a function in debug mode.
+	 *
+	 * @param $res the result of the function
+	 */
+	function traceEnd($res = '') {
+		global $PHPCAS_DEBUG;
+
+		$PHPCAS_DEBUG['indent']--;
+		$dbg = phpCAS :: backtrace();
+		$str = '';
+		$str .= '<= ' . str_replace("\n", "", var_export($res, TRUE));
+		phpCAS :: log($str);
+	}
+
+	/**
+	 * This method is used to indicate the end of the execution of the program
+	 */
+	function traceExit() {
+		global $PHPCAS_DEBUG;
+
+		phpCAS :: log('exit()');
+		while ($PHPCAS_DEBUG['indent'] > 0) {
+			phpCAS :: log('-');
+			$PHPCAS_DEBUG['indent']--;
+		}
+	}
+
+	/** @} */
+	// ########################################################################
+	//  INTERNATIONALIZATION
+	// ########################################################################
+	/**
+	 * @addtogroup publicLang
+	 * @{
+	 */
+
+	/**
+	 * This method is used to set the language used by phpCAS. 
+	 * @note Can be called only once.
+	 *
+	 * @param $lang a string representing the language.
+	 *
+	 * @sa PHPCAS_LANG_FRENCH, PHPCAS_LANG_ENGLISH
+	 */
+	function setLang($lang) {
+		global $PHPCAS_CLIENT;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+		if (gettype($lang) != 'string') {
+			phpCAS :: error('type mismatched for parameter $lang (should be `string\')');
+		}
+		$PHPCAS_CLIENT->setLang($lang);
+	}
+
+	/** @} */
+	// ########################################################################
+	//  VERSION
+	// ########################################################################
+	/**
+	 * @addtogroup public
+	 * @{
+	 */
+
+	/**
+	 * This method returns the phpCAS version.
+	 *
+	 * @return the phpCAS version.
+	 */
+	function getVersion() {
+		return PHPCAS_VERSION;
+	}
+
+	/** @} */
+	// ########################################################################
+	//  HTML OUTPUT
+	// ########################################################################
+	/**
+	 * @addtogroup publicOutput
+	 * @{
+	 */
+
+	/**
+	 * This method sets the HTML header used for all outputs.
+	 *
+	 * @param $header the HTML header.
+	 */
+	function setHTMLHeader($header) {
+		global $PHPCAS_CLIENT;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+		if (gettype($header) != 'string') {
+			phpCAS :: error('type mismatched for parameter $header (should be `string\')');
+		}
+		$PHPCAS_CLIENT->setHTMLHeader($header);
+	}
+
+	/**
+	 * This method sets the HTML footer used for all outputs.
+	 *
+	 * @param $footer the HTML footer.
+	 */
+	function setHTMLFooter($footer) {
+		global $PHPCAS_CLIENT;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+		if (gettype($footer) != 'string') {
+			phpCAS :: error('type mismatched for parameter $footer (should be `string\')');
+		}
+		$PHPCAS_CLIENT->setHTMLFooter($footer);
+	}
+
+	/** @} */
+	// ########################################################################
+	//  PGT STORAGE
+	// ########################################################################
+	/**
+	 * @addtogroup publicPGTStorage
+	 * @{
+	 */
+
+	/**
+	 * This method is used to tell phpCAS to store the response of the
+	 * CAS server to PGT requests onto the filesystem. 
+	 *
+	 * @param $format the format used to store the PGT's (`plain' and `xml' allowed)
+	 * @param $path the path where the PGT's should be stored
+	 */
+	function setPGTStorageFile($format = '', $path = '') {
+		global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL;
+
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if (!$PHPCAS_CLIENT->isProxy()) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if ($PHPCAS_AUTH_CHECK_CALL['done']) {
+			phpCAS :: error('this method should only be called before ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() (called at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ')');
+		}
+		if (gettype($format) != 'string') {
+			phpCAS :: error('type mismatched for parameter $format (should be `string\')');
+		}
+		if (gettype($path) != 'string') {
+			phpCAS :: error('type mismatched for parameter $format (should be `string\')');
+		}
+		$PHPCAS_CLIENT->setPGTStorageFile($format, $path);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * This method is used to tell phpCAS to store the response of the
+	 * CAS server to PGT requests into a database. 
+	 * @note The connection to the database is done only when needed. 
+	 * As a consequence, bad parameters are detected only when 
+	 * initializing PGT storage, except in debug mode.
+	 *
+	 * @param $user the user to access the data with
+	 * @param $password the user's password
+	 * @param $database_type the type of the database hosting the data
+	 * @param $hostname the server hosting the database
+	 * @param $port the port the server is listening on
+	 * @param $database the name of the database
+	 * @param $table the name of the table storing the data
+	 */
+	function setPGTStorageDB($user, $password, $database_type = '', $hostname = '', $port = 0, $database = '', $table = '') {
+		global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL;
+
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if (!$PHPCAS_CLIENT->isProxy()) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if ($PHPCAS_AUTH_CHECK_CALL['done']) {
+			phpCAS :: error('this method should only be called before ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() (called at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ')');
+		}
+		if (gettype($user) != 'string') {
+			phpCAS :: error('type mismatched for parameter $user (should be `string\')');
+		}
+		if (gettype($password) != 'string') {
+			phpCAS :: error('type mismatched for parameter $password (should be `string\')');
+		}
+		if (gettype($database_type) != 'string') {
+			phpCAS :: error('type mismatched for parameter $database_type (should be `string\')');
+		}
+		if (gettype($hostname) != 'string') {
+			phpCAS :: error('type mismatched for parameter $hostname (should be `string\')');
+		}
+		if (gettype($port) != 'integer') {
+			phpCAS :: error('type mismatched for parameter $port (should be `integer\')');
+		}
+		if (gettype($database) != 'string') {
+			phpCAS :: error('type mismatched for parameter $database (should be `string\')');
+		}
+		if (gettype($table) != 'string') {
+			phpCAS :: error('type mismatched for parameter $table (should be `string\')');
+		}
+		$PHPCAS_CLIENT->setPGTStorageDB($user, $password, $database_type, $hostname, $port, $database, $table);
+		phpCAS :: traceEnd();
+	}
+
+	/** @} */
+	// ########################################################################
+	// ACCESS TO EXTERNAL SERVICES
+	// ########################################################################
+	/**
+	 * @addtogroup publicServices
+	 * @{
+	 */
+
+	/**
+	 * This method is used to access an HTTP[S] service.
+	 * 
+	 * @param $url the service to access.
+	 * @param $err_code an error code Possible values are PHPCAS_SERVICE_OK (on
+	 * success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE,
+	 * PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_NOT AVAILABLE.
+	 * @param $output the output of the service (also used to give an error
+	 * message on failure).
+	 *
+	 * @return TRUE on success, FALSE otherwise (in this later case, $err_code
+	 * gives the reason why it failed and $output contains an error message).
+	 */
+	function serviceWeb($url, & $err_code, & $output) {
+		global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL;
+
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if (!$PHPCAS_CLIENT->isProxy()) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if (!$PHPCAS_AUTH_CHECK_CALL['done']) {
+			phpCAS :: error('this method should only be called after the programmer is sure the user has been authenticated (by calling ' . __CLASS__ . '::checkAuthentication() or ' . __CLASS__ . '::forceAuthentication()');
+		}
+		if (!$PHPCAS_AUTH_CHECK_CALL['result']) {
+			phpCAS :: error('authentication was checked (by ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ') but the method returned FALSE');
+		}
+		if (gettype($url) != 'string') {
+			phpCAS :: error('type mismatched for parameter $url (should be `string\')');
+		}
+
+		$res = $PHPCAS_CLIENT->serviceWeb($url, $err_code, $output);
+
+		phpCAS :: traceEnd($res);
+		return $res;
+	}
+
+	/**
+	 * This method is used to access an IMAP/POP3/NNTP service.
+	 * 
+	 * @param $url a string giving the URL of the service, including the mailing box
+	 * for IMAP URLs, as accepted by imap_open().
+	 * @param $service a string giving for CAS retrieve Proxy ticket
+	 * @param $flags options given to imap_open().
+	 * @param $err_code an error code Possible values are PHPCAS_SERVICE_OK (on
+	 * success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE,
+	 * PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_NOT AVAILABLE.
+	 * @param $err_msg an error message on failure
+	 * @param $pt the Proxy Ticket (PT) retrieved from the CAS server to access the URL
+	 * on success, FALSE on error).
+	 *
+	 * @return an IMAP stream on success, FALSE otherwise (in this later case, $err_code
+	 * gives the reason why it failed and $err_msg contains an error message).
+	 */
+	function serviceMail($url, $service, $flags, & $err_code, & $err_msg, & $pt) {
+		global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL;
+
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if (!$PHPCAS_CLIENT->isProxy()) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if (!$PHPCAS_AUTH_CHECK_CALL['done']) {
+			phpCAS :: error('this method should only be called after the programmer is sure the user has been authenticated (by calling ' . __CLASS__ . '::checkAuthentication() or ' . __CLASS__ . '::forceAuthentication()');
+		}
+		if (!$PHPCAS_AUTH_CHECK_CALL['result']) {
+			phpCAS :: error('authentication was checked (by ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ') but the method returned FALSE');
+		}
+		if (gettype($url) != 'string') {
+			phpCAS :: error('type mismatched for parameter $url (should be `string\')');
+		}
+
+		if (gettype($flags) != 'integer') {
+			phpCAS :: error('type mismatched for parameter $flags (should be `integer\')');
+		}
+
+		$res = $PHPCAS_CLIENT->serviceMail($url, $service, $flags, $err_code, $err_msg, $pt);
+
+		phpCAS :: traceEnd($res);
+		return $res;
+	}
+
+	/** @} */
+	// ########################################################################
+	//  AUTHENTICATION
+	// ########################################################################
+	/**
+	 * @addtogroup publicAuth
+	 * @{
+	 */
+
+	/**
+	 * Set the times authentication will be cached before really accessing the CAS server in gateway mode: 
+	 * - -1: check only once, and then never again (until you pree login)
+	 * - 0: always check
+	 * - n: check every "n" time
+	 *
+	 * @param $n an integer.
+	 */
+	function setCacheTimesForAuthRecheck($n) {
+		global $PHPCAS_CLIENT;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+		if (gettype($n) != 'integer') {
+			phpCAS :: error('type mismatched for parameter $header (should be `string\')');
+		}
+		$PHPCAS_CLIENT->setCacheTimesForAuthRecheck($n);
+	}
+
+	/**
+	 * This method is called to check if the user is authenticated (use the gateway feature).
+	 * @return TRUE when the user is authenticated; otherwise FALSE.
+	 */
+	function checkAuthentication() {
+		global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL;
+
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+
+		$auth = $PHPCAS_CLIENT->checkAuthentication();
+
+		// store where the authentication has been checked and the result
+		$dbg = phpCAS :: backtrace();
+		$PHPCAS_AUTH_CHECK_CALL = array (
+			'done' => TRUE,
+			'file' => $dbg[0]['file'],
+			'line' => $dbg[0]['line'],
+			'method' => __CLASS__ . '::' . __FUNCTION__,
+			'result' => $auth
+		);
+		phpCAS :: traceEnd($auth);
+		return $auth;
+	}
+
+	/**
+	 * This method is called to force authentication if the user was not already 
+	 * authenticated. If the user is not authenticated, halt by redirecting to 
+	 * the CAS server.
+	 */
+	function forceAuthentication() {
+		global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL;
+
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+
+		$auth = $PHPCAS_CLIENT->forceAuthentication();
+
+		// store where the authentication has been checked and the result
+		$dbg = phpCAS :: backtrace();
+		$PHPCAS_AUTH_CHECK_CALL = array (
+			'done' => TRUE,
+			'file' => $dbg[0]['file'],
+			'line' => $dbg[0]['line'],
+			'method' => __CLASS__ . '::' . __FUNCTION__,
+			'result' => $auth
+		);
+
+		if (!$auth) {
+			phpCAS :: trace('user is not authenticated, redirecting to the CAS server');
+			$PHPCAS_CLIENT->forceAuthentication();
+		} else {
+			phpCAS :: trace('no need to authenticate (user `' . phpCAS :: getUser() . '\' is already authenticated)');
+		}
+
+		phpCAS :: traceEnd();
+		return $auth;
+	}
+
+	/**
+	 * This method is called to renew the authentication.
+	 **/
+	function renewAuthentication() {
+		global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL;
+
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+
+		// store where the authentication has been checked and the result
+		$dbg = phpCAS :: backtrace();
+		$PHPCAS_AUTH_CHECK_CALL = array (
+			'done' => TRUE,
+			'file' => $dbg[0]['file'],
+			'line' => $dbg[0]['line'],
+			'method' => __CLASS__ . '::' . __FUNCTION__,
+			'result' => $auth
+		);
+
+		$PHPCAS_CLIENT->renewAuthentication();
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * This method has been left from version 0.4.1 for compatibility reasons.
+	 */
+	function authenticate() {
+		phpCAS :: error('this method is deprecated. You should use ' . __CLASS__ . '::forceAuthentication() instead');
+	}
+
+	/**
+	 * This method is called to check if the user is authenticated (previously or by
+	 * tickets given in the URL).
+	 *
+	 * @return TRUE when the user is authenticated.
+	 */
+	function isAuthenticated() {
+		global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL;
+
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+
+		// call the isAuthenticated method of the global $PHPCAS_CLIENT object
+		$auth = $PHPCAS_CLIENT->isAuthenticated();
+
+		// store where the authentication has been checked and the result
+		$dbg = phpCAS :: backtrace();
+		$PHPCAS_AUTH_CHECK_CALL = array (
+			'done' => TRUE,
+			'file' => $dbg[0]['file'],
+			'line' => $dbg[0]['line'],
+			'method' => __CLASS__ . '::' . __FUNCTION__,
+			'result' => $auth
+		);
+		phpCAS :: traceEnd($auth);
+		return $auth;
+	}
+
+	/**
+	 * Checks whether authenticated based on $_SESSION. Useful to avoid
+	 * server calls.
+	 * @return true if authenticated, false otherwise.
+	 * @since 0.4.22 by Brendan Arnold
+	 */
+	function isSessionAuthenticated() {
+		global $PHPCAS_CLIENT;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+		return ($PHPCAS_CLIENT->isSessionAuthenticated());
+	}
+
+	/**
+	 * This method returns the CAS user's login name.
+	 * @warning should not be called only after phpCAS::forceAuthentication()
+	 * or phpCAS::checkAuthentication().
+	 *
+	 * @return the login name of the authenticated user
+	 */
+	function getUser() {
+		global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+		if (!$PHPCAS_AUTH_CHECK_CALL['done']) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::forceAuthentication() or ' . __CLASS__ . '::isAuthenticated()');
+		}
+		if (!$PHPCAS_AUTH_CHECK_CALL['result']) {
+			phpCAS :: error('authentication was checked (by ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ') but the method returned FALSE');
+		}
+		return $PHPCAS_CLIENT->getUser();
+	}
+
+	/**
+	 * This method returns the CAS user's login name.
+	 * @warning should not be called only after phpCAS::forceAuthentication()
+	 * or phpCAS::checkAuthentication().
+	 *
+	 * @return the login name of the authenticated user
+	 */
+	function getAttributes() {
+		global $PHPCAS_CLIENT, $PHPCAS_AUTH_CHECK_CALL;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+		if (!$PHPCAS_AUTH_CHECK_CALL['done']) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::forceAuthentication() or ' . __CLASS__ . '::isAuthenticated()');
+		}
+		if (!$PHPCAS_AUTH_CHECK_CALL['result']) {
+			phpCAS :: error('authentication was checked (by ' . $PHPCAS_AUTH_CHECK_CALL['method'] . '() at ' . $PHPCAS_AUTH_CHECK_CALL['file'] . ':' . $PHPCAS_AUTH_CHECK_CALL['line'] . ') but the method returned FALSE');
+		}
+		return $PHPCAS_CLIENT->getAttributes();
+	}
+	/**
+	 * Handle logout requests.
+	 */
+	function handleLogoutRequests($check_client = true, $allowed_clients = false) {
+		global $PHPCAS_CLIENT;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+		return ($PHPCAS_CLIENT->handleLogoutRequests($check_client, $allowed_clients));
+	}
+
+	/**
+	 * This method returns the URL to be used to login.
+	 * or phpCAS::isAuthenticated().
+	 *
+	 * @return the login name of the authenticated user
+	 */
+	function getServerLoginURL() {
+		global $PHPCAS_CLIENT;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+		return $PHPCAS_CLIENT->getServerLoginURL();
+	}
+
+	/**
+	 * Set the login URL of the CAS server.
+	 * @param $url the login URL
+	 * @since 0.4.21 by Wyman Chan
+	 */
+	function setServerLoginURL($url = '') {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after
+							' . __CLASS__ . '::client()');
+		}
+		if (gettype($url) != 'string') {
+			phpCAS :: error('type mismatched for parameter $url (should be
+						`string\')');
+		}
+		$PHPCAS_CLIENT->setServerLoginURL($url);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * Set the serviceValidate URL of the CAS server.
+	 * Used only in CAS 1.0 validations
+	 * @param $url the serviceValidate URL
+	 * @since 1.1.0 by Joachim Fritschi
+	 */
+	function setServerServiceValidateURL($url = '') {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after
+							' . __CLASS__ . '::client()');
+		}
+		if (gettype($url) != 'string') {
+			phpCAS :: error('type mismatched for parameter $url (should be
+						`string\')');
+		}
+		$PHPCAS_CLIENT->setServerServiceValidateURL($url);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * Set the proxyValidate URL of the CAS server.
+	 * Used for all CAS 2.0 validations
+	 * @param $url the proxyValidate URL
+	 * @since 1.1.0 by Joachim Fritschi
+	 */
+	function setServerProxyValidateURL($url = '') {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after
+							' . __CLASS__ . '::client()');
+		}
+		if (gettype($url) != 'string') {
+			phpCAS :: error('type mismatched for parameter $url (should be
+						`string\')');
+		}
+		$PHPCAS_CLIENT->setServerProxyValidateURL($url);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * Set the samlValidate URL of the CAS server.
+	 * @param $url the samlValidate URL
+	 * @since 1.1.0 by Joachim Fritschi
+	 */
+	function setServerSamlValidateURL($url = '') {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after
+							' . __CLASS__ . '::client()');
+		}
+		if (gettype($url) != 'string') {
+			phpCAS :: error('type mismatched for parameter $url (should be
+						`string\')');
+		}
+		$PHPCAS_CLIENT->setServerSamlValidateURL($url);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * This method returns the URL to be used to login.
+	 * or phpCAS::isAuthenticated().
+	 *
+	 * @return the login name of the authenticated user
+	 */
+	function getServerLogoutURL() {
+		global $PHPCAS_CLIENT;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should not be called before ' . __CLASS__ . '::client() or ' . __CLASS__ . '::proxy()');
+		}
+		return $PHPCAS_CLIENT->getServerLogoutURL();
+	}
+
+	/**
+	 * Set the logout URL of the CAS server.
+	 * @param $url the logout URL
+	 * @since 0.4.21 by Wyman Chan
+	 */
+	function setServerLogoutURL($url = '') {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after
+							' . __CLASS__ . '::client()');
+		}
+		if (gettype($url) != 'string') {
+			phpCAS :: error('type mismatched for parameter $url (should be
+						`string\')');
+		}
+		$PHPCAS_CLIENT->setServerLogoutURL($url);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * This method is used to logout from CAS.
+	 * @params $params an array that contains the optional url and service parameters that will be passed to the CAS server
+	 * @public
+	 */
+	function logout($params = "") {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()');
+		}
+		$parsedParams = array ();
+		if ($params != "") {
+			if (is_string($params)) {
+				phpCAS :: error('method `phpCAS::logout($url)\' is now deprecated, use `phpCAS::logoutWithUrl($url)\' instead');
+			}
+			if (!is_array($params)) {
+				phpCAS :: error('type mismatched for parameter $params (should be `array\')');
+			}
+			foreach ($params as $key => $value) {
+				if ($key != "service" && $key != "url") {
+					phpCAS :: error('only `url\' and `service\' parameters are allowed for method `phpCAS::logout($params)\'');
+				}
+				$parsedParams[$key] = $value;
+			}
+		}
+		$PHPCAS_CLIENT->logout($parsedParams);
+		// never reached
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * This method is used to logout from CAS. Halts by redirecting to the CAS server.
+	 * @param $service a URL that will be transmitted to the CAS server
+	 */
+	function logoutWithRedirectService($service) {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()');
+		}
+		if (!is_string($service)) {
+			phpCAS :: error('type mismatched for parameter $service (should be `string\')');
+		}
+		$PHPCAS_CLIENT->logout(array (
+			"service" => $service
+		));
+		// never reached
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * This method is used to logout from CAS. Halts by redirecting to the CAS server.
+	 * @param $url a URL that will be transmitted to the CAS server
+	 */
+	function logoutWithUrl($url) {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()');
+		}
+		if (!is_string($url)) {
+			phpCAS :: error('type mismatched for parameter $url (should be `string\')');
+		}
+		$PHPCAS_CLIENT->logout(array (
+			"url" => $url
+		));
+		// never reached
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * This method is used to logout from CAS. Halts by redirecting to the CAS server.
+	 * @param $service a URL that will be transmitted to the CAS server
+	 * @param $url a URL that will be transmitted to the CAS server
+	 */
+	function logoutWithRedirectServiceAndUrl($service, $url) {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()');
+		}
+		if (!is_string($service)) {
+			phpCAS :: error('type mismatched for parameter $service (should be `string\')');
+		}
+		if (!is_string($url)) {
+			phpCAS :: error('type mismatched for parameter $url (should be `string\')');
+		}
+		$PHPCAS_CLIENT->logout(array (
+			"service" => $service,
+			"url" => $url
+		));
+		// never reached
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * Set the fixed URL that will be used by the CAS server to transmit the PGT.
+	 * When this method is not called, a phpCAS script uses its own URL for the callback.
+	 *
+	 * @param $url the URL
+	 */
+	function setFixedCallbackURL($url = '') {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if (!$PHPCAS_CLIENT->isProxy()) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if (gettype($url) != 'string') {
+			phpCAS :: error('type mismatched for parameter $url (should be `string\')');
+		}
+		$PHPCAS_CLIENT->setCallbackURL($url);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * Set the fixed URL that will be set as the CAS service parameter. When this
+	 * method is not called, a phpCAS script uses its own URL.
+	 *
+	 * @param $url the URL
+	 */
+	function setFixedServiceURL($url) {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if (gettype($url) != 'string') {
+			phpCAS :: error('type mismatched for parameter $url (should be `string\')');
+		}
+		$PHPCAS_CLIENT->setURL($url);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * Get the URL that is set as the CAS service parameter.
+	 */
+	function getServiceURL() {
+		global $PHPCAS_CLIENT;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		return ($PHPCAS_CLIENT->getURL());
+	}
+
+	/**
+	 * Retrieve a Proxy Ticket from the CAS server.
+	 */
+	function retrievePT($target_service, & $err_code, & $err_msg) {
+		global $PHPCAS_CLIENT;
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::proxy()');
+		}
+		if (gettype($target_service) != 'string') {
+			phpCAS :: error('type mismatched for parameter $target_service(should be `string\')');
+		}
+		return ($PHPCAS_CLIENT->retrievePT($target_service, $err_code, $err_msg));
+	}
+
+	/**
+	 * Set the certificate of the CAS server.
+	 *
+	 * @param $cert the PEM certificate
+	 */
+	function setCasServerCert($cert) {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()');
+		}
+		if (gettype($cert) != 'string') {
+			phpCAS :: error('type mismatched for parameter $cert (should be `string\')');
+		}
+		$PHPCAS_CLIENT->setCasServerCert($cert);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * Set the certificate of the CAS server CA.
+	 *
+	 * @param $cert the CA certificate
+	 */
+	function setCasServerCACert($cert) {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()');
+		}
+		if (gettype($cert) != 'string') {
+			phpCAS :: error('type mismatched for parameter $cert (should be `string\')');
+		}
+		$PHPCAS_CLIENT->setCasServerCACert($cert);
+		phpCAS :: traceEnd();
+	}
+
+	/**
+	 * Set no SSL validation for the CAS server.
+	 */
+	function setNoCasServerValidation() {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()');
+		}
+		$PHPCAS_CLIENT->setNoCasServerValidation();
+		phpCAS :: traceEnd();
+	}
+
+	/** @} */
+
+	/**
+	 * Change CURL options.
+	 * CURL is used to connect through HTTPS to CAS server
+	 * @param $key the option key
+	 * @param $value the value to set
+	 */
+	function setExtraCurlOption($key, $value) {
+		global $PHPCAS_CLIENT;
+		phpCAS :: traceBegin();
+		if (!is_object($PHPCAS_CLIENT)) {
+			phpCAS :: error('this method should only be called after ' . __CLASS__ . '::client() or' . __CLASS__ . '::proxy()');
+		}
+		$PHPCAS_CLIENT->setExtraCurlOption($key, $value);
+		phpCAS :: traceEnd();
+	}
+
+}
+
+// ########################################################################
+// DOCUMENTATION
+// ########################################################################
+
+// ########################################################################
+//  MAIN PAGE
+
+/**
+ * @mainpage
+ *
+ * The following pages only show the source documentation.
+ *
+ */
+
+// ########################################################################
+//  MODULES DEFINITION
+
+/** @defgroup public User interface */
+
+/** @defgroup publicInit Initialization
+ *  @ingroup public */
+
+/** @defgroup publicAuth Authentication
+ *  @ingroup public */
+
+/** @defgroup publicServices Access to external services
+ *  @ingroup public */
+
+/** @defgroup publicConfig Configuration
+ *  @ingroup public */
+
+/** @defgroup publicLang Internationalization
+ *  @ingroup publicConfig */
+
+/** @defgroup publicOutput HTML output
+ *  @ingroup publicConfig */
+
+/** @defgroup publicPGTStorage PGT storage
+ *  @ingroup publicConfig */
+
+/** @defgroup publicDebug Debugging
+ *  @ingroup public */
+
+/** @defgroup internal Implementation */
+
+/** @defgroup internalAuthentication Authentication
+ *  @ingroup internal */
+
+/** @defgroup internalBasic CAS Basic client features (CAS 1.0, Service Tickets)
+ *  @ingroup internal */
+
+/** @defgroup internalProxy CAS Proxy features (CAS 2.0, Proxy Granting Tickets)
+ *  @ingroup internal */
+
+/** @defgroup internalPGTStorage PGT storage
+ *  @ingroup internalProxy */
+
+/** @defgroup internalPGTStorageDB PGT storage in a database
+ *  @ingroup internalPGTStorage */
+
+/** @defgroup internalPGTStorageFile PGT storage on the filesystem
+ *  @ingroup internalPGTStorage */
+
+/** @defgroup internalCallback Callback from the CAS server
+ *  @ingroup internalProxy */
+
+/** @defgroup internalProxied CAS proxied client features (CAS 2.0, Proxy Tickets)
+ *  @ingroup internal */
+
+/** @defgroup internalConfig Configuration
+ *  @ingroup internal */
+
+/** @defgroup internalOutput HTML output
+ *  @ingroup internalConfig */
+
+/** @defgroup internalLang Internationalization
+ *  @ingroup internalConfig
+ *
+ * To add a new language:
+ * - 1. define a new constant PHPCAS_LANG_XXXXXX in CAS/CAS.php
+ * - 2. copy any file from CAS/languages to CAS/languages/XXXXXX.php
+ * - 3. Make the translations
+ */
+
+/** @defgroup internalDebug Debugging
+ *  @ingroup internal */
+
+/** @defgroup internalMisc Miscellaneous
+ *  @ingroup internal */
+
+// ########################################################################
+//  EXAMPLES
+
+/**
+ * @example example_simple.php
+ */
+/**
+ * @example example_proxy.php
+ */
+/**
+ * @example example_proxy2.php
+ */
+/**
+ * @example example_lang.php
+ */
+/**
+ * @example example_html.php
+ */
+/**
+ * @example example_file.php
+ */
+/**
+ * @example example_db.php
+ */
+/**
+ * @example example_service.php
+ */
+/**
+ * @example example_session_proxy.php
+ */
+/**
+ * @example example_session_service.php
+ */
+/**
+ * @example example_gateway.php
+ */
+/**
+ * @example example_custom_urls.php
+ */
+?>

+ 218 - 0
main/auth/cas/lib/CAS/PGTStorage/pgt-db.php

@@ -0,0 +1,218 @@
+<?php
+/*
+ * Copyright © 2003-2010, The ESUP-Portail consortium & the JA-SIG Collaborative.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright notice,
+ *       this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright notice,
+ *       this list of conditions and the following disclaimer in the documentation
+ *       and/or other materials provided with the distribution.
+ *     * Neither the name of the ESUP-Portail consortium & the JA-SIG
+ *       Collaborative nor the names of its contributors may be used to endorse or
+ *       promote products derived from this software without specific prior
+ *       written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file CAS/PGTStorage/pgt-db.php
+ * Basic class for PGT database storage
+ */
+
+/**
+ * @class PGTStorageDB
+ * The PGTStorageDB class is a class for PGT database storage. An instance of 
+ * this class is returned by CASClient::SetPGTStorageDB().
+ *
+ * @author Pascal Aubry <pascal.aubry at univ-rennes1.fr>
+ *
+ * @ingroup internalPGTStorageDB
+ */
+
+class PGTStorageDB extends PGTStorage
+{
+  /** 
+   * @addtogroup internalPGTStorageDB
+   * @{ 
+   */
+
+  /**
+   * a string representing a PEAR DB URL to connect to the database. Written by
+   * PGTStorageDB::PGTStorageDB(), read by getURL().
+   *
+   * @hideinitializer
+   * @private
+   */
+  var $_url='';
+
+  /**
+   * This method returns the PEAR DB URL to use to connect to the database.
+   *
+   * @return a PEAR DB URL
+   *
+   * @private
+   */
+  function getURL()
+    {
+      return $this->_url;
+    }
+
+  /**
+   * The handle of the connection to the database where PGT's are stored. Written by
+   * PGTStorageDB::init(), read by getLink().
+   *
+   * @hideinitializer
+   * @private
+   */
+  var $_link = null;
+
+  /**
+   * This method returns the handle of the connection to the database where PGT's are 
+   * stored.
+   *
+   * @return a handle of connection.
+   *
+   * @private
+   */
+  function getLink()
+    {
+      return $this->_link;
+    }
+
+  /**
+   * The name of the table where PGT's are stored. Written by 
+   * PGTStorageDB::PGTStorageDB(), read by getTable().
+   *
+   * @hideinitializer
+   * @private
+   */
+  var $_table = '';
+
+  /**
+   * This method returns the name of the table where PGT's are stored.
+   *
+   * @return the name of a table.
+   *
+   * @private
+   */
+  function getTable()
+    {
+      return $this->_table;
+    }
+
+  // ########################################################################
+  //  DEBUGGING
+  // ########################################################################
+  
+  /**
+   * This method returns an informational string giving the type of storage
+   * used by the object (used for debugging purposes).
+   *
+   * @return an informational string.
+   * @public
+   */
+  function getStorageType()
+    {
+      return "database";
+    }
+
+  /**
+   * This method returns an informational string giving informations on the
+   * parameters of the storage.(used for debugging purposes).
+   *
+   * @public
+   */
+  function getStorageInfo()
+    {
+      return 'url=`'.$this->getURL().'\', table=`'.$this->getTable().'\'';
+    }
+
+  // ########################################################################
+  //  CONSTRUCTOR
+  // ########################################################################
+  
+  /**
+   * The class constructor, called by CASClient::SetPGTStorageDB().
+   *
+   * @param $cas_parent the CASClient instance that creates the object.
+   * @param $user the user to access the data with
+   * @param $password the user's password
+   * @param $database_type the type of the database hosting the data
+   * @param $hostname the server hosting the database
+   * @param $port the port the server is listening on
+   * @param $database the name of the database
+   * @param $table the name of the table storing the data
+   *
+   * @public
+   */
+  function PGTStorageDB($cas_parent,$user,$password,$database_type,$hostname,$port,$database,$table)
+    {
+      phpCAS::traceBegin();
+
+      // call the ancestor's constructor
+      $this->PGTStorage($cas_parent);
+
+      if ( empty($database_type) ) $database_type = CAS_PGT_STORAGE_DB_DEFAULT_DATABASE_TYPE;
+      if ( empty($hostname) ) $hostname = CAS_PGT_STORAGE_DB_DEFAULT_HOSTNAME;
+      if ( $port==0 ) $port = CAS_PGT_STORAGE_DB_DEFAULT_PORT;
+      if ( empty($database) ) $database = CAS_PGT_STORAGE_DB_DEFAULT_DATABASE;
+      if ( empty($table) ) $table = CAS_PGT_STORAGE_DB_DEFAULT_TABLE;
+
+      // build and store the PEAR DB URL
+      $this->_url = $database_type.':'.'//'.$user.':'.$password.'@'.$hostname.':'.$port.'/'.$database;
+
+      // XXX should use setURL and setTable
+      phpCAS::traceEnd();
+    }
+  
+  // ########################################################################
+  //  INITIALIZATION
+  // ########################################################################
+  
+  /**
+   * This method is used to initialize the storage. Halts on error.
+   *
+   * @public
+   */
+  function init()
+    {
+      phpCAS::traceBegin();
+      // if the storage has already been initialized, return immediatly
+      if ( $this->isInitialized() )
+		return;
+      // call the ancestor's method (mark as initialized)
+      parent::init();
+      
+	  //include phpDB library (the test was introduced in release 0.4.8 for 
+	  //the integration into Tikiwiki).
+	  if (!class_exists('DB')) {
+		include_once('DB.php');
+	  }
+
+      // try to connect to the database
+      $this->_link = DB::connect($this->getURL());
+      if ( DB::isError($this->_link) ) {
+	phpCAS::error('could not connect to database ('.DB::errorMessage($this->_link).')');
+      }
+      var_dump($this->_link);
+      phpCAS::traceBEnd();
+    }
+
+  /** @} */
+}
+
+?>

+ 276 - 0
main/auth/cas/lib/CAS/PGTStorage/pgt-file.php

@@ -0,0 +1,276 @@
+<?php
+/*
+ * Copyright © 2003-2010, The ESUP-Portail consortium & the JA-SIG Collaborative.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright notice,
+ *       this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright notice,
+ *       this list of conditions and the following disclaimer in the documentation
+ *       and/or other materials provided with the distribution.
+ *     * Neither the name of the ESUP-Portail consortium & the JA-SIG
+ *       Collaborative nor the names of its contributors may be used to endorse or
+ *       promote products derived from this software without specific prior
+ *       written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * @file CAS/PGTStorage/pgt-file.php
+ * Basic class for PGT file storage
+ */
+
+/**
+ * @class PGTStorageFile
+ * The PGTStorageFile class is a class for PGT file storage. An instance of 
+ * this class is returned by CASClient::SetPGTStorageFile().
+ *
+ * @author Pascal Aubry <pascal.aubry at univ-rennes1.fr>
+ *
+ * @ingroup internalPGTStorageFile
+ */
+
+class PGTStorageFile extends PGTStorage
+{
+  /** 
+   * @addtogroup internalPGTStorageFile 
+   * @{ 
+   */
+
+  /**
+   * a string telling where PGT's should be stored on the filesystem. Written by
+   * PGTStorageFile::PGTStorageFile(), read by getPath().
+   *
+   * @private
+   */
+  var $_path;
+
+  /**
+   * This method returns the name of the directory where PGT's should be stored 
+   * on the filesystem.
+   *
+   * @return the name of a directory (with leading and trailing '/')
+   *
+   * @private
+   */
+  function getPath()
+    {
+      return $this->_path;
+    }
+
+  /**
+   * a string telling the format to use to store PGT's (plain or xml). Written by
+   * PGTStorageFile::PGTStorageFile(), read by getFormat().
+   *
+   * @private
+   */
+  var $_format;
+
+  /**
+   * This method returns the format to use when storing PGT's on the filesystem.
+   *
+   * @return a string corresponding to the format used (plain or xml).
+   *
+   * @private
+   */
+  function getFormat()
+    {
+      return $this->_format;
+    }
+
+  // ########################################################################
+  //  DEBUGGING
+  // ########################################################################
+  
+  /**
+   * This method returns an informational string giving the type of storage
+   * used by the object (used for debugging purposes).
+   *
+   * @return an informational string.
+   * @public
+   */
+  function getStorageType()
+    {
+      return "file";
+    }
+
+  /**
+   * This method returns an informational string giving informations on the
+   * parameters of the storage.(used for debugging purposes).
+   *
+   * @return an informational string.
+   * @public
+   */
+  function getStorageInfo()
+    {
+      return 'path=`'.$this->getPath().'\', format=`'.$this->getFormat().'\'';
+    }
+
+  // ########################################################################
+  //  CONSTRUCTOR
+  // ########################################################################
+  
+  /**
+   * The class constructor, called by CASClient::SetPGTStorageFile().
+   *
+   * @param $cas_parent the CASClient instance that creates the object.
+   * @param $format the format used to store the PGT's (`plain' and `xml' allowed).
+   * @param $path the path where the PGT's should be stored
+   *
+   * @public
+   */
+  function PGTStorageFile($cas_parent,$format,$path)
+    {
+      phpCAS::traceBegin();
+      // call the ancestor's constructor
+      $this->PGTStorage($cas_parent);
+
+      if (empty($format) ) $format = CAS_PGT_STORAGE_FILE_DEFAULT_FORMAT;
+      if (empty($path) ) $path = CAS_PGT_STORAGE_FILE_DEFAULT_PATH;
+
+      // check that the path is an absolute path
+      if (getenv("OS")=="Windows_NT"){
+      	
+      	 if (!preg_match('`^[a-zA-Z]:`', $path)) {
+	     	phpCAS::error('an absolute path is needed for PGT storage to file');
+      	}
+      	
+      }
+      else
+      {
+      
+      	if ( $path[0] != '/' ) {
+			phpCAS::error('an absolute path is needed for PGT storage to file');
+      	}
+
+      	// store the path (with a leading and trailing '/')      
+      	$path = preg_replace('|[/]*$|','/',$path);
+      	$path = preg_replace('|^[/]*|','/',$path);
+      }
+      
+      $this->_path = $path;
+      // check the format and store it
+      switch ($format) {
+      case CAS_PGT_STORAGE_FILE_FORMAT_PLAIN:
+      case CAS_PGT_STORAGE_FILE_FORMAT_XML:
+	$this->_format = $format;
+	break;
+      default:
+	phpCAS::error('unknown PGT file storage format (`'.CAS_PGT_STORAGE_FILE_FORMAT_PLAIN.'\' and `'.CAS_PGT_STORAGE_FILE_FORMAT_XML.'\' allowed)');
+      }
+      phpCAS::traceEnd();      
+    }
+
+  // ########################################################################
+  //  INITIALIZATION
+  // ########################################################################
+  
+  /**
+   * This method is used to initialize the storage. Halts on error.
+   *
+   * @public
+   */
+  function init()
+    {
+      phpCAS::traceBegin();
+      // if the storage has already been initialized, return immediatly
+      if ( $this->isInitialized() )
+	return;
+      // call the ancestor's method (mark as initialized)
+      parent::init();
+      phpCAS::traceEnd();      
+    }
+
+  // ########################################################################
+  //  PGT I/O
+  // ########################################################################
+
+  /**
+   * This method returns the filename corresponding to a PGT Iou.
+   *
+   * @param $pgt_iou the PGT iou.
+   *
+   * @return a filename
+   * @private
+   */
+  function getPGTIouFilename($pgt_iou)
+    {
+      phpCAS::traceBegin();
+      $filename = $this->getPath().$pgt_iou.'.'.$this->getFormat();
+      phpCAS::traceEnd($filename);
+      return $filename;
+    }
+  
+  /**
+   * This method stores a PGT and its corresponding PGT Iou into a file. Echoes a
+   * warning on error.
+   *
+   * @param $pgt the PGT
+   * @param $pgt_iou the PGT iou
+   *
+   * @public
+   */
+  function write($pgt,$pgt_iou)
+    {
+      phpCAS::traceBegin();
+      $fname = $this->getPGTIouFilename($pgt_iou);
+      if ( $f=fopen($fname,"w") ) {
+	if ( fputs($f,$pgt) === FALSE ) {
+	  phpCAS::error('could not write PGT to `'.$fname.'\'');
+	}
+	fclose($f);
+      } else {
+	phpCAS::error('could not open `'.$fname.'\'');
+      }
+      phpCAS::traceEnd();      
+    }
+
+  /**
+   * This method reads a PGT corresponding to a PGT Iou and deletes the 
+   * corresponding file.
+   *
+   * @param $pgt_iou the PGT iou
+   *
+   * @return the corresponding PGT, or FALSE on error
+   *
+   * @public
+   */
+  function read($pgt_iou)
+    {
+      phpCAS::traceBegin();
+      $pgt = FALSE;
+      $fname = $this->getPGTIouFilename($pgt_iou);
+      if ( !($f=fopen($fname,"r")) ) {
+	phpCAS::trace('could not open `'.$fname.'\'');
+      } else {
+	if ( ($pgt=fgets($f)) === FALSE ) {
+	  phpCAS::trace('could not read PGT from `'.$fname.'\'');
+	} 
+	fclose($f);
+      }
+
+      // delete the PGT file
+      @unlink($fname);
+
+      phpCAS::traceEnd($pgt);
+      return $pgt;
+    }
+  
+  /** @} */
+  
+}
+
+  
+?>

+ 215 - 0
main/auth/cas/lib/CAS/PGTStorage/pgt-main.php

@@ -0,0 +1,215 @@
+<?php
+/*
+ * Copyright © 2003-2010, The ESUP-Portail consortium & the JA-SIG Collaborative.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright notice,
+ *       this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright notice,
+ *       this list of conditions and the following disclaimer in the documentation
+ *       and/or other materials provided with the distribution.
+ *     * Neither the name of the ESUP-Portail consortium & the JA-SIG
+ *       Collaborative nor the names of its contributors may be used to endorse or
+ *       promote products derived from this software without specific prior
+ *       written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * @file CAS/PGTStorage/pgt-main.php
+ * Basic class for PGT storage
+ */
+
+/**
+ * @class PGTStorage
+ * The PGTStorage class is a generic class for PGT storage. This class should
+ * not be instanciated itself but inherited by specific PGT storage classes.
+ *
+ * @author   Pascal Aubry <pascal.aubry at univ-rennes1.fr>
+ *
+ * @ingroup internalPGTStorage
+ */
+
+class PGTStorage
+{
+  /** 
+   * @addtogroup internalPGTStorage
+   * @{ 
+   */
+
+  // ########################################################################
+  //  CONSTRUCTOR
+  // ########################################################################
+  
+  /**
+   * The constructor of the class, should be called only by inherited classes.
+   *
+   * @param $cas_parent the CASclient instance that creates the current object.
+   *
+   * @protected
+   */
+  function PGTStorage($cas_parent)
+    {
+      phpCAS::traceBegin();
+      if ( !$cas_parent->isProxy() ) {
+	phpCAS::error('defining PGT storage makes no sense when not using a CAS proxy'); 
+      }
+      phpCAS::traceEnd();
+    }
+
+  // ########################################################################
+  //  DEBUGGING
+  // ########################################################################
+  
+  /**
+   * This virtual method returns an informational string giving the type of storage
+   * used by the object (used for debugging purposes).
+   *
+   * @public
+   */
+  function getStorageType()
+    {
+      phpCAS::error(__CLASS__.'::'.__FUNCTION__.'() should never be called'); 
+    }
+
+  /**
+   * This virtual method returns an informational string giving informations on the
+   * parameters of the storage.(used for debugging purposes).
+   *
+   * @public
+   */
+  function getStorageInfo()
+    {
+      phpCAS::error(__CLASS__.'::'.__FUNCTION__.'() should never be called'); 
+    }
+
+  // ########################################################################
+  //  ERROR HANDLING
+  // ########################################################################
+  
+  /**
+   * string used to store an error message. Written by PGTStorage::setErrorMessage(),
+   * read by PGTStorage::getErrorMessage().
+   *
+   * @hideinitializer
+   * @private
+   * @deprecated not used.
+   */
+  var $_error_message=FALSE;
+
+  /**
+   * This method sets en error message, which can be read later by 
+   * PGTStorage::getErrorMessage().
+   *
+   * @param $error_message an error message
+   *
+   * @protected
+   * @deprecated not used.
+   */
+  function setErrorMessage($error_message)
+    {
+      $this->_error_message = $error_message;
+    }
+
+  /**
+   * This method returns an error message set by PGTStorage::setErrorMessage().
+   *
+   * @return an error message when set by PGTStorage::setErrorMessage(), FALSE
+   * otherwise.
+   *
+   * @public
+   * @deprecated not used.
+   */
+  function getErrorMessage()
+    {
+      return $this->_error_message;
+    }
+
+  // ########################################################################
+  //  INITIALIZATION
+  // ########################################################################
+
+  /**
+   * a boolean telling if the storage has already been initialized. Written by 
+   * PGTStorage::init(), read by PGTStorage::isInitialized().
+   *
+   * @hideinitializer
+   * @private
+   */
+  var $_initialized = FALSE;
+
+  /**
+   * This method tells if the storage has already been intialized.
+   *
+   * @return a boolean
+   *
+   * @protected
+   */
+  function isInitialized()
+    {
+      return $this->_initialized;
+    }
+
+  /**
+   * This virtual method initializes the object.
+   *
+   * @protected
+   */
+  function init()
+    {
+      $this->_initialized = TRUE;
+    }
+
+  // ########################################################################
+  //  PGT I/O
+  // ########################################################################
+
+  /**
+   * This virtual method stores a PGT and its corresponding PGT Iuo.
+   * @note Should never be called.
+   *
+   * @param $pgt the PGT
+   * @param $pgt_iou the PGT iou
+   *
+   * @protected
+   */
+  function write($pgt,$pgt_iou)
+    {
+      phpCAS::error(__CLASS__.'::'.__FUNCTION__.'() should never be called'); 
+    }
+
+  /**
+   * This virtual method reads a PGT corresponding to a PGT Iou and deletes
+   * the corresponding storage entry.
+   * @note Should never be called.
+   *
+   * @param $pgt_iou the PGT iou
+   *
+   * @protected
+   */
+  function read($pgt_iou)
+    {
+      phpCAS::error(__CLASS__.'::'.__FUNCTION__.'() should never be called'); 
+    }
+
+  /** @} */
+  
+} 
+
+// include specific PGT storage classes
+include_once(dirname(__FILE__).'/pgt-file.php'); 
+include_once(dirname(__FILE__).'/pgt-db.php');
+  
+?>

+ 2700 - 0
main/auth/cas/lib/CAS/client.php

@@ -0,0 +1,2700 @@
+<?php
+
+/*
+ * Copyright © 2003-2010, The ESUP-Portail consortium & the JA-SIG Collaborative.
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright notice,
+ *       this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright notice,
+ *       this list of conditions and the following disclaimer in the documentation
+ *       and/or other materials provided with the distribution.
+ *     * Neither the name of the ESUP-Portail consortium & the JA-SIG
+ *       Collaborative nor the names of its contributors may be used to endorse or
+ *       promote products derived from this software without specific prior
+ *       written permission.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * @file CAS/client.php
+ * Main class of the phpCAS library
+ */
+
+// include internationalization stuff
+include_once(dirname(__FILE__).'/languages/languages.php');
+
+// include PGT storage classes
+include_once(dirname(__FILE__).'/PGTStorage/pgt-main.php');
+
+/**
+ * @class CASClient
+ * The CASClient class is a client interface that provides CAS authentication
+ * to PHP applications.
+ *
+ * @author Pascal Aubry <pascal.aubry at univ-rennes1.fr>
+ */
+
+class CASClient
+{
+	
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	// XX                                                                    XX
+	// XX                          CONFIGURATION                             XX
+	// XX                                                                    XX
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	
+	// ########################################################################
+	//  HTML OUTPUT
+	// ########################################################################
+	/**
+	 * @addtogroup internalOutput
+	 * @{
+	 */  
+	
+	/**
+	 * This method filters a string by replacing special tokens by appropriate values
+	 * and prints it. The corresponding tokens are taken into account:
+	 * - __CAS_VERSION__
+	 * - __PHPCAS_VERSION__
+	 * - __SERVER_BASE_URL__
+	 *
+	 * Used by CASClient::PrintHTMLHeader() and CASClient::printHTMLFooter().
+	 *
+	 * @param $str the string to filter and output
+	 *
+	 * @private
+	 */
+	function HTMLFilterOutput($str)
+		{
+		$str = str_replace('__CAS_VERSION__',$this->getServerVersion(),$str);
+		$str = str_replace('__PHPCAS_VERSION__',phpCAS::getVersion(),$str);
+		$str = str_replace('__SERVER_BASE_URL__',$this->getServerBaseURL(),$str);
+		echo $str;
+		}
+	
+	/**
+	 * A string used to print the header of HTML pages. Written by CASClient::setHTMLHeader(),
+	 * read by CASClient::printHTMLHeader().
+	 *
+	 * @hideinitializer
+	 * @private
+	 * @see CASClient::setHTMLHeader, CASClient::printHTMLHeader()
+	 */
+	var $_output_header = '';
+	
+	/**
+	 * This method prints the header of the HTML output (after filtering). If
+	 * CASClient::setHTMLHeader() was not used, a default header is output.
+	 *
+	 * @param $title the title of the page
+	 *
+	 * @see HTMLFilterOutput()
+	 * @private
+	 */
+	function printHTMLHeader($title)
+		{
+		$this->HTMLFilterOutput(str_replace('__TITLE__',
+			$title,
+			(empty($this->_output_header)
+					? '<html><head><title>__TITLE__</title></head><body><h1>__TITLE__</h1>'
+							: $this->_output_header)
+		)
+		);
+		}
+	
+	/**
+	 * A string used to print the footer of HTML pages. Written by CASClient::setHTMLFooter(),
+	 * read by printHTMLFooter().
+	 *
+	 * @hideinitializer
+	 * @private
+	 * @see CASClient::setHTMLFooter, CASClient::printHTMLFooter()
+	 */
+	var $_output_footer = '';
+	
+	/**
+	 * This method prints the footer of the HTML output (after filtering). If
+	 * CASClient::setHTMLFooter() was not used, a default footer is output.
+	 *
+	 * @see HTMLFilterOutput()
+	 * @private
+	 */
+	function printHTMLFooter()
+		{
+		$this->HTMLFilterOutput(empty($this->_output_footer)
+			?('<hr><address>phpCAS __PHPCAS_VERSION__ '.$this->getString(CAS_STR_USING_SERVER).' <a href="__SERVER_BASE_URL__">__SERVER_BASE_URL__</a> (CAS __CAS_VERSION__)</a></address></body></html>')
+					:$this->_output_footer);
+		}
+	
+	/**
+	 * This method set the HTML header used for all outputs.
+	 *
+	 * @param $header the HTML header.
+	 *
+	 * @public
+	 */
+	function setHTMLHeader($header)
+		{
+		$this->_output_header = $header;
+		}
+	
+	/**
+	 * This method set the HTML footer used for all outputs.
+	 *
+	 * @param $footer the HTML footer.
+	 *
+	 * @public
+	 */
+	function setHTMLFooter($footer)
+		{
+		$this->_output_footer = $footer;
+		}
+	
+	/** @} */
+	// ########################################################################
+	//  INTERNATIONALIZATION
+	// ########################################################################
+	/**
+	 * @addtogroup internalLang
+	 * @{
+	 */  
+	/**
+	 * A string corresponding to the language used by phpCAS. Written by 
+	 * CASClient::setLang(), read by CASClient::getLang().
+	 
+	 * @note debugging information is always in english (debug purposes only).
+	 *
+	 * @hideinitializer
+	 * @private
+	 * @sa CASClient::_strings, CASClient::getString()
+	 */
+	var $_lang = '';
+	
+	/**
+	 * This method returns the language used by phpCAS.
+	 *
+	 * @return a string representing the language
+	 *
+	 * @private
+	 */
+	function getLang()
+		{
+		if ( empty($this->_lang) )
+			$this->setLang(PHPCAS_LANG_DEFAULT);
+		return $this->_lang;
+		}
+	
+	/**
+	 * array containing the strings used by phpCAS. Written by CASClient::setLang(), read by 
+	 * CASClient::getString() and used by CASClient::setLang().
+	 *
+	 * @note This array is filled by instructions in CAS/languages/<$this->_lang>.php
+	 *
+	 * @private
+	 * @see CASClient::_lang, CASClient::getString(), CASClient::setLang(), CASClient::getLang()
+	 */
+	var $_strings;
+	
+	/**
+	 * This method returns a string depending on the language.
+	 *
+	 * @param $str the index of the string in $_string.
+	 *
+	 * @return the string corresponding to $index in $string.
+	 *
+	 * @private
+	 */
+	function getString($str)
+		{
+		// call CASclient::getLang() to be sure the language is initialized
+		$this->getLang();
+		
+		if ( !isset($this->_strings[$str]) ) {
+			trigger_error('string `'.$str.'\' not defined for language `'.$this->getLang().'\'',E_USER_ERROR);
+		}
+		return $this->_strings[$str];
+		}
+	
+	/**
+	 * This method is used to set the language used by phpCAS. 
+	 * @note Can be called only once.
+	 *
+	 * @param $lang a string representing the language.
+	 *
+	 * @public
+	 * @sa CAS_LANG_FRENCH, CAS_LANG_ENGLISH
+	 */
+	function setLang($lang)
+		{
+		// include the corresponding language file
+		include_once(dirname(__FILE__).'/languages/'.$lang.'.php');
+		
+		if ( !is_array($this->_strings) ) {
+			trigger_error('language `'.$lang.'\' is not implemented',E_USER_ERROR);
+		}
+		$this->_lang = $lang;
+		}
+	
+	/** @} */
+	// ########################################################################
+	//  CAS SERVER CONFIG
+	// ########################################################################
+	/**
+	 * @addtogroup internalConfig
+	 * @{
+	 */  
+	
+	/**
+	 * a record to store information about the CAS server.
+	 * - $_server["version"]: the version of the CAS server
+	 * - $_server["hostname"]: the hostname of the CAS server
+	 * - $_server["port"]: the port the CAS server is running on
+	 * - $_server["uri"]: the base URI the CAS server is responding on
+	 * - $_server["base_url"]: the base URL of the CAS server
+	 * - $_server["login_url"]: the login URL of the CAS server
+	 * - $_server["service_validate_url"]: the service validating URL of the CAS server
+	 * - $_server["proxy_url"]: the proxy URL of the CAS server
+	 * - $_server["proxy_validate_url"]: the proxy validating URL of the CAS server
+	 * - $_server["logout_url"]: the logout URL of the CAS server
+	 *
+	 * $_server["version"], $_server["hostname"], $_server["port"] and $_server["uri"]
+	 * are written by CASClient::CASClient(), read by CASClient::getServerVersion(), 
+	 * CASClient::getServerHostname(), CASClient::getServerPort() and CASClient::getServerURI().
+	 *
+	 * The other fields are written and read by CASClient::getServerBaseURL(), 
+	 * CASClient::getServerLoginURL(), CASClient::getServerServiceValidateURL(), 
+	 * CASClient::getServerProxyValidateURL() and CASClient::getServerLogoutURL().
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_server = array(
+		'version' => -1,
+		'hostname' => 'none',
+		'port' => -1,
+		'uri' => 'none'
+	);
+	
+	/**
+	 * This method is used to retrieve the version of the CAS server.
+	 * @return the version of the CAS server.
+	 * @private
+	 */
+	function getServerVersion()
+		{ 
+		return $this->_server['version']; 
+		}
+	
+	/**
+	 * This method is used to retrieve the hostname of the CAS server.
+	 * @return the hostname of the CAS server.
+	 * @private
+	 */
+	function getServerHostname()
+		{ return $this->_server['hostname']; }
+	
+	/**
+	 * This method is used to retrieve the port of the CAS server.
+	 * @return the port of the CAS server.
+	 * @private
+	 */
+	function getServerPort()
+		{ return $this->_server['port']; }
+	
+	/**
+	 * This method is used to retrieve the URI of the CAS server.
+	 * @return a URI.
+	 * @private
+	 */
+	function getServerURI()
+		{ return $this->_server['uri']; }
+	
+	/**
+	 * This method is used to retrieve the base URL of the CAS server.
+	 * @return a URL.
+	 * @private
+	 */
+	function getServerBaseURL()
+		{ 
+		// the URL is build only when needed
+		if ( empty($this->_server['base_url']) ) {
+			$this->_server['base_url'] = 'https://'
+				.$this->getServerHostname()
+				.':'
+				.$this->getServerPort()
+				.$this->getServerURI();
+		}
+		return $this->_server['base_url']; 
+		}
+	
+	/**
+	 * This method is used to retrieve the login URL of the CAS server.
+	 * @param $gateway true to check authentication, false to force it
+	 * @param $renew true to force the authentication with the CAS server
+	 * NOTE : It is recommended that CAS implementations ignore the
+	 "gateway" parameter if "renew" is set
+	 * @return a URL.
+	 * @private
+	 */
+	function getServerLoginURL($gateway=false,$renew=false) {
+		phpCAS::traceBegin();
+		// the URL is build only when needed
+		if ( empty($this->_server['login_url']) ) {
+			$this->_server['login_url'] = $this->getServerBaseURL();
+			$this->_server['login_url'] .= 'login?service=';
+			// $this->_server['login_url'] .= preg_replace('/&/','%26',$this->getURL());
+			$this->_server['login_url'] .= urlencode($this->getURL());
+			if($renew) {
+				// It is recommended that when the "renew" parameter is set, its value be "true"
+				$this->_server['login_url'] .= '&renew=true';
+			} elseif ($gateway) {
+				// It is recommended that when the "gateway" parameter is set, its value be "true"
+				$this->_server['login_url'] .= '&gateway=true';
+			}
+		}
+		phpCAS::traceEnd($this->_server['login_url']);
+		return $this->_server['login_url'];
+	} 
+	
+	/**
+	 * This method sets the login URL of the CAS server.
+	 * @param $url the login URL
+	 * @private
+	 * @since 0.4.21 by Wyman Chan
+	 */
+	function setServerLoginURL($url)
+		{
+		return $this->_server['login_url'] = $url;
+		}
+	
+	
+	/**
+	 * This method sets the serviceValidate URL of the CAS server.
+	 * @param $url the serviceValidate URL
+	 * @private
+	 * @since 1.1.0 by Joachim Fritschi
+	 */
+	function setServerServiceValidateURL($url)
+		{
+		return $this->_server['service_validate_url'] = $url;
+		}
+	
+	
+	/**
+	 * This method sets the proxyValidate URL of the CAS server.
+	 * @param $url the proxyValidate URL
+	 * @private
+	 * @since 1.1.0 by Joachim Fritschi
+	 */
+	function setServerProxyValidateURL($url)
+		{
+		return $this->_server['proxy_validate_url'] = $url;
+		}
+	
+	
+	/**
+	 * This method sets the samlValidate URL of the CAS server.
+	 * @param $url the samlValidate URL
+	 * @private
+	 * @since 1.1.0 by Joachim Fritschi
+	 */
+	function setServerSamlValidateURL($url)
+		{
+		return $this->_server['saml_validate_url'] = $url;
+		}
+	
+	
+	/**
+	 * This method is used to retrieve the service validating URL of the CAS server.
+	 * @return a URL.
+	 * @private
+	 */
+	function getServerServiceValidateURL()
+		{ 
+		// the URL is build only when needed
+		if ( empty($this->_server['service_validate_url']) ) {
+			switch ($this->getServerVersion()) {
+				case CAS_VERSION_1_0:
+					$this->_server['service_validate_url'] = $this->getServerBaseURL().'validate';
+					break;
+				case CAS_VERSION_2_0:
+					$this->_server['service_validate_url'] = $this->getServerBaseURL().'serviceValidate';
+					break;
+			}
+		}
+		//      return $this->_server['service_validate_url'].'?service='.preg_replace('/&/','%26',$this->getURL()); 
+		return $this->_server['service_validate_url'].'?service='.urlencode($this->getURL()); 
+		}
+	/**
+	 * This method is used to retrieve the SAML validating URL of the CAS server.
+	 * @return a URL.
+	 * @private
+	 */
+	function getServerSamlValidateURL()
+		{
+		phpCAS::traceBegin();
+		// the URL is build only when needed
+		if ( empty($this->_server['saml_validate_url']) ) {
+			switch ($this->getServerVersion()) {
+				case SAML_VERSION_1_1:
+					$this->_server['saml_validate_url'] = $this->getServerBaseURL().'samlValidate';
+					break;
+			}
+		}
+		phpCAS::traceEnd($this->_server['saml_validate_url'].'?TARGET='.urlencode($this->getURL()));
+		return $this->_server['saml_validate_url'].'?TARGET='.urlencode($this->getURL());
+		}
+	/**
+	 * This method is used to retrieve the proxy validating URL of the CAS server.
+	 * @return a URL.
+	 * @private
+	 */
+	function getServerProxyValidateURL()
+		{ 
+		// the URL is build only when needed
+		if ( empty($this->_server['proxy_validate_url']) ) {
+			switch ($this->getServerVersion()) {
+				case CAS_VERSION_1_0:
+					$this->_server['proxy_validate_url'] = '';
+					break;
+				case CAS_VERSION_2_0:
+					$this->_server['proxy_validate_url'] = $this->getServerBaseURL().'proxyValidate';
+					break;
+			}
+		}
+		//      return $this->_server['proxy_validate_url'].'?service='.preg_replace('/&/','%26',$this->getURL()); 
+		return $this->_server['proxy_validate_url'].'?service='.urlencode($this->getURL()); 
+		}
+	
+	/**
+	 * This method is used to retrieve the proxy URL of the CAS server.
+	 * @return a URL.
+	 * @private
+	 */
+	function getServerProxyURL()
+		{ 
+		// the URL is build only when needed
+		if ( empty($this->_server['proxy_url']) ) {
+			switch ($this->getServerVersion()) {
+				case CAS_VERSION_1_0:
+					$this->_server['proxy_url'] = '';
+					break;
+				case CAS_VERSION_2_0:
+					$this->_server['proxy_url'] = $this->getServerBaseURL().'proxy';
+					break;
+			}
+		}
+		return $this->_server['proxy_url']; 
+		}
+	
+	/**
+	 * This method is used to retrieve the logout URL of the CAS server.
+	 * @return a URL.
+	 * @private
+	 */
+	function getServerLogoutURL()
+		{ 
+		// the URL is build only when needed
+		if ( empty($this->_server['logout_url']) ) {
+			$this->_server['logout_url'] = $this->getServerBaseURL().'logout';
+		}
+		return $this->_server['logout_url']; 
+		}
+	
+	/**
+	 * This method sets the logout URL of the CAS server.
+	 * @param $url the logout URL
+	 * @private
+	 * @since 0.4.21 by Wyman Chan
+	 */
+	function setServerLogoutURL($url)
+		{
+		return $this->_server['logout_url'] = $url;
+		}
+	
+	/**
+	 * An array to store extra curl options.
+	 */	
+	var $_curl_options = array();
+	
+	/**
+	 * This method is used to set additional user curl options.
+	 */
+	function setExtraCurlOption($key, $value)
+		{
+		$this->_curl_options[$key] = $value;
+		}
+	
+	/**
+	 * This method checks to see if the request is secured via HTTPS
+	 * @return true if https, false otherwise
+	 * @private
+	 */
+	function isHttps() {
+		//if ( isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) ) {
+		//0.4.24 by Hinnack
+		if ( isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
+			return true;
+		} else {
+			return false;
+		}
+	}
+	
+	// ########################################################################
+	//  CONSTRUCTOR
+	// ########################################################################
+	/**
+	 * CASClient constructor.
+	 *
+	 * @param $server_version the version of the CAS server
+	 * @param $proxy TRUE if the CAS client is a CAS proxy, FALSE otherwise
+	 * @param $server_hostname the hostname of the CAS server
+	 * @param $server_port the port the CAS server is running on
+	 * @param $server_uri the URI the CAS server is responding on
+	 * @param $start_session Have phpCAS start PHP sessions (default true)
+	 *
+	 * @return a newly created CASClient object
+	 *
+	 * @public
+	 */
+	function CASClient(
+					   $server_version,
+					   $proxy,
+					   $server_hostname,
+					   $server_port,
+					   $server_uri,
+					   $start_session = true) {
+		
+		phpCAS::traceBegin();
+		
+		// the redirect header() call and DOM parsing code from domxml-php4-php5.php won't work in PHP4 compatibility mode
+		if (version_compare(PHP_VERSION,'5','>=') && ini_get('zend.ze1_compatibility_mode')) {
+			phpCAS::error('phpCAS cannot support zend.ze1_compatibility_mode. Sorry.');
+		}
+		// skip Session Handling for logout requests and if don't want it'
+		if ($start_session && !$this->isLogoutRequest()) {
+			phpCAS::trace("Starting session handling");
+			// Check for Tickets from the CAS server
+			if (empty($_GET['ticket'])){
+				phpCAS::trace("No ticket found");
+				// only create a session if necessary
+				if (!session_id()) {
+					phpCAS::trace("No session found, creating new session");
+					session_start();
+				}
+			}else{
+				phpCAS::trace("Ticket found");
+				// We have to copy any old data before renaming the session
+				if (session_id()) {
+					phpCAS::trace("Old active session found, saving old data and destroying session");
+					$old_session = $_SESSION;
+					session_destroy();	
+				}else{
+					session_start();
+					phpCAS::trace("Starting possible old session to copy variables");
+					$old_session = $_SESSION;
+					session_destroy();	
+				}
+				// set up a new session, of name based on the ticket
+				$session_id = preg_replace('/[^\w]/','',$_GET['ticket']);
+				phpCAS::LOG("Session ID: " . $session_id);
+				session_id($session_id);
+				session_start();
+				// restore old session vars
+				if(isset($old_session)){
+					phpCAS::trace("Restoring old session vars");
+					$_SESSION = $old_session;
+				}
+			}
+		}else{
+			phpCAS::trace("Skipping session creation");
+		}
+		
+		
+		// are we in proxy mode ?
+		$this->_proxy = $proxy;
+		
+		//check version
+		switch ($server_version) {
+			case CAS_VERSION_1_0:
+				if ( $this->isProxy() )
+					phpCAS::error('CAS proxies are not supported in CAS '
+						.$server_version);
+				break;
+			case CAS_VERSION_2_0:
+				break;
+			case SAML_VERSION_1_1:
+				break;
+			default:
+				phpCAS::error('this version of CAS (`'
+					.$server_version
+					.'\') is not supported by phpCAS '
+					.phpCAS::getVersion());
+		}
+		$this->_server['version'] = $server_version;
+		
+		// check hostname
+		if ( empty($server_hostname) 
+				|| !preg_match('/[\.\d\-abcdefghijklmnopqrstuvwxyz]*/',$server_hostname) ) {
+			phpCAS::error('bad CAS server hostname (`'.$server_hostname.'\')');
+		}
+		$this->_server['hostname'] = $server_hostname;
+		
+		// check port
+		if ( $server_port == 0 
+			|| !is_int($server_port) ) {
+			phpCAS::error('bad CAS server port (`'.$server_hostname.'\')');
+		}
+		$this->_server['port'] = $server_port;
+		
+		// check URI
+		if ( !preg_match('/[\.\d\-_abcdefghijklmnopqrstuvwxyz\/]*/',$server_uri) ) {
+			phpCAS::error('bad CAS server URI (`'.$server_uri.'\')');
+		}
+		// add leading and trailing `/' and remove doubles      
+		$server_uri = preg_replace('/\/\//','/','/'.$server_uri.'/');
+		$this->_server['uri'] = $server_uri;
+		
+		// set to callback mode if PgtIou and PgtId CGI GET parameters are provided 
+		if ( $this->isProxy() ) {
+			$this->setCallbackMode(!empty($_GET['pgtIou'])&&!empty($_GET['pgtId']));
+		}
+		
+		if ( $this->isCallbackMode() ) {
+			//callback mode: check that phpCAS is secured
+			if ( !$this->isHttps() ) {
+				phpCAS::error('CAS proxies must be secured to use phpCAS; PGT\'s will not be received from the CAS server');
+			}
+		} else {
+			//normal mode: get ticket and remove it from CGI parameters for developpers
+			$ticket = (isset($_GET['ticket']) ? $_GET['ticket'] : null);
+			switch ($this->getServerVersion()) {
+				case CAS_VERSION_1_0: // check for a Service Ticket
+					if( preg_match('/^ST-/',$ticket) ) {
+						phpCAS::trace('ST \''.$ticket.'\' found');
+						//ST present
+						$this->setST($ticket);
+						//ticket has been taken into account, unset it to hide it to applications
+						unset($_GET['ticket']);
+					} else if ( !empty($ticket) ) {
+						//ill-formed ticket, halt
+						phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
+					}
+					break;
+				case CAS_VERSION_2_0: // check for a Service or Proxy Ticket
+					if( preg_match('/^[SP]T-/',$ticket) ) {
+						phpCAS::trace('ST or PT \''.$ticket.'\' found');
+						$this->setPT($ticket);
+						unset($_GET['ticket']);
+					} else if ( !empty($ticket) ) {
+						//ill-formed ticket, halt
+						phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
+					} 
+					break;
+				case SAML_VERSION_1_1: // SAML just does Service Tickets
+					if( preg_match('/^[SP]T-/',$ticket) ) {
+						phpCAS::trace('SA \''.$ticket.'\' found');
+						$this->setSA($ticket);
+						unset($_GET['ticket']);
+					} else if ( !empty($ticket) ) {
+						//ill-formed ticket, halt
+						phpCAS::error('ill-formed ticket found in the URL (ticket=`'.htmlentities($ticket).'\')');
+					}
+					break;
+			}
+		}
+		phpCAS::traceEnd();
+	}
+	
+	/** @} */
+	
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	// XX                                                                    XX
+	// XX                           AUTHENTICATION                           XX
+	// XX                                                                    XX
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	
+	/**
+	 * @addtogroup internalAuthentication
+	 * @{
+	 */  
+	
+	/**
+	 * The Authenticated user. Written by CASClient::setUser(), read by CASClient::getUser().
+	 * @attention client applications should use phpCAS::getUser().
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_user = '';
+	
+	/**
+	 * This method sets the CAS user's login name.
+	 *
+	 * @param $user the login name of the authenticated user.
+	 *
+	 * @private
+	 */
+	function setUser($user)
+		{
+		$this->_user = $user;
+		}
+	
+	/**
+	 * This method returns the CAS user's login name.
+	 * @warning should be called only after CASClient::forceAuthentication() or 
+	 * CASClient::isAuthenticated(), otherwise halt with an error.
+	 *
+	 * @return the login name of the authenticated user
+	 */
+	function getUser()
+		{
+		if ( empty($this->_user) ) {
+			phpCAS::error('this method should be used only after '.__CLASS__.'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()');
+		}
+		return $this->_user;
+		}
+	
+	
+	
+	/***********************************************************************************************************************
+	 * Atrributes section
+	 * 
+	 * @author Matthias Crauwels <matthias.crauwels@ugent.be>, Ghent University, Belgium
+	 * 
+	 ***********************************************************************************************************************/
+	/**
+	 * The Authenticated users attributes. Written by CASClient::setAttributes(), read by CASClient::getAttributes().
+	 * @attention client applications should use phpCAS::getAttributes().
+	 *
+	 * @hideinitializer
+	 * @private
+	 */	
+	var $_attributes = array();
+	
+	function setAttributes($attributes)	
+		{ $this->_attributes = $attributes; }
+	
+	function getAttributes() {
+		if ( empty($this->_user) ) { // if no user is set, there shouldn't be any attributes also...
+			phpCAS::error('this method should be used only after '.__CLASS__.'::forceAuthentication() or '.__CLASS__.'::isAuthenticated()');
+		}
+		return $this->_attributes;
+	}
+	
+	function hasAttributes()
+		{ return !empty($this->_attributes); }
+	
+	function hasAttribute($key)
+		{ return (is_array($this->_attributes) && array_key_exists($key, $this->_attributes)); }
+	
+	function getAttribute($key)	{
+		if($this->hasAttribute($key)) {
+			return $this->_attributes[$key];
+		}
+	}
+	
+	/**
+	 * This method is called to renew the authentication of the user
+	 * If the user is authenticated, renew the connection
+	 * If not, redirect to CAS
+	 * @public
+	 */
+	function renewAuthentication(){
+		phpCAS::traceBegin();
+		// Either way, the user is authenticated by CAS
+		if( isset( $_SESSION['phpCAS']['auth_checked'] ) )
+			unset($_SESSION['phpCAS']['auth_checked']);
+		if ( $this->isAuthenticated() ) {
+			phpCAS::trace('user already authenticated; renew');
+			$this->redirectToCas(false,true);
+		} else {
+			$this->redirectToCas();
+		}
+		phpCAS::traceEnd();
+	}
+	
+	/**
+	 * This method is called to be sure that the user is authenticated. When not 
+	 * authenticated, halt by redirecting to the CAS server; otherwise return TRUE.
+	 * @return TRUE when the user is authenticated; otherwise halt.
+	 * @public
+	 */
+	function forceAuthentication()
+		{
+		phpCAS::traceBegin();
+		
+		if ( $this->isAuthenticated() ) {
+			// the user is authenticated, nothing to be done.
+			phpCAS::trace('no need to authenticate');
+			$res = TRUE;
+		} else {
+			// the user is not authenticated, redirect to the CAS server
+			if (isset($_SESSION['phpCAS']['auth_checked'])) {
+				unset($_SESSION['phpCAS']['auth_checked']);
+			}
+			$this->redirectToCas(FALSE/* no gateway */);	
+			// never reached
+			$res = FALSE;
+		}
+		phpCAS::traceEnd($res);
+		return $res;
+		}
+	
+	/**
+	 * An integer that gives the number of times authentication will be cached before rechecked.
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_cache_times_for_auth_recheck = 0;
+	
+	/**
+	 * Set the number of times authentication will be cached before rechecked.
+	 *
+	 * @param $n an integer.
+	 *
+	 * @public
+	 */
+	function setCacheTimesForAuthRecheck($n)
+		{
+		$this->_cache_times_for_auth_recheck = $n;
+		}
+	
+	/**
+	 * This method is called to check whether the user is authenticated or not.
+	 * @return TRUE when the user is authenticated, FALSE otherwise.
+	 * @public
+	 */
+	function checkAuthentication()
+		{
+		phpCAS::traceBegin();
+		
+		if ( $this->isAuthenticated() ) {
+			phpCAS::trace('user is authenticated');
+			$res = TRUE;
+		} else if (isset($_SESSION['phpCAS']['auth_checked'])) {
+			// the previous request has redirected the client to the CAS server with gateway=true
+			unset($_SESSION['phpCAS']['auth_checked']);
+			$res = FALSE;
+		} else {
+			//        $_SESSION['phpCAS']['auth_checked'] = true;
+			//	    $this->redirectToCas(TRUE/* gateway */);	
+			//	    // never reached
+			//	    $res = FALSE;
+			// avoid a check against CAS on every request
+			if (! isset($_SESSION['phpCAS']['unauth_count']) )
+				$_SESSION['phpCAS']['unauth_count'] = -2; // uninitialized
+			
+			if (($_SESSION['phpCAS']['unauth_count'] != -2 && $this->_cache_times_for_auth_recheck == -1) 
+					|| ($_SESSION['phpCAS']['unauth_count'] >= 0 && $_SESSION['phpCAS']['unauth_count'] < $this->_cache_times_for_auth_recheck))
+			{
+				$res = FALSE;
+				
+				if ($this->_cache_times_for_auth_recheck != -1)
+				{
+					$_SESSION['phpCAS']['unauth_count']++;
+					phpCAS::trace('user is not authenticated (cached for '.$_SESSION['phpCAS']['unauth_count'].' times of '.$this->_cache_times_for_auth_recheck.')');
+				}
+				else
+				{
+					phpCAS::trace('user is not authenticated (cached for until login pressed)');
+				}
+			}
+			else
+			{
+				$_SESSION['phpCAS']['unauth_count'] = 0;
+				$_SESSION['phpCAS']['auth_checked'] = true;
+				phpCAS::trace('user is not authenticated (cache reset)');
+				$this->redirectToCas(TRUE/* gateway */);	
+				// never reached
+				$res = FALSE;
+			}
+		}
+		phpCAS::traceEnd($res);
+		return $res;
+		}
+	
+	/**
+	 * This method is called to check if the user is authenticated (previously or by
+	 * tickets given in the URL).
+	 *
+	 * @return TRUE when the user is authenticated. Also may redirect to the same URL without the ticket.
+	 *
+	 * @public
+	 */
+	function isAuthenticated()
+		{
+		phpCAS::traceBegin();
+		$res = FALSE;
+		$validate_url = '';
+		
+		if ( $this->wasPreviouslyAuthenticated() ) {
+			// the user has already (previously during the session) been
+			// authenticated, nothing to be done.
+			phpCAS::trace('user was already authenticated, no need to look for tickets');
+			$res = TRUE;
+		}
+		else {
+			if ( $this->hasST() ) {
+				// if a Service Ticket was given, validate it
+				phpCAS::trace('ST `'.$this->getST().'\' is present');
+				$this->validateST($validate_url,$text_response,$tree_response); // if it fails, it halts
+				phpCAS::trace('ST `'.$this->getST().'\' was validated');
+				if ( $this->isProxy() ) {
+					$this->validatePGT($validate_url,$text_response,$tree_response); // idem
+					phpCAS::trace('PGT `'.$this->getPGT().'\' was validated');
+					$_SESSION['phpCAS']['pgt'] = $this->getPGT();
+				}
+				$_SESSION['phpCAS']['user'] = $this->getUser();
+				$res = TRUE;
+			}
+			elseif ( $this->hasPT() ) {
+				// if a Proxy Ticket was given, validate it
+				phpCAS::trace('PT `'.$this->getPT().'\' is present');
+				$this->validatePT($validate_url,$text_response,$tree_response); // note: if it fails, it halts
+				phpCAS::trace('PT `'.$this->getPT().'\' was validated');
+				if ( $this->isProxy() ) {
+					$this->validatePGT($validate_url,$text_response,$tree_response); // idem
+					phpCAS::trace('PGT `'.$this->getPGT().'\' was validated');
+					$_SESSION['phpCAS']['pgt'] = $this->getPGT();
+				}
+				$_SESSION['phpCAS']['user'] = $this->getUser();
+				$res = TRUE;
+			}
+			elseif ( $this->hasSA() ) {
+				// if we have a SAML ticket, validate it.
+				phpCAS::trace('SA `'.$this->getSA().'\' is present');
+				$this->validateSA($validate_url,$text_response,$tree_response); // if it fails, it halts
+				phpCAS::trace('SA `'.$this->getSA().'\' was validated');
+				$_SESSION['phpCAS']['user'] = $this->getUser();
+				$_SESSION['phpCAS']['attributes'] = $this->getAttributes();
+				$res = TRUE;
+			}
+			else {
+				// no ticket given, not authenticated
+				phpCAS::trace('no ticket found');
+			}
+			if ($res) {
+				// if called with a ticket parameter, we need to redirect to the app without the ticket so that CAS-ification is transparent to the browser (for later POSTS)
+				// most of the checks and errors should have been made now, so we're safe for redirect without masking error messages.
+				header('Location: '.$this->getURL());
+				phpCAS::log( "Prepare redirect to : ".$this->getURL() );
+			}
+		}
+		
+		phpCAS::traceEnd($res);
+		return $res;
+		}
+	
+	/**
+	 * This method tells if the current session is authenticated.
+	 * @return true if authenticated based soley on $_SESSION variable
+	 * @since 0.4.22 by Brendan Arnold
+	 */
+	function isSessionAuthenticated ()
+		{
+		return !empty($_SESSION['phpCAS']['user']);
+		}
+	
+	/**
+	 * This method tells if the user has already been (previously) authenticated
+	 * by looking into the session variables.
+	 *
+	 * @note This function switches to callback mode when needed.
+	 *
+	 * @return TRUE when the user has already been authenticated; FALSE otherwise.
+	 *
+	 * @private
+	 */
+	function wasPreviouslyAuthenticated()
+		{
+		phpCAS::traceBegin();
+		
+		if ( $this->isCallbackMode() ) {
+			$this->callback();
+		}
+		
+		$auth = FALSE;
+		
+		if ( $this->isProxy() ) {
+			// CAS proxy: username and PGT must be present
+			if ( $this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) {
+				// authentication already done
+				$this->setUser($_SESSION['phpCAS']['user']);
+				$this->setPGT($_SESSION['phpCAS']['pgt']);
+				phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\', PGT = `'.$_SESSION['phpCAS']['pgt'].'\''); 
+				$auth = TRUE;
+			} elseif ( $this->isSessionAuthenticated() && empty($_SESSION['phpCAS']['pgt']) ) {
+				// these two variables should be empty or not empty at the same time
+				phpCAS::trace('username found (`'.$_SESSION['phpCAS']['user'].'\') but PGT is empty');
+				// unset all tickets to enforce authentication
+				unset($_SESSION['phpCAS']);
+				$this->setST('');
+				$this->setPT('');
+			} elseif ( !$this->isSessionAuthenticated() && !empty($_SESSION['phpCAS']['pgt']) ) {
+				// these two variables should be empty or not empty at the same time
+				phpCAS::trace('PGT found (`'.$_SESSION['phpCAS']['pgt'].'\') but username is empty'); 
+				// unset all tickets to enforce authentication
+				unset($_SESSION['phpCAS']);
+				$this->setST('');
+				$this->setPT('');
+			} else {
+				phpCAS::trace('neither user not PGT found'); 
+			}
+		} else {
+			// `simple' CAS client (not a proxy): username must be present
+			if ( $this->isSessionAuthenticated() ) {
+				// authentication already done
+				$this->setUser($_SESSION['phpCAS']['user']);
+				if(isset($_SESSION['phpCAS']['attributes'])){
+					$this->setAttributes($_SESSION['phpCAS']['attributes']);
+				}
+				phpCAS::trace('user = `'.$_SESSION['phpCAS']['user'].'\''); 
+				$auth = TRUE;
+			} else {
+				phpCAS::trace('no user found');
+			}
+		}
+		
+		phpCAS::traceEnd($auth);
+		return $auth;
+		}
+	
+	/**
+	 * This method is used to redirect the client to the CAS server.
+	 * It is used by CASClient::forceAuthentication() and CASClient::checkAuthentication().
+	 * @param $gateway true to check authentication, false to force it
+	 * @param $renew true to force the authentication with the CAS server
+	 * @public
+	 */
+	function redirectToCas($gateway=false,$renew=false){
+		phpCAS::traceBegin();
+		$cas_url = $this->getServerLoginURL($gateway,$renew);
+		header('Location: '.$cas_url);
+		phpCAS::log( "Redirect to : ".$cas_url );
+		
+		$this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_WANTED));
+		
+		printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
+		$this->printHTMLFooter();
+		
+		phpCAS::traceExit();
+		exit();
+	}
+	
+	
+	/**
+	 * This method is used to logout from CAS.
+	 * @params $params an array that contains the optional url and service parameters that will be passed to the CAS server
+	 * @public
+	 */
+	function logout($params) {
+		phpCAS::traceBegin();
+		$cas_url = $this->getServerLogoutURL();
+		$paramSeparator = '?';
+		if (isset($params['url'])) {
+			$cas_url = $cas_url . $paramSeparator . "url=" . urlencode($params['url']); 
+			$paramSeparator = '&';
+		}
+		if (isset($params['service'])) {
+			$cas_url = $cas_url . $paramSeparator . "service=" . urlencode($params['service']); 
+		}
+		header('Location: '.$cas_url);
+		phpCAS::log( "Prepare redirect to : ".$cas_url );
+		
+		session_unset();
+		session_destroy();
+		
+		$this->printHTMLHeader($this->getString(CAS_STR_LOGOUT));
+		printf('<p>'.$this->getString(CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED).'</p>',$cas_url);
+		$this->printHTMLFooter();
+		
+		phpCAS::traceExit();
+		exit();
+	}
+	
+	/**
+	 * @return true if the current request is a logout request.
+	 * @private
+	 */
+	function isLogoutRequest() {
+		return !empty($_POST['logoutRequest']);
+	}
+	
+	/**
+	 * @return true if a logout request is allowed.
+	 * @private
+	 */
+	function isLogoutRequestAllowed() {
+	}
+	
+	/**
+	 * This method handles logout requests.
+	 * @param $check_client true to check the client bofore handling the request, 
+	 * false not to perform any access control. True by default.
+	 * @param $allowed_clients an array of host names allowed to send logout requests. 
+	 * By default, only the CAs server (declared in the constructor) will be allowed.
+	 * @public
+	 */
+	function handleLogoutRequests($check_client=true, $allowed_clients=false) {
+		phpCAS::traceBegin();
+		if (!$this->isLogoutRequest()) {
+			phpCAS::log("Not a logout request");
+			phpCAS::traceEnd();
+			return;
+		}
+		phpCAS::log("Logout requested");
+		phpCAS::log("SAML REQUEST: ".$_POST['logoutRequest']);
+		if ($check_client) {
+			if (!$allowed_clients) {
+				$allowed_clients = array( $this->getServerHostname() ); 
+			}
+			$client_ip = $_SERVER['REMOTE_ADDR'];
+			$client = gethostbyaddr($client_ip);
+			phpCAS::log("Client: ".$client."/".$client_ip); 
+			$allowed = false;
+			foreach ($allowed_clients as $allowed_client) {
+				if (($client == $allowed_client) or ($client_ip == $allowed_client)) { 
+					phpCAS::log("Allowed client '".$allowed_client."' matches, logout request is allowed");
+					$allowed = true;
+					break;
+				} else {
+					phpCAS::log("Allowed client '".$allowed_client."' does not match");
+				}
+			}
+			if (!$allowed) {
+				phpCAS::error("Unauthorized logout request from client '".$client."'");
+				printf("Unauthorized!");
+				phpCAS::traceExit();
+				exit();
+			}
+		} else {
+			phpCAS::log("No access control set");
+		}
+		// Extract the ticket from the SAML Request
+		preg_match("|<samlp:SessionIndex>(.*)</samlp:SessionIndex>|", $_POST['logoutRequest'], $tick, PREG_OFFSET_CAPTURE, 3);
+		$wrappedSamlSessionIndex = preg_replace('|<samlp:SessionIndex>|','',$tick[0][0]);
+		$ticket2logout = preg_replace('|</samlp:SessionIndex>|','',$wrappedSamlSessionIndex);
+		phpCAS::log("Ticket to logout: ".$ticket2logout);
+		$session_id = preg_replace('/[^\w]/','',$ticket2logout);
+		phpCAS::log("Session id: ".$session_id);
+		
+		// destroy a possible application session created before phpcas
+		if(session_id()){
+			session_unset();
+			session_destroy();
+		}
+		// fix session ID
+		session_id($session_id);
+		$_COOKIE[session_name()]=$session_id;
+		$_GET[session_name()]=$session_id;
+		
+		// Overwrite session
+		session_start();	
+		session_unset();
+		session_destroy();
+		printf("Disconnected!");
+		phpCAS::traceExit();
+		exit();
+	}
+	
+	/** @} */
+	
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	// XX                                                                    XX
+	// XX                  BASIC CLIENT FEATURES (CAS 1.0)                   XX
+	// XX                                                                    XX
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	
+	// ########################################################################
+	//  ST
+	// ########################################################################
+	/**
+	 * @addtogroup internalBasic
+	 * @{
+	 */  
+	
+	/**
+	 * the Service Ticket provided in the URL of the request if present
+	 * (empty otherwise). Written by CASClient::CASClient(), read by 
+	 * CASClient::getST() and CASClient::hasPGT().
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_st = '';
+	
+	/**
+	 * This method returns the Service Ticket provided in the URL of the request.
+	 * @return The service ticket.
+	 * @private
+	 */
+	function getST()
+		{ return $this->_st; }
+	
+	/**
+	 * This method stores the Service Ticket.
+	 * @param $st The Service Ticket.
+	 * @private
+	 */
+	function setST($st)
+		{ $this->_st = $st; }
+	
+	/**
+	 * This method tells if a Service Ticket was stored.
+	 * @return TRUE if a Service Ticket has been stored.
+	 * @private
+	 */
+	function hasST()
+		{ return !empty($this->_st); }
+	
+	/** @} */
+	
+	// ########################################################################
+	//  ST VALIDATION
+	// ########################################################################
+	/**
+	 * @addtogroup internalBasic
+	 * @{
+	 */  
+	
+	/**
+	 * the certificate of the CAS server.
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_cas_server_cert = '';
+	
+	/**
+	 * the certificate of the CAS server CA.
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_cas_server_ca_cert = '';
+	
+	/**
+	 * Set to true not to validate the CAS server.
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_no_cas_server_validation = false;
+	
+	/**
+	 * Set the certificate of the CAS server.
+	 *
+	 * @param $cert the PEM certificate
+	 */
+	function setCasServerCert($cert)
+		{
+		$this->_cas_server_cert = $cert;
+		}
+	
+	/**
+	 * Set the CA certificate of the CAS server.
+	 *
+	 * @param $cert the PEM certificate of the CA that emited the cert of the server
+	 */
+	function setCasServerCACert($cert)
+		{
+		$this->_cas_server_ca_cert = $cert;
+		}
+	
+	/**
+	 * Set no SSL validation for the CAS server.
+	 */
+	function setNoCasServerValidation()
+		{
+		$this->_no_cas_server_validation = true;
+		}
+	
+	/**
+	 * This method is used to validate a ST; halt on failure, and sets $validate_url,
+	 * $text_reponse and $tree_response on success. These parameters are used later
+	 * by CASClient::validatePGT() for CAS proxies.
+	 * Used for all CAS 1.0 validations
+	 * @param $validate_url the URL of the request to the CAS server.
+	 * @param $text_response the response of the CAS server, as is (XML text).
+	 * @param $tree_response the response of the CAS server, as a DOM XML tree.
+	 *
+	 * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
+	 *
+	 * @private
+	 */
+	function validateST($validate_url,&$text_response,&$tree_response)
+		{
+		phpCAS::traceBegin();
+		// build the URL to validate the ticket
+		$validate_url = $this->getServerServiceValidateURL().'&ticket='.$this->getST();
+		if ( $this->isProxy() ) {
+			// pass the callback url for CAS proxies
+			$validate_url .= '&pgtUrl='.$this->getCallbackURL();
+		}
+		
+		// open and read the URL
+		if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
+			phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
+			$this->authError('ST not validated',
+				$validate_url,
+				TRUE/*$no_response*/);
+		}
+		
+		// analyze the result depending on the version
+		switch ($this->getServerVersion()) {
+			case CAS_VERSION_1_0:
+				if (preg_match('/^no\n/',$text_response)) {
+					phpCAS::trace('ST has not been validated');
+					$this->authError('ST not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						FALSE/*$bad_response*/,
+						$text_response);
+				}
+				if (!preg_match('/^yes\n/',$text_response)) {
+					phpCAS::trace('ill-formed response');
+					$this->authError('ST not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						TRUE/*$bad_response*/,
+						$text_response);
+				}
+				// ST has been validated, extract the user name
+				$arr = preg_split('/\n/',$text_response);
+				$this->setUser(trim($arr[1]));
+				break;
+			case CAS_VERSION_2_0:
+				// read the response of the CAS server into a DOM object
+				if ( !($dom = domxml_open_mem($text_response))) {
+					phpCAS::trace('domxml_open_mem() failed');
+					$this->authError('ST not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						TRUE/*$bad_response*/,
+						$text_response);
+				}
+				// read the root node of the XML tree
+				if ( !($tree_response = $dom->document_element()) ) {
+					phpCAS::trace('document_element() failed');
+					$this->authError('ST not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						TRUE/*$bad_response*/,
+						$text_response);
+				}
+				// insure that tag name is 'serviceResponse'
+				if ( $tree_response->node_name() != 'serviceResponse' ) {
+					phpCAS::trace('bad XML root node (should be `serviceResponse\' instead of `'.$tree_response->node_name().'\'');
+					$this->authError('ST not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						TRUE/*$bad_response*/,
+						$text_response);
+				}
+				if ( sizeof($success_elements = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) {
+					// authentication succeded, extract the user name
+					if ( sizeof($user_elements = $success_elements[0]->get_elements_by_tagname("user")) == 0) {
+						phpCAS::trace('<authenticationSuccess> found, but no <user>');
+						$this->authError('ST not validated',
+							$validate_url,
+							FALSE/*$no_response*/,
+							TRUE/*$bad_response*/,
+							$text_response);
+					}
+					$user = trim($user_elements[0]->get_content());
+					phpCAS::trace('user = `'.$user);
+					$this->setUser($user);
+					
+				} else if ( sizeof($failure_elements = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) {
+					phpCAS::trace('<authenticationFailure> found');
+					// authentication failed, extract the error code and message
+					$this->authError('ST not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						FALSE/*$bad_response*/,
+						$text_response,
+						$failure_elements[0]->get_attribute('code')/*$err_code*/,
+						trim($failure_elements[0]->get_content())/*$err_msg*/);
+				} else {
+					phpCAS::trace('neither <authenticationSuccess> nor <authenticationFailure> found');
+					$this->authError('ST not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						TRUE/*$bad_response*/,
+						$text_response);
+				}
+				break;
+		}
+		
+		// at this step, ST has been validated and $this->_user has been set,
+		phpCAS::traceEnd(TRUE);
+		return TRUE;
+		}
+	
+	// ########################################################################
+	//  SAML VALIDATION
+	// ########################################################################
+	/**
+	 * @addtogroup internalBasic
+	 * @{
+	 */
+	
+	/**
+	 * This method is used to validate a SAML TICKET; halt on failure, and sets $validate_url,
+	 * $text_reponse and $tree_response on success. These parameters are used later
+	 * by CASClient::validatePGT() for CAS proxies.
+	 *
+	 * @param $validate_url the URL of the request to the CAS server.
+	 * @param $text_response the response of the CAS server, as is (XML text).
+	 * @param $tree_response the response of the CAS server, as a DOM XML tree.
+	 *
+	 * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
+	 *
+	 * @private
+	 */
+	function validateSA($validate_url,&$text_response,&$tree_response)
+		{
+		phpCAS::traceBegin();
+		
+		// build the URL to validate the ticket
+		$validate_url = $this->getServerSamlValidateURL();
+		
+		// open and read the URL
+		if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
+			phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
+			$this->authError('SA not validated', $validate_url, TRUE/*$no_response*/);
+		}
+		
+		phpCAS::trace('server version: '.$this->getServerVersion());
+		
+		// analyze the result depending on the version
+		switch ($this->getServerVersion()) {
+			case SAML_VERSION_1_1:
+				
+				// read the response of the CAS server into a DOM object
+				if ( !($dom = domxml_open_mem($text_response))) {
+					phpCAS::trace('domxml_open_mem() failed');
+					$this->authError('SA not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						TRUE/*$bad_response*/,
+						$text_response);
+				}
+				// read the root node of the XML tree
+				if ( !($tree_response = $dom->document_element()) ) {
+					phpCAS::trace('document_element() failed');
+					$this->authError('SA not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						TRUE/*$bad_response*/,
+						$text_response);
+				}
+				// insure that tag name is 'Envelope'
+				if ( $tree_response->node_name() != 'Envelope' ) {
+					phpCAS::trace('bad XML root node (should be `Envelope\' instead of `'.$tree_response->node_name().'\'');
+					$this->authError('SA not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						TRUE/*$bad_response*/,
+						$text_response);
+				}
+				// check for the NameIdentifier tag in the SAML response
+				if ( sizeof($success_elements = $tree_response->get_elements_by_tagname("NameIdentifier")) != 0) {
+					phpCAS::trace('NameIdentifier found');
+					$user = trim($success_elements[0]->get_content());
+					phpCAS::trace('user = `'.$user.'`');
+					$this->setUser($user);
+					$this->setSessionAttributes($text_response);
+				} else {
+					phpCAS::trace('no <NameIdentifier> tag found in SAML payload');
+					$this->authError('SA not validated',
+						$validate_url,
+						FALSE/*$no_response*/,
+						TRUE/*$bad_response*/,
+						$text_response);
+				}
+				break;
+		}
+		
+		// at this step, ST has been validated and $this->_user has been set,
+		phpCAS::traceEnd(TRUE);
+		return TRUE;
+		}
+	
+	/**
+	 * This method will parse the DOM and pull out the attributes from the SAML
+	 * payload and put them into an array, then put the array into the session.
+	 *
+	 * @param $text_response the SAML payload.
+	 * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
+	 *
+	 * @private
+	 */
+	function setSessionAttributes($text_response)
+		{
+		phpCAS::traceBegin();
+		
+		$result = FALSE;
+		
+		if (isset($_SESSION[SAML_ATTRIBUTES])) {
+			phpCAS::trace("session attrs already set.");  //testbml - do we care?
+		}
+		
+		$attr_array = array();
+		
+		if (($dom = domxml_open_mem($text_response))) {
+			$xPath = $dom->xpath_new_context();
+			$xPath->xpath_register_ns('samlp', 'urn:oasis:names:tc:SAML:1.0:protocol');
+			$xPath->xpath_register_ns('saml', 'urn:oasis:names:tc:SAML:1.0:assertion');
+			$nodelist = $xPath->xpath_eval("//saml:Attribute");
+			$attrs = $nodelist->nodeset;
+			phpCAS::trace($text_response);
+			foreach($attrs as $attr){
+				$xres = $xPath->xpath_eval("saml:AttributeValue", $attr);
+				$name = $attr->get_attribute("AttributeName");
+				$value_array = array();
+				foreach($xres->nodeset as $node){
+					$value_array[] = $node->get_content();
+					
+				}
+				phpCAS::trace("* " . $name . "=" . $value_array);
+				$attr_array[$name] = $value_array;
+			}
+			$_SESSION[SAML_ATTRIBUTES] = $attr_array;
+			// UGent addition...
+			foreach($attr_array as $attr_key => $attr_value) {
+				if(count($attr_value) > 1) {
+					$this->_attributes[$attr_key] = $attr_value;
+				}
+				else {
+					$this->_attributes[$attr_key] = $attr_value[0];
+				}
+			}
+			$result = TRUE;
+		}
+		phpCAS::traceEnd($result);
+		return $result;
+		}
+	
+	/** @} */
+	
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	// XX                                                                    XX
+	// XX                     PROXY FEATURES (CAS 2.0)                       XX
+	// XX                                                                    XX
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	
+	// ########################################################################
+	//  PROXYING
+	// ########################################################################
+	/**
+	 * @addtogroup internalProxy
+	 * @{
+	 */
+	
+	/**
+	 * A boolean telling if the client is a CAS proxy or not. Written by CASClient::CASClient(), 
+	 * read by CASClient::isProxy().
+	 *
+	 * @private
+	 */
+	var $_proxy;
+	
+	/**
+	 * Tells if a CAS client is a CAS proxy or not
+	 *
+	 * @return TRUE when the CAS client is a CAs proxy, FALSE otherwise
+	 *
+	 * @private
+	 */
+	function isProxy()
+		{
+		return $this->_proxy;
+		}
+	
+	/** @} */
+	// ########################################################################
+	//  PGT
+	// ########################################################################
+	/**
+	 * @addtogroup internalProxy
+	 * @{
+	 */  
+	
+	/**
+	 * the Proxy Grnting Ticket given by the CAS server (empty otherwise). 
+	 * Written by CASClient::setPGT(), read by CASClient::getPGT() and CASClient::hasPGT().
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_pgt = '';
+	
+	/**
+	 * This method returns the Proxy Granting Ticket given by the CAS server.
+	 * @return The Proxy Granting Ticket.
+	 * @private
+	 */
+	function getPGT()
+		{ return $this->_pgt; }
+	
+	/**
+	 * This method stores the Proxy Granting Ticket.
+	 * @param $pgt The Proxy Granting Ticket.
+	 * @private
+	 */
+	function setPGT($pgt)
+		{ $this->_pgt = $pgt; }
+	
+	/**
+	 * This method tells if a Proxy Granting Ticket was stored.
+	 * @return TRUE if a Proxy Granting Ticket has been stored.
+	 * @private
+	 */
+	function hasPGT()
+		{ return !empty($this->_pgt); }
+	
+	/** @} */
+	
+	// ########################################################################
+	//  CALLBACK MODE
+	// ########################################################################
+	/**
+	 * @addtogroup internalCallback
+	 * @{
+	 */  
+	/**
+	 * each PHP script using phpCAS in proxy mode is its own callback to get the
+	 * PGT back from the CAS server. callback_mode is detected by the constructor
+	 * thanks to the GET parameters.
+	 */
+	
+	/**
+	 * a boolean to know if the CAS client is running in callback mode. Written by
+	 * CASClient::setCallBackMode(), read by CASClient::isCallbackMode().
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_callback_mode = FALSE;
+	
+	/**
+	 * This method sets/unsets callback mode.
+	 *
+	 * @param $callback_mode TRUE to set callback mode, FALSE otherwise.
+	 *
+	 * @private
+	 */
+	function setCallbackMode($callback_mode)
+		{
+		$this->_callback_mode = $callback_mode;
+		}
+	
+	/**
+	 * This method returns TRUE when the CAs client is running i callback mode, 
+	 * FALSE otherwise.
+	 *
+	 * @return A boolean.
+	 *
+	 * @private
+	 */
+	function isCallbackMode()
+		{
+		return $this->_callback_mode;
+		}
+	
+	/**
+	 * the URL that should be used for the PGT callback (in fact the URL of the 
+	 * current request without any CGI parameter). Written and read by 
+	 * CASClient::getCallbackURL().
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_callback_url = '';
+	
+	/**
+	 * This method returns the URL that should be used for the PGT callback (in
+	 * fact the URL of the current request without any CGI parameter, except if
+	 * phpCAS::setFixedCallbackURL() was used).
+	 *
+	 * @return The callback URL
+	 *
+	 * @private
+	 */
+	function getCallbackURL()
+		{
+		// the URL is built when needed only
+		if ( empty($this->_callback_url) ) {
+			$final_uri = '';
+			// remove the ticket if present in the URL
+			$final_uri = 'https://';
+			/* replaced by Julien Marchal - v0.4.6
+			 * $this->uri .= $_SERVER['SERVER_NAME'];
+			 */
+			if(empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){
+				/* replaced by teedog - v0.4.12
+				 * $final_uri .= $_SERVER['SERVER_NAME'];
+				 */
+				if (empty($_SERVER['SERVER_NAME'])) {
+					$final_uri .= $_SERVER['HTTP_HOST'];
+				} else {
+					$final_uri .= $_SERVER['SERVER_NAME'];
+				}
+			} else {
+				$final_uri .= $_SERVER['HTTP_X_FORWARDED_SERVER'];
+			}
+			if ( ($this->isHttps() && $_SERVER['SERVER_PORT']!=443)
+					|| (!$this->isHttps() && $_SERVER['SERVER_PORT']!=80) ) {
+				$final_uri .= ':';
+				$final_uri .= $_SERVER['SERVER_PORT'];
+			}
+			$request_uri = $_SERVER['REQUEST_URI'];
+			$request_uri = preg_replace('/\?.*$/','',$request_uri);
+			$final_uri .= $request_uri;
+			$this->setCallbackURL($final_uri);
+		}
+		return $this->_callback_url;
+		}
+	
+	/**
+	 * This method sets the callback url.
+	 *
+	 * @param $callback_url url to set callback 
+	 *
+	 * @private
+	 */
+	function setCallbackURL($url)
+		{
+		return $this->_callback_url = $url;
+		}
+	
+	/**
+	 * This method is called by CASClient::CASClient() when running in callback
+	 * mode. It stores the PGT and its PGT Iou, prints its output and halts.
+	 *
+	 * @private
+	 */
+	function callback()
+		{
+		phpCAS::traceBegin();
+		$this->printHTMLHeader('phpCAS callback');
+		$pgt_iou = $_GET['pgtIou'];
+		$pgt = $_GET['pgtId'];
+		phpCAS::trace('Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\')');
+		echo '<p>Storing PGT `'.$pgt.'\' (id=`'.$pgt_iou.'\').</p>';
+		$this->storePGT($pgt,$pgt_iou);
+		$this->printHTMLFooter();
+		phpCAS::traceExit();
+		exit();
+		}
+	
+	/** @} */
+	
+	// ########################################################################
+	//  PGT STORAGE
+	// ########################################################################
+	/**
+	 * @addtogroup internalPGTStorage
+	 * @{
+	 */  
+	
+	/**
+	 * an instance of a class inheriting of PGTStorage, used to deal with PGT
+	 * storage. Created by CASClient::setPGTStorageFile() or CASClient::setPGTStorageDB(), used 
+	 * by CASClient::setPGTStorageFile(), CASClient::setPGTStorageDB() and CASClient::initPGTStorage().
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_pgt_storage = null;
+	
+	/**
+	 * This method is used to initialize the storage of PGT's.
+	 * Halts on error.
+	 *
+	 * @private
+	 */
+	function initPGTStorage()
+		{
+		// if no SetPGTStorageXxx() has been used, default to file
+		if ( !is_object($this->_pgt_storage) ) {
+			$this->setPGTStorageFile();
+		}
+		
+		// initializes the storage
+		$this->_pgt_storage->init();
+		}
+	
+	/**
+	 * This method stores a PGT. Halts on error.
+	 *
+	 * @param $pgt the PGT to store
+	 * @param $pgt_iou its corresponding Iou
+	 *
+	 * @private
+	 */
+	function storePGT($pgt,$pgt_iou)
+		{
+		// ensure that storage is initialized
+		$this->initPGTStorage();
+		// writes the PGT
+		$this->_pgt_storage->write($pgt,$pgt_iou);
+		}
+	
+	/**
+	 * This method reads a PGT from its Iou and deletes the corresponding storage entry.
+	 *
+	 * @param $pgt_iou the PGT Iou
+	 *
+	 * @return The PGT corresponding to the Iou, FALSE when not found.
+	 *
+	 * @private
+	 */
+	function loadPGT($pgt_iou)
+		{
+		// ensure that storage is initialized
+		$this->initPGTStorage();
+		// read the PGT
+		return $this->_pgt_storage->read($pgt_iou);
+		}
+	
+	/**
+	 * This method is used to tell phpCAS to store the response of the
+	 * CAS server to PGT requests onto the filesystem. 
+	 *
+	 * @param $format the format used to store the PGT's (`plain' and `xml' allowed)
+	 * @param $path the path where the PGT's should be stored
+	 *
+	 * @public
+	 */
+	function setPGTStorageFile($format='',
+		$path='')
+		{
+		// check that the storage has not already been set
+		if ( is_object($this->_pgt_storage) ) {
+			phpCAS::error('PGT storage already defined');
+		}
+		
+		// create the storage object
+		$this->_pgt_storage = new PGTStorageFile($this,$format,$path);
+		}
+	
+	/**
+	 * This method is used to tell phpCAS to store the response of the
+	 * CAS server to PGT requests into a database. 
+	 * @note The connection to the database is done only when needed. 
+	 * As a consequence, bad parameters are detected only when 
+	 * initializing PGT storage.
+	 *
+	 * @param $user the user to access the data with
+	 * @param $password the user's password
+	 * @param $database_type the type of the database hosting the data
+	 * @param $hostname the server hosting the database
+	 * @param $port the port the server is listening on
+	 * @param $database the name of the database
+	 * @param $table the name of the table storing the data
+	 *
+	 * @public
+	 */
+	function setPGTStorageDB($user,
+							 $password,
+							 $database_type,
+							 $hostname,
+							 $port,
+							 $database,
+							 $table)
+		{
+		// check that the storage has not already been set
+		if ( is_object($this->_pgt_storage) ) {
+			phpCAS::error('PGT storage already defined');
+		}
+		
+		// warn the user that he should use file storage...
+		trigger_error('PGT storage into database is an experimental feature, use at your own risk',E_USER_WARNING);
+		
+		// create the storage object
+		$this->_pgt_storage = new PGTStorageDB($this,$user,$password,$database_type,$hostname,$port,$database,$table);
+		}
+	
+	// ########################################################################
+	//  PGT VALIDATION
+	// ########################################################################
+	/**
+	 * This method is used to validate a PGT; halt on failure.
+	 * 
+	 * @param $validate_url the URL of the request to the CAS server.
+	 * @param $text_response the response of the CAS server, as is (XML text); result
+	 * of CASClient::validateST() or CASClient::validatePT().
+	 * @param $tree_response the response of the CAS server, as a DOM XML tree; result
+	 * of CASClient::validateST() or CASClient::validatePT().
+	 *
+	 * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
+	 *
+	 * @private
+	 */
+	function validatePGT(&$validate_url,$text_response,$tree_response)
+		{
+		// here cannot use phpCAS::traceBegin(); alongside domxml-php4-to-php5.php
+		phpCAS::log('start validatePGT()');
+		if ( sizeof($arr = $tree_response->get_elements_by_tagname("proxyGrantingTicket")) == 0) {
+			phpCAS::trace('<proxyGrantingTicket> not found');
+			// authentication succeded, but no PGT Iou was transmitted
+			$this->authError('Ticket validated but no PGT Iou transmitted',
+				$validate_url,
+				FALSE/*$no_response*/,
+				FALSE/*$bad_response*/,
+				$text_response);
+		} else {
+			// PGT Iou transmitted, extract it
+			$pgt_iou = trim($arr[0]->get_content());
+			$pgt = $this->loadPGT($pgt_iou);
+			if ( $pgt == FALSE ) {
+				phpCAS::trace('could not load PGT');
+				$this->authError('PGT Iou was transmitted but PGT could not be retrieved',
+					$validate_url,
+					FALSE/*$no_response*/,
+					FALSE/*$bad_response*/,
+					$text_response);
+			}
+			$this->setPGT($pgt);
+		}
+		// here, cannot use	phpCAS::traceEnd(TRUE); alongside domxml-php4-to-php5.php
+		phpCAS::log('end validatePGT()');
+		return TRUE;
+		}
+	
+	// ########################################################################
+	//  PGT VALIDATION
+	// ########################################################################
+	
+	/**
+	 * This method is used to retrieve PT's from the CAS server thanks to a PGT.
+	 * 
+	 * @param $target_service the service to ask for with the PT.
+	 * @param $err_code an error code (PHPCAS_SERVICE_OK on success).
+	 * @param $err_msg an error message (empty on success).
+	 *
+	 * @return a Proxy Ticket, or FALSE on error.
+	 *
+	 * @private
+	 */
+	function retrievePT($target_service,&$err_code,&$err_msg)
+		{
+		phpCAS::traceBegin();
+		
+		// by default, $err_msg is set empty and $pt to TRUE. On error, $pt is
+		// set to false and $err_msg to an error message. At the end, if $pt is FALSE 
+		// and $error_msg is still empty, it is set to 'invalid response' (the most
+		// commonly encountered error).
+		$err_msg = '';
+		
+		// build the URL to retrieve the PT
+		//      $cas_url = $this->getServerProxyURL().'?targetService='.preg_replace('/&/','%26',$target_service).'&pgt='.$this->getPGT();
+		$cas_url = $this->getServerProxyURL().'?targetService='.urlencode($target_service).'&pgt='.$this->getPGT();
+		
+		// open and read the URL
+		if ( !$this->readURL($cas_url,''/*cookies*/,$headers,$cas_response,$err_msg) ) {
+			phpCAS::trace('could not open URL \''.$cas_url.'\' to validate ('.$err_msg.')');
+			$err_code = PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE;
+			$err_msg = 'could not retrieve PT (no response from the CAS server)';
+			phpCAS::traceEnd(FALSE);
+			return FALSE;
+		}
+		
+		$bad_response = FALSE;
+		
+		if ( !$bad_response ) {
+			// read the response of the CAS server into a DOM object
+			if ( !($dom = @domxml_open_mem($cas_response))) {
+				phpCAS::trace('domxml_open_mem() failed');
+				// read failed
+				$bad_response = TRUE;
+			} 
+		}
+		
+		if ( !$bad_response ) {
+			// read the root node of the XML tree
+			if ( !($root = $dom->document_element()) ) {
+				phpCAS::trace('document_element() failed');
+				// read failed
+				$bad_response = TRUE;
+			} 
+		}
+		
+		if ( !$bad_response ) {
+			// insure that tag name is 'serviceResponse'
+			if ( $root->node_name() != 'serviceResponse' ) {
+				phpCAS::trace('node_name() failed');
+				// bad root node
+				$bad_response = TRUE;
+			} 
+		}
+		
+		if ( !$bad_response ) {
+			// look for a proxySuccess tag
+			if ( sizeof($arr = $root->get_elements_by_tagname("proxySuccess")) != 0) {
+				// authentication succeded, look for a proxyTicket tag
+				if ( sizeof($arr = $root->get_elements_by_tagname("proxyTicket")) != 0) {
+					$err_code = PHPCAS_SERVICE_OK;
+					$err_msg = '';
+					phpCAS::trace('original PT: '.trim($arr[0]->get_content()));
+					$pt = trim($arr[0]->get_content());
+					phpCAS::traceEnd($pt);
+					return $pt;
+				} else {
+					phpCAS::trace('<proxySuccess> was found, but not <proxyTicket>');
+				}
+			} 
+			// look for a proxyFailure tag
+			else if ( sizeof($arr = $root->get_elements_by_tagname("proxyFailure")) != 0) {
+				// authentication failed, extract the error
+				$err_code = PHPCAS_SERVICE_PT_FAILURE;
+				$err_msg = 'PT retrieving failed (code=`'
+					.$arr[0]->get_attribute('code')
+					.'\', message=`'
+					.trim($arr[0]->get_content())
+					.'\')';
+				phpCAS::traceEnd(FALSE);
+				return FALSE;
+			} else {
+				phpCAS::trace('neither <proxySuccess> nor <proxyFailure> found');
+			}
+		}
+		
+		// at this step, we are sure that the response of the CAS server was ill-formed
+		$err_code = PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE;
+		$err_msg = 'Invalid response from the CAS server (response=`'.$cas_response.'\')';
+		
+		phpCAS::traceEnd(FALSE);
+		return FALSE;
+		}
+	
+	// ########################################################################
+	// ACCESS TO EXTERNAL SERVICES
+	// ########################################################################
+	
+	/**
+	 * This method is used to acces a remote URL.
+	 *
+	 * @param $url the URL to access.
+	 * @param $cookies an array containing cookies strings such as 'name=val'
+	 * @param $headers an array containing the HTTP header lines of the response
+	 * (an empty array on failure).
+	 * @param $body the body of the response, as a string (empty on failure).
+	 * @param $err_msg an error message, filled on failure.
+	 *
+	 * @return TRUE on success, FALSE otherwise (in this later case, $err_msg
+	 * contains an error message).
+	 *
+	 * @private
+	 */
+	function readURL($url,$cookies,&$headers,&$body,&$err_msg)
+		{
+		phpCAS::traceBegin();
+		$headers = '';
+		$body = '';
+		$err_msg = '';
+		
+		$res = TRUE;
+		
+		// initialize the CURL session
+		$ch = curl_init($url);
+		
+		if (version_compare(PHP_VERSION,'5.1.3','>=')) {
+			//only avaible in php5
+			curl_setopt_array($ch, $this->_curl_options);
+		} else {
+			foreach ($this->_curl_options as $key => $value) {
+				curl_setopt($ch, $key, $value);
+			}
+		}
+		
+		if ($this->_cas_server_cert == '' && $this->_cas_server_ca_cert == '' && !$this->_no_cas_server_validation) {
+			phpCAS::error('one of the methods phpCAS::setCasServerCert(), phpCAS::setCasServerCACert() or phpCAS::setNoCasServerValidation() must be called.');
+		}
+		if ($this->_cas_server_cert != '' && $this->_cas_server_ca_cert != '') {
+			// This branch added by IDMS. Seems phpCAS implementor got a bit confused about the curl options CURLOPT_SSLCERT and CURLOPT_CAINFO
+			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
+			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
+			curl_setopt($ch, CURLOPT_SSLCERT, $this->_cas_server_cert);
+			curl_setopt($ch, CURLOPT_CAINFO, $this->_cas_server_ca_cert);
+			curl_setopt($ch, CURLOPT_VERBOSE, '1');
+			phpCAS::trace('CURL: Set all required opts for mutual authentication ------');
+		} else if ($this->_cas_server_cert != '' ) {
+			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
+			curl_setopt($ch, CURLOPT_SSLCERT, $this->_cas_server_cert);
+		} else if ($this->_cas_server_ca_cert != '') {
+			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
+			curl_setopt($ch, CURLOPT_CAINFO, $this->_cas_server_ca_cert);
+		} else {
+			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
+			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
+		}
+		
+		// return the CURL output into a variable
+		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+		// get the HTTP header with a callback
+		$this->_curl_headers = array(); // empty the headers array
+		curl_setopt($ch, CURLOPT_HEADERFUNCTION, array($this, '_curl_read_headers'));
+		// add cookies headers
+		if ( is_array($cookies) ) {
+			curl_setopt($ch,CURLOPT_COOKIE,implode(';',$cookies));
+		}
+		// add extra stuff if SAML
+		if ($this->hasSA()) {
+			$more_headers = array ("soapaction: http://www.oasis-open.org/committees/security",
+				"cache-control: no-cache",
+				"pragma: no-cache",
+				"accept: text/xml",
+				"connection: keep-alive",
+			"content-type: text/xml");
+			
+			curl_setopt($ch, CURLOPT_HTTPHEADER, $more_headers);
+			curl_setopt($ch, CURLOPT_POST, 1);
+			$data = $this->buildSAMLPayload();
+			//phpCAS::trace('SAML Payload: '.print_r($data, TRUE));
+			curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
+		}
+		// perform the query
+		$buf = curl_exec ($ch);
+		//phpCAS::trace('CURL: Call completed. Response body is: \''.$buf.'\'');
+		if ( $buf === FALSE ) {
+			phpCAS::trace('curl_exec() failed');
+			$err_msg = 'CURL error #'.curl_errno($ch).': '.curl_error($ch);
+			//phpCAS::trace('curl error: '.$err_msg);
+			// close the CURL session
+			curl_close ($ch);
+			$res = FALSE;
+		} else {
+			// close the CURL session
+			curl_close ($ch);
+			
+			$headers = $this->_curl_headers;
+			$body = $buf;
+		}
+		
+		phpCAS::traceEnd($res);
+		return $res;
+		}
+	
+	/**
+	 * This method is used to build the SAML POST body sent to /samlValidate URL.
+	 *
+	 * @return the SOAP-encased SAMLP artifact (the ticket).
+	 *
+	 * @private
+	 */
+	function buildSAMLPayload()
+		{
+		phpCAS::traceBegin();
+		
+		//get the ticket
+		$sa = $this->getSA();
+		//phpCAS::trace("SA: ".$sa);
+		
+		$body=SAML_SOAP_ENV.SAML_SOAP_BODY.SAMLP_REQUEST.SAML_ASSERTION_ARTIFACT.$sa.SAML_ASSERTION_ARTIFACT_CLOSE.SAMLP_REQUEST_CLOSE.SAML_SOAP_BODY_CLOSE.SAML_SOAP_ENV_CLOSE;
+		
+		phpCAS::traceEnd($body);
+		return ($body);
+		}
+	
+	/**
+	 * This method is the callback used by readURL method to request HTTP headers.
+	 */
+	var $_curl_headers = array();
+	function _curl_read_headers($ch, $header)
+		{
+		$this->_curl_headers[] = $header;
+		return strlen($header);
+		}
+	
+	/**
+	 * This method is used to access an HTTP[S] service.
+	 * 
+	 * @param $url the service to access.
+	 * @param $err_code an error code Possible values are PHPCAS_SERVICE_OK (on
+	 * success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE,
+	 * PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_NOT AVAILABLE.
+	 * @param $output the output of the service (also used to give an error
+	 * message on failure).
+	 *
+	 * @return TRUE on success, FALSE otherwise (in this later case, $err_code
+	 * gives the reason why it failed and $output contains an error message).
+	 *
+	 * @public
+	 */
+	function serviceWeb($url,&$err_code,&$output)
+		{
+		phpCAS::traceBegin();
+		// at first retrieve a PT
+		$pt = $this->retrievePT($url,$err_code,$output);
+		
+		$res = TRUE;
+		
+		// test if PT was retrieved correctly
+		if ( !$pt ) {
+			// note: $err_code and $err_msg are filled by CASClient::retrievePT()
+			phpCAS::trace('PT was not retrieved correctly');
+			$res = FALSE;
+		} else {
+			// add cookies if necessary
+			if ( is_array($_SESSION['phpCAS']['services'][$url]['cookies']) ) {
+				foreach ( $_SESSION['phpCAS']['services'][$url]['cookies'] as $name => $val ) { 
+					$cookies[] = $name.'='.$val;
+				}
+			}
+			
+			// build the URL including the PT
+			if ( strstr($url,'?') === FALSE ) {
+				$service_url = $url.'?ticket='.$pt;
+			} else {
+				$service_url = $url.'&ticket='.$pt;
+			}
+			
+			phpCAS::trace('reading URL`'.$service_url.'\'');
+			if ( !$this->readURL($service_url,$cookies,$headers,$output,$err_msg) ) {
+				phpCAS::trace('could not read URL`'.$service_url.'\'');
+				$err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
+				// give an error message
+				$output = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
+					$service_url,
+					$err_msg);
+				$res = FALSE;
+			} else {
+				// URL has been fetched, extract the cookies
+				phpCAS::trace('URL`'.$service_url.'\' has been read, storing cookies:');
+				foreach ( $headers as $header ) {
+					// test if the header is a cookie
+					if ( preg_match('/^Set-Cookie:/',$header) ) {
+						// the header is a cookie, remove the beginning
+						$header_val = preg_replace('/^Set-Cookie: */','',$header);
+						// extract interesting information
+						$name_val = strtok($header_val,'; ');
+						// extract the name and the value of the cookie
+						$cookie_name = strtok($name_val,'=');
+						$cookie_val = strtok('=');
+						// store the cookie 
+						$_SESSION['phpCAS']['services'][$url]['cookies'][$cookie_name] = $cookie_val;
+						phpCAS::trace($cookie_name.' -> '.$cookie_val);
+					}
+				}
+			}
+		}
+		
+		phpCAS::traceEnd($res);
+		return $res;
+		}
+	
+	/**
+	 * This method is used to access an IMAP/POP3/NNTP service.
+	 * 
+	 * @param $url a string giving the URL of the service, including the mailing box
+	 * for IMAP URLs, as accepted by imap_open().
+	 * @param $service a string giving for CAS retrieve Proxy ticket
+	 * @param $flags options given to imap_open().
+	 * @param $err_code an error code Possible values are PHPCAS_SERVICE_OK (on
+	 * success), PHPCAS_SERVICE_PT_NO_SERVER_RESPONSE, PHPCAS_SERVICE_PT_BAD_SERVER_RESPONSE,
+	 * PHPCAS_SERVICE_PT_FAILURE, PHPCAS_SERVICE_NOT AVAILABLE.
+	 * @param $err_msg an error message on failure
+	 * @param $pt the Proxy Ticket (PT) retrieved from the CAS server to access the URL
+	 * on success, FALSE on error).
+	 *
+	 * @return an IMAP stream on success, FALSE otherwise (in this later case, $err_code
+	 * gives the reason why it failed and $err_msg contains an error message).
+	 *
+	 * @public
+	 */
+	function serviceMail($url,$service,$flags,&$err_code,&$err_msg,&$pt)
+		{
+		phpCAS::traceBegin();
+		// at first retrieve a PT
+		$pt = $this->retrievePT($service,$err_code,$output);
+		
+		$stream = FALSE;
+		
+		// test if PT was retrieved correctly
+		if ( !$pt ) {
+			// note: $err_code and $err_msg are filled by CASClient::retrievePT()
+			phpCAS::trace('PT was not retrieved correctly');
+		} else {
+			phpCAS::trace('opening IMAP URL `'.$url.'\'...');
+			$stream = @imap_open($url,$this->getUser(),$pt,$flags);
+			if ( !$stream ) {
+				phpCAS::trace('could not open URL');
+				$err_code = PHPCAS_SERVICE_NOT_AVAILABLE;
+				// give an error message
+				$err_msg = sprintf($this->getString(CAS_STR_SERVICE_UNAVAILABLE),
+					$service_url,
+					var_export(imap_errors(),TRUE));
+				$pt = FALSE;
+				$stream = FALSE;
+			} else {
+				phpCAS::trace('ok');
+			}
+		}
+		
+		phpCAS::traceEnd($stream);
+		return $stream;
+		}
+	
+	/** @} */
+	
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	// XX                                                                    XX
+	// XX                  PROXIED CLIENT FEATURES (CAS 2.0)                 XX
+	// XX                                                                    XX
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	
+	// ########################################################################
+	//  PT
+	// ########################################################################
+	/**
+	 * @addtogroup internalProxied
+	 * @{
+	 */  
+	
+	/**
+	 * the Proxy Ticket provided in the URL of the request if present
+	 * (empty otherwise). Written by CASClient::CASClient(), read by 
+	 * CASClient::getPT() and CASClient::hasPGT().
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_pt = '';
+	
+	/**
+	 * This method returns the Proxy Ticket provided in the URL of the request.
+	 * @return The proxy ticket.
+	 * @private
+	 */
+	function getPT()
+		{
+		//      return 'ST'.substr($this->_pt, 2);
+		return $this->_pt;
+		}
+	
+	/**
+	 * This method stores the Proxy Ticket.
+	 * @param $pt The Proxy Ticket.
+	 * @private
+	 */
+	function setPT($pt)
+		{ $this->_pt = $pt; }
+	
+	/**
+	 * This method tells if a Proxy Ticket was stored.
+	 * @return TRUE if a Proxy Ticket has been stored.
+	 * @private
+	 */
+	function hasPT()
+		{ return !empty($this->_pt); }
+	/**
+	 * This method returns the SAML Ticket provided in the URL of the request.
+	 * @return The SAML ticket.
+	 * @private
+	 */
+	function getSA()
+		{ return 'ST'.substr($this->_sa, 2); }
+	
+	/**
+	 * This method stores the SAML Ticket.
+	 * @param $sa The SAML Ticket.
+	 * @private
+	 */
+	function setSA($sa)
+		{ $this->_sa = $sa; }
+	
+	/**
+	 * This method tells if a SAML Ticket was stored.
+	 * @return TRUE if a SAML Ticket has been stored.
+	 * @private
+	 */
+	function hasSA()
+		{ return !empty($this->_sa); }
+	
+	/** @} */
+	// ########################################################################
+	//  PT VALIDATION
+	// ########################################################################
+	/**
+	 * @addtogroup internalProxied
+	 * @{
+	 */  
+	
+	/**
+	 * This method is used to validate a ST or PT; halt on failure
+	 * Used for all CAS 2.0 validations
+	 * @return bool TRUE when successfull, halt otherwise by calling CASClient::authError().
+	 *
+	 * @private
+	 */
+	function validatePT(&$validate_url,&$text_response,&$tree_response)
+		{
+		phpCAS::traceBegin();
+		// build the URL to validate the ticket
+		$validate_url = $this->getServerProxyValidateURL().'&ticket='.$this->getPT();
+		
+		if ( $this->isProxy() ) {
+			// pass the callback url for CAS proxies
+			$validate_url .= '&pgtUrl='.$this->getCallbackURL();
+		}
+		
+		// open and read the URL
+		if ( !$this->readURL($validate_url,''/*cookies*/,$headers,$text_response,$err_msg) ) {
+			phpCAS::trace('could not open URL \''.$validate_url.'\' to validate ('.$err_msg.')');
+			$this->authError('PT not validated',
+				$validate_url,
+				TRUE/*$no_response*/);
+		}
+		
+		// read the response of the CAS server into a DOM object
+		if ( !($dom = domxml_open_mem($text_response))) {
+			// read failed
+			$this->authError('PT not validated',
+				$validate_url,
+				FALSE/*$no_response*/,
+				TRUE/*$bad_response*/,
+				$text_response);
+		}
+		// read the root node of the XML tree
+		if ( !($tree_response = $dom->document_element()) ) {
+			// read failed
+			$this->authError('PT not validated',
+				$validate_url,
+				FALSE/*$no_response*/,
+				TRUE/*$bad_response*/,
+				$text_response);
+		}
+		// insure that tag name is 'serviceResponse'
+		if ( $tree_response->node_name() != 'serviceResponse' ) {
+			// bad root node
+			$this->authError('PT not validated',
+				$validate_url,
+				FALSE/*$no_response*/,
+				TRUE/*$bad_response*/,
+				$text_response);
+		}
+		if ( sizeof($arr = $tree_response->get_elements_by_tagname("authenticationSuccess")) != 0) {
+			// authentication succeded, extract the user name
+			if ( sizeof($arr = $tree_response->get_elements_by_tagname("user")) == 0) {
+				// no user specified => error
+				$this->authError('PT not validated',
+					$validate_url,
+					FALSE/*$no_response*/,
+					TRUE/*$bad_response*/,
+					$text_response);
+			}
+			$this->setUser(trim($arr[0]->get_content()));
+			
+		} else if ( sizeof($arr = $tree_response->get_elements_by_tagname("authenticationFailure")) != 0) {
+			// authentication succeded, extract the error code and message
+			$this->authError('PT not validated',
+				$validate_url,
+				FALSE/*$no_response*/,
+				FALSE/*$bad_response*/,
+				$text_response,
+				$arr[0]->get_attribute('code')/*$err_code*/,
+				trim($arr[0]->get_content())/*$err_msg*/);
+		} else {
+			$this->authError('PT not validated',
+				$validate_url,	
+				FALSE/*$no_response*/,
+				TRUE/*$bad_response*/,
+				$text_response);
+		}
+		
+		// at this step, PT has been validated and $this->_user has been set,
+		
+		phpCAS::traceEnd(TRUE);
+		return TRUE;
+		}
+	
+	/** @} */
+	
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	// XX                                                                    XX
+	// XX                               MISC                                 XX
+	// XX                                                                    XX
+	// XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+	
+	/**
+	 * @addtogroup internalMisc
+	 * @{
+	 */  
+	
+	// ########################################################################
+	//  URL
+	// ########################################################################
+	/**
+	 * the URL of the current request (without any ticket CGI parameter). Written 
+	 * and read by CASClient::getURL().
+	 *
+	 * @hideinitializer
+	 * @private
+	 */
+	var $_url = '';
+	
+	/**
+	 * This method returns the URL of the current request (without any ticket
+	 * CGI parameter).
+	 *
+	 * @return The URL
+	 *
+	 * @private
+	 */
+	function getURL()
+		{
+		phpCAS::traceBegin();
+		// the URL is built when needed only
+		if ( empty($this->_url) ) {
+			$final_uri = '';
+			// remove the ticket if present in the URL
+			$final_uri = ($this->isHttps()) ? 'https' : 'http';
+			$final_uri .= '://';
+			/* replaced by Julien Marchal - v0.4.6
+			 * $this->_url .= $_SERVER['SERVER_NAME'];
+			 */
+			if(empty($_SERVER['HTTP_X_FORWARDED_SERVER'])){
+				/* replaced by teedog - v0.4.12
+				 * $this->_url .= $_SERVER['SERVER_NAME'];
+				 */
+				if (empty($_SERVER['SERVER_NAME'])) {
+					$server_name = $_SERVER['HTTP_HOST'];
+				} else {
+					$server_name = $_SERVER['SERVER_NAME'];
+				}
+			} else {
+				$server_name = $_SERVER['HTTP_X_FORWARDED_SERVER'];
+			}
+			$final_uri .= $server_name;
+			if (!strpos($server_name, ':')) {
+				if ( ($this->isHttps() && $_SERVER['SERVER_PORT']!=443)
+						|| (!$this->isHttps() && $_SERVER['SERVER_PORT']!=80) ) {
+					$final_uri .= ':';
+					$final_uri .= $_SERVER['SERVER_PORT'];
+				}
+			}
+			
+			$request_uri	= explode('?', $_SERVER['REQUEST_URI'], 2);
+			$final_uri		.= $request_uri[0];
+			
+			if (isset($request_uri[1]) && $request_uri[1])
+			{
+				$query_string	= $this->removeParameterFromQueryString('ticket', $request_uri[1]);
+				
+				// If the query string still has anything left, append it to the final URI
+				if ($query_string !== '')
+					$final_uri	.= "?$query_string";
+				
+			}
+			
+			phpCAS::trace("Final URI: $final_uri");
+			$this->setURL($final_uri);
+		}
+		phpCAS::traceEnd($this->_url);
+		return $this->_url;
+	}
+	
+
+		
+	/**
+	 * Removes a parameter from a query string
+	 * 
+	 * @param string $parameterName 
+	 * @param string $queryString
+	 * @return string
+	 *
+	 * @link http://stackoverflow.com/questions/1842681/regular-expression-to-remove-one-parameter-from-query-string
+	 */
+	function removeParameterFromQueryString($parameterName, $queryString)
+	{
+		$parameterName	= preg_quote($parameterName);
+		return preg_replace("/&$parameterName(=[^&]*)?|^$parameterName(=[^&]*)?&?/", '', $queryString);
+	}
+
+	
+	/**
+	 * This method sets the URL of the current request 
+	 *
+	 * @param $url url to set for service
+	 *
+	 * @private
+	 */
+	function setURL($url)
+		{
+		$this->_url = $url;
+		}
+	
+	// ########################################################################
+	//  AUTHENTICATION ERROR HANDLING
+	// ########################################################################
+	/**
+	 * This method is used to print the HTML output when the user was not authenticated.
+	 *
+	 * @param $failure the failure that occured
+	 * @param $cas_url the URL the CAS server was asked for
+	 * @param $no_response the response from the CAS server (other 
+	 * parameters are ignored if TRUE)
+	 * @param $bad_response bad response from the CAS server ($err_code
+	 * and $err_msg ignored if TRUE)
+	 * @param $cas_response the response of the CAS server
+	 * @param $err_code the error code given by the CAS server
+	 * @param $err_msg the error message given by the CAS server
+	 *
+	 * @private
+	 */
+	function authError($failure,$cas_url,$no_response,$bad_response='',$cas_response='',$err_code='',$err_msg='')
+		{
+		phpCAS::traceBegin();
+		
+		$this->printHTMLHeader($this->getString(CAS_STR_AUTHENTICATION_FAILED));
+		printf($this->getString(CAS_STR_YOU_WERE_NOT_AUTHENTICATED),htmlentities($this->getURL()),$_SERVER['SERVER_ADMIN']);
+		phpCAS::trace('CAS URL: '.$cas_url);
+		phpCAS::trace('Authentication failure: '.$failure);
+		if ( $no_response ) {
+			phpCAS::trace('Reason: no response from the CAS server');
+		} else {
+			if ( $bad_response ) {
+				phpCAS::trace('Reason: bad response from the CAS server');
+			} else {
+				switch ($this->getServerVersion()) {
+					case CAS_VERSION_1_0:
+						phpCAS::trace('Reason: CAS error');
+						break;
+					case CAS_VERSION_2_0:
+						if ( empty($err_code) )
+							phpCAS::trace('Reason: no CAS error');
+						else
+							phpCAS::trace('Reason: ['.$err_code.'] CAS error: '.$err_msg);
+						break;
+				}
+			}
+			phpCAS::trace('CAS response: '.$cas_response);
+		}
+		$this->printHTMLFooter();
+		phpCAS::traceExit();
+		exit();
+		}
+	
+	/** @} */
+}
+
+?>

+ 499 - 0
main/auth/cas/lib/CAS/domxml-php4-to-php5.php

@@ -0,0 +1,499 @@
+<?php
+/*
+	Requires PHP5, uses built-in DOM extension.
+	To be used in PHP4 scripts using DOMXML extension: allows PHP4/DOMXML scripts to run on PHP5/DOM.
+	(Optional: requires PHP5/XSL extension for domxml_xslt functions, PHP>=5.1 for XPath evaluation functions, and PHP>=5.1/libxml for DOMXML error reports)
+
+	Typical use:
+	{
+		if (PHP_VERSION>='5')
+			require_once('domxml-php4-to-php5.php');
+	}
+
+	Version 1.21, 2008-12-05, http://alexandre.alapetite.net/doc-alex/domxml-php4-php5/
+
+	------------------------------------------------------------------
+	Written by Alexandre Alapetite, http://alexandre.alapetite.net/cv/
+
+	Copyright 2004-2008, GNU Lesser General Public License,
+	http://www.gnu.org/licenses/lgpl.html
+
+	This program is free software: you can redistribute it and/or modify
+	it under the terms of the GNU Lesser General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+	GNU Lesser General Public License for more details.
+	You should have received a copy of the GNU Lesser General Public License
+	along with this program. If not, see <http://www.gnu.org/licenses/lgpl.html>
+
+	== Rights and obligations ==
+	- Attribution: You must give the original author credit.
+	- Share Alike: If you alter or transform this library,
+	   you may distribute the resulting library only under the same license GNU/LGPL.
+	- In case of jurisdiction dispute, the French law is authoritative.
+	- Any of these conditions can be waived if you get permission from Alexandre Alapetite.
+	- Not required, but please send to Alexandre Alapetite the modifications you make,
+	   in order to improve this file for the benefit of everybody.
+
+	If you want to distribute this code, please do it as a link to:
+	http://alexandre.alapetite.net/doc-alex/domxml-php4-php5/
+*/
+
+define('DOMXML_LOAD_PARSING',0);
+define('DOMXML_LOAD_VALIDATING',1);
+define('DOMXML_LOAD_RECOVERING',2);
+define('DOMXML_LOAD_SUBSTITUTE_ENTITIES',4);
+//define('DOMXML_LOAD_COMPLETE_ATTRS',8);
+define('DOMXML_LOAD_DONT_KEEP_BLANKS',16);
+
+function domxml_new_doc($version) {return new php4DOMDocument();}
+function domxml_new_xmldoc($version) {return new php4DOMDocument();}
+function domxml_open_file($filename,$mode=DOMXML_LOAD_PARSING,&$error=null)
+{
+	$dom=new php4DOMDocument($mode);
+	$errorMode=(func_num_args()>2)&&defined('LIBXML_VERSION');
+	if ($errorMode) libxml_use_internal_errors(true);
+	if (!$dom->myDOMNode->load($filename)) $dom=null;
+	if ($errorMode)
+	{
+		$error=array_map('_error_report',libxml_get_errors());
+		libxml_clear_errors();
+	}
+	return $dom;
+}
+function domxml_open_mem($str,$mode=DOMXML_LOAD_PARSING,&$error=null)
+{
+	$dom=new php4DOMDocument($mode);
+	$errorMode=(func_num_args()>2)&&defined('LIBXML_VERSION');
+	if ($errorMode) libxml_use_internal_errors(true);
+	if (!$dom->myDOMNode->loadXML($str)) $dom=null;
+	if ($errorMode)
+	{
+		$error=array_map('_error_report',libxml_get_errors());
+		libxml_clear_errors();
+	}
+	return $dom;
+}
+function html_doc($html_doc,$from_file=false)
+{
+	$dom=new php4DOMDocument();
+	if ($from_file) $result=$dom->myDOMNode->loadHTMLFile($html_doc);
+	else $result=$dom->myDOMNode->loadHTML($html_doc);
+	return $result ? $dom : null;
+}
+function html_doc_file($filename) {return html_doc($filename,true);}
+function xmldoc($str) {return domxml_open_mem($str);}
+function xmldocfile($filename) {return domxml_open_file($filename);}
+function xpath_eval($xpath_context,$eval_str,$contextnode=null) {return $xpath_context->xpath_eval($eval_str,$contextnode);}
+function xpath_new_context($dom_document) {return new php4DOMXPath($dom_document);}
+function xpath_register_ns($xpath_context,$prefix,$namespaceURI) {return $xpath_context->myDOMXPath->registerNamespace($prefix,$namespaceURI);}
+function _entityDecode($text) {return html_entity_decode(strtr($text,array('&apos;'=>'\'')),ENT_QUOTES,'UTF-8');}
+function _error_report($error) {return array('errormessage'=>$error->message,'nodename'=>'','line'=>$error->line,'col'=>$error->column)+($error->file==''?array():array('directory'=>dirname($error->file),'file'=>basename($error->file)));}
+
+class php4DOMAttr extends php4DOMNode
+{
+	function __get($name)
+	{
+		if ($name==='name') return $this->myDOMNode->name;
+		else return parent::__get($name);
+	}
+	function name() {return $this->myDOMNode->name;}
+	function set_content($text) {}
+	//function set_value($content) {return $this->myDOMNode->value=htmlspecialchars($content,ENT_QUOTES);}
+	function specified() {return $this->myDOMNode->specified;}
+	function value() {return $this->myDOMNode->value;}
+}
+
+class php4DOMDocument extends php4DOMNode
+{
+	function php4DOMDocument($mode=DOMXML_LOAD_PARSING)
+	{
+		$this->myDOMNode=new DOMDocument();
+		$this->myOwnerDocument=$this;
+		if ($mode & DOMXML_LOAD_VALIDATING) $this->myDOMNode->validateOnParse=true;
+		if ($mode & DOMXML_LOAD_RECOVERING) $this->myDOMNode->recover=true;
+		if ($mode & DOMXML_LOAD_SUBSTITUTE_ENTITIES) $this->myDOMNode->substituteEntities=true;
+		if ($mode & DOMXML_LOAD_DONT_KEEP_BLANKS) $this->myDOMNode->preserveWhiteSpace=false;
+	}
+	function add_root($name)
+	{
+		if ($this->myDOMNode->hasChildNodes()) $this->myDOMNode->removeChild($this->myDOMNode->firstChild);
+		return new php4DOMElement($this->myDOMNode->appendChild($this->myDOMNode->createElement($name)),$this->myOwnerDocument);
+	}
+	function create_attribute($name,$value)
+	{
+		$myAttr=$this->myDOMNode->createAttribute($name);
+		$myAttr->value=htmlspecialchars($value,ENT_QUOTES);
+		return new php4DOMAttr($myAttr,$this);
+	}
+	function create_cdata_section($content) {return new php4DOMNode($this->myDOMNode->createCDATASection($content),$this);}
+	function create_comment($data) {return new php4DOMNode($this->myDOMNode->createComment($data),$this);}
+	function create_element($name) {return new php4DOMElement($this->myDOMNode->createElement($name),$this);}
+	function create_element_ns($uri,$name,$prefix=null)
+	{
+		if ($prefix==null) $prefix=$this->myDOMNode->lookupPrefix($uri);
+		if (($prefix==null)&&(($this->myDOMNode->documentElement==null)||(!$this->myDOMNode->documentElement->isDefaultNamespace($uri)))) $prefix='a'.sprintf('%u',crc32($uri));
+		return new php4DOMElement($this->myDOMNode->createElementNS($uri,$prefix==null ? $name : $prefix.':'.$name),$this);
+	}
+	function create_entity_reference($content) {return new php4DOMNode($this->myDOMNode->createEntityReference($content),$this);} //By Walter Ebert 2007-01-22
+	function create_processing_instruction($target,$data=''){return new php4DomProcessingInstruction($this->myDOMNode->createProcessingInstruction($target,$data),$this);}
+	function create_text_node($content) {return new php4DOMText($this->myDOMNode->createTextNode($content),$this);}
+	function document_element() {return parent::_newDOMElement($this->myDOMNode->documentElement,$this);}
+	function dump_file($filename,$compressionmode=false,$format=false)
+	{
+		$format0=$this->myDOMNode->formatOutput;
+		$this->myDOMNode->formatOutput=$format;
+		$res=$this->myDOMNode->save($filename);
+		$this->myDOMNode->formatOutput=$format0;
+		return $res;
+	}
+	function dump_mem($format=false,$encoding=false)
+	{
+		$format0=$this->myDOMNode->formatOutput;
+		$this->myDOMNode->formatOutput=$format;
+		$encoding0=$this->myDOMNode->encoding;
+		if ($encoding) $this->myDOMNode->encoding=$encoding;
+		$dump=$this->myDOMNode->saveXML();
+		$this->myDOMNode->formatOutput=$format0;
+		if ($encoding) $this->myDOMNode->encoding= $encoding0=='' ? 'UTF-8' : $encoding0; //UTF-8 is XML default encoding
+		return $dump;
+	}
+	function free()
+	{
+		if ($this->myDOMNode->hasChildNodes()) $this->myDOMNode->removeChild($this->myDOMNode->firstChild);
+		$this->myDOMNode=null;
+		$this->myOwnerDocument=null;
+	}
+	function get_element_by_id($id) {return parent::_newDOMElement($this->myDOMNode->getElementById($id),$this);}
+	function get_elements_by_tagname($name)
+	{
+		$myDOMNodeList=$this->myDOMNode->getElementsByTagName($name);
+		$nodeSet=array();
+		$i=0;
+		if (isset($myDOMNodeList))
+			while ($node=$myDOMNodeList->item($i++)) $nodeSet[]=new php4DOMElement($node,$this);
+		return $nodeSet;
+	}
+	function html_dump_mem() {return $this->myDOMNode->saveHTML();}
+	function root() {return parent::_newDOMElement($this->myDOMNode->documentElement,$this);}
+	function xinclude() {return $this->myDOMNode->xinclude();}
+	function xpath_new_context() {return new php4DOMXPath($this);}
+}
+
+class php4DOMElement extends php4DOMNode
+{
+	function add_namespace($uri,$prefix)
+	{
+		if ($this->myDOMNode->hasAttributeNS('http://www.w3.org/2000/xmlns/',$prefix)) return false;
+		else
+		{
+			$this->myDOMNode->setAttributeNS('http://www.w3.org/2000/xmlns/','xmlns:'.$prefix,$uri); //By Daniel Walker 2006-09-08
+			return true;
+		}
+	}
+	function get_attribute($name) {return $this->myDOMNode->getAttribute($name);}
+	function get_attribute_node($name) {return parent::_newDOMElement($this->myDOMNode->getAttributeNode($name),$this->myOwnerDocument);}
+	function get_elements_by_tagname($name)
+	{
+		$myDOMNodeList=$this->myDOMNode->getElementsByTagName($name);
+		$nodeSet=array();
+		$i=0;
+		if (isset($myDOMNodeList))
+			while ($node=$myDOMNodeList->item($i++)) $nodeSet[]=new php4DOMElement($node,$this->myOwnerDocument);
+		return $nodeSet;
+	}
+	function has_attribute($name) {return $this->myDOMNode->hasAttribute($name);}
+	function remove_attribute($name) {return $this->myDOMNode->removeAttribute($name);}
+	function set_attribute($name,$value)
+	{
+		//return $this->myDOMNode->setAttribute($name,$value); //Does not return a DomAttr
+		$myAttr=$this->myDOMNode->ownerDocument->createAttribute($name);
+		$myAttr->value=htmlspecialchars($value,ENT_QUOTES); //Entity problem reported by AL-DesignWorks 2007-09-07
+		$this->myDOMNode->setAttributeNode($myAttr);
+		return new php4DOMAttr($myAttr,$this->myOwnerDocument);
+	}
+	/*function set_attribute_node($attr)
+	{
+		$this->myDOMNode->setAttributeNode($this->_importNode($attr));
+		return $attr;
+	}*/
+	function set_name($name)
+	{
+		if ($this->myDOMNode->prefix=='') $newNode=$this->myDOMNode->ownerDocument->createElement($name);
+		else $newNode=$this->myDOMNode->ownerDocument->createElementNS($this->myDOMNode->namespaceURI,$this->myDOMNode->prefix.':'.$name);
+		$myDOMNodeList=$this->myDOMNode->attributes;
+		$i=0;
+		if (isset($myDOMNodeList))
+			while ($node=$myDOMNodeList->item($i++))
+				if ($node->namespaceURI=='') $newNode->setAttribute($node->name,$node->value);
+				else $newNode->setAttributeNS($node->namespaceURI,$node->nodeName,$node->value);
+		$myDOMNodeList=$this->myDOMNode->childNodes;
+		if (isset($myDOMNodeList))
+			while ($node=$myDOMNodeList->item(0)) $newNode->appendChild($node);
+		$this->myDOMNode->parentNode->replaceChild($newNode,$this->myDOMNode);
+		$this->myDOMNode=$newNode;
+		return true;
+	}
+	function tagname() {return $this->tagname;}
+}
+
+class php4DOMNode
+{
+	public $myDOMNode;
+	public $myOwnerDocument;
+	function php4DOMNode($aDomNode,$aOwnerDocument)
+	{
+		$this->myDOMNode=$aDomNode;
+		$this->myOwnerDocument=$aOwnerDocument;
+	}
+	function __get($name)
+	{
+		switch ($name)
+		{
+			case 'type': return $this->myDOMNode->nodeType;
+			case 'tagname': return ($this->myDOMNode->nodeType===XML_ELEMENT_NODE) ? $this->myDOMNode->localName : $this->myDOMNode->tagName; //Avoid namespace prefix for DOMElement
+			case 'content': return $this->myDOMNode->textContent;
+			case 'value': return $this->myDOMNode->value;
+			default:
+				$myErrors=debug_backtrace();
+				trigger_error('Undefined property: '.get_class($this).'::$'.$name.' ['.$myErrors[0]['file'].':'.$myErrors[0]['line'].']',E_USER_NOTICE);
+				return false;
+		}
+	}
+	function add_child($newnode) {return append_child($newnode);}
+	function add_namespace($uri,$prefix) {return false;}
+	function append_child($newnode) {return self::_newDOMElement($this->myDOMNode->appendChild($this->_importNode($newnode)),$this->myOwnerDocument);}
+	function append_sibling($newnode) {return self::_newDOMElement($this->myDOMNode->parentNode->appendChild($this->_importNode($newnode)),$this->myOwnerDocument);}
+	function attributes()
+	{
+		$myDOMNodeList=$this->myDOMNode->attributes;
+		if (!(isset($myDOMNodeList)&&$this->myDOMNode->hasAttributes())) return null;
+		$nodeSet=array();
+		$i=0;
+		while ($node=$myDOMNodeList->item($i++)) $nodeSet[]=new php4DOMAttr($node,$this->myOwnerDocument);
+		return $nodeSet;
+	}
+	function child_nodes()
+	{
+		$myDOMNodeList=$this->myDOMNode->childNodes;
+		$nodeSet=array();
+		$i=0;
+		if (isset($myDOMNodeList))
+			while ($node=$myDOMNodeList->item($i++)) $nodeSet[]=self::_newDOMElement($node,$this->myOwnerDocument);
+		return $nodeSet;
+	}
+	function children() {return $this->child_nodes();}
+	function clone_node($deep=false) {return self::_newDOMElement($this->myDOMNode->cloneNode($deep),$this->myOwnerDocument);}
+	//dump_node($node) should only be called on php4DOMDocument
+	function dump_node($node=null) {return $node==null ? $this->myOwnerDocument->myDOMNode->saveXML($this->myDOMNode) : $this->myOwnerDocument->myDOMNode->saveXML($node->myDOMNode);}
+	function first_child() {return self::_newDOMElement($this->myDOMNode->firstChild,$this->myOwnerDocument);}
+	function get_content() {return $this->myDOMNode->textContent;}
+	function has_attributes() {return $this->myDOMNode->hasAttributes();}
+	function has_child_nodes() {return $this->myDOMNode->hasChildNodes();}
+	function insert_before($newnode,$refnode) {return self::_newDOMElement($this->myDOMNode->insertBefore($this->_importNode($newnode),$refnode==null?null:$refnode->myDOMNode),$this->myOwnerDocument);}
+	function is_blank_node() {return ($this->myDOMNode->nodeType===XML_TEXT_NODE)&&preg_match('%^\s*$%',$this->myDOMNode->nodeValue);}
+	function last_child() {return self::_newDOMElement($this->myDOMNode->lastChild,$this->myOwnerDocument);}
+	function new_child($name,$content)
+	{
+		$mySubNode=$this->myDOMNode->ownerDocument->createElement($name);
+		$mySubNode->appendChild($this->myDOMNode->ownerDocument->createTextNode(_entityDecode($content)));
+		$this->myDOMNode->appendChild($mySubNode);
+		return new php4DOMElement($mySubNode,$this->myOwnerDocument);
+	}
+	function next_sibling() {return self::_newDOMElement($this->myDOMNode->nextSibling,$this->myOwnerDocument);}
+	function node_name() {return ($this->myDOMNode->nodeType===XML_ELEMENT_NODE) ? $this->myDOMNode->localName : $this->myDOMNode->nodeName;} //Avoid namespace prefix for DOMElement
+	function node_type() {return $this->myDOMNode->nodeType;}
+	function node_value() {return $this->myDOMNode->nodeValue;}
+	function owner_document() {return $this->myOwnerDocument;}
+	function parent_node() {return self::_newDOMElement($this->myDOMNode->parentNode,$this->myOwnerDocument);}
+	function prefix() {return $this->myDOMNode->prefix;}
+	function previous_sibling() {return self::_newDOMElement($this->myDOMNode->previousSibling,$this->myOwnerDocument);}
+	function remove_child($oldchild) {return self::_newDOMElement($this->myDOMNode->removeChild($oldchild->myDOMNode),$this->myOwnerDocument);}
+	function replace_child($newnode,$oldnode) {return self::_newDOMElement($this->myDOMNode->replaceChild($this->_importNode($newnode),$oldnode->myDOMNode),$this->myOwnerDocument);}
+	function replace_node($newnode) {return self::_newDOMElement($this->myDOMNode->parentNode->replaceChild($this->_importNode($newnode),$this->myDOMNode),$this->myOwnerDocument);}
+	function set_content($text) {return $this->myDOMNode->appendChild($this->myDOMNode->ownerDocument->createTextNode(_entityDecode($text)));} //Entity problem reported by AL-DesignWorks 2007-09-07
+	//function set_name($name) {return $this->myOwnerDocument->renameNode($this->myDOMNode,$this->myDOMNode->namespaceURI,$name);}
+	function set_namespace($uri,$prefix=null)
+	{//Contributions by Daniel Walker 2006-09-08
+		$nsprefix=$this->myDOMNode->lookupPrefix($uri);
+		if ($nsprefix==null)
+		{
+			$nsprefix= $prefix==null ? $nsprefix='a'.sprintf('%u',crc32($uri)) : $prefix;
+			if ($this->myDOMNode->nodeType===XML_ATTRIBUTE_NODE)
+			{
+				if (($prefix!=null)&&$this->myDOMNode->ownerElement->hasAttributeNS('http://www.w3.org/2000/xmlns/',$nsprefix)&&
+					($this->myDOMNode->ownerElement->getAttributeNS('http://www.w3.org/2000/xmlns/',$nsprefix)!=$uri))
+				{//Remove namespace
+					$parent=$this->myDOMNode->ownerElement;
+					$parent->removeAttributeNode($this->myDOMNode);
+					$parent->setAttribute($this->myDOMNode->localName,$this->myDOMNode->nodeValue);
+					$this->myDOMNode=$parent->getAttributeNode($this->myDOMNode->localName);
+					return;
+				}
+				$this->myDOMNode->ownerElement->setAttributeNS('http://www.w3.org/2000/xmlns/','xmlns:'.$nsprefix,$uri);
+			}
+		}
+		if ($this->myDOMNode->nodeType===XML_ATTRIBUTE_NODE)
+		{
+			$parent=$this->myDOMNode->ownerElement;
+			$parent->removeAttributeNode($this->myDOMNode);
+			$parent->setAttributeNS($uri,$nsprefix.':'.$this->myDOMNode->localName,$this->myDOMNode->nodeValue);
+			$this->myDOMNode=$parent->getAttributeNodeNS($uri,$this->myDOMNode->localName);
+		}
+		elseif ($this->myDOMNode->nodeType===XML_ELEMENT_NODE)
+		{
+			$NewNode=$this->myDOMNode->ownerDocument->createElementNS($uri,$nsprefix.':'.$this->myDOMNode->localName);
+			foreach ($this->myDOMNode->attributes as $n) $NewNode->appendChild($n->cloneNode(true));
+			foreach ($this->myDOMNode->childNodes as $n) $NewNode->appendChild($n->cloneNode(true));
+			$xpath=new DOMXPath($this->myDOMNode->ownerDocument);
+			$myDOMNodeList=$xpath->query('namespace::*[name()!="xml"]',$this->myDOMNode); //Add old namespaces
+			foreach ($myDOMNodeList as $n) $NewNode->setAttributeNS('http://www.w3.org/2000/xmlns/',$n->nodeName,$n->nodeValue); 
+			$this->myDOMNode->parentNode->replaceChild($NewNode,$this->myDOMNode);
+			$this->myDOMNode=$NewNode;
+		}
+	}
+	function unlink_node()
+	{
+		if ($this->myDOMNode->parentNode!=null)
+		{
+			if ($this->myDOMNode->nodeType===XML_ATTRIBUTE_NODE) $this->myDOMNode->parentNode->removeAttributeNode($this->myDOMNode);
+			else $this->myDOMNode->parentNode->removeChild($this->myDOMNode);
+		}
+	}
+	protected function _importNode($newnode) {return $this->myOwnerDocument===$newnode->myOwnerDocument ? $newnode->myDOMNode : $this->myOwnerDocument->myDOMNode->importNode($newnode->myDOMNode,true);} //To import DOMNode from another DOMDocument
+	static function _newDOMElement($aDOMNode,$aOwnerDocument)
+	{//Check the PHP5 DOMNode before creating a new associated PHP4 DOMNode wrapper
+		if ($aDOMNode==null) return null;
+		switch ($aDOMNode->nodeType)
+		{
+			case XML_ELEMENT_NODE: return new php4DOMElement($aDOMNode,$aOwnerDocument);
+			case XML_TEXT_NODE: return new php4DOMText($aDOMNode,$aOwnerDocument);
+			case XML_ATTRIBUTE_NODE: return new php4DOMAttr($aDOMNode,$aOwnerDocument);
+			case XML_PI_NODE: return new php4DomProcessingInstruction($aDOMNode,$aOwnerDocument);
+			default: return new php4DOMNode($aDOMNode,$aOwnerDocument);
+		}
+	}
+}
+
+class php4DomProcessingInstruction extends php4DOMNode
+{
+	function data() {return $this->myDOMNode->data;}
+	function target() {return $this->myDOMNode->target;}
+}
+
+class php4DOMText extends php4DOMNode
+{
+	function __get($name)
+	{
+		if ($name==='tagname') return '#text';
+		else return parent::__get($name);
+	}
+	function tagname() {return '#text';}
+	function set_content($text) {$this->myDOMNode->nodeValue=$text; return true;}
+}
+
+if (!defined('XPATH_NODESET'))
+{
+	define('XPATH_UNDEFINED',0);
+	define('XPATH_NODESET',1);
+	define('XPATH_BOOLEAN',2);
+	define('XPATH_NUMBER',3);
+	define('XPATH_STRING',4);
+	/*define('XPATH_POINT',5);
+	define('XPATH_RANGE',6);
+	define('XPATH_LOCATIONSET',7);
+	define('XPATH_USERS',8);
+	define('XPATH_XSLT_TREE',9);*/
+}
+
+class php4DOMNodelist
+{
+	private $myDOMNodelist;
+	public $nodeset;
+	public $type=XPATH_UNDEFINED;
+	public $value;
+	function php4DOMNodelist($aDOMNodelist,$aOwnerDocument)
+	{
+		if (!isset($aDOMNodelist)) return; 
+		elseif (is_object($aDOMNodelist)||is_array($aDOMNodelist))
+		{
+			if ($aDOMNodelist->length>0)
+			{
+				$this->myDOMNodelist=$aDOMNodelist;
+				$this->nodeset=array();
+				$this->type=XPATH_NODESET;
+				$i=0;
+				while ($node=$this->myDOMNodelist->item($i++)) $this->nodeset[]=php4DOMNode::_newDOMElement($node,$aOwnerDocument);
+			}
+		}
+		elseif (is_int($aDOMNodelist)||is_float($aDOMNodelist))
+		{
+			$this->type=XPATH_NUMBER;
+			$this->value=$aDOMNodelist;
+		}
+		elseif (is_bool($aDOMNodelist))
+		{
+			$this->type=XPATH_BOOLEAN;
+			$this->value=$aDOMNodelist;
+		}
+		elseif (is_string($aDOMNodelist))
+		{
+			$this->type=XPATH_STRING;
+			$this->value=$aDOMNodelist;
+		}
+	}
+}
+
+class php4DOMXPath
+{
+	public $myDOMXPath;
+	private $myOwnerDocument;
+	function php4DOMXPath($dom_document)
+	{
+		//TODO: If $dom_document is a DomElement, make that default $contextnode and modify XPath. Ex: '/test'
+		$this->myOwnerDocument=$dom_document->myOwnerDocument;
+		$this->myDOMXPath=new DOMXPath($this->myOwnerDocument->myDOMNode);
+	}
+	function xpath_eval($eval_str,$contextnode=null)
+	{
+		if (method_exists($this->myDOMXPath,'evaluate')) $xp=isset($contextnode) ? $this->myDOMXPath->evaluate($eval_str,$contextnode->myDOMNode) : $this->myDOMXPath->evaluate($eval_str);
+		else $xp=isset($contextnode) ? $this->myDOMXPath->query($eval_str,$contextnode->myDOMNode) : $this->myDOMXPath->query($eval_str);
+		$xp=new php4DOMNodelist($xp,$this->myOwnerDocument);
+		return ($xp->type===XPATH_UNDEFINED) ? false : $xp;
+	}
+	function xpath_register_ns($prefix,$namespaceURI) {return $this->myDOMXPath->registerNamespace($prefix,$namespaceURI);}
+}
+
+if (extension_loaded('xsl'))
+{//See also: http://alexandre.alapetite.net/doc-alex/xslt-php4-php5/
+	function domxml_xslt_stylesheet($xslstring) {return new php4DomXsltStylesheet(DOMDocument::loadXML($xslstring));}
+	function domxml_xslt_stylesheet_doc($dom_document) {return new php4DomXsltStylesheet($dom_document);}
+	function domxml_xslt_stylesheet_file($xslfile) {return new php4DomXsltStylesheet(DOMDocument::load($xslfile));}
+	class php4DomXsltStylesheet
+	{
+		private $myxsltProcessor;
+		function php4DomXsltStylesheet($dom_document)
+		{
+			$this->myxsltProcessor=new xsltProcessor();
+			$this->myxsltProcessor->importStyleSheet($dom_document);
+		}
+		function process($dom_document,$xslt_parameters=array(),$param_is_xpath=false)
+		{
+			foreach ($xslt_parameters as $param=>$value) $this->myxsltProcessor->setParameter('',$param,$value);
+			$myphp4DOMDocument=new php4DOMDocument();
+			$myphp4DOMDocument->myDOMNode=$this->myxsltProcessor->transformToDoc($dom_document->myDOMNode);
+			return $myphp4DOMDocument;
+		}
+		function result_dump_file($dom_document,$filename)
+		{
+			$html=$dom_document->myDOMNode->saveHTML();
+			file_put_contents($filename,$html);
+			return $html;
+		}
+		function result_dump_mem($dom_document) {return $dom_document->myDOMNode->saveHTML();}
+	}
+}
+?>

+ 27 - 0
main/auth/cas/lib/CAS/languages/catalan.php

@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file languages/spanish.php
+ * @author Iván-Benjamín García Torà <ivaniclixx AT gmail DOT com>
+ * @sa @link internalLang Internationalization @endlink
+ * @ingroup internalLang
+ */
+
+$this->_strings = array(
+ CAS_STR_USING_SERVER 
+ => 'usant servidor',
+ CAS_STR_AUTHENTICATION_WANTED 
+ => 'Autentificació CAS necessària!',
+ CAS_STR_LOGOUT 
+ => 'Sortida de CAS necessària!',
+ CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED 
+ => 'Ja hauria d\ haver estat redireccionat al servidor CAS. Feu click <a href="%s">aquí</a> per a continuar.',
+ CAS_STR_AUTHENTICATION_FAILED 
+ => 'Autentificació CAS fallida!',
+ CAS_STR_YOU_WERE_NOT_AUTHENTICATED 
+ => '<p>No estàs autentificat.</p><p>Pots tornar a intentar-ho fent click <a href="%s">aquí</a>.</p><p>Si el problema persisteix hauría de contactar amb l\'<a href="mailto:%s">administrador d\'aquest llocc</a>.</p>',
+ CAS_STR_SERVICE_UNAVAILABLE
+ => 'El servei `<b>%s</b>\' no està disponible (<b>%s</b>).'
+);
+
+?>

+ 27 - 0
main/auth/cas/lib/CAS/languages/english.php

@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file languages/english.php
+ * @author Pascal Aubry <pascal.aubry at univ-rennes1.fr>
+ * @sa @link internalLang Internationalization @endlink
+ * @ingroup internalLang
+ */
+
+$this->_strings = array(
+ CAS_STR_USING_SERVER 
+ => 'using server',
+ CAS_STR_AUTHENTICATION_WANTED 
+ => 'CAS Authentication wanted!',
+ CAS_STR_LOGOUT 
+ => 'CAS logout wanted!',
+ CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED 
+ => 'You should already have been redirected to the CAS server. Click <a href="%s">here</a> to continue.',
+ CAS_STR_AUTHENTICATION_FAILED 
+ => 'CAS Authentication failed!',
+ CAS_STR_YOU_WERE_NOT_AUTHENTICATED 
+ => '<p>You were not authenticated.</p><p>You may submit your request again by clicking <a href="%s">here</a>.</p><p>If the problem persists, you may contact <a href="mailto:%s">the administrator of this site</a>.</p>',
+ CAS_STR_SERVICE_UNAVAILABLE
+ => 'The service `<b>%s</b>\' is not available (<b>%s</b>).'
+);
+
+?>

+ 28 - 0
main/auth/cas/lib/CAS/languages/french.php

@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file languages/english.php
+ * @author Pascal Aubry <pascal.aubry at univ-rennes1.fr>
+ * @sa @link internalLang Internationalization @endlink
+ * @ingroup internalLang
+ */
+
+$this->_strings = array(
+ CAS_STR_USING_SERVER 
+ => 'utilisant le serveur',
+ CAS_STR_AUTHENTICATION_WANTED 
+ => 'Authentication CAS n�cessaire&nbsp;!',
+ CAS_STR_LOGOUT 
+ => 'D�connexion demand�e&nbsp;!',
+ CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED 
+ => 'Vous auriez du etre redirig�(e) vers le serveur CAS. Cliquez <a href="%s">ici</a> pour continuer.',
+ CAS_STR_AUTHENTICATION_FAILED 
+ => 'Authentification CAS infructueuse&nbsp;!',
+ CAS_STR_YOU_WERE_NOT_AUTHENTICATED 
+ => '<p>Vous n\'avez pas �t� authentifi�(e).</p><p>Vous pouvez soumettre votre requete � nouveau en cliquant <a href="%s">ici</a>.</p><p>Si le probl�me persiste, vous pouvez contacter <a href="mailto:%s">l\'administrateur de ce site</a>.</p>',
+ CAS_STR_SERVICE_UNAVAILABLE
+ => 'Le service `<b>%s</b>\' est indisponible (<b>%s</b>)'
+
+);
+
+?>

+ 27 - 0
main/auth/cas/lib/CAS/languages/german.php

@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file languages/german.php
+ * @author Henrik Genssen <hg at mediafactory.de>
+ * @sa @link internalLang Internationalization @endlink
+ * @ingroup internalLang
+ */
+
+$this->_strings = array(
+ CAS_STR_USING_SERVER 
+ => 'via Server',
+ CAS_STR_AUTHENTICATION_WANTED 
+ => 'CAS Authentifizierung erforderlich!',
+ CAS_STR_LOGOUT 
+ => 'CAS Abmeldung!',
+ CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED 
+ => 'eigentlich h&auml;ten Sie zum CAS Server weitergeleitet werden sollen. Dr&uuml;cken Sie <a href="%s">hier</a> um fortzufahren.',
+ CAS_STR_AUTHENTICATION_FAILED 
+ => 'CAS Anmeldung fehlgeschlagen!',
+ CAS_STR_YOU_WERE_NOT_AUTHENTICATED 
+ => '<p>Sie wurden nicht angemeldet.</p><p>Um es erneut zu versuchen klicken Sie <a href="%s">hier</a>.</p><p>Wenn das Problem bestehen bleibt, kontkatieren Sie den <a href="mailto:%s">Administrator</a> dieser Seite.</p>',
+ CAS_STR_SERVICE_UNAVAILABLE
+ => 'Der Dienst `<b>%s</b>\' ist nicht verf&uuml;gbar (<b>%s</b>).'
+);
+
+?>

+ 27 - 0
main/auth/cas/lib/CAS/languages/greek.php

@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file languages/greek.php
+ * @author Vangelis Haniotakis <haniotak at ucnet.uoc.gr>
+ * @sa @link internalLang Internationalization @endlink
+ * @ingroup internalLang
+ */
+
+$this->_strings = array(
+ CAS_STR_USING_SERVER 
+ => '��������������� � ������������',
+ CAS_STR_AUTHENTICATION_WANTED 
+ => '���������� � ����������� CAS!',
+ CAS_STR_LOGOUT 
+ => '���������� � ���������� ��� CAS!',
+ CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED 
+ => '�� ������ �� ������ �������������� ���� ����������� CAS. ����� ���� <a href="%s">���</a> ��� �� ����������.',
+ CAS_STR_AUTHENTICATION_FAILED 
+ => '� ����������� CAS �������!',
+ CAS_STR_YOU_WERE_NOT_AUTHENTICATED 
+ => '<p>��� ���������������.</p><p>�������� �� ����������������, �������� ���� <a href="%s">���</a>.</p><p>��� �� �������� ���������, ����� �� ����� �� ��� <a href="mailto:%s">�����������</a>.</p>',
+ CAS_STR_SERVICE_UNAVAILABLE
+ => '� �������� `<b>%s</b>\' ��� ����� ��������� (<b>%s</b>).'
+);
+
+?>

+ 27 - 0
main/auth/cas/lib/CAS/languages/japanese.php

@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file languages/japanese.php
+ * @author fnorif (fnorif@yahoo.co.jp)
+ * 
+ * Now Encoding is EUC-JP and LF
+ **/
+
+$this->_strings = array(
+ CAS_STR_USING_SERVER 
+ => 'using server',
+ CAS_STR_AUTHENTICATION_WANTED 
+ => 'CAS�ˤ��ǧ�ڤ�Ԥ��ޤ�',
+ CAS_STR_LOGOUT 
+ => 'CAS����?�����Ȥ��ޤ�!',
+ CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED 
+ => 'CAS�����Ф˹Ԥ�ɬ�פ�����ޤ�����ưŪ��ž������ʤ����� <a href="%s">������</a> �򥯥�å�����³�Ԥ��ޤ���',
+ CAS_STR_AUTHENTICATION_FAILED 
+ => 'CAS�ˤ��ǧ�ڤ˼��Ԥ��ޤ���',
+ CAS_STR_YOU_WERE_NOT_AUTHENTICATED 
+ => '<p>ǧ�ڤǤ��ޤ���Ǥ���.</p><p>�⤦���٥ꥯ�����Ȥ������������<a href="%s">������</a>�򥯥�å�.</p><p>���꤬��褷�ʤ����� <a href="mailto:%s">���Υ����Ȥδ����</a>���䤤��碌�Ƥ�������.</p>',
+ CAS_STR_SERVICE_UNAVAILABLE
+ => '�����ӥ� `<b>%s</b>\' �����ѤǤ��ޤ��� (<b>%s</b>).'
+);
+
+?>

+ 24 - 0
main/auth/cas/lib/CAS/languages/languages.php

@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file languages/languages.php
+ * Internationalization constants
+ * @author Pascal Aubry <pascal.aubry at univ-rennes1.fr>
+ * @sa @link internalLang Internationalization @endlink
+ * @ingroup internalLang
+ */
+
+//@{
+/**
+ * a phpCAS string index
+ */
+define("CAS_STR_USING_SERVER",                1);
+define("CAS_STR_AUTHENTICATION_WANTED",       2);
+define("CAS_STR_LOGOUT",                      3);
+define("CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED", 4);
+define("CAS_STR_AUTHENTICATION_FAILED",       5);
+define("CAS_STR_YOU_WERE_NOT_AUTHENTICATED",  6);
+define("CAS_STR_SERVICE_UNAVAILABLE",         7);
+//@}
+
+?>

+ 27 - 0
main/auth/cas/lib/CAS/languages/spanish.php

@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file languages/spanish.php
+ * @author Iván-Benjamín García Torà <ivaniclixx AT gmail DOT com>
+ * @sa @link internalLang Internationalization @endlink
+ * @ingroup internalLang
+ */
+
+$this->_strings = array(
+ CAS_STR_USING_SERVER 
+ => 'usando servidor',
+ CAS_STR_AUTHENTICATION_WANTED 
+ => '¡Autentificación CAS necesaria!',
+ CAS_STR_LOGOUT 
+ => '¡Salida CAS necesaria!',
+ CAS_STR_SHOULD_HAVE_BEEN_REDIRECTED 
+ => 'Ya debería haber sido redireccionado al servidor CAS. Haga click <a href="%s">aquí</a> para continuar.',
+ CAS_STR_AUTHENTICATION_FAILED 
+ => '¡Autentificación CAS fallida!',
+ CAS_STR_YOU_WERE_NOT_AUTHENTICATED 
+ => '<p>No estás autentificado.</p><p>Puedes volver a intentarlo haciendo click <a href="%s">aquí</a>.</p><p>Si el problema persiste debería contactar con el <a href="mailto:%s">administrador de este sitio</a>.</p>',
+ CAS_STR_SERVICE_UNAVAILABLE
+ => 'El servicio `<b>%s</b>\' no está disponible (<b>%s</b>).'
+);
+
+?>

+ 25 - 0
main/auth/cas/logincas.php

@@ -0,0 +1,25 @@
+<?php
+//----------------------------------------------------------------------
+// CAS PLUGIN  
+//----------------------------------------------------------------------
+// Copyright (c) 2006-2007 University Marc Bloch (UMB)
+//----------------------------------------------------------------------
+// This program is under the terms of the GENERAL PUBLIC LICENSE (GPL)
+// as published by the FREE SOFTWARE FOUNDATION. The GPL is available
+// through the world-wide-web at http://www.gnu.org/copyleft/gpl.html
+//----------------------------------------------------------------------
+// Authors: Pierre Cahard
+//----------------------------------------------------------------------
+// Load required 
+require('../..//inc/global.inc.php');
+require_once (api_get_path(LIBRARY_PATH).'events.lib.inc.php');
+require_once('authcas.php');
+global $cas_auth_ver, $cas_auth_server, $cas_auth_port, $cas_auth_uri; 
+// phpCAS
+if (!is_object($PHPCAS_CLIENT) ) {
+	phpCAS::client($cas_auth_ver,$cas_auth_server,$cas_auth_port,$cas_auth_uri);
+	phpCAS::setNoCasServerValidation();
+}
+phpCAS::forceAuthentication();
+//echo 'ici';
+header('Location: '.api_get_path(WEB_PATH).api_get_setting('page_after_login'));

+ 32 - 0
main/auth/conditional_login/complete_phone_number.php

@@ -0,0 +1,32 @@
+<?php
+require_once(dirname(__FILE__).'/../../inc/global.inc.php');
+require_once (api_get_path(LIBRARY_PATH).'conditionallogin.lib.php');
+require_once (api_get_path(LIBRARY_PATH).'usermanager.lib.php');
+$url =  api_get_path(WEB_PATH).'main/auth/conditional_login/complete_phone_number.php';
+
+if (! isset($_SESSION['conditional_login']['uid']))
+  die("Not Authorised");
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html lang="fr" xml:lang="fr" xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
+  </head>
+  <body>
+  <form id="data_completion" name="data_completion" method="post" action="<?php echo $url?>">
+        Téléphone : <input type="text" name="phone_number" />
+        <input type="submit" name="submit" value="Submit" />
+    </form>
+  </body>
+</html>
+<?php
+if (isset($_POST['submit'])){
+  $u = UserManager::get_user_info_by_id($_SESSION['conditional_login']['uid']);
+  $u['phone'] = $_POST['phone_number'];
+  $password = null; // we don't want to change the password 
+  $updated = UserManager::update_user($u['user_id'], $u['firstname'], $u['lastname'], $u['username'], $password, $u['auth_source'], $u['email'], $u['status'], $u['official_code'], $u['phone'], $u['picture_uri'], $u['expiration_date'], $u['active'], $u['creator_id'], $u['hr_dept_id'], $u['extra'], $u['language'],'');
+  if ($updated) {
+    ConditionalLogin::login();
+  }
+}
+?>

+ 32 - 0
main/auth/conditional_login/conditional_login.php

@@ -0,0 +1,32 @@
+<?php
+/*
+ This scrip is included by local.inc.php to redirect users to some url if some conditions are satisfied. Please populate the $dc_conditions array with a conditional function and an url. If the conditional function returns true the user will be redirected to URL at login
+
+ This array must be filled for this module to work. This is an example asking the user to enter his phone number if it is empty. Note you can enter more than one condition in the array. They will be checked in the array order.
+*/
+$dc_conditions = array();
+//array_push($dc_conditions, array(
+//  'conditional_function' => 'dc_check_phone_number',
+//  'url' => api_get_path(WEB_PATH).'main/auth/conditional_login/complete_phone_number.php'
+//));
+//array_push($dc_conditions, array(
+//  'conditional_function' => 'dc_check_first_login',
+//  'url' => api_get_path(WEB_PATH).'main/auth/conditional_login/first_login.php'
+//));
+require_once api_get_path(LIBRARY_PATH).'usermanager.lib.php';
+/*
+  Please implements the functions of the $dc_conditions array. Each of these fucntion will take a user array (user_id, username, password (crypted), auth_sourcen, active, expiration_date)
+ */
+function dc_check_phone_number($user){
+  $uInfo = UserManager::get_user_info_by_id($user['user_id']);
+  if ( empty($uInfo['phone'])) {
+    return true;
+  }
+  return false;
+}
+
+function dc_check_first_login($user){
+  $uInfo = UserManager::get_user_info_by_id($user['user_id']);
+  return(($uInfo['extra']['already_logged_in'] === 'false'));
+}
+?>

+ 93 - 0
main/auth/courses.php

@@ -0,0 +1,93 @@
+<?php
+/*
+ Template to automatically create a new user with information from anywhere.
+ This file is loaded by main/inc/local.inc.php
+ To use it please add this line to main/inc/conf/configuration.php :
+   $extAuthSource["external_logininfo"]["newUser"] = $_configuration['root_sys'].$_configuration['code_append']."auth/external_logininfo/newUser.php";
+ 
+ You also have to implements the external_get_user_info function in this file.
+ */
+	require_once(api_get_path(LIBRARY_PATH).'usermanager.lib.php');
+
+//TODO : Please implements this function for this module to work.
+/**
+ * Gets user info from external source 
+ * @param string login
+ * @param string password
+ * @return user array with at least the following fields:
+ *       firstname
+ *       lastname
+ *       status
+ *       email
+ *       login
+ *       password
+ *   or false if no data
+ **/
+function external_get_user_info($login, $password){
+  //Those are the mandatory fields for user creation.
+  //See external_add_user function for all the fields you can have.
+  $u = array(
+    'firstname' => 'firstname',
+    'lastname' => 'lastname',
+    'status' => STUDENT,
+    'email' => 'email@email.em',
+    'login' => $login,
+    'password' => $password
+  );
+  return $u; //Please return false if user does not exist
+  //return false;
+}
+
+/**
+ * Return an array with all user info
+ * @param associative array with at least thes fields setted :
+        firstname, lastname, status, email, login, password
+ * @return mixed   new user id - if the new user creation succeeds, false otherwise
+ **/
+function external_add_user($u){
+  //Setting default
+  if (! isset($u['official_code']) )
+    $u['official_code'] = '';
+  if (! isset($u['language']) )
+    $u['language'] = '';
+  if (! isset($u['phone']) )
+    $u['phone'] = '';
+  if (! isset($u['picture_uri']) )
+    $u['picture_uri'] = '';
+  if (! isset($u['auth_source']) )
+    $u['auth_source'] = PLATFORM_AUTH_SOURCE;
+  if (! isset($u['expiration_date']) )
+    $u['expiration_date'] = '0000-00-00 00:00:00';
+  if (! isset($u['active']) )
+    $u['active'] = 1;
+  if (! isset($u['hr_dept_id']) )
+    $u['hr_dept_id'] = 0; //id of responsible HR
+  if (! isset($u['extra']) )
+    $u['extra'] = null;
+  if (! isset($u['encrypt_method']) )
+    $u['encrypt_method'] = '';
+  
+  $chamilo_uid = UserManager::create_user($u['firstname'], $u['lastname'],$u['status'], $u['email'], $u['login'], $u['password'], $u['official_code'], $u['language'], $u['phone'],$u['picture_uri'], $u['auth_source'], $u['expiration_date'], $u['active'], $u['hr_dept_id'], $u['extra'], $u['encrypt_method']);
+  return $chamilo_uid;
+}
+
+
+//MAIN CODE
+
+//$login and $password variables are setted in main/inc/local.inc.php
+
+$user = external_get_user_info($login, $password);
+
+if ($user !== false && ($chamilo_uid = external_add_user($user)) !== false) {
+    //log in the user
+    $loginFailed = false;
+    $uidReset = true;
+    $_user['user_id'] = $chamilo_uid;
+    api_session_register('_uid');
+    event_login();
+} else {
+	$loginFailed = true;
+	unset($_user['user_id']);
+	$uidReset = false;
+}
+?>

+ 74 - 1
main/auth/inscription.php

@@ -16,6 +16,7 @@ require_once api_get_path(LIBRARY_PATH).'usermanager.lib.php';
 require_once api_get_path(CONFIGURATION_PATH).'profile.conf.php';
 require_once api_get_path(LIBRARY_PATH).'mail.lib.inc.php';
 require_once api_get_path(LIBRARY_PATH).'legal.lib.php';
+require_once api_get_path(LIBRARY_PATH).'custompages.lib.php';
 
 // Load terms & conditions from the current lang
 if (api_get_setting('allow_terms_conditions') == 'true') {
@@ -47,7 +48,79 @@ if (api_get_setting('allow_terms_conditions') == 'true') {
     }
 }
 
-$tool_name = get_lang('Registration',null,(!empty($_POST['language'])?api_get_valid_language($_POST['language']):$_user['language']));
+// Custom pages
+if (api_get_setting('use_custom_pages') == 'true') {
+	if (isset($_POST['username'])) {
+		$values = array();
+		$values['firstname'] = $_POST['firstname'];
+		$values['lastname'] = $_POST['lastname'];
+		$values['status'] = $_POST['status'];
+		$values['email'] = $_POST['email'];
+		$values['username'] = $_POST['username'];
+		$values['pass1'] = $_POST['pass1'];
+		$values['official_code'] = '';
+		$values['language'] = $_POST['language'];
+		$values['phone'] = $_POST['phone'];
+		$picture_uri = null;
+		$user_id = UserManager::create_user($values['firstname'], $values['lastname'], $values['status'], $values['email'], $values['username'], $values['pass1'], $values['official_code'], $values['language'], $values['phone'], $picture_uri);
+		if ($user_id) {
+			/*
+					  SESSION REGISTERING
+			 */
+			$_user['firstName'] = stripslashes($values['firstname']);
+			$_user['lastName'] 	= stripslashes($values['lastname']);
+			$_user['mail'] 		= $values['email'];
+			$_user['language'] 	= $values['language'];
+			$_user['user_id']	= $user_id;
+			$is_allowedCreateCourse = $values['status'] == 1;
+			api_session_register('_user');
+			api_session_register('is_allowedCreateCourse');
+
+			//stats
+			event_login();
+			// last user login date is now
+			$user_last_login_datetime = 0; // used as a unix timestamp it will correspond to : 1 1 1970
+
+			api_session_register('user_last_login_datetime');
+
+			/*
+						 EMAIL NOTIFICATION
+			 */
+
+			if (strpos($values['email'], '@') !== false) {
+				// Let us predefine some variables. Be sure to change the from address!
+				$recipient_name = api_get_person_name($values['firstname'], $values['lastname']);
+				$email = $values['email'];
+				$emailfromaddr = api_get_setting('emailAdministrator');
+				$emailfromname = api_get_setting('siteName');
+				$emailsubject = '['.api_get_setting('siteName').'] '.get_lang('YourReg',null,$_user['language']).' '.api_get_setting('siteName');
+
+				// The body can be as long as you wish, and any combination of text and variables
+				$portal_url = $_configuration['root_web'];
+				if ($_configuration['multiple_access_urls']) {
+					$access_url_id = api_get_current_access_url_id();
+					if ($access_url_id != -1 ){
+						$url = api_get_access_url($access_url_id);
+						$portal_url = $url['url'];
+					}
+				}
+
+				$emailbody = get_lang('Dear',null,$_user['language']).' '.stripslashes(Security::remove_XSS($recipient_name)).",\n\n".get_lang('YouAreReg',null,$_user['language']).' '.api_get_setting('siteName').' '.get_lang('WithTheFollowingSettings',null,$_user['language'])."\n\n".get_lang('Username',null,$_user['language']).' : '.$values['username']."\n".get_lang('Pass',null,$_user['language']).' : '.stripslashes($values['pass1'])."\n\n".get_lang('Address',null,$_user['language']).' '.api_get_setting('siteName').' '.get_lang('Is',null,$_user['language']).' : '.$portal_url."\n\n".get_lang('Problem',null,$_user['language'])."\n\n".get_lang('Formula',null,$_user['language']).",\n\n".api_get_person_name(api_get_setting('administratorName'), api_get_setting('administratorSurname'))."\n".get_lang('Manager',null,$_user['language']).' '.api_get_setting('siteName')."\nT. ".api_get_setting('administratorTelephone')."\n".get_lang('Email',null,$_user['language']).' : '.api_get_setting('emailAdministrator');
+
+				// Here we are forming one large header line
+				// Every header must be followed by a \n except the last
+				$sender_name = api_get_person_name(api_get_setting('administratorName'), api_get_setting('administratorSurname'), null, PERSON_NAME_EMAIL_ADDRESS);
+				$email_admin = api_get_setting('emailAdministrator');
+				@api_mail($recipient_name, $email, $emailsubject, $emailbody, $sender_name, $email_admin);
+			}
+			CustomPages::displayPage('registration-feedback');
+	}
+	}
+	else {
+	CustomPages::displayPage('registration');
+	}
+}
+$tool_name = get_lang('Registration',null,(!empty($_POST['language'])?$_POST['language']:$_user['language']));
 Display :: display_header($tool_name);
 
 echo Display::tag('h1', $tool_name);

+ 50 - 2
main/auth/ldap/authldap.php

@@ -21,7 +21,55 @@ require_once '../inc/global.inc.php';
 require_once api_get_path(LIBRARY_PATH).'login.lib.php';
 require_once api_get_path(LIBRARY_PATH).'formvalidator/FormValidator.class.php';
 require_once api_get_path(LIBRARY_PATH).'mail.lib.inc.php';
+require_once api_get_path(LIBRARY_PATH).'custompages.lib.php';
 
+// Custom pages
+// Had to move the form handling in here, because otherwise there would already be some display output.
+if (api_get_setting('use_custom_pages') == 'true') {
+	if (isset ($_POST['user']) && isset ($_POST['email'])) {
+		$user = $_POST['user'];
+		$email = $_POST['email'];
+
+		$condition = '';
+		if (!empty($email)) {
+			$condition = " AND LOWER(email) = '".Database::escape_string($email)."' ";
+		}
+
+		$tbl_user = Database :: get_main_table(TABLE_MAIN_USER);
+		$query = " SELECT user_id AS uid, lastname AS lastName, firstname AS firstName,
+					username AS loginName, password, email, status AS status,
+					official_code, phone, picture_uri, creator_id
+					FROM ".$tbl_user."
+					WHERE ( username = '".Database::escape_string($user)."' $condition ) ";
+
+		$result 	= Database::query($query);
+		$num_rows 	= Database::num_rows($result);
+
+		if ($result && $num_rows > 0) {
+			if ($num_rows > 1) {
+				$by_username = false; // more than one user
+				while ($data = Database::fetch_array($result)) {
+					$user[] = $data;
+				}
+			} else {
+				$by_username = true; // single user (valid user + email)
+				$user = Database::fetch_array($result);
+			}
+			if ($userPasswordCrypted != 'none') {
+				Login::handle_encrypted_password($user, $by_username);
+			} else {
+				Login::send_password_to_user($user, $by_username);
+			}
+		} else {
+			Display::display_error_message(get_lang('NoUserAccountWithThisEmailAddress'));
+		}
+		$msg = Login::reset_password($_GET["reset"], $_GET["id"], true);
+		CustomPages::displayPage('lostpassword-feedback');
+	}
+	else {
+		CustomPages::displayPage('lostpassword');
+	}
+}
 $tool_name = get_lang('LostPassword');
 Display :: display_header($tool_name);
 
@@ -35,7 +83,7 @@ if (api_get_setting('allow_lostpassword') == 'false') {
 echo Display::tag('h1', $tool_name);
 
 if (isset ($_GET['reset']) && isset ($_GET['id'])) {
-	$msg = Login::reset_password($_GET["reset"], $_GET["id"], true);
+	//$msg = Login::reset_password($_GET["reset"], $_GET["id"], true);
 	$msg1= '<a href="'.api_get_path(WEB_CODE_PATH).'auth/lostPassword.php" class="fake_button_back" >'.get_lang('Back').'</a>';	
 	echo '<br /><br /><div class="actions" >'.$msg1.'</div>';
 } else {
@@ -98,4 +146,4 @@ if (isset ($_GET['reset']) && isset ($_GET['id'])) {
 		$form->display();
 	}
 }
-Display::display_footer();
+Display::display_footer();

+ 1 - 1
main/auth/lost_password.lib.php

@@ -362,7 +362,7 @@ if ($form->validate() && is_settings_editable()) {
 	foreach ($update_values as $index =>$value) {
 		$update_values[$index] = Database::escape_string($value);
 	}
-    
+	unset($value);
     
 	$table_course = Database :: get_main_table(TABLE_MAIN_COURSE);
 	$sql = "UPDATE $table_course SET

+ 538 - 0
main/course_progress/index.php

@@ -0,0 +1,538 @@
+/*
+ *  File:         demo_table.css
+ *  CVS:          $Id$
+ *  Description:  CSS descriptions for DataTables demo pages
+ *  Author:       Allan Jardine
+ *  Created:      Tue May 12 06:47:22 BST 2009
+ *  Modified:     $Date$ by $Author$
+ *  Language:     CSS
+ *  Project:      DataTables
+ *
+ *  Copyright 2009 Allan Jardine. All Rights Reserved.
+ *
+ * ***************************************************************************
+ * DESCRIPTION
+ *
+ * The styles given here are suitable for the demos that are used with the standard DataTables
+ * distribution (see www.datatables.net). You will most likely wish to modify these styles to
+ * meet the layout requirements of your site.
+ *
+ * Common issues:
+ *   'full_numbers' pagination - I use an extra selector on the body tag to ensure that there is
+ *     no conflict between the two pagination types. If you want to use full_numbers pagination
+ *     ensure that you either have "example_alt_pagination" as a body class name, or better yet,
+ *     modify that selector.
+ *   Note that the path used for Images is relative. All images are by default located in
+ *     images/dataTable/ - relative to this CSS file.
+ */
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables features
+ */
+
+.dataTables_wrapper {
+	position: relative;
+	min-height: 302px;
+	clear: both;
+	_height: 302px;
+	zoom: 1; /* Feeling sorry for IE */
+}
+
+.dataTables_processing {
+	position: absolute;
+	top: 50%;
+	left: 50%;
+	width: 250px;
+	height: 30px;
+	margin-left: -125px;
+	margin-top: -15px;
+	padding: 14px 0 2px 0;
+	border: 1px solid #ddd;
+	text-align: center;
+	color: #999;
+	font-size: 14px;
+	background-color: white;
+}
+
+.dataTables_length {
+	width: 40%;
+	float: left;
+}
+
+.dataTables_filter {
+	width: 50%;
+	float: right;
+	text-align: right;
+}
+
+.dataTables_info {
+	width: 60%;
+	float: left;
+}
+
+.dataTables_paginate {
+	width: 44px;
+	* width: 50px;
+	float: right;
+	text-align: right;
+}
+
+/* Pagination nested */
+.paginate_disabled_previous, .paginate_enabled_previous, .paginate_disabled_next, .paginate_enabled_next {
+	height: 19px;
+	width: 19px;
+	margin-left: 3px;
+	float: left;
+}
+
+.paginate_disabled_previous {
+	background-image: url('images/dataTale/back_disabled.jpg');
+}
+
+.paginate_enabled_previous {
+	background-image: url('images/dataTable/back_enabled.jpg');
+}
+
+.paginate_disabled_next {
+	background-image: url('images/dataTable/forward_disabled.jpg');
+}
+
+.paginate_enabled_next {
+	background-image: url('images/dataTable/forward_enabled.jpg');
+}
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables display
+ */
+table.display {
+	margin: 0 auto;
+	clear: both;
+	width: 100%;
+	
+	/* Note Firefox 3.5 and before have a bug with border-collapse
+	 * ( https://bugzilla.mozilla.org/show%5Fbug.cgi?id=155955 ) 
+	 * border-spacing: 0; is one possible option. Conditional-css.com is
+	 * useful for this kind of thing
+	 *
+	 * Further note IE 6/7 has problems when calculating widths with border width.
+	 * It subtracts one px relative to the other browsers from the first column, and
+	 * adds one to the end...
+	 *
+	 * If you want that effect I'd suggest setting a border-top/left on th/td's and 
+	 * then filling in the gaps with other borders.
+	 */
+}
+
+table.display thead th {
+	padding: 3px 18px 3px 10px;
+	border-bottom: 1px solid black;
+	font-weight: bold;
+	cursor: pointer;
+	* cursor: hand;
+}
+
+table.display tfoot th {
+	padding: 3px 18px 3px 10px;
+	border-top: 1px solid black;
+	font-weight: bold;
+}
+
+table.display tr.heading2 td {
+	border-bottom: 1px solid #aaa;
+}
+
+table.display td {
+	padding: 3px 10px;
+}
+
+table.display td.center {
+	text-align: center;
+}
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables sorting
+ */
+
+.sorting_asc {
+	background: url('images/dataTable/sort_asc.png') no-repeat center right;
+}
+
+.sorting_desc {
+	background: url('images/dataTable/sort_desc.png') no-repeat center right;
+}
+
+.sorting {
+	background: url('images/dataTable/sort_both.png') no-repeat center right;
+}
+
+.sorting_asc_disabled {
+	background: url('images/dataTable/sort_asc_disabled.png') no-repeat center right;
+}
+
+.sorting_desc_disabled {
+	background: url('images/dataTable/sort_desc_disabled.png') no-repeat center right;
+}
+
+
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables row classes
+ */
+table.display tr.odd.gradeA {
+	background-color: #ddffdd;
+}
+
+table.display tr.even.gradeA {
+	background-color: #eeffee;
+}
+
+table.display tr.odd.gradeC {
+	background-color: #ddddff;
+}
+
+table.display tr.even.gradeC {
+	background-color: #eeeeff;
+}
+
+table.display tr.odd.gradeX {
+	background-color: #ffdddd;
+}
+
+table.display tr.even.gradeX {
+	background-color: #ffeeee;
+}
+
+table.display tr.odd.gradeU {
+	background-color: #ddd;
+}
+
+table.display tr.even.gradeU {
+	background-color: #eee;
+}
+
+
+tr.odd {
+	background-color: #E2E4FF;
+}
+
+tr.even {
+	background-color: white;
+}
+
+
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Misc
+ */
+.dataTables_scroll {
+	clear: both;
+}
+
+.dataTables_scrollBody {
+	*margin-top: -1px;
+}
+
+.top, .bottom {
+	padding: 15px;
+	background-color: #F5F5F5;
+	border: 1px solid #CCCCCC;
+}
+
+.top .dataTables_info {
+	float: none;
+}
+
+.clear {
+	clear: both;
+}
+
+.dataTables_empty {
+	text-align: center;
+}
+
+tfoot input {
+	margin: 0.5em 0;
+	width: 100%;
+	color: #444;
+}
+
+tfoot input.search_init {
+	color: #999;
+}
+
+td.group {
+	background-color: #d1cfd0;
+	border-bottom: 2px solid #A19B9E;
+	border-top: 2px solid #A19B9E;
+}
+
+td.details {
+	background-color: #d1cfd0;
+	border: 2px solid #A19B9E;
+}
+
+
+.example_alt_pagination div.dataTables_info {
+	width: 40%;
+}
+
+.paging_full_numbers {
+	width: 400px;
+	height: 22px;
+	line-height: 22px;
+}
+
+.paging_full_numbers span.paginate_button,
+ 	.paging_full_numbers span.paginate_active {
+	border: 1px solid #aaa;
+	-webkit-border-radius: 5px;
+	-moz-border-radius: 5px;
+	padding: 2px 5px;
+	margin: 0 3px;
+	cursor: pointer;
+	*cursor: hand;
+}
+
+.paging_full_numbers span.paginate_button {
+	background-color: #ddd;
+}
+
+.paging_full_numbers span.paginate_button:hover {
+	background-color: #ccc;
+}
+
+.paging_full_numbers span.paginate_active {
+	background-color: #99B3FF;
+}
+
+table.display tr.even.row_selected td {
+	background-color: #B0BED9;
+}
+
+table.display tr.odd.row_selected td {
+	background-color: #9FAFD1;
+}
+
+
+/*
+ * Sorting classes for columns
+ */
+/* For the standard odd/even */
+tr.odd td.sorting_1 {
+	background-color: #D3D6FF;
+}
+
+tr.odd td.sorting_2 {
+	background-color: #DADCFF;
+}
+
+tr.odd td.sorting_3 {
+	background-color: #E0E2FF;
+}
+
+tr.even td.sorting_1 {
+	background-color: #EAEBFF;
+}
+
+tr.even td.sorting_2 {
+	background-color: #F2F3FF;
+}
+
+tr.even td.sorting_3 {
+	background-color: #F9F9FF;
+}
+
+
+/* For the Conditional-CSS grading rows */
+/*
+ 	Colour calculations (based off the main row colours)
+  Level 1:
+		dd > c4
+		ee > d5
+	Level 2:
+	  dd > d1
+	  ee > e2
+ */
+tr.odd.gradeA td.sorting_1 {
+	background-color: #c4ffc4;
+}
+
+tr.odd.gradeA td.sorting_2 {
+	background-color: #d1ffd1;
+}
+
+tr.odd.gradeA td.sorting_3 {
+	background-color: #d1ffd1;
+}
+
+tr.even.gradeA td.sorting_1 {
+	background-color: #d5ffd5;
+}
+
+tr.even.gradeA td.sorting_2 {
+	background-color: #e2ffe2;
+}
+
+tr.even.gradeA td.sorting_3 {
+	background-color: #e2ffe2;
+}
+
+tr.odd.gradeC td.sorting_1 {
+	background-color: #c4c4ff;
+}
+
+tr.odd.gradeC td.sorting_2 {
+	background-color: #d1d1ff;
+}
+
+tr.odd.gradeC td.sorting_3 {
+	background-color: #d1d1ff;
+}
+
+tr.even.gradeC td.sorting_1 {
+	background-color: #d5d5ff;
+}
+
+tr.even.gradeC td.sorting_2 {
+	background-color: #e2e2ff;
+}
+
+tr.even.gradeC td.sorting_3 {
+	background-color: #e2e2ff;
+}
+
+tr.odd.gradeX td.sorting_1 {
+	background-color: #ffc4c4;
+}
+
+tr.odd.gradeX td.sorting_2 {
+	background-color: #ffd1d1;
+}
+
+tr.odd.gradeX td.sorting_3 {
+	background-color: #ffd1d1;
+}
+
+tr.even.gradeX td.sorting_1 {
+	background-color: #ffd5d5;
+}
+
+tr.even.gradeX td.sorting_2 {
+	background-color: #ffe2e2;
+}
+
+tr.even.gradeX td.sorting_3 {
+	background-color: #ffe2e2;
+}
+
+tr.odd.gradeU td.sorting_1 {
+	background-color: #c4c4c4;
+}
+
+tr.odd.gradeU td.sorting_2 {
+	background-color: #d1d1d1;
+}
+
+tr.odd.gradeU td.sorting_3 {
+	background-color: #d1d1d1;
+}
+
+tr.even.gradeU td.sorting_1 {
+	background-color: #d5d5d5;
+}
+
+tr.even.gradeU td.sorting_2 {
+	background-color: #e2e2e2;
+}
+
+tr.even.gradeU td.sorting_3 {
+	background-color: #e2e2e2;
+}
+
+
+/*
+ * Row highlighting example
+ */
+.ex_highlight #example tbody tr.even:hover, #example tbody tr.even td.highlighted {
+	background-color: #ECFFB3;
+}
+
+.ex_highlight #example tbody tr.odd:hover, #example tbody tr.odd td.highlighted {
+	background-color: #E6FF99;
+}
+
+.ex_highlight_row #example tr.even:hover {
+	background-color: #ECFFB3;
+}
+
+.ex_highlight_row #example tr.even:hover td.sorting_1 {
+	background-color: #DDFF75;
+}
+
+.ex_highlight_row #example tr.even:hover td.sorting_2 {
+	background-color: #E7FF9E;
+}
+
+.ex_highlight_row #example tr.even:hover td.sorting_3 {
+	background-color: #E2FF89;
+}
+
+.ex_highlight_row #example tr.odd:hover {
+	background-color: #E6FF99;
+}
+
+.ex_highlight_row #example tr.odd:hover td.sorting_1 {
+	background-color: #D6FF5C;
+}
+
+.ex_highlight_row #example tr.odd:hover td.sorting_2 {
+	background-color: #E0FF84;
+}
+
+.ex_highlight_row #example tr.odd:hover td.sorting_3 {
+	background-color: #DBFF70;
+}
+
+
+/*
+ * KeyTable
+ */
+table.KeyTable td {
+	border: 3px solid transparent;
+}
+
+table.KeyTable td.focus {
+	border: 3px solid #3366FF;
+}
+
+table.display tr.gradeA {
+	background-color: #eeffee;
+}
+
+table.display tr.gradeC {
+	background-color: #ddddff;
+}
+
+table.display tr.gradeX {
+	background-color: #ffdddd;
+}
+
+table.display tr.gradeU {
+	background-color: #ddd;
+}
+
+div.box {
+	height: 100px;
+	padding: 10px;
+	overflow: auto;
+	border: 1px solid #8080FF;
+	background-color: #E5E5FF;
+}

BIN
main/css/academica/default.css


BIN
main/css/academica/images/dataTable/back_disabled.jpg


BIN
main/css/academica/images/dataTable/back_enabled.jpg


BIN
main/css/academica/images/dataTable/favicon.ico


BIN
main/css/academica/images/dataTable/forward_disabled.jpg


BIN
main/css/academica/images/dataTable/forward_enabled.jpg


BIN
main/css/academica/images/dataTable/sort_asc.png


BIN
main/css/academica/images/dataTable/sort_asc_disabled.png


BIN
main/css/academica/images/dataTable/sort_both.png


BIN
main/css/academica/images/dataTable/sort_desc.png


BIN
main/css/academica/images/dataTable/sort_desc_disabled.png


+ 538 - 0
main/css/academica/learnpath.css

@@ -0,0 +1,538 @@
+/*
+ *  File:         demo_table.css
+ *  CVS:          $Id$
+ *  Description:  CSS descriptions for DataTables demo pages
+ *  Author:       Allan Jardine
+ *  Created:      Tue May 12 06:47:22 BST 2009
+ *  Modified:     $Date$ by $Author$
+ *  Language:     CSS
+ *  Project:      DataTables
+ *
+ *  Copyright 2009 Allan Jardine. All Rights Reserved.
+ *
+ * ***************************************************************************
+ * DESCRIPTION
+ *
+ * The styles given here are suitable for the demos that are used with the standard DataTables
+ * distribution (see www.datatables.net). You will most likely wish to modify these styles to
+ * meet the layout requirements of your site.
+ *
+ * Common issues:
+ *   'full_numbers' pagination - I use an extra selector on the body tag to ensure that there is
+ *     no conflict between the two pagination types. If you want to use full_numbers pagination
+ *     ensure that you either have "example_alt_pagination" as a body class name, or better yet,
+ *     modify that selector.
+ *   Note that the path used for Images is relative. All images are by default located in
+ *     images/dataTable/ - relative to this CSS file.
+ */
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables features
+ */
+
+.dataTables_wrapper {
+	position: relative;
+	min-height: 302px;
+	clear: both;
+	_height: 302px;
+	zoom: 1; /* Feeling sorry for IE */
+}
+
+.dataTables_processing {
+	position: absolute;
+	top: 50%;
+	left: 50%;
+	width: 250px;
+	height: 30px;
+	margin-left: -125px;
+	margin-top: -15px;
+	padding: 14px 0 2px 0;
+	border: 1px solid #ddd;
+	text-align: center;
+	color: #999;
+	font-size: 14px;
+	background-color: white;
+}
+
+.dataTables_length {
+	width: 40%;
+	float: left;
+}
+
+.dataTables_filter {
+	width: 50%;
+	float: right;
+	text-align: right;
+}
+
+.dataTables_info {
+	width: 60%;
+	float: left;
+}
+
+.dataTables_paginate {
+	width: 44px;
+	* width: 50px;
+	float: right;
+	text-align: right;
+}
+
+/* Pagination nested */
+.paginate_disabled_previous, .paginate_enabled_previous, .paginate_disabled_next, .paginate_enabled_next {
+	height: 19px;
+	width: 19px;
+	margin-left: 3px;
+	float: left;
+}
+
+.paginate_disabled_previous {
+	background-image: url('images/dataTale/back_disabled.jpg');
+}
+
+.paginate_enabled_previous {
+	background-image: url('images/dataTable/back_enabled.jpg');
+}
+
+.paginate_disabled_next {
+	background-image: url('images/dataTable/forward_disabled.jpg');
+}
+
+.paginate_enabled_next {
+	background-image: url('images/dataTable/forward_enabled.jpg');
+}
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables display
+ */
+table.display {
+	margin: 0 auto;
+	clear: both;
+	width: 100%;
+	
+	/* Note Firefox 3.5 and before have a bug with border-collapse
+	 * ( https://bugzilla.mozilla.org/show%5Fbug.cgi?id=155955 ) 
+	 * border-spacing: 0; is one possible option. Conditional-css.com is
+	 * useful for this kind of thing
+	 *
+	 * Further note IE 6/7 has problems when calculating widths with border width.
+	 * It subtracts one px relative to the other browsers from the first column, and
+	 * adds one to the end...
+	 *
+	 * If you want that effect I'd suggest setting a border-top/left on th/td's and 
+	 * then filling in the gaps with other borders.
+	 */
+}
+
+table.display thead th {
+	padding: 3px 18px 3px 10px;
+	border-bottom: 1px solid black;
+	font-weight: bold;
+	cursor: pointer;
+	* cursor: hand;
+}
+
+table.display tfoot th {
+	padding: 3px 18px 3px 10px;
+	border-top: 1px solid black;
+	font-weight: bold;
+}
+
+table.display tr.heading2 td {
+	border-bottom: 1px solid #aaa;
+}
+
+table.display td {
+	padding: 3px 10px;
+}
+
+table.display td.center {
+	text-align: center;
+}
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables sorting
+ */
+
+.sorting_asc {
+	background: url('images/dataTable/sort_asc.png') no-repeat center right;
+}
+
+.sorting_desc {
+	background: url('images/dataTable/sort_desc.png') no-repeat center right;
+}
+
+.sorting {
+	background: url('images/dataTable/sort_both.png') no-repeat center right;
+}
+
+.sorting_asc_disabled {
+	background: url('images/dataTable/sort_asc_disabled.png') no-repeat center right;
+}
+
+.sorting_desc_disabled {
+	background: url('images/dataTable/sort_desc_disabled.png') no-repeat center right;
+}
+
+
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables row classes
+ */
+table.display tr.odd.gradeA {
+	background-color: #ddffdd;
+}
+
+table.display tr.even.gradeA {
+	background-color: #eeffee;
+}
+
+table.display tr.odd.gradeC {
+	background-color: #ddddff;
+}
+
+table.display tr.even.gradeC {
+	background-color: #eeeeff;
+}
+
+table.display tr.odd.gradeX {
+	background-color: #ffdddd;
+}
+
+table.display tr.even.gradeX {
+	background-color: #ffeeee;
+}
+
+table.display tr.odd.gradeU {
+	background-color: #ddd;
+}
+
+table.display tr.even.gradeU {
+	background-color: #eee;
+}
+
+
+tr.odd {
+	background-color: #E2E4FF;
+}
+
+tr.even {
+	background-color: white;
+}
+
+
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Misc
+ */
+.dataTables_scroll {
+	clear: both;
+}
+
+.dataTables_scrollBody {
+	*margin-top: -1px;
+}
+
+.top, .bottom {
+	padding: 15px;
+	background-color: #F5F5F5;
+	border: 1px solid #CCCCCC;
+}
+
+.top .dataTables_info {
+	float: none;
+}
+
+.clear {
+	clear: both;
+}
+
+.dataTables_empty {
+	text-align: center;
+}
+
+tfoot input {
+	margin: 0.5em 0;
+	width: 100%;
+	color: #444;
+}
+
+tfoot input.search_init {
+	color: #999;
+}
+
+td.group {
+	background-color: #d1cfd0;
+	border-bottom: 2px solid #A19B9E;
+	border-top: 2px solid #A19B9E;
+}
+
+td.details {
+	background-color: #d1cfd0;
+	border: 2px solid #A19B9E;
+}
+
+
+.example_alt_pagination div.dataTables_info {
+	width: 40%;
+}
+
+.paging_full_numbers {
+	width: 400px;
+	height: 22px;
+	line-height: 22px;
+}
+
+.paging_full_numbers span.paginate_button,
+ 	.paging_full_numbers span.paginate_active {
+	border: 1px solid #aaa;
+	-webkit-border-radius: 5px;
+	-moz-border-radius: 5px;
+	padding: 2px 5px;
+	margin: 0 3px;
+	cursor: pointer;
+	*cursor: hand;
+}
+
+.paging_full_numbers span.paginate_button {
+	background-color: #ddd;
+}
+
+.paging_full_numbers span.paginate_button:hover {
+	background-color: #ccc;
+}
+
+.paging_full_numbers span.paginate_active {
+	background-color: #99B3FF;
+}
+
+table.display tr.even.row_selected td {
+	background-color: #B0BED9;
+}
+
+table.display tr.odd.row_selected td {
+	background-color: #9FAFD1;
+}
+
+
+/*
+ * Sorting classes for columns
+ */
+/* For the standard odd/even */
+tr.odd td.sorting_1 {
+	background-color: #D3D6FF;
+}
+
+tr.odd td.sorting_2 {
+	background-color: #DADCFF;
+}
+
+tr.odd td.sorting_3 {
+	background-color: #E0E2FF;
+}
+
+tr.even td.sorting_1 {
+	background-color: #EAEBFF;
+}
+
+tr.even td.sorting_2 {
+	background-color: #F2F3FF;
+}
+
+tr.even td.sorting_3 {
+	background-color: #F9F9FF;
+}
+
+
+/* For the Conditional-CSS grading rows */
+/*
+ 	Colour calculations (based off the main row colours)
+  Level 1:
+		dd > c4
+		ee > d5
+	Level 2:
+	  dd > d1
+	  ee > e2
+ */
+tr.odd.gradeA td.sorting_1 {
+	background-color: #c4ffc4;
+}
+
+tr.odd.gradeA td.sorting_2 {
+	background-color: #d1ffd1;
+}
+
+tr.odd.gradeA td.sorting_3 {
+	background-color: #d1ffd1;
+}
+
+tr.even.gradeA td.sorting_1 {
+	background-color: #d5ffd5;
+}
+
+tr.even.gradeA td.sorting_2 {
+	background-color: #e2ffe2;
+}
+
+tr.even.gradeA td.sorting_3 {
+	background-color: #e2ffe2;
+}
+
+tr.odd.gradeC td.sorting_1 {
+	background-color: #c4c4ff;
+}
+
+tr.odd.gradeC td.sorting_2 {
+	background-color: #d1d1ff;
+}
+
+tr.odd.gradeC td.sorting_3 {
+	background-color: #d1d1ff;
+}
+
+tr.even.gradeC td.sorting_1 {
+	background-color: #d5d5ff;
+}
+
+tr.even.gradeC td.sorting_2 {
+	background-color: #e2e2ff;
+}
+
+tr.even.gradeC td.sorting_3 {
+	background-color: #e2e2ff;
+}
+
+tr.odd.gradeX td.sorting_1 {
+	background-color: #ffc4c4;
+}
+
+tr.odd.gradeX td.sorting_2 {
+	background-color: #ffd1d1;
+}
+
+tr.odd.gradeX td.sorting_3 {
+	background-color: #ffd1d1;
+}
+
+tr.even.gradeX td.sorting_1 {
+	background-color: #ffd5d5;
+}
+
+tr.even.gradeX td.sorting_2 {
+	background-color: #ffe2e2;
+}
+
+tr.even.gradeX td.sorting_3 {
+	background-color: #ffe2e2;
+}
+
+tr.odd.gradeU td.sorting_1 {
+	background-color: #c4c4c4;
+}
+
+tr.odd.gradeU td.sorting_2 {
+	background-color: #d1d1d1;
+}
+
+tr.odd.gradeU td.sorting_3 {
+	background-color: #d1d1d1;
+}
+
+tr.even.gradeU td.sorting_1 {
+	background-color: #d5d5d5;
+}
+
+tr.even.gradeU td.sorting_2 {
+	background-color: #e2e2e2;
+}
+
+tr.even.gradeU td.sorting_3 {
+	background-color: #e2e2e2;
+}
+
+
+/*
+ * Row highlighting example
+ */
+.ex_highlight #example tbody tr.even:hover, #example tbody tr.even td.highlighted {
+	background-color: #ECFFB3;
+}
+
+.ex_highlight #example tbody tr.odd:hover, #example tbody tr.odd td.highlighted {
+	background-color: #E6FF99;
+}
+
+.ex_highlight_row #example tr.even:hover {
+	background-color: #ECFFB3;
+}
+
+.ex_highlight_row #example tr.even:hover td.sorting_1 {
+	background-color: #DDFF75;
+}
+
+.ex_highlight_row #example tr.even:hover td.sorting_2 {
+	background-color: #E7FF9E;
+}
+
+.ex_highlight_row #example tr.even:hover td.sorting_3 {
+	background-color: #E2FF89;
+}
+
+.ex_highlight_row #example tr.odd:hover {
+	background-color: #E6FF99;
+}
+
+.ex_highlight_row #example tr.odd:hover td.sorting_1 {
+	background-color: #D6FF5C;
+}
+
+.ex_highlight_row #example tr.odd:hover td.sorting_2 {
+	background-color: #E0FF84;
+}
+
+.ex_highlight_row #example tr.odd:hover td.sorting_3 {
+	background-color: #DBFF70;
+}
+
+
+/*
+ * KeyTable
+ */
+table.KeyTable td {
+	border: 3px solid transparent;
+}
+
+table.KeyTable td.focus {
+	border: 3px solid #3366FF;
+}
+
+table.display tr.gradeA {
+	background-color: #eeffee;
+}
+
+table.display tr.gradeC {
+	background-color: #ddddff;
+}
+
+table.display tr.gradeX {
+	background-color: #ffdddd;
+}
+
+table.display tr.gradeU {
+	background-color: #ddd;
+}
+
+div.box {
+	height: 100px;
+	padding: 10px;
+	overflow: auto;
+	border: 1px solid #8080FF;
+	background-color: #E5E5FF;
+}

BIN
main/css/baby_orange/default.css


BIN
main/css/baby_orange/images/dataTable/back_disabled.jpg


BIN
main/css/baby_orange/images/dataTable/back_enabled.jpg


BIN
main/css/baby_orange/images/dataTable/favicon.ico


BIN
main/css/baby_orange/images/dataTable/forward_disabled.jpg


BIN
main/css/baby_orange/images/dataTable/forward_enabled.jpg


BIN
main/css/baby_orange/images/dataTable/sort_asc.png


BIN
main/css/baby_orange/images/dataTable/sort_asc_disabled.png


BIN
main/css/baby_orange/images/dataTable/sort_both.png


BIN
main/css/baby_orange/images/dataTable/sort_desc.png


BIN
main/css/baby_orange/images/dataTable/sort_desc_disabled.png


+ 538 - 0
main/css/baby_orange/learnpath.css

@@ -0,0 +1,538 @@
+/*
+ *  File:         demo_table.css
+ *  CVS:          $Id$
+ *  Description:  CSS descriptions for DataTables demo pages
+ *  Author:       Allan Jardine
+ *  Created:      Tue May 12 06:47:22 BST 2009
+ *  Modified:     $Date$ by $Author$
+ *  Language:     CSS
+ *  Project:      DataTables
+ *
+ *  Copyright 2009 Allan Jardine. All Rights Reserved.
+ *
+ * ***************************************************************************
+ * DESCRIPTION
+ *
+ * The styles given here are suitable for the demos that are used with the standard DataTables
+ * distribution (see www.datatables.net). You will most likely wish to modify these styles to
+ * meet the layout requirements of your site.
+ *
+ * Common issues:
+ *   'full_numbers' pagination - I use an extra selector on the body tag to ensure that there is
+ *     no conflict between the two pagination types. If you want to use full_numbers pagination
+ *     ensure that you either have "example_alt_pagination" as a body class name, or better yet,
+ *     modify that selector.
+ *   Note that the path used for Images is relative. All images are by default located in
+ *     images/dataTable/ - relative to this CSS file.
+ */
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables features
+ */
+
+.dataTables_wrapper {
+	position: relative;
+	min-height: 302px;
+	clear: both;
+	_height: 302px;
+	zoom: 1; /* Feeling sorry for IE */
+}
+
+.dataTables_processing {
+	position: absolute;
+	top: 50%;
+	left: 50%;
+	width: 250px;
+	height: 30px;
+	margin-left: -125px;
+	margin-top: -15px;
+	padding: 14px 0 2px 0;
+	border: 1px solid #ddd;
+	text-align: center;
+	color: #999;
+	font-size: 14px;
+	background-color: white;
+}
+
+.dataTables_length {
+	width: 40%;
+	float: left;
+}
+
+.dataTables_filter {
+	width: 50%;
+	float: right;
+	text-align: right;
+}
+
+.dataTables_info {
+	width: 60%;
+	float: left;
+}
+
+.dataTables_paginate {
+	width: 44px;
+	* width: 50px;
+	float: right;
+	text-align: right;
+}
+
+/* Pagination nested */
+.paginate_disabled_previous, .paginate_enabled_previous, .paginate_disabled_next, .paginate_enabled_next {
+	height: 19px;
+	width: 19px;
+	margin-left: 3px;
+	float: left;
+}
+
+.paginate_disabled_previous {
+	background-image: url('images/dataTale/back_disabled.jpg');
+}
+
+.paginate_enabled_previous {
+	background-image: url('images/dataTable/back_enabled.jpg');
+}
+
+.paginate_disabled_next {
+	background-image: url('images/dataTable/forward_disabled.jpg');
+}
+
+.paginate_enabled_next {
+	background-image: url('images/dataTable/forward_enabled.jpg');
+}
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables display
+ */
+table.display {
+	margin: 0 auto;
+	clear: both;
+	width: 100%;
+	
+	/* Note Firefox 3.5 and before have a bug with border-collapse
+	 * ( https://bugzilla.mozilla.org/show%5Fbug.cgi?id=155955 ) 
+	 * border-spacing: 0; is one possible option. Conditional-css.com is
+	 * useful for this kind of thing
+	 *
+	 * Further note IE 6/7 has problems when calculating widths with border width.
+	 * It subtracts one px relative to the other browsers from the first column, and
+	 * adds one to the end...
+	 *
+	 * If you want that effect I'd suggest setting a border-top/left on th/td's and 
+	 * then filling in the gaps with other borders.
+	 */
+}
+
+table.display thead th {
+	padding: 3px 18px 3px 10px;
+	border-bottom: 1px solid black;
+	font-weight: bold;
+	cursor: pointer;
+	* cursor: hand;
+}
+
+table.display tfoot th {
+	padding: 3px 18px 3px 10px;
+	border-top: 1px solid black;
+	font-weight: bold;
+}
+
+table.display tr.heading2 td {
+	border-bottom: 1px solid #aaa;
+}
+
+table.display td {
+	padding: 3px 10px;
+}
+
+table.display td.center {
+	text-align: center;
+}
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables sorting
+ */
+
+.sorting_asc {
+	background: url('images/dataTable/sort_asc.png') no-repeat center right;
+}
+
+.sorting_desc {
+	background: url('images/dataTable/sort_desc.png') no-repeat center right;
+}
+
+.sorting {
+	background: url('images/dataTable/sort_both.png') no-repeat center right;
+}
+
+.sorting_asc_disabled {
+	background: url('images/dataTable/sort_asc_disabled.png') no-repeat center right;
+}
+
+.sorting_desc_disabled {
+	background: url('images/dataTable/sort_desc_disabled.png') no-repeat center right;
+}
+
+
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables row classes
+ */
+table.display tr.odd.gradeA {
+	background-color: #ddffdd;
+}
+
+table.display tr.even.gradeA {
+	background-color: #eeffee;
+}
+
+table.display tr.odd.gradeC {
+	background-color: #ddddff;
+}
+
+table.display tr.even.gradeC {
+	background-color: #eeeeff;
+}
+
+table.display tr.odd.gradeX {
+	background-color: #ffdddd;
+}
+
+table.display tr.even.gradeX {
+	background-color: #ffeeee;
+}
+
+table.display tr.odd.gradeU {
+	background-color: #ddd;
+}
+
+table.display tr.even.gradeU {
+	background-color: #eee;
+}
+
+
+tr.odd {
+	background-color: #E2E4FF;
+}
+
+tr.even {
+	background-color: white;
+}
+
+
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Misc
+ */
+.dataTables_scroll {
+	clear: both;
+}
+
+.dataTables_scrollBody {
+	*margin-top: -1px;
+}
+
+.top, .bottom {
+	padding: 15px;
+	background-color: #F5F5F5;
+	border: 1px solid #CCCCCC;
+}
+
+.top .dataTables_info {
+	float: none;
+}
+
+.clear {
+	clear: both;
+}
+
+.dataTables_empty {
+	text-align: center;
+}
+
+tfoot input {
+	margin: 0.5em 0;
+	width: 100%;
+	color: #444;
+}
+
+tfoot input.search_init {
+	color: #999;
+}
+
+td.group {
+	background-color: #d1cfd0;
+	border-bottom: 2px solid #A19B9E;
+	border-top: 2px solid #A19B9E;
+}
+
+td.details {
+	background-color: #d1cfd0;
+	border: 2px solid #A19B9E;
+}
+
+
+.example_alt_pagination div.dataTables_info {
+	width: 40%;
+}
+
+.paging_full_numbers {
+	width: 400px;
+	height: 22px;
+	line-height: 22px;
+}
+
+.paging_full_numbers span.paginate_button,
+ 	.paging_full_numbers span.paginate_active {
+	border: 1px solid #aaa;
+	-webkit-border-radius: 5px;
+	-moz-border-radius: 5px;
+	padding: 2px 5px;
+	margin: 0 3px;
+	cursor: pointer;
+	*cursor: hand;
+}
+
+.paging_full_numbers span.paginate_button {
+	background-color: #ddd;
+}
+
+.paging_full_numbers span.paginate_button:hover {
+	background-color: #ccc;
+}
+
+.paging_full_numbers span.paginate_active {
+	background-color: #99B3FF;
+}
+
+table.display tr.even.row_selected td {
+	background-color: #B0BED9;
+}
+
+table.display tr.odd.row_selected td {
+	background-color: #9FAFD1;
+}
+
+
+/*
+ * Sorting classes for columns
+ */
+/* For the standard odd/even */
+tr.odd td.sorting_1 {
+	background-color: #D3D6FF;
+}
+
+tr.odd td.sorting_2 {
+	background-color: #DADCFF;
+}
+
+tr.odd td.sorting_3 {
+	background-color: #E0E2FF;
+}
+
+tr.even td.sorting_1 {
+	background-color: #EAEBFF;
+}
+
+tr.even td.sorting_2 {
+	background-color: #F2F3FF;
+}
+
+tr.even td.sorting_3 {
+	background-color: #F9F9FF;
+}
+
+
+/* For the Conditional-CSS grading rows */
+/*
+ 	Colour calculations (based off the main row colours)
+  Level 1:
+		dd > c4
+		ee > d5
+	Level 2:
+	  dd > d1
+	  ee > e2
+ */
+tr.odd.gradeA td.sorting_1 {
+	background-color: #c4ffc4;
+}
+
+tr.odd.gradeA td.sorting_2 {
+	background-color: #d1ffd1;
+}
+
+tr.odd.gradeA td.sorting_3 {
+	background-color: #d1ffd1;
+}
+
+tr.even.gradeA td.sorting_1 {
+	background-color: #d5ffd5;
+}
+
+tr.even.gradeA td.sorting_2 {
+	background-color: #e2ffe2;
+}
+
+tr.even.gradeA td.sorting_3 {
+	background-color: #e2ffe2;
+}
+
+tr.odd.gradeC td.sorting_1 {
+	background-color: #c4c4ff;
+}
+
+tr.odd.gradeC td.sorting_2 {
+	background-color: #d1d1ff;
+}
+
+tr.odd.gradeC td.sorting_3 {
+	background-color: #d1d1ff;
+}
+
+tr.even.gradeC td.sorting_1 {
+	background-color: #d5d5ff;
+}
+
+tr.even.gradeC td.sorting_2 {
+	background-color: #e2e2ff;
+}
+
+tr.even.gradeC td.sorting_3 {
+	background-color: #e2e2ff;
+}
+
+tr.odd.gradeX td.sorting_1 {
+	background-color: #ffc4c4;
+}
+
+tr.odd.gradeX td.sorting_2 {
+	background-color: #ffd1d1;
+}
+
+tr.odd.gradeX td.sorting_3 {
+	background-color: #ffd1d1;
+}
+
+tr.even.gradeX td.sorting_1 {
+	background-color: #ffd5d5;
+}
+
+tr.even.gradeX td.sorting_2 {
+	background-color: #ffe2e2;
+}
+
+tr.even.gradeX td.sorting_3 {
+	background-color: #ffe2e2;
+}
+
+tr.odd.gradeU td.sorting_1 {
+	background-color: #c4c4c4;
+}
+
+tr.odd.gradeU td.sorting_2 {
+	background-color: #d1d1d1;
+}
+
+tr.odd.gradeU td.sorting_3 {
+	background-color: #d1d1d1;
+}
+
+tr.even.gradeU td.sorting_1 {
+	background-color: #d5d5d5;
+}
+
+tr.even.gradeU td.sorting_2 {
+	background-color: #e2e2e2;
+}
+
+tr.even.gradeU td.sorting_3 {
+	background-color: #e2e2e2;
+}
+
+
+/*
+ * Row highlighting example
+ */
+.ex_highlight #example tbody tr.even:hover, #example tbody tr.even td.highlighted {
+	background-color: #ECFFB3;
+}
+
+.ex_highlight #example tbody tr.odd:hover, #example tbody tr.odd td.highlighted {
+	background-color: #E6FF99;
+}
+
+.ex_highlight_row #example tr.even:hover {
+	background-color: #ECFFB3;
+}
+
+.ex_highlight_row #example tr.even:hover td.sorting_1 {
+	background-color: #DDFF75;
+}
+
+.ex_highlight_row #example tr.even:hover td.sorting_2 {
+	background-color: #E7FF9E;
+}
+
+.ex_highlight_row #example tr.even:hover td.sorting_3 {
+	background-color: #E2FF89;
+}
+
+.ex_highlight_row #example tr.odd:hover {
+	background-color: #E6FF99;
+}
+
+.ex_highlight_row #example tr.odd:hover td.sorting_1 {
+	background-color: #D6FF5C;
+}
+
+.ex_highlight_row #example tr.odd:hover td.sorting_2 {
+	background-color: #E0FF84;
+}
+
+.ex_highlight_row #example tr.odd:hover td.sorting_3 {
+	background-color: #DBFF70;
+}
+
+
+/*
+ * KeyTable
+ */
+table.KeyTable td {
+	border: 3px solid transparent;
+}
+
+table.KeyTable td.focus {
+	border: 3px solid #3366FF;
+}
+
+table.display tr.gradeA {
+	background-color: #eeffee;
+}
+
+table.display tr.gradeC {
+	background-color: #ddddff;
+}
+
+table.display tr.gradeX {
+	background-color: #ffdddd;
+}
+
+table.display tr.gradeU {
+	background-color: #ddd;
+}
+
+div.box {
+	height: 100px;
+	padding: 10px;
+	overflow: auto;
+	border: 1px solid #8080FF;
+	background-color: #E5E5FF;
+}

BIN
main/css/blue_lagoon/default.css


BIN
main/css/blue_lagoon/images/dataTable/back_disabled.jpg


BIN
main/css/blue_lagoon/images/dataTable/back_enabled.jpg


BIN
main/css/blue_lagoon/images/dataTable/favicon.ico


BIN
main/css/blue_lagoon/images/dataTable/forward_disabled.jpg


BIN
main/css/blue_lagoon/images/dataTable/forward_enabled.jpg


BIN
main/css/blue_lagoon/images/dataTable/sort_asc.png


BIN
main/css/blue_lagoon/images/dataTable/sort_asc_disabled.png


BIN
main/css/blue_lagoon/images/dataTable/sort_both.png


BIN
main/css/blue_lagoon/images/dataTable/sort_desc.png


BIN
main/css/blue_lagoon/images/dataTable/sort_desc_disabled.png


+ 538 - 0
main/css/blue_lagoon/learnpath.css

@@ -0,0 +1,538 @@
+/*
+ *  File:         demo_table.css
+ *  CVS:          $Id$
+ *  Description:  CSS descriptions for DataTables demo pages
+ *  Author:       Allan Jardine
+ *  Created:      Tue May 12 06:47:22 BST 2009
+ *  Modified:     $Date$ by $Author$
+ *  Language:     CSS
+ *  Project:      DataTables
+ *
+ *  Copyright 2009 Allan Jardine. All Rights Reserved.
+ *
+ * ***************************************************************************
+ * DESCRIPTION
+ *
+ * The styles given here are suitable for the demos that are used with the standard DataTables
+ * distribution (see www.datatables.net). You will most likely wish to modify these styles to
+ * meet the layout requirements of your site.
+ *
+ * Common issues:
+ *   'full_numbers' pagination - I use an extra selector on the body tag to ensure that there is
+ *     no conflict between the two pagination types. If you want to use full_numbers pagination
+ *     ensure that you either have "example_alt_pagination" as a body class name, or better yet,
+ *     modify that selector.
+ *   Note that the path used for Images is relative. All images are by default located in
+ *     images/dataTable/ - relative to this CSS file.
+ */
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables features
+ */
+
+.dataTables_wrapper {
+	position: relative;
+	min-height: 302px;
+	clear: both;
+	_height: 302px;
+	zoom: 1; /* Feeling sorry for IE */
+}
+
+.dataTables_processing {
+	position: absolute;
+	top: 50%;
+	left: 50%;
+	width: 250px;
+	height: 30px;
+	margin-left: -125px;
+	margin-top: -15px;
+	padding: 14px 0 2px 0;
+	border: 1px solid #ddd;
+	text-align: center;
+	color: #999;
+	font-size: 14px;
+	background-color: white;
+}
+
+.dataTables_length {
+	width: 40%;
+	float: left;
+}
+
+.dataTables_filter {
+	width: 50%;
+	float: right;
+	text-align: right;
+}
+
+.dataTables_info {
+	width: 60%;
+	float: left;
+}
+
+.dataTables_paginate {
+	width: 44px;
+	* width: 50px;
+	float: right;
+	text-align: right;
+}
+
+/* Pagination nested */
+.paginate_disabled_previous, .paginate_enabled_previous, .paginate_disabled_next, .paginate_enabled_next {
+	height: 19px;
+	width: 19px;
+	margin-left: 3px;
+	float: left;
+}
+
+.paginate_disabled_previous {
+	background-image: url('images/dataTale/back_disabled.jpg');
+}
+
+.paginate_enabled_previous {
+	background-image: url('images/dataTable/back_enabled.jpg');
+}
+
+.paginate_disabled_next {
+	background-image: url('images/dataTable/forward_disabled.jpg');
+}
+
+.paginate_enabled_next {
+	background-image: url('images/dataTable/forward_enabled.jpg');
+}
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables display
+ */
+table.display {
+	margin: 0 auto;
+	clear: both;
+	width: 100%;
+	
+	/* Note Firefox 3.5 and before have a bug with border-collapse
+	 * ( https://bugzilla.mozilla.org/show%5Fbug.cgi?id=155955 ) 
+	 * border-spacing: 0; is one possible option. Conditional-css.com is
+	 * useful for this kind of thing
+	 *
+	 * Further note IE 6/7 has problems when calculating widths with border width.
+	 * It subtracts one px relative to the other browsers from the first column, and
+	 * adds one to the end...
+	 *
+	 * If you want that effect I'd suggest setting a border-top/left on th/td's and 
+	 * then filling in the gaps with other borders.
+	 */
+}
+
+table.display thead th {
+	padding: 3px 18px 3px 10px;
+	border-bottom: 1px solid black;
+	font-weight: bold;
+	cursor: pointer;
+	* cursor: hand;
+}
+
+table.display tfoot th {
+	padding: 3px 18px 3px 10px;
+	border-top: 1px solid black;
+	font-weight: bold;
+}
+
+table.display tr.heading2 td {
+	border-bottom: 1px solid #aaa;
+}
+
+table.display td {
+	padding: 3px 10px;
+}
+
+table.display td.center {
+	text-align: center;
+}
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables sorting
+ */
+
+.sorting_asc {
+	background: url('images/dataTable/sort_asc.png') no-repeat center right;
+}
+
+.sorting_desc {
+	background: url('images/dataTable/sort_desc.png') no-repeat center right;
+}
+
+.sorting {
+	background: url('images/dataTable/sort_both.png') no-repeat center right;
+}
+
+.sorting_asc_disabled {
+	background: url('images/dataTable/sort_asc_disabled.png') no-repeat center right;
+}
+
+.sorting_desc_disabled {
+	background: url('images/dataTable/sort_desc_disabled.png') no-repeat center right;
+}
+
+
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables row classes
+ */
+table.display tr.odd.gradeA {
+	background-color: #ddffdd;
+}
+
+table.display tr.even.gradeA {
+	background-color: #eeffee;
+}
+
+table.display tr.odd.gradeC {
+	background-color: #ddddff;
+}
+
+table.display tr.even.gradeC {
+	background-color: #eeeeff;
+}
+
+table.display tr.odd.gradeX {
+	background-color: #ffdddd;
+}
+
+table.display tr.even.gradeX {
+	background-color: #ffeeee;
+}
+
+table.display tr.odd.gradeU {
+	background-color: #ddd;
+}
+
+table.display tr.even.gradeU {
+	background-color: #eee;
+}
+
+
+tr.odd {
+	background-color: #E2E4FF;
+}
+
+tr.even {
+	background-color: white;
+}
+
+
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Misc
+ */
+.dataTables_scroll {
+	clear: both;
+}
+
+.dataTables_scrollBody {
+	*margin-top: -1px;
+}
+
+.top, .bottom {
+	padding: 15px;
+	background-color: #F5F5F5;
+	border: 1px solid #CCCCCC;
+}
+
+.top .dataTables_info {
+	float: none;
+}
+
+.clear {
+	clear: both;
+}
+
+.dataTables_empty {
+	text-align: center;
+}
+
+tfoot input {
+	margin: 0.5em 0;
+	width: 100%;
+	color: #444;
+}
+
+tfoot input.search_init {
+	color: #999;
+}
+
+td.group {
+	background-color: #d1cfd0;
+	border-bottom: 2px solid #A19B9E;
+	border-top: 2px solid #A19B9E;
+}
+
+td.details {
+	background-color: #d1cfd0;
+	border: 2px solid #A19B9E;
+}
+
+
+.example_alt_pagination div.dataTables_info {
+	width: 40%;
+}
+
+.paging_full_numbers {
+	width: 400px;
+	height: 22px;
+	line-height: 22px;
+}
+
+.paging_full_numbers span.paginate_button,
+ 	.paging_full_numbers span.paginate_active {
+	border: 1px solid #aaa;
+	-webkit-border-radius: 5px;
+	-moz-border-radius: 5px;
+	padding: 2px 5px;
+	margin: 0 3px;
+	cursor: pointer;
+	*cursor: hand;
+}
+
+.paging_full_numbers span.paginate_button {
+	background-color: #ddd;
+}
+
+.paging_full_numbers span.paginate_button:hover {
+	background-color: #ccc;
+}
+
+.paging_full_numbers span.paginate_active {
+	background-color: #99B3FF;
+}
+
+table.display tr.even.row_selected td {
+	background-color: #B0BED9;
+}
+
+table.display tr.odd.row_selected td {
+	background-color: #9FAFD1;
+}
+
+
+/*
+ * Sorting classes for columns
+ */
+/* For the standard odd/even */
+tr.odd td.sorting_1 {
+	background-color: #D3D6FF;
+}
+
+tr.odd td.sorting_2 {
+	background-color: #DADCFF;
+}
+
+tr.odd td.sorting_3 {
+	background-color: #E0E2FF;
+}
+
+tr.even td.sorting_1 {
+	background-color: #EAEBFF;
+}
+
+tr.even td.sorting_2 {
+	background-color: #F2F3FF;
+}
+
+tr.even td.sorting_3 {
+	background-color: #F9F9FF;
+}
+
+
+/* For the Conditional-CSS grading rows */
+/*
+ 	Colour calculations (based off the main row colours)
+  Level 1:
+		dd > c4
+		ee > d5
+	Level 2:
+	  dd > d1
+	  ee > e2
+ */
+tr.odd.gradeA td.sorting_1 {
+	background-color: #c4ffc4;
+}
+
+tr.odd.gradeA td.sorting_2 {
+	background-color: #d1ffd1;
+}
+
+tr.odd.gradeA td.sorting_3 {
+	background-color: #d1ffd1;
+}
+
+tr.even.gradeA td.sorting_1 {
+	background-color: #d5ffd5;
+}
+
+tr.even.gradeA td.sorting_2 {
+	background-color: #e2ffe2;
+}
+
+tr.even.gradeA td.sorting_3 {
+	background-color: #e2ffe2;
+}
+
+tr.odd.gradeC td.sorting_1 {
+	background-color: #c4c4ff;
+}
+
+tr.odd.gradeC td.sorting_2 {
+	background-color: #d1d1ff;
+}
+
+tr.odd.gradeC td.sorting_3 {
+	background-color: #d1d1ff;
+}
+
+tr.even.gradeC td.sorting_1 {
+	background-color: #d5d5ff;
+}
+
+tr.even.gradeC td.sorting_2 {
+	background-color: #e2e2ff;
+}
+
+tr.even.gradeC td.sorting_3 {
+	background-color: #e2e2ff;
+}
+
+tr.odd.gradeX td.sorting_1 {
+	background-color: #ffc4c4;
+}
+
+tr.odd.gradeX td.sorting_2 {
+	background-color: #ffd1d1;
+}
+
+tr.odd.gradeX td.sorting_3 {
+	background-color: #ffd1d1;
+}
+
+tr.even.gradeX td.sorting_1 {
+	background-color: #ffd5d5;
+}
+
+tr.even.gradeX td.sorting_2 {
+	background-color: #ffe2e2;
+}
+
+tr.even.gradeX td.sorting_3 {
+	background-color: #ffe2e2;
+}
+
+tr.odd.gradeU td.sorting_1 {
+	background-color: #c4c4c4;
+}
+
+tr.odd.gradeU td.sorting_2 {
+	background-color: #d1d1d1;
+}
+
+tr.odd.gradeU td.sorting_3 {
+	background-color: #d1d1d1;
+}
+
+tr.even.gradeU td.sorting_1 {
+	background-color: #d5d5d5;
+}
+
+tr.even.gradeU td.sorting_2 {
+	background-color: #e2e2e2;
+}
+
+tr.even.gradeU td.sorting_3 {
+	background-color: #e2e2e2;
+}
+
+
+/*
+ * Row highlighting example
+ */
+.ex_highlight #example tbody tr.even:hover, #example tbody tr.even td.highlighted {
+	background-color: #ECFFB3;
+}
+
+.ex_highlight #example tbody tr.odd:hover, #example tbody tr.odd td.highlighted {
+	background-color: #E6FF99;
+}
+
+.ex_highlight_row #example tr.even:hover {
+	background-color: #ECFFB3;
+}
+
+.ex_highlight_row #example tr.even:hover td.sorting_1 {
+	background-color: #DDFF75;
+}
+
+.ex_highlight_row #example tr.even:hover td.sorting_2 {
+	background-color: #E7FF9E;
+}
+
+.ex_highlight_row #example tr.even:hover td.sorting_3 {
+	background-color: #E2FF89;
+}
+
+.ex_highlight_row #example tr.odd:hover {
+	background-color: #E6FF99;
+}
+
+.ex_highlight_row #example tr.odd:hover td.sorting_1 {
+	background-color: #D6FF5C;
+}
+
+.ex_highlight_row #example tr.odd:hover td.sorting_2 {
+	background-color: #E0FF84;
+}
+
+.ex_highlight_row #example tr.odd:hover td.sorting_3 {
+	background-color: #DBFF70;
+}
+
+
+/*
+ * KeyTable
+ */
+table.KeyTable td {
+	border: 3px solid transparent;
+}
+
+table.KeyTable td.focus {
+	border: 3px solid #3366FF;
+}
+
+table.display tr.gradeA {
+	background-color: #eeffee;
+}
+
+table.display tr.gradeC {
+	background-color: #ddddff;
+}
+
+table.display tr.gradeX {
+	background-color: #ffdddd;
+}
+
+table.display tr.gradeU {
+	background-color: #ddd;
+}
+
+div.box {
+	height: 100px;
+	padding: 10px;
+	overflow: auto;
+	border: 1px solid #8080FF;
+	background-color: #E5E5FF;
+}

BIN
main/css/chamilo/default.css


BIN
main/css/chamilo/images/dataTable/back_disabled.jpg


BIN
main/css/chamilo/images/dataTable/back_enabled.jpg


BIN
main/css/chamilo/images/dataTable/favicon.ico


BIN
main/css/chamilo/images/dataTable/forward_disabled.jpg


BIN
main/css/chamilo/images/dataTable/forward_enabled.jpg


BIN
main/css/chamilo/images/dataTable/sort_asc.png


BIN
main/css/chamilo/images/dataTable/sort_asc_disabled.png


BIN
main/css/chamilo/images/dataTable/sort_both.png


BIN
main/css/chamilo/images/dataTable/sort_desc.png


BIN
main/css/chamilo/images/dataTable/sort_desc_disabled.png


+ 538 - 0
main/css/chamilo/learnpath.css

@@ -0,0 +1,538 @@
+/*
+ *  File:         demo_table.css
+ *  CVS:          $Id$
+ *  Description:  CSS descriptions for DataTables demo pages
+ *  Author:       Allan Jardine
+ *  Created:      Tue May 12 06:47:22 BST 2009
+ *  Modified:     $Date$ by $Author$
+ *  Language:     CSS
+ *  Project:      DataTables
+ *
+ *  Copyright 2009 Allan Jardine. All Rights Reserved.
+ *
+ * ***************************************************************************
+ * DESCRIPTION
+ *
+ * The styles given here are suitable for the demos that are used with the standard DataTables
+ * distribution (see www.datatables.net). You will most likely wish to modify these styles to
+ * meet the layout requirements of your site.
+ *
+ * Common issues:
+ *   'full_numbers' pagination - I use an extra selector on the body tag to ensure that there is
+ *     no conflict between the two pagination types. If you want to use full_numbers pagination
+ *     ensure that you either have "example_alt_pagination" as a body class name, or better yet,
+ *     modify that selector.
+ *   Note that the path used for Images is relative. All images are by default located in
+ *     images/dataTable/ - relative to this CSS file.
+ */
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables features
+ */
+
+.dataTables_wrapper {
+	position: relative;
+	min-height: 302px;
+	clear: both;
+	_height: 302px;
+	zoom: 1; /* Feeling sorry for IE */
+}
+
+.dataTables_processing {
+	position: absolute;
+	top: 50%;
+	left: 50%;
+	width: 250px;
+	height: 30px;
+	margin-left: -125px;
+	margin-top: -15px;
+	padding: 14px 0 2px 0;
+	border: 1px solid #ddd;
+	text-align: center;
+	color: #999;
+	font-size: 14px;
+	background-color: white;
+}
+
+.dataTables_length {
+	width: 40%;
+	float: left;
+}
+
+.dataTables_filter {
+	width: 50%;
+	float: right;
+	text-align: right;
+}
+
+.dataTables_info {
+	width: 60%;
+	float: left;
+}
+
+.dataTables_paginate {
+	width: 44px;
+	* width: 50px;
+	float: right;
+	text-align: right;
+}
+
+/* Pagination nested */
+.paginate_disabled_previous, .paginate_enabled_previous, .paginate_disabled_next, .paginate_enabled_next {
+	height: 19px;
+	width: 19px;
+	margin-left: 3px;
+	float: left;
+}
+
+.paginate_disabled_previous {
+	background-image: url('images/dataTale/back_disabled.jpg');
+}
+
+.paginate_enabled_previous {
+	background-image: url('images/dataTable/back_enabled.jpg');
+}
+
+.paginate_disabled_next {
+	background-image: url('images/dataTable/forward_disabled.jpg');
+}
+
+.paginate_enabled_next {
+	background-image: url('images/dataTable/forward_enabled.jpg');
+}
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables display
+ */
+table.display {
+	margin: 0 auto;
+	clear: both;
+	width: 100%;
+	
+	/* Note Firefox 3.5 and before have a bug with border-collapse
+	 * ( https://bugzilla.mozilla.org/show%5Fbug.cgi?id=155955 ) 
+	 * border-spacing: 0; is one possible option. Conditional-css.com is
+	 * useful for this kind of thing
+	 *
+	 * Further note IE 6/7 has problems when calculating widths with border width.
+	 * It subtracts one px relative to the other browsers from the first column, and
+	 * adds one to the end...
+	 *
+	 * If you want that effect I'd suggest setting a border-top/left on th/td's and 
+	 * then filling in the gaps with other borders.
+	 */
+}
+
+table.display thead th {
+	padding: 3px 18px 3px 10px;
+	border-bottom: 1px solid black;
+	font-weight: bold;
+	cursor: pointer;
+	* cursor: hand;
+}
+
+table.display tfoot th {
+	padding: 3px 18px 3px 10px;
+	border-top: 1px solid black;
+	font-weight: bold;
+}
+
+table.display tr.heading2 td {
+	border-bottom: 1px solid #aaa;
+}
+
+table.display td {
+	padding: 3px 10px;
+}
+
+table.display td.center {
+	text-align: center;
+}
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables sorting
+ */
+
+.sorting_asc {
+	background: url('images/dataTable/sort_asc.png') no-repeat center right;
+}
+
+.sorting_desc {
+	background: url('images/dataTable/sort_desc.png') no-repeat center right;
+}
+
+.sorting {
+	background: url('images/dataTable/sort_both.png') no-repeat center right;
+}
+
+.sorting_asc_disabled {
+	background: url('images/dataTable/sort_asc_disabled.png') no-repeat center right;
+}
+
+.sorting_desc_disabled {
+	background: url('images/dataTable/sort_desc_disabled.png') no-repeat center right;
+}
+
+
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * DataTables row classes
+ */
+table.display tr.odd.gradeA {
+	background-color: #ddffdd;
+}
+
+table.display tr.even.gradeA {
+	background-color: #eeffee;
+}
+
+table.display tr.odd.gradeC {
+	background-color: #ddddff;
+}
+
+table.display tr.even.gradeC {
+	background-color: #eeeeff;
+}
+
+table.display tr.odd.gradeX {
+	background-color: #ffdddd;
+}
+
+table.display tr.even.gradeX {
+	background-color: #ffeeee;
+}
+
+table.display tr.odd.gradeU {
+	background-color: #ddd;
+}
+
+table.display tr.even.gradeU {
+	background-color: #eee;
+}
+
+
+tr.odd {
+	background-color: #E2E4FF;
+}
+
+tr.even {
+	background-color: white;
+}
+
+
+
+
+
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ * Misc
+ */
+.dataTables_scroll {
+	clear: both;
+}
+
+.dataTables_scrollBody {
+	*margin-top: -1px;
+}
+
+.top, .bottom {
+	padding: 15px;
+	background-color: #F5F5F5;
+	border: 1px solid #CCCCCC;
+}
+
+.top .dataTables_info {
+	float: none;
+}
+
+.clear {
+	clear: both;
+}
+
+.dataTables_empty {
+	text-align: center;
+}
+
+tfoot input {
+	margin: 0.5em 0;
+	width: 100%;
+	color: #444;
+}
+
+tfoot input.search_init {
+	color: #999;
+}
+
+td.group {
+	background-color: #d1cfd0;
+	border-bottom: 2px solid #A19B9E;
+	border-top: 2px solid #A19B9E;
+}
+
+td.details {
+	background-color: #d1cfd0;
+	border: 2px solid #A19B9E;
+}
+
+
+.example_alt_pagination div.dataTables_info {
+	width: 40%;
+}
+
+.paging_full_numbers {
+	width: 400px;
+	height: 22px;
+	line-height: 22px;
+}
+
+.paging_full_numbers span.paginate_button,
+ 	.paging_full_numbers span.paginate_active {
+	border: 1px solid #aaa;
+	-webkit-border-radius: 5px;
+	-moz-border-radius: 5px;
+	padding: 2px 5px;
+	margin: 0 3px;
+	cursor: pointer;
+	*cursor: hand;
+}
+
+.paging_full_numbers span.paginate_button {
+	background-color: #ddd;
+}
+
+.paging_full_numbers span.paginate_button:hover {
+	background-color: #ccc;
+}
+
+.paging_full_numbers span.paginate_active {
+	background-color: #99B3FF;
+}
+
+table.display tr.even.row_selected td {
+	background-color: #B0BED9;
+}
+
+table.display tr.odd.row_selected td {
+	background-color: #9FAFD1;
+}
+
+
+/*
+ * Sorting classes for columns
+ */
+/* For the standard odd/even */
+tr.odd td.sorting_1 {
+	background-color: #D3D6FF;
+}
+
+tr.odd td.sorting_2 {
+	background-color: #DADCFF;
+}
+
+tr.odd td.sorting_3 {
+	background-color: #E0E2FF;
+}
+
+tr.even td.sorting_1 {
+	background-color: #EAEBFF;
+}
+
+tr.even td.sorting_2 {
+	background-color: #F2F3FF;
+}
+
+tr.even td.sorting_3 {
+	background-color: #F9F9FF;
+}
+
+
+/* For the Conditional-CSS grading rows */
+/*
+ 	Colour calculations (based off the main row colours)
+  Level 1:
+		dd > c4
+		ee > d5
+	Level 2:
+	  dd > d1
+	  ee > e2
+ */
+tr.odd.gradeA td.sorting_1 {
+	background-color: #c4ffc4;
+}
+
+tr.odd.gradeA td.sorting_2 {
+	background-color: #d1ffd1;
+}
+
+tr.odd.gradeA td.sorting_3 {
+	background-color: #d1ffd1;
+}
+
+tr.even.gradeA td.sorting_1 {
+	background-color: #d5ffd5;
+}
+
+tr.even.gradeA td.sorting_2 {
+	background-color: #e2ffe2;
+}
+
+tr.even.gradeA td.sorting_3 {
+	background-color: #e2ffe2;
+}
+
+tr.odd.gradeC td.sorting_1 {
+	background-color: #c4c4ff;
+}
+
+tr.odd.gradeC td.sorting_2 {
+	background-color: #d1d1ff;
+}
+
+tr.odd.gradeC td.sorting_3 {
+	background-color: #d1d1ff;
+}
+
+tr.even.gradeC td.sorting_1 {
+	background-color: #d5d5ff;
+}
+
+tr.even.gradeC td.sorting_2 {
+	background-color: #e2e2ff;
+}
+
+tr.even.gradeC td.sorting_3 {
+	background-color: #e2e2ff;
+}
+
+tr.odd.gradeX td.sorting_1 {
+	background-color: #ffc4c4;
+}
+
+tr.odd.gradeX td.sorting_2 {
+	background-color: #ffd1d1;
+}
+
+tr.odd.gradeX td.sorting_3 {
+	background-color: #ffd1d1;
+}
+
+tr.even.gradeX td.sorting_1 {
+	background-color: #ffd5d5;
+}
+
+tr.even.gradeX td.sorting_2 {
+	background-color: #ffe2e2;
+}
+
+tr.even.gradeX td.sorting_3 {
+	background-color: #ffe2e2;
+}
+
+tr.odd.gradeU td.sorting_1 {
+	background-color: #c4c4c4;
+}
+
+tr.odd.gradeU td.sorting_2 {
+	background-color: #d1d1d1;
+}
+
+tr.odd.gradeU td.sorting_3 {
+	background-color: #d1d1d1;
+}
+
+tr.even.gradeU td.sorting_1 {
+	background-color: #d5d5d5;
+}
+
+tr.even.gradeU td.sorting_2 {
+	background-color: #e2e2e2;
+}
+
+tr.even.gradeU td.sorting_3 {
+	background-color: #e2e2e2;
+}
+
+
+/*
+ * Row highlighting example
+ */
+.ex_highlight #example tbody tr.even:hover, #example tbody tr.even td.highlighted {
+	background-color: #ECFFB3;
+}
+
+.ex_highlight #example tbody tr.odd:hover, #example tbody tr.odd td.highlighted {
+	background-color: #E6FF99;
+}
+
+.ex_highlight_row #example tr.even:hover {
+	background-color: #ECFFB3;
+}
+
+.ex_highlight_row #example tr.even:hover td.sorting_1 {
+	background-color: #DDFF75;
+}
+
+.ex_highlight_row #example tr.even:hover td.sorting_2 {
+	background-color: #E7FF9E;
+}
+
+.ex_highlight_row #example tr.even:hover td.sorting_3 {
+	background-color: #E2FF89;
+}
+
+.ex_highlight_row #example tr.odd:hover {
+	background-color: #E6FF99;
+}
+
+.ex_highlight_row #example tr.odd:hover td.sorting_1 {
+	background-color: #D6FF5C;
+}
+
+.ex_highlight_row #example tr.odd:hover td.sorting_2 {
+	background-color: #E0FF84;
+}
+
+.ex_highlight_row #example tr.odd:hover td.sorting_3 {
+	background-color: #DBFF70;
+}
+
+
+/*
+ * KeyTable
+ */
+table.KeyTable td {
+	border: 3px solid transparent;
+}
+
+table.KeyTable td.focus {
+	border: 3px solid #3366FF;
+}
+
+table.display tr.gradeA {
+	background-color: #eeffee;
+}
+
+table.display tr.gradeC {
+	background-color: #ddddff;
+}
+
+table.display tr.gradeX {
+	background-color: #ffdddd;
+}
+
+table.display tr.gradeU {
+	background-color: #ddd;
+}
+
+div.box {
+	height: 100px;
+	padding: 10px;
+	overflow: auto;
+	border: 1px solid #8080FF;
+	background-color: #E5E5FF;
+}

BIN
main/css/chamilo_electric_blue/default.css


BIN
main/css/chamilo_electric_blue/images/dataTable/back_disabled.jpg


BIN
main/css/chamilo_electric_blue/images/dataTable/back_enabled.jpg


BIN
main/css/chamilo_electric_blue/images/dataTable/favicon.ico


BIN
main/css/chamilo_electric_blue/images/dataTable/forward_disabled.jpg


BIN
main/css/chamilo_electric_blue/images/dataTable/forward_enabled.jpg


BIN
main/css/chamilo_electric_blue/images/dataTable/sort_asc.png


BIN
main/css/chamilo_electric_blue/images/dataTable/sort_asc_disabled.png


BIN
main/css/chamilo_electric_blue/images/dataTable/sort_both.png


BIN
main/css/chamilo_electric_blue/images/dataTable/sort_desc.png


BIN
main/css/chamilo_electric_blue/images/dataTable/sort_desc_disabled.png


+ 538 - 0
main/css/chamilo_electric_blue/images/header-logo.png


BIN
main/css/chamilo_green/default.css


BIN
main/css/chamilo_green/images/dataTable/back_disabled.jpg


BIN
main/css/chamilo_green/images/dataTable/back_enabled.jpg


BIN
main/css/chamilo_green/images/dataTable/favicon.ico


BIN
main/css/chamilo_green/images/dataTable/forward_disabled.jpg


Деякі файли не було показано, через те що забагато файлів було змінено