Kaynağa Gözat

Merge branch '1.9.x' of github.com:chamilo/chamilo-lms into 1.9.x

Yannick Warnier 11 yıl önce
ebeveyn
işleme
0c1fe5f9c3
62 değiştirilmiş dosya ile 6052 ekleme ve 1230 silme
  1. 167 0
      main/admin/extra_field_options.php
  2. 212 0
      main/admin/extra_field_workflow.php
  3. 218 0
      main/admin/extra_fields.php
  4. 1 1
      main/admin/specific_fields_add.php
  5. 35 8
      main/announcements/announcement_email.class.php
  6. 15 17
      main/announcements/announcements.inc.php
  7. 7 5
      main/announcements/announcements.php
  8. 21 23
      main/auth/profile.php
  9. 137 144
      main/course_description/course_description_controller.php
  10. 5 3
      main/document/document.php
  11. 83 5
      main/exercice/exercise.class.php
  12. 14 6
      main/exercice/exercise.lib.php
  13. 1 1
      main/exercice/exercise_show.php
  14. 0 1
      main/exercice/overview.php
  15. 4 3
      main/group/group.php
  16. 102 67
      main/inc/ajax/course.ajax.php
  17. 39 33
      main/inc/ajax/course_home.ajax.php
  18. 1 1
      main/inc/ajax/document.ajax.php
  19. 30 0
      main/inc/ajax/extra_field.ajax.php
  20. 138 28
      main/inc/ajax/model.ajax.php
  21. 31 3
      main/inc/ajax/session.ajax.php
  22. 1 1
      main/inc/global_error_message.inc.php
  23. 6 0
      main/inc/introductionSection.inc.php
  24. 5 0
      main/inc/lib/autoload.class.php
  25. 115 107
      main/inc/lib/certificate.lib.php
  26. 30 5
      main/inc/lib/course.lib.php
  27. 4 0
      main/inc/lib/database.constants.inc.php
  28. 33 12
      main/inc/lib/display.lib.php
  29. 46 33
      main/inc/lib/document.lib.php
  30. 26 6
      main/inc/lib/events.lib.inc.php
  31. 37 4
      main/inc/lib/export.lib.inc.php
  32. 1648 0
      main/inc/lib/extra_field.lib.php
  33. 659 0
      main/inc/lib/extra_field_option.lib.php
  34. 654 0
      main/inc/lib/extra_field_value.lib.php
  35. 1 0
      main/inc/lib/fckeditor/editor/plugins/ImageManager/config.inc.php
  36. 22 14
      main/inc/lib/fileUpload.lib.php
  37. 20 4
      main/inc/lib/formvalidator/Element/select_ajax.php
  38. 10 5
      main/inc/lib/main_api.lib.php
  39. 8 8
      main/inc/lib/pear/HTML/QuickForm.php
  40. 362 375
      main/inc/lib/sessionmanager.lib.php
  41. 3 16
      main/inc/lib/social.lib.php
  42. 4 2
      main/inc/lib/sortable_table.class.php
  43. 628 74
      main/inc/lib/tracking.lib.php
  44. 37 19
      main/inc/lib/usermanager.lib.php
  45. 1 5
      main/inc/local.inc.php
  46. 10 9
      main/lang/english/admin.inc.php
  47. 24 24
      main/lang/english/exercice.inc.php
  48. 9 8
      main/lang/spanish/admin.inc.php
  49. 7 1
      main/lang/spanish/tracking.inc.php
  50. 1 0
      main/lang/spanish_latin/trad4all.inc.php
  51. 3 3
      main/mySpace/lp_tracking.php
  52. 28 12
      main/mySpace/myStudents.php
  53. 261 93
      main/mySpace/myspace.lib.php
  54. 1 1
      main/newscorm/learnpathList.class.php
  55. 8 2
      main/newscorm/lp_ajax_save_item.php
  56. 6 0
      main/newscorm/lp_controller.php
  57. 18 8
      main/newscorm/lp_edit.php
  58. 5 5
      main/newscorm/lp_stats.php
  59. 20 2
      main/newscorm/scorm_api.php
  60. 10 1
      main/session/index.php
  61. 19 21
      main/social/message_for_group_form.inc.php
  62. 1 1
      main/social/profile.php

+ 167 - 0
main/admin/extra_field_options.php

@@ -0,0 +1,167 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+/**
+ *  @package chamilo.admin
+ */
+
+// Language files that need to be included.
+$language_file = array('admin');
+
+$cidReset = true;
+require_once '../inc/global.inc.php';
+
+$this_section = SECTION_PLATFORM_ADMIN;
+
+$type = isset($_REQUEST['type']) ? $_REQUEST['type'] : null;
+
+api_protect_admin_script();
+
+//Add the JS needed to use the jqgrid
+$htmlHeadXtra[] = api_get_jqgrid_js();
+
+// setting breadcrumbs
+$interbreadcrumb[]=array('url' => 'index.php','name' => get_lang('PlatformAdmin'));
+
+$tool_name = null;
+
+$action = isset($_GET['action']) ? $_GET['action'] : null;
+$field_id = isset($_GET['field_id']) ? $_GET['field_id'] : null;
+
+if (empty($field_id)) {
+    api_not_allowed();
+}
+if (!in_array($type, ExtraField::getValidExtraFieldTypes())) {
+    api_not_allowed();
+}
+
+$extra_field = new ExtraField($type);
+$extra_field_info = $extra_field->get($field_id);
+
+$check = Security::check_token('request');
+$token = Security::get_token();
+
+if ($action == 'add') {
+    $interbreadcrumb[]=array('url' => 'extra_fields.php?type='.$extra_field->type,'name' => $extra_field->pageName);
+    $interbreadcrumb[]=array('url' => 'extra_fields.php?type='.$extra_field->type.'&action=edit&id='.$extra_field_info['id'],'name' => $extra_field_info['field_display_text']);
+    $interbreadcrumb[]=array('url' => 'extra_field_options.php?type='.$extra_field->type.'&field_id='.$extra_field_info['id'], 'name' => get_lang('EditExtraFieldOptions'));
+    $interbreadcrumb[]=array('url' => '#','name' => get_lang('Add'));
+} elseif ($action == 'edit') {
+    $interbreadcrumb[]=array('url' => 'extra_fields.php?type='.$extra_field->type,'name' => $extra_field->pageName);
+    $interbreadcrumb[]=array('url' => 'extra_fields.php?type='.$extra_field->type.'&action=edit&id='.$extra_field_info['id'],'name' => $extra_field_info['field_display_text']);
+    $interbreadcrumb[]=array('url' => 'extra_field_options.php?type='.$extra_field->type.'&field_id='.$extra_field_info['id'], 'name' => get_lang('EditExtraFieldOptions'));
+
+    $interbreadcrumb[]=array('url' => '#','name' => get_lang('Edit'));
+} else {
+    $interbreadcrumb[]=array('url' => 'extra_fields.php?type='.$extra_field->type,'name' => $extra_field->pageName);
+    $interbreadcrumb[]=array('url' =>  'extra_fields.php?type='.$extra_field->type.'&action=edit&id='.$extra_field_info['id'],'name' => $extra_field_info['field_display_text']);
+    $interbreadcrumb[]=array('url' => '#','name' => get_lang('EditExtraFieldOptions'));
+}
+
+//jqgrid will use this URL to do the selects
+$params = 'field_id='.$field_id.'&type='.$extra_field->type;
+$url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_extra_field_options&'.$params;
+
+//The order is important you need to check the the $column variable in the model.ajax.php file
+$columns        = array(get_lang('Name'), get_lang('Value'),  get_lang('Order'), get_lang('Actions'));
+
+//Column config
+$column_model   = array(
+    array('name'=>'option_display_text', 'index'=>'option_display_text',      'width'=>'180',   'align'=>'left'),
+    array('name'=>'option_value',       'index'=>'option_value',          'width'=>'',  'align'=>'left','sortable'=>'false'),
+    array('name'=>'option_order',         'index'=>'option_order',              'width'=>'',  'align'=>'left','sortable'=>'false'),
+    array('name'=>'actions',            'index'=>'actions',                 'width'=>'100',  'align'=>'left','formatter'=>'action_formatter','sortable'=>'false')
+);
+//Autowidth
+$extra_params['autowidth'] = 'true';
+//height auto
+$extra_params['height'] = 'auto';
+
+//With this function we can add actions to the jgrid (edit, delete, etc)
+$action_links = 'function action_formatter(cellvalue, options, rowObject) {
+         return \'<a href="?action=edit&'.$params.'&id=\'+options.rowId+\'">'.Display::return_icon('edit.png',get_lang('Edit'),'',ICON_SIZE_SMALL).'</a>'.
+         '&nbsp;<a onclick="javascript:if(!confirm('."\'".addslashes(get_lang("ConfirmYourChoice"))."\'".')) return false;"  href="?sec_token='.$token.'&action=delete&'.$params.'&id=\'+options.rowId+\'">'.Display::return_icon('delete.png',get_lang('Delete'),'',ICON_SIZE_SMALL).'</a>'.
+         '\';
+ }';
+$htmlHeadXtra[]='
+<script>
+$(function() {
+    // grid definition see the $obj->display() function
+    '.Display::grid_js('extra_field_options',  $url, $columns, $column_model, $extra_params, array(), $action_links, true).'
+
+});
+</script>';
+
+// The header.
+Display::display_header($tool_name);
+
+echo Display::page_header($extra_field_info['field_display_text']);
+
+$obj = new ExtraFieldOption($extra_field->type);
+$obj->field_id = $field_id;
+
+// Action handling: Add
+switch ($action) {
+    case 'add':
+        if (api_get_session_id() != 0 && !api_is_allowed_to_session_edit(false, true)) {
+            api_not_allowed();
+        }
+        $url  = api_get_self().'?action='.Security::remove_XSS($_GET['action']).'&'.$params;
+        $form = $obj->return_form($url, 'add');
+
+        // The validation or display
+        if ($form->validate()) {
+            if ($check) {
+                $values = $form->exportValues();
+                $res    = $obj->save_one_item($values);
+                if ($res) {
+                    Display::display_confirmation_message(get_lang('ItemAdded'));
+                }
+            }
+            $obj->display();
+        } else {
+            /*echo '<div class="actions">';
+            echo '<a href="'.api_get_self().'">'.Display::return_icon('back.png',get_lang('Back'),'',ICON_SIZE_MEDIUM).'</a>';
+            echo '</div>';            */
+            $form->addElement('hidden', 'sec_token');
+            $form->setConstants(array('sec_token' => $token));
+            $form->display();
+        }
+        break;
+    case 'edit':
+        // Action handling: Editing
+        $url  = api_get_self().'?action='.Security::remove_XSS($_GET['action']).'&id='.intval($_GET['id']).'&'.$params;
+        $form = $obj->return_form($url, 'edit');
+
+        // The validation or display
+        if ($form->validate()) {
+            if ($check) {
+                $values = $form->exportValues();
+                $res    = $obj->update($values);
+                Display::display_confirmation_message(sprintf(get_lang('ItemUpdated'), $values['name']), false);
+            }
+            $obj->display();
+        } else {
+            /*echo '<div class="actions">';
+            echo '<a href="'.api_get_self().'">'.Display::return_icon('back.png',get_lang('Back'),'',ICON_SIZE_MEDIUM).'</a>';
+            echo '</div>';*/
+            $form->addElement('hidden', 'sec_token');
+            $form->setConstants(array('sec_token' => $token));
+            $form->display();
+        }
+        break;
+    case 'delete':
+        // Action handling: delete
+        if ($check) {
+            $res = $obj->delete($_GET['id']);
+            if ($res) {
+                Display::display_confirmation_message(get_lang('ItemDeleted'));
+            }
+        }
+        $obj->display();
+        break;
+    default:
+        $obj->display();
+        break;
+}
+Display :: display_footer();

+ 212 - 0
main/admin/extra_field_workflow.php

@@ -0,0 +1,212 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+/**
+ *  @package chamilo.admin
+ */
+
+// Language files that need to be included.
+$language_file = array('admin');
+
+$cidReset = true;
+require_once '../inc/global.inc.php';
+
+$this_section = SECTION_PLATFORM_ADMIN;
+
+$type = isset($_REQUEST['type']) ? $_REQUEST['type'] : null;
+
+api_protect_admin_script();
+
+// setting breadcrumbs
+$interbreadcrumb[] = array('url' => 'index.php','name' => get_lang('PlatformAdmin'));
+
+$tool_name = null;
+
+$action = isset($_GET['action']) ? $_GET['action'] : null;
+$field_id = isset($_GET['field_id']) ? $_GET['field_id'] : null;
+
+if (empty($field_id)) {
+    api_not_allowed();
+}
+if (!in_array($type, ExtraField::getValidExtraFieldTypes())) {
+    api_not_allowed();
+}
+
+$extraField = new ExtraField($type);
+$extraFieldInfo = $extraField->get($field_id);
+
+$check = Security::check_token('request');
+$token = Security::get_token();
+
+if ($action == 'add') {
+    $interbreadcrumb[]=array('url' => 'extra_fields.php?type='.$extraField->type,'name' => $extraField->pageName);
+    $interbreadcrumb[]=array('url' => 'extra_fields.php?type='.$extraField->type.'&action=edit&id='.$extraFieldInfo['id'],'name' => $extraFieldInfo['field_display_text']);
+    $interbreadcrumb[]=array('url' => 'extra_field_options.php?type='.$extraField->type.'&field_id='.$extraFieldInfo['id'], 'name' => get_lang('EditExtraFieldOptions'));
+    $interbreadcrumb[]=array('url' => '#','name' => get_lang('Add'));
+} elseif ($action == 'edit') {
+    $interbreadcrumb[]=array('url' => 'extra_fields.php?type='.$extraField->type,'name' => $extraField->pageName);
+    $interbreadcrumb[]=array('url' => 'extra_fields.php?type='.$extraField->type.'&action=edit&id='.$extraFieldInfo['id'],'name' => $extraFieldInfo['field_display_text']);
+    $interbreadcrumb[]=array('url' => 'extra_field_options.php?type='.$extraField->type.'&field_id='.$extraFieldInfo['id'], 'name' => get_lang('EditExtraFieldOptions'));
+
+    $interbreadcrumb[]=array('url' => '#','name' => get_lang('Edit'));
+} else {
+    $interbreadcrumb[]=array('url' => 'extra_fields.php?type='.$extraField->type,'name' => $extraField->pageName);
+    $interbreadcrumb[]=array('url' =>  'extra_fields.php?type='.$extraField->type.'&action=edit&id='.$extraFieldInfo['id'],'name' => $extraFieldInfo['field_display_text']);
+    $interbreadcrumb[]=array('url' => '#','name' => get_lang('EditExtraFieldOptions'));
+}
+
+$roleId = isset($_REQUEST['roleId']) ? $_REQUEST['roleId'] : null;
+
+//jqgrid will use this URL to do the selects
+$params = 'field_id='.$field_id.'&type='.$extraField->type.'&roleId='.$roleId;
+$paramsNoRole = 'field_id='.$field_id.'&type='.$extraField->type;
+
+//The order is important you need to check the the $column variable in the model.ajax.php file
+$columns = array(get_lang('Name'), get_lang('Value'),  get_lang('Order'), get_lang('Actions'));
+
+$htmlHeadXtra[]='<script>
+
+    function setHidden(obj) {
+        var name = $(obj).attr("name");
+        var hiddenName = "hidden_" + name;
+        if ($("#" + hiddenName).attr("value") == 1) {
+            $("#" + hiddenName).attr("value", 0);
+        } else {
+            $("#" + hiddenName).attr("value", 1);
+        }
+    }
+
+    function changeStatus(obj) {
+        var roleId = $(obj).find(":selected").val();
+        if (roleId != 0) {
+            window.location.replace("'.api_get_self().'?'.$paramsNoRole.'&roleId="+roleId);
+        }
+    }
+    $().ready( function() {
+        $(".select_all").on("click", function() {
+            $("#workflow :checkbox").prop("checked", 1);
+            $("#workflow :hidden").prop("value", 1);
+            return false;
+        });
+        $(".unselect_all").on("click", function() {
+            $("#workflow :checkbox").prop("checked", 0);
+            $("#workflow :hidden").prop("value", 0);
+            return false;
+        });
+    });
+</script>';
+
+// The header.
+Display::display_header($tool_name);
+
+echo Display::page_header($extraFieldInfo['field_display_text']);
+
+$obj = new ExtraFieldOption($type);
+$columns = array('option_display_text', 'option_value', 'option_order');
+$result  = Database::select('*', $obj->table, array('where' => array("field_id = ? " => $field_id), 'order'=>"option_order ASC"));
+
+$table = new HTML_Table(array('class' => 'data_table'));
+$column = 0;
+$row = 0;
+$table->setHeaderContents($row, $column, get_lang('CurrentStatus'));
+$column++;
+foreach ($result as $item) {
+    $table->setHeaderContents($row, $column, $item['option_display_text']);
+    $column++;
+}
+$row++;
+
+$form = new FormValidator('workflow', 'post', api_get_self().'?'.$params);
+$options = api_get_user_roles();
+$options[0] = get_lang('SelectAnOption');
+ksort($options);
+$form->addElement('select', 'status', get_lang('SelectRole'), $options, array('onclick' => 'changeStatus(this)'));
+
+$checks = $app['orm.em']->getRepository('ChamiloLMS\Entity\ExtraFieldOptionRelFieldOption')->findBy(array('fieldId' => $field_id, 'roleId' => $roleId));
+$includedFields = array();
+if (!empty($checks)) {
+    foreach ($checks as $availableField) {
+        $includedFields[$availableField->getFieldOptionId()][] = $availableField->getRelatedFieldOptionId();
+    }
+}
+
+foreach ($result as $item) {
+    $column = 0;
+    $table->setCellContents($row, $column, $item['option_display_text']);
+    $column++;
+    $value = null;
+
+    foreach ($result as $itemCol) {
+        $id = 'extra_field_status_'.$item['id'].'_'.$itemCol['id'];
+        $idForm = 'extra_field_status['.$item['id'].']['.$itemCol['id'].']';
+        $attributes = array('onclick' => 'setHidden(this)');
+        $value = 0;
+
+        if (isset($includedFields[$itemCol['id']]) && in_array($item['id'], $includedFields[$itemCol['id']])) {
+            $value = 1;
+            $attributes['checked'] = 'checked';
+        }
+
+        $element = Display::input('checkbox', $id, null, $attributes);
+        $table->setCellContents($row, $column, $element);
+        $form->addElement('hidden', 'hidden_'.$idForm, $value, array('id' => 'hidden_'.$id));
+        $column++;
+    }
+    $row++;
+}
+
+if (!empty($roleId)) {
+    $form->addElement('html', $table->toHtml());
+    $group = array();
+    $group[]= $form->createElement('button', 'submit', get_lang('Save'));
+    $group[]= $form->createElement('button', 'select_all', get_lang('SelectAll'), array('class' => 'btn select_all'));
+    $group[]= $form->createElement('button', 'unselect_all', get_lang('UnSelectAll'), array('class' => 'btn unselect_all'));
+    $form->addGroup($group, '', null, ' ');
+
+    $form->setDefaults(array('status' => $roleId));
+} else {
+    $form->addElement('button', 'submit', get_lang('Edit'));
+}
+
+$form->display();
+
+if ($form->validate()) {
+    $values = $form->getSubmitValues();
+    $result = $values['hidden_extra_field_status'];
+    if (!empty($result)) {
+        foreach ($result as $id => $items) {
+            foreach ($items as $subItemId => $value) {
+                $extraFieldOptionRelFieldOption = $app['orm.em']->getRepository('ChamiloLMS\Entity\ExtraFieldOptionRelFieldOption')->findOneBy(
+                    array(
+                    'fieldId' => $field_id,
+                    'fieldOptionId' => $subItemId,
+                    'roleId' => $roleId,
+                    'relatedFieldOptionId' => $id
+                    )
+                );
+
+                if ($value == 1) {
+                    if (empty($extraFieldOptionRelFieldOption)) {
+                        $extraFieldOptionRelFieldOption = new \ChamiloLMS\Entity\ExtraFieldOptionRelFieldOption();
+                        $extraFieldOptionRelFieldOption->setFieldId($field_id);
+                        $extraFieldOptionRelFieldOption->setFieldOptionId($subItemId);
+                        $extraFieldOptionRelFieldOption->setRelatedFieldOptionId($id);
+                        $extraFieldOptionRelFieldOption->setRoleId($roleId);
+                        $app['orm.ems']['db_write']->persist($extraFieldOptionRelFieldOption);
+                    }
+                } else {
+
+                    if ($extraFieldOptionRelFieldOption) {
+                        $app['orm.ems']['db_write']->remove($extraFieldOptionRelFieldOption);
+                    }
+                }
+
+            }
+        }
+        $app['orm.ems']['db_write']->flush();
+        header('Location:'.api_get_self().'?'.$params);
+        exit;
+    }
+}
+
+Display :: display_footer();

+ 218 - 0
main/admin/extra_fields.php

@@ -0,0 +1,218 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+/**
+ *  @package chamilo.admin
+ */
+
+// Language files that need to be included.
+$language_file = array('admin');
+
+$cidReset = true;
+
+$cidReset = true;
+require_once '../inc/global.inc.php';
+
+$extraFieldType =  isset($_REQUEST['type']) ? $_REQUEST['type'] : null;
+
+$this_section = SECTION_PLATFORM_ADMIN;
+
+api_protect_admin_script();
+
+//Add the JS needed to use the jqgrid
+$htmlHeadXtra[] = api_get_jqgrid_js();
+
+// setting breadcrumbs
+$interbreadcrumb[]=array('url' => 'index.php','name' => get_lang('PlatformAdmin'));
+
+$tool_name = null;
+
+$action = isset($_GET['action']) ? $_GET['action'] : null;
+if (!in_array($extraFieldType, ExtraField::getValidExtraFieldTypes())) {
+    api_not_allowed();
+}
+
+$check = Security::check_token('request');
+$token = Security::get_token();
+
+$obj = new ExtraField($extraFieldType);
+
+$obj->setupBreadcrumb($interbreadcrumb, $action);
+
+//jqgrid will use this URL to do the selects
+$url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_extra_fields&type='.$extraFieldType;
+
+//The order is important you need to check the the $column variable in the model.ajax.php file
+$columns = $obj->getJqgridColumnNames();
+
+//Column config
+$column_model = $obj->getJqgridColumnModel();
+
+//Autowidth
+$extra_params['autowidth'] = 'true';
+//height auto
+$extra_params['height'] = 'auto';
+$extra_params['sortname'] = 'field_order';
+
+$action_links = $obj->getJqgridActionLinks($token);
+
+$htmlHeadXtra[]='<script>
+$(function() {
+    // grid definition see the $obj->display() function
+    '.Display::grid_js($obj->type.'_fields',  $url, $columns, $column_model, $extra_params, array(), $action_links, true).'
+
+    $("#field_type").on("change", function() {
+        id = $(this).val();
+        switch(id) {
+            case "1":
+                $("#example").html("'.addslashes(Display::return_icon('userfield_text.png')).'");
+                break;
+            case "2":
+                $("#example").html("'.addslashes(Display::return_icon('userfield_text_area.png')).'");
+                break;
+            case "3":
+                $("#example").html("'.addslashes(Display::return_icon('add_user_field_howto.png')).'");
+                break;
+            case "4":
+                $("#example").html("'.addslashes(Display::return_icon('userfield_drop_down.png')).'");
+                break;
+            case "5":
+                $("#example").html("'.addslashes(Display::return_icon('userfield_multidropdown.png')).'");
+                break;
+            case "6":
+                $("#example").html("'.addslashes(Display::return_icon('userfield_data.png')).'");
+                break;
+            case "7":
+                $("#example").html("'.addslashes(Display::return_icon('userfield_date_time.png')).'");
+                break;
+            case "8":
+                $("#example").html("'.addslashes(Display::return_icon('userfield_doubleselect.png')).'");
+                break;
+            case "9":
+                $("#example").html("'.addslashes(Display::return_icon('userfield_divider.png')).'");
+                break;
+            case "10":
+                $("#example").html("'.addslashes(Display::return_icon('userfield_user_tag.png')).'");
+                break;
+            case "11":
+                $("#example").html("'.addslashes(Display::return_icon('userfield_data.png')).'");
+                break;
+        }
+    });
+});
+</script>';
+
+// The header.
+Display::display_header($tool_name);
+
+// Action handling: Add
+
+switch ($action) {
+    case 'add':
+        if (api_get_session_id() != 0 && !api_is_allowed_to_session_edit(false, true)) {
+            api_not_allowed();
+        }
+        $url  = api_get_self().'?type='.$obj->type.'&action='.Security::remove_XSS($_GET['action']);
+        $form = $obj->return_form($url, 'add');
+
+        // The validation or display
+        if ($form->validate()) {
+            //if ($check) {
+                $values = $form->exportValues();
+                $res    = $obj->save($values);
+                if ($res) {
+                    Display::display_confirmation_message(get_lang('ItemAdded'));
+                }
+            //}
+            $obj->display();
+        } else {
+            echo '<div class="actions">';
+            echo '<a href="'.api_get_self().'?type='.$obj->type.'">'.Display::return_icon('back.png',get_lang('Back'),'',ICON_SIZE_MEDIUM).'</a>';
+            echo '</div>';
+            $form->addElement('hidden', 'sec_token');
+            $form->setConstants(array('sec_token' => $token));
+            $form->display();
+        }
+        break;
+    case 'edit':
+        // Action handling: Editing
+        $url  = api_get_self().'?type='.$obj->type.'&action='.Security::remove_XSS($_GET['action']).'&id='.intval($_GET['id']);
+        $form = $obj->return_form($url, 'edit');
+
+        // The validation or display
+        if ($form->validate()) {
+            //if ($check) {
+                $values = $form->exportValues();
+                $res    = $obj->update($values);
+                Display::display_confirmation_message(sprintf(get_lang('ItemUpdated'), $values['field_variable']), false);
+            //}
+            $obj->display();
+        } else {
+            echo '<div class="actions">';
+            echo '<a href="'.api_get_self().'?type='.$obj->type.'">'.Display::return_icon('back.png',get_lang('Back'),'',ICON_SIZE_MEDIUM).'</a>';
+            echo '</div>';
+            $form->addElement('hidden', 'sec_token');
+            $form->setConstants(array('sec_token' => $token));
+            $form->display();
+        }
+        break;
+    case 'delete':
+        // Action handling: delete
+        //if ($check) {
+            $res = $obj->delete($_GET['id']);
+            if ($res) {
+                Display::display_confirmation_message(get_lang('ItemDeleted'));
+            }
+        //}
+        $obj->display();
+        break;
+    default:
+        $obj->display();
+        break;
+}
+Display :: display_footer();
+
+
+/*
+
+CREATE TABLE IF NOT EXISTS lp_field(
+    id INT NOT NULL auto_increment,
+    field_type int NOT NULL DEFAULT 1,
+    field_variable	varchar(64) NOT NULL,
+    field_display_text	varchar(64),
+    field_default_value text,
+    field_order int,
+    field_visible tinyint default 0,
+    field_changeable tinyint default 0,
+    field_filter tinyint default 0,
+    tms	DATETIME NOT NULL default '0000-00-00 00:00:00',
+    PRIMARY KEY(id)
+);
+DROP TABLE IF EXISTS lp_field_options;
+CREATE TABLE IF NOT EXISTS lp_field_options (
+    id int NOT NULL auto_increment,
+    field_id int NOT NULL,
+    option_value text,
+    option_display_text varchar(64),
+    option_order int,
+    tms	DATETIME NOT NULL default '0000-00-00 00:00:00',
+    priority VARCHAR(255),
+    priority_message VARCHAR(255),
+    PRIMARY KEY (id)
+);
+DROP TABLE IF EXISTS lp_field_values;
+CREATE TABLE IF NOT EXISTS lp_field_values(
+    id bigint NOT NULL auto_increment,
+    lp_id int unsigned NOT NULL,
+    field_id int NOT NULL,
+    field_value	text,
+    comment VARCHAR(100) default '',
+    tms DATETIME NOT NULL default '0000-00-00 00:00:00',
+    PRIMARY KEY(id)
+);
+
+ALTER TABLE lp_field_values ADD INDEX (lp_id, field_id);
+
+
+
+ */

+ 1 - 1
main/admin/specific_fields_add.php

@@ -74,4 +74,4 @@ if ($form->validate()) {
 // Display form
 Display::display_header($tool_name);
 $form->display();
-Display::display_footer();
+Display::display_footer();

+ 35 - 8
main/announcements/announcement_email.class.php

@@ -220,19 +220,26 @@ class AnnouncementEmail
      */
     public function message($receiverUserId)
     {
-        //$title = $this->announcement('title');
         $content = $this->announcement('content');
-        $content = stripslashes($content);
-        $content = AnnouncementManager::parse_content($receiverUserId, $content, $this->course('code'));
+        $session_id = $this->session_id;
+
+        $content = AnnouncementManager::parse_content(
+            $receiverUserId,
+            $content,
+            $this->course('code'),
+            $session_id
+        );
 
         $user_email = $this->sender('mail');
-        $course_param = api_get_cidreq();
+        //$course_param = api_get_cidreq();
+        // Build the link by hand because api_get_cidreq() doesn't accept course params
+        $course_param = 'cidReq='.api_get_course_id().'&amp;id_session='.$session_id.'&amp;gidReq='.api_get_group_id();
         $course_name = $this->course('title');
 
         $result = "<div>$content</div>";
 
         // Adding attachment
-        $attachment = $this->attachement();
+        $attachment = $this->attachment();
         if (!empty($attachment)) {
             $result .= '<br />';
             $result .= Display::url(
@@ -242,7 +249,11 @@ class AnnouncementEmail
         }
 
         $result .= '<hr />';
-        $sender_name = api_get_person_name($this->sender('firstName'), $this->sender('lastName'), PERSON_NAME_EMAIL_ADDRESS);
+        $sender_name = api_get_person_name(
+            $this->sender('firstName'),
+            $this->sender('lastName'),
+            PERSON_NAME_EMAIL_ADDRESS
+        );
         $result .= '<a href="mailto:'.$user_email.'">'.$sender_name.'</a><br/>';
         $result .= '<a href="'.api_get_path(WEB_CODE_PATH).'announcements/announcements.php?'.$course_param.'">'.$course_name.'</a><br/>';
 
@@ -254,7 +265,7 @@ class AnnouncementEmail
      *
      * @return array
      */
-    public function attachement()
+    public function attachment()
     {
         $result = array();
         $tbl_announcement_attachment = Database::get_course_table(TABLE_ANNOUNCEMENT_ATTACHMENT);
@@ -277,7 +288,7 @@ class AnnouncementEmail
     /**
      * Send emails to users.
      */
-    public function send()
+    public function send($sendToUsersInSession = false)
     {
         $sender = $this->sender();
         $subject = $this->subject();
@@ -294,6 +305,22 @@ class AnnouncementEmail
                 $sender['user_id']
             );
         }
+
+        if ($sendToUsersInSession) {
+            $sessionList = SessionManager::get_session_by_course($this->course['code']);
+            if (!empty($sessionList)) {
+                foreach ($sessionList as $sessionInfo) {
+                    $sessionId = $sessionInfo['id'];
+                    $message = $this->message(null, $sessionId);
+                    $userList = CourseManager::get_user_list_from_course_code($this->course['code'], $sessionId);
+                    if (!empty($userList)) {
+                        foreach ($userList as $user) {
+                            MessageManager::send_message_simple($user['user_id'], $subject, $message, $sender['user_id']);
+                        }
+                    }
+                }
+            }
+        }
         $this->log_mail_sent();
     }
 

+ 15 - 17
main/announcements/announcements.inc.php

@@ -1,15 +1,11 @@
 <?php
 /* For licensing terms, see /license.txt */
-/**
-
- */
 
 /**
  * Include file with functions for the announcements module.
  * @author jmontoya
  * @package chamilo.announcements
  * @todo use OOP
- *
  */
 class AnnouncementManager
 {
@@ -39,10 +35,11 @@ class AnnouncementManager
      * @param int       $userId
      * @param string    $content
      * @param string    $course_code
+     * @param int       $session_id
      *
      * @return mixed
      */
-    public static function parse_content($userId, $content, $course_code)
+    public static function parse_content($userId, $content, $course_code, $session_id = 0)
     {
         $readerInfo = api_get_user_info($userId);
         $courseInfo = api_get_course_info($course_code);
@@ -57,7 +54,8 @@ class AnnouncementManager
             }
         }
 
-        $courseLink = api_get_course_url();
+        $courseLink = api_get_course_url($course_code, $session_id);
+
         $data['user_name'] = $readerInfo['username'];
         $data['user_firstname'] = $readerInfo['firstname'];
         $data['user_lastname'] = $readerInfo['lastname'];
@@ -236,7 +234,7 @@ class AnnouncementManager
                 echo "<tr><th style='text-align:right'>$modify_icons</th></tr>";
             }
 
-            $content = self::parse_content($result['to_user_id'], $content, api_get_course_id());
+            $content = self::parse_content($result['to_user_id'], $content, api_get_course_id(), api_get_session_id());
 
             echo "<tr><td>$content</td></tr>";
 
@@ -306,7 +304,7 @@ class AnnouncementManager
         $sentTo,
         $file = array(),
         $file_comment = null,
-        $end_date = null, 
+        $end_date = null,
         $sendToUsersInSession = false
     ) {
         $_course = api_get_course_info();
@@ -372,7 +370,6 @@ class AnnouncementManager
                 self::addAnnouncementToAllUsersInSessions($last_id);
             }
 
-
             return $last_id;
         }
     }
@@ -490,6 +487,11 @@ class AnnouncementManager
         $sql_delete = "DELETE FROM $tbl_item_property WHERE c_id = $course_id AND ref='$id' AND tool='announcement'";
         Database::query($sql_delete);
 
+        if ($sendToUsersInSession) {
+            self::addAnnouncementToAllUsersInSessions($id);
+        }
+
+
         if ($sendToUsersInSession) {
             self::addAnnouncementToAllUsersInSessions($id);
         }
@@ -552,7 +554,6 @@ class AnnouncementManager
                 }
             }
         }
-
     }
 
     /**
@@ -709,7 +710,7 @@ class AnnouncementManager
      */
     public static function construct_not_selected_select_form($group_list = null, $user_list = null, $to_already_selected)
     {
-        echo '<select id="not_selected_form" name="not_selected_form[]" size="7" class="span4" multiple>';
+        echo '<select name="not_selected_form[]" size="7" class="span4" multiple>';
         // adding the groups to the select form
         if ($group_list) {
             foreach ($group_list as $this_group) {
@@ -1208,12 +1209,9 @@ class AnnouncementManager
         Database::query($sql);
     }
 
-    /**
-     * @param int $id
-     */
-    public static function send_email($id)
+    public static function send_email($annoucement_id, $sendToUsersInSession = false)
     {
-        $email = AnnouncementEmail::create(null, $id);
-        $email->send();
+        $email = AnnouncementEmail::create(null, $annoucement_id);
+        $email->send($sendToUsersInSession);
     }
 }

+ 7 - 5
main/announcements/announcements.php

@@ -365,7 +365,7 @@ if (api_is_allowed_to_edit(false,true) OR (api_get_course_setting('allow_user_ed
 
                 /*		MAIL FUNCTION	*/
                 if ($_POST['email_ann'] && empty($_POST['onlyThoseMails'])) {
-                    AnnouncementManager::send_email($id);
+                    AnnouncementManager::send_email($id, $sendToUsersInSession);
                 }
 				$message = get_lang('AnnouncementModified');
 			}
@@ -385,7 +385,7 @@ if (api_is_allowed_to_edit(false,true) OR (api_get_course_setting('allow_user_ed
 
                 /*		MAIL FUNCTION	*/
                 if ($_POST['email_ann'] && empty($_POST['onlyThoseMails'])) {
-                    AnnouncementManager::send_email($insert_id);
+                    AnnouncementManager::send_email($insert_id, $sendToUsersInSession);
                 }
 
 			} // end condition token
@@ -647,9 +647,11 @@ if ($display_form) {
 		echo '		</div>
 					</div>';
 
-        echo '<div class="control-group"><div class="controls">';
-        echo '<label class="checkbox" ><input name="send_to_users_in_session" type="checkbox" />'.get_lang('SendToUsersInSessions').'</label>';
-        echo '</div></div>';
+        if (api_get_session_id() == 0) {
+            echo '<div class="control-group"><div class="controls">';
+            echo '<label class="checkbox" ><input name="send_to_users_in_session" type="checkbox" />'.get_lang('SendToUsersInSessions').'</label>';
+            echo '</div></div>';
+        }
 
 		if (!isset($announcement_to_modify) ) $announcement_to_modify ='';
 

+ 21 - 23
main/auth/profile.php

@@ -236,27 +236,26 @@ if (is_profile_editable() && api_get_setting('user_selected_theme') == 'true') {
 
 //    EXTENDED PROFILE  this make the page very slow!
 if (api_get_setting('extended_profile') == 'true') {
-    if (!isset($_GET['type']) || (isset($_GET['type']) && $_GET['type'] == 'extended')) {
-        $width_extended_profile = 500;
-        //$form->addElement('html', '<a href="javascript: void(0);" onclick="javascript: show_extend();"> show_extend_profile</a>');
-        //$form->addElement('static', null, '<em>'.get_lang('OptionalTextFields').'</em>');
-        //    MY COMPETENCES
-        $form->add_html_editor('competences', get_lang('MyCompetences'), false, false, array('ToolbarSet' => 'Profile', 'Width' => $width_extended_profile, 'Height' => '130'));
-        //    MY DIPLOMAS
-        $form->add_html_editor('diplomas', get_lang('MyDiplomas'), false, false, array('ToolbarSet' => 'Profile', 'Width' => $width_extended_profile, 'Height' => '130'));
-        //    WHAT I AM ABLE TO TEACH
-        $form->add_html_editor('teach', get_lang('MyTeach'), false, false, array('ToolbarSet' => 'Profile', 'Width' => $width_extended_profile, 'Height' => '130'));
-
-        //    MY PRODUCTIONS
-        $form->addElement('file', 'production', get_lang('MyProductions'));
-        if ($production_list = UserManager::build_production_list(api_get_user_id(), '', true)) {
-            $form->addElement('static', 'productions_list', null, $production_list);
-        }
-        //    MY PERSONAL OPEN AREA
-        $form->add_html_editor('openarea', get_lang('MyPersonalOpenArea'), false, false, array('ToolbarSet' => 'Profile', 'Width' => $width_extended_profile, 'Height' => '350'));
-        $form->applyFilter(array('competences', 'diplomas', 'teach', 'openarea'), 'stripslashes');
-        $form->applyFilter(array('competences', 'diplomas', 'teach'), 'trim'); // openarea is untrimmed for maximum openness
+    $width_extended_profile = 500;
+    //$form->addElement('html', '<a href="javascript: void(0);" onclick="javascript: show_extend();"> show_extend_profile</a>');
+    //$form->addElement('static', null, '<em>'.get_lang('OptionalTextFields').'</em>');
+    //    MY COMPETENCES
+    $form->add_html_editor('competences', get_lang('MyCompetences'), false, false, array('ToolbarSet' => 'Profile', 'Width' => $width_extended_profile, 'Height' => '130'));
+    //    MY DIPLOMAS
+    $form->add_html_editor('diplomas', get_lang('MyDiplomas'), false, false, array('ToolbarSet' => 'Profile', 'Width' => $width_extended_profile, 'Height' => '130'));
+    //    WHAT I AM ABLE TO TEACH
+    $form->add_html_editor('teach', get_lang('MyTeach'), false, false, array('ToolbarSet' => 'Profile', 'Width' => $width_extended_profile, 'Height' => '130'));
+
+    //    MY PRODUCTIONS
+    $form->addElement('file', 'production', get_lang('MyProductions'));
+    if ($production_list = UserManager::build_production_list(api_get_user_id(), '', true)) {
+        $form->addElement('static', 'productions_list', null, $production_list);
     }
+    //    MY PERSONAL OPEN AREA
+    $form->add_html_editor('openarea', get_lang('MyPersonalOpenArea'), false, false, array('ToolbarSet' => 'Profile', 'Width' => $width_extended_profile, 'Height' => '350'));
+    $form->applyFilter(array('competences', 'diplomas', 'teach', 'openarea'), 'stripslashes');
+    $form->applyFilter(array('competences', 'diplomas', 'teach'), 'trim'); // openarea is untrimmed for maximum openness
+
 }
 
 //    PASSWORD, if auth_source is platform
@@ -336,9 +335,8 @@ function is_profile_editable() {
  * @return    The filename of the new production or FALSE if the upload has failed
  */
 function upload_user_production($user_id) {
-    $image_path = UserManager::get_user_picture_path_by_id($user_id, 'system', true);
-
-    $production_repository = $image_path['dir'].$user_id.'/';
+    $image_path = UserManager::get_user_picture_path_by_id($user_id, 'system');
+    $production_repository = $image_path['dir'];
 
     if (!file_exists($production_repository)) {
         @mkdir($production_repository, api_get_permissions_for_new_directories(), true);

+ 137 - 144
main/course_description/course_description_controller.php

@@ -2,191 +2,183 @@
 /* For licensing terms, see /license.txt */
 
 /**
+ * Class CourseDescriptionController
  * This file contains class used like controller, it should be included inside a dispatcher file (e.g: index.php)
  * @author Christian Fasanando <christian1827@gmail.com>
  * @package chamilo.course_description
  */
+class CourseDescriptionController
+{
+	private $toolname;
+	private $view;
 
-/**
- * Controller script. Prepares the common background variables to give to the scripts corresponding to
- * the requested action
- * @package chamilo.course_description 
- */
-class CourseDescriptionController { // extends Controller {	
-		
-	private $toolname;    
-	private $view; 
-	
 	/**
 	 * Constructor
 	 */
-	public function __construct() {		
-		$this->toolname = 'course_description';	
-		$this->view = new View($this->toolname);			
+	public function __construct()
+    {
+		$this->toolname = 'course_description';
+		$this->view = new View($this->toolname);
 	}
 
     /**
      * It's used for listing course description,
      * render to listing view
      * @param boolean   	true for listing history (optional)
-     * @param array 	message for showing by action['edit','add','destroy'] (optional) 
+     * @param array 	message for showing by action['edit','add','destroy'] (optional)
      */
-    public function listing($history=false, $messages=array()) {
+    public function listing($history=false, $messages=array())
+    {
         $course_description = new CourseDescription();
         $session_id = api_get_session_id();
-        $course_description->set_session_id($session_id);        
-        $data = array();		
-
-        $course_description_data = $course_description->get_description_data();	
-        	
+        $course_description->set_session_id($session_id);
+        $data = array();
+        $course_description_data = $course_description->get_description_data();
         $data['descriptions'] = $course_description_data['descriptions'];
         $data['default_description_titles'] = $course_description->get_default_description_title();
         $data['default_description_title_editable'] = $course_description->get_default_description_title_editable();
-        $data['default_description_icon'] = $course_description->get_default_description_icon();		
+        $data['default_description_icon'] = $course_description->get_default_description_icon();
         $data['messages'] = $messages;
-
-        // Fix for chrome XSS filter for videos in iframes - BT#7930
         $browser = api_get_navigator();
+
         if (strpos($data['descriptions'], '<iframe') !== false && $browser['name'] == 'Chrome') {
-            header('X-XSS-Protection: 0');
+            header("X-XSS-Protection: 0");
         }
-        
+
         // render to the view
         $this->view->set_data($data);
-        $this->view->set_layout('layout'); 
-        $this->view->set_template('listing');		       
-        $this->view->render();				
+        $this->view->set_layout('layout');
+        $this->view->set_template('listing');
+        $this->view->render();
     }
-	
-	/**
-	 * It's used for editing a course description,
-	 * render to listing or edit view
-	 * @param int description type
-	 */
-	public function edit($id, $description_type) {
-		$course_description = new CourseDescription();
-		$session_id = api_get_session_id();
-		$course_description->set_session_id($session_id);		
-		$data = array();      
+
+    /**
+     * It's used for editing a course description,
+     * render to listing or edit view
+     * @param int description type
+     */
+    public function edit($id, $description_type)
+    {
+        $course_description = new CourseDescription();
+        $session_id = api_get_session_id();
+        $course_description->set_session_id($session_id);
+        $data = array();
         $data['id'] = $id;
-        if (strtoupper($_SERVER['REQUEST_METHOD']) == "POST") {    	
-        	if (!empty($_POST['title']) && !empty($_POST['contentDescription'])) {
-        		
-        		$check = Security::check_token();        		
-        		if ($check) {
-        			$title = $_POST['title'];
-		        	if (api_get_setting('wcag_anysurfer_public_pages')=='true') {
-						$content = WCAG_Rendering::prepareXHTML();
-					} else {
-						$content = $_POST['contentDescription'];
-					}        	
-		        	$description_type = $_POST['description_type'];
-                    $id         = $_POST['id'];
-		        	$progress   = $_POST['progress'];
-                    
-					$course_description->set_description_type($description_type);
-		    		$course_description->set_title($title);
-		    		$course_description->set_content($content);
-                    
-		    		$course_description->set_progress($progress);                   
-		           			        		
-		        	$thematic_advance = $course_description->get_data_by_id($id);			        		        		
-                    
-                    if (!empty($thematic_advance)) {		   		        			
+        if (strtoupper($_SERVER['REQUEST_METHOD']) == "POST") {
+            if (!empty($_POST['title']) && !empty($_POST['contentDescription'])) {
+                $check = Security::check_token();
+                if ($check) {
+                    $title = $_POST['title'];
+                    if (api_get_setting('wcag_anysurfer_public_pages') == 'true') {
+                        $content = WCAG_Rendering::prepareXHTML();
+                    } else {
+                        $content = $_POST['contentDescription'];
+                    }
+                    $description_type = $_POST['description_type'];
+                    $id = $_POST['id'];
+                    $progress = $_POST['progress'];
+                    $course_description->set_description_type($description_type);
+                    $course_description->set_title($title);
+                    $course_description->set_content($content);
+                    $course_description->set_progress($progress);
+                    $thematic_advance = $course_description->get_data_by_id($id);
+
+                    if (!empty($thematic_advance)) {
                         $course_description->set_id($id);
-                        $affected_rows = $course_description->update();	        			
+                        $affected_rows = $course_description->update();
                     } else {
                         $affected_rows = $course_description->insert();
                     }
-		        	Security::clear_token();		        		        		
-        		}
-        		
-        		if ($affected_rows) {        			
-					$message['edit'] = true;        			
-				}
-				$this->listing(false,$message);		        		        		
-        		      		
-        	} else {
-        		$data['error'] = 1;
-        		$data['default_description_titles'] = $course_description->get_default_description_title();
-				$data['default_description_title_editable'] = $course_description->get_default_description_title_editable();
-				$data['default_description_icon'] = $course_description->get_default_description_icon();
-				$data['question'] = $course_description->get_default_question();
-				$data['information'] = $course_description->get_default_information();
-        		$data['description_title'] = $_POST['title'];
-        		$data['description_content'] = $_POST['contentDescription'];
-        		$data['description_type'] = $_POST['description_type'];
-        		$data['progress'] = $_POST['progress'];
-        		$data['descriptions'] = $course_description->get_data_by_id($_POST['id']);
-        		// render to the view
-				$this->view->set_data($data);
-				$this->view->set_layout('layout');
-				$this->view->set_template('edit');			        
-				$this->view->render();        		
-        	}
+                    Security::clear_token();
+                }
+
+                if ($affected_rows) {
+                    $message['edit'] = true;
+                }
+                $this->listing(false, $message);
+            } else {
+                $data['error'] = 1;
+                $data['default_description_titles'] = $course_description->get_default_description_title();
+                $data['default_description_title_editable'] = $course_description->get_default_description_title_editable();
+                $data['default_description_icon'] = $course_description->get_default_description_icon();
+                $data['question'] = $course_description->get_default_question();
+                $data['information'] = $course_description->get_default_information();
+                $data['description_title'] = $_POST['title'];
+                $data['description_content'] = $_POST['contentDescription'];
+                $data['description_type'] = $_POST['description_type'];
+                $data['progress'] = $_POST['progress'];
+                $data['descriptions'] = $course_description->get_data_by_id($_POST['id']);
+                // render to the view
+                $this->view->set_data($data);
+                $this->view->set_layout('layout');
+                $this->view->set_template('edit');
+                $this->view->render();
+            }
         } else {
-            
+
             $data['default_description_titles'] = $course_description->get_default_description_title();
             $data['default_description_title_editable'] = $course_description->get_default_description_title_editable();
             $data['default_description_icon'] = $course_description->get_default_description_icon();
             $data['question'] = $course_description->get_default_question();
             $data['information'] = $course_description->get_default_information();
-            
+
             $data['description_type'] = $description_type;
-            
-			if (!empty($id)) {				
-				if (isset($_GET['id_session'])) {
-					$session_id = intval($_GET['id_session']);
-				}				
-        		$course_description_data        = $course_description->get_data_by_id($id, null, $session_id);        		        		
-                $data['description_type']       = $course_description_data['description_type'];
-				$data['description_title']      = $course_description_data['description_title'];
-        		$data['description_content']    = $course_description_data['description_content'];        		
-        		$data['progress']               = $course_description_data['progress'];        		
-        		$data['descriptions']           = $course_description->get_data_by_description_type($description_type, null, $session_id);
-        	}
-        	// render to the view        					
-			$this->view->set_data($data);
-			$this->view->set_layout('layout');
-			$this->view->set_template('edit');			        
-			$this->view->render();        	
+
+            if (!empty($id)) {
+                if (isset($_GET['id_session'])) {
+                    $session_id = intval($_GET['id_session']);
+                }
+                $course_description_data = $course_description->get_data_by_id($id, null, $session_id);
+                $data['description_type'] = $course_description_data['description_type'];
+                $data['description_title'] = $course_description_data['description_title'];
+                $data['description_content'] = $course_description_data['description_content'];
+                $data['progress'] = $course_description_data['progress'];
+                $data['descriptions'] = $course_description->get_data_by_description_type($description_type, null, $session_id);
+            }
+
+            // render to the view
+            $this->view->set_data($data);
+            $this->view->set_layout('layout');
+            $this->view->set_template('edit');
+            $this->view->render();
         }
-	}
-	
-	/**
-	 * It's used for adding a course description,
-	 * render to listing or add view
-	 */
-	public function add() {
+    }
+
+    /**
+    * It's used for adding a course description,
+    * render to listing or add view
+    */
+	public function add()
+    {
 		$course_description = new CourseDescription();
 		$session_id = api_get_session_id();
 		$course_description->set_session_id($session_id);
-		
-		$data = array();            
-        if (strtoupper($_SERVER['REQUEST_METHOD']) == "POST") {        	
+
+		$data = array();
+        if (strtoupper($_SERVER['REQUEST_METHOD']) == "POST") {
         	if (!empty($_POST['title']) && !empty($_POST['contentDescription'])) {
-        		
-        		$check = Security::check_token();        		
+
+        		$check = Security::check_token();
         		if ($check) {
         			$title = $_POST['title'];
 		        	if (api_get_setting('wcag_anysurfer_public_pages')=='true') {
 						$content = WCAG_Rendering::prepareXHTML();
 					} else {
 						$content = $_POST['contentDescription'];
-					}        	
-		        	$description_type = $_POST['description_type'];	
-		        	if ($description_type >= ADD_BLOCK) {        		
+					}
+		        	$description_type = $_POST['description_type'];
+		        	if ($description_type >= ADD_BLOCK) {
 		        		$course_description->set_description_type($description_type);
 		        		$course_description->set_title($title);
 		        		$course_description->set_content($content);
 						$affected_rows = $course_description->insert(api_get_course_int_id());
 		        	}
-		        	Security::clear_token();	
-        		} 
-        		if ($affected_rows) {        			
-					$message['add'] = true;        			
+		        	Security::clear_token();
+        		}
+        		if ($affected_rows) {
+					$message['add'] = true;
 				}
-				$this->listing(false,$message);	        			
+				$this->listing(false,$message);
         	} else {
         		$data['error'] = 1;
         		$data['default_description_titles'] = $course_description->get_default_description_title();
@@ -196,43 +188,44 @@ class CourseDescriptionController { // extends Controller {
 				$data['information'] = $course_description->get_default_information();
         		$data['description_title'] = $_POST['title'];
         		$data['description_content'] = $_POST['contentDescription'];
-        		$data['description_type'] = $_POST['description_type'];        		
+        		$data['description_type'] = $_POST['description_type'];
 				$this->view->set_data($data);
 				$this->view->set_layout('layout');
-				$this->view->set_template('add');			        
+				$this->view->set_template('add');
 				$this->view->render();
-        	}        	        
-        } else {											
+        	}
+        } else {
 			$data['default_description_titles'] = $course_description->get_default_description_title();
 			$data['default_description_title_editable'] = $course_description->get_default_description_title_editable();
 			$data['default_description_icon'] = $course_description->get_default_description_icon();
 			$data['question'] = $course_description->get_default_question();
 			$data['information'] = $course_description->get_default_information();
-			$data['description_type'] = $course_description->get_max_description_type(); 				
+			$data['description_type'] = $course_description->get_max_description_type();
 			// render to the view
 			$this->view->set_data($data);
-			$this->view->set_layout('layout'); 
-			$this->view->set_template('add');			       
-			$this->view->render(); 	
+			$this->view->set_layout('layout');
+			$this->view->set_template('add');
+			$this->view->render();
         }
 	}
-	
+
 	/**
 	 * It's used for destroy a course description,
 	 * render to listing view
 	 * @param int description type
 	 */
-	public function destroy($id) {		
+	public function destroy($id)
+    {
 		$course_description = new CourseDescription();
 		$session_id = api_get_session_id();
-		$course_description->set_session_id($session_id);		
+		$course_description->set_session_id($session_id);
 		if (!empty($id)) {
 			$course_description->set_id($id);
 			$affected_rows = $course_description->delete();
-		}		
-		if ($affected_rows) {        			
-			$message['destroy'] = true;        			
 		}
-		$this->listing(false, $message);		
+		if ($affected_rows) {
+			$message['destroy'] = true;
+		}
+		$this->listing(false, $message);
 	}
 }

+ 5 - 3
main/document/document.php

@@ -345,8 +345,7 @@ switch ($action) {
             $parent_id = $document_info['parent_id'];
             $my_path = UserManager::get_user_picture_path_by_id(
                 api_get_user_id(),
-                'system',
-                true
+                'system'
             );
             $user_folder = $my_path['dir'].'my_files/';
             $my_path = null;
@@ -1511,11 +1510,13 @@ if (isset($documentAndFolders) && is_array($documentAndFolders)) {
     if ($groupId == 0 || GroupManager::user_has_access($userId, $groupId, GroupManager::GROUP_TOOL_DOCUMENTS)) {
         $count = 1;
         $countedPaths = array();
+        $countedPaths = array();
+
         foreach ($documentAndFolders as $key => $document_data) {
             $row = array();
             $row['id'] = $document_data['id'];
             $row['type'] = $document_data['filetype'];
-
+            
             // If the item is invisible, wrap it in a span with class invisible.
             $is_visible = DocumentManager::is_visible_by_id(
                 $document_data['id'],
@@ -1607,6 +1608,7 @@ if (isset($documentAndFolders) && is_array($documentAndFolders)) {
             $row[] = $size;
             $row[] = $document_name;
 
+            $total_size = $total_size + $size;
             if (!isset($countedPaths[$document_data['path']])) {
                 $total_size = $total_size + $size;
                 $countedPaths[$document_data['path']] = true;

+ 83 - 5
main/exercice/exercise.class.php

@@ -3410,6 +3410,7 @@ class Exercise
         );
         return $return_array;
     }
+
     /**
      * Sends a notification when a user ends an examn
      *
@@ -3911,13 +3912,13 @@ class Exercise
                 $isRandomByCategory = $this->selectRandomByCat();
                 // on tri les categories en fonction du terme entre [] en tete de la description de la categorie
                 /*
-                 * ex de catégories :
+                 * ex de catégories :
                  * [biologie] Maitriser les mecanismes de base de la genetique
                  * [biologie] Relier les moyens de depenses et les agents infectieux
                  * [biologie] Savoir ou est produite l'enrgie dans les cellules et sous quelle forme
                  * [chimie] Classer les molles suivant leur pouvoir oxydant ou reacteur
-                 * [chimie] Connaître la denition de la theoie acide/base selon Brönsted
-                 * [chimie] Connaître les charges des particules
+                 * [chimie] Connaître la denition de la theoie acide/base selon Brönsted
+                 * [chimie] Connaître les charges des particules
                  * On veut dans l'ordre des groupes definis par le terme entre crochet au debut du titre de la categorie
                 */
                 // If test option is Grouped By Categories
@@ -4179,8 +4180,8 @@ class Exercise
     }
 
     /**
-     * @return string
-     */
+    * @return string
+    */
     public function get_formated_title()
     {
         return api_html_entity_decode($this->selectTitle());
@@ -4213,4 +4214,81 @@ class Exercise
         return api_htmlentities($in_title);
     }
 
+    /**
+     * @param int $courseId
+     * @param int $sessionId
+     * @return array exercises
+     */
+    public function getExercisesByCouseSession($courseId, $sessionId)
+    {
+        $courseId = intval($courseId);
+        $sessionId = intval($sessionId);
+
+        $tbl_quiz = Database::get_course_table(TABLE_QUIZ_TEST);
+        $sql = "SELECT * FROM $tbl_quiz cq
+                WHERE
+                    cq.c_id = %s AND
+                    (cq.session_id = %s OR cq.session_id = 0) AND
+                    cq.active = 0
+                ORDER BY cq.id";
+        $sql = sprintf($sql, $courseId, $sessionId);
+
+        $result = Database::query($sql);
+
+        $rows = array();
+        while ($row = Database::fetch_array($result, 'ASSOC')) {
+            $rows[] = $row;
+        }
+
+        return $rows;
+    }
+
+    /**
+     *
+     * @param int $courseId
+     * @param int $sessionId
+     * @param array $quizId
+     * @return array exercises
+     */
+    public function getExerciseAndResult($courseId, $sessionId, $quizId = array())
+    {
+        if (empty($quizId)) {
+            return array();
+        }
+
+        $sessionId = intval($sessionId);
+
+        $ids = is_array($quizId) ? $quizId : array($quizId);
+        $ids = array_map('intval', $ids);
+        $ids = implode(',', $ids);
+        $track_exercises = Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);
+        if ($sessionId != 0) {
+            $sql = "SELECT * FROM $track_exercises te "
+              . "INNER JOIN c_quiz cq ON cq.id = te.exe_exo_id "
+              . "INNER JOIN course c ON te.exe_cours_id = c.code AND c.id = cq.c_id "
+              . "WHERE "
+              . "c.id = %s AND "
+              . "te.session_id = %s AND "
+              . "cq.id IN (%s) "
+              . "ORDER BY cq.id ";
+
+            $sql = sprintf($sql, $courseId, $sessionId, $ids);
+        } else {
+            $sql = "SELECT * FROM $track_exercises te "
+              . "INNER JOIN c_quiz cq ON cq.id = te.exe_exo_id "
+              . "INNER JOIN course c ON te.exe_cours_id = c.code AND c.id = cq.c_id "
+              . "WHERE "
+              . "c.id = %s AND "
+              . "cq.id IN (%s) "
+              . "ORDER BY cq.id ";
+            $sql = sprintf($sql, $courseId, $ids);
+        }
+        $result = Database::query($sql);
+        $rows = array();
+        while ($row = Database::fetch_array($result, 'ASSOC')) {
+            $rows[] = $row;
+        }
+
+        return $rows;
+    }
 }

+ 14 - 6
main/exercice/exercise.lib.php

@@ -1482,6 +1482,7 @@ function convert_score($score, $weight) {
  * @param   int     0 = only inactive exercises
  *                  1 = only active exercises,
  *                  2 = all exercises
+ *                  3 = active <> -1
  * @return  array   array with exercise data
  */
 function get_all_exercises($course_info = null, $session_id = 0, $check_publication_dates = false, $search_exercise = '', $search_all_sessions = false, $active = 2) {
@@ -1511,7 +1512,9 @@ function get_all_exercises($course_info = null, $session_id = 0, $check_publicat
 
     //Show courses by active status
     $active_sql = '';
-    if ($active != 2) {
+    if ($active == 3) {
+        $active_sql = ' active <> -1 AND';
+    } else if ($active != 2) {
         $active_sql = sprintf(' active = %d AND', $active);
     }
 
@@ -1529,13 +1532,18 @@ function get_all_exercises($course_info = null, $session_id = 0, $check_publicat
 }
 /**
  * Get exercise information by id
- * @param int Exercise Id
- * @return array Exercise info
+ * @param int $exerciseId Exercise Id
+ * @param int $courseId The course ID (necessary as c_quiz.id is not unique)
+ * @return array Exercise info 
  */
-function get_exercise_by_id($exerciseId = 0)
-{
+function get_exercise_by_id($exerciseId = 0, $courseId = null) {
     $TBL_EXERCICES = Database :: get_course_table(TABLE_QUIZ_TEST);
-    $conditions  = array('where' => array('id = ?' => array($exerciseId)));
+    if (empty($courseId)) {
+        $courseId = api_get_course_int_id();
+    } else {
+        $courseId = intval($courseId);
+    }
+    $conditions  = array('where' => array('id = ?' => array($exerciseId), ' AND c_id = ? ' => $courseId));
     return Database::select('*', $TBL_EXERCICES, $conditions);
 }
 /**

+ 1 - 1
main/exercice/exercise_show.php

@@ -166,7 +166,7 @@ if (!empty($track_exercise_info)) {
 	// if the results_disabled of the Quiz is 1 when block the script
 	$result_disabled		= $track_exercise_info['results_disabled'];
 
-	if (!(api_is_platform_admin() || api_is_course_admin()) ) {
+	if (!(api_is_platform_admin() || api_is_course_admin() || api_is_course_coach()) ) {
 		if ($result_disabled == 1) {
 			//api_not_allowed();
 			$show_results = false;

+ 0 - 1
main/exercice/overview.php

@@ -237,7 +237,6 @@ if ($objExercise->selectAttempts()) {
     if ($visible_return['value'] == true) {
         $message .=   $attempt_message;
     }
-	//$options.= $attempt_message; //Display::div($attempt_message, array('class'=>"offset2 span2"));
 }
 
 if ($time_control) {

+ 4 - 3
main/group/group.php

@@ -215,15 +215,16 @@ if (api_get_setting('allow_group_categories') == 'true') {
         }
 
         echo Display::page_header($category['title'].' '. $label.' '.$actions);
-        echo '<p style="margin: 0px;margin-left: 50px;">'.$category['description'].'</p><p/>';
-        GroupManager ::process_groups($group_list, $category['id']);
+        echo $category['description'];
+        GroupManager::process_groups($group_list, $category['id']);
     }
 } else {
     $group_list = GroupManager::get_group_list();
     GroupManager::process_groups($group_list);
+
 }
 
-if (!isset ($_GET['origin']) || $_GET['origin'] != 'learnpath') {
+if (!isset($_GET['origin']) || $_GET['origin'] != 'learnpath') {
     Display::display_footer();
 }
 $_SESSION['_gid'] = 0;

+ 102 - 67
main/inc/ajax/course.ajax.php

@@ -54,38 +54,46 @@ switch ($action) {
         break;
     case 'search_course':
         if (api_is_platform_admin()) {
-            $courseList = Coursemanager::get_courses_list(
-                0,
-                10,
-                1, //$orderby = 1,
-                'ASC',
-                -1,
-                $_REQUEST['q'],
-                null,
-                true
-            );
+            if (!empty($_GET['session_id']) && intval($_GET['session_id'])) {
+                //if session is defined, lets find only courses of this session
+                $courseList = SessionManager::get_course_list_by_session_id(
+                    $_GET['session_id'],
+                    $_GET['q']
+                );
+            } else {
+                //if session is not defined lets search all courses STARTING with $_GET['q']
+                //TODO change this function to search not only courses STARTING with $_GET['q']
+                $courseList = Coursemanager::get_courses_list(
+                    0, //offset
+                    0, //howMany
+                    1, //$orderby = 1
+                    'ASC',
+                    -1,  //visibility
+                    $_GET['q'],
+                    null, //$urlId
+                    true //AlsoSearchCode
+                );
+            }
+
             $results = array();
 
             require_once api_get_path(LIBRARY_PATH).'course_category.lib.php';
 
-            foreach ($courseList as $courseInfo) {
-                $title = $courseInfo['title'];
+            if (!empty($courseList)) {
 
-                if (!empty($courseInfo['category_code'])) {
-                    $parents = getParentsToString($courseInfo['category_code']);
-                    $title = $parents.$courseInfo['title'];
-                }
+                foreach ($courseList as $courseInfo) {
+                    $title = $courseInfo['title'];
 
-                $results[] = array(
-                    'id' => $courseInfo['id'],
-                    'text' => $title
-                );
-            }
+                    if (!empty($courseInfo['category_code'])) {
+                        $parents = getParentsToString($courseInfo['category_code']);
+                        $title = $parents.$courseInfo['title'];
+                    }
 
-            if (!empty($results)) {
-                /*foreach ($results as &$item) {
-                    $item['id'] = $item['code'];
-                }*/
+                    $results[] = array(
+                        'id' => $courseInfo['id'],
+                        'text' => $title
+                        );
+                }
                 echo json_encode($results);
             } else {
                 echo json_encode(array());
@@ -93,8 +101,7 @@ switch ($action) {
         }
         break;
     case 'search_course_by_session':
-        if (api_is_platform_admin())
-        {
+        if (api_is_platform_admin()) {
             $results = SessionManager::get_course_list_by_session_id($_GET['session_id'], $_GET['q']);
 
             //$results = SessionManager::get_sessions_list(array('s.name LIKE' => "%".$_REQUEST['q']."%"));
@@ -118,15 +125,44 @@ switch ($action) {
             }
         }
         break;
+    case 'search_course_by_session_all':
+        if (api_is_platform_admin()) {
+            if ($_GET['session_id'] == 'TODOS' || $_GET['session_id'] == 'T') {
+                $_GET['session_id'] = '%';
+            }
+
+            $results = SessionManager::get_course_list_by_session_id_like(
+                $_GET['session_id'],
+                $_GET['q']
+            );
+            $results2 = array();
+            if (!empty($results)) {
+                foreach ($results as $item) {
+                    $item2 = array();
+                    foreach ($item as $id => $internal) {
+                        if ($id == 'id') {
+                            $item2[$id] = $internal;
+                        }
+                        if ($id == 'title') {
+                            $item2['text'] = $internal;
+                        }
+                    }
+                    $results2[] = $item2;
+                }
+                echo json_encode($results2);
+            } else {
+                echo json_encode(array());
+            }
+        }
+        break;
     case 'search_user_by_course':
-        if (api_is_platform_admin())
-        {
+        if (api_is_platform_admin()) {
             $user                   = Database :: get_main_table(TABLE_MAIN_USER);
             $session_course_user    = Database :: get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
 
             $course = api_get_course_info_by_id($_GET['course_id']);
 
-            $sql = "SELECT u.user_id as id, u.username, u.lastname, u.firstname 
+            $sql = "SELECT u.user_id as id, u.username, u.lastname, u.firstname
                     FROM $user u
                     INNER JOIN $session_course_user r ON u.user_id = r.id_user
                     WHERE id_session = %d AND course_code =  '%s'
@@ -135,64 +171,63 @@ switch ($action) {
             $sql_query = sprintf($sql, $_GET['session_id'], $course['code'], $needle, $needle, $needle);
 
             $result = Database::query($sql_query);
-            while ($user = Database::fetch_assoc($result)) 
-            {
-                $data[] = array('id' => $user['id'], 'text' => $user['username'] );
+            while ($user = Database::fetch_assoc($result)) {
+                $data[] = array('id' => $user['id'], 'text' => $user['username'] . ' (' . $user['firstname'] . ' ' . $user['lastname'] . ')');
 
             }
-            if (!empty($data)) 
-            {
+            if (!empty($data)) {
                 echo json_encode($data);
-            } else
-            {
+            } else {
                 echo json_encode(array());
             }
         }
         break;
     case 'search_exercise_by_course':
-        if (api_is_platform_admin())
-        {
-
+        if (api_is_platform_admin()) {
             $course = api_get_course_info_by_id($_GET['course_id']);
             require_once api_get_path(SYS_CODE_PATH).'exercice/exercise.lib.php';
             $session_id = (!empty($_GET['session_id'])) ?  intval($_GET['session_id']) : 0 ;
-            $exercises = get_all_exercises($course, $session_id, false, $_GET['q'], true, 2);
+            $exercises = get_all_exercises($course, $session_id, false, $_GET['q'], true, 3);
 
-            foreach ($exercises as $exercise)    
-            {
-                $data[] = array('id' => $exercise['id'], 'text' => $exercise['title'] );
+            foreach ($exercises as $exercise) {
+                $data[] = array('id' => $exercise['id'], 'text' => html_entity_decode($exercise['title']) );
             }
-            if (!empty($data)) 
-            {
+            if (!empty($data)) {
+                $data[] = array('id' => 'T', 'text' => 'TODOS');
                 echo json_encode($data);
-            } else
-            {
-                echo json_encode(array());
+            } else {
+                echo json_encode(array(array('id' => 'T', 'text' => 'TODOS')));
             }
         }
         break;
     case 'search_survey_by_course':
-        if (api_is_platform_admin())
-        {
-            $survey     = Database :: get_course_table(TABLE_SURVEY);
-            
-            $sql = "SELECT survey_id as id, title
-            FROM $survey 
-            WHERE c_id = %d 
-            AND session_id = %d 
-            AND title LIKE '%s'";
-
-            $sql_query = sprintf($sql, intval($_GET['course_id']), intval($_GET['session_id']), '%' . $_GET['q'] .'%');
+        if (api_is_platform_admin()) {
+            $survey = Database :: get_course_table(TABLE_SURVEY);
+
+            $sql = "SELECT survey_id as id, title, anonymous
+                    FROM $survey
+                    WHERE
+                      c_id = %d AND
+                      session_id = %d AND
+                      title LIKE '%s'";
+
+            $sql_query = sprintf(
+                $sql,
+                intval($_GET['course_id']),
+                intval($_GET['session_id']),
+                '%' . Database::escape_string($_GET['q']).'%'
+            );
             $result = Database::query($sql_query);
-            while ($survey = Database::fetch_assoc($result)) 
-            {
-                $data[] = array('id' => $survey['id'], 'text' => $survey['title'] );
+            while ($survey = Database::fetch_assoc($result)) {
+                $survey['title'] .= ($survey['anonymous'] == 1) ? ' (' . get_lang('Anonymous') . ')' : '';
+                $data[] = array(
+                    'id' => $survey['id'],
+                    'text' => strip_tags(html_entity_decode($survey['title']))
+                );
             }
-            if (!empty($data)) 
-            {
+            if (!empty($data)) {
                 echo json_encode($data);
-            } else
-            {
+            } else {
                 echo json_encode(array());
             }
         }

+ 39 - 33
main/inc/ajax/course_home.ajax.php

@@ -5,7 +5,7 @@
  * Responses to AJAX calls
 */
 $action = $_GET['a'];
-$now    = time();
+$now = time();
 
 switch ($action) {
 	case 'set_visibility':
@@ -36,7 +36,7 @@ switch ($action) {
             $requested_view    = ($tool_visibility == 0 ) ? 'visible.png' : 'invisible.png';
             $requested_visible = ($tool_visibility == 0 ) ? 1 : 0;
 			//HIDE AND REACTIVATE TOOL
-			if ($_GET["id"]==strval(intval($_GET["id"]))) {
+            if ($_GET["id"] == strval(intval($_GET["id"]))) {
 
 				/* -- session condition for visibility
 				 if (!empty($session_id)) {
@@ -66,15 +66,12 @@ switch ($action) {
 			echo json_encode($response_data);
         }
         break;
-
 	case 'show_course_information' :
-
 		$language_file = array('course_description');
 		require_once '../global.inc.php';
 
 		// Get the name of the database course.
 		$tbl_course_description = Database::get_course_table(TABLE_COURSE_DESCRIPTION);
-
 		$course_info = api_get_course_info($_GET['code']);
 
 		if ($course_info['visibility'] != COURSE_VISIBILITY_OPEN_WORLD) {
@@ -85,22 +82,29 @@ switch ($action) {
 		echo Display::tag('h2', $course_info['name']);
 		echo '<br />';
 
-		$sql = "SELECT * FROM $tbl_course_description WHERE c_id = ".$course_info['real_id']." AND session_id = 0 ORDER BY id";
+		$sql = "SELECT * FROM $tbl_course_description
+		        WHERE c_id = ".$course_info['real_id']." AND session_id = 0
+		        ORDER BY id";
 		$result = Database::query($sql);
 		if (Database::num_rows($result) > 0 ) {
 		    while ($description = Database::fetch_object($result)) {
 			    $descriptions[$description->id] = $description;
 		    }
             // Function that displays the details of the course description in html.
-		    echo CourseManager::get_details_course_description_html($descriptions, api_get_system_encoding(), false);
+		    echo CourseManager::get_details_course_description_html(
+                $descriptions,
+                api_get_system_encoding(),
+                false
+            );
 		} else {
 		    echo get_lang('NoDescription');
 		}
 	    break;
-    /**
-     * @todo this functions need to belong to a class or a special wrapper to process the AJAX petitions from the jqgrid
-     */
     case 'session_courses_lp_default':
+        /**
+         * @todo this functions need to belong to a class or a special
+         * wrapper to process the AJAX petitions from the jqgrid
+         */
 
         require_once '../global.inc.php';
         require_once api_get_path(SYS_CODE_PATH).'newscorm/learnpathList.class.php';
@@ -128,18 +132,15 @@ switch ($action) {
             }
         }
 
-        if(!$sidx) $sidx = 1;
+        if (!$sidx) {
+            $sidx = 1;
+        }
 
         $start = $limit*$page - $limit;
         $course_list    = SessionManager::get_course_list_by_session_id($session_id);
         $count = 0;
 
         foreach ($course_list as $item) {
-            if (isset($course_id) && !empty($course_id)) {
-                if ($course_id != $item['id']) {
-                    continue;
-                }
-            }
             $list               = new LearnpathList(api_get_user_id(), $item['code'], $session_id);
             $flat_list          = $list->get_flat_list();
             $lps[$item['code']] = $flat_list;
@@ -262,8 +263,8 @@ switch ($action) {
                 }
             }
 
-            $list               = new LearnpathList(api_get_user_id(),$item['code'], $session_id, 'publicated_on DESC');
-            $flat_list          = $list->get_flat_list();
+            $list = new LearnpathList(api_get_user_id(),$item['code'], $session_id, 'publicated_on DESC');
+            $flat_list = $list->get_flat_list();
             $lps[$item['code']] = $flat_list;
             $item['title'] = Display::url($item['title'],api_get_path(WEB_COURSE_PATH).$item['directory'].'/?id_session='.$session_id,array('target'=>SESSION_LINK_TARGET));
 
@@ -330,12 +331,12 @@ switch ($action) {
             }
         }
 
-        if($count > 0 && $limit > 0) {
+        if ($count > 0 && $limit > 0) {
             $total_pages = ceil($count/$limit);
         } else {
             $total_pages = 0;
         }
-        $response->total    = $total_pages;
+        $response->total = $total_pages;
         if ($page > $total_pages) {
             $response->page = $total_pages;
         } else {
@@ -344,10 +345,7 @@ switch ($action) {
         $response->records = $count;
         echo json_encode($response);
         break;
-
-
     case 'session_courses_lp_by_course':
-
         require_once '../global.inc.php';
         require_once api_get_path(SYS_CODE_PATH).'newscorm/learnpathList.class.php';
 
@@ -374,10 +372,11 @@ switch ($action) {
             }
         }
 
-        if(!$sidx) $sidx =1;
+        if (!$sidx) {
+            $sidx = 1;
+        }
 
         $start = $limit*$page - $limit;
-
         $course_list = SessionManager::get_course_list_by_session_id($session_id);
 
         $count = 0;
@@ -389,10 +388,13 @@ switch ($action) {
                 }
             }
 
-            $list               = new LearnpathList(api_get_user_id(),$item['code'],$session_id);
-            $flat_list          = $list->get_flat_list();
+            $list = new LearnpathList(api_get_user_id(),$item['code'],$session_id);
+            $flat_list = $list->get_flat_list();
             $lps[$item['code']] = $flat_list;
-            $item['title']      = Display::url($item['title'],api_get_path(WEB_COURSE_PATH).$item['directory'].'/?id_session='.$session_id, array('target'=>SESSION_LINK_TARGET));
+            $item['title']      = Display::url(
+                $item['title'],
+                api_get_path(WEB_COURSE_PATH).$item['directory'].'/?id_session='.$session_id, array('target'=>SESSION_LINK_TARGET)
+            );
             foreach($flat_list as $lp_id => $lp_item) {
                 $temp[$count]['id']= $lp_id;
                 $lp_url = api_get_path(WEB_CODE_PATH).'newscorm/lp_controller.php?cidReq='.$item['code'].'&id_session='.$session_id.'&lp_id='.$lp_id.'&action=view';
@@ -426,7 +428,11 @@ switch ($action) {
                         continue;
                     }
                 }
-                $temp[$count]['cell'] = array($date, $item['title'], Display::url($icons.' '.$lp_item['lp_name'], $lp_url, array('target'=>SESSION_LINK_TARGET)));
+                $temp[$count]['cell'] = array(
+                    $date,
+                    $item['title'],
+                    Display::url($icons.' '.$lp_item['lp_name'], $lp_url, array('target'=>SESSION_LINK_TARGET))
+                );
                 $temp[$count]['course'] = strip_tags($item['title']);
                 $temp[$count]['lp']     = $lp_item['lp_name'];
                 $temp[$count]['date']   = $lp_item['publicated_on'];
@@ -450,14 +456,14 @@ switch ($action) {
             }
         }
 
-        if($count > 0 && $limit > 0) {
-            $total_pages = ceil($count/$limit);
+        if ($count > 0 && $limit > 0) {
+            $total_pages = ceil($count / $limit);
         } else {
             $total_pages = 0;
         }
-        $response->total    = $total_pages;
+        $response->total = $total_pages;
         if ($page > $total_pages) {
-            $response->page= $total_pages;
+            $response->page = $total_pages;
         } else {
             $response->page = $page;
         }

+ 1 - 1
main/inc/ajax/document.ajax.php

@@ -33,7 +33,7 @@ switch($action) {
                 $_FILES,
                 $_POST['curdirpath'],
                 $file['name'],
-                null,
+                'overwrite',
                 0,
                 $ifExists,
                 false,

+ 30 - 0
main/inc/ajax/extra_field.ajax.php

@@ -0,0 +1,30 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+$language_file = array('admin', 'registration', 'userInfo');
+require_once '../global.inc.php';
+$action = $_GET['a'];
+
+switch ($action) {
+    case 'get_second_select_options':
+        $type = isset($_REQUEST['type']) ? $_REQUEST['type'] : null;
+        $field_id = isset($_REQUEST['field_id']) ? $_REQUEST['field_id'] : null;
+        $option_value_id = isset($_REQUEST['option_value_id']) ? $_REQUEST['option_value_id'] : null;
+
+        if (!empty($type) && !empty($field_id) && !empty($option_value_id)) {
+            $field_options = new ExtraFieldOption($type);
+            echo $field_options->get_second_select_field_options_by_field($field_id, $option_value_id, true);
+        }
+        break;
+    case 'search_tags':
+        $type = isset($_REQUEST['type']) ? $_REQUEST['type'] : null;
+        $fieldId = isset($_REQUEST['field_id']) ? $_REQUEST['field_id'] : null;
+        $tag = isset($_REQUEST['tag']) ? $_REQUEST['tag'] : null;
+        $extraFieldOption = new ExtraFieldOption($type);
+        echo $extraFieldOption->getSearchOptionsByField($tag, $fieldId, 10, 'json');
+        break;
+    default:
+        exit;
+        break;
+}
+exit;

+ 138 - 28
main/inc/ajax/model.ajax.php

@@ -316,6 +316,7 @@ switch ($action) {
         break;
     case 'get_sessions':
         $courseId = isset($_GET['course_id']) && !empty($_GET['course_id']) ? intval($_GET['course_id']) : null;
+        $where_condition = str_replace('category_name', 'sc.name', $where_condition);
         if (!empty($courseId)) {
             $whereCondition .= " AND c.id = $courseId";
         }
@@ -324,26 +325,33 @@ switch ($action) {
     case 'get_session_lp_progress':
     case 'get_session_progress':
         //@TODO replace this for a more efficient function (not retrieving the whole data)
-        $course = api_get_course_info_by_id($courseId);
-        $users = CourseManager::get_student_list_from_course_code($course['code'], true, intval($_GET['session_id']));
+        $course = api_get_course_info_by_id($_GET['course_id']);
+        $users = CourseManager::get_student_list_from_course_code($course['code'], true, $_GET['session_id'], $_GET['date_from'], $_GET['date_to']);
         $count = count($users);
         break;
     case 'get_exercise_progress':
         //@TODO replace this for a more efficient function (not retrieving the whole data)
-        $records = SessionManager::get_exercise_progress(intval($_GET['session_id']), intval($_GET['course_id']), intval($_GET['exercise_id']));
+        $records = Tracking::get_exercise_progress($_GET['session_id'], $_GET['course_id'], $_GET['exercise_id'], $_GET['date_from'], $_GET['date_to']);
         $count = count($records);
         break;
     case 'get_session_access_overview':
         //@TODO replace this for a more efficient function (not retrieving the whole data)
-        $records = SessionManager::get_user_data_access_tracking_overview($_GET['session_id'], $_GET['course_id'], $_GET['student_id'], $_GET['profile'], $_GET['date_to'], $_GET['date_from'], $options);
+        $records = SessionManager::get_user_data_access_tracking_overview($_GET['session_id'], $_GET['course_id'], $_GET['student_id'], $_GET['profile'], $_GET['date_from'], $_GET['date_to'], $options);
         $count = count($records);
         break;
     case 'get_survey_overview':
         //@TODO replace this for a more efficient function (not retrieving the whole data)
-        $records = SessionManager::get_survey_overview(intval($_GET['session_id']), intval($_GET['course_id']), intval($_GET['survey_id']), $options);
+        $records = SessionManager::get_survey_overview($_GET['session_id'], $_GET['course_id'], $_GET['survey_id'], $_GET['date_from'], $_GET['date_to'], $options);
         $count = count($records);
         break;
-    /*case 'get_extra_fields':
+    case 'get_exercise_grade':
+        //@TODO replace this for a more efficient function (not retrieving the whole data)
+        $course = api_get_course_info_by_id($_GET['course_id']);
+        $users = CourseManager::get_student_list_from_course_code($course['code'], true, $_GET['session_id']);
+
+        $count = count($users);
+        break;
+    case 'get_extra_fields':
         $type = $_REQUEST['type'];
         $obj = new ExtraField($type);
         $count = $obj->get_count();
@@ -353,7 +361,7 @@ switch ($action) {
         $field_id = $_REQUEST['field_id'];
         $obj = new ExtraFieldOption($type);
         $count = $obj->get_count_by_field_id($field_id);
-        break;*/
+        break;
     case 'get_timelines':
         require_once $libpath.'timeline.lib.php';
         $obj        = new Timeline();
@@ -720,6 +728,9 @@ switch ($action) {
             'name', 'nbr_courses', 'nbr_users', 'category_name', 'date_start','date_end', 'coach_name', 'session_active', 'visibility'
         );
 
+        //Rename Category_name
+        $where_condition = str_replace('category_name', 'sc.name', $where_condition);
+
         $result = SessionManager::get_sessions_admin(
             array(
                 'where' => $whereCondition,
@@ -729,14 +740,11 @@ switch ($action) {
         );
         break;
     case 'get_exercise_progress':
-        $sessionId = 0;
-        if (!empty($_GET['course_id']) && !empty($_GET['exercise_id']))
-        {
-            $sessionId  = intval($_GET['session_id']);
-            $courseId   = intval($_GET['course_id']);
-            $exerciseId = intval($_GET['exercise_id']);
-            $answer     = intval($_GET['answer']);
-        }
+        $sessionId  = intval($_GET['session_id']);
+        $courseId   = intval($_GET['course_id']);
+        $exerciseId = intval($_GET['exercise_id']);
+        $date_from  = $_GET['date_from'];
+        $date_to    = $_GET['date_to'];
 
         $columns = array(
             'session',
@@ -748,11 +756,17 @@ switch ($action) {
             'time',
             'question_id',
             'question',
+            'description',
             'answer',
-            'correct'
+            'correct',
         );
 
-        $result = SessionManager::get_exercise_progress($sessionId, $courseId, $exerciseId, $answer,
+        $result = Tracking::get_exercise_progress(
+            $sessionId,
+            $courseId,
+            $exerciseId,
+            $date_from,
+            $date_to,
             array(
                 'where' => $whereCondition,
                 'order' => "$sidx $sord",
@@ -767,6 +781,7 @@ switch ($action) {
             $courseId = intval($_GET['course_id']);
             $course = api_get_course_info_by_id($courseId);
         }
+
         /**
          * Add lessons of course
          *
@@ -778,13 +793,12 @@ switch ($action) {
         );
         require_once api_get_path(SYS_CODE_PATH).'newscorm/learnpathList.class.php';
         $lessons = LearnpathList::get_course_lessons($course['code'], $sessionId);
-        foreach ($lessons as $lesson_id => $lesson)
-        {
+        foreach ($lessons as $lesson_id => $lesson) {
             $columns[] = $lesson_id;
         }
         $columns[] = 'total';
 
-        $result = SessionManager::get_session_lp_progress($sessionId, $courseId,
+        $result = SessionManager::get_session_lp_progress($sessionId, $courseId, $date_from, $date_to,
             array(
                 'where' => $whereCondition,
                 'order' => "$sidx $sord",
@@ -801,6 +815,8 @@ switch ($action) {
             $sessionId = intval($_GET['session_id']);
             $courseId  = intval($_GET['course_id']);
             $surveyId  = intval($_GET['survey_id']);
+            $date_from  = $_GET['date_from'];
+            $date_to    = $_GET['date_to'];
             //$course    = api_get_course_info_by_id($courseId);
         }
         /**
@@ -819,7 +835,7 @@ switch ($action) {
             $columns[] = $question_id;
         }
 
-        $result = SessionManager::get_survey_overview($sessionId, $courseId, $surveyId,
+        $result = SessionManager::get_survey_overview($sessionId, $courseId, $surveyId, $date_from, $date_to,
             array(
                 'where' => $whereCondition,
                 'order' => "$sidx $sord",
@@ -841,8 +857,6 @@ switch ($action) {
             'homeworks',
             'wikis',
             'surveys',
-            //course description
-            'course_description_progress',
             //exercises
             'lessons_total' ,
             'lessons_done' ,
@@ -897,6 +911,7 @@ switch ($action) {
             'clicks',
             'ip',
             'timeLoggedIn',
+            'session'
         );
         $sessionId = 0;
         if (!empty($_GET['course_id']) && !empty($_GET['session_id'])) {
@@ -1074,6 +1089,95 @@ switch ($action) {
             $result = $new_result;
         }
         break;
+    case 'get_exercise_grade':
+        $objExercise = new Exercise();
+        $exercises = $objExercise->getExercisesByCouseSession($_GET['course_id'], $_GET['session_id']);
+        $cntExer = 4;
+        if (!empty($exercises)) {
+            $cntExer += count($exercises);
+        }
+
+        $columns = array();
+        //Get dynamic column names
+        $i = 1;
+        $column_names = array();
+        foreach (range(1, $cntExer) as $cnt) {
+            switch ($cnt) {
+                case 1:
+                    $columns[] = 'session';
+                    $column_names[] = get_lang('Section');
+                    break;
+                case 2:
+                    $columns[] = 'username';
+                    $column_names[] = get_lang('Username');
+                    break;
+                case 3:
+                    $columns[] = 'name';
+                    $column_names[] = get_lang('Name');
+                    break;
+                case $cntExer:
+                    $columns[] = 'finalScore';
+                    $column_names[] = get_lang('FinalScore');
+                    break;
+                default:
+                    $title = "";
+                    if (!empty($exercises[$cnt - 4]['title'])) {
+                        $title = ucwords(strtolower(trim($exercises[$cnt - 4]['title'])));
+                    }
+                    $columns[] = 'exer' . $i;
+                    $column_names[] = $title;
+                    $i++;
+                    break;
+            }
+        }
+
+        $quizIds = array();
+        if (!empty($exercises)) {
+            foreach($exercises as $exercise) {
+                $quizIds[] = $exercise['id'];
+            }
+        }
+
+        $course = api_get_course_info_by_id($_GET['course_id']);
+        $listUserSess = CourseManager::get_student_list_from_course_code($course['code'], true, $_GET['session_id']);
+
+        $usersId = array_keys($listUserSess);
+
+        $users = UserManager::get_user_list_by_ids($usersId, null, "lastname, firstname",  "$start , $limit");
+        $exeResults = $objExercise->getExerciseAndResult($_GET['course_id'], $_GET['session_id'], $quizIds);
+
+        $arrGrade = array();
+        foreach ($exeResults as $exeResult) {
+            $arrGrade[$exeResult['exe_user_id']][$exeResult['exe_exo_id']] = $exeResult['exe_result'];
+        }
+
+        $result = array();
+        $i = 0;
+        foreach ($users as $user) {
+            $sessionInfo = SessionManager::fetch($listUserSess[$user['user_id']]['id_session']);
+            $result[$i]['session'] = $sessionInfo['name'];
+            $result[$i]['username'] = $user['username'];
+            $result[$i]['name'] = $user['lastname'] . " " . $user['firstname'];
+            $j = 1;
+            $finalScore = 0;
+            foreach ($quizIds as $quizID) {
+                $grade = "";
+                if (!empty($arrGrade [$user['user_id']][$quizID]) || $arrGrade [$user['user_id']][$quizID] == 0) {
+                    $finalScore += $grade = $arrGrade [$user['user_id']][$quizID];
+                }
+                $result[$i]['exer' . $j] = $grade;
+                $j++;
+            }
+
+            if ($finalScore > 20) {
+                $finalScore = 20;
+            }
+
+            $result[$i]['finalScore'] = number_format($finalScore, 2);
+
+            $i++;
+        }
+        break;
     case 'get_extra_field_options':
         $obj = new ExtraFieldOption($type);
         $columns = array('option_display_text', 'option_value', 'option_order');
@@ -1146,11 +1250,12 @@ $allowed_actions = array(
     'get_grade_models',
     'get_event_email_template',
     'get_user_skill_ranking',
-    //'get_extra_fields',
-    //'get_extra_field_options',
+    'get_extra_fields',
+    'get_extra_field_options',
     //'get_course_exercise_medias',
     'get_user_course_report',
-    'get_user_course_report_resumed'
+    'get_user_course_report_resumed',
+    'get_exercise_grade'
 );
 
 //5. Creating an obj to return a json
@@ -1183,7 +1288,13 @@ if (in_array($action, $allowed_actions)) {
             case 'xls':
                 //TODO add date if exists
                 $file_name = (!empty($action)) ? $action : 'company_report';
-                Export::export_table_xls($array, $file_name);
+                require_once api_get_path(LIBRARY_PATH).'browser/Browser.php';
+                $browser = new Browser();
+                if ($browser->getPlatform() == Browser::PLATFORM_WINDOWS) {
+                    Export::export_table_xls_html($array, $file_name, 'ISO-8859-15');
+                } else {
+                    Export::export_table_xls_html($array, $file_name);
+                }
                 break;
             case 'csv':
             default:
@@ -1199,7 +1310,6 @@ if (in_array($action, $allowed_actions)) {
 
     if (!empty($result)) {
         foreach ($result as $row) {
-            //print_r($row);
             // if results tab give not id, set id to $i otherwise id="null" for all <tr> of the jqgrid - ref #4235
             if (!isset($row['id']) || isset($row['id']) && $row['id'] == '') {
                 $response->rows[$i]['id']= $i;

+ 31 - 3
main/inc/ajax/session.ajax.php

@@ -26,7 +26,10 @@ switch ($action) {
         break;
     case 'search_session':
         if (api_is_platform_admin()) {
-            $results = SessionManager::get_sessions_list(array('s.name LIKE' => "%".$_REQUEST['q']."%"));
+            //$results = SessionManager::get_sessions_list(array('s.name LIKE' => "%".$_REQUEST['q']."%"));
+            $results = SessionManager::get_sessions_list(
+                array('s.name LIKE' => "%".$_REQUEST['q']."%")
+            );
             $results2 = array();
             if (!empty($results)) {
                 foreach ($results as $item) {
@@ -47,9 +50,33 @@ switch ($action) {
             }
         }
         break;
+    case 'search_session_all':
+        if (api_is_platform_admin()) {
+            $results = SessionManager::get_sessions_list(array('s.name LIKE' => "%".$_REQUEST['q']."%", 'c.id ='=>$_REQUEST['course_id']));
+            $results2 = array();
+            if (!empty($results)) {
+                foreach ($results as $item) {
+                    $item2 = array();
+                    foreach ($item as $id => $internal) {
+                        if ($id == 'id') {
+                            $item2[$id] = $internal;
+                        }
+                        if ($id == 'name') {
+                            $item2['text'] = $internal;
+                        }
+                    }
+                    $results2[] = $item2;
+                }
+                $results2[] = array('T', 'text' => 'TODOS', 'id' => 'T');
+                echo json_encode($results2);
+            } else {
+                echo json_encode(array(array('T', 'text' => 'TODOS', 'id' => 'T')));
+            }
+        }
+        break;
     case 'search_session_by_course':
         if (api_is_platform_admin()) {
-            $results = SessionManager::get_sessions_list(array('s.name LIKE' => "%".$_REQUEST['q']."%"));
+            $results = SessionManager::get_sessions_list(array('s.name LIKE' => "%".$_REQUEST['q']."%", 'c.id ='=>$_REQUEST['course_id']));
             $results2 = array();
             if (!empty($results)) {
                 foreach ($results as $item) {
@@ -64,9 +91,10 @@ switch ($action) {
                     }
                     $results2[] = $item2;
                 }
+                $results2[] = array('T', 'text' => 'TODOS', 'id' => 'T');
                 echo json_encode($results2);
             } else {
-                echo json_encode(array());
+                echo json_encode(array(array('T', 'text' => 'TODOS', 'id' => 'T')));
             }
         }
         break;

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

@@ -50,7 +50,7 @@ $TechnicalIssuesDescription = 'This portal is currently experiencing technical i
 
 if (is_int($global_error_code) && $global_error_code > 0) {
 
-	$theme = 'chamilo/';
+	$theme = 'chamilo_red_utp/';
 	$css_path = 'main/css/';
     $css_file              = $css_path.$theme.'default.css';
     $bootstrap_file        = $css_path.'bootstrap.css';

+ 6 - 0
main/inc/introductionSection.inc.php

@@ -280,3 +280,9 @@ if ($intro_dispCommand) {
 	}
 }
 $introduction_section .=  '</div>';
+
+$browser = api_get_navigator();
+
+if (strpos($introduction_section, '<iframe') !== false && $browser['name'] == 'Chrome') {
+    header("X-XSS-Protection: 0");
+}

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

@@ -917,6 +917,11 @@ class Autoload
         $result['UserForm'] = '/main/gradebook/lib/fe/userform.class.php';
         $result['UserGroup'] = '/main/inc/lib/usergroup.lib.php';
         $result['UserManager'] = '/main/inc/lib/usermanager.lib.php';
+
+        $result['ExtraField'] = '/main/inc/lib/extra_field.lib.php';
+        $result['ExtraFieldOption'] = '/main/inc/lib/extra_field_option.lib.php';
+        $result['ExtraFieldValue'] = '/main/inc/lib/extra_field_value.lib.php';
+
         $result['UserTable'] = '/main/gradebook/lib/fe/usertable.class.php';
         $result['Utf8'] = '/main/inc/lib/system/text/utf8.class.php';
         $result['Utf8Decoder'] = '/main/inc/lib/system/text/utf8_decoder.class.php';

+ 115 - 107
main/inc/lib/certificate.lib.php

@@ -1,39 +1,45 @@
 <?php
 /* For licensing terms, see /license.txt */
+
 /**
  * The certificates class is used to generate certificates from inside the
  * gradebook tool.
  * @package chamilo.library.certificates
  */
-class Certificate extends Model {
-    var $table;
-    var $columns = array('id','cat_id','score_certificate','created_at','path_certificate');
+class Certificate extends Model
+{
+    public $table;
+    public $columns = array('id','cat_id','score_certificate','created_at','path_certificate');
     /**
-     * Certification data 
+     * Certification data
      */
-    var $certificate_data = array();
-    
+    public $certificate_data = array();
+
     /**
      * Student's certification path
-     */    
-    var $certification_user_path = null;  
-    var $certification_web_user_path = null;  
-    var $html_file     = null;
-    var $qr_file     = null;
-    var $user_id;
-    
-    //If true every time we enter to the certificate URL we would generate a new certificate
-    // (good thing because we can edit the certificate and all users will have the latest certificate bad because we load the certificate everytime)
-    var $force_certificate_generation = true;  //default true
-    
+     */
+    public $certification_user_path = null;
+    public $certification_web_user_path = null;
+    public $html_file     = null;
+    public $qr_file     = null;
+    public $user_id;
+
+    /* If true every time we enter to the certificate URL
+    we would generate a new certificate (good thing because we can edit the
+    certificate and all users will have the latest certificate bad because we
+    load the certificate everytime*/
+
+    public $force_certificate_generation = true;
+
     /**
      * Constructor
      * @param	int	ID of the certificate. If no ID given, take user_id and try to generate one
      */
-    public function __construct($certificate_id = null) {
+    public function __construct($certificate_id = null)
+    {
         $this->table             =  Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
         $this->certificate_data = null;
-        
+
         if (isset($certificate_id)) {
             $this->certificate_data = $this->get($certificate_id);
             $this->user_id          = $this->certificate_data['user_id'];
@@ -41,69 +47,66 @@ class Certificate extends Model {
             //Try with the current user
             $this->user_id = api_get_user_id();
         }
-        
-        if ($this->user_id) { 
-            
+
+        if ($this->user_id) {
+
             //Need to be called before any operation
             $this->check_certificate_path();
-                    
+
             //To force certification generation
             if ($this->force_certificate_generation) {
                 $this->generate();
             }
-            
-            if (isset($this->certificate_data) && $this->certificate_data) {        
+
+            if (isset($this->certificate_data) && $this->certificate_data) {
                 if (empty($this->certificate_data['path_certificate'])) {
-                    $this->generate();                    
-                }            
+                    $this->generate();
+                }
             }
         }
-        
+
         //Setting the qr and html variables
         if (isset($certificate_id) && !empty($this->certification_user_path)) {
             $pathinfo = pathinfo($this->certificate_data['path_certificate']);
             $this->html_file = $this->certification_user_path.basename($this->certificate_data['path_certificate']);
             $this->qr_file = $this->certification_user_path.$pathinfo['filename'].'_qr.png';
-        }        
+        }
     }
-    
-    
+
     /**
      * Checks if the certificate user path directory is created
      */
-    public function check_certificate_path() {
+    public function check_certificate_path()
+    {
         $this->certification_user_path = null;
-        
+
         //Setting certification path
-        $path_info = UserManager::get_user_picture_path_by_id($this->user_id, 'system', true);
-        
-        $web_path_info = UserManager::get_user_picture_path_by_id($this->user_id, 'web', true);
-        
+        $path_info = UserManager::get_user_picture_path_by_id($this->user_id, 'system');
+        $web_path_info = UserManager::get_user_picture_path_by_id($this->user_id, 'web');
+
         if (!empty($path_info) && isset($path_info['dir'])) {
-            
             $this->certification_user_path = $path_info['dir'].'certificate/';
-            $this->certification_web_user_path = $web_path_info['dir'].'certificate/';            
-            
+            $this->certification_web_user_path = $web_path_info['dir'].'certificate/';
+
             if (!is_dir($path_info['dir'])) {
                 mkdir($path_info['dir'], 0777, true);
             }
-                    
             if (!is_dir($this->certification_user_path)) {
                 mkdir($this->certification_user_path, 0777);
             }
-        }        
+        }
     }
-    
+
     /**
      * Deletes the current certificate object. This is generally triggered by
      * the teacher from the gradebook tool to re-generate the certificate because
      * the original version wa flawed.
      */
-    public function delete($force_delete = false) {        
+    public function delete($force_delete = false)
+    {
         if (!empty($this->certificate_data)) {
-                         
             if (!is_null($this->html_file) || $this->html_file != '' || strlen($this->html_file)) {
-                //Deleting HTML file                
+                //Deleting HTML file
                 if (is_file($this->html_file)) {
                     @unlink($this->html_file);
                     if (is_file($this->html_file) === false) {
@@ -112,98 +115,98 @@ class Certificate extends Model {
                         $delete_db = false;
                     }
                 }
-                //Deleting QR code PNG image file                
+                //Deleting QR code PNG image file
                 if (is_file($this->qr_file)) {
                     @unlink($this->qr_file);
                 }
                 if ($delete_db || $force_delete) {
                     return parent::delete($this->certificate_data['id']);
-                }                
+                }
             } else {
                 return parent::delete($this->certificate_data['id']);
             }
         }
         return false;
     }
-    
-    /** 
-     *     Generates an HTML Certificate and fills the path_certificate field in the DB 
-     * */
-    
-    public function generate($params = array()) {
+
+    /**
+    *  Generates an HTML Certificate and fills the path_certificate field in the DB
+    **/
+    public function generate($params = array())
+    {
         //The user directory should be set
         if (empty($this->certification_user_path) && $this->force_certificate_generation == false) {
             return false;
-        }        
+        }
         require_once api_get_path(SYS_CODE_PATH).'gradebook/lib/be.inc.php';
         require_once api_get_path(SYS_CODE_PATH).'gradebook/lib/gradebook_functions.inc.php';
         require_once api_get_path(SYS_CODE_PATH).'gradebook/lib/scoredisplay.class.php';
-        
+
         $params['hide_print_button'] = isset($params['hide_print_button']) ? true : false;
-        
+
         $my_category = Category :: load($this->certificate_data['cat_id']);
-                
+
         if (isset($my_category[0]) && $my_category[0]->is_certificate_available($this->user_id)) {
-                        
+
             $user         = api_get_user_info($this->user_id);
             $scoredisplay = ScoreDisplay :: instance();
             $scorecourse  = $my_category[0]->calc_score($this->user_id);
-    
+
             $scorecourse_display = (isset($scorecourse) ? $scoredisplay->display_score($scorecourse,SCORE_AVERAGE) : get_lang('NoResultsAvailable'));
-                
+
             //Prepare all necessary variables:
             $organization_name     = api_get_setting('Institution');
             //$portal_name         = api_get_setting('siteName');
             $stud_fn             = $user['firstname'];
             $stud_ln             = $user['lastname'];
-                
+
             //@todo this code is not needed
             $certif_text         = sprintf(get_lang('CertificateWCertifiesStudentXFinishedCourseYWithGradeZ'), $organization_name, $stud_fn.' '.$stud_ln, $my_category[0]->get_name(), $scorecourse_display);
             $certif_text         = str_replace("\\n","\n", $certif_text);
-            
+
             //If the gradebook is related to skills we added the skills to the user
-                                
+
             $skill = new Skill();
-            $skill->add_skill_to_user($this->user_id, $this->certificate_data['cat_id']);            
-    
+            $skill->add_skill_to_user($this->user_id, $this->certificate_data['cat_id']);
+
             if (is_dir($this->certification_user_path)) {
-                if (!empty($this->certificate_data)) { 
+                if (!empty($this->certificate_data)) {
                     $new_content_html = get_user_certificate_content($this->user_id, $my_category[0]->get_course_code(), false, $params['hide_print_button']);
-                                        
+
                     if ($my_category[0]->get_id() == strval(intval($this->certificate_data['cat_id']))) {
                         $name = $this->certificate_data['path_certificate'];
                         $my_path_certificate = $this->certification_user_path.basename($name);
                         if (file_exists($my_path_certificate) && !empty($name) && !is_dir($my_path_certificate) && $this->force_certificate_generation == false) {
-                            //Seems that the file was already generated                            
+                            //Seems that the file was already generated
                             return true;
                         } else {
                             //Creating new name
                             $name    = md5($this->user_id.$this->certificate_data['cat_id']).'.html';
-                            $my_path_certificate = $this->certification_user_path.$name;                            
+                            $my_path_certificate = $this->certification_user_path.$name;
                             $path_certificate    ='/'.$name;
-                            
+
                             //Getting QR filename
                             $file_info = pathinfo($path_certificate);
-                            $qr_code_filename = $this->certification_user_path.$file_info['filename'].'_qr.png';                            
-                            
+                            $qr_code_filename = $this->certification_user_path.$file_info['filename'].'_qr.png';
+
                             $my_new_content_html = str_replace('((certificate_barcode))', Display::img($this->certification_web_user_path.$file_info['filename'].'_qr.png', 'QR'), $new_content_html['content']);
                             $my_new_content_html = mb_convert_encoding($my_new_content_html,'UTF-8', api_get_system_encoding());
-                            
+
                             $result = @file_put_contents($my_path_certificate, $my_new_content_html);
-                            if ($result) {                                
+                            if ($result) {
                                 //Updating the path
-                                self::update_user_info_about_certificate($this->certificate_data['cat_id'], $this->user_id, $path_certificate);                                
+                                self::update_user_info_about_certificate($this->certificate_data['cat_id'], $this->user_id, $path_certificate);
                                 $this->certificate_data['path_certificate'] = $path_certificate;
-                                
+
                                 if ($this->html_file_is_generated()) {
-                                    if (!empty($file_info)) {                             
-                                        $text = $this->parse_certificate_variables($new_content_html['variables']);                                        
+                                    if (!empty($file_info)) {
+                                        $text = $this->parse_certificate_variables($new_content_html['variables']);
                                         $this->generate_qr($text, $qr_code_filename);
                                     }
-                                }                                
+                                }
                             }
                             return $result;
-                        }                        
+                        }
                     }
                 }
             }
@@ -218,7 +221,8 @@ class Certificate extends Model {
     * @param string the path name of the certificate
     * @return void()
     */
-    function update_user_info_about_certificate ($cat_id,$user_id,$path_certificate) {
+    public function update_user_info_about_certificate ($cat_id,$user_id,$path_certificate)
+    {
         $table_certificate = Database::get_main_table(TABLE_MAIN_GRADEBOOK_CERTIFICATE);
         if (!UserManager::is_user_certified($cat_id,$user_id)) {
             $sql='UPDATE '.$table_certificate.' SET path_certificate="'.Database::escape_string($path_certificate).'"
@@ -226,29 +230,31 @@ class Certificate extends Model {
             Database::query($sql);
         }
     }
-    
+
     /**
-     * 
+     *
      * Check if the file was generated
-     * 
+     *
      * @return boolean
      */
-    function html_file_is_generated() {
+    public function html_file_is_generated()
+    {
         if (empty($this->certification_user_path)) {
             return false;
         }
         if (!empty($this->certificate_data) && isset($this->certificate_data['path_certificate']) && !empty($this->certificate_data['path_certificate'])) {
-            return true;            
+            return true;
         }
         return false;
-    } 
-    
+    }
+
     /**
      * Generates a QR code for the certificate. The QR code embeds the text given
      * @param    string    Text to be added in the QR code
      * @param    string    file path of the image
      * */
-    public function generate_qr($text, $path) {        
+    public function generate_qr($text, $path)
+    {
         //Make sure HTML certificate is generated
         if (!empty($text) && !empty($path)) {
             require_once api_get_path(LIBRARY_PATH).'phpqrcode/qrlib.php';
@@ -257,28 +263,29 @@ class Certificate extends Model {
         }
         return false;
     }
-    
+
     /**
      * Transforms certificate tags into text values. This function is very static
      * (it doesn't allow for much flexibility in terms of what tags are printed).
      * @param array Contains two array entris: first are the headers, second is an array of contents
      * @return string The translated string
      */
-    public function parse_certificate_variables($array) {
-        $text = '';        
+    public function parse_certificate_variables($array)
+    {
+        $text = '';
         $headers = $array[0];
         $content = $array[1];
         $final_content = array();
-        
+
         if (!empty($content)) {
-            foreach($content as $key => $value) {                
+            foreach($content as $key => $value) {
                 $my_header = str_replace(array('((', '))') , '', $headers[$key]);
                 $final_content[$my_header] = $value;
             }
         }
-        
+
         /* Certificate tags
-         * 
+         *
           0 => string '((user_firstname))' (length=18)
           1 => string '((user_lastname))' (length=17)
           2 => string '((gradebook_institution))' (length=25)
@@ -292,32 +299,33 @@ class Certificate extends Model {
           10 => string '((gradebook_grade))' (length=19)
           11 => string '((certificate_link))' (length=20)
           12 => string '((certificate_link_html))' (length=25)
-          13 => string '((certificate_barcode))' (length=23)          
+          13 => string '((certificate_barcode))' (length=23)
          */
-        
+
         $break_space = " \n\r ";
-        
+
         $text = $final_content['gradebook_institution'].' - '.$final_content['gradebook_sitename'].' - '.get_lang('Certification').$break_space.
                 get_lang('Student'). ': '.$final_content['user_firstname'].' '.$final_content['user_lastname'].$break_space.
                 get_lang('Teacher'). ': '.$final_content['teacher_firstname'].' '.$final_content['teacher_lastname'].$break_space.
                 get_lang('Date'). ': '.$final_content['date_certificate'].$break_space.
                 get_lang('Score'). ': '.$final_content['gradebook_grade'].$break_space.
-                'URL'. ': '.$final_content['certificate_link'];        
+                'URL'. ': '.$final_content['certificate_link'];
         return $text;
     }
-    
+
     /**
-    * Shows the student's certificate (HTML file). If the global setting 
+    * Shows the student's certificate (HTML file). If the global setting
     * allow_public_certificates is set to 'false', no certificate can be printed.
     * If the global allow_public_certificates is set to 'true' and the course
     * setting allow_public_certificates is set to 0, no certificate *in this
     * course* can be printed (for anonymous users). Connected users can always
     * print them.
     */
-    public function show() {
+    public function show()
+    {
         // Special rules for anonymous users
         $failed = false;
-        if (api_is_anonymous()) {            
+        if (api_is_anonymous()) {
             if (api_get_setting('allow_public_certificates') != 'true') {
                 // The "non-public" setting is set, so do not print
                 $failed = true;
@@ -359,5 +367,5 @@ class Certificate extends Model {
             Display :: display_warning_message(get_lang('NoCertificateAvailable'));
         }
         exit;
-    }    
-}
+    }
+}

+ 30 - 5
main/inc/lib/course.lib.php

@@ -1617,10 +1617,19 @@ class CourseManager
      *    This only returns the users that are registered in this actual course, not linked courses.
      *
      *    @param string $course_code
-     *    @param boolean $full list to true if we want sessions students too
+     *    @param boolean $with_session
+     *    @param integer $session_id
+     *    @param date $date_from
+     *    @param date $date_to
      *    @return array with user id
      */
-    public static function get_student_list_from_course_code($course_code, $with_session = false, $session_id = 0) {
+    public static function get_student_list_from_course_code(
+        $course_code,
+        $with_session = false,
+        $session_id = 0,
+        $date_from = null,
+        $date_to = null
+    ) {
         $session_id = intval($session_id);
         $course_code = Database::escape_string($course_code);
 
@@ -1639,11 +1648,27 @@ class CourseManager
         // students subscribed to the course through a session
 
         if ($with_session) {
-            $sql_query = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)."
-                          WHERE course_code = '$course_code' AND status <> 2";
+
+            $joinSession = "";
+            //Session creation date
+            if (!empty($date_from) && !empty($date_to)) {
+                $joinSession = "INNER JOIN " . Database::get_main_table(TABLE_MAIN_SESSION) . " s";
+            }
+
+            $sql_query = "SELECT * FROM ".Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER)." scu
+                          $joinSession
+                          WHERE scu.course_code = '$course_code' AND scu.status <> 2";
+
+            if (!empty($date_from) && !empty($date_to)) {
+                $date_from = Database::escape_string($date_from);
+                $date_to = Database::escape_string($date_to);
+                $sql_query .= " AND s.date_start >= '$date_from' AND s.date_end <= '$date_to'";
+            }
+
             if ($session_id != 0) {
-                $sql_query .= ' AND id_session = '.$session_id;
+                $sql_query .= ' AND scu.id_session = '.$session_id;
             }
+
             $rs = Database::query($sql_query);
             while($student = Database::fetch_array($rs)) {
                 $students[$student['id_user']] = $student;

+ 4 - 0
main/inc/lib/database.constants.inc.php

@@ -62,6 +62,10 @@ define('TABLE_MAIN_USER_FIELD',         'user_field');
 define('TABLE_MAIN_USER_FIELD_OPTIONS', 'user_field_options');
 define('TABLE_MAIN_USER_FIELD_VALUES',  'user_field_values');
 
+define('TABLE_MAIN_LP_FIELD',         'lp_field');
+define('TABLE_MAIN_LP_FIELD_OPTIONS', 'lp_field_options');
+define('TABLE_MAIN_LP_FIELD_VALUES',  'lp_field_values');
+
 //User tags
 define('TABLE_MAIN_TAG',                'tag');
 define('TABLE_MAIN_USER_REL_TAG',       'user_rel_tag');

+ 33 - 12
main/inc/lib/display.lib.php

@@ -95,9 +95,6 @@ class Display
         if (!empty($page_header)) {
             self::$global_template->assign('header', $page_header);
         }
-
-        self::$global_template->assign('course_code', api_get_course_id());
-
         echo self::$global_template->show_header_template();
     }
 
@@ -935,15 +932,17 @@ class Display
      * This function need to be in the ready jquery function example --> $(function() { <?php echo Display::grid_js('grid' ...); ?> }
      * In order to work this function needs the Display::grid_html function with the same div id
      *
-     * @param   string  div id
-     * @param   string  url where the jqgrid will ask for data (if datatype = json)
-     * @param   array   Visible columns (you should use get_lang). An array in which we place the names of the columns.
+     * @param   string  $div_id div id
+     * @param   string  $url url where the jqgrid will ask for data (if datatype = json)
+     * @param   array   $column_names Visible columns (you should use get_lang). An array in which we place the names of the columns.
      * 					This is the text that appears in the head of the grid (Header layer).
      * 					Example: colname   {name:'date',     index:'date',   width:120, align:'right'},
-     * @param   array   the column model :  Array which describes the parameters of the columns.This is the most important part of the grid.
+     * @param   array   $column_model the column model :  Array which describes the parameters of the columns.This is the most important part of the grid.
      * 					For a full description of all valid values see colModel API. See the url above.
-     * @param   array   extra parameters
-     * @param   array   data that will be loaded
+     * @param   array   $extra_params extra parameters
+     * @param   array   $data data that will be loaded
+     * @param	string	$formatter A string that will be appended to the JSON returned
+     * @param	bool	$fixed_width not implemented yet
      * @return  string  the js code
      *
      */
@@ -955,7 +954,7 @@ class Display
         $extra_params,
         $data = array(),
         $formatter = '',
-        $width_fix = false
+        $fixed_width = false
     ) {
         $obj = new stdClass();
         $obj->first = 'first';
@@ -1022,7 +1021,10 @@ class Display
         // Adding extra params
         if (!empty($extra_params)) {
             foreach ($extra_params as $key => $element) {
-                $obj->$key = $element;
+                // the groupHeaders key gets a special treatment
+                if ($key != 'groupHeaders') {
+                    $obj->$key = $element;
+                }
             }
         }
 
@@ -1040,7 +1042,8 @@ class Display
         $json_encode = json_encode($obj);
 
         if (!empty($data)) {
-            //Converts the "data":"js_variable" to "data":js_variable othersiwe it will not work
+            //Converts the "data":"js_variable" to "data":js_variable,
+            // otherwise it will not work
             $json_encode = str_replace('"data":"'.$data_var.'"', '"data":'.$data_var.'', $json_encode);
         }
 
@@ -1055,9 +1058,27 @@ class Display
         // Creating the jqgrid element.
         $json .= '$("#'.$div_id.'").jqGrid({';
         //$json .= $beforeSelectRow;
+
         $json .= $json_encode;
+
         $json .= '});';
 
+        // Grouping headers option
+        if (isset($extra_params['groupHeaders'])) {
+            $groups = '';
+            foreach ($extra_params['groupHeaders'] as $group) {
+                //{ "startColumnName" : "courses", "numberOfColumns" : 1, "titleText" : "Order Info" },
+                $groups .= '{ "startColumnName" : "' . $group['startColumnName'] . '", "numberOfColumns" : ' . $group['numberOfColumns'] . ', "titleText" : "' . $group['titleText']  . '" },';
+
+            }
+            $json .= '$("#'.$div_id.'").jqGrid("setGroupHeaders", {
+                "useColSpanStyle" : false,
+                "groupHeaders"    : [
+                    ' . $groups . '
+                ]
+            });';
+        }
+
         $all_text = addslashes(get_lang('All'));
         $json .= '$("'.$obj->pager.' option[value='.$all_value.']").text("'.$all_text.'");';
         $json.= "\n";

+ 46 - 33
main/inc/lib/document.lib.php

@@ -655,6 +655,7 @@ class DocumentManager
      * @param array $_course
      * @param int $to_group_id
      * @param boolean $can_see_invisible
+     *
      * @return array with paths
      */
     public static function get_all_document_folders(
@@ -664,8 +665,8 @@ class DocumentManager
     ) {
         $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
         $TABLE_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
-
         $to_group_id = intval($to_group_id);
+        $document_folders = array();
 
         if ($can_see_invisible) {
             //condition for the session
@@ -673,8 +674,9 @@ class DocumentManager
             $condition_session = api_get_session_condition($session_id);
             $show_users_condition = "";
             if (api_get_setting('show_users_folders') == 'false') {
-                $show_users_condition = "AND docs.path NOT LIKE '%shared_folder%'";
+                $show_users_condition = " AND docs.path NOT LIKE '%shared_folder%'";
             }
+
             if ($to_group_id <> 0) {
                $sql = "SELECT DISTINCT docs.id, path
                        FROM $TABLE_ITEMPROPERTY  AS last INNER JOIN $TABLE_DOCUMENT  AS docs
@@ -723,25 +725,28 @@ class DocumentManager
                 return false;
             }
         } else {
-            //no invisible folders
-            //condition for the session
+            // No invisible folders
+            // Condition for the session
             $session_id = api_get_session_id();
             $condition_session = api_get_session_condition($session_id);
             //get visible folders
-            $visible_sql = "SELECT DISTINCT docs.id, path
-                        FROM $TABLE_ITEMPROPERTY AS last,
-                        $TABLE_DOCUMENT AS docs
-                        WHERE docs.id = last.ref
-                        AND docs.filetype = 'folder'
-                        AND last.tool = '" . TOOL_DOCUMENT . "'
-                        AND last.to_group_id = " . $to_group_id . "
-                        AND last.visibility = 1 $condition_session AND
+            $sql = "SELECT DISTINCT docs.id, path
+                    FROM
+                        $TABLE_ITEMPROPERTY AS last, $TABLE_DOCUMENT AS docs
+                    WHERE
+                        docs.id = last.ref AND
+                        docs.filetype = 'folder' AND
+                        last.tool = '" . TOOL_DOCUMENT . "' AND
+                        last.to_group_id = " . $to_group_id . " AND
+                        last.visibility = 1 $condition_session AND
                         last.c_id = {$_course['real_id']}  AND
                         docs.c_id = {$_course['real_id']} ";
-            $visibleresult = Database::query($visible_sql);
-            while ($all_visible_folders = Database::fetch_array($visibleresult, 'ASSOC')) {
-                $visiblefolders[$all_visible_folders['id']] = $all_visible_folders['path'];
+            $result = Database::query($sql);
+            $visibleFolders = array();
+            while ($row = Database::fetch_array($result, 'ASSOC')) {
+                $visibleFolders[$row['id']] = $row['path'];
             }
+
             //condition for the session
             $session_id = api_get_session_id();
             $condition_session = api_get_session_condition($session_id);
@@ -756,8 +761,9 @@ class DocumentManager
                         last.visibility = 0 $condition_session AND
                         last.c_id = {$_course['real_id']} AND
                         docs.c_id = {$_course['real_id']} ";
-            $invisibleresult = Database::query($sql);
-            while ($invisible_folders = Database::fetch_array($invisibleresult, 'ASSOC')) {
+            $result = Database::query($sql);
+            $invisibleFolders = array();
+            while ($row = Database::fetch_array($result, 'ASSOC')) {
                 //condition for the session
                 $session_id = api_get_session_id();
                 $condition_session = api_get_session_condition($session_id);
@@ -766,29 +772,27 @@ class DocumentManager
                         FROM $TABLE_ITEMPROPERTY AS last, $TABLE_DOCUMENT AS docs
                         WHERE
                             docs.id = last.ref AND
-                            docs.path LIKE '" . Database::escape_string($invisible_folders['path']) . "/%' AND
+                            docs.path LIKE '" . Database::escape_string($row['path']) . "/%' AND
                             docs.filetype = 'folder' AND
                             last.tool = '" . TOOL_DOCUMENT . "' AND
                             last.to_group_id = " . $to_group_id . " AND
                             last.visibility = 1 $condition_session AND
-                            last.c_id = {$_course['real_id']}  AND
+                            last.c_id = {$_course['real_id']} AND
                             docs.c_id = {$_course['real_id']}  ";
                 $folder_in_invisible_result = Database::query($sql);
                 while ($folders_in_invisible_folder = Database::fetch_array($folder_in_invisible_result, 'ASSOC')) {
-                    $invisiblefolders[$folders_in_invisible_folder['id']] = $folders_in_invisible_folder['path'];
+                    $invisibleFolders[$folders_in_invisible_folder['id']] = $folders_in_invisible_folder['path'];
                 }
             }
 
             //if both results are arrays -> //calculate the difference between the 2 arrays -> only visible folders are left :)
-            if (is_array($visiblefolders) && is_array($invisiblefolders)) {
-                $document_folders = array_diff($visiblefolders, $invisiblefolders);
+            if (is_array($visibleFolders) && is_array($invisibleFolders)) {
+                $document_folders = array_diff($visibleFolders, $invisibleFolders);
                 natsort($document_folders);
                 return $document_folders;
-            } elseif (is_array($visiblefolders)) {
-                //only visible folders found
-                //sort($visiblefolders);
-                natsort($visiblefolders);
-                return $visiblefolders;
+            } elseif (is_array($visibleFolders)) {
+                natsort($visibleFolders);
+                return $visibleFolders;
             } else {
                 //no visible folders found
                 return false;
@@ -1784,7 +1788,16 @@ class DocumentManager
             $visibility_command = 'invisible';
 
             if (!is_dir($base_work_dir_test)) {
-                $created_dir = create_unexisting_directory($course_info, api_get_user_id(), api_get_session_id(), $to_group_id, $to_user_id, $base_work_dir, $dir_name, $post_dir_name);
+                $created_dir = create_unexisting_directory(
+                    $course_info,
+                    api_get_user_id(),
+                    api_get_session_id(),
+                    $to_group_id,
+                    $to_user_id,
+                    $base_work_dir,
+                    $dir_name,
+                    $post_dir_name
+                );
                 $update_id = self::get_document_id_of_directory_certificate();
                 api_item_property_update($course_info, TOOL_DOCUMENT, $update_id, $visibility_command, api_get_user_id());
             }
@@ -2244,9 +2257,7 @@ class DocumentManager
         $user_id = api_get_user_id();
 
         if (!empty($orig_source_html)) {
-
             foreach ($orig_source_html as $source) {
-
                 // get information about source url
                 $real_orig_url = $source[0]; // url
                 $scope_url = $source[1];   // scope (local, remote)
@@ -2254,8 +2265,8 @@ class DocumentManager
 
                 // Get path and query from origin url
                 $orig_parse_url = parse_url($real_orig_url);
-                $real_orig_path = $orig_parse_url['path'];
-                $real_orig_query = $orig_parse_url['query'];
+                $real_orig_path = isset($orig_parse_url['path']) ? $orig_parse_url['path'] : null;
+                $real_orig_query = isset($orig_parse_url['query']) ? $orig_parse_url['query'] : null;
 
                 // Replace origin course code by destination course code from origin url query
                 $dest_url_query = '';
@@ -2684,7 +2695,9 @@ class DocumentManager
             $session_condition = " AND props.id_session='" . $session_id . "' ";
         }
 
-        $sql = "SELECT SUM(size) FROM  " . $TABLE_ITEMPROPERTY . "  AS props, " . $TABLE_DOCUMENT . "  AS docs
+        $sql = "SELECT SUM(size)
+                FROM $TABLE_ITEMPROPERTY  AS props,
+                     $TABLE_DOCUMENT AS docs
 		        WHERE 	props.c_id 	= $course_id AND
 		        		docs.c_id 	= $course_id AND
 		        		docs.id 	= props.ref AND

+ 26 - 6
main/inc/lib/events.lib.inc.php

@@ -1472,7 +1472,7 @@ function delete_attempt($exe_id, $user_id, $course_code, $session_id, $question_
  * @param int $user_id
  * @param string $course_code
  * @param int $question_id
- * @todo add session_id for 1.10
+ * @todo add session_id for 10
  */
 function delete_attempt_hotspot($exe_id, $user_id, $course_code, $session_id = 0, $question_id) {
     $table_track_attempt   = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_HOTSPOT);
@@ -1495,7 +1495,8 @@ function delete_attempt_hotspot($exe_id, $user_id, $course_code, $session_id = 0
  * @param int $user_id
  * @param int $session_id
  */
-function event_course_login($course_code, $user_id, $session_id) {
+function event_course_login($course_code, $user_id, $session_id)
+{
     global $course_tracking_table;
 
     //@todo use api_get_utc_datetime
@@ -1504,11 +1505,30 @@ function event_course_login($course_code, $user_id, $session_id) {
     $course_code = Database::escape_string($course_code);
     $user_id	 = intval($user_id);
     $session_id  = intval($session_id);
+    $session_lifetime = 3600;
+
+    //We select the last record for the current course in the course tracking table
+    $sql = "SELECT course_access_id
+            FROM $course_tracking_table
+            WHERE
+                user_id     = $user_id AND
+                course_code = '$course_code' AND
+                session_id  = $session_id AND
+                login_course_date > '$time' - INTERVAL $session_lifetime SECOND
+            ORDER BY login_course_date DESC LIMIT 0,1";
+    $result = Database::query($sql);
 
-    $sql	= "INSERT INTO $course_tracking_table(course_code, user_id, login_course_date, logout_course_date, counter, session_id)
-        	  VALUES('".$course_code."', '".$user_id."', '$time', '$time', '1', '".$session_id."')";
-    Database::query($sql);
-
+    if (Database::num_rows($result) > 0) {
+        $i_course_access_id = Database::result($result,0,0);
+        //We update the course tracking table
+        $sql = "UPDATE $course_tracking_table  SET logout_course_date = '$time', counter = counter+1
+            WHERE course_access_id = ".intval($i_course_access_id)." AND session_id = ".$session_id;
+        Database::query($sql);
+    } else {
+        $sql="INSERT INTO $course_tracking_table (course_code, user_id, login_course_date, logout_course_date, counter, session_id)" .
+            "VALUES('".$course_code."', '".$user_id."', '$time', '$time', '1','".$session_id."')";
+        Database::query($sql);
+    }
     // Course catalog stats modifications see #4191
     CourseManager::update_course_ranking(null, null, null, null, true, false);
 }

+ 37 - 4
main/inc/lib/export.lib.inc.php

@@ -68,7 +68,7 @@ class Export
             $file->put($row);
         }
 		$file->close();
-		DocumentManager::file_send_for_download($path, true, $filename.'.csv');
+		DocumentManager::file_send_for_download($path, false, $filename.'.csv');
         unlink($path);
         exit;
 	}
@@ -78,17 +78,50 @@ class Export
      * @param array $data
      * @param string $filename
      */
-    public static function export_table_xls($data, $filename = 'export')
+    public static function export_table_xls($data, $filename = 'export', $encoding = 'utf-8')
     {
         $file = api_get_path(SYS_ARCHIVE_PATH).uniqid('').'.xls';
         $handle = fopen($file, 'a+');
+        $systemEncoding = api_get_system_encoding();
         foreach ($data as $row) {
-            fwrite($handle, implode("\t", $row)."\n");
+            $string = implode("\t", $row);
+            if ($encoding != 'utf-8') {
+                $string = api_convert_encoding($string, $encoding, $systemEncoding);
+            }
+            fwrite($handle, $string."\n");
         }
         fclose($handle);
-        DocumentManager::file_send_for_download($file, true, $filename.'.xls');
+        DocumentManager::file_send_for_download($file, false, $filename.'.xls');
 	}
 
+    /**
+     * Export tabular data to XLS-file (as html table)
+     * @param array $data
+     * @param string $filename
+     */
+    public static function export_table_xls_html($data, $filename = 'export', $encoding = 'utf-8')
+    {
+        $file = api_get_path(SYS_ARCHIVE_PATH).uniqid('').'.xls';
+        $handle = fopen($file, 'a+');
+        $systemEncoding = api_get_system_encoding();
+        fwrite($handle, '<!DOCTYPE html><html><meta http-equiv="Content-Type" content="text/html" charset="utf-8" /><body><table>');
+        foreach ($data as $id => $row) {
+            foreach ($row as $id2 => $row2) {
+                $data[$id][$id2] = api_htmlentities($row2);
+            }
+        }
+        foreach ($data as $row) {
+            $string = implode("</td><td>", $row);
+            $string = '<tr><td>' . $string . '</td></tr>';
+            if ($encoding != 'utf-8') {
+                $string = api_convert_encoding($string, $encoding, $systemEncoding);
+            }
+            fwrite($handle, $string."\n");
+        }
+        fwrite($handle, '</table></body></html>');
+        fclose($handle);
+        DocumentManager::file_send_for_download($file, false, $filename.'.xls');
+    }
     /**
     * Export tabular data to XML-file
     * @param array  Simple array of data to put in XML

+ 1648 - 0
main/inc/lib/extra_field.lib.php

@@ -0,0 +1,1648 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+/**
+ * Class ExtraField
+ */
+class ExtraField extends Model
+{
+    public $columns = array(
+        'id',
+        'field_type',
+        'field_variable',
+        'field_display_text',
+        'field_default_value',
+        'field_order',
+        'field_visible',
+        'field_changeable',
+        'field_filter',
+        'field_loggeable',
+        'tms'
+    );
+
+
+    public $ops = array(
+        'eq' => '=',        //equal
+        'ne' => '<>',       //not equal
+        'lt' => '<',        //less than
+        'le' => '<=',       //less than or equal
+        'gt' => '>',        //greater than
+        'ge' => '>=',       //greater than or equal
+        'bw' => 'LIKE',     //begins with
+        'bn' => 'NOT LIKE', //doesn't begin with
+        'in' => 'LIKE',     //is in
+        'ni' => 'NOT LIKE', //is not in
+        'ew' => 'LIKE',     //ends with
+        'en' => 'NOT LIKE', //doesn't end with
+        'cn' => 'LIKE',     //contains
+        'nc' => 'NOT LIKE'  //doesn't contain
+    );
+
+    const FIELD_TYPE_TEXT            = 1;
+    const FIELD_TYPE_TEXTAREA        = 2;
+    const FIELD_TYPE_RADIO           = 3;
+    const FIELD_TYPE_SELECT          = 4;
+    const FIELD_TYPE_SELECT_MULTIPLE = 5;
+    const FIELD_TYPE_DATE            = 6;
+    const FIELD_TYPE_DATETIME        = 7;
+    const FIELD_TYPE_DOUBLE_SELECT   = 8;
+    const FIELD_TYPE_DIVIDER         = 9;
+    const FIELD_TYPE_TAG             = 10;
+    const FIELD_TYPE_TIMEZONE        = 11;
+    const FIELD_TYPE_SOCIAL_PROFILE  = 12;
+    const FIELD_TYPE_CHECKBOX        = 13;
+
+    public $type = 'user'; //or session or course
+    public $handler_id = 'user_id';
+    public $pageName;
+    public $pageUrl;
+
+    /**
+     * @param string $type
+     */
+    public function __construct($type)
+    {
+        $this->type = $type;
+        switch ($this->type) {
+            case 'course':
+                $this->table_field_options = Database::get_main_table(TABLE_MAIN_COURSE_FIELD_OPTIONS);
+                $this->table_field_values  = Database::get_main_table(TABLE_MAIN_COURSE_FIELD_VALUES);
+
+                //Used for the model
+                $this->table      = Database::get_main_table(TABLE_MAIN_COURSE_FIELD);
+                $this->handler_id = 'course_code';
+                $this->handlerEntityId = 'courseCode';
+                $this->primaryKey = 'id';
+                break;
+            case 'user':
+                $this->table_field_options = Database::get_main_table(TABLE_MAIN_USER_FIELD_OPTIONS);
+                $this->table_field_values  = Database::get_main_table(TABLE_MAIN_USER_FIELD_VALUES);
+
+                //Used for the model
+                $this->table      = Database::get_main_table(TABLE_MAIN_USER_FIELD);
+                $this->handler_id = 'user_id';
+                $this->handlerEntityId = 'userId';
+                $this->primaryKey = 'user_id';
+                break;
+            case 'session':
+                $this->table_field_options = Database::get_main_table(TABLE_MAIN_SESSION_FIELD_OPTIONS);
+                $this->table_field_values  = Database::get_main_table(TABLE_MAIN_SESSION_FIELD_VALUES);
+
+                //Used for the model
+                $this->table      = Database::get_main_table(TABLE_MAIN_SESSION_FIELD);
+                $this->handler_id = 'session_id';
+                $this->handlerEntityId = 'sessionId';
+                $this->primaryKey = 'id';
+                break;
+            case 'question':
+                $this->table_field_options = Database::get_main_table(TABLE_MAIN_QUESTION_FIELD_OPTIONS);
+                $this->table_field_values  = Database::get_main_table(TABLE_MAIN_QUESTION_FIELD_VALUES);
+
+                //Used for the model
+                $this->table      = Database::get_main_table(TABLE_MAIN_QUESTION_FIELD);
+                $this->handler_id = 'question_id';
+                $this->handlerEntityId = 'questionId';
+                $this->primaryKey = 'iid';
+                break;
+            case 'lp':
+                $this->table_field_options = Database::get_main_table(TABLE_MAIN_LP_FIELD_OPTIONS);
+                $this->table_field_values  = Database::get_main_table(TABLE_MAIN_LP_FIELD_VALUES);
+
+                // Used for the model
+                $this->table      = Database::get_main_table(TABLE_MAIN_LP_FIELD);
+                $this->handler_id = 'lp_id';
+                $this->handlerEntityId = 'lpId';
+                $this->primaryKey = 'id';
+                break;
+        }
+        $this->pageUrl  = 'extra_fields.php?type='.$this->type;
+        // Example QuestionFields
+        $this->pageName = get_lang(ucwords($this->type).'Fields');
+    }
+
+    static function getValidExtraFieldTypes()
+    {
+        return array(
+            'user',
+            'course',
+            'session',
+            'question',
+            'lp'
+        );
+    }
+
+    public function get_count()
+    {
+        $row = Database::select('count(*) as count', $this->table, array(), 'first');
+
+        return $row['count'];
+    }
+
+    public function get_all($where_conditions = array(), $order_field_options_by = null)
+    {
+        $options = Database::select(
+            '*',
+            $this->table,
+            array('where' => $where_conditions, 'order' => 'field_order ASC')
+        );
+
+        $field_option = new ExtraFieldOption($this->type);
+        if (!empty($options)) {
+            foreach ($options as &$option) {
+                $option['options'] = $field_option->get_field_options_by_field(
+                    $option['id'],
+                    false,
+                    $order_field_options_by
+                );
+            }
+        }
+
+        return $options;
+    }
+
+
+    public function get_handler_field_info_by_field_variable($field_variable)
+    {
+        $field_variable = Database::escape_string($field_variable);
+        $sql_field      = "SELECT * FROM {$this->table} WHERE field_variable = '$field_variable'";
+        $result         = Database::query($sql_field);
+        if (Database::num_rows($result)) {
+            $r_field = Database::fetch_array($result, 'ASSOC');
+
+            return $r_field;
+        } else {
+            return false;
+        }
+    }
+
+    public function get_max_field_order()
+    {
+        $sql = "SELECT MAX(field_order) FROM {$this->table}";
+        $res = Database::query($sql);
+
+        $order = 0;
+        if (Database::num_rows($res) > 0) {
+            $row   = Database::fetch_row($res);
+            $order = $row[0] + 1;
+        }
+
+        return $order;
+    }
+
+    public static function get_extra_fields_by_handler($handler)
+    {
+        $types                                   = array();
+        $types[self::FIELD_TYPE_TEXT]            = get_lang('FieldTypeText');
+        $types[self::FIELD_TYPE_TEXTAREA]        = get_lang('FieldTypeTextarea');
+        $types[self::FIELD_TYPE_RADIO]           = get_lang('FieldTypeRadio');
+        $types[self::FIELD_TYPE_SELECT]          = get_lang('FieldTypeSelect');
+        $types[self::FIELD_TYPE_SELECT_MULTIPLE] = get_lang('FieldTypeSelectMultiple');
+        $types[self::FIELD_TYPE_DATE]            = get_lang('FieldTypeDate');
+        $types[self::FIELD_TYPE_DATETIME]        = get_lang('FieldTypeDatetime');
+        $types[self::FIELD_TYPE_DOUBLE_SELECT]   = get_lang('FieldTypeDoubleSelect');
+        $types[self::FIELD_TYPE_DIVIDER]         = get_lang('FieldTypeDivider');
+        $types[self::FIELD_TYPE_TAG]             = get_lang('FieldTypeTag');
+        $types[self::FIELD_TYPE_TIMEZONE]        = get_lang('FieldTypeTimezone');
+        $types[self::FIELD_TYPE_SOCIAL_PROFILE]  = get_lang('FieldTypeSocialProfile');
+
+        switch ($handler) {
+            case 'course':
+            case 'session':
+            case 'user':
+                break;
+        }
+
+        return $types;
+    }
+
+    /**
+     * Add elements to a form
+     *
+     * @param FormValidator $form
+     * @param int $item_id
+     * @return array|bool
+     */
+    public function addElements($form, $item_id = null)
+    {
+        if (empty($form)) {
+            return false;
+        }
+
+        $extra_data = false;
+        if (!empty($item_id)) {
+            $extra_data = self::get_handler_extra_data($item_id);
+            if ($form) {
+                $form->setDefaults($extra_data);
+            }
+        }
+
+        $extra_fields = $this->get_all(null, 'option_order');
+        $extra = $this->set_extra_fields_in_form(
+            $form,
+            $extra_data,
+            $this->type.'_field',
+            false,
+            false,
+            $extra_fields,
+            $item_id
+        );
+
+        return $extra;
+    }
+
+    /**
+     *
+     * @param int $item_id (session_id, question_id, course id)
+     * @return array
+     */
+    public function get_handler_extra_data($item_id)
+    {
+        if (empty($item_id)) {
+            return array();
+        }
+
+        $extra_data   = array();
+        $fields       = self::get_all();
+        $field_values = new ExtraFieldValue($this->type);
+
+        if (!empty($fields) > 0) {
+            foreach ($fields as $field) {
+                $field_value = $field_values->get_values_by_handler_and_field_id($item_id, $field['id']);
+                if ($field_value) {
+                    $field_value = $field_value['field_value'];
+
+                    switch ($field['field_type']) {
+                        case ExtraField::FIELD_TYPE_DOUBLE_SELECT:
+                            $selected_options                                                                           = explode(
+                                '::',
+                                $field_value
+                            );
+                            $extra_data['extra_'.$field['field_variable']]['extra_'.$field['field_variable']]           = $selected_options[0];
+                            $extra_data['extra_'.$field['field_variable']]['extra_'.$field['field_variable'].'_second'] = $selected_options[1];
+                            break;
+                        case ExtraField::FIELD_TYPE_SELECT_MULTIPLE:
+                            $field_value = explode(';', $field_value);
+                        case ExtraField::FIELD_TYPE_RADIO:
+                            $extra_data['extra_'.$field['field_variable']]['extra_'.$field['field_variable']] = $field_value;
+                            break;
+                        default:
+                            $extra_data['extra_'.$field['field_variable']] = $field_value;
+                            break;
+                    }
+                } else {
+                    // Set default values
+                    if (isset($field['field_default_value']) && !empty($field['field_default_value'])) {
+                        $extra_data['extra_'.$field['field_variable']] = $field['field_default_value'];
+                    }
+                }
+            }
+        }
+
+        return $extra_data;
+    }
+
+    public function get_all_extra_field_by_type($field_type)
+    {
+        // all the information of the field
+        $sql    = "SELECT * FROM  {$this->table} WHERE field_type='".Database::escape_string($field_type)."'";
+        $result = Database::query($sql);
+        $return = array();
+        while ($row = Database::fetch_array($result)) {
+            $return[] = $row['id'];
+        }
+
+        return $return;
+    }
+
+
+    public function get_field_types()
+    {
+        return self::get_extra_fields_by_handler($this->type);
+    }
+
+    public function get_field_type_by_id($id)
+    {
+        $types = self::get_field_types();
+        if (isset($types[$id])) {
+            return $types[$id];
+        }
+
+        return null;
+    }
+
+    /**
+     * Converts a string like this:
+     * France:Paris;Bretagne;Marseilles;Lyon|Belgique:Bruxelles;Namur;Liège;Bruges|Peru:Lima;Piura;
+     * into
+     * array('France' => array('Paris', 'Bregtane', 'Marseilles'), 'Belgique' => array('Namur', 'Liège', etc
+     * @param string $string
+     * @return array
+     */
+    static function extra_field_double_select_convert_string_to_array($string)
+    {
+        $options        = explode('|', $string);
+        $options_parsed = array();
+        $id             = 0;
+        if (!empty($options)) {
+            foreach ($options as $sub_options) {
+                $options             = explode(':', $sub_options);
+                $sub_sub_options     = explode(';', $options[1]);
+                $options_parsed[$id] = array('label' => $options[0], 'options' => $sub_sub_options);
+                $id++;
+            }
+        }
+
+        return $options_parsed;
+    }
+
+    static function extra_field_double_select_convert_array_to_ordered_array($options)
+    {
+        $options_parsed = array();
+        if (!empty($options)) {
+            foreach ($options as $option) {
+                if ($option['option_value'] == 0) {
+                    $options_parsed[$option['id']][] = $option;
+                } else {
+                    $options_parsed[$option['option_value']][] = $option;
+                }
+            }
+        }
+
+        return $options_parsed;
+    }
+
+    /**
+     * @param array options the result of the get_field_options_by_field() array
+     */
+    static function extra_field_double_select_convert_array_to_string($options)
+    {
+        $string         = null;
+        $options_parsed = self::extra_field_double_select_convert_array_to_ordered_array($options);
+
+        if (!empty($options_parsed)) {
+            foreach ($options_parsed as $option) {
+                foreach ($option as $key => $item) {
+                    $string .= $item['option_display_text'];
+                    if ($key == 0) {
+                        $string .= ':';
+                    } else {
+                        if (isset($option[$key + 1])) {
+                            $string .= ';';
+                        }
+                    }
+                }
+                $string .= '|';
+            }
+        }
+
+        if (!empty($string)) {
+            $string = substr($string, 0, strlen($string) - 1);
+        }
+
+        return $string;
+    }
+
+    /**
+     * @param array $params
+     * @return array
+     */
+    public function clean_parameters($params)
+    {
+        if (!isset($params['field_variable']) || empty($params['field_variable'])) {
+            $params['field_variable'] = trim(strtolower(str_replace(" ", "_", $params['field_display_text'])));
+        }
+
+        if (!isset($params['field_order'])) {
+            $max_order             = self::get_max_field_order();
+            $params['field_order'] = $max_order;
+        }
+
+        return $params;
+    }
+
+    /**
+     * @param array $params
+     * @param bool $show_query
+     * @return bool
+     */
+    public function save($params, $show_query = false)
+    {
+        $session_field_info = self::get_handler_field_info_by_field_variable($params['field_variable']);
+        $params = self::clean_parameters($params);
+        if ($session_field_info) {
+            return $session_field_info['id'];
+        } else {
+            if (!isset($params['tms'])) {
+                $params['tms'] = api_get_utc_datetime();
+            }
+            $id = parent::save($params, $show_query);
+            if ($id) {
+                $session_field_option = new ExtraFieldOption($this->type);
+                $params['field_id']   = $id;
+                $session_field_option->save($params);
+            }
+
+            return $id;
+        }
+    }
+
+
+    public function update($params)
+    {
+        $params = self::clean_parameters($params);
+        if (isset($params['id'])) {
+            $field_option       = new ExtraFieldOption($this->type);
+            $params['field_id'] = $params['id'];
+            $field_option->save($params);
+        }
+        parent::update($params);
+    }
+
+    public function delete($id)
+    {
+        parent::delete($id);
+        $field_option = new ExtraFieldOption($this->type);
+        $field_option->delete_all_options_by_field_id($id);
+
+        $session_field_values = new ExtraFieldValue($this->type);
+        $session_field_values->delete_all_values_by_field_id($id);
+    }
+
+    /**
+     * @param FormValidator $form
+     * @param array $extraData
+     * @param string $form_name
+     * @param bool $admin_permissions
+     * @param int $user_id
+     * @param array $extra
+     * @param int $itemId
+     *
+     * @return array
+     */
+    public function set_extra_fields_in_form(
+        $form,
+        $extraData,
+        $form_name,
+        $admin_permissions = false,
+        $user_id = null,
+        $extra = array(),
+        $itemId = null
+    ) {
+        $user_id = intval($user_id);
+        $type = $this->type;
+
+        // User extra fields
+        if ($type == 'user') {
+            $extra = UserManager::get_extra_fields(0, 50, 5, 'ASC', true, null, true);
+        }
+
+        $jquery_ready_content = null;
+
+        if (!empty($extra)) {
+            foreach ($extra as $field_details) {
+
+                // Getting default value id if is set
+                $defaultValueId = null;
+                if (isset($field_details['options']) && !empty($field_details['options'])) {
+                    $valueToFind = null;
+                    if (isset($field_details['field_default_value'])) {
+                        $valueToFind = $field_details['field_default_value'];
+                    }
+                    // If a value is found we override the default value
+                    if (isset($extraData['extra_'.$field_details['field_variable']])) {
+                        $valueToFind = $extraData['extra_'.$field_details['field_variable']];
+                    }
+
+                    foreach ($field_details['options'] as $option) {
+                        if ($option['option_value'] == $valueToFind) {
+                            $defaultValueId = $option['id'];
+                        }
+                    }
+                }
+
+                if (!$admin_permissions) {
+                    if ($field_details['field_visible'] == 0) {
+                        continue;
+                    }
+                }
+
+                switch ($field_details['field_type']) {
+                    case ExtraField::FIELD_TYPE_TEXT:
+                        $form->addElement(
+                            'text',
+                            'extra_'.$field_details['field_variable'],
+                            $field_details['field_display_text'],
+                            array('class' => 'span4')
+                        );
+                        $form->applyFilter('extra_'.$field_details['field_variable'], 'stripslashes');
+                        $form->applyFilter('extra_'.$field_details['field_variable'], 'trim');
+                        if (!$admin_permissions) {
+                            if ($field_details['field_visible'] == 0) {
+                                $form->freeze(
+                                    'extra_'.$field_details['field_variable']
+                                );
+                            }
+                        }
+                        break;
+                    case ExtraField::FIELD_TYPE_TEXTAREA:
+                        $form->add_html_editor(
+                            'extra_'.$field_details['field_variable'],
+                            $field_details['field_display_text'],
+                            false,
+                            false,
+                            array('ToolbarSet' => 'Profile', 'Width' => '100%', 'Height' => '130')
+                        );
+                        $form->applyFilter('extra_'.$field_details['field_variable'], 'stripslashes');
+                        $form->applyFilter('extra_'.$field_details['field_variable'], 'trim');
+                        if (!$admin_permissions) {
+                            if ($field_details['field_visible'] == 0) {
+                                $form->freeze(
+                                    'extra_'.$field_details['field_variable']
+                                );
+                            }
+                        }
+                        break;
+                    case ExtraField::FIELD_TYPE_RADIO:
+                        $group = array();
+                        if (isset($field_details['options']) && !empty($field_details['options'])) {
+                            foreach ($field_details['options'] as $option_details) {
+                                $options[$option_details['option_value']] = $option_details['option_display_text'];
+                                $group[]                                  = $form->createElement(
+                                    'radio',
+                                    'extra_'.$field_details['field_variable'],
+                                    $option_details['option_value'],
+                                    $option_details['option_display_text'].'<br />',
+                                    $option_details['option_value']
+                                );
+                            }
+                        }
+                        $form->addGroup(
+                            $group,
+                            'extra_'.$field_details['field_variable'],
+                            $field_details['field_display_text'],
+                            ''
+                        );
+                        if (!$admin_permissions) {
+                            if ($field_details['field_visible'] == 0) {
+                                $form->freeze(
+                                    'extra_'.$field_details['field_variable']
+                                );
+                            }
+                        }
+                        break;
+                    case ExtraField::FIELD_TYPE_CHECKBOX:
+                        $group = array();
+                        if (isset($field_details['options']) && !empty($field_details['options'])) {
+                            foreach ($field_details['options'] as $option_details) {
+                                $options[$option_details['option_value']] = $option_details['option_display_text'];
+                                $group[]                                  = $form->createElement(
+                                    'checkbox',
+                                    'extra_'.$field_details['field_variable'],
+                                    $option_details['option_value'],
+                                    $option_details['option_display_text'].'<br />',
+                                    $option_details['option_value']
+                                );
+                            }
+                        } else {
+                            // We assume that is a switch on/off with 1 and 0 as values
+                            $group[] = $form->createElement(
+                                'checkbox',
+                                'extra_'.$field_details['field_variable'],
+                                null,
+                                //$field_details['field_display_text'].'<br />',
+                                'Yes <br />',
+                                null
+                            );
+                        }
+                        $form->addGroup(
+                            $group,
+                            'extra_'.$field_details['field_variable'],
+                            $field_details['field_display_text'],
+                            ''
+                        );
+                        if (!$admin_permissions) {
+                            if ($field_details['field_visible'] == 0) {
+                                $form->freeze(
+                                    'extra_'.$field_details['field_variable']
+                                );
+                            }
+                        }
+                        break;
+                    case ExtraField::FIELD_TYPE_SELECT:
+                        $get_lang_variables = false;
+                        if (in_array(
+                            $field_details['field_variable'],
+                            array('mail_notify_message', 'mail_notify_invitation', 'mail_notify_group_message')
+                        )
+                        ) {
+                            $get_lang_variables = true;
+                        }
+
+                        // Get extra field workflow
+                        $userInfo = api_get_user_info();
+
+                        $addOptions = array();
+
+                        global $app;
+                        $optionsExists = $app['orm.em']->getRepository('ChamiloLMS\Entity\ExtraFieldOptionRelFieldOption')->
+                            findOneBy(array('fieldId' => $field_details['id']));
+
+                        if ($optionsExists) {
+                            if (isset($userInfo['status']) && !empty($userInfo['status'])) {
+
+                                $fieldWorkFlow = $app['orm.em']->getRepository('ChamiloLMS\Entity\ExtraFieldOptionRelFieldOption')
+                                    ->findBy(
+                                        array(
+                                            'fieldId' => $field_details['id'],
+                                            'relatedFieldOptionId' => $defaultValueId,
+                                            'roleId' => $userInfo['status']
+                                        )
+                                    );
+                                foreach ($fieldWorkFlow as $item) {
+                                    $addOptions[] = $item->getFieldOptionId();
+                                }
+                            }
+                        }
+
+                        $options = array();
+                        if (empty($defaultValueId)) {
+                            $options[''] = get_lang('SelectAnOption');
+                        }
+
+                        $optionList = array();
+                        if (!empty($field_details['options'])) {
+                            foreach ($field_details['options'] as $option_details) {
+                                $optionList[$option_details['id']] = $option_details;
+                                if ($get_lang_variables) {
+                                    $options[$option_details['option_value']] = get_lang($option_details['option_display_text']);
+                                } else {
+                                    if ($optionsExists) {
+                                        // Adding always the default value
+                                        if ($option_details['id'] == $defaultValueId) {
+                                            $options[$option_details['option_value']] = $option_details['option_display_text'];
+                                        } else {
+                                            if (isset($addOptions) && !empty($addOptions)) {
+                                                // Parsing filters
+                                                if (in_array($option_details['id'], $addOptions)) {
+                                                    $options[$option_details['option_value']] = $option_details['option_display_text'];
+                                                }
+                                            }
+                                        }
+                                    } else {
+                                        // Normal behaviour
+                                        $options[$option_details['option_value']] = $option_details['option_display_text'];
+                                    }
+                                }
+                            }
+
+                            if (isset($optionList[$defaultValueId])) {
+
+                                if (isset($optionList[$defaultValueId]['option_value']) && $optionList[$defaultValueId]['option_value'] == 'aprobada') {
+                                    if (api_is_question_manager() == false) {
+                                        $form->freeze();
+                                    }
+                                }
+                            }
+
+                            // Setting priority message
+                            if (isset($optionList[$defaultValueId]) && isset($optionList[$defaultValueId]['priority'])) {
+
+                                if (!empty($optionList[$defaultValueId]['priority'])) {
+                                    $priorityId = $optionList[$defaultValueId]['priority'];
+                                    $option = new ExtraFieldOption($this->type);
+                                    $messageType = $option->getPriorityMessageType($priorityId);
+                                    $form->addElement('label', null, Display::return_message($optionList[$defaultValueId]['priority_message'], $messageType));
+                                }
+                            }
+                        }
+
+                        if ($get_lang_variables) {
+                            $field_details['field_display_text'] = get_lang($field_details['field_display_text']);
+                        }
+
+                        // chzn-select doesn't work for sessions??
+                        $form->addElement(
+                            'select',
+                            'extra_'.$field_details['field_variable'],
+                            $field_details['field_display_text'],
+                            $options,
+                            array('id' => 'extra_'.$field_details['field_variable'])
+                        );
+
+                        if ($optionsExists && $field_details['field_loggeable'] && !empty($defaultValueId)) {
+
+                            $form->addElement(
+                                'textarea',
+                                'extra_'.$field_details['field_variable'].'_comment',
+                                $field_details['field_display_text'].' '.get_lang('Comment')
+                            );
+
+                            $extraFieldValue = new ExtraFieldValue($this->type);
+                            $repo = $app['orm.em']->getRepository($extraFieldValue->entityName);
+                            $repoLog = $app['orm.em']->getRepository('Gedmo\Loggable\Entity\LogEntry');
+                            $newEntity = $repo->findOneBy(
+                                array(
+                                    $this->handlerEntityId => $itemId,
+                                    'fieldId' => $field_details['id']
+                                )
+                            );
+                            // @todo move this in a function inside the class
+                            if ($newEntity) {
+                                $logs = $repoLog->getLogEntries($newEntity);
+                                if (!empty($logs)) {
+                                    $html = '<b>'.get_lang('LatestChanges').'</b><br /><br />';
+
+                                    $table = new HTML_Table(array('class' => 'data_table'));
+                                    $table->setHeaderContents(0, 0, get_lang('Value'));
+                                    $table->setHeaderContents(0, 1, get_lang('Comment'));
+                                    $table->setHeaderContents(0, 2, get_lang('ModifyDate'));
+                                    $table->setHeaderContents(0, 3, get_lang('Username'));
+                                    $row = 1;
+                                    foreach ($logs as $log) {
+                                        $column = 0;
+                                        $data = $log->getData();
+                                        $fieldValue = isset($data['fieldValue']) ? $data['fieldValue'] : null;
+                                        $comment = isset($data['comment']) ? $data['comment'] : null;
+
+                                        $table->setCellContents($row, $column, $fieldValue);
+                                        $column++;
+                                        $table->setCellContents($row, $column, $comment);
+                                        $column++;
+                                        $table->setCellContents($row, $column, api_get_local_time($log->getLoggedAt()->format('Y-m-d H:i:s')));
+                                        $column++;
+                                        $table->setCellContents($row, $column, $log->getUsername());
+                                        $row++;
+                                    }
+                                    $form->addElement('label', null, $html.$table->toHtml());
+                                }
+                            }
+                        }
+
+                        if (!$admin_permissions) {
+                            if ($field_details['field_visible'] == 0) {
+                                $form->freeze('extra_'.$field_details['field_variable']);
+                            }
+                        }
+                        break;
+                    case ExtraField::FIELD_TYPE_SELECT_MULTIPLE:
+                        $options = array();
+                        foreach ($field_details['options'] as $option_id => $option_details) {
+                            $options[$option_details['option_value']] = $option_details['option_display_text'];
+                        }
+                        $form->addElement(
+                            'select',
+                            'extra_'.$field_details['field_variable'],
+                            $field_details['field_display_text'],
+                            $options,
+                            array('multiple' => 'multiple')
+                        );
+                        if (!$admin_permissions) {
+                            if ($field_details['field_visible'] == 0) {
+                                $form->freeze('extra_'.$field_details['field_variable']);
+                            }
+                        }
+                        break;
+                    case ExtraField::FIELD_TYPE_DATE:
+                        $form->addElement(
+                            'datepickerdate',
+                            'extra_'.$field_details['field_variable'],
+                            $field_details['field_display_text'],
+                            array('form_name' => $form_name)
+                        );
+                        $form->_elements[$form->_elementIndex['extra_'.$field_details['field_variable']]]->setLocalOption(
+                            'minYear',
+                            1900
+                        );
+                        $defaults['extra_'.$field_details['field_variable']] = date('Y-m-d 12:00:00');
+                        if (!isset($form->_defaultValues['extra_'.$field_details['field_variable']])) {
+                            $form->setDefaults($defaults);
+                        }
+                        if (!$admin_permissions) {
+                            if ($field_details['field_visible'] == 0) {
+                                $form->freeze('extra_'.$field_details['field_variable']);
+                            }
+                        }
+                        $form->applyFilter('theme', 'trim');
+                        break;
+                    case ExtraField::FIELD_TYPE_DATETIME:
+                        $form->addElement(
+                            'datepicker',
+                            'extra_'.$field_details['field_variable'],
+                            $field_details['field_display_text'],
+                            array('form_name' => $form_name)
+                        );
+                        $form->_elements[$form->_elementIndex['extra_'.$field_details['field_variable']]]->setLocalOption(
+                            'minYear',
+                            1900
+                        );
+                        $defaults['extra_'.$field_details['field_variable']] = date('Y-m-d 12:00:00');
+                        if (!isset($form->_defaultValues['extra_'.$field_details['field_variable']])) {
+                            $form->setDefaults($defaults);
+                        }
+                        if (!$admin_permissions) {
+                            if ($field_details['field_visible'] == 0) {
+                                $form->freeze('extra_'.$field_details['field_variable']);
+                            }
+                        }
+                        $form->applyFilter('theme', 'trim');
+                        break;
+                    case ExtraField::FIELD_TYPE_DOUBLE_SELECT:
+                        $first_select_id = 'first_extra_'.$field_details['field_variable'];
+                        $url = api_get_path(WEB_AJAX_PATH).'extra_field.ajax.php?1=1';
+
+                        $jquery_ready_content .= '
+                        $("#'.$first_select_id.'").on("change", function() {
+                            var id = $(this).val();
+                            if (id) {
+                                $.ajax({
+                                    url: "'.$url.'&a=get_second_select_options",
+                                    dataType: "json",
+                                    data: "type='.$type.'&field_id='.$field_details['id'].'&option_value_id="+id,
+                                    success: function(data) {
+                                        $("#second_extra_'.$field_details['field_variable'].'").empty();
+                                        $.each(data, function(index, value) {
+                                            $("#second_extra_'.$field_details['field_variable'].'").append($("<option/>", {
+                                                value: index,
+                                                text: value
+                                            }));
+                                        });
+                                    },
+                                });
+                            } else {
+                                $("#second_extra_'.$field_details['field_variable'].'").empty();
+                            }
+                        });';
+
+                        $first_id  = null;
+                        $second_id = null;
+
+                        if (!empty($extraData)) {
+                            $first_id  = $extraData['extra_'.$field_details['field_variable']]['extra_'.$field_details['field_variable']];
+                            $second_id = $extraData['extra_'.$field_details['field_variable']]['extra_'.$field_details['field_variable'].'_second'];
+                        }
+
+                        $options = ExtraField::extra_field_double_select_convert_array_to_ordered_array(
+                            $field_details['options']
+                        );
+                        $values  = array('' => get_lang('Select'));
+
+                        $second_values = array();
+                        if (!empty($options)) {
+                            foreach ($options as $option) {
+                                foreach ($option as $sub_option) {
+                                    if ($sub_option['option_value'] == '0') {
+                                        $values[$sub_option['id']] = $sub_option['option_display_text'];
+                                    } else {
+                                        if ($first_id === $sub_option['option_value']) {
+                                            $second_values[$sub_option['id']] = $sub_option['option_display_text'];
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                        $group   = array();
+                        $group[] = $form->createElement(
+                            'select',
+                            'extra_'.$field_details['field_variable'],
+                            null,
+                            $values,
+                            array('id' => $first_select_id)
+                        );
+                        $group[] = $form->createElement(
+                            'select',
+                            'extra_'.$field_details['field_variable'].'_second',
+                            null,
+                            $second_values,
+                            array('id' => 'second_extra_'.$field_details['field_variable'])
+                        );
+                        $form->addGroup(
+                            $group,
+                            'extra_'.$field_details['field_variable'],
+                            $field_details['field_display_text'],
+                            '&nbsp;'
+                        );
+
+                        if (!$admin_permissions) {
+                            if ($field_details['field_visible'] == 0) {
+                                $form->freeze('extra_'.$field_details['field_variable']);
+                            }
+                        }
+                        break;
+                    case ExtraField::FIELD_TYPE_DIVIDER:
+                        $form->addElement(
+                            'static',
+                            $field_details['field_variable'],
+                            '<br /><strong>'.$field_details['field_display_text'].'</strong>'
+                        );
+                        break;
+                    case ExtraField::FIELD_TYPE_TAG:
+                        $field_variable = $field_details['field_variable'];
+                        $field_id       = $field_details['id'];
+
+                        if ($this->type == 'user') {
+
+                            // The magic should be here
+                            $user_tags = UserManager::get_user_tags($user_id, $field_details['id']);
+
+                            $tag_list = '';
+                            if (is_array($user_tags) && count($user_tags) > 0) {
+                                foreach ($user_tags as $tag) {
+                                    $tag_list .= '<option value="'.$tag['tag'].'" class="selected">'.$tag['tag'].'</option>';
+                                }
+                            }
+                            $url = api_get_path(WEB_AJAX_PATH).'user_manager.ajax.php?';
+                        } else {
+                            $extraFieldValue = new ExtraFieldValue($this->type);
+                            $tags = array();
+                            if (!empty($itemId)) {
+                                $tags = $extraFieldValue->getAllValuesByItemAndField($itemId, $field_id);
+                            }
+                            $tag_list = '';
+                            if (is_array($tags) && count($tags) > 0) {
+                                $extraFieldOption = new ExtraFieldOption($this->type);
+                                foreach ($tags as $tag) {
+                                    $option = $extraFieldOption->get($tag['field_value']);
+                                    $tag_list .= '<option value="'.$option['id'].'" class="selected">'.$option['option_display_text'].'</option>';
+                                }
+                            }
+                            $url = api_get_path(WEB_AJAX_PATH).'extra_field.ajax.php';
+                        }
+
+                        $form->addElement('hidden', 'extra_'.$field_details['field_variable'].'__persist__', 1);
+
+                        $multiSelect = '<select id="extra_'.$field_details['field_variable'].'" name="extra_'.$field_details['field_variable'].'">
+                                        '.$tag_list.'
+                                        </select>';
+
+                        $form->addElement('label', $field_details['field_display_text'], $multiSelect);
+                        $complete_text = get_lang('StartToType');
+
+                        //if cache is set to true the jquery will be called 1 time
+
+                        $jquery_ready_content .= <<<EOF
+                    $("#extra_$field_variable").fcbkcomplete({
+                        json_url: "$url?a=search_tags&field_id=$field_id&type={$this->type}",
+                        cache: false,
+                        filter_case: true,
+                        filter_hide: true,
+                        complete_text:"$complete_text",
+                        firstselected: false,
+                        filter_selected: true,
+                        newel: true
+                    });
+EOF;
+                        break;
+                    case ExtraField::FIELD_TYPE_TIMEZONE:
+                        $form->addElement(
+                            'select',
+                            'extra_'.$field_details['field_variable'],
+                            $field_details['field_display_text'],
+                            api_get_timezones(),
+                            ''
+                        );
+                        if ($field_details['field_visible'] == 0) {
+                            $form->freeze(
+                                'extra_'.$field_details['field_variable']
+                            );
+                        }
+                        break;
+                    case ExtraField::FIELD_TYPE_SOCIAL_PROFILE:
+                        // get the social network's favicon
+                        $icon_path = UserManager::get_favicon_from_url(
+                            $extraData['extra_'.$field_details['field_variable']],
+                            $field_details['field_default_value']
+                        );
+                        // special hack for hi5
+                        $leftpad = '1.7';
+                        $top     = '0.4';
+                        $domain  = parse_url($icon_path, PHP_URL_HOST);
+                        if ($domain == 'www.hi5.com' or $domain == 'hi5.com') {
+                            $leftpad = '3';
+                            $top     = '0';
+                        }
+                        // print the input field
+                        $form->addElement(
+                            'text',
+                            'extra_'.$field_details['field_variable'],
+                            $field_details['field_display_text'],
+                            array(
+                                'size'  => 60,
+                                'style' => 'background-image: url(\''.$icon_path.'\'); background-repeat: no-repeat; background-position: 0.4em '.$top.'em; padding-left: '.$leftpad.'em; '
+                            )
+                        );
+                        $form->applyFilter('extra_'.$field_details['field_variable'], 'stripslashes');
+                        $form->applyFilter('extra_'.$field_details['field_variable'], 'trim');
+                        if ($field_details['field_visible'] == 0) {
+                            $form->freeze('extra_'.$field_details['field_variable']);
+                        }
+                        break;
+                }
+            }
+        }
+        $return = array();
+        $return['jquery_ready_content'] = $jquery_ready_content;
+
+        return $return;
+    }
+
+    function setupBreadcrumb(&$breadcrumb, $action)
+    {
+        if ($action == 'add') {
+            $breadcrumb[] = array('url' => $this->pageUrl, 'name' => $this->pageName);
+            $breadcrumb[] = array('url' => '#', 'name' => get_lang('Add'));
+        } elseif ($action == 'edit') {
+            $breadcrumb[] = array('url' => $this->pageUrl, 'name' => $this->pageName);
+            $breadcrumb[] = array('url' => '#', 'name' => get_lang('Edit'));
+        } else {
+            $breadcrumb[] = array('url' => '#', 'name' => $this->pageName);
+        }
+    }
+
+
+    /**
+     * Displays the title + grid
+     */
+    public function display()
+    {
+        // action links
+        echo '<div class="actions">';
+        echo '<a href="../admin/index.php">'.Display::return_icon(
+                'back.png',
+                get_lang('BackTo').' '.get_lang('PlatformAdmin'),
+                '',
+                ICON_SIZE_MEDIUM
+            ).'</a>';
+        echo '<a href="'.api_get_self().'?action=add&type='.$this->type.'">'.Display::return_icon(
+                'add_user_fields.png',
+                get_lang('Add'),
+                '',
+                ICON_SIZE_MEDIUM
+            ).'</a>';
+        echo '</div>';
+        echo Display::grid_html($this->type.'_fields');
+    }
+
+    public function getJqgridColumnNames()
+    {
+        return array(
+            get_lang('Name'),
+            get_lang('FieldLabel'),
+            get_lang('Type'),
+            get_lang('FieldChangeability'),
+            get_lang('Visibility'),
+            get_lang('Filter'),
+            get_lang('FieldOrder'),
+            get_lang('Actions')
+        );
+    }
+
+    public function getJqgridColumnModel()
+    {
+        return array(
+            array('name' => 'field_display_text', 'index' => 'field_display_text', 'width' => '180', 'align' => 'left'),
+            array(
+                'name'     => 'field_variable',
+                'index'    => 'field_variable',
+                'width'    => '',
+                'align'    => 'left',
+                'sortable' => 'true'
+            ),
+            array(
+                'name'     => 'field_type',
+                'index'    => 'field_type',
+                'width'    => '',
+                'align'    => 'left',
+                'sortable' => 'true'
+            ),
+            array(
+                'name'     => 'field_changeable',
+                'index'    => 'field_changeable',
+                'width'    => '50',
+                'align'    => 'left',
+                'sortable' => 'true'
+            ),
+            array(
+                'name'     => 'field_visible',
+                'index'    => 'field_visible',
+                'width'    => '40',
+                'align'    => 'left',
+                'sortable' => 'true'
+            ),
+            array(
+                'name'     => 'field_filter',
+                'index'    => 'field_filter',
+                'width'    => '30',
+                'align'    => 'left',
+                'sortable' => 'true'
+            ),
+            array(
+                'name'     => 'field_order',
+                'index'    => 'field_order',
+                'width'    => '40',
+                'align'    => 'left',
+                'sortable' => 'true'
+            ),
+            array(
+                'name'      => 'actions',
+                'index'     => 'actions',
+                'width'     => '100',
+                'align'     => 'left',
+                'formatter' => 'action_formatter',
+                'sortable'  => 'false'
+            )
+        );
+
+    }
+
+    /**
+     * @param string $url
+     * @param string $action
+     * @return FormValidator
+     */
+    public function return_form($url, $action)
+    {
+        $form = new FormValidator($this->type.'_field', 'post', $url);
+
+        $form->addElement('hidden', 'type', $this->type);
+        $id = isset($_GET['id']) ? intval($_GET['id']) : null;
+        $form->addElement('hidden', 'id', $id);
+
+        // Setting the form elements
+        $header   = get_lang('Add');
+        $defaults = array();
+
+        if ($action == 'edit') {
+            $header = get_lang('Modify');
+            // Setting the defaults
+            $defaults = $this->get($id);
+        }
+
+        $form->addElement('header', $header);
+        $form->addElement('text', 'field_display_text', get_lang('Name'), array('class' => 'span5'));
+
+        // Field type
+        $types = self::get_field_types();
+
+        $form->addElement(
+            'select',
+            'field_type',
+            get_lang('FieldType'),
+            $types,
+            array('id' => 'field_type', 'class' => 'chzn-select', 'data-placeholder' => get_lang('Select'))
+        );
+        $form->addElement('label', get_lang('Example'), '<div id="example">-</div>');
+        $form->addElement('text', 'field_variable', get_lang('FieldLabel'), array('class' => 'span5'));
+        $form->addElement(
+            'text',
+            'field_options',
+            get_lang('FieldPossibleValues'),
+            array('id' => 'field_options', 'class' => 'span6')
+        );
+
+        $fieldWithOptions = array(
+            ExtraField::FIELD_TYPE_SELECT,
+            ExtraField::FIELD_TYPE_TAG,
+            ExtraField::FIELD_TYPE_DOUBLE_SELECT,
+        );
+
+        if ($action == 'edit') {
+            if (in_array($defaults['field_type'], $fieldWithOptions)) {
+                $url = Display::url(
+                    get_lang('EditExtraFieldOptions'),
+                    'extra_field_options.php?type='.$this->type.'&field_id='.$id
+                );
+                $form->addElement('label', null, $url);
+
+                if ($defaults['field_type'] == ExtraField::FIELD_TYPE_SELECT) {
+                    $urlWorkFlow = Display::url(
+                        get_lang('EditExtraFieldWorkFlow'),
+                        'extra_field_workflow.php?type='.$this->type.'&field_id='.$id
+                    );
+                    $form->addElement('label', null, $urlWorkFlow);
+                }
+
+                $form->freeze('field_options');
+            }
+        }
+        $form->addElement(
+            'text',
+            'field_default_value',
+            get_lang('FieldDefaultValue'),
+            array('id' => 'field_default_value', 'class' => 'span5')
+        );
+
+        $group   = array();
+        $group[] = $form->createElement('radio', 'field_visible', null, get_lang('Yes'), 1);
+        $group[] = $form->createElement('radio', 'field_visible', null, get_lang('No'), 0);
+        $form->addGroup($group, '', get_lang('Visible'), '', false);
+
+        $group   = array();
+        $group[] = $form->createElement('radio', 'field_changeable', null, get_lang('Yes'), 1);
+        $group[] = $form->createElement('radio', 'field_changeable', null, get_lang('No'), 0);
+        $form->addGroup($group, '', get_lang('FieldChangeability'), '', false);
+
+        $group   = array();
+        $group[] = $form->createElement('radio', 'field_filter', null, get_lang('Yes'), 1);
+        $group[] = $form->createElement('radio', 'field_filter', null, get_lang('No'), 0);
+        $form->addGroup($group, '', get_lang('FieldFilter'), '', false);
+
+        $group   = array();
+        $group[] = $form->createElement('radio', 'field_loggeable', null, get_lang('Yes'), 1);
+        $group[] = $form->createElement('radio', 'field_loggeable', null, get_lang('No'), 0);
+        $form->addGroup($group, '', get_lang('FieldLoggeable'), '', false);
+
+
+        $form->addElement('text', 'field_order', get_lang('FieldOrder'), array('class' => 'span1'));
+
+        if ($action == 'edit') {
+            $option = new ExtraFieldOption($this->type);
+            if ($defaults['field_type'] == ExtraField::FIELD_TYPE_DOUBLE_SELECT) {
+                $form->freeze('field_options');
+            }
+            $defaults['field_options'] = $option->get_field_options_by_field_to_string($id);
+            $form->addElement('button', 'submit', get_lang('Modify'), 'class="save"');
+        } else {
+            $defaults['field_visible']    = 0;
+            $defaults['field_changeable'] = 0;
+            $defaults['field_filter']     = 0;
+            $form->addElement('button', 'submit', get_lang('Add'), 'class="save"');
+        }
+
+        /*if (!empty($defaults['created_at'])) {
+            $defaults['created_at'] = api_convert_and_format_date($defaults['created_at']);
+        }
+        if (!empty($defaults['updated_at'])) {
+            $defaults['updated_at'] = api_convert_and_format_date($defaults['updated_at']);
+        }*/
+        $form->setDefaults($defaults);
+
+        // Setting the rules
+        $form->addRule('field_display_text', get_lang('ThisFieldIsRequired'), 'required');
+        //$form->addRule('field_variable', get_lang('ThisFieldIsRequired'), 'required');
+        $form->addRule('field_type', get_lang('ThisFieldIsRequired'), 'required');
+
+        return $form;
+    }
+
+    public function getJqgridActionLinks($token)
+    {
+        //With this function we can add actions to the jgrid (edit, delete, etc)
+        return 'function action_formatter(cellvalue, options, rowObject) {
+     return \'<a href="?action=edit&type='.$this->type.'&id=\'+options.rowId+\'">'.Display::return_icon(
+            'edit.png',
+            get_lang('Edit'),
+            '',
+            ICON_SIZE_SMALL
+        ).'</a>'.
+        '&nbsp;<a onclick="javascript:if(!confirm('."\'".addslashes(
+            api_htmlentities(get_lang("ConfirmYourChoice"), ENT_QUOTES)
+        )."\'".')) return false;"  href="?sec_token='.$token.'&type='.$this->type.'&action=delete&id=\'+options.rowId+\'">'.Display::return_icon(
+            'delete.png',
+            get_lang('Delete'),
+            '',
+            ICON_SIZE_SMALL
+        ).'</a>'.
+        '\';
+    }';
+    }
+
+    /**
+     * @param array $columns
+     * @param array  $column_model
+     * @param array  $extraFields
+     * @return array
+     */
+    public function getRules(&$columns, &$column_model, $extraFields = array(), $checkExtraFieldExistence = false)
+    {
+        $fields = $this->get_all(
+            array('field_visible = ? AND field_filter = ?'
+            => array(1, 1)
+            ),
+            'option_display_text'
+        );
+        $extraFieldOption = new ExtraFieldOption($this->type);
+
+        $rules = array();
+        if (!empty($fields)) {
+            foreach ($fields as $field) {
+
+                $search_options = array();
+                $type           = 'text';
+                if (in_array($field['field_type'], array(self::FIELD_TYPE_SELECT, self::FIELD_TYPE_DOUBLE_SELECT))) {
+                    $type                   = 'select';
+                    $search_options['sopt'] = array('eq', 'ne'); //equal not equal
+                } else {
+                    $search_options['sopt'] = array('cn', 'nc'); //contains not contains
+                }
+
+                $search_options['searchhidden'] = 'true';
+                $search_options['defaultValue'] = isset($search_options['field_default_value']) ? $search_options['field_default_value'] : null;
+
+                if ($field['field_type'] == self::FIELD_TYPE_DOUBLE_SELECT) {
+                    //Add 2 selects
+                    $options       = $extraFieldOption->get_field_options_by_field($field['id']);
+                    $options       = self::extra_field_double_select_convert_array_to_ordered_array($options);
+                    $first_options = array();
+
+                    if (!empty($options)) {
+                        foreach ($options as $option) {
+                            foreach ($option as $sub_option) {
+                                if ($sub_option['option_value'] == 0) {
+                                    $first_options[] = $sub_option['field_id'].'#'.$sub_option['id'].':'.$sub_option['option_display_text'];
+                                }
+                            }
+                        }
+                    }
+
+                    $search_options['value']    = implode(';', $first_options);
+                    $search_options['dataInit'] = 'fill_second_select';
+
+                    //First
+                    $column_model[] = array(
+                        'name'          => 'extra_'.$field['field_variable'],
+                        'index'         => 'extra_'.$field['field_variable'],
+                        'width'         => '100',
+                        'hidden'        => 'true',
+                        'search'        => 'true',
+                        'stype'         => 'select',
+                        'searchoptions' => $search_options
+                    );
+                    $columns[]      = $field['field_display_text'].' (1)';
+                    $rules[]        = array('field' => 'extra_'.$field['field_variable'], 'op' => 'cn');
+
+                    //Second
+                    $search_options['value']    = $field['id'].':';
+                    $search_options['dataInit'] = 'register_second_select';
+
+                    $column_model[] = array(
+                        'name'          => 'extra_'.$field['field_variable'].'_second',
+                        'index'         => 'extra_'.$field['field_variable'].'_second',
+                        'width'         => '100',
+                        'hidden'        => 'true',
+                        'search'        => 'true',
+                        'stype'         => 'select',
+                        'searchoptions' => $search_options
+                    );
+                    $columns[]      = $field['field_display_text'].' (2)';
+                    $rules[]        = array('field' => 'extra_'.$field['field_variable'].'_second', 'op' => 'cn');
+                    continue;
+                } else {
+                    $search_options['value'] = $extraFieldOption->get_field_options_to_string(
+                        $field['id'],
+                        false,
+                        'option_display_text'
+                    );
+                }
+                $column_model[] = array(
+                    'name'          => 'extra_'.$field['field_variable'],
+                    'index'         => 'extra_'.$field['field_variable'],
+                    'width'         => '100',
+                    'hidden'        => 'true',
+                    'search'        => 'true',
+                    'stype'         => $type,
+                    'searchoptions' => $search_options
+                );
+                $columns[]      = $field['field_display_text'];
+                $rules[]        = array('field' => 'extra_'.$field['field_variable'], 'op' => 'cn');
+            }
+        }
+
+        return $rules;
+    }
+
+    /**
+     * @param array $options
+     * @return array
+     */
+    public function parseConditions($options)
+    {
+        $inject_extra_fields = null;
+        $extraFieldOption    = new ExtraFieldOption($this->type);
+        $double_fields       = array();
+
+        if (isset($options['extra'])) {
+            $extra_fields = $options['extra'];
+            if (!empty($extra_fields)) {
+                $counter = 1;
+                foreach ($extra_fields as &$extra) {
+                    $extra_field_obj           = new ExtraField($this->type);
+                    $extra_field_info          = $extra_field_obj->get($extra['id']);
+                    $extra['extra_field_info'] = $extra_field_info;
+
+                    if (isset($extra_field_info['field_type']) && in_array(
+                            $extra_field_info['field_type'],
+                            array(
+                                ExtraField::FIELD_TYPE_SELECT,
+                                ExtraField::FIELD_TYPE_SELECT,
+                                ExtraField::FIELD_TYPE_DOUBLE_SELECT
+                            )
+                        )
+                    ) {
+                        $inject_extra_fields .= " fvo$counter.option_display_text as {$extra['field']}, ";
+                    } else {
+                        $inject_extra_fields .= " fv$counter.field_value as {$extra['field']}, ";
+                    }
+
+                    if (isset($extra_fields_info[$extra['id']])) {
+                        $info = $extra_fields_info[$extra['id']];
+                    } else {
+                        $info = $this->get($extra['id']);
+                        $extra_fields_info[$extra['id']] = $info;
+                    }
+                    if (isset($info['field_type']) && $info['field_type'] == ExtraField::FIELD_TYPE_DOUBLE_SELECT) {
+                        $double_fields[$info['id']] = $info;
+                    }
+                    $counter++;
+                }
+            }
+        }
+        $options_by_double = array();
+        foreach ($double_fields as $double) {
+            $my_options = $extraFieldOption->get_field_options_by_field(
+                $double['id'],
+                true
+            );
+            $options_by_double['extra_'.$double['field_variable']] = $my_options;
+        }
+
+        $field_value_to_join = array();
+
+        //filter can be all/any = and/or
+        $inject_joins = null;
+        $inject_where = null;
+        $where        = null;
+
+        if (!empty($options['where'])) {
+            if (!empty($options['extra'])) {
+                // Removing double 1=1
+                $options['where'] = str_replace(' 1 = 1  AND', '', $options['where']);
+                // Always OR
+                $counter = 1;
+                foreach ($extra_fields as $extra_info) {
+                    $extra_field_info = $extra_info['extra_field_info'];
+                    $inject_joins .= " INNER JOIN $this->table_field_values fv$counter ON (s.".$this->primaryKey." = fv$counter.".$this->handler_id.") ";
+
+                    //Add options
+                    if (isset($extra_field_info['field_type']) && in_array(
+                            $extra_field_info['field_type'],
+                            array(
+                                ExtraField::FIELD_TYPE_SELECT,
+                                ExtraField::FIELD_TYPE_SELECT,
+                                ExtraField::FIELD_TYPE_DOUBLE_SELECT
+                            )
+                        )
+                    ) {
+                        $options['where'] = str_replace(
+                            $extra_info['field'],
+                            'fv'.$counter.'.field_id = '.$extra_info['id'].' AND fvo'.$counter.'.option_value',
+                            $options['where']
+                        );
+                        $inject_joins .= " INNER JOIN $this->table_field_options fvo$counter ".
+                            " ON (fv$counter.field_id = fvo$counter.field_id AND fv$counter.field_value = fvo$counter.option_value) ";
+                    } else {
+                        //text, textarea, etc
+                        $options['where'] = str_replace(
+                            $extra_info['field'],
+                            'fv'.$counter.'.field_id = '.$extra_info['id'].' AND fv'.$counter.'.field_value',
+                            $options['where']
+                        );
+                    }
+
+                    $field_value_to_join[] = " fv$counter.$this->handler_id ";
+                    $counter++;
+                }
+                if (!empty($field_value_to_join)) {
+                    //$inject_where .= " AND s.id = ".implode(' = ', $field_value_to_join);
+                }
+            }
+            $where .= ' AND '.$options['where'];
+        }
+
+        $order = null;
+        if (!empty($options['order'])) {
+            $order = " ORDER BY ".$options['order'];
+        }
+        $limit = null;
+        if (!empty($options['limit'])) {
+            $limit = " LIMIT ".$options['limit'];
+        }
+
+        return array(
+            'order'               => $order,
+            'limit'               => $limit,
+            'where'               => $where,
+            'inject_where'        => $inject_where,
+            'inject_joins'        => $inject_joins,
+            'field_value_to_join' => $field_value_to_join,
+            'inject_extra_fields' => $inject_extra_fields,
+        );
+    }
+
+
+    //@todo move this in the display_class or somewhere else
+    /**
+     * @param $col
+     * @param $oper
+     * @param $val
+     * @return string
+     */
+    public function get_where_clause($col, $oper, $val)
+    {
+
+        if (empty($col)) {
+            return '';
+        }
+        if ($oper == 'bw' || $oper == 'bn') {
+            $val .= '%';
+        }
+        if ($oper == 'ew' || $oper == 'en') {
+            $val = '%'.$val;
+        }
+        if ($oper == 'cn' || $oper == 'nc' || $oper == 'in' || $oper == 'ni') {
+            $val = '%'.$val.'%';
+        }
+        $val = \Database::escape_string($val);
+
+        return " $col {$this->ops[$oper]} '$val' ";
+    }
+
+    public function getExtraFieldRules($filters, $stringToSearch = 'extra_')
+    {
+        $extra_fields = array();
+
+        // Getting double select if exists
+        $double_select = array();
+        foreach ($filters->rules as $rule) {
+            if (strpos($rule->field, '_second') === false) {
+
+            } else {
+                $my_field = str_replace('_second', '', $rule->field);
+                $double_select[$my_field] = $rule->data;
+            }
+        }
+
+        $condition_array = array();
+
+        foreach ($filters->rules as $rule) {
+
+            if (strpos($rule->field, $stringToSearch) === false) {
+                //normal fields
+                $field = $rule->field;
+
+                if (isset($rule->data) && $rule->data != -1) {
+                    $condition_array[] = $this->get_where_clause($field, $rule->op, $rule->data);
+                }
+            } else {
+                // Extra fields
+
+                if (strpos($rule->field, '_second') === false) {
+                    //No _second
+                    $original_field = str_replace($stringToSearch, '', $rule->field);
+                    $field_option = $this->get_handler_field_info_by_field_variable($original_field);
+
+                    if ($field_option['field_type'] == ExtraField::FIELD_TYPE_DOUBLE_SELECT) {
+
+                        if (isset($double_select[$rule->field])) {
+                            $data = explode('#', $rule->data);
+                            $rule->data = $data[1].'::'.$double_select[$rule->field];
+                        } else {
+                            // only was sent 1 select
+                            $data = explode('#', $rule->data);
+                            $rule->data = $data[1];
+                        }
+
+                        if (!isset($rule->data)) {
+                            $condition_array[] = ' ('.$this->get_where_clause($rule->field, $rule->op, $rule->data).') ';
+                            $extra_fields[] = array('field' => $rule->field, 'id' => $field_option['id']);
+                        }
+                    } else {
+                        if (isset($rule->data)) {
+                            if ($rule->data == -1) {
+                                continue;
+                            }
+                            $condition_array[] = ' ('.$this->get_where_clause($rule->field, $rule->op, $rule->data).') ';
+                            $extra_fields[] = array(
+                                'field' => $rule->field,
+                                'id' => $field_option['id'],
+                                'data' => $rule->data
+                            );
+                        }
+                    }
+                } else {
+                    $my_field = str_replace('_second', '', $rule->field);
+                    $original_field = str_replace($stringToSearch, '', $my_field);
+                    $field_option = $this->get_handler_field_info_by_field_variable($original_field);
+                    $extra_fields[] = array(
+                        'field' => $rule->field,
+                        'id' => $field_option['id']
+                    );
+                }
+            }
+        }
+
+        return array(
+            'extra_fields' => $extra_fields,
+            'condition_array' => $condition_array
+        );
+    }
+}

+ 659 - 0
main/inc/lib/extra_field_option.lib.php

@@ -0,0 +1,659 @@
+<?php
+/* For licensing terms, see /license.txt */
+/**
+ * Declaration of the ExtraFieldOption class
+ */
+/**
+ * Handles the extra fields for various objects (users, sessions, courses)
+ */
+class ExtraFieldOption extends Model
+{
+    public $columns = array(
+        'id', 'field_id', 'option_value', 'option_display_text', 'option_order', 'priority', 'priority_message', 'tms'
+    );
+
+    /**
+     * Gets the table for the type of object for which we are using an extra field
+     * @param string Type of object (course, user or session)
+     */
+    public function __construct($type)
+    {
+        $this->type = $type;
+        switch ($this->type) {
+            case 'course':
+                $this->table = Database::get_main_table(TABLE_MAIN_COURSE_FIELD_OPTIONS);
+                break;
+            case 'user':
+                $this->table = Database::get_main_table(TABLE_MAIN_USER_FIELD_OPTIONS);
+                break;
+            case 'session':
+                $this->table = Database::get_main_table(TABLE_MAIN_SESSION_FIELD_OPTIONS);
+                break;
+            case 'question':
+                $this->table = Database::get_main_table(TABLE_MAIN_QUESTION_FIELD_OPTIONS);
+                break;
+            case 'lp':
+                $this->table = Database::get_main_table(TABLE_MAIN_LP_FIELD_OPTIONS);
+                break;
+        }
+    }
+
+    /**
+     * Gets the number of options already available in the table for this item type
+     * @return int Number of options available
+     * @assert () >= 0
+     */
+    public function get_count()
+    {
+        $row = Database::select('count(*) as count', $this->table, array(), 'first');
+
+        return $row['count'];
+    }
+
+    /**
+     * Gets the number of options available for this field
+     * @param int Field ID
+     * @return int Number of options
+     * @assert ('') === false
+     * @assert (-1) == 0
+     * @assert (0) == 0
+     */
+    public function get_count_by_field_id($field_id)
+    {
+        if (empty($field_id)) {
+            return false;
+        }
+        $row = Database::select(
+            'count(*) as count',
+            $this->table,
+            array('where' => array('field_id = ?' => $field_id)),
+            'first'
+        );
+
+        return $row['count'];
+    }
+
+    /**
+     * Returns a list of options for a specific field, separated by ";"
+     * @param int Field ID
+     * @param bool Indicates whether we want the results to be given with their id
+     * @param string Order by clause (without the "order by") to be added to the SQL query
+     * @return string List of options separated by ;
+     * @assert (-1, false, null) == ''
+     */
+    public function get_field_options_to_string($field_id, $add_id_in_array = false, $ordered_by = null)
+    {
+        $options = self::get_field_options_by_field($field_id, $add_id_in_array, $ordered_by);
+        $new_options = array();
+        if (!empty($options)) {
+            foreach ($options as $option) {
+                $new_options[] = $option['option_value'].':'.$option['option_display_text'];
+            }
+            $string = implode(';', $new_options);
+
+            return $string;
+        }
+
+        return '';
+    }
+
+    /**
+     * Delete all the options of a specific field
+     * @param int Field ID
+     * @result void
+     * @assert (-1) === false
+     */
+    public function delete_all_options_by_field_id($field_id)
+    {
+        $field_id = intval($field_id);
+        $sql = "DELETE FROM  {$this->table} WHERE field_id = $field_id";
+        $r = Database::query($sql);
+
+        return $r;
+    }
+
+    /**
+     * @param array $params
+     *
+     * @return int
+     */
+    public function saveOptions($params, $show_query = false)
+    {
+        $optionInfo = self::get_field_option_by_field_and_option($params['field_id'], $params['option_value']);
+
+        // Use URLify only for new items
+        //$optionValue = URLify::filter($params['option_value']);
+        $optionValue = replace_dangerous_char($params['option_value']);
+        $option = $params['option_value'];
+
+        if ($optionInfo == false) {
+            $order = self::get_max_order($params['field_id']);
+            $new_params = array(
+                'field_id'            => $params['field_id'],
+                'option_value'        => trim($optionValue),
+                'option_display_text' => trim($option),
+                'option_order'        => $order,
+                'tms'                 => api_get_utc_datetime(),
+            );
+            return parent::save($new_params, $show_query);
+        }
+
+        return false;
+    }
+
+    /**
+     * Saves an option into the corresponding *_field_options table
+     * @param array Parameters to be considered for the insertion
+     * @param bool Whether to show the query (sent to the parent save() method)
+     * @return bool True on success, false on error
+     * @assert (array('field_id'=>0), false) === false
+     * @assert (array('field_id'=>1), false) === true
+     */
+    public function save($params, $show_query = false)
+    {
+        $field_id = intval($params['field_id']);
+
+        if (empty($field_id)) {
+            return false;
+        }
+
+        $time = api_get_utc_datetime();
+        if (!empty($params['field_options']) &&
+            in_array(
+                $params['field_type'],
+                array(
+                    ExtraField::FIELD_TYPE_RADIO,
+                    ExtraField::FIELD_TYPE_SELECT,
+                    ExtraField::FIELD_TYPE_SELECT_MULTIPLE,
+                    ExtraField::FIELD_TYPE_DOUBLE_SELECT
+                )
+            )
+        ) {
+            if ($params['field_type'] == ExtraField::FIELD_TYPE_DOUBLE_SELECT) {
+                //$params['field_options'] = France:Paris;Bretagne;Marseilles;Lyon|Belgique:Bruxelles;Namur;Liège;Bruges|Peru:Lima;Piura;
+                $options_parsed = ExtraField::extra_field_double_select_convert_string_to_array(
+                    $params['field_options']
+                );
+
+                if (!empty($options_parsed)) {
+                    foreach ($options_parsed as $key => $option) {
+                        $sub_options = $option['options'];
+
+                        $new_params = array(
+                            'field_id'            => $field_id,
+                            'option_value'        => 0,
+                            'option_display_text' => $option['label'],
+                            'option_order'        => 0,
+                            'tms'                 => $time,
+                        );
+
+                        // Looking if option already exists:
+                        $option_info = self::get_field_option_by_field_id_and_option_display_text(
+                            $field_id,
+                            $option['label']
+                        );
+
+                        if (empty($option_info)) {
+                            $sub_id = parent::save($new_params, $show_query);
+                        } else {
+                            $sub_id           = $option_info['id'];
+                            $new_params['id'] = $sub_id;
+                            parent::update($new_params, $show_query);
+                        }
+
+                        foreach ($sub_options as $sub_option) {
+                            if (!empty($sub_option)) {
+                                $new_params  = array(
+                                    'field_id'            => $field_id,
+                                    'option_value'        => $sub_id,
+                                    'option_display_text' => $sub_option,
+                                    'option_order'        => 0,
+                                    'tms'                 => $time,
+                                );
+                                $option_info = self::get_field_option_by_field_id_and_option_display_text_and_option_value(
+                                    $field_id,
+                                    $sub_option,
+                                    $sub_id
+                                );
+                                if (empty($option_info)) {
+                                    parent::save($new_params, $show_query);
+                                } else {
+                                    $new_params['id'] = $option_info['id'];
+                                    parent::update($new_params, $show_query);
+                                }
+                            }
+
+                        }
+                    }
+                }
+                $list = array();
+            } else {
+                $list = explode(';', $params['field_options']);
+            }
+
+            if (!empty($list)) {
+                foreach ($list as $option) {
+                    $option_info = self::get_field_option_by_field_and_option($field_id, $option);
+                    // Use URLify only for new items
+                    $optionValue = URLify::filter($option);
+                    $option = trim($option);
+
+                    if ($option_info == false) {
+                        $order      = self::get_max_order($field_id);
+
+                        $new_params = array(
+                            'field_id'            => $field_id,
+                            'option_value'        => trim($optionValue),
+                            'option_display_text' => trim($option),
+                            'option_order'        => $order,
+                            'tms'                 => $time,
+                        );
+                        parent::save($new_params, $show_query);
+                    }
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Save one option item at a time
+     * @param array Parameters specific to the option
+     * @param bool Whether to show the query (sent to parent save() method)
+     * @param bool Whether to insert even if the option already exists
+     * @return bool True on success, false on failure
+     * @assert (array('field_id'=>0),false) === false
+     * @assert (array('field_id'=>0),false) === true
+     */
+    public function save_one_item($params, $show_query = false, $insert_repeated = true)
+    {
+        $field_id = intval($params['field_id']);
+        if (empty($field_id)) {
+            return false;
+        }
+
+        if (isset($params['option_value'])) {
+            $params['option_value'] = trim($params['option_value']);
+        }
+
+        if (isset($params['option_display_text'])) {
+            $params['option_display_text'] = trim($params['option_display_text']);
+        }
+
+        $params['tms'] = api_get_utc_datetime();
+        if (empty($params['option_order'])) {
+            $order                  = self::get_max_order($field_id);
+            $params['option_order'] = $order;
+        }
+        if ($insert_repeated) {
+            parent::save($params, $show_query);
+        } else {
+            $check = self::get_field_option_by_field_and_option($field_id, $params['option_value']);
+            if ($check == false) {
+                parent::save($params, $show_query);
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Get the complete row of a specific option of a specific field
+     * @param int Field ID
+     * @param string Value of the option
+     * @return mixed The row on success or false on failure
+     * @assert (0,'') === false
+     */
+    public function get_field_option_by_field_and_option($field_id, $option_value)
+    {
+        $field_id     = intval($field_id);
+        $option_value = Database::escape_string($option_value);
+
+        $sql = "SELECT * FROM {$this->table}
+                WHERE field_id = $field_id AND option_value = '".$option_value."'";
+        $result = Database::query($sql);
+        if (Database::num_rows($result) > 0) {
+            return Database::store_result($result, 'ASSOC');
+        }
+
+        return false;
+    }
+
+    /**
+     * Get the complete row of a specific option's display text of a specific field
+     * @param int Field ID
+     * @param string Display value of the option
+     * @return mixed The row on success or false on failure
+     * @assert (0, '') === false
+     */
+    public function get_field_option_by_field_id_and_option_display_text($field_id, $option_display_text)
+    {
+        $field_id = intval($field_id);
+        $option_display_text = Database::escape_string($option_display_text);
+
+        $sql = "SELECT * FROM {$this->table}
+                WHERE field_id = $field_id AND option_display_text = '".$option_display_text."'";
+        $result = Database::query($sql);
+        if (Database::num_rows($result) > 0) {
+            return Database::fetch_array($result, 'ASSOC');
+        }
+
+        return false;
+    }
+
+    /**
+     * Get the complete row of a specific option's display text of a specific field
+     * @param int Field ID
+     * @param string Display value of the option
+     * @param string Value of the option
+     * @return mixed The row on success or false on failure
+     * @assert (0, '', '') === false
+     */
+    public function get_field_option_by_field_id_and_option_display_text_and_option_value(
+        $field_id,
+        $option_display_text,
+        $option_value
+    ) {
+        $field_id            = intval($field_id);
+        $option_display_text = Database::escape_string($option_display_text);
+        $option_value        = Database::escape_string($option_value);
+
+        $sql = "SELECT * FROM {$this->table}
+                WHERE
+                    field_id = $field_id AND
+                    option_display_text = '".$option_display_text."' AND
+                    option_value = '$option_value'";
+        $result = Database::query($sql);
+        if (Database::num_rows($result) > 0) {
+            return Database::fetch_array($result, 'ASSOC');
+        }
+
+        return false;
+    }
+
+    /**
+     * Gets an array of options for a specific field
+     * @param int The field ID
+     * @param bool Whether to add the row ID in the result
+     * @param string Extra ordering query bit
+     * @result mixed Row on success, false on failure
+     * @assert (0, '') === false
+     */
+    public function get_field_options_by_field($field_id, $add_id_in_array = false, $ordered_by = null)
+    {
+        $field_id = intval($field_id);
+
+        $sql = "SELECT * FROM {$this->table} WHERE field_id = $field_id ";
+
+        if (!empty($ordered_by)) {
+            $sql .= " ORDER BY $ordered_by ";
+        }
+        $result = Database::query($sql);
+        if (Database::num_rows($result) > 0) {
+            if ($add_id_in_array) {
+                $options = array();
+                while ($row = Database::fetch_array($result, 'ASSOC')) {
+                    $options[$row['id']] = $row;
+                }
+
+                return $options;
+            } else {
+                return Database::store_result($result, 'ASSOC');
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Get options for a specific field as array or in JSON format suited for the double-select format
+     * @param int Field ID
+     * @param int Option value ID
+     * @param bool Return format (whether it should be formatted to JSON or not)
+     * @return mixed Row/JSON on success
+     */
+    public function get_second_select_field_options_by_field($field_id, $option_value_id, $to_json = false)
+    {
+        $field_id        = intval($field_id);
+        $option_value_id = intval($option_value_id);
+        $options         = array();
+        $sql             = "SELECT * FROM {$this->table}
+                WHERE field_id = $field_id AND option_value = $option_value_id
+                ORDER BY option_display_text";
+        $result          = Database::query($sql);
+        if (Database::num_rows($result) > 0) {
+            $options = Database::store_result($result, 'ASSOC');
+        }
+
+        if ($to_json) {
+            $string = null;
+            if (!empty($options)) {
+                $array = array();
+                foreach ($options as $option) {
+                    $array[$option['id']] = $option['option_display_text'];
+                }
+                $string = json_encode($array);
+            }
+
+            return $string;
+        }
+
+        return $options;
+    }
+
+    /**
+     * Get options for a specific field as string split by ;
+     * @param int Field ID
+     * @param string Extra query bit for reordering
+     * @return string HTML string of options
+     * @assert (0, '') === null
+     */
+    public function get_field_options_by_field_to_string($field_id, $ordered_by = null)
+    {
+        $field      = new ExtraField($this->type);
+        $field_info = $field->get($field_id);
+        $options    = self::get_field_options_by_field($field_id, false, $ordered_by);
+        $elements   = array();
+        if (!empty($options)) {
+            switch ($field_info['field_type']) {
+                case ExtraField::FIELD_TYPE_DOUBLE_SELECT:
+                    $html = ExtraField::extra_field_double_select_convert_array_to_string($options);
+                    break;
+                default:
+                    foreach ($options as $option) {
+                        $elements[] = $option['option_value'];
+                    }
+                    $html = implode(';', $elements);
+                    break;
+            }
+
+            return $html;
+        }
+
+        return null;
+
+    }
+
+    /**
+     * Get the maximum order value for a specific field
+     * @param int Field ID
+     * @return int Current max ID + 1 (we start from 0)
+     * @assert (0, '') === 1
+     */
+    public function get_max_order($field_id)
+    {
+        $field_id = intval($field_id);
+        $sql      = "SELECT MAX(option_order) FROM {$this->table} WHERE field_id = $field_id";
+        $res      = Database::query($sql);
+        $max      = 1;
+        if (Database::num_rows($res) > 0) {
+            $row = Database::fetch_array($res);
+            $max = $row[0] + 1;
+        }
+
+        return $max;
+    }
+
+    /**
+     * Update the option using the given params
+     * @param array $params data to be saved
+     */
+    public function update($params)
+    {
+        parent::update($params);
+    }
+
+    /**
+     * Display a form with the options for the field_id given in REQUEST
+     * @return void Prints output
+     */
+    function display()
+    {
+        // action links
+        echo '<div class="actions">';
+        //echo  '<a href="../admin/index.php">'.Display::return_icon('back.png', get_lang('BackTo').' '.get_lang('PlatformAdmin'),'', ICON_SIZE_MEDIUM).'</a>';
+        $field_id = isset($_REQUEST['field_id']) ? intval($_REQUEST['field_id']) : null;
+        echo '<a href="'.api_get_self(
+        ).'?action=add&type='.$this->type.'&field_id='.$field_id.'">'.Display::return_icon(
+            'add_user_fields.png',
+            get_lang('Add'),
+            '',
+            ICON_SIZE_MEDIUM
+        ).'</a>';
+        echo '</div>';
+        echo Display::grid_html('extra_field_options');
+    }
+
+    public function getPriorityOptions()
+    {
+        return  array(
+            '' => get_lang('SelectAnOption'),
+            1 => get_lang('Success'),
+            2 => get_lang('Info'),
+            3 => get_lang('Warning'),
+            4 => get_lang('Error'),
+        );
+    }
+
+    public function getPriorityMessageType($priority)
+    {
+        switch ($priority) {
+            case 1:
+                return 'success';
+            case 2:
+                return 'info';
+            case 3:
+                return 'warning';
+            case 4:
+                return 'error';
+        }
+        return null;
+
+    }
+
+    /**
+     * Returns an HTML form for the current field
+     * @param string URL to send the form to (action=...)
+     * @param string Type of action to offer through the form (edit, usually)
+     * @return string HTML form
+     */
+    public function return_form($url, $action)
+    {
+        $form_name = $this->type.'_field';
+        $form = new FormValidator($form_name, 'post', $url);
+        // Settting the form elements
+        $header = get_lang('Add');
+        if ($action == 'edit') {
+            $header = get_lang('Modify');
+        }
+
+        $form->addElement('header', $header);
+        $id = isset($_GET['id']) ? intval($_GET['id']) : '';
+
+        $form->addElement('hidden', 'id', $id);
+        $form->addElement('hidden', 'type', $this->type);
+        $form->addElement('hidden', 'field_id', $this->field_id);
+
+        $form->addElement('text', 'option_display_text', get_lang('Name'), array('class' => 'span5'));
+        $form->addElement('text', 'option_value', get_lang('Value'), array('class' => 'span5'));
+        $form->addElement('text', 'option_order', get_lang('Order'), array('class' => 'span2'));
+        $form->addElement('select', 'priority', get_lang('Priority'), $this->getPriorityOptions());
+        $form->addElement('textarea', 'priority_message', get_lang('PriorityMessage'));
+
+        $defaults = array();
+
+        if ($action == 'edit') {
+            // Setting the defaults
+            $defaults = $this->get($id);
+            $form->freeze('option_value');
+            $form->addElement('button', 'submit', get_lang('Modify'), 'class="save"');
+        } else {
+            $form->addElement('button', 'submit', get_lang('Add'), 'class="save"');
+        }
+
+        $form->setDefaults($defaults);
+
+        // Setting the rules
+        $form->addRule('option_display_text', get_lang('ThisFieldIsRequired'), 'required');
+        //$form->addRule('field_variable', get_lang('ThisFieldIsRequired'), 'required');
+        $form->addRule('option_value', get_lang('ThisFieldIsRequired'), 'required');
+
+        return $form;
+    }
+
+    /**
+     * @param string $tag
+     * @param int $field_id
+     * @param int $limit
+     * @return array
+     */
+    public function searchByField($tag, $field_id, $limit = 10)
+    {
+        $field_id = intval($field_id);
+        $limit = intval($limit);
+        $tag = Database::escape_string($tag);
+        $sql = "SELECT DISTINCT id, option_display_text
+                FROM {$this->table}
+                WHERE
+                    field_id = '".$field_id."' AND
+                    option_value LIKE '%$tag%'
+                ORDER BY option_value
+                LIMIT 0, $limit
+                ";
+        $result = Database::query($sql);
+        $values = array();
+        if (Database::num_rows($result)) {
+            $values = Database::store_result($result, 'ASSOC');
+        }
+        return $values;
+    }
+
+
+    /**
+     * @param string $tag
+     * @param int $field_id
+     * @param int $limit
+     *
+     * @return string
+     */
+    public function getSearchOptionsByField($tag, $field_id, $limit = 10)
+    {
+        $result = $this->searchByField($tag, $field_id, $limit = 10);
+        $values = array();
+        $json = null;
+        if (!empty($result)) {
+            foreach ($result as $item) {
+                $values[] = array(
+                    'value' => $item['id'],
+                    'caption' => $item['option_display_text'],
+                );
+            }
+            $json = json_encode($values);
+        }
+
+        return $json;
+    }
+
+
+}

+ 654 - 0
main/inc/lib/extra_field_value.lib.php

@@ -0,0 +1,654 @@
+<?php
+/* For licensing terms, see /license.txt */
+
+/**
+ * Declaration for the ExtraFieldValue class, managing the values in extra
+ * fields for any datatype
+ * @package chamilo.library
+ */
+/**
+ * Class managing the values in extra fields for any datatype
+ * @package chamilo.library.extrafields
+ */
+
+class ExtraFieldValue extends Model
+{
+    public $type = null;
+    public $columns = array('id', 'field_id', 'field_value', 'tms', 'comment');
+    /** @var string session_id, course_code, user_id, question id */
+    public $handler_id = null;
+    public $entityName;
+
+    /**
+     * Formats the necessary elements for the given datatype
+     * @param string The type of data to which this extra field applies (user, course, session, ...)
+     * @return void (or false if unmanaged datatype)
+     * @assert (-1) === false
+     */
+    public function __construct($type)
+    {
+        $this->type = $type;
+        $extra_field = new ExtraField($this->type);
+        $this->handler_id = $extra_field->handler_id;
+
+        switch ($this->type) {
+            case 'course':
+                $this->table = Database::get_main_table(TABLE_MAIN_COURSE_FIELD_VALUES);
+                $this->table_handler_field = Database::get_main_table(TABLE_MAIN_COURSE_FIELD);
+                $this->author_id = 'user_id';
+                $this->entityName = 'ChamiloLMS\Entity\CourseFieldValues';
+                break;
+            case 'user':
+                $this->table = Database::get_main_table(TABLE_MAIN_USER_FIELD_VALUES);
+                $this->table_handler_field = Database::get_main_table(TABLE_MAIN_USER_FIELD);
+                $this->author_id = 'author_id';
+                $this->entityName = 'ChamiloLMS\Entity\UserFieldValues';
+                break;
+            case 'session':
+                $this->table = Database::get_main_table(TABLE_MAIN_SESSION_FIELD_VALUES);
+                $this->table_handler_field = Database::get_main_table(TABLE_MAIN_SESSION_FIELD);
+                $this->author_id = 'user_id';
+                $this->entityName = 'ChamiloLMS\Entity\SessionFieldValues';
+                break;
+            case 'question':
+                $this->table = Database::get_main_table(TABLE_MAIN_QUESTION_FIELD_VALUES);
+                $this->table_handler_field = Database::get_main_table(TABLE_MAIN_QUESTION_FIELD);
+                $this->author_id = 'user_id';
+                $this->entityName = 'ChamiloLMS\Entity\QuestionFieldValues';
+                break;
+            case 'lp':
+                $this->table = Database::get_main_table(TABLE_MAIN_LP_FIELD_VALUES);
+                $this->table_handler_field = Database::get_main_table(TABLE_MAIN_LP_FIELD);
+                $this->author_id = 'lp_id';
+                //$this->entityName = 'ChamiloLMS\Entity\QuestionFieldValues';
+                break;
+            default:
+                //unmanaged datatype, return false to let the caller know it
+                // didn't work
+                return false;
+        }
+        $this->columns[] = $this->handler_id;
+        $this->columns[] = $this->author_id;
+    }
+
+    /**
+     * Gets the number of values stored in the table (all fields together)
+     * for this type of resource
+     * @return integer Number of rows in the table
+     * @assert () !== false
+     */
+    public function get_count()
+    {
+        $row = Database::select('count(*) as count', $this->table, array(), 'first');
+        return $row['count'];
+    }
+
+    /**
+     * Saves a series of records given as parameter into the coresponding table
+     * @param array  Structured parameter for the insertion into the *_field_values table
+     * @return mixed false on empty params, void otherwise
+     * @assert (array()) === false
+     */
+    public function save_field_values($params)
+    {
+        $extra_field = new ExtraField($this->type);
+
+        if (empty($params[$this->handler_id])) {
+            return false;
+        }
+
+        foreach ($params as $key => $value) {
+            $found = strpos($key, '__persist__');
+            if ($found) {
+                $tempKey = str_replace('__persist__', '', $key);
+                if (!isset($params[$tempKey])) {
+                    $params[$tempKey] = array();
+                }
+                break;
+            }
+        }
+
+        // Parse params.
+        foreach ($params as $key => $value) {
+            if (substr($key, 0, 6) == 'extra_') {
+                // An extra field.
+                $field_variable = substr($key, 6);
+                $extra_field_info = $extra_field->get_handler_field_info_by_field_variable($field_variable);
+
+                if ($extra_field_info) {
+                    $commentVariable = 'extra_'.$field_variable.'_comment';
+                    $comment = isset($params[$commentVariable]) ? $params[$commentVariable] : null;
+
+                    switch ($extra_field_info['field_type']) {
+                        case ExtraField::FIELD_TYPE_TAG :
+
+                            $old = self::getAllValuesByItemAndField(
+                                $extra_field_info['id'],
+                                $params[$this->handler_id]
+                            );
+
+                            $deleteItems = array();
+                            if (!empty($old)) {
+                                $oldIds = array();
+                                foreach ($old as $oldItem) {
+                                    $oldIds[] = $oldItem['field_value'];
+                                }
+                                $deleteItems = array_diff($oldIds, $value);
+                            }
+
+                            foreach ($value as $optionId) {
+                                $new_params = array(
+                                    $this->handler_id   => $params[$this->handler_id],
+                                    'field_id'          => $extra_field_info['id'],
+                                    'field_value'       => $optionId,
+                                    'comment'           => $comment
+                                );
+                                self::save($new_params);
+                            }
+
+                            if (!empty($deleteItems)) {
+                                foreach ($deleteItems as $deleteFieldValue) {
+                                    self::deleteValuesByHandlerAndFieldAndValue(
+                                        $extra_field_info['id'],
+                                        $params[$this->handler_id],
+                                        $deleteFieldValue
+                                    );
+                                }
+                            }
+                            break;
+                        default;
+                            $new_params = array(
+                                $this->handler_id   => $params[$this->handler_id],
+                                'field_id'          => $extra_field_info['id'],
+                                'field_value'       => $value,
+                                'comment'           => $comment
+                            );
+                            self::save($new_params);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Save values in the *_field_values table
+     * @param array Structured array with the values to save
+     * @param boolean Whether to show the insert query (passed to the parent save() method)
+     * @result mixed The result sent from the parent method
+     * @assert (array()) === false
+     */
+    public function save($params, $show_query = false)
+    {
+        $extra_field = new ExtraField($this->type);
+
+        // Setting value to insert.
+        $value = $params['field_value'];
+        $value_to_insert = null;
+
+        if (is_array($value)) {
+            $value_to_insert = implode(';', $value);
+        } else {
+            $value_to_insert = Database::escape_string($value);
+        }
+
+        $params['field_value'] = $value_to_insert;
+
+        //If field id exists
+        $extra_field_info = $extra_field->get($params['field_id']);
+
+        if ($extra_field_info) {
+            switch ($extra_field_info['field_type']) {
+                case ExtraField::FIELD_TYPE_RADIO:
+                case ExtraField::FIELD_TYPE_SELECT:
+                case ExtraField::FIELD_TYPE_SELECT_MULTIPLE:
+                    //$field_options = $session_field_option->get_field_options_by_field($params['field_id']);
+					//$params['field_value'] = split(';', $value_to_insert);
+               /*
+                   if ($field_options) {
+                       $check = false;
+                       foreach ($field_options as $option) {
+                           if (in_array($option['option_value'], $values)) {
+                               $check = true;
+                               break;
+                           }
+                      }
+                      if (!$check) {
+                          return false; //option value not found
+                      }
+                  } else {
+                      return false; //enumerated type but no option found
+                  }*/
+                    break;
+                case ExtraField::FIELD_TYPE_TEXT:
+                case ExtraField::FIELD_TYPE_TEXTAREA:
+                    break;
+                case ExtraField::FIELD_TYPE_DOUBLE_SELECT:
+                    if (is_array($value)) {
+                        if (isset($value['extra_'.$extra_field_info['field_variable']]) &&
+                            isset($value['extra_'.$extra_field_info['field_variable'].'_second'])
+                             ) {
+                            $value_to_insert = $value['extra_'.$extra_field_info['field_variable']].'::'.$value['extra_'.$extra_field_info['field_variable'].'_second'];
+                        } else {
+                            $value_to_insert = null;
+                        }
+                    }
+                    break;
+                default:
+                    break;
+            }
+
+            if ($extra_field_info['field_type'] == ExtraField::FIELD_TYPE_TAG) {
+                $field_values = self::getAllValuesByItemAndFieldAndValue(
+                    $params[$this->handler_id],
+                    $params['field_id'],
+                    $value
+                );
+            } else {
+                $field_values = self::get_values_by_handler_and_field_id(
+                    $params[$this->handler_id],
+                    $params['field_id']
+                );
+            }
+
+            $params['field_value'] = $value_to_insert;
+            $params['tms'] = api_get_utc_datetime();
+            $params[$this->author_id] = api_get_user_id();
+
+            // Insert
+            if (empty($field_values)) {
+                if ($extra_field_info['field_loggeable'] == 1) {
+                    global $app;
+                    switch($this->type) {
+                        case 'question':
+                            $extraFieldValue = new ChamiloLMS\Entity\QuestionFieldValues();
+                            $extraFieldValue->setUserId(api_get_user_id());
+                            $extraFieldValue->setQuestionId($params[$this->handler_id]);
+                            break;
+                        case 'course':
+                            $extraFieldValue = new ChamiloLMS\Entity\CourseFieldValues();
+                            $extraFieldValue->setUserId(api_get_user_id());
+                            $extraFieldValue->setQuestionId($params[$this->handler_id]);
+                            break;
+                        case 'user':
+                            $extraFieldValue = new ChamiloLMS\Entity\UserFieldValues();
+                            $extraFieldValue->setUserId($params[$this->handler_id]);
+                            $extraFieldValue->setAuthorId(api_get_user_id());
+                            break;
+                        case 'session':
+                            $extraFieldValue = new ChamiloLMS\Entity\SessionFieldValues();
+                            $extraFieldValue->setUserId(api_get_user_id());
+                            $extraFieldValue->setSessionId($params[$this->handler_id]);
+                            break;
+                    }
+                    if (isset($extraFieldValue)) {
+                        if (!empty($params['field_value'])) {
+                            $extraFieldValue->setComment($params['comment']);
+                            $extraFieldValue->setFieldValue($params['field_value']);
+                            $extraFieldValue->setFieldId($params['field_id']);
+                            $extraFieldValue->setTms(api_get_utc_datetime(null, false, true));
+                            $app['orm.ems']['db_write']->persist($extraFieldValue);
+                            $app['orm.ems']['db_write']->flush();
+                        }
+                    }
+                } else {
+                    if ($extra_field_info['field_type'] == ExtraField::FIELD_TYPE_TAG) {
+
+                        $option = new ExtraFieldOption($this->type);
+                        $optionExists = $option->get($params['field_value']);
+                        if (empty($optionExists)) {
+                            $optionParams = array(
+                                'field_id' => $params['field_id'],
+                                'option_value' => $params['field_value']
+                            );
+                            $optionId = $option->saveOptions($optionParams);
+                        } else {
+                            $optionId = $optionExists['id'];
+                        }
+
+                        $params['field_value'] = $optionId;
+                        if ($optionId) {
+                            return parent::save($params, $show_query);
+                        }
+                    } else {
+                        return parent::save($params, $show_query);
+                    }
+                }
+            } else {
+                // Update
+                if ($extra_field_info['field_loggeable'] == 1) {
+                    global $app;
+                    switch($this->type) {
+                        case 'question':
+                            $extraFieldValue = $app['orm.ems']['db_write']->getRepository('ChamiloLMS\Entity\QuestionFieldValues')->find($field_values['id']);
+                            $extraFieldValue->setUserId(api_get_user_id());
+                            $extraFieldValue->setQuestionId($params[$this->handler_id]);
+                            break;
+                        case 'course':
+                            $extraFieldValue = $app['orm.ems']['db_write']->getRepository('ChamiloLMS\Entity\CourseFieldValues')->find($field_values['id']);
+                            $extraFieldValue->setUserId(api_get_user_id());
+                            $extraFieldValue->setCourseCode($params[$this->handler_id]);
+                            break;
+                        case 'user':
+                            $extraFieldValue = $app['orm.ems']['db_write']->getRepository('ChamiloLMS\Entity\UserFieldValues')->find($field_values['id']);
+                            $extraFieldValue->setUserId(api_get_user_id());
+                            $extraFieldValue->setAuthorId(api_get_user_id());
+                            break;
+                        case 'session':
+                            $extraFieldValue = $app['orm.ems']['db_write']->getRepository('ChamiloLMS\Entity\SessionFieldValues')->find($field_values['id']);
+                            $extraFieldValue->setUserId(api_get_user_id());
+                            $extraFieldValue->setSessionId($params[$this->handler_id]);
+                            break;
+                    }
+
+                    if (isset($extraFieldValue)) {
+                        if (!empty($params['field_value'])) {
+
+                            /*
+                             *  If the field value is similar to the previous value then the comment will be the same
+                                in order to no save in the log an empty record
+                            */
+                            if ($extraFieldValue->getFieldValue() == $params['field_value']) {
+                                if (empty($params['comment'])) {
+                                    $params['comment'] = $extraFieldValue->getComment();
+                                }
+                            }
+
+                            $extraFieldValue->setComment($params['comment']);
+                            $extraFieldValue->setFieldValue($params['field_value']);
+                            $extraFieldValue->setFieldId($params['field_id']);
+                            $extraFieldValue->setTms(api_get_utc_datetime(null, false, true));
+                            $app['orm.ems']['db_write']->persist($extraFieldValue);
+                            $app['orm.ems']['db_write']->flush();
+                        }
+                    }
+                } else {
+                    $params['id'] = $field_values['id'];
+                    return parent::update($params, $show_query);
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns the value of the given extra field on the given resource
+     * @param int Item ID (It could be a session_id, course_id or user_id)
+     * @param int Field ID (the ID from the *_field table)
+     * @param bool Whether to transform the result to a human readable strings
+     * @return mixed A structured array with the field_id and field_value, or false on error
+     * @assert (-1,-1) === false
+     */
+    public function get_values_by_handler_and_field_id($item_id, $field_id, $transform = false)
+    {
+        $field_id = intval($field_id);
+        $item_id = Database::escape_string($item_id);
+
+        $sql = "SELECT s.*, field_type FROM {$this->table} s
+                INNER JOIN {$this->table_handler_field} sf ON (s.field_id = sf.id)
+                WHERE {$this->handler_id} = '$item_id'  AND
+                      field_id = '".$field_id."'
+                ORDER BY id";
+        $result = Database::query($sql);
+        if (Database::num_rows($result)) {
+            $result = Database::fetch_array($result, 'ASSOC');
+            if ($transform) {
+                if (!empty($result['field_value'])) {
+                    switch ($result['field_type']) {
+                        case ExtraField::FIELD_TYPE_DOUBLE_SELECT:
+                            $field_option = new ExtraFieldOption($this->type);
+                            $options = explode('::', $result['field_value']);
+                            // only available for PHP 5.4  :( $result['field_value'] = $field_option->get($options[0])['id'].' -> ';
+                            $result = $field_option->get($options[0]);
+                            $result_second = $field_option->get($options[1]);
+                            if (!empty($result)) {
+                                $result['field_value'] = $result['option_display_text'].' -> ';
+                                $result['field_value'] .= $result_second['option_display_text'];
+                            }
+                            break;
+                        case ExtraField::FIELD_TYPE_SELECT:
+                            $field_option = new ExtraFieldOption($this->type);
+                            $extra_field_option_result = $field_option->get_field_option_by_field_and_option(
+                                $result['field_id'],
+                                $result['field_value']
+                            );
+                            if (isset($extra_field_option_result[0])) {
+                                $result['field_value'] = $extra_field_option_result[0]['option_display_text'];
+                            }
+                            break;
+                    }
+                }
+            }
+            return $result;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * @param string $tag
+     * @param int $field_id
+     * @param int $limit
+     * @return array
+     */
+    public function searchValuesByField($tag, $field_id, $limit = 10)
+    {
+        $field_id = intval($field_id);
+        $limit = intval($limit);
+        $tag = Database::escape_string($tag);
+        $sql = "SELECT DISTINCT s.field_value, s.field_id
+                FROM {$this->table} s
+                INNER JOIN {$this->table_handler_field} sf ON (s.field_id = sf.id)
+                WHERE
+                    field_id = '".$field_id."' AND
+                    field_value LIKE '%$tag%'
+                ORDER BY field_value
+                LIMIT 0, $limit
+                ";
+        $result = Database::query($sql);
+        $values = array();
+        if (Database::num_rows($result)) {
+            $values = Database::store_result($result, 'ASSOC');
+        }
+        return $values;
+    }
+
+    /**
+     * Gets a structured array of the original item and its extra values, using
+     * a specific original item and a field name (like "branch", or "birthdate")
+     * @param int Item ID from the original table
+     * @param string The name of the field we are looking for
+     * @return mixed Array of results, or false on error or not found
+     * @assert (-1,'') === false
+     */
+    public function get_values_by_handler_and_field_variable($item_id, $field_variable, $transform = false)
+    {
+        $item_id = Database::escape_string($item_id);
+        $field_variable = Database::escape_string($field_variable);
+
+        $sql = "SELECT s.*, field_type FROM {$this->table} s
+                INNER JOIN {$this->table_handler_field} sf
+                ON (s.field_id = sf.id)
+                WHERE
+                    {$this->handler_id} = '$item_id'  AND
+                    field_variable = '".$field_variable."'
+                ORDER BY id";
+        $result = Database::query($sql);
+        if (Database::num_rows($result)) {
+            $result = Database::fetch_array($result, 'ASSOC');
+            if ($transform) {
+                if ($result['field_type'] == ExtraField::FIELD_TYPE_DOUBLE_SELECT) {
+                    if (!empty($result['field_value'])) {
+                        $field_option = new ExtraFieldOption($this->type);
+                        $options = explode('::', $result['field_value']);
+                        // only available for PHP 5.4  :( $result['field_value'] = $field_option->get($options[0])['id'].' -> ';
+                        $result = $field_option->get($options[0]);
+                        $result_second = $field_option->get($options[1]);
+                        if (!empty($result)) {
+                            $result['field_value'] = $result['option_display_text'].' -> ';
+                            $result['field_value'] .= $result_second['option_display_text'];
+                        }
+                    }
+                }
+            }
+            return $result;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Gets the ID from the item (course, session, etc) for which
+     * the given field is defined with the given value
+     * @param string Field (type of data) we want to check
+     * @param string Data we are looking for in the given field
+     * @return mixed Give the ID if found, or false on failure or not found
+     * @assert (-1,-1) === false
+     */
+    public function get_item_id_from_field_variable_and_field_value($field_variable, $field_value, $transform = false)
+    {
+        $field_value = Database::escape_string($field_value);
+        $field_variable = Database::escape_string($field_variable);
+
+        $sql = "SELECT {$this->handler_id} FROM {$this->table} s
+                INNER JOIN {$this->table_handler_field} sf
+                ON (s.field_id = sf.id)
+                WHERE
+                    field_value  = '$field_value' AND
+                    field_variable = '".$field_variable."'
+                ";
+
+        $result = Database::query($sql);
+        if ($result !== false && Database::num_rows($result)) {
+            $result = Database::fetch_array($result, 'ASSOC');
+            return $result;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Get all values for a specific field id
+     * @param int Field ID
+     * @return mixed Array of values on success, false on failure or not found
+     * @assert (-1) === false
+     */
+    public function get_values_by_field_id($field_id)
+    {
+        $field_id = intval($field_id);
+        $sql = "SELECT s.*, field_type FROM {$this->table} s
+                INNER JOIN {$this->table_handler_field} sf
+                ON (s.field_id = sf.id)
+                WHERE field_id = '".$field_id."' ORDER BY id";
+        $result = Database::query($sql);
+        if (Database::num_rows($result)) {
+            return Database::store_result($result, 'ASSOC');
+        }
+        return false;
+    }
+
+    /**
+     * @param int $itemId
+     * @param int $fieldId
+     * @return array
+     */
+    public function getAllValuesByItemAndField($itemId, $fieldId)
+    {
+        $fieldId = intval($fieldId);
+        $itemId = intval($itemId);
+        $sql = "SELECT s.* FROM {$this->table} s
+                INNER JOIN {$this->table_handler_field} sf
+                ON (s.field_id = sf.id)
+                WHERE
+                    field_id = '".$fieldId."' AND
+                    {$this->handler_id} = '$itemId'
+                ORDER BY field_value";
+        $result = Database::query($sql);
+        if (Database::num_rows($result)) {
+            return Database::store_result($result, 'ASSOC');
+        }
+        return false;
+    }
+
+    /**
+     * @param int $itemId
+     * @param int $fieldId
+     * @param string $fieldValue
+     * @return array|bool
+     */
+    public function getAllValuesByItemAndFieldAndValue($itemId, $fieldId, $fieldValue)
+    {
+        $fieldId = intval($fieldId);
+        $itemId = intval($itemId);
+        $fieldValue = Database::escape_string($fieldValue);
+        $sql = "SELECT s.* FROM {$this->table} s
+                INNER JOIN {$this->table_handler_field} sf
+                ON (s.field_id = sf.id)
+                WHERE
+                    field_id = '".$fieldId."' AND
+                    {$this->handler_id} = '$itemId' AND
+                    field_value = $fieldValue
+                ORDER BY field_value";
+
+        $result = Database::query($sql);
+        if (Database::num_rows($result)) {
+            return Database::store_result($result, 'ASSOC');
+        }
+        return false;
+    }
+
+    /**
+     * Deletes all the values related to a specific field ID
+     * @param int Field ID
+     * @return void
+     * @assert ('a') == null
+     */
+    public function delete_all_values_by_field_id($field_id)
+    {
+        $field_id = intval($field_id);
+        $sql = "DELETE FROM  {$this->table} WHERE field_id = $field_id";
+        Database::query($sql);
+    }
+
+    /**
+     * Deletes values of a specific field for a specific item
+     * @param int Item ID (session id, course id, etc)
+     * @param int Field ID
+     * @return void
+     * @assert (-1,-1) == null
+     */
+    public function delete_values_by_handler_and_field_id($item_id, $field_id)
+    {
+        $field_id = intval($field_id);
+        $item_id = Database::escape_string($item_id);
+        $sql = "DELETE FROM {$this->table} WHERE {$this->handler_id} = '$item_id' AND field_id = '".$field_id."' ";
+        Database::query($sql);
+    }
+
+    /**
+     * @param int $itemId
+     * @param int $fieldId
+     * @param int $fieldValue
+     */
+    public function deleteValuesByHandlerAndFieldAndValue($itemId, $fieldId, $fieldValue)
+    {
+        $itemId = intval($itemId);
+        $fieldId = intval($fieldId);
+        $fieldValue = Database::escape_string($fieldValue);
+
+        $sql = "DELETE FROM {$this->table}
+                WHERE
+                    {$this->handler_id} = '$itemId' AND
+                    field_id = '".$fieldId."' AND
+                    field_value = '$fieldValue'";
+        Database::query($sql);
+    }
+
+    /**
+     * Not yet implemented - Compares the field values of two items
+     * @param int Item 1
+     * @param int Item 2
+     * @return mixed Differential array generated from the comparison
+     */
+    public function compare_item_values($item_id, $item_to_compare)
+    {
+    }
+}

+ 1 - 0
main/inc/lib/fckeditor/editor/plugins/ImageManager/config.inc.php

@@ -29,6 +29,7 @@
 $language_file = array('document');
 
 require_once '../../../../../../inc/global.inc.php';
+api_block_anonymous_users();
 
 // Disabling access for anonymous users.
 api_block_anonymous_users();

+ 22 - 14
main/inc/lib/fileUpload.lib.php

@@ -249,12 +249,14 @@ function handle_uploaded_document(
 			$where_to_save = $base_work_dir.$upload_path;
 
 			// At least if the directory doesn't exist, tell so
-			if (!is_dir($where_to_save)) {
-			    if ($output){
-				    Display::display_error_message(get_lang('DestDirectoryDoesntExist').' ('.$upload_path.')');
-				}
-				return false;
-			}
+            if (!is_dir($where_to_save)) {
+                if (!mkdir($where_to_save, api_get_permissions_for_new_directories())) {
+                    if ($output) {
+                        Display::display_error_message(get_lang('DestDirectoryDoesntExist').' ('.$upload_path.')');
+                    }
+                    return false;
+                }
+            }
 			// Full path of the destination
 			$store_path = $where_to_save.$clean_name;
 
@@ -441,14 +443,20 @@ function moveUploadedFile($file, $storePath)
  *
  * @see    - enough_size() uses  dir_total_space() function
  */
-function enough_size($file_size, $dir, $max_dir_space) {
-	if ($max_dir_space) {
-		$already_filled_space = dir_total_space($dir);
-		if (($file_size + $already_filled_space) > $max_dir_space) {
-			return false;
-		}
-	}
-	return true;
+function enough_size($file_size, $dir, $max_dir_space)
+{
+    // If the directory is the archive directory, safely ignore the size limit
+    if (api_get_path(SYS_ARCHIVE_PATH) == $dir) { 
+        return true; 
+    }
+
+    if ($max_dir_space) {
+        $already_filled_space = dir_total_space($dir);
+        if (($file_size + $already_filled_space) > $max_dir_space) {
+            return false;
+        }
+    }
+    return true;
 }
 
 

+ 20 - 4
main/inc/lib/formvalidator/Element/select_ajax.php

@@ -53,14 +53,30 @@ class HTML_QuickForm_Select_Ajax extends HTML_QuickForm_select
             $dataCondition = '$("#'.$this->getAttribute('name').'").select2("data", '.$result.')';
             $tags = ', tags : function() { return '.$result.'} ';
         }
+        $width = 'element';
+        $givenWidth = $this->getAttribute('width');
+        if (!empty($givenWidth)) {
+            $width = $givenWidth;
+        }
+
+        //Get the minimumInputLength for select2
+        $minimumInputLength = $this->getAttribute('minimumInputLength') > 3 ?
+            $this->getAttribute('minimumInputLength') :
+            3
+        ;
+
+        $plHolder = $this->getAttribute('placeholder');
+        if (empty($plHolder)) {
+            $plHolder = get_lang('SelectAnOption');
+        }
 
         $html .= '<script>
                 $(function() {
                     $("#'.$this->getAttribute('name').'").select2({
-                        placeholder: "'.get_lang('SelectAnOption').'",
+                        placeholder: "' . $plHolder . '",
                         allowClear: true,
-                        width: "element",
-                        minimumInputLength: 2,
+                        width: "'.$width.'",
+                        minimumInputLength: ' . $minimumInputLength . ',
                         // instead of writing the function to execute the request we use Select2s convenient helper
                         ajax: {
                             url: "'.$this->getAttribute('url').'",
@@ -89,4 +105,4 @@ class HTML_QuickForm_Select_Ajax extends HTML_QuickForm_select
         $html .= '<input id="'.$this->getAttribute('name').'" name="'.$this->getAttribute('name').'" />';
         return $html;
     }
-}
+}

+ 10 - 5
main/inc/lib/main_api.lib.php

@@ -3223,25 +3223,30 @@ function api_get_datetime($time = null) {
  * @param int       The session ID (optional)
  * @return int      -1 on error, 0 if invisible, 1 if visible
  */
-function api_get_item_visibility($_course, $tool, $id, $session=0)
+function api_get_item_visibility($_course, $tool, $id, $session = 0)
 {
     if (!is_array($_course) || count($_course) == 0 || empty($tool) || empty($id)) {
         return -1;
     }
     $tool = Database::escape_string($tool);
-    $id = Database::escape_string($id);
+    $id = intval($id);
     $session = (int) $session;
     $TABLE_ITEMPROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
-    $course_id	 = $_course['real_id'];
+    $course_id	 = intval($_course['real_id']);
     $sql = "SELECT visibility FROM $TABLE_ITEMPROPERTY
     		WHERE 	c_id = $course_id AND
     				tool = '$tool' AND
     				ref = $id AND
     				(id_session = $session OR id_session = 0)
-    		ORDER BY id_session DESC, lastedit_date DESC LIMIT 1";
+    		ORDER BY id_session DESC, lastedit_date DESC
+            LIMIT 1";
+
     $res = Database::query($sql);
-    if ($res === false || Database::num_rows($res) == 0) { return -1; }
+    if ($res === false || Database::num_rows($res) == 0) {
+        return -1;
+    }
     $row = Database::fetch_array($res);
+
     return $row['visibility'];
 }
 

+ 8 - 8
main/inc/lib/pear/HTML/QuickForm.php

@@ -341,14 +341,14 @@ class HTML_QuickForm extends HTML_Common
             }
         }
 
-        $course_id = api_get_course_int_id();
-        //If I'm in a course replace the default max filesize with the course limits
-        if (!empty($course_id)) {
-            $free_course_quota = DocumentManager::get_course_quota() - DocumentManager::documents_total_space();
-            if (empty($this->_maxFileSize) || $free_course_quota <= $this->_maxFileSize) {
-                $this->_maxFileSize = intval($free_course_quota);
-            }
-        }
+//        $course_id = api_get_course_int_id();
+//        //If I'm in a course replace the default max filesize with the course limits
+//        if (!empty($course_id)) {
+//            $free_course_quota = DocumentManager::get_course_quota() - DocumentManager::documents_total_space();
+//            if (empty($this->_maxFileSize) || $free_course_quota <= $this->_maxFileSize) {
+//                $this->_maxFileSize = intval($free_course_quota);
+//            }
+//        }
     } // end constructor
 
     // }}}

Dosya farkı çok büyük olduğundan ihmal edildi
+ 362 - 375
main/inc/lib/sessionmanager.lib.php


+ 3 - 16
main/inc/lib/social.lib.php

@@ -957,26 +957,13 @@ class SocialManager extends UserManager
     public static function display_productions($user_id)
     {
         $sysdir_array = UserManager::get_user_picture_path_by_id($user_id, 'system', true);
-        $sysdir = $sysdir_array['dir'].$user_id.'/';
+        $sysdir = $sysdir_array['dir'];
         $webdir_array = UserManager::get_user_picture_path_by_id($user_id, 'web', true);
-        $webdir = $webdir_array['dir'].$user_id.'/';
+        $webdir = $webdir_array['dir'];
         if (!is_dir($sysdir)) {
             mkdir($sysdir, api_get_permissions_for_new_directories(), true);
         }
-        /*
-          $handle = opendir($sysdir);
-          $productions = array();
-          while ($file = readdir($handle)) {
-          if ($file == '.' || $file == '..' || $file == '.htaccess') {
-          continue;                        // Skip current and parent directories
-          }
-          if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
-          // User's photos should not be listed as productions.
-          continue;
-          }
-          $productions[] = $file;
-          }
-         */
+
         $productions = UserManager::get_user_productions($user_id);
 
         if (count($productions) > 0) {

+ 4 - 2
main/inc/lib/sortable_table.class.php

@@ -326,7 +326,7 @@ class SortableTable extends HTML_Table
                 $html .= '<td style="text-align:right;">';
                 $html .= $nav;
                 $html .= '</td>';
-            }else{
+            } else {
                 $html .= '<td> ';
                 $html .= '</td>';
             }
@@ -919,6 +919,7 @@ class SortableTableFromArray extends SortableTable
      */
     public function get_table_data($from = 1, $per_page = null, $column = null, $direction = null, $sort = true)
     {
+
         if ($sort) {
             $content = TableSort::sort_table($this->table_data, $this->column, $this->direction == 'ASC' ? SORT_ASC : SORT_DESC);
         } else {
@@ -981,7 +982,7 @@ class SortableTableFromArrayConfig extends SortableTable
      * @param string $direction
      * @param bool $doc_filter special modification to fix the document name order
      */
-    public function __construct (
+    public function __construct(
         $table_data,
         $default_column = 1,
         $default_items_per_page = 20,
@@ -1018,6 +1019,7 @@ class SortableTableFromArrayConfig extends SortableTable
             SORT_REGULAR,
             $this->doc_filter
         );
+
         return array_slice($content, $from, $this->per_page);
     }
 

+ 628 - 74
main/inc/lib/tracking.lib.php

@@ -556,14 +556,30 @@ class Tracking
     /**
      * Gets the score average from all tests in a course by student
      *
-     * @param    mixed		Student(s) id
-     * @param    string 	Course code
-     * @param    int    	Exercise id (optional), filtered by exercise
-     * @param    int    	Session id (optional), if param $session_id is null it'll return results including sessions, 0 = session is not filtered
-     * @return   string 	value (number %) Which represents a round integer about the score average.
+     * @param $student_id
+     * @param $course_code
+     * @param int $exercise_id
+     * @param null $session_id
+     * @param int $active_filter    2 for consider all tests
+     *                              1 for active <> -1
+     *                              0 for active <> 0
+     * @param int $into_lp  1 for all exercises
+     *                      0 for without LP
+     * @internal param \Student $mixed id
+     * @internal param \Course $string code
+     * @internal param \Exercise $int id (optional), filtered by exercise
+     * @internal param \Session $int id (optional), if param $session_id is null it'll return results including sessions, 0 = session is not filtered
+     * @return   string    value (number %) Which represents a round integer about the score average.
      */
-    public static function get_avg_student_exercise_score($student_id, $course_code, $exercise_id = 0, $session_id = null)
-    {
+    public static function get_avg_student_exercise_score(
+        $student_id,
+        $course_code,
+        $exercise_id = 0,
+        $session_id = null,
+        $active_filter = 1,
+        $into_lp = 0
+    ) {
+        $course_code = Database::escape_string($course_code);
     	$course_info = api_get_course_info($course_code);
     	if (!empty($course_info)) {
     		// table definition
@@ -583,20 +599,37 @@ class Tracking
     			$session_id = intval($session_id);
     			$condition_session = " AND session_id = $session_id ";
     		}
+            if ($active_filter == 1) {
+                $condition_active = 'AND active <> -1';
+            } elseif ($active_filter == 0) {
+                $condition_active = 'AND active <> 0';
+            } else {
+                $condition_active = '';
+            }
+            $condition_into_lp = '';
+            $select_lp_id = '';
+            if ($into_lp == 0) {
+                $condition_into_lp = 'AND orig_lp_id = 0 AND orig_lp_item_id = 0';
+            } else {
+                $select_lp_id = ', orig_lp_id as lp_id ';
+            }
+
     		$sql = "SELECT count(id) FROM $tbl_course_quiz
-    				WHERE c_id = {$course_info['real_id']} AND active <> -1 $condition_quiz ";
+    				WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz ";
     		$count_quiz = Database::fetch_row(Database::query($sql));
 
     		if (!empty($count_quiz[0]) && !empty($student_id)) {
     			if (is_array($student_id)) {
-    				$condition_user = " AND exe_user_id IN (".implode(',',$student_id).") ";
+                    $student_id = array_map('intval', $student_id);
+    				$condition_user = " AND exe_user_id IN (".implode(',', $student_id).") ";
     			} else {
+                    $student_id = intval($student_id);
     				$condition_user = " AND exe_user_id = '$student_id' ";
     			}
 
     			if (empty($exercise_id)) {
     				$sql = "SELECT id FROM $tbl_course_quiz
-    						WHERE c_id = {$course_info['real_id']} AND active <> -1 $condition_quiz";
+    						WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz";
                     $result = Database::query($sql);
                     $exercise_list = array();
     				$exercise_id = null;
@@ -612,19 +645,23 @@ class Tracking
 
     			$count_quiz = Database::fetch_row(Database::query($sql));
 
-    			$sql = "SELECT SUM(exe_result/exe_weighting*100) as avg_score, COUNT(*) as num_attempts
+    			$sql = "SELECT
+    			        SUM(exe_result/exe_weighting*100) as avg_score,
+    			        COUNT(*) as num_attempts
+    			        $select_lp_id
                         FROM $tbl_stats_exercise
-                        WHERE exe_exo_id IN ('".$exercise_id."')
-    			        $condition_user AND
-                        orig_lp_id = 0 AND
-                        status = '' AND
-                        exe_cours_id = '$course_code' AND
-                        orig_lp_item_id = 0 $condition_session
+                        WHERE
+                            exe_exo_id IN ('".$exercise_id."')
+                            $condition_user AND
+                            status = '' AND
+                            exe_cours_id = '$course_code'
+                            $condition_session
+                            $condition_into_lp
                         ORDER BY exe_date DESC";
 
     			$res = Database::query($sql);
     			$row = Database::fetch_array($res);
-    			$quiz_avg_score = 0;
+    			$quiz_avg_score = null;
 
     			if (!empty($row['avg_score'])) {
     				$quiz_avg_score = round($row['avg_score'],2);
@@ -636,7 +673,28 @@ class Tracking
     			if (is_array($student_id)) {
     				$quiz_avg_score = round($quiz_avg_score / count($student_id), 2);
     			}
-    			return $quiz_avg_score;
+                if ($into_lp == 0) {
+                    return $quiz_avg_score;
+                } else {
+                    if (!empty($row['lp_id'])) {
+                        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
+                        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
+                        $sql = "SELECT lp.name
+                                FROM $tbl_lp as lp, $tbl_course as c
+                                WHERE
+                                    c.code = '$course_code' AND
+                                    lp.id = ".$row['lp_id']." AND
+                                    lp.c_id = c.id
+                                LIMIT 1;
+                        ";
+                        $result = Database::query($sql);
+                        $row_lp = Database::fetch_row($result);
+                        $lp_name = $row_lp[0];
+                        return array($quiz_avg_score, $lp_name);
+                    } else {
+                        return array($quiz_avg_score, null);
+                    }
+                }
     		}
     	}
     	return null;
@@ -644,13 +702,21 @@ class Tracking
 
     /**
      * Get count student's exercise COMPLETED attempts
-     * @param int tudent id
-     * @param string Course code
-     * @param int Exercise id
-     * @param int Learning path id (optional), for showing attempts inside a learning path $lp_id and $lp_item_id params are required.
-     * @param int Learning path item id (optional), for showing attempts inside a learning path $lp_id and $lp_item_id params are required.
-     *
-     * @return int     count of attempts
+     * @param $student_id
+     * @param $course_code
+     * @param $exercise_id
+     * @param int $lp_id
+     * @param int $lp_item_id
+     * @param int $session_id
+     * @param int $find_all_lp  0 = just LP specified
+     *                          1 = LP specified or whitout LP,
+     *                          2 = all rows
+     * @internal param \Student $int id
+     * @internal param \Course $string code
+     * @internal param \Exercise $int id
+     * @internal param \Learning $int path id (optional), for showing attempts inside a learning path $lp_id and $lp_item_id params are required.
+     * @internal param \Learning $int path item id (optional), for showing attempts inside a learning path $lp_id and $lp_item_id params are required.
+     * @return  int     count of attempts
      */
     public static function count_student_exercise_attempts(
         $student_id,
@@ -658,27 +724,33 @@ class Tracking
         $exercise_id,
         $lp_id = 0,
         $lp_item_id = 0,
-        $session_id = 0
+        $session_id = 0,
+        $find_all_lp = 0
     ) {
     	$course_code = Database::escape_string($course_code);
     	$student_id  = intval($student_id);
     	$exercise_id = intval($exercise_id);
     	$session_id  = intval($session_id);
 
-    	if (!empty($lp_id)) $lp_id = intval($lp_id);
-    	if (!empty($lp_item_id)) $lp_id = intval($lp_item_id);
-
+    	$lp_id = intval($lp_id);
+        $lp_item_id = intval($lp_item_id);
     	$tbl_stats_exercices = Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);
 
     	$sql = "SELECT COUNT(ex.exe_id) as essais FROM $tbl_stats_exercices AS ex
                 WHERE  ex.exe_cours_id = '$course_code'
                 AND ex.exe_exo_id = $exercise_id
                 AND status = ''
-                AND orig_lp_id = $lp_id
-                AND orig_lp_item_id = $lp_item_id
                 AND exe_user_id= $student_id
                 AND session_id = $session_id ";
 
+        if ($find_all_lp == 1) {
+            $sql .= "AND (orig_lp_id = $lp_id OR orig_lp_id = 0)
+                AND (orig_lp_item_id = $lp_item_id OR orig_lp_item_id = 0)";
+        } elseif ($find_all_lp == 0) {
+            $sql .= "AND orig_lp_id = $lp_id
+                AND orig_lp_item_id = $lp_item_id";
+        }
+
     	$rs = Database::query($sql);
     	$row = Database::fetch_row($rs);
     	$count_attempts = $row[0];
@@ -749,6 +821,167 @@ class Tracking
         return $result.'%';
     }
 
+    /**
+     * get teacher progress by course and session
+     * @param int course id
+     * @param int session id
+     * @return data array
+     */
+    static function get_teachers_progress_by_course($courseId, $sessionId)
+    {
+        $course = api_get_course_info_by_id($courseId);
+        $sessionId = intval($sessionId);
+        $courseId = intval($courseId);
+
+        //get teachers
+        $sql = "SELECT scu.id_session, scu.id_user, s.name
+                FROM session_rel_course_rel_user scu, session s
+                WHERE
+                    scu.id_session = s.id
+                    AND scu.status = 2
+                    AND scu.visibility = 1
+                    AND scu.course_code = '%s'
+                    AND scu.id_session = %s";
+        $query = sprintf($sql,$course['code'], $sessionId);
+        $rs = Database::query($query);
+        $teachers = array();
+        while ($teacher = Database::fetch_array($rs,'ASSOC')) {
+            $teachers[] = $teacher;
+        }
+        foreach ($teachers as $teacher) {
+            //total documents added
+            $sql = "SELECT count(*) as total
+                    FROM c_item_property
+                    WHERE lastedit_type = 'DocumentAdded'
+                    AND c_id = %s
+                    AND insert_user_id = %s
+                    AND id_session = %s";
+            $query = sprintf($sql,
+                $courseId,
+                $teacher['id_user'],
+                $teacher['id_session']
+            );
+
+            $rs = Database::query($query);
+            $totalDocuments = 0;
+            if ($rs) {
+                $row = Database::fetch_row($rs);
+                $totalDocuments = $row[0];
+            }
+            //total links added
+            $sql = "SELECT count(*) as total
+                    FROM c_item_property
+                    WHERE lastedit_type = 'LinkAdded'
+                    AND c_id = %s
+                    AND insert_user_id = %s
+                    AND id_session = %s";
+            $query = sprintf($sql,
+                $courseId,
+                $teacher['id_user'],
+                $teacher['id_session']
+            );
+            $rs = Database::query($query);
+
+            $totalLinks = 0;
+            if ($rs) {
+                $row = Database::fetch_row($rs);
+                $totalLinks = $row[0];
+            }
+            //total forums added
+            $sql = "SELECT count(*) as total
+                    FROM c_item_property
+                    WHERE lastedit_type = 'ForumthreadVisible'
+                    AND c_id = %s
+                    AND insert_user_id = %s
+                    AND id_session = %s";
+            $query = sprintf($sql,
+                $courseId,
+                $teacher['id_user'],
+                $teacher['id_session']
+            );
+            $rs = Database::query($query);
+
+            $totalForums = 0;
+            if ($rs) {
+                $row = Database::fetch_row($rs);
+                $totalForums = $row[0];
+            }
+            //total wikis added
+            $sql = "SELECT COUNT(DISTINCT(ref)) as total
+                    FROM c_item_property
+                    WHERE lastedit_type = 'WikiAdded'
+                    AND c_id = %s
+                    AND insert_user_id = %s
+                    AND id_session = %s";
+            $query = sprintf($sql,
+                $courseId,
+                $teacher['id_user'],
+                $teacher['id_session']
+            );
+            $rs = Database::query($query);
+
+            $totalWikis = 0;
+            if ($rs) {
+                $row = Database::fetch_row($rs);
+                $totalWikis = $row[0];
+            }
+            //total works added
+            $sql = "SELECT COUNT(*) as total
+                    FROM c_item_property
+                    WHERE lastedit_type = 'DirectoryCreated'
+                    AND tool = 'work'
+                    AND c_id = %s
+                    AND insert_user_id = %s
+                    AND id_session = %s";
+            $query = sprintf($sql,
+                $courseId,
+                $teacher['id_user'],
+                $teacher['id_session']
+            );
+            $rs = Database::query($query);
+
+            $totalWorks = 0;
+            if ($rs) {
+                $row = Database::fetch_row($rs);
+                $totalWorks = $row[0];
+            }
+            //total announcements added
+            $sql = "SELECT COUNT(*) as total
+                    FROM c_item_property
+                    WHERE lastedit_type = 'AnnouncementAdded'
+                    AND c_id = %s
+                    AND insert_user_id = %s
+                    AND id_session = %s";
+            $query = sprintf($sql,
+                $courseId,
+                $teacher['id_user'],
+                $teacher['id_session']
+            );
+            $rs = Database::query($query);
+
+            $totalAnnouncements = 0;
+            if ($rs) {
+                $row = Database::fetch_row($rs);
+                $totalAnnouncements = $row[0];
+            }
+            $tutor = get_user_info_by_id($teacher['id_user']);
+            $data[] = array(
+                'course'        => $course['title'],
+                'session'       => $teacher['name'],
+                'tutor'         => $tutor['username'] . ' - ' . $tutor['lastname'] . ' ' . $tutor['firstname'],
+                'documents'     => $totalDocuments,
+                'links'         => $totalLinks,
+                'forums'        => $totalForums,
+                'works'         => $totalWorks,
+                'wikis'         => $totalWikis,
+                'announcements' => $totalAnnouncements,
+            );
+        }
+
+        return $data;
+    }
+
+
     /**
      * Returns the average student progress in the learning paths of the given
      * course.
@@ -762,6 +995,8 @@ class Tracking
     public static function get_avg_student_progress($student_id, $course_code = null, $lp_ids = array(), $session_id = null, $return_array = false)
     {
         $conditions = array();
+        $session_id = intval($session_id);
+
     	// Get the information of the course.
     	$course_info = api_get_course_info($course_code);
     	if (!empty($course_info)) {
@@ -800,10 +1035,10 @@ class Tracking
                         AVG(progress) average,
                         SUM(progress) sum_progress,
                         count(progress) count_progress
-                      FROM $tbl_course_lp_view lp_view
-                      WHERE
+                    FROM $tbl_course_lp_view lp_view
+                    WHERE
                       $conditionToString
-                      GROUP BY lp_id";
+                    GROUP BY lp_id";
             $result = Database::query($sql);
             $row = Database::fetch_array($result, 'ASSOC');
             if (!$return_array) {
@@ -2368,46 +2603,132 @@ class Tracking
         return $data;
     }
     /**
-     * Get total clicks by session
-     * @param    int        Session id (optional), if param $session_id is null(default) it'll return results including sessions, 0 = session is not filtered
-     * @return    array     data
-     * @todo    implement total click by $course_id
+     * Get total clicks
+     * THIS FUNCTION IS NOT BEEN USED, IT WAS MEANT TO BE USE WITH track_e_course_access.date_from and track_e_course_access.date_to,
+     * BUT NO ROW MATCH THE CONDITION, IT SHOULD BE FINE TO USE IT WHEN YOU USE USER DEFINED DATES AND NO CHAMILO DATES
+     * @param   int     User Id
+     * @param   int     Course Id
+     * @param   int     Session Id (optional), if param $session_id is null(default) it'll return results including sessions, 0 = session is not filtered
+     * @param   string  Date from
+     * @param   string  Date to
+     * @return  array   Data
+     * @author  César Perales cesar.perales@beeznest.com 2014-01-16
      */
-    public static function get_total_clicks_by_session($session_id = null)
+    public static function get_total_clicks($userId, $courseId, $sessionId = 0, $date_from = '', $date_to = '')
     {
+        $course = api_get_course_info_by_id($courseId);
         $tables = array(
-            TABLE_STATISTIC_TRACK_E_LASTACCESS => array('access_session_id','access_user_id'),
-            TABLE_STATISTIC_TRACK_E_ACCESS => array('access_session_id', 'access_user_id'),
-            #TABLE_STATISTIC_TRACK_E_LOGIN,
-            TABLE_STATISTIC_TRACK_E_DOWNLOADS => array('down_session_id', 'down_user_id'),
-            TABLE_STATISTIC_TRACK_E_LINKS => array('links_session_id', 'links_user_id'),
-            TABLE_STATISTIC_TRACK_E_ONLINE => array('session_id', 'login_user_id'),
+            TABLE_STATISTIC_TRACK_E_LASTACCESS => array(
+                'course'    => 'access_cours_code',
+                'session'   => 'access_session_id',
+                'user'      => 'access_user_id',
+                'start_date'=> 'access_date',
+                ),
+            TABLE_STATISTIC_TRACK_E_ACCESS => array(
+                'course'    => 'access_cours_code',
+                'session'   => 'access_session_id',
+                'user'      => 'access_user_id',
+                'start_date'=> 'access_date',
+                ),
+            #TABLE_STATISTIC_TRACK_E_LOGIN, array(,, 'login_date', 'logout_date');
+            TABLE_STATISTIC_TRACK_E_DOWNLOADS => array(
+                'course'    => 'down_cours_id',
+                'session'   => 'down_session_id',
+                'user'      => 'down_user_id',
+                'start_date'=> 'down_date',
+                ),
+            TABLE_STATISTIC_TRACK_E_LINKS => array(
+                'course'    => 'links_cours_id',
+                'session'   => 'links_session_id',
+                'user'      => 'links_user_id',
+                'start_date'=> 'links_date',
+                ),
+            TABLE_STATISTIC_TRACK_E_ONLINE => array(
+                'course'    => 'course',
+                'session'   => 'session_id',
+                'user'      => 'login_user_id',
+                'start_date'=> 'login_date',
+                ),
             #TABLE_STATISTIC_TRACK_E_HOTPOTATOES,
-            TABLE_STATISTIC_TRACK_E_COURSE_ACCESS => array('session_id', 'user_id'),
-            TABLE_STATISTIC_TRACK_E_EXERCICES => array('session_id', 'exe_user_id'),
-            TABLE_STATISTIC_TRACK_E_ATTEMPT => array('session_id', 'user_id'),
+            /*TABLE_STATISTIC_TRACK_E_COURSE_ACCESS => array(
+                'course'    => 'course_code',
+                'session'   => 'session_id',
+                'user'      => 'user_id',
+                'start_date'=> 'login_course_date',
+                'end_date'  => 'logout_course_date',
+                ),*/
+            TABLE_STATISTIC_TRACK_E_EXERCICES => array(
+                'course'    => 'exe_cours_id',
+                'session'   => 'session_id',
+                'user'      => 'exe_user_id',
+                'start_date'=> 'exe_date',
+                ),
+            TABLE_STATISTIC_TRACK_E_ATTEMPT => array(
+                'course'    => 'course_code',
+                'session'   => 'session_id',
+                'user'      => 'user_id',
+                'start_date'=> 'tms',
+                ),
             #TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING,
             #TABLE_STATISTIC_TRACK_E_DEFAULT,
-            TABLE_STATISTIC_TRACK_E_UPLOADS => array('upload_session_id', 'upload_user_id'),
+            TABLE_STATISTIC_TRACK_E_UPLOADS => array(
+                'course'    => 'upload_cours_id',
+                'session'   => 'upload_session_id',
+                'user'      => 'upload_user_id',
+                'start_date'=> 'upload_date',
+                ),
             #TABLE_STATISTIC_TRACK_E_HOTSPOT,
             #TABLE_STATISTIC_TRACK_E_ITEM_PROPERTY,
             #TABLE_STATISTIC_TRACK_E_OPEN,
-        );
-
-        if (isset($_GET['session_id']) && !empty($_GET['session_id'])) {
-            $sessionId = intval($_GET['session_id']);
-        }
+            );
 
+        $error_sql = '';
         foreach ($tables as $tableName => $fields) {
-            $sql = sprintf('SELECT %s as user, count(*) as total FROM %s WHERE %s = %s GROUP BY %s', $fields[1], $tableName, $fields[0], $sessionId, $fields[1]);
+            //If session is defined, add it to query
+            $where = '';
+            if (isset($sessionId) && !empty($sessionId)) {
+                $sessionField = $fields['session'];
+                $where .= " AND $sessionField = $sessionId";
+            }
+
+            //filter by date
+            if (!empty($date_from) && !empty($date_to)) {
+                $fieldStartDate = $fields['start_date'];
+                if (!isset($fields['end_date'])) {
+                    $where .= sprintf(" AND ($fieldStartDate BETWEEN '%s' AND '%s' )", $date_from, $date_to) ;
+                } else {
+                    $fieldEndDate = $fields['end_date'];
+                    $where .= sprintf(" AND fieldStartDate >= '%s'
+                        AND $fieldEndDate <= '%s'", $date_from, $date_to);
+                }
+            }
+
+
+            //query
+            $sql = "SELECT %s as user, count(*) as total
+                FROM %s
+                WHERE %s = '%s'
+                AND %s = %s
+                $where
+                GROUP BY %s";
+            $sql = sprintf($sql,
+                $fields['user'],    //user field
+                $tableName,         //FROM
+                $fields['course'],  //course condition
+                $course['code'],    //course condition
+                $fields['user'],    //user condition
+                $userId,            //user condition
+                $fields['user']     //GROUP BY
+                );
             $rs = Database::query($sql);
+
+            //iterate query
             if (Database::num_rows($rs) > 0) {
                 while ($row = Database::fetch_array($rs)) {
                     $data[$row['user']] = (isset($data[$row['user']])) ?  $data[$row['user']] + $row[total]: $row['total'];
                 }
             }
         }
-
         return $data;
     }
 
@@ -3496,27 +3817,260 @@ class Tracking
     }
 
     /**
-     * @param FormValidator $form
-     * @return mixed
-     */
+    * @param FormValidator $form
+    * @return mixed
+    */
     public static function setUserSearchForm($form)
     {
-        global $_configuration;
-        $form->addElement('text', 'keyword', get_lang('Keyword'));
-        $form->addElement('select', 'active', get_lang('Status'), array(1 => get_lang('Active'), 0 => get_lang('Inactive')));
-        if (isset($_configuration['save_user_last_login']) &&
-            $_configuration['save_user_last_login']
-        ) {
-            $form->addElement(
-                'select',
-                'sleeping_days',
-                get_lang('InactiveDays'),
-                array('', 1 => 1, 5 => 5, 15 => 15, 30 => 30, 60 => 60, 90 => 90, 120 => 120)
-            );
+    global $_configuration;
+    $form->addElement('text', 'keyword', get_lang('Keyword'));
+    $form->addElement('select', 'active', get_lang('Status'), array(1 => get_lang('Active'), 0 => get_lang('Inactive')));
+    if (isset($_configuration['save_user_last_login']) &&
+        $_configuration['save_user_last_login']
+    ) {
+        $form->addElement(
+            'select',
+            'sleeping_days',
+            get_lang('InactiveDays'),
+            array('', 1 => 1, 5 => 5, 15 => 15, 30 => 30, 60 => 60, 90 => 90, 120 => 120)
+        );
+    }
+    $form->addElement('button', 'submit', get_lang('Search'));
+
+    return $form;
+    }
+
+    /**
+     * Get the progress of a exercise
+     * @param   int $sessionId  The session ID (session.id)
+     * @param   int $courseId   The course ID (course.id)
+     * @param   int $exerciseId The quiz ID (c_quiz.id)
+     * @param   int $answer     The answer status (0 = incorrect, 1 = correct, 2 = both)
+     * @param   array   $options    An array of options you can pass to the query (limit, where and order)
+     * @return array An array with the data of exercise(s) progress
+     */
+    public static function get_exercise_progress(
+        $sessionId = 0,
+        $courseId = 0,
+        $exerciseId = 0,
+        $date_from = null,
+        $date_to = null,
+        $options = array()
+    ) {
+        $sessionId  = intval($sessionId);
+        $courseId   = intval($courseId);
+        $exerciseId = intval($exerciseId);
+        $date_from  = Database::escape_string($date_from);
+        $date_to    = Database::escape_string($date_to);
+        /*
+         * This method gets the data by blocks, as previous attempts at one single
+         * query made it take ages. The logic of query division is described below
+         */
+        // Get tables names
+        $tuser = Database::get_main_table(TABLE_MAIN_USER);
+        $tquiz = Database::get_course_table(TABLE_QUIZ_TEST);
+        $tquiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER);
+        $tquiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION);
+        $tquiz_rel_question = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
+        $ttrack_exercises  = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_EXERCICES);
+        $ttrack_attempt    = Database::get_statistic_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
+
+        require_once api_get_path(SYS_CODE_PATH).'exercice/exercise.lib.php';
+
+        $sessions = array();
+        $courses = array();
+        // if session ID is defined but course ID is empty, get all the courses
+        // from that session
+        if (!empty($sessionId) && empty($courseId)) {
+            // $courses is an array of course int id as index and course details hash as value
+            $courses = SessionManager::get_course_list_by_session_id($sessionId);
+            $sessions[$sessionId] = api_get_session_info($sessionId);
+        } elseif (empty($sessionId) && !empty($courseId)) {
+            // if, to the contrary, course is defined but not sessions, get the sessions that include this course
+            // $sessions is an array like: [0] => ('id' => 3, 'name' => 'Session 35'), [1] => () etc;
+            $course = api_get_course_info_by_id($courseId);
+            $sessionsTemp = SessionManager::get_session_by_course($course['code']);
+            $courses[$courseId] = $course;
+            foreach ($sessionsTemp as $sessionItem) {
+                $sessions[$sessionItem['id']] = $sessionItem;
+            }
+        } elseif (!empty($courseId) && !empty($sessionId)) {
+            //none is empty
+            $course = api_get_course_info_by_id($courseId);
+            $courses[$courseId] = array($course['code']);
+            $courses[$courseId]['code'] = $course['code'];
+            $sessions[$sessionId] = api_get_session_info($sessionId);
+        } else {
+            //both are empty, not enough data, return an empty array
+            return array();
+        }
+        // Now we have two arrays of courses and sessions with enough data to proceed
+        // If no course could be found, we shouldn't return anything. Sessions can be empty (then we only return the pure-course-context results)
+        if (count($courses) < 1) {
+            return array();
         }
-        $form->addElement('button', 'submit', get_lang('Search'));
 
-        return $form;
+        $data = array();
+        // The following loop is less expensive than what it seems:
+        // - if a course was defined, then we only loop through sessions
+        // - if a session was defined, then we only loop through courses
+        // - if a session and a course were defined, then we only loop once
+        foreach ($courses as $courseIdx => $courseData) {
+            $where = '';
+            $whereParams = array();
+            $whereCourseCode = $courseData['code'];
+            $whereSessionParams = '';
+            if (count($sessions > 0)) {
+                foreach ($sessions as $sessionIdx => $sessionData) {
+                    if (!empty($sessionIdx)) {
+                        $whereSessionParams .= $sessionIdx.',';
+                    }
+                }
+                $whereSessionParams = substr($whereSessionParams,0,-1);
+            }
+
+            if (!empty($exerciseId)) {
+                $exerciseId = intval($exerciseId);
+                $where .= ' AND q.id = %d ';
+                $whereParams[] = $exerciseId;
+            }
+
+            /*
+             * This feature has been disabled for now, to avoid having to
+             * join two very large tables
+            //2 = show all questions (wrong and correct answered)
+            if ($answer != 2) {
+                $answer = intval($answer);
+                //$where .= ' AND qa.correct = %d';
+                //$whereParams[] = $answer;
+            }
+            */
+
+            $limit = '';
+            if (!empty($options['limit'])) {
+                $limit = " LIMIT ".$options['limit'];
+            }
+
+            if (!empty($options['where'])) {
+                $where .= ' AND '.Database::escape_string($options['where']);
+            }
+
+            $order = '';
+            if (!empty($options['order'])) {
+                $order = " ORDER BY ".$options['order'];
+            }
+
+            if (!empty($date_to) && !empty($date_from)) {
+                $where .= sprintf(" AND (te.start_date BETWEEN '%s 00:00:00' AND '%s 23:59:59')", $date_from, $date_to);
+            }
+
+            $sql = "SELECT
+                te.session_id,
+                ta.id as attempt_id,
+                te.exe_user_id as user_id,
+                te.exe_id as exercise_attempt_id,
+                ta.question_id,
+                ta.answer as answer_id,
+                ta.tms as time,
+                te.exe_exo_id as quiz_id,
+                CONCAT ('c', q.c_id, '_e', q.id) as exercise_id,
+                q.title as quiz_title,
+                qq.description as description
+                FROM $ttrack_exercises te
+                INNER JOIN $ttrack_attempt ta ON ta.exe_id = te.exe_id
+                INNER JOIN $tquiz q ON q.id = te.exe_exo_id
+                INNER JOIN $tquiz_rel_question rq ON rq.exercice_id = q.id AND rq.c_id = q.c_id
+                INNER JOIN $tquiz_question qq
+                ON
+                    qq.id = rq.question_id AND
+                    qq.c_id = rq.c_id AND
+                    qq.position = rq.question_order AND
+                    ta.question_id = rq.question_id
+                WHERE
+                    te.exe_cours_id = '$whereCourseCode' ".(empty($whereSessionParams)?'':"AND te.session_id IN ($whereSessionParams)")."
+                    AND q.c_id = $courseIdx
+                    $where $order $limit";
+            $sql_query = vsprintf($sql, $whereParams);
+
+            // Now browse through the results and get the data
+            $rs = Database::query($sql_query);
+            $userIds = array();
+            $questionIds = array();
+            $answerIds = array();
+            while ($row = Database::fetch_array($rs)) {
+                //only show if exercise is visible
+                if (api_get_item_visibility($courseData, 'quiz', $row['exercise_id'])) {
+                    $userIds[$row['user_id']] = $row['user_id'];
+                    $questionIds[$row['question_id']] = $row['question_id'];
+                    $answerIds[$row['question_id']][$row['answer_id']] = $row['answer_id'];
+                    $row['session'] = $sessions[$row['session_id']];
+                    $data[] = $row;
+                }
+            }
+            // Now fill questions data. Query all questions and answers for this test to avoid
+            $sqlQuestions = "SELECT tq.c_id, tq.id as question_id, tq.question, tqa.id_auto,
+                            tqa.answer, tqa.correct, tq.position, tqa.id_auto as answer_id
+                            FROM $tquiz_question tq, $tquiz_answer tqa
+                            WHERE
+                                tqa.question_id = tq.id AND
+                                tqa.c_id = tq.c_id AND
+                                tq.c_id = $courseIdx AND
+                                tq.id IN (".implode(',', $questionIds).")";
+
+            $resQuestions = Database::query($sqlQuestions);
+            $answer = array();
+            $question = array();
+            while ($rowQuestion = Database::fetch_assoc($resQuestions)) {
+                $questionId = $rowQuestion['question_id'];
+                $answerId = $rowQuestion['answer_id'];
+                $answer[$questionId][$answerId] = array(
+                    'position' => $rowQuestion['position'],
+                    'question' => $rowQuestion['question'],
+                    'answer' => $rowQuestion['answer'],
+                    'correct' => $rowQuestion['correct']
+                );
+                $question[$questionId]['question'] = $rowQuestion['question'];
+            }
+
+            // Now fill users data
+            $sqlUsers = "SELECT user_id, username, lastname, firstname
+                         FROM $tuser
+                         WHERE user_id IN (".implode(',',$userIds).")";
+            $resUsers = Database::query($sqlUsers);
+            while ($rowUser = Database::fetch_assoc($resUsers)) {
+                $users[$rowUser['user_id']] = $rowUser;
+            }
+
+            foreach ($data as $id => $row) {
+                $rowQuestId = $row['question_id'];
+                $rowAnsId = $row['answer_id'];
+
+                $data[$id]['session'] = $sessions[$row['session_id']]['name'];
+                $data[$id]['firstname'] = $users[$row['user_id']]['firstname'];
+                $data[$id]['lastname'] = $users[$row['user_id']]['lastname'];
+                $data[$id]['username'] = $users[$row['user_id']]['username'];
+                $data[$id]['answer'] = $answer[$rowQuestId][$rowAnsId]['answer'];
+                $data[$id]['correct'] = ($answer[$rowQuestId][$rowAnsId]['correct'] == 0 ? get_lang('No') : get_lang('Yes'));
+                $data[$id]['question'] = $question[$rowQuestId]['question'];
+                $data[$id]['question_id'] = $rowQuestId;
+                $data[$id]['description'] = $row['description'];
+            }
+            /*
+            The minimum expected array structure at the end is:
+            attempt_id,
+            session name,
+            exercise_id,
+            quiz_title,
+            username,
+            lastname,
+            firstname,
+            time,
+            question_id,
+            question,
+            answer,
+            */
+        }
+        return $data;
     }
 }
 

+ 37 - 19
main/inc/lib/usermanager.lib.php

@@ -932,11 +932,14 @@ class UserManager
     }
 
     /**
-     * @param array $ids
-     * @param null $active
-     * @return array
-     */
-    public static function get_user_list_by_ids($ids = array(), $active = null)
+    * Get the users by ID
+    * @param array $ids student ids
+    * @param string $active
+    * @param string $order
+    * @param string $limit
+    * @return array $result student information
+    */
+    public static function get_user_list_by_ids($ids = array(), $active = null, $order = null, $limit = null)
     {
         if (empty($ids)) {
             return array();
@@ -952,6 +955,16 @@ class UserManager
             $sql .= ' AND active='.($active ? '1' : '0');
         }
 
+        if (!is_null($order)) {
+            $order = Database::escape_string($order);
+            $sql .= ' ORDER BY ' . $order;
+        }
+
+        if (!is_null($limit)) {
+            $limit = Database::escape_string($limit);
+            $sql .= ' LIMIT ' . $limit;
+        }
+
         $rs = Database::query($sql);
         $result = array();
         while ($row = Database::fetch_array($rs)) {
@@ -1120,7 +1133,7 @@ class UserManager
      * the same directory.
      * @param    integer    User ID
      * @param    string    Type of path to return (can be 'none', 'system', 'rel', 'web')
-     * @param    bool    Whether we want to have the directory name returned 'as if' there was a file or not (in the case we want to know which directory to create - otherwise no file means no split subdir)
+     * @param    bool    deprecated see #7110
      * @param    bool    If we want that the function returns the /main/img/unknown.jpg image set it at true
      * @return    array     Array of 2 elements: 'dir' and 'file' which contain the dir and file as the name implies if image does not exist it will return the unknow image if anonymous parameter is true if not it returns an empty er's
      */
@@ -1159,11 +1172,12 @@ class UserManager
         $picture_filename = trim($user['picture_uri']);
 
         if (api_get_setting('split_users_upload_directory') === 'true') {
-            if (!empty($picture_filename) or $preview) {
+            /*if (!empty($picture_filename) or $preview) {
                 $dir = $base.'upload/users/'.substr((string) $user_id, 0, 1).'/'.$user_id.'/';
             } else {
                 $dir = $base.'upload/users/'.$user_id.'/';
-            }
+            }*/
+            $dir = $base.'upload/users/'.substr((string) $user_id, 0, 1).'/'.$user_id.'/';
         } else {
             $dir = $base.'upload/users/'.$user_id.'/';
         }
@@ -1352,8 +1366,8 @@ class UserManager
             return false;
         }
 
-        $production_path = self::get_user_picture_path_by_id($user_id, 'web', true);
-        $production_dir = $production_path['dir'].$user_id.'/';
+        $production_path = self::get_user_picture_path_by_id($user_id, 'web');
+        $production_dir = $production_path['dir'];
         $del_image = api_get_path(WEB_CODE_PATH).'img/delete.gif';
         $del_text = get_lang('Delete');
         $production_list = '';
@@ -1379,17 +1393,22 @@ class UserManager
      */
     public static function get_user_productions($user_id)
     {
-        $production_path = self::get_user_picture_path_by_id($user_id, 'system', true);
-        $production_repository = $production_path['dir'].$user_id.'/';
+        $production_path = self::get_user_picture_path_by_id($user_id, 'system');
+        $production_repository = $production_path['dir'];
         $productions = array();
 
         if (is_dir($production_repository)) {
             $handle = opendir($production_repository);
-
             while ($file = readdir($handle)) {
-                if ($file == '.' || $file == '..' || $file == '.htaccess' || is_dir($production_repository.$file)) {
-                    continue; // skip current/parent directory and .htaccess
+                if ($file == '.' ||
+                    $file == '..' ||
+                    $file == '.htaccess' ||
+                    is_dir($production_repository.$file)
+                ) {
+                    // skip current/parent directory and .htaccess
+                    continue;
                 }
+
                 if (preg_match('/('.$user_id.'|[0-9a-f]{13}|saved)_.+\.(png|jpg|jpeg|gif)$/i', $file)) {
                     // User's photos should not be listed as productions.
                     continue;
@@ -1398,7 +1417,7 @@ class UserManager
             }
         }
 
-        return $productions; // can be an empty array
+        return $productions;
     }
 
     /**
@@ -1409,8 +1428,8 @@ class UserManager
      */
     public static function remove_user_production($user_id, $production)
     {
-        $production_path = self::get_user_picture_path_by_id($user_id, 'system', true);
-        $production_file = $production_path['dir'].$user_id.'/'.$production;
+        $production_path = self::get_user_picture_path_by_id($user_id, 'system');
+        $production_file = $production_path['dir'].$production;
         if (is_file($production_file)) {
             unlink($production_file);
             return true;
@@ -1725,7 +1744,6 @@ class UserManager
         $extra_data = self::get_extra_user_data_by_field($user_id, $extra_field);
         $extra_files = $extra_data[$extra_field];
         if (is_array($extra_files)) {
-            var_dump($extra_files); exit;
             foreach ($extra_files as $key => $value) {
                 if (!$full_path) {
                     // Relative path from user folder

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

@@ -374,7 +374,6 @@ if (!empty($_SESSION['_user']['user_id']) && !($login || $logout)) {
                                     }
                                 }
                             } else {
-                                //error_log('Loggedin');
                                 ConditionalLogin::check_conditions($uData);
                                 $_user['user_id'] = $uData['user_id'];
                                 $_user['status']  = $uData['status'];
@@ -822,7 +821,7 @@ if (isset($cidReset) && $cidReset) {
 
         // these lines are usefull for tracking. Indeed we can have lost the id_session and not the cid.
         // Moreover, if we want to track a course with another session it can be usefull
-        if (!empty($_GET['id_session'])) {
+        if (!empty($_GET['id_session']) && is_numeric($_GET['id_session'])) {
             $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
             $sql = 'SELECT name FROM '.$tbl_session . ' WHERE id="'.intval($_SESSION['id_session']). '"';
             $rs = Database::query($sql);
@@ -885,18 +884,15 @@ if (isset($cidReset) && $cidReset) {
                                     login_course_date > now() - INTERVAL $session_lifetime SECOND
                         ORDER BY login_course_date DESC LIMIT 0,1";
                     $result = Database::query($sql);
-
                     if (Database::num_rows($result) > 0) {
                         $i_course_access_id = Database::result($result,0,0);
                         //We update the course tracking table
                         $sql = "UPDATE $course_tracking_table  SET logout_course_date = '$time', counter = counter+1
                                 WHERE course_access_id = ".intval($i_course_access_id)." AND session_id = ".api_get_session_id();
-                        //error_log($sql);
                         Database::query($sql);
                     } else {
                         $sql="INSERT INTO $course_tracking_table (course_code, user_id, login_course_date, logout_course_date, counter, session_id)" .
                             "VALUES('".$course_code."', '".$_user['user_id']."', '$time', '$time', '1','".api_get_session_id()."')";
-                        //error_log($sql);
                         Database::query($sql);
                     }
                 }

+ 10 - 9
main/lang/english/admin.inc.php

@@ -17,10 +17,10 @@ $ShowHotCoursesComment = "The hot courses list will be added in the index page";
 $ShowHotCoursesTitle = "Show hot courses";
 $EnableIframeInclusionComment = "Allowing arbitrary iframes in the HTML Editor will enhance the edition capabilities of the users, but it can represent a security risk. Please make sure you can rely on your users (i.e. you know who they are) before enabling this feature.";
 $EnableIframeInclusionTitle = "Allow iframes in HTML Editor";
-$MailTemplateRegistrationMessage = "Dear ((firstname)) ((lastname)),\n\nYou are registered on
-((sitename)) with the following settings:\n\nUsername :
-((username))\nPass : ((password))\n\nThe address of ((sitename)) is :
-((url))\n\nIn case of trouble, contact us.\n\nYours sincerely
+$MailTemplateRegistrationMessage = "Dear ((firstname)) ((lastname)),\n\nYou are registered on
+((sitename)) with the following settings:\n\nUsername :
+((username))\nPass : ((password))\n\nThe address of ((sitename)) is :
+((url))\n\nIn case of trouble, contact us.\n\nYours sincerely
 \n((admin_name)) ((admin_surname)).";
 $MailTemplateRegistrationTitle = "New user on ((sitename))";
 $AllowTeachersToCreateSessionsComment = "Teachers can create, edit and delete their own sessions.";
@@ -48,6 +48,7 @@ $ConfigureHomePage = "Edit portal homepage";
 $CourseCreateActiveToolsTitle = "Modules active upon course creation";
 $CourseCreateActiveToolsComment = "Which tools have to be enabled (visible) by default when a new course is created?";
 $SearchUsers = "Search users";
+$SearchUser = "Search users";
 $CreateUser = "Create user";
 $ModifyInformation = "Edit information";
 $ModifyUser = "Edit user";
@@ -1365,7 +1366,7 @@ $EnabledImageMapsTitle = "Activate Image maps";
 $EnabledImageMapsComment = "Activate the button to insert Image maps. This allows you to associate URLs to areas of an image, creating hotspots.";
 $CourseTool = "Course tool";
 $BigBlueButtonEnableTitle = "BigBlueButton videoconference tool";
-$BigBlueButtonEnableComment = "Choose whether you want to enable the BigBlueButton videoconference tool. Once enabled, it will show as an additional course tool in all courses' homepage, and teachers will be able to launch a conference at any time. Learners will not be able to launch a conference, only join one. If you don't have a BigBlueButton server, please <a href=\"http://bigbluebutton.org/\" target=\"_blank\">set one up</a> or ask the <a href=\"http://www.chamilo.org/en/providers\" target=\"_blank\">Chamilo official providers</a> for a quote.
+$BigBlueButtonEnableComment = "Choose whether you want to enable the BigBlueButton videoconference tool. Once enabled, it will show as an additional course tool in all courses' homepage, and teachers will be able to launch a conference at any time. Learners will not be able to launch a conference, only join one. If you don't have a BigBlueButton server, please <a href=\"http://bigbluebutton.org/\" target=\"_blank\">set one up</a> or ask the <a href=\"http://www.chamilo.org/en/providers\" target=\"_blank\">Chamilo official providers</a> for a quote.
 BigBlueButton is a free (as in freedom *and* beer), but its installation requires a set of technical skills that might not be immediately available to all. You can install it on your own or seek professional help to assist you or do it for you. This help, however, will generate a certain cost. In the pure logic of the free software, we offer you the tools to make your work easier and recommend professionals (the Chamilo Official Providers) that will be able to help you if this were too difficult.";
 $BigBlueButtonHostTitle = "BigBlueButton server host";
 $BigBlueButtonHostComment = "This is the name of the server where your BigBlueButton server is running. Might be <i>localhost</i>, an IP address (e.g. 192.168.13.54) or a domain name (e.g. my.video.com).";
@@ -1390,8 +1391,8 @@ $IncludeAsciiMathMlComment = "Activate this setting if you want to show ASCIIMat
 $CourseHideToolsTitle = "Hide tools from teachers";
 $CourseHideToolsComment = "Check the tools you want to hide from teachers. This will prohibit access to the tool.";
 $MoveUserStats = "Move users results from/to a session";
-$CompareUserResultsBetweenCoursesAndCoursesInASession = "This advanced tool allows you to manually improve the tracking of users results when moving from courses methodology to sessions methodology. In most cases, you won't need to use it.<br />
-On this screen, you can compare results of users between the context of a standalone course, and the context of the same course inside a session.<br />
+$CompareUserResultsBetweenCoursesAndCoursesInASession = "This advanced tool allows you to manually improve the tracking of users results when moving from courses methodology to sessions methodology. In most cases, you won't need to use it.<br />
+On this screen, you can compare results of users between the context of a standalone course, and the context of the same course inside a session.<br />
 Once you are sure about what to do, you can choose to move the tracking data of the students (exercises results and learning paths tracking) from a course to a session.";
 $PDFExportWatermarkEnableTitle = "Enable watermark in PDF export";
 $PDFExportWatermarkEnableComment = "By enabling this option, you can upload an image or a text that will be automatically added as watermark to all PDF exports of documents on the system.";
@@ -1446,8 +1447,8 @@ $EnableAccessibilityFontResizeTitle = "Font resize accessibility feature";
 $EnableAccessibilityFontResizeComment = "Enable this option to show a set of font resize options on the top-right side of your campus. This will allow visually impaired to read their course contents more easily.";
 $GlobalEvent = "Platform event";
 $SearchEnabledTitle = "Fulltext search";
-$SearchEnabledComment = "This feature allows you to index most of the documents uploaded to your portal, then provide a search feature for users.<br />
-This feature will not index documents that have already been uploaded, so it is important to enable (if wanted) at the beginning of your implementation.<br />
+$SearchEnabledComment = "This feature allows you to index most of the documents uploaded to your portal, then provide a search feature for users.<br />
+This feature will not index documents that have already been uploaded, so it is important to enable (if wanted) at the beginning of your implementation.<br />
 Once enabled, a search box will appear in the courses list of every user. Searching for a specific term will bring a list of corresponding documents, exercises or forum topics, filtered depending on the availability of these contents to the user.";
 $SpecificSearchFieldsAvailable = "Available custom search fields";
 $XapianModuleInstalled = "Xapian module installed";

+ 24 - 24
main/lang/english/exercice.inc.php

@@ -9,18 +9,18 @@ $AreYouSureToEmptyAllTestResults = "Clear all learners results for every exercis
 $CleanAllStudentsResultsForAllTests = "Are you sure to delete all test's results ?";
 $TestFeedbackNotShown = "This test is configured not to display feedback to learners. Comments will not be seen at the end of the test, but may be useful for you, as teacher, when reviewing the question details.";
 $FeedbackDisplayOptions = "How should we show the feedback/comment for each question? This option defines how it will be shown to the learner when taking the test. We recommend you try different options by editing your test options before having learners take it.";
-$ImportAikenQuizExplanationExample = "This is the text for question 1
-A. Answer 1
-B. Answer 2
-C. Answer 3
-ANSWER: B
-
-This is the text for question 2
-A. Answer 1
-B. Answer 2
-C. Answer 3
-D. Answer 4
-ANSWER: D
+$ImportAikenQuizExplanationExample = "This is the text for question 1
+A. Answer 1
+B. Answer 2
+C. Answer 3
+ANSWER: B
+
+This is the text for question 2
+A. Answer 1
+B. Answer 2
+C. Answer 3
+D. Answer 4
+ANSWER: D
 ANSWER_EXPLANATION: this is an optional feedback comment that will appear next to the correct answer.";
 $ImportAikenQuizExplanation = "The Aiken format comes in a simple text (.txt) file, with several question blocks, each separated by a blank line. The first line is the question, the answer lines are prefixed by a letter and a dot, and the correct answer comes next with the ANSWER: prefix. See example below.";
 $ExerciseAikenErrorNoAnswerOptionGiven = "The imported file has at least one question without any answer (or the answers do not include the required prefix letter). Please make sure each question has at least one answer and that it is prefixed by a letter and a dot or a parenthesis, like this: A. answer one";
@@ -36,18 +36,18 @@ $YesImSure = "Yes, I'm sure!";
 $NoIWantToTurnBack = "No, I want to continue the exercise.";
 $QuestionReused = "Question added in the exercise";
 $QuestionCopied = "Question copied to the exercise";
-$ZeroMeansNoQuestionWillBeSelectedMinusOneMeansThatAllQuestionsWillBeSelected = "-1 = All questions will be selected.
+$ZeroMeansNoQuestionWillBeSelectedMinusOneMeansThatAllQuestionsWillBeSelected = "-1 = All questions will be selected.
  0 = No questions will be selected.";
-$EmailNotificationTemplateDescription = "You can customize the email sent to users when they finished the exercise. You can use tags like these:
-
-1. {{ student.username }}
-2. {{ student.firstname }}
-3. {{ student.lastname }}
-4. {{ student.official_code }}
-5. {{ exercise.title }}
-6. {{ exercise.start_time }}
-7. {{ exercise.end_time }}
-8. {{ course.title }}
+$EmailNotificationTemplateDescription = "You can customize the email sent to users when they finished the exercise. You can use tags like these:
+
+1. {{ student.username }}
+2. {{ student.firstname }}
+3. {{ student.lastname }}
+4. {{ student.official_code }}
+5. {{ exercise.title }}
+6. {{ exercise.start_time }}
+7. {{ exercise.end_time }}
+8. {{ course.title }}
 9. {{ course.code }}";
 $EmailNotificationTemplate = "Email notification template";
 $ExerciseEndButtonDisconnect = "Logout";
@@ -585,4 +585,4 @@ $NoNegativeScore = "No negative score";
 $GlobalMultipleAnswer = "Global multiple answer";
 $AllQuestionsShort = "All";
 $ProblemsRecordingUploadYourOwnAudioFile = "Problem recording? Upload your own audio file.";
-?>
+?>

Dosya farkı çok büyük olduğundan ihmal edildi
+ 9 - 8
main/lang/spanish/admin.inc.php


+ 7 - 1
main/lang/spanish/tracking.inc.php

@@ -297,7 +297,7 @@ $DashboardBlocks = "Bloques del panel de control";
 $DashboardList = "Lista del panel de control";
 $YouHaveNotEnabledBlocks = "No ha habilitado ningún bloque";
 $BlocksHaveBeenUpdatedSuccessfully = "Los bloques han sido actualizados";
-$AttendanceSheetDescription = "Las listas de asistencia permiten registrar las faltas de asistencia de los estudiantes. En caso de ausencia de un estudiante, el profesor deberá registrarlo manualmente en la casilla correspondiente.
+$AttendanceSheetDescription = "Las listas de asistencia permiten registrar las faltas de asistencia de los estudiantes. En caso de ausencia de un estudiante, el profesor deberá registrarlo manualmente en la casilla correspondiente.
 Es posible crear más de una lista de asistencia por cada curso; así por ejemplo, podrá registrar separadamente la asistencia a las clases teóricas y prácticas.";
 $AttendanceSheetReport = "Informe de hojas de asistencia";
 $YouDoNotHaveDoneAttendances = "No tiene asistencias";
@@ -379,4 +379,10 @@ $FollowedSessions = "Sesiones seguidas";
 $FollowedCourses = "Cursos seguidos";
 $FollowedUsers = "Usuarios seguidos";
 $Timeline = "Línea del tiempo";
+$QuestionId = "ID de pregunta";
+$ExerciseId = "ID de ejercicio";
+$QuestionTitle = "Título pregunta";
+$DisplayGradeOverview = "Reporte de Notas por Curso";
+$LpGradeReport = "Reporte de Notas por Curso";
+$FinalScore = "Nota Final";
 ?>

+ 1 - 0
main/lang/spanish_latin/trad4all.inc.php

@@ -398,4 +398,5 @@ $AssessmentQuestions = "¿ Cómo van a ser evaluados los estudiantes ? ¿ Cuále
 $AssessmentInformation = "Ejemplos de las preguntas de examen. Criterios de evaluación. Pistas y trucos.";
 $UploadADocument = "Subir un documento";
 $LowerCaseUser = "usuario";
+$ChooseEvaluation = "Seleccione la Evaluación";
 ?>

+ 3 - 3
main/mySpace/lp_tracking.php

@@ -89,7 +89,7 @@ $table_title = ($session_name? Display::return_icon('session.png', get_lang('Ses
                 Display::return_icon('user.png', get_lang('User'), array(), ICON_SIZE_SMALL).' '.$name;
 echo Display::page_header($table_title);
 echo Display::page_subheader('<h3>'.Display::return_icon('learnpath.png', get_lang('ToolLearnpath'), array(), ICON_SIZE_SMALL).' '.$lp_title.'</h3>');
- 
+
 //Needed in newscorm/lp_stats.php
 $list = learnpath :: get_flat_ordered_items_list($lp_id, 0, $course_info['real_id']);
 
@@ -101,8 +101,8 @@ if ($export_csv) {
 	ob_start();
 	require_once  api_get_path(SYS_CODE_PATH).'newscorm/lp_stats.php';
 	$tracking_content = ob_get_contents();
-	ob_end_clean();    
+	ob_end_clean();
 	echo api_utf8_decode($tracking_content, $charset);
-    
+
 }
 Display :: display_footer();

+ 28 - 12
main/mySpace/myStudents.php

@@ -202,8 +202,7 @@ if ($check) {
 	}
 	Security::clear_token();
 }
-
-
+//Generate a new token
 // user info
 $user_info = api_get_user_info($student_id);
 
@@ -861,7 +860,8 @@ if (empty($_GET['details'])) {
 		<table class="data_table">
 			<tr>
 				<th><?php echo get_lang('Exercices'); ?></th>
-				<th><?php echo get_lang('AverageScore').' '.Display :: return_icon('info3.gif', get_lang('AverageScore'), array('align' => 'absmiddle', 'hspace' => '3px')) ?></th>
+                <th><?php echo get_lang('LearningPath');?></th>
+				<th><?php echo get_lang('AvgCourseScore').' '.Display :: return_icon('info3.gif', get_lang('AverageScore'), array('align' => 'absmiddle', 'hspace' => '3px')) ?></th>
 				<th><?php echo get_lang('Attempts'); ?></th>
 				<th><?php echo get_lang('LatestAttempt'); ?></th>
 				<th><?php echo get_lang('AllAttempts'); ?></th>
@@ -878,7 +878,6 @@ if (empty($_GET['details'])) {
 		$t_quiz = Database :: get_course_table(TABLE_QUIZ_TEST);
 		$sql_exercices = "SELECT quiz.title, id FROM " . $t_quiz . " AS quiz
 						  WHERE quiz.c_id =  ".$info_course['real_id']." AND
-						  		active='1' AND
 								(quiz.session_id = $session_id OR quiz.session_id = 0)
 							ORDER BY quiz.title ASC ";
 
@@ -888,21 +887,40 @@ if (empty($_GET['details'])) {
 			while ($exercices = Database :: fetch_array($result_exercices)) {
 				$exercise_id = intval($exercices['id']);
 
-				$count_attempts   = Tracking::count_student_exercise_attempts($student_id, $course_code, $exercise_id, 0, 0, $session_id);
-				$score_percentage = Tracking::get_avg_student_exercise_score($student_id, $course_code, $exercise_id, $session_id);
+				$count_attempts   = Tracking::count_student_exercise_attempts($student_id, $course_code, $exercise_id, 0, 0, $session_id, 2);
+				$score_percentage = Tracking::get_avg_student_exercise_score($student_id, $course_code, $exercise_id, $session_id, 1, 0);
 
+                if (!isset($score_percentage) && $count_attempts > 0) {
+                    $scores_lp = Tracking::get_avg_student_exercise_score($student_id, $course_code, $exercise_id, $session_id, 2, 1);
+                    $score_percentage = $scores_lp[0];
+                    $lp_name = $scores_lp[1];
+                } else {
+                    $lp_name = '-';
+                }
+                $lp_name = !empty($lp_name) ? $lp_name : get_lang('NoLearnpath');
 				$csv_content[] = array (
 					$exercices['title'],
 					$score_percentage . '%',
 					$count_attempts
 				);
 
-				if ($i % 2) $css_class = 'row_odd';
-				else $css_class = 'row_even';
+                if ($i % 2) {
+                    $css_class = 'row_odd';
+                } else {
+                    $css_class = 'row_even';
+                }
 
 				echo '<tr class="'.$css_class.'"><td>'.$exercices['title'].'</td>';
+                echo '<td>';
 
-				echo '<td>';
+                if (!empty($lp_name)) {
+                    echo $lp_name;
+                } else {
+                    echo '-';
+                }
+
+                echo '</td>';
+                echo '<td>';
 
 				if ($count_attempts > 0) {
 					echo $score_percentage . '%';
@@ -920,9 +938,7 @@ if (empty($_GET['details'])) {
 				                            exe_user_id     ="'.$student_id.'" AND
 				                            exe_cours_id    ="'.$course_code.'" AND
                                             session_id      ="'.$session_id.'" AND
-				                            status          = "" AND
-				                            orig_lp_id      = 0 AND
-				                            orig_lp_item_id = 0
+				                            status          = ""
 				                            ORDER BY exe_date DESC LIMIT 1';
 				$result_last_attempt = Database::query($sql_last_attempt);
 				if (Database :: num_rows($result_last_attempt) > 0) {

+ 261 - 93
main/mySpace/myspace.lib.php

@@ -1,19 +1,15 @@
 <?php
 /* For licensing terms, see /license.txt */
-/**
- * MySpace class definition
- * @package chamilo.reporting
- */
-/**
- * Init
- */
+
 require_once api_get_path(LIBRARY_PATH).'export.lib.inc.php';
 require_once api_get_path(LIBRARY_PATH).'tracking.lib.php';
+
 /**
- * MySpace class definition
+ * Class MySpace
+ * @package chamilo.reporting
  */
-class MySpace {
-
+class MySpace
+{
 	/**
 	 * This function serves exporting data in CSV format.
 	 * @param array $header			The header labels.
@@ -60,8 +56,8 @@ class MySpace {
 	 * @param	int		Session id (optional, default = 0)
 	 * @return 	array   Conections
 	 */
-	static function get_connections_to_course($user_id, $course_code, $session_id = 0) {
-
+	static function get_connections_to_course($user_id, $course_code, $session_id = 0)
+    {
 		// Database table definitions
 	    $tbl_track_course 	= Database :: get_statistic_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
 
@@ -274,7 +270,7 @@ class MySpace {
      * Display a sortable table that contains an overview off all the progress of the user in a session
      * @author César Perales <cesar.perales@beeznest.com>, Beeznest Team
      */
-    public function display_tracking_lp_progress_overview($sessionId = '', $courseId = '') {
+    public static function display_tracking_lp_progress_overview($sessionId = '', $courseId = '', $date_from, $date_to) {
 
         $course = api_get_course_info_by_id($courseId);
         /**
@@ -316,7 +312,7 @@ class MySpace {
 
         $action_links = '';
         // jqgrid will use this URL to do the selects
-        $url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_session_lp_progress&session_id=' . intval($sessionId) . '&course_id=' . intval($courseId);
+        $url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_session_lp_progress&session_id=' . $sessionId . '&course_id=' . $courseId . '&date_to=' . $date_to . '&date_from=' . $date_from;
 
         //Table Id
         $tableId = 'lpProgress';
@@ -344,13 +340,25 @@ class MySpace {
     }
     /**
      * Display a sortable table that contains an overview off all the progress of the user in a session
+     * @param   int $sessionId  The session ID
+     * @param   int $courseId   The course ID
+     * @param   int $exerciseId The quiz ID
+     * @param   int $answer Answer status (0 = incorrect, 1 = correct, 2 = both)
+     * @return  string  HTML array of results formatted for gridJS
      * @author César Perales <cesar.perales@beeznest.com>, Beeznest Team
      */
-    function display_tracking_exercise_progress_overview($sessionId = 0, $courseId = 0, $exerciseId = 0, $answer = 2) {
-
+    static function display_tracking_exercise_progress_overview(
+        $sessionId = 0,
+        $courseId = 0,
+        $exerciseId = 0,
+        $date_from = null,
+        $date_to = null
+    ) {
+        $date_from = Security::remove_XSS($date_from);
+        $date_to = Security::remove_XSS($date_to);
         /**
-         * Column name
-         * The order is important you need to check the $column variable in the model.ajax.php file
+         * Column names
+         * The column order is important. Check $column variable in the main/inc/ajax/model.ajax.php file
          */
         $columns = array(
             get_lang('Session'),
@@ -362,6 +370,7 @@ class MySpace {
             get_lang('Time'),
             get_lang('QuestionId'),
             get_lang('QuestionTitle'),
+            get_lang('WorkDescription'),
             get_lang('Answer'),
             get_lang('Correct'),
         );
@@ -370,32 +379,32 @@ class MySpace {
          * Column config
          */
         $column_model   = array(
-            array('name'=>'session',        'index'=>'session',       'align'=>'left', 'search' => 'true', 'wrap_cell' => "true"),
-            array('name'=>'exercise_id',    'index'=>'exercise_id',   'align'=>'left', 'search' => 'true'),
-            array('name'=>'quiz_title',     'index'=>'quiz_title',    'align'=>'left', 'search' => 'true'),
-            array('name'=>'username',       'index'=>'username',      'align'=>'left', 'search' => 'true'),
-            array('name'=>'lastname',       'index'=>'lastname',      'align'=>'left', 'search' => 'true'),
-            array('name'=>'firstname',      'index'=>'firstname',     'align'=>'left', 'search' => 'true'),
-            array('name'=>'time',           'index'=>'time',          'align'=>'left', 'search' => 'true', 'wrap_cell' => "true"),
-            array('name'=>'question_id',    'index'=>'question_id',   'align'=>'left', 'search' => 'true'),
-            array('name'=>'question',       'index'=>'question',      'align'=>'left', 'search' => 'true', 'wrap_cell' => "true"),
-            array('name'=>'answer',         'index'=>'answer',        'align'=>'left', 'search' => 'true', 'wrap_cell' => "true"),
-            array('name'=>'correct',        'index'=>'correct',       'align'=>'left', 'search' => 'true'),
+            array('name'=>'session', 'index'=>'session', 'align'=>'left', 'search' => 'true', 'wrap_cell' => "true"),
+            array('name'=>'exercise_id', 'index'=>'exercise_id', 'align'=>'left', 'search' => 'true'),
+            array('name'=>'quiz_title', 'index'=>'quiz_title', 'align'=>'left', 'search' => 'true'),
+            array('name'=>'username', 'index'=>'username', 'align'=>'left', 'search' => 'true'),
+            array('name'=>'lastname', 'index'=>'lastname', 'align'=>'left', 'search' => 'true'),
+            array('name'=>'firstname', 'index'=>'firstname', 'align'=>'left', 'search' => 'true'),
+            array('name'=>'time', 'index'=>'time', 'align'=>'left', 'search' => 'true', 'wrap_cell' => "true"),
+            array('name'=>'question_id', 'index'=>'question_id', 'align'=>'left', 'search' => 'true'),
+            array('name'=>'question', 'index'=>'question', 'align'=>'left', 'search' => 'true', 'wrap_cell' => "true"),
+            array('name'=>'description', 'index'=>'description', 'align'=>'left', 'width' => '550', 'search' => 'true', 'wrap_cell' => "true"),
+            array('name'=>'answer', 'index'=>'answer', 'align'=>'left', 'search' => 'true', 'wrap_cell' => "true"),
+            array('name'=>'correct', 'index'=>'correct', 'align'=>'left', 'search' => 'true', 'wrap_cell' => "true"),
         );
-        //get dinamic column names
+        //get dynamic column names
 
-        $action_links = '';
         // jqgrid will use this URL to do the selects
-        $url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_exercise_progress&session_id=' . intval($sessionId) . '&course_id=' . intval($courseId)  . '&exercise_id=' . intval($exerciseId) . '&answer=' . intval($answer);
+        $url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_exercise_progress&session_id=' . $sessionId . '&course_id=' . $courseId  . '&exercise_id=' . $exerciseId . '&date_to=' . $date_to . '&date_from=' . $date_from;
 
-        //Autowidth
+        // Autowidth
         $extra_params['autowidth'] = 'true';
 
-        //height auto
+        // height auto
         $extra_params['height'] = 'auto';
 
         $tableId = 'exerciseProgressOverview';
-        $table = Display::grid_js($tableId, $url, $columns, $column_model, $extra_params, array(), $action_links, true);
+        $table = Display::grid_js($tableId, $url, $columns, $column_model, $extra_params, array(), '', true);
 
         $return = '<script>$(function() {'. $table .
             'jQuery("#'.$tableId.'").jqGrid("navGrid","#'.$tableId.'_pager",{view:false, edit:false, add:false, del:false, search:false, excel:true});
@@ -410,13 +419,127 @@ class MySpace {
         $return .= Display::grid_html($tableId);
         return $return;
     }
+
     /**
      * Display a sortable table that contains an overview off all the progress of the user in a session
-     * @author César Perales <cesar.perales@beeznest.com>, Beeznest Team
+     * @param   int $sessionId The session ID
+     * @param   int $courseId The course ID
+     * @param null $date_from
+     * @param null $date_to
+     * @internal param int $exerciseId The quiz ID
+     * @internal param int $answer Answer status (0 = incorrect, 1 = correct, 2 = both)
+     * @return  string  HTML array of results formatted for gridJS
+     * @author Francis Gonzales <francis.gonzales@beeznest.com>, Beeznest Team
      */
-    function display_survey_overview($sessionId = 0, $courseId = 0, $surveyId = 0, $answer = 2) {
+    static function display_tracking_grade_overview($sessionId = 0, $courseId = 0, $date_from = null, $date_to = null)
+    {
+        /**
+         * Column names
+         * The column order is important. Check $column variable in the main/inc/ajax/model.ajax.php file
+         */
+        $objExercise = new Exercise();
+        $exercises = $objExercise->getExercisesByCouseSession($courseId, $sessionId);
 
-        $course = api_get_course_info_by_id($courseId);
+        $cntExer = 4;
+        if (!empty($exercises)) {
+            $cntExer += count($exercises);
+        }
+
+        $column = array();
+        $column_model = array();
+        $i = 1;
+        //Get dynamic column names
+        foreach (range(1, $cntExer) as $cnt) {
+            switch ($cnt) {
+                case 1:
+                    $column[] = get_lang('Section');
+                    $column_model[] = array(
+                        'name' => 'session',
+                        'index' => 'session',
+                        'align' => 'center',
+                        'search' => 'true',
+                    );
+                    break;
+                case 2:
+                    $column[] = get_lang('Username');
+                    $column_model[] = array(
+                        'name' => 'username',
+                        'index' => 'username',
+                        'align' => 'center',
+                        'search' => 'true',
+                    );
+                    break;
+                case 3:
+                    $column[] = get_lang('FirstName');
+                    $column_model[] = array(
+                        'name' => 'name',
+                        'index' => 'name',
+                        'align' => 'left',
+                        'search' => 'true',
+                    );
+                    break;
+                case $cntExer:
+                    $column[] = get_lang('FinalScore');
+                    $column_model[] = array(
+                        'name' => 'finalscore',
+                        'index' => 'finalscore',
+                        'align' => 'center',
+                        'search' => 'true',
+                        'wrap_cell' => "true"
+                    );
+                    break;
+                default:
+                    $title = "";
+                    if (!empty($exercises[$cnt - 4]['title'])) {
+                        $title = ucwords(strtolower(trim($exercises[$cnt - 4]['title'])));
+                    }
+
+                    $column[] = $title;
+                    $column_model[] = array(
+                        'name' => 'exer' . $i,
+                        'index' => 'exer' . $i,
+                        'align' => 'center',
+                        'search' => 'true',
+                        'wrap_cell' => "true"
+                    );
+                    $i++;
+                    break;
+            }
+        }
+
+        //end get dynamic column names
+        // jqgrid will use this URL to do the selects
+        $url = api_get_path(WEB_AJAX_PATH) . 'model.ajax.php?a=get_exercise_grade&session_id=' . $sessionId . '&course_id=' . $courseId;
+
+        //Autowidth
+        $extra_params['autowidth'] = 'true';
+
+        //height auto
+        $extra_params['height'] = 'auto';
+
+        $tableId = 'exerciseGradeOverview';
+        $table = Display::grid_js($tableId, $url, $column, $column_model, $extra_params, array(), '', true);
+
+        $return = '<script>$(function() {' . $table .
+                'jQuery("#' . $tableId . '").jqGrid("navGrid","#' . $tableId . '_pager",{view:false, edit:false, add:false, del:false, search:false, excel:true});
+                jQuery("#' . $tableId . '").jqGrid("navButtonAdd","#' . $tableId . '_pager",{
+                       caption:"",
+                       title:"' . get_lang('ExportExcel') . '",
+                       onClickButton : function () {
+                           jQuery("#' . $tableId . '").jqGrid("excelExport",{"url":"' . $url . '&export_format=xls"});
+                       }
+                });
+            });</script>';
+        $return .= Display::grid_html($tableId);
+        return $return;
+    }
+
+    /**
+     * Display a sortable table that contains an overview off all the progress of the user in a session
+     * @author César Perales <cesar.perales@beeznest.com>, Beeznest Team
+     */
+    function display_survey_overview($sessionId = 0, $courseId = 0, $surveyId = 0, $date_from, $date_to)
+    {
         /**
          * Column name
          * The order is important you need to check the $column variable in the model.ajax.php file
@@ -429,8 +552,7 @@ class MySpace {
         //add lessons of course
         $questions = survey_manager::get_questions($surveyId, $courseId);
 
-        foreach ($questions as $question_id => $question)
-        {
+        foreach ($questions as $question) {
             $columns[] = $question['question'];
         }
 
@@ -443,15 +565,20 @@ class MySpace {
             array('name'=>'lastname',   'index'=>'lastname',    'align'=>'left', 'search' => 'true'),
         );
         //get dinamic column names
-        foreach ($questions as $question_id => $question)
-        {
-            $column_model[] = array('name'=> $question_id,   'index'=>$question_id,    'width'=>'70',   'align'=>'left', 'search' => 'true');
+        foreach ($questions as $question_id => $question) {
+            $column_model[] = array(
+                'name'=> $question_id,
+                'index'=>$question_id,
+                'width'=>'70',
+                'align'=>'left',
+                'search' => 'true'
+            );
         }
 
         $action_links = '';
 
         // jqgrid will use this URL to do the selects
-        $url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_survey_overview&session_id=' . intval($sessionId) . '&course_id=' . intval($courseId) . '&survey_id=' . intval($surveyId);
+        $url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_survey_overview&session_id=' . $sessionId . '&course_id=' . $courseId . '&survey_id=' . $surveyId . '&date_to=' . $date_to . '&date_from=' . $date_from;
 
         //Table Id
         $tableId = 'lpProgress';
@@ -475,14 +602,16 @@ class MySpace {
                 });
             });</script>';
         $return .= Display::grid_html($tableId);
+
         return $return;
     }
+
     /**
      * Display a sortable table that contains an overview off all the progress of the user in a session
      * @author César Perales <cesar.perales@beeznest.com>, Beeznest Team
      */
-    function display_tracking_progress_overview($sessionId = 0, $courseId = 0) {
-
+    static function display_tracking_progress_overview($sessionId = 0, $courseId = 0,  $date_from, $date_to)
+    {
        //The order is important you need to check the the $column variable in the model.ajax.php file
         $columns = array(
             get_lang('LastName'),
@@ -490,15 +619,13 @@ class MySpace {
             get_lang('Username'),
             #get_lang('Profile'),
             get_lang('Total'),
-            get_lang('CourseDescription'),
+            get_lang('Courses'),
             get_lang('LearningPaths'),
             get_lang('Exercises'),
             get_lang('Forums'),
             get_lang('Assignments'),
             get_lang('ToolWiki'),
             get_lang('ToolSurvey'),
-            //course description
-            get_lang('CourseDescriptionProgress'),
             //Learning paths
             get_lang('LearnpathsTotal'),
             get_lang('LearnpathsDone'),
@@ -534,56 +661,54 @@ class MySpace {
 
         //Column config
         $column_model   = array(
-            array('name'=>'lastname',   'index'=>'lastname',     'align'=>'left', 'search' => 'true'),
-            array('name'=>'firstname',  'index'=>'firstname',    'align'=>'left', 'search' => 'true'),
-            array('name'=>'username',   'index'=>'username',     'align'=>'left', 'search' => 'true'),
-            #array('name'=>'profile',   'index'=>'username',     'align'=>'left', 'search' => 'true'),
-            array('name'=>'total',      'index'=>'total',        'align'=>'left', 'search' => 'true'),
-            array('name'=>'courses',    'index'=>'courses',      'align'=>'left', 'search' => 'true'),
-            array('name'=>'lessons',    'index'=>'lessons',      'align'=>'left', 'search' => 'true'),
-            array('name'=>'exercises',  'index'=>'exercises',    'align'=>'left', 'search' => 'true'),
-            array('name'=>'forums',     'index'=>'forums',       'align'=>'left', 'search' => 'true'),
-            array('name'=>'homeworks',  'index'=>'homeworks',    'align'=>'left', 'search' => 'true'),
-            array('name'=>'wikis',      'index'=>'wikis',        'align'=>'left', 'search' => 'true'),
-            array('name'=>'surveys',    'index'=>'surveys',      'align'=>'left', 'search' => 'true'),
-            //Course description
-            array('name'=>'course_description_progress',    'index'=>'course_description_progress',      'align'=>'left', 'search' => 'true'),
+            array('name'=>'lastname',   'index'=>'lastname',     'align'=>'left'),
+            array('name'=>'firstname',  'index'=>'firstname',    'align'=>'left'),
+            array('name'=>'username',   'index'=>'username',     'align'=>'left'),
+            #array('name'=>'profile',   'index'=>'username',     'align'=>'left'),
+            array('name'=>'total',      'index'=>'total',        'align'=>'left'),
+            array('name'=>'courses',    'index'=>'courses',      'align'=>'left', 'sortable' => 'false'),
+            array('name'=>'lessons',    'index'=>'lessons',      'align'=>'left', 'sortable' => 'false'),
+            array('name'=>'exercises',  'index'=>'exercises',    'align'=>'left', 'sortable' => 'false'),
+            array('name'=>'forums',     'index'=>'forums',       'align'=>'left', 'sortable' => 'false'),
+            array('name'=>'homeworks',  'index'=>'homeworks',    'align'=>'left', 'sortable' => 'false'),
+            array('name'=>'wikis',      'index'=>'wikis',        'align'=>'left', 'sortable' => 'false'),
+            array('name'=>'surveys',    'index'=>'surveys',      'align'=>'left', 'sortable' => 'false'),
             //Lessons
-            array('name'=>'lessons_total',    'index'=>'lessons_total',      'align'=>'left', 'search' => 'true'),
-            array('name'=>'lessons_done',     'index'=>'lessons_done',       'align'=>'left', 'search' => 'true'),
-            array('name'=>'lessons_left',     'index'=>'lessons_left',       'align'=>'left', 'search' => 'true'),
-            array('name'=>'lessons_progress', 'index'=>'lessons_progress',   'align'=>'left', 'search' => 'true'),
+            array('name'=>'lessons_total',    'index'=>'lessons_total',      'align'=>'center', 'sortable' => 'false'),
+            array('name'=>'lessons_done',     'index'=>'lessons_done',       'align'=>'center', 'sortable' => 'false'),
+            array('name'=>'lessons_left',     'index'=>'lessons_left',       'align'=>'center', 'sortable' => 'false'),
+            array('name'=>'lessons_progress', 'index'=>'lessons_progress',   'align'=>'center', 'sortable' => 'false'),
             //Exercises
-            array('name'=>'exercises_total',    'index'=>'exercises_total',      'align'=>'left', 'search' => 'true'),
-            array('name'=>'exercises_done',     'index'=>'exercises_done',       'align'=>'left', 'search' => 'true'),
-            array('name'=>'exercises_left',     'index'=>'exercises_left',       'align'=>'left', 'search' => 'true'),
-            array('name'=>'exercises_progress', 'index'=>'exercises_progress',   'align'=>'left', 'search' => 'true'),
+            array('name'=>'exercises_total',    'index'=>'exercises_total',      'align'=>'center', 'sortable' => 'false'),
+            array('name'=>'exercises_done',     'index'=>'exercises_done',       'align'=>'center', 'sortable' => 'false'),
+            array('name'=>'exercises_left',     'index'=>'exercises_left',       'align'=>'center', 'sortable' => 'false'),
+            array('name'=>'exercises_progress', 'index'=>'exercises_progress',   'align'=>'center', 'sortable' => 'false'),
             //Assignments
-            array('name'=>'forums_total',    'index'=>'forums_total',        'align'=>'left', 'search' => 'true'),
-            array('name'=>'forums_done',     'index'=>'forums_done',         'align'=>'left', 'search' => 'true'),
-            array('name'=>'forums_left',     'index'=>'forums_left',         'align'=>'left', 'search' => 'true'),
-            array('name'=>'forums_progress', 'index'=>'forums_progress',     'align'=>'left', 'search' => 'true'),
+            array('name'=>'forums_total',    'index'=>'forums_total',        'align'=>'center', 'sortable' => 'false'),
+            array('name'=>'forums_done',     'index'=>'forums_done',         'align'=>'center', 'sortable' => 'false'),
+            array('name'=>'forums_left',     'index'=>'forums_left',         'align'=>'center', 'sortable' => 'false'),
+            array('name'=>'forums_progress', 'index'=>'forums_progress',     'align'=>'center', 'sortable' => 'false'),
             //Assignments
-            array('name'=>'assigments_total',    'index'=>'assigments_total',        'align'=>'left', 'search' => 'true'),
-            array('name'=>'assigments_done',     'index'=>'assigments_done',         'align'=>'left', 'search' => 'true'),
-            array('name'=>'assigments_left',     'index'=>'assigments_left',         'align'=>'left', 'search' => 'true'),
-            array('name'=>'assigments_progress', 'index'=>'assigments_progress',     'align'=>'left', 'search' => 'true'),
+            array('name'=>'assigments_total',    'index'=>'assigments_total',        'align'=>'center', 'sortable' => 'false'),
+            array('name'=>'assigments_done',     'index'=>'assigments_done',         'align'=>'center', 'sortable' => 'false'),
+            array('name'=>'assigments_left',     'index'=>'assigments_left',         'align'=>'center', 'sortable' => 'false'),
+            array('name'=>'assigments_progress', 'index'=>'assigments_progress',     'align'=>'center', 'sortable' => 'false'),
             //Assignments
-            array('name'=>'wiki_total',         'index'=>'wiki_total',       'align'=>'left', 'search' => 'true'),
-            array('name'=>'wiki_revisions',     'index'=>'wiki_revisions',   'align'=>'left', 'search' => 'true'),
-            array('name'=>'wiki_read',          'index'=>'wiki_read',        'align'=>'left', 'search' => 'true'),
-            array('name'=>'wiki_unread',        'index'=>'wiki_unread',      'align'=>'left', 'search' => 'true'),
-            array('name'=>'wiki_progress',      'index'=>'wiki_progress',    'align'=>'left', 'search' => 'true'),
+            array('name'=>'wiki_total',         'index'=>'wiki_total',       'align'=>'center', 'sortable' => 'false'),
+            array('name'=>'wiki_revisions',     'index'=>'wiki_revisions',   'align'=>'center', 'sortable' => 'false'),
+            array('name'=>'wiki_read',          'index'=>'wiki_read',        'align'=>'center', 'sortable' => 'false'),
+            array('name'=>'wiki_unread',        'index'=>'wiki_unread',      'align'=>'center', 'sortable' => 'false'),
+            array('name'=>'wiki_progress',      'index'=>'wiki_progress',    'align'=>'center', 'sortable' => 'false'),
             //Surveys
-            array('name'=>'surveys_total',    'index'=>'surveys_total',      'align'=>'left', 'search' => 'true'),
-            array('name'=>'surveys_done',     'index'=>'surveys_done',       'align'=>'left', 'search' => 'true'),
-            array('name'=>'surveys_left',     'index'=>'surveys_left',       'align'=>'left', 'search' => 'true'),
-            array('name'=>'surveys_progress', 'index'=>'surveys_progress',   'align'=>'left', 'search' => 'true'),
+            array('name'=>'surveys_total',    'index'=>'surveys_total',      'align'=>'center', 'sortable' => 'false'),
+            array('name'=>'surveys_done',     'index'=>'surveys_done',       'align'=>'center', 'sortable' => 'false'),
+            array('name'=>'surveys_left',     'index'=>'surveys_left',       'align'=>'center', 'sortable' => 'false'),
+            array('name'=>'surveys_progress', 'index'=>'surveys_progress',   'align'=>'center', 'sortable' => 'false'),
         );
 
         $action_links = '';
         // jqgrid will use this URL to do the selects
-        $url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_session_progress&session_id=' . intval($sessionId) . '&course_id=' . intval($courseId);
+        $url = api_get_path(WEB_AJAX_PATH).'model.ajax.php?a=get_session_progress&session_id=' . $sessionId . '&course_id=' . $courseId . '&date_to=' . $date_to . '&date_from=' . $date_from;
 
         //Table Id
         $tableId = 'progressOverview';
@@ -591,6 +716,47 @@ class MySpace {
         //Autowidth
         $extra_params['autowidth'] = 'true';
 
+        $extra_params['shrinkToFit'] = 'true';
+
+        $extra_params['headertitles'] = 'true';
+
+        $extra_params['groupHeaders'] = array(
+            'courses_detail' => array(
+                "startColumnName"   => 'courses',
+                "numberOfColumns"   =>  7,
+                "titleText"         =>  get_lang('Global'),
+                ),
+            'lessons' => array(
+                "startColumnName"   => 'lessons_total',
+                "numberOfColumns"   =>  4,
+                "titleText"         =>  get_lang('LearningPaths'),
+                ),
+            'exercises' => array(
+                "startColumnName"   => 'exercises_total',
+                "numberOfColumns"   =>  4,
+                "titleText"         =>  get_lang('Exercises'),
+                ),
+            'forums' => array(
+                "startColumnName"   => 'forums_total',
+                "numberOfColumns"   =>  4,
+                "titleText"         =>  get_lang('Forums'),
+                ),
+            'assignments' => array(
+                "startColumnName"   => 'assigments_total',
+                "numberOfColumns"   =>  4,
+                "titleText"         =>  get_lang('Assignments'),
+                ),
+            'wikis' => array(
+                "startColumnName"   => 'wiki_total',
+                "numberOfColumns"   =>  5,
+                "titleText"         =>  get_lang('Wiki'),
+                ),
+            'surveys' => array(
+                "startColumnName"   => 'surveys_total',
+                "numberOfColumns"   =>  4,
+                "titleText"         =>  get_lang('Survey'),
+                ),
+        );
         //height auto
         $extra_params['height'] = 'auto';
 
@@ -614,16 +780,17 @@ class MySpace {
      * @author César Perales <cesar.perales@beeznest.com>, Beeznest Team
      * @version Chamilo 1.9.6
      */
-    function display_tracking_access_overview($sessionId = 0, $courseId = 0, $studentId = '', $profile = '', $date_to, $date_from) {
+    static function display_tracking_access_overview($sessionId = 0, $courseId = 0, $studentId = '', $profile = '', $date_from, $date_to) {
         //The order is important you need to check the the $column variable in the model.ajax.php file
         $columns = array(
             get_lang('LoginDate'),
-            get_lang('Username'),
-            get_lang('Firstname'),
-            get_lang('Lastname'),
+            get_lang('UserName'),
+            get_lang('LastName'),
+            get_lang('FirstName'),
             get_lang('Clicks'),
             get_lang('IP'),
             get_lang('TimeLoggedIn'),
+            get_lang('Section'),
         );
 
         $column_model   = array(
@@ -634,6 +801,7 @@ class MySpace {
             array('name'=>'clicks',         'index'=>'clicks',         'align'=>'left', 'search' => 'true'),
             array('name'=>'ip',             'index'=>'ip',             'align'=>'left', 'search' => 'true'),
             array('name'=>'timeloggedin',   'index'=>'timeLoggedIn',   'align'=>'left', 'search' => 'true'),
+            array('name'=>'session',   'index'=>'session',   'align'=>'left')
         );
 
         $action_links = '';

+ 1 - 1
main/newscorm/learnpathList.class.php

@@ -170,7 +170,7 @@ class learnpathList {
      *  @param int  Id of session
      *  @return array List of lessons with lessons id as keys
      */
-    function get_course_lessons($course_code, $session_id) {
+    static function  get_course_lessons($course_code, $session_id) {
         $tbl_course_lp          = Database::get_course_table(TABLE_LP_MAIN);
 
         $course = api_get_course_info($course_code);

+ 8 - 2
main/newscorm/lp_ajax_save_item.php

@@ -44,7 +44,7 @@ require_once 'aiccItem.class.php';
  * @param   array   Interactions array
  * @param   string  Core exit SCORM string
  */
-function save_item($lp_id, $user_id, $view_id, $item_id, $score = -1, $max = -1, $min = -1, $status = '', $time = 0, $suspend = '', $location = '', $interactions = array(), $core_exit = 'none')
+function save_item($lp_id, $user_id, $view_id, $item_id, $score = -1, $max = -1, $min = -1, $status = '', $time = 0, $suspend = '', $location = '', $interactions = array(), $core_exit = 'none', $sessionId = null, $courseId = null)
 {
     global $debug;
     $return = null;
@@ -58,6 +58,9 @@ function save_item($lp_id, $user_id, $view_id, $item_id, $score = -1, $max = -1,
 
     $mylp = null;
     $lpobject = Session::read('lpobject');
+    if (!is_object($lpobject) && isset($sessionId) && isset($courseId)) {
+        $lpobject = new learnpathItem($lp_id, $user_id, $courseId);
+    }
     if (isset($lpobject)) {
         $oLP = unserialize($lpobject);
         if ($debug) error_log("lpobject was set");
@@ -305,5 +308,8 @@ echo save_item(
     (!empty($_REQUEST['suspend'])?$_REQUEST['suspend']:null),
     (!empty($_REQUEST['loc'])?$_REQUEST['loc']:null),
     $interactions,
-    (!empty($_REQUEST['core_exit'])?$_REQUEST['core_exit']:'')
+    (!empty($_REQUEST['core_exit'])?$_REQUEST['core_exit']:''),
+    '',
+    (!empty($_REQUEST['session_id'])?$_REQUEST['session_id']:''),
+    (!empty($_REQUEST['course_id'])?$_REQUEST['course_id']:'')
 );

+ 6 - 0
main/newscorm/lp_controller.php

@@ -864,6 +864,12 @@ switch ($action) {
                 $_SESSION['oLP']->delete_lp_image();
             }
 
+            $extraFieldValue = new ExtraFieldValue('lp');
+            $params = array(
+                'lp_id' => $_SESSION['oLP']->id
+            );
+            $extraFieldValue->save_field_values($_REQUEST);
+
             if ($_FILES['lp_preview_image']['size'] > 0)
                 $_SESSION['oLP']->upload_image($_FILES['lp_preview_image']);
 

+ 18 - 8
main/newscorm/lp_edit.php

@@ -23,9 +23,9 @@ if (isset($_SESSION['gradebook'])) {
 
 if (!empty($gradebook) && $gradebook == 'view') {
     $interbreadcrumb[] = array (
-            'url' => '../gradebook/'.$_SESSION['gradebook_dest'],
-            'name' => get_lang('ToolGradebook')
-        );
+        'url' => '../gradebook/'.$_SESSION['gradebook_dest'],
+        'name' => get_lang('ToolGradebook')
+    );
 }
 $interbreadcrumb[] = array('url' => 'lp_controller.php?action=list', 'name' => get_lang('LearningPaths'));
 $interbreadcrumb[] = array('url' => api_get_self()."?action=build&lp_id=".$_SESSION['oLP']->get_id(), 'name' => $_SESSION['oLP']->get_name());
@@ -50,11 +50,6 @@ function activate_end_date() {
 
 </script>';
 
-
-Display::display_header(get_lang('CourseSettings'), 'Path');
-
-echo $_SESSION['oLP']->build_action_menu();
-
 $gradebook = isset($_GET['gradebook']) ? Security::remove_XSS($_GET['gradebook']) : null;
 
 $defaults=array();
@@ -198,6 +193,9 @@ if (api_is_platform_admin()) {
     $defaults['use_max_score'] = $_SESSION['oLP']->use_max_score;
 }
 
+$extraField = new ExtraField('lp');
+$extra = $extraField->addElements($form, $_SESSION['oLP']->get_id());
+
 //Submit button
 $form->addElement('style_submit_button', 'Submit',get_lang('SaveLPSettings'),'class="save"');
 
@@ -205,12 +203,24 @@ $form->addElement('style_submit_button', 'Submit',get_lang('SaveLPSettings'),'cl
 $form->addElement('hidden', 'action', 'update_lp');
 $form->addElement('hidden', 'lp_id', $_SESSION['oLP']->get_id());
 
+$htmlHeadXtra[] = '<script src="'.api_get_path(WEB_LIBRARY_PATH).'javascript/tag/jquery.fcbkcomplete.js" type="text/javascript" language="javascript"></script>';
+$htmlHeadXtra[] = '<link  href="'.api_get_path(WEB_LIBRARY_PATH).'javascript/tag/style.css" rel="stylesheet" type="text/css" />';
+$htmlHeadXtra[] ='<script>
+$(function() {
+    '.$extra['jquery_ready_content'].'
+});
+</script>';
+
 
 $defaults['publicated_on']  = ($publicated_on!='0000-00-00 00:00:00' && !empty($publicated_on))? api_get_local_time($publicated_on) : date('Y-m-d 12:00:00');
 $defaults['expired_on']     = ($expired_on   !='0000-00-00 00:00:00' && !empty($expired_on) )? api_get_local_time($expired_on): date('Y-m-d 12:00:00',time()+84600);
 
 $form->setDefaults($defaults);
 
+Display::display_header(get_lang('CourseSettings'), 'Path');
+
+echo $_SESSION['oLP']->build_action_menu();
+
 echo '<div class="row">';
 
 if ($_SESSION['oLP']->get_hide_toc_frame() == 1) {

+ 5 - 5
main/newscorm/lp_stats.php

@@ -48,7 +48,7 @@ $course_id = $course_info['real_id'];
 if (isset($_GET['student_id'])) {
     $student_id = intval($_GET['student_id']);
 }
-$session_id = api_get_session_id();
+$session_id = isset($_GET['id_session']) ? intval($_GET['id_session']) : api_get_session_id();
 $session_condition = api_get_session_condition($session_id);
 
 //When origin is not set that means that the lp_stats are viewed from the "man running" icon
@@ -859,10 +859,10 @@ if (!empty($a_my_id)) {
     //var_dump($my_studen_id, $my_course_id,$a_my_id);
     if (isset($_GET['extend_attempt'])) {
         //"Right green cross" extended
-        $total_score = Tracking::get_avg_student_score($my_studen_id, $my_course_id, $a_my_id, api_get_session_id(), false, false);
+        $total_score = Tracking::get_avg_student_score($my_studen_id, $my_course_id, $a_my_id, $session_id, false, false);
     } else {
         //"Left green cross" extended
-        $total_score = Tracking::get_avg_student_score($my_studen_id, $my_course_id, $a_my_id, api_get_session_id(), false, true);
+        $total_score = Tracking::get_avg_student_score($my_studen_id, $my_course_id, $a_my_id, $session_id, false, true);
     }
 } else {
     // Extend all "left green cross"
@@ -870,12 +870,12 @@ if (!empty($a_my_id)) {
         $my_course_id = Database::escape_string($_GET['course']);
         //    var_dump($student_id, $my_course_id );
         if (!empty($student_id) && !empty($my_course_id)) {
-            $total_score = Tracking::get_avg_student_score($student_id, $my_course_id, array(intval($_GET['lp_id'])), api_get_session_id(), false, false);
+            $total_score = Tracking::get_avg_student_score($student_id, $my_course_id, array(intval($_GET['lp_id'])), $session_id, false, false);
         } else {
             $total_score = 0;
         }
     } else {
-        $total_score = Tracking::get_avg_student_score(api_get_user_id(), api_get_course_id(), array(intval($_GET['lp_id'])), api_get_session_id(), false, false);
+        $total_score = Tracking::get_avg_student_score(api_get_user_id(), api_get_course_id(), array(intval($_GET['lp_id'])), $session_id, false, false);
     }
 }
 

+ 20 - 2
main/newscorm/scorm_api.php

@@ -183,8 +183,8 @@ olms.lms_item_lesson_mode = '<?php echo $oItem->get_lesson_mode();?>';
 olms.lms_item_launch_data = '<?php echo addslashes($oItem->get_launch_data());?>';
 olms.lms_item_core_exit = '<?php echo $oItem->get_core_exit();?>';
 olms.lms_course_id = '<?php echo $oLP->get_course_int_id(); ?>';
+olms.lms_session_id = '<?php echo api_get_session_id(); ?>';
 olms.lms_course_code = '<?php echo $oLP->getCourseCode(); ?>';
-
 <?php echo $oLP->get_items_details_as_js('olms.lms_item_types');?>
 
 olms.asset_timer = 0;
@@ -1498,13 +1498,31 @@ function switch_item(current_item, next_item){
  * @return  void
  * @uses lp_ajax_save_item.php through an AJAX call
  */
-function xajax_save_item(lms_lp_id, lms_user_id, lms_view_id, lms_item_id, score, max, min, lesson_status, session_time, suspend_data, lesson_location, interactions, lms_item_core_exit, item_type) {
+function xajax_save_item(
+        lms_lp_id,
+        lms_user_id,
+        lms_view_id,
+        lms_item_id,
+        score,
+        max,
+        min,
+        lesson_status,
+        session_time,
+        suspend_data,
+        lesson_location,
+        interactions,
+        lms_item_core_exit,
+        item_type,
+        session_id,
+        course_id) {
     var params = '';
     params += 'lid='+lms_lp_id+'&uid='+lms_user_id+'&vid='+lms_view_id;
     params += '&iid='+lms_item_id+'&s='+score+'&max='+max+'&min='+min;
     params += '&status='+lesson_status+'&t='+session_time;
     params += '&suspend='+suspend_data+'&loc='+lesson_location;
     params += '&core_exit='+lms_item_core_exit;
+    params += '&session_id='+session_id;
+    params += '&course_id='+course_id;
     //console.info(session_time);
     if (olms.lms_lp_type == 1 || item_type == 'document') {
         logit_lms('xajax_save_item with params:' + params,3);

+ 10 - 1
main/session/index.php

@@ -119,7 +119,16 @@ if (!empty($course_list)) {
             true
         );
         $lp_list        = $list->get_flat_list();
-        $lp_count       = count($lp_list);
+        
+        $lp_count = 0;
+        
+        if(!empty($lp_list)) {
+            foreach ($lp_list as $valLp) {
+                if($valLp['lp_visibility']) {
+                    $lp_count++;
+                }
+            }
+        }
         $course_info    = api_get_course_info($course_data['code']);
         $exercise_count = count(get_all_exercises($course_info, $session_id, true));
 

+ 19 - 21
main/social/message_for_group_form.inc.php

@@ -22,8 +22,6 @@ require_once api_get_path(LIBRARY_PATH).'fileDisplay.lib.php';
 $tok = Security::get_token();
 
 if (isset($_REQUEST['user_friend'])) {
-	$info_user_friend=array();
-	$info_path_friend=array();
  	$userfriend_id = intval($_REQUEST['user_friend']);
  	// panel=1  send message
  	// panel=2  send invitation
@@ -43,24 +41,24 @@ $to_group = '';
 $subject = '';
 $message = '';
 if (!empty($group_id) && $allowed_action) {
-	$group_info = GroupPortalManager::get_group_data($group_id);	
+	$group_info = GroupPortalManager::get_group_data($group_id);
 	$is_member = GroupPortalManager::is_group_member($group_id);
-	
+
     if ($group_info['visibility'] == GROUP_PERMISSION_CLOSED && !$is_member) {
-        api_not_allowed(true);        
+        api_not_allowed(true);
     }
 
 	$to_group   = $group_info['name'];
 	if (!empty($message_id)) {
-		$message_info = MessageManager::get_message_by_id($message_id);		
-		if ($allowed_action == 'reply_message_group') {				
+		$message_info = MessageManager::get_message_by_id($message_id);
+		if ($allowed_action == 'reply_message_group') {
 			$subject  = get_lang('Reply').': '.api_xml_http_response_encode($message_info['title']);
 			//$message  = api_xml_http_response_encode($message_info['content']);
 		} else {
 			$subject  = api_xml_http_response_encode($message_info['title']);
 			$message  = api_xml_http_response_encode($message_info['content']);
-		}	
-	} 	
+		}
+	}
 }
 
 $page_item = !empty($_GET['topics_page_nr'])?intval($_GET['topics_page_nr']):1;
@@ -84,27 +82,27 @@ $page_topic  = !empty($_GET['topics_page_nr'])?intval($_GET['topics_page_nr']):1
 		if (api_get_setting('allow_message_tool')=='true') {
             //normal message
 	   		$user_info = api_get_user_info($userfriend_id);
-	  		//echo api_xml_http_response_encode(get_lang('To')).":&nbsp;&nbsp;".api_xml_http_response_encode($to_group); 
+	  		//echo api_xml_http_response_encode(get_lang('To')).":&nbsp;&nbsp;".api_xml_http_response_encode($to_group);
 	  		$height = 180;
 	  		if ($allowed_action == 'add_message_group') {
 	  		    $height = 140;
 	  		    echo '<span style="color:red">*</span> '.api_xml_http_response_encode(get_lang('Title')).' :<br />';
-	  		    echo '<input id="txt_subject_id" name="title" type="text" style="width:450px;" value="'.$subject.'"><br /><br />';		 	 
+	  		    echo '<input id="txt_subject_id" name="title" type="text" style="width:450px;" value="'.$subject.'"><br /><br />';
 	  		}
-	  		//echo api_xml_http_response_encode(get_lang('Description')).' :<br />';		   		
-	   		
+	  		//echo api_xml_http_response_encode(get_lang('Description')).' :<br />';
+
 			$oFCKeditor = new FCKeditor('content') ;
 			$oFCKeditor->ToolbarSet = 'messages';
 			$oFCKeditor->Width		= '95%';
 			$oFCKeditor->Height		= $height;
 			$oFCKeditor->Value		= $message;
-								
-			$return =	$oFCKeditor->CreateHtml();	
+
+			$return =	$oFCKeditor->CreateHtml();
 			echo $return;
 			if ($allowed_action == 'add_message_group') {
-			    echo '<div><span style="color:red"> * </span>'.get_lang('FieldRequired').'</div>';    
+			    echo '<div><span style="color:red"> * </span>'.get_lang('FieldRequired').'</div>';
 			}
-	   		?>	   				   		
+	   		?>
 	   		<br /><br />
 	   		<?php echo api_xml_http_response_encode(get_lang('AttachmentFiles')); ?> :<br />
 			<span id="filepaths"><div id="filepath_1"><input type="file" name="attach_1" size="20" /></div></span>
@@ -112,19 +110,19 @@ $page_topic  = !empty($_GET['topics_page_nr'])?intval($_GET['topics_page_nr']):1
 				<a href="javascript://" onclick="return add_image_form()">
 			    <?php echo get_lang('AddOneMoreFile') ?></a>
 			</div>
-			(<?php echo api_xml_http_response_encode(sprintf(get_lang('MaximunFileSizeX'), format_file_size(api_get_setting('message_max_upload_filesize')))) ?>)		   				   				   		
+			(<?php echo api_xml_http_response_encode(sprintf(get_lang('MaximunFileSizeX'), format_file_size(api_get_setting('message_max_upload_filesize')))) ?>)
 	   		<br />
 	   		<br />
-	   		
+
 	   		<?php if ($allowed_action == 'add_message_group') { ?>
 	   			<button class="btn save" onclick="if(validate_text_empty(this.form.title.value,'<?php echo get_lang('YouShouldWriteASubject')?>')){return false;}" type="submit" value="<?php echo api_xml_http_response_encode(get_lang('SendMessage')); ?>"><?php echo api_xml_http_response_encode(get_lang('SendMessage')) ?></button>
 	   		<?php } else { ?>
 	   			<button class="btn save" type="submit" value="<?php echo api_xml_http_response_encode(get_lang('SendMessage')); ?>"><?php echo api_xml_http_response_encode(get_lang('SendMessage')) ?></button>
-	   		<?php } ?>	
+	   		<?php } ?>
 	<?php } ?>
 	</dl>
 </td>
 </tr>
 </div>
 </table>
-</form>
+</form>

+ 1 - 1
main/social/profile.php

@@ -767,4 +767,4 @@ $tpl->assign('social_left_content', $social_left_content);
 $tpl->assign('social_right_content', $social_right_content);
 
 $social_layout = $tpl->get_template('layout/social_layout.tpl');
-$tpl->display($social_layout);
+$tpl->display($social_layout);

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor