Browse Source

Merge pull request #20 from chamilo/1.9.x

1.9.x
ycastillo 11 years ago
parent
commit
edca938fe0
88 changed files with 3281 additions and 499 deletions
  1. 11 2
      index.php
  2. 42 49
      main/admin/add_courses_to_usergroup.php
  3. 15 12
      main/admin/course_add.php
  4. 11 9
      main/admin/course_edit.php
  5. 29 9
      main/admin/user_import.php
  6. 7 2
      main/admin/user_list.php
  7. 5 5
      main/auth/external_login/login.ldap.php
  8. 84 0
      main/auth/external_login/login.ws.php
  9. 24 0
      main/coursecopy/classes/CourseRestorer.class.php
  10. 101 0
      main/exercice/aiken.php
  11. 2 1
      main/exercice/exercice.php
  12. 142 0
      main/exercice/export/aiken/aiken_classes.php
  13. 224 0
      main/exercice/export/aiken/aiken_import.inc.php
  14. 3 1
      main/gradebook/lib/gradebook_functions.inc.php
  15. 6 3
      main/group/group.php
  16. 2 22
      main/group/group_creation.php
  17. 34 19
      main/inc/ajax/course.ajax.php
  18. 84 39
      main/inc/lib/course.lib.php
  19. 53 4
      main/inc/lib/course_category.lib.php
  20. 191 97
      main/inc/lib/document.lib.php
  21. 1 0
      main/inc/lib/fckeditor/editor/_source/classes/fckxml_ie.js
  22. 93 0
      main/inc/lib/formvalidator/Element/select_ajax.php
  23. 2 1
      main/inc/lib/formvalidator/FormValidator.class.php
  24. 129 117
      main/inc/lib/groupmanager.lib.php
  25. 18 0
      main/inc/lib/javascript/select2/LICENSE
  26. 83 0
      main/inc/lib/javascript/select2/README.md
  27. 8 0
      main/inc/lib/javascript/select2/bower.json
  28. 87 0
      main/inc/lib/javascript/select2/select2-bootstrap.css
  29. BIN
      main/inc/lib/javascript/select2/select2-spinner.gif
  30. 615 0
      main/inc/lib/javascript/select2/select2.css
  31. 36 0
      main/inc/lib/javascript/select2/select2.jquery.json
  32. 100 0
      main/inc/lib/javascript/select2/select2.js
  33. 20 0
      main/inc/lib/javascript/select2/select2.min.js
  34. BIN
      main/inc/lib/javascript/select2/select2.png
  35. 17 0
      main/inc/lib/javascript/select2/select2_locale_ar.js
  36. 17 0
      main/inc/lib/javascript/select2/select2_locale_bg.js
  37. 17 0
      main/inc/lib/javascript/select2/select2_locale_ca.js
  38. 49 0
      main/inc/lib/javascript/select2/select2_locale_cs.js
  39. 17 0
      main/inc/lib/javascript/select2/select2_locale_da.js
  40. 15 0
      main/inc/lib/javascript/select2/select2_locale_de.js
  41. 17 0
      main/inc/lib/javascript/select2/select2_locale_el.js
  42. 17 0
      main/inc/lib/javascript/select2/select2_locale_en.js.template
  43. 15 0
      main/inc/lib/javascript/select2/select2_locale_es.js
  44. 17 0
      main/inc/lib/javascript/select2/select2_locale_et.js
  45. 43 0
      main/inc/lib/javascript/select2/select2_locale_eu.js
  46. 17 0
      main/inc/lib/javascript/select2/select2_locale_fa.js
  47. 28 0
      main/inc/lib/javascript/select2/select2_locale_fi.js
  48. 15 0
      main/inc/lib/javascript/select2/select2_locale_fr.js
  49. 43 0
      main/inc/lib/javascript/select2/select2_locale_gl.js
  50. 17 0
      main/inc/lib/javascript/select2/select2_locale_he.js
  51. 42 0
      main/inc/lib/javascript/select2/select2_locale_hr.js
  52. 15 0
      main/inc/lib/javascript/select2/select2_locale_hu.js
  53. 17 0
      main/inc/lib/javascript/select2/select2_locale_id.js
  54. 16 0
      main/inc/lib/javascript/select2/select2_locale_is.js
  55. 15 0
      main/inc/lib/javascript/select2/select2_locale_it.js
  56. 15 0
      main/inc/lib/javascript/select2/select2_locale_ja.js
  57. 17 0
      main/inc/lib/javascript/select2/select2_locale_ko.js
  58. 29 0
      main/inc/lib/javascript/select2/select2_locale_lt.js
  59. 16 0
      main/inc/lib/javascript/select2/select2_locale_lv.js
  60. 17 0
      main/inc/lib/javascript/select2/select2_locale_mk.js
  61. 17 0
      main/inc/lib/javascript/select2/select2_locale_ms.js
  62. 15 0
      main/inc/lib/javascript/select2/select2_locale_nl.js
  63. 18 0
      main/inc/lib/javascript/select2/select2_locale_no.js
  64. 37 0
      main/inc/lib/javascript/select2/select2_locale_pl.js
  65. 15 0
      main/inc/lib/javascript/select2/select2_locale_pt-BR.js
  66. 15 0
      main/inc/lib/javascript/select2/select2_locale_pt-PT.js
  67. 15 0
      main/inc/lib/javascript/select2/select2_locale_ro.js
  68. 15 0
      main/inc/lib/javascript/select2/select2_locale_ru.js
  69. 48 0
      main/inc/lib/javascript/select2/select2_locale_sk.js
  70. 17 0
      main/inc/lib/javascript/select2/select2_locale_sv.js
  71. 17 0
      main/inc/lib/javascript/select2/select2_locale_th.js
  72. 17 0
      main/inc/lib/javascript/select2/select2_locale_tr.js
  73. 17 0
      main/inc/lib/javascript/select2/select2_locale_ua.js
  74. 18 0
      main/inc/lib/javascript/select2/select2_locale_vi.js
  75. 14 0
      main/inc/lib/javascript/select2/select2_locale_zh-CN.js
  76. 14 0
      main/inc/lib/javascript/select2/select2_locale_zh-TW.js
  77. BIN
      main/inc/lib/javascript/select2/select2x2.png
  78. 1 0
      main/inc/lib/main_api.lib.php
  79. 64 41
      main/inc/lib/sessionmanager.lib.php
  80. 27 16
      main/inc/lib/usermanager.lib.php
  81. 1 1
      main/inc/lib/zombie/zombie_manager.class.php
  82. 1 1
      main/inc/local.inc.php
  83. 2 0
      main/install/configuration.dist.php
  84. 1 1
      main/template/default/layout/topbar.tpl
  85. 3 3
      main/user/user.php
  86. 0 31
      main/work/add_document.php
  87. 29 0
      main/work/extra.sql
  88. 21 13
      main/work/work.lib.php

+ 11 - 2
index.php

@@ -177,9 +177,16 @@ if (!isset($_REQUEST['include'])) {
     $announcements_block = $controller->return_announcements();
 }
 
+global $_configuration;
+//hiding global announcements when user not connected
+if ($_configuration['hide_global_announcements_when_not_connected'] && isset($_user['user_id'])) {
+    $controller->tpl->assign('announcements_block', $announcements_block);    
+}
+//hiding home top whe user not connected
+if ($_configuration['hide_home_top_when_connected'] && !isset($_user['user_id'])) {
+    $controller->tpl->assign('home_page_block', $controller->return_home_page());    
+}
 $controller->tpl->assign('hot_courses', $hot_courses);
-$controller->tpl->assign('announcements_block', $announcements_block);
-$controller->tpl->assign('home_page_block', $controller->return_home_page());
 $controller->tpl->assign('navigation_course_links', $controller->return_navigation_links());
 $controller->tpl->assign('notice_block', $controller->return_notice());
 $controller->tpl->assign('main_navigation_block', $controller->return_navigation_links());
@@ -190,3 +197,5 @@ if (api_is_platform_admin() || api_is_drh()) {
 }
 
 $controller->tpl->display_two_col_template();
+#api_block_anonymous_users
+#isset ($_user['user_id'])

+ 42 - 49
main/admin/add_courses_to_usergroup.php

@@ -82,28 +82,27 @@ $errorMsg   = '';
 $sessions=array();
 $usergroup = new UserGroup();
 $id = intval($_GET['id']);
