Browse Source

Merge pull request #4 from chamilo/1.9.x

1.9.x
ycastillo 11 years ago
parent
commit
5764143d15
73 changed files with 8295 additions and 1541 deletions
  1. 1 0
      documentation/changelog.html
  2. 33 7
      main/admin/user_add.php
  3. 94 57
      main/admin/user_list.php
  4. 13 6
      main/auth/inscription.php
  5. 9 2
      main/auth/profile.php
  6. 3 2
      main/course_home/activity.php
  7. 3 1
      main/course_info/infocours.php
  8. 612 264
      main/cron/import_csv.php
  9. 13 6
      main/css/base.css
  10. 16 19
      main/document/create_audio.php
  11. 12 12
      main/document/upload.php
  12. 8 6
      main/exercice/answer.class.php
  13. 49 51
      main/gradebook/lib/be/forumthreadlink.class.php
  14. BIN
      main/img/icons/22/course_na.png
  15. 1 0
      main/inc/ajax/course.ajax.php
  16. 25 0
      main/inc/ajax/form.ajax.php
  17. 79 36
      main/inc/ajax/model.ajax.php
  18. 32 0
      main/inc/ajax/session.ajax.php
  19. 14 0
      main/inc/lib/autoload.class.php
  20. 8 5
      main/inc/lib/banner.lib.php
  21. 21 55
      main/inc/lib/course.lib.php
  22. 7 3
      main/inc/lib/course_home.lib.php
  23. 6 1
      main/inc/lib/formvalidator/FormValidator.class.php
  24. 43 22
      main/inc/lib/groupmanager.lib.php
  25. 315 0
      main/inc/lib/javascript/strength/strength.js
  26. 60 22
      main/inc/lib/link.lib.php
  27. 100 0
      main/inc/lib/main_api.lib.php
  28. 388 0
      main/inc/lib/pear/Exception.php
  29. 41 39
      main/inc/lib/pear/HTML/QuickForm.php
  30. 268 0
      main/inc/lib/pear/HTML/QuickForm/CAPTCHA.php
  31. 93 0
      main/inc/lib/pear/HTML/QuickForm/CAPTCHA/Equation.php
  32. 126 0
      main/inc/lib/pear/HTML/QuickForm/CAPTCHA/Figlet.php
  33. 160 0
      main/inc/lib/pear/HTML/QuickForm/CAPTCHA/Image.php
  34. 90 0
      main/inc/lib/pear/HTML/QuickForm/CAPTCHA/Word.php
  35. 50 0
      main/inc/lib/pear/HTML/QuickForm/Rule/CAPTCHA.php
  36. 1183 0
      main/inc/lib/pear/Image/Text.php
  37. 26 0
      main/inc/lib/pear/Image/Text/Exception.php
  38. 163 0
      main/inc/lib/pear/Text/CAPTCHA.php
  39. 56 0
      main/inc/lib/pear/Text/CAPTCHA/Driver.php
  40. 97 0
      main/inc/lib/pear/Text/CAPTCHA/Driver/Base.php
  41. 215 0
      main/inc/lib/pear/Text/CAPTCHA/Driver/Equation.php
  42. 233 0
      main/inc/lib/pear/Text/CAPTCHA/Driver/Figlet.php
  43. 290 0
      main/inc/lib/pear/Text/CAPTCHA/Driver/Image.php
  44. 268 0
      main/inc/lib/pear/Text/CAPTCHA/Driver/Numeral.php
  45. 127 0
      main/inc/lib/pear/Text/CAPTCHA/Driver/Word.php
  46. 28 0
      main/inc/lib/pear/Text/CAPTCHA/Exception.php
  47. 533 0
      main/inc/lib/pear/Text/Password.php
  48. 7 3
      main/inc/lib/promotion.lib.php
  49. 161 47
      main/inc/lib/sessionmanager.lib.php
  50. 2 2
      main/inc/lib/symfony/Monolog/Handler/NativeMailerHandler.php
  51. 4 4
      main/inc/lib/usermanager.lib.php
  52. 54 4
      main/inc/lib/userportal.lib.php
  53. 93 3
      main/inc/local.inc.php
  54. 29 20
      main/newscorm/embed.php
  55. 4 0
      main/newscorm/learnpath.class.php
  56. 5 5
      main/newscorm/lp_add_audio.php
  57. 1 1
      main/newscorm/lp_add_item.php
  58. 15 13
      main/newscorm/scorm_api.php
  59. 6 2
      main/survey/fillsurvey.php
  60. 26 26
      main/template/default/layout/head.tpl
  61. 10 10
      main/template/default/layout/login_form.tpl
  62. 80 0
      main/webservices/registration.soap.php
  63. 27 10
      main/work/downloadfolder.inc.php
  64. 244 0
      main/work/edit.php
  65. 129 0
      main/work/student_work.php
  66. 332 0
      main/work/upload.php
  67. 282 166
      main/work/work.lib.php
  68. 234 608
      main/work/work.php
  69. 136 0
      main/work/work_list.php
  70. 151 0
      main/work/work_list_all.php
  71. 133 0
      main/work/work_list_others.php
  72. 115 0
      main/work/work_missing.php
  73. 3 1
      plugin/bbb/lib/bbb.lib.php

+ 1 - 0
documentation/changelog.html

@@ -59,6 +59,7 @@
 </ul>
 <h3>Improvements</h3>
 <ul>
+  <li>Increased maximum lifetime of videoconference sessions - now 5h (#6261)</li>
   <li></li>
 </ul>
 <h3>Debugging</h3>

+ 33 - 7
main/admin/user_add.php

@@ -23,12 +23,34 @@ api_protect_admin_script(true);
 $is_platform_admin = api_is_platform_admin() ? 1 : 0;
 
 $message = null;
-
+$htmlHeadXtra[] = api_get_password_checker_js('#username', '#password');
 $htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/tag/jquery.fcbkcomplete.js" type="text/javascript" language="javascript"></script>';
 $htmlHeadXtra[] = '<link href="'.api_get_path(WEB_LIBRARY_PATH).'javascript/tag/style.css" rel="stylesheet" type="text/css" />';
+
+if (isset($_configuration['allow_strength_pass_checker']) && $_configuration['allow_strength_pass_checker']) {
+    $htmlHeadXtra[] = '
+    <script>
+    $(document).ready(function() {
+        $("input[name=\'password[password_auto]\']").each(function(index, value) {
+            $(this).click(function() {
+                var value = $(this).attr("value");
+                if (value == 0) {
+                    $("#password_progress").show();
+                    $(".password-verdict").show();
+                    $(".error-list").show();
+                } else {
+                    $("#password_progress").hide();
+                    $(".password-verdict").hide();
+                    $(".error-list").hide();
+                }
+            });
+        });
+    });
+    </script>';
+}
+
 $htmlHeadXtra[] = '
 <script>
-<!--
 function enable_expiration_date() { //v2.0
 	document.user_add.radio_expiration_date[0].checked=false;
 	document.user_add.radio_expiration_date[1].checked=true;
@@ -63,11 +85,10 @@ function display_drh_list(){
             document.getElementById("id_platform_admin").style.display="none";
 	}
 }
-//-->
 </script>';
 
 if (!empty($_GET['message'])) {
-	$message = urldecode($_GET['message']);
+    $message = urldecode($_GET['message']);
 }
 
 $interbreadcrumb[] = array ('url' => 'index.php', 'name' => get_lang('PlatformAdmin'));
@@ -126,7 +147,7 @@ $form->addRule('picture', get_lang('OnlyImagesAllowed').' ('.implode(',', $allow
 
 // Username
 if (api_get_setting('login_is_email') != 'true') {
-    $form->addElement('text', 'username', get_lang('LoginName'), array('maxlength' => USERNAME_MAX_LENGTH));
+    $form->addElement('text', 'username', get_lang('LoginName'), array('id'=> 'username', 'maxlength' => USERNAME_MAX_LENGTH));
     $form->addRule('username', get_lang('ThisFieldIsRequired'), 'required');
     $form->addRule('username', sprintf(get_lang('UsernameMaxXCharacters'), (string)USERNAME_MAX_LENGTH), 'maxlength', USERNAME_MAX_LENGTH);
     $form->addRule('username', get_lang('OnlyLettersAndNumbersAllowed'), 'username');
@@ -157,9 +178,14 @@ if (count($extAuthSource) > 0) {
 }
 $group[] = $form->createElement('radio', 'password_auto', get_lang('Password'), get_lang('AutoGeneratePassword').'<br />', 1);
 $group[] = $form->createElement('radio', 'password_auto', 'id="radio_user_password"', null, 0);
-$group[] = $form->createElement('password', 'password', null, array('onkeydown' => 'javascript: password_switch_radio_button();'));
+$group[] = $form->createElement('password', 'password', null, array('id'=> 'password', 'onkeydown' => 'javascript: password_switch_radio_button();'));
+
 $form->addGroup($group, 'password', get_lang('Password'), '');
 
+if (isset($_configuration['allow_strength_pass_checker']) && $_configuration['allow_strength_pass_checker']) {
+    $form->addElement('label', null, '<div id="password_progress" style="display:none"></div>');
+}
+
 // Status
 $status = array();
 $status[COURSEMANAGER] = get_lang('Teacher');
@@ -337,4 +363,4 @@ $tpl = new Template($tool_name);
 //$tpl->assign('actions', $actions);
 $tpl->assign('message', $message);
 $tpl->assign('content', $content);
-$tpl->display_one_col_template();
+$tpl->display_one_col_template();

+ 94 - 57
main/admin/user_list.php

@@ -21,8 +21,9 @@ if (isset($_configuration['deny_delete_users']) &&  $_configuration['deny_delete
 	$delete_user_available = false;
 }
 $url = api_get_path(WEB_AJAX_PATH).'course.ajax.php?a=get_user_courses';
-        
-$htmlHeadXtra[] = '<script type="text/javascript">
+$urlSession = api_get_path(WEB_AJAX_PATH).'session.ajax.php?a=get_user_sessions';
+
+$htmlHeadXtra[] = '<script>
 function load_course_list (div_course,my_user_id) {
 	 $.ajax({
 		contentType: "application/x-www-form-urlencoded",
@@ -38,6 +39,21 @@ function load_course_list (div_course,my_user_id) {
 		}
 	});
 }
+function load_session_list (div_session,my_user_id) {
+	 $.ajax({
+		contentType: "application/x-www-form-urlencoded",
+		beforeSend: function(objeto) {
+            $("div#"+div_session).html("<img src=\'../inc/lib/javascript/indicator.gif\' />"); },
+		type: "POST",
+		url: "'.$urlSession.'",
+		data: "user_id="+my_user_id,
+		success: function(datos) {
+			$("div#"+div_session).html(datos);
+			$("div#div_s_"+my_user_id).attr("class","blackboard_show");
+			$("div#div_s_"+my_user_id).attr("style","");
+		}
+	});
+}
 
 function active_user(element_div) {
 	id_image=$(element_div).attr("id");
@@ -62,7 +78,7 @@ function active_user(element_div) {
 				if (data == 1) {
 					$(ident).attr("src","'.api_get_path(WEB_IMG_PATH).'icons/16/accept.png'.'");
 					$(ident).attr("title","'.get_lang('Lock').'");
-				} 
+				}
                 if (data == 0) {
 					$(ident).attr("src","'.api_get_path(WEB_IMG_PATH).'icons/16/error.png'.'");
 					$(ident).attr("title","'.get_lang('Unlock').'");
@@ -80,6 +96,10 @@ function clear_course_list (div_course) {
 	$("div#"+div_course).html("&nbsp;");
 	$("div#"+div_course).hide("");
 }
+function clear_session_list (div_session) {
+	$("div#"+div_session).html("&nbsp;");
+	$("div#"+div_session).hide("");
+}
 
 function display_advanced_search_form () {
         if ($("#advanced_search_form").css("display") == "none") {
@@ -95,24 +115,24 @@ $(document).ready(function() {
 
     var select_val = $("#input_select_extra_data").val();
     if ( document.getElementById(\'extra_data_text\')) {
-    
+
         if (select_val != 0) {
             document.getElementById(\'extra_data_text\').style.display="block";
-            if (document.getElementById(\'input_extra_text\')) 
+            if (document.getElementById(\'input_extra_text\'))
                 document.getElementById(\'input_extra_text\').value = "";
         } else {
             document.getElementById(\'extra_data_text\').style.display="none";
         }
     }
-    
-    
+
+
     $(".agenda_opener").live("click", function() {
         var url = this.href;
         var dialog = $("#dialog");
-                
+
         if ($("#dialog").length == 0) {
             dialog = $(\'<div id="dialog" style="display:hidden"></div> \').appendTo(\'body\');
-        }     
+        }
         // load remote content
         dialog.load(
                 url,
@@ -123,14 +143,13 @@ $(document).ready(function() {
             );
         //prevent the browser to follow the link
         return false;
-    });    
+    });
 });
 
 //Load user calendar
-function load_calendar(user_id, month, year) {  
+function load_calendar(user_id, month, year) {
  	var url = "'.api_get_path(WEB_AJAX_PATH).'agenda.ajax.php?a=get_user_agenda&user_id=" +user_id + "&month="+month+"&year="+year;
-	$("#dialog").load( url    	 	
-	);    	
+	$("#dialog").load(url);
 }
 </script>';
 
@@ -147,20 +166,20 @@ api_protect_admin_script(true);
 *	@author Evie Embrechts
 *   @author Yannick Warnier <yannick.warnier@dokeos.com>
 */
-function login_user($user_id) {    
+function login_user($user_id) {
     $user_id = intval($user_id);
-    
+
     if (empty($user_id)) {
         return false;
     }
     if ($user_id != strval(intval($user_id))) {
     	return false;
     }
-    
+
     //Only superadmins can login to admin accounts
     if (!api_global_admin_can_edit_admin($user_id)) {
         return false;
-    }    
+    }
 
     //Load $_user to be sure we clean it before logging in
 	global $uidReset, $loginFailed, $_configuration, $_user;
@@ -201,7 +220,7 @@ function login_user($user_id) {
 			LEFT JOIN $track_e_login_table login
 			ON user.user_id = login.login_user_id
 			WHERE user.user_id = '".$user_id."'
-			ORDER BY login.login_date DESC LIMIT 1";		
+			ORDER BY login.login_date DESC LIMIT 1";
 
 		$sql_result = Database::query($sql_query);
 
@@ -357,8 +376,9 @@ function get_user_data($from, $number_of_items, $column, $direction) {
                  u.status				AS col7,
                  u.active				AS col8,
                  u.user_id				AS col9,
-                 u.registration_date    AS col10 ".
-                 ", u.expiration_date   AS exp ".
+                 u.registration_date    AS col10,
+                 u.expiration_date      AS exp,
+                 u.password ".
             " FROM $user_table u ";
 
     // adding the filter to see the user's only of the current access_url
@@ -397,7 +417,7 @@ function get_user_data($from, $number_of_items, $column, $direction) {
 		}
 
 		$keyword_extra_value = '';
-		
+
         if (isset($_GET['keyword_extra_data'])) {
         	if (!empty($_GET['keyword_extra_data']) && !empty($_GET['keyword_extra_data_text'])) {
             	$keyword_extra_data_text = Database::escape_string($_GET['keyword_extra_data_text']);
@@ -428,6 +448,17 @@ function get_user_data($from, $number_of_items, $column, $direction) {
 		$sql.= " AND url_rel_user.access_url_id=".api_get_current_access_url_id();
     }
 
+    $checkPassStrength = isset($_GET['check_easy_passwords']) && $_GET['check_easy_passwords'] == 1 ? true : false;
+
+    if ($checkPassStrength) {
+        $easyPasswordList = api_get_easy_password_list();
+        $easyPasswordList = array_map('api_get_encrypted_password', $easyPasswordList);
+        $easyPasswordList = array_map(array('Database', 'escape_string'), $easyPasswordList);
+        $easyPassword = implode("' OR password LIKE '", $easyPasswordList);
+
+        $sql .= "AND password LIKE '$easyPassword' ";
+    }
+
     if (!in_array($direction, array('ASC','DESC'))) {
     	$direction = 'ASC';
     }
@@ -442,7 +473,7 @@ function get_user_data($from, $number_of_items, $column, $direction) {
 
 	$users = array ();
     $t = time();
-	while ($user = Database::fetch_row($res)) {        
+	while ($user = Database::fetch_row($res)) {
 		$image_path 	= UserManager::get_user_picture_path_by_id($user[0], 'web', false, true);
 		$user_profile 	= UserManager::get_picture_user($user[0], $image_path['file'], 22, USER_IMAGE_SIZE_SMALL, ' width="22" height="22" ');
 		if (!api_is_anonymous()) {
@@ -491,23 +522,28 @@ function user_filter($name, $params, $row) {
  * @return string Some HTML-code with modify-buttons
  */
 function modify_filter($user_id, $url_params, $row) {
-	global $charset, $_admins_list, $delete_user_available;    
+	global $charset, $_admins_list, $delete_user_available;
 	$is_admin   = in_array($user_id,$_admins_list);
 	$statusname = api_get_status_langvars();
 	$user_is_anonymous = false;
     $current_user_status_label = $row['7'];
-        
+
 	if ($current_user_status_label == $statusname[ANONYMOUS]) {
 		$user_is_anonymous =true;
 	}
 	$result = '';
 	if (!$user_is_anonymous) {
 		$result .= '<a href="javascript:void(0)" onclick="load_course_list(\'div_'.$user_id.'\','.$user_id.')" >
-					<img onmouseout="clear_course_list (\'div_'.$user_id.'\')" src="../img/course.gif" title="'.get_lang('Courses').'" alt="'.get_lang('Courses').'"/>
+					<img onmouseout="clear_course_list (\'div_'.$user_id.'\')" src="../img/icons/22/course.png" title="'.get_lang('Courses').'" alt="'.get_lang('Courses').'"/>
 					<div class="blackboard_hide" id="div_'.$user_id.'">&nbsp;&nbsp;</div>
-					</a>&nbsp;&nbsp;';
+					</a>';
+		$result .= '<a href="javascript:void(0)" onclick="load_session_list(\'div_s_'.$user_id.'\','.$user_id.')" >
+					<img onmouseout="clear_session_list (\'div_s_'.$user_id.'\')" src="../img/icons/22/session.png" title="'.get_lang('Sessions').'" alt="'.get_lang('Sessions').'"/>
+					<div class="blackboard_hide" id="div_s_'.$user_id.'">&nbsp;&nbsp;</div>
+					</a>';
 	} else {
-		$result .= Display::return_icon('course_na.gif',get_lang('Courses')).'&nbsp;&nbsp;';
+		$result .= Display::return_icon('course_na.png',get_lang('Courses')).'&nbsp;&nbsp;';
+		$result .= Display::return_icon('course_na.png',get_lang('Sessions')).'&nbsp;&nbsp;';
 	}
 
 	if (api_is_platform_admin()) {
@@ -524,7 +560,7 @@ function modify_filter($user_id, $url_params, $row) {
             if (api_global_admin_can_edit_admin($user_id)) {
                 $result .= '<a href="user_list.php?action=login_as&amp;user_id='.$user_id.'&amp;sec_token='.$_SESSION['sec_token'].'">'.Display::return_icon('login_as.gif', get_lang('LoginAs')).'</a>&nbsp;&nbsp;';
             } else {
-                $result .= Display::return_icon('login_as_na.gif', get_lang('LoginAs')).'&nbsp;&nbsp;';                
+                $result .= Display::return_icon('login_as_na.gif', get_lang('LoginAs')).'&nbsp;&nbsp;';
             }
             //$result .= '<a href="user_list.php?action=login_as&amp;user_id='.$user_id.'&amp;sec_token='.$_SESSION['sec_token'].'">'.Display::return_icon('login_as.gif', get_lang('LoginAs')).'</a>&nbsp;&nbsp;';
     	} else {
@@ -533,29 +569,29 @@ function modify_filter($user_id, $url_params, $row) {
     } else {
     	$result .= Display::return_icon('login_as_na.gif', get_lang('LoginAs')).'&nbsp;&nbsp;';
     }
-    
+
 	if ($current_user_status_label != $statusname[STUDENT]) {
 		$result .= Display::return_icon('statistics_na.gif', get_lang('Reporting')).'&nbsp;&nbsp;';
 	} else {
 		$result .= '<a href="../mySpace/myStudents.php?student='.$user_id.'">'.Display::return_icon('statistics.gif', get_lang('Reporting')).'</a>&nbsp;&nbsp;';
 	}
 
-	if (api_is_platform_admin(true)) {        
-        
+	if (api_is_platform_admin(true)) {
+
 		if (!$user_is_anonymous && api_global_admin_can_edit_admin($user_id, null, true)) {
             $result .= '<a href="user_edit.php?user_id='.$user_id.'">'.Display::return_icon('edit.png', get_lang('Edit'), array(), ICON_SIZE_SMALL).'</a>&nbsp;';
 		} else {
             $result .= Display::return_icon('edit_na.png', get_lang('Edit'), array(), ICON_SIZE_SMALL).'</a>&nbsp;';
 		}
 	}
-    
-    
+
+
 	if ($is_admin) {
 		$result .= Display::return_icon('admin_star.png', get_lang('IsAdministrator'),array('width'=> ICON_SIZE_SMALL, 'heigth'=> ICON_SIZE_SMALL));
 	} else {
 		$result .= Display::return_icon('admin_star_na.png', get_lang('IsNotAdministrator'));
 	}
-	
+
 
 	// actions for assigning sessions, courses or users
 	if (api_is_session_admin()) {
@@ -571,17 +607,17 @@ function modify_filter($user_id, $url_params, $row) {
 			$result .= '<a href="dashboard_add_sessions_to_user.php?user='.$user_id.'">'.Display::return_icon('view_more_stats.gif', get_lang('AssignSessions')).'</a>&nbsp;&nbsp;';
 		}
 	}
-	
-    if (api_is_platform_admin()) {        
+
+    if (api_is_platform_admin()) {
         $result .= ' <a href="'.api_get_path(WEB_AJAX_PATH).'agenda.ajax.php?a=get_user_agenda&amp;user_id='.$user_id.'" class="agenda_opener">'.Display::return_icon('month.png', get_lang('FreeBusyCalendar'), array(), ICON_SIZE_SMALL).'</a>';
         if ($delete_user_available) {
-            if ($user_id != api_get_user_id() && !$user_is_anonymous && api_global_admin_can_edit_admin($user_id)) {                  
+            if ($user_id != api_get_user_id() && !$user_is_anonymous && api_global_admin_can_edit_admin($user_id)) {
                 // you cannot lock yourself out otherwise you could disable all the accounts including your own => everybody is locked out and nobody can change it anymore.
                 $result .= ' <a href="user_list.php?action=delete_user&amp;user_id='.$user_id.'&amp;'.$url_params.'&amp;sec_token='.$_SESSION['sec_token'].'"  onclick="javascript:if(!confirm('."'".addslashes(api_htmlentities(get_lang("ConfirmYourChoice"),ENT_QUOTES,$charset))."'".')) return false;">'.Display::return_icon('delete.png', get_lang('Delete'), array(), ICON_SIZE_SMALL).'</a>';
             } else {
                 $result .= Display::return_icon('delete_na.png', get_lang('Delete'), array(), ICON_SIZE_SMALL);
             }
-        }       
+        }
     }
 	return $result;
 }
@@ -614,7 +650,7 @@ function active_filter($active, $url_params, $row) {
     if ($action=='edit') {
         $result = Display::return_icon($image.'.png', get_lang('AccountExpired'), array(), 16);
     } elseif ($row['0']<>$_user['user_id']) {
-    	// you cannot lock yourself out otherwise you could disable all the accounts including your own => everybody is locked out and nobody can change it anymore.		
+    	// you cannot lock yourself out otherwise you could disable all the accounts including your own => everybody is locked out and nobody can change it anymore.
 		$result = Display::return_icon($image.'.png', get_lang(ucfirst($action)), array('onclick'=>'active_user(this);', 'id'=>'img_'.$row['0']), 16).'</a>';
 	}
 	return $result;
@@ -648,7 +684,7 @@ if (isset($_GET['keyword']) || isset($_GET['keyword_firstname'])) {
 $message = '';
 
 if (!empty($action)) {
-	$check = Security::check_token('get');    
+	$check = Security::check_token('get');
 	if ($check) {
 		switch ($action) {
             case 'add_user_to_my_url':
@@ -658,7 +694,7 @@ if (!empty($action)) {
                     $user_info = api_get_user_info($user_id);
                     $message = get_lang('UserAdded').' '.$user_info['firstname'].' '.$user_info['lastname'].' ('.$user_info['username'].')';
                     $message  = Display::return_message($message, 'confirmation');
-                }                
+                }
                 break;
             case 'login_as':
                 $login_as_user_id = $_GET["user_id"];
@@ -682,7 +718,7 @@ if (!empty($action)) {
 				if (api_is_platform_admin()) {
                     $user_to_delete = $_GET['user_id'];
                     $current_user_id = api_get_user_id();
-                    
+
 					if ($delete_user_available && api_global_admin_can_edit_admin($_GET['user_id'])) {
 						if ($user_to_delete != $current_user_id && UserManager :: delete_user($_GET['user_id'])) {
 							$message = Display :: return_message(get_lang('UserDeleted'), 'confirmation');
@@ -707,10 +743,10 @@ if (!empty($action)) {
 							}
 						}
 					}
-					if ($number_of_selected_users == $number_of_deleted_users) {						
+					if ($number_of_selected_users == $number_of_deleted_users) {
                         $message = Display :: return_message(get_lang('SelectedUsersDeleted'), 'confirmation');
 					} else {
-                        $message = Display :: return_message(get_lang('SomeUsersNotDeleted'), 'error');						
+                        $message = Display :: return_message(get_lang('SomeUsersNotDeleted'), 'error');
 					}
 				}
 				break;
@@ -799,6 +835,7 @@ $active_group[] = $form->createElement('checkbox','keyword_inactive','', get_lan
 $form->addGroup($active_group,'',get_lang('ActiveAccount'),'<br/>',false);
 $form->addElement('html', '</td><td>');
 
+$form->addElement('checkbox', 'check_easy_passwords', null, get_lang('CheckEasyPasswords'));
 
 /*
  * @todo fix this code
@@ -873,27 +910,27 @@ $extra_search_options = '';
 
 //Try to search the user everywhere
 if ($table->get_total_number_of_items() == 0) {
-    
-    if (api_get_multiple_access_url() && isset($_REQUEST['keyword'])) {     
+
+    if (api_get_multiple_access_url() && isset($_REQUEST['keyword'])) {
         $keyword = Database::escape_string($_REQUEST['keyword']);
         //$conditions = array('firstname' => $keyword, 'lastname' => $keyword, 'username' => $keyword);
         $conditions = array('username' => $keyword);
-        $user_list = UserManager::get_user_list($conditions, array(), false, ' OR ');        
+        $user_list = UserManager::get_user_list($conditions, array(), false, ' OR ');
         if (!empty($user_list)) {
-            
+
             $extra_search_options = Display::page_subheader(get_lang('UsersFoundInOtherPortals'));
-            
+
             $table = new HTML_Table(array('class' => 'data_table'));
             $column = 0;
-            $row = 0;            
+            $row = 0;
             $headers = array(get_lang('User'), 'URL', get_lang('Actions'));
             foreach ($headers as $header) {
                 $table->setHeaderContents($row, $column, $header);
                 $column++;
             }
             $row++;
-            
-            foreach ($user_list as $user) {      
+
+            foreach ($user_list as $user) {
                 $column = 0;
                 $access_info = UrlManager::get_access_url_from_user($user['user_id']);
                 $access_info_to_string = '';
@@ -901,18 +938,18 @@ if ($table->get_total_number_of_items() == 0) {
                 if (!empty($access_info)) {
                     foreach ($access_info as $url_info) {
                         if ($current_access_url_id == $url_info['access_url_id']) {
-                            $add_user = false;                            
+                            $add_user = false;
                         }
                         $access_info_to_string .= $url_info['url'].'<br />';
                     }
                 }
-                if ($add_user) {   
+                if ($add_user) {
                     $row_table = array();
                     $row_table[] =  api_get_person_name($user['firstname'], $user['lastname']).' ('.$user['username'].') ';
                     $row_table[] =  $access_info_to_string;
                     $url = api_get_self().'?action=add_user_to_my_url&user_id='.$user['user_id'].'&sec_token='.$_SESSION['sec_token'];
                     $row_table[] =  Display::url(get_lang('AddUserToMyURL'), $url, array('class' => 'btn'));
-                    	
+
                     foreach ($row_table as $cell) {
                         $table->setCellContents($row, $column, $cell);
                         $table->updateCellAttributes($row, $column, 'align="center"');
@@ -925,7 +962,7 @@ if ($table->get_total_number_of_items() == 0) {
             $extra_search_options .= $table->toHtml();
             $table_result = '';
         }
-    }    
+    }
 }
 
 $tpl = new Template($tool_name);
@@ -933,4 +970,4 @@ $tpl = new Template($tool_name);
 $tpl->assign('actions', $actions);
 $tpl->assign('message', $message);
 $tpl->assign('content', $form.$table_result.$extra_search_options);
-$tpl->display_one_col_template();
+$tpl->display_one_col_template();

+ 13 - 6
main/auth/inscription.php

@@ -17,6 +17,8 @@ require_once '../inc/global.inc.php';
 require_once api_get_path(CONFIGURATION_PATH).'profile.conf.php';
 require_once api_get_path(LIBRARY_PATH).'mail.lib.inc.php';
 
+$htmlHeadXtra[] = api_get_password_checker_js('#username', '#pass1');
+
 if (api_get_setting('allow_registration') === 'false') {
     api_not_allowed(true);
 }
@@ -51,10 +53,10 @@ if ($user_already_registered_show_terms == false) {
     if (api_is_western_name_order()) {
         //	FIRST NAME and LAST NAME
         $form->addElement('text', 'firstname', get_lang('FirstName'), array('size' => 40));
-        $form->addElement('text', 'lastname',  get_lang('LastName'),  array('size' => 40));
+        $form->addElement('text', 'lastname', get_lang('LastName'), array('size' => 40));
     } else {
         //	LAST NAME and FIRST NAME
-        $form->addElement('text', 'lastname',  get_lang('LastName'),  array('size' => 40));
+        $form->addElement('text', 'lastname', get_lang('LastName'), array('size' => 40));
         $form->addElement('text', 'firstname', get_lang('FirstName'), array('size' => 40));
     }
     $form->applyFilter(array('lastname', 'firstname'), 'trim');
@@ -68,7 +70,7 @@ if ($user_already_registered_show_terms == false) {
     }
 
     if (api_get_setting('login_is_email') == 'true') {
-        $form->applyFilter('email','trim');
+        $form->applyFilter('email', 'trim');
         if (api_get_setting('registration', 'email') != 'true') {
             $form->addRule('email', get_lang('ThisFieldIsRequired'), 'required');
         }
@@ -90,7 +92,7 @@ if ($user_already_registered_show_terms == false) {
 
     //	USERNAME
     if (api_get_setting('login_is_email') != 'true') {
-        $form->addElement('text', 'username', get_lang('UserName'), array('size' => USERNAME_MAX_LENGTH));
+        $form->addElement('text', 'username', get_lang('UserName'), array('id' => 'username', 'size' => USERNAME_MAX_LENGTH));
         $form->applyFilter('username','trim');
         $form->addRule('username', get_lang('ThisFieldIsRequired'), 'required');
         $form->addRule('username', sprintf(get_lang('UsernameMaxXCharacters'), (string)USERNAME_MAX_LENGTH), 'maxlength', USERNAME_MAX_LENGTH);
@@ -99,8 +101,13 @@ if ($user_already_registered_show_terms == false) {
     }
 
     //	PASSWORD
-    $form->addElement('password', 'pass1', get_lang('Pass'),         array('size' => 20, 'autocomplete' => 'off'));
-    $form->addElement('password', 'pass2', get_lang('Confirmation'), array('size' => 20, 'autocomplete' => 'off'));
+    $form->addElement('password', 'pass1', get_lang('Pass'), array('id' => 'pass1', 'size' => 20, 'autocomplete' => 'off'));
+
+    if (isset($_configuration['allow_strength_pass_checker']) && $_configuration['allow_strength_pass_checker']) {
+        $form->addElement('label', null, '<div id="password_progress"></div>');
+    }
+
+    $form->addElement('password', 'pass2', get_lang('Confirmation'), array('id' => 'pass2', 'size' => 20, 'autocomplete' => 'off'));
     $form->addRule('pass1', get_lang('ThisFieldIsRequired'), 'required');
     $form->addRule('pass2', get_lang('ThisFieldIsRequired'), 'required');
     $form->addRule(array('pass1', 'pass2'), get_lang('PassTwo'), 'compare');

+ 9 - 2
main/auth/profile.php

@@ -23,6 +23,9 @@ if (api_get_setting('allow_social_tool') == 'true') {
     $this_section = SECTION_MYPROFILE;
 }
 
+
+$htmlHeadXtra[] = api_get_password_checker_js('#username', '#password1');
+
 $_SESSION['this_section'] = $this_section;
 
 if (!(isset($_user['user_id']) && $_user['user_id']) || api_is_anonymous($_user['user_id'], true)) {
@@ -142,7 +145,7 @@ $form->addRule('lastname' , get_lang('ThisFieldIsRequired'), 'required');
 $form->addRule('firstname', get_lang('ThisFieldIsRequired'), 'required');
 
 //    USERNAME
-$form->addElement('text', 'username', get_lang('UserName'), array('maxlength' => USERNAME_MAX_LENGTH, 'size' => USERNAME_MAX_LENGTH));
+$form->addElement('text', 'username', get_lang('UserName'), array('id' => 'username', 'maxlength' => USERNAME_MAX_LENGTH, 'size' => USERNAME_MAX_LENGTH));
 if (api_get_setting('profile', 'login') !== 'true') {
     $form->freeze('username');
 }
@@ -257,7 +260,11 @@ if (api_get_setting('extended_profile') == 'true') {
 //    PASSWORD, if auth_source is platform
 if (is_platform_authentication() && is_profile_editable() && api_get_setting('profile', 'password') == 'true') {
     $form->addElement('password', 'password0', array(get_lang('Pass'), get_lang('Enter2passToChange')), array('size' => 40));
-    $form->addElement('password', 'password1', get_lang('NewPass'), array('size' => 40));
+    $form->addElement('password', 'password1', get_lang('NewPass'), array('id'=> 'password1', 'size' => 40));
+
+    if (isset($_configuration['allow_strength_pass_checker']) && $_configuration['allow_strength_pass_checker']) {
+        $form->addElement('label', null, '<div id="password_progress"></div>');
+    }
     $form->addElement('password', 'password2', get_lang('Confirmation'), array('size' => 40));
     //    user must enter identical password twice so we can prevent some user errors
     $form->addRule(array('password1', 'password2'), get_lang('PassTwo'), 'compare');

+ 3 - 2
main/course_home/activity.php

@@ -56,7 +56,8 @@ if (api_is_platform_admin()) {
 //	COURSE ADMIN ONLY VIEW
 
 // Start of tools for CourseAdmins (teachers/tutors)
-if (api_is_allowed_to_edit(null, true) && !api_is_coach()) {
+
+if ($session_id == 0 && api_is_course_admin() && api_is_allowed_to_edit(null, true)) {
 	$content .=  '<div class="courseadminview" style="border:0px; margin-top: 0px;padding:0px;">
 		<div class="normal-message" id="id_normal_message" style="display:none">';
 			$content .=  '<img src="'.api_get_path(WEB_PATH).'main/inc/lib/javascript/indicator.gif"/>&nbsp;&nbsp;';
@@ -119,4 +120,4 @@ if (api_is_allowed_to_edit(null, true) && !api_is_coach()) {
 function return_block($title, $content) {
     $html = '<div class="row"><div class="span12"><div class="page-header"><h3>'.$title.'</h3></div></div></div><div class="row">'.$content.'</div>';
     return $html;
-}
+}

+ 3 - 1
main/course_info/infocours.php

@@ -134,7 +134,7 @@ $form->addElement('html', $image_html);
 
 
 $form->add_textfield('title', get_lang('Title'), true, array('class' => 'span6'));
-//$form->applyFilter('title', 'html_filter');
+$form->applyFilter('title', 'html_filter');
 $form->applyFilter('title', 'trim');
 
 //$form->add_textfield('tutor_name', get_lang('Professors'), true, array ('size' => '60'));
@@ -154,9 +154,11 @@ $form->addElement('select', 'category_code', get_lang('Fac'), $categories, array
 $form->addElement('select_language', 'course_language', array(get_lang('Ln'), get_lang('TipLang')));
 
 $form->add_textfield('department_name', get_lang('Department'), false, array('class' => 'span5'));
+$form->applyFilter('department_name', 'html_filter');
 $form->applyFilter('department_name', 'trim');
 
 $form->add_textfield('department_url', get_lang('DepartmentUrl'), false, array('class' => 'span5'));
+$form->applyFilter('department_url', 'html_filter');
 //$form->addRule('tutor_name', get_lang('ThisFieldIsRequired'), 'required');
 
 

+ 612 - 264
main/cron/import_csv.php

@@ -1,318 +1,666 @@
 <?php
 
-if (PHP_SAPI!='cli') {
+if (PHP_SAPI !='cli') {
     die('Run this script through the command line or comment this line in the code');
 }
 
-require_once '../inc/global.inc.php';
+require_once __DIR__.'/../inc/global.inc.php';
 require_once api_get_path(LIBRARY_PATH).'log.class.php';
 
-$path = api_get_path(SYS_CODE_PATH).'cron/incoming/';
-$files = scandir($path);
-if (!empty($files)) {
-    foreach ($files as $file) {
-        $fileInfo = pathinfo($file);
-        if ($fileInfo['extension'] == 'csv') {
-            // teachers_yyyymmdd.csv, courses_yyyymmdd.csv, students_yyyymmdd.csv and sessions_yyyymmdd.csv
-            $parts = explode('_', $fileInfo['filename']);
-            $method = 'import'.ucwords($parts[0]);
-            if (function_exists($method)) {
-                $method($path.$fileInfo['basename']);
-            } else {
-                echo "Error - This file can't be processed.";
-                exit;
-            }
-        }
-    }
-}
-
 /**
- * @param $file
+ * Class ImportCsv
  */
-function moveFile($file)
+class ImportCsv
 {
-    $moved = str_replace('incoming', 'treated', $file);
-    // $result = rename($file, $moved);
-    $result = 1;
-    if ($result) {
-        Log::info("Moving file to the treated folder: $file");
-    } else {
-        Log::error("Error - Cant move file to the treated folder: $file");
+    private $logger;
+    private $dumpValues;
+    public $test;
+    public $defaultLanguage = 'dutch';
+    public $extraFieldIdNameList = array(
+        'session' => 'external_session_id',
+        'course' => 'external_course_id',
+        'user' => 'external_user_id',
+    );
+    public $defaultAdminId = 1;
+    public $defaultSessionVisibility = 1;
+
+    /**
+     * When creating a user the expiration date is set to registration date + this value
+     * @var int number of years
+     */
+    public $expirationDateInUserCreation = 1;
+
+    /**
+     * When updating a user the expiration date is set to update date + this value
+     * @var int number of years
+     */
+    public $expirationDateInUserUpdate = 1;
+    public $daysCoachAccessBeforeBeginning = 30;
+    public $daysCoachAccessAfterBeginning = 60;
+    public $conditions;
+
+    /**
+     * @param Logger $logger
+     */
+    public function __construct($logger, $conditions)
+    {
+        $this->logger = $logger;
+        $this->conditions = $conditions;
     }
-}
 
-/**
- * File to import
- * @param string $file
- */
-function importTeachers($file)
-{
-    $data = Import::csv_to_array($file);
-    Log::info("-- Import Teachers --");
-    Log::info("Reading file: $file");
-
-    /* Unique identifier: official-code username.
-    Email address and password should never get updated. *ok
-    The only fields that I can think of that should update if the data changes in the csv file are FirstName and LastName. *ok
-    A slight edit of these fields should be taken into account. ???
-    Adding teachers is no problem, but deleting them shouldn’t be automated, but we should get a log of “to delete teachers”.
-    We’ll handle that manually if applicable.
-    No delete!
-    */
-
-    if (!empty($data)) {
-        foreach ($data as $row) {
-            $userInfo = api_get_user_info_from_username($row['username']);
-            $userInfoByOfficialCode = api_get_user_info_from_official_code($row['official_code']);
-
-            if (empty($userInfo) && empty($userInfoByOfficialCode)) {
-                // Create user
-                $result = UserManager::create_user(
-                    $row['firstname'],
-                    $row['lastname'],
-                    COURSEMANAGER,
-                    $row['email'],
-                    $row['username'],
-                    $row['password'],
-                    $row['official_code'],
-                    $row['language'],
-                    $row['phone'],
-                    $row['picture'], //picture
-                    PLATFORM_AUTH_SOURCE, // ?
-                    $row['expiration_date'], //$expiration_date = '0000-00-00 00:00:00',
-                    1, //active
-                    0,
-                    null, // extra
-                    null, //$encrypt_method = '',
-                    false //$send_mail = false
-                );
-
-                if ($result) {
-                    Log::info("Info - Teachers - User created: ".$row['username']);
-                } else {
-                    Log::error("Error - Teachers - User NOT created: ".$row['username']." ".$row['firstname']." ".$row['lastname']);
-                }
-            } else {
-                if (empty($userInfo)) {
-                    Log::error("Error - Teachers - Can't update user :".$row['username']);
-                    continue;
+    /**
+     * @param bool $dump
+     */
+    function setDumpValues($dump)
+    {
+        $this->dumpValues = $dump;
+    }
+
+    /**
+     * @return mixed
+     */
+    function getDumpValues()
+    {
+        return $this->dumpValues;
+    }
+
+    /**
+     * Runs the import process
+     */
+    public function run()
+    {
+        $path = api_get_path(SYS_CODE_PATH).'cron/incoming/';
+        if (!is_dir($path)) {
+            echo "The folder! $path does not exits";
+            exit;
+        }
+
+        if ($this->getDumpValues()) {
+            $this->dumpDatabaseTables();
+        }
+
+        echo "Starting with reading the files: ".PHP_EOL.PHP_EOL;
+
+        $files = scandir($path);
+        $fileToProcess = array();
+
+        if (!empty($files)) {
+            foreach ($files as $file) {
+                $fileInfo = pathinfo($file);
+                if ($fileInfo['extension'] == 'csv') {
+                    // teachers_yyyymmdd.csv, courses_yyyymmdd.csv, students_yyyymmdd.csv and sessions_yyyymmdd.csv
+                    $parts = explode('_', $fileInfo['filename']);
+                    $method = 'import'.ucwords($parts[1]);
+
+                    if (method_exists($this, $method)) {
+                         $fileToProcess[$parts[1]][] = array(
+                            'method' => $method,
+                            'file' => $path.$fileInfo['basename']
+                        );
+                        //$this->$method($path.$fileInfo['basename']);
+                    } else {
+                        echo "Error - This file '$file' can't be processed.".PHP_EOL;
+                        echo "The file have to has this format:".PHP_EOL;
+                        echo "prefix_students_ddmmyyyy.csv, prefix_teachers_ddmmyyyy.csv, prefix_courses_ddmmyyyy.csv, prefix_sessions_ddmmyyyy.csv ".PHP_EOL;
+                        exit;
+                    }
                 }
+            }
+
+            if (empty($fileToProcess)) {
+                echo 'Error - no files to process.';
+                exit;
+            }
+
+            $sections = array('students', 'teachers', 'courses', 'sessions');
+
+            $this->prepareImport();
+
+            foreach ($sections as $section) {
+                $this->logger->addInfo("-- Import $section --");
+
+                if (isset($fileToProcess[$section]) && !empty($fileToProcess[$section])) {
+                    $files = $fileToProcess[$section];
+                    foreach ($files as $fileInfo) {
+                        $method = $fileInfo['method'];
+                        $file = $fileInfo['file'];
+
+                        echo 'Reading file: '.$file.PHP_EOL;
+                        $this->logger->addInfo("Reading file: $file");
+
+                        $this->$method($file);
+                    }
 
-                // Update user
-                $result = UserManager::update_user(
-                    $userInfo['user_id'],
-                    $row['firstname'], // <<-- changed
-                    $row['lastname'],  // <<-- changed
-                    $userInfo['username'],
-                    null, //$password = null,
-                    $auth_source = null,
-                    $userInfo['email'],
-                    COURSEMANAGER,
-                    $userInfo['official_code'],
-                    $userInfo['phone'],
-                    $userInfo['picture_uri'],
-                    $userInfo['expiration_date'],
-                    $userInfo['active'],
-                    null, //$creator_id = null,
-                    0, //$hr_dept_id = 0,
-                    null, // $extra = null,
-                    null, //$language = 'english',
-                    null, //$encrypt_method = '',
-                    false, //$send_email = false,
-                    0 //$reset_password = 0
-                );
-
-                if ($result) {
-                    Log::info("Teachers - User updated: ".$row['username']);
-                } else {
-                    Log::error("Teachers - User not updated: ".$row['username']);
                 }
             }
+        }
+    }
+
+    private function prepareImport()
+    {
+        // Create user extra field: extra_external_user_id
+        UserManager::create_extra_field($this->extraFieldIdNameList['user'], 1, 'External user id', null);
+
+        // Create course extra field: extra_external_course_id
+        CourseManager::create_course_extra_field($this->extraFieldIdNameList['course'], 1, 'External course id');
+        // Create session extra field extra_external_session_id
+        SessionManager::create_session_extra_field($this->extraFieldIdNameList['session'], 1, 'External session id');
+    }
 
-            // UserManager::delete_user();
-            moveFile($file);
+    /**
+     * @param string $file
+     */
+    function moveFile($file)
+    {
+        $moved = str_replace('incoming', 'treated', $file);
+
+        if ($this->test) {
+            $result = 1;
+        } else {
+            $result = rename($file, $moved);
+        }
+
+        if ($result) {
+            $this->logger->addInfo("Moving file to the treated folder: $file");
+        } else {
+            $this->logger->addError("Error - Cant move file to the treated folder: $file");
         }
     }
-}
 
-function importStudents($file)
-{
-    $data = Import::csv_to_array($file);
-    Log::info("-- Import Students --");
-    Log::info("Reading file: $file");
-
-    /*
-     * Another users import.
-    Unique identifier: official code and username . ok
-    Username and password should never get updated. ok
-    If an update should need to occur (because it changed in the .csv), we’ll want that logged. We will handle this manually in that case.
-    All other fields should be updateable, though passwords should of course not get updated. ok
-    If a user gets deleted (not there anymore),
-    He should be set inactive one year after the current date. So I presume you’ll just update the expiration date. We want to grant access to courses up to a year after deletion.
+    /**
+     * @param array $row
+     *
+     * @return array
      */
+    private function cleanUserRow($row)
+    {
+        $row['lastname'] = $row['LastName'];
+        $row['firstname'] = $row['FirstName'];
+        $row['email'] = $row['Email'];
+        $row['username'] = $row['UserName'];
+        $row['password'] = $row['Password'];
+        $row['auth_source'] = $row['AuthSource'];
+        $row['official_code'] = $row['OfficialCode'];
+        $row['phone'] = $row['PhoneNumber'];
+
+        if (isset($row['StudentID'])) {
+            $row['extra_'.$this->extraFieldIdNameList['user']] = $row['StudentID'];
+        }
 
+        if (isset($row['TeacherID'])) {
+            $row['extra_'.$this->extraFieldIdNameList['user']] = $row['TeacherID'];
+        }
 
-    if (!empty($data)) {
-        foreach ($data as $row) {
-            $userInfo = api_get_user_info_from_username($row['username']);
-            $userInfoByOfficialCode = api_get_user_info_from_official_code($row['official_code']);
-
-            if (empty($userInfo) && empty($userInfoByOfficialCode)) {
-                // Create user
-                $result = UserManager::create_user(
-                    $row['firstname'],
-                    $row['lastname'],
-                    STUDENT,
-                    $row['email'],
-                    $row['username'],
-                    $row['password'],
-                    $row['official_code'],
-                    $row['language'],
-                    $row['phone'],
-                    $row['picture'], //picture
-                    PLATFORM_AUTH_SOURCE, // ?
-                    $row['expiration_date'], //$expiration_date = '0000-00-00 00:00:00',
-                    1, //active
-                    0,
-                    null, // extra
-                    null, //$encrypt_method = '',
-                    false //$send_mail = false
-                );
-
-                if ($result) {
-                    Log::info("Students - User created: ".$row['username']);
-                } else {
-                    Log::error("Students - User NOT created: ".$row['username']." ".$row['firstname']." ".$row['lastname']);
-                }
-            } else {
-                if (empty($userInfo)) {
-                    Log::error("Students - Can't update user :".$row['username']);
-                    continue;
-                }
+        //$row['lastname'] =  Status
+        return $row;
+    }
 
-                if ($row['action'] == 'delete') {
-                    // INactive one year later
-                    $userInfo['expiration_date'] = api_get_utc_datetime(api_strtotime(time() + 365*24*60*60));
+    /**
+     * @param array $row
+     * @return array
+     */
+    private function cleanCourseRow($row)
+    {
+        $row['title'] = $row['Title'];
+        $row['course_code'] = $row['Code'];
+        $row['course_category'] = $row['CourseCategory'];
+        $row['email'] = $row['Teacher'];
+        $row['language'] = $row['Language'];
+
+        $row['teachers'] = array();
+        if (isset($row['Teacher']) && !empty($row['Teacher'])) {
+            $userInfo = api_get_user_info_from_username($row['Teacher']);
+            if (!empty($userInfo)) {
+                $row['teachers'] = $userInfo['user_id'];
+            }
+        }
+
+        if (isset($row['CourseID'])) {
+            $row['extra_'.$this->extraFieldIdNameList['course']] = $row['CourseID'];
+        }
+        return $row;
+    }
+
+    /**
+     * File to import
+     * @param string $file
+     */
+    private function importTeachers($file)
+    {
+        $data = Import::csv_to_array($file);
+
+        /* Unique identifier: official-code username.
+        Email address and password should never get updated. *ok
+        The only fields that I can think of that should update if the data changes in the csv file are FirstName and LastName. *ok
+        A slight edit of these fields should be taken into account. ???
+        Adding teachers is no problem, but deleting them shouldn’t be automated, but we should get a log of “to delete teachers”.
+        We’ll handle that manually if applicable.
+        No delete!
+        */
+        $language = $this->defaultLanguage;
+
+        if (!empty($data)) {
+            $this->logger->addInfo(count($data)." records found.");
+            foreach ($data as $row) {
+                $row = $this->cleanUserRow($row);
+
+                $user_id = UserManager::get_user_id_from_original_id($row['extra_'.$this->extraFieldIdNameList['user']], $this->extraFieldIdNameList['user']);
+                $userInfo  = array();
+                $userInfoByOfficialCode  = null;
+
+                if (!empty($user_id)) {
+                    $userInfo = api_get_user_info($user_id);
+                    //$userInfo = api_get_user_info_from_username($row['username']);
+                    $userInfoByOfficialCode = api_get_user_info_from_official_code($row['official_code']);
                 }
 
-                // Update user
-                $result = UserManager::update_user(
-                    $userInfo['user_id'],
-                    $row['firstname'], // <<-- changed
-                    $row['lastname'],  // <<-- changed
-                    $userInfo['username'],
-                    null, //$password = null,
-                    $auth_source = null,
-                    $userInfo['email'],
-                    STUDENT,
-                    $userInfo['official_code'],
-                    $userInfo['phone'],
-                    $userInfo['picture_uri'],
-                    $userInfo['expiration_date'],
-                    $userInfo['active'],
-                    null, //$creator_id = null,
-                    0, //$hr_dept_id = 0,
-                    null, // $extra = null,
-                    null, //$language = 'english',
-                    null, //$encrypt_method = '',
-                    false, //$send_email = false,
-                    0 //$reset_password = 0
-                );
-
-                if ($result) {
-                    Log::info("Students - User updated: ".$row['username']);
+                $expirationDate = api_get_utc_datetime(strtotime("+".intval($this->expirationDateInUserCreation)."years"));
+
+                if (empty($userInfo) && empty($userInfoByOfficialCode)) {
+                    // Create user
+                    $userId = UserManager::create_user(
+                        $row['firstname'],
+                        $row['lastname'],
+                        COURSEMANAGER,
+                        $row['email'],
+                        $row['username'],
+                        $row['password'],
+                        $row['official_code'],
+                        $language, //$row['language'],
+                        $row['phone'],
+                        null, //$row['picture'], //picture
+                        PLATFORM_AUTH_SOURCE, // ?
+                        $expirationDate, //'0000-00-00 00:00:00', //$row['expiration_date'], //$expiration_date = '0000-00-00 00:00:00',
+                        1, //active
+                        0,
+                        null, // extra
+                        null, //$encrypt_method = '',
+                        false //$send_mail = false
+                    );
+
+                    if ($userId) {
+                        foreach ($row as $key => $value) {
+                            if (substr($key, 0, 6) == 'extra_') { //an extra field
+                                UserManager::update_extra_field_value($userId, substr($key, 6), $value);
+                            }
+                        }
+                        $this->logger->addInfo("Teachers - User created: ".$row['username']);
+                    } else {
+                        $this->logger->addError("Teachers - User NOT created: ".$row['username']." ".$row['firstname']." ".$row['lastname']);
+                    }
                 } else {
-                    Log::error("Students - User NOT updated: ".$row['username']." ".$row['firstname']." ".$row['lastname']);
+                    if (empty($userInfo)) {
+                        $this->logger->addError("Teachers - Can't update user :".$row['username']);
+                        continue;
+                    }
+
+                    $expirationDate = api_get_utc_datetime(strtotime("+".intval($this->expirationDateInUserUpdate)."years"));
+
+                    // Update user
+                    $result = UserManager::update_user(
+                        $userInfo['user_id'],
+                        $row['firstname'], // <<-- changed
+                        $row['lastname'],  // <<-- changed
+                        $userInfo['username'],
+                        null, //$password = null,
+                        $auth_source = null,
+                        $userInfo['email'],
+                        COURSEMANAGER,
+                        $userInfo['official_code'],
+                        $userInfo['phone'],
+                        $userInfo['picture_uri'],
+                        $expirationDate,
+                        $userInfo['active'],
+                        null, //$creator_id = null,
+                        0, //$hr_dept_id = 0,
+                        null, // $extra = null,
+                        null, //$language = 'english',
+                        null, //$encrypt_method = '',
+                        false, //$send_email = false,
+                        0 //$reset_password = 0
+                    );
+
+                    if ($result) {
+                        foreach ($row as $key => $value) {
+                            if (substr($key, 0, 6) == 'extra_') { //an extra field
+                                UserManager::update_extra_field_value($userInfo['user_id'], substr($key, 6), $value);
+                            }
+                        }
+                        $this->logger->addInfo("Teachers - User updated: ".$row['username']);
+                    } else {
+                        $this->logger->addError("Teachers - User not updated: ".$row['username']);
+                    }
                 }
             }
-
-            // UserManager::delete_user();
-            moveFile($file);
         }
+        $this->moveFile($file);
     }
 
-    moveFile($file);
-}
+    /**
+     * @param string $file
+     */
+    private function importStudents($file)
+    {
+        $data = Import::csv_to_array($file);
+
+        /*
+         * Another users import.
+        Unique identifier: official code and username . ok
+        Username and password should never get updated. ok
+        If an update should need to occur (because it changed in the .csv), we’ll want that logged. We will handle this manually in that case.
+        All other fields should be updateable, though passwords should of course not get updated. ok
+        If a user gets deleted (not there anymore),
+        He should be set inactive one year after the current date. So I presume you’ll just update the expiration date. We want to grant access to courses up to a year after deletion.
+         */
+
+        if (!empty($data)) {
+            $language = $this->defaultLanguage;
+            $this->logger->addInfo(count($data)." records found.");
+            foreach ($data as $row) {
+                $row = $this->cleanUserRow($row);
+                //$userInfo = api_get_user_info_from_username($row['username']);
+                $user_id = UserManager::get_user_id_from_original_id($row['extra_'.$this->extraFieldIdNameList['user']], $this->extraFieldIdNameList['user']);
+
+                $userInfo = array();
+                $userInfoByOfficialCode = null;
+                if (!empty($user_id)) {
+                    $userInfo = api_get_user_info($user_id);
+                    $userInfoByOfficialCode = api_get_user_info_from_official_code($row['official_code']);
+                }
 
-function importCourses($file)
-{
-    $data = Import::csv_to_array($file);
-    Log::info("Reading file: $file");
-
-    if (!empty($data)) {
-        foreach ($data as $row) {
-            $courseInfo = api_get_course_info($row['course_code']);
-            if (empty($courseInfo)) {
-                // Create
-                $params = array();
-                $params['title']                = $row['title'];
-                $params['exemplary_content']    = false;
-                $params['wanted_code']          = $row['course_code'];
-                $params['category_code']        = $row['category_code'];
-                $params['course_language']      = $row['language'];
-                //$params['gradebook_model_id']   = isset($course_values['gradebook_model_id']) ? $course_values['gradebook_model_id'] : null;
-                $courseInfo = CourseManager::create_course($params);
-
-                if (!empty($courseInfo)) {
-                    Log::info("Courses - Course created ".$courseInfo['code']);
+                $expirationDate = api_get_utc_datetime(strtotime("+".intval($this->expirationDateInUserCreation)."years"));
+
+                if (empty($userInfo) && empty($userInfoByOfficialCode)) {
+                    // Create user
+                    $result = UserManager::create_user(
+                        $row['firstname'],
+                        $row['lastname'],
+                        STUDENT,
+                        $row['email'],
+                        $row['username'],
+                        $row['password'],
+                        $row['official_code'],
+                        $language, //$row['language'],
+                        $row['phone'],
+                        null, //$row['picture'], //picture
+                        PLATFORM_AUTH_SOURCE, // ?
+                        $expirationDate, //'0000-00-00 00:00:00', //$row['expiration_date'], //$expiration_date = '0000-00-00 00:00:00',
+                        1, //active
+                        0,
+                        null, // extra
+                        null, //$encrypt_method = '',
+                        false //$send_mail = false
+                    );
+
+                    if ($result) {
+                        foreach ($row as $key => $value) {
+                            if (substr($key, 0, 6) == 'extra_') { //an extra field
+                                UserManager::update_extra_field_value($result, substr($key, 6), $value);
+                            }
+                        }
+                        $this->logger->addInfo("Students - User created: ".$row['username']);
+                    } else {
+                        $this->logger->addError("Students - User NOT created: ".$row['username']." ".$row['firstname']." ".$row['lastname']);
+                    }
                 } else {
-                    Log::error("Courses - Can't create course:".$row['title']);
+                    if (empty($userInfo)) {
+                        $this->logger->addError("Students - Can't update user :".$row['username']);
+                        continue;
+                    }
+
+                    if ($row['action'] == 'delete') {
+                        // INactive one year later
+                        $userInfo['expiration_date'] = api_get_utc_datetime(api_strtotime(time() + 365*24*60*60));
+                    }
+
+                    if (isset($this->conditions['importStudents'])) {
+                        if (isset($this->conditions['importStudents']['update']) && isset($this->conditions['importStudents']['update']['avoid'])) {
+                            $avoidUsersWithEmail = $this->conditions['importStudents']['update']['avoid']['email'];
+                            if ($userInfo['email'] != $row['email'] && in_array($row['email'], $avoidUsersWithEmail)) {
+                                $this->logger->addInfo("Students - User skipped: ".$row['username']." because the avoid conditions (email).");
+                                continue;
+                            }
+
+                            $avoidUsersWithPassword = $this->conditions['importStudents']['update']['avoid']['password'];
+
+                            if ($userInfo['password'] != api_get_encrypted_password($row['password']) && in_array($row['password'], $avoidUsersWithPassword)) {
+                                $this->logger->addInfo("Students - User skipped: ".$row['username']." because the avoid conditions (password).");
+                                continue;
+                            }
+                        }
+                    }
+
+                    $expirationDate = api_get_utc_datetime(strtotime("+".intval($this->expirationDateInUserUpdate)."years"));
+
+                    // Update user
+                    $result = UserManager::update_user(
+                        $userInfo['user_id'],
+                        $row['firstname'], // <<-- changed
+                        $row['lastname'],  // <<-- changed
+                        $userInfo['username'],
+                        null, //$password = null,
+                        $auth_source = null,
+                        $userInfo['email'],
+                        STUDENT,
+                        $userInfo['official_code'],
+                        $userInfo['phone'],
+                        $userInfo['picture_uri'],
+                        $expirationDate,
+                        $userInfo['active'],
+                        null, //$creator_id = null,
+                        0, //$hr_dept_id = 0,
+                        null, // $extra = null,
+                        null, //$language = 'english',
+                        null, //$encrypt_method = '',
+                        false, //$send_email = false,
+                        0 //$reset_password = 0
+                    );
+
+                    if ($result) {
+                        foreach ($row as $key => $value) {
+                            if (substr($key, 0, 6) == 'extra_') { //an extra field
+                                UserManager::update_extra_field_value($userInfo['user_id'], substr($key, 6), $value);
+                            }
+                        }
+
+                        $this->logger->addInfo("Students - User updated: ".$row['username']);
+                    } else {
+                        $this->logger->addError("Students - User NOT updated: ".$row['username']." ".$row['firstname']." ".$row['lastname']);
+                    }
                 }
+            }
+        }
 
-            } else {
-                // Update
-                $params = array(
-                    'title' => $row['title'],
-                );
-
-                $result = CourseManager::update_attributes($courseInfo['id'], $params);
+        $this->moveFile($file);
+    }
 
-                if ($result) {
-                    Log::info("Courses - Course updated ".$courseInfo['code']);
+    /**
+     * @param string $file
+     */
+    private function importCourses($file)
+    {
+        $data = Import::csv_to_array($file);
+
+        //$language = $this->defaultLanguage;
+
+        if (!empty($data)) {
+            $this->logger->addInfo(count($data)." records found.");
+
+            foreach ($data as $row) {
+                $row = $this->cleanCourseRow($row);
+                $courseCode = CourseManager::get_course_id_from_original_id($row['extra_'.$this->extraFieldIdNameList['course']], $this->extraFieldIdNameList['course']);
+
+                //$courseInfo = api_get_course_info($row['course_code']);
+                $courseInfo = api_get_course_info($courseCode);
+                if (empty($courseInfo)) {
+                    // Create
+                    $params = array();
+                    $params['title']                = $row['title'];
+                    $params['exemplary_content']    = false;
+                    $params['wanted_code']          = $row['course_code'];
+                    $params['course_category']      = $row['course_category'];
+                    $params['course_language']      = $row['language'];
+                    $params['teachers']             = $row['teachers'];
+
+                    $courseInfo = CourseManager::create_course($params);
+
+                    if (!empty($courseInfo)) {
+                        CourseManager::update_course_extra_field_value($courseInfo['code'], 'external_course_id', $row['extra_'.$this->extraFieldIdNameList['course']]);
+                        $this->logger->addInfo("Courses - Course created ".$courseInfo['code']);
+                    } else {
+                        $this->logger->addError("Courses - Can't create course:".$row['title']);
+                    }
                 } else {
-                    Log::error("Courses - Course NOT updated ".$courseInfo['code']);
+                    // Update
+                    $params = array(
+                        'title' => $row['title'],
+                    );
+                    $result = CourseManager::update_attributes($courseInfo['real_id'], $params);
+
+                    if ($result) {
+                        $this->logger->addInfo("Courses - Course updated ".$courseInfo['code']);
+                    } else {
+                        $this->logger->addError("Courses - Course NOT updated ".$courseInfo['code']);
+                    }
                 }
-
-                /*course_language='".Database::escape_string($course_language)."',
-                title='".Database::escape_string($title)."',
-                category_code='".Database::escape_string($category_code)."',
-                tutor_name='".Database::escape_string($tutor_name)."',
-                visual_code='".Database::escape_string($visual_code)."',
-                department_name='".Database::escape_string($department_name)."',
-                department_url='".Database::escape_string($department_url)."',
-                disk_quota='".Database::escape_string($disk_quota)."',
-                visibility = '".Database::escape_string($visibility)."',
-                subscribe = '".Database::escape_string($subscribe)."',
-                unsubscribe='*/
             }
         }
+        $this->moveFile($file);
     }
-    moveFile($file);
-}
 
-function importSessions($file)
-{
-    $result = SessionManager::importCSV($file, true, 1, true);
+    /**
+     * @param string $file
+     */
+    private function importSessions($file)
+    {
+        $result = SessionManager::importCSV(
+            $file,
+            true,
+            $this->defaultAdminId,
+            $this->logger,
+            array('SessionID' => 'extra_'.$this->extraFieldIdNameList['session']),
+            $this->extraFieldIdNameList['session'],
+            $this->daysCoachAccessBeforeBeginning,
+            $this->daysCoachAccessAfterBeginning,
+            $this->defaultSessionVisibility
+        );
+
+        if (!empty($result['error_message'])) {
+            $this->logger->addError($result['error_message']);
+        }
+        $this->logger->addInfo("Sessions - Sessions parsed: ".$result['session_counter']);
+        $this->moveFile($file);
+    }
 
-    if (!empty($result['error_message'])) {
-        Log::error($result['error_message']);
+    private function dumpDatabaseTables()
+    {
+        echo 'Dumping tables'.PHP_EOL;
+
+        // User
+        $table = Database::get_main_table(TABLE_MAIN_USER);
+        $tableAdmin = Database::get_main_table(TABLE_MAIN_ADMIN);
+        //$sql = "DELETE FROM $table WHERE username NOT IN ('admin') AND lastname <> 'Anonymous' ";
+        $sql = "DELETE FROM $table WHERE user_id not in (select user_id from $tableAdmin) and status <> ".ANONYMOUS;
+        Database::query($sql);
+        echo $sql.PHP_EOL;
+
+        // Course
+        $table = Database::get_main_table(TABLE_MAIN_COURSE);
+        $sql = "DELETE FROM $table";
+        Database::query($sql);
+        echo $sql.PHP_EOL;
+
+        $table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
+        $sql = "DELETE FROM $table";
+        Database::query($sql);
+        echo $sql.PHP_EOL;
+
+        $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
+        $sql = "DELETE FROM $table";
+        Database::query($sql);
+        echo $sql.PHP_EOL;
+
+        // Sessions
+        $table = Database::get_main_table(TABLE_MAIN_SESSION);
+        $sql = "DELETE FROM $table";
+        Database::query($sql);
+        echo $sql.PHP_EOL;
+
+        $table = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
+        $sql = "DELETE FROM $table";
+        Database::query($sql);
+        echo $sql.PHP_EOL;
+
+        $table = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
+        $sql = "DELETE FROM $table";
+        Database::query($sql);
+        echo $sql.PHP_EOL;
+
+        $table = Database::get_main_table(TABLE_MAIN_SESSION_USER);
+        $sql = "DELETE FROM $table";
+        Database::query($sql);
+        echo $sql.PHP_EOL;
+
+        // Extra fields
+
+        $table = Database::get_main_table(TABLE_MAIN_SESSION_FIELD_VALUES);
+        $sql = "DELETE FROM $table";
+        Database::query($sql);
+        echo $sql.PHP_EOL;
+
+        $table = Database::get_main_table(TABLE_MAIN_COURSE_FIELD_VALUES);
+        $sql = "DELETE FROM $table";
+        Database::query($sql);
+        echo $sql.PHP_EOL;
+
+        $table = Database::get_main_table(TABLE_MAIN_USER_FIELD_VALUES);
+        $sql = "DELETE FROM $table";
+        Database::query($sql);
+        echo $sql.PHP_EOL;
     }
+}
 
-    Log::info("Sessions - Sessions parsed: ".$result['session_counter']);
+use Monolog\Logger;
+use Monolog\Handler\StreamHandler;
+use Monolog\Handler\NativeMailerHandler;
+use Monolog\Handler\RotatingFileHandler;
+use Monolog\Handler\BufferHandler;
 
-    moveFile($file);
+$logger = new Logger('cron');
+$emails = isset($_configuration['cron_notification_mails']) ? $_configuration['cron_notification_mails'] : null;
 
-    /*$data = Import::csv_to_array($file);
-    Log::info("Reading file: $file");
-    if (!empty($data)) {
-        foreach ($data as $row) {
+$minLevel = Logger::DEBUG;
 
-            if (empty($courseInfo)) {
+if (!is_array($emails)) {
+    $emails = array($emails);
+}
+$subject = "Cron main/cron/import_csv.php ".date('Y-m-d h:i:s');
+$from = api_get_setting('emailAdministrator');
 
-            }
-        }
+if (!empty($emails)) {
+    foreach ($emails as $email) {
+        $stream = new NativeMailerHandler($email, $subject, $from, $minLevel);
+        $logger->pushHandler(new BufferHandler($stream, 0, $minLevel));
     }
-    moveFile($file);*/
 }
 
+$stream = new StreamHandler(api_get_path(SYS_ARCHIVE_PATH).'import_csv.log', $minLevel);
+$logger->pushHandler(new BufferHandler($stream, 0, $minLevel));
+$logger->pushHandler(new RotatingFileHandler('import_csv', 5, $minLevel));
+
+$import = new ImportCsv($logger, $_configuration['cron_import_csv_conditions']);
+
+if (isset($_configuration['default_admin_user_id_for_cron'])) {
+    $import->defaultAdminId = $_configuration['default_admin_user_id_for_cron'];
+}
+// @todo in production disable the dump option
+$dump = false;
+
+if (isset($argv[1]) && $argv[1] = '--dump') {
+    $dump = true;
+}
 
+$import->setDumpValues($dump);
+// Do not moves the files to treated
+$import->test = true;
+$import->run();

+ 13 - 6
main/css/base.css

@@ -27,7 +27,7 @@ legend {
 #header_right {
     text-align: right;
     float: right;
-    
+
 
 }
 
@@ -175,9 +175,9 @@ header #logo {
 #main .nav .dropdown {
   width:150px;
   text-align: right;
-   
-} 
- 
+
+}
+
 #main .nav .dropdown:nth-child(n) {
   width:auto;
 }
@@ -198,7 +198,7 @@ header {
 .breadcrumb li img {
   width: 0%;
  }
- 
+
  .breadcrumb li img:nth-child(n) {
   width: auto;
  }
@@ -434,7 +434,7 @@ footer #footer_left {
     vertical-align:middle;
 }
 .actions .actions-pagination {
-  float:right; 
+  float:right;
   margin-top:2px;
 }
 .actions .actions-pagination * {
@@ -581,6 +581,7 @@ a.tag {
 }
 #course_category li,
 #hot_courses h5,
+.course_item h5,
 .course-box h5 {
     font-size: 12px;
     overflow-y : auto;
@@ -4874,3 +4875,9 @@ i.size-32.icon-new-work{
   background-color: #e6e6e6;
 }
 
+.password-verdict {
+    padding-left:5px;
+}
+#password_progress {
+    width: 220px;
+}

+ 16 - 19
main/document/create_audio.php

@@ -274,20 +274,17 @@ $(document).ready(function(){
 		var voiceslist=document.form2.voices     
 		var voices=new Array()
 		
-		<!--Default message -->
-		voices[0]=["<?php echo get_lang('FirstSelectALanguage'); ?>"]
-		
 		<!--German -->
-		voices[1]=["<?php echo get_lang('Female').' (de1)'; ?>|de1", "<?php echo get_lang('Male').' (de2)'; ?>|de2", "<?php echo get_lang('Female').' (de3)'; ?>|de3", "<?php echo get_lang('Male').' (de4)'; ?>|de4", "<?php echo get_lang('Female').' (de5)'; ?>|de5", "<?php echo get_lang('Male').' (de6)'; ?>|de6", "<?php echo get_lang('Female').' (de7)'; ?>|de7", "<?php echo get_lang('Female').' (de8 HQ)'; ?>|de8"]
+		voices[0]=["<?php echo get_lang('Female').' (de1)'; ?>|de1", "<?php echo get_lang('Male').' (de2)'; ?>|de2", "<?php echo get_lang('Female').' (de3)'; ?>|de3", "<?php echo get_lang('Male').' (de4)'; ?>|de4", "<?php echo get_lang('Female').' (de5)'; ?>|de5", "<?php echo get_lang('Male').' (de6)'; ?>|de6", "<?php echo get_lang('Female').' (de7)'; ?>|de7", "<?php echo get_lang('Female').' (de8 HQ)'; ?>|de8"]
 		
 		<!--English -->
-		voices[2]=["<?php echo get_lang('Male').' (en1)'; ?>|en1", "<?php echo get_lang('Male').' (en2 HQ)'; ?>|en2", "<?php echo get_lang('Female').' (us1)'; ?>| us1", "<?php echo get_lang('Male').' (us2)'; ?>|us2", "<?php echo get_lang('Male').' (us3)'; ?>|us3", "<?php echo get_lang('Female').'(us4 HQ)'; ?>|us4"]	
+		voices[1]=["<?php echo get_lang('Male').' (en1)'; ?>|en1", "<?php echo get_lang('Male').' (en2 HQ)'; ?>|en2", "<?php echo get_lang('Female').' (us1)'; ?>| us1", "<?php echo get_lang('Male').' (us2)'; ?>|us2", "<?php echo get_lang('Male').' (us3)'; ?>|us3", "<?php echo get_lang('Female').'(us4 HQ)'; ?>|us4"]	
 		
 		<!--Spanish -->
-		voices[3]=["<?php echo get_lang('Male').' (es5 HQ)'; ?>|es5"]
+		voices[2]=["<?php echo get_lang('Male').' (es5 HQ)'; ?>|es5"]
 		
 		<!--French -->
-		voices[4]=["<?php echo get_lang('Female').' (fr8 HQ)'; ?>|fr8"]
+		voices[3]=["<?php echo get_lang('Female').' (fr8 HQ)'; ?>|fr8"]
 			
 			 
 		function update_voices(selectedvoicegroup){
@@ -295,9 +292,7 @@ $(document).ready(function(){
 		for (i=0; i<voices[selectedvoicegroup].length; i++)
 			voiceslist.options[voiceslist.options.length]=new Option(voices[selectedvoicegroup][i].split("|")[0], voices[selectedvoicegroup][i].split("|")[1])
 		}
-		</script>    
-
-     
+		</script>
     <?php
 	}//end pediaphon
 	
@@ -494,7 +489,7 @@ function downloadMP3_pediaphon($filepath, $dir){
 	//adding the file
 	
 		
-		if($clean_lang=='de'){
+		if ($clean_lang=='de'){
 			$url_pediaphon='http://www.pediaphon.org/~bischoff/radiopedia/sprich_multivoice.cgi';
 			$find_t2v = '/http\:\/\/www\.pediaphon\.org\/\~bischoff\/radiopedia\/mp3\/(.*)\.mp3\"/';
 		}		
@@ -503,7 +498,7 @@ function downloadMP3_pediaphon($filepath, $dir){
 			$find_t2v = '/http\:\/\/www\.pediaphon\.org\/\~bischoff\/radiopedia\/mp3\/'.$clean_lang.'\/(.*)\.mp3\"/';
 		}
 
-		$data="stimme=".$clean_voices."&inputtext=".$clean_text."&speed=".$clean_speed."&go=go";
+		$data="stimme=".$clean_voices."&inputtext=".$clean_text."&speed=".$clean_speed."&go=speak";
 		$opts = array('http' =>
 			array(
 			 'method'  => 'POST',
@@ -513,20 +508,22 @@ function downloadMP3_pediaphon($filepath, $dir){
 			)
 		);                   
 		$context  = stream_context_create($opts);
+                // Download the whole HTML page
 		$previous_returntext2voice = file_get_contents($url_pediaphon,false,$context);
 		
-		//clean file contents
-		
+		//extract the audio file path
 		$search_source=preg_match($find_t2v, $previous_returntext2voice, $hits);
 		$souce_end=substr($hits[0], 0,-1);
+		//download file
 		$returntext2voice = file_get_contents($souce_end);
-				
 		//save file
-		file_put_contents($documentPath, $returntext2voice);
-		
+		$f = @file_put_contents($documentPath, $returntext2voice);
+		if ($f === false && !empty($php_errormsg)) {
+                    error_log($php_errormsg);
+                }
 		//add document to database
 		$current_session_id = api_get_session_id();
-		$groupId=$_SESSION['_gid'];
+		$groupId = $_SESSION['_gid'];
 		$file_size = filesize($documentPath);
 		$relativeUrlPath=$dir;
 		$doc_id = add_document($_course, $relativeUrlPath.$audio_filename, 'file', filesize($documentPath), $audio_title);
@@ -534,4 +531,4 @@ function downloadMP3_pediaphon($filepath, $dir){
         Display::display_confirmation_message(get_lang('DocumentCreated'));
 	//return to location
 	echo '<script>window.location.href="'.$location.'"</script>';
-}
+}

+ 12 - 12
main/document/upload.php

@@ -86,7 +86,7 @@ $(function () {
         buildUploadRow: function (files, index) {
 			$('.files').show();
             return $('<tr><td>' + files[index].name + '<\/td>' +
-					'<td class=\"file_upload_progress\"><div><\/div><\/td>' +                    
+					'<td class=\"file_upload_progress\"><div><\/div><\/td>' +
                     '<td class=\"file_upload_cancel\">' +
                     '<button class=\"ui-state-default ui-corner-all\" title=\"".get_lang('Cancel')."\">' + '<span class=\"ui-icon ui-icon-cancel\">".get_lang('Cancel')."<\/span>' +'<\/button>'+
 					'<\/td><\/tr>');
@@ -94,7 +94,7 @@ $(function () {
         buildDownloadRow: function (file) {
             return $('<tr><td>' + file.name + '<\/td> <td> ' + file.size + ' <\/td>  <td>&nbsp;' + file.result + ' <\/td> <\/tr>');
         }
-    });    
+    });
     $('#tabs').tabs();
 });
 </script>";
@@ -112,7 +112,7 @@ $selectcat = isset($_GET['selectcat']) ? Security::remove_XSS($_GET['selectcat']
 $document_data  = DocumentManager::get_document_data_by_id($_REQUEST['id'], api_get_course_id(), true);
 if (empty($document_data)) {
     $document_id  = $parent_id =  0;
-    $path = '/';    
+    $path = '/';
 } else {
     $document_id    = $document_data['id'];
     $path           = $document_data['path'];
@@ -120,13 +120,13 @@ if (empty($document_data)) {
 }
 $group_properties = array();
 // This needs cleaning!
-if (api_get_group_id()) { 
+if (api_get_group_id()) {
     // If the group id is set, check if the user has the right to be here
 	// Needed for group related stuff
 	require_once api_get_path(LIBRARY_PATH).'groupmanager.lib.php';
 	// Get group info
 	$group_properties = GroupManager::get_group_properties(api_get_group_id());
-	
+
 	if ($is_allowed_to_edit || GroupManager::is_user_in_group($_user['user_id'], api_get_group_id())) { // Only courseadmin or group members allowed
 		$to_group_id = api_get_group_id();
 		$req_gid = '&amp;gidReq='.api_get_group_id();
@@ -135,7 +135,7 @@ if (api_get_group_id()) {
 		api_not_allowed(true);
 	}
 } elseif ($is_allowed_to_edit || is_my_shared_folder(api_get_user_id(), $path, api_get_session_id())) {
-     
+
     // Admin for "regular" upload, no group documents. And check if is my shared folder
 	$to_group_id = 0;
 	$req_gid = '';
@@ -190,7 +190,7 @@ Display::display_header($nameTools, 'Doc');
 /*	Here we do all the work */
 
 // User has submitted a file
-if (!empty($_FILES)) {    
+if (!empty($_FILES)) {
     DocumentManager::upload_document($_FILES, $_POST['curdirpath'], $_POST['title'], $_POST['comment'], $_POST['unzip'], $_POST['if_exists'], $_POST['index_document'], true);
 }
 
@@ -230,7 +230,7 @@ $course_quota = format_file_size(DocumentManager::get_course_quota() - DocumentM
 
 $label = get_lang('MaxFileSize').': '.ini_get('upload_max_filesize').'<br/>'.get_lang('DocumentQuota').': '.$course_quota;
 
-$form->addElement('file', 'file', array(get_lang('File'), $label), 'id="user_upload" size="45"');
+$form->addElement('file', 'file', array(get_lang('File'), $label), 'style="width: 250px" id="user_upload"');
 
 $form->addElement('text', 'title', get_lang('Title'), array('size' => '20', 'style' => 'width:300px', 'id' => 'title_file'));
 $form->addElement('textarea', 'comment', get_lang('Comment'), 'wrap="virtual" style="width:300px;"');
@@ -281,11 +281,11 @@ $multiple_form =  get_lang('ClickToSelectOrDragAndDropMultipleFilesOnTheUploadFi
 //<div style="width:50%;margin:0 auto;"> '.Display::div(Display::return_icon('folder_document.png', '', array(), 64), array('style'=>'float:left')).' '.get_lang('UploadFiles').'</div>
 $multiple_form .=  '
 	<center>
-	<form id="file_upload" action="'.$url.'" method="POST" enctype="multipart/form-data">    
-    	<input type="hidden" name="curdirpath" value="'.$path.'" />    
+	<form id="file_upload" action="'.$url.'" method="POST" enctype="multipart/form-data">
+    	<input type="hidden" name="curdirpath" value="'.$path.'" />
     	<input type="file" name="file" multiple>
     	<button type="submit">Upload</button>
-    	'.get_lang('UploadFiles').' 
+    	'.get_lang('UploadFiles').'
 	</form>
 	</center>
 	<table style="display:none; width:50%" class="files data_table">
@@ -301,6 +301,6 @@ if ($nav_info ['name'] == 'Internet Explorer') {
 	echo $simple_form;
 } else {
 	$headers = array(get_lang('Send') , get_lang('Send').' ('.get_lang('Simple').')');
-	echo Display::tabs($headers, array($multiple_form, $simple_form),'tabs');	
+	echo Display::tabs($headers, array($multiple_form, $simple_form),'tabs');
 }
 Display::display_footer();

+ 8 - 6
main/exercice/answer.class.php

@@ -442,13 +442,14 @@ class Answer {
 	 * @param	integer	Answer weighting
 	 * @param	integer	Answer position
 	 */
-	function updateAnswers($answer,$comment,$weighting,$position,$destination) {
+	function updateAnswers($answer,$comment, $correct, $weighting,$position,$destination) {
 		$TBL_REPONSES = Database :: get_course_table(TABLE_QUIZ_ANSWER);
 
 		$questionId=$this->questionId;
 		$sql = "UPDATE $TBL_REPONSES SET 
                 answer = '".Database::escape_string($answer)."',
-				comment = '".Database::escape_string($comment)."', 
+				comment = '".Database::escape_string($comment)."',
+                correct = '".Database::escape_string($correct)."', 
 				ponderation = '".Database::escape_string($weighting)."', 
 				position = '".Database::escape_string($position)."', 
 				destination = '".Database::escape_string($destination)."'
@@ -466,7 +467,6 @@ class Answer {
 		$TBL_REPONSES = Database :: get_course_table(TABLE_QUIZ_ANSWER);
         $table_track_e_attempt   = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
 		$questionId   = intval($this->questionId);
-       
 		       
 		$c_id = $this->course['real_id'];
 		// inserts new answers into data base
@@ -486,12 +486,14 @@ class Answer {
                 $flag = 1;
 			    $sql.="($c_id, '$i','$questionId','$answer','$correct','$comment','$weighting','$position','$hotspot_coordinates','$hotspot_type','$destination'),";
             } else {
-                $this->updateAnswers($answer, $comment, $weighting, $position, $destination);
+                // https://support.chamilo.org/issues/6558
+                // function updateAnswers already escape_string, error if we do it twice. Feed function updateAnswers with none escaped strings
+                $this->updateAnswers($this->new_answer[$i], $this->new_comment[$i], $this->new_correct[$i], $this->new_weighting[$i], $this->new_position[$i], $this->new_destination[$i]);
             }
         }
 		if ($flag == 1) {
-        $sql = api_substr($sql,0,-1);        
-		Database::query($sql);
+            $sql = api_substr($sql,0,-1);        
+            Database::query($sql);
         }
         if (count($this->position) > $this->new_nbrAnswers) {
             $i = $this->new_nbrAnswers+1;

+ 49 - 51
main/gradebook/lib/be/forumthreadlink.class.php

@@ -113,58 +113,56 @@ class ForumThreadLink extends AbstractLink
     }
 
     public function calc_score($stud_id = null) {    			
-		if (!empty($database_name)) {
-			$thread_qualify = Database :: get_course_table(TABLE_FORUM_THREAD_QUALIFY);
+        $thread_qualify = Database :: get_course_table(TABLE_FORUM_THREAD_QUALIFY);
 
-	  		$sql = 'SELECT thread_qualify_max FROM '.Database :: get_course_table(TABLE_FORUM_THREAD)." 
-	  				WHERE c_id = ".$this->course_id." AND thread_id = '".$this->get_ref_id()."'";
-			$query = Database::query($sql);
-			$assignment = Database::fetch_array($query);
-
-	  	    $sql = "SELECT * FROM $thread_qualify WHERE c_id = ".$this->course_id." AND thread_id = ".$this->get_ref_id();
-	    	if (isset($stud_id)) {
-	    		$sql .= ' AND user_id = '."'".intval($stud_id)."'";
-	    	}
-
-	    	// order by id, that way the student's first attempt is accessed first
-			$sql .= ' ORDER BY qualify_time DESC';
-
-	    	$scores = Database::query($sql);
-
-			// for 1 student
-	    	if (isset($stud_id)) {
-	    	    
-	    		if ($data = Database::fetch_array($scores)) {	    		    
-	    			return array ($data['qualify'], $assignment['thread_qualify_max']);
-	    		} else {
-	    			//We sent the 0/thread_qualify_max instead of null for correct calculations
-	      			//return null;
-	      			return array (0, $assignment['thread_qualify_max']);
-	    		}
-	    	} else {
-	    	    // all students -> get average
-	    		$students=array();  // user list, needed to make sure we only
-	    							// take first attempts into account
-				$rescount = 0;
-				$sum = 0;
-
-				while ($data=Database::fetch_array($scores)) {
-					if (!(array_key_exists($data['user_id'],$students))) {
-						if ($assignment['thread_qualify_max'] != 0) {
-							$students[$data['user_id']] = $data['qualify'];
-							$rescount++;
-							$sum += ($data['qualify'] / $assignment['thread_qualify_max']);
-						}
-					 }
-				}
-
-				if ($rescount == 0) {
-					return null;
-				  } else {
-					return array ($sum , $rescount);
-				 }    
-	    	}
-        }
+  		$sql = 'SELECT thread_qualify_max FROM '.Database :: get_course_table(TABLE_FORUM_THREAD)." 
+  				WHERE c_id = ".$this->course_id." AND thread_id = '".$this->get_ref_id()."'";
+		$query = Database::query($sql);
+		$assignment = Database::fetch_array($query);
+
+  	    $sql = "SELECT * FROM $thread_qualify WHERE c_id = ".$this->course_id." AND thread_id = ".$this->get_ref_id();
+    	if (isset($stud_id)) {
+    		$sql .= ' AND user_id = '."'".intval($stud_id)."'";
+    	}
+
+    	// order by id, that way the student's first attempt is accessed first
+		$sql .= ' ORDER BY qualify_time DESC';
+
+    	$scores = Database::query($sql);
+
+		// for 1 student
+    	if (isset($stud_id)) {
+    	    
+    		if ($data = Database::fetch_array($scores)) {	    		    
+    			return array ($data['qualify'], $assignment['thread_qualify_max']);
+			} else {
+    			//We sent the 0/thread_qualify_max instead of null for correct calculations
+      			//return null;
+      			return array (0, $assignment['thread_qualify_max']);
+    		}
+    	} else {
+    	    // all students -> get average
+    		$students=array();  // user list, needed to make sure we only
+    							// take first attempts into account
+			$rescount = 0;
+			$sum = 0;
+
+			while ($data=Database::fetch_array($scores)) {
+				if (!(array_key_exists($data['user_id'],$students))) {
+					if ($assignment['thread_qualify_max'] != 0) {
+						$students[$data['user_id']] = $data['qualify'];
+						$rescount++;
+						$sum += ($data['qualify'] / $assignment['thread_qualify_max']);
+					}
+				 }
+			}
+
+			if ($rescount == 0) {
+			    return null;
+		    } else {
+                return array ($sum , $rescount);
+   		    }    
+    	}
     }
 
 // INTERNAL FUNCTIONS

BIN
main/img/icons/22/course_na.png


+ 1 - 0
main/inc/ajax/course.ajax.php

@@ -4,6 +4,7 @@
  * Responses to AJAX calls
  */
 
+$language_file[] = 'admin';
 require_once '../global.inc.php';
 
 $action = $_REQUEST['a'];

+ 25 - 0
main/inc/ajax/form.ajax.php

@@ -0,0 +1,25 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+require_once '../global.inc.php';
+
+$action = isset($_REQUEST['a']) ? $_REQUEST['a'] : null;
+
+switch ($action) {
+    case 'get_captcha':
+        header('Content-Type: image/jpeg');
+
+        $sessionVar = (empty($_REQUEST['var']))
+                      ? '_HTML_QuickForm_CAPTCHA'
+                      : $_REQUEST['var'];
+        // Force a new CAPTCHA for each one displayed
+
+        /** @var Text_CAPTCHA $obj */
+        $obj = $_SESSION[$sessionVar];
+        $obj->generate(true);
+        echo $image = $obj->getCAPTCHA();
+
+        //echo $_SESSION[$sessionVar]->getCAPTCHAAsJPEG();
+        exit;
+        break;
+}

+ 79 - 36
main/inc/ajax/model.ajax.php

@@ -31,15 +31,20 @@ if (!in_array($sord, array('asc','desc'))) {
     $sord = 'desc';
 }
 
-if (!in_array($action, array(
+if (!in_array(
+    $action,
+    array(
         'get_exercise_results',
         'get_hotpotatoes_exercise_results',
         'get_work_user_list',
+        'get_work_user_list_others',
+        'get_work_user_list_all',
         'get_timelines',
         'get_user_skill_ranking',
-        'get_usergroups_teacher'))
-    ) {
-	api_protect_admin_script(true);
+        'get_usergroups_teacher'
+    )
+)) {
+    api_protect_admin_script(true);
 }
 
 //Search features
@@ -63,7 +68,8 @@ $ops = array (
 
 //@todo move this in the display_class or somewhere else
 
-function get_where_clause($col, $oper, $val) {
+function get_where_clause($col, $oper, $val)
+{
     global $ops;
     if (empty($col)){
         return '';
@@ -126,17 +132,33 @@ switch ($action) {
         $course_id = api_get_course_int_id();
         $count = Question::get_count_course_medias($course_id);
         break;
-	case 'get_user_skill_ranking':
-    	$skill = new Skill();
-	    $count = $skill->get_user_list_skill_ranking_count();
-	    break;
-    case 'get_work_user_list':
+    case 'get_user_skill_ranking':
+        $skill = new Skill();
+        $count = $skill->get_user_list_skill_ranking_count();
+        break;
+    case 'get_work_user_list_all':
         require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
         $work_id = $_REQUEST['work_id'];
         $count = get_count_work($work_id);
         break;
-	case 'get_exercise_results':
-		require_once api_get_path(SYS_CODE_PATH).'exercice/exercise.lib.php';
+    case 'get_work_user_list_others':
+        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
+        $work_id = $_REQUEST['work_id'];
+        $count = get_count_work($work_id, api_get_user_id());
+        break;
+    case 'get_work_user_list':
+        require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
+        $work_id = $_REQUEST['work_id'];
+        $courseInfo = api_get_course_info();
+        // All
+        if ($courseInfo['show_score'] == '0') {
+            $count = get_count_work($work_id, null, api_get_user_id());
+        } else {
+            $count = get_count_work($work_id, api_get_user_id());
+        }
+        break;
+    case 'get_exercise_results':
+        require_once api_get_path(SYS_CODE_PATH).'exercice/exercise.lib.php';
         $exercise_id = $_REQUEST['exerciseId'];
 
         if (isset($_GET['filter_by_user']) && !empty($_GET['filter_by_user'])) {
@@ -147,13 +169,13 @@ switch ($action) {
                 $where_condition .= " AND te.exe_user_id  = '$filter_user'";
             }
         }
-		$count = get_count_exam_results($exercise_id, $where_condition);
-		break;
-	case 'get_hotpotatoes_exercise_results':
-		require_once api_get_path(SYS_CODE_PATH).'exercice/exercise.lib.php';
+        $count = get_count_exam_results($exercise_id, $where_condition);
+        break;
+    case 'get_hotpotatoes_exercise_results':
+        require_once api_get_path(SYS_CODE_PATH).'exercice/exercise.lib.php';
         $hotpot_path = $_REQUEST['path'];
-		$count = get_count_exam_hotpotatoes_results($hotpot_path);
-		break;
+        $count = get_count_exam_hotpotatoes_results($hotpot_path);
+        break;
     case 'get_sessions':
         $count = SessionManager::get_count_admin();
         break;
@@ -294,12 +316,30 @@ switch ($action) {
 	        }
 	    }
     	break;
+    case 'get_work_user_list_all':
+        if (isset($_GET['type'])  && $_GET['type'] == 'simple') {
+            $columns = array('type', 'firstname', 'lastname',  'username', 'title', 'qualification', 'sent_date', 'qualificator_id', 'actions');
+        } else {
+            $columns = array('type', 'firstname', 'lastname',  'username', 'title', 'sent_date', 'actions');
+        }
+        $result = get_work_user_list($start, $limit, $sidx, $sord, $work_id, $where_condition);
+        break;
+    case 'get_work_user_list_others':
+        if (isset($_GET['type'])  && $_GET['type'] == 'simple') {
+            $columns = array('type', 'firstname', 'lastname',  'username', 'title', 'qualification', 'sent_date', 'qualificator_id', 'actions');
+        } else {
+            $columns = array('type', 'firstname', 'lastname',  'username', 'title', 'sent_date', 'actions');
+        }
+        $where_condition .= " AND u.user_id <> ".api_get_user_id();
+        $result = get_work_user_list($start, $limit, $sidx, $sord, $work_id, $where_condition);
+        break;
     case 'get_work_user_list':
         if (isset($_GET['type'])  && $_GET['type'] == 'simple') {
             $columns = array('type', 'firstname', 'lastname',  'username', 'title', 'qualification', 'sent_date', 'qualificator_id', 'actions');
         } else {
             $columns = array('type', 'firstname', 'lastname',  'username', 'title', 'sent_date', 'actions');
         }
+        $where_condition .= " AND u.user_id = ".api_get_user_id();
         $result = get_work_user_list($start, $limit, $sidx, $sord, $work_id, $where_condition);
         break;
 	case 'get_exercise_results':
@@ -540,24 +580,27 @@ switch ($action) {
         exit;
 }
 
-$allowed_actions = array('get_careers',
-                         'get_promotions',
-                         'get_usergroups',
-                         'get_usergroups_teacher',
-                         'get_gradebooks',
-                         'get_sessions',
-                         'get_exercise_results',
-                         'get_hotpotatoes_exercise_results',
-                         'get_work_user_list',
-                         'get_timelines',
-                         'get_grade_models',
-                         'get_event_email_template',
-                         'get_user_skill_ranking',
-                         //'get_extra_fields',
-                         //'get_extra_field_options',
-                         //'get_course_exercise_medias',
-                         'get_user_course_report',
-                         'get_user_course_report_resumed'
+$allowed_actions = array(
+    'get_careers',
+    'get_promotions',
+    'get_usergroups',
+    'get_usergroups_teacher',
+    'get_gradebooks',
+    'get_sessions',
+    'get_exercise_results',
+    'get_hotpotatoes_exercise_results',
+    'get_work_user_list',
+    'get_work_user_list_others',
+    'get_work_user_list_all',
+    'get_timelines',
+    'get_grade_models',
+    'get_event_email_template',
+    'get_user_skill_ranking',
+    //'get_extra_fields',
+    //'get_extra_field_options',
+    //'get_course_exercise_medias',
+    'get_user_course_report',
+    'get_user_course_report_resumed'
 );
 
 //5. Creating an obj to return a json

+ 32 - 0
main/inc/ajax/session.ajax.php

@@ -0,0 +1,32 @@
+<?php
+/* For licensing terms, see /license.txt */
+/**
+ * Responses to AJAX calls
+ */
+
+$language_file[] = 'admin';
+require_once '../global.inc.php';
+
+$action = $_REQUEST['a'];
+
+//$user_id = api_get_user_id();
+
+switch ($action) {    
+    case  'get_user_sessions':
+        if (api_is_platform_admin()) {
+            $user_id = intval($_POST['user_id']);            
+            $list_sessions = SessionManager::get_sessions_by_user($user_id);
+            if (!empty($list_sessions)) {
+                foreach($list_sessions as $session_item) {                
+                    echo $session_item['session_name'].'<br />';
+                }
+            } else {            
+                echo get_lang('NoSessionsForThisUser');
+            } 
+            unset($list_sessions);
+        }
+        break;
+    default:
+        echo '';
+}
+exit;

+ 14 - 0
main/inc/lib/autoload.class.php

@@ -685,6 +685,20 @@ class Autoload
         $result['HTML_QuickForm_text'] = '/main/inc/lib/pear/HTML/QuickForm/text.php';
         $result['HTML_QuickForm_textarea'] = '/main/inc/lib/pear/HTML/QuickForm/textarea.php';
         $result['HTML_QuickForm_xbutton'] = '/main/inc/lib/pear/HTML/QuickForm/xbutton.php';
+
+        $result['Text_CAPTCHA_Driver_Equation'] = '/main/inc/lib/pear/Text/CAPTCHA/Driver/Equation.php';
+        $result['Text_CAPTCHA_Driver_Figlet'] = '/main/inc/lib/pear/Text/CAPTCHA/Driver/Figlet.php';
+        $result['Text_CAPTCHA_Driver_Image'] = '/main/inc/lib/pear/Text/CAPTCHA/Driver/Image.php';
+        $result['Text_CAPTCHA_Driver_Numeral'] = '/main/inc/lib/pear/Text/CAPTCHA/Driver/Numeral.php';
+        $result['Text_CAPTCHA_Driver'] = '/main/inc/lib/pear/Text/CAPTCHA/Driver.php';
+        $result['Text_CAPTCHA_Exception'] = '/main/inc/lib/pear/Text/CAPTCHA/Exception.php';
+        $result['Text_CAPTCHA'] = '/main/inc/lib/pear/Text/CAPTCHA.php';
+
+        $result['HTML_QuickForm_CAPTCHA'] = '/main/inc/lib/pear/HTML/QuickForm/CAPTCHA.php';
+        $result['HTML_QuickForm_CAPTCHA_Image'] = '/main/inc/lib/pear/HTML/QuickForm/CAPTCHA/Image.php';
+        $result['HTML_QuickForm_CAPTCHA_Word'] = '/main/inc/lib/pear/HTML/QuickForm/CAPTCHA/Word.php';
+
+
         $result['HTML_Table'] = '/main/inc/lib/pear/HTML/Table.php';
         $result['HTML_Table_Storage'] = '/main/inc/lib/pear/HTML/Table/Storage.php';
         $result['Header'] = '/main/inc/lib/system/web/header.class.php';

+ 8 - 5
main/inc/lib/banner.lib.php

@@ -201,11 +201,14 @@ function return_notification_menu() {
             }
         }
 
-        // Display the who's online for the session
-        if (isset($user_id) && api_get_session_id() != 0) {
-            $html .= '<li><a href="'.api_get_path(WEB_PATH).'whoisonlinesession.php?id_coach='.$user_id.'&amp;referer='.urlencode($_SERVER['REQUEST_URI']).'" target="_top">'.
-                    Display::return_icon('session.png', get_lang('UsersConnectedToMySessions'), array(), ICON_SIZE_TINY).' </a></li>';
-        }
+        //if (api_get_setting('showonline', 'session') == 'true') {
+
+            // Display the who's online for the session
+            if (isset($user_id) && api_get_session_id() != 0) {
+                $html .= '<li><a href="'.api_get_path(WEB_PATH).'whoisonlinesession.php?id_coach='.$user_id.'&amp;referer='.urlencode($_SERVER['REQUEST_URI']).'" target="_top">'.
+                        Display::return_icon('session.png', get_lang('UsersConnectedToMySessions'), array(), ICON_SIZE_TINY).' </a></li>';
+            }
+        //}
     }
 
 

+ 21 - 55
main/inc/lib/course.lib.php

@@ -17,50 +17,6 @@
  * @package chamilo.library
  */
 
-/**
-    DOCUMENTATION
-    (list not up to date, you can auto generate documentation with phpDocumentor)
-
-    CourseManager::get_real_course_code_select_html($element_name, $has_size=true, $only_current_user_courses=true)
-    CourseManager::check_parameter($parameter, $error_message)
-    CourseManager::check_parameter_or_fail($parameter, $error_message)
-    CourseManager::course_code_exists($wanted_course_code)
-    CourseManager::get_real_course_list()
-    CourseManager::get_virtual_course_list()
-
-    GENERAL COURSE FUNCTIONS
-    CourseManager::get_access_settings($course_code)
-    CourseManager::set_course_tool_visibility($tool_table_id, $visibility)
-    CourseManager::get_user_in_course_status($user_id, $course_code)
-    CourseManager::add_user_to_course($user_id, $course_code)
-    CourseManager::get_virtual_course_info($real_course_code)
-    CourseManager::is_virtual_course_from_visual_code($visual_code)
-    CourseManager::is_virtual_course_from_system_code($system_code)
-    CourseManager::get_virtual_courses_linked_to_real_course($real_course_code)
-    CourseManager::get_list_of_virtual_courses_for_specific_user_and_real_course($user_id, $real_course_code)
-    CourseManager::has_virtual_courses_from_code($real_course_code, $user_id)
-    CourseManager::get_target_of_linked_course($virtual_course_code)
-
-    TITLE AND CODE FUNCTIONS
-    CourseManager::create_combined_name($user_is_registered_in_real_course, $real_course_name, $virtual_course_list)
-    CourseManager::create_combined_code($user_is_registered_in_real_course, $real_course_code, $virtual_course_list)
-
-    USER FUNCTIONS
-    CourseManager::get_real_course_list_of_user_as_course_admin($user_id)
-    CourseManager::get_course_list_of_user_as_course_admin($user_id)
-
-    CourseManager::is_user_subscribed_in_course($user_id, $course_code)
-    CourseManager::is_user_subscribed_in_real_or_linked_course($user_id, $course_code)
-    CourseManager::get_user_list_from_course_code($course_code)
-    CourseManager::get_real_and_linked_user_list($course_code);
-
-    GROUP FUNCTIONS
-    CourseManager::get_group_list_of_course($course_code)
-
-    CREATION FUNCTIONS
-    CourseManager::attempt_create_virtual_course($real_course_code, $course_title, $wanted_course_code, $course_language, $course_category)
-*/
-
 /*    INIT SECTION */
 
 require_once api_get_path(CONFIGURATION_PATH).'add_course.conf.php';
@@ -86,7 +42,8 @@ class CourseManager {
      * @param   array   with the columns in the main.course table
      * @param   mixed   false if the course was not created, array with the course info
      */
-    static function create_course($params) {
+    static function create_course($params)
+    {
         global $_configuration;
         // Check portal limits
         $access_url_id = 1;
@@ -1068,7 +1025,7 @@ class CourseManager {
      *
      *    @return true if the user is registered in the real course or linked courses, false otherwise
      */
-    public static function is_user_subscribed_in_real_or_linked_course ($user_id, $course_code, $session_id = '') {
+    public static function is_user_subscribed_in_real_or_linked_course($user_id, $course_code, $session_id = '') {
 
         if ($user_id != strval(intval($user_id))) {
             return false;
@@ -1129,7 +1086,17 @@ class CourseManager {
      * @param int       if using the session_id: 0 or 2 (student, coach), if using session_id = 0 STUDENT or COURSEMANAGER
      * @return array
      */
-    public static function get_user_list_from_course_code($course_code = null, $session_id = 0, $limit = null, $order_by = null, $filter_by_status = null, $return_count = null, $add_reports = false, $resumed_report = false, $extra_field = null) {
+    public static function get_user_list_from_course_code(
+        $course_code = null,
+        $session_id = 0,
+        $limit = null,
+        $order_by = null,
+        $filter_by_status = null,
+        $return_count = null,
+        $add_reports = false,
+        $resumed_report = false,
+        $extra_field = null
+    ) {
         // variable initialisation
         $session_id     = intval($session_id);
         $course_code    = Database::escape_string($course_code);
@@ -1212,7 +1179,7 @@ class CourseManager {
         }
 
         if ($return_count && $resumed_report) {
-            $sql .= ' AND field_id IS NOT NULL  GROUP BY field_value ';
+            $sql .= ' AND field_id IS NOT NULL GROUP BY field_value ';
         }
 
         $sql .= ' '.$order_by.' '.$limit;
@@ -1351,7 +1318,7 @@ class CourseManager {
         $session_id     = intval($session_id);
         $course_code    = Database::escape_string($course_code);
 
-        $sql .= 'SELECT DISTINCT count(*) as count  FROM '.Database::get_main_table(TABLE_MAIN_USER).' as user ';
+        $sql = 'SELECT DISTINCT count(*) as count  FROM '.Database::get_main_table(TABLE_MAIN_USER).' as user ';
         $where = array();
         if (!empty($session_id)) {
             $sql .= ' LEFT JOIN '.Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER).' as session_course_user
@@ -1671,8 +1638,7 @@ class CourseManager {
                     ON (g.id = gu.group_id AND g.c_id = $course_id AND gu.c_id = $course_id)
                     $session_condition
                     ORDER BY g.name";
-                }
-        else {
+        } else {
             // get all groups even if they are empty
             $sql = "SELECT g.id, g.name
                     FROM ".Database::get_course_table(TABLE_GROUP)." AS g
@@ -3244,18 +3210,18 @@ class CourseManager {
     /**
      * Get the course id based on the original id and field name in the extra fields. Returns 0 if course was not found
      *
-     * @param string Original course id
+     * @param string Original course code
      * @param string Original field name
      * @return int Course id
      */
     public static function get_course_id_from_original_id($original_course_id_value, $original_course_id_name) {
         $t_cfv = Database::get_main_table(TABLE_MAIN_COURSE_FIELD_VALUES);
         $table_field = Database::get_main_table(TABLE_MAIN_COURSE_FIELD);
-        $sql_course = "SELECT id FROM $table_field cf INNER JOIN $t_cfv cfv ON cfv.field_id=cf.id WHERE field_variable='$original_course_id_name' AND field_value='$original_course_id_value'";
+        $sql_course = "SELECT course_code FROM $table_field cf INNER JOIN $t_cfv cfv ON cfv.field_id=cf.id WHERE field_variable='$original_course_id_name' AND field_value='$original_course_id_value'";
         $res = Database::query($sql_course);
         $row = Database::fetch_object($res);
-        if($row != false) {
-            return $row->id;
+        if ($row != false) {
+            return $row->course_code;
         } else {
             return 0;
         }

+ 7 - 3
main/inc/lib/course_home.lib.php

@@ -600,10 +600,14 @@ class CourseHome
             }
         }
         $web_code_path = api_get_path(WEB_CODE_PATH);
-        $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
+        $session_id = api_get_session_id();
         $is_platform_admin = api_is_platform_admin();
 
-        $session_id = api_get_session_id();
+        if ($session_id == 0 ) {
+            $is_allowed_to_edit = api_is_allowed_to_edit(null, true) && api_is_course_admin();
+        } else {
+            $is_allowed_to_edit = api_is_allowed_to_edit(null, true) && !api_is_coach();
+        }
 
         $i = 0;
         $items = array();
@@ -641,7 +645,7 @@ class CourseHome
                 unset($lnk);
 
                 $item['extra'] = null;
-                if ($is_allowed_to_edit && !api_is_coach()) {
+                if ($is_allowed_to_edit) {
 
                     if (empty($session_id)) {
                         if ($tool['visibility'] == '1' && $tool['admin'] != '1') {

+ 6 - 1
main/inc/lib/formvalidator/FormValidator.class.php

@@ -135,6 +135,9 @@ class FormValidator extends HTML_QuickForm
         $this->registerElementType('style_reset_button', $dir . 'Element/style_reset_button.php', 'HTML_QuickForm_styleresetbutton');
         $this->registerElementType('button', $dir . 'Element/style_submit_button.php', 'HTML_QuickForm_stylesubmitbutton');
 
+        $this->registerElementType('captcha', 'HTML/QuickForm/CAPTCHA.php', 'HTML_QuickForm_CAPTCHA');
+        $this->registerElementType('CAPTCHA_Image', 'HTML/QuickForm/CAPTCHA/Image.php', 'HTML_QuickForm_CAPTCHA_Image');
+
         $this->registerRule('date', null, 'HTML_QuickForm_Rule_Date', $dir . 'Rule/Date.php');
         $this->registerRule('date_compare', null, 'HTML_QuickForm_Rule_DateCompare', $dir . 'Rule/DateCompare.php');
         $this->registerRule('html', null, 'HTML_QuickForm_Rule_HTML', $dir . 'Rule/HTML.php');
@@ -145,6 +148,8 @@ class FormValidator extends HTML_QuickForm
         $this->registerRule('url', null, 'HTML_QuickForm_Rule_Url', $dir . 'Rule/Url.php');
         $this->registerRule('compare_fields', null, 'HTML_QuickForm_Compare_Fields', $dir . 'Rule/CompareFields.php');
 
+        $this->registerRule('CAPTCHA', 'rule', 'HTML_QuickForm_Rule_CAPTCHA', 'HTML/QuickForm/Rule/CAPTCHA.php');
+
         // Modify the default templates
         $renderer = & $this->defaultRenderer();
 
@@ -574,4 +579,4 @@ function html_filter_teacher_fullpage($html)
 function html_filter_student_fullpage($html)
 {
     return html_filter($html, STUDENT_HTML_FULLPAGE);
-}
+}

+ 43 - 22
main/inc/lib/groupmanager.lib.php

@@ -18,7 +18,8 @@ require_once 'document.lib.php';
  * @todo Add $course_code parameter to all functions. So this GroupManager can
  * be used outside a session.
  */
-class GroupManager {
+class GroupManager
+{
     //- VIRTUAL_COURSE_CATEGORY: in this category groups are created based on the virtual  course of a course
     CONST VIRTUAL_COURSE_CATEGORY = 1;
 
@@ -58,10 +59,12 @@ class GroupManager {
     CONST GROUP_TOOL_CHAT = 6;
 
     //GROUP FUNCTIONS
-    private function __construct() {
+    private function __construct()
+    {
     }
 
-    public static function get_groups() {
+    public static function get_groups()
+    {
         $table_group         = Database :: get_course_table(TABLE_GROUP);
         $course_id          = api_get_course_int_id();
 
@@ -77,7 +80,8 @@ class GroupManager {
      * @param string $course_code Default is current course
      * @return array An array with all information about the groups.
      */
-    public static function get_group_list ($category = null, $course_code = null) {
+    public static function get_group_list ($category = null, $course_code = null)
+    {
         $my_user_id = api_get_user_id();
 
         $course_info         = api_get_course_info($course_code);
@@ -153,7 +157,6 @@ class GroupManager {
         }
 
         $groups = array();
-        $thisGroup= array();
         while ($thisGroup = Database::fetch_array($groupList)) {
             $thisGroup['number_of_members'] = count(self::get_subscribed_users($thisGroup['id']));
 
@@ -177,7 +180,8 @@ class GroupManager {
      * @param int $tutor The user-id of the group's tutor
      * @param int $places How many people can subscribe to the new group
      */
-    public static function create_group ($name, $category_id, $tutor, $places) {
+    public static function create_group ($name, $category_id, $tutor, $places)
+    {
         global $_course;
         $table_group = Database :: get_course_table(TABLE_GROUP);
 
@@ -751,9 +755,10 @@ class GroupManager {
     /**
      * Get all users from a given group
      * @param int $group_id The group
-         * @return array list of user id
+     * @return array list of user id
      */
-    public static function get_users($group_id, $load_extra_info = false) {
+    public static function get_users($group_id, $load_extra_info = false)
+    {
         $group_user_table = Database :: get_course_table(TABLE_GROUP_USER);
         $group_id = Database::escape_string($group_id);
         $course_id = api_get_course_int_id();
@@ -770,7 +775,12 @@ class GroupManager {
         return $users;
     }
 
-    public static function get_members_and_tutors($group_id) {
+    /**
+     * @param int $group_id
+     * @return array
+     */
+    public static function get_members_and_tutors($group_id)
+    {
         $group_user_table = Database :: get_course_table(TABLE_GROUP_USER);
         $tutor_user_table = Database :: get_course_table(TABLE_GROUP_TUTOR);
         $course_id = api_get_course_int_id();
@@ -792,12 +802,13 @@ class GroupManager {
     }
 
     /**
-        * Returns users belonging to any of the group
-        *
-        * @param array $groups list of group ids
-        * @return array list of user ids
-        */
-    public static function get_groups_users($groups = array()) {
+    * Returns users belonging to any of the group
+    *
+    * @param array $groups list of group ids
+    * @return array list of user ids
+    */
+    public static function get_groups_users($groups = array())
+    {
         $result = array();
         $tbl_group_user = Database::get_course_table(TABLE_GROUP_USER);
         $course_id = api_get_course_int_id();
@@ -837,7 +848,8 @@ class GroupManager {
      * @author Bart Mollet - code cleaning, use other GroupManager-functions
      * @return void
      */
-    public static function fill_groups ($group_ids) {
+    public static function fill_groups($group_ids)
+    {
         global $_course;
 
         $group_ids = is_array($group_ids) ? $group_ids : array ($group_ids);
@@ -944,7 +956,8 @@ class GroupManager {
      * @param int $group_id
      * @return int Number of students in the given group.
      */
-    public static function number_of_students ($group_id, $course_id = null) {
+    public static function number_of_students ($group_id, $course_id = null)
+    {
         $table_group_user = Database :: get_course_table(TABLE_GROUP_USER);
         $group_id = Database::escape_string($group_id);
         if (empty($course_id)) {
@@ -957,12 +970,14 @@ class GroupManager {
         $db_object = Database::fetch_object($db_result);
         return $db_object->number_of_students;
     }
+
     /**
      * Maximum number of students in a group
      * @param int $group_id
      * @return int Maximum number of students in the given group.
      */
-    public static function maximum_number_of_students ($group_id) {
+    public static function maximum_number_of_students ($group_id)
+    {
         $table_group = Database :: get_course_table(TABLE_GROUP);
         $group_id = Database::escape_string($group_id);
         $course_id = api_get_course_int_id();
@@ -973,12 +988,14 @@ class GroupManager {
         }
         return $db_object->max_student;
     }
+
     /**
      * Number of groups of a user
      * @param int $user_id
      * @return int The number of groups the user is subscribed in.
      */
-    public static function user_in_number_of_groups ($user_id, $cat_id = null) {
+    public static function user_in_number_of_groups ($user_id, $cat_id = null)
+    {
         $table_group_user     = Database :: get_course_table(TABLE_GROUP_USER);
         $table_group         = Database :: get_course_table(TABLE_GROUP);
         $user_id             = Database::escape_string($user_id);
@@ -1020,6 +1037,7 @@ class GroupManager {
             return false;
         }
     }
+
     /**
      * Is sef-unregistration allowed?
      * @param int $user_id
@@ -1036,6 +1054,7 @@ class GroupManager {
         $db_object = Database::fetch_object($db_result);
         return $db_object->self_unregistration_allowed == 1 && self :: can_user_unsubscribe($user_id, $group_id);
     }
+
     /**
      * Is user subscribed in group?
      * @param int $user_id
@@ -1052,6 +1071,7 @@ class GroupManager {
         $db_result = Database::query($sql);
         return Database::num_rows($db_result) > 0;
     }
+
     /**
      * Can a user subscribe to a specified group in a course
      * @param int $user_id
@@ -1091,7 +1111,8 @@ class GroupManager {
      * @return array An array with information of all users from the given group.
      *               (user_id, firstname, lastname, email)
      */
-    public static function get_subscribed_users($group_id) {
+    public static function get_subscribed_users($group_id)
+    {
         $table_user = Database :: get_main_table(TABLE_MAIN_USER);
         $table_group_user = Database :: get_course_table(TABLE_GROUP_USER);
         $order_clause = api_sort_by_first_name() ? ' ORDER BY u.firstname, u.lastname' : ' ORDER BY u.lastname, u.firstname';
@@ -1202,7 +1223,7 @@ class GroupManager {
      * Unsubscribe user(s) from a specified group in current course
      * @param mixed $user_ids Can be an array with user-id's or a single user-id
      * @param int $group_id
-     * @return bool TRUE if successfull
+     * @return bool TRUE if successful
      */
     public static function unsubscribe_users ($user_ids, $group_id) {
         $user_ids = is_array($user_ids) ? $user_ids : array ($user_ids);
@@ -1210,7 +1231,7 @@ class GroupManager {
         $group_id = Database::escape_string($group_id);
         $course_id = api_get_course_int_id();
 
-        $result &= Database::query('DELETE FROM '.$table_group_user.' WHERE c_id = '.$course_id.' AND group_id = '.$group_id.' AND user_id IN ('.implode(',', $user_ids).')');
+        Database::query('DELETE FROM '.$table_group_user.' WHERE c_id = '.$course_id.' AND group_id = '.$group_id.' AND user_id IN ('.implode(',', $user_ids).')');
     }
     /**
      * Unsubscribe all users from one or more groups

+ 315 - 0
main/inc/lib/javascript/strength/strength.js

@@ -0,0 +1,315 @@
+/*jslint vars: false, browser: true, nomen: true, regexp: true */
+/*global jQuery */
+
+/*
+* jQuery Password Strength plugin for Twitter Bootstrap
+*
+* Copyright (c) 2008-2013 Tane Piper
+* Copyright (c) 2013 Alejandro Blanco
+* Dual licensed under the MIT and GPL licenses.
+*
+*/
+
+(function ($) {
+    "use strict";
+
+    var options = {
+            errors: [],
+            // Options
+            minChar: 8,
+            errorMessages: {
+                password_to_short: "The Password is too short",
+                same_as_username: "Your password cannot be the same as your username"
+            },
+            scores: [17, 26, 40, 50],
+            verdicts: ["Weak", "Normal", "Medium", "Strong", "Very Strong"],
+            showVerdicts: true,
+            raisePower: 1.4,
+            usernameField: "#username",
+            onLoad: undefined,
+            onKeyUp: undefined,
+            viewports: {
+                progress: undefined,
+                verdict: undefined,
+                errors: undefined
+            },
+            // Rules stuff
+            ruleScores: {
+                wordNotEmail: -100,
+                wordLength: -100,
+                wordSimilarToUsername: -100,
+                wordLowercase: 1,
+                wordUppercase: 3,
+                wordOneNumber: 3,
+                wordThreeNumbers: 5,
+                wordOneSpecialChar: 3,
+                wordTwoSpecialChar: 5,
+                wordUpperLowerCombo: 2,
+                wordLetterNumberCombo: 2,
+                wordLetterNumberCharCombo: 2
+            },
+            rules: {
+                wordNotEmail: true,
+                wordLength: true,
+                wordSimilarToUsername: true,
+                wordLowercase: true,
+                wordUppercase: true,
+                wordOneNumber: true,
+                wordThreeNumbers: true,
+                wordOneSpecialChar: true,
+                wordTwoSpecialChar: true,
+                wordUpperLowerCombo: true,
+                wordLetterNumberCombo: true,
+                wordLetterNumberCharCombo: true
+            },
+            validationRules: {
+                wordNotEmail: function (options, word, score) {
+                    return word.match(/^([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*[\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,6})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)$/i) && score;
+                },
+                wordLength: function (options, word, score) {
+                    var wordlen = word.length,
+                        lenScore = Math.pow(wordlen, options.raisePower);
+                    if (wordlen < options.minChar) {
+                        lenScore = (lenScore + score);
+                        options.errors.push(options.errorMessages.password_to_short);
+                    }
+                    return lenScore;
+                },
+                wordSimilarToUsername: function (options, word, score) {
+                    var username = $(options.usernameField).val();
+                    if (username && word.toLowerCase().match(username.toLowerCase())) {
+                        options.errors.push(options.errorMessages.same_as_username);
+                        return score;
+                    }
+                    return true;
+                },
+                wordLowercase: function (options, word, score) {
+                    return word.match(/[a-z]/) && score;
+                },
+                wordUppercase: function (options, word, score) {
+                    return word.match(/[A-Z]/) && score;
+                },
+                wordOneNumber : function (options, word, score) {
+                    return word.match(/\d+/) && score;
+                },
+                wordThreeNumbers : function (options, word, score) {
+                    return word.match(/(.*[0-9].*[0-9].*[0-9])/) && score;
+                },
+                wordOneSpecialChar : function (options, word, score) {
+                    return word.match(/.[!,@,#,$,%,\^,&,*,?,_,~]/) && score;
+                },
+                wordTwoSpecialChar : function (options, word, score) {
+                    return word.match(/(.*[!,@,#,$,%,\^,&,*,?,_,~].*[!,@,#,$,%,\^,&,*,?,_,~])/) && score;
+                },
+                wordUpperLowerCombo : function (options, word, score) {
+                    return word.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/) && score;
+                },
+                wordLetterNumberCombo : function (options, word, score) {
+                    return word.match(/([a-zA-Z])/) && word.match(/([0-9])/) && score;
+                },
+                wordLetterNumberCharCombo : function (options, word, score) {
+                    return word.match(/([a-zA-Z0-9].*[!,@,#,$,%,\^,&,*,?,_,~])|([!,@,#,$,%,\^,&,*,?,_,~].*[a-zA-Z0-9])/) && score;
+                }
+            }
+        },
+
+        setProgressBar = function ($el, score) {
+            var options = $el.data("pwstrength"),
+                progressbar = options.progressbar,
+                $verdict;
+
+            if (options.showVerdicts) {
+                if (options.viewports.verdict) {
+                    $verdict = $(options.viewports.verdict).find(".password-verdict");
+                } else {
+                    $verdict = $el.parent().find(".password-verdict");
+                    if ($verdict.length === 0) {
+                        $verdict = $('<span class="password-verdict"></span>');
+                        $verdict.insertAfter($el);
+                    }
+                }
+            }
+
+            if (score < options.scores[0]) {
+                progressbar.addClass("progress-danger").removeClass("progress-warning").removeClass("progress-success");
+                progressbar.find(".bar").css("width", "5%");
+                if (options.showVerdicts) {
+                    $verdict.text(options.verdicts[0]);
+                }
+            } else if (score >= options.scores[0] && score < options.scores[1]) {
+                progressbar.addClass("progress-danger").removeClass("progress-warning").removeClass("progress-success");
+                progressbar.find(".bar").css("width", "25%");
+                if (options.showVerdicts) {
+                    $verdict.text(options.verdicts[1]);
+                }
+            } else if (score >= options.scores[1] && score < options.scores[2]) {
+                progressbar.addClass("progress-warning").removeClass("progress-danger").removeClass("progress-success");
+                progressbar.find(".bar").css("width", "50%");
+                if (options.showVerdicts) {
+                    $verdict.text(options.verdicts[2]);
+                }
+            } else if (score >= options.scores[2] && score < options.scores[3]) {
+                progressbar.addClass("progress-warning").removeClass("progress-danger").removeClass("progress-success");
+                progressbar.find(".bar").css("width", "75%");
+                if (options.showVerdicts) {
+                    $verdict.text(options.verdicts[3]);
+                }
+            } else if (score >= options.scores[3]) {
+                progressbar.addClass("progress-success").removeClass("progress-warning").removeClass("progress-danger");
+                progressbar.find(".bar").css("width", "100%");
+                if (options.showVerdicts) {
+                    $verdict.text(options.verdicts[4]);
+                }
+            }
+        },
+
+        calculateScore = function ($el) {
+            var self = this,
+                word = $el.val(),
+                totalScore = 0,
+                options = $el.data("pwstrength");
+
+            $.each(options.rules, function (rule, active) {
+                if (active === true) {
+                    var score = options.ruleScores[rule],
+                        result = options.validationRules[rule](options, word, score);
+                    if (result) {
+                        totalScore += result;
+                    }
+                }
+            });
+            setProgressBar($el, totalScore);
+            return totalScore;
+        },
+
+        progressWidget = function () {
+            return '<div class="progress"><div class="bar"></div></div>';
+        },
+
+        methods = {
+            init: function (settings) {
+                var self = this,
+                    allOptions = $.extend(options, settings);
+
+                return this.each(function (idx, el) {
+                    var $el = $(el),
+                        progressbar,
+                        verdict;
+
+                    $el.data("pwstrength", allOptions);
+
+                    $el.on("keyup", function (event) {
+                        var options = $el.data("pwstrength");
+                        options.errors = [];
+                        calculateScore.call(self, $el);
+                        if ($.isFunction(options.onKeyUp)) {
+                            options.onKeyUp(event);
+                        }
+                    });
+
+                    progressbar = $(progressWidget());
+                    if (allOptions.viewports.progress) {
+                        $(allOptions.viewports.progress).append(progressbar);
+                    } else {
+                        progressbar.insertAfter($el);
+                    }
+                    progressbar.find(".bar").css("width", "0%");
+                    $el.data("pwstrength").progressbar = progressbar;
+
+                    if (allOptions.showVerdicts) {
+                        verdict = $('<span class="password-verdict">' + allOptions.verdicts[0] + '</span>');
+                        if (allOptions.viewports.verdict) {
+                            $(allOptions.viewports.verdict).append(verdict);
+                        } else {
+                            verdict.insertAfter($el);
+                        }
+                    }
+
+                    if ($.isFunction(allOptions.onLoad)) {
+                        allOptions.onLoad();
+                    }
+                });
+            },
+
+            destroy: function () {
+                this.each(function (idx, el) {
+                    var $el = $(el);
+                    $el.parent().find("span.password-verdict").remove();
+                    $el.parent().find("div.progress").remove();
+                    $el.parent().find("ul.error-list").remove();
+                    $el.removeData("pwstrength");
+                });
+            },
+
+            forceUpdate: function () {
+                var self = this;
+                this.each(function (idx, el) {
+                    var $el = $(el),
+                        options = $el.data("pwstrength");
+                    options.errors = [];
+                    calculateScore.call(self, $el);
+                });
+            },
+
+            outputErrorList: function () {
+                this.each(function (idx, el) {
+                    var output = '<ul class="error-list">',
+                        $el = $(el),
+                        errors = $el.data("pwstrength").errors,
+                        viewports = $el.data("pwstrength").viewports,
+                        verdict;
+                    $el.parent().find("ul.error-list").remove();
+
+                    if (errors.length > 0) {
+                        $.each(errors, function (i, item) {
+                            output += '<li>' + item + '</li>';
+                        });
+                        output += '</ul>';
+                        if (viewports.errors) {
+                            $(viewports.errors).html(output);
+                        } else {
+                            output = $(output);
+                            verdict = $el.parent().find("span.password-verdict");
+                            if (verdict.length > 0) {
+                                el = verdict;
+                            }
+                            output.insertAfter(el);
+                        }
+                    }
+                });
+            },
+
+            addRule: function (name, method, score, active) {
+                this.each(function (idx, el) {
+                    var options = $(el).data("pwstrength");
+                    options.rules[name] = active;
+                    options.ruleScores[name] = score;
+                    options.validationRules[name] = method;
+                });
+            },
+
+            changeScore: function (rule, score) {
+                this.each(function (idx, el) {
+                    $(el).data("pwstrength").ruleScores[rule] = score;
+                });
+            },
+
+            ruleActive: function (rule, active) {
+                this.each(function (idx, el) {
+                    $(el).data("pwstrength").rules[rule] = active;
+                });
+            }
+        };
+
+    $.fn.pwstrength = function (method) {
+        var result;
+        if (methods[method]) {
+            result = methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
+        } else if (typeof method === "object" || !method) {
+            result = methods.init.apply(this, arguments);
+        } else {
+            $.error("Method " +  method + " does not exist on jQuery.pwstrength");
+        }
+        return result;
+    };
+}(jQuery));

+ 60 - 22
main/inc/lib/link.lib.php

@@ -20,17 +20,20 @@
 
 /* FUNCTIONS */
 
-class Link extends Model {
-    var $table;
-    var $is_course_model = true;
-    var $columns = array('id', 'c_id','url','title','description','category_id', 'display_order', 'on_homepage', 'target', 'session_id');
-    var $required = array('url', 'title');
-
-    public function __construct() {
+class Link extends Model
+{
+    public $table;
+    public $is_course_model = true;
+    public $columns = array('id', 'c_id','url','title','description','category_id', 'display_order', 'on_homepage', 'target', 'session_id');
+    public $required = array('url', 'title');
+
+    public function __construct()
+    {
         $this->table =  Database::get_course_table(TABLE_LINK);
 	}
 
-    public function save($params, $show_query = null) {
+    public function save($params, $show_query = null)
+    {
         $course_info = api_get_course_info();
         $params['session_id'] = api_get_session_id();
         $params['category_id'] = isset($params['category_id']) ? $params['category_id'] : 0;
@@ -50,7 +53,8 @@ class Link extends Model {
  * @todo replace strings by constants
  * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University
  */
-function addlinkcategory($type) {
+function addlinkcategory($type)
+{
 	global $catlinkstatus;
 	global $msgErr;
 
@@ -115,16 +119,16 @@ function addlinkcategory($type) {
 			$catlinkstatus = get_lang('LinkAdded');
 			Database :: query($sql);
 			$link_id = Database :: insert_id();
-            
+
             if ($link_id) {
-                api_set_default_visibility($link_id, TOOL_LINK);           
+                api_set_default_visibility($link_id, TOOL_LINK);
             }
 
 			if ((api_get_setting('search_enabled') == 'true') && $link_id && extension_loaded('xapian')) {
 				require_once api_get_path(LIBRARY_PATH) . 'search/ChamiloIndexer.class.php';
 				require_once api_get_path(LIBRARY_PATH) . 'search/IndexableChunk.class.php';
 				require_once api_get_path(LIBRARY_PATH) . 'specific_fields_manager.lib.php';
-                
+
                 $course_int_id = api_get_course_int_id();
 
 				$courseid = api_get_course_id();
@@ -236,7 +240,8 @@ function addlinkcategory($type) {
  * Used to delete a link or a category
  * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University
  */
-function deletelinkcategory($type) {
+function deletelinkcategory($type)
+{
 	global $catlinkstatus;
 	global $_course;
 	$tbl_link              = Database :: get_course_table(TABLE_LINK);
@@ -281,7 +286,8 @@ function deletelinkcategory($type) {
  * @param string $course_id Course code
  * @param int $document_id Document id to delete
  */
-function delete_link_from_search_engine($course_id, $link_id) {
+function delete_link_from_search_engine($course_id, $link_id)
+{
 	// Remove from search engine if enabled.
 	if (api_get_setting('search_enabled') == 'true') {
 		$tbl_se_ref = Database :: get_main_table(TABLE_MAIN_SEARCH_ENGINE_REF);
@@ -313,7 +319,8 @@ function delete_link_from_search_engine($course_id, $link_id) {
  *
  * */
 
-function get_link_info($id) {
+function get_link_info($id)
+{
      $tbl_link      = Database :: get_course_table(TABLE_LINK);
      $course_id     = api_get_course_int_id();
      $sql           = "SELECT * FROM " . $tbl_link . " WHERE c_id = $course_id AND id='" . intval($id) . "' ";
@@ -334,7 +341,8 @@ function get_link_info($id) {
  * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University
  * @todo replace the globals with the appropriate $_POST or $_GET values
  */
-function editlinkcategory($type) {
+function editlinkcategory($type)
+{
 
 	global $catlinkstatus;
 	global $id;
@@ -440,9 +448,9 @@ function editlinkcategory($type) {
 			// Update search enchine and its values table if enabled.
 			if (api_get_setting('search_enabled') == 'true') {
 				$link_id = intval($_POST['id']);
-                
+
                 $course_int_id = api_get_course_int_id();
-                
+
 				$course_id = api_get_course_id();
 				$link_url = Database :: escape_string($_POST['urllink']);
 				$link_title = Database :: escape_string($_POST['title']);
@@ -549,7 +557,7 @@ function editlinkcategory($type) {
 
 		// This is used to put the modified info of the category-form into the database.
 		if ($submit_category) {
-			$sql = "UPDATE " . $tbl_categories . " 
+			$sql = "UPDATE " . $tbl_categories . "
                     SET category_title='" . Database :: escape_string($_POST['category_title']) . "', description='" . Database :: escape_string($_POST['description']) . "'
 			        WHERE c_id = $course_id AND id='" . Database :: escape_string($_POST['id']) . "'";
 			Database :: query($sql);
@@ -923,7 +931,8 @@ function import_link($linkdata) {
  * CSV file import functions
  * @author René Haentjens , Ghent University
  */
-function import_csvfile() {
+function import_csvfile()
+{
 
 	global $catlinkstatus; // Feedback message to user.
 
@@ -976,18 +985,47 @@ function import_csvfile() {
 	}
 }
 
+/**
+ * This function checks if the url is a vimeo link
+ * @author Julio Montoya
+ * @version 1.0
+ */
+function isVimeoLink($url)
+{
+	$isLink = strrpos($url, "vimeo.com");
+    return $isLink;
+}
+
+function getVimeoLinkId($url)
+{
+    $possibleUrls = array(
+        'http://www.vimeo.com/',
+        'http://vimeo.com/',
+        'https://www.vimeo.com/',
+        'https://vimeo.com/'
+    );
+    $url = str_replace($possibleUrls, '', $url);
+
+    if (is_numeric($url)) {
+        return $url;
+    }
+    return false;
+}
+
 /**
  * This function checks if the url is a youtube link
  * @author Jorge Frisancho
  * @author Julio Montoya - Fixing code
  * @version 1.0
  */
-function is_youtube_link($url) {
+function is_youtube_link($url)
+{
 	$is_youtube_link = strrpos($url, "youtube") || strrpos($url, "youtu.be");
     return $is_youtube_link;
 }
 
-function get_youtube_video_id($url) {
+function get_youtube_video_id($url)
+{
     // This is the length of YouTube's video IDs
 	$len = 11;
 

+ 100 - 0
main/inc/lib/main_api.lib.php

@@ -6451,3 +6451,103 @@ function api_get_user_info_from_official_code($official_code = '') {
     }
     return false;
 }
+
+/**
+ *
+ * @param string $inputId the jquery id example: #password
+ * @return string
+ */
+function api_get_password_checker_js($usernameInputId, $passwordInputid)
+{
+    global $_configuration;
+    $useStrengthPassChecker = isset($_configuration['allow_strength_pass_checker']) ? $_configuration['allow_strength_pass_checker'] : false;
+
+    if ($useStrengthPassChecker == false) {
+        return null;
+    }
+
+    $verdicts = array(get_lang('PasswordWeak'), get_lang('PasswordNormal'), get_lang('PasswordMedium'), get_lang('PasswordStrong'), get_lang('PasswordVeryStrong'));
+    $js = api_get_js('strength/strength.js');
+    $js .=  "<script>
+
+    var verdicts = ['".implode("','", $verdicts)."'];
+    var errorMessages = {
+        password_to_short : '".get_lang('PasswordIsTooShort')."',
+        same_as_username : '".get_lang('YourPasswordCannotBeTheSameAsYourUsername')."',
+    };
+
+    $(document).ready(function() {
+        var options = {
+            verdicts: verdicts,
+            onLoad : function () {
+                //$('#messages').text('Start typing password');
+            },
+            onKeyUp: function (evt) {
+                $(evt.target).pwstrength('outputErrorList');
+            },
+            errorMessages : errorMessages,
+            viewports: {
+                progress: '#password_progress',
+                //verdict: undefined,
+                //errors: undefined
+            },
+            usernameField: '$usernameInputId'
+        };
+        $('".$passwordInputid."').pwstrength(options);
+    });
+    </script>";
+    return $js;
+}
+
+/**
+ * Gets an array with "easy" passwords
+ * @return array
+ */
+function api_get_easy_password_list()
+{
+    $passwordList = array('123', '1234', '123456', 'admin', 'user', 'student', 'teacher');
+    $file = api_get_path(CONFIGURATION_PATH).'easy_password_list.php';
+    if (file_exists($file)) {
+        $passwordList = require_once $file;
+    }
+    return $passwordList;
+}
+
+/**
+ *
+* create an user extra field called 'captcha_blocked_until_date'
+ */
+function api_block_account_captcha($username)
+{
+    $userInfo = api_get_user_info_from_username($username);
+    if (empty($userInfo)) {
+        return false;
+    }
+    global $_configuration;
+    $minutesToBlock = isset($_configuration['captcha_time_to_block']) ? $_configuration['captcha_time_to_block'] : 10;
+    $time = time() + $minutesToBlock*60;
+    Usermanager::update_extra_field_value($userInfo['user_id'], 'captcha_blocked_until_date', api_get_utc_datetime($time));
+}
+
+function api_clean_account_captcha($username)
+{
+    $userInfo = api_get_user_info_from_username($username);
+    if (empty($userInfo)) {
+        return false;
+    }
+    Session::erase('loginFailedCount');
+    Usermanager::update_extra_field_value($userInfo['user_id'], 'captcha_blocked_until_date', null);
+}
+
+function api_get_user_blocked_by_captcha($username)
+{
+    $userInfo = api_get_user_info_from_username($username);
+    if (empty($userInfo)) {
+        return false;
+    }
+    $data = UserManager::get_extra_user_data_by_field($userInfo['user_id'], 'captcha_blocked_until_date');
+    if (isset($data)) {
+        return $data['captcha_blocked_until_date'];
+    }
+    return false;
+}

+ 388 - 0
main/inc/lib/pear/Exception.php

@@ -0,0 +1,388 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
+/**
+ * PEAR_Exception
+ *
+ * PHP version 5
+ *
+ * @category   pear
+ * @package    PEAR_Exception
+ * @author     Tomas V. V. Cox <cox@idecnet.com>
+ * @author     Hans Lellelid <hans@velum.net>
+ * @author     Bertrand Mansion <bmansion@mamasam.com>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @link       http://pear.php.net/package/PEAR_Exception
+ * @since      File available since Release 1.0.0
+ */
+
+
+/**
+ * Base PEAR_Exception Class
+ *
+ * 1) Features:
+ *
+ * - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception))
+ * - Definable triggers, shot when exceptions occur
+ * - Pretty and informative error messages
+ * - Added more context info available (like class, method or cause)
+ * - cause can be a PEAR_Exception or an array of mixed
+ *   PEAR_Exceptions/PEAR_ErrorStack warnings
+ * - callbacks for specific exception classes and their children
+ *
+ * 2) Ideas:
+ *
+ * - Maybe a way to define a 'template' for the output
+ *
+ * 3) Inherited properties from PHP Exception Class:
+ *
+ * protected $message
+ * protected $code
+ * protected $line
+ * protected $file
+ * private   $trace
+ *
+ * 4) Inherited methods from PHP Exception Class:
+ *
+ * __clone
+ * __construct
+ * getMessage
+ * getCode
+ * getFile
+ * getLine
+ * getTraceSafe
+ * getTraceSafeAsString
+ * __toString
+ *
+ * 5) Usage example
+ *
+ * <code>
+ *  require_once 'PEAR/Exception.php';
+ *
+ *  class Test {
+ *     function foo() {
+ *         throw new PEAR_Exception('Error Message', ERROR_CODE);
+ *     }
+ *  }
+ *
+ *  function myLogger($pear_exception) {
+ *     echo $pear_exception->getMessage();
+ *  }
+ *  // each time a exception is thrown the 'myLogger' will be called
+ *  // (its use is completely optional)
+ *  PEAR_Exception::addObserver('myLogger');
+ *  $test = new Test;
+ *  try {
+ *     $test->foo();
+ *  } catch (PEAR_Exception $e) {
+ *     print $e;
+ *  }
+ * </code>
+ *
+ * @category   pear
+ * @package    PEAR_Exception
+ * @author     Tomas V.V.Cox <cox@idecnet.com>
+ * @author     Hans Lellelid <hans@velum.net>
+ * @author     Bertrand Mansion <bmansion@mamasam.com>
+ * @author     Greg Beaver <cellog@php.net>
+ * @copyright  1997-2009 The Authors
+ * @license    http://opensource.org/licenses/bsd-license.php New BSD License
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/PEAR_Exception
+ * @since      Class available since Release 1.0.0
+ *
+ */
+class PEAR_Exception extends Exception
+{
+    const OBSERVER_PRINT = -2;
+    const OBSERVER_TRIGGER = -4;
+    const OBSERVER_DIE = -8;
+    protected $cause;
+    private static $_observers = array();
+    private static $_uniqueid = 0;
+    private $_trace;
+
+    /**
+     * Supported signatures:
+     *  - PEAR_Exception(string $message);
+     *  - PEAR_Exception(string $message, int $code);
+     *  - PEAR_Exception(string $message, Exception $cause);
+     *  - PEAR_Exception(string $message, Exception $cause, int $code);
+     *  - PEAR_Exception(string $message, PEAR_Error $cause);
+     *  - PEAR_Exception(string $message, PEAR_Error $cause, int $code);
+     *  - PEAR_Exception(string $message, array $causes);
+     *  - PEAR_Exception(string $message, array $causes, int $code);
+     * @param string exception message
+     * @param int|Exception|PEAR_Error|array|null exception cause
+     * @param int|null exception code or null
+     */
+    public function __construct($message, $p2 = null, $p3 = null)
+    {
+        if (is_int($p2)) {
+            $code = $p2;
+            $this->cause = null;
+        } elseif (is_object($p2) || is_array($p2)) {
+            // using is_object allows both Exception and PEAR_Error
+            if (is_object($p2) && !($p2 instanceof Exception)) {
+                if (!class_exists('PEAR_Error') || !($p2 instanceof PEAR_Error)) {
+                    throw new PEAR_Exception('exception cause must be Exception, ' .
+                        'array, or PEAR_Error');
+                }
+            }
+            $code = $p3;
+            if (is_array($p2) && isset($p2['message'])) {
+                // fix potential problem of passing in a single warning
+                $p2 = array($p2);
+            }
+            $this->cause = $p2;
+        } else {
+            $code = null;
+            $this->cause = null;
+        }
+        parent::__construct($message, $code);
+        $this->signal();
+    }
+
+    /**
+     * @param mixed $callback  - A valid php callback, see php func is_callable()
+     *                         - A PEAR_Exception::OBSERVER_* constant
+     *                         - An array(const PEAR_Exception::OBSERVER_*,
+     *                           mixed $options)
+     * @param string $label    The name of the observer. Use this if you want
+     *                         to remove it later with removeObserver()
+     */
+    public static function addObserver($callback, $label = 'default')
+    {
+        self::$_observers[$label] = $callback;
+    }
+
+    public static function removeObserver($label = 'default')
+    {
+        unset(self::$_observers[$label]);
+    }
+
+    /**
+     * @return int unique identifier for an observer
+     */
+    public static function getUniqueId()
+    {
+        return self::$_uniqueid++;
+    }
+
+    private function signal()
+    {
+        foreach (self::$_observers as $func) {
+            if (is_callable($func)) {
+                call_user_func($func, $this);
+                continue;
+            }
+            settype($func, 'array');
+            switch ($func[0]) {
+                case self::OBSERVER_PRINT :
+                    $f = (isset($func[1])) ? $func[1] : '%s';
+                    printf($f, $this->getMessage());
+                    break;
+                case self::OBSERVER_TRIGGER :
+                    $f = (isset($func[1])) ? $func[1] : E_USER_NOTICE;
+                    trigger_error($this->getMessage(), $f);
+                    break;
+                case self::OBSERVER_DIE :
+                    $f = (isset($func[1])) ? $func[1] : '%s';
+                    die(printf($f, $this->getMessage()));
+                    break;
+                default:
+                    trigger_error('invalid observer type', E_USER_WARNING);
+            }
+        }
+    }
+
+    /**
+     * Return specific error information that can be used for more detailed
+     * error messages or translation.
+     *
+     * This method may be overridden in child exception classes in order
+     * to add functionality not present in PEAR_Exception and is a placeholder
+     * to define API
+     *
+     * The returned array must be an associative array of parameter => value like so:
+     * <pre>
+     * array('name' => $name, 'context' => array(...))
+     * </pre>
+     * @return array
+     */
+    public function getErrorData()
+    {
+        return array();
+    }
+
+    /**
+     * Returns the exception that caused this exception to be thrown
+     * @access public
+     * @return Exception|array The context of the exception
+     */
+    public function getCause()
+    {
+        return $this->cause;
+    }
+
+    /**
+     * Function must be public to call on caused exceptions
+     * @param array
+     */
+    public function getCauseMessage(&$causes)
+    {
+        $trace = $this->getTraceSafe();
+        $cause = array('class'   => get_class($this),
+                       'message' => $this->message,
+                       'file' => 'unknown',
+                       'line' => 'unknown');
+        if (isset($trace[0])) {
+            if (isset($trace[0]['file'])) {
+                $cause['file'] = $trace[0]['file'];
+                $cause['line'] = $trace[0]['line'];
+            }
+        }
+        $causes[] = $cause;
+        if ($this->cause instanceof PEAR_Exception) {
+            $this->cause->getCauseMessage($causes);
+        } elseif ($this->cause instanceof Exception) {
+            $causes[] = array('class'   => get_class($this->cause),
+                              'message' => $this->cause->getMessage(),
+                              'file' => $this->cause->getFile(),
+                              'line' => $this->cause->getLine());
+        } elseif (class_exists('PEAR_Error') && $this->cause instanceof PEAR_Error) {
+            $causes[] = array('class' => get_class($this->cause),
+                              'message' => $this->cause->getMessage(),
+                              'file' => 'unknown',
+                              'line' => 'unknown');
+        } elseif (is_array($this->cause)) {
+            foreach ($this->cause as $cause) {
+                if ($cause instanceof PEAR_Exception) {
+                    $cause->getCauseMessage($causes);
+                } elseif ($cause instanceof Exception) {
+                    $causes[] = array('class'   => get_class($cause),
+                                   'message' => $cause->getMessage(),
+                                   'file' => $cause->getFile(),
+                                   'line' => $cause->getLine());
+                } elseif (class_exists('PEAR_Error') && $cause instanceof PEAR_Error) {
+                    $causes[] = array('class' => get_class($cause),
+                                      'message' => $cause->getMessage(),
+                                      'file' => 'unknown',
+                                      'line' => 'unknown');
+                } elseif (is_array($cause) && isset($cause['message'])) {
+                    // PEAR_ErrorStack warning
+                    $causes[] = array(
+                        'class' => $cause['package'],
+                        'message' => $cause['message'],
+                        'file' => isset($cause['context']['file']) ?
+                                            $cause['context']['file'] :
+                                            'unknown',
+                        'line' => isset($cause['context']['line']) ?
+                                            $cause['context']['line'] :
+                                            'unknown',
+                    );
+                }
+            }
+        }
+    }
+
+    public function getTraceSafe()
+    {
+        if (!isset($this->_trace)) {
+            $this->_trace = $this->getTrace();
+            if (empty($this->_trace)) {
+                $backtrace = debug_backtrace();
+                $this->_trace = array($backtrace[count($backtrace)-1]);
+            }
+        }
+        return $this->_trace;
+    }
+
+    public function getErrorClass()
+    {
+        $trace = $this->getTraceSafe();
+        return $trace[0]['class'];
+    }
+
+    public function getErrorMethod()
+    {
+        $trace = $this->getTraceSafe();
+        return $trace[0]['function'];
+    }
+
+    public function __toString()
+    {
+        if (isset($_SERVER['REQUEST_URI'])) {
+            return $this->toHtml();
+        }
+        return $this->toText();
+    }
+
+    public function toHtml()
+    {
+        $trace = $this->getTraceSafe();
+        $causes = array();
+        $this->getCauseMessage($causes);
+        $html =  '<table style="border: 1px" cellspacing="0">' . "\n";
+        foreach ($causes as $i => $cause) {
+            $html .= '<tr><td colspan="3" style="background: #ff9999">'
+               . str_repeat('-', $i) . ' <b>' . $cause['class'] . '</b>: '
+               . htmlspecialchars($cause['message']) . ' in <b>' . $cause['file'] . '</b> '
+               . 'on line <b>' . $cause['line'] . '</b>'
+               . "</td></tr>\n";
+        }
+        $html .= '<tr><td colspan="3" style="background-color: #aaaaaa; text-align: center; font-weight: bold;">Exception trace</td></tr>' . "\n"
+               . '<tr><td style="text-align: center; background: #cccccc; width:20px; font-weight: bold;">#</td>'
+               . '<td style="text-align: center; background: #cccccc; font-weight: bold;">Function</td>'
+               . '<td style="text-align: center; background: #cccccc; font-weight: bold;">Location</td></tr>' . "\n";
+
+        foreach ($trace as $k => $v) {
+            $html .= '<tr><td style="text-align: center;">' . $k . '</td>'
+                   . '<td>';
+            if (!empty($v['class'])) {
+                $html .= $v['class'] . $v['type'];
+            }
+            $html .= $v['function'];
+            $args = array();
+            if (!empty($v['args'])) {
+                foreach ($v['args'] as $arg) {
+                    if (is_null($arg)) $args[] = 'null';
+                    elseif (is_array($arg)) $args[] = 'Array';
+                    elseif (is_object($arg)) $args[] = 'Object('.get_class($arg).')';
+                    elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false';
+                    elseif (is_int($arg) || is_double($arg)) $args[] = $arg;
+                    else {
+                        $arg = (string)$arg;
+                        $str = htmlspecialchars(substr($arg, 0, 16));
+                        if (strlen($arg) > 16) $str .= '&hellip;';
+                        $args[] = "'" . $str . "'";
+                    }
+                }
+            }
+            $html .= '(' . implode(', ',$args) . ')'
+                   . '</td>'
+                   . '<td>' . (isset($v['file']) ? $v['file'] : 'unknown')
+                   . ':' . (isset($v['line']) ? $v['line'] : 'unknown')
+                   . '</td></tr>' . "\n";
+        }
+        $html .= '<tr><td style="text-align: center;">' . ($k+1) . '</td>'
+               . '<td>{main}</td>'
+               . '<td>&nbsp;</td></tr>' . "\n"
+               . '</table>';
+        return $html;
+    }
+
+    public function toText()
+    {
+        $causes = array();
+        $this->getCauseMessage($causes);
+        $causeMsg = '';
+        foreach ($causes as $i => $cause) {
+            $causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': '
+                   . $cause['message'] . ' in ' . $cause['file']
+                   . ' on line ' . $cause['line'] . "\n";
+        }
+        return $causeMsg . $this->getTraceAsString();
+    }
+}

+ 41 - 39
main/inc/lib/pear/HTML/QuickForm.php

@@ -39,35 +39,34 @@ require_once 'HTML/Common.php';
  * @global array $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES']
  */
 $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'] =
-        array(
-            'group'             =>array('HTML/QuickForm/group.php','HTML_QuickForm_group'),
-            'hidden'            =>array('HTML/QuickForm/hidden.php','HTML_QuickForm_hidden'),
-            'reset'             =>array('HTML/QuickForm/reset.php','HTML_QuickForm_reset'),
-            'checkbox'          =>array('HTML/QuickForm/checkbox.php','HTML_QuickForm_checkbox'),
-            'file'              =>array('HTML/QuickForm/file.php','HTML_QuickForm_file'),
-            'image'             =>array('HTML/QuickForm/image.php','HTML_QuickForm_image'),
-            'password'          =>array('HTML/QuickForm/password.php','HTML_QuickForm_password'),
-            'radio'             =>array('HTML/QuickForm/radio.php','HTML_QuickForm_radio'),
-            'button'            =>array('HTML/QuickForm/button.php','HTML_QuickForm_button'),
-            'submit'            =>array('HTML/QuickForm/submit.php','HTML_QuickForm_submit'),
-            'select'            =>array('HTML/QuickForm/select.php','HTML_QuickForm_select'),
-            'hiddenselect'      =>array('HTML/QuickForm/hiddenselect.php','HTML_QuickForm_hiddenselect'),
-            'text'              =>array('HTML/QuickForm/text.php','HTML_QuickForm_text'),
-            'textarea'          =>array('HTML/QuickForm/textarea.php','HTML_QuickForm_textarea'),
-            'link'              =>array('HTML/QuickForm/link.php','HTML_QuickForm_link'),
-            'advcheckbox'       =>array('HTML/QuickForm/advcheckbox.php','HTML_QuickForm_advcheckbox'),
-            'date'              =>array('HTML/QuickForm/date.php','HTML_QuickForm_date'),
-            'static'            =>array('HTML/QuickForm/static.php','HTML_QuickForm_static'),
-            'header'            =>array('HTML/QuickForm/header.php', 'HTML_QuickForm_header'),
-            'html'              =>array('HTML/QuickForm/html.php', 'HTML_QuickForm_html'),
-            'hierselect'        =>array('HTML/QuickForm/hierselect.php', 'HTML_QuickForm_hierselect'),
-            'autocomplete'      =>array('HTML/QuickForm/autocomplete.php', 'HTML_QuickForm_autocomplete'),
-            'xbutton'           =>array('HTML/QuickForm/xbutton.php','HTML_QuickForm_xbutton'),
-            'advanced_settings' =>array('HTML/QuickForm/advanced_settings.php','HTML_QuickForm_advanced_settings'),
-            'label'             =>array('HTML/QuickForm/label.php','HTML_QuickForm_label'),
-            'email'             =>array('HTML/QuickForm/email.php','HTML_QuickForm_email'),
-            
-        );
+array(
+    'group'             => array('HTML/QuickForm/group.php','HTML_QuickForm_group'),
+    'hidden'            => array('HTML/QuickForm/hidden.php','HTML_QuickForm_hidden'),
+    'reset'             => array('HTML/QuickForm/reset.php','HTML_QuickForm_reset'),
+    'checkbox'          => array('HTML/QuickForm/checkbox.php','HTML_QuickForm_checkbox'),
+    'file'              => array('HTML/QuickForm/file.php','HTML_QuickForm_file'),
+    'image'             => array('HTML/QuickForm/image.php','HTML_QuickForm_image'),
+    'password'          => array('HTML/QuickForm/password.php','HTML_QuickForm_password'),
+    'radio'             => array('HTML/QuickForm/radio.php','HTML_QuickForm_radio'),
+    'button'            => array('HTML/QuickForm/button.php','HTML_QuickForm_button'),
+    'submit'            => array('HTML/QuickForm/submit.php','HTML_QuickForm_submit'),
+    'select'            => array('HTML/QuickForm/select.php','HTML_QuickForm_select'),
+    'hiddenselect'      => array('HTML/QuickForm/hiddenselect.php','HTML_QuickForm_hiddenselect'),
+    'text'              => array('HTML/QuickForm/text.php','HTML_QuickForm_text'),
+    'textarea'          => array('HTML/QuickForm/textarea.php','HTML_QuickForm_textarea'),
+    'link'              => array('HTML/QuickForm/link.php','HTML_QuickForm_link'),
+    'advcheckbox'       => array('HTML/QuickForm/advcheckbox.php','HTML_QuickForm_advcheckbox'),
+    'date'              => array('HTML/QuickForm/date.php','HTML_QuickForm_date'),
+    'static'            => array('HTML/QuickForm/static.php','HTML_QuickForm_static'),
+    'header'            => array('HTML/QuickForm/header.php', 'HTML_QuickForm_header'),
+    'html'              => array('HTML/QuickForm/html.php', 'HTML_QuickForm_html'),
+    'hierselect'        => array('HTML/QuickForm/hierselect.php', 'HTML_QuickForm_hierselect'),
+    'autocomplete'      => array('HTML/QuickForm/autocomplete.php', 'HTML_QuickForm_autocomplete'),
+    'xbutton'           => array('HTML/QuickForm/xbutton.php','HTML_QuickForm_xbutton'),
+    'advanced_settings' => array('HTML/QuickForm/advanced_settings.php','HTML_QuickForm_advanced_settings'),
+    'label'             => array('HTML/QuickForm/label.php','HTML_QuickForm_label'),
+    'email'             => array('HTML/QuickForm/email.php','HTML_QuickForm_email')
+);
 
 /**
  * Validation rules known to HTML_QuickForm
@@ -92,7 +91,8 @@ $GLOBALS['_HTML_QuickForm_registered_rules'] = array(
     //'compare'       => array('html_quickform_rule_compare',  'HTML/QuickForm/Rule/Compare.php')
     'compare'       => array('html_quickform_rule_compare',  'HTML/QuickForm/Rule/Compare.php'),
     'comparedate'   => array('html_quickform_rule_comparedate',  'HTML/QuickForm/Rule/CompareDate.php'),
-    'errordate'  	=> array('html_quickform_rule_date',  'HTML/QuickForm/Rule/Date.php')
+    'errordate'  	=> array('html_quickform_rule_date',  'HTML/QuickForm/Rule/Date.php'),
+    'captcha'  	    => array('HTML_QuickForm_Rule_CAPTCHA',  'HTML/QuickForm/Rule/CAPTCHA.php')
     //
 );
 
@@ -327,7 +327,7 @@ class HTML_QuickForm extends HTML_Common
             unset($this->_submitValues['_qf__' . $formName]);
             $this->addElement('hidden', '_qf__' . $formName, null);
         }
-        
+
         if (preg_match('/^([0-9]+)([a-zA-Z]*)$/', ini_get('upload_max_filesize'), $matches)) {
             // see http://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
             switch (strtoupper($matches['2'])) {
@@ -344,10 +344,10 @@ class HTML_QuickForm extends HTML_Common
                     $this->_maxFileSize = $matches['1'];
             }
         }
-        
+
         $course_id = api_get_course_int_id();
         //If I'm in a course replace the default max filesize with the course limits
-        if (!empty($course_id)) {            
+        if (!empty($course_id)) {
             $free_course_quota = DocumentManager::get_course_quota() - DocumentManager::documents_total_space();
             if (empty($this->_maxFileSize) || $free_course_quota <= $this->_maxFileSize) {
                 $this->_maxFileSize = intval($free_course_quota);
@@ -618,11 +618,12 @@ class HTML_QuickForm extends HTML_Common
         }
         $className = $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type][1];
         $includeFile = $GLOBALS['HTML_QUICKFORM_ELEMENT_TYPES'][$type][0];
-        include_once($includeFile);
+
+        include_once $includeFile;
         // Modified by Ivan Tcholakov, 16-MAR-2010. Suppressing a deprecation warning on PHP 5.3
         //$elementObject =& new $className();
         $elementObject = new $className();
-        
+
         for ($i = 0; $i < 5; $i++) {
             if (!isset($args[$i])) {
                 $args[$i] = null;
@@ -1548,6 +1549,7 @@ class HTML_QuickForm extends HTML_Common
         foreach ($this->_rules as $target => $rules) {
             $submitValue = $this->getSubmitValue($target);
 
+
             foreach ($rules as $rule) {
                 if ((isset($rule['group']) && isset($this->_errors[$rule['group']])) ||
                      isset($this->_errors[$target])) {
@@ -1733,7 +1735,7 @@ class HTML_QuickForm extends HTML_Common
             // Modified by Ivan Tcholakov, 16-MAR-2010. Suppressing a deprecation warning on PHP 5.3
             //$GLOBALS['_HTML_QuickForm_default_renderer'] =& new HTML_QuickForm_Renderer_Default();
             $GLOBALS['_HTML_QuickForm_default_renderer'] = new HTML_QuickForm_Renderer_Default();
-        }        
+        }
         return $GLOBALS['_HTML_QuickForm_default_renderer'];
     } // end func defaultRenderer
 
@@ -1750,11 +1752,11 @@ class HTML_QuickForm extends HTML_Common
      * @since     1.0
      * @access   public
      */
-    function toHtml ($in_data = null) {         
+    function toHtml ($in_data = null) {
         if (!is_null($in_data)) {
             $this->addElement('html', $in_data);
         }
-        $renderer =& $this->defaultRenderer();             
+        $renderer =& $this->defaultRenderer();
         $this->accept($renderer);
         return $renderer->toHtml();
     } // end func toHtml
@@ -2091,4 +2093,4 @@ class HTML_QuickForm_Error extends PEAR_Error {
     }
     // }}}
 } // end class HTML_QuickForm_Error
-?>
+?>

+ 268 - 0
main/inc/lib/pear/HTML/QuickForm/CAPTCHA.php

@@ -0,0 +1,268 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+
+/**
+ * Common class for HTML_QuickForm elements to display a CAPTCHA
+ *
+ * The HTML_QuickForm_CAPTCHA package adds an element to the
+ * HTML_QuickForm package to display a CAPTCHA question (image, riddle, etc...)
+ *
+ * This package requires the use of a PHP session ($_SESSION).
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE:
+ *
+ * Copyright (c) 2006-2008, Philippe Jausions / 11abacus
+ *
+ * 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 11abacus 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.
+ *
+ * @category  HTML
+ * @package   HTML_QuickForm_CAPTCHA
+ * @author    Philippe Jausions <Philippe.Jausions@11abacus.com>
+ * @copyright 2006-2008 by Philippe Jausions / 11abacus
+ * @license   http://www.opensource.org/licenses/bsd-license.php New BSD
+ * @version   CVS: $Id: CAPTCHA.php,v 1.1 2008/04/26 23:27:28 jausions Exp $
+ * @link      http://pear.php.net/package/HTML_QuickForm_CAPTCHA
+ */
+
+/**
+ * Required packages
+ */
+require_once 'HTML/QuickForm/input.php';
+require_once 'Text/CAPTCHA.php';
+
+/**
+ * Common class for HTML_QuickForm elements to display a CAPTCHA
+ *
+ * The HTML_QuickForm_CAPTCHA package adds an element to the
+ * HTML_QuickForm package to display a CAPTCHA question (image, riddle, etc...)
+ *
+ * This package requires the use of a PHP session ($_SESSION).
+ *
+ * Because the CAPTCHA element is serialized in the PHP session,
+ * you need to include the class declaration BEFORE the session starts.
+ * So BEWARE if you have php.ini session.auto_start enabled, you won't be
+ * able to use this element, unless you're also using PHP 5's __autoload()
+ * or php.ini's unserialize_callback_func setting
+ *
+ * @category  HTML
+ * @package   HTML_QuickForm_CAPTCHA
+ * @author    Philippe Jausions <Philippe.Jausions@11abacus.com>
+ * @copyright 2006-2008 by Philippe Jausions / 11abacus
+ * @license   http://www.opensource.org/licenses/bsd-license.php New BSD
+ * @version   Release: 0.3.0
+ * @link      http://pear.php.net/package/HTML_QuickForm_CAPTCHA
+ * @abstract
+ */
+class HTML_QuickForm_CAPTCHA extends HTML_QuickForm_input
+{
+    /**
+     * Default options
+     *
+     * @var array
+     * @access protected
+     */
+    var $_options = array(
+            'sessionVar' => '_HTML_QuickForm_CAPTCHA',
+            'phrase'     => null,
+            );
+
+    /**
+     * CAPTCHA driver
+     *
+     * @var string
+     * @access protected
+     */
+    var $_CAPTCHA_driver;
+
+    /**
+     * Class constructor
+     *
+     * @param string $elementName  Name
+     * @param mixed  $elementLabel Label for the CAPTCHA
+     * @param array  $options      Options for the Text_CAPTCHA package
+     * <ul>
+     *  <li>'sessionVar' (string)  name of session variable containing
+     *                             the Text_CAPTCHA instance (defaults to
+     *                             _HTML_QuickForm_CAPTCHA.)</li>
+     *  <li>Other options depend on the driver used</li>
+     * </ul>
+     * @param mixed  $attributes   HTML Attributes for the <a> tag surrounding
+     *                             the image. Can be a string or array.
+     *
+     * @access public
+     */
+    function HTML_QuickForm_CAPTCHA($elementName = null, $elementLabel = null,
+                                    $options = null, $attributes = null)
+    {
+        HTML_QuickForm_input::HTML_QuickForm_input($elementName, $elementLabel, $attributes);
+        $this->setType('CAPTCHA_'.$this->_CAPTCHA_driver);
+
+        if (is_array($options)) {
+            $this->_options = array_merge($this->_options, $options);
+        }
+    }
+
+    /**
+     * Initializes the CAPTCHA instance (if needed)
+     *
+     * @return boolean TRUE or PEAR_Error on error
+     * @access protected
+     */
+    function _initCAPTCHA()
+    {
+        $sessionVar = $this->_options['sessionVar'];
+
+        if (empty($_SESSION[$sessionVar])) {
+
+            $_SESSION[$sessionVar] = Text_CAPTCHA::factory($this->_CAPTCHA_driver);
+
+            if (PEAR::isError($_SESSION[$sessionVar])) {
+                return $_SESSION[$sessionVar];
+            }
+            $result = $_SESSION[$sessionVar]->init($this->_options);
+            if (PEAR::isError($result)) {
+                return $result;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns the answer/phrase of the CAPTCHA
+     *
+     * @param mixed &$values Ignored by this element
+     *
+     * @return string
+     * @access private
+     */
+    function _findValue(&$values)
+    {
+        return $this->getValue();
+    }
+
+    /**
+     * Returns the answer/phrase of the CAPTCHA
+     *
+     * @return string
+     * @access public
+     */
+    function getValue()
+    {
+        $sessionVar = $this->_options['sessionVar'];
+
+        return (!empty($_SESSION[$sessionVar]))
+                 ? $_SESSION[$sessionVar]->getPhrase()
+                 : null;
+    }
+
+    /**
+     * Returns the answer/phrase of the CAPTCHA
+     *
+     * @param mixed   &$submitValues Ignored by this element
+     * @param boolean $assoc         Whether to return an array
+     *
+     * @return string
+     * @access public
+     */
+    function exportValue(&$submitValues, $assoc = false)
+    {
+        return ($assoc)
+               ? array($this->getName() => $this->getValue())
+               : $this->getValue();
+    }
+
+    /**
+     * Sets the CAPTCHA question/phrase
+     *
+     * Pass NULL or no argument for a random question/phrase to be generated
+     *
+     * @param string $phrase Value of the CAPTCHA to set
+     *
+     * @return void
+     * @access public
+     */
+    function setPhrase($phrase = null)
+    {
+        $this->_options['phrase'] = $phrase;
+
+        if (!empty($_SESSION[$this->_options['sessionVar']])) {
+            $_SESSION[$this->_options['sessionVar']]->setPhrase($phrase);
+        }
+    }
+
+    /**
+     * Destroys the CAPTCHA instance to prevent reuse
+     *
+     * @return void
+     * @access public
+     */
+    function destroy()
+    {
+        unset($_SESSION[$this->_options['sessionVar']]);
+    }
+
+    /**
+     * Returns the HTML for the CAPTCHA
+     *
+     * This can be overwritten by sub-classes for specific output behavior
+     * (for instance the Image CAPTCHA displays an image)
+     *
+     * @return string
+     * @access public
+     */
+    function toHtml()
+    {
+        $result = $this->_initCAPTCHA();
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        $captcha = $_SESSION[$this->_options['sessionVar']]->getCAPTCHA();
+
+        $attr = $this->_attributes;
+        unset($attr['type']);
+        unset($attr['value']);
+        unset($attr['name']);
+
+        $html = $this->_getTabs()
+                . '<span' . $this->_getAttrString($attr) . '>'
+                . htmlspecialchars($captcha)
+                . '</span>';
+        return $html;
+    }
+}
+
+/**
+ * Register the rule with QuickForm
+ */
+require_once 'HTML/QuickForm/Rule/CAPTCHA.php';
+
+?>

+ 93 - 0
main/inc/lib/pear/HTML/QuickForm/CAPTCHA/Equation.php

@@ -0,0 +1,93 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+
+/**
+ * Element for HTML_QuickForm to display a CAPTCHA equation
+ *
+ * The HTML_QuickForm_CAPTCHA package adds an element to the
+ * HTML_QuickForm package to display a CAPTCHA equation.
+ *
+ * This package requires the use of a PHP session.
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  HTML
+ * @package   HTML_QuickForm_CAPTCHA
+ * @author    Philippe Jausions <Philippe.Jausions@11abacus.com>
+ * @copyright 2006-2008 by Philippe Jausions / 11abacus
+ * @license   http://www.opensource.org/licenses/bsd-license.php New BSD
+ * @version   CVS: $Id: Equation.php,v 1.1 2008/04/26 23:27:30 jausions Exp $
+ * @link      http://pear.php.net/package/HTML_QuickForm_CAPTCHA
+ */
+
+/**
+ * Required packages
+ */
+require_once 'HTML/QuickForm/CAPTCHA.php';
+require_once 'Text/CAPTCHA/Driver/Equation.php';
+
+/**
+ * Element for HTML_QuickForm to display a CAPTCHA equation question
+ *
+ * The HTML_QuickForm_CAPTCHA package adds an element to the
+ * HTML_QuickForm package to display a CAPTCHA equation question.
+ *
+ * Options for the element
+ * <ul>
+ *  <li>'min'           (integer)  Minimal number to use in an equation.</li>
+ *  <li>'max'           (integer)  Maximal number to use in an equation.</li>
+ *  <li>'severity'      (integer)  Complexity of the equation to resolve
+ *                                 (1 = easy, 2 = harder)</li>
+ *  <li>'numbersToText' (boolean)  Whether to use the Numbers_Words
+ *                                 package to convert numbers to text,</li>
+ *  <li>'sessionVar'    (string)   name of session variable containing
+ *                                 the Text_CAPTCHA instance (defaults to
+ *                                 _HTML_QuickForm_CAPTCHA.)</li>
+ * </ul>
+ *
+ * This package requires the use of a PHP session.
+ *
+ * @category  HTML
+ * @package   HTML_QuickForm_CAPTCHA
+ * @author    Philippe Jausions <Philippe.Jausions@11abacus.com>
+ * @copyright 2006-2008 by Philippe Jausions / 11abacus
+ * @license   http://www.opensource.org/licenses/bsd-license.php New BSD
+ * @version   Release: 0.3.0
+ * @link      http://pear.php.net/package/HTML_QuickForm_CAPTCHA
+ * @see       Text_CAPTCHA_Driver_Equation
+ */
+class HTML_QuickForm_CAPTCHA_Equation extends HTML_QuickForm_CAPTCHA
+{
+    /**
+     * Default options
+     *
+     * @var    array
+     * @access protected
+     */
+    var $_options = array(
+            'sessionVar'    => '_HTML_QuickForm_CAPTCHA',
+            'severity'      => 1,
+            'numbersToText' => false,
+            'phrase'        => null,
+            );
+
+    /**
+     * CAPTCHA driver
+     *
+     * @var    string
+     * @access protected
+     */
+    var $_CAPTCHA_driver = 'Equation';
+}
+
+/**
+ * Registers the class with QuickForm
+ */
+if (class_exists('HTML_QuickForm')) {
+    HTML_QuickForm::registerElementType('CAPTCHA_Equation',
+            'HTML/QuickForm/CAPTCHA/Equation.php',
+            'HTML_QuickForm_CAPTCHA_Equation');
+}
+
+?>

+ 126 - 0
main/inc/lib/pear/HTML/QuickForm/CAPTCHA/Figlet.php

@@ -0,0 +1,126 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+
+/**
+ * Element for HTML_QuickForm to display a CAPTCHA figlet
+ *
+ * The HTML_QuickForm_CAPTCHA package adds an element to the
+ * HTML_QuickForm package to display a CAPTCHA figlet.
+ *
+ * This package requires the use of a PHP session.
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  HTML
+ * @package   HTML_QuickForm_CAPTCHA
+ * @author    Philippe Jausions <Philippe.Jausions@11abacus.com>
+ * @copyright 2006-2008 by Philippe Jausions / 11abacus
+ * @license   http://www.opensource.org/licenses/bsd-license.php New BSD
+ * @version   CVS: $Id: Figlet.php,v 1.1 2008/04/26 23:27:30 jausions Exp $
+ * @link      http://pear.php.net/package/HTML_QuickForm_CAPTCHA
+ */
+
+/**
+ * Required packages
+ */
+require_once 'HTML/QuickForm/CAPTCHA.php';
+require_once 'Text/CAPTCHA/Driver/Figlet.php';
+
+/**
+ * Element for HTML_QuickForm to display a CAPTCHA figlet
+ *
+ * The HTML_QuickForm_CAPTCHA package adds an element to the
+ * HTML_QuickForm package to display a CAPTCHA figlet
+ *
+ * Options for the element
+ * <ul>
+ *  <li>'width'      (integer)  Width of figlet (default is 200px)</li>
+ *  <li>'output'     (string)   Output format: "html", "text" or
+ *                              "javascript" (default is "html").</li>
+ *  <li>'length'     (integer)  number of letters in the figlet
+ *                              (default is 6)</li>
+ *  <li>'options'    (array)    only index supported is "font_file", which
+ *                              should either be one figlet font file path,
+ *                              or an array of figlet font file paths
+ *                              (one we be picked randomly)</li>
+ *  <li>'sessionVar' (string)   name of session variable containing
+ *                              the Text_CAPTCHA instance (defaults to
+ *                              _HTML_QuickForm_CAPTCHA.)</li>
+ * </ul>
+ *
+ * This package requires the use of a PHP session.
+ *
+ * @category  HTML
+ * @package   HTML_QuickForm_CAPTCHA
+ * @author    Philippe Jausions <Philippe.Jausions@11abacus.com>
+ * @copyright 2006-2008 by Philippe Jausions / 11abacus
+ * @license   http://www.opensource.org/licenses/bsd-license.php New BSD
+ * @version   Release: 0.3.0
+ * @link      http://pear.php.net/package/HTML_QuickForm_CAPTCHA
+ * @see       Text_CAPTCHA_Driver_Figlet
+ */
+class HTML_QuickForm_CAPTCHA_Figlet extends HTML_QuickForm_CAPTCHA
+{
+    /**
+     * Default options
+     *
+     * @var    array
+     * @access protected
+     */
+    var $_options = array(
+            'sessionVar'    => '_HTML_QuickForm_CAPTCHA',
+            'output'        => 'html',
+            'width'         => 200,
+            'length'        => 6,
+            'phrase'        => null,
+            );
+
+    /**
+     * CAPTCHA driver
+     *
+     * @var    string
+     * @access protected
+     */
+    var $_CAPTCHA_driver = 'Figlet';
+
+
+    /**
+     * Returns the HTML for the CAPTCHA
+     *
+     * This can be overwritten by sub-classes for specific output behavior
+     * (for instance the Image CAPTCHA displays an image)
+     *
+     * @access public
+     * @return string
+     */
+    function toHtml()
+    {
+        $result = $this->_initCAPTCHA();
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        $attr = $this->_attributes;
+        unset($attr['type']);
+        unset($attr['value']);
+        unset($attr['name']);
+
+        $html = $this->_getTabs()
+                . '<div' . $this->_getAttrString($attr) . '>'
+                . $_SESSION[$this->_options['sessionVar']]->getCAPTCHA()
+                . '</div>';
+        return $html;
+    }
+}
+
+/**
+ * Registers the class with QuickForm
+ */
+if (class_exists('HTML_QuickForm')) {
+    HTML_QuickForm::registerElementType('CAPTCHA_Figlet',
+            'HTML/QuickForm/CAPTCHA/Figlet.php',
+            'HTML_QuickForm_CAPTCHA_Figlet');
+}
+
+?>

+ 160 - 0
main/inc/lib/pear/HTML/QuickForm/CAPTCHA/Image.php

@@ -0,0 +1,160 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+
+/**
+ * Element for HTML_QuickForm to display a CAPTCHA image
+ *
+ * The HTML_QuickForm_CAPTCHA package adds an element to the
+ * HTML_QuickForm package to display a CAPTCHA image.
+ *
+ * This package requires the use of a PHP session.
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  HTML
+ * @package   HTML_QuickForm_CAPTCHA
+ * @author    Philippe Jausions <Philippe.Jausions@11abacus.com>
+ * @copyright 2006-2008 by Philippe Jausions / 11abacus
+ * @license   http://www.opensource.org/licenses/bsd-license.php New BSD
+ * @version   CVS: $Id: Image.php,v 1.1 2008/04/26 23:27:30 jausions Exp $
+ * @link      http://pear.php.net/package/HTML_QuickForm_CAPTCHA
+ */
+
+/**
+ * Required packages
+ */
+require_once 'HTML/QuickForm/CAPTCHA.php';
+require_once 'Text/CAPTCHA/Driver/Image.php';
+
+/**
+ * Element for HTML_QuickForm to display a CAPTCHA image
+ *
+ * The HTML_QuickForm_CAPTCHA package adds an element to the
+ * HTML_QuickForm package to display a CAPTCHA image.
+ *
+ * Options for the element
+ * <ul>
+ *  <li>'width'        (integer) width of the image,</li>
+ *  <li>'height'       (integer) height of the image,</li>
+ *  <li>'imageOptions' (array)   options passed to the Image_Text
+ *                               constructor,</li>
+ *  <li>'callback'     (string)  URL of callback script that will generate
+ *                               and output the image itself,</li>
+ *  <li>'alt'          (string)  the alt text for the image,</li>
+ *  <li>'sessionVar'   (string)  name of session variable containing
+ *                               the Text_CAPTCHA instance (defaults to
+ *                               _HTML_QuickForm_CAPTCHA.)</li>
+ * </ul>
+ *
+ * This package requires the use of a PHP session.
+ *
+ * @category  HTML
+ * @package   HTML_QuickForm_CAPTCHA
+ * @author    Philippe Jausions <Philippe.Jausions@11abacus.com>
+ * @copyright 2006-2008 by Philippe Jausions / 11abacus
+ * @license   http://www.opensource.org/licenses/bsd-license.php New BSD
+ * @version   Release: 0.3.0
+ * @link      http://pear.php.net/package/HTML_QuickForm_CAPTCHA
+ * @see       Text_CAPTCHA_Driver_Image
+ */
+class HTML_QuickForm_CAPTCHA_Image extends HTML_QuickForm_CAPTCHA
+{
+
+    function HTML_QuickForm_CAPTCHA_Image($elementName = null, $elementLabel = null, $options = null, $attributes = null) {
+        return parent::HTML_QuickForm_CAPTCHA($elementName, $elementLabel, $options, $attributes);
+    }
+
+    /**
+     * Default options
+     *
+     * @var    array
+     * @access protected
+     */
+    var $_options = array(
+            'sessionVar'   => '_HTML_QuickForm_CAPTCHA',
+            'width'        => '200',
+            'height'       => '80',
+            'alt'          => 'Click to view another image',
+            'callback'     => '',
+            'imageOptions' => null,
+            'phrase'       => null,
+    );
+
+    /**
+     * CAPTCHA driver
+     *
+     * @var    string
+     * @access protected
+     */
+    var $_CAPTCHA_driver = 'Image';
+
+    /**
+     * Returns the HTML for the CAPTCHA image
+     *
+     * @return string
+     * @access public
+     */
+    function toHtml()
+    {
+        if ($this->_flagFrozen) {
+            return '';
+        }
+
+        $result = parent::_initCAPTCHA();
+
+        if (PEAR::isError($result)) {
+            return $result;
+        }
+
+        $html      = '';
+        $tabs      = $this->_getTabs();
+        $inputName = $this->getName();
+        $imgName   = 'QF_CAPTCHA_'.$inputName;
+
+        if ($this->getComment() != '') {
+            $html .= $tabs.'<!-- '.$this->getComment().' // -->';
+        }
+
+        $attr = $this->_attributes;
+        unset($attr['type']);
+        unset($attr['value']);
+        unset($attr['name']);
+
+        $html = $tabs.'<a href="'.$this->_options['callback']
+               .'" target="_blank" '
+               .$this->_getAttrString($attr)
+               .' onclick="var cancelClick = false; '
+               .$this->getOnclickJs($imgName)
+               .' return !cancelClick;"><img src="'
+               .$this->_options['callback'].'" name="'.$imgName
+               .'" id="'.$imgName.'" width="'.$this->_options['width']
+               .'" height="'.$this->_options['height'].'" title="'
+               .htmlspecialchars($this->_options['alt']).'" /></a>';
+
+        return $html;
+    }
+
+    /**
+     * Creates the javascript for the onclick event which will
+     * reload a new CAPTCHA image
+     *
+     * @param string $imageName The image name/id
+     *
+     * @return string
+     * @access public
+     */
+    function getOnclickJs($imageName)
+    {
+        $onclickJs = ''
+            .'if (document.images) {'
+            .'  var img = new Image();'
+            .'  var d = new Date();'
+            .'  img.src = this.href + ((this.href.indexOf(\'?\') == -1) '
+                                     .'? \'?\' : \'&\') + d.getTime();'
+            .'  document.images[\''.addslashes($imageName).'\'].src = img.src;'
+            .'  cancelClick = true;'
+            .'}';
+        return $onclickJs;
+    }
+}

+ 90 - 0
main/inc/lib/pear/HTML/QuickForm/CAPTCHA/Word.php

@@ -0,0 +1,90 @@
+<?php
+
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+
+/**
+ * Element for HTML_QuickForm to display a CAPTCHA "Word"
+ *
+ * The HTML_QuickForm_CAPTCHA package adds an element to the
+ * HTML_QuickForm package to display a CAPTCHA "word".
+ *
+ * This package requires the use of a PHP session.
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  HTML
+ * @package   HTML_QuickForm_CAPTCHA
+ * @author    Philippe Jausions <Philippe.Jausions@11abacus.com>
+ * @copyright 2006-2008 by Philippe Jausions / 11abacus
+ * @license   http://www.opensource.org/licenses/bsd-license.php New BSD
+ * @version   CVS: $Id: Word.php,v 1.1 2008/04/26 23:27:30 jausions Exp $
+ * @link      http://pear.php.net/package/HTML_QuickForm_CAPTCHA
+ */
+
+/**
+ * Required packages
+ */
+require_once 'HTML/QuickForm/CAPTCHA.php';
+require_once 'Text/CAPTCHA/Driver/Word.php';
+
+/**
+ * Element for HTML_QuickForm to display a CAPTCHA "word" question
+ *
+ * The HTML_QuickForm_CAPTCHA package adds an element to the
+ * HTML_QuickForm package to display a CAPTCHA "word" question.
+ *
+ * Options for the element
+ * <ul>
+ *  <li>'length'     (integer)  the length of the Word.</li>
+ *  <li>'mode'       (string)   'single' for separated words.</li>
+ *  <li>'locale'     (string)   locale for Numbers_Words package</li>
+ *  <li>'sessionVar' (string)   name of session variable containing
+ *                              the Text_CAPTCHA instance (defaults to
+ *                              _HTML_QuickForm_CAPTCHA.)</li>
+ * </ul>
+ *
+ * This package requires the use of a PHP session.
+ *
+ * @category  HTML
+ * @package   HTML_QuickForm_CAPTCHA
+ * @author    Philippe Jausions <Philippe.Jausions@11abacus.com>
+ * @copyright 2006-2008 by Philippe Jausions / 11abacus
+ * @license   http://www.opensource.org/licenses/bsd-license.php New BSD
+ * @version   Release: 0.3.0
+ * @link      http://pear.php.net/package/HTML_QuickForm_CAPTCHA
+ * @see       Text_CAPTCHA_Driver_Equation
+ */
+class HTML_QuickForm_CAPTCHA_Word extends HTML_QuickForm_CAPTCHA
+{
+    /**
+     * Default options
+     *
+     * @var    array
+     * @access protected
+     */
+    var $_options = array(
+            'sessionVar' => '_HTML_QuickForm_CAPTCHA',
+            'length'     => 4,
+            'mode'       => 'single',
+            'locale'     => 'en_US',
+            'phrase'     => null,
+            );
+
+    /**
+     * CAPTCHA driver
+     *
+     * @var    string
+     * @access protected
+     */
+    var $_CAPTCHA_driver = 'Word';
+}
+
+/**
+ * Registers the class with QuickForm
+ */
+if (class_exists('HTML_QuickForm')) {
+    HTML_QuickForm::registerElementType('CAPTCHA_Word',
+            'HTML/QuickForm/CAPTCHA/Word.php', 'HTML_QuickForm_CAPTCHA_Word');
+}
+
+?>

+ 50 - 0
main/inc/lib/pear/HTML/QuickForm/Rule/CAPTCHA.php

@@ -0,0 +1,50 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4: */
+
+/**
+ * Rule for HTML_QuickForm to display a CAPTCHA image
+ *
+ * This package requires the use of a PHP session.
+ *
+ * PHP versions 4 and 5
+ *
+ * @category  HTML
+ * @package   HTML_QuickForm_CAPTCHA
+ * @author    Philippe Jausions <Philippe.Jausions@11abacus.com>
+ * @copyright 2006-2008 by Philippe Jausions / 11abacus
+ * @license   http://www.opensource.org/licenses/bsd-license.php New BSD
+ * @version   CVS: $Id: CAPTCHA.php,v 1.1 2008/04/26 23:27:30 jausions Exp $
+ * @link      http://pear.php.net/package/HTML_QuickForm_CAPTCHA
+ */
+
+require_once 'HTML/QuickForm/Rule.php';
+
+/**
+ * Rule to compare a field with a CAPTCHA image
+ *
+ * @category  HTML
+ * @package   HTML_QuickForm_CAPTCHA
+ * @author    Philippe Jausions <Philippe.Jausions@11abacus.com>
+ * @copyright 2006-2008 by Philippe Jausions / 11abacus
+ * @license   http://www.opensource.org/licenses/bsd-license.php New BSD
+ * @version   Release: 0.3.0
+ * @link      http://pear.php.net/package/HTML_QuickForm_CAPTCHA
+ */
+class HTML_QuickForm_Rule_CAPTCHA extends HTML_QuickForm_Rule
+{
+    /**
+     * Validates the data entered matches the CAPTCHA image that was
+     * displayed
+     *
+     * @param string                        $value   data to validate
+     * @param HTML_QuickForm_CAPTCHA_Common $captcha element to check against
+     *
+     * @return boolean TRUE if valid, FALSE otherwise
+     * @access public
+     * @static
+     */
+    function validate($value, $captcha)
+    {
+        return ($value == $captcha->getValue());
+    }
+}

+ 1183 - 0
main/inc/lib/pear/Image/Text.php

@@ -0,0 +1,1183 @@
+<?php
+/**
+ * Image_Text.
+ *
+ * This is the main file of the Image_Text package. This file has to be included for
+ * usage of Image_Text.
+ *
+ * This is a simple example script, showing Image_Text's facilities.
+ *
+ * PHP version 5
+ *
+ * @category  Image
+ * @package   Image_Text
+ * @author    Tobias Schlitt <toby@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license   http://www.php.net/license/3_01.txt PHP License
+ * @version   CVS: $Id$
+ * @link      http://pear.php.net/package/Image_Text
+ * @since     File available since Release 0.0.1
+ */
+require_once 'Image/Text/Exception.php';
+/**
+ * Image_Text - Advanced text manipulations in images.
+ *
+ * Image_Text provides advanced text manipulation facilities for GD2 image generation
+ * with PHP. Simply add text clippings to your images, let the class automatically
+ * determine lines, rotate text boxes around their center or top left corner. These
+ * are only a couple of features Image_Text provides.
+ *
+ * @category  Image
+ * @package   Image_Text
+ * @author    Tobias Schlitt <toby@php.net>
+ * @copyright 1997-2005 The PHP Group
+ * @license   http://www.php.net/license/3_01.txt PHP License
+ * @version   Release: @package_version@
+ * @link      http://pear.php.net/package/Image_Text
+ * @since     0.0.1
+ */
+class Image_Text
+{
+    /**
+     * Regex to match HTML style hex triples.
+     */
+    const IMAGE_TEXT_REGEX_HTMLCOLOR
+        = "/^[#|]([a-f0-9]{2})?([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i";
+
+    /**
+     * Defines horizontal alignment to the left of the text box. (This is standard.)
+     */
+    const IMAGE_TEXT_ALIGN_LEFT = "left";
+
+    /**
+     * Defines horizontal alignment to the center of the text box.
+     */
+    const IMAGE_TEXT_ALIGN_RIGHT = "right";
+
+    /**
+     * Defines horizontal alignment to the center of the text box.
+     */
+    const IMAGE_TEXT_ALIGN_CENTER = "center";
+
+    /**
+     * Defines vertical alignment to the to the top of the text box. (This is
+     * standard.)
+     */
+    const IMAGE_TEXT_ALIGN_TOP = "top";
+
+    /**
+     * Defines vertical alignment to the to the middle of the text box.
+     */
+    const IMAGE_TEXT_ALIGN_MIDDLE = "middle";
+
+    /**
+     * Defines vertical alignment to the to the bottom of the text box.
+     */
+    const IMAGE_TEXT_ALIGN_BOTTOM = "bottom";
+
+    /**
+     * TODO: This constant is useless until now, since justified alignment does not
+     * work yet
+     */
+    const IMAGE_TEXT_ALIGN_JUSTIFY = "justify";
+
+    /**
+     * Options array. these options can be set through the constructor or the set()
+     * method.
+     *
+     * Possible options to set are:
+     * <pre>
+     *   'x'                | This sets the top left coordinates (using x/y) or the
+     *   'y'                | center point coordinates (using cx/cy) for your text
+     *   'cx'               | box. The values from cx/cy will overwrite x/y.
+     *   'cy'               |
+     *
+     *   'canvas'           | You can set different values as a canvas:
+     *                      |   - A gd image resource.
+     *                      |   - An array with 'width' and 'height'.
+     *                      |   - Nothing (the canvas will be measured after the real
+     *                      |     text size).
+     *
+     *   'antialias'        | This is usually true. Set it to false to switch
+     *                      | antialiasing off.
+     *
+     *   'width'            | The width and height for your text box.
+     *   'height'           |
+     *
+     *   'halign'           | Alignment of your text inside the textbox. Use
+     *   'valign'           | alignment constants to define vertical and horizontal
+     *                      | alignment.
+     *
+     *   'angle'            | The angle to rotate your text box.
+     *
+     *   'color'            | An array of color values. Colors will be rotated in the
+     *                      | mode you choose (linewise or paragraphwise). Can be in
+     *                      | the following formats:
+     *                      |   - String representing HTML style hex couples
+     *                      |     (+ unusual alpha couple in the first place,
+     *                      |      optional).
+     *                      |   - Array of int values using 'r', 'g', 'b' and
+     *                      |     optionally 'a' as keys.
+     *
+     *   'color_mode'       | The color rotation mode for your color sets. Does only
+     *                      | apply if you defined multiple colors. Use 'line' or
+     *                      | 'paragraph'.
+     *
+     *   'background_color' | defines the background color. NULL sets it transparent
+     *   'enable_alpha'     | if alpha channel should be enabled. Automatically
+     *                      | enabled when background_color is set to NULL
+     *
+     *   'font_path'        | Location of the font to use. The path only gives the
+     *                      | directory path (ending with a /).
+     *   'font_file'        | The fontfile is given in the 'font_file' option.
+     *
+     *   'font_size'        | The font size to render text in (will be overwriten, if
+     *                      | you use automeasurize).
+     *
+     *   'line_spacing'     | Measure for the line spacing to use. Default is 0.5.
+     *
+     *   'min_font_size'    | Automeasurize settings. Try to keep this area as small
+     *   'max_font_size'    | as possible to get better performance.
+     *
+     *   'image_type'       | The type of image (use image type constants). Is
+     *                      | default set to PNG.
+     *
+     *   'dest_file'        | The destination to (optionally) save your file.
+     * </pre>
+     *
+     * @var array
+     * @see Image_Text::set()
+     */
+
+    private $_options = array(
+        // orientation
+        'x' => 0,
+        'y' => 0,
+
+        // surface
+        'canvas' => null,
+        'antialias' => true,
+
+        // text clipping
+        'width' => 0,
+        'height' => 0,
+
+        // text alignment inside the clipping
+        'halign' => self::IMAGE_TEXT_ALIGN_LEFT,
+        'valign' => self::IMAGE_TEXT_ALIGN_TOP,
+
+        // angle to rotate the text clipping
+        'angle' => 0,
+
+        // color settings
+        'color' => array('#000000'),
+
+        'color_mode' => 'line',
+
+        'background_color' => '#000000',
+        'enable_alpha' => false,
+
+        // font settings
+        'font_path' => "./",
+        'font_file' => null,
+        'font_size' => 2,
+        'line_spacing' => 0.5,
+
+        // automasurizing settings
+        'min_font_size' => 1,
+        'max_font_size' => 100,
+
+        //max. lines to render
+        'max_lines' => 100,
+
+        // misc settings
+        'image_type' => IMAGETYPE_PNG,
+        'dest_file' => ''
+    );
+
+    /**
+     * Contains option names, which can cause re-initialization force.
+     *
+     * @var array
+     */
+    private $_reInits = array(
+        'width', 'height', 'canvas', 'angle', 'font_file', 'font_path', 'font_size'
+    );
+
+    /**
+     * The text you want to render.
+     *
+     * @var string
+     */
+    private $_text;
+
+    /**
+     * Resource ID of the image canvas.
+     *
+     * @var resource
+     */
+    private $_img;
+
+    /**
+     * Tokens (each word).
+     *
+     * @var array
+     */
+    private $_tokens = array();
+
+    /**
+     * Fullpath to the font.
+     *
+     * @var string
+     */
+    private $_font;
+
+    /**
+     * Contains the bbox of each rendered lines.
+     *
+     * @var array
+     */
+    private $_bbox = array();
+
+    /**
+     * Defines in which mode the canvas has be set.
+     *
+     * @var array
+     */
+    private $_mode = '';
+
+    /**
+     * Color indices returned by imagecolorallocatealpha.
+     *
+     * @var array
+     */
+    private $_colors = array();
+
+    /**
+     * Width and height of the (rendered) text.
+     *
+     * @var array
+     */
+    private $_realTextSize = array('width' => false, 'height' => false);
+
+    /**
+     * Measurized lines.
+     *
+     * @var array
+     */
+    private $_lines = false;
+
+    /**
+     * Fontsize for which the last measuring process was done.
+     *
+     * @var array
+     */
+    private $_measurizedSize = false;
+
+    /**
+     * Is the text object initialized?
+     *
+     * @var bool
+     */
+    private $_init = false;
+
+    /**
+     * Constructor
+     *
+     * Set the text and options. This initializes a new Image_Text object. You must
+     * set your text here. Optionally you can set all options here using the $options
+     * parameter. If you finished switching all options you have to call the init()
+     * method first before doing anything further! See Image_Text::set() for further
+     * information.
+     *
+     * @param string $text    Text to print.
+     * @param array  $options Options.
+     *
+     * @see Image_Text::set(), Image_Text::construct(), Image_Text::init()
+     */
+    public function __construct($text, $options = null)
+    {
+        $this->set('text', $text);
+        if (!empty($options)) {
+            $this->_options = array_merge($this->_options, $options);
+        }
+    }
+
+    /**
+     * Construct and initialize an Image_Text in one step.
+     * This method is called statically and creates plus initializes an Image_Text
+     * object. Beware: You will have to recall init() if you set an option afterwards
+     * manually.
+     *
+     * @param string $text    Text to print.
+     * @param array  $options Options.
+     *
+     * @return Image_Text
+     * @see Image_Text::set(), Image_Text::Image_Text(), Image_Text::init()
+     */
+    public static function construct($text, $options)
+    {
+        $itext = new Image_Text($text, $options);
+        $itext->init();
+        return $itext;
+    }
+
+    /**
+     * Set options
+     *
+     * Set a single or multiple options. It may happen that you have to reinitialize
+     * the Image_Text object after changing options. For possible options, please
+     * take a look at the class options array!
+     *
+     * @param array|string $option A single option name or the options array.
+     * @param mixed        $value  Option value if $option is string.
+     *
+     * @return void
+     * @see    Image_Text::Image_Text()
+     * @throws Image_Text_Exception setting the value failed
+     */
+    public function set($option, $value = null)
+    {
+        $reInits = array_flip($this->_reInits);
+        if (!is_array($option)) {
+            if (!isset($value)) {
+                throw new Image_Text_Exception('No value given.');
+            }
+            $option = array($option => $value);
+        }
+        foreach ($option as $opt => $val) {
+            switch ($opt) {
+            case 'color':
+                $this->setColors($val);
+                break;
+            case 'text':
+                if (is_array($val)) {
+                    $this->_text = implode('\n', $val);
+                } else {
+                    $this->_text = $val;
+                }
+                break;
+            default:
+                $this->_options[$opt] = $val;
+                break;
+            }
+            if (isset($reInits[$opt])) {
+                $this->_init = false;
+            }
+        }
+    }
+
+    /**
+     * Set the color-set
+     *
+     * Using this method you can set multiple colors for your text. Use a simple
+     * numeric array to determine their order and give it to this function. Multiple
+     * colors will be cycled by the options specified 'color_mode' option. The given
+     * array will overwrite the existing color settings!
+     *
+     * The following colors syntaxes are understood by this method:
+     * <ul>
+     * <li>"#ffff00" hexadecimal format (HTML style), with and without #.</li>
+     * <li>"#08ffff00" hexadecimal format (HTML style) with alpha channel (08),
+     * with and without #.</li>
+     * <li>array with 'r','g','b' and (optionally) 'a' keys, using int values.</li>
+     * </ul>
+     * A single color or an array of colors are allowed here.
+     *
+     * @param array|string $colors Single color or array of colors.
+     *
+     * @return void
+     * @see Image_Text::setColor(), Image_Text::set()
+     * @throws Image_Text_Exception
+     */
+    public function setColors($colors)
+    {
+        $i = 0;
+        if (is_array($colors) && (is_string($colors[0]) || is_array($colors[0]))) {
+            foreach ($colors as $color) {
+                $this->setColor($color, $i++);
+            }
+        } else {
+            $this->setColor($colors, $i);
+        }
+    }
+
+    /**
+     * Set a color
+     *
+     * This method is used to set a color at a specific color ID inside the color
+     * cycle.
+     *
+     * The following colors syntaxes are understood by this method:
+     * <ul>
+     * <li>"#ffff00" hexadecimal format (HTML style), with and without #.</li>
+     * <li>"#08ffff00" hexadecimal format (HTML style) with alpha channel (08), with
+     * and without #.</li>
+     * <li>array with 'r','g','b' and (optionally) 'a' keys, using int values.</li>
+     * </ul>
+     *
+     * @param array|string $color Color value.
+     * @param int          $id    ID (in the color array) to set color to.
+     *
+     * @return void
+     * @see Image_Text::setColors(), Image_Text::set()
+     * @throws Image_Text_Exception
+     */
+
+    function setColor($color, $id = 0)
+    {
+        if (is_array($color)) {
+            if (isset($color['r']) && isset($color['g']) && isset($color['b'])) {
+                $color['a'] = isset($color['a']) ? $color['a'] : 0;
+                $this->_options['colors'][$id] = $color;
+            } else if (isset($color[0]) && isset($color[1]) && isset($color[2])) {
+                $color['r'] = $color[0];
+                $color['g'] = $color[1];
+                $color['b'] = $color[2];
+                $color['a'] = isset($color[3]) ? $color[3] : 0;
+                $this->_options['colors'][$id] = $color;
+            } else {
+                throw new Image_Text_Exception(
+                    'Use keys 1,2,3 (optionally) 4 or r,g,b and (optionally) a.'
+                );
+            }
+        } elseif (is_string($color)) {
+            $color = $this->convertString2RGB($color);
+            if ($color) {
+                $this->_options['color'][$id] = $color;
+            } else {
+                throw new Image_Text_Exception('Invalid color.');
+            }
+        }
+        if ($this->_img) {
+            $aaFactor = ($this->_options['antialias']) ? 1 : -1;
+            if (function_exists('imagecolorallocatealpha') && isset($color['a'])) {
+                $this->_colors[$id] = $aaFactor *
+                    imagecolorallocatealpha(
+                        $this->_img,
+                        $color['r'], $color['g'], $color['b'], $color['a']
+                    );
+            } else {
+                $this->_colors[$id] = $aaFactor *
+                    imagecolorallocate(
+                        $this->_img,
+                        $color['r'], $color['g'], $color['b']
+                    );
+            }
+            if ($this->_colors[$id] == 0 && $aaFactor == -1) {
+                // correction for black with antialiasing OFF
+                // since color cannot be negative zero
+                $this->_colors[$id] = -256;
+            }
+        }
+    }
+
+    /**
+     * Initialize the Image_Text object.
+     *
+     * This method has to be called after setting the options for your Image_Text
+     * object. It initializes the canvas, normalizes some data and checks important
+     * options. Be sure to check the initialization after you switched some options.
+     * The set() method may force you to reinitialize the object.
+     *
+     * @return void
+     * @see Image_Text::set()
+     * @throws Image_Text_Exception
+     */
+    public function init()
+    {
+        // Does the fontfile exist and is readable?
+        $fontFile = rtrim($this->_options['font_path'], '/\\');
+        $fontFile .= defined('OS_WINDOWS') && OS_WINDOWS ? '\\' : '/';
+        $fontFile .= $this->_options['font_file'];
+        $fontFile = realpath($fontFile);
+
+        if (empty($fontFile)) {
+            throw new Image_Text_Exception('You must supply a font file.');
+        } elseif (!file_exists($fontFile)) {
+            throw new Image_Text_Exception('Font file was not found.');
+        } elseif (!is_readable($fontFile)) {
+            throw new Image_Text_Exception('Font file is not readable.');
+        } elseif (!imagettfbbox(1, 1, $fontFile, 1)) {
+            throw new Image_Text_Exception('Font file is not valid.');
+        }
+        $this->_font = $fontFile;
+
+        // Is the font size to small?
+        if ($this->_options['width'] < 1) {
+            throw new Image_Text_Exception('Width too small. Has to be > 1.');
+        }
+
+        // Check and create canvas
+        $image_canvas = false;
+        switch (true) {
+
+        case (empty($this->_options['canvas'])):
+            // Create new image from width && height of the clipping
+            $this->_img = imagecreatetruecolor(
+                $this->_options['width'], $this->_options['height']
+            );
+            if (!$this->_img) {
+                throw new Image_Text_Exception('Could not create image canvas.');
+            }
+            break;
+
+        case (is_resource($this->_options['canvas']) &&
+            get_resource_type($this->_options['canvas']) == 'gd'):
+            // The canvas is an image resource
+            $image_canvas = true;
+            $this->_img = $this->_options['canvas'];
+            break;
+
+        case (is_array($this->_options['canvas']) &&
+            isset($this->_options['canvas']['width']) &&
+            isset($this->_options['canvas']['height'])):
+
+            // Canvas must be a width and height measure
+            $this->_img = imagecreatetruecolor(
+                $this->_options['canvas']['width'],
+                $this->_options['canvas']['height']
+            );
+            break;
+
+        case (is_array($this->_options['canvas']) &&
+            isset($this->_options['canvas']['size']) &&
+            ($this->_options['canvas']['size'] = 'auto')):
+
+        case (is_string($this->_options['canvas']) &&
+            ($this->_options['canvas'] = 'auto')):
+            $this->_mode = 'auto';
+            break;
+
+        default:
+            throw new Image_Text_Exception('Could not create image canvas.');
+        }
+
+        if ($this->_img) {
+            $this->_options['canvas'] = array();
+            $this->_options['canvas']['width'] = imagesx($this->_img);
+            $this->_options['canvas']['height'] = imagesy($this->_img);
+        }
+
+        if ($this->_options['enable_alpha']) {
+            imagesavealpha($this->_img, true);
+            imagealphablending($this->_img, false);
+        }
+
+        if ($this->_options['background_color'] === null) {
+            $this->_options['enable_alpha'] = true;
+            imagesavealpha($this->_img, true);
+            imagealphablending($this->_img, false);
+            $colBg = imagecolorallocatealpha($this->_img, 255, 255, 255, 127);
+        } else {
+            $arBg = $this->convertString2RGB($this->_options['background_color']);
+            if ($arBg === false) {
+                throw new Image_Text_Exception('Background color is invalid.');
+            }
+            $colBg = imagecolorallocatealpha(
+                $this->_img, $arBg['r'], $arBg['g'], $arBg['b'], $arBg['a']
+            );
+        }
+        if ($image_canvas === false) {
+            imagefilledrectangle(
+                $this->_img,
+                0, 0,
+                $this->_options['canvas']['width'] - 1,
+                $this->_options['canvas']['height'] - 1,
+                $colBg
+            );
+        }
+
+        // Save and repair angle
+        $angle = $this->_options['angle'];
+        while ($angle < 0) {
+            $angle += 360;
+        }
+        if ($angle > 359) {
+            $angle = $angle % 360;
+        }
+        $this->_options['angle'] = $angle;
+
+        // Set the color values
+        $this->setColors($this->_options['color']);
+
+        $this->_lines = null;
+
+        // Initialization is complete
+        $this->_init = true;
+    }
+
+    /**
+     * Auto measurize text
+     *
+     * Automatically determines the greatest possible font size to fit the text into
+     * the text box. This method may be very resource intensive on your webserver. A
+     * good tweaking point are the $start and $end parameters, which specify the
+     * range of font sizes to search through. Anyway, the results should be cached if
+     * possible. You can optionally set $start and $end here as a parameter or the
+     * settings of the options array are used.
+     *
+     * @param int|bool $start Fontsize to start testing with.
+     * @param int|bool $end   Fontsize to end testing with.
+     *
+     * @return int Fontsize measured
+     * @see  Image_Text::measurize()
+     * @throws Image_Text_Exception
+     * @todo Beware of initialize
+     */
+    public function autoMeasurize($start = false, $end = false)
+    {
+        if (!$this->_init) {
+            throw new Image_Text_Exception('Not initialized. Call ->init() first!');
+        }
+
+        $start = (empty($start)) ? $this->_options['min_font_size'] : $start;
+        $end = (empty($end)) ? $this->_options['max_font_size'] : $end;
+
+        // Run through all possible font sizes until a measurize fails
+        // Not the optimal way. This can be tweaked!
+        for ($i = $start; $i <= $end; $i++) {
+            $this->_options['font_size'] = $i;
+            $res = $this->measurize();
+
+            if ($res === false) {
+                if ($start == $i) {
+                    $this->_options['font_size'] = -1;
+                    throw new Image_Text_Exception("No possible font size found");
+                }
+                $this->_options['font_size'] -= 1;
+                $this->_measurizedSize = $this->_options['font_size'];
+                break;
+            }
+            // Always the last couple of lines is stored here.
+            $this->_lines = $res;
+        }
+        return $this->_options['font_size'];
+    }
+
+    /**
+     * Measurize text into the text box
+     *
+     * This method makes your text fit into the defined textbox by measurizing the
+     * lines for your given font-size. You can do this manually before rendering (or
+     * use even Image_Text::autoMeasurize()) or the renderer will do measurizing
+     * automatically.
+     *
+     * @param bool $force Optionally, default is false, set true to force
+     *                    measurizing.
+     *
+     * @return array Array of measured lines.
+     * @see    Image_Text::autoMeasurize()
+     * @throws Image_Text_Exception
+     */
+    public function measurize($force = false)
+    {
+        if (!$this->_init) {
+            throw new Image_Text_Exception('Not initialized. Call ->init() first!');
+        }
+        $this->_processText();
+
+        // Precaching options
+        $font = $this->_font;
+        $size = $this->_options['font_size'];
+
+        $space = (1 + $this->_options['line_spacing'])
+            * $this->_options['font_size'];
+
+        $max_lines = (int)$this->_options['max_lines'];
+
+        if (($max_lines < 1) && !$force) {
+            return false;
+        }
+
+        $block_width = $this->_options['width'];
+        $block_height = $this->_options['height'];
+
+        $colors_cnt = sizeof($this->_colors);
+
+        $text_line = '';
+
+        $lines_cnt = 0;
+
+        $lines = array();
+
+        $text_height = 0;
+        $text_width = 0;
+
+        $i = 0;
+        $para_cnt = 0;
+        $width = 0;
+
+        $beginning_of_line = true;
+
+        // Run through tokens and order them in lines
+        foreach ($this->_tokens as $token) {
+            // Handle new paragraphs
+            if ($token == "\n") {
+                $bounds = imagettfbbox($size, 0, $font, $text_line);
+                if ((++$lines_cnt >= $max_lines) && !$force) {
+                    return false;
+                }
+                if ($this->_options['color_mode'] == 'paragraph') {
+                    $c = $this->_colors[$para_cnt % $colors_cnt];
+                    $i++;
+                } else {
+                    $c = $this->_colors[$i++ % $colors_cnt];
+                }
+                $lines[] = array(
+                    'string' => $text_line,
+                    'width' => $bounds[2] - $bounds[0],
+                    'height' => $bounds[1] - $bounds[7],
+                    'bottom_margin' => $bounds[1],
+                    'left_margin' => $bounds[0],
+                    'color' => $c
+                );
+                $text_width = max($text_width, ($bounds[2] - $bounds[0]));
+                $text_height += (int)$space;
+                if (($text_height > $block_height) && !$force) {
+                    return false;
+                }
+                $para_cnt++;
+                $text_line = '';
+                $beginning_of_line = true;
+                continue;
+            }
+
+            // Usual lining up
+
+            if ($beginning_of_line) {
+                $text_line = '';
+                $text_line_next = $token;
+                $beginning_of_line = false;
+            } else {
+                $text_line_next = $text_line . ' ' . $token;
+            }
+            $bounds = imagettfbbox($size, 0, $font, $text_line_next);
+            $prev_width = isset($prev_width) ? $width : 0;
+            $width = $bounds[2] - $bounds[0];
+
+            // Handling of automatic new lines
+            if ($width > $block_width) {
+                if ((++$lines_cnt >= $max_lines) && !$force) {
+                    return false;
+                }
+                if ($this->_options['color_mode'] == 'line') {
+                    $c = $this->_colors[$i++ % $colors_cnt];
+                } else {
+                    $c = $this->_colors[$para_cnt % $colors_cnt];
+                    $i++;
+                }
+
+                $lines[] = array(
+                    'string' => $text_line,
+                    'width' => $prev_width,
+                    'height' => $bounds[1] - $bounds[7],
+                    'bottom_margin' => $bounds[1],
+                    'left_margin' => $bounds[0],
+                    'color' => $c
+                );
+                $text_width = max($text_width, ($bounds[2] - $bounds[0]));
+                $text_height += (int)$space;
+                if (($text_height > $block_height) && !$force) {
+                    return false;
+                }
+
+                $text_line = $token;
+                $bounds = imagettfbbox($size, 0, $font, $text_line);
+                $width = $bounds[2] - $bounds[0];
+                $beginning_of_line = false;
+            } else {
+                $text_line = $text_line_next;
+            }
+        }
+        // Store remaining line
+        $bounds = imagettfbbox($size, 0, $font, $text_line);
+        $i++;
+        if ($this->_options['color_mode'] == 'line') {
+            $c = $this->_colors[$i % $colors_cnt];
+        } else {
+            $c = $this->_colors[$para_cnt % $colors_cnt];
+        }
+        $lines[] = array(
+            'string' => $text_line,
+            'width' => $bounds[2] - $bounds[0],
+            'height' => $bounds[1] - $bounds[7],
+            'bottom_margin' => $bounds[1],
+            'left_margin' => $bounds[0],
+            'color' => $c
+        );
+
+        // add last line height, but without the line-spacing
+        $text_height += $this->_options['font_size'];
+
+        $text_width = max($text_width, ($bounds[2] - $bounds[0]));
+
+        if (($text_height > $block_height) && !$force) {
+            return false;
+        }
+
+        $this->_realTextSize = array(
+            'width' => $text_width, 'height' => $text_height
+        );
+        $this->_measurizedSize = $this->_options['font_size'];
+
+        return $lines;
+    }
+
+    /**
+     * Render the text in the canvas using the given options.
+     *
+     * This renders the measurized text or automatically measures it first. The
+     * $force parameter can be used to switch of measurizing problems (this may cause
+     * your text being rendered outside a given text box or destroy your image
+     * completely).
+     *
+     * @param bool $force Optional, initially false, set true to silence measurize
+     *                    errors.
+     *
+     * @return void
+     * @throws Image_Text_Exception
+     */
+    public function render($force = false)
+    {
+        if (!$this->_init) {
+            throw new Image_Text_Exception('Not initialized. Call ->init() first!');
+        }
+
+        if (!$this->_tokens) {
+            $this->_processText();
+        }
+
+        if (empty($this->_lines)
+            || ($this->_measurizedSize != $this->_options['font_size'])
+        ) {
+            $this->_lines = $this->measurize($force);
+        }
+        $lines = $this->_lines;
+
+        if ($this->_mode === 'auto') {
+            $this->_img = imagecreatetruecolor(
+                $this->_realTextSize['width'],
+                $this->_realTextSize['height']
+            );
+            if (!$this->_img) {
+                throw new Image_Text_Exception('Could not create image canvas.');
+            }
+            $this->_mode = '';
+            $this->setColors($this->_options['color']);
+        }
+
+        $block_width = $this->_options['width'];
+
+        $max_lines = $this->_options['max_lines'];
+
+        $angle = $this->_options['angle'];
+        $radians = round(deg2rad($angle), 3);
+
+        $font = $this->_font;
+        $size = $this->_options['font_size'];
+
+        $line_spacing = $this->_options['line_spacing'];
+
+        $align = $this->_options['halign'];
+
+        $offset = $this->_getOffset();
+
+        $start_x = $offset['x'];
+        $start_y = $offset['y'];
+
+        $sinR = sin($radians);
+        $cosR = cos($radians);
+
+        switch ($this->_options['valign']) {
+        case self::IMAGE_TEXT_ALIGN_TOP:
+            $valign_space = 0;
+            break;
+        case self::IMAGE_TEXT_ALIGN_MIDDLE:
+            $valign_space = ($this->_options['height']
+                    - $this->_realTextSize['height']) / 2;
+            break;
+        case self::IMAGE_TEXT_ALIGN_BOTTOM:
+            $valign_space = $this->_options['height']
+                - $this->_realTextSize['height'];
+            break;
+        default:
+            $valign_space = 0;
+        }
+
+        $space = (1 + $line_spacing) * $size;
+
+        // Adjustment of align + translation of top-left-corner to bottom-left-corner
+        // of first line
+        $new_posx = $start_x + ($sinR * ($valign_space + $size));
+        $new_posy = $start_y + ($cosR * ($valign_space + $size));
+
+        $lines_cnt = min($max_lines, sizeof($lines));
+
+        $bboxes = array();
+        // Go thorugh lines for rendering
+        for ($i = 0; $i < $lines_cnt; $i++) {
+
+            // Calc the new start X and Y (only for line>0)
+            // the distance between the line above is used
+            if ($i > 0) {
+                $new_posx += $sinR * $space;
+                $new_posy += $cosR * $space;
+            }
+
+            // Calc the position of the 1st letter. We can then get the left and
+            // bottom margins 'i' is really not the same than 'j' or 'g'.
+            $left_margin = $lines[$i]['left_margin'];
+            $line_width = $lines[$i]['width'];
+
+            // Calc the position using the block width, the current line width and
+            // obviously the angle. That gives us the offset to slide the line.
+            switch ($align) {
+            case self::IMAGE_TEXT_ALIGN_LEFT:
+                $hyp = 0;
+                break;
+            case self::IMAGE_TEXT_ALIGN_RIGHT:
+                $hyp = $block_width - $line_width - $left_margin;
+                break;
+            case self::IMAGE_TEXT_ALIGN_CENTER:
+                $hyp = ($block_width - $line_width) / 2 - $left_margin;
+                break;
+            default:
+                $hyp = 0;
+                break;
+            }
+
+            $posx = $new_posx + $cosR * $hyp;
+            $posy = $new_posy - $sinR * $hyp;
+
+            $c = $lines[$i]['color'];
+
+            // Render textline
+            $bboxes[] = imagettftext(
+                $this->_img, $size, $angle, $posx, $posy,
+                $c, $font, $lines[$i]['string']
+            );
+        }
+        $this->_bbox = $bboxes;
+    }
+
+    /**
+     * Return the image ressource.
+     *
+     * Get the image canvas.
+     *
+     * @return resource Used image resource
+     */
+    public function getImg()
+    {
+        return $this->_img;
+    }
+
+    /**
+     * Display the image (send it to the browser).
+     *
+     * This will output the image to the users browser. You can use the standard
+     * IMAGETYPE_* constants to determine which image type will be generated.
+     * Optionally you can save your image to a destination you set in the options.
+     *
+     * @param bool $save Save or not the image on printout.
+     * @param bool $free Free the image on exit.
+     *
+     * @return  bool         True on success
+     * @see Image_Text::save()
+     * @throws Image_Text_Exception
+     */
+    public function display($save = false, $free = false)
+    {
+        if (!headers_sent()) {
+            header(
+                "Content-type: " .
+                image_type_to_mime_type($this->_options['image_type'])
+            );
+        } else {
+            throw new Image_Text_Exception('Header already sent.');
+        }
+        switch ($this->_options['image_type']) {
+        case IMAGETYPE_PNG:
+            $imgout = 'imagepng';
+            break;
+        case IMAGETYPE_JPEG:
+            $imgout = 'imagejpeg';
+            break;
+        case IMAGETYPE_BMP:
+            $imgout = 'imagebmp';
+            break;
+        default:
+            throw new Image_Text_Exception('Unsupported image type.');
+        }
+        if ($save) {
+            $imgout($this->_img);
+            $this->save();
+        } else {
+            $imgout($this->_img);
+        }
+
+        if ($free) {
+            $res = imagedestroy($this->_img);
+            if (!$res) {
+                throw new Image_Text_Exception('Destroying image failed.');
+            }
+        }
+    }
+
+    /**
+     * Save image canvas.
+     *
+     * Saves the image to a given destination. You can leave out the destination file
+     * path, if you have the option for that set correctly. Saving is possible with
+     * the display() method, too.
+     *
+     * @param bool|string $destFile The destination to save to (optional, uses
+     *                              options value else).
+     *
+     * @see Image_Text::display()
+     * @return void
+     * @throws Image_Text_Exception
+     */
+    public function save($destFile = false)
+    {
+        if (!$destFile) {
+            $destFile = $this->_options['dest_file'];
+        }
+        if (!$destFile) {
+            throw new Image_Text_Exception("Invalid desitination file.");
+        }
+
+        switch ($this->_options['image_type']) {
+        case IMAGETYPE_PNG:
+            $imgout = 'imagepng';
+            break;
+        case IMAGETYPE_JPEG:
+            $imgout = 'imagejpeg';
+            break;
+        case IMAGETYPE_BMP:
+            $imgout = 'imagebmp';
+            break;
+        default:
+            throw new Image_Text_Exception('Unsupported image type.');
+            break;
+        }
+
+        $res = $imgout($this->_img, $destFile);
+        if (!$res) {
+            throw new Image_Text_Exception('Saving file failed.');
+        }
+    }
+
+    /**
+     * Get completely translated offset for text rendering.
+     *
+     * Get completely translated offset for text rendering. Important for usage of
+     * center coords and angles.
+     *
+     * @return array Array of x/y coordinates.
+     */
+    private function _getOffset()
+    {
+        // Presaving data
+        $width = $this->_options['width'];
+        $height = $this->_options['height'];
+        $angle = $this->_options['angle'];
+        $x = $this->_options['x'];
+        $y = $this->_options['y'];
+        // Using center coordinates
+        if (!empty($this->_options['cx']) && !empty($this->_options['cy'])) {
+            $cx = $this->_options['cx'];
+            $cy = $this->_options['cy'];
+            // Calculation top left corner
+            $x = $cx - ($width / 2);
+            $y = $cy - ($height / 2);
+            // Calculating movement to keep the center point on himslf after rotation
+            if ($angle) {
+                $ang = deg2rad($angle);
+                // Vector from the top left cornern ponting to the middle point
+                $vA = array(($cx - $x), ($cy - $y));
+                // Matrix to rotate vector
+                // sinus and cosinus
+                $sin = round(sin($ang), 14);
+                $cos = round(cos($ang), 14);
+                // matrix
+                $mRot = array(
+                    $cos, (-$sin),
+                    $sin, $cos
+                );
+                // Multiply vector with matrix to get the rotated vector
+                // This results in the location of the center point after rotation
+                $vB = array(
+                    ($mRot[0] * $vA[0] + $mRot[2] * $vA[0]),
+                    ($mRot[1] * $vA[1] + $mRot[3] * $vA[1])
+                );
+                // To get the movement vector, we subtract the original middle
+                $vC = array(
+                    ($vA[0] - $vB[0]),
+                    ($vA[1] - $vB[1])
+                );
+                // Finally we move the top left corner coords there
+                $x += $vC[0];
+                $y += $vC[1];
+            }
+        }
+        return array('x' => (int)round($x, 0), 'y' => (int)round($y, 0));
+    }
+
+    /**
+     * Convert a color to an array.
+     *
+     * The following colors syntax must be used:
+     * "#08ffff00" hexadecimal format with alpha channel (08)
+     *
+     * @param string $scolor string of colorcode.
+     *
+     * @see Image_Text::IMAGE_TEXT_REGEX_HTMLCOLOR
+     * @return bool|array false if string can't be converted to array
+     */
+    public static function convertString2RGB($scolor)
+    {
+        if (preg_match(self::IMAGE_TEXT_REGEX_HTMLCOLOR, $scolor, $matches)) {
+            return array(
+                'r' => hexdec($matches[2]),
+                'g' => hexdec($matches[3]),
+                'b' => hexdec($matches[4]),
+                'a' => hexdec(!empty($matches[1]) ? $matches[1] : 0),
+            );
+        }
+        return false;
+    }
+
+    /**
+     * Extract the tokens from the text.
+     *
+     * @return void
+     */
+    private function _processText()
+    {
+        if (!isset($this->_text)) {
+            return;
+        }
+        $this->_tokens = array();
+
+        // Normalize linebreak to "\n"
+        $this->_text = preg_replace("[\r\n]", "\n", $this->_text);
+
+        // Get each paragraph
+        $paras = explode("\n", $this->_text);
+
+        // loop though the paragraphs
+        // and get each word (token)
+        foreach ($paras as $para) {
+            $words = explode(' ', $para);
+            foreach ($words as $word) {
+                $this->_tokens[] = $word;
+            }
+            // add a "\n" to mark the end of a paragraph
+            $this->_tokens[] = "\n";
+        }
+        // we do not need an end paragraph as the last token
+        array_pop($this->_tokens);
+    }
+}

+ 26 - 0
main/inc/lib/pear/Image/Text/Exception.php

@@ -0,0 +1,26 @@
+<?php
+/**
+ * Exception for Image_Text
+ *
+ * PHP version 5
+ *
+ * @category Image
+ * @package  Image_Text
+ * @author   Daniel O'Connor <daniel.oconnor@gmail.com>
+ * @license  http://www.php.net/license/3_01.txt PHP License
+ * @link     http://pear.php.net/package/Image_Text
+ */
+require_once 'PEAR/Exception.php';
+/**
+ * Exception for Image_Text
+ *
+ * @category Image
+ * @package  Image_Text
+ * @author   Daniel O'Connor <daniel.oconnor@gmail.com>
+ * @author   Michael Cramer <michael@bigmichi1.de>
+ * @license  http://www.php.net/license/3_01.txt PHP License
+ * @link     http://pear.php.net/package/Image_Text
+ */
+class Image_Text_Exception extends PEAR_Exception
+{
+}

+ 163 - 0
main/inc/lib/pear/Text/CAPTCHA.php

@@ -0,0 +1,163 @@
+<?php
+/**
+ * Text_CAPTCHA - creates a CAPTCHA for Turing tests.
+ * Base class file for using Text_CAPTCHA.
+ *
+ * PHP version 5
+ *
+ * @category Text
+ * @package  Text_CAPTCHA
+ * @author   Christian Wenz <wenz@php.net>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @link     http://pear.php.net/package/Text_CAPTCHA
+ */
+/**
+ * Require Exception class for error handling.
+ */
+require_once 'Text/CAPTCHA/Exception.php';
+/**
+ * Require Text_Password class for generating the phrase.
+ */
+require_once 'Text/Password.php';
+
+/**
+ * Text_CAPTCHA - creates a CAPTCHA for Turing tests.
+ * Class to create a Turing test for websites by creating an image, ASCII art or
+ * something else with some (obfuscated) characters.
+ *
+ * @category Text
+ * @package  Text_CAPTCHA
+ * @author   Christian Wenz <wenz@php.net>
+ * @author   Michael Cramer <michael@bigmichi1.de>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @link     http://pear.php.net/package/Text_CAPTCHA
+ */
+class Text_CAPTCHA
+{
+    /**
+     * driver for Text_CAPTCHA
+     *
+     * @var Text_CAPTCHA_Driver_Base
+     */
+    private $_driver;
+
+    /**
+     * check if an initial driver init was done.
+     *
+     * @var bool
+     */
+    private $_driverInitDone = false;
+
+    /**
+     * Constructor for the TEXT_CAPTCHA object with the given driver.
+     *
+     * @param Text_CAPTCHA_Driver $driver driver
+     *
+     * @throws Text_CAPTCHA_Exception no driver given
+     */
+    function __construct($driver)
+    {
+        if (is_null($driver)) {
+            throw new Text_CAPTCHA_Exception("No driver given");
+        }
+        $this->_driver = $driver;
+        $this->_driverInitDone = false;
+    }
+
+    /**
+     * Create a new Text_CAPTCHA object.
+     *
+     * @param string $driver name of driver class to initialize
+     *
+     * @return Text_CAPTCHA a newly created Text_CAPTCHA object
+     * @throws Text_CAPTCHA_Exception when driver could not be loaded
+     *
+     */
+    public static function factory($driver)
+    {
+        $driver = basename($driver);
+        $class = 'Text_CAPTCHA_Driver_' . $driver;
+        $file = str_replace('_', '/', $class) . '.php';
+        //check if it exists and can be loaded
+        if (!@fclose(@fopen($file, 'r', true))) {
+            throw new Text_CAPTCHA_Exception(
+                'Driver ' . $driver . ' cannot be loaded.'
+            );
+        }
+        //continue with including the driver
+        include_once $file;
+
+        $driver = new $class;
+
+        return new Text_CAPTCHA($driver);
+    }
+
+    /**
+     * Create random CAPTCHA phrase
+     *
+     * @param boolean|string $newPhrase new Phrase to use or true to generate a new
+     *                                  one
+     *
+     * @return void
+     * @throws Text_CAPTCHA_Exception when driver is not initialized
+     */
+    public final function generate($newPhrase = false)
+    {
+        if (!$this->_driverInitDone) {
+            throw new Text_CAPTCHA_Exception("Driver not initialized");
+        }
+        if ($newPhrase === true || is_null($this->_driver->getPhrase())) {
+            $this->_driver->createPhrase();
+        } else if (strlen($newPhrase) > 0) {
+            $this->_driver->setPhrase($newPhrase);
+        }
+        $this->_driver->createCAPTCHA();
+    }
+
+    /**
+     * Reinitialize the entire Text_CAPTCHA object.
+     *
+     * @param array $options Options to pass in.
+     *
+     * @return void
+     */
+    public final function init($options = array())
+    {
+        $this->_driver->resetDriver();
+        $this->_driver->initDriver($options);
+        $this->_driverInitDone = true;
+        $this->generate();
+    }
+
+    /**
+     * Place holder for the real getCAPTCHA() method used by extended classes to
+     * return the generated CAPTCHA (as an image resource, as an ASCII text, ...).
+     *
+     * @return string|object
+     */
+    public final function getCAPTCHA()
+    {
+        return $this->_driver->getCAPTCHA();
+    }
+
+    /**
+     * Return secret CAPTCHA phrase.
+     *
+     * @return string secret phrase
+     */
+    public final function getPhrase()
+    {
+        return $this->_driver->getPhrase();
+    }
+
+    /**
+     * Place holder for the real getCAPTCHA() method used by extended classes to
+     * return the generated CAPTCHA (as an image resource, as an ASCII text, ...).
+     *
+     * @return string|object
+     */
+    public function getCAPTCHAAsJPEG()
+    {
+        return $this->_driver->_getCAPTCHAAsJPEG();
+    }
+}

+ 56 - 0
main/inc/lib/pear/Text/CAPTCHA/Driver.php

@@ -0,0 +1,56 @@
+<?php
+/**
+ * Interface for Drivers
+ *
+ * PHP version 5
+ *
+ * @category Text
+ * @package  Text_CAPTCHA
+ * @author   Michael Cramer <michael@bigmichi1.de>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @link     http://pear.php.net/package/Text_CAPTCHA
+ */
+/**
+ * Interface for Text_CAPTCHA drivers
+ *
+ * @category Text
+ * @package  Text_CAPTCHA
+ * @author   Michael Cramer <michael@bigmichi1.de>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @link     http://pear.php.net/package/Text_CAPTCHA
+ */
+interface Text_CAPTCHA_Driver
+{
+    /**
+     * Clear the internal state of the driver.
+     *
+     * @return void
+     */
+    function resetDriver();
+
+    /**
+     * Initialize the driver with the given options.
+     *
+     * @param array $options options for the driver as array
+     *
+     * @return void
+     * @throws Text_CAPTCHA_Exception something went wrong during init
+     */
+    function initDriver($options);
+
+    /**
+     * Generate the CAPTCHA.
+     *
+     * @return void
+     * @throws Text_CAPTCHA_Exception something went wrong during creation of CAPTCHA
+     */
+    function createCAPTCHA();
+
+    /**
+     * Generate the phrase for the CAPTCHA.
+     *
+     * @return void
+     * @throws Text_CAPTCHA_Exception something went wrong during creation of phrase
+     */
+    function createPhrase();
+}

+ 97 - 0
main/inc/lib/pear/Text/CAPTCHA/Driver/Base.php

@@ -0,0 +1,97 @@
+<?php
+/**
+ * Base class file for all Text_CAPTCHA drivers.
+ *
+ * PHP version 5
+ *
+ * @category Text
+ * @package  Text_CAPTCHA
+ * @author   Michael Cramer <michael@bigmichi1.de>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @link     http://pear.php.net/package/Text_CAPTCHA
+ */
+require_once 'Text/CAPTCHA/Driver.php';
+/**
+ * Base class file for all Text_CAPTCHA drivers.
+ *
+ * @category Text
+ * @package  Text_CAPTCHA
+ * @author   Michael Cramer <michael@bigmichi1.de>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @link     http://pear.php.net/package/Text_CAPTCHA
+ */
+abstract class Text_CAPTCHA_Driver_Base implements Text_CAPTCHA_Driver
+{
+    /**
+     * Captcha
+     *
+     * @var object|string
+     */
+    private $_captcha;
+
+    /**
+     * Phrase
+     *
+     * @var string
+     */
+    private $_phrase;
+
+    /**
+     * Sets secret CAPTCHA phrase.
+     * This method sets the CAPTCHA phrase (use null for a random phrase)
+     *
+     * @param string $phrase The (new) phrase
+     *
+     * @return void
+     */
+    public final function setPhrase($phrase)
+    {
+        $this->_phrase = $phrase;
+    }
+
+    /**
+     * Return secret CAPTCHA phrase
+     * This method returns the CAPTCHA phrase
+     *
+     * @return  string   secret phrase
+     */
+    public final function getPhrase()
+    {
+        return $this->_phrase;
+    }
+
+    /**
+     * Sets the generated captcha.
+     *
+     * @param object|string $captcha the generated captcha
+     *
+     * @return void
+     */
+    protected final function setCaptcha($captcha)
+    {
+        $this->_captcha = $captcha;
+    }
+
+    /**
+     * Place holder for the real getCAPTCHA() method
+     * used by extended classes to return the generated CAPTCHA
+     * (as an image resource, as an ASCII text, ...)
+     *
+     * @return string|object
+     */
+    public final function getCAPTCHA()
+    {
+        return $this->_captcha;
+    }
+
+    /**
+     * Reset the phrase and the CAPTCHA.
+     *
+     * @return void
+     */
+    public function resetDriver()
+    {
+        $this->setPhrase(null);
+        $this->setCaptcha(null);
+    }
+}

+ 215 - 0
main/inc/lib/pear/Text/CAPTCHA/Driver/Equation.php

@@ -0,0 +1,215 @@
+<?php
+/**
+ * Equation driver for Text_CAPTCHA.
+ * Returns simple equations as string, e.g. "9 - 2"
+ *
+ * PHP version 5
+ *
+ * @category Text
+ * @package  Text_CAPTCHA
+ * @author   Christian Weiske <cweiske@php.net>
+ * @author   Christian Wenz <wenz@php.net>
+ * @author   Michael Cramer <michael@bigmichi1.de>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @link     http://pear.php.net/package/Text_CAPTCHA
+ */
+require_once 'Text/CAPTCHA/Driver/Base.php';
+/**
+ * Equation driver for Text_CAPTCHA.
+ * Returns simple equations as string, e.g. "9 - 2"
+ *
+ * @category Text
+ * @package  Text_CAPTCHA
+ * @author   Christian Weiske <cweiske@php.net>
+ * @author   Christian Wenz <wenz@php.net>
+ * @author   Michael Cramer <michael@bigmichi1.de>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @link     http://pear.php.net/package/Text_CAPTCHA
+ */
+class Text_CAPTCHA_Driver_Equation extends Text_CAPTCHA_Driver_Base
+{
+    /**
+     * Operators that may be used in the equation. Two numbers have to be filled in,
+     * and %s is needed since number2text conversion may be applied and strings
+     * filled in.
+     *
+     * @var array
+     */
+    private $_operators = array(
+        '%s * %s',
+        '%s + %s',
+        '%s - %s',
+        'min(%s, %s)',
+        'max(%s, %s)'
+    );
+
+    /**
+     * Minimal number to use in an equation.
+     *
+     * @var int
+     */
+    private $_min = 1;
+
+    /**
+     * Maximum number to use in an equation.
+     *
+     * @var int
+     */
+    private $_max = 10;
+
+    /**
+     * Whether numbers shall be converted to text.
+     *
+     * @var bool
+     */
+    private $_numbersToText = false;
+
+    /**
+     * This variable holds the locale for Numbers_Words.
+     *
+     * @var string
+     */
+    private $_locale = '';
+
+    /**
+     * Complexity of the generated equations.<br>
+     * 1 - simple ones such as "1 + 10"<br>
+     * 2 - harder ones such as "(3-2)*(min(5,6))"
+     *
+     * @var int
+     */
+    private $_severity = 1;
+
+    /**
+     * Initialize the driver.
+     *
+     * @param array $options CAPTCHA options with these keys:<br>
+     *                       min           minimum numeric value
+     *                       max           maximum numeric value
+     *                       numbersToText boolean for number to text conversion
+     *                       locale        locale for number to text conversion
+     *                       severity      number for complexity
+     *
+     * @return void
+     * @throws Text_CAPTCHA_Exception when numbersToText is true, but Number_Words
+     *                                package is not available
+     */
+    public function initDriver($options = array())
+    {
+        if (isset($options['min'])) {
+            $this->_min = (int)$options['min'];
+        } else {
+            $this->_min = 1;
+        }
+        if (isset($options['max'])) {
+            $this->_max = (int)$options['max'];
+        } else {
+            $this->_max = 10;
+        }
+        if (isset($options['numbersToText'])) {
+            $this->_numbersToText = (bool)$options['numbersToText'];
+        } else {
+            $this->_numbersToText = false;
+        }
+        if (isset($options['locale'])) {
+            $this->_locale = (string)$options['locale'];
+        } else {
+            $this->_locale = '';
+        }
+        if (isset($options['severity'])) {
+            $this->_severity = (int)$options['severity'];
+        } else {
+            $this->_severity = 1;
+        }
+
+        if ($this->_numbersToText) {
+            include_once 'Numbers/Words.php';
+            if (!class_exists('Numbers_Words')) {
+                throw new Text_CAPTCHA_Exception('Number_Words package required');
+            }
+        }
+    }
+
+    /**
+     * Create random CAPTCHA equation.
+     * This method creates a random equation.
+     *
+     * @return void
+     * @throws Text_CAPTCHA_Exception when invalid severity is specified
+     */
+    public function createCAPTCHA()
+    {
+        switch ($this->_severity) {
+        case 1:
+            list($equation, $phrase) = $this->_createSimpleEquation();
+            break;
+        case 2:
+            list($eq1, $sol1) = $this->_createSimpleEquation();
+            list($eq2, $sol2) = $this->_createSimpleEquation();
+            $op3 = $this->_operators[mt_rand(0, count($this->_operators) - 1)];
+            list(, $phrase) = $this->_solveSimpleEquation($sol1, $sol2, $op3);
+            $equation = sprintf($op3, '(' . $eq1 . ')', '(' . $eq2 . ')');
+            break;
+        default:
+            throw new Text_CAPTCHA_Exception(
+                'Equation complexity of ' . $this->_severity . ' not supported'
+            );
+        }
+        $this->setCaptcha($equation);
+        $this->setPhrase($phrase);
+    }
+
+    /**
+     * Creates a simple equation of type (number operator number).
+     *
+     * @return array Array with equation and solution
+     */
+    private function _createSimpleEquation()
+    {
+        $one = mt_rand($this->_min, $this->_max);
+        $two = mt_rand($this->_min, $this->_max);
+        $operator = $this->_operators[mt_rand(0, count($this->_operators) - 1)];
+
+        return $this->_solveSimpleEquation($one, $two, $operator);
+    }
+
+    /**
+     * Solves a simple equation with two given numbers and one operator as defined
+     * in $this->_operators.
+     * Also converts the numbers to words if required.
+     *
+     * @param int    $one      First number
+     * @param int    $two      Second number
+     * @param string $operator Operator used with those two numbers
+     *
+     * @return array    Array with equation and solution
+     */
+    private function _solveSimpleEquation($one, $two, $operator)
+    {
+        $equation = sprintf($operator, $one, $two);
+
+        $function = create_function('', 'return ' . $equation . ';');
+
+        if ($this->_numbersToText) {
+            $numberWords = new Numbers_Words();
+            $equation = sprintf(
+                $operator,
+                $numberWords->toWords($one, $this->_locale),
+                $numberWords->toWords($two, $this->_locale)
+            );
+        }
+        return array($equation, $function());
+    }
+
+    /**
+     * Creates the captcha. This method is a placeholder, since the equation is
+     * created in createCAPTCHA()
+     *
+     * @return void
+     * @see createCAPTCHA()
+     */
+    public function createPhrase()
+    {
+        $this->setPhrase(null);
+    }
+}

+ 233 - 0
main/inc/lib/pear/Text/CAPTCHA/Driver/Figlet.php

@@ -0,0 +1,233 @@
+<?php
+/**
+ * Require Figlet class for rendering the text.
+ *
+ * PHP version 5
+ *
+ * @category Text
+ * @package  Text_CAPTCHA
+ * @author   Aaron Wormus <wormus@php.net>
+ * @author   Christian Wenz <wenz@php.net>
+ * @author   Michael Cramer <michael@bigmichi1.de>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @link     http://pear.php.net/package/Text_CAPTCHA
+ */
+require_once 'Text/CAPTCHA/Driver/Base.php';
+require_once 'Text/Figlet.php';
+/**
+ * Text_CAPTCHA_Driver_Figlet - Text_CAPTCHA driver Figlet based CAPTCHAs
+ *
+ * @category Text
+ * @package  Text_CAPTCHA
+ * @author   Aaron Wormus <wormus@php.net>
+ * @author   Christian Wenz <wenz@php.net>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @link     http://pear.php.net/package/Text_CAPTCHA
+ * @todo     define an obfuscation algorithm
+ */
+class Text_CAPTCHA_Driver_Figlet extends Text_CAPTCHA_Driver_Base
+{
+    /**
+     * Text_Password options.
+     *
+     * @var array
+     */
+    private $_textPasswordOptions;
+
+    /**
+     * Width of CAPTCHA
+     *
+     * @var int
+     */
+    private $_width;
+
+    /**
+     * Length of CAPTCHA
+     *
+     * @var int
+     */
+    private $_length;
+
+    /**
+     * Figlet font
+     *
+     * @var string
+     */
+    private $_font;
+
+    /**
+     * Figlet font
+     *
+     * @var array
+     */
+    private $_style = array();
+
+    /**
+     * Output Format
+     *
+     * @var string
+     */
+    private $_output;
+
+    /**
+     * init function
+     *
+     * Initializes the new Text_CAPTCHA_Driver_Figlet object and creates a GD image
+     *
+     * @param array $options CAPTCHA options
+     *
+     * @return void
+     * @throws Text_CAPTCHA_Exception when no options are given
+     */
+    public function initDriver($options = array())
+    {
+        if (!empty($options['output'])) {
+            $this->_output = (string)$options['output'];
+        } else {
+            $this->_output = 'html';
+        }
+
+        if (isset($options['width']) && $options['width']) {
+            $this->_width = (int)$options['width'];
+        } else {
+            $this->_width = 200;
+        }
+
+        if (!empty($options['length'])) {
+            $this->_length = $options['length'];
+        } else {
+            $this->_length = 6;
+        }
+
+        if (!isset($options['phrase']) || empty($options['phrase'])) {
+            $phraseOptions = (isset($options['phraseOptions'])
+                && is_array($options['phraseOptions']))
+                ? $options['phraseOptions'] : array();
+            $this->_textPasswordOptions = $phraseOptions;
+        } else {
+            $this->setPhrase($options['phrase']);
+        }
+
+        if (!empty($options['style'])
+            && is_array($options['style'])
+        ) {
+            $this->_style = $options['style'];
+        }
+
+        if (empty($this->_style['padding'])) {
+            $this->_style['padding'] = '5px';
+        }
+
+        if (!empty($options['font_file'])) {
+            if (is_array($options['font_file'])) {
+                $arr = $options['font_file'];
+                $this->_font = $arr[array_rand($arr)];
+            } else {
+                $this->_font = $options['font_file'];
+            }
+        }
+    }
+
+    /**
+     * Create the passphrase.
+     *
+     * @return string
+     */
+    public function createPhrase()
+    {
+        $options = $this->_textPasswordOptions;
+        $textPassword = new Text_Password();
+        if (!is_array($options) || count($options) === 0) {
+            $this->setPhrase($textPassword->create($this->_length));
+        } else {
+            if (count($options) === 1) {
+                $this->setPhrase($textPassword->create($this->_length, $options[0]));
+            } else {
+                $this->setPhrase(
+                    $textPassword->create($this->_length, $options[0], $options[1])
+                );
+            }
+        }
+    }
+
+    /**
+     * Create CAPTCHA image.
+     *
+     * This method creates a CAPTCHA image.
+     *
+     * @return void on error
+     * @throws Text_CAPTCHA_Exception when loading font fails
+     */
+    public function createCAPTCHA()
+    {
+        $pear = new PEAR();
+        $figlet = new Text_Figlet();
+        if ($pear->isError($figlet->loadFont($this->_font))) {
+            throw new Text_CAPTCHA_Exception('Error loading Text_Figlet font');
+        }
+
+        $outputString = $figlet->lineEcho($this->getPhrase());
+
+        switch ($this->_output) {
+        case 'text':
+            $this->setCaptcha($outputString);
+            break;
+        case 'html':
+            $this->setCaptcha($this->_getCAPTCHAAsHTML($outputString));
+            break;
+        case 'javascript':
+            $this->setCaptcha($this->_getCAPTCHAAsJavascript($outputString));
+            break;
+        default:
+            throw new Text_CAPTCHA_Exception('Invalid output option given');
+        }
+    }
+
+    /**
+     * Return CAPTCHA as HTML.
+     *
+     * This method returns the CAPTCHA as HTML.
+     *
+     * @param string $figletOutput output string from Figlet.
+     *
+     * @return string HTML Figlet image or PEAR error
+     */
+    private function _getCAPTCHAAsHTML($figletOutput)
+    {
+        $charWidth = strpos($figletOutput, "\n");
+        $data = str_replace("\n", '<br />', $figletOutput);
+        $textSize = ($this->_width / $charWidth) * 1.4;
+        $cssOutput = "";
+        foreach ($this->_style as $key => $value) {
+            $cssOutput .= "$key: $value;";
+        }
+
+        $htmlOutput = '<div style="font-family: courier;
+          font-size: ' . $textSize . 'px;
+          width:' . $this->_width . 'px;
+          text-align:center;">';
+        $htmlOutput .= '<div style="' . $cssOutput . 'margin:0px;">
+          <pre style="padding: 0px; margin: 0px;">' . $data . '</pre></div></div>';
+
+        return $htmlOutput;
+    }
+
+    /**
+     * Return CAPTCHA as Javascript version of HTML.
+     *
+     * This method returns the CAPTCHA as a Javascript string.
+     * I'm not exactly sure what the point of doing this would be.
+     *
+     * @param string $figletOutput output string from Figlet.
+     *
+     * @return string javascript string or PEAR error
+     */
+    private function _getCAPTCHAAsJavascript($figletOutput)
+    {
+        $obfusData = rawurlencode($figletOutput);
+        $javascript = "<script language=\"javascript\">
+          document.write(unescape(\"$obfusData\" ) );
+          </script>";
+        return $javascript;
+    }
+}

+ 290 - 0
main/inc/lib/pear/Text/CAPTCHA/Driver/Image.php

@@ -0,0 +1,290 @@
+<?php
+/**
+ * Require Image_Text class for generating the text.
+ *
+ * PHP version 5
+ *
+ * @category Text
+ * @package  Text_CAPTCHA
+ * @author   Christian Wenz <wenz@php.net>
+ * @author   Michael Cramer <michael@bigmichi1.de>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @link     http://pear.php.net/package/Text_CAPTCHA
+ */
+require_once 'Text/CAPTCHA/Driver/Base.php';
+require_once 'Image/Text.php';
+
+/**
+ * Text_CAPTCHA_Driver_Image - Text_CAPTCHA driver graphical CAPTCHAs
+ *
+ * Class to create a graphical Turing test
+ *
+ * @category Text
+ * @package  Text_CAPTCHA
+ * @author   Christian Wenz <wenz@php.net>
+ * @author   Michael Cramer <michael@bigmichi1.de>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @link     http://pear.php.net/package/Text_CAPTCHA
+ * @todo     refine the obfuscation algorithm :-)
+ * @todo     consider removing Image_Text dependency
+ */
+class Text_CAPTCHA_Driver_Image extends Text_CAPTCHA_Driver_Base
+{
+    /**
+     * Text_Password options.
+     *
+     * @var array
+     */
+    private $_textPasswordOptions;
+    /**
+     * Width of CAPTCHA
+     *
+     * @var int
+     */
+    private $_width;
+
+    /**
+     * Height of CAPTCHA
+     *
+     * @var int
+     */
+    private $_height;
+
+    /**
+     * CAPTCHA output format
+     *
+     * @var string
+     */
+    private $_output;
+
+    /**
+     * Further options (here: for Image_Text)
+     *
+     * @var array
+     */
+    private $_imageOptions = array(
+        'font_size' => 24,
+        'font_path' => './',
+        'font_file' => 'COUR.TTF',
+        'text_color' => '#000000',
+        'lines_color' => '#CACACA',
+        'background_color' => '#555555',
+        'antialias' => false);
+
+    /**
+     * Init function
+     *
+     * Initializes the new Text_CAPTCHA_Driver_Image object and creates a GD image
+     *
+     * @param array $options CAPTCHA options
+     *
+     * @return void
+     */
+    public function initDriver($options = array())
+    {
+        if (is_array($options)) {
+            if (isset($options['width']) && is_int($options['width'])) {
+                $this->_width = $options['width'];
+            } else {
+                $this->_width = 200;
+            }
+            if (isset($options['height']) && is_int($options['height'])) {
+                $this->_height = $options['height'];
+            } else {
+                $this->_height = 80;
+            }
+            if (!isset($options['phrase']) || empty($options['phrase'])) {
+                $phraseOptions = (isset($options['phraseOptions'])
+                    && is_array($options['phraseOptions']))
+                    ? $options['phraseOptions'] : array();
+                $this->_textPasswordOptions = $phraseOptions;
+            } else {
+                $this->setPhrase($options['phrase']);
+            }
+            if (!isset($options['output']) || empty($options['output'])) {
+                $this->_output = 'jpeg';
+            } else {
+                $this->_output = $options['output'];
+            }
+            if (isset($options['imageOptions'])
+                && is_array($options['imageOptions'])
+                && count($options['imageOptions']) > 0
+            ) {
+                $this->_imageOptions = array_merge(
+                    $this->_imageOptions, $options['imageOptions']
+                );
+            }
+        }
+    }
+
+    /**
+     * Create CAPTCHA image.
+     *
+     * This method creates a CAPTCHA image.
+     *
+     * @return void
+     * @throws Text_CAPTCHA_Exception when image generation with Image_Text produces
+     *               an error
+     */
+    public function createCAPTCHA()
+    {
+        $options['canvas'] = array(
+            'width' => $this->_width,
+            'height' => $this->_height
+        );
+        $options['width'] = $this->_width - 20;
+        $options['height'] = $this->_height - 20;
+        $options['cx'] = ceil(($this->_width) / 2 + 10);
+        $options['cy'] = ceil(($this->_height) / 2 + 10);
+        $options['angle'] = rand(0, 30) - 15;
+        $options['font_size'] = $this->_imageOptions['font_size'];
+        $options['font_path'] = $this->_imageOptions['font_path'];
+        $options['font_file'] = $this->_imageOptions['font_file'];
+        $options['color'] = array($this->_imageOptions['text_color']);
+        $options['background_color'] = $this->_imageOptions['background_color'];
+        $options['max_lines'] = 1;
+        $options['mode'] = 'auto';
+
+        do {
+            $imageText = new Image_Text($this->getPhrase(), $options);
+            $imageText->init();
+            $result = $imageText->measurize();
+        } while ($result === false && --$options['font_size'] > 0);
+        if ($result === false) {
+            throw new Text_CAPTCHA_Exception(
+                'The text provided does not fit in the image dimensions'
+            );
+        }
+        $imageText->render();
+        $image = $imageText->getImg();
+
+        if ($this->_imageOptions['antialias'] && function_exists('imageantialias')) {
+            imageantialias($image, true);
+        }
+
+        $colors = Image_Text::convertString2RGB(
+            $this->_imageOptions['lines_color']
+        );
+        $linesColor = imagecolorallocate(
+            $image, $colors['r'], $colors['g'], $colors['b']
+        );
+        //some obfuscation
+        for ($i = 0; $i < 3; $i++) {
+            $x1 = rand(0, $this->_width - 1);
+            $y1 = rand(0, round($this->_height / 10, 0));
+            $x2 = rand(0, round($this->_width / 10, 0));
+            $y2 = rand(0, $this->_height - 1);
+            imageline($image, $x1, $y1, $x2, $y2, $linesColor);
+            $x1 = rand(0, $this->_width - 1);
+            $y1 = $this->_height - rand(1, round($this->_height / 10, 0));
+            $x2 = $this->_width - rand(1, round($this->_width / 10, 0));
+            $y2 = rand(0, $this->_height - 1);
+            imageline($image, $x1, $y1, $x2, $y2, $linesColor);
+            $cx = rand(0, $this->_width - 50) + 25;
+            $cy = rand(0, $this->_height - 50) + 25;
+            $w = rand(1, 24);
+            imagearc($image, $cx, $cy, $w, $w, 0, 360, $linesColor);
+        }
+
+        // @todo remove hardcoded value
+        $this->_output = 'jpg';
+
+        if ($this->_output == 'gif' && imagetypes() & IMG_GIF) {
+            $this->setCaptcha($this->_getCAPTCHAAsGIF($image));
+        } else if (($this->_output == 'jpg' && imagetypes() & IMG_JPG)
+            || ($this->_output == 'jpeg' && imagetypes() & IMG_JPEG)
+        ) {
+            $this->setCaptcha($this->_getCAPTCHAAsJPEG($image));
+        } else if ($this->_output == 'png' && imagetypes() & IMG_PNG) {
+            $this->setCaptcha($this->_getCAPTCHAAsPNG($image));
+        } else if ($this->_output == 'resource') {
+            $this->setCaptcha($image);
+        } else {
+            throw new Text_CAPTCHA_Exception(
+                "Unknown or unsupported output type specified"
+            );
+        }
+
+        imagedestroy($image);
+    }
+
+    /**
+     * Return CAPTCHA as PNG.
+     *
+     * This method returns the CAPTCHA as PNG.
+     *
+     * @param resource $image generated image
+     *
+     * @return string image contents
+     */
+    private function _getCAPTCHAAsPNG($image)
+    {
+        ob_start();
+        imagepng($image);
+        $data = ob_get_contents();
+        ob_end_clean();
+        return $data;
+    }
+
+    /**
+     * Return CAPTCHA as JPEG.
+     *
+     * This method returns the CAPTCHA as JPEG.
+     *
+     * @param resource $image generated image
+     *
+     * @return string image contents
+     */
+    private function _getCAPTCHAAsJPEG($image)
+    {
+        ob_start();
+        imagejpeg($image);
+        $data = ob_get_contents();
+        ob_end_clean();
+        return $data;
+    }
+
+    /**
+     * Return CAPTCHA as GIF.
+     *
+     * This method returns the CAPTCHA as GIF.
+     *
+     * @param resource $image generated image
+     *
+     * @return string image contents
+     */
+    private function _getCAPTCHAAsGIF($image)
+    {
+        ob_start();
+        imagegif($image);
+        $data = ob_get_contents();
+        ob_end_clean();
+        return $data;
+    }
+
+    /**
+     * Create random CAPTCHA phrase, Image edition (with size check).
+     *
+     * This method creates a random phrase, maximum 8 characters or width / 25,
+     * whatever is smaller.
+     *
+     * @return void
+     */
+    public function createPhrase()
+    {
+        $len = intval(min(8, $this->_width / 25));
+        $options = $this->_textPasswordOptions;
+        $textPassword = new Text_Password();
+        if (!is_array($options) || count($options) === 0) {
+            $this->setPhrase($textPassword->create($len));
+        } else {
+            if (count($options) === 1) {
+                $this->setPhrase($textPassword->create($len, $options[0]));
+            } else {
+                $this->setPhrase(
+                    $textPassword->create($len, $options[0], $options[1])
+                );
+            }
+        }
+    }
+}

+ 268 - 0
main/inc/lib/pear/Text/CAPTCHA/Driver/Numeral.php

@@ -0,0 +1,268 @@
+<?php
+/**
+ * Class used for numeral captchas
+ *
+ * PHP version 5
+ *
+ * @category Text
+ * @package  Text_CAPTCHA
+ * @author   David Coallier <davidc@agoraproduction.com>
+ * @author   Christian Wenz <wenz@php.net>
+ * @author   Michael Cramer <michael@bigmichi1.de>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @link     http://pear.php.net/package/Text_CAPTCHA
+ */
+require_once 'Text/CAPTCHA/Driver/Base.php';
+/**
+ * Class used for numeral captchas
+ *
+ * This class is intended to be used to generate numeral captchas as such as:
+ * Example:
+ *  Give me the answer to "54 + 2" to prove that you are human.
+ *
+ * @category Text
+ * @package  Text_CAPTCHA
+ * @author   David Coallier <davidc@agoraproduction.com>
+ * @author   Christian Wenz <wenz@php.net>
+ * @author   Michael Cramer <michael@bigmichi1.de>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @link     http://pear.php.net/package/Text_CAPTCHA
+ */
+class Text_CAPTCHA_Driver_Numeral extends Text_CAPTCHA_Driver_Base
+{
+    /**
+     * This variable holds the minimum range value default set to "1".
+     *
+     * @var integer $_minValue The minimum value of the number range.
+     */
+    private $_minValue = 1;
+
+    /**
+     * This variable holds the maximum range value default set to "50".
+     *
+     * @var integer $_maxValue The maximum value of the number range.
+     */
+    private $_maxValue = 50;
+
+    /**
+     * The valid operators to use in the numeral captcha. We could use / and * but
+     * not yet.
+     *
+     * @var array $_operators The operations for the captcha.
+     */
+    private $_operators = array('-', '+');
+
+    /**
+     * This variable is basically the operation that we're going to be using in the
+     * numeral captcha we are about to generate.
+     *
+     * @var string $_operator The operation's operator to use.
+     */
+    private $_operator = '';
+
+    /**
+     * This variable holds the first number of the numeral operation we are about to
+     * generate.
+     *
+     * @var integer $_firstNumber The first number of the operation.
+     */
+    private $_firstNumber = 0;
+
+    /**
+     * This variable holds the value of the second variable of the operation we are
+     * about to generate for the captcha.
+     *
+     * @var integer $_secondNumber The second number of the operation.
+     */
+    private $_secondNumber = 0;
+
+    /**
+     * Initialize numeric CAPTCHA.
+     *
+     * @param array $options CAPTCHA options with these keys:<br>
+     *                       minValue minimum value<br>
+     *                       maxValue maximum value
+     *
+     * @return void
+     */
+    public function initDriver($options = array())
+    {
+        if (isset($options['minValue'])) {
+            $this->_minValue = (int)$options['minValue'];
+        } else {
+            $this->_minValue = 1;
+        }
+        if (isset($options['maxValue'])) {
+            $this->_maxValue = (int)$options['maxValue'];
+        } else {
+            $this->_maxValue = 50;
+        }
+        if (isset($options['operator'])) {
+            $this->_operator = $options['operator'];
+        } else {
+            $this->_operator = '';
+        }
+        if (isset($options['firstValue'])) {
+            $this->_firstNumber = (int)$options['firstValue'];
+        } else {
+            $this->_firstNumber = 0;
+        }
+        if (isset($options['secondValue'])) {
+            $this->_secondNumber = (int)$options['secondValue'];
+        } else {
+            $this->_secondNumber = 0;
+        }
+    }
+
+    /**
+     * Create the CAPTCHA (the numeral expression).
+     *
+     * This function determines a random numeral expression and set the associated
+     * class properties.
+     *
+     * @return void
+     * @see _generateFirstNumber()
+     * @see _generateSecondNumber()
+     * @see _generateOperator()
+     * @see _generateOperation()
+     */
+    public function createCAPTCHA()
+    {
+        if ($this->_firstNumber == 0) {
+            $this->_firstNumber = $this->_generateNumber();
+        }
+        if ($this->_secondNumber == 0) {
+            $this->_secondNumber = $this->_generateNumber();
+        }
+        if (empty($this->_operator)) {
+            $this->_operator = $this->_operators[array_rand($this->_operators)];
+        }
+        $this->_generateOperation();
+    }
+
+    /**
+     * Set operation.
+     *
+     * This variable sets the operation variable by taking the firstNumber,
+     * secondNumber and operator.
+     *
+     * @return void
+     * @see _operation
+     * @see _firstNumber
+     * @see _operator
+     * @see _secondNumber
+     */
+    private function _setOperation()
+    {
+        $this->setCaptcha(
+            $this->_firstNumber . ' ' . $this->_operator . ' ' . $this->_secondNumber
+        );
+    }
+
+    /**
+     * Generate a number.
+     *
+     * This function takes the parameters that are in the $this->_maxValue and
+     * $this->_minValue and get the random number from them using mt_rand().
+     *
+     * @return integer Random value between _minValue and _maxValue
+     * @see _minValue
+     * @see _maxValue
+     */
+    private function _generateNumber()
+    {
+        return mt_rand($this->_minValue, $this->_maxValue);
+    }
+
+    /**
+     * Adds values.
+     *
+     * This function will add the firstNumber and the secondNumber value and then
+     * call setAnswer to set the answer value.
+     *
+     * @return void
+     * @see _firstNumber
+     * @see _secondNumber
+     * @see _setAnswer()
+     */
+    private function _doAdd()
+    {
+        $phrase = $this->_firstNumber + $this->_secondNumber;
+        $this->setPhrase($phrase);
+    }
+
+    /**
+     * Does a subtract on the values.
+     *
+     * This function executes a subtraction on the firstNumber and the secondNumber
+     * to then call $this->setAnswer to set the answer value.
+     *
+     * If the _firstNumber value is smaller than the _secondNumber value then we
+     * regenerate the first number and regenerate the operation.
+     *
+     * @return void
+     * @see _firstNumber
+     * @see _secondNumber
+     * @see _setOperation()
+     * @see Text_CAPTCHA::setPhrase()
+     */
+    private function _doSubtract()
+    {
+        $first = $this->_firstNumber;
+        $second = $this->_secondNumber;
+
+        /**
+         * Check if firstNumber is smaller than secondNumber
+         */
+        if ($first < $second) {
+            $this->_firstNumber = $second;
+            $this->_secondNumber = $first;
+            $this->_setOperation();
+        }
+
+        $phrase = $this->_firstNumber - $this->_secondNumber;
+        $this->setPhrase($phrase);
+    }
+
+    /**
+     * Generate the operation
+     *
+     * This function will call the _setOperation() function to set the operation
+     * string that will be called to display the operation, and call the function
+     * necessary depending on which operation is set by this->operator.
+     *
+     * @return void
+     * @see _setOperation()
+     * @see _operator
+     * @see _doAdd()
+     * @see _doSubtract()
+     */
+    private function _generateOperation()
+    {
+        $this->_setOperation();
+        switch ($this->_operator) {
+        case '+':
+            $this->_doAdd();
+            break;
+        case '-':
+            $this->_doSubtract();
+            break;
+        default:
+            $this->_operator = "+";
+            $this->_setOperation();
+            $this->_doAdd();
+            break;
+        }
+    }
+
+    /**
+     * Create random CAPTCHA phrase. This method is a placeholder, since the equation
+     * is created in createCAPTCHA()
+     *
+     * @return string
+     */
+    public function createPhrase()
+    {
+        $this->setCaptcha(null);
+    }
+}

+ 127 - 0
main/inc/lib/pear/Text/CAPTCHA/Driver/Word.php

@@ -0,0 +1,127 @@
+<?php
+/**
+ * Text_CAPTCHA_Driver_Word - Text_CAPTCHA driver word CAPTCHAs
+ * Class to create a textual Turing test
+ *
+ * PHP version 5
+ *
+ * @category Text
+ * @package  Text_CAPTCHA
+ * @author   Tobias Schlitt <schlitt@php.net>
+ * @author   Christian Wenz <wenz@php.net>
+ * @author   Michael Cramer <michael@bigmichi1.de>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @link     http://pear.php.net/package/Text_CAPTCHA
+ */
+require_once 'Text/CAPTCHA/Driver/Base.php';
+require_once 'Numbers/Words.php';
+/**
+ * Require Numbers_Words class for generating the text.
+ *
+ * @category Text
+ * @package  Text_CAPTCHA
+ * @author   Tobias Schlitt <schlitt@php.net>
+ * @author   Christian Wenz <wenz@php.net>
+ * @author   Michael Cramer <michael@bigmichi1.de>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @link     http://pear.php.net/package/Text_CAPTCHA
+ */
+class Text_CAPTCHA_Driver_Word extends Text_CAPTCHA_Driver_Base
+{
+    /**
+     * Phrase length.
+     * This variable holds the length of the Word.
+     *
+     * @var integer
+     */
+    private $_length;
+
+    /**
+     * Numbers_Words mode.
+     * This variable holds the mode for Numbers_Words.
+     *
+     * @var String
+     */
+    private $_mode;
+
+    /**
+     * Locale
+     * This variable holds the locale for Numbers_Words
+     *
+     * @var string
+     */
+    private $_locale;
+
+    /**
+     * Initializes the new Text_CAPTCHA_Driver_Word object.
+     *
+     * @param array $options CAPTCHA options with these keys:<br>
+     *                       phrase  The "secret word" of the CAPTCHA<br>
+     *                       length  The number of characters in the phrase<br>
+     *                       locale  The locale for Numbers_Words<br>
+     *                       mode    The mode for Numbers_Words
+     *
+     * @return void
+     */
+    public function initDriver($options = array())
+    {
+        if (isset($options['length']) && is_int($options['length'])) {
+            $this->_length = $options['length'];
+        } else {
+            $this->_length = 4;
+        }
+        if (isset($options['phrase']) && !empty($options['phrase'])) {
+            $this->setPhrase((string)$options['phrase']);
+        } else {
+            $this->createPhrase();
+        }
+        if (isset($options['mode']) && !empty($options['mode'])) {
+            $this->_mode = $options['mode'];
+        } else {
+            $this->_mode = 'single';
+        }
+        if (isset($options['locale']) && !empty($options['locale'])) {
+            $this->_locale = $options['locale'];
+        } else {
+            $this->_locale = 'en_US';
+        }
+    }
+
+    /**
+     * Create random CAPTCHA phrase, "Word edition" (numbers only).
+     * This method creates a random phrase
+     *
+     * @return void
+     */
+    public function createPhrase()
+    {
+        $phrase = new Text_Password();
+        $this->setPhrase(
+            $phrase->create(
+                $this->_length, 'unpronounceable', 'numeric'
+            )
+        );
+    }
+
+    /**
+     * Place holder for the real _createCAPTCHA() method
+     * used by extended classes to generate CAPTCHA from phrase
+     *
+     * @return void
+     */
+    public function createCAPTCHA()
+    {
+        $res = '';
+        $numberWords = new Numbers_Words();
+        $phrase = $this->getPhrase();
+        if ($this->_mode == 'single') {
+            $phraseArr = str_split($phrase);
+            for ($i = 0; $i < strlen($phrase); $i++) {
+                $res .= ' ' . $numberWords->toWords($phraseArr[$i], $this->_locale);
+            }
+        } else {
+            $res = $numberWords->toWords($phrase, $this->_locale);
+        }
+        $this->setCaptcha($res);
+    }
+}

+ 28 - 0
main/inc/lib/pear/Text/CAPTCHA/Exception.php

@@ -0,0 +1,28 @@
+<?php
+/**
+ * Exception for Text_CAPTCHA
+ *
+ * PHP version 5
+ *
+ * @category Text
+ * @package  Text_CAPTCHA
+ * @author   Christian Weiske <cweiske@php.net>
+ * @author   Christian Wenz <wenz@php.net>
+ * @author   Michael Cramer <michael@bigmichi1.de>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @link     http://pear.php.net/package/Text_CAPTCHA
+ */
+require_once 'PEAR/Exception.php';
+/**
+ * Exception for Text_CAPTCHA
+ *
+ * @category Text
+ * @package  Text_CAPTCHA
+ * @author   Michael Cramer <michael@bigmichi1.de>
+ * @license  http://www.opensource.org/licenses/bsd-license.php BSD License
+ * @link     http://pear.php.net/package/Text_CAPTCHA
+ */
+class Text_CAPTCHA_Exception extends PEAR_Exception
+{
+
+}

+ 533 - 0
main/inc/lib/pear/Text/Password.php

@@ -0,0 +1,533 @@
+<?php
+/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
+
+/**
+ * Class to create passwords
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to version 3.0 of the PHP license
+ * that is available through the world-wide-web at the following URI:
+ * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
+ * the PHP License and are unable to obtain it through the web, please
+ * send a note to license@php.net so we can mail you a copy immediately.
+ *
+ * @category   Text
+ * @package    Text_Password
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Olivier Vanhoucke <olivier@php.net>
+ * @copyright  2004-2005 Martin Jansen, Olivier Vanhoucke
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    CVS: $Id: Password.php,v 1.18 2008/11/30 13:38:56 mj Exp $
+ * @link       http://pear.php.net/package/Text_Password
+ */
+
+/**
+ * Number of possible characters in the password
+ */
+$GLOBALS['_Text_Password_NumberOfPossibleCharacters'] = 0;
+
+/**
+ * Main class for the Text_Password package
+ *
+ * @category   Text
+ * @package    Text_Password
+ * @author     Martin Jansen <mj@php.net>
+ * @author     Olivier Vanhoucke <olivier@php.net>
+ * @copyright  2004-2005 Martin Jansen, Olivier Vanhoucke
+ * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
+ * @version    Release: @package_version@
+ * @link       http://pear.php.net/package/Text_Password
+ */
+class Text_Password {
+
+    /**
+     * Create a single password.
+     *
+     * @access public
+     * @param  integer Length of the password.
+     * @param  string  Type of password (pronounceable, unpronounceable)
+     * @param  string  Character which could be use in the
+     *                 unpronounceable password ex : 'ABCDEFG'
+     *                 or numeric, alphabetical or alphanumeric.
+     * @return string  Returns the generated password.
+     */
+    function create($length = 10, $type = 'pronounceable', $chars = '')
+    {
+        switch ($type) {
+        case 'unpronounceable' :
+            return Text_Password::_createUnpronounceable($length, $chars);
+
+        case 'pronounceable' :
+        default :
+            return Text_Password::_createPronounceable($length);
+        }
+    }
+
+    /**
+     * Create multiple, different passwords
+     *
+     * Method to create a list of different passwords which are
+     * all different.
+     *
+     * @access public
+     * @param  integer Number of different password
+     * @param  integer Length of the password
+     * @param  string  Type of password (pronounceable, unpronounceable)
+     * @param  string  Character which could be use in the
+     *                 unpronounceable password ex : 'A,B,C,D,E,F,G'
+     *                 or numeric, alphabetical or alphanumeric.
+     * @return array   Array containing the passwords
+     */
+    function createMultiple($number, $length = 10, $type = 'pronounceable', $chars = '')
+    {
+        $passwords = array();
+
+        while ($number > 0) {
+            while (true) {
+                $password = Text_Password::create($length, $type, $chars);
+                if (!in_array($password, $passwords)) {
+                    $passwords[] = $password;
+                    break;
+                }
+            }
+            $number--;
+        }
+        return $passwords;
+    }
+
+    /**
+     * Create password from login
+     *
+     * Method to create password from login
+     *
+     * @access public
+     * @param  string  Login
+     * @param  string  Type
+     * @param  integer Key
+     * @return string
+     */
+    function createFromLogin($login, $type, $key = 0)
+    {
+        switch ($type) {
+        case 'reverse':
+            return strrev($login);
+
+        case 'shuffle':
+            return Text_Password::_shuffle($login);
+
+        case 'xor':
+            return Text_Password::_xor($login, $key);
+
+        case 'rot13':
+            return str_rot13($login);
+
+        case 'rotx':
+            return Text_Password::_rotx($login, $key);
+
+        case 'rotx++':
+            return Text_Password::_rotxpp($login, $key);
+
+        case 'rotx--':
+            return Text_Password::_rotxmm($login, $key);
+
+        case 'ascii_rotx':
+            return Text_Password::_asciiRotx($login, $key);
+
+        case 'ascii_rotx++':
+            return Text_Password::_asciiRotxpp($login, $key);
+
+        case 'ascii_rotx--':
+            return Text_Password::_asciiRotxmm($login, $key);
+        }
+    }
+
+    /**
+     * Create multiple, different passwords from an array of login
+     *
+     * Method to create a list of different password from login
+     *
+     * @access public
+     * @param  array   Login
+     * @param  string  Type
+     * @param  integer Key
+     * @return array   Array containing the passwords
+     */
+    function createMultipleFromLogin($login, $type, $key = 0)
+    {
+        $passwords = array();
+        $number    = count($login);
+        $save      = $number;
+
+        while ($number > 0) {
+            while (true) {
+                $password = Text_Password::createFromLogin($login[$save - $number], $type, $key);
+                if (!in_array($password, $passwords)) {
+                    $passwords[] = $password;
+                    break;
+                }
+            }
+            $number--;
+        }
+        return $passwords;
+    }
+
+    /**
+     * Helper method to create password
+     *
+     * Method to create a password from a login
+     *
+     * @access private
+     * @param  string  Login
+     * @param  integer Key
+     * @return string
+     */
+    function _xor($login, $key)
+    {
+        $tmp = '';
+
+        for ($i = 0; $i < strlen($login); $i++) {
+            $next = ord($login{$i}) ^ $key;
+            if ($next > 255) {
+                $next -= 255;
+            } elseif ($next < 0) {
+                $next += 255;
+            }
+            $tmp .= chr($next);
+        }
+
+        return $tmp;
+    }
+
+    /**
+     * Helper method to create password
+     *
+     * Method to create a password from a login
+     * lowercase only
+     *
+     * @access private
+     * @param  string  Login
+     * @param  integer Key
+     * @return string
+     */
+    function _rotx($login, $key)
+    {
+        $tmp = '';
+        $login = strtolower($login);
+
+        for ($i = 0; $i < strlen($login); $i++) {
+            if ((ord($login{$i}) >= 97) && (ord($login{$i}) <= 122)) { // 65, 90 for uppercase
+                $next = ord($login{$i}) + $key;
+                if ($next > 122) {
+                    $next -= 26;
+                } elseif ($next < 97) {
+                    $next += 26;
+                }
+                $tmp .= chr($next);
+            } else {
+                $tmp .= $login{$i};
+            }
+        }
+
+        return $tmp;
+    }
+
+    /**
+     * Helper method to create password
+     *
+     * Method to create a password from a login
+     * lowercase only
+     *
+     * @access private
+     * @param  string  Login
+     * @param  integer Key
+     * @return string
+     */
+    function _rotxpp($login, $key)
+    {
+        $tmp = '';
+        $login = strtolower($login);
+
+        for ($i = 0; $i < strlen($login); $i++, $key++) {
+            if ((ord($login{$i}) >= 97) && (ord($login{$i}) <= 122)) { // 65, 90 for uppercase
+                $next = ord($login{$i}) + $key;
+                if ($next > 122) {
+                    $next -= 26;
+                } elseif ($next < 97) {
+                    $next += 26;
+                }
+                $tmp .= chr($next);
+            } else {
+                $tmp .= $login{$i};
+            }
+        }
+
+        return $tmp;
+    }
+
+    /**
+     * Helper method to create password
+     *
+     * Method to create a password from a login
+     * lowercase only
+     *
+     * @access private
+     * @param  string  Login
+     * @param  integer Key
+     * @return string
+     */
+    function _rotxmm($login, $key)
+    {
+        $tmp = '';
+        $login = strtolower($login);
+
+        for ($i = 0; $i < strlen($login); $i++, $key--) {
+            if ((ord($login{$i}) >= 97) && (ord($login{$i}) <= 122)) { // 65, 90 for uppercase
+                $next = ord($login{$i}) + $key;
+                if ($next > 122) {
+                    $next -= 26;
+                } elseif ($next < 97) {
+                    $next += 26;
+                }
+                $tmp .= chr($next);
+            } else {
+                $tmp .= $login{$i};
+            }
+        }
+
+        return $tmp;
+    }
+
+    /**
+     * Helper method to create password
+     *
+     * Method to create a password from a login
+     *
+     * @access private
+     * @param  string  Login
+     * @param  integer Key
+     * @return string
+     */
+    function _asciiRotx($login, $key)
+    {
+        $tmp = '';
+
+        for ($i = 0; $i < strlen($login); $i++) {
+            $next = ord($login{$i}) + $key;
+            if ($next > 255) {
+                $next -= 255;
+            } elseif ($next < 0) {
+                $next += 255;
+            }
+            switch ($next) { // delete white space
+            case 0x09:
+            case 0x20:
+            case 0x0A:
+            case 0x0D:
+                $next++;
+            }
+            $tmp .= chr($next);
+        }
+
+        return $tmp;
+    }
+
+    /**
+     * Helper method to create password
+     *
+     * Method to create a password from a login
+     *
+     * @access private
+     * @param  string  Login
+     * @param  integer Key
+     * @return string
+     */
+    function _asciiRotxpp($login, $key)
+    {
+        $tmp = '';
+
+        for ($i = 0; $i < strlen($login); $i++, $key++) {
+            $next = ord($login{$i}) + $key;
+            if ($next > 255) {
+                $next -= 255;
+            } elseif ($next < 0) {
+                $next += 255;
+            }
+            switch ($next) { // delete white space
+            case 0x09:
+            case 0x20:
+            case 0x0A:
+            case 0x0D:
+                $next++;
+            }
+            $tmp .= chr($next);
+        }
+
+        return $tmp;
+    }
+
+    /**
+     * Helper method to create password
+     *
+     * Method to create a password from a login
+     *
+     * @access private
+     * @param  string  Login
+     * @param  integer Key
+     * @return string
+     */
+    function _asciiRotxmm($login, $key)
+    {
+        $tmp = '';
+
+        for ($i = 0; $i < strlen($login); $i++, $key--) {
+            $next = ord($login{$i}) + $key;
+            if ($next > 255) {
+                $next -= 255;
+            } elseif ($next < 0) {
+                $next += 255;
+            }
+            switch ($next) { // delete white space
+            case 0x09:
+            case 0x20:
+            case 0x0A:
+            case 0x0D:
+                $next++;
+            }
+            $tmp .= chr($next);
+        }
+
+        return $tmp;
+    }
+
+    /**
+     * Helper method to create password
+     *
+     * Method to create a password from a login
+     *
+     * @access private
+     * @param  string  Login
+     * @return string
+     */
+    function _shuffle($login)
+    {
+        $tmp = array();
+
+        for ($i = 0; $i < strlen($login); $i++) {
+            $tmp[] = $login{$i};
+        }
+
+        shuffle($tmp);
+
+        return implode($tmp, '');
+    }
+
+    /**
+     * Create pronounceable password
+     *
+     * This method creates a string that consists of
+     * vowels and consonats.
+     *
+     * @access private
+     * @param  integer Length of the password
+     * @return string  Returns the password
+     */
+    function _createPronounceable($length)
+    {
+
+        $retVal = '';
+
+        /**
+         * List of vowels and vowel sounds
+         */
+        $v = array('a', 'e', 'i', 'o', 'u', 'ae', 'ou', 'io',
+                   'ea', 'ou', 'ia', 'ai'
+                   );
+
+        /**
+         * List of consonants and consonant sounds
+         */
+        $c = array('b', 'c', 'd', 'g', 'h', 'j', 'k', 'l', 'm',
+                   'n', 'p', 'r', 's', 't', 'u', 'v', 'w',
+                   'tr', 'cr', 'fr', 'dr', 'wr', 'pr', 'th',
+                   'ch', 'ph', 'st', 'sl', 'cl'
+                   );
+
+        $v_count = 12;
+        $c_count = 29;
+
+        $GLOBALS['_Text_Password_NumberOfPossibleCharacters'] = $v_count + $c_count;
+
+        for ($i = 0; $i < $length; $i++) {
+            $retVal .= $c[mt_rand(0, $c_count-1)] . $v[mt_rand(0, $v_count-1)];
+        }
+
+        return substr($retVal, 0, $length);
+    }
+
+    /**
+     * Create unpronounceable password
+     *
+     * This method creates a random unpronounceable password
+     *
+     * @access private
+     * @param  integer Length of the password
+     * @param  string  Character which could be use in the
+     *                 unpronounceable password ex : 'ABCDEFG'
+     *                 or numeric, alphabetical or alphanumeric.
+     * @return string  Returns the password
+     */
+    function _createUnpronounceable($length, $chars)
+    {
+        $password = '';
+
+        /**
+         * List of character which could be use in the password
+         */
+         switch($chars) {
+
+         case 'alphanumeric':
+             $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+             $GLOBALS['_Text_Password_NumberOfPossibleCharacters'] = 62;
+             break;
+
+         case 'alphabetical':
+             $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
+             $GLOBALS['_Text_Password_NumberOfPossibleCharacters'] = 52;
+             break;
+
+         case 'numeric':
+             $chars = '0123456789';
+             $GLOBALS['_Text_Password_NumberOfPossibleCharacters'] = 10;
+             break;
+
+         case '':
+             $chars = '_#@%&ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
+             $GLOBALS['_Text_Password_NumberOfPossibleCharacters'] = 67;
+             break;
+
+         default:
+             /**
+              * Some characters shouldn't be used
+              */
+             $chars = trim($chars);
+             $chars = str_replace(array('+', '|', '$', '^', '/', '\\', ','), '', $chars);
+
+             $GLOBALS['_Text_Password_NumberOfPossibleCharacters'] = strlen($chars);
+         }
+
+         /**
+          * Generate password
+          */
+         for ($i = 0; $i < $length; $i++) {
+             $num = mt_rand(0, $GLOBALS['_Text_Password_NumberOfPossibleCharacters'] - 1);
+             $password .= $chars{$num};
+         }
+
+         /**
+          * Return password
+          */
+         return $password;
+    }
+}
+?>

+ 7 - 3
main/inc/lib/promotion.lib.php

@@ -86,7 +86,7 @@ class Promotion extends Model {
                         $new_session_list = array();
                         
 						foreach($session_list as $item) {
-							$sid = SessionManager::copy_session($item['id'], true, false, true, true);						
+							$sid = SessionManager::copy_session($item['id'], true, false, false, true);						
                             $new_session_list[] = $sid;
 						}
                         
@@ -218,8 +218,12 @@ class Promotion extends Model {
     }
     
     public function delete($id) {
-    	parent::delete($id);
-    	event_system(LOG_PROMOTION_DELETE, LOG_PROMOTION_ID, $id, api_get_utc_datetime(), api_get_user_id());    	
+    	if (parent::delete($id)) {
+           SessionManager::clear_session_ref_promotion($id);
+    	   event_system(LOG_PROMOTION_DELETE, LOG_PROMOTION_ID, $id, api_get_utc_datetime(), api_get_user_id());
+        } else {
+            return false;
+        }   	
     }
    
     

+ 161 - 47
main/inc/lib/sessionmanager.lib.php

@@ -11,8 +11,13 @@
  * Code
  */
 
-class SessionManager {
-	private function __construct() {
+/**
+ * Class SessionManager
+ */
+class SessionManager
+{
+	private function __construct()
+    {
 	}
 
     /**
@@ -20,7 +25,8 @@ class SessionManager {
      * @param   int     Session ID
      * @return  array   Session details (id, id_coach, name, nbr_courses, nbr_users, nbr_classes, date_start, date_end, nb_days_access_before_beginning,nb_days_access_after_end, session_admin_id)
      */
-    public static function fetch($id) {
+    public static function fetch($id)
+    {
     	$t = Database::get_main_table(TABLE_MAIN_SESSION);
         if ($id != strval(intval($id))) { return array(); }
         $s = "SELECT * FROM $t WHERE id = $id";
@@ -296,7 +302,6 @@ class SessionManager {
         if (!empty($options['order'])) {
             $query .= " ORDER BY ".$options['order'];
         }
-        //var_dump($query);
 
 		$result = Database::query($query);
 		$formatted_sessions = array();
@@ -576,6 +581,17 @@ class SessionManager {
 		event_system(LOG_SESSION_DELETE, LOG_SESSION_ID, $id_checked, api_get_utc_datetime(), $user_id);
 	}
 
+    public static function clear_session_ref_promotion($id_promotion) {
+        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
+        $id_promotion = intval($id_promotion);
+        $update_sql = "UPDATE $tbl_session SET promotion_id=0 WHERE promotion_id='$id_promotion'";
+        if (Database::query($update_sql)) {
+            return true;
+        } else {
+            return false;
+        }
+
+    }
 	 /**
 	  * Subscribes users (students)  to the given session and optionally (default) unsubscribes previous users
 	  * @author Carlos Vargas from existing code
@@ -1712,6 +1728,7 @@ class SessionManager {
             	foreach ($courses as $course) {
             		$short_courses[] = $course;
             	}
+
             }
             $courses = null;
 
@@ -1754,6 +1771,7 @@ class SessionManager {
                          $new_short_courses[] = $course_data['code'];
                     }
                 }
+
                 $short_courses = $new_short_courses;
                 $res = self::add_courses_to_session($sid, $short_courses, true);
                 $short_courses = null;
@@ -1849,16 +1867,52 @@ class SessionManager {
      * true: if the session exists it will be updated
      * false: if session exists a new session will be created adding a counter session1, session2, etc
      * @param int $user_id
+     * @param $logger
+     * @param array convert a file row to an extra field. Example in CSV file there's a SessionID then it will
+     * converted to extra_external_session_id if you set this: array('SessionId' => 'extra_external_session_id')
+     * @param array extra fields
+     * @param string extra field id
+     * @param int $daysCoachAccessBeforeBeginning
+     * @param int $daysCoachAccessAfterBeginning
+     * @param int $sessionVisibility
      * @return array
      */
-    static function importCSV($file, $updatesession, $user_id = null, $debug = false)
+    static function importCSV(
+        $file,
+        $updatesession,
+        $defaultUserId = null,
+        $logger = null,
+        $extraFields = array(),
+        $extraFieldId = null,
+        $daysCoachAccessBeforeBeginning = null,
+        $daysCoachAccessAfterBeginning = null,
+        $sessionVisibility = 1
+    )
     {
         $content = file($file);
+
         $error_message = null;
         $session_counter = 0;
 
-        if (empty($user_id)) {
-            $user_id = api_get_user_id();
+        if (empty($defaultUserId)) {
+            $defaultUserId = api_get_user_id();
+        }
+
+        $eol = PHP_EOL;
+        if (PHP_SAPI !='cli') {
+            $eol = '<br />';
+        }
+
+        $debug = false;
+        if (isset($logger)) {
+            $debug = true;
+        }
+
+        $extraParameters = null;
+
+        if (!empty($daysCoachAccessBeforeBeginning) && !empty($daysCoachAccessAfterBeginning)) {
+            $extraParameters .= ' , nb_days_access_before_beginning = '.intval($daysCoachAccessBeforeBeginning);
+            $extraParameters .= ' , nb_days_access_after_end = '.intval($daysCoachAccessAfterBeginning);
         }
 
         $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
@@ -1866,12 +1920,15 @@ class SessionManager {
         $tbl_session_course         = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
         $tbl_session_course_user    = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
 
+        $sessions = array();
+
         if (!api_strstr($content[0], ';')) {
             $error_message = get_lang('NotCSV');
         } else {
             $tag_names = array();
 
             foreach ($content as $key => $enreg) {
+
                 $enreg = explode(';', trim($enreg));
                 if ($key) {
                     foreach ($tag_names as $tag_key => $tag_name) {
@@ -1893,10 +1950,16 @@ class SessionManager {
                 $user_counter = 0;
                 $course_counter = 0;
 
-                $session_name           = $enreg['SessionName'];
+                if (isset($extraFields) && !empty($extraFields)) {
+                    foreach ($extraFields as $original => $to) {
+                        $enreg[$to] = $enreg[$original];
+                    }
+                }
+
+                $session_name           = Database::escape_string($enreg['SessionName']);
                 $date_start             = $enreg['DateStart'];
                 $date_end               = $enreg['DateEnd'];
-                $visibility             = $enreg['Visibility'];
+                $visibility             = isset($enreg['Visibility']) ? $enreg['Visibility'] : $sessionVisibility;
                 $session_category_id    = $enreg['SessionCategory'];
 
                 // Searching a coach.
@@ -1904,10 +1967,10 @@ class SessionManager {
                     $coach_id = UserManager::get_user_id_from_username($enreg['Coach']);
                     if ($coach_id === false) {
                         // If the coach-user does not exist - I'm the coach.
-                        $coach_id = api_get_user_id();
+                        $coach_id = $defaultUserId;
                     }
                 } else {
-                    $coach_id = api_get_user_id();
+                    $coach_id = $defaultUserId;
                 }
 
                 if (!$updatesession) {
@@ -1919,7 +1982,7 @@ class SessionManager {
                         if ($i > 1) {
                             $suffix = ' - '.$i;
                         }
-                        $sql = 'SELECT 1 FROM '.$tbl_session.' WHERE name="'.Database::escape_string($session_name.$suffix).'"';
+                        $sql = 'SELECT 1 FROM '.$tbl_session.' WHERE name="'.$session_name.$suffix.'"';
                         $rs = Database::query($sql);
 
                         if (Database::result($rs, 0, 0)) {
@@ -1932,26 +1995,46 @@ class SessionManager {
 
                     // Creating the session.
                     $sql_session = "INSERT IGNORE INTO $tbl_session SET
-                            name = '".Database::escape_string($session_name)."',
+                            name = '".$session_name."',
                             id_coach = '$coach_id',
                             date_start = '$date_start',
                             date_end = '$date_end',
                             visibility = '$visibility',
                             session_category_id = '$session_category_id',
-                            session_admin_id=".intval($user_id);
+                            session_admin_id=".intval($defaultUserId).$extraParameters;
                     Database::query($sql_session);
                     $session_id = Database::insert_id();
 
                     if ($debug) {
                         if ($session_id) {
-                            Log::info("Sessions - Session created: #$session_id - $session_name");
+
+                            foreach ($enreg as $key => $value) {
+                                if (substr($key, 0, 6) == 'extra_') { //an extra field
+                                    self::update_session_extra_field_value($session_id, substr($key, 6), $value);
+                                }
+                            }
+
+                            $logger->addInfo("Sessions - Session created: #$session_id - $session_name");
                         } else {
-                            Log::error("Sessions - Session NOT created $session_name");
+                            $logger->addError("Sessions - Session NOT created: $session_name");
                         }
                     }
                     $session_counter++;
                 } else {
-                    $my_session_result = SessionManager::get_session_by_name($session_name);
+                    $sessionId = null;
+
+                    if (isset($extraFields) && !empty($extraFields)) {
+                        $sessionId = self::get_session_id_from_original_id($enreg['extra_'.$extraFieldId], $extraFieldId);
+
+                        if (empty($sessionId)) {
+                            $my_session_result = false;
+                        } else {
+                            $my_session_result = true;
+                        }
+                    } else {
+                        $my_session_result = self::get_session_by_name($session_name);
+                    }
+
                     if ($my_session_result === false) {
 
                         // Creating a session.
@@ -1961,7 +2044,7 @@ class SessionManager {
                                 date_start = '$date_start',
                                 date_end = '$date_end',
                                 visibility = '$visibility',
-                                session_category_id = '$session_category_id'";
+                                session_category_id = '$session_category_id'".$extraParameters;
 
                         Database::query($sql_session);
                         // We get the last insert id.
@@ -1970,23 +2053,51 @@ class SessionManager {
 
                         if ($debug) {
                             if ($session_id) {
-                                Log::info("Sessions - #$session_id created: $session_name");
+                                foreach ($enreg as $key => $value) {
+                                    if (substr($key, 0, 6) == 'extra_') { //an extra field
+                                        self::update_session_extra_field_value($session_id, substr($key, 6), $value);
+                                    }
+                                }
+                                $logger->addInfo("Sessions - #$session_id created: $session_name");
                             } else {
-                                Log::error("Sessions - Session NOT created $session_name");
+                                $logger->addError("Sessions - Session NOT created: $session_name");
                             }
                         }
                     } else {
-                        // The session already exists, update it then.
-                        $sql_session = "UPDATE $tbl_session SET
-                                id_coach = '$coach_id',
-                                date_start = '$date_start',
-                                date_end = '$date_end',
-                                visibility = '$visibility',
-                                session_category_id = '$session_category_id'
-                            WHERE name = '$session_name'";
-                        Database::query($sql_session);
-                        $session_id = Database::query("SELECT id FROM $tbl_session WHERE name='$session_name'");
-                        list($session_id) = Database::fetch_array($session_id);
+
+                        if (isset($sessionId) && !empty($sessionId)) {
+                            // The session already exists, update it then.
+                            $sql_session = "UPDATE $tbl_session SET
+                                    id_coach = '$coach_id',
+                                    date_start = '$date_start',
+                                    date_end = '$date_end',
+                                    visibility = '$visibility',
+                                    session_category_id = '$session_category_id'
+                                WHERE id = '$sessionId'";
+                            //name = '$session_name'
+                            Database::query($sql_session);
+                            $session_id = $sessionId;
+                        } else {
+
+                            // The session already exists, update it then.
+                            $sql_session = "UPDATE $tbl_session SET
+                                    id_coach = '$coach_id',
+                                    date_start = '$date_start',
+                                    date_end = '$date_end',
+                                    visibility = '$visibility',
+                                    session_category_id = '$session_category_id'
+                                WHERE name = '$session_name'";
+                            Database::query($sql_session);
+                            $row = Database::query("SELECT id FROM $tbl_session WHERE name='$session_name'");
+                            list($session_id) = Database::fetch_array($row);
+                        }
+
+                        foreach ($enreg as $key => $value) {
+                            if (substr($key, 0, 6) == 'extra_') { //an extra field
+                                self::update_session_extra_field_value($session_id, substr($key, 6), $value);
+                            }
+                        }
+
                         Database::query("DELETE FROM $tbl_session_user WHERE id_session='$session_id'");
                         Database::query("DELETE FROM $tbl_session_course WHERE id_session='$session_id'");
                         Database::query("DELETE FROM $tbl_session_course_user WHERE id_session='$session_id'");
@@ -2003,11 +2114,11 @@ class SessionManager {
                         if ($user_id !== false) {
                             // Insert new users.
                             $sql = "INSERT IGNORE INTO $tbl_session_user SET
-                                    id_user='$user_id',
+                                    id_user = '$user_id',
                                     id_session = '$session_id'";
                             Database::query($sql);
                             if ($debug) {
-                                Log::info("Sessions - Adding User #$user_id to session #$session_id");
+                                $logger->addInfo("Sessions - Adding User #$user_id ($user) to session #$session_id");
                             }
                             $user_counter++;
                         }
@@ -2021,9 +2132,6 @@ class SessionManager {
 
                     if (CourseManager::course_exists($course_code)) {
 
-                        // If the course exists we continue.
-                        $course_info = CourseManager::get_course_information($course_code);
-
                         $coach = api_strstr($course, '[');
                         $coach = api_substr($coach, 1, api_strpos($coach,']') - 1);
 
@@ -2043,7 +2151,7 @@ class SessionManager {
                         Database::query($sql_course);
 
                         if ($debug) {
-                            Log::info("Sessions - Adding course '$course_code' to session #$session_id");
+                            $logger->addInfo("Sessions - Adding course '$course_code' to session #$session_id");
                         }
                         $course_counter++;
 
@@ -2070,10 +2178,10 @@ class SessionManager {
                                             status = 2 ";
                                     Database::query($sql);
                                     if ($debug) {
-                                        Log::info("Sessions - Adding course coach: user #$coach_id to course: '$course_code' and session #$session_id");
+                                        $logger->addInfo("Sessions - Adding course coach: user #$coach_id ($course_coach) to course: '$course_code' and session #$session_id");
                                     }
                                 } else {
-                                    $error_message .= get_lang('UserDoesNotExist').' : '.$user.'<br />';
+                                    $error_message .= get_lang('UserDoesNotExist').' : '.$course_coach.$eol;
                                 }
                             }
                         }
@@ -2081,8 +2189,10 @@ class SessionManager {
                         $users_in_course_counter = 0;
 
                         // Adding the relationship "Session - Course - User".
+
                         foreach ($course_users as $user) {
                             $user_id = UserManager::get_user_id_from_username($user);
+
                             if ($user_id !== false) {
                                 $sql = "INSERT IGNORE INTO $tbl_session_course_user SET
                                         id_user='$user_id',
@@ -2090,13 +2200,14 @@ class SessionManager {
                                         id_session = '$session_id'";
                                 Database::query($sql);
                                 if ($debug) {
-                                    Log::info("Sessions - Adding student: user #$user_id to course: '$course_code' and session #$session_id");
+                                    $logger->addInfo("Sessions - Adding student: user #$user_id ($user) to course: '$course_code' and session #$session_id");
                                 }
                                 $users_in_course_counter++;
                             } else {
-                                $error_message .= get_lang('UserDoesNotExist').' : '.$user.'<br />';
+                                $error_message .= get_lang('UserDoesNotExist').': '.$user.$eol;
                             }
                         }
+
                         $sql = "UPDATE $tbl_session_course SET nbr_users='$users_in_course_counter' WHERE course_code='$course_code'";
                         Database::query($sql);
 
@@ -2106,11 +2217,13 @@ class SessionManager {
                         // TODO: We should create the course as in the XML import.
                     }
 
+
                     if (CourseManager::course_exists($course_code, true)) {
 
                         $list = CourseManager :: get_courses_info_from_visual_code($course_code);
 
                         foreach ($list as $vcourse) {
+
                             if ($vcourse['code'] == $course_code) {
                                 // Ignore, this has already been inserted.
                             } else {
@@ -2126,10 +2239,10 @@ class SessionManager {
                                 Database::query($sql_course);
 
                                 if ($debug) {
-                                    Log::info("Sessions - Adding course to session: course: '".$vcourse['code']."' and session #$session_id");
+                                    $logger->addInfo("Sessions - Adding course to session: course: '".$vcourse['code']."' and session #$session_id");
                                 }
 
-                                // adding coachs to session course user
+                                // Adding coachs to session course user
                                 foreach ($course_coaches as $course_coach) {
                                     $coach_id = UserManager::get_user_id_from_username($course_coach);
                                     if ($coach_id !== false) {
@@ -2141,14 +2254,15 @@ class SessionManager {
                                         Database::query($sql);
 
                                         if ($debug) {
-                                            Log::info("Sessions - Adding coach to session: user #$coach_id course: '".$vcourse['code']."' and session #$session_id");
+                                            $logger->addInfo("Sessions - Adding coach to session: user #$coach_id ($course_coach) course: '".$vcourse['code']."' and session #$session_id");
                                         }
 
                                     } else {
-                                        $error_message .= get_lang('UserDoesNotExist').' : '.$user.'<br />';
+                                        $error_message .= get_lang('UserDoesNotExist').' : '.$course_coach.$eol;
                                     }
                                 }
 
+
                                 $users_in_course_counter = 0;
                                 // Adding the relationship "Session - Course - User".
                                 foreach ($course_users as $user) {
@@ -2161,12 +2275,12 @@ class SessionManager {
                                         Database::query($sql);
 
                                         if ($debug) {
-                                            Log::info("Sessions - Adding user to session: user #$user_id course: '".$vcourse['code']."' and session #$session_id");
+                                            $logger->addInfo("Sessions - Adding user to session: user #$user_id ($user) course: '".$vcourse['code']."' and session #$session_id");
                                         }
 
                                         $users_in_course_counter++;
                                     } else {
-                                        $error_message .= get_lang('UserDoesNotExist').' : '.$user.'<br />';
+                                        $error_message .= get_lang('UserDoesNotExist').' : '.$user.$eol;
                                     }
                                 }
                                 Database::query("UPDATE $tbl_session_course SET nbr_users='$users_in_course_counter' WHERE course_code='".$vcourse['code']."'");

+ 2 - 2
main/inc/lib/symfony/Monolog/Handler/NativeMailerHandler.php

@@ -44,6 +44,6 @@ class NativeMailerHandler extends MailHandler
      */
     protected function send($content)
     {
-        mail($this->to, $this->subject, wordwrap($content, 70), $this->headers);
+        mail($this->to, $this->subject, wordwrap($content, 120), $this->headers);
     }
-}
+}

+ 4 - 4
main/inc/lib/usermanager.lib.php

@@ -177,7 +177,6 @@ class UserManager
                     $emailbody = get_lang('Dear')." ".stripslashes(api_get_person_name($firstName, $lastName)).",\n\n".get_lang('YouAreReg')." ".api_get_setting('siteName')." ".get_lang('WithTheFollowingSettings')."\n\n".get_lang('Username')." : ".$loginName."\n".get_lang('Pass')." : ".stripslashes($original_password)."\n\n".get_lang('Address')." ".api_get_setting('siteName')." ".get_lang('Is')." : ".$_configuration['root_web']."\n\n".get_lang('Problem')."\n\n".get_lang('Formula').",\n\n".api_get_person_name(api_get_setting('administratorName'), api_get_setting('administratorSurname'))."\n".get_lang('Manager')." ".api_get_setting('siteName')."\nT. ".api_get_setting('administratorTelephone')."\n".get_lang('Email')." : ".api_get_setting('emailAdministrator');
                 }
 
-
                 /* MANAGE EVENT WITH MAIL */
                 if (EventsMail::check_if_using_class('user_registration')) {
                     $values["about_user"] = $return;
@@ -536,7 +535,7 @@ class UserManager
         $change_active = 0;
         if ($user_info['active'] != $active) {
             $change_active = 1;
-        }        
+        }
         $sql = "UPDATE $table_user SET
                 lastname='".Database::escape_string($lastname)."',
                 firstname='".Database::escape_string($firstname)."',
@@ -583,7 +582,7 @@ class UserManager
            } else {
                 $event_title = LOG_USER_DISABLE;
            }
-           event_system($event_title, LOG_USER_ID, $user_id, api_get_utc_datetime(), null, null); 
+           event_system($event_title, LOG_USER_ID, $user_id, api_get_utc_datetime(), null, null);
         }
         if (is_array($extra) && count($extra) > 0) {
             $res = true;
@@ -1340,12 +1339,13 @@ class UserManager
         } else {
             $fvalues = Database::escape_string($fvalue);
         }
-        $sqluf = "SELECT * FROM $t_uf WHERE field_variable='$fname'";
+         $sqluf = "SELECT * FROM $t_uf WHERE field_variable='$fname'";
         $resuf = Database::query($sqluf);
         if (Database::num_rows($resuf) == 1) {
             //ok, the field exists
             // Check if enumerated field, if the option is available
             $rowuf = Database::fetch_array($resuf);
+
             switch ($rowuf['field_type']) {
                 case self::USER_FIELD_TYPE_TAG :
                     //4. Tags are process here comes from main/auth/profile.php

+ 54 - 4
main/inc/lib/userportal.lib.php

@@ -332,6 +332,12 @@ class IndexManager {
 				case 'access_url_inactive':
 					$message = get_lang('AccountURLInactive');
 					break;
+                case 'wrong_captcha':
+                    $message = get_lang('TheTextYouEnteredDoesNotMatchThePicture');
+                    break;
+                case 'blocked_by_captcha':
+                    $message = get_lang('AccountBlockedByCaptcha');
+                    break;
                 case 'unrecognize_sso_origin':
                     //$message = get_lang('SSOError');
                     break;
@@ -638,15 +644,59 @@ class IndexManager {
 	 * Adds a form to let users login
 	 * @version 1.1
 	 */
-	function display_login_form() {
+	function display_login_form()
+    {
 		$form = new FormValidator('formLogin', 'POST', null,  null, array('class'=>'form-vertical'));
-        // 'placeholder'=>get_lang('UserName')
-        //'autocomplete'=>"off",
-
 		$form->addElement('text', 'login', get_lang('UserName'), array('class' => 'span2 autocapitalize_off', 'autofocus' => 'autofocus'));
 		$form->addElement('password', 'password', get_lang('Pass'), array('class' => 'span2'));
+        global $_configuration;
+
+        // Captcha
+        $allowCaptcha = isset($_configuration['allow_captcha']) ? $_configuration['allow_captcha'] : false;
+
+        if ($allowCaptcha) {
+
+            $useCaptcha = isset($_SESSION['loginFailed']) ? $_SESSION['loginFailed'] : null;
+
+            if ($useCaptcha) {
+
+                $ajax = api_get_path(WEB_AJAX_PATH).'form.ajax.php?a=get_captcha';
+
+                $options = array(
+                    'width'        => 250,
+                    'height'       => 90,
+                    'callback'     => $ajax.'&var='.basename(__FILE__, '.php'),
+                    'sessionVar'   => basename(__FILE__, '.php'),
+                    'imageOptions' => array(
+                        'font_size' => 20,
+                        'font_path' => api_get_path(LIBRARY_PATH).'pchart/fonts/',
+                        'font_file' => 'tahoma.ttf',
+                        //'output' => 'gif'
+                    )
+                );
+
+                // Minimum options using all defaults (including defaults for Image_Text):
+                //$options = array('callback' => 'qfcaptcha_image.php');
+
+                $captcha_question =  $form->addElement('CAPTCHA_Image', 'captcha_question', '', $options);
+                $form->addElement('static', null, null, get_lang('ClickOnTheImageForANewOne'));
+
+                $form->addElement('text', 'captcha', get_lang('EnterTheLettersYouSee'));
+                $form->addRule('captcha', get_lang('EnterTheCharactersYouReadInTheImage'), 'required', null, 'client');
+
+                $form->addRule('captcha', 'What you entered didn\'t match the picture', 'CAPTCHA', $captcha_question);
+            }
+        }
+
 		$form->addElement('style_submit_button','submitAuth', get_lang('LoginEnter'), array('class' => 'btn'));
+
 		$html = $form->return_form();
+        // The validation is located in the local.inc
+        /*if ($form->validate()) {
+            // Prevent re-use of the same CAPTCHA phrase
+            $captcha_question->destroy();
+        }*/
+
 		if (api_get_setting('openid_authentication') == 'true') {
 			include_once 'main/auth/openid/login.php';
 			$html .= '<div>'.openid_form().'</div>';

+ 93 - 3
main/inc/local.inc.php

@@ -237,15 +237,69 @@ if (!empty($_SESSION['_user']['user_id']) && !($login || $logout)) {
                 WHERE username = '".Database::escape_string($login)."'";
         $result = Database::query($sql);
 
+        $captchaValidated = true;
+        $allowCaptcha = isset($_configuration['allow_captcha']) ? $_configuration['allow_captcha'] : false;
+
         if (Database::num_rows($result) > 0) {
             $uData = Database::fetch_array($result);
 
+            if ($allowCaptcha) {
+
+                // Checking captcha
+                if (isset($_POST['captcha'])) {
+                    // Check captcha
+                    $captchaText = $_POST['captcha'];
+                    /** @var Text_CAPTCHA $obj */
+                    $obj = isset($_SESSION['userportal.lib']) ? $_SESSION['userportal.lib'] : null;
+                    if ($obj) {
+                        $obj->getPhrase();
+                        if ($obj->getPhrase() != $captchaText) {
+                            $captchaValidated = false;
+                        } else {
+                            $captchaValidated = true;
+                        }
+                    }
+                    if (isset($_SESSION['captcha_question'])) {
+                        $captcha_question = $_SESSION['captcha_question'];
+                        $captcha_question->destroy();
+                    }
+                }
+
+                // Redirect to login page
+                if ($captchaValidated == false) {
+                    $loginFailed = true;
+                    Session::erase('_uid');
+                    Session::write('loginFailed', '1');
+
+                    header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=wrong_captcha');
+                    exit;
+                }
+
+                // Check if account is blocked by captcha user extra field see function api_block_account_captcha()
+                $blockedUntilDate = api_get_user_blocked_by_captcha($login);
+
+                if (isset($blockedUntilDate) && !empty($blockedUntilDate)) {
+                    if (time() > api_strtotime($blockedUntilDate, 'UTC')) {
+                        api_clean_account_captcha($login);
+
+                    } else {
+                        $loginFailed = true;
+                        Session::erase('_uid');
+                        Session::write('loginFailed', '1');
+
+                        header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=blocked_by_captcha');
+                        exit;
+                    }
+                }
+            }
+
+
             if ($uData['auth_source'] == PLATFORM_AUTH_SOURCE || $uData['auth_source'] == CAS_AUTH_SOURCE) {
                 //The authentification of this user is managed by Chamilo itself
                 $password = api_get_encrypted_password(trim(stripslashes($password)));
 
                 // Check the user's password
-                if (($password == $uData['password']  OR $cas_login) AND (trim($login) == $uData['username'])) {
+                if (($password == $uData['password'] OR $cas_login) AND (trim($login) == $uData['username'])) {
                     $update_type = UserManager::get_extra_user_data_by_field($uData['user_id'], 'update_type');
                     $update_type= $update_type['update_type'];
                     if (!empty($extAuthSource[$update_type]['updateUser']) && file_exists($extAuthSource[$update_type]['updateUser'])) {
@@ -279,11 +333,12 @@ if (!empty($_SESSION['_user']['user_id']) && !($login || $logout)) {
 
                                             $_user['user_id'] = $uData['user_id'];
                                             $_user['status']  = $uData['status'];
-                                            Session::write('_user',$_user);
+                                            Session::write('_user', $_user);
                                             event_login();
                                             $logging_in = true;
                                         } else {
                                             $loginFailed = true;
+                                            Session::write('loginFailed', '1');
                                             Session::erase('_uid');
                                             header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=access_url_inactive');
                                             exit;
@@ -291,6 +346,7 @@ if (!empty($_SESSION['_user']['user_id']) && !($login || $logout)) {
                                     } else {
                                         $loginFailed = true;
                                         Session::erase('_uid');
+                                        Session::write('loginFailed', '1');
                                         header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=access_url_inactive');
                                         exit;
                                     }
@@ -311,6 +367,7 @@ if (!empty($_SESSION['_user']['user_id']) && !($login || $logout)) {
                                         } else {
                                             $loginFailed = true;
                                             Session::erase('_uid');
+                                            Session::write('loginFailed', '1');
                                             header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=access_url_inactive');
                                             exit;
                                         }
@@ -329,12 +386,14 @@ if (!empty($_SESSION['_user']['user_id']) && !($login || $logout)) {
                         } else {
                             $loginFailed = true;
                             Session::erase('_uid');
+                            Session::write('loginFailed', '1');
                             header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=account_expired');
                             exit;
                         }
                     } else {
                         $loginFailed = true;
                         Session::erase('_uid');
+                        Session::write('loginFailed', '1');
                         header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=account_inactive');
                         exit;
                     }
@@ -342,6 +401,25 @@ if (!empty($_SESSION['_user']['user_id']) && !($login || $logout)) {
                     // login failed: username or password incorrect
                     $loginFailed = true;
                     Session::erase('_uid');
+                    Session::write('loginFailed', '1');
+
+                    if ($allowCaptcha) {
+
+                        if (isset($_SESSION['loginFailedCount'])) {
+                            $_SESSION['loginFailedCount']++;
+                        } else {
+                            $_SESSION['loginFailedCount'] = 1;
+                        }
+
+                        $numberMistakesToBlockAccount = isset($_configuration['captcha_number_mistakes_to_block_account']) ? $_configuration['captcha_number_mistakes_to_block_account'] : 10;
+
+                        if (isset($_SESSION['loginFailedCount'])) {
+                            if ($_SESSION['loginFailedCount'] >= $numberMistakesToBlockAccount) {
+                                api_block_account_captcha($login);
+                            }
+                        }
+                    }
+
                     header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=user_password_incorrect');
                     exit;
                 }
@@ -399,7 +477,9 @@ if (!empty($_SESSION['_user']['user_id']) && !($login || $logout)) {
                 }
             } //end if is_array($extAuthSource)
             if ($loginFailed) { //If we are here username given is wrong
+                Session::write('loginFailed', '1');
                 header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=user_password_incorrect');
+                exit;
             }
         } //end else login failed
     } elseif (api_get_setting('sso_authentication') === 'true' && !in_array('webservices', explode('/', $_SERVER['REQUEST_URI']))) {
@@ -460,6 +540,7 @@ if (!empty($_SESSION['_user']['user_id']) && !($login || $logout)) {
                     //Request comes from unknown source
                     $loginFailed = true;
                     Session::erase('_uid');
+                    Session::write('loginFailed', '1');
                     header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=unrecognize_sso_origin');
                     exit;
                 }
@@ -506,12 +587,14 @@ if (!empty($_SESSION['_user']['user_id']) && !($login || $logout)) {
                                 } else {
                                     $loginFailed = true;
                                     Session::erase('_uid');
+                                    Session::write('loginFailed', '1');
                                     header('Location: index.php?loginFailed=1&error=account_expired');
                                     exit;
                                 }
                             } else {
                                 $loginFailed = true;
                                 Session::erase('_uid');
+                                Session::write('loginFailed', '1');
                                 header('Location: index.php?loginFailed=1&error=account_inactive');
                                 exit;
                             }
@@ -524,6 +607,8 @@ if (!empty($_SESSION['_user']['user_id']) && !($login || $logout)) {
                     } else {
                         //Redirect to the subscription form
                         header('Location: '.api_get_path(WEB_CODE_PATH).'auth/inscription.php?username='.$res['openid.sreg.nickname'].'&email='.$res['openid.sreg.email'].'&openid='.$res['openid.identity'].'&openid_msg=idnotfound');
+                        Session::write('loginFailed', '1');
+                        exit;
                         //$loginFailed = true;
                     }
                 } else {
@@ -576,7 +661,12 @@ if (isset($uidReset) && $uidReset) {    // session data refresh requested
     $is_allowedCreateCourse = false;
 
     if (isset($_user['user_id']) && $_user['user_id'] && ! api_is_anonymous()) {
-    // a uid is given (log in succeeded)
+        // a uid is given (log in succeeded)
+
+        $_SESSION['loginFailed'] = false;
+        unset($_SESSION['loginFailedCount']);
+        unset($_SESSION['loginToBlock']);
+
         $user_table     = Database::get_main_table(TABLE_MAIN_USER);
         $admin_table    = Database::get_main_table(TABLE_MAIN_ADMIN);
         $track_e_login  = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_LOGIN);

+ 29 - 20
main/newscorm/embed.php

@@ -1,26 +1,35 @@
 <?php
-    require_once '../inc/global.inc.php';
-    $type = $_REQUEST['type'];
-    $src  = Security::remove_XSS($_REQUEST['src']);
-    if ($type == 'youtube')  {
+/* For licensing terms, see /license.txt */
+
+require_once '../inc/global.inc.php';
+
+$type = $_REQUEST['type'];
+$src  = Security::remove_XSS($_REQUEST['src']);
+if (empty($type) || empty($src)) {
+    api_not_allowed();
+}
+
+switch ($type) {
+    case 'youtube':
         $src = 'http://www.youtube.com/embed/'.$src;
+        $iframe = '<iframe class="youtube-player" type="text/html" width="640" height="385" src="'.$src.'" frameborder="0"></iframe>';
+        break;
+    case 'vimeo':
+        $src = 'http://player.vimeo.com/video/'.$src;
+        $iframe = '<iframe src="'.$src.'" width="640" height="385" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe>';
+        break;
+}
 ?>
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<!doctype html>
 <html>
-  <head>
-    <title></title>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-  </head>
-  <body>
-      <div id="content" style="width: 700px ;margin-left:auto; margin-right:auto;">
+    <head>
+        <meta charset="utf-8">
+        <title></title>
+    </head>
+    <body>
+        <div id="content" style="width: 700px ;margin-left:auto; margin-right:auto;">
         <br />
-        <iframe class="youtube-player" type="text/html" width="640" height="385" src="<?php echo $src; ?>" frameborder="0">
-        </iframe>
-      </div>
-  </body>
+        <?php echo $iframe; ?>
+        </div>
+    </body>
 </html>
-
-<?php 
-    } else {
-        api_not_allowed();
-    }

+ 4 - 0
main/newscorm/learnpath.class.php

@@ -3017,6 +3017,10 @@ class learnpath {
                                 $src  = get_youtube_video_id($file);
                                 $file = 'embed.php?type=youtube&src='.$src;
                             }
+                            if (isVimeoLink($file)) {
+                                $src  = getVimeoLinkId($file);
+                                $file = 'embed.php?type=vimeo&src='.$src;
+                            }
                         } else {
                             // check how much attempts of a exercise exits in lp
                             $lp_item_id = $this->get_current_item_id();

+ 5 - 5
main/newscorm/lp_add_audio.php

@@ -85,7 +85,7 @@ echo $_SESSION['oLP']->build_action_menu();
 
 echo '<div class="row-fluid" style="overflow:hidden">';
 echo '<div id="lp_sidebar" class="span4">';
-echo $_SESSION['oLP']->return_new_tree(null, true); 
+echo $_SESSION['oLP']->return_new_tree(null, true);
 // Show the template list.
 echo '</div>';
 
@@ -93,21 +93,21 @@ echo '<div id="doc_form" class="span8">';
 
 $form = new FormValidator('add_audio', 'post', api_get_self().'?action=add_audio&id='.$lp_item_id, null, array('enctype' => 'multipart/form-data'));
 $form->addElement('header', get_lang('UplUpload'));
-$form->addElement('file', 'file', get_lang('File'));
+$form->addElement('file', 'file', get_lang('AudioFile'), 'style="width: 250px"');
 $form->addElement('hidden', 'id', $lp_item_id);
 
 $lp_item = new learnpathItem($lp_item_id);
 
 if (isset($lp_item->audio) && !empty($lp_item->audio))  {
     $form->addElement('checkbox', 'delete_file', null, get_lang('RemoveAudio'));
-    
+
     $player = '<script type="text/javascript" src="../inc/lib/mediaplayer/swfobject.js"></script>';
     $player .= '<div id="preview"></div><script type="text/javascript">
                     var s1 = new SWFObject("../inc/lib/mediaplayer/player.swf","ply","250","20","9","#FFFFFF");
                     s1.addParam("allowscriptaccess","always");
                     s1.addParam("flashvars","file=../../courses/' . $_course['path'] . '/document/audio/' . $lp_item->audio . '");
                     s1.write("preview");
-                </script>';    
+                </script>';
     $form->addElement('label', get_lang('Preview'), $player);
 }
 $form->addElement('button', 'submit', get_lang('Edit'));
@@ -124,4 +124,4 @@ echo '</div>';
 echo '</div>';
 
 /* FOOTER */
-Display::display_footer();
+Display::display_footer();

+ 1 - 1
main/newscorm/lp_add_item.php

@@ -31,7 +31,7 @@ var load_default_template = '. ((isset($_POST['submit']) || empty($_SERVER['QUER
 
 function FCKeditor_OnComplete( editorInstance ) {
     editorInstance.Events.AttachEvent( \'OnSelectionChange\', check_for_title) ;
-    //document.getElementById(\'frmModel\').innerHTML = "<iframe id=\'frame_template\' name=\'my_frame_template\' height=890px width=220px; frameborder=0 src=\''.api_get_path(WEB_LIBRARY_PATH).'fckeditor/editor/fckdialogframe.html \'>";
+    document.getElementById(\'frmModel\').innerHTML = "<iframe id=\'frame_template\' name=\'my_frame_template\' height=890px width=220px; frameborder=0 src=\''.api_get_path(WEB_LIBRARY_PATH).'fckeditor/editor/fckdialogframe.html \'>";
     loaded = true;
 }
 

+ 15 - 13
main/newscorm/scorm_api.php

@@ -219,7 +219,7 @@ $(document).ready(function() {
 //oxajax = new XAJAXobject();
 
 // This code was moved inside LMSInitialize()
-if (olms.lms_lp_type == 1 || olms.lms_item_type == 'asset') {
+if (olms.lms_lp_type == 1 || olms.lms_item_type == 'asset' || olms.lms_item_type == 'document') {
     xajax_start_timer();
 }
 
@@ -294,7 +294,7 @@ function LMSInitialize() {
 
         logit_scorm('LMSInitialize() with params: '+log);
 
-        if (olms.lms_lp_type == 1 || olms.lms_item_type == 'asset') {
+	if (olms.lms_lp_type == 1 || olms.lms_item_type == 'asset' || olms.lms_item_type == 'document') {
             xajax_start_timer();
         }
 
@@ -1026,7 +1026,7 @@ function addListeners(){
         return;
     }
     //assign event handlers to objects
-    if (olms.lms_lp_type==1 || olms.lms_item_type=='asset'){
+    if (olms.lms_lp_type==1 || olms.lms_item_type=='asset' || olms.lms_item_type == 'document') {
         logit_lms('Chamilo LP or asset',2);
         //if this path is a Chamilo learnpath, then start manual save
         //when something is loaded in there
@@ -1049,7 +1049,7 @@ function lms_save_asset() {
     }
 
     //For scorms do not show stats
-    if (olms.lms_lp_type == 2) {
+    if (olms.lms_lp_type == 2 && olms.lms_lp_item_type != 'document') {
        olms.execute_stats = false;
     }
 
@@ -1057,10 +1057,10 @@ function lms_save_asset() {
         olms.execute_stats = true;
     }
 
-    if (olms.lms_lp_type == 1 || olms.lms_item_type == 'asset') {
+    if (olms.lms_lp_type == 1 || olms.lms_item_type == 'asset' || olms.lms_item_type == 'document') {
         logit_lms('lms_save_asset');
         logit_lms('execute_stats :'+ olms.execute_stats);
-        xajax_save_item(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, olms.score, olms.max, olms.min, olms.lesson_status, olms.session_time, olms.suspend_data, olms.lesson_location, olms.interactions, olms.lms_item_core_exit);
+        xajax_save_item(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, olms.score, olms.max, olms.min, olms.lesson_status, olms.session_time, olms.suspend_data, olms.lesson_location, olms.interactions, olms.lms_item_core_exit, olms.lms_item_type);
         if (olms.item_objectives.length>0) {
             xajax_save_objectives(olms.lms_lp_id,olms.lms_user_id,olms.lms_view_id,olms.lms_item_id,olms.item_objectives);
         }
@@ -1345,12 +1345,12 @@ function switch_item(current_item, next_item){
         if (next_item_type != 'sco' ) {
             //case 1
             logit_lms('Case 1');
-            xajax_save_item(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, olms.score, olms.max, olms.min, olms.lesson_status, olms.asset_timer, olms.suspend_data, olms.lesson_location,olms.interactions, olms.lms_item_core_exit);
+            xajax_save_item(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, olms.score, olms.max, olms.min, olms.lesson_status, olms.asset_timer, olms.suspend_data, olms.lesson_location,olms.interactions, olms.lms_item_core_exit, orig_item_type);
             xajax_switch_item_details(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, next_item);
         } else {
             logit_lms('Case 2');
             //case 2
-            xajax_save_item(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, olms.score, olms.max, olms.min, olms.lesson_status, olms.asset_timer, olms.suspend_data, olms.lesson_location,olms.interactions, olms.lms_item_core_exit);
+            xajax_save_item(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, olms.score, olms.max, olms.min, olms.lesson_status, olms.asset_timer, olms.suspend_data, olms.lesson_location,olms.interactions, olms.lms_item_core_exit, orig_item_type);
             xajax_switch_item_details(olms.lms_lp_id,olms.lms_user_id,olms.lms_view_id,olms.lms_item_id,next_item);
         }
     } else {
@@ -1397,7 +1397,7 @@ function switch_item(current_item, next_item){
         // new status
         savedata('finish');
     }
-    xajax_save_item(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, olms.score, olms.max, olms.min, olms.lesson_status, olms.session_time, olms.suspend_data, olms.lesson_location,olms.interactions, olms.lms_item_core_exit);
+    xajax_save_item(olms.lms_lp_id, olms.lms_user_id, olms.lms_view_id, olms.lms_item_id, olms.score, olms.max, olms.min, olms.lesson_status, olms.session_time, olms.suspend_data, olms.lesson_location,olms.interactions, olms.lms_item_core_exit, olms.lms_item_type);
     */
 
     olms.execute_stats = false;
@@ -1455,10 +1455,11 @@ function switch_item(current_item, next_item){
     }
 
     <?php } else { ?>
+            console.log('loading '+mysrc+' in frame');
             cont_f.attr("src",mysrc);
     <?php } ?>
 
-    if (olms.lms_lp_type==1 || olms.lms_item_type == 'asset'){
+    if (olms.lms_lp_type==1 || olms.lms_item_type == 'asset' || olms.lms_item_type == 'document') {
         xajax_start_timer();
     }
 
@@ -1499,7 +1500,7 @@ function switch_item(current_item, next_item){
  * @return  void
  * @uses lp_ajax_save_item.php through an AJAX call
  */
-function xajax_save_item(lms_lp_id, lms_user_id, lms_view_id, lms_item_id, score, max, min, lesson_status, session_time, suspend_data, lesson_location, interactions, lms_item_core_exit) {
+function xajax_save_item(lms_lp_id, lms_user_id, lms_view_id, lms_item_id, score, max, min, lesson_status, session_time, suspend_data, lesson_location, interactions, lms_item_core_exit, item_type) {
     var params = '';
     params += 'lid='+lms_lp_id+'&uid='+lms_user_id+'&vid='+lms_view_id;
     params += '&iid='+lms_item_id+'&s='+score+'&max='+max+'&min='+min;
@@ -1507,7 +1508,7 @@ function xajax_save_item(lms_lp_id, lms_user_id, lms_view_id, lms_item_id, score
     params += '&suspend='+suspend_data+'&loc='+lesson_location;
     params += '&core_exit='+lms_item_core_exit;
     //console.info(session_time);
-    if (olms.lms_lp_type == 1) {
+    if (olms.lms_lp_type == 1 || item_type == 'document') {
         logit_lms('xajax_save_item with params:' + params);
         $.ajax({
             type:"POST",
@@ -1675,6 +1676,7 @@ function xajax_save_objectives(lms_lp_id,lms_user_id,lms_view_id,lms_item_id,ite
  * @uses    lp_ajax_switch_item.php
  */
 function xajax_switch_item_details(lms_lp_id,lms_user_id,lms_view_id,lms_item_id,next_item) {
+
     var params = {
         'lid': lms_lp_id,
         'uid': lms_user_id,
@@ -1894,4 +1896,4 @@ function attach_glossary_into_scorm(type) {
             });
         }
     }
-}
+}

+ 6 - 2
main/survey/fillsurvey.php

@@ -159,7 +159,9 @@ if (count($_POST) > 0) {
         while ($row = Database::fetch_array($result, 'ASSOC')) {
             $types[$row['question_id']] = $row['type'];
         }
-
+        if ($survey_data['anonymous'] == 0) {
+            $survey_invitation['user'] = api_get_user_id();
+        }
         // Looping through all the post values
         foreach ($_POST as $key => & $value) {
             // If the post value key contains the string 'question' then it is an answer on a question
@@ -219,7 +221,9 @@ if (count($_POST) > 0) {
         while ($row = Database::fetch_array($result, 'ASSOC')) {
             $types[$row['question_id']] = $row['type'];
         }
-
+        if ($survey_data['anonymous'] == 0) {
+            $survey_invitation['user'] = api_get_user_id();
+        }
         // Looping through all the post values
         foreach ($_POST as $key => & $value) {
             // If the post value key contains the string 'question' then it is an answer to a question

+ 26 - 26
main/template/default/layout/head.tpl

@@ -21,21 +21,19 @@
 {% raw %}
 if ((navigator.userAgent.toLowerCase().indexOf('msie') != -1 ) && ( navigator.userAgent.toLowerCase().indexOf('opera') == -1 )) {
     window.attachEvent( 'onunload', function() {
-            window['__flash__removeCallback'] = function ( instance, name ) {
-                try {
-                    if ( instance ) {
-                        instance[name] = null ;
-                    }
-                } catch ( flashEx ) {
+        window['__flash__removeCallback'] = function ( instance, name ) {
+            try {
+                if ( instance ) {
+                    instance[name] = null ;
                 }
-            } ;
+            } catch ( flashEx ) {
+            }
+        } ;
     });
 }
 {% endraw %}
 //]]>
 
-
-
 function setCheckbox(value, table_id) {
     checkboxes = $("#"+table_id+" input:checkbox");
     $.each(checkboxes, function(index, checkbox) {
@@ -141,7 +139,7 @@ $(document).scroll(function() {
         }
     }
 
-    //Admin -> Settings toolbar
+    // Admin -> Settings toolbar.
     if ($('body').width() > 959) {
         if ($('.new_actions').length) {
             if (!$('.new_actions').attr('data-top')) {
@@ -165,7 +163,7 @@ $(document).scroll(function() {
         }
     }
 
-    //Bottom actions
+    // Bottom actions.
     if ($('.bottom_actions').length) {
         if (!$('.bottom_actions').attr('data-top')) {
             // If already fixed, then do nothing
@@ -200,27 +198,28 @@ $(function() {
 
     check_brand();
 
-    //Removes the yellow input in Chrome
+    // Removes the yellow input in Chrome
     if (navigator.userAgent.toLowerCase().indexOf("chrome") >= 0) {
         $(window).load(function(){
             $('input:-webkit-autofill').each(function(){
                 var text = $(this).val();
                 var name = $(this).attr('name');
                 $(this).after(this.outerHTML).remove();
+                //var has_string = $(name).find(":contains('[')");
                 $('input[name=' + name + ']').val(text);
             });
         });
     }
 
-    //Fixes buttons to the new btn class
+    // Fixes buttons to the new btn class.
     if (!$('#button').hasClass('btn')) {
         $("button").addClass('btn');
     }
 
-    //Dropdown effect
+    // Dropdown effect.
     $('.dropdown-toggle').dropdown();
 
-    //Responsive effect
+    // Responsive effect.
     $(".collapse").collapse();
 
     $(".accordion_jquery").accordion({
@@ -230,7 +229,7 @@ $(function() {
         header: ".accordion-heading"
     });
 
-    //Global popup
+    // Global popup
     $('.ajax').on('click', function() {
         var url     = this.href;
         var dialog  = $("#dialog");
@@ -259,16 +258,17 @@ $(function() {
 
         // load remote content
         dialog.load(
-                        url,
-                        {},
-                        function(responseText, textStatus, XMLHttpRequest) {
-                                dialog.dialog({
-                                        modal       : true,
-                                        width       : width_value,
-                                        height      : height_value,
-                                        resizable   : resizable_value
-                                });
-        });
+            url,
+            {},
+            function(responseText, textStatus, XMLHttpRequest) {
+                dialog.dialog({
+                    modal       : true,
+                    width       : width_value,
+                    height      : height_value,
+                    resizable   : resizable_value
+                });
+            }
+        );
         //prevent the browser to follow the link
         return false;
     });

+ 10 - 10
main/template/default/layout/login_form.tpl

@@ -1,22 +1,22 @@
 <div id="login_block" class="well sidebar-nav">
-        
+
 	{{ login_language_form }}
-        
+
     {% if plugin_login_top is not null %}
         <div id="plugin_login_top">
             {{ plugin_login_top }}
         </div>
     {% endif %}
-    
+
+    {{login_failed}}
+
 	{{login_form}}
-        
-	{{login_failed}}
-        
-	{{login_options}} 
-       
-    {% if plugin_login_bottom is not null %}        
+
+	{{login_options}}
+
+    {% if plugin_login_bottom is not null %}
         <div id="plugin_login_bottom">
             {{ plugin_login_bottom }}
         </div>
     {% endif %}
-</div>
+</div>

+ 80 - 0
main/webservices/registration.soap.php

@@ -5145,6 +5145,86 @@ function WSUpdateUserApiKey($params) {
     return $apikey;
 }
 
+/** WSListSessions **/
+
+$server->wsdl->addComplexType(
+    'session',
+    'complexType',
+    'struct',
+    'all',
+    '',
+    array(
+        'id' => array ('name' => 'id'  , 'type' => 'xsd:int'),
+        'title' => array ('name' => 'title', 'type' => 'xsd:string'),
+        'url' => array ('name' => 'url', 'type' => 'xsd:string'),
+        'date_start' => array ('name' => 'date_start', 'type' => 'xsd:string'),
+        'date_end' => array ('name' => 'date_end', 'type' => 'xsd:string'),
+    )
+);
+
+$server->wsdl->addComplexType(
+    'sessions',
+    'complexType',
+    'array',
+    '',
+    'SOAP-ENC:Array',
+    array(),
+    array(
+        array('ref'=>'SOAP:ENC:arrayType',
+            'wsdl:arrayType'=>'tns:session[]')
+    ),
+    'tns:session'
+);
+
+// Register the method to expose
+$server->register('WSListSessions',         // method name
+    array('secret_key' => 'xsd:string',
+        'date_start' => 'xsd:string',
+        'date_end' => 'xsd:string'),      // input parameters
+    array('return' => 'tns:sessions'),             // output parameters
+    'urn:WSRegistration',                         // namespace
+    'urn:WSRegistration#WSListSessions',      // soapaction
+    'rpc',                                      // style
+    'encoded',                                  // use
+    'This service returns a list of sessions'    // documentation
+);
+
+
+/**
+ * Get a list of sessions (id, title, url, date_start, date_end) and
+ * return to caller. Date start can be set to ask only for the sessions
+ * starting at or after this date. Date end can be set to ask only for the
+ * sessions ending before or at this date.
+ * Function registered as service. Returns strings in UTF-8.
+ * @param array List of parameters (security key, date_start and date_end)
+ * @return array Sessions list (id=>[title=>'title',url='http://...',date_start=>'...',date_end=>''])
+ */
+function WSListSessions($params) {
+    if(!WSHelperVerifyKey($params)) {
+        return return_error(WS_ERROR_SECRET_KEY);
+    }
+    $sql_params = array();
+    // Dates should be provided in YYYY-MM-DD format, UTC
+    if (!empty($params['date_start'])) {
+        $sql_params['date_start >='] = $params['date_start'];
+    }
+    if (!empty($params['date_end'])) {
+        $sql_params['date_end <='] = $params['date_end'];
+    }
+    $sessions_list = SessionManager::get_sessions_list($sql_params);
+    $return_list = array();
+    foreach ($sessions_list as $session) {
+        $return_list[] = array(
+            'id' => $session['id'],
+            'title' => $session['name'],
+            'url' => api_get_path(WEB_CODE_PATH).'session/index.php?session_id='.$session['id'], // something like http://my.chamilo.net/main/session/index.php?session_id=5
+            'date_start' => $session['date_start'],
+            'date_end' => $session['date_end'],
+        );
+    }
+    return $return_list;
+}
+
 // Use the request to (try to) invoke the service
 $HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
 $server->service($HTTP_RAW_POST_DATA);

+ 27 - 10
main/work/downloadfolder.inc.php

@@ -17,18 +17,20 @@ api_protect_course_script(true);
 require_once 'work.lib.php';
 
 $work_data = get_work_data_by_id($work_id);
+$groupId = api_get_group_id();
 if (empty($work_data)) {
     exit;
 }
 
 //prevent some stuff
 if (empty($path)) {
-	$path = '/';
+    $path = '/';
 }
 
 if (empty($_course) || empty($_course['path'])) {
     api_not_allowed();
 }
+
 $sys_course_path = api_get_path(SYS_COURSE_PATH);
 
 //zip library for creation of the zipfile
@@ -52,19 +54,34 @@ $course_id = api_get_course_int_id();
 
 if (api_is_allowed_to_edit()) {
     //Search for all files that are not deleted => visibility != 2
-    $sql = "SELECT url, title, description, insert_user_id, insert_date, contains_file
+    $sql = "SELECT DISTINCT url, title, description, insert_user_id, insert_date, contains_file
             FROM $tbl_student_publication AS work INNER JOIN $prop_table AS props
-                ON (props.c_id = $course_id AND
+                ON (
+                    props.c_id = $course_id AND
                     work.c_id = $course_id AND
-                    work.id = props.ref)
+                    work.id = props.ref
+                  )
  			WHERE   props.tool='work' AND
  			        work.parent_id = $work_id AND
  			        work.filetype = 'file' AND
- 			        props.visibility<>'2' ";
+ 			        props.visibility<>'2' AND
+ 			        work.post_group_id = $groupId
+            ";
 
 } else {
+    $courseInfo = api_get_course_info();
+    $userCondition = null;
+
+    // All users
+    if ($courseInfo['show_score'] == 0) {
+        // Do another filter
+    } else {
+        // Only teachers
+        $userCondition = " AND props.insert_user_id='".api_get_user_id();
+    }
+
     //for other users, we need to create a zipfile with only visible files and folders
-    $sql = "SELECT url, title, description, insert_user_id, insert_date, contains_file
+    $sql = "SELECT DISTINCT url, title, description, insert_user_id, insert_date, contains_file
             FROM $tbl_student_publication AS work INNER JOIN $prop_table AS props
                 ON (props.c_id = $course_id AND
                     work.c_id = $course_id AND
@@ -72,13 +89,14 @@ if (api_is_allowed_to_edit()) {
            WHERE
                     props.tool='work' AND
                     work.accepted = 1 AND
+                    work.active = 1 AND
                     work.parent_id = $work_id AND
                     work.filetype='file' AND
                     props.visibility = '1' AND
-                    props.insert_user_id='".api_get_user_id()."'
+                    work.post_group_id = $groupId
+                    $userCondition
             ";
 }
-
 $query = Database::query($sql);
 
 //add tem to the zip file
@@ -105,7 +123,6 @@ while ($not_deleted_file = Database::fetch_assoc($query)) {
     }
 }
 
-
 if (!empty($files)) {
     //logging
     event_download(basename($work_data['title']).'.zip (folder)');
@@ -151,4 +168,4 @@ function diff($arr1, $arr2) {
 		}
 	}
 	return $res;
-}
+}

+ 244 - 0
main/work/edit.php

@@ -0,0 +1,244 @@
+<?php
+
+use ChamiloSession as Session;
+
+$language_file = array('exercice', 'work', 'document', 'admin', 'gradebook');
+
+require_once '../inc/global.inc.php';
+$current_course_tool  = TOOL_STUDENTPUBLICATION;
+
+/*	Configuration settings */
+
+api_protect_course_script(true);
+
+// Including necessary files
+require_once 'work.lib.php';
+require_once api_get_path(LIBRARY_PATH).'fileManage.lib.php';
+require_once api_get_path(LIBRARY_PATH).'fileUpload.lib.php';
+require_once api_get_path(LIBRARY_PATH).'fileDisplay.lib.php';
+
+$this_section = SECTION_COURSES;
+
+$work_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : null;
+$item_id = isset($_REQUEST['item_id']) ? intval($_REQUEST['item_id']) : null;
+
+$work_table = Database :: get_course_table(TABLE_STUDENT_PUBLICATION);
+
+$is_allowed_to_edit = api_is_allowed_to_edit();
+$course_id = api_get_course_int_id();
+$user_id = api_get_user_id();
+$session_id = api_get_session_id();
+$course_code = api_get_course_id();
+$course_info = api_get_course_info();
+
+if (empty($work_id) || empty($item_id)) {
+    api_not_allowed(true);
+}
+
+$parent_data = $my_folder_data = get_work_data_by_id($work_id);
+
+if (empty($parent_data)) {
+    api_not_allowed(true);
+}
+
+$is_course_member = CourseManager::is_user_subscribed_in_real_or_linked_course($user_id, $course_code, $session_id);
+$is_course_member = $is_course_member || api_is_platform_admin();
+
+if ($is_course_member == false) {
+    api_not_allowed(true);
+}
+
+$check = Security::check_token('post');
+$token = Security::get_token();
+
+$student_can_edit_in_session = api_is_allowed_to_session_edit(false, true);
+$has_ended   = false;
+
+$is_author = false;
+
+$work_item = get_work_data_by_id($item_id);
+
+//Get the author ID for that document from the item_property table
+$is_author = user_is_author($item_id);
+if (!$is_author) {
+    Display::display_warning_message(get_lang('NotAllowed'));
+    Display::display_footer();
+}
+
+
+if (!empty($my_folder_data)) {
+    $homework = get_work_assignment_by_id($my_folder_data['id']);
+
+    if ($homework['expires_on'] != '0000-00-00 00:00:00' || $homework['ends_on'] != '0000-00-00 00:00:00') {
+        $time_now		= time();
+
+        if (!empty($homework['expires_on']) && $homework['expires_on'] != '0000-00-00 00:00:00') {
+            $time_expires 	= api_strtotime($homework['expires_on'], 'UTC');
+            $difference 	= $time_expires - $time_now;
+            if ($difference < 0) {
+                $has_expired = true;
+            }
+        }
+
+        if (empty($homework['expires_on']) || $homework['expires_on'] == '0000-00-00 00:00:00') {
+            $has_expired = false;
+        }
+
+        if (!empty($homework['ends_on']) && $homework['ends_on'] != '0000-00-00 00:00:00') {
+            $time_ends 		= api_strtotime($homework['ends_on'], 'UTC');
+            $difference2 	= $time_ends - $time_now;
+            if ($difference2 < 0) {
+                $has_ended = true;
+            }
+        }
+
+        $ends_on 	= api_convert_and_format_date($homework['ends_on']);
+        $expires_on = api_convert_and_format_date($homework['expires_on']);
+    }
+}
+
+$interbreadcrumb[] = array('url' => api_get_path(WEB_CODE_PATH).'work/work.php?'.api_get_cidreq(), 'name' => get_lang('StudentPublications'));
+$interbreadcrumb[] = array('url' => api_get_path(WEB_CODE_PATH).'work/work_list.php?'.api_get_cidreq().'&id='.$work_id, 'name' =>  $parent_data['title']);
+
+// form title
+$form_title = get_lang('Edit');
+
+$interbreadcrumb[] = array('url' => '#', 'name'  => $form_title);
+
+$form = new FormValidator('form', 'POST', api_get_self()."?".api_get_cidreq()."&id=".$work_id."&gradebook=".Security::remove_XSS($_GET['gradebook'])."&origin=$origin", '', array('enctype' => "multipart/form-data"));
+$form->addElement('header', $form_title);
+
+$show_progress_bar = false;
+
+if ($submitGroupWorkUrl) {
+    // For user comming from group space to publish his work
+    $realUrl = str_replace($_configuration['root_sys'], api_get_path(WEB_PATH), str_replace("\\", '/', realpath($submitGroupWorkUrl)));
+    $form->addElement('hidden', 'newWorkUrl', $submitGroupWorkUrl);
+    $text_document = $form->addElement('text', 'document', get_lang('Document'));
+    $defaults['document'] = '<a href="' . format_url($submitGroupWorkUrl) . '">' . $realUrl . '</a>';
+    $text_document->freeze();
+} elseif ($item_id && ($is_allowed_to_edit or $is_author)) {
+    $workUrl = $currentCourseRepositoryWeb . $workUrl;
+}
+
+$form->addElement('hidden', 'id', $work_id);
+$form->addElement('hidden', 'item_id', $item_id);
+$form->addElement('text', 'title', get_lang('Title'), array('id' => 'file_upload', 'class' => 'span4'));
+$form->add_html_editor('description', get_lang('Description'), false, false, array('ToolbarSet' => 'Work', 'Width' => '100%', 'Height' => '200'));
+
+$defaults['title'] 			= $work_item['title'];
+$defaults["description"] 	= $work_item['description'];
+$defaults['qualification']  = $work_item['qualification'];
+
+if ($is_allowed_to_edit && !empty($item_id)) {
+    // Get qualification from parent_id that'll allow the validation qualification over
+    $sql = "SELECT qualification FROM $work_table WHERE c_id = $course_id AND id ='$work_id' ";
+    $result = Database::query($sql);
+    $row = Database::fetch_array($result);
+    $qualification_over = $row['qualification'];
+    if (!empty($qualification_over) && intval($qualification_over) > 0) {
+        $form->addElement('text', 'qualification', array(get_lang('Qualification'),  null, " / ".$qualification_over), 'size="10"');
+        $form->addElement('hidden', 'qualification_over', $qualification_over);
+    }
+}
+
+$form->addElement('hidden', 'active',   1);
+$form->addElement('hidden', 'accepted', 1);
+$form->addElement('hidden', 'item_to_edit', $item_id);
+$form->addElement('hidden', 'sec_token', $token);
+
+$text = get_lang('UpdateWork');
+$class = 'save';
+
+// fix the Ok button when we see the tool in the learn path
+$form->addElement('style_submit_button', 'editWork', $text, array('class'=> $class, 'value' => "editWork"));
+
+$form->setDefaults($defaults);
+$error_message = null;
+$_course = api_get_course_info();
+$currentCourseRepositorySys = api_get_path(SYS_COURSE_PATH).$_course['path'] . '/';
+
+$succeed = false;
+if ($form->validate()) {
+    if ($student_can_edit_in_session && $check) {
+
+        if (isset($_POST['editWork'])) {
+            /*
+             * SPECIAL CASE ! For a work edited
+            */
+            //Get the author ID for that document from the item_property table
+            $item_to_edit_id 	= intval($_POST['item_to_edit']);
+            $is_author 			= user_is_author($item_to_edit_id);
+
+            if ($is_author) {
+                $work_data = get_work_data_by_id($item_to_edit_id);
+
+                if (!empty($_POST['title']))
+                    $title 		 = isset($_POST['title']) ? $_POST['title'] : $work_data['title'];
+                    $description = isset($_POST['description']) ? $_POST['description'] : $work_data['description'];
+
+                if ($is_allowed_to_edit && ($_POST['qualification'] !='' )) {
+                    $add_to_update = ', qualificator_id ='."'".api_get_user_id()."',";
+                    $add_to_update .= ' qualification = '."'".Database::escape_string($_POST['qualification'])."',";
+                    $add_to_update .= ' date_of_qualification ='."'".api_get_utc_datetime()."'";
+                }
+
+                if ((int)$_POST['qualification'] > (int)$_POST['qualification_over']) {
+                    Display::display_error_message(get_lang('QualificationMustNotBeMoreThanQualificationOver'));
+                } else {
+                    $sql = "UPDATE  " . $work_table . "
+                            SET	title       = '" . Database::escape_string($title) . "',
+                                description = '" . Database::escape_string($description) . "'
+                                ".$add_to_update."
+                            WHERE c_id = $course_id AND id = $item_to_edit_id";
+                    Database::query($sql);
+                }
+                api_item_property_update($_course, 'work', $item_to_edit_id, 'DocumentUpdated', $user_id);
+                $succeed = true;
+                $error_message .= Display::return_message(get_lang('ItemUpdated'), false);
+            } else {
+                $error_message .= Display::return_message(get_lang('IsNotPosibleSaveTheDocument'), 'error');
+            }
+        } else {
+            $error_message .= Display::return_message(get_lang('IsNotPosibleSaveTheDocument'), 'error');
+        }
+        Security::clear_token();
+    } else {
+        //Bad token or can't add works
+        $error_message = Display::return_message(get_lang('IsNotPosibleSaveTheDocument'), 'error');
+    }
+    $script = 'work_list.php';
+    if ($is_allowed_to_edit) {
+        $script = 'work_list_all.php';
+    }
+
+    header('Location: '.api_get_path(WEB_CODE_PATH).'work/'.$script.'?'.api_get_cidreq().'&id='.$work_id.'&error_message='.$error_message);
+    exit;
+}
+
+$htmlHeadXtra[] = to_javascript_work();
+Display :: display_header(null);
+
+if (!empty($work_id)) {
+    if ($is_allowed_to_edit) {
+        if (api_resource_is_locked_by_gradebook($work_id, LINK_STUDENTPUBLICATION)) {
+            echo Display::display_warning_message(get_lang('ResourceLockedByGradebook'));
+        } else {
+            $form->display();
+        }
+    } elseif ($is_author) {
+        if (empty($work_item['qualificator_id']) || $work_item['qualificator_id'] == 0) {
+            $form->display();
+        } else {
+            Display::display_error_message(get_lang('ActionNotAllowed'));
+        }
+    } elseif ($student_can_edit_in_session && $has_ended == false) {
+        $form->display();
+    } else {
+        Display::display_error_message(get_lang('ActionNotAllowed'));
+    }
+} else {
+    Display::display_error_message(get_lang('ActionNotAllowed'));
+}
+
+Display :: display_footer();

+ 129 - 0
main/work/student_work.php

@@ -0,0 +1,129 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+use ChamiloSession as Session;
+
+$language_file = array('exercice', 'work', 'document', 'admin', 'gradebook');
+
+require_once '../inc/global.inc.php';
+$current_course_tool  = TOOL_STUDENTPUBLICATION;
+
+/*	Configuration settings */
+
+api_protect_course_script(true);
+
+// Including necessary files
+require_once 'work.lib.php';
+$this_section = SECTION_COURSES;
+
+//$workId = isset($_GET['id']) ? intval($_GET['id']) : null;
+$studentId = isset($_GET['studentId']) ? intval($_GET['studentId']) : null;
+
+if (empty($studentId)) {
+    api_not_allowed(true);
+}
+
+$tool_name = get_lang('StudentPublications');
+$group_id = api_get_group_id();
+
+$userInfo = api_get_user_info($studentId);
+
+if (empty($userInfo)) {
+    api_not_allowed(true);
+}
+
+if (!empty($group_id)) {
+    $group_properties  = GroupManager :: get_group_properties($group_id);
+    $show_work = false;
+
+    if (api_is_allowed_to_edit(false, true)) {
+        $show_work = true;
+    } else {
+        // you are not a teacher
+        $show_work = GroupManager::user_has_access($user_id, $group_id, GroupManager::GROUP_TOOL_WORK);
+    }
+
+    if (!$show_work) {
+        api_not_allowed();
+    }
+
+    $interbreadcrumb[] = array ('url' => '../group/group.php', 'name' => get_lang('Groups'));
+    $interbreadcrumb[] = array ('url' => '../group/group_space.php?gidReq='.$group_id, 'name' => get_lang('GroupSpace').' '.$group_properties['name']);
+} else {
+    if (!api_is_course_admin()) {
+        api_not_allowed(true);
+    }
+}
+
+$interbreadcrumb[] = array ('url' => api_get_path(WEB_CODE_PATH).'work/work.php?'.api_get_cidreq(), 'name' => get_lang('StudentPublications'));
+$interbreadcrumb[] = array ('url' => '#', 'name' => $userInfo['complete_name']);
+
+Display :: display_header(null);
+
+echo '<div class="actions">';
+echo '<a href="'.api_get_path(WEB_CODE_PATH).'work/work.php?'.api_get_cidreq().'&origin='.$origin.'&gradebook='.$gradebook.'">'.Display::return_icon('back.png', get_lang('BackToWorksList'),'',ICON_SIZE_MEDIUM).'</a>';
+echo '</div>';
+
+$workPerUser = getWorkPerUser($studentId);
+
+$table = new HTML_Table(array('class' => 'data_table'));
+$column = 0;
+$row = 0;
+$headers = array(get_lang('Title'), get_lang('HandedOutDate'), get_lang('HandedOutDateLimit'), get_lang('Score'), get_lang('Actions'));
+foreach ($headers as $header) {
+    $table->setHeaderContents($row, $column, $header);
+    $column++;
+}
+$row++;
+$column = 0;
+
+foreach ($workPerUser as $work) {
+    $work = $work['work'];
+    $scoreWeight = intval($work->qualification) == 0 ? null : $work->qualification;
+    $workId = $work->id;
+    $workExtraData = get_work_assignment_by_id($workId);
+
+    foreach ($work->user_results as $userResult) {
+        $table->setCellContents($row, $column, $work->title.' ['.strip_tags($userResult['title']).']');
+        $table->setCellAttributes($row, $column, array('width' => '300px'));
+        $column++;
+        $table->setCellContents($row, $column, $userResult['sent_date']);
+        $column++;
+        //$dateQualification = !empty($workExtraData['expires_on']) && $workExtraData['expires_on'] != '0000-00-00 00:00:00' ? api_get_utc_datetime($workExtraData['expires_on']) : '-';
+        $dateQualification = !empty($workExtraData['ends_on']) && $workExtraData['ends_on'] != '0000-00-00 00:00:00' ? api_get_utc_datetime($workExtraData['ends_on']) : '-';
+        $table->setCellContents($row, $column, $dateQualification);
+        $column++;
+        //var_dump($userResult);
+        $score = '-';
+        if (!empty($scoreWeight)) {
+            $score = strip_tags($userResult['qualification'])."/".$scoreWeight;
+        }
+        $table->setCellContents($row, $column, $score);
+        $column++;
+
+        // Actions
+        $links = null;
+
+        if (empty($userResult['url'])) {
+            // is a text
+            $url = api_get_path(WEB_CODE_PATH).'work/view.php?'.api_get_cidreq().'&id='.$userResult['id'];
+            $links .= Display::url(Display::return_icon('default.png'), $url);
+        } else {
+
+            $url = api_get_path(WEB_CODE_PATH).'work/download.php?'.api_get_cidreq().'&id='.$userResult['id'];
+            $links .= Display::url(Display::return_icon('save.png'), $url);
+        }
+
+        $url = api_get_path(WEB_CODE_PATH).'work/edit.php?'.api_get_cidreq().'&item_id='.$userResult['id'].'&id='.$workId.'&parent_id='.$workId;
+        $links .= Display::url(Display::return_icon('edit.png'), $url);
+
+        $table->setCellContents($row, $column, $links);
+
+        $row++;
+        $column = 0;
+    }
+}
+
+echo $table->toHtml();
+
+Display :: display_footer();

+ 332 - 0
main/work/upload.php

@@ -0,0 +1,332 @@
+<?php
+
+use ChamiloSession as Session;
+
+$language_file = array('exercice', 'work', 'document', 'admin', 'gradebook');
+
+require_once '../inc/global.inc.php';
+$current_course_tool  = TOOL_STUDENTPUBLICATION;
+
+/*	Configuration settings */
+
+api_protect_course_script(true);
+
+// Including necessary files
+require_once 'work.lib.php';
+require_once api_get_path(LIBRARY_PATH).'fileManage.lib.php';
+require_once api_get_path(LIBRARY_PATH).'fileUpload.lib.php';
+require_once api_get_path(LIBRARY_PATH).'fileDisplay.lib.php';
+
+$this_section = SECTION_COURSES;
+
+$work_id = isset($_REQUEST['id']) ? intval($_REQUEST['id']) : null;
+
+$work_table = Database :: get_course_table(TABLE_STUDENT_PUBLICATION);
+
+$is_allowed_to_edit = api_is_allowed_to_edit();
+$course_id = api_get_course_int_id();
+$user_id = api_get_user_id();
+$session_id = api_get_session_id();
+$course_code = api_get_course_id();
+$course_info = api_get_course_info();
+$group_id = api_get_group_id();
+
+if (empty($work_id)) {
+    api_not_allowed(true);
+}
+
+$parent_data = $my_folder_data = get_work_data_by_id($work_id);
+
+if (empty($parent_data)) {
+    api_not_allowed(true);
+}
+
+$is_course_member = CourseManager::is_user_subscribed_in_real_or_linked_course($user_id, $course_code, $session_id);
+$is_course_member = $is_course_member || api_is_platform_admin();
+
+if ($is_course_member == false) {
+    api_not_allowed(true);
+}
+
+$check = Security::check_token('post');
+$token = Security::get_token();
+
+$student_can_edit_in_session = api_is_allowed_to_session_edit(false, true);
+$has_ended   = false;
+
+$is_author = false;
+
+$parent_data['qualification'] = intval($parent_data['qualification']);
+
+if (!empty($parent_data) && !empty($parent_data['qualification'])) {
+    $count =  get_work_count_by_student($user_id, $work_id);
+    if ($count >= 1) {
+        Display::display_header();
+        if (api_get_course_setting('student_delete_own_publication') == '1') {
+            Display::display_warning_message(get_lang('CantUploadDeleteYourPaperFirst'));
+        } else {
+            Display::display_warning_message(get_lang('YouAlreadySentAPaperYouCantUpload'));
+        }
+        Display::display_footer();
+        exit;
+    }
+}
+
+if (!empty($my_folder_data)) {
+    $homework = get_work_assignment_by_id($my_folder_data['id']);
+
+    if ($homework['expires_on'] != '0000-00-00 00:00:00' || $homework['ends_on'] != '0000-00-00 00:00:00') {
+        $time_now		= time();
+
+        if (!empty($homework['expires_on']) && $homework['expires_on'] != '0000-00-00 00:00:00') {
+            $time_expires 	= api_strtotime($homework['expires_on'], 'UTC');
+            $difference 	= $time_expires - $time_now;
+            if ($difference < 0) {
+                $has_expired = true;
+            }
+        }
+
+        if (empty($homework['expires_on']) || $homework['expires_on'] == '0000-00-00 00:00:00') {
+            $has_expired = false;
+        }
+
+        if (!empty($homework['ends_on']) && $homework['ends_on'] != '0000-00-00 00:00:00') {
+            $time_ends 		= api_strtotime($homework['ends_on'], 'UTC');
+            $difference2 	= $time_ends - $time_now;
+            if ($difference2 < 0) {
+                $has_ended = true;
+            }
+        }
+
+        $ends_on 	= api_convert_and_format_date($homework['ends_on']);
+        $expires_on = api_convert_and_format_date($homework['expires_on']);
+    }
+}
+
+$interbreadcrumb[] = array('url' => api_get_path(WEB_CODE_PATH).'work/work.php?'.api_get_cidreq(), 'name' => get_lang('StudentPublications'));
+$interbreadcrumb[] = array('url' => api_get_path(WEB_CODE_PATH).'work/work_list.php?'.api_get_cidreq().'&id='.$work_id, 'name' =>  $parent_data['title']);
+
+// form title
+$form_title = get_lang('UploadADocument');
+
+$interbreadcrumb[] = array('url' => '#', 'name'  => $form_title);
+
+$form = new FormValidator('form', 'POST', api_get_self()."?".api_get_cidreq()."&id=".$work_id."&gradebook=".Security::remove_XSS($_GET['gradebook'])."&origin=$origin", '', array('enctype' => "multipart/form-data"));
+$form->addElement('header', $form_title);
+
+$show_progress_bar = false;
+
+if ($submitGroupWorkUrl) {
+    // For user comming from group space to publish his work
+    $realUrl = str_replace($_configuration['root_sys'], api_get_path(WEB_PATH), str_replace("\\", '/', realpath($submitGroupWorkUrl)));
+    $form->addElement('hidden', 'newWorkUrl', $submitGroupWorkUrl);
+    $text_document = $form->addElement('text', 'document', get_lang('Document'));
+    $defaults['document'] = '<a href="' . format_url($submitGroupWorkUrl) . '">' . $realUrl . '</a>';
+    $text_document->freeze();
+} else {
+    // else standard upload option
+    $form->addElement('file', 'file', get_lang('UploadADocument'), 'size="40" onchange="updateDocumentTitle(this.value)"');
+    $show_progress_bar = true;
+}
+
+$form->addElement('hidden', 'id', $work_id);
+$form->addElement('hidden', 'contains_file', 0, array('id'=>'contains_file_id'));
+$form->addElement('text', 'title', get_lang('Title'), array('id' => 'file_upload', 'class' => 'span4'));
+//$form->addElement('html_editor', 'description', get_lang("Description"));
+$form->add_html_editor('description', get_lang('Description'), false, false, array('ToolbarSet' => 'Work', 'Width' => '100%', 'Height' => '200'));
+
+$form->addElement('hidden', 'active',   1);
+$form->addElement('hidden', 'accepted', 1);
+$form->addElement('hidden', 'sec_token', $token);
+
+$text = get_lang('Send');
+$class = 'upload';
+
+
+// fix the Ok button when we see the tool in the learn path
+if ($origin == 'learnpath') {
+    $form->addElement('html', '<div style="margin-left:137px">');
+    $form->addElement('style_submit_button', 'submitWork', $text, array('class'=> $class, 'value' => "submitWork"));
+    $form->addElement('html', '</div>');
+} else {
+    $form->addElement('style_submit_button', 'submitWork', $text, array('class'=> $class, 'value' => "submitWork"));
+}
+
+if (!empty($_POST['submitWork']) || $item_id) {
+    $form->addElement('style_submit_button', 'cancelForm', get_lang('Cancel'), 'class="cancel"');
+}
+
+if ($show_progress_bar) {
+    $form->add_real_progress_bar('uploadWork', 'file');
+}
+
+$form->setDefaults($defaults);
+$error_message = null;
+$_course = api_get_course_info();
+$currentCourseRepositorySys = api_get_path(SYS_COURSE_PATH).$_course['path'] . '/';
+
+$succeed = false;
+if ($form->validate()) {
+    if ($student_can_edit_in_session && $check) {
+
+        //check the token inserted into the form
+
+        if (isset($_POST['submitWork'])) {
+            $url = null;
+            $contains_file = 0;
+
+            if ($_POST['contains_file'] && !empty($_FILES['file']['size'])) {
+                $updir = $currentCourseRepositorySys . 'work/'; //directory path to upload
+
+                // Try to add an extension to the file if it has'nt one
+                $new_file_name = add_ext_on_mime(stripslashes($_FILES['file']['name']), $_FILES['file']['type']);
+
+                // Replace dangerous characters
+                $new_file_name = replace_dangerous_char($new_file_name, 'strict');
+
+                // Transform any .php file in .phps fo security
+                $new_file_name = php2phps($new_file_name);
+
+                $filesize = filesize($_FILES['file']['tmp_name']);
+
+                if (empty($filesize)) {
+                    $error_message .= Display :: return_message(get_lang('UplUploadFailedSizeIsZero'), 'error');
+                    $succeed = false;
+                } elseif (!filter_extension($new_file_name)) {
+                    //filter extension
+                    $error_message .= Display :: return_message(get_lang('UplUnableToSaveFileFilteredExtension'), 'error');
+                    $succeed = false;
+                }
+
+                if (!$title) {
+                    $title = $_FILES['file']['name'];
+                }
+
+                // Compose a unique file name to avoid any conflict
+                $new_file_name = api_get_unique_id();
+                $curdirpath = basename($my_folder_data['url']);
+
+                //if we come from the group tools the groupid will be saved in $work_table
+                $result = move_uploaded_file($_FILES['file']['tmp_name'], $updir.$curdirpath.'/'.$new_file_name);
+
+                if ($result) {
+                    $url = 'work/'.$curdirpath.'/'.$new_file_name;
+                    $contains_file = 1;
+                }
+            }
+
+            if (empty($title)) {
+                $title = get_lang('Untitled');
+            }
+
+            $documents_total_space = DocumentManager::documents_total_space();
+            $course_max_space = DocumentManager::get_course_quota();
+            $total_size = $filesize + $documents_total_space;
+
+            if ($total_size > $course_max_space) {
+                $error_message .= Display::return_message(get_lang('NoSpace'), 'warning');
+            } else {
+                $active = '1';
+                //author      	= '" . Database::escape_string($authors) . "',
+                $sql_add_publication = "INSERT INTO " . $work_table . " SET
+                                   c_id 		= $course_id ,
+                                   url         	= '" . $url . "',
+                                   title       	= '" . Database::escape_string($title) . "',
+                                   description	= '" . Database::escape_string($description) . "',
+                                   contains_file = '".$contains_file."',
+                                   active		= '" . $active . "',
+                                   accepted		= '1',
+                                   post_group_id = '".$group_id."',
+                                   sent_date	=  '".api_get_utc_datetime()."',
+                                   parent_id 	=  '".$work_id."' ,
+                                   session_id	= '".intval($id_session)."' ,
+                                   user_id 		= '".$user_id."'";
+                //var_dump($sql_add_publication);exit;
+                Database::query($sql_add_publication);
+                $id = Database::insert_id();
+            }
+
+            if ($id) {
+                api_item_property_update($course_info, 'work', $id, 'DocumentAdded', $user_id, api_get_group_id());
+                $succeed = true;
+            }
+        } else {
+            $error_message .= Display::return_message(get_lang('IsNotPosibleSaveTheDocument'), 'error');
+        }
+        Security::clear_token();
+    } else {
+        //Bad token or can't add works
+        $error_message = Display::return_message(get_lang('IsNotPosibleSaveTheDocument'), 'error');
+    }
+
+    if (!empty($succeed) && !empty($id)) {
+        //last value is to check this is not "just" an edit
+        //YW Tis part serve to send a e-mail to the tutors when a new file is sent
+        $send = api_get_course_setting('email_alert_manager_on_new_doc');
+
+        if ($send > 0) {
+            // Lets predefine some variables. Be sure to change the from address!
+            if (empty($id_session)) {
+                //Teachers
+                $user_list = CourseManager::get_user_list_from_course_code(api_get_course_id(), null, null, null, COURSEMANAGER);
+            } else {
+                //Coaches
+                $user_list = CourseManager::get_user_list_from_course_code(api_get_course_id(), $session_id, null, null, 2);
+            }
+
+            $emailsubject = "[" . api_get_setting('siteName') . "] ".get_lang('SendMailBody')."\n".get_lang('CourseName')." : ".$_course['name']."  ";
+
+            foreach ($user_list as $user_data) {
+                $to_user_id = $user_data['user_id'];
+                $emailbody = get_lang('SendMailBody')."\n".get_lang('CourseName')." : ".$_course['name']."\n";
+                $user_info = api_get_user_info($user_id);
+                $emailbody .= get_lang('UserName')." : ".api_get_person_name($user_info['firstname'], $user_info['lastname'])."\n";
+                $emailbody .= get_lang('DateSent')." : ".api_format_date(api_get_local_time())."\n";
+                $emailbody .= get_lang('WorkName')." : ".$title."\n\n".get_lang('DownloadLink')."\n";
+                $url = api_get_path(WEB_CODE_PATH)."work/work.php?".api_get_cidreq()."&amp;id=".$work_id;
+                $emailbody .= $url;
+
+                MessageManager::send_message_simple($to_user_id, $emailsubject, $emailbody);
+            }
+        }
+        $message = get_lang('DocAdd');
+        event_upload($id);
+        $error_message .= Display :: return_message(get_lang('DocAdd'));
+
+        $script = 'work_list.php';
+        if ($is_allowed_to_edit) {
+            $script = 'work_list_all.php';
+        }
+
+        header('Location: '.api_get_path(WEB_CODE_PATH).'work/'.$script.'?'.api_get_cidreq().'&id='.$work_id.'&error_message='.$error_message);
+        exit;
+    }
+}
+
+$htmlHeadXtra[] = to_javascript_work();
+
+Display :: display_header(null);
+
+if (!empty($work_id)) {
+    if ($is_allowed_to_edit) {
+        if (api_resource_is_locked_by_gradebook($work_id, LINK_STUDENTPUBLICATION)) {
+            echo Display::display_warning_message(get_lang('ResourceLockedByGradebook'));
+        } else {
+            $form->display();
+        }
+    } elseif ($is_author) {
+        if (empty($work_item['qualificator_id']) || $work_item['qualificator_id'] == 0) {
+            $form->display();
+        } else {
+            Display::display_error_message(get_lang('ActionNotAllowed'));
+        }
+    } elseif ($student_can_edit_in_session && $has_ended == false) {
+        $form->display();
+    } else {
+        Display::display_error_message(get_lang('ActionNotAllowed'));
+    }
+} else {
+    Display::display_error_message(get_lang('ActionNotAllowed'));
+}
+
+Display :: display_footer();

+ 282 - 166
main/work/work.lib.php

@@ -24,7 +24,8 @@ require_once api_get_path(SYS_CODE_PATH).'gradebook/lib/gradebook_functions.inc.
  * @param	integer	Whether to show upload form option
  * @return	void
  */
-function display_action_links($id, $cur_dir_path, $show_tool_options, $display_upload_link, $action) {
+function display_action_links($id, $cur_dir_path, $show_tool_options, $display_upload_link, $action)
+{
 	global $gradebook;
 
     $id = $my_back_id = intval($id);
@@ -32,64 +33,37 @@ function display_action_links($id, $cur_dir_path, $show_tool_options, $display_u
         $my_back_id = 0;
     }
 
-	$display_output = '';
-	$origin = isset($_GET['origin']) ? Security::remove_XSS($_GET['origin']) : '';
+    $display_output = '';
+    $origin = isset($_GET['origin']) ? Security::remove_XSS($_GET['origin']) : '';
 
-	if (!empty($id)) {
-		$display_output .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&origin='.$origin.'&gradebook='.$gradebook.'&id='.$my_back_id.'">'.Display::return_icon('back.png', get_lang('BackToWorksList'),'',ICON_SIZE_MEDIUM).'</a>';
-	}
-
-	if ($show_tool_options && api_is_allowed_to_edit(null, true) && $origin != 'learnpath') {
-		// Create dir
-		if (empty($id)) {
-			$display_output .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&amp;action=create_dir&origin='.$origin.'&gradebook='.$gradebook.'">';
-			$display_output .= Display::return_icon('new_work.png', get_lang('CreateAssignment'),'',ICON_SIZE_MEDIUM).'</a>';
-		}
-		if (empty($id)) {
-			// Options
-			$display_output .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&amp;action=settings&amp;origin='.$origin.'&amp;gradebook='.$gradebook.'">';
-			$display_output .= Display::return_icon('settings.png', get_lang('EditToolOptions'),'',ICON_SIZE_MEDIUM).'</a>';
-		}
-	}
+    if (!empty($id)) {
+        $display_output .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&origin='.$origin.'&gradebook='.$gradebook.'&id='.$my_back_id.'">'.Display::return_icon('back.png', get_lang('BackToWorksList'),'',ICON_SIZE_MEDIUM).'</a>';
+    }
 
-    if ($display_upload_link && api_is_allowed_to_session_edit(false, true) && !empty($id)) {
-        $display_output .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&id='.$id.'&action=upload_form&origin='.$origin.'&gradebook='.$gradebook.'">';
-        $display_output .= Display::return_icon('upload_file.png', get_lang('UploadADocument'),'',ICON_SIZE_MEDIUM).'</a>';
+    if ($show_tool_options && api_is_allowed_to_edit(null, true) && $origin != 'learnpath') {
+        // Create dir
+        if (empty($id)) {
+            $display_output .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&amp;action=create_dir&origin='.$origin.'&gradebook='.$gradebook.'">';
+            $display_output .= Display::return_icon('new_work.png', get_lang('CreateAssignment'),'',ICON_SIZE_MEDIUM).'</a>';
+        }
+        if (empty($id)) {
+            // Options
+            $display_output .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&amp;action=settings&amp;origin='.$origin.'&amp;gradebook='.$gradebook.'">';
+            $display_output .= Display::return_icon('settings.png', get_lang('EditToolOptions'),'',ICON_SIZE_MEDIUM).'</a>';
+        }
     }
 
-	if (api_is_allowed_to_edit(null, true) && $origin != 'learnpath' && api_is_allowed_to_session_edit(false, true)) {
-		// Delete all files
-		if (api_get_setting('permanently_remove_deleted_files') == 'true'){
-			$message = get_lang('ConfirmYourChoiceDeleteAllfiles');
-		} else {
-			$message = get_lang('ConfirmYourChoice');
-		}
-	}
 
-	if (api_is_allowed_to_edit(null, true)) {
-		global $token;
 
-		if (!empty($id)) {
-			if (empty($_GET['list']) or Security::remove_XSS($_GET['list']) == 'with') {
-				$display_output .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&amp;id='.$id.'&amp;curdirpath='.$cur_dir_path.'&amp;origin='.$origin.'&amp;gradebook='.$gradebook.'&amp;list=without">'.
-				Display::return_icon('exercice_uncheck.png', get_lang('ViewUsersWithoutTask'),'',ICON_SIZE_MEDIUM)."</a>\n";
-                                $count = get_count_work($id);
-                                if ($count > 0) {
-                                    $display_output .= '<a href="downloadfolder.inc.php?id='.$id.'">'.Display::return_icon('save_pack.png', get_lang('Save'), array('style' => 'float:right;'), ICON_SIZE_MEDIUM).'</a>';
-                                }
-			} else {
-				$display_output .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&amp;id='.$id.'&amp;curdirpath='.$cur_dir_path.'&amp;origin='.$origin.'&amp;gradebook='.$gradebook.'&amp;list=with">'.
-				Display::return_icon('exercice_check.png', get_lang('ViewUsersWithTask'),'',ICON_SIZE_MEDIUM)."</a>\n";
-                                $display_output .= '<a href="downloadfolder.inc.php?id='.$id.'">'.Display::return_icon('save_pack.png', get_lang('Save'), array('style' => 'float:right;'), ICON_SIZE_MEDIUM).'</a>';
-                if (!isset($_GET['action']) || (isset($_GET['action']) && $_GET['action'] != 'send_mail')) {
-                    $display_output .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&amp;id='.$id.'&amp;curdirpath='.$cur_dir_path.'&amp;origin='.$origin.'&amp;gradebook='.$gradebook.'&amp;list=without&amp;action=send_mail&amp;sec_token='.$token.'">'.
-                    Display::return_icon('mail_send.png', get_lang('ReminderMessage'),'',ICON_SIZE_MEDIUM)."</a>";
-                } else {
-                    $display_output .= Display::return_icon('mail_send_na.png', get_lang('ReminderMessage'),'',ICON_SIZE_MEDIUM);
-                }
-			}
-		}
-	}
+    if (api_is_allowed_to_edit(null, true) && $origin != 'learnpath' && api_is_allowed_to_session_edit(false, true)) {
+        // Delete all files
+        if (api_get_setting('permanently_remove_deleted_files') == 'true'){
+            $message = get_lang('ConfirmYourChoiceDeleteAllfiles');
+        } else {
+            $message = get_lang('ConfirmYourChoice');
+        }
+    }
+
 
 	if ($display_output != '') {
 		echo '<div class="actions">';
@@ -309,30 +283,149 @@ function get_work_assignment_by_id($id) {
 	return $return;
 }
 
+function getWorkList($id, $my_folder_data, $add_in_where_query)
+{
+    $work_table      = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
+
+    $course_id          = api_get_course_int_id();
+    $session_id         = api_get_session_id();
+    $condition_session  = api_get_session_condition($session_id);
+    $group_id           = api_get_group_id();
+
+    $link_info = is_resource_in_course_gradebook(api_get_course_id(), 3 , $id, api_get_session_id());
+    $work_in_gradebook_link_id = $link_info['id'];
+    $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
+
+    if ($work_in_gradebook_link_id) {
+        if ($is_allowed_to_edit)
+            if (intval($my_folder_data['qualification']) == 0) {
+                Display::display_warning_message(get_lang('MaxWeightNeedToBeProvided'));
+            }
+    }
+
+    $contains_file_query = '';
+
+    //Get list from database
+    if ($is_allowed_to_edit) {
+        $active_condition = ' active IN (0, 1)';
+        $sql_get_publications_list = "SELECT *  FROM  $work_table
+                WHERE c_id = $course_id $add_in_where_query $condition_session AND $active_condition AND (parent_id = 0) $contains_file_query ";
+        if (!empty($group_id)) {
+            $sql_get_publications_list .= " AND post_group_id = '".$group_id."' ";
+        }
+        $sql_get_publications_list .= " ORDER BY sent_date DESC";
+    } else {
+        if (!empty($group_id)) {
+            $group_query = " WHERE c_id = $course_id AND post_group_id = '".$group_id."' "; // set to select only messages posted by the user's group
+            $subdirs_query = "AND parent_id = 0";
+        } else {
+            $group_query = " WHERE c_id = $course_id AND  post_group_id = '0' ";
+            $subdirs_query = "AND parent_id = 0";
+        }
+        //@todo how we can active or not an assignment?
+        $active_condition = ' AND active IN (1,0)';
+        $sql_get_publications_list = "SELECT * FROM  $work_table $group_query $subdirs_query $add_in_where_query $active_condition $condition_session ORDER BY title";
+    }
+
+    $work_parents = array();
+
+    $sql_result = Database::query($sql_get_publications_list);
+    if (Database::num_rows($sql_result)) {
+        while ($work = Database::fetch_object($sql_result)) {
+            if ($work->parent_id == 0) {
+                $work_parents[] = $work;
+            }
+        }
+    }
+    return $work_parents;
+}
+
+function getWorkPerUser($userId)
+{
+    $works = getWorkList(null, null, null);
+    $result = array();
+    if (!empty($works)) {
+        foreach ($works as $workData) {
+            $workId = $workData->id;
+            $result[$workId]['work'] = $workData;
+            $result[$workId]['work']->user_results = get_work_user_list(0, 100, null, null, $workId, null, $userId);
+        }
+    }
+    return $result;
+}
+
+/**
+ * @param int $workId
+ * @param int $course_id
+ * @param int $sessionId
+ * @param int $userId user id to filter
+ * @param array $onlyUserList only parse this user list
+ * @return mixed
+ */
+function getUniqueStudentAttempts($workId, $groupId, $course_id, $sessionId, $userId = null, $onlyUserList = array())
+{
+    $work_table      = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
+    $user_table      = Database::get_main_table(TABLE_MAIN_USER);
+
+    $course_id = intval($course_id);
+    $workId = intval($workId);
+    $sessionId = intval($sessionId);
+    $groupId = intval($groupId);
+
+    $studentCondition = null;
+
+    if (!empty($onlyUserList)) {
+        $studentCondition = "AND u.user_id IN ('".implode("', '", $onlyUserList)."') ";
+    } else {
+        if (empty($userId)) {
+            return 0;
+        }
+    }
+
+    $sql_document = "SELECT count(*) FROM (
+                        SELECT count(*)
+                        FROM $work_table w INNER JOIN $user_table u ON w.user_id = u.user_id
+                        WHERE   w.c_id = $course_id AND
+                                w.session_id = $sessionId AND
+                                w.parent_id = ".$workId." AND
+                                w.post_group_id = ".$groupId." AND
+                                w.active IN (0, 1) $studentCondition
+                        ";
+
+    if (!empty($userId)) {
+        $userId = intval($userId);
+        $sql_document .= " AND u.user_id = ".$userId;
+    }
+    $sql_document .= " GROUP BY u.user_id) as t";
+
+    $res_document = Database::query($sql_document);
+    $rowCount = Database::fetch_row($res_document);
+
+    return $rowCount[0];
+}
+
 /**
  * Display the list of student publications, taking into account the user status
  *
- * @param $link_target_parameter - should there be a target parameter for the links
- * @param $dateFormatLong - date format
  * @param $origin - typically empty or 'learnpath'
  */
 
-function display_student_publications_list($id, $link_target_parameter, $dateFormatLong, $origin, $add_in_where_query = '') {
-	global $timeNoSecFormat, $dateFormatShort, $gradebook, $_course;
+function display_student_publications_list($id, $my_folder_data, $work_parents, $origin, $add_in_where_query = '', $userList = array())
+{
+	global $gradebook;
+
+    $_course = api_get_course_info();
 	// Database table names
 	$work_table      = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
 	$iprop_table     = Database::get_course_table(TABLE_ITEM_PROPERTY);
-        $user_table      = Database::get_main_table(TABLE_MAIN_USER);
+    $user_table      = Database::get_main_table(TABLE_MAIN_USER);
 	$work_assigment  = Database::get_course_table(TABLE_STUDENT_PUBLICATION_ASSIGNMENT);
 
 	$is_allowed_to_edit = api_is_allowed_to_edit(null, true);
 
-	$user_id 			= api_get_user_id();
 	$session_id         = api_get_session_id();
     $condition_session  = api_get_session_condition($session_id);
     $course_id          = api_get_course_int_id();
-    $group_id           = api_get_group_id();
-
     $course_info        = api_get_course_info(api_get_course_id());
 
 	$sort_params = array();
@@ -354,63 +447,13 @@ function display_student_publications_list($id, $link_target_parameter, $dateFor
 	$origin         = Security::remove_XSS($origin);
 
 	// Getting the work data
-	$my_folder_data = get_work_data_by_id($id);
+	//$my_folder_data = get_work_data_by_id($id);
 
     $qualification_exists = false;
     if (!empty($my_folder_data['qualification']) && intval($my_folder_data['qualification']) > 0) {
         $qualification_exists = true;
     }
 
-    $work_dir = api_get_path(SYS_COURSE_PATH).$_course['path'].'/work';
-    if (!empty($my_folder_data)) {
-        $work_dir = api_get_path(SYS_COURSE_PATH).$_course['path'].'/work'.$my_folder_data['url'];
-    }
-
-    if (empty($my_folder_data)) {
-    	$link_info = is_resource_in_course_gradebook(api_get_course_id(), 3 , $id, api_get_session_id());
-        $work_in_gradebook_link_id = $link_info['id'];
-
-    	if ($work_in_gradebook_link_id) {
-    		if ($is_allowed_to_edit)
-    			if (intval($my_folder_data['qualification']) == 0) {
-    				Display::display_warning_message(get_lang('MaxWeightNeedToBeProvided'));
-    			}
-    	}
-    	$contains_file_query = '';
-
-    	//Get list from database
-    	if ($is_allowed_to_edit) {
-    		$active_condition = ' active IN (0, 1)';
-    		$sql_get_publications_list = "SELECT *  FROM  $work_table ".
-                    " WHERE c_id = $course_id $add_in_where_query $condition_session AND $active_condition AND ".
-                    "  ( parent_id = 0) ".
-                    " $contains_file_query ".
-                    " ORDER BY sent_date DESC";
-    	} else {
-    		if (!empty($group_id)) {
-    			$group_query = " WHERE c_id = $course_id AND post_group_id = '".$group_id."' "; // set to select only messages posted by the user's group
-    			$subdirs_query = "AND parent_id = 0";
-    		} else {
-    			$group_query = " WHERE c_id = $course_id AND  post_group_id = '0' ";
-    			$subdirs_query = "AND parent_id = 0";
-    		}
-            //@todo how we can active or not an assignment?
-    		$active_condition = ' AND active IN (1,0)';
-    		$sql_get_publications_list = "SELECT * FROM  $work_table $group_query $subdirs_query $add_in_where_query  $active_condition $condition_session ORDER BY title";
-    	}
-
-        $work_parents = array();
-
-        $sql_result = Database::query($sql_get_publications_list);
-        if (Database::num_rows($sql_result)) {
-            while ($work = Database::fetch_object($sql_result)) {
-                if ($work->parent_id == 0) {
-                    $work_parents[] = $work;
-                }
-            }
-        }
-    }
-
 	$edit_dir = isset($_GET['edit_dir']) ? $_GET['edit_dir'] : '';
 
 	$table_header = array();
@@ -421,19 +464,25 @@ function display_student_publications_list($id, $link_target_parameter, $dateFor
 	if (!empty($id)) {
 		$table_header[] = array(get_lang('FirstName'), true);
 		$table_header[] = array(get_lang('LastName'), true);
-		if ($qualification_exists) {
-			$table_header[] = array(get_lang('Qualification'), true);
-		}
 	}
 
-	$table_header[] = array(get_lang('Date'), true, 'style="width:200px"');
+    $table_header[] = array(get_lang('HandOutDateLimit'), true, 'style="width:200px"');
 
 	if ($is_allowed_to_edit) {
+        $table_header[] = array(get_lang('HandedOut'), false);
 		$table_header[] = array(get_lang('Actions'), false, 'style="width:90px"', array('class'=>'td_actions'));
 		$table_has_actions_column = true;
-	}
-	// the following column name seems both undefined and unused
-	//$table_header[] = array('RealDate', true);
+
+        if ($qualification_exists) {
+            $table_header[] = array(get_lang('Qualification'), true);
+        }
+
+	} else {
+        // All users
+        if ($course_info['show_score'] == 0) {
+            $table_header[] = array(get_lang('Others'), false);
+        }
+    }
 
 	$table_data = array();
 
@@ -443,9 +492,20 @@ function display_student_publications_list($id, $link_target_parameter, $dateFor
 
 	if (is_array($work_parents)) {
 		foreach ($work_parents as $work_parent) {
-			$sql_select_directory = "SELECT title, url, prop.insert_date, prop.lastedit_date, work.id, author, has_properties, view_properties, description, qualification, weight, allow_text_assignment ".
-                  " FROM ".$iprop_table." prop INNER JOIN ".$work_table." work ON (prop.ref=work.id AND prop.c_id = $course_id  ) ".
-                  " WHERE active IN (0, 1) AND ";
+			$sql_select_directory = "SELECT
+			        title,
+			        url,
+			        prop.insert_date,
+			        prop.lastedit_date,
+			        work.id, author,
+			        has_properties,
+			        view_properties,
+			        description,
+			        qualification,
+			        weight,
+			        allow_text_assignment
+                FROM ".$iprop_table." prop INNER JOIN ".$work_table." work ON (prop.ref=work.id AND prop.c_id = $course_id)
+                WHERE active IN (0, 1) AND ";
 
 			if (!empty($group_id)) {
 				$sql_select_directory .= " work.post_group_id = '".$group_id."' "; // set to select only messages posted by the user's group
@@ -464,6 +524,7 @@ function display_student_publications_list($id, $link_target_parameter, $dateFor
 				// the folder belongs to another session
 				continue;
 			}
+
 			$direc_date      = $row['lastedit_date']; //directory's date
 			$author          = $row['author']; //directory's author
 			$view_properties = $row['view_properties'];
@@ -472,15 +533,16 @@ function display_student_publications_list($id, $link_target_parameter, $dateFor
 
             $locked = api_resource_is_locked_by_gradebook($id2, LINK_STUDENTPUBLICATION);
 
+            // form edit directory
+            if (!empty($row['has_properties'])) {
+                $sql = Database::query('SELECT * FROM '.$work_assigment.' WHERE c_id = '.$course_id.' AND id = "'.$row['has_properties'].'" LIMIT 1');
+                $homework = Database::fetch_array($sql);
+            }
+
 			if ($is_allowed_to_edit && $locked == false) {
-			    // form edit directory
 
 				if (!empty($edit_dir) && $edit_dir == $id2) {
 
-					if (!empty($row['has_properties'])) {
-						$sql = Database::query('SELECT * FROM '.$work_assigment.' WHERE c_id = '.$course_id.' AND id = "'.$row['has_properties'].'" LIMIT 1');
-						$homework = Database::fetch_array($sql);
-					}
 					$form_folder = new FormValidator('edit_dir', 'post', api_get_self().'?origin='.$origin.'&gradebook='.$gradebook.'&edit_dir='.$id2);
                     $form_folder->addElement('text', 'dir_name', get_lang('Title'));
                     $form_folder->addElement('hidden', 'work_id', $id2);
@@ -492,11 +554,13 @@ function display_student_publications_list($id, $link_target_parameter, $dateFor
 					$form_folder->add_html_editor('description', get_lang('Description'), false, false, array('ToolbarSet' => 'work', 'Width' => '80%', 'Height' => '200'));
 
 					$there_is_a_end_date = false;
-					$form_folder -> addElement('advanced_settings', '<a href="javascript://" onclick="javascript: return plus();" >
-						 	  	                                 <span id="plus">&nbsp;<img style="vertical-align:middle;" src="../img/div_show.gif" alt="" />
-						 	  	                                 &nbsp;'.get_lang('AdvancedParameters').'
-						 	  	                                 </span>
-						 	  	                                 </a>');
+					$form_folder -> addElement('advanced_settings',
+                        '<a href="javascript://" onclick="javascript: return plus();" >
+                         <span id="plus">&nbsp;<img style="vertical-align:middle;" src="../img/div_show.gif" alt="" />
+                         &nbsp;'.get_lang('AdvancedParameters').'
+                         </span>
+                         </a>'
+                    );
 					$form_folder->addElement('html', '<div id="options" style="display: none;">');
 
 					if (empty($default)) {
@@ -734,17 +798,23 @@ function display_student_publications_list($id, $link_target_parameter, $dateFor
 			$action = '';
 			$row = array();
 			$class = '';
-			$cant_files = 0;
 			$course_id  = api_get_course_int_id();
 			$session_id = api_get_session_id();
 
 			if (api_is_allowed_to_edit()) {
-                            $sql_document = "SELECT count(*) FROM $work_table w INNER JOIN $user_table u ON w.user_id = u.user_id WHERE w.c_id = $course_id AND w.parent_id = ".$work_data['id']." AND w.active IN (0, 1)";
+                $cant_files = get_count_work($work_data['id']);
+
+                /*$sql_document = "SELECT count(*)
+                                 FROM $work_table w INNER JOIN $user_table u ON w.user_id = u.user_id
+                                 WHERE w.c_id = $course_id AND w.parent_id = ".$work_data['id']." AND w.active IN (0, 1)";*/
 			} else {
+                $cant_files = get_count_work($work_data['id'], api_get_user_id());
+                /*
                 $user_filter = "user_id = ".api_get_user_id()." AND ";
                 if ($course_info['show_score'] == 0) {
                     $user_filter  = null;
                 }
+
                 $sql_document = "SELECT count(*) FROM $work_table s, $iprop_table p
                                   WHERE s.c_id = $course_id  AND
                                         p.c_id = $course_id AND
@@ -754,13 +824,14 @@ function display_student_publications_list($id, $link_target_parameter, $dateFor
                                         $user_filter
                                         parent_id = ".$work_data['id']." AND
                                         active = 1 AND
-                                        parent_id = ".$work_parent->id."";
+                                        parent_id = ".$work_parent->id."";*/
 			}
 
+
 			//count documents
-			$res_document   = Database::query($sql_document);
+			/*$res_document   = Database::query($sql_document);
 			$count_document = Database::fetch_row($res_document);
-			$cant_files     = $count_document[0];
+			$cant_files     = $count_document[0];*/
 
 			$text_file = get_lang('FilesUpload');
 
@@ -799,24 +870,39 @@ function display_student_publications_list($id, $link_target_parameter, $dateFor
                         $zip = '<a href="downloadfolder.inc.php?id='.$work_data['id'].'">'.Display::return_icon('save_pack.png', get_lang('Save'), array('style' => 'float:right;'), ICON_SIZE_SMALL).'</a>';
                     }
 				//}
-				$url = $zip.'<a href="'.api_get_self().'?'.api_get_cidreq().'&origin='.$origin.'&gradebook='.Security::remove_XSS($_GET['gradebook']).'&id='.$work_data['id'].'"'.$class.'>'.
+
+                $link = 'work_list.php';
+                if (api_is_allowed_to_edit()) {
+                    $link = 'work_list_all.php';
+                }
+
+				$url = $zip.'<a href="'.api_get_path(WEB_CODE_PATH).'work/'.$link.'?'.api_get_cidreq().'&origin='.$origin.'&gradebook='.Security::remove_XSS($_GET['gradebook']).'&id='.$work_data['id'].'"'.$class.'>'.
 						$work_title.'</a> '.$add_to_name.'<br />'.$cant_files.' '.$text_file.$dirtext;
 				$row[] = $url;
 			}
+
 			if ($count_files != 0) {
 				$row[] = '';
 			}
 
-			if ($direc_date != '' && $direc_date != '0000-00-00 00:00:00') {
-				$direc_date_local = api_get_local_time($direc_date);
-				$row[] = date_to_str_ago($direc_date_local).'<br /><span class="dropbox_date">'.api_format_date($direc_date_local).'</span>';
-			} else {
-				$direc_date_local = '0000-00-00 00:00:00';
-				$row[] = '';
-			}
+            if (!empty($homework)) {
+                $row[] = !empty($homework['ends_on']) && $homework['ends_on'] != '0000-00-00 00:00:00' ? api_get_local_time($homework['ends_on']): '-';
+            } else {
+                $row[] = '-';
+            }
+
+            if (!$is_allowed_to_edit) {
+                if ($course_info['show_score'] == 0) {
+                    $url = api_get_path(WEB_CODE_PATH).'work/work_list_others.php?'.api_get_cidreq().'&id='.$work_parent->id;
+                    $row[] = Display::url(Display::return_icon('group.png', get_lang('Others')), $url);
+                }
+            }
 
 			if ($origin != 'learnpath') {
 				if ($is_allowed_to_edit) {
+			        $cant_files_per_user = getUniqueStudentAttempts($work_data['id'], $group_id, $course_id, api_get_session_id(), null, $userList);
+
+                    $row[] = $cant_files_per_user.'/'.count($userList);
                     if (api_resource_is_locked_by_gradebook($id2, LINK_STUDENTPUBLICATION)) {
                         $action .= Display::return_icon('edit_na.png', get_lang('Edit'), array(), ICON_SIZE_SMALL);
                         $action .= Display::return_icon('delete_na.png', get_lang('Delete'), array(), ICON_SIZE_SMALL);
@@ -824,13 +910,12 @@ function display_student_publications_list($id, $link_target_parameter, $dateFor
                         $action .= '<a href="'.api_get_self().'?cidReq='.api_get_course_id().'&origin='.$origin.'&gradebook='.$gradebook.'&edit_dir='.$id2.'">'.Display::return_icon('edit.png', get_lang('Modify'), array(), ICON_SIZE_SMALL).'</a>';
                         $action .= ' <a href="'.api_get_self().'?'.api_get_cidreq().'&origin='.$origin.'&gradebook='.$gradebook.'&delete_dir='.$id2.'" onclick="javascript:if(!confirm('."'".addslashes(api_htmlentities(get_lang('ConfirmYourChoice'), ENT_QUOTES))."'".')) return false;" title="'.get_lang('DirDelete').'"  >'.Display::return_icon('delete.png',get_lang('DirDelete'),'',ICON_SIZE_SMALL).'</a>';
                     }
-
 					$row[] = $action;
 				} else {
 					$row[] = '';
 				}
 			}
-			$row[] = $direc_date_local;
+			//$row[] = $direc_date_local;
             $row[] = $work_data['title'];
 			$table_data[] = $row;
 		}
@@ -872,7 +957,8 @@ function display_student_publications_list($id, $link_target_parameter, $dateFor
 	if ($table_has_actions_column) {
 		$column_show[] = 1; // modify
 	}
-	$column_show[] = 0; //real date in correct format
+	$column_show[] = 1; //real date in correct format
+    $column_show[] = 0; //real date in correct format
 
 	$paging_options = array();
 	if (isset($_GET['curdirpath'])) {
@@ -887,6 +973,7 @@ function display_student_publications_list($id, $link_target_parameter, $dateFor
 	$my_params['origin'] = $origin;
 	Display::display_sortable_config_table('work', $table_header, $table_data, $sorting_options, $paging_options, $my_params, $column_show, $column_order);
 }
+
 /**
  * Returns a list of subdirectories found in the given directory.
  *
@@ -1492,7 +1579,14 @@ function get_work_id($path) {
 	}
 }
 
-function get_count_work($work_id) {
+/**
+ * @param int $work_id
+ * @param int $onlyMeUserId show only my works
+ * @param int $notMeUserId show works from everyone except me
+ * @return int
+ */
+function get_count_work($work_id, $onlyMeUserId = null, $notMeUserId = null)
+{
     $work_table 	 = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
     $iprop_table     = Database::get_course_table(TABLE_ITEM_PROPERTY);
     $user_table      = Database::get_main_table(TABLE_MAIN_USER);
@@ -1526,7 +1620,17 @@ function get_count_work($work_id) {
 
     $extra_conditions .= " AND parent_id  = ".$work_id."  ";
 
-    $sql = "SELECT  count(*) as count ".
+    $where_condition = null;
+
+    if (!empty($notMeUserId)) {
+        $where_condition .= " AND u.user_id <> ".intval($notMeUserId);
+    }
+
+    if (!empty($onlyMeUserId)) {
+        $where_condition .= " AND u.user_id =  ".intval($onlyMeUserId);
+    }
+
+    $sql = "SELECT count(*) as count ".
            " FROM ".$iprop_table." prop INNER JOIN ".$work_table." work ".
            " ON (prop.ref=work.id AND prop.c_id = $course_id ".
            " AND prop.tool='work' AND work.active = 1 ".
@@ -1543,7 +1647,8 @@ function get_count_work($work_id) {
     return $users_with_work;
 }
 
-function get_work_user_list($start, $limit, $column, $direction, $work_id, $where_condition) {
+function get_work_user_list($start, $limit, $column, $direction, $work_id, $where_condition, $studentId = null)
+{
     $work_table         = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
     $iprop_table        = Database::get_course_table(TABLE_ITEM_PROPERTY);
     $user_table         = Database::get_main_table(TABLE_MAIN_USER);
@@ -1590,7 +1695,7 @@ function get_work_user_list($start, $limit, $column, $direction, $work_id, $wher
 
         $extra_conditions .= " AND parent_id  = ".$work_id."  ";
 
-        $select = 'DISTINCT work.id as id, title as title, description, url, sent_date, contains_file, has_properties, view_properties,
+        $select = 'DISTINCT u.user_id, work.id as id, title as title, description, url, sent_date, contains_file, has_properties, view_properties,
                     qualification, weight, allow_text_assignment, u.firstname, u.lastname, u.username, parent_id, accepted, qualificator_id';
 
         $user_condition = "INNER JOIN $user_table u  ON (work.user_id = u.user_id) ";
@@ -1598,6 +1703,10 @@ function get_work_user_list($start, $limit, $column, $direction, $work_id, $wher
 
         $work_assignment = get_work_assignment_by_id($work_id);
 
+        if (!empty($studentId)) {
+            $where_condition.= " AND u.user_id = ".intval($studentId);
+        }
+
         $sql = "SELECT $select
                 FROM $work_condition  $user_condition $course_conditions
                 WHERE  $extra_conditions $where_condition $condition_session ";
@@ -1653,8 +1762,7 @@ function get_work_user_list($start, $limit, $column, $direction, $work_id, $wher
             }
 
             $add_string = '';
-            $time_expires = api_strtotime($work['expires_on'], 'UTC');
-
+            $time_expires = api_strtotime($work_assignment['expires_on'], 'UTC');
             if (!empty($work_assignment['expires_on']) && $work_assignment['expires_on'] != '0000-00-00 00:00:00' && $time_expires && ($time_expires < api_strtotime($work['sent_date'], 'UTC'))) {
                 $add_string = Display::label(get_lang('Expired'),'important');
             }
@@ -1695,7 +1803,7 @@ function get_work_user_list($start, $limit, $column, $direction, $work_id, $wher
                 $work['sent_date'] = date_to_str_ago(api_get_local_time($work['sent_date'])).' '.$add_string.'<br />'.$work_date;
 
                 //Actions
-                $url = api_get_path(WEB_CODE_PATH).'work/work.php?'.api_get_cidreq().'&id='.$work_id.'&origin='.$origin.'&gradebook='.Security::remove_XSS($_GET['gradebook']);
+                $url = api_get_path(WEB_CODE_PATH).'work/';
                 $action = '';
                 if ($is_allowed_to_edit) {
                     if ($locked) {
@@ -1706,10 +1814,10 @@ function get_work_user_list($start, $limit, $column, $direction, $work_id, $wher
                         }
                     } else {
                         if ($qualification_exists) {
-                            $action .= '<a href="'.$url.'&amp;action=edit&item_id='.$item_id.'&amp;parent_id='.$work['parent_id'].'" title="'.get_lang('Modify').'"  >'.
+                            $action .= '<a href="'.$url.'edit.php?'.api_get_cidreq().'&item_id='.$item_id.'&id='.$work['parent_id'].'" title="'.get_lang('Modify').'"  >'.
                             Display::return_icon('rate_work.png', get_lang('CorrectAndRate'),array(), ICON_SIZE_SMALL).'</a>';
                         } else {
-                            $action .= '<a href="'.$url.'&amp;action=edit&item_id='.$item_id.'&gradebook='.Security::remove_XSS($_GET['gradebook']).'&amp;parent_id='.$work['parent_id'].'" title="'.get_lang('Modify').'"  >'.
+                            $action .= '<a href="'.$url.'edit.php?'.api_get_cidreq().'&item_id='.$item_id.'&id='.$work['parent_id'].'&gradebook='.Security::remove_XSS($_GET['gradebook']).'" title="'.get_lang('Modify').'">'.
                             Display::return_icon('edit.png', get_lang('Comment'),array(), ICON_SIZE_SMALL).'</a>';
                         }
                     }
@@ -1717,27 +1825,27 @@ function get_work_user_list($start, $limit, $column, $direction, $work_id, $wher
                         if ($locked) {
                             $action .= Display::return_icon('move_na.png', get_lang('Move'),array(), ICON_SIZE_SMALL);
                         } else {
-                            $action .= '<a href="'.$url.'&amp;action=move&item_id='.$item_id.'" title="'.get_lang('Move').'">'.Display::return_icon('move.png', get_lang('Move'),array(), ICON_SIZE_SMALL).'</a>';
+                            $action .= '<a href="'.$url.'work.php?'.api_get_cidreq().'&action=move&item_id='.$item_id.'" title="'.get_lang('Move').'">'.Display::return_icon('move.png', get_lang('Move'),array(), ICON_SIZE_SMALL).'</a>';
                         }
                     }
                     if ($work['accepted'] == '1') {
-                        $action .= '<a href="'.$url.'&amp;action=make_invisible&item_id='.$item_id.'&amp;'.$sort_params.'" title="'.get_lang('Invisible').'" >'.Display::return_icon('visible.png', get_lang('Invisible'),array(), ICON_SIZE_SMALL).'</a>';
+                        $action .= '<a href="'.$url.'work.php?'.api_get_cidreq().'&action=make_invisible&item_id='.$item_id.'&amp;'.$sort_params.'" title="'.get_lang('Invisible').'" >'.Display::return_icon('visible.png', get_lang('Invisible'),array(), ICON_SIZE_SMALL).'</a>';
                     } else {
-                        $action .= '<a href="'.$url.'&amp;action=make_visible&item_id='.$item_id.'&amp;'.$sort_params.'" title="'.get_lang('Visible').'" >'.Display::return_icon('invisible.png', get_lang('Visible'),array(), ICON_SIZE_SMALL).'</a> ';
+                        $action .= '<a href="'.$url.'work.php?'.api_get_cidreq().'&action=make_visible&item_id='.$item_id.'&amp;'.$sort_params.'" title="'.get_lang('Visible').'" >'.Display::return_icon('invisible.png', get_lang('Visible'),array(), ICON_SIZE_SMALL).'</a> ';
                     }
                     if ($locked) {
                         $action .= Display::return_icon('delete_na.png', get_lang('Delete'),'',ICON_SIZE_SMALL);
                     } else {
-                        $action .= '<a href="'.$url.'&amp;action=delete&amp;item_id='.$item_id.'" onclick="javascript:if(!confirm('."'".addslashes(api_htmlentities(get_lang('ConfirmYourChoice'),ENT_QUOTES))."'".')) return false;" title="'.get_lang('Delete').'" >'.Display::return_icon('delete.png', get_lang('Delete'),'',ICON_SIZE_SMALL).'</a>';
+                        $action .= '<a href="'.$url.'work.php?'.api_get_cidreq().'&action=delete&amp;item_id='.$item_id.'" onclick="javascript:if(!confirm('."'".addslashes(api_htmlentities(get_lang('ConfirmYourChoice'),ENT_QUOTES))."'".')) return false;" title="'.get_lang('Delete').'" >'.Display::return_icon('delete.png', get_lang('Delete'),'',ICON_SIZE_SMALL).'</a>';
                     }
                 } elseif ($is_author && (empty($work['qualificator_id']) || $work['qualificator_id'] == 0)) {
                     if (api_is_allowed_to_session_edit(false, true)) {
-                        $action .= '<a href="'.$url.'&amp;action=edit&item_id='.$item_id.'" title="'.get_lang('Modify').'"  >'.Display::return_icon('edit.png', get_lang('Modify'),array(), ICON_SIZE_SMALL).'</a>';
+                        $action .= '<a href="'.$url.'edit.php?'.api_get_cidreq().'&item_id='.$item_id.'&id='.$work['parent_id'].'" title="'.get_lang('Modify').'"  >'.Display::return_icon('edit.png', get_lang('Modify'),array(), ICON_SIZE_SMALL).'</a>';
                     } else {
                         $action .= Display::return_icon('edit_na.png', get_lang('Modify'),array(), ICON_SIZE_SMALL);
                     }
                     if (api_get_course_setting('student_delete_own_publication') == 1) {
-                        $action .= '<a href="'.$url.'&amp;action=delete&amp;item_id='.$item_id.'" onclick="javascript:if(!confirm('."'".addslashes(api_htmlentities(get_lang('ConfirmYourChoice'),ENT_QUOTES))."'".')) return false;" title="'.get_lang('Delete').'"  >'.Display::return_icon('delete.png',get_lang('Delete'),'',ICON_SIZE_SMALL).'</a>';
+                        $action .= '<a href="'.$url.'work.php?'.api_get_cidreq().'&action=delete&amp;item_id='.$item_id.'" onclick="javascript:if(!confirm('."'".addslashes(api_htmlentities(get_lang('ConfirmYourChoice'),ENT_QUOTES))."'".')) return false;" title="'.get_lang('Delete').'"  >'.Display::return_icon('delete.png',get_lang('Delete'),'',ICON_SIZE_SMALL).'</a>';
                     }
                 } else {
                     $action .= Display::return_icon('edit_na.png', get_lang('Modify'),array(), ICON_SIZE_SMALL);
@@ -1765,10 +1873,12 @@ function get_work_user_list($start, $limit, $column, $direction, $work_id, $wher
  * @return array
  * @author cvargas carlos.vargas@beeznest.com cfasanando, christian.fasanado@beeznest.com
  */
-function send_reminder_users_without_publication($task_data) {
+function send_reminder_users_without_publication($task_data)
+{
 	global $_course;
     $sender_name = api_get_person_name(api_get_setting('administratorName'), api_get_setting('administratorSurname'), null, PERSON_NAME_EMAIL_ADDRESS);
 
+
 	$task_id = $task_data['id'];
 	$task_title = !empty($task_data['title']) ? $task_data['title'] : basename($task_data['url']);
 
@@ -1916,7 +2026,8 @@ function user_is_author($item_id, $user_id = null) {
  * @author cvargas
  * @author Julio Montoya <gugli100@gmail.com> Fixing query
  */
-function get_list_users_without_publication($task_id) {
+function get_list_users_without_publication($task_id, $studentId = null)
+{
 	$work_table 			 = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
 	$table_course_user 		 = Database::get_main_table(TABLE_MAIN_COURSE_USER);
 	$table_user 			 = Database::get_main_table(TABLE_MAIN_USER);
@@ -1933,6 +2044,7 @@ function get_list_users_without_publication($task_id) {
 	} else {
 		$sql = "SELECT user_id as id FROM $work_table WHERE c_id = $course_id AND parent_id='$task_id' and session_id='".$session_id."'";
 	}
+
 	$result = Database::query($sql);
 	$users_with_tasks = array();
 	while($row = Database::fetch_array($result)) {
@@ -1947,6 +2059,10 @@ function get_list_users_without_publication($task_id) {
 		              WHERE u.status!=1 and cu.course_code='".api_get_course_id()."' AND u.user_id=cu.id_user and cu.id_session='".$session_id."'";
 	}
 
+    if (!empty($studentId)) {
+        $sql_users.= "AND u.user_id = ".intval($studentId);
+    }
+
     $group_id = api_get_group_id();
 
     $new_group_user_list = array();
@@ -1986,7 +2102,7 @@ function get_list_users_without_publication($task_id) {
  * @author cvargas carlos.vargas@beeznest.com cfasanando, christian.fasanado@beeznest.com
  * @author Julio Montoya <gugli100@gmail.com> Fixes
  */
-function display_list_users_without_publication($task_id) {
+function display_list_users_without_publication($task_id, $studentId = null) {
 	global $origin;
 	$table_header[] = array(get_lang('LastName'), true);
 	$table_header[] = array(get_lang('FirstName'), true);

+ 234 - 608
main/work/work.php

@@ -56,7 +56,7 @@ api_protect_course_script(true);
 require_once 'work.lib.php';
 
 require_once api_get_path(LIBRARY_PATH).'mail.lib.inc.php';
-include_once api_get_path(LIBRARY_PATH).'fileManage.lib.php';
+require_once api_get_path(LIBRARY_PATH).'fileManage.lib.php';
 require_once api_get_path(LIBRARY_PATH).'fileUpload.lib.php';
 require_once api_get_path(LIBRARY_PATH).'fileDisplay.lib.php';
 
@@ -72,10 +72,8 @@ $my_folder_data = get_work_data_by_id($work_id);
 
 $curdirpath = '';
 $htmlHeadXtra[] = api_get_jqgrid_js();
-
 $htmlHeadXtra[] = to_javascript_work();
-
-$htmlHeadXtra[] = '<script type="text/javascript">
+$htmlHeadXtra[] = '<script>
 function setFocus(){
     $("#work_title").focus();
 }
@@ -93,15 +91,14 @@ $table_user			= Database :: get_main_table(TABLE_MAIN_USER);
 $table_session		= Database :: get_main_table(TABLE_MAIN_SESSION);
 $table_session_course_user = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
 
+$_course = api_get_course_info();
+
 /*	Constants and variables */
 
 $tool_name 		= get_lang('StudentPublications');
 $course_code 	= api_get_course_id();
 $session_id 	= api_get_session_id();
 
-$is_course_member = CourseManager::is_user_subscribed_in_real_or_linked_course($user_id, $course_code,$session_id);
-$is_course_member = $is_course_member || api_is_platform_admin();
-
 $currentCourseRepositorySys = api_get_path(SYS_COURSE_PATH) . $_course['path'] . '/';
 $currentCourseRepositoryWeb = api_get_path(WEB_COURSE_PATH) . $_course['path'] . '/';
 
@@ -112,11 +109,9 @@ $currentUserEmail 		= $_user['mail'];
 $item_id 		        = isset($_REQUEST['item_id']) ? intval($_REQUEST['item_id']) : null;
 $parent_id 		        = isset($_REQUEST['parent_id']) ? Database::escape_string($_REQUEST['parent_id']) : '';
 $origin 		        = isset($_REQUEST['origin']) ? Security::remove_XSS($_REQUEST['origin']) : '';
-
 $submitGroupWorkUrl     = isset($_REQUEST['submitGroupWorkUrl']) ? Security::remove_XSS($_REQUEST['submitGroupWorkUrl']) : '';
 $title 			        = isset($_REQUEST['title']) ? $_REQUEST['title'] : '';
 $description 	        = isset($_REQUEST['description']) ? $_REQUEST['description'] : '';
-
 $uploadvisibledisabled  = isset($_REQUEST['uploadvisibledisabled']) ? Database::escape_string($_REQUEST['uploadvisibledisabled']) : $course_info['show_score'];
 
 // get data for publication assignment
@@ -142,7 +137,7 @@ if ($action == 'downloadfolder') {
 /*	More init stuff */
 
 if (isset ($_POST['cancelForm']) && !empty ($_POST['cancelForm'])) {
-    header('Location: ' . api_get_self() . '?origin='.$origin.'&amp;gradebook='.$gradebook);
+    header('Location: '.api_get_self().'?origin='.$origin.'&amp;gradebook='.$gradebook);
     exit;
 }
 
@@ -241,15 +236,14 @@ if (!empty($group_id)) {
     }
 }
 
-
 //stats
 event_access_tool(TOOL_STUDENTPUBLICATION);
 
-$is_allowed_to_edit = api_is_allowed_to_edit(); //has to come after display_tool_view_option();
+$is_allowed_to_edit = api_is_allowed_to_edit();
 
 $student_can_edit_in_session = api_is_allowed_to_session_edit(false, true);
 
-Display :: display_introduction_section(TOOL_STUDENTPUBLICATION);
+Display::display_introduction_section(TOOL_STUDENTPUBLICATION);
 
 // introduction section
 
@@ -297,9 +291,7 @@ if (!empty($my_folder_data)) {
         $expires_on = api_convert_and_format_date($homework['expires_on']);
 
         if ($has_ended) {
-            //if (!api_is_allowed_to_edit()) {
             $display_upload_link = false;
-            //}
             $message = Display::return_message(get_lang('EndDateAlreadyPassed').' '.$ends_on, 'error');
         } elseif ($has_expired) {
             $display_upload_link = true;
@@ -319,23 +311,14 @@ echo $message;
 //for teachers
 
 switch ($action) {
-    case 'send_mail':
-        if (Security::check_token('get')) {
-            $mails_sent_to = send_reminder_users_without_publication($my_folder_data);
-            if (empty($mails_sent_to)) {
-                Display::display_warning_message(get_lang('NoResults'));
-            } else {
-                Display::display_confirmation_message(get_lang('MessageHasBeenSent').' '.implode(', ', $mails_sent_to));
-            }
-            Security::clear_token();
-        }
-        break;
     case 'settings':
         //if posts
         if ($is_allowed_to_edit && !empty($_POST['changeProperties'])) {
-            // changing the tool setting: default visibility of an uploaded document
-            $query = "UPDATE " . $main_course_table . " SET show_score='" . $uploadvisibledisabled . "' WHERE code='" . api_get_course_id() . "'";
+            // Changing the tool setting: default visibility of an uploaded document
+            // @todo i
+            $query = "UPDATE ".$main_course_table." SET show_score='" . $uploadvisibledisabled . "' WHERE code='" . api_get_course_id() . "'";
             $res = Database::query($query);
+
             /**
              * Course data are cached in session so we need to update both the database
              * and the session data
@@ -343,7 +326,6 @@ switch ($action) {
             $_course['show_score'] = $uploadvisibledisabled;
             Session::write('_course', $course);
 
-
             // changing the tool setting: is a student allowed to delete his/her own document
             // database table definition
             $table_course_setting = Database :: get_course_table(TOOL_COURSE_SETTING);
@@ -355,11 +337,11 @@ switch ($action) {
 
             if ($number_of_setting == 1) {
                 $query = "UPDATE " . $table_course_setting . " SET value='" . Database::escape_string($_POST['student_delete_own_publication']) . "'
-						WHERE variable='student_delete_own_publication' AND c_id = $course_id";
+                        WHERE variable='student_delete_own_publication' AND c_id = $course_id";
                 Database::query($query);
             } else {
                 $query = "INSERT INTO " . $table_course_setting . " (c_id, variable, value, category) VALUES
-				($course_id, 'student_delete_own_publication','" . Database::escape_string($_POST['student_delete_own_publication']) . "','work')";
+                ($course_id, 'student_delete_own_publication','" . Database::escape_string($_POST['student_delete_own_publication']) . "','work')";
                 Database::query($query);
             }
             Display::display_confirmation_message(get_lang('Saved'));
@@ -373,362 +355,6 @@ switch ($action) {
             Display::display_footer();
         }
         break;
-    case 'edit':
-    case 'upload_form': //can be add or edit work
-        $is_author = false;
-        if (empty($item_id)) {
-            $parent_data = get_work_data_by_id($work_id);
-            $parent_data['qualification'] = intval($parent_data['qualification']);
-
-            if (!empty($parent_data) && !empty($parent_data['qualification']))  {
-                $count =  get_work_count_by_student($user_id, $work_id);
-                if ($count >= 1 ) {
-                    if (api_get_course_setting('student_delete_own_publication') == '1') {
-                        Display::display_warning_message(get_lang('CantUploadDeleteYourPaperFirst'));
-                    } else {
-                        Display::display_warning_message(get_lang('YouAlreadySentAPaperYouCantUpload'));
-                    }
-                    Display::display_footer();
-                    exit;
-                }
-            }
-        } else {
-            //we found the current user is the author
-            $sql = "SELECT * FROM  $work_table WHERE c_id = $course_id AND id = $item_id";
-            $result = Database::query($sql);
-            $work_item = array();
-            if ($result) {
-                $work_item = Database::fetch_array($result);
-            }
-
-            //Get the author ID for that document from the item_property table
-            $is_author 			= user_is_author($item_id);
-            if (!$is_author) {
-                Display::display_warning_message(get_lang('NotAllowed'));
-                Display::display_footer();
-            }
-        }
-
-        $form = new FormValidator('form', 'POST', api_get_self()."?".api_get_cidreq()."&action=upload&id=".$work_id."&gradebook=".Security::remove_XSS($_GET['gradebook'])."&origin=$origin", '', array('enctype' => "multipart/form-data"));
-
-        // form title
-        if ($item_id) {
-            $form_title = get_lang('Edit');
-        } else {
-            $form_title = get_lang('UploadADocument');
-        }
-        $form->addElement('header', $form_title);
-
-        if (!empty ($error_message)) {
-            Display :: display_error_message($error_message);
-        }
-        $show_progress_bar = false;
-
-        if ($submitGroupWorkUrl) {
-            // For user comming from group space to publish his work
-            $realUrl = str_replace($_configuration['root_sys'], api_get_path(WEB_PATH), str_replace("\\", '/', realpath($submitGroupWorkUrl)));
-            $form->addElement('hidden', 'newWorkUrl', $submitGroupWorkUrl);
-            $text_document = $form->addElement('text', 'document', get_lang('Document'));
-            $defaults['document'] = '<a href="' . format_url($submitGroupWorkUrl) . '">' . $realUrl . '</a>';
-            $text_document->freeze();
-        } elseif ($item_id && ($is_allowed_to_edit or $is_author)) {
-            $workUrl = $currentCourseRepositoryWeb . $workUrl;
-        } else {
-            // else standard upload option
-            $form->addElement('file', 'file', get_lang('UploadADocument'), 'size="40" onchange="updateDocumentTitle(this.value)"');
-            $show_progress_bar = true;
-        }
-
-        $form->addElement('hidden', 'id', $work_id);
-        if (empty($item_id)) {
-            $form->addElement('hidden', 'contains_file', 0, array('id'=>'contains_file_id'));
-        } else {
-            $form->addElement('hidden', 'item_id', $item_id);
-        }
-        $form->addElement('text', 'title', get_lang('Title'), array('id' => 'file_upload', 'class' => 'span4'));
-        //$form->addElement('html_editor', 'description', get_lang("Description"));
-        $form->add_html_editor('description', get_lang('Description'), false, false, array('ToolbarSet' => 'Work', 'Width' => '100%', 'Height' => '200'));
-
-        if ($item_id && !empty($work_item)) {
-            $defaults['title'] 			= $work_item['title'];
-            $defaults["description"] 	= $work_item['description'];
-            $defaults['qualification']  = $work_item['qualification'];
-        }
-
-        if ($is_allowed_to_edit && !empty($item_id)) {
-            // Get qualification from parent_id that'll allow the validation qualification over
-            $sql = "SELECT qualification FROM $work_table WHERE c_id = $course_id AND id ='$parent_id' ";
-            $result = Database::query($sql);
-            $row = Database::fetch_array($result);
-            $qualification_over = $row['qualification'];
-            if (!empty($qualification_over) && intval($qualification_over) > 0) {
-                $form->addElement('text', 'qualification', array(get_lang('Qualification'),  null, " / ".$qualification_over), 'size="10"');
-                $form->addElement('hidden', 'qualification_over', $qualification_over);
-            }
-        }
-
-        $form->addElement('hidden', 'active',   1);
-        $form->addElement('hidden', 'accepted', 1);
-        $form->addElement('hidden', 'item_to_edit', $item_id);
-        $token = Security::get_token();
-        $form->addElement('hidden', 'sec_token', $token);
-
-        if ($item_id) {
-            $text = get_lang('UpdateWork');
-            $class = 'save';
-        } else {
-            $text = get_lang('Send');
-            $class = 'upload';
-        }
-
-        // fix the Ok button when we see the tool in the learn path
-        if ($origin == 'learnpath') {
-            $form->addElement('html', '<div style="margin-left:137px">');
-            $form->addElement('style_submit_button', 'submitWork', $text, array('class'=> $class, 'value' => "submitWork"));
-            $form->addElement('html', '</div>');
-        } else {
-            if ($item_id) {
-                $form->addElement('style_submit_button', 'editWork', $text, array('class'=> $class, 'value' => "editWork"));
-            } else {
-                $form->addElement('style_submit_button', 'submitWork', $text, array('class'=> $class, 'value' => "submitWork"));
-            }
-        }
-
-        if (!empty($_POST['submitWork']) || $item_id) {
-            $form->addElement('style_submit_button', 'cancelForm', get_lang('Cancel'), 'class="cancel"');
-        }
-
-        if ($show_progress_bar) {
-            $form->add_real_progress_bar('uploadWork', 'file');
-        }
-        $form->setDefaults($defaults);
-
-        //fixes bug when showing modification form
-        if (!empty($work_id)) {
-            if ($is_allowed_to_edit) {
-                if (api_resource_is_locked_by_gradebook($work_id, LINK_STUDENTPUBLICATION)) {
-                    echo Display::display_warning_message(get_lang('ResourceLockedByGradebook'));
-                } else {
-                    $form->display();
-                }
-            } elseif ($is_author) {
-                if (empty($work_item['qualificator_id']) || $work_item['qualificator_id'] == 0) {
-                    $form->display();
-                } else {
-                    Display::display_error_message(get_lang('ActionNotAllowed'));
-                }
-            } elseif ($student_can_edit_in_session && $has_ended == false) {
-                $form->display();
-            } else {
-                Display::display_error_message(get_lang('ActionNotAllowed'));
-            }
-        } else {
-            Display::display_error_message(get_lang('ActionNotAllowed'));
-        }
-        break;
-    case 'upload':
-        $check = Security::check_token('post');
-
-        if ($student_can_edit_in_session && $check) {
-
-            //check the token inserted into the form
-            if (isset($_POST['submitWork']) && !empty($is_course_member)) {
-                $authors = api_get_person_name($currentUserFirstName, $currentUserLastName);
-                $url = null;
-                $contains_file = 0;
-
-                if ($_POST['contains_file'] && !empty($_FILES['file']['size'])) {
-                    $updir = $currentCourseRepositorySys . 'work/'; //directory path to upload
-
-                    // Try to add an extension to the file if it has'nt one
-                    $new_file_name = add_ext_on_mime(stripslashes($_FILES['file']['name']), $_FILES['file']['type']);
-
-                    // Replace dangerous characters
-                    $new_file_name = replace_dangerous_char($new_file_name, 'strict');
-
-                    // Transform any .php file in .phps fo security
-                    $new_file_name = php2phps($new_file_name);
-
-                    $filesize = filesize($_FILES['file']['tmp_name']);
-
-                    if (empty($filesize)) {
-                        Display :: display_error_message(get_lang('UplUploadFailedSizeIsZero'));
-                        $succeed = false;
-                    } elseif (!filter_extension($new_file_name)) {
-                        //filter extension
-                        Display :: display_error_message(get_lang('UplUnableToSaveFileFilteredExtension'));
-                        $succeed = false;
-                    }
-                    if (!$title) {
-                        $title = $_FILES['file']['name'];
-                    }
-
-                    // Compose a unique file name to avoid any conflict
-                    $new_file_name = api_get_unique_id();
-                    $curdirpath = basename($my_folder_data['url']);
-
-                    //if we come from the group tools the groupid will be saved in $work_table
-                    $result = @move_uploaded_file($_FILES['file']['tmp_name'], $updir.$curdirpath.'/'.$new_file_name);
-                    if ($result) {
-                        $url = 'work/'.$curdirpath.'/'.$new_file_name;
-                        $contains_file = 1;
-                    }
-                }
-
-                if (empty($title)) {
-                    $title = get_lang('Untitled');
-                }
-
-                $documents_total_space = DocumentManager::documents_total_space();
-                $course_max_space = DocumentManager::get_course_quota();
-                $total_size = $filesize + $documents_total_space;
-                if ($total_size > $course_max_space) {
-                    $error_message = get_lang('NoSpace');
-                } else {
-
-                    $active = '1';
-                    $sql_add_publication = "INSERT INTO " . $work_table . " SET
-									   c_id 		= $course_id ,
-								       url         	= '" . $url . "',
-								       title       	= '" . Database::escape_string($title) . "',
-					                   description	= '" . Database::escape_string($description) . "',
-					                   author      	= '" . Database::escape_string($authors) . "',
-					                   contains_file = '".$contains_file."',
-									   active		= '" . $active . "',
-									   accepted		= '1',
-									   post_group_id = '".$group_id."',
-									   sent_date	=  '".api_get_utc_datetime()."',
-									   parent_id 	=  '".$work_id."' ,
-                                       session_id	= '".intval($id_session)."' ,
-                                       user_id 		= '".$user_id."'";
-                    //var_dump($sql_add_publication);exit;
-                    Database::query($sql_add_publication);
-                    $id = Database::insert_id();
-                }
-
-                if ($id) {
-                    api_item_property_update($course_info, 'work', $id, 'DocumentAdded', $user_id, api_get_group_id());
-                    /*
-                     * See task #5145
-                    if($uploadvisibledisabled){
-                        $sql = "UPDATE $work_table SET accepted = 0 WHERE c_id = $course_id AND id = $id";
-                        Database::query($sql);
-                        api_item_property_update($course_info, 'work', $id, 'invisible', api_get_user_id());
-                    }else{
-                        //api_item_property_update($course_info, 'work', $id, 'visible', api_get_user_id());
-                    }*/
-                    $succeed = true;
-                }
-            } elseif ($newWorkUrl) {
-
-                // SPECIAL CASE ! For a work coming from another area (i.e. groups)
-                /*
-                            $url = str_replace('../../' . $_course['path'] . '/', '', $newWorkUrl);
-
-                            if (!$title) {
-                                $title = basename($workUrl);
-                            }
-                            $sql = "INSERT INTO  " . $work_table . " SET
-                                                c_id = $course_id,
-                                                url        	= '" . $url . "',
-                                                title       	= '" . Database::escape_string($title) . "',
-                                                description 	= '" . Database::escape_string($description) . "',
-                                                author      	= '" . Database::escape_string($authors) . "',
-                                                post_group_id   = '".$group_id."',
-                                                sent_date    	= '".api_get_utc_datetime()."',
-                                                session_id 		= '".intval($id_session)."',
-                                                user_id 		= '".$user_id."'";
-
-                            Database::query($sql);
-
-                            $insertId = Database::insert_id();
-                            api_item_property_update($_course, 'work', $insertId, 'DocumentAdded', $user_id, $group_id);
-                            $succeed = true;*/
-            } elseif (isset($_POST['editWork'])) {
-                /*
-                 * SPECIAL CASE ! For a work edited
-                */
-                //Get the author ID for that document from the item_property table
-                $item_to_edit_id 	= intval($_POST['item_to_edit']);
-                $is_author 			= user_is_author($item_to_edit_id);
-
-                if ($is_author) {
-                    $work_data = get_work_data_by_id($item_to_edit_id);
-
-                    if (!empty($_POST['title']))
-                        $title 		 = isset($_POST['title']) ? $_POST['title'] : $work_data['title'];
-                    $description = isset($_POST['description']) ? $_POST['description'] : $work_data['description'];
-
-                    if ($is_allowed_to_edit && ($_POST['qualification'] !='' )) {
-                        $add_to_update = ', qualificator_id ='."'".api_get_user_id()."',";
-                        $add_to_update .= ' qualification = '."'".Database::escape_string($_POST['qualification'])."',";
-                        $add_to_update .= ' date_of_qualification ='."'".api_get_utc_datetime()."'";
-                    }
-
-                    if ((int)$_POST['qualification'] > (int)$_POST['qualification_over']) {
-                        Display::display_error_message(get_lang('QualificationMustNotBeMoreThanQualificationOver'));
-                    } else {
-                        $sql = "UPDATE  " . $work_table . "
-						        SET	title       = '" . Database::escape_string($title) . "',
-						            description = '" . Database::escape_string($description) . "'
-						            ".$add_to_update."
-						        WHERE c_id = $course_id AND id = $item_to_edit_id";
-                        Database::query($sql);
-                    }
-                    api_item_property_update($_course, 'work', $item_to_edit_id, 'DocumentUpdated', $user_id);
-                    $succeed = true;
-                    Display :: display_confirmation_message(get_lang('ItemUpdated'), false);
-                } else {
-                    $error_message = get_lang('IsNotPosibleSaveTheDocument');
-                }
-            } else {
-                $error_message = get_lang('IsNotPosibleSaveTheDocument');
-            }
-            Security::clear_token();
-        } else {
-            //Bad token or can't add works
-            $error_message = get_lang('IsNotPosibleSaveTheDocument');
-        }
-
-        if (!empty($succeed) && !empty($id)) {
-            //last value is to check this is not "just" an edit
-            //YW Tis part serve to send a e-mail to the tutors when a new file is sent
-            $send = api_get_course_setting('email_alert_manager_on_new_doc');
-
-            if ($send > 0) {
-                // Lets predefine some variables. Be sure to change the from address!
-                if (empty($id_session)) {
-                    //Teachers
-                    $user_list = CourseManager::get_user_list_from_course_code(api_get_course_id(), null, null, null, COURSEMANAGER);
-                } else {
-                    //Coaches
-                    $user_list = CourseManager::get_user_list_from_course_code(api_get_course_id(), $session_id, null, null, 2);
-                }
-
-                $emailsubject = "[" . api_get_setting('siteName') . "] ".get_lang('SendMailBody')."\n".get_lang('CourseName')." : ".$_course['name']."  ";
-
-                foreach ($user_list as $user_data) {
-                    $to_user_id = $user_data['user_id'];
-                    $emailbody = get_lang('SendMailBody')."\n".get_lang('CourseName')." : ".$_course['name']."\n";
-                    $user_id = api_get_user_id();
-                    $user_info = api_get_user_info($user_id);
-                    $emailbody .= get_lang('UserName')." : ".api_get_person_name($user_info['firstname'], $user_info['lastname'])."\n";
-                    $emailbody .= get_lang('DateSent')." : ".api_format_date(api_get_local_time())."\n";
-                    $emailbody .= get_lang('WorkName')." : ".$title."\n\n".get_lang('DownloadLink')."\n";
-                    $url = api_get_path(WEB_CODE_PATH)."work/work.php?".api_get_cidreq()."&amp;id=".$work_id;
-                    $emailbody .= $url;
-
-                    MessageManager::send_message_simple($to_user_id, $emailsubject, $emailbody);
-                }
-            }
-            $message = get_lang('DocAdd');
-            event_upload($id);
-            Display :: display_confirmation_message(get_lang('DocAdd'), false);
-        } else {
-            if (!empty($error_message)) {
-                Display :: display_warning_message($error_message, false);
-            }
-        }
     case 'create_dir':
     case 'add':
         //$check = Security::check_token('post');
@@ -778,7 +404,6 @@ switch ($action) {
             $form->addElement('advanced_settings',draw_date_picker('expires'));
             $form->addElement('html', '</div>');
 
-
             $form->addElement('checkbox', 'type2', null, get_lang('EnableEndDate'), array('id' =>'make_calification_id', 'onclick' => "javascript: if(this.checked){document.getElementById('option3').style.display='block';}else{document.getElementById('option3').style.display='none';}"));
 
             $form->addElement('html', '<div id="option3" style="display: none;">');
@@ -827,8 +452,8 @@ switch ($action) {
                                             date_of_qualification	= '0000-00-00 00:00:00',
                                             weight              = '".Database::escape_string($_POST['weight'])."',
                                             session_id          = '".intval($id_session)."',
-                                            allow_text_assignment   = '".Database::escape_string($_POST['allow_text_assignment'])."',
-                                            contains_file    = 0,
+                                            allow_text_assignment = '".Database::escape_string($_POST['allow_text_assignment'])."',
+                                            contains_file       = 0,
                                             user_id 			= '".$user_id."'";
 
                     Database::query($sql_add_publication);
@@ -922,266 +547,267 @@ switch ($action) {
     case 'make_invisible':
     case 'move':
     case 'move_to':
-case 'list':
-    /*	Move file command */
-    if ($is_allowed_to_edit && $action == 'move_to') {
-        $move_to_path = get_work_path($_REQUEST['move_to_id']);
-
-        if ($move_to_path==-1) {
-            $move_to_path = '/';
-        } elseif (substr($move_to_path, -1, 1) != '/') {
-            $move_to_path = $move_to_path .'/';
-        }
+    case 'list':
+        /* Move file command */
+        if ($is_allowed_to_edit && $action == 'move_to') {
+            $move_to_path = get_work_path($_REQUEST['move_to_id']);
+
+            if ($move_to_path==-1) {
+                $move_to_path = '/';
+            } elseif (substr($move_to_path, -1, 1) != '/') {
+                $move_to_path = $move_to_path .'/';
+            }
 
-        //security fix: make sure they can't move files that are not in the document table
-        if ($path = get_work_path($item_id)) {
+            //security fix: make sure they can't move files that are not in the document table
+            if ($path = get_work_path($item_id)) {
 
-            if (move($course_dir.'/'.$path, $base_work_dir . $move_to_path)) {
-                //update db
-                update_work_url($item_id, 'work' . $move_to_path, $_REQUEST['move_to_id']);
+                if (move($course_dir.'/'.$path, $base_work_dir . $move_to_path)) {
+                    //update db
+                    update_work_url($item_id, 'work' . $move_to_path, $_REQUEST['move_to_id']);
 
-                api_item_property_update($_course, 'work', $_REQUEST['move_to_id'], 'FolderUpdated', $user_id);
+                    api_item_property_update($_course, 'work', $_REQUEST['move_to_id'], 'FolderUpdated', $user_id);
 
-                /*
-                // update all the parents in the table item propery
-                $list_id = get_parent_directories($move_to_path);
-                for ($i = 0; $i < count($list_id); $i++) {
-                    api_item_property_update($_course, 'work', $list_id[$i], 'FolderUpdated', $user_id);
-                }*/
-                Display :: display_confirmation_message(get_lang('DirMv'));
+                    /*
+                    // update all the parents in the table item propery
+                    $list_id = get_parent_directories($move_to_path);
+                    for ($i = 0; $i < count($list_id); $i++) {
+                        api_item_property_update($_course, 'work', $list_id[$i], 'FolderUpdated', $user_id);
+                    }*/
+                    Display :: display_confirmation_message(get_lang('DirMv'));
+                } else {
+                    Display :: display_error_message(get_lang('Impossible'));
+                }
             } else {
                 Display :: display_error_message(get_lang('Impossible'));
             }
-        } else {
-            Display :: display_error_message(get_lang('Impossible'));
         }
-    }
 
-    /*	Move file form request */
-    if ($is_allowed_to_edit && $action == 'move') {
-        if (!empty($item_id)) {
-            $folders = array();
-            $session_id = api_get_session_id();
-            $session_id == 0 ? $withsession = " AND session_id = 0 " : $withsession = " AND session_id='".$session_id."'";
-            $sql = "SELECT id, url, title FROM $work_table
-				        WHERE c_id = $course_id AND active IN (0, 1) AND url LIKE '/%' AND post_group_id = '".$group_id."'".$withsession;
-            $res = Database::query($sql);
-            while($folder = Database::fetch_array($res)) {
-                $folders[$folder['id']] = $folder['title'];
+        /*	Move file form request */
+        if ($is_allowed_to_edit && $action == 'move') {
+            if (!empty($item_id)) {
+                $folders = array();
+                $session_id = api_get_session_id();
+                $session_id == 0 ? $withsession = " AND session_id = 0 " : $withsession = " AND session_id='".$session_id."'";
+                $sql = "SELECT id, url, title FROM $work_table
+                        WHERE c_id = $course_id AND active IN (0, 1) AND url LIKE '/%' AND post_group_id = '".$group_id."'".$withsession;
+                $res = Database::query($sql);
+                while ($folder = Database::fetch_array($res)) {
+                    $folders[$folder['id']] = $folder['title'];
+                }
+                echo build_work_move_to_selector($folders, $curdirpath, $item_id);
             }
-            echo build_work_move_to_selector($folders, $curdirpath, $item_id);
         }
-    }
 
-    /*	MAKE VISIBLE WORK COMMAND */
-    if ($is_allowed_to_edit && $action == 'make_visible') {
-        if (!empty($item_id)) {
-            if (isset($item_id) && $item_id == 'all') {
-                //never happens
-                /*
-                $sql = "ALTER TABLE  " . $work_table . " CHANGE accepted accepted TINYINT(1) DEFAULT '1'";
-                Database::query($sql);
-                $sql = "UPDATE  " . $work_table . " SET accepted = 1";
-                Database::query($sql);
-                Display::display_confirmation_message(get_lang('AllFilesVisible'));*/
-            } else {
-                $sql = "UPDATE " . $work_table . "	SET accepted = 1 WHERE c_id = $course_id AND id = '" . $item_id . "'";
-                Database::query($sql);
-                api_item_property_update($course_info, 'work', $item_id, 'visible', api_get_user_id());
-                Display::display_confirmation_message(get_lang('FileVisible'));
+        /*	MAKE VISIBLE WORK COMMAND */
+        if ($is_allowed_to_edit && $action == 'make_visible') {
+            if (!empty($item_id)) {
+                if (isset($item_id) && $item_id == 'all') {
+                    //never happens
+                    /*
+                    $sql = "ALTER TABLE  " . $work_table . " CHANGE accepted accepted TINYINT(1) DEFAULT '1'";
+                    Database::query($sql);
+                    $sql = "UPDATE  " . $work_table . " SET accepted = 1";
+                    Database::query($sql);
+                    Display::display_confirmation_message(get_lang('AllFilesVisible'));*/
+                } else {
+                    $sql = "UPDATE " . $work_table . "	SET accepted = 1 WHERE c_id = $course_id AND id = '" . $item_id . "'";
+                    Database::query($sql);
+                    api_item_property_update($course_info, 'work', $item_id, 'visible', api_get_user_id());
+                    Display::display_confirmation_message(get_lang('FileVisible'));
+                }
             }
         }
-    }
 
-    if ($is_allowed_to_edit && $action == 'make_invisible') {
+        if ($is_allowed_to_edit && $action == 'make_invisible') {
 
-        /*	MAKE INVISIBLE WORK COMMAND */
-        if (!empty($item_id)) {
-            if (isset($item_id) && $item_id == 'all') {
-                /*
-                $sql = "ALTER TABLE " . $work_table . "
-                    CHANGE accepted accepted TINYINT(1) DEFAULT '0'";
-                Database::query($sql);
-                $sql = "UPDATE  " . $work_table . " SET accepted = 0";
-                Database::query($sql);
-                Display::display_confirmation_message(get_lang('AllFilesInvisible'));*/
-            } else {
-                $sql = "UPDATE  " . $work_table . " SET accepted = 0
-							WHERE c_id = $course_id AND id = '" . $item_id . "'";
-                Database::query($sql);
-                api_item_property_update($course_info, 'work', $item_id, 'invisible', api_get_user_id());
-                Display::display_confirmation_message(get_lang('FileInvisible'));
+            /*	MAKE INVISIBLE WORK COMMAND */
+            if (!empty($item_id)) {
+                if (isset($item_id) && $item_id == 'all') {
+                    /*
+                    $sql = "ALTER TABLE " . $work_table . "
+                        CHANGE accepted accepted TINYINT(1) DEFAULT '0'";
+                    Database::query($sql);
+                    $sql = "UPDATE  " . $work_table . " SET accepted = 0";
+                    Database::query($sql);
+                    Display::display_confirmation_message(get_lang('AllFilesInvisible'));*/
+                } else {
+                    $sql = "UPDATE  " . $work_table . " SET accepted = 0
+                            WHERE c_id = $course_id AND id = '" . $item_id . "'";
+                    Database::query($sql);
+                    api_item_property_update($course_info, 'work', $item_id, 'invisible', api_get_user_id());
+                    Display::display_confirmation_message(get_lang('FileInvisible'));
+                }
             }
         }
-    }
 
-    /*	Delete dir command */
+        /*	Delete dir command */
 
-    if ($is_allowed_to_edit && !empty($_REQUEST['delete_dir'])) {
-        $delete_dir_id = intval($_REQUEST['delete_dir']);
-        $locked = api_resource_is_locked_by_gradebook($delete_dir_id, LINK_STUDENTPUBLICATION);
+        if ($is_allowed_to_edit && !empty($_REQUEST['delete_dir'])) {
+            $delete_dir_id = intval($_REQUEST['delete_dir']);
+            $locked = api_resource_is_locked_by_gradebook($delete_dir_id, LINK_STUDENTPUBLICATION);
 
-        if ($locked == false) {
+            if ($locked == false) {
 
-            $work_to_delete = get_work_data_by_id($delete_dir_id);
-            del_dir($delete_dir_id);
+                $work_to_delete = get_work_data_by_id($delete_dir_id);
+                del_dir($delete_dir_id);
 
-            // gets calendar_id from student_publication_assigment
-            $sql = "SELECT add_to_calendar FROM $TSTDPUBASG WHERE c_id = $course_id AND publication_id ='$delete_dir_id'";
-            $res = Database::query($sql);
-            $calendar_id = Database::fetch_row($res);
+                // gets calendar_id from student_publication_assigment
+                $sql = "SELECT add_to_calendar FROM $TSTDPUBASG WHERE c_id = $course_id AND publication_id ='$delete_dir_id'";
+                $res = Database::query($sql);
+                $calendar_id = Database::fetch_row($res);
 
-            // delete from agenda if it exists
-            if (!empty($calendar_id[0])) {
-                $t_agenda   = Database::get_course_table(TABLE_AGENDA);
-                $sql = "DELETE FROM $t_agenda WHERE c_id = $course_id AND id ='".$calendar_id[0]."'";
+                // delete from agenda if it exists
+                if (!empty($calendar_id[0])) {
+                    $t_agenda   = Database::get_course_table(TABLE_AGENDA);
+                    $sql = "DELETE FROM $t_agenda WHERE c_id = $course_id AND id ='".$calendar_id[0]."'";
+                    Database::query($sql);
+                }
+                $sql = "DELETE FROM $TSTDPUBASG WHERE c_id = $course_id AND publication_id ='$delete_dir_id'";
                 Database::query($sql);
-            }
-            $sql = "DELETE FROM $TSTDPUBASG WHERE c_id = $course_id AND publication_id ='$delete_dir_id'";
-            Database::query($sql);
 
-            $link_info = is_resource_in_course_gradebook(api_get_course_id(), 3 , $delete_dir_id, api_get_session_id());
-            $link_id = $link_info['id'];
-            if ($link_info !== false) {
-                remove_resource_from_course_gradebook($link_id);
+                $link_info = is_resource_in_course_gradebook(api_get_course_id(), 3 , $delete_dir_id, api_get_session_id());
+                $link_id = $link_info['id'];
+                if ($link_info !== false) {
+                    remove_resource_from_course_gradebook($link_id);
+                }
+                Display :: display_confirmation_message(get_lang('DirDeleted') . ': '.$work_to_delete['title']);
+            } else {
+                Display::display_warning_message(get_lang('ResourceLockedByGradebook'));
             }
-            Display :: display_confirmation_message(get_lang('DirDeleted') . ': '.$work_to_delete['title']);
-        } else {
-            Display::display_warning_message(get_lang('ResourceLockedByGradebook'));
         }
-    }
 
-    /*	DELETE WORK COMMAND */
-
-    if ($action == 'delete' && $item_id) {
-
-        $file_deleted = false;
-        $is_author = user_is_author($item_id);
-        $work_data = get_work_data_by_id($item_id);
-        $locked = api_resource_is_locked_by_gradebook($work_data['parent_id'], LINK_STUDENTPUBLICATION);
-
-        if ( ($is_allowed_to_edit && $locked == false) || ($locked == false AND $is_author && api_get_course_setting('student_delete_own_publication') == 1 && $work_data['qualificator_id'] == 0)) {
-            //we found the current user is the author
-            $queryString1 	= "SELECT url, contains_file FROM  " . $work_table . "  WHERE c_id = $course_id AND id = $item_id";
-            $result1 		= Database::query($queryString1);
-            $row 			= Database::fetch_array($result1);
-
-            if (Database::num_rows($result1) > 0) {
-                $queryString2 	= "UPDATE " . $work_table . "  SET active  = 2 WHERE c_id = $course_id AND id = $item_id";
-                $queryString3 	= "DELETE FROM  " . $TSTDPUBASG . "  WHERE c_id = $course_id AND publication_id = $item_id";
-                Database::query($queryString2);
-                Database::query($queryString3);
-                api_item_property_update($_course, 'work', $item_id, 'DocumentDeleted', $user_id);
-                $work = $row['url'];
-
-                if ($row['contains_file'] == 1) {
-                    if (!empty($work)) {
-                        if (api_get_setting('permanently_remove_deleted_files') == 'true') {
-                            my_delete($currentCourseRepositorySys.'/'.$work);
-                            Display::display_confirmation_message(get_lang('TheDocumentHasBeenDeleted'));
-                            $file_deleted = true;
-                        } else {
-                            $extension = pathinfo($work, PATHINFO_EXTENSION);
-                            $new_dir = $work.'_DELETED_'.$item_id.'.'.$extension;
-
-                            if (file_exists($currentCourseRepositorySys.'/'.$work)) {
-                                rename($currentCourseRepositorySys.'/'.$work, $currentCourseRepositorySys.'/'.$new_dir);
+        /*	DELETE WORK COMMAND */
+
+        if ($action == 'delete' && $item_id) {
+
+            $file_deleted = false;
+            $is_author = user_is_author($item_id);
+            $work_data = get_work_data_by_id($item_id);
+            $locked = api_resource_is_locked_by_gradebook($work_data['parent_id'], LINK_STUDENTPUBLICATION);
+
+            if (($is_allowed_to_edit && $locked == false) || ($locked == false AND $is_author && api_get_course_setting('student_delete_own_publication') == 1 && $work_data['qualificator_id'] == 0)) {
+                //we found the current user is the author
+                $queryString1 	= "SELECT url, contains_file FROM ".$work_table." WHERE c_id = $course_id AND id = $item_id";
+                $result1 		= Database::query($queryString1);
+                $row 			= Database::fetch_array($result1);
+
+
+                if (Database::num_rows($result1) > 0) {
+                    $queryString2 	= "UPDATE " . $work_table . "  SET active = 2 WHERE c_id = $course_id AND id = $item_id";
+                    $queryString3 	= "DELETE FROM  ".$TSTDPUBASG ." WHERE c_id = $course_id AND publication_id = $item_id";
+                    Database::query($queryString2);
+                    Database::query($queryString3);
+
+                    api_item_property_update($_course, 'work', $item_id, 'DocumentDeleted', $user_id);
+                    $work = $row['url'];
+
+                    if ($row['contains_file'] == 1) {
+                        if (!empty($work)) {
+                            if (api_get_setting('permanently_remove_deleted_files') == 'true') {
+                                my_delete($currentCourseRepositorySys.'/'.$work);
                                 Display::display_confirmation_message(get_lang('TheDocumentHasBeenDeleted'));
                                 $file_deleted = true;
+                            } else {
+                                $extension = pathinfo($work, PATHINFO_EXTENSION);
+                                $new_dir = $work.'_DELETED_'.$item_id.'.'.$extension;
+
+                                if (file_exists($currentCourseRepositorySys.'/'.$work)) {
+                                    rename($currentCourseRepositorySys.'/'.$work, $currentCourseRepositorySys.'/'.$new_dir);
+                                    Display::display_confirmation_message(get_lang('TheDocumentHasBeenDeleted'));
+                                    $file_deleted = true;
+                                }
                             }
                         }
+                    } else {
+                        $file_deleted = true;
                     }
-                } else {
-                    $file_deleted = true;
                 }
             }
+
+            if (!$file_deleted) {
+                Display::display_error_message(get_lang('YouAreNotAllowedToDeleteThisDocument'));
+            }
         }
-        if (!$file_deleted) {
-            Display::display_error_message(get_lang('YouAreNotAllowedToDeleteThisDocument'));
+
+        /*	Display list of student publications */
+        if ($curdirpath == '/') {
+            $my_cur_dir_path = '';
+        } else {
+            $my_cur_dir_path = $curdirpath;
         }
-    }
 
-    /*	Display list of student publications */
-    if ($curdirpath == '/') {
-        $my_cur_dir_path = '';
-    } else {
-        $my_cur_dir_path = $curdirpath;
-    }
+        if (!empty($my_folder_data['description'])) {
+            echo '<p><div><strong>'.get_lang('Description').':</strong><p>'.Security::remove_XSS($my_folder_data['description'], STUDENT).'</p></div></p>';
+        }
 
-    if (!empty($my_folder_data['description'])) {
-        echo '<p><div><strong>'.get_lang('Description').':</strong><p>'.Security::remove_XSS($my_folder_data['description'], STUDENT).'</p></div></p>';
-    }
+        if (isset($work_id) && !empty($work_id) && !$display_list_users_without_publication) {
 
-    //User works
-if (isset($work_id) && !empty($work_id) && !$display_list_users_without_publication) {
-    $work_data = get_work_assignment_by_id($work_id);
-    $check_qualification = intval($my_folder_data['qualification']);
-
-    if (!empty($work_data['enable_qualification']) && !empty($check_qualification)) {
-        $type = 'simple';
-        $columns        = array(get_lang('Type'), get_lang('FirstName'), get_lang('LastName'), get_lang('LoginName'), get_lang('Title'),
-            get_lang('Qualification'), get_lang('Date'),  get_lang('Status'), get_lang('Actions'));
-        $column_model   = array (
-            array('name'=>'type',           'index'=>'file',            'width'=>'12',   'align'=>'left', 'search' => 'false'),
-            array('name'=>'firstname',      'index'=>'firstname',       'width'=>'35',   'align'=>'left', 'search' => 'true'),
-            array('name'=>'lastname',		'index'=>'lastname',        'width'=>'35',   'align'=>'left', 'search' => 'true'),
-            array('name'=>'username',       'index'=>'username',        'width'=>'30',   'align'=>'left', 'search' => 'true'),
-            array('name'=>'title',          'index'=>'title',           'width'=>'40',   'align'=>'left', 'search' => 'false', 'wrap_cell' => 'true'),
-            //                array('name'=>'file',           'index'=>'file',            'width'=>'20',   'align'=>'left', 'search' => 'false'),
-            array('name'=>'qualification',	'index'=>'qualification',	'width'=>'20',   'align'=>'left', 'search' => 'true'),
-            array('name'=>'sent_date',           'index'=>'sent_date',            'width'=>'50',   'align'=>'left', 'search' => 'true'),
-            array('name'=>'qualificator_id','index'=>'qualificator_id', 'width'=>'30',   'align'=>'left', 'search' => 'true'),
-            array('name'=>'actions',        'index'=>'actions',         'width'=>'40',   'align'=>'left', 'search' => 'false', 'sortable'=>'false')
-
-        );
-    } else {
-        $type = 'complex';
-        $columns        = array(get_lang('Type'), get_lang('FirstName'), get_lang('LastName'), get_lang('LoginName'), get_lang('Title'),
-            get_lang('Date'),  get_lang('Actions'));
-        $column_model   = array (
-            array('name'=>'type',           'index'=>'file',            'width'=>'12',   'align'=>'left', 'search' => 'false'),
-            array('name'=>'firstname',      'index'=>'firstname',       'width'=>'35',   'align'=>'left', 'search' => 'true'),
-            array('name'=>'lastname',		'index'=>'lastname',        'width'=>'35',   'align'=>'left', 'search' => 'true'),
-            array('name'=>'username',       'index'=>'username',        'width'=>'30',   'align'=>'left', 'search' => 'true'),
-            array('name'=>'title',          'index'=>'title',           'width'=>'40',   'align'=>'left', 'search' => 'false', 'wrap_cell' => "true"),
-            //                array('name'=>'file',           'index'=>'file',            'width'=>'20',   'align'=>'left', 'search' => 'false'),
-            //array('name'=>'qualification',	'index'=>'qualification',	'width'=>'20',   'align'=>'left', 'search' => 'true'),
-            array('name'=>'sent_date',       'index'=>'sent_date',            'width'=>'50',   'align'=>'left', 'search' => 'true'),
-            //array('name'=>'qualificator_id','index'=>'qualificator_id', 'width'=>'30',   'align'=>'left', 'search' => 'true'),
-            array('name'=>'actions',        'index'=>'actions',         'width'=>'40',   'align'=>'left', 'search' => 'false', 'sortable'=>'false')
-        );
-    }
+        } elseif (isset($_GET['list']) && $_GET['list'] == 'without') {
 
-    $extra_params = array();
-
-    //Autowidth
-    $extra_params['autowidth'] = 'true';
-
-    //height auto
-    $extra_params['height'] = 'auto';
-    //$extra_params['excel'] = 'excel';
-
-    //$extra_params['rowList'] = array(10, 20 ,30);
-
-    $extra_params['sortname'] = 'firstname';
-    $url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_work_user_list&work_id='.$work_id.'&type='.$type;
-    ?>
-    <script>
-        $(function() {
-            <?php
-            echo Display::grid_js('results', $url, $columns, $column_model, $extra_params);
-        ?>
-        });
-    </script>
-    <?php
-    echo Display::grid_html('results');
-} elseif (isset($_GET['list']) && $_GET['list'] == 'without') {
-    //User with no works
-    display_list_users_without_publication($work_id);
-} else {
-    //Work list
-    display_student_publications_list($work_id, $link_target_parameter, $dateFormatLong, $origin, $add_query);
-}
+        } else {
+
+            $my_folder_data = get_work_data_by_id($work_id);
+
+            $work_parents = array();
+            if (empty($my_folder_data)) {
+                $work_parents = getWorkList($work_id, $my_folder_data, $add_query);
+            }
+
+            if (api_is_allowed_to_edit()) {
+
+                // Work list
+                echo '<div class="row">';
+                echo '<div class="span9">';
+
+                if (!empty($group_id)) {
+                    $userList = GroupManager::get_users($group_id);
+                } else {
+                    if (empty($session_id)) {
+                        $userList = CourseManager::get_user_list_from_course_code($course_code, $session_id, null, null, STUDENT);
+                    } else {
+                        $userList = CourseManager::get_user_list_from_course_code($course_code, $session_id, null, null, 0);
+                    }
+                    $userList = array_keys($userList);
+                }
+
+                display_student_publications_list($work_id, $my_folder_data, $work_parents, $origin, $add_query, $userList);
+
+                echo '</div>';
+                echo '<div class="span3">';
+
+                $table = new HTML_Table(array('class' => 'data_table'));
+                $column = 0;
+                $row = 0;
+                $headers = array(get_lang('Students'), get_lang('Works'));
+                foreach ($headers as $header) {
+                    $table->setHeaderContents($row, $column, $header);
+                    $column++;
+                }
+                $row++;
+                $column = 0;
+
+                foreach ($userList as $userId) {
+                    $user = api_get_user_info($userId);
+                    $link = api_get_path(WEB_CODE_PATH).'work/student_work.php?'.api_get_cidreq().'&studentId='.$user['user_id'];
+                    $url = Display::url(api_get_person_name($user['firstname'], $user['lastname']), $link);
+                    $table->setCellContents($row, $column, $url);
+                    $column++;
+                    $userWorks = 0;
+                    foreach ($work_parents as $work) {
+                        $userWorks += getUniqueStudentAttempts($work->id, $group_id, $course_id, $session_id, $user['user_id']);
+                    }
+                    $cell = $userWorks." / ".count($work_parents);
+                    $table->setCellContents($row, $column, $cell);
+                    $row++;
+                    $column = 0;
+                }
+
+                echo $table->toHtml();
+            echo '</div>';
+        } else {
+            display_student_publications_list($work_id, $my_folder_data, $work_parents, $origin, $add_query, null);
+        }
+    }
     break;
 }
 if ($origin != 'learnpath') {

+ 136 - 0
main/work/work_list.php

@@ -0,0 +1,136 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+use ChamiloSession as Session;
+
+$language_file = array('exercice', 'work', 'document', 'admin', 'gradebook');
+
+require_once '../inc/global.inc.php';
+$current_course_tool  = TOOL_STUDENTPUBLICATION;
+
+/*	Configuration settings */
+
+api_protect_course_script(true);
+
+// Including necessary files
+require_once 'work.lib.php';
+$this_section = SECTION_COURSES;
+
+$workId = isset($_GET['id']) ? intval($_GET['id']) : null;
+
+if (empty($workId)) {
+    api_not_allowed(true);
+}
+
+$my_folder_data = get_work_data_by_id($workId);
+if (empty($my_folder_data)) {
+    api_not_allowed(true);
+}
+
+$work_data = get_work_assignment_by_id($workId);
+$tool_name = get_lang('StudentPublications');
+
+$group_id = api_get_group_id();
+$courseInfo = api_get_course_info();
+$htmlHeadXtra[] = api_get_jqgrid_js();
+$url_dir = api_get_path(WEB_CODE_PATH).'work/work.php?'.api_get_cidreq();
+
+
+if (!empty($group_id)) {
+    $group_properties  = GroupManager :: get_group_properties($group_id);
+    $show_work = false;
+
+    if (api_is_allowed_to_edit(false, true)) {
+        $show_work = true;
+    } else {
+        // you are not a teacher
+        $show_work = GroupManager::user_has_access($user_id, $group_id, GroupManager::GROUP_TOOL_WORK);
+    }
+
+    if (!$show_work) {
+        api_not_allowed();
+    }
+
+    $interbreadcrumb[] = array ('url' => '../group/group.php', 'name' => get_lang('Groups'));
+    $interbreadcrumb[] = array ('url' => '../group/group_space.php?gidReq='.$group_id, 'name' => get_lang('GroupSpace').' '.$group_properties['name']);
+}
+
+$interbreadcrumb[] = array ('url' => api_get_path(WEB_CODE_PATH).'work/work.php?'.api_get_cidreq(), 'name' => get_lang('StudentPublications'));
+$interbreadcrumb[] = array ('url' => api_get_path(WEB_CODE_PATH).'work/work_list.php?'.api_get_cidreq().'&id='.$workId, 'name' =>  $my_folder_data['title']);
+
+Display :: display_header(null);
+
+echo '<div class="actions">';
+echo '<a href="'.api_get_path(WEB_CODE_PATH).'work/work.php?'.api_get_cidreq().'&origin='.$origin.'&gradebook='.$gradebook.'">'.Display::return_icon('back.png', get_lang('BackToWorksList'),'',ICON_SIZE_MEDIUM).'</a>';
+if (api_is_allowed_to_session_edit(false, true) && !empty($workId)) {
+    echo '<a href="'.api_get_path(WEB_CODE_PATH).'work/upload.php?'.api_get_cidreq().'&id='.$workId.'&origin='.$origin.'&gradebook='.$gradebook.'">';
+    echo Display::return_icon('upload_file.png', get_lang('UploadADocument'),'',ICON_SIZE_MEDIUM).'</a>';
+}
+echo '</div>';
+
+
+$error_message = isset($_GET['error_message']) ? Security::remove_XSS($_GET['error_message']) : null;
+if (!empty($error_message)) {
+    echo $error_message;
+}
+
+
+$check_qualification = intval($my_folder_data['qualification']);
+
+if (!empty($work_data['enable_qualification']) && !empty($check_qualification)) {
+    $type = 'simple';
+    $columns        = array(get_lang('Type'), get_lang('FirstName'), get_lang('LastName'), get_lang('LoginName'), get_lang('Title'), get_lang('Qualification'), get_lang('Date'),  get_lang('Status'), get_lang('Actions'));
+    $column_model   = array (
+        array('name'=>'type',           'index'=>'file',            'width'=>'12',   'align'=>'left', 'search' => 'false'),
+        array('name'=>'firstname',      'index'=>'firstname',       'width'=>'35',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'lastname',		'index'=>'lastname',        'width'=>'35',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'username',       'index'=>'username',        'width'=>'30',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'title',          'index'=>'title',           'width'=>'40',   'align'=>'left', 'search' => 'false', 'wrap_cell' => 'true'),
+        //                array('name'=>'file',           'index'=>'file',            'width'=>'20',   'align'=>'left', 'search' => 'false'),
+        array('name'=>'qualification',	'index'=>'qualification',	'width'=>'20',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'sent_date',           'index'=>'sent_date',            'width'=>'50',   'align'=>'left', 'search' => 'true', 'wrap_cell' => 'true'),
+        array('name'=>'qualificator_id','index'=>'qualificator_id', 'width'=>'30',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'actions',        'index'=>'actions',         'width'=>'40',   'align'=>'left', 'search' => 'false', 'sortable'=>'false')
+    );
+} else {
+    $type = 'complex';
+    $columns  = array(get_lang('Type'), get_lang('FirstName'), get_lang('LastName'), get_lang('LoginName'), get_lang('Title'), get_lang('Date'),  get_lang('Actions'));
+    $column_model   = array (
+        array('name'=>'type',           'index'=>'file',            'width'=>'12',   'align'=>'left', 'search' => 'false'),
+        array('name'=>'firstname',      'index'=>'firstname',       'width'=>'35',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'lastname',		'index'=>'lastname',        'width'=>'35',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'username',       'index'=>'username',        'width'=>'30',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'title',          'index'=>'title',           'width'=>'40',   'align'=>'left', 'search' => 'false', 'wrap_cell' => "true"),
+        //                array('name'=>'file',           'index'=>'file',            'width'=>'20',   'align'=>'left', 'search' => 'false'),
+        //array('name'=>'qualification',	'index'=>'qualification',	'width'=>'20',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'sent_date',       'index'=>'sent_date',            'width'=>'50',   'align'=>'left', 'search' => 'true', 'wrap_cell' => 'true'),
+        //array('name'=>'qualificator_id','index'=>'qualificator_id', 'width'=>'30',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'actions',        'index'=>'actions',         'width'=>'40',   'align'=>'left', 'search' => 'false', 'sortable'=>'false')
+    );
+}
+
+$extra_params = array();
+
+//Autowidth
+$extra_params['autowidth'] = 'true';
+
+//height auto
+$extra_params['height'] = 'auto';
+//$extra_params['excel'] = 'excel';
+
+//$extra_params['rowList'] = array(10, 20 ,30);
+
+$extra_params['sortname'] = 'firstname';
+$url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_work_user_list&work_id='.$workId.'&type='.$type;
+?>
+<script>
+$(function() {
+    <?php
+    echo Display::grid_js('results', $url, $columns, $column_model, $extra_params);
+?>
+});
+</script>
+<?php
+echo Display::grid_html('results');
+
+Display :: display_footer();

+ 151 - 0
main/work/work_list_all.php

@@ -0,0 +1,151 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+use ChamiloSession as Session;
+
+$language_file = array('exercice', 'work', 'document', 'admin', 'gradebook');
+
+require_once '../inc/global.inc.php';
+$current_course_tool  = TOOL_STUDENTPUBLICATION;
+
+/*	Configuration settings */
+
+api_protect_course_script(true);
+
+// Including necessary files
+require_once 'work.lib.php';
+$this_section = SECTION_COURSES;
+
+$workId = isset($_GET['id']) ? intval($_GET['id']) : null;
+
+if (empty($workId)) {
+    api_not_allowed(true);
+}
+
+$my_folder_data = get_work_data_by_id($workId);
+if (empty($my_folder_data)) {
+    api_not_allowed(true);
+}
+
+$work_data = get_work_assignment_by_id($workId);
+
+if (!api_is_allowed_to_edit()) {
+    api_not_allowed(true);
+}
+
+$tool_name = get_lang('StudentPublications');
+$group_id = api_get_group_id();
+$courseInfo = api_get_course_info();
+$htmlHeadXtra[] = api_get_jqgrid_js();
+
+if (!empty($group_id)) {
+    $group_properties  = GroupManager :: get_group_properties($group_id);
+    $show_work = false;
+
+    if (api_is_allowed_to_edit(false, true)) {
+        $show_work = true;
+    } else {
+        // you are not a teacher
+        $show_work = GroupManager::user_has_access($user_id, $group_id, GroupManager::GROUP_TOOL_WORK);
+    }
+
+    if (!$show_work) {
+        api_not_allowed();
+    }
+
+    $interbreadcrumb[] = array ('url' => '../group/group.php', 'name' => get_lang('Groups'));
+    $interbreadcrumb[] = array ('url' => '../group/group_space.php?gidReq='.$group_id, 'name' => get_lang('GroupSpace').' '.$group_properties['name']);
+}
+
+$interbreadcrumb[] = array ('url' => api_get_path(WEB_CODE_PATH).'work/work.php?'.api_get_cidreq(), 'name' => get_lang('StudentPublications'));
+$interbreadcrumb[] = array ('url' => api_get_path(WEB_CODE_PATH).'work/work_list_all.php?'.api_get_cidreq().'&id='.$workId, 'name' =>  $my_folder_data['title']);
+
+$error_message = null;
+
+Display :: display_header(null);
+
+echo '<div class="actions">';
+echo '<a href="'.api_get_path(WEB_CODE_PATH).'work/work.php?'.api_get_cidreq().'&origin='.$origin.'&gradebook='.$gradebook.'">'.Display::return_icon('back.png', get_lang('BackToWorksList'),'',ICON_SIZE_MEDIUM).'</a>';
+if (api_is_allowed_to_session_edit(false, true) && !empty($workId)) {
+    echo '<a href="'.api_get_path(WEB_CODE_PATH).'work/upload.php?'.api_get_cidreq().'&id='.$workId.'&origin='.$origin.'&gradebook='.$gradebook.'">';
+    echo Display::return_icon('upload_file.png', get_lang('UploadADocument'),'',ICON_SIZE_MEDIUM).'</a>';
+
+    $display_output .= '<a href="'.api_get_path(WEB_CODE_PATH).'work/work_missing.php?'.api_get_cidreq().'&amp;id='.$workId.'&amp;curdirpath='.$cur_dir_path.'&amp;origin='.$origin.'&amp;gradebook='.$gradebook.'&amp;list=without">'.
+    Display::return_icon('exercice_uncheck.png', get_lang('ViewUsersWithoutTask'),'',ICON_SIZE_MEDIUM)."</a>\n";
+    $count = get_count_work($workId);
+    if ($count > 0) {
+        $display_output .= '<a href="downloadfolder.inc.php?id='.$workId.'">'.Display::return_icon('save_pack.png', get_lang('Save'), array('style' => 'float:right;'), ICON_SIZE_MEDIUM).'</a>';
+    }
+    echo $display_output;
+}
+
+echo '</div>';
+
+if (empty($error_message)) {
+    $error_message = isset($_GET['error_message']) ? Security::remove_XSS($_GET['error_message']) : null;
+}
+if (!empty($error_message)) {
+    echo $error_message;
+}
+
+// User works
+$check_qualification = intval($my_folder_data['qualification']);
+
+if (!empty($work_data['enable_qualification']) && !empty($check_qualification)) {
+    $type = 'simple';
+    $columns        = array(get_lang('Type'), get_lang('FirstName'), get_lang('LastName'), get_lang('LoginName'), get_lang('Title'), get_lang('Qualification'), get_lang('Date'),  get_lang('Status'), get_lang('Actions'));
+    $column_model   = array (
+        array('name'=>'type',           'index'=>'file',            'width'=>'10',   'align'=>'left', 'search' => 'false'),
+        array('name'=>'firstname',      'index'=>'firstname',       'width'=>'35',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'lastname',		'index'=>'lastname',        'width'=>'35',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'username',       'index'=>'username',        'width'=>'30',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'title',          'index'=>'title',           'width'=>'40',   'align'=>'left', 'search' => 'false', 'wrap_cell' => 'true'),
+        //                array('name'=>'file',           'index'=>'file',            'width'=>'20',   'align'=>'left', 'search' => 'false'),
+        array('name'=>'qualification',	'index'=>'qualification',	'width'=>'20',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'sent_date',           'index'=>'sent_date',            'width'=>'50',   'align'=>'left', 'search' => 'true', 'wrap_cell' => 'true'),
+        array('name'=>'qualificator_id','index'=>'qualificator_id', 'width'=>'30',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'actions',        'index'=>'actions',         'width'=>'40',   'align'=>'left', 'search' => 'false', 'sortable'=>'false')
+    );
+} else {
+    $type = 'complex';
+    $columns  = array(get_lang('Type'), get_lang('FirstName'), get_lang('LastName'), get_lang('LoginName'), get_lang('Title'), get_lang('Date'),  get_lang('Actions'));
+    $column_model   = array (
+        array('name'=>'type',           'index'=>'file',            'width'=>'10',   'align'=>'left', 'search' => 'false'),
+        array('name'=>'firstname',      'index'=>'firstname',       'width'=>'35',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'lastname',		'index'=>'lastname',        'width'=>'35',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'username',       'index'=>'username',        'width'=>'30',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'title',          'index'=>'title',           'width'=>'40',   'align'=>'left', 'search' => 'false', 'wrap_cell' => "true"),
+        //                array('name'=>'file',           'index'=>'file',            'width'=>'20',   'align'=>'left', 'search' => 'false'),
+        //array('name'=>'qualification',	'index'=>'qualification',	'width'=>'20',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'sent_date',       'index'=>'sent_date',            'width'=>'50',   'align'=>'left', 'search' => 'true', 'wrap_cell' => 'true'),
+        //array('name'=>'qualificator_id','index'=>'qualificator_id', 'width'=>'30',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'actions',        'index'=>'actions',         'width'=>'40',   'align'=>'left', 'search' => 'false', 'sortable'=>'false')
+    );
+}
+
+$extra_params = array();
+
+//Autowidth
+$extra_params['autowidth'] = 'true';
+
+//height auto
+$extra_params['height'] = 'auto';
+//$extra_params['excel'] = 'excel';
+
+//$extra_params['rowList'] = array(10, 20 ,30);
+
+$extra_params['sortname'] = 'firstname';
+$url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_work_user_list_all&work_id='.$workId.'&type='.$type;
+?>
+<script>
+$(function() {
+    <?php
+    echo Display::grid_js('results', $url, $columns, $column_model, $extra_params);
+?>
+});
+</script>
+<?php
+
+echo Display::grid_html('results');
+
+Display :: display_footer();

+ 133 - 0
main/work/work_list_others.php

@@ -0,0 +1,133 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+use ChamiloSession as Session;
+
+$language_file = array('exercice', 'work', 'document', 'admin', 'gradebook');
+
+require_once '../inc/global.inc.php';
+$current_course_tool  = TOOL_STUDENTPUBLICATION;
+
+/*	Configuration settings */
+
+api_protect_course_script(true);
+
+// Including necessary files
+require_once 'work.lib.php';
+$this_section = SECTION_COURSES;
+
+$workId = isset($_GET['id']) ? intval($_GET['id']) : null;
+
+if (empty($workId)) {
+    api_not_allowed(true);
+}
+
+$my_folder_data = get_work_data_by_id($workId);
+
+if (empty($my_folder_data)) {
+    api_not_allowed(true);
+}
+
+$work_data = get_work_assignment_by_id($workId);
+$tool_name = get_lang('StudentPublications');
+$group_id = api_get_group_id();
+$courseInfo = api_get_course_info();
+
+// not all users
+if ($courseInfo['show_score'] == 1) {
+    api_not_allowed(true);
+}
+
+$htmlHeadXtra[] = api_get_jqgrid_js();
+
+
+if (!empty($group_id)) {
+    $group_properties  = GroupManager :: get_group_properties($group_id);
+    $show_work = false;
+
+    if (api_is_allowed_to_edit(false, true)) {
+        $show_work = true;
+    } else {
+        // you are not a teacher
+        $show_work = GroupManager::user_has_access($user_id, $group_id, GroupManager::GROUP_TOOL_WORK);
+    }
+
+    if (!$show_work) {
+        api_not_allowed();
+    }
+
+    $interbreadcrumb[] = array ('url' => '../group/group.php', 'name' => get_lang('Groups'));
+    $interbreadcrumb[] = array ('url' => '../group/group_space.php?gidReq='.$group_id, 'name' => get_lang('GroupSpace').' '.$group_properties['name']);
+}
+
+$interbreadcrumb[] = array ('url' => api_get_path(WEB_CODE_PATH).'work/work.php?'.api_get_cidreq(), 'name' => get_lang('StudentPublications'));
+$interbreadcrumb[] = array ('url' => api_get_path(WEB_CODE_PATH).'work/work_list_others.php?'.api_get_cidreq().'&id='.$workId, 'name' =>  $my_folder_data['title']);
+
+
+Display :: display_header(null);
+
+echo '<div class="actions">';
+echo '<a href="'.api_get_path(WEB_CODE_PATH).'work/work.php?'.api_get_cidreq().'&origin='.$origin.'&gradebook='.$gradebook.'">'.Display::return_icon('back.png', get_lang('BackToWorksList'),'',ICON_SIZE_MEDIUM).'</a>';
+echo '</div>';
+
+
+if (!empty($my_folder_data['description'])) {
+    echo '<p><div><strong>'.get_lang('Description').':</strong><p>'.Security::remove_XSS($my_folder_data['description'], STUDENT).'</p></div></p>';
+}
+
+$check_qualification = intval($my_folder_data['qualification']);
+
+if (!empty($work_data['enable_qualification']) && !empty($check_qualification)) {
+    $type = 'simple';
+    $columns        = array(get_lang('Type'), get_lang('FirstName'), get_lang('LastName'), get_lang('LoginName'), get_lang('Title'), get_lang('Qualification'), get_lang('Date'),  get_lang('Status'), get_lang('Actions'));
+    $column_model   = array (
+        array('name'=>'type',           'index'=>'file',            'width'=>'12',   'align'=>'left', 'search' => 'false'),
+        array('name'=>'firstname',      'index'=>'firstname',       'width'=>'35',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'lastname',		'index'=>'lastname',        'width'=>'35',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'username',       'index'=>'username',        'width'=>'30',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'title',          'index'=>'title',           'width'=>'40',   'align'=>'left', 'search' => 'false', 'wrap_cell' => 'true'),
+        //                array('name'=>'file',           'index'=>'file',            'width'=>'20',   'align'=>'left', 'search' => 'false'),
+        array('name'=>'qualification',	'index'=>'qualification',	'width'=>'20',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'sent_date',           'index'=>'sent_date',            'width'=>'50',   'align'=>'left', 'search' => 'true', 'wrap_cell' => 'true'),
+        array('name'=>'qualificator_id','index'=>'qualificator_id', 'width'=>'30',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'actions',        'index'=>'actions',         'width'=>'40',   'align'=>'left', 'search' => 'false', 'sortable'=>'false')
+    );
+} else {
+    $type = 'complex';
+    $columns  = array(get_lang('Type'), get_lang('FirstName'), get_lang('LastName'), get_lang('LoginName'), get_lang('Title'), get_lang('Date'),  get_lang('Actions'));
+    $column_model   = array (
+        array('name'=>'type',           'index'=>'file',            'width'=>'12',   'align'=>'left', 'search' => 'false'),
+        array('name'=>'firstname',      'index'=>'firstname',       'width'=>'35',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'lastname',		'index'=>'lastname',        'width'=>'35',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'username',       'index'=>'username',        'width'=>'30',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'title',          'index'=>'title',           'width'=>'40',   'align'=>'left', 'search' => 'false', 'wrap_cell' => "true"),
+        //                array('name'=>'file',           'index'=>'file',            'width'=>'20',   'align'=>'left', 'search' => 'false'),
+        //array('name'=>'qualification',	'index'=>'qualification',	'width'=>'20',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'sent_date',       'index'=>'sent_date',            'width'=>'50',   'align'=>'left', 'search' => 'true', 'wrap_cell' => 'true'),
+        //array('name'=>'qualificator_id','index'=>'qualificator_id', 'width'=>'30',   'align'=>'left', 'search' => 'true'),
+        array('name'=>'actions',        'index'=>'actions',         'width'=>'40',   'align'=>'left', 'search' => 'false', 'sortable'=>'false')
+    );
+}
+
+$extra_params = array();
+
+//Autowidth
+$extra_params['autowidth'] = 'true';
+
+//height auto
+$extra_params['height'] = 'auto';
+
+$extra_params['sortname'] = 'firstname';
+$url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_work_user_list_others&work_id='.$workId.'&type='.$type;
+?>
+<script>
+$(function() {
+    <?php
+    echo Display::grid_js('results', $url, $columns, $column_model, $extra_params);
+?>
+});
+</script>
+<?php
+echo Display::grid_html('results');
+
+Display :: display_footer();

+ 115 - 0
main/work/work_missing.php

@@ -0,0 +1,115 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+use ChamiloSession as Session;
+
+$language_file = array('exercice', 'work', 'document', 'admin', 'gradebook');
+
+require_once '../inc/global.inc.php';
+$current_course_tool  = TOOL_STUDENTPUBLICATION;
+
+/*	Configuration settings */
+
+api_protect_course_script(true);
+
+// Including necessary files
+require_once 'work.lib.php';
+$this_section = SECTION_COURSES;
+
+$workId = isset($_GET['id']) ? intval($_GET['id']) : null;
+
+$group_id = api_get_group_id();
+
+if (empty($workId)) {
+    api_not_allowed(true);
+}
+
+$my_folder_data = get_work_data_by_id($workId);
+if (empty($my_folder_data)) {
+    api_not_allowed(true);
+}
+
+if (!api_is_allowed_to_edit(null, true)) {
+    api_not_allowed(true);
+}
+
+// User with no works
+$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : 'list';
+
+switch ($action) {
+    case 'send_mail':
+        $check = Security::check_token('get');
+        if ($check) {
+            $mails_sent_to = send_reminder_users_without_publication($my_folder_data);
+
+            if (empty($mails_sent_to)) {
+                $error_message = Display::return_message(get_lang('NoResults'), 'warning');
+            } else {
+                $error_message = Display::return_message(get_lang('MessageHasBeenSent').' '.implode(', ', $mails_sent_to), 'success');
+            }
+            Security::clear_token();
+        }
+        break;
+}
+
+$token = Security::get_token();
+
+if (!empty($group_id)) {
+    $group_properties  = GroupManager :: get_group_properties($group_id);
+    $show_work = false;
+
+    if (api_is_allowed_to_edit(false, true)) {
+        $show_work = true;
+    } else {
+        // you are not a teacher
+        $show_work = GroupManager::user_has_access($user_id, $group_id, GroupManager::GROUP_TOOL_WORK);
+    }
+
+    if (!$show_work) {
+        api_not_allowed();
+    }
+
+    $interbreadcrumb[] = array ('url' => '../group/group.php', 'name' => get_lang('Groups'));
+    $interbreadcrumb[] = array ('url' => '../group/group_space.php?gidReq='.$group_id, 'name' => get_lang('GroupSpace').' '.$group_properties['name']);
+}
+
+$interbreadcrumb[] = array ('url' => api_get_path(WEB_CODE_PATH).'work/work.php?'.api_get_cidreq(), 'name' => get_lang('StudentPublications'));
+$interbreadcrumb[] = array ('url' => api_get_path(WEB_CODE_PATH).'work/work_list_all.php?'.api_get_cidreq().'&id='.$workId, 'name' =>  $my_folder_data['title']);
+
+if (isset($_GET['list']) && $_GET['list'] == 'with') {
+    $interbreadcrumb[] = array ('url' => '#', 'name' => get_lang('UsersWithTask'));
+} else {
+    $interbreadcrumb[] = array ('url' => '#', 'name' => get_lang('UsersWithoutTask'));
+}
+
+
+Display :: display_header(null);
+
+echo '<div class="actions">';
+echo '<a href="'.api_get_path(WEB_CODE_PATH).'work/work_list_all.php?id='.$workId.'&'.api_get_cidreq().'&origin='.$origin.'&gradebook='.$gradebook.'">'.Display::return_icon('back.png', get_lang('BackToWorksList'),'',ICON_SIZE_MEDIUM).'</a>';
+
+if (!empty($workId)) {
+    if (empty($_GET['list']) or Security::remove_XSS($_GET['list']) == 'with') {
+        $display_output .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&amp;id='.$workId.'&amp;curdirpath='.$cur_dir_path.'&amp;origin='.$origin.'&amp;gradebook='.$gradebook.'&amp;list=without">'.
+        Display::return_icon('exercice_uncheck.png', get_lang('ViewUsersWithoutTask'),'',ICON_SIZE_MEDIUM)."</a>";
+    } else {
+        if (!isset($_GET['action']) || (isset($_GET['action']) && $_GET['action'] != 'send_mail')) {
+            $display_output .= '<a href="'.api_get_self().'?'.api_get_cidreq().'&amp;id='.$workId.'&amp;curdirpath='.$cur_dir_path.'&amp;origin='.$origin.'&amp;gradebook='.$gradebook.'&amp;list=without&amp;action=send_mail&amp;sec_token='.$token.'">'.
+            Display::return_icon('mail_send.png', get_lang('ReminderMessage'),'',ICON_SIZE_MEDIUM)."</a>";
+        } else {
+            $display_output .= Display::return_icon('mail_send_na.png', get_lang('ReminderMessage'),'',ICON_SIZE_MEDIUM);
+        }
+    }
+}
+
+echo $display_output;
+echo '</div>';
+
+if (empty($error_message)) {
+    $error_message = isset($_GET['error_message']) ? Security::remove_XSS($_GET['error_message']) : null;
+}
+if (!empty($error_message)) {
+    echo $error_message;
+}
+
+display_list_users_without_publication($workId);

+ 3 - 1
plugin/bbb/lib/bbb.lib.php

@@ -118,7 +118,9 @@ class bbb {
             $welcome_msg        = isset($params['welcome_msg']) ? $params['welcome_msg'] : null;
             $record             = isset($params['record']) && $params['record'] ? 'true' : 'false';
             $duration           = isset($params['duration']) ? intval($params['duration']) : 0;
-            $duration           = 30;
+            // This setting currently limits the maximum conference duration,
+            // to avoid llingering sessions on the videoconference server #6261
+            $duration           = 300;
 
             $bbb_params = array(
                 'meetingId' => $id, 					// REQUIRED