diff --git a/app/Resources/public/css/base.css b/app/Resources/public/css/base.css index 3d6db9e84a..5bde7b111e 100644 --- a/app/Resources/public/css/base.css +++ b/app/Resources/public/css/base.css @@ -2954,6 +2954,9 @@ a:active{ tr.forum_category_header a { color: #fff; } +.forum-description { + display: inline-block; +} /* **** FORUM **** */ .forum_header { background-color: #EEF; @@ -6032,4 +6035,4 @@ a.sessionView { border: 2px dashed #bbbbbb; font-size: 120%; margin-bottom: 0; -} \ No newline at end of file +} diff --git a/documentation/optimization.html b/documentation/optimization.html index 84fb71dcb7..e0c23cbea0 100755 --- a/documentation/optimization.html +++ b/documentation/optimization.html @@ -56,6 +56,7 @@
  • IGBinary for faster courses backups and better sessions
  • Removing files download permissions check
  • MySQL/MariaDB compression
  • +
  • Increasing PHP limits
  • 1. Using opcaches

    @@ -598,13 +599,42 @@ In 1.10.0, we have added the possibility to enable this compression very This should have an immediate effect on the load average on your server.


    +

    Increasing PHP limits

    +

    + As your use of Chamilo increases and you get above the thousands of users, + you're likely to hit a few milestones set by PHP to avoid hacks. + One of them is PHP5.4's Suhosin extension limit post_max_vars, which was + extended into PHP5.5 and above through the max_input_vars limit. This limit + is usually set to 1000. What does it mean?
    + It means that, when you manipulate any list greater than 1000 items, PHP will + automatically remove anything sent above the first 1000 registers (usually + a little bit less because it needs to add the other input fields of the page). + For example, if subscribing 5 new users to a course where you already have + 1000 users subscribed, you will remain at 1000, although the 1000 will not + necessarily be the 1000 that were there in the first place (they are sent + in order of the elements inside the form, so probably alphabetically, + depending on the page).

    + Increasing this limit to a higher level (say 10,000 instead of 1000) should + be relatively safe, considering your application is normally not open to + the public (and so also open to the evil kind of users). So, in your + php.ini, this limit should now look like this:
    +

    +    max_input_vars = 10000
    +    


    + A number of other limits might also become an issue in the long run, like + memory_limit, post_max_size, etc. We have given reasonnable recommendations + in the installation process for these values, but remember that if you + have a larger portal than anyone else, you probably need to give it more + care than anyone else. +

    +

    Authors


    Don't have time or resources to optimize your Chamilo installation - yourself? Hire an official Chamilo provider and get it sorted out professionally by specialists. + yourself? Hire an official Chamilo provider and get it sorted out professionally by specialists.
    Valid XHTML 1.0 Transitional Valid CSS diff --git a/main/course_info/infocours.php b/main/course_info/infocours.php index de15b3ffb7..270e154f76 100755 --- a/main/course_info/infocours.php +++ b/main/course_info/infocours.php @@ -536,7 +536,7 @@ $form->setDefaults($values); // Validate form if ($form->validate() && is_settings_editable()) { - $updateValues = $form->exportValues(); + $updateValues = $form->getSubmitValues(); // update course picture $picture = $_FILES['picture']; @@ -624,7 +624,6 @@ if ($form->validate() && is_settings_editable()) { ]; Database::update($table_course, $params, ['id = ?' => $courseId]); - // Insert/Updates course_settings table foreach ($courseSettings as $setting) { $value = isset($updateValues[$setting]) ? $updateValues[$setting] : null; diff --git a/main/exercice/admin.php b/main/exercice/admin.php index 828b9df3fb..095a2c2865 100755 --- a/main/exercice/admin.php +++ b/main/exercice/admin.php @@ -363,11 +363,11 @@ $inATest = isset($exerciseId) && $exerciseId > 0; if ($inATest) { echo '
    '; if (isset($_GET['hotspotadmin']) || isset($_GET['newQuestion']) || isset($_GET['myid'])) - echo ''. + echo ''. Display::return_icon('back.png', get_lang('GoBackToQuestionList'),'',ICON_SIZE_MEDIUM).''; if (!isset($_GET['hotspotadmin']) && !isset($_GET['newQuestion']) && !isset($_GET['myid']) && !isset($_GET['editQuestion'])) { - echo ''. + echo ''. Display::return_icon('back.png', get_lang('BackToExercisesList'),'',ICON_SIZE_MEDIUM).''; } echo ''. @@ -375,7 +375,7 @@ if ($inATest) { echo Display::url( Display::return_icon('test_results.png', get_lang('Results'),'',ICON_SIZE_MEDIUM), - api_get_path(WEB_CODE_PATH).'exercice/exercise_report.php?'.api_get_cidReq().'&exerciseId='.$objExercise->id + api_get_path(WEB_CODE_PATH).'exercice/exercise_report.php?'.api_get_cidreq().'&exerciseId='.$objExercise->id ); echo ''. diff --git a/main/exercice/exercise_admin.php b/main/exercice/exercise_admin.php index 562920d6eb..84dc4dd6ee 100755 --- a/main/exercice/exercise_admin.php +++ b/main/exercice/exercise_admin.php @@ -187,7 +187,7 @@ if ($form->validate()) { echo '
    '; if ($objExercise->id != 0) { - echo '' . + echo '' . Display :: return_icon('back.png', get_lang('GoBackToQuestionList'), '', ICON_SIZE_MEDIUM).''; } else { if (!empty($_GET['lp_id']) || !empty($_POST['lp_id'])){ diff --git a/main/exercice/question_pool.php b/main/exercice/question_pool.php index ace1c07a52..190fcf563b 100755 --- a/main/exercice/question_pool.php +++ b/main/exercice/question_pool.php @@ -219,7 +219,7 @@ if (isset($fromExercise) && $fromExercise > 0) { Display::return_icon('back.png', get_lang('GoBackToQuestionList'),'',ICON_SIZE_MEDIUM).''; $titleAdd = get_lang('AddQuestionToTest'); } else { - echo ''. + echo ''. Display::return_icon('back.png', get_lang('BackToExercisesList'),'',ICON_SIZE_MEDIUM).''; echo "".Display::return_icon('add_question.gif', get_lang('NewQu'), '', ICON_SIZE_MEDIUM).""; $titleAdd = get_lang('ManageAllQuestions'); diff --git a/main/exercice/upload_exercise.php b/main/exercice/upload_exercise.php index 11da44db4f..aac9a6d76e 100755 --- a/main/exercice/upload_exercise.php +++ b/main/exercice/upload_exercise.php @@ -58,7 +58,7 @@ lp_upload_quiz_main(); function lp_upload_quiz_actions() { - $return = ''. + $return = ''. Display::return_icon('back.png', get_lang('BackToExercisesList'),'',ICON_SIZE_MEDIUM).''; return $return; } @@ -622,7 +622,7 @@ function lp_upload_quiz_action_handling() { exit; } else { // header('location: exercice.php?' . api_get_cidreq()); - echo ''; + echo ''; } } } diff --git a/main/gradebook/gradebook_display_certificate.php b/main/gradebook/gradebook_display_certificate.php index 802f47ca13..0e58fe362b 100755 --- a/main/gradebook/gradebook_display_certificate.php +++ b/main/gradebook/gradebook_display_certificate.php @@ -181,15 +181,15 @@ if ($filter === 'true') { } echo '
    '; -$url = api_get_self().'?action=generate_all_certificates'.'&'.api_get_cidReq().'&cat_id='.$cat_id.'&filter='.$filterOfficialCode; +$url = api_get_self().'?action=generate_all_certificates'.'&'.api_get_cidreq().'&cat_id='.$cat_id.'&filter='.$filterOfficialCode; echo Display::url(get_lang('GenerateCertificates'), $url, array('class' => 'btn btn-default')); -$url = api_get_self().'?action=delete_all_certificates'.'&'.api_get_cidReq().'&cat_id='.$cat_id.'&filter='.$filterOfficialCode; +$url = api_get_self().'?action=delete_all_certificates'.'&'.api_get_cidreq().'&cat_id='.$cat_id.'&filter='.$filterOfficialCode; echo Display::url(get_lang('DeleteAllCertificates'), $url, array('class' => 'btn btn-default')); $hideCertificateExport = api_get_setting('hide_certificate_export_link'); if (count($certificate_list) > 0 && $hideCertificateExport !== 'true') { - $url = api_get_self().'?action=export_all_certificates'.'&'.api_get_cidReq().'&cat_id='.$cat_id.'&filter='.$filterOfficialCode; + $url = api_get_self().'?action=export_all_certificates'.'&'.api_get_cidreq().'&cat_id='.$cat_id.'&filter='.$filterOfficialCode; echo Display::url(get_lang('ExportAllCertificatesToPDF'), $url, array('class' => 'btn btn-default')); } echo '
    '; diff --git a/main/gradebook/gradebook_display_summary.php b/main/gradebook/gradebook_display_summary.php index c531be4e62..9ad2f63543 100644 --- a/main/gradebook/gradebook_display_summary.php +++ b/main/gradebook/gradebook_display_summary.php @@ -126,7 +126,7 @@ echo Display::page_header(get_lang('GradebookListOfStudentsReports')); echo '
    '; if (count($userList) > 0) { - $url = api_get_self().'?action=export_all&'.api_get_cidReq().'&selectcat='.$cat_id; + $url = api_get_self().'?action=export_all&'.api_get_cidreq().'&selectcat='.$cat_id; echo Display::url(get_lang('ExportAllToPDF'), $url, array('class' => 'btn btn-default')); } echo '
    '; diff --git a/main/group/group_category.php b/main/group/group_category.php index f7dc335439..ae54ebf406 100755 --- a/main/group/group_category.php +++ b/main/group/group_category.php @@ -80,7 +80,7 @@ $(document).ready( function() { }); '; -$interbreadcrumb[] = array('url' => 'group.php?'.api_get_cidReq(), 'name' => get_lang('Groups')); +$interbreadcrumb[] = array('url' => 'group.php?'.api_get_cidreq(), 'name' => get_lang('Groups')); $course_id = api_get_course_int_id(); @@ -91,7 +91,7 @@ if (isset($_GET['id'])) { $form = new FormValidator( 'group_category', 'post', - api_get_self().'?id='.$category['id'].'&'.api_get_cidReq() + api_get_self().'?id='.$category['id'].'&'.api_get_cidreq() ); $form->addElement('hidden', 'id'); } else { diff --git a/main/group/group_edit.php b/main/group/group_edit.php index 9cbd454fd8..99892cbaf2 100755 --- a/main/group/group_edit.php +++ b/main/group/group_edit.php @@ -22,8 +22,8 @@ $group_id = api_get_group_id(); $current_group = GroupManager :: get_group_properties($group_id); $nameTools = get_lang('EditGroup'); -$interbreadcrumb[] = array ('url' => 'group.php?'.api_get_cidReq(), 'name' => get_lang('Groups')); -$interbreadcrumb[] = array ('url' => 'group_space.php?'.api_get_cidReq(), 'name' => $current_group['name']); +$interbreadcrumb[] = array ('url' => 'group.php?'.api_get_cidreq(), 'name' => get_lang('Groups')); +$interbreadcrumb[] = array ('url' => 'group_space.php?'.api_get_cidreq(), 'name' => $current_group['name']); $is_group_member = GroupManager :: is_tutor_of_group(api_get_user_id(), $group_id); diff --git a/main/group/group_overview.php b/main/group/group_overview.php index 683822ca49..3dc8ea9707 100755 --- a/main/group/group_overview.php +++ b/main/group/group_overview.php @@ -64,7 +64,7 @@ if (isset($_GET['action'])) { } /* Header */ -$interbreadcrumb[] = array('url' => 'group.php?'.api_get_cidReq(), 'name' => get_lang('Groups')); +$interbreadcrumb[] = array('url' => 'group.php?'.api_get_cidreq(), 'name' => get_lang('Groups')); if (!isset ($_GET['origin']) || $_GET['origin'] != 'learnpath') { // So we are not in learnpath tool if (!$is_allowed_in_course) { diff --git a/main/group/group_space.php b/main/group/group_space.php index dc05184b86..84b5a27433 100755 --- a/main/group/group_space.php +++ b/main/group/group_space.php @@ -20,7 +20,6 @@ require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php'; require_once api_get_path(SYS_CODE_PATH).'forum/forumconfig.inc.php'; /* MAIN CODE */ - $group_id = api_get_group_id(); $user_id = api_get_user_id(); $current_group = GroupManager::get_group_properties($group_id); @@ -30,7 +29,7 @@ if (empty($current_group)) { $this_section = SECTION_COURSES; $nameTools = get_lang('GroupSpace'); -$interbreadcrumb[] = array('url' => 'group.php?'.api_get_cidReq(), 'name' => get_lang('Groups')); +$interbreadcrumb[] = array('url' => 'group.php?'.api_get_cidreq(), 'name' => get_lang('Groups')); /* Ensure all private groups // Juan Carlos Raña Trabado */ @@ -205,6 +204,17 @@ if (api_is_allowed_to_edit(false, true) || } } + $enabled = api_get_plugin_setting('bbb', 'tool_enable'); + if ($enabled === 'true') { + $bbb = new bbb(); + if ($bbb->hasGroupSupport()) { + $actions_array[] = array( + 'url' => api_get_path(WEB_PLUGIN_PATH)."bbb/start.php?".api_get_cidreq(), + 'content' => Display::return_icon('bbb.png', get_lang('VideoConference'), array(), 32) + ); + } + } + if (!empty($actions_array)) { echo Display::actions($actions_array); } diff --git a/main/group/member_settings.php b/main/group/member_settings.php index c4ce3bf299..5fd9b66ddb 100755 --- a/main/group/member_settings.php +++ b/main/group/member_settings.php @@ -23,7 +23,7 @@ $current_group = GroupManager::get_group_properties($group_id); $nameTools = get_lang('EditGroup'); $interbreadcrumb[] = array('url' => 'group.php', 'name' => get_lang('Groups')); -$interbreadcrumb[] = array('url' => 'group_space.php?'.api_get_cidReq(), 'name' => $current_group['name']); +$interbreadcrumb[] = array('url' => 'group_space.php?'.api_get_cidreq(), 'name' => $current_group['name']); $is_group_member = GroupManager::is_tutor_of_group(api_get_user_id(), $group_id); diff --git a/main/group/settings.php b/main/group/settings.php index b477e0e13f..c6316e0434 100755 --- a/main/group/settings.php +++ b/main/group/settings.php @@ -21,8 +21,8 @@ $group_id = api_get_group_id(); $current_group = GroupManager::get_group_properties($group_id); $nameTools = get_lang('EditGroup'); -$interbreadcrumb[] = array('url' => 'group.php?'.api_get_cidReq(), 'name' => get_lang('Groups')); -$interbreadcrumb[] = array('url' => 'group_space.php?'.api_get_cidReq(), 'name' => $current_group['name']); +$interbreadcrumb[] = array('url' => 'group.php?'.api_get_cidreq(), 'name' => get_lang('Groups')); +$interbreadcrumb[] = array('url' => 'group_space.php?'.api_get_cidreq(), 'name' => $current_group['name']); $is_group_member = GroupManager::is_tutor_of_group(api_get_user_id(), $group_id); if (!api_is_allowed_to_edit(false, true) && !$is_group_member) { diff --git a/main/group/tutor_settings.php b/main/group/tutor_settings.php index ea2e934d18..1dff16728d 100755 --- a/main/group/tutor_settings.php +++ b/main/group/tutor_settings.php @@ -22,8 +22,8 @@ $group_id = api_get_group_id(); $current_group = GroupManager :: get_group_properties($group_id); $nameTools = get_lang('EditGroup'); -$interbreadcrumb[] = array ('url' => 'group.php?'.api_get_cidReq(), 'name' => get_lang('Groups')); -$interbreadcrumb[] = array ('url' => 'group_space.php?'.api_get_cidReq(), 'name' => $current_group['name']); +$interbreadcrumb[] = array ('url' => 'group.php?'.api_get_cidreq(), 'name' => get_lang('Groups')); +$interbreadcrumb[] = array ('url' => 'group_space.php?'.api_get_cidreq(), 'name' => $current_group['name']); $is_group_member = GroupManager :: is_tutor_of_group(api_get_user_id(), $group_id); diff --git a/main/img/icons/32/bbb.png b/main/img/icons/32/bbb.png new file mode 100644 index 0000000000..6810a03756 Binary files /dev/null and b/main/img/icons/32/bbb.png differ diff --git a/main/img/icons/32/bbb_na.png b/main/img/icons/32/bbb_na.png new file mode 100644 index 0000000000..92ed37a5cf Binary files /dev/null and b/main/img/icons/32/bbb_na.png differ diff --git a/main/inc/lib/api.lib.php b/main/inc/lib/api.lib.php index a267c56a4b..617fb87d49 100644 --- a/main/inc/lib/api.lib.php +++ b/main/inc/lib/api.lib.php @@ -4413,21 +4413,6 @@ function api_number_of_plugins($location) { return isset($_plugins[$location]) && is_array($_plugins[$location]) ? count($_plugins[$location]) : 0; } -/** - * Checks to see wether a certain plugin is installed. - * @return boolean true if the plugin is installed, false otherwise. - */ -function api_is_plugin_installed($plugin_list, $plugin_name) { - if (is_array($plugin_list)) { - foreach ($plugin_list as $plugin_location) { - if (array_search($plugin_name, $plugin_location) !== false) { - return true; - } - } - } - return false; -} - /** * Transforms a number of seconds in hh:mm:ss format * @author Julian Prud'homme diff --git a/main/inc/lib/course.lib.php b/main/inc/lib/course.lib.php index d234354517..d5a08ec60a 100755 --- a/main/inc/lib/course.lib.php +++ b/main/inc/lib/course.lib.php @@ -5124,11 +5124,11 @@ class CourseManager $settingList = self::getCourseSettingVariables($appPlugin); if (!in_array($variable, $settingList)) { + return false; } $courseSettingTable = Database::get_course_table(TABLE_COURSE_SETTING); - if (self::hasCourseSetting($variable, $courseId)) { // Update Database::update( diff --git a/main/inc/lib/database.lib.php b/main/inc/lib/database.lib.php index 3037dd11e7..e267efa597 100755 --- a/main/inc/lib/database.lib.php +++ b/main/inc/lib/database.lib.php @@ -686,4 +686,13 @@ class Database { return self::getManager()->getConnection()->getSchemaManager()->tablesExist($table); } + + /** + * @param $table + * @return \Doctrine\DBAL\Schema\Column[] + */ + public static function listTableColumns($table) + { + return self::getManager()->getConnection()->getSchemaManager()->listTableColumns($table); + } } diff --git a/main/inc/lib/internationalization.lib.php b/main/inc/lib/internationalization.lib.php index 342c0898d4..2ec09745a3 100755 --- a/main/inc/lib/internationalization.lib.php +++ b/main/inc/lib/internationalization.lib.php @@ -3,7 +3,7 @@ /** * File: internationalization.lib.php - * Internationalization library for Chamilo 1.8.7 LMS + * Internationalization library for Chamilo 1.x LMS * A library implementing internationalization related functions. * License: GNU General Public License Version 3 (Free Software Foundation)ww * @author Ivan Tcholakov, , 2009, 2010 @@ -275,7 +275,7 @@ function api_get_language_isocode($language = null, $default_code = 'en') } /** - * Gets language isocode column from the language table + * Gets language iso code column from the language table * * @return array An array with the current isocodes * @@ -283,7 +283,8 @@ function api_get_language_isocode($language = null, $default_code = 'en') function api_get_platform_isocodes() { $iso_code = array(); - $sql_result = Database::query("SELECT isocode FROM ".Database::get_main_table(TABLE_MAIN_LANGUAGE)." ORDER BY isocode "); + $sql = "SELECT isocode FROM ".Database::get_main_table(TABLE_MAIN_LANGUAGE)." ORDER BY isocode "; + $sql_result = Database::query($sql); if (Database::num_rows($sql_result)) { while ($row = Database::fetch_array($sql_result)) {; $iso_code[] = trim($row['isocode']); @@ -348,6 +349,7 @@ function api_get_timezones() } $null_option = array('' => ''); $result = array_merge($null_option, $out); + return $result; } @@ -439,8 +441,12 @@ function api_get_utc_datetime($time = null, $return_null_if_invalid_date = false * * @author Guillaume Viguier */ -function api_get_local_time($time = null, $to_timezone = null, $from_timezone = null, $return_null_if_invalid_date = false) -{ +function api_get_local_time( + $time = null, + $to_timezone = null, + $from_timezone = null, + $return_null_if_invalid_date = false +) { // Determining the timezone to be converted from if (is_null($from_timezone)) { $from_timezone = 'UTC'; @@ -471,8 +477,10 @@ function api_get_local_time($time = null, $to_timezone = null, $from_timezone = try { $date = new DateTime($time, new DateTimezone($from_timezone)); $date->setTimezone(new DateTimeZone($to_timezone)); + return $date->format('Y-m-d H:i:s'); } catch (Exception $e) { + return null; } } @@ -480,8 +488,8 @@ function api_get_local_time($time = null, $to_timezone = null, $from_timezone = /** * Converts a string into a timestamp safely (handling timezones), using strtotime * - * @param string String to be converted - * @param string Timezone (if null, the timezone will be determined based + * @param string $time to be converted + * @param string $timezone (if null, the timezone will be determined based * on user preference, or timezone chosen by the admin for the platform) * @return int Timestamp * @@ -494,6 +502,7 @@ function api_strtotime($time, $timezone = null) { } $timestamp = strtotime($time); date_default_timezone_set($system_timezone); + return $timestamp; } diff --git a/main/inc/lib/pear/PEAR.php b/main/inc/lib/pear/PEAR.php index ed0d020ad6..ff9208c9cb 100755 --- a/main/inc/lib/pear/PEAR.php +++ b/main/inc/lib/pear/PEAR.php @@ -558,12 +558,6 @@ class PEAR $ec = 'PEAR_Error'; } - if (intval(PHP_VERSION) < 5) { - // little non-eval hack to fix bug #12147 - include 'PEAR/FixPHP5PEARWarnings.php'; - return $a; - } - if ($skipmsg) { $a = new $ec($code, $mode, $options, $userinfo); } else { diff --git a/main/inc/lib/plugin.class.php b/main/inc/lib/plugin.class.php index 8334d2e04b..0fb3d988dc 100755 --- a/main/inc/lib/plugin.class.php +++ b/main/inc/lib/plugin.class.php @@ -748,4 +748,13 @@ class Plugin } } } + + /** + * @param string $variable + * @return bool + */ + public function validateCourseSetting($variable) + { + return true; + } } diff --git a/main/inc/lib/plugin.lib.php b/main/inc/lib/plugin.lib.php index f1ae10cadb..916d51b83e 100755 --- a/main/inc/lib/plugin.lib.php +++ b/main/inc/lib/plugin.lib.php @@ -372,8 +372,7 @@ class AppPlugin $_template['plugin_info'] = $plugin_info; } - // Setting the plugin info available in the template if exists - + // Setting the plugin info available in the template if exists. $template->assign($plugin_name, $_template); // Loading the Twig template plugin files if exists @@ -548,6 +547,9 @@ class AppPlugin $groups = array(); foreach ($obj->course_settings as $setting) { + if ($obj->validateCourseSetting($setting['name']) === false) { + continue; + } if ($setting['type'] != 'checkbox') { $form->addElement($setting['type'], $setting['name'], $obj->get_lang($setting['name'])); } else { diff --git a/main/newscorm/index.php b/main/newscorm/index.php index 0f74a29f4a..a00496b590 100755 --- a/main/newscorm/index.php +++ b/main/newscorm/index.php @@ -10,4 +10,4 @@ $use_anonymous = true; require_once '../inc/global.inc.php'; -header('location: lp_controller.php?'.api_get_cidReq().'&action=list'); +header('location: lp_controller.php?'.api_get_cidreq().'&action=list'); diff --git a/main/survey/index.php b/main/survey/index.php index b1ca097541..174342ce24 100755 --- a/main/survey/index.php +++ b/main/survey/index.php @@ -1,4 +1,4 @@ 0 && !isset($_POST['submit'])) { $form = new FormValidator( 'publish_form', 'post', - api_get_self().'?survey_id='.$survey_id.'&'.api_get_cidReq() + api_get_self().'?survey_id='.$survey_id.'&'.api_get_cidreq() ); $form->addElement('header', '', $tool_name); diff --git a/plugin/bbb/changelog.md b/plugin/bbb/changelog.md index 6d94acb6b7..edf37fe39f 100644 --- a/plugin/bbb/changelog.md +++ b/plugin/bbb/changelog.md @@ -1,10 +1,19 @@ -version 2.3 - 2015-05-18 +Version 2.4 2016-05 +------------------------ +Changes: + +* Global conference support (Requires to update the plugin settings). +* Course group conference support + * Requires a database change: "ALTER TABLE plugin_bbb_meeting ADD COLUMN group_id INT DEFAULT 0" + * Requires to update the plugin settings. + +Version 2.3 - 2015-05-18 ------------------------ Changes: * Added support for variable voiceBridge to be sent on meeting creation. See https://code.google.com/p/bigbluebutton/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Type%20Status%20Priority%20Milestone%20Owner%20Component%20Summary&groupby=&sort=&id=1186 and https://support.chamilo.org/issues/7669 for details. -* Requires you to "ALTER TABLE plugin_bbb_meeting ADD COLUMN voice_bridge INT NOT NULL DEFAULT 1;" +* Requires a database change: "ALTER TABLE plugin_bbb_meeting ADD COLUMN voice_bridge INT NOT NULL DEFAULT 1;" -version 2.2 - 2014-10-15 +Version 2.2 - 2014-10-15 ------------------------ Changes: * Add a pseudo-random guid to avoid clashing conferences when several Chamilo portals use the same server. If you were using this plugin before, you will have to update the plugin_bbb_meeting table to "alter table plugin_bbb_meeting add column remote_id char(36);". @@ -13,16 +22,15 @@ Changes: * Hide the ID of the meeting (this was an internal ID, useless to the final user). It is still in the HTML source, however * Show number of minutes of the recording (in the recordings list) -version 2.1 +Version 2.1 ----------- Released with: Chamilo LMS 1.9.8 Changes: * now supports sessions (requires you to "alter table plugin_bbb_meeting add column session_id int default 0;") -version 2.0 +Version 2.0 ----------- -(to be described) -version 1.0 +Version 1.0 ----------- Released with: Chamilo LMS 1.9.0 diff --git a/plugin/bbb/lang/english.php b/plugin/bbb/lang/english.php index 5ff171edec..1846866483 100755 --- a/plugin/bbb/lang/english.php +++ b/plugin/bbb/lang/english.php @@ -45,7 +45,11 @@ $strings['tool_enable_help'] = "Choose whether you want to enable the BigBlueBut 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. Students will not be able to launch a conference, only join one. If you don't have a BigBlueButton server, please set one up or ask the Chamilo official providers 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.
    "; $strings['big_blue_button_welcome_message'] = 'Welcome message'; +$strings['enable_global_conference'] = 'Enable global conference'; +$strings['enable_conference_in_course_groups'] = 'Enable conference in course groups'; + $strings['big_blue_button_record_and_store'] = 'Record and store sessions'; +$strings['bbb_enable_conference_in_groups'] = 'Allow conference in groups'; $strings['plugin_tool_bbb'] = 'Video'; diff --git a/plugin/bbb/lang/french.php b/plugin/bbb/lang/french.php index 5f271382e9..38ec3e6edd 100755 --- a/plugin/bbb/lang/french.php +++ b/plugin/bbb/lang/french.php @@ -13,38 +13,28 @@ $strings['CloseMeeting'] = "Fermer la session"; $strings['VideoConferenceXCourseX'] = "Vidéoconférence #%s, cours %s"; $strings['VideoConferenceAddedToTheCalendar'] = "Vidéoconférence ajoutée au calendrier"; $strings['VideoConferenceAddedToTheLinkTool'] = "Vidéoconférence ajoutée comme lien. Vous pouvez éditer et publier le lien sur la page principale du cours depuis l'outil liens."; - $strings['GoToTheVideoConference'] = "Entrer dans la salle de conférence"; - $strings['Records'] = "Enregistrement"; $strings['Meeting'] = "Salle de conférence"; - $strings['ViewRecord'] = "Voir l'enregistrement"; $strings['CopyToLinkTool'] = "Ajouter comme lien du cours"; - $strings['EnterConference'] = "Entrer dans la salle de conférence"; $strings['RecordList'] = "Liste des enregistrements"; $strings['ServerIsNotRunning'] = "Le serveur de vidéoconférence ne fonctionne pas"; $strings['ServerIsNotConfigured'] = "Le serveur de vidéoconférence n'est pas configuré correctement"; - $strings['XUsersOnLine'] = "%s utilisateurs dans la salle"; - $strings['host'] = 'Hôte de BigBlueButton'; $strings['host_help'] = "C'est le nom du serveur où le serveur de vidéoconférence a été habilité. Cela peut être localhost, une adresse IP (du genre 192.168.13.54) ou un nom de domaine (du genre ma.video.com)."; - $strings['salt'] = 'Clef BigBlueButton'; $strings['salt_help'] = "C'est la clef de sécurité de votre serveur BigBlueButton (appelée 'salt' en anglais) qui permet à votre serveur de vérifier l'identité de votre installation de Chamilo et ainsi l'autoriser à se connecter. Veuillez vous référer à la documentation de BigBlueButton pour la localiser, ou utilisez la commande 'bbb-conf --salt' si vous disposez d'un accès en ligne de commande au serveur de vidéoconférence."; - $strings['tool_enable'] = 'Outil de vidéoconférence BigBlueButton activé'; $strings['tool_enable_help'] = "Choisissez si vous souhaitez activer l'outil de vidéoconférence BigBlueButton. Une fois activé, il apparaîtra comme un outil additionnel sur toutes les pages principales de cours, et les enseignants pourront démarrer une conférence à n'importe quel moment. Les étudiants ne pourront pas lancer de nouvelle session de conférence, seulement se joindre à une session existante. Si vous ne disposez pas d'un serveur de vidéoconférence BigBlueButton, veuillez en installer un avant de poursuivre, ou demander un devis à l'un des fournisseurs officiels de Chamilo. BigBlueButton est un outil de logiciel libre (et gratuit), mais son installation pourrait présenter une certaine complexité et demander des compétences qui ne sont peut-être pas à la portée de tous. Vous pouvez l'installer vous-même à partir de la documentation (disponible publiquement) de BigBlueButton, ou recherchez un soutien professionnel. Ce soutien pourrait générer certains coûts (au moins le temps de la personne qui vous assiste dans l'opération). Dans le plus pur esprit du logiciel libre, nous vous fournissons les outils pour simplifier votre travail dans la mesure de nos possibilités, et nous vous recommandons des professionnels (les fournisseurs officiels de Chamilo) pour vous venir en aide au cas où ceux-ci seraient insuffisants.
    "; $strings['big_blue_button_welcome_message'] = 'Message de bienvenue de BigBlueButton'; $strings['big_blue_button_record_and_store'] = 'Enregistrer les sessions de vidéoconférence'; - +$strings['bbb_enable_conference_in_groups'] = 'Permettre la création de vidéoconférence pour les groupes'; $strings['plugin_tool_bbb'] = 'Vidéo'; - $strings['ThereAreNotRecordingsForTheMeetings'] = 'Aucun enregistrement disponible'; $strings['NoRecording'] = "Pas d'enregistrement"; - $strings['ClickToContinue'] = 'Cliquez pour continuer'; diff --git a/plugin/bbb/lang/spanish.php b/plugin/bbb/lang/spanish.php index 1ab48767d1..6cacc628bf 100755 --- a/plugin/bbb/lang/spanish.php +++ b/plugin/bbb/lang/spanish.php @@ -40,7 +40,8 @@ $strings['tool_enable_help'] = "Escoja si desea activar la herramienta de videoc Una vez activada, se mostrará como una herramienta adicional en todas las páginas principales de cursos, y los profesores podrán iniciar una conferencia en cualquier momento. Los estudiantes no podrían lanzar una conferencia, solo juntarse a una existente. Si no tiene un servidor de videoconferencia BigBlueButton, por favor configure uno antes de seguir, o pida una cotización a uno de los proveedores oficiales de Chamilo. BigBlueButton es una herramienta de software libre (y gratuita), pero su instalación requiere de competencias que quizás no sean inmediatamente disponibles para todos. Puede instalarla usted mismo o buscar ayuda profesional. Esta ayuda podría no obstante generar algunos costos (por lo menos el tiempo de la persona quien lo ayude). En el puro espíritu del software libre, le ofrecemos las herramientas para hacer su trabajo más simple, en la medida de nuestras posibilidades, y le recomendamos profesionales (los proveedores oficiales de Chamilo) para ayudarlo en caso esto fuera demasiado complicado.
    "; $strings['big_blue_button_welcome_message'] = 'Mensaje de bienvenida de BigBlueButton'; -$strings['big_blue_button_record_and_store'] = 'Grabar las sesiones de videoconferencia'; +$strings['big_blue_button_record_and_store'] = 'Grabar las sesiones de videoconferencia.'; +$strings['bbb_enable_conference_in_groups'] = 'Activar la creación de videoconferencia en los grupos.'; $strings['plugin_tool_bbb'] = 'Video'; diff --git a/plugin/bbb/lib/bbb.lib.php b/plugin/bbb/lib/bbb.lib.php index 418fd58721..c85f09f5d9 100755 --- a/plugin/bbb/lib/bbb.lib.php +++ b/plugin/bbb/lib/bbb.lib.php @@ -20,19 +20,21 @@ class bbb public $url; public $salt; public $api; - public $user_complete_name = ''; + public $userCompleteName = ''; public $protocol = 'http://'; public $debug = false; public $logoutUrl = ''; public $pluginEnabled = false; public $enableGlobalConference = false; public $isGlobalConference = false; + public $groupSupport = false; /** * Constructor (generates a connection to the API and the Chamilo settings * required for the connection to the video conference server) * @param string $host * @param string $salt + * @param bool $isGlobalConference */ public function __construct($host = '', $salt = '', $isGlobalConference = false) { @@ -49,9 +51,22 @@ class bbb $this->enableGlobalConference = $plugin->get('enable_global_conference'); $this->isGlobalConference = (bool) $isGlobalConference; + $columns = Database::listTableColumns($this->table); + $this->groupSupport = isset($columns['group_id']) ? true : false; + + if ($this->groupSupport) { + $this->groupSupport = (bool) $plugin->get('enable_conference_in_course_groups'); + if ($this->groupSupport) { + $courseInfo = api_get_course_info(); + if ($courseInfo) { + $this->groupSupport = api_get_course_setting('bbb_enable_conference_in_groups') === '1'; + } + } + } + if ($bbbPlugin === true) { $userInfo = api_get_user_info(); - $this->user_complete_name = $userInfo['complete_name']; + $this->userCompleteName = $userInfo['complete_name']; $this->salt = $bbb_salt; $info = parse_url($bbb_host); $this->url = $bbb_host.'/bigbluebutton/'; @@ -90,6 +105,14 @@ class bbb return (bool) $this->isGlobalConference; } + /** + * @return bool + */ + public function hasGroupSupport() + { + return $this->groupSupport; + } + /** * Checks whether a user is teacher in the current course * @return bool True if the user can be considered a teacher in this course, false otherwise @@ -129,7 +152,8 @@ class bbb # a user joins. If after this period, a user hasn't joined, the meeting is # removed from memory. defaultMeetingCreateJoinDuration=5 - * + * + * @return mixed */ public function createMeeting($params) { @@ -137,6 +161,10 @@ class bbb $params['c_id'] = api_get_course_int_id(); $params['session_id'] = api_get_session_id(); + if ($this->hasGroupSupport()) { + $params['group_id'] = api_get_group_id(); + } + $params['attendee_pw'] = isset($params['moderator_pw']) ? $params['moderator_pw'] : $courseCode; $attendeePassword = $params['attendee_pw']; $params['moderator_pw'] = isset($params['moderator_pw']) ? $params['moderator_pw'] : $this->getModMeetingPassword(); @@ -166,7 +194,7 @@ class bbb error_log("create_meeting: $id "); } - $meetingName = isset($params['meeting_name']) ? $params['meeting_name'] : api_get_course_id().'-'.api_get_session_id(); + $meetingName = isset($params['meeting_name']) ? $params['meeting_name'] : $this->getCurrentVideoConferenceName(); $welcomeMessage = isset($params['welcome_msg']) ? $params['welcome_msg'] : null; $record = isset($params['record']) && $params['record'] ? 'true' : 'false'; $duration = isset($params['duration']) ? intval($params['duration']) : 0; @@ -235,17 +263,31 @@ class bbb $courseId = api_get_course_int_id(); $sessionId = api_get_session_id(); + $conditions = array( + 'where' => array( + 'c_id = ? AND session_id = ? AND meeting_name = ? AND status = 1 ' => + array($courseId, $sessionId, $meetingName) + ) + ); + + if ($this->hasGroupSupport()) { + $groupId = api_get_group_id(); + $conditions = array( + 'where' => array( + 'c_id = ? AND session_id = ? AND meeting_name = ? AND group_id = ? AND status = 1 ' => + array($courseId, $sessionId, $meetingName, $groupId) + ) + ); + } + $meetingData = Database::select( '*', $this->table, - array( - 'where' => array( - 'c_id = ? AND session_id = ? AND meeting_name = ? AND status = 1 ' => - array($courseId, $sessionId, $meetingName) - ) - ), + $conditions, 'first' ); + + if ($this->debug) { error_log("meeting_exists ".print_r($meetingData, 1)); } @@ -345,7 +387,7 @@ class bbb if ($meetingInfoExists) { $joinParams = array( 'meetingId' => $meetingData['remote_id'], // -- REQUIRED - A unique id for the meeting - 'username' => $this->user_complete_name, //-- REQUIRED - The name that will display for the user in the meeting + 'username' => $this->userCompleteName, //-- REQUIRED - The name that will display for the user in the meeting 'password' => $pass, //-- REQUIRED - The attendee or moderator password, depending on what's passed here //'createTime' => api_get_utc_datetime(), //-- OPTIONAL - string. Leave blank ('') unless you set this correctly. 'userID' => api_get_user_id(), //-- OPTIONAL - string @@ -396,17 +438,32 @@ class bbb public function getMeetings() { $pass = $this->getUserMeetingPassword(); + $courseId = api_get_course_int_id(); + $sessionId = api_get_session_id(); + + $conditions = array( + 'where' => array( + 'c_id = ? AND session_id = ? ' => array( + $courseId, + $sessionId, + ), + ), + ); + + if ($this->hasGroupSupport()) { + $groupId = api_get_group_id(); + $conditions = array( + 'where' => array( + 'c_id = ? AND session_id = ? AND group_id = ? ' => + array($courseId, $sessionId, $groupId) + ) + ); + } + $meetingList = Database::select( '*', $this->table, - array( - 'where' => array( - 'c_id = ? AND session_id = ? ' => array( - api_get_course_int_id(), - api_get_session_id(), - ), - ), - ) + $conditions ); $isGlobal = $this->isGlobalConference(); $newMeetingList = array(); @@ -614,7 +671,7 @@ class bbb if ($meetingDB['status'] == 1) { $joinParams = array( 'meetingId' => $meetingDB['remote_id'], //-- REQUIRED - A unique id for the meeting - 'username' => $this->user_complete_name, //-- REQUIRED - The name that will display for the user in the meeting + 'username' => $this->userCompleteName, //-- REQUIRED - The name that will display for the user in the meeting 'password' => $pass, //-- REQUIRED - The attendee or moderator password, depending on what's passed here 'createTime' => '', //-- OPTIONAL - string. Leave blank ('') unless you set this correctly. 'userID' => '', // -- OPTIONAL - string @@ -732,12 +789,35 @@ class bbb { $courseId = api_get_course_int_id(); $sessionId = api_get_session_id(); + + $conditions = array( + 'where' => array( + 'c_id = ? AND session_id = ? AND status = 1 ' => array( + $courseId, + $sessionId, + ), + ), + ); + + if ($this->hasGroupSupport()) { + $groupId = api_get_group_id(); + $conditions = array( + 'where' => array( + 'c_id = ? AND session_id = ? AND group_id = ? AND status = 1 ' => array( + $courseId, + $sessionId, + $groupId + ), + ), + ); + } $meetingData = Database::select( '*', $this->table, - array('where' => array('c_id = ? AND session_id = ? AND status = 1 ' => array($courseId, $sessionId))), + $conditions, 'first' ); + if (empty($meetingData)) { return 0; } @@ -850,7 +930,7 @@ class bbb } /** - * Checks if the videoconference server is running. + * Checks if the video conference server is running. * Function currently disabled (always returns 1) * @return bool True if server is running, false otherwise * @assert () === false @@ -890,12 +970,6 @@ class bbb header("Location: $url"); exit; } - - // js - /*echo ''; - exit;*/ } /** @@ -926,6 +1000,11 @@ class bbb return 'url_'.api_get_current_access_url_id(); } + if ($this->hasGroupSupport()) { + + return api_get_course_id().'-'.api_get_session_id().'-'.api_get_group_id(); + } + return api_get_course_id().'-'.api_get_session_id(); } diff --git a/plugin/bbb/lib/bbb_plugin.class.php b/plugin/bbb/lib/bbb_plugin.class.php index 8ad9fb41fd..dc83bf43f9 100755 --- a/plugin/bbb/lib/bbb_plugin.class.php +++ b/plugin/bbb/lib/bbb_plugin.class.php @@ -17,12 +17,16 @@ class BBBPlugin extends Plugin public $isCoursePlugin = true; // When creating a new course this settings are added to the course - public $course_settings = array( - array( + public $course_settings = [ + [ 'name' => 'big_blue_button_record_and_store', - 'type' => 'checkbox' - ) - ); + 'type' => 'checkbox', + ], + [ + 'name' => 'bbb_enable_conference_in_groups', + 'type' => 'checkbox', + ] + ]; /** * BBBPlugin constructor. @@ -30,17 +34,36 @@ class BBBPlugin extends Plugin protected function __construct() { parent::__construct( - '2.3', + '2.4', 'Julio Montoya, Yannick Warnier', [ 'tool_enable' => 'boolean', 'host' => 'text', 'salt' => 'text', 'enable_global_conference' => 'boolean', + 'enable_conference_in_course_groups' => 'boolean', ] ); } + /** + * @param string $variable + * @return bool + */ + public function validateCourseSetting($variable) + { + if ($variable == 'bbb_enable_conference_in_groups') { + if ($this->get('enable_conference_in_course_groups') === 'true') { + + return true; + } else { + return false; + } + } + + return true; + } + /** * @return BBBPlugin|null */ @@ -59,6 +82,7 @@ class BBBPlugin extends Plugin $sql = "CREATE TABLE IF NOT EXISTS $table ( id INT unsigned NOT NULL auto_increment PRIMARY KEY, c_id INT unsigned NOT NULL DEFAULT 0, + group_id INT unsigned NOT NULL DEFAULT 0, meeting_name VARCHAR(255) NOT NULL DEFAULT '', attendee_pw VARCHAR(255) NOT NULL DEFAULT '', moderator_pw VARCHAR(255) NOT NULL DEFAULT '', @@ -75,7 +99,7 @@ class BBBPlugin extends Plugin )"; Database::query($sql); - //Installing course settings + // Installing course settings $this->install_course_fields_in_all_courses(); } @@ -88,25 +112,26 @@ class BBBPlugin extends Plugin $t_options = Database::get_main_table(TABLE_MAIN_SETTINGS_OPTIONS); $t_tool = Database::get_course_table(TABLE_TOOL_LIST); - // New settings - $sql = "DELETE FROM $t_settings WHERE variable = 'bbb_tool_enable'"; - Database::query($sql); - $sql = "DELETE FROM $t_settings WHERE variable = 'bbb_salt'"; - Database::query($sql); - $sql = "DELETE FROM $t_settings WHERE variable = 'bbb_host'"; - Database::query($sql); + $variables = [ + 'bbb_salt', + 'bbb_host', + 'bbb_tool_enable', + 'enable_global_conference', + 'enable_conference_in_course_groups', + 'bbb_plugin', + 'bbb_plugin_host', + 'bbb_plugin_salt' + ]; + + foreach ($variables as $variable) { + $sql = "DELETE FROM $t_settings WHERE variable = '$variable'"; + Database::query($sql); + } - //Old settings deleting just in case - $sql = "DELETE FROM $t_settings WHERE variable = 'bbb_plugin'"; - Database::query($sql); $sql = "DELETE FROM $t_options WHERE variable = 'bbb_plugin'"; Database::query($sql); - $sql = "DELETE FROM $t_settings WHERE variable = 'bbb_plugin_host'"; - Database::query($sql); - $sql = "DELETE FROM $t_settings WHERE variable = 'bbb_plugin_salt'"; - Database::query($sql); - //hack to get rid of Database::query warning (please add c_id...) + // hack to get rid of Database::query warning (please add c_id...) $sql = "DELETE FROM $t_tool WHERE name = 'bbb' AND c_id != 0"; Database::query($sql); @@ -114,7 +139,7 @@ class BBBPlugin extends Plugin $sql = "DROP TABLE IF EXISTS $t"; Database::query($sql); - //Deleting course settings + // Deleting course settings $this->uninstall_course_fields_in_all_courses($this->course_settings); } } diff --git a/plugin/bbb/listing.php b/plugin/bbb/listing.php index 195e7d9854..b059157a93 100755 --- a/plugin/bbb/listing.php +++ b/plugin/bbb/listing.php @@ -105,6 +105,7 @@ if ($conferenceManager) { break; } } + $meetings = $bbb->getMeetings(); if (!empty($meetings)) { diff --git a/tests/main/inc/lib/main_api.lib.test.php b/tests/main/inc/lib/main_api.lib.test.php index e83b1856cf..bc625924ec 100755 --- a/tests/main/inc/lib/main_api.lib.test.php +++ b/tests/main/inc/lib/main_api.lib.test.php @@ -488,13 +488,6 @@ class TestMainApi extends UnitTestCase { $this->assertTrue($_plugins[$location]); } - function testApiIsPluginInstalled(){ - $plugin_name = false; - $plugin_list = true; - $res = api_is_plugin_installed($plugin_list, $plugin_name); - $this->assertTrue(is_bool($res)); - } - function testApiTimeToHms(){ $seconds = -1; ob_start();