-if($_POST['form_sent']) {
-    $form_sent              = $_POST['form_sent'];    
-    $elements_posted        = $_POST['elements_in_name'];     
+if ($_POST['form_sent']) {
+    $form_sent = $_POST['form_sent'];
+    $elements_posted = $_POST['elements_in_name'];
     if (!is_array($elements_posted)) {
         $elements_posted=array();
     }
     if ($form_sent == 1) {
         $usergroup->subscribe_courses_to_usergroup($id, $elements_posted);
         header('Location: usergroups.php');
-        exit;        
+        exit;
     }
 }
-$data               = $usergroup->get($id);
-$course_list_in     = $usergroup->get_courses_by_usergroup($id);
-$course_list        = CourseManager::get_courses_list(0,0,'title');
+$data = $usergroup->get($id);
+$course_list_in = $usergroup->get_courses_by_usergroup($id);
+$course_list = CourseManager::get_courses_list(0, 0, 'title', 'asc', -1, null, api_get_current_access_url_id());
 
-//api_display_tool_title($tool_name.' ('.$session_info['name'].')');
 $elements_not_in = $elements_in= array();
 
 if (!empty($course_list)) {
-    foreach($course_list as $item) {        
-        if (in_array($item['id'], $course_list_in)) {            
+    foreach ($course_list as $item) {
+        if (in_array($item['id'], $course_list_in)) {
             $elements_in[$item['id']] = $item['title']." (".$item['visual_code'].")";
         } else {
             $elements_not_in[$item['id']] = $item['title']." (".$item['visual_code'].")";
@@ -114,8 +113,9 @@ $ajax_search = $add_type == 'unique' ? true : false;
 
 //checking for extra field with filter on
 
-function search($needle,$type) {
-    global $tbl_user,$elements_in;
+function search($needle,$type)
+{
+    global $elements_in;
     $xajax_response = new XajaxResponse();
     $return = '';
     if (!empty($needle) && !empty($type)) {
@@ -135,8 +135,7 @@ function search($needle,$type) {
                 ' LIMIT 11';*/
         } else {
             $list = CourseManager::get_courses_list(0, 0, 2, 'ASC', -1, $needle);
-        }     
-        $i=0;        
+        }
         if ($type=='single') {
             /*
             while ($user = Database :: fetch_array($rs)) {
@@ -151,42 +150,45 @@ function search($needle,$type) {
             $xajax_response -> addAssign('ajax_list_users_single','innerHTML',api_utf8_encode($return));*/
         } else {
             $return .= '<select id="elements_not_in" name="elements_not_in_name[]" multiple="multiple" size="15" style="width:360px;">';
-            
-            foreach ($list as $row ) {         
-                if (!in_array($row['id'], array_keys($elements_in))) {       
+
+            foreach ($list as $row) {
+                if (!in_array($row['id'], array_keys($elements_in))) {
                     $return .= '<option value="'.$row['id'].'">'.$row['title'].' ('.$row['visual_code'].')</option>';
                 }
             }
             $return .= '</select>';
-            $xajax_response -> addAssign('ajax_list_multiple','innerHTML',api_utf8_encode($return));
+            $xajax_response->addAssign('ajax_list_multiple', 'innerHTML', api_utf8_encode($return));
         }
     }
     return $xajax_response;
 }
-$xajax -> processRequests();
+
+$xajax->processRequests();
 
 Display::display_header($tool_name);
 
 if ($add_type == 'multiple') {
-    $link_add_type_unique = '<a href="'.api_get_self().'?id_session='.$id_session.'&add='.Security::remove_XSS($_GET['add']).'&add_type=unique">'.Display::return_icon('single.gif').get_lang('SessionAddTypeUnique').'</a>';
+    $link_add_type_unique = '<a href="'.api_get_self().'?id_session='.$id_session.'&add='.Security::remove_XSS($_GET['add']).'&add_type=unique">'.
+        Display::return_icon('single.gif').get_lang('SessionAddTypeUnique').'</a>';
     $link_add_type_multiple = Display::return_icon('multiple.gif').get_lang('SessionAddTypeMultiple');
 } else {
     $link_add_type_unique = Display::return_icon('single.gif').get_lang('SessionAddTypeUnique');
-    $link_add_type_multiple = '<a href="'.api_get_self().'?id_session='.$id_session.'&add='.Security::remove_XSS($_GET['add']).'&add_type=multiple">'.Display::return_icon('multiple.gif').get_lang('SessionAddTypeMultiple').'</a>';
+    $link_add_type_multiple = '<a href="'.api_get_self().'?id_session='.$id_session.'&add='.Security::remove_XSS($_GET['add']).'&add_type=multiple">'.
+        Display::return_icon('multiple.gif').get_lang('SessionAddTypeMultiple').'</a>';
 }
 
 echo '<div class="actions">';
-echo '<a href="usergroups.php">'.Display::return_icon('back.png',get_lang('Back'), array(), ICON_SIZE_MEDIUM).'</a>';       
+echo '<a href="usergroups.php">'.Display::return_icon('back.png',get_lang('Back'), array(), ICON_SIZE_MEDIUM).'</a>';
 echo '</div>';
 ?>
 
 <form name="formulaire" method="post" action="<?php echo api_get_self(); ?>?id=<?php echo $id; if(!empty($_GET['add'])) echo '&add=true' ; ?>" style="margin:0px;" <?php if($ajax_search){echo ' onsubmit="valide();"';}?>>
 
-<?php echo '<legend>'.$data['name'].': '.$tool_name.'</legend>'; 
+<?php echo '<legend>'.$data['name'].': '.$tool_name.'</legend>';
 
 if ($add_type=='multiple') {
     if (is_array($extra_field_list)) {
-        if (is_array($new_field_list) && count($new_field_list)>0 ) {
+        if (is_array($new_field_list) && count($new_field_list) > 0) {
             echo '<h3>'.get_lang('FilterUsers').'</h3>';
             foreach ($new_field_list as $new_field) {
                 echo $new_field['name'];
@@ -210,10 +212,10 @@ if ($add_type=='multiple') {
         }
     }
 }
-echo Display::input('hidden','id',$id);
-echo Display::input('hidden','form_sent','1');
-echo Display::input('hidden','add_type',null);
-if(!empty($errorMsg)) {
+echo Display::input('hidden', 'id', $id);
+echo Display::input('hidden', 'form_sent', '1');
+echo Display::input('hidden', 'add_type', null);
+if (!empty($errorMsg)) {
     Display::display_normal_message($errorMsg); //main API
 }
 ?>
@@ -230,12 +232,12 @@ if(!empty($errorMsg)) {
 <tr>
 <td align="center">
 <?php echo get_lang('FirstLetterCourseTitle'); ?> :
-     <select name="firstLetterUser" onchange = "xajax_search(this.value,'multiple')" >
-      <option value = "%">--</option>
-      <?php
-        echo Display :: get_alphabet_options();
-      ?>
-     </select>
+    <select name="firstLetterUser" onchange = "xajax_search(this.value,'multiple')" >
+    <option value = "%">--</option>
+    <?php
+    echo Display :: get_alphabet_options();
+    ?>
+    </select>
 </td>
 <td align="center">&nbsp;</td>
 </tr>
@@ -243,16 +245,16 @@ if(!empty($errorMsg)) {
 <tr>
   <td align="center">
   <div id="content_source">
-      <?php           
-      if (!($add_type=='multiple')) {        
+      <?php
+      if (!($add_type=='multiple')) {
         ?>
         <input type="text" id="user_to_add" onkeyup="xajax_search_users(this.value,'single')" />
         <div id="ajax_list_users_single"></div>
         <?php
-      } else {               
+      } else {
       ?>
       <div id="ajax_list_multiple">
-        <?php echo Display::select('elements_not_in_name',$elements_not_in, '',array('style'=>'width:360px', 'multiple'=>'multiple','id'=>'elements_not_in','size'=>'15px'),false); ?> 
+        <?php echo Display::select('elements_not_in_name', $elements_not_in, '', array('style'=>'width:360px', 'multiple'=>'multiple','id'=>'elements_not_in','size'=>'15px'),false); ?>
       </div>
     <?php
       }
@@ -294,8 +296,7 @@ if(!empty($errorMsg)) {
 </form>
 
 <script type="text/javascript">
-<!--
-function moveItem(origin , destination){
+function moveItem(origin , destination) {
 
     for(var i = 0 ; i<origin.options.length ; i++) {
         if(origin.options[i].selected) {
@@ -339,9 +340,7 @@ function valide(){
     document.forms.formulaire.submit();
 }
 
-
-function loadUsersInSelect(select){
-
+function loadUsersInSelect(select) {
     var xhr_object = null;
 
     if(window.XMLHttpRequest) // Firefox
@@ -351,12 +350,9 @@ function loadUsersInSelect(select){
     else  // XMLHttpRequest non supporté par le navigateur
     alert("Votre navigateur ne supporte pas les objets XMLHTTPRequest...");
 
-    //xhr_object.open("GET", "loadUsersInSelect.ajax.php?id_session=<?php echo $id_session ?>&letter="+select.options[select.selectedIndex].text, false);
     xhr_object.open("POST", "loadUsersInSelect.ajax.php");
-
     xhr_object.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
 
-
     nosessionUsers = makepost(document.getElementById('elements_not_in'));
     sessionUsers = makepost(document.getElementById('elements_in'));
     nosessionClasses = makepost(document.getElementById('origin_classes'));
@@ -366,7 +362,6 @@ function loadUsersInSelect(select){
     xhr_object.onreadystatechange = function() {
         if(xhr_object.readyState == 4) {
             document.getElementById('content_source').innerHTML = result = xhr_object.responseText;
-            //alert(xhr_object.responseText);
         }
     }
 }
@@ -376,10 +371,8 @@ function makepost(select){
     var ret = "";
     for (i = 0 ; i<options.length ; i++)
         ret = ret + options[i].value +'::'+options[i].text+";;";
-
     return ret;
 }
--->
 </script>
 <?php
 Display::display_footer();

+ 15 - 12
main/admin/course_add.php

@@ -17,13 +17,13 @@ $this_section = SECTION_PLATFORM_ADMIN;
 api_protect_admin_script();
 
 require_once api_get_path(LIBRARY_PATH).'fileManage.lib.php';
+require_once api_get_path(LIBRARY_PATH).'course_category.lib.php';
 
 $tool_name = get_lang('AddCourse');
 $interbreadcrumb[] = array('url' => 'index.php', 'name' => get_lang('PlatformAdmin'));
 $interbreadcrumb[] = array('url' => 'course_list.php', 'name' => get_lang('CourseList'));
 
 /* MAIN CODE */
-
 global $_configuration;
 
 // Get all possible teachers.
@@ -41,7 +41,7 @@ if ($_configuration['multiple_access_urls']) {
 $res = Database::query($sql);
 $teachers = array();
 //$teachers[0] = '-- '.get_lang('NoManager').' --';
-while($obj = Database::fetch_object($res)) {
+while ($obj = Database::fetch_object($res)) {
     $teachers[$obj->user_id] = api_get_person_name($obj->firstname, $obj->lastname);
 }
 
@@ -61,17 +61,22 @@ $form->applyFilter('visual_code', 'api_strtoupper');
 $form->applyFilter('visual_code', 'html_filter');
 $form->addRule('visual_code', get_lang('Max'), 'maxlength', CourseManager::MAX_COURSE_LENGTH_CODE);
 
-//$form->addElement('select', 'tutor_id', get_lang('CourseTitular'), $teachers, array('style' => 'width:350px', 'class'=>'chzn-select', 'id'=>'tutor_id'));
-//$form->applyFilter('tutor_id', 'html_filter');
-
 $form->addElement('select', 'course_teachers', get_lang('CourseTeachers'), $teachers, ' id="course_teachers" class="chzn-select"  style="width:350px" multiple="multiple" ');
 $form->applyFilter('course_teachers', 'html_filter');
 
-$categories_select = $form->addElement('select', 'category_code', get_lang('CourseFaculty'), $categories, array('style' => 'width:350px', 'class'=>'chzn-select', 'id'=>'category_code'));
-$categories_select->addOption('-','');
-$form->applyFilter('category_code', 'html_filter');
-//This function fills the category_code select ...
-CourseManager::select_and_sort_categories($categories_select);
+// Category code
+$url = api_get_path(WEB_AJAX_PATH).'course.ajax.php?a=search_category';
+
+$form->addElement(
+    'select_ajax',
+    'category_code',
+    get_lang('CourseFaculty'),
+    null,
+    array(
+        'url' => $url
+    //    'formatResult' => 'function(item) { return item.name + "'" +item.code; }'
+    )
+);
 
 // Course department
 $form->add_textfield('department_name', get_lang('CourseDepartment'), false, array ('size' => '60'));
@@ -128,8 +133,6 @@ if (isset($default_course_visibility)) {
 }
 $values['subscribe']        = 1;
 $values['unsubscribe']      = 0;
-reset($teachers);
-//$values['course_teachers'] = key($teachers);
 
 $form->setDefaults($values);
 

+ 11 - 9
main/admin/course_edit.php

@@ -14,6 +14,7 @@ $this_section = SECTION_PLATFORM_ADMIN;
 api_protect_admin_script();
 
 require_once api_get_path(LIBRARY_PATH).'fileManage.lib.php';
+require_once api_get_path(LIBRARY_PATH).'course_category.lib.php';
 
 $course_table       = Database::get_main_table(TABLE_MAIN_COURSE);
 $course_user_table  = Database::get_main_table(TABLE_MAIN_COURSE_USER);
@@ -180,15 +181,16 @@ if (!empty($coursesInSession)) {
     }
 }
 
-$categories_select = $form->addElement(
-    'select',
-    'category_code',
-    get_lang('CourseFaculty'),
-    array(),
-    array('style'=>'width:350px','id'=>'category_code_id', 'class'=>'chzn-select')
-);
-$categories_select->addOption('-', '');
-CourseManager::select_and_sort_categories($categories_select);
+// Category code
+$url = api_get_path(WEB_AJAX_PATH).'course.ajax.php?a=search_category';
+$categoryList = array();
+if (!empty($course['category_code'])) {
+    $data = getCategory($course['category_code']);
+    $categoryList[] = array('id' => $course['category_code'], 'text' => $data['name']);
+}
+
+$form->addElement('select_ajax', 'category_code', get_lang('CourseFaculty'), null, array('url' => $url, 'defaults' => $categoryList));
+
 
 $form->add_textfield('department_name', get_lang('CourseDepartment'), false, array('size' => '60'));
 $form->applyFilter('department_name', 'html_filter');

+ 29 - 9
main/admin/user_import.php

@@ -114,26 +114,45 @@ function complete_missing_data($user) {
  * @return  void
  * @uses global variable $inserted_in_course, which returns the list of courses the user was inserted in
  */
-function save_data($users) {
+function save_data($users)
+{
     global $inserted_in_course;
     // Not all scripts declare the $inserted_in_course array (although they should).
     if (!isset($inserted_in_course)) {
         $inserted_in_course = array();
     }
     require_once api_get_path(LIBRARY_PATH).'mail.lib.inc.php';
-    $send_mail = $_POST['sendMail'] ? 1 : 0;
+    $send_mail = $_POST['sendMail'] ? true : false;
     if (is_array($users)) {
         foreach ($users as $user) {
             $user = complete_missing_data($user);
             $user['Status'] = api_status_key($user['Status']);
-            $user_id = UserManager :: create_user($user['FirstName'], $user['LastName'], $user['Status'], $user['Email'], $user['UserName'], $user['Password'], $user['OfficialCode'], $user['language'], $user['PhoneNumber'], '', $user['AuthSource'], null, 1, 0, null, null, $send_mail);
+            $user_id = UserManager :: create_user(
+                $user['FirstName'],
+                $user['LastName'],
+                $user['Status'],
+                $user['Email'],
+                $user['UserName'],
+                $user['Password'],
+                $user['OfficialCode'],
+                $user['language'],
+                $user['PhoneNumber'],
+                '',
+                $user['AuthSource'],
+                null,
+                1,
+                0,
+                null,
+                null,
+                $send_mail
+            );
             if (!is_array($user['Courses']) && !empty($user['Courses'])) {
                 $user['Courses'] = array($user['Courses']);
             }
             if (is_array($user['Courses'])) {
-                foreach ($user['Courses'] as $index => $course) {
-                    if (CourseManager :: course_exists($course)) {
-                        CourseManager :: subscribe_user($user_id, $course,$user['Status']);
+                foreach ($user['Courses'] as $course) {
+                    if (CourseManager::course_exists($course)) {
+                        CourseManager::subscribe_user($user_id, $course, $user['Status']);
                         $course_info = CourseManager::get_course_information($course);
                         $inserted_in_course[$course] = $course_info['title'];
                     }
@@ -144,13 +163,14 @@ function save_data($users) {
                             if ($vcourse['code'] == $course) {
                                 // Ignore, this has already been inserted.
                             } else {
-                                CourseManager :: subscribe_user($user_id, $vcourse['code'],$user['Status']);
+                                CourseManager :: subscribe_user($user_id, $vcourse['code'], $user['Status']);
                                 $inserted_in_course[$vcourse['code']] = $vcourse['title'];
                             }
                         }
                     }
                 }
             }
+
             if (!empty($user['ClassName'])) {
                 $class_id = ClassManager :: get_class_id($user['ClassName']);
                 ClassManager :: add_user($user_id, $class_id);
@@ -160,11 +180,11 @@ function save_data($users) {
             global $extra_fields;
 
             // We are sure that the extra field exists.
-            foreach($extra_fields as $extras) {
+            foreach ($extra_fields as $extras) {
                 if (isset($user[$extras[1]])) {
                     $key 	= $extras[1];
                     $value 	= $user[$extras[1]];
-                    UserManager::update_extra_field_value($user_id, $key,$value);
+                    UserManager::update_extra_field_value($user_id, $key, $value);
                 }
             }
         }

+ 7 - 2
main/admin/user_list.php

@@ -897,8 +897,13 @@ $table->set_column_filter(7, 'status_filter');
 $table->set_column_filter(8, 'active_filter');
 $table->set_column_filter(10, 'modify_filter');
 
-if (api_is_platform_admin())
-	$table->set_form_actions(array ('delete' => get_lang('DeleteFromPlatform')));
+// Only show empty actions bar if delete users has been blocked
+if (api_is_platform_admin() && !(isset($_configuration['deny_delete_users']) && $_configuration['deny_delete_users'])) {
+        $table->set_form_actions(array ('delete' => get_lang('DeleteFromPlatform')));
+} else {
+        $table->set_form_actions(array ('none' => get_lang('NoActionAvailable')));
+}
+
 
 $table_result = $table->return_table();
 

+ 5 - 5
main/auth/external_login/login.ldap.php

@@ -4,7 +4,7 @@
 /**
  *
  * This file is included in main/inc/local.inc.php at user login if the user have 'external_ldap' in
- * his auth_source field insted of platform
+ * his auth_source field instead of platform
  *
  * Variables that can be used :
  *    - $login : string containing the username posted by the user
@@ -18,14 +18,14 @@
  *
  * If login succeeds, we have 2 choices :
  *    1.  - set $loginFailed to false,
- *        - set $_SESSION['_user']['user_id'] with the dokeos user_id
+ *        - set $_SESSION['_user']['user_id'] with the Chamilo user_id
  *        - set $uidReset to true
- *        - upgrade user info in dokeos database if needeed
+ *        - upgrade user info in chamilo database if needed
  *        - let the script local.inc.php continue
  *
- *    2.  - set $_SESSION['_user']['user_id'] with the dokeos user_id
+ *    2.  - set $_SESSION['_user']['user_id'] with the Chamilo user_id
  *        - set $_SESSION['_user']['uidReset'] to true
- *        - upgrade user info in dokeos database if needeed
+ *        - upgrade user info in chamilo database if needed
  *        - redirect to any page and let local.inc.php do the magic
  *
  * If login fails we have to redirect to index.php with the right message

+ 84 - 0
main/auth/external_login/login.ws.php

@@ -0,0 +1,84 @@
+<?php /* For licensing terms, see /license.txt */
+// External login module : WS (for Web Services)
+/**
+ *
+ * This file is included in main/inc/local.inc.php at user login if the user
+ * have 'ws' in his auth_source field instead of 'platform'
+ *
+ * Variables that can be used :
+ *    - $uData : associative array with those keys :
+ *           -username
+ *           -password
+ *           -auth_source
+ *           -active
+ *           -expiration_date
+ *
+ * If login succeeds, we have 2 choices :
+ *    1.  - set $loginFailed to false,
+ *        - set $_SESSION['_user']['user_id'] with the Chamilo user_id
+ *        - set $uidReset to true
+ *        - upgrade user info in Chamilo database if needed
+ *        - let the script local.inc.php continue
+ *
+ *    2.  - set $_SESSION['_user']['user_id'] with the Chamilo user_id
+ *        - set $_SESSION['_user']['uidReset'] to true
+ *        - upgrade user info in Chamilo database if needed
+ *        - redirect to any page and let local.inc.php do the magic
+ *
+ * If login fails we have to redirect to index.php with the right message
+ * Possible messages are :
+ *  - index.php?loginFailed=1&error=access_url_inactive
+ *  - index.php?loginFailed=1&error=account_expired
+ *  - index.php?loginFailed=1&error=account_inactive
+ *  - index.php?loginFailed=1&error=user_password_incorrect
+ *  - index.php?loginFailed=1&error=unrecognize_sso_origin');
+ *
+ * */
+
+use \ChamiloSession as Session;
+
+// Configure the web service URL here. e.g. http://190.1.1.19:8051/login.asmx?WSDL
+$wsUrl = '';
+
+require_once dirname(__FILE__) . '/functions.inc.php';
+//error_log('Entering login.ws.php');
+$isValid = loginWSAuthenticate($login, $password, $wsUrl);
+if ($isValid !== 0) {
+    //error_log('ws_authenticate worked');
+    $chamiloUser = UserManager::get_user_info($login);
+
+    $loginFailed = false;
+    $_user['user_id'] = $chamiloUser['user_id'];
+    $_user['status'] = (isset($chamiloUser['status']) ? $chamiloUser['status'] : 5);
+    $_user['uidReset'] = true;
+    Session::write('_user', $_user);
+    $uidReset = true;
+    $logging_in = true;
+    event_login();
+    //error_log('Calling event_login');
+} else {
+    //error_log('ws_authenticate error');
+    $loginFailed = true;
+    $uidReset = false;
+    if (isset($_user) && isset($_user['user_id'])) {
+        unset($_user['user_id']);
+    }
+}
+
+/**
+ * Checks whether a user has the right to enter on the platform or not
+ * @param $username
+ * @param $password
+ */
+function loginWSAuthenticate($username, $password, $wsUrl) {
+    if (empty($username) or empty($password) or empty($wsUrl)) {
+        return false;
+    }
+    $client = new SoapClient($wsUrl);
+    if (!$client) {
+        return false;
+    }
+    $something =  $client->validaUsuarioAD(array($username, $password, 'chamilo'));
+    error_log(print_r($something,1));
+    return $something->validaUsuarioADResult;
+}

+ 24 - 0
main/coursecopy/classes/CourseRestorer.class.php

@@ -2026,10 +2026,12 @@ class CourseRestorer
 
                 // check resources inside html from fckeditor tool and copy correct urls into recipient course
                 $obj->params['description'] = DocumentManager::replace_urls_inside_content_html_from_copy_course($obj->params['description'], $this->course->code, $this->course->destination_path, $this->course->backup_path, $this->course->info['path']);
+                $id_work = $obj->params['id'];
                 $obj->params['id'] = null;
                 $obj->params['c_id'] = $this->destination_course_id;
 
                 $last_id = Database::insert($table_work, $obj->params);
+                
                 // re-create dir
                 // @todo check security against injection of dir in crafted course backup here!
                 $path = $obj->params['url'];
@@ -2041,6 +2043,28 @@ class CourseRestorer
                 }
 
                 if (is_numeric($last_id)) {
+                    $sql = 'SELECT *
+                            FROM '.$table_work_assignment.'
+                            WHERE  c_id = '.$this->course_origin_id.' AND
+                                   publication_id = '.$id_work;
+
+                    $result = Database::query($sql);
+                    $cant = Database::num_rows($result);
+                    if ($cant > 0) {                    
+                        $row = Database::fetch_assoc($result);
+                        $expires_date = $row['expires_on'];
+                        $end_date = $row['ends_on'];
+                        $add_to_calendar = $row['add_to_calendar'];
+                        $enable_calification = $row['enable_calification'];
+                        $sql_add_homework = "INSERT INTO $table_work_assignment SET
+                                                    c_id                 = $this->destination_course_id ,
+                                                    expires_on       		= '$expires_date',
+                                                    ends_on        	 	 = '$end_date',
+                                                    add_to_calendar  		= '$add_to_calendar',
+                                                    enable_qualification = '$enable_calification',
+                                                    publication_id 			= '$last_id'";
+                        Database::query($sql_add_homework);
+                    }
                     api_item_property_update($this->destination_course_info, 'work', $last_id,"DirectoryCreated", api_get_user_id());
                 }
             }

+ 101 - 0
main/exercice/aiken.php

@@ -0,0 +1,101 @@
+<?php
+/* For licensing terms, see /license.txt */
+/**
+*	Code for Aiken import integration.
+*	@package chamilo.exercise
+* 	@author Ronny Velasquez
+*	@author César Perales <cesar.perales@gmail.com> Updated function names and import files for Aiken format support
+* 	@version $Id: Aiken.php  2010-03-12 12:14:25Z $
+*/
+/**
+ * Code
+ */
+// name of the language file that needs to be included
+$language_file = 'exercice';
+
+// including the global Chamilo file
+require_once '../inc/global.inc.php';
+require_once api_get_path(LIBRARY_PATH).'document.lib.php';
+
+// including additional libraries
+require_once 'exercise.class.php';
+require_once 'question.class.php';
+require_once 'answer.class.php';
+require_once 'unique_answer.class.php';
+
+
+// section (for the tabs)
+$this_section = SECTION_COURSES;
+
+// access restriction: only teachers are allowed here
+if (!api_is_allowed_to_edit(null, true)) {
+	api_not_allowed();
+}
+
+// the breadcrumbs
+$interbreadcrumb[]= array ("url"=>"exercice.php", "name"=> get_lang('Exercices'));
+$is_allowedToEdit = api_is_allowed_to_edit(null, true);
+
+/**
+ * This function displays the form for import of the zip file with qti2
+ */
+function aiken_display_form() {
+	$name_tools = get_lang('ImportAikenQuiz');
+	$form  = '<div class="actions">';
+	$form .= '<a href="exercice.php?show=test">' . Display :: return_icon('back.png', get_lang('BackToExercisesList'),'',ICON_SIZE_MEDIUM).'</a>';
+	$form .= '</div>';
+    $form_validator  = new FormValidator('aiken_upload', 'post',api_get_self()."?".api_get_cidreq(), null, array('enctype' => 'multipart/form-data') );
+    $form_validator->addElement('header', $name_tools);    
+    $form_validator->addElement('file', 'userFile', get_lang('DownloadFile'));    
+    $form_validator->addElement('style_submit_button', 'submit', get_lang('Send'), 'class="upload"');    
+    $form .= $form_validator->return_form();	
+
+	echo $form;
+}
+
+/**
+ * This function will import the zip file with the respective qti2
+ * @param array $uploaded_file ($_FILES)
+ */
+function aiken_import_file($array_file) {
+
+	$unzip = 0;
+	$lib_path = api_get_path(LIBRARY_PATH);
+	require_once $lib_path.'fileUpload.lib.php';
+	require_once $lib_path.'fileManage.lib.php';
+	$process = process_uploaded_file($array_file);
+	if (preg_match('/\.zip$/i', $array_file['name'])) {
+		// if it's a zip, allow zip upload
+		$unzip = 1;
+	}
+
+	if ($process && $unzip == 1) {
+		$main_path = api_get_path(SYS_CODE_PATH);
+		require_once $main_path.'exercice/export/aiken/aiken_import.inc.php';
+        require_once $main_path.'exercice/export/aiken/aiken_classes.php';
+        $imported = import_exercise($array_file['name']);
+
+        if ($imported) {
+        	header('Location: exercice.php?'.api_get_cidreq());
+        } else {
+            Display::display_error_message(get_lang('UplNoFileUploaded'));
+            return false;
+        }
+	}
+}
+
+// display header
+Display::display_header(get_lang('ImportAikenQuiz'), 'Exercises');
+
+// import file
+if ((api_is_allowed_to_edit(null, true))) {
+	if (isset($_POST['submit'])) {
+		aiken_import_file($_FILES['userFile']);
+	}
+}
+
+// display Aiken form
+aiken_display_form();
+
+// display the footer
+Display::display_footer();

+ 2 - 1
main/exercice/exercice.php

@@ -381,6 +381,7 @@ if ($is_allowedToEdit && $origin != 'learnpath') {
     echo '<a href="hotpotatoes.php?'.api_get_cidreq().'">'.Display :: return_icon('import_hotpotatoes.png', get_lang('ImportHotPotatoesQuiz'), '', ICON_SIZE_MEDIUM).'</a>';
     // link to import qti2 ...
     echo '<a href="qti2.php?'.api_get_cidreq().'">'.Display :: return_icon('import_qti2.png', get_lang('ImportQtiQuiz'), '', ICON_SIZE_MEDIUM).'</a>';
+    echo '<a href="aiken.php?'.api_get_cidreq().'">'.Display :: return_icon('import_aiken.png', get_lang('ImportAikenQuiz'), '', ICON_SIZE_MEDIUM).'</a>';
     echo '<a href="upload_exercise.php?'.api_get_cidreq().'">'.Display :: return_icon('import_excel.png', get_lang('ImportExcelQuiz'), '', ICON_SIZE_MEDIUM).'</a>';
 }
 
@@ -883,4 +884,4 @@ if (empty($exercise_list) && $hotpotatoes_exist == false) {
 }
 if ($origin != 'learnpath') { //so we are not in learnpath tool
     Display :: display_footer();
-}
+}

+ 142 - 0
main/exercice/export/aiken/aiken_classes.php

@@ -0,0 +1,142 @@
+<?php // $Id: $
+/* For licensing terms, see /license.txt */
+/**
+ * @author Claro Team <cvs@claroline.net>
+ * @author Yannick Warnier <yannick.warnier@beeznest.com> - updated ImsAnswerHotspot to match QTI norms
+ * @author César Perales <cesar.perales@gmail.com> Updated function names and import files for Aiken format support
+ * @package chamilo.exercise
+ */
+/**
+ * Code
+ */
+if ( count( get_included_files() ) == 1 ) die( '---' );
+
+if (!function_exists('mime_content_type')) {
+	require_once api_get_path(LIBRARY_PATH).'document.lib.php';
+	function mime_content_type($filename) {
+		return DocumentManager::file_get_mime_type((string)$filename);
+	}
+}
+
+require_once(api_get_path(SYS_CODE_PATH).'/exercice/answer.class.php');
+require_once(api_get_path(SYS_CODE_PATH).'/exercice/exercise.class.php');
+require_once(api_get_path(SYS_CODE_PATH).'/exercice/question.class.php');
+//require_once(api_get_path(SYS_CODE_PATH).'/exercice/hotspot.class.php');
+require_once(api_get_path(SYS_CODE_PATH).'/exercice/unique_answer.class.php');
+//require_once(api_get_path(SYS_CODE_PATH).'/exercice/multiple_answer.class.php');
+//require_once(api_get_path(SYS_CODE_PATH).'/exercice/multiple_answer_combination.class.php');
+//require_once(api_get_path(SYS_CODE_PATH).'/exercice/matching.class.php');
+//require_once(api_get_path(SYS_CODE_PATH).'/exercice/freeanswer.class.php');
+//require_once(api_get_path(SYS_CODE_PATH).'/exercice/fill_blanks.class.php');
+//include_once $path . '/../../lib/answer_multiplechoice.class.php';
+//include_once $path . '/../../lib/answer_truefalse.class.php';
+//include_once $path . '/../../lib/answer_fib.class.php';
+//include_once $path . '/../../lib/answer_matching.class.php';
+/**
+ *
+ * @package chamilo.exercise
+ */
+class Aiken2Question extends Question
+{
+    /**
+     * Include the correct answer class and create answer
+     */
+    function setAnswer()
+    {
+        switch($this->type)
+        {
+            case MCUA :
+                $answer = new AikenAnswerMultipleChoice($this->id);
+            	return $answer;
+            default :
+                $answer = null;
+                break;
+        }
+        return $answer;
+    }
+    function createAnswersForm($form)
+    {
+    	return true;
+    }
+    function processAnswersCreation($form)
+    {
+    	return true;
+    }
+}
+
+
+/**
+ * Class
+ * @package chamilo.exercise
+ */
+class AikenAnswerMultipleChoice extends Answer
+{
+    /**
+     * Return the XML flow for the possible answers.
+     *
+     */
+    function imsExportResponses($questionIdent, $questionStatment)
+    {
+        $this->answerList = $this->getAnswersList(true);
+        $out  = '    <choiceInteraction responseIdentifier="' . $questionIdent . '" >' . "\n";
+        $out .= '      <prompt> ' . $questionStatment . ' </prompt>'. "\n";
+        if (is_array($this->answerList)) {
+            foreach ($this->answerList as $current_answer) {
+                
+                
+                $out .= '      <simpleChoice identifier="answer_' . $current_answer['id'] . '" fixed="false">' . $current_answer['answer'];
+                if (isset($current_answer['comment']) && $current_answer['comment'] != '')
+                {
+                    $out .= '<feedbackInline identifier="answer_' . $current_answer['id'] . '">' . $current_answer['comment'] . '</feedbackInline>';
+                }
+                $out .= '</simpleChoice>'. "\n";
+            }
+        }
+        $out .= '    </choiceInteraction>'. "\n";
+        return $out;
+    }
+
+    /**
+     * Return the XML flow of answer ResponsesDeclaration
+     *
+     */
+    function imsExportResponsesDeclaration($questionIdent)
+    {
+        $this->answerList = $this->getAnswersList(true);
+        $type = $this->getQuestionType();
+        if ($type == MCMA)  $cardinality = 'multiple'; else $cardinality = 'single';
+
+        $out = '  <responseDeclaration identifier="' . $questionIdent . '" cardinality="' . $cardinality . '" baseType="identifier">' . "\n";
+
+        //Match the correct answers
+
+        $out .= '    <correctResponse>'. "\n";
+        if (is_array($this->answerList)) {
+            foreach($this->answerList as $current_answer) {
+                if ($current_answer['correct'])
+                {
+                    $out .= '      <value>answer_'. $current_answer['id'] .'</value>'. "\n";
+                }
+            }
+        }
+        $out .= '    </correctResponse>'. "\n";
+
+        //Add the grading
+
+        $out .= '    <mapping>'. "\n";
+        if (is_array($this->answerList)) {
+            foreach($this->answerList as $current_answer)
+            {
+                if (isset($current_answer['grade']))
+                {
+                    $out .= '      <mapEntry mapKey="answer_'. $current_answer['id'] .'" mappedValue="'.$current_answer['grade'].'" />'. "\n";
+                }
+            }
+        }
+        $out .= '    </mapping>'. "\n";
+
+        $out .= '  </responseDeclaration>'. "\n";
+
+        return $out;
+    }
+}

+ 224 - 0
main/exercice/export/aiken/aiken_import.inc.php

@@ -0,0 +1,224 @@
+<?php
+/**
+ * @copyright (c) 2001-2006 Universite catholique de Louvain (UCL)
+ *
+ * @license http://www.gnu.org/copyleft/gpl.html (GPL) GENERAL PUBLIC LICENSE
+ *
+ * @package chamilo.exercise
+ *
+ * @author claro team <cvs@claroline.net>
+ * @author Guillaume Lederer <guillaume@claroline.net>
+ * @author César Perales <cesar.perales@gmail.com> Parse function for Aiken format
+ */
+/**
+ * Security check
+ */
+if (count(get_included_files()) == 1)
+	die('---');
+
+/**
+ * function to create a temporary directory (SAME AS IN MODULE ADMIN)
+ */
+
+function tempdir($dir, $prefix = 'tmp', $mode = 0777) {
+	if (substr($dir, -1) != '/')
+		$dir .= '/';
+
+	do {
+		$path = $dir . $prefix . mt_rand(0, 9999999);
+	} while (!mkdir($path, $mode));
+
+	return $path;
+}
+
+/**
+ * @return the path of the temporary directory where the exercise was uploaded and unzipped
+ */
+
+function get_and_unzip_uploaded_exercise($baseWorkDir, $uploadPath) {
+	global $_course, $_user;
+	//Check if the file is valid (not to big and exists)
+	if (!isset ($_FILES['userFile']) || !is_uploaded_file($_FILES['userFile']['tmp_name'])) {
+		// upload failed
+		return false;
+	}	
+	if (preg_match('/.zip$/i', $_FILES['userFile']['name']) && handle_uploaded_document($_course, $_FILES['userFile'], $baseWorkDir, $uploadPath, $_user['user_id'], 0, null, 1)) {
+		if (!function_exists('gzopen')) {			
+			//claro_delete_file($uploadPath);
+			return false;
+		}
+		// upload successfull
+		return true;
+	} else {
+		//claro_delete_file($uploadPath);
+		return false;
+	}
+}
+/**
+ * main function to import an exercise,
+ *
+ * @return an array as a backlog of what was really imported, and error or debug messages to display
+ */
+
+function import_exercise($file) {
+	global $exercise_info;
+	global $element_pile;
+	global $non_HTML_tag_to_avoid;
+	global $record_item_body;
+	// used to specify the question directory where files could be found in relation in any question
+	global $questionTempDir;
+
+	$archive_path = api_get_path(SYS_ARCHIVE_PATH) . 'aiken';
+	$baseWorkDir = $archive_path;
+
+	if (!is_dir($baseWorkDir)) {
+		mkdir($baseWorkDir, api_get_permissions_for_new_directories(), true);
+	}
+
+	$uploadPath = '/';
+
+	// set some default values for the new exercise
+	$exercise_info = array ();
+	$exercise_info['name'] = preg_replace('/.zip$/i', '', $file);
+	$exercise_info['question'] = array();
+	$element_pile = array ();
+
+	// create parser and array to retrieve info from manifest
+	$element_pile = array (); //pile to known the depth in which we are
+	//$module_info = array (); //array to store the info we need
+
+	// if file is not a .zip, then we cancel all
+	if (!preg_match('/.zip$/i', $file)) {
+		Display :: display_error_message(get_lang('You must upload a zip file'));
+		return false;
+	}
+
+	// unzip the uploaded file in a tmp directory
+	if (!get_and_unzip_uploaded_exercise($baseWorkDir, $uploadPath)) {
+		Display :: display_error_message(get_lang('You must upload a zip file'));
+		return false;
+	}
+
+	// find the different manifests for each question and parse them.
+	$exerciseHandle = opendir($baseWorkDir);
+	//$question_number = 0;
+	$file_found = false;
+	$operation = false;
+    $result = false;
+	// parse every subdirectory to search txt question files
+	while (false !== ($file = readdir($exerciseHandle))) {
+		if (is_dir($baseWorkDir . '/' . $file) && $file != "." && $file != "..") {
+			//find each manifest for each question repository found
+			$questionHandle = opendir($baseWorkDir . '/' . $file);
+			while (false !== ($questionFile = readdir($questionHandle))) {
+				if (preg_match('/.txt$/i', $questionFile)) {
+					$result = parse_file($baseWorkDir, $file, $questionFile);
+					$file_found = true;
+				}
+			}
+		} elseif (preg_match('/.txt$/i', $file)) {
+			$result = parse_file($baseWorkDir, '', $file);
+			$file_found = true;
+		} // else ignore file
+	}
+	if (!$file_found) {
+		Display :: display_error_message(get_lang('No TXT file found in the zip'));        
+		return false;
+	}
+    if ($result == false ) {        
+        return false;
+    }
+
+	//add exercise in tool
+
+	//1.create exercise
+	$exercise = new Exercise();
+	$exercise->exercise = $exercise_info['name'];
+    
+	$exercise->save();
+	$last_exercise_id = $exercise->selectId();
+	if (!empty($last_exercise_id)) {
+		//For each question found...
+		foreach ($exercise_info['question'] as $key => $question_array) {
+			//2.create question
+			$question = new Aiken2Question();
+			$question->type = $question_array['type'];
+			$question->setAnswer();
+			$question->updateTitle($question_array['title']); // question ...
+			$type = $question->selectType();
+			$question->type = constant($type); // type ...
+			$question->save($last_exercise_id); // save computed grade
+			$last_question_id = $question->selectId();
+			//3.create answer
+			$answer = new Answer($last_question_id);
+			$answer->new_nbrAnswers = count($question_array['answer']);
+			foreach ($question_array['answer'] as $key => $answers) {
+				$key++; 				
+				$answer->new_answer[$key] = $answers['value']; // answer ...
+				$answer->new_comment[$key] = $answers['feedback']; // comment ...
+				$answer->new_position[$key] = $key; // position ...
+				// correct answers ...
+				if (in_array($key, $question_array['correct_answers'])) {
+					$answer->new_correct[$key] = 1;
+				} else {
+					$answer->new_correct[$key] = 0;
+				}
+				error_log($question_array['weighting']);
+				$answer->new_weighting[$key] = $question_array['weighting'][$key - 1];
+			}
+			$answer->save();
+		}
+		// delete the temp dir where the exercise was unzipped
+		my_delete($baseWorkDir . $uploadPath);
+		$operation = true;
+	}
+	return $operation;
+}
+
+function parse_file($exercisePath, $file, $questionFile) {
+	global $exercise_info;
+	global $questionTempDir;
+
+	$questionTempDir = $exercisePath . '/' . $file . '/';
+	$questionFilePath = $questionTempDir . $questionFile;
+
+
+	$data = file($questionFilePath);
+
+	$question_index = 0;
+	$correct_answer = '';
+	$answers_array = array();
+	foreach ($data as $linea => $info) {
+		$exercise_info['question'][$question_index]['type'] = 'MCUA';
+		if (preg_match('/^([A-Z])(\)|\.)\s(.*)/', $info, $matches)) {
+			//adding one of the posible answers
+			$exercise_info['question'][$question_index]['answer'][]['value'] = $matches[3];
+			$answers_array[] = $matches[1];
+		} elseif (preg_match('/^ANSWER:\s?([A-Z])\s?/', $info, $matches)) {
+			//the correct answers
+			$correct_answer_index = array_search($matches[1], $answers_array);
+			$exercise_info['question'][$question_index]['correct_answers'][] = $correct_answer_index + 1;
+			//weight for correct answer
+			$exercise_info['question'][$question_index]['weighting'][$correct_answer_index] = 1;
+		} elseif (preg_match('/^ANSWER_EXPLANATION:\s?(.*)\s?/', $info, $matches)) {  
+			//Comment of correct answer
+			$exercise_info['question'][$question_index]['answer'][$correct_answer_index]['feedback'] = $matches[1];
+		} elseif (preg_match('/^TAGS:\s?([A-Z])\s?/', $info, $matches)) { 
+		 	//TAGS for chamilo >= 1.10
+			$exercise_info['question'][$question_index]['answer_tags'] = explode(',', $matches[1]);
+		} elseif (preg_match('/^\n/',$info)) {
+			//moving to next question
+			$question_index++;
+			//emptying answers array when moving to next question
+			$answers_array = array();
+		} else {
+			//Question itself
+			$exercise_info['question'][$question_index]['title'] = $info;
+		}
+	}
+	$total_questions = count($exercise_info['question']);
+	foreach  ($exercise_info['question'] as $key => $question) {
+		$exercise_info['question'][$key]['weighting'][current(array_keys($exercise_info['question'][$key]['weighting']))] = 20 / $total_questions;
+	}
+	return true;
+}

+ 3 - 1
main/gradebook/lib/gradebook_functions.inc.php

@@ -361,8 +361,10 @@ function is_resource_in_course_gradebook($course_code, $resource_type, $resource
 {
     $l = Database::get_main_table(TABLE_MAIN_GRADEBOOK_LINK);
     $course_code = Database::escape_string($course_code);
-    $sql = "SELECT * FROM $l l WHERE course_code = '$course_code' AND type = " . (int) $resource_type . " and ref_id = " . (int) $resource_id;
+    $sql = "SELECT * FROM $l l
+            WHERE course_code = '$course_code' AND type = ".(int)$resource_type . " AND ref_id = " . (int)$resource_id;
     $res = Database::query($sql);
+
     if (Database::num_rows($res) < 1) {
         return false;
     }

+ 6 - 3
main/group/group.php

@@ -19,6 +19,9 @@
 $language_file = 'group';
 
 require_once '../inc/global.inc.php';
+
+$is_allowed_in_course = api_is_allowed_to_edit(false, true);
+
 $this_section = SECTION_COURSES;
 $current_course_tool  = TOOL_GROUP;
 
@@ -27,12 +30,12 @@ api_protect_course_script(true);
 
 $htmlHeadXtra[] = '<script>
 $(document).ready( function() {
-	for (i=0;i<$(".actions").length;i++) {
+	for (i=0; i<$(".actions").length; i++) {
 		if ($(".actions:eq("+i+")").html()=="<table border=\"0\"></table>" || $(".actions:eq("+i+")").html()=="" || $(".actions:eq("+i+")").html()==null) {
 			$(".actions:eq("+i+")").hide();
 		}
 	}
- } );
+});
  </script>';
 $nameTools = get_lang('GroupManagement');
 $course_id = api_get_course_int_id();
@@ -45,7 +48,7 @@ if (api_get_setting('allow_group_categories') == 'false') {
     $num = Database::num_rows($res);
     if ($num == 0) {
         $sql = "INSERT INTO ".$cat_table." ( c_id, id , title , description , forum_state, wiki_state, max_student, self_reg_allowed, self_unreg_allowed, groups_per_user, display_order)
-        VALUES ($course_id, '2', '".lang2db(get_lang('DefaultGroupCategory'))."', '', '1', '1', '8', '0', '0', '0', '0');";
+        VALUES ($course_id, '2', '".Database::escape_string(get_lang('DefaultGroupCategory'))."', '', '1', '1', '8', '0', '0', '0', '0');";
         Database::query($sql);
     }
 }

+ 2 - 22
main/group/group_creation.php

@@ -92,7 +92,6 @@ elseif (isset($_POST['number_of_groups'])) {
         if ($number_of_groups > 1) {
     ?>
     <script type="text/javascript">
-    <!--
     var number_of_groups = <?php echo $number_of_groups; ?>;
     function switch_state(key) {
         for( i=1; i<number_of_groups; i++) {
@@ -124,7 +123,6 @@ elseif (isset($_POST['number_of_groups'])) {
             element.value = ref.value;
         }
     }
-    -->
     </script>
     <?php
 		}
@@ -136,7 +134,7 @@ elseif (isset($_POST['number_of_groups'])) {
 				$cat_options[$category['id']] = $category['title'];
 			}
 		}
-		$form = new FormValidator('create_groups_step2');
+        $form = new FormValidator('create_groups_step2', 'POST', api_get_self().'?'.api_get_cidreq());
 
 		// Modify the default templates
 		$renderer = $form->defaultRenderer();
@@ -216,7 +214,7 @@ EOT;
 	$categories = GroupManager :: get_categories();
 	//echo '<blockquote>';
 	if (count($categories) > 1 || isset ($categories[0]) && $categories[0]['id'] != GroupManager::VIRTUAL_COURSE_CATEGORY) {
-		$create_groups_form = new FormValidator('create_groups');
+        $create_groups_form = new FormValidator('create_groups', 'post', api_get_self().'?'.api_get_cidreq());
 		$create_groups_form->addElement('header', '', $nameTools);
 		$group_el = array ();
 		$group_el[] = $create_groups_form->createElement('static', null, null, get_lang('Create'));
@@ -231,24 +229,6 @@ EOT;
 	} else {
 		echo get_lang('NoCategoriesDefined');
 	}
-	//echo '</blockquote>';
-
-	/*
-	 * Show form to generate groups from virtual courses
-	 */
-	$virtual_courses = CourseManager :: get_virtual_courses_linked_to_real_course($_course['sysCode']);
-	if (count($virtual_courses) > 0) {
-		echo '<b>'.get_lang('CreateGroupsFromVirtualCourses').'</b>';
-		echo '<blockquote>';
-		echo get_lang('CreateGroupsFromVirtualCoursesInfo');
-		$create_virtual_groups_form = new FormValidator('create_virtual_groups');
-		$create_virtual_groups_form->addElement('hidden', 'action');
-		$create_virtual_groups_form->addElement('submit', 'submit', get_lang('Ok'));
-		$create_virtual_groups_form->setDefaults(array('action' => 'create_virtual_groups'));
-		$create_virtual_groups_form->display();
-		echo '</blockquote>';
-	}
-
 	/*
 	 * Show form to generate subgroups
 	 */

+ 34 - 19
main/inc/ajax/course.ajax.php

@@ -8,36 +8,51 @@ $language_file[] = 'admin';
 require_once '../global.inc.php';
 
 $action = $_REQUEST['a'];
-
 $user_id = api_get_user_id();
 
-switch ($action) {    
+switch ($action) {
     case 'add_course_vote':
-		
-		$course_id = intval($_REQUEST['course_id']);
+
+        $course_id = intval($_REQUEST['course_id']);
         $star      = intval($_REQUEST['star']);
-		
-        if (!api_is_anonymous()) {     	    
-            CourseManager::add_course_vote($user_id, $star, $course_id, 0);                
-        }		
-		$point_info = CourseManager::get_course_ranking($course_id, 0); 		
-		$ajax_url = api_get_path(WEB_AJAX_PATH).'course.ajax.php?a=add_course_vote';		
-	    $rating = Display::return_rating_system('star_'.$course_id, $ajax_url.'&amp;course_id='.$course_id, $point_info, false);
-		echo $rating;
-		
+
+        if (!api_is_anonymous()) {
+            CourseManager::add_course_vote($user_id, $star, $course_id, 0);
+        }
+        $point_info = CourseManager::get_course_ranking($course_id, 0);
+        $ajax_url = api_get_path(WEB_AJAX_PATH).'course.ajax.php?a=add_course_vote';
+        $rating = Display::return_rating_system('star_'.$course_id, $ajax_url.'&amp;course_id='.$course_id, $point_info, false);
+        echo $rating;
+
         break;
-    case  'get_user_courses':
+    case 'get_user_courses':
         if (api_is_platform_admin()) {
-            $user_id = intval($_POST['user_id']);            
+            $user_id = intval($_POST['user_id']);
             $list_course_all_info = CourseManager::get_courses_list_by_user_id($user_id, false);
             if (!empty($list_course_all_info)) {
-                foreach($list_course_all_info as $course_item) {                
-                    $course_info = api_get_course_info($course_item['code']);                    
+                foreach ($list_course_all_info as $course_item) {
+                    $course_info = api_get_course_info($course_item['code']);
                     echo $course_info['title'].'<br />';
                 }
-            } else {            
+            } else {
                 echo get_lang('UserHasNoCourse');
-            } 
+            }
+        }
+        break;
+    case 'search_category':
+        require_once api_get_path(LIBRARY_PATH).'course_category.lib.php';
+        if (api_is_platform_admin()) {
+            $results = searchCategoryByKeyword($_REQUEST['q']);
+
+            if (!empty($results)) {
+                foreach ($results as &$item) {
+                    $item['id'] = $item['code'];
+                }
+                echo json_encode($results);
+            } else {
+                echo json_encode(array());
+            }
+
         }
         break;
     default:

+ 84 - 39
main/inc/lib/course.lib.php

@@ -130,10 +130,25 @@ class CourseManager
      * @param    string    The direction of the order (ASC or DESC). Optional, defaults to ASC.
      * @param    string    The visibility of the course, or all by default.
      * @param    string    If defined, only return results for which the course *title* begins with this string
+     * @return array
      */
-    public static function get_courses_list($from = 0, $howmany = 0, $orderby = 1, $orderdirection = 'ASC', $visibility = -1, $startwith = '') {
+    public static function get_courses_list(
+        $from = 0,
+        $howmany = 0,
+        $orderby = 1,
+        $orderdirection = 'ASC',
+        $visibility = -1,
+        $startwith = '',
+        $urlId = null
+    ) {
+
+        $sql = "SELECT course.* FROM ".Database::get_main_table(TABLE_MAIN_COURSE)." course ";
+
+        if (!empty($urlId)) {
+            $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
+            $sql .= " INNER JOIN $table url ON (url.course_code = course.code) ";
+        }
 
-        $sql = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_COURSE)." ";
         if (!empty($startwith)) {
             $sql .= "WHERE title LIKE '".Database::escape_string($startwith)."%' ";
             if ($visibility !== -1 && $visibility == strval(intval($visibility))) {
@@ -145,6 +160,12 @@ class CourseManager
                 $sql .= " AND visibility = $visibility ";
             }
         }
+
+        if (!empty($urlId)) {
+            $urlId = intval($urlId);
+            $sql .= " AND access_url_id= $urlId";
+        }
+
         if (!empty($orderby)) {
             $sql .= " ORDER BY ".Database::escape_string($orderby)." ";
         } else {
@@ -222,7 +243,7 @@ class CourseManager
     /**
      * Unsubscribe one or more users from a course
      *
-     * @param   int   user_id or an array with user ids
+     * @param   mixed   user_id or an array with user ids
      * @param   string  course code
      * @param   int     session id
      * @assert ('', '') === false
@@ -230,14 +251,13 @@ class CourseManager
      */
     public static function unsubscribe_user($user_id, $course_code, $session_id = 0)
     {
-
         if (!is_array($user_id)) {
             $user_id = array($user_id);
         }
+
         if (count($user_id) == 0) {
             return;
         }
-        $table_user = Database::get_main_table(TABLE_MAIN_USER);
 
         if (!empty($session_id)) {
             $session_id = intval($session_id);
@@ -261,60 +281,76 @@ class CourseManager
             $user_list[] = $user_id;
         }
 
-
         $course_info = api_get_course_info($course_code);
         $course_id = $course_info['real_id'];
 
         // Unsubscribe user from all groups in the course.
-        Database::query("DELETE FROM ".Database::get_course_table(TABLE_GROUP_USER)."  WHERE c_id = $course_id AND user_id IN (".$user_ids.")");
-        Database::query("DELETE FROM ".Database::get_course_table(TABLE_GROUP_TUTOR)." WHERE c_id = $course_id AND user_id IN (".$user_ids.")");
+        $sql = "DELETE FROM ".Database::get_course_table(TABLE_GROUP_USER)."  WHERE c_id = $course_id AND user_id IN (".$user_ids.")";
+        Database::query($sql);
+        $sql = "DELETE FROM ".Database::get_course_table(TABLE_GROUP_TUTOR)." WHERE c_id = $course_id AND user_id IN (".$user_ids.")";
+        Database::query($sql);
 
         // Erase user student publications (works) in the course - by André Boivin
-        //@todo field student_publication.author should be the user id
-
-        $sqlu = "SELECT * FROM $table_user WHERE user_id IN (".$user_ids.")";
-        $resu = Database::query($sqlu);
-        $username = Database::fetch_array($resu,'ASSOC');
-        $userfirstname = $username['firstname'];
-        $userlastname = $username['lastname'];
-        $publication_name = $userfirstname.' '.$userlastname ;
 
-        $table_course_user_publication     = Database :: get_course_table(TABLE_STUDENT_PUBLICATION);
-        $sql = "DELETE FROM $table_course_user_publication WHERE c_id = $course_id AND author = '".Database::escape_string($publication_name)."'";
-        Database::query($sql);
+        if (!empty($user_list)) {
+            require_once api_get_path(SYS_CODE_PATH).'work/work.lib.php';
+            foreach ($user_list as $userId) {
+                // Getting all work from user
+                $workList = getWorkPerUser($userId);
+                if (!empty($workList)) {
+                    foreach ($workList as $work) {
+                        $work = $work['work'];
+                        // Getting user results
+                        if (!empty($work->user_results)) {
+                            foreach ($work->user_results as $workSent) {
+                                deleteWorkItem($workSent['id'], $course_info);
+                            }
+                        }
+                    }
+                }
+            }
+        }
 
         // Unsubscribe user from all blogs in the course.
         Database::query("DELETE FROM ".Database::get_course_table(TABLE_BLOGS_REL_USER)." WHERE c_id = $course_id AND  user_id IN (".$user_ids.")");
         Database::query("DELETE FROM ".Database::get_course_table(TABLE_BLOGS_TASKS_REL_USER)." WHERE c_id = $course_id AND  user_id IN (".$user_ids.")");
 
-        //Deleting users in forum_notification and mailqueue course tables
-        $sql_delete_forum_notification = "DELETE FROM  ".Database::get_course_table(TABLE_FORUM_NOTIFICATION)." WHERE c_id = $course_id AND user_id IN (".$user_ids.")";
-        Database::query($sql_delete_forum_notification);
+        // Deleting users in forum_notification and mailqueue course tables
+        $sql = "DELETE FROM  ".Database::get_course_table(TABLE_FORUM_NOTIFICATION)."
+                WHERE c_id = $course_id AND user_id IN (".$user_ids.")";
+        Database::query($sql);
 
-        $sql_delete_mail_queue = "DELETE FROM ".Database::get_course_table(TABLE_FORUM_MAIL_QUEUE)." WHERE c_id = $course_id AND user_id IN (".$user_ids.")";
-        Database::query($sql_delete_mail_queue);
+        $sql = "DELETE FROM ".Database::get_course_table(TABLE_FORUM_MAIL_QUEUE)."
+                WHERE c_id = $course_id AND user_id IN (".$user_ids.")";
+        Database::query($sql);
 
         // Unsubscribe user from the course.
         if (!empty($session_id)) {
+
             // Delete in table session_rel_course_rel_user
             $sql = "DELETE FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)."
-                    WHERE id_session ='".$session_id."' AND course_code = '".Database::escape_string($course_info['code'])."' AND id_user IN ($user_ids)";
+                    WHERE   id_session ='".$session_id."' AND
+                            course_code = '".Database::escape_string($course_info['code'])."' AND
+                            id_user IN ($user_ids)";
             Database::query($sql);
 
-            foreach ($user_id as $uid) {
+            foreach ($user_list as $uid) {
                 // check if a user is register in the session with other course
-                $sql = "SELECT id_user FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)." WHERE id_session='$session_id' AND id_user='$uid'";
+                $sql = "SELECT id_user FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)."
+                        WHERE id_session='$session_id' AND id_user='$uid'";
                 $rs = Database::query($sql);
+
                 if (Database::num_rows($rs) == 0) {
                     // Delete in table session_rel_user
-                    Database::query("DELETE FROM ".Database::get_main_table(TABLE_MAIN_SESSION_USER)."
-                                     WHERE id_session ='".$session_id."' AND id_user='$uid' AND relation_type<>".SESSION_RELATION_TYPE_RRHH."");
+                    $sql = "DELETE FROM ".Database::get_main_table(TABLE_MAIN_SESSION_USER)."
+                            WHERE id_session ='".$session_id."' AND id_user='$uid' AND relation_type<>".SESSION_RELATION_TYPE_RRHH."";
+                    Database::query($sql);
                 }
             }
 
             // Update the table session
             $sql = "SELECT COUNT(*) FROM ".Database::get_main_table(TABLE_MAIN_SESSION_USER)."
-                    WHERE id_session = '".$session_id."' AND relation_type<>".SESSION_RELATION_TYPE_RRHH;
+                    WHERE id_session = '".$session_id."' AND relation_type <> ".SESSION_RELATION_TYPE_RRHH;
             $row = Database::fetch_array(Database::query($sql));
             $count = $row[0];
             // number of users by session
@@ -323,24 +359,32 @@ class CourseManager
             Database::query($sql);
 
             // Update the table session_rel_course
-            $sql = "SELECT COUNT(*) FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)." WHERE id_session = '$session_id' AND course_code = '$course_code' AND status<>2";
+            $sql = "SELECT COUNT(*) FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)."
+                    WHERE id_session = '$session_id' AND course_code = '$course_code' AND status<>2";
             $row = Database::fetch_array(@Database::query($sql));
             $count = $row[0];
+
             // number of users by session and course
-            Database::query("UPDATE ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE)." SET nbr_users = '$count' WHERE id_session = '$session_id' AND course_code = '$course_code'");
+            $sql = "UPDATE ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE)."
+                    SET nbr_users = '$count'
+                    WHERE id_session = '$session_id' AND course_code = '$course_code'";
+            Database::query($sql);
 
         } else {
             $sql = "DELETE FROM ".Database::get_main_table(TABLE_MAIN_COURSE_USER)."
-                    WHERE user_id IN (".$user_ids.") AND relation_type<>".COURSE_RELATION_TYPE_RRHH." AND course_code = '".$course_code."'";
+                    WHERE
+                        user_id IN (".$user_ids.") AND
+                        relation_type<>".COURSE_RELATION_TYPE_RRHH." AND
+                        course_code = '".$course_code."'";
             Database::query($sql);
 
             // add event to system log
             $user_id = api_get_user_id();
             event_system(LOG_UNSUBSCRIBE_USER_FROM_COURSE, LOG_COURSE_CODE, $course_code, api_get_utc_datetime(), $user_id);
 
-            foreach ($user_list as $user_id_to_delete) {
-                $user_info = api_get_user_info($user_id_to_delete);
-                event_system(LOG_UNSUBSCRIBE_USER_FROM_COURSE, LOG_USER_OBJECT, $user_info, api_get_utc_datetime(), $user_id);
+            foreach ($user_list as $userId) {
+                $userInfo = api_get_user_info($userId);
+                event_system(LOG_UNSUBSCRIBE_USER_FROM_COURSE, LOG_USER_OBJECT, $userInfo, api_get_utc_datetime(), $user_id);
             }
         }
     }
@@ -2094,15 +2138,15 @@ class CourseManager
     /**
      * create recursively all categories as option of the select passed in parameter.
      *
-     * @param FormValidator $form
+     * @param HTML_QuickForm_Element $element
      * @param string $defaultCode the option value to select by default (used mainly for edition of courses)
      * @param string $parentCode the parent category of the categories added (default=null for root category)
      * @param string $padding the indent param (you shouldn't indicate something here)
      */
-    public static function select_and_sort_categories($form, $defaultCode = null, $parentCode = null , $padding = null)
+    public static function select_and_sort_categories($element, $defaultCode = null, $parentCode = null , $padding = null)
     {
         require_once api_get_path(LIBRARY_PATH).'course_category.lib.php';
-        setCategoriesInForm($form, $defaultCode, $parentCode , $padding);
+        setCategoriesInForm($element, $defaultCode, $parentCode , $padding);
     }
 
     /**
@@ -4167,4 +4211,5 @@ class CourseManager
         $result = Database::query($sql);
         return Database::num_rows($result) > 0;
     }
+
 }

+ 53 - 4
main/inc/lib/course_category.lib.php

@@ -580,12 +580,12 @@ function browseCoursesInCategory($category_code, $random_value = null)
 /**
  * create recursively all categories as option of the select passed in parameter.
  *
- * @param FormValidator $form
+ * @param HTML_QuickForm_Element $element
  * @param string $defaultCode the option value to select by default (used mainly for edition of courses)
  * @param string $parentCode the parent category of the categories added (default=null for root category)
  * @param string $padding the indent param (you shouldn't indicate something here)
  */
-function setCategoriesInForm($form, $defaultCode = null, $parentCode = null , $padding = null)
+function setCategoriesInForm($element, $defaultCode = null, $parentCode = null, $padding = null)
 {
     $tbl_category = Database::get_main_table(TABLE_MAIN_CATEGORY);
     $conditions = null;
@@ -609,9 +609,9 @@ function setCategoriesInForm($form, $defaultCode = null, $parentCode = null , $p
         $params .= ($cat['code'] == $defaultCode) ? ' selected' : '';
         $option = $padding.' '.$cat['name'].' ('.$cat['code'].')';
 
-        $form->addOption($option, $cat['code'], $params);
+        $element->addOption($option, $cat['code'], $params);
         if ($cat['auth_cat_child'] == 'TRUE') {
-            setCategoriesInForm($form, $defaultCode, $cat['code'], $padding.' - ');
+            setCategoriesInForm($element, $defaultCode, $cat['code'], $padding.' - ');
         }
     }
 }
@@ -635,7 +635,56 @@ function getCourseCategoryNotInList($list)
     return Database::store_result($result, 'ASSOC');
 }
 
+/**
+ * @param $keyword
+ * @return array|null
+ */
+function searchCategoryByKeyword($keyword)
+{
+    if (empty($keyword)) {
+        return null;
+    }
+
+    $tableCategory = Database::get_main_table(TABLE_MAIN_CATEGORY);
+    $conditions = null;
+    $whereCondition = null;
+    if (isMultipleUrlSupport()) {
+        $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY);
+        $conditions = " INNER JOIN $table a ON (c.id = a.course_category_id)";
+        $whereCondition = " AND a.access_url_id = ".api_get_current_access_url_id();
+    }
+
+    $keyword = Database::escape_string($keyword);
+
+    $sql = "SELECT c.*, c.name as text FROM $tableCategory c $conditions
+            WHERE (BINARY c.code LIKE '%$keyword%' or BINARY name LIKE '%$keyword%') AND auth_course_child = 'TRUE' $whereCondition ";
+    $result = Database::query($sql);
+    return Database::store_result($result, 'ASSOC');
+}
 
+function searchCategoryById($list)
+{
+    if (empty($list)) {
+        return array();
+    } else {
+        $list = array_map('intval', $list);
+        $list = implode("','", $list);
+    }
+
+    $tableCategory = Database::get_main_table(TABLE_MAIN_CATEGORY);
+    $conditions = null;
+    $whereCondition = null;
+    if (isMultipleUrlSupport()) {
+        $table = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE_CATEGORY);
+        $conditions = " INNER JOIN $table a ON (c.id = a.course_category_id)";
+        $whereCondition = " AND a.access_url_id = ".api_get_current_access_url_id();
+    }
+
+    $sql = "SELECT c.*, c.name as text FROM $tableCategory c $conditions
+            WHERE c.id IN $list $whereCondition";
+    $result = Database::query($sql);
+    return Database::store_result($result, 'ASSOC');
+}
 
 /**
  CREATE TABLE IF NOT EXISTS access_url_rel_course_category (access_url_id int unsigned NOT NULL, course_category_id int unsigned NOT NULL, PRIMARY KEY (access_url_id, course_category_id));

+ 191 - 97
main/inc/lib/document.lib.php

@@ -16,15 +16,12 @@
  */
 class DocumentManager
 {
-    /**
-     *
-     */
     private function __construct()
     {
-
     }
 
     /**
+     * @param string
      * @return the document folder quota for the current course, in bytes, or the default quota
      */
     public static function get_course_quota($course_code = null)
@@ -56,6 +53,7 @@ class DocumentManager
      * 	@param string $filename or boolean TRUE to return complete array
      * 	@author ? first version
      * 	@author Bert Vanderkimpen
+     *  @return string
      *
      */
     public static function file_get_mime_type($filename)
@@ -275,14 +273,15 @@ class DocumentManager
     }
 
     /**
+     *  @param string
+     *  @param string
      * 	@return true if the user is allowed to see the document, false otherwise
      * 	@author Sergio A Kessler, first version
-     * 	@author Roan Embrechts, bugfix
-     *   @todo ??not only check if a file is visible, but also check if the user is allowed to see the file??
+     * 	@author Roan Embrechts, bugfix     *
+     *  @todo not only check if a file is visible, but also check if the user is allowed to see the file??
      */
-    public static function file_visible_to_user($this_course, $doc_url) {
-        $current_session_id = api_get_session_id();
-
+    public static function file_visible_to_user($this_course, $doc_url)
+    {
         $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
 
         if ($is_allowed_to_edit) {
@@ -291,10 +290,8 @@ class DocumentManager
             $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
             $tbl_item_property = $this_course . 'item_property';
             $doc_url = Database::escape_string($doc_url);
-            //$doc_url = addslashes($doc_url);
             $query = "SELECT 1 FROM $tbl_document AS docs,$tbl_item_property AS props
                       WHERE props.tool = 'document' AND docs.id=props.ref AND props.visibility <> '1' AND docs.path = '$doc_url'";
-            //echo $query;
             $result = Database::query($query);
 
             return (Database::num_rows($result) == 0);
@@ -393,7 +390,8 @@ class DocumentManager
      * @param string The name of the file in the end (including extension)
      * @return false if file doesn't exist, true if stream succeeded
      */
-    public static function string_send_for_download($full_string, $forced = false, $name = '') {
+    public static function string_send_for_download($full_string, $forced = false, $name = '')
+    {
         $filename = $name;
         $len = strlen($full_string);
 
@@ -520,7 +518,8 @@ class DocumentManager
                         last.visibility,
                         last.insert_user_id
                     FROM $TABLE_ITEMPROPERTY AS last INNER JOIN $TABLE_DOCUMENT AS docs
-                        ON (docs.id = last.ref AND last.tool = '".TOOL_DOCUMENT."' AND docs.c_id = {$_course['real_id']} AND last.c_id = {$_course['real_id']})
+                        ON (docs.id = last.ref AND last.tool = '".TOOL_DOCUMENT."' AND
+                        docs.c_id = {$_course['real_id']} AND last.c_id = {$_course['real_id']})
                     WHERE
                         docs.path LIKE '" . $path . $added_slash . "%' AND
                         docs.path NOT LIKE '" . $path . $added_slash . "%/%' AND
@@ -635,7 +634,8 @@ class DocumentManager
      * @param int $to_group_id
      * @return array with paths
      */
-    public static function get_all_document_folders($_course, $to_group_id = '0', $can_see_invisible = false) {
+    public static function get_all_document_folders($_course, $to_group_id = '0', $can_see_invisible = false)
+    {
         $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
         $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
 
@@ -678,10 +678,8 @@ class DocumentManager
                     }
                     $document_folders[$row['id']] = $row['path'];
                 }
-                //sort($document_folders);
                 natsort($document_folders);
 
-                //return results
                 return $document_folders;
             } else {
                 return false;
@@ -767,7 +765,8 @@ class DocumentManager
      * @param int    $document_id in case you dont have the file path ,insert the id of the file here and leave $file in blank ''
      * @return boolean true/false
      * */
-    public static function check_readonly($_course, $user_id, $file, $document_id = '', $to_delete = false) {
+    public static function check_readonly($_course, $user_id, $file, $document_id = '', $to_delete = false)
+    {
         if (!(!empty($document_id) && is_numeric($document_id))) {
             $document_id = self::get_document_id($_course, $file);
         }
@@ -831,15 +830,24 @@ class DocumentManager
      * @param int    $document_id of the item
      * @return boolean true/false
      * */
-    public static function is_folder($_course, $document_id) {
+    public static function is_folder($_course, $document_id)
+    {
         $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
         $course_id = $_course['real_id'];
         $document_id = Database::escape_string($document_id);
-        $result = Database::fetch_array(Database::query("SELECT filetype FROM $TABLE_DOCUMENT WHERE c_id = $course_id AND id= $document_id"), 'ASSOC');
+        $sql = "SELECT filetype FROM $TABLE_DOCUMENT WHERE c_id = $course_id AND id= $document_id";
+        $result = Database::fetch_array(Database::query($sql), 'ASSOC');
         return $result['filetype'] == 'folder';
     }
 
-    public static function delete_document_from_db($document_id, $course_info = array(), $session_id = 0, $remove_content_from_db = false) {
+    /**
+     * @param int $document_id
+     * @param array $course_info
+     * @param int $session_id
+     * @param bool $remove_content_from_db
+     */
+    public static function delete_document_from_db($document_id, $course_info = array(), $session_id = 0, $remove_content_from_db = false)
+    {
         $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
         $TABLE_ITEMPROPERTY = Database :: get_course_table(TABLE_ITEM_PROPERTY);
 
@@ -853,7 +861,7 @@ class DocumentManager
             $session_id = api_get_session_id();
         }
 
-        //Soft DB delete
+        // Soft DB delete
         api_item_property_update($course_info, TOOL_DOCUMENT, $document_id, 'delete', $user_id, null, null, null, null, $session_id);
         self::delete_document_from_search_engine($course_info['code'], $document_id);
         self::unset_document_as_template($document_id, $course_info['code'], $user_id);
@@ -870,7 +878,11 @@ class DocumentManager
         }
     }
 
-    public static function delete_document_metadata($document_id) {
+    /**
+     * @param int $document_id
+     */
+    public static function delete_document_metadata($document_id)
+    {
         //needed to deleted medadata
         require_once api_get_path(SYS_CODE_PATH) . 'metadata/md_funcs.php';
         require_once api_get_path(LIBRARY_PATH) . 'fileManage.lib.php';
@@ -892,7 +904,8 @@ class DocumentManager
      * @return boolean true/false
      * @todo now only files/folders in a folder get visibility 2, we should rename them too.
      */
-    public static function delete_document($_course, $path, $base_work_dir) {
+    public static function delete_document($_course, $path, $base_work_dir)
+    {
         $TABLE_DOCUMENT = Database :: get_course_table(TABLE_DOCUMENT);
 
         if (empty($path) || empty($base_work_dir)) {
@@ -914,8 +927,8 @@ class DocumentManager
 
         if ($document_id) {
             self::delete_document_from_db($document_id);
-            //checking
-            //$file_exists_in_db = self::get_document_data_by_id($document_id, $_course['code']);
+            // Checking
+            // $file_exists_in_db = self::get_document_data_by_id($document_id, $_course['code']);
             $file_deleted_from_db = true;
         }
 
@@ -923,38 +936,37 @@ class DocumentManager
 
             if (api_get_setting('permanently_remove_deleted_files') == 'true') {
                 //Deleted files are *really* deleted
-                $what_to_delete_sql = "SELECT id FROM " . $TABLE_DOCUMENT . " WHERE c_id = $course_id AND path='" . $path . "' OR path LIKE BINARY '" . $path . "/%'";
-                //get all id's of documents that are deleted
-                $what_to_delete_result = Database::query($what_to_delete_sql);
-
-                if ($what_to_delete_result && Database::num_rows($what_to_delete_result) != 0) {
-                    //delete all item_property entries
-                    while ($row = Database::fetch_array($what_to_delete_result)) {
-                        //query to delete from item_property table (hard way)
+                $sql = "SELECT id FROM $TABLE_DOCUMENT
+                        WHERE c_id = $course_id AND path='".$path."' OR path LIKE BINARY '".$path."/%'";
+                // Get all id's of documents that are deleted
+                $result = Database::query($sql);
+
+                if ($result && Database::num_rows($result) != 0) {
+                    // Delete all item_property entries
+                    while ($row = Database::fetch_array($result)) {
+                        // Query to delete from item_property table (hard way)
                         self::delete_document_from_db($row['id'], $_course, $current_session_id, true);
                     }
-                    //delete documents, do it like this so metadata get's deleted too
-                    //update_db_info('delete', $path);
-                    //throw it away
+                    // Delete documents, do it like this so metadata gets deleted too
                     my_delete($base_work_dir . $path);
                     $file_deleted_from_disk = true;
                 }
             } else {
-                //Set visibility to 2 and rename file/folder to xxx_DELETED_#id (soft delete)
+                // Set visibility to 2 and rename file/folder to xxx_DELETED_#id (soft delete)
 
                 if (is_file($base_work_dir . $path) || is_dir($base_work_dir . $path)) {
-                    if (rename($base_work_dir . $path, $base_work_dir . $new_path)) {
+                    if (rename($base_work_dir . $path, $base_work_dir.$new_path)) {
 
-                        $sql = "UPDATE $TABLE_DOCUMENT set path='" . $new_path . "' WHERE c_id = $course_id AND id='" . $document_id . "'";
+                        $sql = "UPDATE $TABLE_DOCUMENT set path='".$new_path."' WHERE c_id = $course_id AND id='".$document_id."'";
                         Database::query($sql);
 
-                        $sql = "SELECT id, path FROM $TABLE_DOCUMENT WHERE c_id = $course_id AND path LIKE BINARY '" . $path . "/%'";
+                        $sql = "SELECT id, path FROM $TABLE_DOCUMENT WHERE c_id = $course_id AND path LIKE BINARY '".$path."/%'";
                         $result = Database::query($sql);
                         if ($result && Database::num_rows($result) > 0) {
                             while ($deleted_items = Database::fetch_array($result, 'ASSOC')) {
                                 self::delete_document_from_db($deleted_items['id']);
 
-                                //Change path of subfolders and documents in database
+                                // Change path of subfolders and documents in database.
                                 $old_item_path = $deleted_items['path'];
                                 $new_item_path = $new_path . substr($old_item_path, strlen($path));
 
@@ -964,16 +976,17 @@ class DocumentManager
                         }
                         $file_renamed_from_disk = true;
                     } else {
-                        //Couldn't rename - file permissions problem?
-                        error_log(__FILE__ . ' ' . __LINE__ . ': Error renaming ' . $base_work_dir . $path . ' to ' . $base_work_dir . $new_path . '. This is probably due to file permissions', 0);
+                        // Couldn't rename - file permissions problem?
+                        error_log(__FILE__ . ' ' . __LINE__ . ': Error renaming '.$base_work_dir.$path.' to '.$base_work_dir.$new_path.'. This is probably due to file permissions', 0);
                     }
                 }
             }
         }
 
-        //Checking inconsistency
+        // Checking inconsistency
         if ($file_deleted_from_db && $file_deleted_from_disk ||
-                $file_deleted_from_db && $file_renamed_from_disk) {
+            $file_deleted_from_db && $file_renamed_from_disk
+        ) {
             return true;
         } else {
             //Something went wrong
@@ -981,7 +994,7 @@ class DocumentManager
             // This means it has been removed externally. To prevent a
             // blocking error from happening, we drop the related items from the
             // item_property and the document table.
-            error_log(__FILE__ . ' ' . __LINE__ . ': System inconsistency detected. The file or directory ' . $base_work_dir . $path . ' seems to have been removed from the filesystem independently from the web platform. To restore consistency, the elements using the same path will be removed from the database', 0);
+            error_log(__FILE__.' '.__LINE__.': System inconsistency detected. The file or directory '.$base_work_dir.$path.' seems to have been removed from the filesystem independently from the web platform. To restore consistency, the elements using the same path will be removed from the database', 0);
             return false;
         }
     }
@@ -992,7 +1005,8 @@ class DocumentManager
      * @param string $course_id Course code
      * @param int $document_id Document id to delete
      */
-    public static function delete_document_from_search_engine($course_id, $document_id) {
+    public static function delete_document_from_search_engine($course_id, $document_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);
@@ -1022,7 +1036,8 @@ class DocumentManager
      * @param string $path
      * @return int id of document / false if no doc found
      */
-    public static function get_document_id($course_info, $path) {
+    public static function get_document_id($course_info, $path)
+    {
         $TABLE_DOCUMENT = Database :: get_course_table(TABLE_DOCUMENT);
         $course_id = $course_info['real_id'];
         $path = Database::escape_string($path);
@@ -1120,7 +1135,8 @@ class DocumentManager
      * @param string $couse_code
      * @param int $user_id
      */
-    public static function set_document_as_template($title, $description, $document_id_for_template, $couse_code, $user_id, $image) {
+    public static function set_document_as_template($title, $description, $document_id_for_template, $couse_code, $user_id, $image)
+    {
         // Database table definition
         $table_template = Database::get_main_table(TABLE_MAIN_TEMPLATES);
 
@@ -1146,7 +1162,8 @@ class DocumentManager
      * @param string $couse_code
      * @param int $user_id
      */
-    public static function unset_document_as_template($document_id, $course_code, $user_id) {
+    public static function unset_document_as_template($document_id, $course_code, $user_id)
+    {
 
         $table_template = Database::get_main_table(TABLE_MAIN_TEMPLATES);
         $course_code = Database::escape_string($course_code);
@@ -1170,8 +1187,11 @@ class DocumentManager
      *
      * @param string $document_path the relative complete path of the document
      * @param array  $course the _course array info of the document's course
+     * @param int
+     * @param string
      */
-    public static function is_visible($doc_path, $course, $session_id = 0, $file_type = 'file') {
+    public static function is_visible($doc_path, $course, $session_id = 0, $file_type = 'file')
+    {
         $docTable = Database::get_course_table(TABLE_DOCUMENT);
         $propTable = Database::get_course_table(TABLE_ITEM_PROPERTY);
 
@@ -1233,10 +1253,13 @@ class DocumentManager
      *
      * @param   int     document id
      * @param   array   course info
-     * @param   array  	$course the _course array info of the document's course
+     * @param   int
+     * @param   int
+     * @param bool
      * @return  bool
      */
-    public static function is_visible_by_id($doc_id, $course_info, $session_id, $user_id, $admins_can_see_everything = true) {
+    public static function is_visible_by_id($doc_id, $course_info, $session_id, $user_id, $admins_can_see_everything = true)
+    {
         $is_visible = false;
         $user_in_course = false;
 
@@ -1324,7 +1347,8 @@ class DocumentManager
      * @param int The document id
      * @return void()
      */
-    function attach_gradebook_certificate($course_id, $document_id) {
+    function attach_gradebook_certificate($course_id, $document_id)
+    {
         $tbl_category = Database :: get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
         $session_id = api_get_session_id();
         if ($session_id == 0 || is_null($session_id)) {
@@ -1344,7 +1368,8 @@ class DocumentManager
      * @param string The course id
      * @return int The default certificate id
      */
-    static function get_default_certificate_id($course_id) {
+    static function get_default_certificate_id($course_id)
+    {
         $tbl_category = Database :: get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
         $session_id = api_get_session_id();
         if ($session_id == 0 || is_null($session_id)) {
@@ -1369,7 +1394,8 @@ class DocumentManager
      * @param string The course code
      * @return string The html content of the certificate
      */
-    static function replace_user_info_into_html($user_id, $course_code, $is_preview = false) {
+    static function replace_user_info_into_html($user_id, $course_code, $is_preview = false)
+    {
         $user_id = intval($user_id);
         $course_info = api_get_course_info($course_code);
         $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
@@ -1400,7 +1426,8 @@ class DocumentManager
     /**
      * return all content to replace and all content to be replace
      */
-    static function get_all_info_to_certificate($user_id, $course_id, $is_preview = false) {
+    static function get_all_info_to_certificate($user_id, $course_id, $is_preview = false)
+    {
         $info_list = array();
         $user_id = intval($user_id);
 
@@ -1496,7 +1523,8 @@ class DocumentManager
      * @param int The document id of the default certificate
      * @return void()
      */
-    function remove_attach_certificate($course_id, $default_certificate_id) {
+    function remove_attach_certificate($course_id, $default_certificate_id)
+    {
         $default_certificate = self::get_default_certificate_id($course_id);
         if ((int) $default_certificate == (int) $default_certificate_id) {
             $tbl_category = Database :: get_main_table(TABLE_MAIN_GRADEBOOK_CATEGORY);
@@ -1520,7 +1548,8 @@ class DocumentManager
      * @param string The course id
      * @return void()
      */
-    static function create_directory_certificate_in_course($course_id) {
+    static function create_directory_certificate_in_course($course_id)
+    {
         $course_info = api_get_course_info($course_id);
         if (!empty($course_info)) {
             $to_group_id = 0;
@@ -1546,7 +1575,8 @@ class DocumentManager
      * @param string The course id
      * @return int The document id of the directory certificate
      */
-    static function get_document_id_of_directory_certificate() {
+    static function get_document_id_of_directory_certificate()
+    {
         $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
         $course_id = api_get_course_int_id();
         $sql = "SELECT id FROM $tbl_document WHERE c_id = $course_id AND path='/certificates' ";
@@ -1560,8 +1590,9 @@ class DocumentManager
      * @param string path of directory
      * @return bool  true if is a certificate or false otherwise
      */
-    static function is_certificate_mode($dir) {
-        //I'm in the certification module?
+    static function is_certificate_mode($dir)
+    {
+        // I'm in the certification module?
         $is_certificate_mode = false;
         $is_certificate_array = explode('/', $dir);
         array_shift($is_certificate_array);
@@ -1579,11 +1610,13 @@ class DocumentManager
      * to be an HTML file or string html. If it is not, then the function will return and empty list.
      * @param	string  source html (content or path)
      * @param	bool  	is file or string html
-     * @param	string	type (one of the Dokeos tools) - optional (otherwise takes the current item's type)
+     * @param	string	type (one of the app tools) - optional (otherwise takes the current item's type)
      * @param	int		level of recursivity we're in
-     * @return	array	List of file paths. An additional field containing 'local' or 'remote' helps determine if the file should be copied into the zip or just linked
+     * @return	array	List of file paths. An additional field containing 'local' or 'remote' helps determine
+     * if the file should be copied into the zip or just linked
      */
-    static function get_resources_from_source_html($source_html, $is_file = false, $type = null, $recursivity = 1) {
+    static function get_resources_from_source_html($source_html, $is_file = false, $type = null, $recursivity = 1)
+    {
         $max = 5;
         $attributes = array();
         $wanted_attributes = array('src', 'url', '@import', 'href', 'value', 'flashvars');
@@ -1668,7 +1701,9 @@ class DocumentManager
                                     } else {
                                         $files_list[] = array($source, 'local', 'abs');
                                     }
-                                    continue; //skipping anything else to avoid two entries (while the others can have sub-files in their url, flv's can't)
+                                    /* skipping anything else to avoid two entries
+                                    (while the others can have sub-files in their url, flv's can't)*/
+                                    continue;
                                 }
                             }
                             if (strpos($source, '://') > 0) {
@@ -1861,10 +1896,12 @@ class DocumentManager
      *
      * @param    string  HTML attribute string
      * @param	 array	 List of attributes that we want to get back
+     * @param    array
      * @return   array   An associative array of attributes
-     * @author 	 Based on a function from the HTML_Common2 PEAR module
+     * @author 	 Based on a function from the HTML_Common2 PEAR module     *
      */
-    static function parse_HTML_attributes($attrString, $wanted = array(), $explode_variables = array()) {
+    static function parse_HTML_attributes($attrString, $wanted = array(), $explode_variables = array())
+    {
         $attributes = array();
         $regs = array();
         $reduced = false;
@@ -1940,8 +1977,6 @@ class DocumentManager
                     }
                 }
             }
-        } else {
-            //error_log('preg_match did not find anything', 0);
         }
         return $attributes;
     }
@@ -1951,9 +1986,16 @@ class DocumentManager
      * @param string		content html
      * @param string		origin course code
      * @param string		destination course directory
+     * @param string
      * @return string	new content html with replaced urls or return false if content is not a string
      */
-    static function replace_urls_inside_content_html_from_copy_course($content_html, $origin_course_code, $destination_course_directory, $origin_course_path_from_zip = null, $origin_course_info_path = null) {
+    static function replace_urls_inside_content_html_from_copy_course(
+        $content_html,
+        $origin_course_code,
+        $destination_course_directory,
+        $origin_course_path_from_zip = null,
+        $origin_course_info_path = null
+    ) {
         require_once api_get_path(LIBRARY_PATH) . 'fileUpload.lib.php';
 
         if (empty($content_html)) {
@@ -2078,7 +2120,8 @@ class DocumentManager
      * @param string     destination
      * @return string    new content html with replaced urls or return false if content is not a string
      */
-    function replace_urls_inside_content_html_when_moving_file($file_name, $original_path, $destiny_path) {
+    function replace_urls_inside_content_html_when_moving_file($file_name, $original_path, $destiny_path)
+    {
         if (substr($original_path, strlen($original_path) - 1, strlen($original_path)) == '/') {
             $original = $original_path . $file_name;
         } else {
@@ -2225,7 +2268,8 @@ class DocumentManager
      * @param int $document_id
      * @param string $course_code
      */
-    public static function export_to_pdf($document_id, $course_code) {
+    public static function export_to_pdf($document_id, $course_code)
+    {
         require_once api_get_path(LIBRARY_PATH) . 'pdf.lib.php';
         $course_data = api_get_course_info($course_code);
         $document_data = self::get_document_data_by_id($document_id, $course_code);
@@ -2328,7 +2372,8 @@ class DocumentManager
      * Obtains the text inside the file with the right parser
 
      */
-    function get_text_content($doc_path, $doc_mime) {
+    function get_text_content($doc_path, $doc_mime)
+    {
         // TODO: review w$ compatibility
         // Use usual exec output lines array to store stdout instead of a temp file
         // because we need to store it at RAM anyway before index on ChamiloIndexer object
@@ -2408,7 +2453,8 @@ class DocumentManager
      * @param  int $to_group_id (to calculate group document space)
      * @return int total size
      */
-    static function documents_total_space($course_id = null, $group_id = null, $session_id = null) {
+    static function documents_total_space($course_id = null, $group_id = null, $session_id = null)
+    {
         $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
         $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
 
@@ -2454,7 +2500,8 @@ class DocumentManager
     /**
      *  Here we count 1 kilobyte = 1000 byte, 12 megabyte = 1000 kilobyte.
      */
-    static function display_quota($course_quota, $already_consumed_space) {
+    static function display_quota($course_quota, $already_consumed_space)
+    {
         $course_quota_m = round($course_quota / 1000000);
         $already_consumed_space_m = round($already_consumed_space / 1000000);
 
@@ -2502,7 +2549,8 @@ class DocumentManager
      *
      *  Here we count 1 kilobyte = 1000 byte, 12 megabyte = 1000 kilobyte.
      */
-    static function display_simple_quota($course_quota, $already_consumed_space) {
+    static function display_simple_quota($course_quota, $already_consumed_space)
+    {
         $course_quota_m = round($course_quota / 1000000);
         $already_consumed_space_m = round($already_consumed_space / 1000000, 2);
         $percentage = $already_consumed_space / $course_quota * 100;
@@ -2518,7 +2566,6 @@ class DocumentManager
      *
      * @author Bert Vanderkimpen
      * @param  int file_size size of the file in byte
-     * @param array $_course
      * @param  int max_dir_space maximum size
      * @return boolean true if there is enough space, false otherwise
      *
@@ -2535,11 +2582,11 @@ class DocumentManager
     }
 
     /**
-     *
      * @param array paremeters: count, url, extension
      * @return string
      */
-    static function generate_jplayer_jquery($params = array()) {
+    static function generate_jplayer_jquery($params = array())
+    {
         $js_path = api_get_path(WEB_LIBRARY_PATH) . 'javascript/';
 
         $jplayer_definition = ' $("#jquery_jplayer_' . $params['count'] . '").jPlayer({
@@ -2567,9 +2614,11 @@ class DocumentManager
      *
      * Shows a play icon next to the document title in the document list
      * @param int
+     * @param string
      * @return string	html content
      */
-    static function generate_media_preview($i, $type = 'simple') {
+    static function generate_media_preview($i, $type = 'simple')
+    {
         $i = intval($i);
 
         $extra_controls = $progress = '';
@@ -2602,7 +2651,12 @@ class DocumentManager
         return $html;
     }
 
-    static function generate_video_preview($document_data = array()) {
+    /**
+     * @param array $document_data
+     * @return string
+     */
+    static function generate_video_preview($document_data = array())
+    {
         $html = '
         <div id="jp_container_1" class="jp-video">
 			<div class="jp-type-single">
@@ -2653,7 +2707,24 @@ class DocumentManager
         return $html;
     }
 
-    static function get_document_preview($course_info, $lp_id = false, $target = '', $session_id = 0, $add_move_button = false, $filter_by_folder = null, $overwrite_url = null) {
+    /**
+     * @param array $course_info
+     * @param bool $lp_id
+     * @param string $target
+     * @param int $session_id
+     * @param bool $add_move_button
+     * @param string $filter_by_folder
+     * @param string $overwrite_url
+     * @return string
+     */
+    static function get_document_preview(
+        $course_info, $lp_id = false,
+        $target = '',
+        $session_id = 0,
+        $add_move_button = false,
+        $filter_by_folder = null,
+        $overwrite_url = null
+    ) {
         if (empty($course_info['real_id']) || empty($course_info['code']) || !is_array($course_info)) {
             return '';
         }
@@ -2843,11 +2914,26 @@ class DocumentManager
      * Generate and return an HTML list of resources based on a given array.
      * This list is used to show the course creator a list of available resources to choose from
      * when creating a learning path.
-     * @param	array	Array of elements to add to the list
-     * @param	integer Enables the tree display by shifting the new elements a certain distance to the right
-     * @return	string	The HTML list
+     * @param array $course_info
+     * @param int $session_id
+     * @param array $resources_sorted
+     * @param int $num
+     * @param bool $lp_id
+     * @param string $target
+     * @param bool $add_move_button
+     * @param string $overwrite_url
+     * @return string
      */
-    public static function write_resources_tree($course_info, $session_id, $resources_sorted, $num = 0, $lp_id = false, $target = '', $add_move_button = false, $overwrite_url = null) {
+    public static function write_resources_tree(
+        $course_info,
+        $session_id,
+        $resources_sorted,
+        $num = 0,
+        $lp_id = false,
+        $target = '',
+        $add_move_button = false,
+        $overwrite_url = null
+    ) {
         require_once api_get_path(LIBRARY_PATH) . 'fileDisplay.lib.php';
 
         $img_path = api_get_path(WEB_IMG_PATH);
@@ -2983,7 +3069,8 @@ class DocumentManager
      * @param int $user_id
      * @return bool
      */
-    public static function check_visibility_tree($doc_id, $course_code, $session_id, $user_id) {
+    public static function check_visibility_tree($doc_id, $course_code, $session_id, $user_id)
+    {
         $document_data = self::get_document_data_by_id($doc_id, $course_code);
 
         if (!empty($document_data)) {
@@ -3021,7 +3108,15 @@ class DocumentManager
      * @param   bool    When set to true, this runs the indexer without actually saving anything to any database
      * @return  bool    Returns true on presumed success, false on failure
      */
-    public function index_document($docid, $course_code, $session_id = 0, $lang = 'english', $specific_fields_values = array(), $if_exists = '', $simulation = false) {
+    public function index_document(
+        $docid,
+        $course_code,
+        $session_id = 0,
+        $lang = 'english',
+        $specific_fields_values = array(),
+        $if_exists = '',
+        $simulation = false
+    ) {
         if (api_get_setting('search_enabled') !== 'true') {
             return false;
         }
@@ -3210,16 +3305,18 @@ class DocumentManager
     /**
      * @return array
      */
-    public static function get_web_odf_extension_list() {
+    public static function get_web_odf_extension_list()
+    {
         return array('ods', 'odt');
     }
 
     /**
-     * @param $path
+     * @param string $path
      * @param bool $is_certificate_mode
      * @return bool
      */
-    public static function is_folder_to_avoid($path, $is_certificate_mode = false) {
+    public static function is_folder_to_avoid($path, $is_certificate_mode = false)
+    {
         $folders_to_avoid = array(
             '/HotPotatoes_files',
             '/certificates',
@@ -3268,7 +3365,8 @@ class DocumentManager
     /**
      * @return array
      */
-    static function get_system_folders() {
+    static function get_system_folders()
+    {
         $system_folders = array(
             '/certificates',
             '/chat_files',
@@ -3337,9 +3435,5 @@ class DocumentManager
                 );
             }
         }
-
     }
-
 }
-
-//end class DocumentManager

+ 1 - 0
main/inc/lib/fckeditor/editor/_source/classes/fckxml_ie.js

@@ -38,6 +38,7 @@ FCKXml.prototype =
 
 		oXmlHttp.open( "GET", urlToCall, false ) ;
 
+        try { oXmlHttp.responseType = 'msxml-document'; } catch(e){}
 		oXmlHttp.send( null ) ;
 
 		if ( oXmlHttp.status == 200 || oXmlHttp.status == 304 || ( oXmlHttp.status == 0 && oXmlHttp.readyState == 4 ) )

+ 93 - 0
main/inc/lib/formvalidator/Element/select_ajax.php

@@ -0,0 +1,93 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+require_once 'HTML/QuickForm/select.php';
+require_once 'HTML/QuickForm/html.php';
+
+/**
+* A drop down list with all languages to use with QuickForm
+*/
+class HTML_QuickForm_Select_Ajax extends HTML_QuickForm_select
+{
+    /**
+     * Class constructor
+     */
+    function HTML_QuickForm_Select_Ajax($elementName = null, $elementLabel = null, $options = null, $attributes = null)
+    {
+        parent::HTML_QuickForm_Select($elementName, $elementLabel, $options, $attributes);
+    }
+
+    /**
+     * The ajax call must contain an array of id and text
+     * @return string
+     */
+    function toHtml()
+    {
+        $html = api_get_js('select2/select2.js');
+
+        $iso = api_get_language_isocode(api_get_interface_language());
+        $localeFile = 'select2_locale_'.$iso.'.js';
+        if (file_exists(api_get_path(LIBRARY_PATH).'javascript/select2/'.$localeFile)) {
+            $html .= api_get_js('select2/'.$localeFile);
+        }
+        $html .= api_get_css(api_get_path(WEB_LIBRARY_PATH).'javascript/select2/select2.css');
+
+        $formatResult = $this->getAttribute('formatResult');
+
+        $formatCondition = null;
+
+        if (!empty($formatResult)) {
+            $formatCondition = ',
+                formatResult : '.$formatResult.',
+                formatSelection : '.$formatResult.',';
+        }
+
+        $defaultValues = $this->getAttribute('defaults');
+
+        $dataCondition = null;
+        $tags = null;
+        if (!empty($defaultValues)) {
+
+            $result = json_encode($defaultValues);
+            $result = str_replace('"id"', 'id', $result);
+            $result = str_replace('"text"', 'text', $result);
+            $dataCondition = '$("#'.$this->getAttribute('name').'").select2("data", '.$result.')';
+            $tags = ',tags : function() { return '.$result.'} ';
+        }
+
+        $html .= '<script>
+                $(function() {
+                    $("#'.$this->getAttribute('name').'").select2({
+                        placeholder: "'.get_lang('SelectAnOption').'",
+                        allowClear: true,
+                        width: "element",
+                        minimumInputLength: 2,
+                        // instead of writing the function to execute the request we use Select2s convenient helper
+                        ajax: {
+                            url: "'.$this->getAttribute('url').'",
+                            dataType: "json",
+                            data: function (term, page) {
+                                return {
+                                    q: term, // search term
+                                    page_limit: 10,
+                                };
+                            },
+                            results: function (data, page) { // parse the results into the format expected by Select2.
+                                // since we are using custom formatting functions we do not need to alter remote JSON data
+                                return {
+                                    results: data
+                                };
+                            }
+                        }
+                        '.$tags.'
+                        '.$formatCondition.'
+                    });
+                    '.$dataCondition.'
+
+                });
+
+        </script>';
+        $html .= '<input id="'.$this->getAttribute('name').'" name = "'.$this->getAttribute('name').'" />';
+        return $html;
+    }
+}

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

@@ -130,6 +130,8 @@ class FormValidator extends HTML_QuickForm
         $this->registerElementType('datepickerdate', $dir . 'Element/datepickerdate.php', 'HTML_QuickForm_datepickerdate');
         $this->registerElementType('receivers', $dir . 'Element/receivers.php', 'HTML_QuickForm_receivers');
         $this->registerElementType('select_language', $dir . 'Element/select_language.php', 'HTML_QuickForm_Select_Language');
+        $this->registerElementType('select_ajax', $dir . 'Element/select_ajax.php', 'HTML_QuickForm_Select_Ajax');
+
         $this->registerElementType('select_theme', $dir . 'Element/select_theme.php', 'HTML_QuickForm_Select_Theme');
         $this->registerElementType('style_submit_button', $dir . 'Element/style_submit_button.php', 'HTML_QuickForm_stylesubmitbutton');
         $this->registerElementType('style_reset_button', $dir . 'Element/style_reset_button.php', 'HTML_QuickForm_styleresetbutton');
@@ -147,7 +149,6 @@ class FormValidator extends HTML_QuickForm
         $this->registerRule('multiple_required', 'required', 'HTML_QuickForm_Rule_MultipleRequired', $dir . 'Rule/MultipleRequired.php');
         $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

+ 129 - 117
main/inc/lib/groupmanager.lib.php

@@ -185,7 +185,7 @@ class GroupManager
      */
     public static function create_group($name, $category_id, $tutor, $places)
     {
-        global $_course;
+        $_course = api_get_course_info();
         $table_group = Database :: get_course_table(TABLE_GROUP);
 
         $session_id = api_get_session_id();
@@ -204,7 +204,7 @@ class GroupManager
             }
         }
 
-         $sql = "INSERT INTO ".$table_group." SET
+        $sql = "INSERT INTO ".$table_group." SET
                 c_id = $course_id ,
                 category_id='".Database::escape_string($category_id)."',
                 max_student = '".$places."',
@@ -412,7 +412,7 @@ class GroupManager
                 if (file_exists($source_directory)) {
                     if (api_get_setting('permanently_remove_deleted_files') == 'true') {
                         // Delete
-                         my_delete($source_directory);
+                        my_delete($source_directory);
                     } else {
                         // Rename
                         rename($source_directory, $destination_dir);
@@ -1017,11 +1017,11 @@ class GroupManager
     }
 
     /**
-    * Returns users belonging to any of the group
-    *
-    * @param array $groups list of group ids
-    * @return array list of user ids
-    */
+     * 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();
@@ -1151,7 +1151,6 @@ class GroupManager
                     if (self :: can_user_subscribe($user_id, $group_id)) {
                         self :: subscribe_users($user_id, $group_id);
                         $group_available_place[$group_id]--;
-                        //$userToken[$user_id]--;
                         unset($userToken[$user_id]);
                         $changed = true;
                         break;
@@ -1239,10 +1238,11 @@ class GroupManager
     public static function is_self_registration_allowed($user_id, $group_id)
     {
         $course_id = api_get_course_int_id();
-        if (!$user_id > 0)
+        if (!$user_id > 0) {
             return false;
+        }
         $table_group = Database :: get_course_table(TABLE_GROUP);
-        $group_id= intval($group_id);
+        $group_id = intval($group_id);
         if (isset($group_id)) {
             $sql = "SELECT  self_registration_allowed FROM $table_group WHERE c_id = $course_id AND id = $group_id";
             $db_result = Database::query($sql);
@@ -1261,13 +1261,17 @@ class GroupManager
      */
     public static function is_self_unregistration_allowed($user_id, $group_id)
     {
-        if (!$user_id > 0)
+        if (!$user_id > 0) {
             return false;
+        }
         $table_group = Database :: get_course_table(TABLE_GROUP);
         $group_id = Database::escape_string($group_id);
         $course_id = api_get_course_int_id();
-        $db_result = Database::query('SELECT  self_unregistration_allowed FROM '.$table_group.' WHERE c_id = '.$course_id.' AND id = '.$group_id);
+        $db_result = Database::query(
+            'SELECT  self_unregistration_allowed FROM '.$table_group.' WHERE c_id = '.$course_id.' AND id = '.$group_id
+        );
         $db_object = Database::fetch_object($db_result);
+
         return $db_object->self_unregistration_allowed == 1 && self :: can_user_unsubscribe($user_id, $group_id);
     }
 
@@ -1279,13 +1283,16 @@ class GroupManager
      */
     public static function is_subscribed($user_id, $group_id)
     {
-        if(empty($user_id) or empty($group_id)){return false;}
+        if (empty($user_id) or empty($group_id)) {
+            return false;
+        }
         $table_group_user = Database :: get_course_table(TABLE_GROUP_USER);
         $group_id = Database::escape_string($group_id);
         $user_id = Database::escape_string($user_id);
         $course_id = api_get_course_int_id();
         $sql = 'SELECT 1 FROM '.$table_group_user.' WHERE c_id = '.$course_id.' AND group_id = '.$group_id.' AND user_id = '.$user_id;
         $db_result = Database::query($sql);
+
         return Database::num_rows($db_result) > 0;
     }
 
@@ -1297,10 +1304,7 @@ class GroupManager
      */
     public static function can_user_subscribe($user_id, $group_id)
     {
-        global $_course;
-        $course_code = $_course['sysCode'];
         $category = self :: get_category_from_group($group_id);
-        $result = CourseManager :: is_user_subscribed_in_real_or_linked_course($user_id, $course_code);
         $result = !self :: is_subscribed($user_id, $group_id);
         $result &= (self :: number_of_students($group_id) < self :: maximum_number_of_students($group_id));
         if ($category['groups_per_user'] == self::GROUP_PER_MEMBER_NO_LIMIT) {
@@ -1308,6 +1312,7 @@ class GroupManager
         }
         $result &= (self :: user_in_number_of_groups($user_id, $category['id']) < $category['groups_per_user']);
         $result &= !self :: is_tutor_of_group($user_id, $group_id);
+
         return $result;
     }
 
@@ -1370,7 +1375,8 @@ class GroupManager
     {
         $table_user = Database :: get_main_table(TABLE_MAIN_USER);
         $table_group_tutor = Database :: get_course_table(TABLE_GROUP_TUTOR);
-        $order_clause = api_sort_by_first_name() ? ' ORDER BY u.firstname, u.lastname' : ' ORDER BY u.lastname, u.firstname';
+        $order_clause = api_sort_by_first_name(
+        ) ? ' ORDER BY u.firstname, u.lastname' : ' ORDER BY u.lastname, u.firstname';
         $group_id = Database::escape_string($group_id);
         $course_id = api_get_course_int_id();
 
@@ -1399,11 +1405,11 @@ class GroupManager
      * Subscribe user(s) to 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 successful
+     * @return bool TRUE if successfull
      */
     public static function subscribe_users($user_ids, $group_id)
     {
-        $user_ids = is_array($user_ids) ? $user_ids : array ($user_ids);
+        $user_ids = is_array($user_ids) ? $user_ids : array($user_ids);
         $result = true;
         $course_id = api_get_course_int_id();
         $table_group_user = Database :: get_course_table(TABLE_GROUP_USER);
@@ -1425,11 +1431,11 @@ class GroupManager
      * @param int $group_id
      * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University
      * @see subscribe_users. This function is almost an exact copy of that function.
-     * @return bool TRUE if successful
+     * @return bool TRUE if successfull
      */
     public static function subscribe_tutors($user_ids, $group_id)
     {
-        $user_ids = is_array($user_ids) ? $user_ids : array ($user_ids);
+        $user_ids = is_array($user_ids) ? $user_ids : array($user_ids);
         $result = true;
         $course_id = api_get_course_int_id();
         $table_group_tutor = Database :: get_course_table(TABLE_GROUP_TUTOR);
@@ -1448,7 +1454,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 successful
+     * @return bool TRUE if successfull
      */
     public static function unsubscribe_users($user_ids, $group_id)
     {
@@ -1464,23 +1470,23 @@ class GroupManager
     /**
      * Unsubscribe all users from one or more groups
      * @param mixed $group_id Can be an array with group-id's or a single group-id
-     * @return bool TRUE if successful
+     * @return bool TRUE if successfull
      */
     public static function unsubscribe_all_users($group_ids)
     {
         $course_id = api_get_course_int_id();
 
-        $group_ids = is_array($group_ids) ? $group_ids : array ($group_ids);
+        $group_ids = is_array($group_ids) ? $group_ids : array($group_ids);
         $group_ids = array_map('intval', $group_ids);
-        if( count($group_ids) > 0) {
-            if(api_is_course_coach()) {
-                for($i=0 ; $i<count($group_ids) ; $i++) {
-                    if(!api_is_element_in_the_session(TOOL_GROUP,$group_ids[$i])) {
-                        array_splice($group_ids,$i,1);
+        if (count($group_ids) > 0) {
+            if (api_is_course_coach()) {
+                for ($i = 0; $i < count($group_ids); $i++) {
+                    if (!api_is_element_in_the_session(TOOL_GROUP, $group_ids[$i])) {
+                        array_splice($group_ids, $i, 1);
                         $i--;
                     }
                 }
-                if(count($group_ids)==0) {
+                if (count($group_ids) == 0) {
                     return false;
                 }
             }
@@ -1502,8 +1508,8 @@ class GroupManager
     public static function unsubscribe_all_tutors($group_ids)
     {
         $course_id = api_get_course_int_id();
-        $group_ids = is_array($group_ids) ? $group_ids : array ($group_ids);
-        if( count($group_ids) > 0) {
+        $group_ids = is_array($group_ids) ? $group_ids : array($group_ids);
+        if (count($group_ids) > 0) {
             $table_group_tutor = Database :: get_course_table(TABLE_GROUP_TUTOR);
             $sql = 'DELETE FROM '.$table_group_tutor.' WHERE group_id IN ('.implode(',', $group_ids).') AND c_id = '.$course_id;
             $result = Database::query($sql);
@@ -1520,7 +1526,7 @@ class GroupManager
      * @todo use the function user_has_access that includes this function
      * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University
      */
-    public static function is_tutor_of_group($user_id,$group_id)
+    public static function is_tutor_of_group($user_id, $group_id)
     {
         $table_group_tutor = Database :: get_course_table(TABLE_GROUP_TUTOR);
         $user_id = Database::escape_string($user_id);
@@ -1529,7 +1535,7 @@ class GroupManager
 
         $sql = "SELECT * FROM ".$table_group_tutor." WHERE c_id = $course_id AND user_id='".$user_id."' AND group_id='".$group_id."'";
         $result = Database::query($sql);
-        if (Database::num_rows($result)>0) {
+        if (Database::num_rows($result) > 0) {
             return true;
         } else {
             return false;
@@ -1544,11 +1550,11 @@ class GroupManager
      * @param $group_id the id of the group
      * @return boolean true/false
      * @author Patrick Cool <patrick.cool@UGent.be>, Ghent University
-    */
+     */
     public static function is_user_in_group($user_id, $group_id)
     {
-        $member = self :: is_subscribed($user_id,$group_id);
-        $tutor  = self :: is_tutor_of_group($user_id,$group_id);
+        $member = self :: is_subscribed($user_id, $group_id);
+        $tutor = self :: is_tutor_of_group($user_id, $group_id);
         if ($member OR $tutor) {
             return true;
         } else {
@@ -1564,16 +1570,15 @@ class GroupManager
      */
     public static function get_all_tutors()
     {
-        global $_course;
         $course_user_table = Database :: get_main_table(TABLE_MAIN_COURSE_USER);
         $user_table = Database :: get_main_table(TABLE_MAIN_USER);
         $sql = "SELECT user.user_id AS user_id, user.lastname AS lastname, user.firstname AS firstname
-                FROM ".$user_table." user, ".$course_user_table." cu
-                WHERE cu.user_id=user.user_id
-                AND cu.tutor_id='1'
-                AND cu.course_code='".$_course['sysCode']."'";
+				FROM ".$user_table." user, ".$course_user_table." cu
+				WHERE cu.user_id=user.user_id
+				AND cu.tutor_id='1'
+				AND cu.c_id='".api_get_course_int_id()."'";
         $resultTutor = Database::query($sql);
-        $tutors = array ();
+        $tutors = array();
         while ($tutor = Database::fetch_array($resultTutor)) {
             $tutors[] = $tutor;
         }
@@ -1587,15 +1592,16 @@ class GroupManager
      * @return bool TRUE if given user is a tutor in the current course.
      * @deprecated this function uses the old tutor implementation
      */
-    public static function is_tutor($user_id) {
-        global $_course;
-        $course_user_table     = Database::get_main_table(TABLE_MAIN_COURSE_USER);
-        $user_id             = Database::escape_string($user_id);
+    public static function is_tutor($user_id)
+    {
+        $course_user_table = Database::get_main_table(TABLE_MAIN_COURSE_USER);
+        $user_id = Database::escape_string($user_id);
 
         $sql = "SELECT tutor_id FROM ".$course_user_table."
-                WHERE user_id='".$user_id."' AND course_code='".$_course['code']."'"."AND tutor_id=1";
+		        WHERE user_id = '".$user_id."' AND c_id ='".api_get_course_int_id()."'"."AND tutor_id=1";
         $db_result = Database::query($sql);
         $result = (Database::num_rows($db_result) > 0);
+
         return $result;
     }
 
@@ -1607,55 +1613,59 @@ class GroupManager
      * @param integer $user_id: the ID of the user you want to know all its
      * group memberships
      */
-    public static function get_group_ids ($course_id, $user_id) {
+    public static function get_group_ids($course_id, $user_id)
+    {
         $groups = array();
-        $tbl_group     = Database::get_course_table(TABLE_GROUP_USER);
-        $tbl_group_tutor     = Database::get_course_table(TABLE_GROUP_TUTOR);
-        $user_id     = intval($user_id);
-        $course_id     = intval($course_id);
+        $tbl_group = Database::get_course_table(TABLE_GROUP_USER);
+        $tbl_group_tutor = Database::get_course_table(TABLE_GROUP_TUTOR);
+        $user_id = intval($user_id);
+        $course_id = intval($course_id);
 
         $sql = "SELECT group_id FROM $tbl_group WHERE c_id = $course_id AND user_id = '$user_id'";
         $groupres = Database::query($sql);
 
         if ($groupres) {
-            while ($myrow= Database::fetch_array($groupres))
-                $groups[]=$myrow['group_id'];
+            while ($myrow = Database::fetch_array($groupres)) {
+                $groups[] = $myrow['group_id'];
+            }
         }
 
         //Also loading if i'm the tutor
         $sql = "SELECT group_id FROM $tbl_group_tutor WHERE c_id = $course_id AND user_id = '$user_id'";
         $groupres = Database::query($sql);
         if ($groupres) {
-            while ($myrow= Database::fetch_array($groupres)) {
-                $groups[]=$myrow['group_id'];
+            while ($myrow = Database::fetch_array($groupres)) {
+                $groups[] = $myrow['group_id'];
             }
         }
         if (!empty($groups)) {
-             array_filter($groups);
+            array_filter($groups);
         }
+
         return $groups;
     }
 
     /*
         Group functions - these take virtual/linked courses into account when necessary
     */
+
     /**
-    *    Get a combined list of all users of the real course $course_code
-    *        and all users in virtual courses linked to this course $course_code
-    *    Filter user list: remove duplicate users; plus
-    *        remove users that
-    *        - are already in the current group $group_id;
-    *        - do not have student status in these courses;
-    *        - are not appointed as tutor (group assistent) for this group;
-    *        - have already reached their maximum # of groups in this course.
-    *
-    *    Originally to get the correct list of users a big SQL statement was used,
-    *    but this has become more complicated now there is not just one real course but many virtual courses.
-    *    Still, that could have worked as well.
-    *
-    *    @version 1.1.3
-    *    @author Roan Embrechts
-    */
+     *    Get a combined list of all users of the real course $course_code
+     *        and all users in virtual courses linked to this course $course_code
+     *    Filter user list: remove duplicate users; plus
+     *        remove users that
+     *        - are already in the current group $group_id;
+     *        - do not have student status in these courses;
+     *        - are not appointed as tutor (group assistent) for this group;
+     *        - have already reached their maximum # of groups in this course.
+     *
+     *    Originally to get the correct list of users a big SQL statement was used,
+     *    but this has become more complicated now there is not just one real course but many virtual courses.
+     *    Still, that could have worked as well.
+     *
+     *    @version 1.1.3
+     *    @author Roan Embrechts
+     */
     public static function get_complete_list_of_users_that_can_be_added_to_group ($course_code, $group_id) {
         global $_course, $_user;
         $category = self :: get_category_from_group($group_id, $course_code);
@@ -1711,28 +1721,30 @@ class GroupManager
         return $complete_user_list;
     }
     /**
-    *    Filter out duplicates in a multidimensional array
-    *    by comparing field $compare_field.
-    *
-    *    @param $user_array_in list of users (must be sorted).
-    *    @param string $compare_field, the field to be compared
-    */
-    public static function filter_duplicates ($user_array_in, $compare_field) {
+     *    Filter out duplicates in a multidimensional array
+     *    by comparing field $compare_field.
+     *
+     * @param $user_array_in list of users (must be sorted).
+     * @param string $compare_field, the field to be compared
+     */
+    public static function filter_duplicates($user_array_in, $compare_field)
+    {
         $total_number = count($user_array_in);
         $user_array_out[0] = $user_array_in[0];
         $count_out = 0;
-        for ($count_in = 1; $count_in < $total_number; $count_in ++) {
+        for ($count_in = 1; $count_in < $total_number; $count_in++) {
             if ($user_array_in[$count_in][$compare_field] != $user_array_out[$count_out][$compare_field]) {
-                $count_out ++;
+                $count_out++;
                 $user_array_out[$count_out] = $user_array_in[$count_in];
             }
         }
         return $user_array_out;
     }
     /**
-    *    Filters from the array $user_array_in the users already in the group $group_id.
-    */
-    public static function filter_users_already_in_group ($user_array_in, $group_id) {
+     *    Filters from the array $user_array_in the users already in the group $group_id.
+     */
+    public static function filter_users_already_in_group($user_array_in, $group_id)
+    {
         foreach ($user_array_in as $this_user) {
             if (!self :: is_subscribed($this_user['user_id'], $group_id)) {
                 $user_array_out[] = $this_user;
@@ -1742,11 +1754,12 @@ class GroupManager
     }
 
     /**
-    * Remove all users that are not students and all users who have tutor status
-    * from  the list.
-    */
-    public static function filter_only_students($user_array_in) {
-        $user_array_out = array ();
+     * Remove all users that are not students and all users who have tutor status
+     * from  the list.
+     */
+    public static function filter_only_students($user_array_in)
+    {
+        $user_array_out = array();
         foreach ($user_array_in as $this_user) {
             //if ($this_user['status_rel'] == STUDENT && $this_user['tutor_id'] == 0) {
             if (api_get_session_id()) {
@@ -1771,7 +1784,9 @@ class GroupManager
      * @return bool True if the given user has access to the given tool in the
      * given course.
      */
-    public static function user_has_access($user_id, $group_id, $tool) {
+    public static function user_has_access($user_id, $group_id, $tool)
+    {
+
         // Admin have access everywhere
         if (api_is_platform_admin()) {
             return true;
@@ -1808,7 +1823,7 @@ class GroupManager
                 return false;
         }
 
-        $user_is_in_group = self::is_user_in_group($user_id, $group_id);
+        $user_is_in_group = self :: is_user_in_group($user_id, $group_id);
 
         // Check group properties
         $group_info = self :: get_group_properties($group_id);
@@ -1834,11 +1849,11 @@ class GroupManager
             return false;
         } elseif ($group_info[$state_key] == self::TOOL_PUBLIC) {
             return true;
-        } elseif (api_is_allowed_to_edit(false,true)) {
+        } elseif (api_is_allowed_to_edit(false, true)) {
             return true;
-        } elseif($group_info['tutor_id'] == $user_id) { //this tutor implementation was dropped
+        } elseif ($group_info['tutor_id'] == $user_id) { //this tutor implementation was dropped
             return true;
-        } elseif($group_info[$state_key] == self::TOOL_PRIVATE && !$user_is_in_group) {
+        } elseif ($group_info[$state_key] == self::TOOL_PRIVATE && !$user_is_in_group) {
             return false;
         } else {
             return $user_is_in_group;
@@ -1850,17 +1865,17 @@ class GroupManager
      */
     public static function get_user_group_name($user_id)
     {
-        $table_group_user   = Database::get_course_table(TABLE_GROUP_USER);
-        $table_group        = Database::get_course_table(TABLE_GROUP);
-        $user_id            = intval($user_id);
-        $course_id          = api_get_course_int_id();
+        $table_group_user = Database::get_course_table(TABLE_GROUP_USER);
+        $table_group = Database::get_course_table(TABLE_GROUP);
+        $user_id = intval($user_id);
+        $course_id = api_get_course_int_id();
         $sql_groups = "SELECT name FROM $table_group g  INNER JOIN $table_group_user gu
                        ON (gu.group_id=g.id)
-                       WHERE    gu.c_id= $course_id AND
+		               WHERE    gu.c_id= $course_id AND
                                 g.c_id= $course_id AND
                                 gu.user_id = $user_id";
         $res = Database::query($sql_groups);
-        $groups=array();
+        $groups = array();
         while ($group = Database::fetch_array($res)) {
             $groups[] .= $group['name'];
         }
@@ -1882,31 +1897,28 @@ class GroupManager
         $group_ids = array_map('intval', $group_ids);
 
         if (api_is_course_coach()) {
-            for ($i=0 ; $i<count($group_ids) ; $i++) {
-                if (!api_is_element_in_the_session(TOOL_GROUP,$group_ids[$i])) {
+            for ($i = 0; $i < count($group_ids); $i++) {
+                if (!api_is_element_in_the_session(TOOL_GROUP, $group_ids[$i])) {
                     array_splice($group_ids, $i, 1);
                     $i--;
                 }
             }
-            if (count($group_ids)==0) {
+            if (count($group_ids) == 0) {
                 return false;
             }
         }
 
-        global $_course;
+        $_course = api_get_course_info();
 
-        $session_id = api_get_session_id();
-        $course_id = api_get_course_int_id();
-
-        $category = self::get_category_from_group($group_ids[0]);
+        $category = self :: get_category_from_group($group_ids[0]);
         $groups_per_user = $category['groups_per_user'];
+
         $group_table = Database :: get_course_table(TABLE_GROUP);
         $group_user_table = Database :: get_course_table(TABLE_GROUP_USER);
-
-
+        $session_id = api_get_session_id();
         $complete_user_list = CourseManager :: get_real_and_linked_user_list($_course['sysCode'], true, $session_id);
         $number_groups_per_user = ($groups_per_user == self::GROUP_PER_MEMBER_NO_LIMIT ? self::INFINITE : $groups_per_user);
-
+        $course_id = api_get_course_int_id();
         /*
          * Retrieve all the groups where enrollment is still allowed
          * (reverse) ordered by the number of place available
@@ -1928,7 +1940,7 @@ class GroupManager
             } else {
                 $places = self::MEMBER_PER_GROUP_NO_LIMIT;
             }
-            $group_available_place[$group['gid']] = $group['nbPlaces'];
+            $group_available_place[$group['gid']] = $places;
         }
 
         /*
@@ -1980,7 +1992,7 @@ class GroupManager
             $row = array();
 
             // Checkbox
-            if (api_is_allowed_to_edit(false,true) && count($group_list) > 1) {
+            if (api_is_allowed_to_edit(false, true) && count($group_list) > 1) {
                 $row[] = $this_group['id'];
             }
 
@@ -1994,7 +2006,7 @@ class GroupManager
                     self::user_has_access($user_id, $this_group['id'], self::GROUP_TOOL_ANNOUNCEMENT) ||
                     self::user_has_access($user_id, $this_group['id'], self::GROUP_TOOL_WORK) ||
                     self::user_has_access($user_id, $this_group['id'], self::GROUP_TOOL_WIKI))
-                    && !(api_is_course_coach() && intval($this_group['session_id']) != $session_id)
+                && !(api_is_course_coach() && intval($this_group['session_id']) != $session_id)
             ) {
 
                 $group_name = '<a href="group_space.php?cidReq='.api_get_course_id().'&amp;origin='.$orig.'&amp;gidReq='.$this_group['id'].'">'.

+ 18 - 0
main/inc/lib/javascript/select2/LICENSE

@@ -0,0 +1,18 @@
+Copyright 2012 Igor Vaynberg
+
+Version: @@ver@@ Timestamp: @@timestamp@@
+
+This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU
+General Public License version 2 (the "GPL License"). You may choose either license to govern your
+use of this software only upon the condition that you accept all of the terms of either the Apache
+License or the GPL License.
+
+You may obtain a copy of the Apache License and the GPL License at:
+
+http://www.apache.org/licenses/LICENSE-2.0
+http://www.gnu.org/licenses/gpl-2.0.html
+
+Unless required by applicable law or agreed to in writing, software distributed under the Apache License
+or the GPL Licesnse is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+either express or implied. See the Apache License and the GPL License for the specific language governing
+permissions and limitations under the Apache License and the GPL License.

+ 83 - 0
main/inc/lib/javascript/select2/README.md

@@ -0,0 +1,83 @@
+Select2
+=======
+
+Select2 is a jQuery-based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results.
+
+To get started, checkout examples and documentation at http://ivaynberg.github.com/select2
+
+Use cases
+---------
+
+* Enhancing native selects with search.
+* Enhancing native selects with a better multi-select interface.
+* Loading data from JavaScript: easily load items via ajax and have them searchable.
+* Nesting optgroups: native selects only support one level of nested. Select2 does not have this restriction.
+* Tagging: ability to add new items on the fly.
+* Working with large, remote datasets: ability to partially load a dataset based on the search term.
+* Paging of large datasets: easy support for loading more pages when the results are scrolled to the end.
+* Templating: support for custom rendering of results and selections.
+
+Browser compatibility
+---------------------
+* IE 8+
+* Chrome 8+
+* Firefox 10+
+* Safari 3+
+* Opera 10.6+
+
+Integrations
+------------
+
+* [Wicket-Select2](https://github.com/ivaynberg/wicket-select2) (Java / [Apache Wicket](http://wicket.apache.org))
+* [select2-rails](https://github.com/argerim/select2-rails) (Ruby on Rails)
+* [AngularUI](http://angular-ui.github.com/#directives-select2) ([AngularJS](angularjs.org))
+* [Django](https://github.com/applegrew/django-select2)
+* [Symfony](https://github.com/19Gerhard85/sfSelect2WidgetsPlugin)
+* [Bootstrap](https://github.com/t0m/select2-bootstrap-css) (CSS skin)
+* [Yii](https://github.com/tonybolzan/yii-select2)
+
+Internationalization (i18n)
+---------------------------
+
+Select2 supports multiple languages by simply including the right
+language JS file (`select2_locale_it.js`, `select2_locale_nl.js`, etc.).
+
+Missing a language? Just copy `select2_locale_en.js.template`, translate
+it, and make a pull request back to Select2 here on GitHub.
+
+Bug tracker
+-----------
+
+Have a bug? Please create an issue here on GitHub!
+
+https://github.com/ivaynberg/select2/issues
+
+Mailing list
+------------
+
+Have a question? Ask on our mailing list!
+
+select2@googlegroups.com
+
+https://groups.google.com/d/forum/select2
+
+
+Copyright and license
+---------------------
+
+Copyright 2012 Igor Vaynberg
+
+This software is licensed under the Apache License, Version 2.0 (the "Apache License") or the GNU
+General Public License version 2 (the "GPL License"). You may choose either license to govern your
+use of this software only upon the condition that you accept all of the terms of either the Apache
+License or the GPL License.
+
+You may obtain a copy of the Apache License and the GPL License in the LICENSE file, or at:
+
+http://www.apache.org/licenses/LICENSE-2.0
+http://www.gnu.org/licenses/gpl-2.0.html
+
+Unless required by applicable law or agreed to in writing, software distributed under the Apache License
+or the GPL License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+either express or implied. See the Apache License and the GPL License for the specific language governing
+permissions and limitations under the Apache License and the GPL License.

+ 8 - 0
main/inc/lib/javascript/select2/bower.json

@@ -0,0 +1,8 @@
+{
+    "name": "select2",
+    "version": "3.4.5",
+    "main": ["select2.js", "select2.css", "select2.png", "select2x2.png", "select2-spinner.gif"],
+    "dependencies": {
+        "jquery": ">= 1.7.1"
+    }
+}

+ 87 - 0
main/inc/lib/javascript/select2/select2-bootstrap.css

@@ -0,0 +1,87 @@
+.form-control .select2-choice {
+    border: 0;
+    border-radius: 2px;
+}
+
+.form-control .select2-choice .select2-arrow {
+    border-radius: 0 2px 2px 0;   
+}
+
+.form-control.select2-container {
+    height: auto !important;
+    padding: 0px;
+}
+
+.form-control.select2-container.select2-dropdown-open {
+    border-color: #5897FB;
+    border-radius: 3px 3px 0 0;
+}
+
+.form-control .select2-container.select2-dropdown-open .select2-choices {
+    border-radius: 3px 3px 0 0;
+}
+
+.form-control.select2-container .select2-choices {
+    border: 0 !important;
+    border-radius: 3px;
+}
+
+.control-group.warning .select2-container .select2-choice,
+.control-group.warning .select2-container .select2-choices,
+.control-group.warning .select2-container-active .select2-choice,
+.control-group.warning .select2-container-active .select2-choices,
+.control-group.warning .select2-dropdown-open.select2-drop-above .select2-choice,
+.control-group.warning .select2-dropdown-open.select2-drop-above .select2-choices,
+.control-group.warning .select2-container-multi.select2-container-active .select2-choices {
+    border: 1px solid #C09853 !important;
+}
+
+.control-group.warning .select2-container .select2-choice div {
+    border-left: 1px solid #C09853 !important;
+    background: #FCF8E3 !important;
+}
+
+.control-group.error .select2-container .select2-choice,
+.control-group.error .select2-container .select2-choices,
+.control-group.error .select2-container-active .select2-choice,
+.control-group.error .select2-container-active .select2-choices,
+.control-group.error .select2-dropdown-open.select2-drop-above .select2-choice,
+.control-group.error .select2-dropdown-open.select2-drop-above .select2-choices,
+.control-group.error .select2-container-multi.select2-container-active .select2-choices {
+    border: 1px solid #B94A48 !important;
+}
+
+.control-group.error .select2-container .select2-choice div {
+    border-left: 1px solid #B94A48 !important;
+    background: #F2DEDE !important;
+}
+
+.control-group.info .select2-container .select2-choice,
+.control-group.info .select2-container .select2-choices,
+.control-group.info .select2-container-active .select2-choice,
+.control-group.info .select2-container-active .select2-choices,
+.control-group.info .select2-dropdown-open.select2-drop-above .select2-choice,
+.control-group.info .select2-dropdown-open.select2-drop-above .select2-choices,
+.control-group.info .select2-container-multi.select2-container-active .select2-choices {
+    border: 1px solid #3A87AD !important;
+}
+
+.control-group.info .select2-container .select2-choice div {
+    border-left: 1px solid #3A87AD !important;
+    background: #D9EDF7 !important;
+}
+
+.control-group.success .select2-container .select2-choice,
+.control-group.success .select2-container .select2-choices,
+.control-group.success .select2-container-active .select2-choice,
+.control-group.success .select2-container-active .select2-choices,
+.control-group.success .select2-dropdown-open.select2-drop-above .select2-choice,
+.control-group.success .select2-dropdown-open.select2-drop-above .select2-choices,
+.control-group.success .select2-container-multi.select2-container-active .select2-choices {
+    border: 1px solid #468847 !important;
+}
+
+.control-group.success .select2-container .select2-choice div {
+    border-left: 1px solid #468847 !important;
+    background: #DFF0D8 !important;
+}

BIN
main/inc/lib/javascript/select2/select2-spinner.gif


+ 615 - 0
main/inc/lib/javascript/select2/select2.css

@@ -0,0 +1,615 @@
+/*
+Version: 3.4.5 Timestamp: Mon Nov  4 08:22:42 PST 2013
+*/
+.select2-container {
+    margin: 0;
+    position: relative;
+    display: inline-block;
+    /* inline-block for ie7 */
+    zoom: 1;
+    *display: inline;
+    vertical-align: middle;
+}
+
+.select2-container,
+.select2-drop,
+.select2-search,
+.select2-search input {
+  /*
+    Force border-box so that % widths fit the parent
+    container without overlap because of margin/padding.
+
+    More Info : http://www.quirksmode.org/css/box.html
+  */
+  -webkit-box-sizing: border-box; /* webkit */
+     -moz-box-sizing: border-box; /* firefox */
+          box-sizing: border-box; /* css3 */
+}
+
+.select2-container .select2-choice {
+    display: block;
+    height: 26px;
+    padding: 0 0 0 8px;
+    overflow: hidden;
+    position: relative;
+
+    border: 1px solid #aaa;
+    white-space: nowrap;
+    line-height: 26px;
+    color: #444;
+    text-decoration: none;
+
+    border-radius: 4px;
+
+    background-clip: padding-box;
+
+    -webkit-touch-callout: none;
+      -webkit-user-select: none;
+         -moz-user-select: none;
+          -ms-user-select: none;
+              user-select: none;
+
+    background-color: #fff;
+    background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eee), color-stop(0.5, #fff));
+    background-image: -webkit-linear-gradient(center bottom, #eee 0%, #fff 50%);
+    background-image: -moz-linear-gradient(center bottom, #eee 0%, #fff 50%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#ffffff', endColorstr = '#eeeeee', GradientType = 0);
+    background-image: linear-gradient(top, #fff 0%, #eee 50%);
+}
+
+.select2-container.select2-drop-above .select2-choice {
+    border-bottom-color: #aaa;
+
+    border-radius: 0 0 4px 4px;
+
+    background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eee), color-stop(0.9, #fff));
+    background-image: -webkit-linear-gradient(center bottom, #eee 0%, #fff 90%);
+    background-image: -moz-linear-gradient(center bottom, #eee 0%, #fff 90%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#eeeeee', GradientType=0);
+    background-image: linear-gradient(top, #eee 0%, #fff 90%);
+}
+
+.select2-container.select2-allowclear .select2-choice .select2-chosen {
+    margin-right: 42px;
+}
+
+.select2-container .select2-choice > .select2-chosen {
+    margin-right: 26px;
+    display: block;
+    overflow: hidden;
+
+    white-space: nowrap;
+
+    text-overflow: ellipsis;
+}
+
+.select2-container .select2-choice abbr {
+    display: none;
+    width: 12px;
+    height: 12px;
+    position: absolute;
+    right: 24px;
+    top: 8px;
+
+    font-size: 1px;
+    text-decoration: none;
+
+    border: 0;
+    background: url('select2.png') right top no-repeat;
+    cursor: pointer;
+    outline: 0;
+}
+
+.select2-container.select2-allowclear .select2-choice abbr {
+    display: inline-block;
+}
+
+.select2-container .select2-choice abbr:hover {
+    background-position: right -11px;
+    cursor: pointer;
+}
+
+.select2-drop-mask {
+    border: 0;
+    margin: 0;
+    padding: 0;
+    position: fixed;
+    left: 0;
+    top: 0;
+    min-height: 100%;
+    min-width: 100%;
+    height: auto;
+    width: auto;
+    opacity: 0;
+    z-index: 9998;
+    /* styles required for IE to work */
+    background-color: #fff;
+    filter: alpha(opacity=0);
+}
+
+.select2-drop {
+    width: 100%;
+    margin-top: -1px;
+    position: absolute;
+    z-index: 9999;
+    top: 100%;
+
+    background: #fff;
+    color: #000;
+    border: 1px solid #aaa;
+    border-top: 0;
+
+    border-radius: 0 0 4px 4px;
+
+    -webkit-box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
+            box-shadow: 0 4px 5px rgba(0, 0, 0, .15);
+}
+
+.select2-drop-auto-width {
+    border-top: 1px solid #aaa;
+    width: auto;
+}
+
+.select2-drop-auto-width .select2-search {
+    padding-top: 4px;
+}
+
+.select2-drop.select2-drop-above {
+    margin-top: 1px;
+    border-top: 1px solid #aaa;
+    border-bottom: 0;
+
+    border-radius: 4px 4px 0 0;
+
+    -webkit-box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
+            box-shadow: 0 -4px 5px rgba(0, 0, 0, .15);
+}
+
+.select2-drop-active {
+    border: 1px solid #5897fb;
+    border-top: none;
+}
+
+.select2-drop.select2-drop-above.select2-drop-active {
+    border-top: 1px solid #5897fb;
+}
+
+.select2-container .select2-choice .select2-arrow {
+    display: inline-block;
+    width: 18px;
+    height: 100%;
+    position: absolute;
+    right: 0;
+    top: 0;
+
+    border-left: 1px solid #aaa;
+    border-radius: 0 4px 4px 0;
+
+    background-clip: padding-box;
+
+    background: #ccc;
+    background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee));
+    background-image: -webkit-linear-gradient(center bottom, #ccc 0%, #eee 60%);
+    background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr = '#eeeeee', endColorstr = '#cccccc', GradientType = 0);
+    background-image: linear-gradient(top, #ccc 0%, #eee 60%);
+}
+
+.select2-container .select2-choice .select2-arrow b {
+    display: block;
+    width: 100%;
+    height: 100%;
+    background: url('select2.png') no-repeat 0 1px;
+}
+
+.select2-search {
+    display: inline-block;
+    width: 100%;
+    min-height: 26px;
+    margin: 0;
+    padding-left: 4px;
+    padding-right: 4px;
+
+    position: relative;
+    z-index: 10000;
+
+    white-space: nowrap;
+}
+
+.select2-search input {
+    width: 100%;
+    height: auto !important;
+    min-height: 26px;
+    padding: 4px 20px 4px 5px;
+    margin: 0;
+
+    outline: 0;
+    font-family: sans-serif;
+    font-size: 1em;
+
+    border: 1px solid #aaa;
+    border-radius: 0;
+
+    -webkit-box-shadow: none;
+            box-shadow: none;
+
+    background: #fff url('select2.png') no-repeat 100% -22px;
+    background: url('select2.png') no-repeat 100% -22px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee));
+    background: url('select2.png') no-repeat 100% -22px, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%);
+    background: url('select2.png') no-repeat 100% -22px, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%);
+    background: url('select2.png') no-repeat 100% -22px, linear-gradient(top, #fff 85%, #eee 99%);
+}
+
+.select2-drop.select2-drop-above .select2-search input {
+    margin-top: 4px;
+}
+
+.select2-search input.select2-active {
+    background: #fff url('select2-spinner.gif') no-repeat 100%;
+    background: url('select2-spinner.gif') no-repeat 100%, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, #fff), color-stop(0.99, #eee));
+    background: url('select2-spinner.gif') no-repeat 100%, -webkit-linear-gradient(center bottom, #fff 85%, #eee 99%);
+    background: url('select2-spinner.gif') no-repeat 100%, -moz-linear-gradient(center bottom, #fff 85%, #eee 99%);
+    background: url('select2-spinner.gif') no-repeat 100%, linear-gradient(top, #fff 85%, #eee 99%);
+}
+
+.select2-container-active .select2-choice,
+.select2-container-active .select2-choices {
+    border: 1px solid #5897fb;
+    outline: none;
+
+    -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .3);
+            box-shadow: 0 0 5px rgba(0, 0, 0, .3);
+}
+
+.select2-dropdown-open .select2-choice {
+    border-bottom-color: transparent;
+    -webkit-box-shadow: 0 1px 0 #fff inset;
+            box-shadow: 0 1px 0 #fff inset;
+
+    border-bottom-left-radius: 0;
+    border-bottom-right-radius: 0;
+
+    background-color: #eee;
+    background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #fff), color-stop(0.5, #eee));
+    background-image: -webkit-linear-gradient(center bottom, #fff 0%, #eee 50%);
+    background-image: -moz-linear-gradient(center bottom, #fff 0%, #eee 50%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0);
+    background-image: linear-gradient(top, #fff 0%, #eee 50%);
+}
+
+.select2-dropdown-open.select2-drop-above .select2-choice,
+.select2-dropdown-open.select2-drop-above .select2-choices {
+    border: 1px solid #5897fb;
+    border-top-color: transparent;
+
+    background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #fff), color-stop(0.5, #eee));
+    background-image: -webkit-linear-gradient(center top, #fff 0%, #eee 50%);
+    background-image: -moz-linear-gradient(center top, #fff 0%, #eee 50%);
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0);
+    background-image: linear-gradient(bottom, #fff 0%, #eee 50%);
+}
+
+.select2-dropdown-open .select2-choice .select2-arrow {
+    background: transparent;
+    border-left: none;
+    filter: none;
+}
+.select2-dropdown-open .select2-choice .select2-arrow b {
+    background-position: -18px 1px;
+}
+
+/* results */
+.select2-results {
+    max-height: 200px;
+    padding: 0 0 0 4px;
+    margin: 4px 4px 4px 0;
+    position: relative;
+    overflow-x: hidden;
+    overflow-y: auto;
+    -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+
+.select2-results ul.select2-result-sub {
+    margin: 0;
+    padding-left: 0;
+}
+
+.select2-results ul.select2-result-sub > li .select2-result-label { padding-left: 20px }
+.select2-results ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 40px }
+.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 60px }
+.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 80px }
+.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 100px }
+.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 110px }
+.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub > li .select2-result-label { padding-left: 120px }
+
+.select2-results li {
+    list-style: none;
+    display: list-item;
+    background-image: none;
+}
+
+.select2-results li.select2-result-with-children > .select2-result-label {
+    font-weight: bold;
+}
+
+.select2-results .select2-result-label {
+    padding: 3px 7px 4px;
+    margin: 0;
+    cursor: pointer;
+
+    min-height: 1em;
+
+    -webkit-touch-callout: none;
+      -webkit-user-select: none;
+         -moz-user-select: none;
+          -ms-user-select: none;
+              user-select: none;
+}
+
+.select2-results .select2-highlighted {
+    background: #3875d7;
+    color: #fff;
+}
+
+.select2-results li em {
+    background: #feffde;
+    font-style: normal;
+}
+
+.select2-results .select2-highlighted em {
+    background: transparent;
+}
+
+.select2-results .select2-highlighted ul {
+    background: #fff;
+    color: #000;
+}
+
+
+.select2-results .select2-no-results,
+.select2-results .select2-searching,
+.select2-results .select2-selection-limit {
+    background: #f4f4f4;
+    display: list-item;
+}
+
+/*
+disabled look for disabled choices in the results dropdown
+*/
+.select2-results .select2-disabled.select2-highlighted {
+    color: #666;
+    background: #f4f4f4;
+    display: list-item;
+    cursor: default;
+}
+.select2-results .select2-disabled {
+  background: #f4f4f4;
+  display: list-item;
+  cursor: default;
+}
+
+.select2-results .select2-selected {
+    display: none;
+}
+
+.select2-more-results.select2-active {
+    background: #f4f4f4 url('select2-spinner.gif') no-repeat 100%;
+}
+
+.select2-more-results {
+    background: #f4f4f4;
+    display: list-item;
+}
+
+/* disabled styles */
+
+.select2-container.select2-container-disabled .select2-choice {
+    background-color: #f4f4f4;
+    background-image: none;
+    border: 1px solid #ddd;
+    cursor: default;
+}
+
+.select2-container.select2-container-disabled .select2-choice .select2-arrow {
+    background-color: #f4f4f4;
+    background-image: none;
+    border-left: 0;
+}
+
+.select2-container.select2-container-disabled .select2-choice abbr {
+    display: none;
+}
+
+
+/* multiselect */
+
+.select2-container-multi .select2-choices {
+    height: auto !important;
+    height: 1%;
+    margin: 0;
+    padding: 0;
+    position: relative;
+
+    border: 1px solid #aaa;
+    cursor: text;
+    overflow: hidden;
+
+    background-color: #fff;
+    background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(1%, #eee), color-stop(15%, #fff));
+    background-image: -webkit-linear-gradient(top, #eee 1%, #fff 15%);
+    background-image: -moz-linear-gradient(top, #eee 1%, #fff 15%);
+    background-image: linear-gradient(top, #eee 1%, #fff 15%);
+}
+
+.select2-locked {
+  padding: 3px 5px 3px 5px !important;
+}
+
+.select2-container-multi .select2-choices {
+    min-height: 26px;
+}
+
+.select2-container-multi.select2-container-active .select2-choices {
+    border: 1px solid #5897fb;
+    outline: none;
+
+    -webkit-box-shadow: 0 0 5px rgba(0, 0, 0, .3);
+            box-shadow: 0 0 5px rgba(0, 0, 0, .3);
+}
+.select2-container-multi .select2-choices li {
+    float: left;
+    list-style: none;
+}
+.select2-container-multi .select2-choices .select2-search-field {
+    margin: 0;
+    padding: 0;
+    white-space: nowrap;
+}
+
+.select2-container-multi .select2-choices .select2-search-field input {
+    padding: 5px;
+    margin: 1px 0;
+
+    font-family: sans-serif;
+    font-size: 100%;
+    color: #666;
+    outline: 0;
+    border: 0;
+    -webkit-box-shadow: none;
+            box-shadow: none;
+    background: transparent !important;
+}
+
+.select2-container-multi .select2-choices .select2-search-field input.select2-active {
+    background: #fff url('select2-spinner.gif') no-repeat 100% !important;
+}
+
+.select2-default {
+    color: #999 !important;
+}
+
+.select2-container-multi .select2-choices .select2-search-choice {
+    padding: 3px 5px 3px 18px;
+    margin: 3px 0 3px 5px;
+    position: relative;
+
+    line-height: 13px;
+    color: #333;
+    cursor: default;
+    border: 1px solid #aaaaaa;
+
+    border-radius: 3px;
+
+    -webkit-box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05);
+            box-shadow: 0 0 2px #fff inset, 0 1px 0 rgba(0, 0, 0, 0.05);
+
+    background-clip: padding-box;
+
+    -webkit-touch-callout: none;
+      -webkit-user-select: none;
+         -moz-user-select: none;
+          -ms-user-select: none;
+              user-select: none;
+
+    background-color: #e4e4e4;
+    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eeeeee', endColorstr='#f4f4f4', GradientType=0);
+    background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(20%, #f4f4f4), color-stop(50%, #f0f0f0), color-stop(52%, #e8e8e8), color-stop(100%, #eee));
+    background-image: -webkit-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
+    background-image: -moz-linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
+    background-image: linear-gradient(top, #f4f4f4 20%, #f0f0f0 50%, #e8e8e8 52%, #eee 100%);
+}
+.select2-container-multi .select2-choices .select2-search-choice .select2-chosen {
+    cursor: default;
+}
+.select2-container-multi .select2-choices .select2-search-choice-focus {
+    background: #d4d4d4;
+}
+
+.select2-search-choice-close {
+    display: block;
+    width: 12px;
+    height: 13px;
+    position: absolute;
+    right: 3px;
+    top: 4px;
+
+    font-size: 1px;
+    outline: none;
+    background: url('select2.png') right top no-repeat;
+}
+
+.select2-container-multi .select2-search-choice-close {
+    left: 3px;
+}
+
+.select2-container-multi .select2-choices .select2-search-choice .select2-search-choice-close:hover {
+  background-position: right -11px;
+}
+.select2-container-multi .select2-choices .select2-search-choice-focus .select2-search-choice-close {
+    background-position: right -11px;
+}
+
+/* disabled styles */
+.select2-container-multi.select2-container-disabled .select2-choices {
+    background-color: #f4f4f4;
+    background-image: none;
+    border: 1px solid #ddd;
+    cursor: default;
+}
+
+.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice {
+    padding: 3px 5px 3px 5px;
+    border: 1px solid #ddd;
+    background-image: none;
+    background-color: #f4f4f4;
+}
+
+.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close {    display: none;
+    background: none;
+}
+/* end multiselect */
+
+
+.select2-result-selectable .select2-match,
+.select2-result-unselectable .select2-match {
+    text-decoration: underline;
+}
+
+.select2-offscreen, .select2-offscreen:focus {
+    clip: rect(0 0 0 0) !important;
+    width: 1px !important;
+    height: 1px !important;
+    border: 0 !important;
+    margin: 0 !important;
+    padding: 0 !important;
+    overflow: hidden !important;
+    position: absolute !important;
+    outline: 0 !important;
+    left: 0px !important;
+    top: 0px !important;
+}
+
+.select2-display-none {
+    display: none;
+}
+
+.select2-measure-scrollbar {
+    position: absolute;
+    top: -10000px;
+    left: -10000px;
+    width: 100px;
+    height: 100px;
+    overflow: scroll;
+}
+/* Retina-ize icons */
+
+@media only screen and (-webkit-min-device-pixel-ratio: 1.5), only screen and (min-resolution: 144dpi)  {
+  .select2-search input, .select2-search-choice-close, .select2-container .select2-choice abbr, .select2-container .select2-choice .select2-arrow b {
+      background-image: url('select2x2.png') !important;
+      background-repeat: no-repeat !important;
+      background-size: 60px 40px !important;
+  }
+  .select2-search input {
+      background-position: 100% -21px !important;
+  }
+}

+ 36 - 0
main/inc/lib/javascript/select2/select2.jquery.json

@@ -0,0 +1,36 @@
+{
+    "name": "select2",
+    "title": "Select2",
+    "description": "Select2 is a jQuery based replacement for select boxes. It supports searching, remote data sets, and infinite scrolling of results.",
+    "keywords": [
+        "select",
+        "autocomplete",
+        "typeahead",
+        "dropdown",
+        "multiselect",
+        "tag",
+        "tagging"
+    ],
+    "version": "3.4.5",
+    "author": {
+        "name": "Igor Vaynberg",
+        "url": "https://github.com/ivaynberg"
+    },
+    "licenses": [
+        {
+            "type": "Apache",
+            "url": "http://www.apache.org/licenses/LICENSE-2.0"
+        },
+        {
+            "type": "GPL v2",
+            "url": "http://www.gnu.org/licenses/gpl-2.0.html"
+        }
+    ],
+    "bugs": "https://github.com/ivaynberg/select2/issues",
+    "homepage": "http://ivaynberg.github.com/select2",
+    "docs": "http://ivaynberg.github.com/select2/",
+    "download": "https://github.com/ivaynberg/select2/tags",
+    "dependencies": {
+        "jquery": ">=1.7.1"
+    }
+}

File diff suppressed because it is too large
+ 100 - 0
main/inc/lib/javascript/select2/select2.js


File diff suppressed because it is too large
+ 20 - 0
main/inc/lib/javascript/select2/select2.min.js


BIN
main/inc/lib/javascript/select2/select2.png


+ 17 - 0
main/inc/lib/javascript/select2/select2_locale_ar.js

@@ -0,0 +1,17 @@
+/**
+ * Select2 Arabic translation.
+ * 
+ * Author: Your Name <amedhat3@gmail.com>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "لا توجد نتائج"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "من فضلك أدخل " + n + " حروف أكثر"; },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "من فضلك أحذف  " + n + " حروف"; },
+        formatSelectionTooBig: function (limit) { return "يمكنك ان تختار " + limit + " أختيارات فقط"; },
+        formatLoadMore: function (pageNumber) { return "تحمل المذيد من النتائج ..."; },
+        formatSearching: function () { return "جاري البحث ..."; }
+    });
+})(jQuery);

+ 17 - 0
main/inc/lib/javascript/select2/select2_locale_bg.js

@@ -0,0 +1,17 @@
+/**
+ * Select2 <Language> translation.
+ * 
+ * Author: Lubomir Vikev <lubomirvikev@gmail.com>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Няма намерени съвпадения"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Моля въведете още " + n + " символ" + (n == 1 ? "" : "а"); },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Моля въведете с " + n + " по-малко символ" + (n == 1? "" : "а"); },
+        formatSelectionTooBig: function (limit) { return "Можете да направите до " + limit + (limit == 1 ? " избор" : " избора"); },
+        formatLoadMore: function (pageNumber) { return "Зареждат се още..."; },
+        formatSearching: function () { return "Търсене..."; }
+    });
+})(jQuery);

+ 17 - 0
main/inc/lib/javascript/select2/select2_locale_ca.js

@@ -0,0 +1,17 @@
+/**
+ * Select2 Catalan translation.
+ * 
+ * Author: David Planella <david.planella@gmail.com>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "No s'ha trobat cap coincidència"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Introduïu " + n + " caràcter" + (n == 1 ? "" : "s") + " més"; },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Introduïu " + n + " caràcter" + (n == 1? "" : "s") + "menys"; },
+        formatSelectionTooBig: function (limit) { return "Només podeu seleccionar " + limit + " element" + (limit == 1 ? "" : "s"); },
+        formatLoadMore: function (pageNumber) { return "S'estan carregant més resultats..."; },
+        formatSearching: function () { return "S'està cercant..."; }
+    });
+})(jQuery);

+ 49 - 0
main/inc/lib/javascript/select2/select2_locale_cs.js

@@ -0,0 +1,49 @@
+/**
+ * Select2 Czech translation.
+ * 
+ * Author: Michal Marek <ahoj@michal-marek.cz>
+ * Author - sklonovani: David Vallner <david@vallner.net>
+ */
+(function ($) {
+    "use strict";
+    // use text for the numbers 2 through 4
+    var smallNumbers = {
+        2: function(masc) { return (masc ? "dva" : "dvě"); },
+        3: function() { return "tři"; },
+        4: function() { return "čtyři"; }
+    }
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Nenalezeny žádné položky"; },
+        formatInputTooShort: function (input, min) {
+            var n = min - input.length;
+            if (n == 1) {
+                return "Prosím zadejte ještě jeden znak";
+            } else if (n <= 4) {
+                return "Prosím zadejte ještě další "+smallNumbers[n](true)+" znaky";
+            } else {
+                return "Prosím zadejte ještě dalších "+n+" znaků";
+            }
+        },
+        formatInputTooLong: function (input, max) {
+            var n = input.length - max;
+            if (n == 1) {
+                return "Prosím zadejte o jeden znak méně";
+            } else if (n <= 4) {
+                return "Prosím zadejte o "+smallNumbers[n](true)+" znaky méně";
+            } else {
+                return "Prosím zadejte o "+n+" znaků méně";
+            }
+        },
+        formatSelectionTooBig: function (limit) {
+            if (limit == 1) {
+                return "Můžete zvolit jen jednu položku";
+            } else if (limit <= 4) {
+                return "Můžete zvolit maximálně "+smallNumbers[limit](false)+" položky";
+            } else {
+                return "Můžete zvolit maximálně "+limit+" položek";
+            }
+        },
+        formatLoadMore: function (pageNumber) { return "Načítají se další výsledky..."; },
+        formatSearching: function () { return "Vyhledávání..."; }
+    });
+})(jQuery);

+ 17 - 0
main/inc/lib/javascript/select2/select2_locale_da.js

@@ -0,0 +1,17 @@
+/**
+ * Select2 Danish translation.
+ *
+ * Author: Anders Jenbo <anders@jenbo.dk>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Ingen resultater fundet"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Angiv venligst " + n + " tegn mere"; },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Angiv venligst " + n + " tegn mindre"; },
+        formatSelectionTooBig: function (limit) { return "Du kan kun vælge " + limit + " emne" + (limit === 1 ? "" : "r"); },
+        formatLoadMore: function (pageNumber) { return "Indlæser flere resultater…"; },
+        formatSearching: function () { return "Søger…"; }
+    });
+})(jQuery);

+ 15 - 0
main/inc/lib/javascript/select2/select2_locale_de.js

@@ -0,0 +1,15 @@
+/**
+ * Select2 German translation
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Keine Übereinstimmungen gefunden"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Bitte " + n + " Zeichen mehr eingeben"; },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Bitte " + n + " Zeichen weniger eingeben"; },
+        formatSelectionTooBig: function (limit) { return "Sie können nur " + limit + " Eintr" + (limit === 1 ? "ag" : "äge") + " auswählen"; },
+        formatLoadMore: function (pageNumber) { return "Lade mehr Ergebnisse..."; },
+        formatSearching: function () { return "Suche..."; }
+    });
+})(jQuery);

+ 17 - 0
main/inc/lib/javascript/select2/select2_locale_el.js

@@ -0,0 +1,17 @@
+/**
+ * Select2 <Language> translation.
+ * 
+ * Author: Your Name <your@email>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Δεν βρέθηκαν αποτελέσματα"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Παρακαλούμε εισάγετε " + n + " περισσότερο" + (n == 1 ? "" : "υς") + " χαρακτήρ" + (n == 1 ? "α" : "ες"); },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Παρακαλούμε διαγράψτε " + n + " χαρακτήρ" + (n == 1 ? "α" : "ες"); },
+        formatSelectionTooBig: function (limit) { return "Μπορείτε να επιλέξετε μόνο " + limit + " αντικείμεν" + (limit == 1 ? "ο" : "α"); },
+        formatLoadMore: function (pageNumber) { return "Φόρτωση περισσότερων..."; },
+        formatSearching: function () { return "Αναζήτηση..."; }
+    });
+})(jQuery);

+ 17 - 0
main/inc/lib/javascript/select2/select2_locale_en.js.template

@@ -0,0 +1,17 @@
+/**
+ * Select2 <Language> translation.
+ * 
+ * Author: Your Name <your@email>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "No matches found"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Please enter " + n + " more character" + (n == 1 ? "" : "s"); },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Please delete " + n + " character" + (n == 1 ? "" : "s"); },
+        formatSelectionTooBig: function (limit) { return "You can only select " + limit + " item" + (limit == 1 ? "" : "s"); },
+        formatLoadMore: function (pageNumber) { return "Loading more results..."; },
+        formatSearching: function () { return "Searching..."; }
+    });
+})(jQuery);

+ 15 - 0
main/inc/lib/javascript/select2/select2_locale_es.js

@@ -0,0 +1,15 @@
+/**
+ * Select2 Spanish translation
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "No se encontraron resultados"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Por favor, introduzca " + n + " car" + (n == 1? "á" : "a") + "cter" + (n == 1? "" : "es"); },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Por favor, elimine " + n + " car" + (n == 1? "á" : "a") + "cter" + (n == 1? "" : "es"); },
+        formatSelectionTooBig: function (limit) { return "Sólo puede seleccionar " + limit + " elemento" + (limit == 1 ? "" : "s"); },
+        formatLoadMore: function (pageNumber) { return "Cargando más resultados..."; },
+        formatSearching: function () { return "Buscando..."; }
+    });
+})(jQuery);

+ 17 - 0
main/inc/lib/javascript/select2/select2_locale_et.js

@@ -0,0 +1,17 @@
+/**
+ * Select2 Estonian translation.
+ *
+ * Author: Kuldar Kalvik <kuldar@kalvik.ee>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Tulemused puuduvad"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Sisesta " + n + " täht" + (n == 1 ? "" : "e") + " rohkem"; },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Sisesta " + n + " täht" + (n == 1? "" : "e") + " vähem"; },
+        formatSelectionTooBig: function (limit) { return "Saad vaid " + limit + " tulemus" + (limit == 1 ? "e" : "t") + " valida"; },
+        formatLoadMore: function (pageNumber) { return "Laen tulemusi.."; },
+        formatSearching: function () { return "Otsin.."; }
+    });
+})(jQuery);

+ 43 - 0
main/inc/lib/javascript/select2/select2_locale_eu.js

@@ -0,0 +1,43 @@
+/**
+ * Select2 Basque translation.
+ *
+ * Author: Julen Ruiz Aizpuru <julenx at gmail dot com>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () {
+          return "Ez da bat datorrenik aurkitu";
+        },
+        formatInputTooShort: function (input, min) {
+          var n = min - input.length;
+          if (n === 1) {
+            return "Idatzi karaktere bat gehiago";
+          } else {
+            return "Idatzi " + n + " karaktere gehiago";
+          }
+        },
+        formatInputTooLong: function (input, max) {
+          var n = input.length - max;
+          if (n === 1) {
+            return "Idatzi karaktere bat gutxiago";
+          } else {
+            return "Idatzi " + n + " karaktere gutxiago";
+          }
+        },
+        formatSelectionTooBig: function (limit) {
+          if (limit === 1 ) {
+            return "Elementu bakarra hauta dezakezu";
+          } else {
+            return limit + " elementu hauta ditzakezu soilik";
+          }
+        },
+        formatLoadMore: function (pageNumber) {
+          return "Emaitza gehiago kargatzen...";
+        },
+        formatSearching: function () {
+          return "Bilatzen...";
+        }
+    });
+})(jQuery);

+ 17 - 0
main/inc/lib/javascript/select2/select2_locale_fa.js

@@ -0,0 +1,17 @@
+/**
+ * Select2 <fa> translation.
+ * 
+ * Author: Ali Choopan <choopan@arsh.co>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "نتیجه‌ای یافت نشد."; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return " لطفا بیش از"+n+"کاراکتر وارد نمایید "; },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return " لطفا" + n + " کاراکتر را حذف کنید."; },
+        formatSelectionTooBig: function (limit) { return "شما فقط می‌توانید " + limit + " مورد را انتخاب کنید"; },
+        formatLoadMore: function (pageNumber) { return "در حال بارگذاری موارد بیشتر ..."; },
+        formatSearching: function () { return "در حال جستجو"; }
+    });
+})(jQuery);

+ 28 - 0
main/inc/lib/javascript/select2/select2_locale_fi.js

@@ -0,0 +1,28 @@
+/**
+ * Select2 Finnish translation
+ */
+(function ($) {
+    "use strict";
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () {
+            return "Ei tuloksia";
+        },
+        formatInputTooShort: function (input, min) {
+            var n = min - input.length;
+            return "Ole hyvä ja anna " + n + " merkkiä lisää";
+        },
+        formatInputTooLong: function (input, max) {
+            var n = input.length - max;
+            return "Ole hyvä ja anna " + n + " merkkiä vähemmän";
+        },
+        formatSelectionTooBig: function (limit) {
+            return "Voit valita ainoastaan " + limit + " kpl";
+        },
+        formatLoadMore: function (pageNumber) {
+            return "Ladataan lisää tuloksia...";
+        },
+        formatSearching: function () {
+            return "Etsitään...";
+        }
+    });
+})(jQuery);

+ 15 - 0
main/inc/lib/javascript/select2/select2_locale_fr.js

@@ -0,0 +1,15 @@
+/**
+ * Select2 French translation
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Aucun résultat trouvé"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Merci de saisir " + n + " caractère" + (n == 1? "" : "s") + " de plus"; },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Merci de supprimer " + n + " caractère" + (n == 1? "" : "s"); },
+        formatSelectionTooBig: function (limit) { return "Vous pouvez seulement sélectionner " + limit + " élément" + (limit == 1 ? "" : "s"); },
+        formatLoadMore: function (pageNumber) { return "Chargement de résultats supplémentaires..."; },
+        formatSearching: function () { return "Recherche en cours..."; }
+    });
+})(jQuery);

+ 43 - 0
main/inc/lib/javascript/select2/select2_locale_gl.js

@@ -0,0 +1,43 @@
+/**
+ * Select2 Galician translation
+ * 
+ * Author: Leandro Regueiro <leandro.regueiro@gmail.com>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () {
+            return "Non se atoparon resultados";
+        },
+        formatInputTooShort: function (input, min) {
+            var n = min - input.length;
+            if (n === 1) {
+                return "Engada un carácter";
+            } else {
+                return "Engada " + n + " caracteres";
+            }
+        },
+        formatInputTooLong: function (input, max) {
+            var n = input.length - max;
+            if (n === 1) {
+                return "Elimine un carácter";
+            } else {
+                return "Elimine " + n + " caracteres";
+            }
+        },
+        formatSelectionTooBig: function (limit) {
+            if (limit === 1 ) {
+                return "Só pode seleccionar un elemento";
+            } else {
+                return "Só pode seleccionar " + limit + " elementos";
+            }
+        },
+        formatLoadMore: function (pageNumber) {
+            return "Cargando máis resultados...";
+        },
+        formatSearching: function () {
+            return "Buscando...";
+        }
+    });
+})(jQuery);

+ 17 - 0
main/inc/lib/javascript/select2/select2_locale_he.js

@@ -0,0 +1,17 @@
+/**
+* Select2 Hebrew translation.
+*
+* Author: Yakir Sitbon <http://www.yakirs.net/>
+*/
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "לא נמצאו התאמות"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "נא להזין עוד " + n + " תווים נוספים"; },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "נא להזין פחות " + n + " תווים"; },
+        formatSelectionTooBig: function (limit) { return "ניתן לבחור " + limit + " פריטים"; },
+        formatLoadMore: function (pageNumber) { return "טוען תוצאות נוספות..."; },
+        formatSearching: function () { return "מחפש..."; }
+    });
+})(jQuery);

+ 42 - 0
main/inc/lib/javascript/select2/select2_locale_hr.js

@@ -0,0 +1,42 @@
+/**
+ * Select2 Croatian translation.
+ *
+ * Author: Edi Modrić <edi.modric@gmail.com>
+ */
+(function ($) {
+    "use strict";
+
+    var specialNumbers = {
+        1: function(n) { return (n % 100 != 11 ? "znak" : "znakova"); },
+        2: function(n) { return (n % 100 != 12 ? "znaka" : "znakova"); },
+        3: function(n) { return (n % 100 != 13 ? "znaka" : "znakova"); },
+        4: function(n) { return (n % 100 != 14 ? "znaka" : "znakova"); }
+    };
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Nema rezultata"; },
+        formatInputTooShort: function (input, min) {
+            var n = min - input.length;
+            var nMod10 = n % 10;
+
+            if (nMod10 > 0 && nMod10 < 5) {
+                return "Unesite još " + n + " " + specialNumbers[nMod10](n);
+            }
+
+            return "Unesite još " + n + " znakova";
+        },
+        formatInputTooLong: function (input, max) {
+            var n = input.length - max;
+            var nMod10 = n % 10;
+
+            if (nMod10 > 0 && nMod10 < 5) {
+                return "Unesite " + n + " " + specialNumbers[nMod10](n) + " manje";
+            }
+
+            return "Unesite " + n + " znakova manje";
+        },
+        formatSelectionTooBig: function (limit) { return "Maksimalan broj odabranih stavki je " + limit; },
+        formatLoadMore: function (pageNumber) { return "Učitavanje rezultata..."; },
+        formatSearching: function () { return "Pretraga..."; }
+    });
+})(jQuery);

+ 15 - 0
main/inc/lib/javascript/select2/select2_locale_hu.js

@@ -0,0 +1,15 @@
+/**
+ * Select2 Hungarian translation
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Nincs találat."; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Túl rövid. Még " + n + " karakter hiányzik."; },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Túl hosszú. " + n + " kerekterrel több mint kellene."; },
+        formatSelectionTooBig: function (limit) { return "Csak " + limit + " elemet lehet kiválasztani."; },
+        formatLoadMore: function (pageNumber) { return "Töltés..."; },
+        formatSearching: function () { return "Keresés..."; }
+    });
+})(jQuery);

+ 17 - 0
main/inc/lib/javascript/select2/select2_locale_id.js

@@ -0,0 +1,17 @@
+/**
+ * Select2 Indonesian translation.
+ * 
+ * Author: Ibrahim Yusuf <ibrahim7usuf@gmail.com>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Tidak ada data yang sesuai"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Masukkan " + n + " huruf lagi" + (n == 1 ? "" : "s"); },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Hapus " + n + " huruf" + (n == 1 ? "" : "s"); },
+        formatSelectionTooBig: function (limit) { return "Anda hanya dapat memilih " + limit + " pilihan" + (limit == 1 ? "" : "s"); },
+        formatLoadMore: function (pageNumber) { return "Mengambil data..."; },
+        formatSearching: function () { return "Mencari..."; }
+    });
+})(jQuery);

+ 16 - 0
main/inc/lib/javascript/select2/select2_locale_is.js

@@ -0,0 +1,16 @@
+/**
+ * Select2 Icelandic translation.
+ * 
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Ekkert fannst"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Vinsamlegast skrifið " + n + " staf" + (n == 1 ? "" : "i") + " í viðbót"; },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Vinsamlegast styttið texta um " + n + " staf" + (n == 1 ? "" : "i"); },
+        formatSelectionTooBig: function (limit) { return "Þú getur aðeins valið " + limit + " atriði"; },
+        formatLoadMore: function (pageNumber) { return "Sæki fleiri niðurstöður..."; }, 
+        formatSearching: function () { return "Leita..."; }
+    });
+})(jQuery);

+ 15 - 0
main/inc/lib/javascript/select2/select2_locale_it.js

@@ -0,0 +1,15 @@
+/**
+ * Select2 Italian translation
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Nessuna corrispondenza trovata"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Inserisci ancora " + n + " caratter" + (n == 1? "e" : "i"); },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Inserisci " + n + " caratter" + (n == 1? "e" : "i") + " in meno"; },
+        formatSelectionTooBig: function (limit) { return "Puoi selezionare solo " + limit + " element" + (limit == 1 ? "o" : "i"); },
+        formatLoadMore: function (pageNumber) { return "Caricamento in corso..."; },
+        formatSearching: function () { return "Ricerca..."; }
+    });
+})(jQuery);

+ 15 - 0
main/inc/lib/javascript/select2/select2_locale_ja.js

@@ -0,0 +1,15 @@
+/**
+ * Select2 Japanese translation.
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "該当なし"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "後" + n + "文字入れてください"; },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "検索文字列が" + n + "文字長すぎます"; },
+        formatSelectionTooBig: function (limit) { return "最多で" + limit + "項目までしか選択できません"; },
+        formatLoadMore: function (pageNumber) { return "読込中・・・"; },
+        formatSearching: function () { return "検索中・・・"; }
+    });
+})(jQuery);

+ 17 - 0
main/inc/lib/javascript/select2/select2_locale_ko.js

@@ -0,0 +1,17 @@
+/**
+ * Select2 <Language> translation.
+ * 
+ * Author: Swen Mun <longfinfunnel@gmail.com>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "결과 없음"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "너무 짧습니다. "+n+"글자 더 입력해주세요."; },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "너무 깁니다. "+n+"글자 지워주세요."; },
+        formatSelectionTooBig: function (limit) { return "최대 "+limit+"개까지만 선택하실 수 있습니다."; },
+        formatLoadMore: function (pageNumber) { return "불러오는 중…"; },
+        formatSearching: function () { return "검색 중…"; }
+    });
+})(jQuery);

+ 29 - 0
main/inc/lib/javascript/select2/select2_locale_lt.js

@@ -0,0 +1,29 @@
+/**
+ * Select2 lithuanian translation.
+ * 
+ * Author: CRONUS Karmalakas <cronus dot karmalakas at gmail dot com>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Atitikmenų nerasta"; },
+        formatInputTooShort: function (input, min) {
+        	var n = min - input.length,
+        	    suffix = (n % 10 == 1) && (n % 100 != 11) ? 'į' : (((n % 10 >= 2) && ((n % 100 < 10) || (n % 100 >= 20))) ? 'ius' : 'ių');
+        	return "Įrašykite dar " + n + " simbol" + suffix;
+        },
+        formatInputTooLong: function (input, max) {
+        	var n = input.length - max,
+        	    suffix = (n % 10 == 1) && (n % 100 != 11) ? 'į' : (((n % 10 >= 2) && ((n % 100 < 10) || (n % 100 >= 20))) ? 'ius' : 'ių');
+        	return "Pašalinkite " + n + " simbol" + suffix;
+        },
+        formatSelectionTooBig: function (limit) {
+        	var n = limit,
+                suffix = (n % 10 == 1) && (n % 100 != 11) ? 'ą' : (((n % 10 >= 2) && ((n % 100 < 10) || (n % 100 >= 20))) ? 'us' : 'ų');
+        	return "Jūs galite pasirinkti tik " + limit + " element" + suffix;
+        },
+        formatLoadMore: function (pageNumber) { return "Kraunama daugiau rezultatų..."; },
+        formatSearching: function () { return "Ieškoma..."; }
+    });
+})(jQuery);

+ 16 - 0
main/inc/lib/javascript/select2/select2_locale_lv.js

@@ -0,0 +1,16 @@
+/**
+ * Select2 Latvian translation
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Sakritību nav"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Lūdzu ievadiet vēl " + n + " simbol" + (n == 11 ? "us" : (/^\d*[1]$/im.test(n)? "u" : "us")); },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Lūdzu ievadiet par " + n + " simbol" + (n == 11 ? "iem" : (/^\d*[1]$/im.test(n)? "u" : "iem")) + " mazāk"; },
+        formatSelectionTooBig: function (limit) { return "Jūs varat izvēlēties ne vairāk kā " + limit + " element" + (limit == 11 ? "us" : (/^\d*[1]$/im.test(limit)? "u" : "us")); },
+        formatLoadMore: function (pageNumber) { return "Datu ielāde..."; },
+        formatSearching: function () { return "Meklēšana..."; }
+    });
+	
+})(jQuery);

+ 17 - 0
main/inc/lib/javascript/select2/select2_locale_mk.js

@@ -0,0 +1,17 @@
+/**
+ * Select2 Macedonian translation.
+ * 
+ * Author: Marko Aleksic <psybaron@gmail.com>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Нема пронајдено совпаѓања"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Ве молиме внесете уште " + n + " карактер" + (n == 1 ? "" : "и"); },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Ве молиме внесете " + n + " помалку карактер" + (n == 1? "" : "и"); },
+        formatSelectionTooBig: function (limit) { return "Можете да изберете само " + limit + " ставк" + (limit == 1 ? "а" : "и"); },
+        formatLoadMore: function (pageNumber) { return "Вчитување резултати..."; },
+        formatSearching: function () { return "Пребарување..."; }
+    });
+})(jQuery);

+ 17 - 0
main/inc/lib/javascript/select2/select2_locale_ms.js

@@ -0,0 +1,17 @@
+/**
+ * Select2 Malay translation.
+ * 
+ * Author: Kepoweran <kepoweran@gmail.com>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Tiada padanan yang ditemui"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Sila masukkan " + n + " aksara lagi"; },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Sila hapuskan " + n + " aksara"; },
+        formatSelectionTooBig: function (limit) { return "Anda hanya boleh memilih " + limit + " pilihan"; },
+        formatLoadMore: function (pageNumber) { return "Sedang memuatkan keputusan..."; },
+        formatSearching: function () { return "Mencari..."; }
+    });
+})(jQuery);

+ 15 - 0
main/inc/lib/javascript/select2/select2_locale_nl.js

@@ -0,0 +1,15 @@
+/**
+ * Select2 Dutch translation
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Geen resultaten gevonden"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Vul " + n + " karakter" + (n == 1? "" : "s") + " meer in"; },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Vul " + n + " karakter" + (n == 1? "" : "s") + " minder in"; },
+        formatSelectionTooBig: function (limit) { return "Maximaal " + limit + " item" + (limit == 1 ? "" : "s") + " toegestaan"; },
+        formatLoadMore: function (pageNumber) { return "Meer resultaten laden..."; },
+        formatSearching: function () { return "Zoeken..."; }
+    });
+})(jQuery);

+ 18 - 0
main/inc/lib/javascript/select2/select2_locale_no.js

@@ -0,0 +1,18 @@
+/**
+ * Select2 Norwegian translation.
+ *
+ * Author: Torgeir Veimo <torgeir.veimo@gmail.com>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Ingen treff"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Vennligst skriv inn " + n + (n>1 ? " flere tegn" : " tegn til"); },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Vennligst fjern " + n + " tegn"; },
+        formatSelectionTooBig: function (limit) { return "Du kan velge maks " + limit + " elementer"; },
+        formatLoadMore: function (pageNumber) { return "Laster flere resultater..."; },
+        formatSearching: function () { return "Søker..."; }
+    });
+})(jQuery);
+

+ 37 - 0
main/inc/lib/javascript/select2/select2_locale_pl.js

@@ -0,0 +1,37 @@
+/**
+ * Select2 Polish translation.
+ * 
+ * Author: Jan Kondratowicz <jan@kondratowicz.pl>
+ */
+(function ($) {
+    "use strict";
+    
+    var pl_suffix = function(n) {
+        if(n == 1) return "";
+        if((n%100 > 1 && n%100 < 5) || (n%100 > 20 && n%10 > 1 && n%10 < 5)) return "i";
+        return "ów";
+    };
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () {
+            return "Brak wyników.";
+        },
+        formatInputTooShort: function (input, min) {
+            var n = min - input.length;
+            return "Wpisz jeszcze " + n + " znak" + pl_suffix(n) + ".";
+        },
+        formatInputTooLong: function (input, max) {
+            var n = input.length - max;
+            return "Wpisana fraza jest za długa o " + n + " znak" + pl_suffix(n) + ".";
+        },
+        formatSelectionTooBig: function (limit) {
+            return "Możesz zaznaczyć najwyżej " + limit + " element" + pl_suffix(limit) + ".";
+        },
+        formatLoadMore: function (pageNumber) {
+            return "Ładowanie wyników...";
+        },
+        formatSearching: function () {
+            return "Szukanie...";
+        }
+    });
+})(jQuery);

+ 15 - 0
main/inc/lib/javascript/select2/select2_locale_pt-BR.js

@@ -0,0 +1,15 @@
+/**
+ * Select2 Brazilian Portuguese translation
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Nenhum resultado encontrado"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Informe " + n + " caractere" + (n == 1? "" : "s"); },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Apague " + n + " caractere" + (n == 1? "" : "s"); },
+        formatSelectionTooBig: function (limit) { return "Só é possível selecionar " + limit + " elemento" + (limit == 1 ? "" : "s"); },
+        formatLoadMore: function (pageNumber) { return "Carregando mais resultados..."; },
+        formatSearching: function () { return "Buscando..."; }
+    });
+})(jQuery);

+ 15 - 0
main/inc/lib/javascript/select2/select2_locale_pt-PT.js

@@ -0,0 +1,15 @@
+/**
+ * Select2 Portuguese (Portugal) translation
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Nenhum resultado encontrado"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Introduza " + n + " car" + (n == 1 ? "ácter" : "acteres"); },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Apague " + n + " car" + (n == 1 ? "ácter" : "acteres"); },
+        formatSelectionTooBig: function (limit) { return "Só é possível selecionar " + limit + " elemento" + (limit == 1 ? "" : "s"); },
+        formatLoadMore: function (pageNumber) { return "A carregar mais resultados..."; },
+        formatSearching: function () { return "A pesquisar..."; }
+    });
+})(jQuery);

+ 15 - 0
main/inc/lib/javascript/select2/select2_locale_ro.js

@@ -0,0 +1,15 @@
+/**
+ * Select2 Romanian translation.
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Nu a fost găsit nimic"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Vă rugăm să introduceți incă " + n + " caracter" + (n == 1 ? "" : "e"); },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Vă rugăm să introduceți mai puțin de " + n + " caracter" + (n == 1? "" : "e"); },
+        formatSelectionTooBig: function (limit) { return "Aveți voie să selectați cel mult " + limit + " element" + (limit == 1 ? "" : "e"); },
+        formatLoadMore: function (pageNumber) { return "Se încarcă..."; },
+        formatSearching: function () { return "Căutare..."; }
+    });
+})(jQuery);

+ 15 - 0
main/inc/lib/javascript/select2/select2_locale_ru.js

@@ -0,0 +1,15 @@
+/**
+ * Select2 Russian translation
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Совпадений не найдено"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Пожалуйста, введите еще " + n + " символ" + (n == 1 ? "" : ((n > 1)&&(n < 5) ? "а" : "ов")); },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Пожалуйста, введите на " + n + " символ" + (n == 1 ? "" : ((n > 1)&&(n < 5)? "а" : "ов")) + " меньше"; },
+        formatSelectionTooBig: function (limit) { return "Вы можете выбрать не более " + limit + " элемент" + (limit == 1 ? "а" : "ов"); },
+        formatLoadMore: function (pageNumber) { return "Загрузка данных..."; },
+        formatSearching: function () { return "Поиск..."; }
+    });
+})(jQuery);

+ 48 - 0
main/inc/lib/javascript/select2/select2_locale_sk.js

@@ -0,0 +1,48 @@
+/**
+ * Select2 Slovak translation.
+ *
+ * Author: David Vallner <david@vallner.net>
+ */
+(function ($) {
+    "use strict";
+    // use text for the numbers 2 through 4
+    var smallNumbers = {
+        2: function(masc) { return (masc ? "dva" : "dve"); },
+        3: function() { return "tri"; },
+        4: function() { return "štyri"; }
+    }
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Nenašli sa žiadne položky"; },
+        formatInputTooShort: function (input, min) {
+            var n = min - input.length;
+            if (n == 1) {
+                return "Prosím zadajte ešte jeden znak";
+            } else if (n <= 4) {
+                return "Prosím zadajte ešte ďalšie "+smallNumbers[n](true)+" znaky";
+            } else {
+                return "Prosím zadajte ešte ďalších "+n+" znakov";
+            }
+        },
+        formatInputTooLong: function (input, max) {
+            var n = input.length - max;
+            if (n == 1) {
+                return "Prosím zadajte o jeden znak menej";
+            } else if (n <= 4) {
+                return "Prosím zadajte o "+smallNumbers[n](true)+" znaky menej";
+            } else {
+                return "Prosím zadajte o "+n+" znakov menej";
+            }
+        },
+        formatSelectionTooBig: function (limit) {
+            if (limit == 1) {
+                return "Môžete zvoliť len jednu položku";
+            } else if (limit <= 4) {
+                return "Môžete zvoliť najviac "+smallNumbers[limit](false)+" položky";
+            } else {
+                return "Môžete zvoliť najviac "+limit+" položiek";
+            }
+        },
+        formatLoadMore: function (pageNumber) { return "Načítavajú sa ďalšie výsledky..."; },
+        formatSearching: function () { return "Vyhľadávanie..."; }
+    });
+})(jQuery);

+ 17 - 0
main/inc/lib/javascript/select2/select2_locale_sv.js

@@ -0,0 +1,17 @@
+/**
+ * Select2 Swedish translation.
+ *
+ * Author: Jens Rantil <jens.rantil@telavox.com>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Inga träffar"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Var god skriv in " + n + (n>1 ? " till tecken" : " tecken till"); },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Var god sudda ut " + n + " tecken"; },
+        formatSelectionTooBig: function (limit) { return "Du kan max välja " + limit + " element"; },
+        formatLoadMore: function (pageNumber) { return "Laddar fler resultat..."; },
+        formatSearching: function () { return "Söker..."; }
+    });
+})(jQuery);

+ 17 - 0
main/inc/lib/javascript/select2/select2_locale_th.js

@@ -0,0 +1,17 @@
+/**
+ * Select2 Thai translation.
+ *
+ * Author: Atsawin Chaowanakritsanakul <joke@nakhon.net>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "ไม่พบข้อมูล"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "โปรดพิมพ์เพิ่มอีก " + n + " ตัวอักษร"; },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "โปรดลบออก " + n + " ตัวอักษร"; },
+        formatSelectionTooBig: function (limit) { return "คุณสามารถเลือกได้ไม่เกิน " + limit + " รายการ"; },
+        formatLoadMore: function (pageNumber) { return "กำลังค้นข้อมูลเพิ่ม..."; },
+        formatSearching: function () { return "กำลังค้นข้อมูล..."; }
+    });
+})(jQuery);

+ 17 - 0
main/inc/lib/javascript/select2/select2_locale_tr.js

@@ -0,0 +1,17 @@
+/**
+ * Select2 Turkish translation.
+ * 
+ * Author: Salim KAYABAŞI <salim.kayabasi@gmail.com>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Sonuç bulunamadı"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "En az " + n + " karakter daha girmelisiniz"; },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return n + " karakter azaltmalısınız"; },
+        formatSelectionTooBig: function (limit) { return "Sadece " + limit + " seçim yapabilirsiniz"; },
+        formatLoadMore: function (pageNumber) { return "Daha fazla..."; },
+        formatSearching: function () { return "Aranıyor..."; }
+    });
+})(jQuery);

+ 17 - 0
main/inc/lib/javascript/select2/select2_locale_ua.js

@@ -0,0 +1,17 @@
+/**
+ * Select2 <Language> translation.
+ * 
+ * Author: bigmihail <bigmihail@bigmir.net>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Нічого не знайдено"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length, s = ["", "и", "ів"], p = [2,0,1,1,1,2]; return "Введіть буль ласка ще " + n + " символ" + s[ (n%100>4 && n%100<=20)? 2 : p[Math.min(n%10, 5)] ]; },
+        formatInputTooLong: function (input, max) { var n = input.length - max, s = ["", "и", "ів"], p = [2,0,1,1,1,2]; return "Введіть буль ласка на " + n + " символ" + s[ (n%100>4 && n%100<=20)? 2 : p[Math.min(n%10, 5)] ] + " менше"; },
+        formatSelectionTooBig: function (limit) {var s = ["", "и", "ів"], p = [2,0,1,1,1,2];  return "Ви можете вибрати лише " + limit + " елемент" + s[ (limit%100>4 && limit%100<=20)? 2 : p[Math.min(limit%10, 5)] ]; },
+        formatLoadMore: function (pageNumber) { return "Завантаження даних..."; },
+        formatSearching: function () { return "Пошук..."; }
+    });
+})(jQuery);

+ 18 - 0
main/inc/lib/javascript/select2/select2_locale_vi.js

@@ -0,0 +1,18 @@
+/**
+ * Select2 Vietnamese translation.
+ * 
+ * Author: Long Nguyen <olragon@gmail.com>
+ */
+(function ($) {
+    "use strict";
+
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "Không tìm thấy kết quả"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "Vui lòng nhập nhiều hơn " + n + " ký tự" + (n == 1 ? "" : "s"); },
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "Vui lòng nhập ít hơn " + n + " ký tự" + (n == 1? "" : "s"); },
+        formatSelectionTooBig: function (limit) { return "Chỉ có thể chọn được " + limit + " tùy chọn" + (limit == 1 ? "" : "s"); },
+        formatLoadMore: function (pageNumber) { return "Đang lấy thêm kết quả..."; },
+        formatSearching: function () { return "Đang tìm..."; }
+    });
+})(jQuery);
+

+ 14 - 0
main/inc/lib/javascript/select2/select2_locale_zh-CN.js

@@ -0,0 +1,14 @@
+/**
+ * Select2 Chinese translation
+ */
+(function ($) {
+    "use strict";
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "没有找到匹配项"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "请再输入" + n + "个字符";},
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "请删掉" + n + "个字符";},
+        formatSelectionTooBig: function (limit) { return "你只能选择最多" + limit + "项"; },
+        formatLoadMore: function (pageNumber) { return "加载结果中..."; },
+        formatSearching: function () { return "搜索中..."; }
+    });
+})(jQuery);

+ 14 - 0
main/inc/lib/javascript/select2/select2_locale_zh-TW.js

@@ -0,0 +1,14 @@
+/**
+ * Select2 Traditional Chinese translation
+ */
+(function ($) {
+    "use strict";
+    $.extend($.fn.select2.defaults, {
+        formatNoMatches: function () { return "沒有找到相符的項目"; },
+        formatInputTooShort: function (input, min) { var n = min - input.length; return "請再輸入" + n + "個字元";},
+        formatInputTooLong: function (input, max) { var n = input.length - max; return "請刪掉" + n + "個字元";},
+        formatSelectionTooBig: function (limit) { return "你只能選擇最多" + limit + "項"; },
+        formatLoadMore: function (pageNumber) { return "載入中..."; },
+        formatSearching: function () { return "搜尋中..."; }
+    });
+})(jQuery);

BIN
main/inc/lib/javascript/select2/select2x2.png


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

@@ -89,6 +89,7 @@ define('TOOL_LINK', 'link');
 define('TOOL_COURSE_DESCRIPTION', 'course_description');
 define('TOOL_SEARCH', 'search');
 define('TOOL_LEARNPATH', 'learnpath');
+define('TOOL_AGENDA', 'agenda');
 define('TOOL_ANNOUNCEMENT', 'announcement');
 define('TOOL_FORUM', 'forum');
 define('TOOL_FORUM_CATEGORY','forum_category');

+ 64 - 41
main/inc/lib/sessionmanager.lib.php

@@ -23,7 +23,7 @@ class SessionManager
     /**
     * Fetches a session from the database
     * @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)
+    * @return  array   Session details
     */
     public static function fetch($id)
     {
@@ -57,11 +57,28 @@ class SessionManager
     * @param  integer     Visibility after end date (0 = read-only, 1 = invisible, 2 = accessible)
     * @param  string      Start limit = true if the start date has to be considered
     * @param  string      End limit = true if the end date has to be considered
+    * @param  string $fix_name
     * @todo use an array to replace all this parameters or use the model.lib.php ...
     * @return mixed       Session ID on success, error message otherwise
     **/
-    public static function create_session($sname,$syear_start,$smonth_start,$sday_start,$syear_end,$smonth_end,$sday_end,$snb_days_acess_before,$snb_days_acess_after, $nolimit,$coach_username, $id_session_category,$id_visibility, $start_limit = true, $end_limit = true, $fix_name = false)
-    {
+    public static function create_session(
+        $sname,
+        $syear_start,
+        $smonth_start,
+        $sday_start,
+        $syear_end,
+        $smonth_end,
+        $sday_end,
+        $snb_days_acess_before,
+        $snb_days_acess_after,
+        $nolimit,
+        $coach_username,
+        $id_session_category,
+        $id_visibility,
+        $start_limit = true,
+        $end_limit = true,
+        $fix_name = false
+    ) {
 		global $_configuration;
 
 		//Check portal limits
@@ -76,19 +93,19 @@ class SessionManager
             }
         }
 
-		$name                 = Database::escape_string(trim($sname));
-		$year_start           = intval($syear_start);
-		$month_start          = intval($smonth_start);
-		$day_start            = intval($sday_start);
-		$year_end             = intval($syear_end);
-		$month_end            = intval($smonth_end);
-		$day_end              = intval($sday_end);
-		$nb_days_acess_before = intval($snb_days_acess_before);
-		$nb_days_acess_after  = intval($snb_days_acess_after);
-		$id_session_category  = intval($id_session_category);
-		$id_visibility        = intval($id_visibility);
-		$tbl_user		      = Database::get_main_table(TABLE_MAIN_USER);
-		$tbl_session	      = Database::get_main_table(TABLE_MAIN_SESSION);
+        $name                 = Database::escape_string(trim($sname));
+        $year_start           = intval($syear_start);
+        $month_start          = intval($smonth_start);
+        $day_start            = intval($sday_start);
+        $year_end             = intval($syear_end);
+        $month_end            = intval($smonth_end);
+        $day_end              = intval($sday_end);
+        $nb_days_acess_before = intval($snb_days_acess_before);
+        $nb_days_acess_after  = intval($snb_days_acess_after);
+        $id_session_category  = intval($id_session_category);
+        $id_visibility        = intval($id_visibility);
+        $tbl_user		      = Database::get_main_table(TABLE_MAIN_USER);
+        $tbl_session	      = Database::get_main_table(TABLE_MAIN_SESSION);
 
 		if (is_int($coach_username)) {
 			$id_coach = $coach_username;
@@ -200,7 +217,7 @@ class SessionManager
         $tbl_session            = Database::get_main_table(TABLE_MAIN_SESSION);
         $tbl_session_category   = Database::get_main_table(TABLE_MAIN_SESSION_CATEGORY);
         $tbl_user               = Database::get_main_table(TABLE_MAIN_USER);
-        $table_access_url_rel_session= Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
+        $table_access_url_rel_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
         $tbl_session_rel_user 	= Database::get_main_table(TABLE_MAIN_SESSION_USER);
 
         $where = 'WHERE 1=1 ';
@@ -492,6 +509,11 @@ class SessionManager
 	 * @param 	integer		nolimit
 	 * @param 	integer		id_coach
 	 * @param 	integer		id_session_category
+     * @param   int $id_visibility
+     * @param bool
+     * @param bool
+     * @param string $description
+     * @param int  $showDescription
 	 * @return $id;
 	 * The parameter id is a primary key
 	**/
@@ -514,8 +536,7 @@ class SessionManager
         $end_limit = true,
         $description = null,
         $showDescription = null
-    )
-    {
+    ) {
 		$name = trim(stripslashes($name));
 		$year_start=intval($year_start);
 		$month_start=intval($month_start);
@@ -929,8 +950,9 @@ class SessionManager
      * @param	bool	Whether to unsubscribe existing users (true, default) or not (false)
      * @return	void	Nothing, or false on error
      **/
-     public static function add_courses_to_session ($id_session, $course_list, $empty_courses = true) {
-     	// security checks
+     public static function add_courses_to_session($id_session, $course_list, $empty_courses = true)
+     {
+     	// Security checks
      	if ($id_session!= strval(intval($id_session))) {
             return false;
         }
@@ -1070,15 +1092,15 @@ class SessionManager
 				$order = $row[0]+1;
 			}
 
-			$sql = "INSERT INTO $t_sf
-						                SET field_type = '$fieldtype',
-						                field_variable = '$fieldvarname',
-						                field_display_text = '$fieldtitle',
-						                field_order = '$order',
-						                tms = FROM_UNIXTIME($time)";
-			$result = Database::query($sql);
+			$sql = "INSERT INTO $t_sf SET
+                    field_type = '$fieldtype',
+                    field_variable = '$fieldvarname',
+                    field_display_text = '$fieldtitle',
+                    field_order = '$order',
+                    tms = FROM_UNIXTIME($time)";
+			Database::query($sql);
 
-			$field_id=Database::insert_id();
+			$field_id = Database::insert_id();
 		}
 		return $field_id;
 	}
@@ -1325,7 +1347,7 @@ class SessionManager
         Database::query($sql);
 
 		$sql = "SELECT id FROM $tbl_session WHERE session_category_id IN (".$id_checked.")";
-		$result = @Database::query($sql);
+		$result = Database::query($sql);
 		while ($rows = Database::fetch_array($result)) {
 			$session_id = $rows['id'];
 			if ($delete_session) {
@@ -1337,8 +1359,7 @@ class SessionManager
 			}
 		}
 		$sql = "DELETE FROM $tbl_session_category WHERE id IN (".$id_checked.")";
-		$rs = Database::query($sql);
-		$result = Database::affected_rows();
+		Database::query($sql);
 
 		// Add event to system log
 		$user_id = api_get_user_id();
@@ -1540,6 +1561,7 @@ class SessionManager
     * Subscribes sessions to human resource manager (Dashboard feature)
     * @param	int 		Human Resource Manager id
     * @param	array 		Sessions id
+    * @return int
     **/
 	public static function suscribe_sessions_to_hr_manager($hr_manager_id, $sessions_list)
     {
@@ -2047,17 +2069,17 @@ class SessionManager
     /**
      * @param string $file
      * @param bool $updatesession options:
-     * 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
+     *  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
+     * @param array $extraFields 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 string $extraFieldId
      * @param int $daysCoachAccessBeforeBeginning
      * @param int $daysCoachAccessAfterBeginning
      * @param int $sessionVisibility
+     * @param array $fieldsToAvoidUpdate
      * @return array
      */
     static function importCSV(
@@ -2238,7 +2260,6 @@ class SessionManager
 
                         // We get the last insert id.
                         $my_session_result = SessionManager::get_session_by_name($enreg['SessionName']);
-
                         $session_id = $my_session_result['id'];
 
                         if ($debug) {
@@ -2257,7 +2278,7 @@ class SessionManager
 
                         // Updating the session.
                         $params = array(
-                            'id_coach' =>  $coach_id,
+                            'id_coach' => $coach_id,
                             'date_start' => $date_start,
                             'date_end' => $date_end,
                             'visibility' => $visibility,
@@ -2290,6 +2311,7 @@ class SessionManager
                             }
                         }
 
+                        // Delete session-user relation only for students
                         $sql = "DELETE FROM $tbl_session_user
                                 WHERE id_session = '$session_id' AND relation_type <> ".SESSION_RELATION_TYPE_RRHH;
                         Database::query($sql);
@@ -2297,7 +2319,7 @@ class SessionManager
                         $sql = "DELETE FROM $tbl_session_course WHERE id_session = '$session_id'";
                         Database::query($sql);
 
-                        // Delete session course user relation ships *only* for students
+                        // Delete session-course-user relation ships *only* for students
                         $sql = "DELETE FROM $tbl_session_course_user WHERE id_session = '$session_id' AND status <> 2";
                         Database::query($sql);
                     }
@@ -2306,7 +2328,7 @@ class SessionManager
 
                 $users = explode('|', $enreg['Users']);
 
-                // Adding the relationship "Session - User".
+                // Adding the relationship "Session - User" for students
                 if (is_array($users)) {
                     foreach ($users as $user) {
                         $user_id = UserManager::get_user_id_from_username($user);
@@ -2394,6 +2416,7 @@ class SessionManager
                             }
                         }
 
+
                         // Adding Students, updating relationship "Session - Course - User".
                         foreach ($course_users as $user) {
                             $user = trim($user);

+ 27 - 16
main/inc/lib/usermanager.lib.php

@@ -63,8 +63,25 @@ class UserManager
      * @assert ('Sam','Gamegie',5,'sam@example.com','jo','jo') > 1
      * @assert ('Pippin','Took',null,null,'jo','jo') === false
      */
-    public static function create_user($firstName, $lastName, $status, $email, $loginName, $password, $official_code = '', $language = '', $phone = '', $picture_uri = '', $auth_source = PLATFORM_AUTH_SOURCE, $expiration_date = '0000-00-00 00:00:00', $active = 1, $hr_dept_id = 0, $extra = null, $encrypt_method = '', $send_mail = false)
-    {
+    public static function create_user(
+        $firstName,
+        $lastName,
+        $status,
+        $email,
+        $loginName,
+        $password,
+        $official_code = '',
+        $language = '',
+        $phone = '',
+        $picture_uri = '',
+        $auth_source = PLATFORM_AUTH_SOURCE,
+        $expiration_date = '0000-00-00 00:00:00',
+        $active = 1,
+        $hr_dept_id = 0,
+        $extra = null,
+        $encrypt_method = '',
+        $send_mail = false
+    ) {
         global $_user, $_configuration;
         $original_password = $password;
         $access_url_id = 1;
@@ -163,7 +180,6 @@ class UserManager
             if (!empty($email) && $send_mail) {
                 $recipient_name = api_get_person_name($firstName, $lastName, null, PERSON_NAME_EMAIL_ADDRESS);
                 $emailsubject = '['.api_get_setting('siteName').'] '.get_lang('YourReg').' '.api_get_setting('siteName');
-
                 $sender_name = api_get_person_name(api_get_setting('administratorName'), api_get_setting('administratorSurname'), null, PERSON_NAME_EMAIL_ADDRESS);
                 $email_admin = api_get_setting('emailAdministrator');
 
@@ -185,17 +201,12 @@ class UserManager
                     $values["prior_lang"] = null;
                     EventsDispatcher::events('user_registration', $values);
                 } else {
-                    @api_mail_html($recipient_name, $email, $emailsubject, $emailbody, $sender_name, $email_admin);
+                    api_mail_html($recipient_name, $email, $emailsubject, $emailbody, $sender_name, $email_admin);
                 }
                 /* ENDS MANAGE EVENT WITH MAIL */
             }
-
-
-            // Add event to system log
-            $user_id_manager = api_get_user_id();
             event_system(LOG_USER_CREATE, LOG_USER_ID, $return);
         } else {
-            $return = false;
             return api_set_failure('error inserting in Database');
         }
 
@@ -224,7 +235,7 @@ class UserManager
     public static function can_delete_user($user_id)
     {
         global $_configuration;
-        if (isset($_configuration['delete_users']) && $_configuration['delete_users'] == false) {
+        if (isset($_configuration['deny_delete_users']) && $_configuration['deny_delete_users'] == true) {
             return false;
         }
         $table_course_user = Database :: get_main_table(TABLE_MAIN_COURSE_USER);
@@ -250,7 +261,7 @@ class UserManager
      * super-admins. Other roles should only be able to disable a user,
      * which removes access to the platform but doesn't delete anything.
      * @param int The ID of th user to be deleted
-     * @return boolean true if user is succesfully deleted, false otherwise
+     * @return boolean true if user is successfully deleted, false otherwise
      * @assert (null) === false
      * @assert ('abc') === false
      */
@@ -2136,7 +2147,7 @@ class UserManager
 
                 // Checking session visibility
                 $visibility = api_get_session_visibility($session_id, null, $ignore_visibility_for_admins);
-                
+
                 switch ($visibility) {
                     case SESSION_VISIBLE_READ_ONLY:
                     case SESSION_VISIBLE:
@@ -2144,7 +2155,7 @@ class UserManager
                         break;
                     case SESSION_INVISIBLE:
                         continue(2);
-                } 
+                }
 
                 $categories[$row['session_category_id']]['sessions'][$row['id']]['session_name'] = $row['name'];
                 $categories[$row['session_category_id']]['sessions'][$row['id']]['session_id'] = $row['id'];
@@ -2153,12 +2164,12 @@ class UserManager
                 $categories[$row['session_category_id']]['sessions'][$row['id']]['nb_days_access_before_beginning'] = $row['nb_days_access_before_beginning'];
                 $categories[$row['session_category_id']]['sessions'][$row['id']]['nb_days_access_after_end'] = $row['nb_days_access_after_end'];
                 $categories[$row['session_category_id']]['sessions'][$row['id']]['courses'] = UserManager::get_courses_list_by_session($user_id, $row['id']);
-                
+
             }
         }
-        
+
         return $categories;
-        
+
     }
 
     /**

+ 1 - 1
main/inc/lib/zombie/zombie_manager.class.php

@@ -60,7 +60,7 @@ class ZombieManager
                                              ) AND
                         access.login_date <= '$ceiling' AND
                         user.user_id = access.login_user_id AND 
-                        url.login_user_id = user.user_id AND url.access_url_id=$current_url_id";
+                        url.user_id = user.user_id AND url.access_url_id=$current_url_id";
         } else {
             $sql .= " FROM $user_table as user, $login_table as access
                       WHERE 

+ 1 - 1
main/inc/local.inc.php

@@ -521,7 +521,7 @@ if (!empty($_SESSION['_user']['user_id']) && !($login || $logout)) {
                     $protocol = api_get_setting('sso_authentication_protocol');
                     // sso_authentication_domain can list
                     // several, comma-separated, domains
-                    $master_urls = split(',',api_get_setting('sso_authentication_domain'));
+                    $master_urls = preg_split('/,/',api_get_setting('sso_authentication_domain'));
                     if (!empty($master_urls)) {
                         $master_auth_uri = api_get_setting('sso_authentication_auth_uri');
                         foreach ($master_urls as $mu) {

+ 2 - 0
main/install/configuration.dist.php

@@ -163,3 +163,5 @@ $_configuration['deny_delete_users'] = false;
 // Version settings
 $_configuration['system_version']    = '{NEW_VERSION}';
 $_configuration['system_stable']     = NEW_VERSION_STABLE;
+$_configuration['hide_home_top_when_connected'] = true;
+$_configuration['hide_global_announcements_when_not_connected'] = true;

+ 1 - 1
main/template/default/layout/topbar.tpl

@@ -77,7 +77,7 @@
                                     <li><a href="{{ _p.web_main }}social/invitations.php">{{ "PendingInvitations"|get_lang }}</a></li> -->
                                 </ul>
                             </li>
-                            <li><a href="{{  _p.web }}index.php?logout=logout">{{ "Logout"|get_lang }}</a></li>
+                            <li><a href="{{  _p.web }}index.php?logout=logout&uid={{_u.user_id}}">{{ "Logout"|get_lang }}</a></li>
                         </ul>
                     </div> <!-- /nav collapse -->
                 {% endif %}                 

+ 3 - 3
main/user/user.php

@@ -419,14 +419,14 @@ function get_number_of_users() {
 		$a_course_users = CourseManager :: get_user_list_from_course_code($_SESSION['_course']['id'], 0);
 	}
 	foreach ($a_course_users as $user_id => $o_course_user) {
-		if ((isset($_GET['keyword']) && search_keyword($o_course_user['firstname'], $o_course_user['lastname'], $o_course_user['username'], $o_course_user['official_code'], $_GET['keyword'])) || !isset($_GET['keyword']) || empty($_GET['keyword'])) {
+		if ((isset($_GET['keyword']) && searchUserKeyword($o_course_user['firstname'], $o_course_user['lastname'], $o_course_user['username'], $o_course_user['official_code'], $_GET['keyword'])) || !isset($_GET['keyword']) || empty($_GET['keyword'])) {
 			$counter++;
 		}
 	}
 	return $counter;
 }
 
-function search_keyword($firstname, $lastname, $username, $official_code, $keyword) {
+function searchUserKeyword($firstname, $lastname, $username, $official_code, $keyword) {
 	if (api_strripos($firstname, $keyword) !== false || api_strripos($lastname, $keyword) !== false || api_strripos($username, $keyword) !== false || api_strripos($official_code, $keyword) !== false) {
 		return true;
 	} else {
@@ -491,7 +491,7 @@ function get_user_data($from, $number_of_items, $column, $direction) {
     $a_course_users = CourseManager :: get_user_list_from_course_code($course_code, $session_id, $limit, $order_by);
 
 	foreach ($a_course_users as $user_id => $o_course_user) {
-		if ((isset($_GET['keyword']) && search_keyword($o_course_user['firstname'], $o_course_user['lastname'], $o_course_user['username'], $o_course_user['official_code'], $_GET['keyword'])) || !isset($_GET['keyword']) || empty($_GET['keyword'])) {
+		if ((isset($_GET['keyword']) && searchUserKeyword($o_course_user['firstname'], $o_course_user['lastname'], $o_course_user['username'], $o_course_user['official_code'], $_GET['keyword'])) || !isset($_GET['keyword']) || empty($_GET['keyword'])) {
 
 			$groups_name = GroupManager :: get_user_group_name($user_id);
 			$temp = array();

+ 0 - 31
main/work/add_document.php

@@ -120,34 +120,3 @@ if (empty($docId)) {
     $form->display();
 }
 
-/**
- * DB changes needed for new features in work tool
- *
- *
- * 1. Create tables
- *
-CREATE TABLE IF NOT EXISTS c_student_publication_rel_document (
-    id  INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
-    work_id INT NOT NULL,
-    document_id INT NOT NULL,
-    c_id INT NOT NULL
-);
-
-CREATE TABLE IF NOT EXISTS c_student_publication_rel_user (
-    id  INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
-    work_id INT NOT NULL,
-    user_id INT NOT NULL,
-    c_id INT NOT NULL
-);
-
-CREATE TABLE IF NOT EXISTS c_student_publication_comment (id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,  work_id INT NOT NULL,  c_id INT NOT NULL,  comment text,  user_id int NOT NULL,  sent_at datetime NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-
-ALTER TABLE c_student_publication ADD COLUMN document_id int DEFAULT 0;
- *
- * Update configuration.php:
- *  $_configuration['add_document_to_work'] = true;
-
- *
-*/
-
-

+ 29 - 0
main/work/extra.sql

@@ -0,0 +1,29 @@
+-- Extra DB changes needed for new features in work tool
+
+CREATE TABLE IF NOT EXISTS c_student_publication_rel_document (
+    id  INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
+    work_id INT NOT NULL,
+    document_id INT NOT NULL,
+    c_id INT NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS c_student_publication_rel_user (
+    id  INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
+    work_id INT NOT NULL,
+    user_id INT NOT NULL,
+    c_id INT NOT NULL
+);
+
+CREATE TABLE IF NOT EXISTS c_student_publication_comment (
+  id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
+  work_id INT NOT NULL,
+  c_id INT NOT NULL,
+  comment text,
+  user_id int NOT NULL,
+  sent_at datetime NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+ALTER TABLE c_student_publication ADD COLUMN document_id int DEFAULT 0;
+
+-- Update configuration.php:
+-- $_configuration['add_document_to_work'] = true;

+ 21 - 13
main/work/work.lib.php

@@ -287,16 +287,20 @@ function getWorkList($id, $my_folder_data, $add_in_where_query)
     $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'));
+    $linkInfo = is_resource_in_course_gradebook(api_get_course_id(), 3 , $id, api_get_session_id());
+
+    if ($linkInfo) {
+        $workInGradeBookLinkId = $linkInfo['id'];
+
+        if ($workInGradeBookLinkId) {
+            if ($is_allowed_to_edit) {
+                if (intval($my_folder_data['qualification']) == 0) {
+                    Display::display_warning_message(get_lang('MaxWeightNeedToBeProvided'));
+                }
             }
+        }
     }
 
     $contains_file_query = '';
@@ -304,15 +308,16 @@ function getWorkList($id, $my_folder_data, $add_in_where_query)
     // Get list from database
     if ($is_allowed_to_edit) {
         $active_condition = ' active IN (0, 1)';
-        $sql_get_publications_list = "SELECT *  FROM  $work_table
+        $sql = "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 .= " AND post_group_id = '".$group_id."' ";
         }
-        $sql_get_publications_list .= " ORDER BY sent_date DESC";
+        $sql .= " 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
+            // set to select only messages posted by the user's group
+            $group_query = " WHERE c_id = $course_id AND post_group_id = '".$group_id."' ";
             $subdirs_query = "AND parent_id = 0";
         } else {
             $group_query = " WHERE c_id = $course_id AND  post_group_id = '0' ";
@@ -320,12 +325,14 @@ function getWorkList($id, $my_folder_data, $add_in_where_query)
         }
         //@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";
+        $sql = "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);
+    $sql_result = Database::query($sql);
     if (Database::num_rows($sql_result)) {
         while ($work = Database::fetch_object($sql_result)) {
             if ($work->parent_id == 0) {
@@ -333,6 +340,7 @@ function getWorkList($id, $my_folder_data, $add_in_where_query)
             }
         }
     }
+
     return $work_parents;
 }
 

Some files were not shown because too many files changed in this diff