Update from 1.11.x

pull/3006/head
Julio Montoya 5 years ago
parent 2361e4f8b0
commit f2b7e48165
  1. 2
      plugin/advanced_subscription/views/open_session.tpl
  2. 26
      plugin/azure_active_directory/README.md
  3. 28
      plugin/azure_active_directory/index.php
  4. 8
      plugin/azure_active_directory/install.php
  5. 20
      plugin/azure_active_directory/lang/english.php
  6. 44
      plugin/azure_active_directory/layout/login_form.tpl
  7. 35
      plugin/azure_active_directory/login.php
  8. 107
      plugin/azure_active_directory/src/AzureActiveDirectory.php
  9. 113
      plugin/azure_active_directory/src/callback.php
  10. 11
      plugin/azure_active_directory/view/block.tpl
  11. 19
      plugin/azure_active_directory/view/login.tpl
  12. 6
      plugin/bbb/README.md
  13. 2
      plugin/bbb/admin.php
  14. 2
      plugin/bbb/lang/english.php
  15. 1
      plugin/bbb/lang/french.php
  16. 332
      plugin/bbb/lib/bbb.lib.php
  17. 106
      plugin/bbb/lib/bbb_api.php
  18. 22
      plugin/bbb/lib/bbb_plugin.class.php
  19. 101
      plugin/bbb/listing.php
  20. 97
      plugin/bbb/listing.tpl
  21. 5
      plugin/bbb/start.php
  22. 48
      plugin/bbb/view/admin.tpl
  23. 173
      plugin/bbb/view/listing.tpl
  24. 10
      plugin/buycourses/src/index.buycourses.php
  25. 1
      plugin/customcertificate/lang/english.php
  26. 1
      plugin/customcertificate/lang/spanish.php
  27. 33
      plugin/customcertificate/src/index.php
  28. 14
      plugin/customcertificate/src/print_certificate.php
  29. 2
      plugin/grading_electronic/generate.php
  30. 4
      plugin/redirection/README.md
  31. 14
      plugin/redirection/RedirectionPlugin.php
  32. 33
      plugin/studentfollowup/StudentFollowUpPlugin.php
  33. 3
      plugin/studentfollowup/lang/dutch.php
  34. 108
      plugin/studentfollowup/my_students.php
  35. 4
      plugin/studentfollowup/view/my_students.html.twig
  36. 12
      plugin/studentfollowup/view/post.html.twig
  37. 5
      plugin/whispeakauth/README.md
  38. 2
      plugin/whispeakauth/view/record_audio.html.twig

@ -1,7 +1,7 @@
<link href="{{ _p.web_plugin }}advanced_subscription/views/css/style.css" rel="stylesheet" type="text/css">
<script>
$(document).on('ready', function () {
$(function () {
$('#asp-close-window').on('click', function (e) {
e.preventDefault();

@ -1,17 +1,21 @@
# The Azure Active Directory Plugin
Allow authentication with Microsoft's Azure Active Directory
Allow authentication (with OAuth2) with Microsoft's Azure Active Directory.
> This plugin use the [`thenetworg/oauth2-azure`](https://github.com/TheNetworg/oauth2-azure) package.
### To configure Azure Active Directory
* [Create an Azure AD B2C tenant](https://azure.microsoft.com/en-us/documentation/articles/active-directory-b2c-get-started/)
* [Register your application](https://azure.microsoft.com/en-us/documentation/articles/active-directory-b2c-app-registration/)
* [Configure Facebook, Google+, Microsoft account, Amazon, and LinkedIn accounts for use in your consumer-facing applications](https://azure.microsoft.com/en-us/documentation/articles/active-directory-b2c-overview/#how-to-articles)
* Create and configure an application.
* In _Authentication_ section, set an _Reply URL_ with `https://{CHAMILO_URL}/plugin/azure_active_directory/src/callback.php`.
* In _Certificates & secrets_, create a secret string (or application password). And keep copied.
### To configure this plugin
* Enable
* Application ID: Enter the Application Id assinged to your app by the Azure portal, e.g. 580e250c-8f26-49d0-bee8-1c078add1609
* Tenant: Enter the name of your B2C directory, e.g. contoso.onmicrosoft.com
* Sign up policy: Enter your sign up policy name, e.g. b2c_1_sign_up
* Sign in policy: Enter your sign in policy name, e.g. b2c_1_sign_in
* Block name: (Optional) The name to show above the buttons
* _Enable_
* _Application ID_: Enter the Application Id assinged to your app by the Azure portal.
* _Application secret_: Enter the client secret created.
* _Block name_: (Optional) The name to show above the login button.
* _Force logout button_: (Optional) Add a button to force logout from Azure.
* _Management login_: (Optional) Disable the chamilo login and enable an alternative login page for users.
You will need copy the `/plugin/azure_active_directory/layout/login_form.tpl` file to `/main/template/overrides/layout/` directory.
* _Name for the management login_: A name for the management login. By default is "Management Login".
And assign a region. Preferably `login_bottom`
And assign a region. Preferably `login_bottom`.

@ -10,23 +10,25 @@
$activeDirectoryPlugin = AzureActiveDirectory::create();
if ($activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_ENABLE) === 'true') {
$signUp = $activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_SIGNIN_POLICY);
$signIn = $activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_SIGNUP_POLICY);
$signUnified = $activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_SIGNUNIFIED_POLICY);
$_template['block_title'] = $activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_BLOCK_NAME);
if ($signUp) {
$_template['signup_url'] = $activeDirectoryPlugin->getUrl(AzureActiveDirectory::URL_TYPE_SIGNUP);
}
$_template['signin_url'] = $activeDirectoryPlugin->getUrl(AzureActiveDirectory::URL_TYPE_AUTHORIZE);
if ($signIn) {
$_template['signin_url'] = $activeDirectoryPlugin->getUrl(AzureActiveDirectory::URL_TYPE_SIGNIN);
if ('true' === $activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_FORCE_LOGOUT_BUTTON)) {
$_template['signout_url'] = $activeDirectoryPlugin->getUrl(AzureActiveDirectory::URL_TYPE_LOGOUT);
}
if ($signUnified) {
$_template['signunified_url'] = $activeDirectoryPlugin->getUrl(AzureActiveDirectory::URL_TYPE_SIGNUNIFIED);
}
$managementLoginEnabled = 'true' === $activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_MANAGEMENT_LOGIN_ENABLE);
$_template['management_login_enabled'] = $managementLoginEnabled;
$_template['signout_url'] = $activeDirectoryPlugin->getUrl(AzureActiveDirectory::URL_TYPE_SIGNOUT);
if ($managementLoginEnabled) {
$managementLoginName = $activeDirectoryPlugin->get(AzureActiveDirectory::SETTING_MANAGEMENT_LOGIN_NAME);
if (empty($managementLoginName)) {
$managementLoginName = $activeDirectoryPlugin->get_lang('ManagementLogin');
}
$_template['management_login_name'] = $managementLoginName;
}
}

@ -0,0 +1,8 @@
<?php
/* For licensing terms, see /license.txt */
if (!api_is_platform_admin()) {
die('You must have admin permissions to install plugins');
}
AzureActiveDirectory::create()->install();

@ -13,13 +13,15 @@ $strings['plugin_comment'] = 'Allow authentication with Microsoft\'s Azure Activ
$strings['enable'] = 'Enable';
$strings['app_id'] = 'Application ID';
$strings['app_id_help'] = 'Enter the Application Id assinged to your app by the Azure portal, e.g. 580e250c-8f26-49d0-bee8-1c078add1609';
$strings['tenant'] = 'Tenant';
$strings['tenant_help'] = 'Enter the name of your B2C directory, e.g. contoso.onmicrosoft.com';
$strings['signup_policy'] = 'Sign up policy';
$strings['signup_policy_help'] = 'Enter your sign up policy name, e.g.g b2c_1_sign_up';
$strings['signin_policy'] = 'Sign in policy';
$strings['signin_policy_help'] = 'Enter your sign in policy name, e.g. b2c_1_sign_in';
$strings['signunified_policy'] = 'Unified sign in and sign up policy';
$strings['signunified_policy_help'] = 'Enter your name of unified sign in and sign up policy, e.g. b2c_1_sign_unified';
$strings['app_secret'] = 'Application secret';
$strings['force_logout'] = 'Force logout button';
$strings['force_logout_help'] = 'Show a button to force logout session from Azure.';
$strings['block_name'] = 'Block name';
$strings['SignUpAndSignIn'] = 'Sign up and sign in';
$strings['management_login_enable'] = 'Management login';
$strings['management_login_enable_help'] = 'Disable the chamilo login and enable an alternative login page for users.<br>'
.'You will need copy the <code>/plugin/azure_active_directory/layout/login_form.tpl</code> file to <code>/main/template/overrides/layout/</code> directory.';
$strings['management_login_name'] = 'Name for the management login';
$strings['management_login_name_help'] = 'By default is "Management Login".';
$strings['OrganisationEmail'] = 'Organisation e-mail';
$strings['AzureId'] = 'Azure ID (mailNickname)';
$strings['ManagementLogin'] = 'Management Login';

@ -0,0 +1,44 @@
{% if _u.logged == 0 %}
{% if login_form %}
<div id="login-block" class="panel panel-default">
<div class="panel-body">
{{ login_language_form }}
{% if plugin_login_top is not null %}
<div id="plugin_login_top">
{{ plugin_login_top }}
</div>
{% endif %}
{{ login_failed }}
{% set azure_plugin_enabled = 'azure_active_directory'|api_get_plugin_setting('enable') %}
{% set azure_plugin_manage_login = 'azure_active_directory'|api_get_plugin_setting('manage_login_enable') %}
{% if 'false' == azure_plugin_enabled or 'false' == azure_plugin_manage_login %}
{{ login_form }}
{% if "allow_lostpassword" | api_get_setting == 'true' or "allow_registration"|api_get_setting == 'true' %}
<ul class="nav nav-pills nav-stacked">
{% if "allow_registration"|api_get_setting != 'false' %}
<li><a href="{{ _p.web_main }}auth/inscription.php"> {{ 'SignUp'|get_lang }} </a></li>
{% endif %}
{% if "allow_lostpassword"|api_get_setting == 'true' %}
<li>
<a href="{{ _p.web_main }}auth/lostPassword.php">{{ 'LostPassword'|get_lang }}</a>
</li>
{% endif %}
</ul>
{% endif %}
{% endif %}
{% if plugin_login_bottom is not null %}
<div id="plugin_login_bottom">
{{ plugin_login_bottom }}
</div>
{% endif %}
</div>
</div>
{% endif %}
{% endif %}

@ -0,0 +1,35 @@
<?php
/* For license terms, see /license.txt */
require __DIR__.'/../../main/inc/global.inc.php';
$plugin = AzureActiveDirectory::create();
$pluginEnabled = $plugin->get(AzureActiveDirectory::SETTING_ENABLE);
$managementLoginEnabled = $plugin->get(AzureActiveDirectory::SETTING_MANAGEMENT_LOGIN_ENABLE);
if ('true' !== $pluginEnabled || 'true' !== $managementLoginEnabled) {
header('Location: '.api_get_path(WEB_PATH));
exit;
}
$userId = api_get_user_id();
if (!($userId) || api_is_anonymous($userId)) {
$managementLoginName = $plugin->get(AzureActiveDirectory::SETTING_MANAGEMENT_LOGIN_NAME);
if (empty($managementLoginName)) {
$managementLoginName = $plugin->get_lang('ManagementLogin');
}
$template = new Template($managementLoginName);
// Only display if the user isn't logged in.
$template->assign('login_language_form', api_display_language_form(true, true));
$template->assign('login_form', $template->displayLoginForm());
$content = $template->fetch('azure_active_directory/view/login.tpl');
$template->assign('content', $content);
$template->display_one_col_template();
}

@ -1,6 +1,8 @@
<?php
/* For license terms, see /license.txt */
use TheNetworg\OAuth2\Client\Provider\Azure;
/**
* AzureActiveDirectory plugin class.
*
@ -12,15 +14,17 @@ class AzureActiveDirectory extends Plugin
{
const SETTING_ENABLE = 'enable';
const SETTING_APP_ID = 'app_id';
const SETTING_TENANT = 'tenant';
const SETTING_SIGNUP_POLICY = 'signup_policy';
const SETTING_SIGNIN_POLICY = 'signin_policy';
const SETTING_SIGNUNIFIED_POLICY = 'signunified_policy';
const SETTING_APP_SECRET = 'app_secret';
const SETTING_BLOCK_NAME = 'block_name';
const URL_TYPE_SIGNUP = 'sign-up';
const URL_TYPE_SIGNIN = 'sign-in';
const URL_TYPE_SIGNUNIFIED = 'sign-unified';
const URL_TYPE_SIGNOUT = 'sign-out';
const SETTING_FORCE_LOGOUT_BUTTON = 'force_logout';
const SETTING_MANAGEMENT_LOGIN_ENABLE = 'management_login_enable';
const SETTING_MANAGEMENT_LOGIN_NAME = 'management_login_name';
const URL_TYPE_AUTHORIZE = 'login';
const URL_TYPE_LOGOUT = 'logout';
const EXTRA_FIELD_ORGANISATION_EMAIL = 'organisationemail';
const EXTRA_FIELD_AZURE_ID = 'azure_id';
/**
* AzureActiveDirectory constructor.
@ -30,14 +34,14 @@ class AzureActiveDirectory extends Plugin
$settings = [
self::SETTING_ENABLE => 'boolean',
self::SETTING_APP_ID => 'text',
self::SETTING_TENANT => 'text',
self::SETTING_SIGNUP_POLICY => 'text',
self::SETTING_SIGNIN_POLICY => 'text',
self::SETTING_SIGNUNIFIED_POLICY => 'text',
self::SETTING_APP_SECRET => 'text',
self::SETTING_BLOCK_NAME => 'text',
self::SETTING_FORCE_LOGOUT_BUTTON => 'boolean',
self::SETTING_MANAGEMENT_LOGIN_ENABLE => 'boolean',
self::SETTING_MANAGEMENT_LOGIN_NAME => 'text',
];
parent::__construct('1.1', 'Angel Fernando Quiroz Campos', $settings);
parent::__construct('2.1', 'Angel Fernando Quiroz Campos', $settings);
}
/**
@ -63,52 +67,53 @@ class AzureActiveDirectory extends Plugin
}
/**
* @param $urlType Type of URL to generate
* @return Azure
*/
public function getProvider()
{
$provider = new Azure([
'clientId' => $this->get(self::SETTING_APP_ID),
'clientSecret' => $this->get(self::SETTING_APP_SECRET),
'redirectUri' => api_get_path(WEB_PLUGIN_PATH).'azure_active_directory/src/callback.php',
]);
return $provider;
}
/**
* @param string $urlType Type of URL to generate
*
* @return string
*/
public function getUrl($urlType)
{
$settingsInfo = $this->get_settings();
$settings = [];
if (self::URL_TYPE_LOGOUT === $urlType) {
$provider = $this->getProvider();
foreach ($settingsInfo as $settingInfo) {
$variable = str_replace($this->get_name().'_', '', $settingInfo['variable']);
$settings[$variable] = $settingInfo['selected_value'];
return $provider->getLogoutUrl(
api_get_path(WEB_PATH)
);
}
$url = "https://login.microsoftonline.com/{$settings[self::SETTING_TENANT]}/oauth2/v2.0/";
$callback = api_get_path(WEB_PLUGIN_PATH).$this->get_name().'/src/callback.php';
if ($urlType === self::URL_TYPE_SIGNOUT) {
$action = 'logout';
$urlParams = [
'p' => $settings[self::SETTING_SIGNIN_POLICY],
'post_logout_redirect_uri' => $callback,
];
} else {
$action = 'authorize';
$policy = $settings[self::SETTING_SIGNUP_POLICY];
if ($urlType === self::URL_TYPE_SIGNIN) {
$policy = $settings[self::SETTING_SIGNIN_POLICY];
} elseif ($urlType === self::URL_TYPE_SIGNUNIFIED) {
$policy = $settings[self::SETTING_SIGNUNIFIED_POLICY];
}
$urlParams = [
'client_id' => $settings[self::SETTING_APP_ID],
'response_type' => 'id_token',
'redirect_uri' => $callback,
'scope' => 'openid',
'response_mode' => 'form_post',
'state' => time(),
'nonce' => time(),
'p' => $policy,
];
}
return api_get_path(WEB_PLUGIN_PATH).$this->get_name().'/src/callback.php';
}
return $url.$action.'?'.http_build_query($urlParams);
/**
* Create extra fields for user when installing.
*/
public function install()
{
UserManager::create_extra_field(
self::EXTRA_FIELD_ORGANISATION_EMAIL,
ExtraField::FIELD_TYPE_TEXT,
$this->get_lang('OrganisationEmail'),
''
);
UserManager::create_extra_field(
self::EXTRA_FIELD_AZURE_ID,
ExtraField::FIELD_TYPE_TEXT,
$this->get_lang('AzureId'),
''
);
}
}

@ -2,56 +2,79 @@
/* For license terms, see /license.txt */
require __DIR__.'/../../../main/inc/global.inc.php';
require_once __DIR__.'/../../../main/auth/external_login/functions.inc.php';
if (isset($_POST['error']) || empty($_REQUEST)) {
header('Location: '.api_get_path(WEB_PATH).'index.php?logout=logout');
$plugin = AzureActiveDirectory::create();
$provider = $plugin->getProvider();
if (!isset($_GET['code'])) {
// If we don't have an authorization code then get one
$authUrl = $provider->getAuthorizationUrl();
ChamiloSession::write('oauth2state', $provider->getState());
header('Location: '.$authUrl);
exit;
}
// Check given state against previously stored one to mitigate CSRF attack
if (empty($_GET['state']) || ($_GET['state'] !== ChamiloSession::read('oauth2state'))) {
ChamiloSession::erase('oauth2state');
exit;
}
// Try to get an access token (using the authorization code grant)
$token = $provider->getAccessToken('authorization_code', [
'code' => $_GET['code'],
'resource' => 'https://graph.windows.net',
]);
$me = null;
try {
$me = $provider->get("me", $token);
} catch (Exception $e) {
exit;
}
list($jwtHeader, $jwtPayload, $jwtSignature) = explode('.', $_REQUEST['id_token']);
$jwtHeader = json_decode(
base64_decode($jwtHeader)
);
$jwtPayload = json_decode(
base64_decode($jwtPayload)
);
$u = [
'firstname' => $jwtPayload->given_name,
'lastname' => $jwtPayload->family_name,
'status' => STUDENT,
'email' => $jwtPayload->emails[0],
'username' => $jwtPayload->emails[0],
'language' => 'en',
'password' => 'azure_active_directory',
'auth_source' => 'azure_active_directory '.$jwtPayload->idp,
'extra' => [],
];
$userInfo = api_get_user_info_from_email($jwtPayload->emails[0]);
if ($userInfo === false) {
// we have to create the user
$chamilo_uid = external_add_user($u);
if ($chamilo_uid !== false) {
$_user['user_id'] = $chamilo_uid;
$_user['uidReset'] = true;
$_SESSION['_user'] = $_user;
}
} else {
// User already exists, update info and login
$chamilo_uid = $userInfo['user_id'];
$u['user_id'] = $chamilo_uid;
external_update_user($u);
$_user['user_id'] = $chamilo_uid;
$_user['uidReset'] = true;
$_SESSION['_user'] = $_user;
$userInfo = [];
if (!empty($me['email'])) {
$userInfo = api_get_user_info_from_email($me['email']);
}
if (empty($userInfo) && !empty($me['email'])) {
$extraFieldValue = new ExtraFieldValue('user');
$itemValue = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
'organisationemail',
$me['email']
);
$userInfo = api_get_user_info($itemValue['item_id']);
}
if (empty($userInfo) && !empty($me['mailNickname'])) {
$extraFieldValue = new ExtraFieldValue('user');
$itemValue = $extraFieldValue->get_item_id_from_field_variable_and_field_value(
'azure_id',
$me['mailNickname']
);
$userInfo = api_get_user_info($itemValue['item_id']);
}
if (empty($userInfo)) {
header('Location: '.api_get_path(WEB_PATH).'index.php?loginFailed=1&error=user_password_incorrect');
exit;
}
$_user['user_id'] = $userInfo['user_id'];
$_user['uidReset'] = true;
ChamiloSession::write('_user', $_user);
ChamiloSession::write('_user_auth_source', 'azure_active_directory');
header('Location: '.api_get_path(WEB_PATH));
exit;

@ -8,12 +8,15 @@
<a href="{{ azure_active_directory.signin_url }}" class="btn btn-default">{{ 'SignIn'|get_lang }}</a>
{% endif %}
{% if not azure_active_directory.signup_url is empty %}
<a href="{{ azure_active_directory.signup_url }}" class="btn btn-success">{{ 'SignUp'|get_lang }}</a>
{% if not azure_active_directory.signout_url is empty %}
<a href="{{ azure_active_directory.signout_url }}" class="btn btn-danger">{{ 'Logout'|get_lang }}</a>
{% endif %}
{% if not azure_active_directory.signunified_url is empty %}
<a href="{{ azure_active_directory.signunified_url }}" class="btn btn-info">{{ 'SignUpAndSignIn'|get_plugin_lang('AzureActiveDirectory') }}</a>
{% if azure_active_directory.management_login_enabled %}
<hr>
<a href="{{ _p.web_plugin ~ 'azure_active_directory/login.php' }}">
{{ azure_active_directory.management_login_name }}
</a>
{% endif %}
</div>
{% endif %}

@ -0,0 +1,19 @@
<div class="row">
<div class="col-sm-4 col-sm-offset-4">
{{ login_language_form }}
{{ login_form }}
{% if "allow_lostpassword"|api_get_setting == 'true' or "allow_registration"|api_get_setting == 'true' %}
<ul class="nav nav-pills nav-stacked">
{% if "allow_registration"|api_get_setting != 'false' %}
<li><a href="{{ _p.web_main }}auth/inscription.php">{{ 'SignUp'|get_lang }}</a></li>
{% endif %}
{% if "allow_lostpassword"|api_get_setting == 'true' %}
<li><a href="{{ _p.web_main }}auth/lostPassword.php">{{ 'LostPassword'|get_lang }}</a></li>
{% endif %}
</ul>
{% endif %}
</div>
</div>

@ -56,4 +56,8 @@ ALTER TABLE plugin_bbb_meeting ADD COLUMN interface INT NOT NULL DEFAULT 0;
ALTER TABLE plugin_bbb_room ADD COLUMN interface INT NOT NULL DEFAULT 0;
ALTER TABLE plugin_bbb_room MODIFY COLUMN in_at datetime;
ALTER TABLE plugin_bbb_room MODIFY COLUMN out_at datetime;
```
```
For version 2.8
ALTER TABLE plugin_bbb_meeting ADD COLUMN internal_meeting_id VARCHAR(255) DEFAULT NULL;

@ -50,7 +50,7 @@ foreach ($meetings as &$meeting) {
foreach ($participants as $meetingParticipant) {
/** @var User $participant */
$participant = $meetingParticipant['participant'];
$meeting['participants'][] = UserManager::formatUserFullName($participant).' ('.$participant->getEmail().')';
$meeting['participants'][] = $participant->getCompleteName().' ('.$participant->getEmail().')';
}
}

@ -75,3 +75,5 @@ $strings['ParticipantsWillUseSameInterface'] = 'Participants will use the same i
$strings['SetByStudent'] = 'Set by student';
$strings['SetByTeacher'] = 'Set by teacher';
$strings['SetByDefault'] = 'Set to default interface';
$strings['allow_regenerate_recording'] = 'Allow regenerate recording';
$strings['bbb_force_record_generation'] = 'Force record generation at the end of the meeting';

@ -73,3 +73,4 @@ $strings['ParticipantsWillUseSameInterface'] = 'Les apprenants utiliseront la m
$strings['SetByDefault'] = 'Lancement de l\'interface par défaut';
$strings['SetByTeacher'] = 'Choisi par le professeur';
$strings['SetByStudent'] = 'Choisi par l\'apprenant';
$strings['bbb_force_record_generation'] = 'Forcer la génération de l\'enregistrement à la fin de la session';

@ -143,8 +143,8 @@ class bbb
public function forceCIdReq($courseCode, $sessionId = 0, $groupId = 0)
{
$this->courseCode = $courseCode;
$this->sessionId = intval($sessionId);
$this->groupId = intval($groupId);
$this->sessionId = (int) $sessionId;
$this->groupId = (int) $groupId;
}
/**
@ -265,39 +265,40 @@ class bbb
if ($max < 0) {
$max = 0;
}
$this->maxUsersLimit = intval($max);
$this->maxUsersLimit = (int) $max;
}
/**
* See this file in you BBB to set up default values
* @param array $params Array of parameters that will be completed if not containing all expected variables
/var/lib/tomcat6/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties
*
More record information:
http://code.google.com/p/bigbluebutton/wiki/RecordPlaybackSpecification
# Default maximum number of users a meeting can have.
# Doesn't get enforced yet but is the default value when the create
# API doesn't pass a value.
defaultMaxUsers=20
# Default duration of the meeting in minutes.
# Current default is 0 (meeting doesn't end).
defaultMeetingDuration=0
# Remove the meeting from memory when the end API is called.
# This allows 3rd-party apps to recycle the meeting right-away
# instead of waiting for the meeting to expire (see below).
removeMeetingWhenEnded=false
# The number of minutes before the system removes the meeting from memory.
defaultMeetingExpireDuration=1
# The number of minutes the system waits when a meeting is created and when
# a user joins. If after this period, a user hasn't joined, the meeting is
# removed from memory.
defaultMeetingCreateJoinDuration=5
* @param array $params Array of parameters that will be completed if not containing all expected variables
*
* /var/lib/tomcat6/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties
*
* More record information:
* http://code.google.com/p/bigbluebutton/wiki/RecordPlaybackSpecification
*
* Default maximum number of users a meeting can have.
* Doesn't get enforced yet but is the default value when the create
* API doesn't pass a value.
* defaultMaxUsers=20
*
* Default duration of the meeting in minutes.
* Current default is 0 (meeting doesn't end).
* defaultMeetingDuration=0
*
* Remove the meeting from memory when the end API is called.
* This allows 3rd-party apps to recycle the meeting right-away
* instead of waiting for the meeting to expire (see below).
* removeMeetingWhenEnded=false
*
* The number of minutes before the system removes the meeting from memory.
* defaultMeetingExpireDuration=1
*
* The number of minutes the system waits when a meeting is created and when
* a user joins. If after this period, a user hasn't joined, the meeting is
* removed from memory.
* defaultMeetingCreateJoinDuration=5
*
* @return mixed
*/
@ -331,11 +332,6 @@ class bbb
// Each simultaneous conference room needs to have a different
// voice_bridge composed of a 5 digits number, so generating a random one
$params['voice_bridge'] = rand(10000, 99999);
if ($this->debug) {
error_log("enter create_meeting ".print_r($params, 1));
}
$params['created_at'] = api_get_utc_datetime();
$params['access_url'] = $this->accessUrl;
@ -350,14 +346,10 @@ class bbb
$id = Database::insert($this->table, $params);
if ($id) {
if ($this->debug) {
error_log("create_meeting: $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;
//$duration = isset($params['duration']) ? intval($params['duration']) : 0;
// This setting currently limits the maximum conference duration,
// to avoid lingering sessions on the video-conference server #6261
$duration = 300;
@ -370,38 +362,31 @@ class bbb
'dialNumber' => '', // The main number to call into. Optional.
'voiceBridge' => $params['voice_bridge'], // PIN to join voice. Required.
'webVoice' => '', // Alphanumeric to join voice. Optional.
'logoutUrl' => $this->logoutUrl,
'logoutUrl' => $this->logoutUrl.'&action=logout&remote_id='.$params['remote_id'],
'maxParticipants' => $max, // Optional. -1 = unlimitted. Not supported in BBB. [number]
'record' => $record, // New. 'true' will tell BBB to record the meeting.
'duration' => $duration, // Default = 0 which means no set duration in minutes. [number]
//'meta_category' => '', // Use to pass additional info to BBB server. See API docs.
//'meta_category' => '', // Use to pass additional info to BBB server. See API docs.
);
if ($this->debug) {
error_log("create_meeting params: ".print_r($bbbParams, 1));
}
$status = false;
$meeting = null;
while ($status === false) {
$result = $this->api->createMeetingWithXmlResponseArray(
$bbbParams
);
$result = $this->api->createMeetingWithXmlResponseArray($bbbParams);
if (isset($result) && strval($result['returncode']) == 'SUCCESS') {
if ($this->debug) {
error_log(
"create_meeting result: ".print_r($result, 1)
);
if ($this->plugin->get('allow_regenerate_recording') === 'true') {
$internalId = Database::escape_string($result['internalMeetingID']);
$sql = "UPDATE $this->table SET internal_meeting_id = '".$internalId."'
WHERE id = $id";
Database::query($sql);
}
$meeting = $this->joinMeeting($meetingName, true);
return $meeting;
}
}
return false;
}
return false;
}
@ -579,7 +564,7 @@ class bbb
}
}
if (strval($meetingIsRunningInfo['returncode']) == 'SUCCESS' &&
if (strval($meetingIsRunningInfo['returncode']) === 'SUCCESS' &&
isset($meetingIsRunningInfo['meetingName']) &&
!empty($meetingIsRunningInfo['meetingName'])
) {
@ -628,9 +613,8 @@ class bbb
if ($this->debug) {
error_log("Failed to get any response. Maybe we can't contact the BBB server.");
}
} else {
return $result;
}
return $result;
} catch (Exception $e) {
if ($this->debug) {
error_log('Caught exception: ', $e->getMessage(), "\n");
@ -734,7 +718,7 @@ class bbb
);
}
$conditions['order'] = "created_at ASC";
$conditions['order'] = 'created_at ASC';
$meetingList = Database::select(
'*',
@ -766,7 +750,7 @@ class bbb
);
if ($meetingBBB === false) {
//checking with the remote_id didn't work, so just in case and
// Checking with the remote_id didn't work, so just in case and
// to provide backwards support, check with the id
$params = array(
'meetingId' => $meetingDB['id'],
@ -783,7 +767,7 @@ class bbb
$meetingBBB['end_url'] = $this->endUrl($meetingDB);
if (isset($meetingBBB['returncode']) && (string) $meetingBBB['returncode'] == 'FAILED') {
if (isset($meetingBBB['returncode']) && (string) $meetingBBB['returncode'] === 'FAILED') {
if ($meetingDB['status'] == 1 && $this->isConferenceManager()) {
$this->endMeeting($meetingDB['id'], $courseCode);
}
@ -804,35 +788,35 @@ class bbb
}
$record = [];
if (empty($meetingDB['video_url'])) {
$recordingParams = ['meetingId' => $mId];
$records = $this->api->getRecordingsWithXmlResponseArray($recordingParams);
if (!empty($records)) {
if (!isset($records['messageKey']) || $records['messageKey'] != 'noRecordings') {
$record = end($records);
if (!is_array($record) || !isset($record['recordId'])) {
continue;
}
$recordingParams = ['meetingId' => $mId];
$records = $this->api->getRecordingsWithXmlResponseArray($recordingParams);
if (!empty($records)) {
if (!isset($records['messageKey']) || $records['messageKey'] !== 'noRecordings') {
$record = end($records);
if (!is_array($record) || !isset($record['recordId'])) {
continue;
}
if (!empty($record['playbackFormatUrl'])) {
$this->updateMeetingVideoUrl($meetingDB['id'], $record['playbackFormatUrl']);
}
if (!$this->isConferenceManager()) {
$record = [];
}
if (!$this->isConferenceManager()) {
$record = [];
}
}
} else {
$record['playbackFormatUrl'] = $meetingDB['video_url'];
}
$recordLink = isset($record['playbackFormatUrl']) && !empty($record['playbackFormatUrl'])
? Display::url(
if (isset($record['playbackFormatUrl']) && !empty($record['playbackFormatUrl'])) {
$recordLink = Display::url(
$this->plugin->get_lang('ViewRecord'),
$record['playbackFormatUrl'],
['target' => '_blank']
)
: $this->plugin->get_lang('NoRecording');
['target' => '_blank', 'class' => 'btn btn-default']
);
} else {
$recordLink = $this->plugin->get_lang('NoRecording');
}
if ($isAdminReport) {
$this->forceCIdReq(
@ -1115,6 +1099,61 @@ class bbb
return 0;
}
/**
* @param int $id
* @param string $recordId
*
* @return bool
*/
public function regenerateRecording($id, $recordId = '')
{
if ($this->plugin->get('allow_regenerate_recording') !== 'true') {
return false;
}
if (empty($id)) {
return false;
}
$meetingData = Database::select(
'*',
$this->table,
array('where' => array('id = ?' => array($id))),
'first'
);
// Check if there are recordings for this meeting
$recordings = $this->api->getRecordings(['meetingId' => $meetingData['remote_id']]);
if (!empty($recordings) && isset($recordings['messageKey']) && $recordings['messageKey'] === 'noRecordings') {
// Regenerate the meeting id
if (!empty($meetingData['internal_meeting_id'])) {
return $this->api->generateRecording(['recordId' => $meetingData['internal_meeting_id']]);
}
/*$pass = $this->getModMeetingPassword();
$info = $this->getMeetingInfo(['meetingId' => $meetingData['remote_id'], 'password' => $pass]);
if (!empty($info) && isset($info['internalMeetingID'])) {
return $this->api->generateRecording(['recordId' => $meetingData['internal_meeting_id']]);
}*/
return false;
} else {
if (!empty($recordings['records'])) {
$recordExists = false;
foreach ($recordings['records'] as $record) {
if ($recordId == $record['recordId']) {
$recordExists = true;
break;
}
}
if ($recordExists) {
return $this->api->generateRecording(['recordId' => $recordId]);
}
}
}
return false;
}
/**
* Deletes a recording of a meeting
*
@ -1389,6 +1428,49 @@ class bbb
return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().'&action=delete_record&id='.$meeting['id'];
}
/**
* @param array $meeting
* @param array $recordInfo
*
* @return string
*/
public function regenerateRecordUrl($meeting, $recordInfo)
{
if ($this->plugin->get('allow_regenerate_recording') !== 'true') {
return '';
}
if (!isset($meeting['id'])) {
return '';
}
if (empty($recordInfo) || (!empty($recordInfo['recordId']) && !isset($recordInfo['recordId']))) {
return '';
}
return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().
'&action=regenerate_record&id='.$meeting['id'].'&record_id='.$recordInfo['recordId'];
}
/**
* @param array $meeting
*
* @return string
*/
public function regenerateRecordUrlFromMeeting($meeting)
{
if ($this->plugin->get('allow_regenerate_recording') !== 'true') {
return '';
}
if (!isset($meeting['id'])) {
return '';
}
return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().
'&action=regenerate_record&id='.$meeting['id'];
}
/**
* @param array $meeting
* @return string
@ -1399,7 +1481,8 @@ class bbb
return '';
}
return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().'&action=copy_record_to_link_tool&id='.$meeting['id'];
return api_get_path(WEB_PLUGIN_PATH).
'bbb/listing.php?'.$this->getUrlParams().'&action=copy_record_to_link_tool&id='.$meeting['id'];
}
/**
@ -1438,6 +1521,25 @@ class bbb
return $meetingData;
}
/**
* Get the meeting info.
*
* @param int $id
*
* @return array
*/
public function getMeetingByRemoteId($id)
{
$meetingData = Database::select(
'*',
'plugin_bbb_meeting',
array('where' => array('remote_id = ?' => $id)),
'first'
);
return $meetingData;
}
/**
* @param int $meetingId
* @return array
@ -1496,8 +1598,9 @@ class bbb
/**
* @param array $meetingInfo
* @param array $recordInfo
* @param bool $isGlobal
* @param bool $isAdminReport
* @param bool $isGlobal
* @param bool $isAdminReport
*
* @return array
*/
private function getActionLinks(
@ -1518,14 +1621,29 @@ class bbb
);
$links = [];
if ($this->plugin->get('allow_regenerate_recording') === 'true' && $meetingInfo['record'] == 1) {
if (!empty($recordInfo)) {
$links[] = Display::url(
Display::return_icon('reload.png', get_lang('RegenerateRecord')),
$this->regenerateRecordUrl($meetingInfo, $recordInfo)
);
} else {
$links[] = Display::url(
Display::return_icon('reload.png', get_lang('RegenerateRecord')),
$this->regenerateRecordUrlFromMeeting($meetingInfo)
);
}
}
if (empty($recordInfo)) {
if (!$isAdminReport) {
$links[] = Display::url(
Display::return_icon('delete.png', get_lang('Delete')),
$this->deleteRecordUrl($meetingInfo)
);
$links[] = $linkVisibility;
if ($meetingInfo['status'] == 0) {
$links[] = Display::url(
Display::return_icon('delete.png', get_lang('Delete')),
$this->deleteRecordUrl($meetingInfo)
);
$links[] = $linkVisibility;
}
return $links;
} else {
@ -1552,25 +1670,26 @@ class bbb
$hide = $this->plugin->get('disable_download_conference_link') === 'true' ? true : false;
if ($hide == false) {
if ($meetingInfo['has_video_m4v']) {
$links[] = Display::url(
Display::return_icon('save.png', get_lang('DownloadFile')),
$recordInfo['playbackFormatUrl'].'/capture.m4v',
['target' => '_blank']
);
} else {
$links[] = Display::url(
Display::return_icon('save.png', get_lang('DownloadFile')),
'#',
[
'id' => "btn-check-meeting-video-{$meetingInfo['id']}",
'class' => 'check-meeting-video',
'data-id' => $meetingInfo['id']
]
);
if ($meetingInfo['has_video_m4v']) {
$links[] = Display::url(
Display::return_icon('save.png', get_lang('DownloadFile')),
$recordInfo['playbackFormatUrl'].'/capture.m4v',
['target' => '_blank']
);
} else {
$links[] = Display::url(
Display::return_icon('save.png', get_lang('DownloadFile')),
'#',
[
'id' => "btn-check-meeting-video-{$meetingInfo['id']}",
'class' => 'check-meeting-video',
'data-id' => $meetingInfo['id']
]
);
}
}
if (!$isAdminReport) {
$links[] = Display::url(
Display::return_icon('delete.png', get_lang('Delete')),
@ -1584,6 +1703,7 @@ class bbb
);
}
return $links;
}

@ -37,32 +37,27 @@ Versions:
-- See included samples for usage examples
*/
/* _______________________________________________________________________*/
/* get the config values */
//require_once "config.php";
class BigBlueButtonBN {
class BigBlueButtonBN
{
private $_securitySalt;
private $_bbbServerBaseUrl;
/* ___________ General Methods for the BigBlueButton Class __________ */
function __construct() {
/*
Establish just our basic elements in the constructor:
*/
public function __construct()
{
/*
Establish just our basic elements in the constructor:
*/
// BASE CONFIGS - set these for your BBB server in config.php and they will
// simply flow in here via the constants:
$this->_securitySalt = CONFIG_SECURITY_SALT;
$this->_bbbServerBaseUrl = CONFIG_SERVER_BASE_URL;
}
private function _processXmlResponse($url){
/*
A private utility method used by other public methods to process XML responses.
*/
private function _processXmlResponse($url)
{
/*
A private utility method used by other public methods to process XML responses.
*/
if (extension_loaded('curl')) {
$ch = curl_init() or die ( curl_error($ch) );
$timeout = 10;
@ -143,7 +138,8 @@ class BigBlueButtonBN {
return ( $creationUrl.$params.'&checksum='.sha1("create".$params.$this->_securitySalt) );
}
public function createMeetingWithXmlResponseArray($creationParams) {
public function createMeetingWithXmlResponseArray($creationParams)
{
/*
USAGE:
$creationParams = array(
@ -165,27 +161,28 @@ class BigBlueButtonBN {
$xml = $this->_processXmlResponse($this->getCreateMeetingURL($creationParams));
if ($xml) {
if($xml->meetingID)
return array(
'returncode' => $xml->returncode->__toString(),
'message' => $xml->message->__toString(),
'messageKey' => $xml->messageKey->__toString(),
'meetingId' => $xml->meetingID->__toString(),
'attendeePw' => $xml->attendeePW->__toString(),
'moderatorPw' => $xml->moderatorPW->__toString(),
'hasBeenForciblyEnded' => $xml->hasBeenForciblyEnded->__toString(),
'createTime' => $xml->createTime->__toString()
);
else
return array(
'returncode' => $xml->returncode->__toString(),
'message' => $xml->message->__toString(),
'messageKey' => $xml->messageKey->__toString()
);
}
else {
return null;
}
if ($xml->meetingID) {
return array(
'returncode' => $xml->returncode->__toString(),
'message' => $xml->message->__toString(),
'messageKey' => $xml->messageKey->__toString(),
'meetingId' => $xml->meetingID->__toString(),
'attendeePw' => $xml->attendeePW->__toString(),
'moderatorPw' => $xml->moderatorPW->__toString(),
'hasBeenForciblyEnded' => $xml->hasBeenForciblyEnded->__toString(),
'createTime' => $xml->createTime->__toString(),
'internalMeetingID' => $xml->internalMeetingID->__toString()
);
} else {
return array(
'returncode' => $xml->returncode->__toString(),
'message' => $xml->message->__toString(),
'messageKey' => $xml->messageKey->__toString(),
);
}
} else {
return null;
}
}
public function getJoinMeetingURL($joinParams) {
@ -396,8 +393,7 @@ class BigBlueButtonBN {
'message' => $xml->message->__toString()
);
return $result;
}
else {
} else {
// In this case, we have success and meeting info:
$result = array(
'returncode' => $xml->returncode->__toString(),
@ -415,7 +411,9 @@ class BigBlueButtonBN {
'participantCount' => $xml->participantCount->__toString(),
'maxUsers' => $xml->maxUsers->__toString(),
'moderatorCount' => $xml->moderatorCount->__toString(),
'internalMeetingID' => $xml->internalMeetingID->__toString()
);
// Then interate through attendee results and return them as part of the array:
foreach ($xml->attendees->attendee as $a) {
$result[] = array(
@ -640,8 +638,30 @@ class BigBlueButtonBN {
}
/** USAGE:
* $recordingParams = array(
* 'recordId' => '1234', -- REQUIRED - comma separate if multiple ids
* );
*/
public function generateRecording($recordingParams)
{
if (empty($recordingParams)) {
return false;
}
$recordingsUrl = $this->_bbbServerBaseUrl.'../demo/regenerateRecord.jsp?';
$params = 'recordID='.urlencode($recordingParams['recordId']);
$url = $recordingsUrl.$params.'&checksum='.sha1('regenerateRecord'.$params.$this->_securitySalt);
} // END OF BIGBLUEBUTTON CLASS
$ch = curl_init() or die ( curl_error($ch) );
$timeout = 10;
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $timeout);
$data = curl_exec( $ch );
curl_close( $ch );
?>
return true;
}
}

@ -33,6 +33,10 @@ class BBBPlugin extends Plugin
[
'name' => 'bbb_enable_conference_in_groups',
'type' => 'checkbox',
],
[
'name' => 'bbb_force_record_generation',
'type' => 'checkbox',
]
];
@ -42,7 +46,7 @@ class BBBPlugin extends Plugin
protected function __construct()
{
parent::__construct(
'2.7',
'2.8.1',
'Julio Montoya, Yannick Warnier, Angel Fernando Quiroz Campos',
[
'tool_enable' => 'boolean',
@ -79,7 +83,8 @@ class BBBPlugin extends Plugin
self::LAUNCH_TYPE_SET_BY_STUDENT => 'SetByStudent',
],
'translate_options' => true, // variables will be translated using the plugin->get_lang
]
],
'allow_regenerate_recording' => 'boolean',
]
);
@ -95,9 +100,9 @@ class BBBPlugin extends Plugin
if ($variable === 'bbb_enable_conference_in_groups') {
if ($this->get('enable_conference_in_course_groups') === 'true') {
return true;
} else {
return false;
}
return false;
}
return true;
@ -133,12 +138,13 @@ class BBBPlugin extends Plugin
welcome_msg VARCHAR(255) NOT NULL DEFAULT '',
session_id INT unsigned DEFAULT 0,
remote_id CHAR(30),
internal_meeting_id VARCHAR(255) DEFAULT NULL,
visibility TINYINT NOT NULL DEFAULT 1,
voice_bridge INT NOT NULL DEFAULT 1,
access_url INT NOT NULL DEFAULT 1,
video_url TEXT NULL,
has_video_m4v TINYINT NOT NULL DEFAULT 0,
interface INT NOT NULL DEFAULT 0
interface INT NOT NULL DEFAULT 0
)";
Database::query($sql);
@ -280,7 +286,7 @@ class BBBPlugin extends Plugin
{
$data = [
'text' => $this->get_lang('EnterConferenceFlash'),
'url' => $conferenceUrl . '&interface=' . self::INTERFACE_FLASH,
'url' => $conferenceUrl.'&interface='.self::INTERFACE_FLASH,
'icon' => 'resources/img/64/videoconference_flash.png'
];
return $data;
@ -295,8 +301,8 @@ class BBBPlugin extends Plugin
{
$data = [
'text' => $this->get_lang('EnterConferenceHTML5'),
'url' => $conferenceUrl . '&interface=' . self::INTERFACE_HTML5,
'icon' => 'resources/img/64/videoconference_html5.png'
'url' => $conferenceUrl.'&interface='.self::INTERFACE_HTML5,
'icon' => 'resources/img/64/videoconference_html5.png',
];
return $data;
}

@ -27,6 +27,9 @@ if ($bbb->isGlobalConference()) {
api_protect_course_script(true);
}
$courseInfo = api_get_course_info();
$courseCode = isset($courseInfo['code']) ? $courseInfo['code'] : '';
$message = '';
if ($conferenceManager) {
switch ($action) {
@ -62,6 +65,22 @@ if ($conferenceManager) {
$message = Display::return_message(get_lang('Error'), 'error');
}
break;
case 'regenerate_record':
if ($plugin->get('allow_regenerate_recording') !== 'true') {
api_not_allowed(true);
}
$recordId = isset($_GET['record_id']) ? $_GET['record_id'] : '';
$result = $bbb->regenerateRecording($_GET['id'], $recordId);
if ($result) {
$message = Display::return_message(get_lang('Success'), 'success');
} else {
$message = Display::return_message(get_lang('Error'), 'error');
}
Display::addFlash($message);
header('Location: '.$bbb->getListingUrl());
exit;
break;
case 'delete_record':
$result = $bbb->deleteRecording($_GET['id']);
if ($result) {
@ -77,9 +96,7 @@ if ($conferenceManager) {
case 'end':
$bbb->endMeeting($_GET['id']);
$message = Display::return_message(
$plugin->get_lang('MeetingClosed').'<br />'.$plugin->get_lang(
'MeetingClosedComment'
),
$plugin->get_lang('MeetingClosed').'<br />'.$plugin->get_lang('MeetingClosedComment'),
'success',
false
);
@ -101,10 +118,35 @@ if ($conferenceManager) {
exit;
break;
case 'publish':
$result = $bbb->publishMeeting($_GET['id']);
$bbb->publishMeeting($_GET['id']);
Display::addFlash(Display::return_message(get_lang('Updated')));
header('Location: '.$bbb->getListingUrl());
exit;
break;
case 'unpublish':
$result = $bbb->unpublishMeeting($_GET['id']);
$bbb->unpublishMeeting($_GET['id']);
Display::addFlash(Display::return_message(get_lang('Updated')));
header('Location: '.$bbb->getListingUrl());
exit;
break;
case 'logout':
if ($plugin->get('allow_regenerate_recording') !== 'true') {
api_not_allowed(true);
}
$allow = api_get_course_setting('bbb_force_record_generation', $courseCode) == 1 ? true : false;
if ($allow) {
$result = $bbb->getMeetingByRemoteId($_GET['remote_id']);
if (!empty($result)) {
$result = $bbb->regenerateRecording($result['id']);
if ($result) {
Display::addFlash(Display::return_message(get_lang('Success')));
} else {
Display::addFlash(Display::return_message(get_lang('Error'), 'error'));
}
}
}
header('Location: '.$bbb->getListingUrl());
exit;
break;
default:
break;
@ -137,7 +179,6 @@ if (($meetingExists || $userCanSeeJoinButton) && ($maxUsers == 0 || $maxUsers >
$showJoinButton = true;
}
$conferenceUrl = $bbb->getConferenceUrl();
$courseInfo = api_get_course_info();
$formToString = '';
@ -148,7 +189,7 @@ if ($bbb->isGlobalConference() === false &&
) {
$url = api_get_self().'?'.api_get_cidreq(true, false).'&gidReq=';
$htmlHeadXtra[] = '<script>
$(document).ready(function(){
$(document).ready(function() {
$("#group_select").on("change", function() {
var groupId = $(this).find("option:selected").val();
var url = "'.$url.'";
@ -180,18 +221,6 @@ if ($bbb->isGlobalConference() === false &&
}
}
$tpl = new Template($tool_name);
$tpl->assign('allow_to_edit', $conferenceManager);
$tpl->assign('meetings', $meetings);
$tpl->assign('conference_url', $conferenceUrl);
$tpl->assign('users_online', $usersOnline);
$tpl->assign('conference_manager', $conferenceManager);
$tpl->assign('max_users_limit', $maxUsers);
$tpl->assign('bbb_status', $status);
$tpl->assign('show_join_button', $showJoinButton);
$tpl->assign('message', $message);
$tpl->assign('form', $formToString);
// Default URL
$urlList[] = Display::url(
$plugin->get_lang('EnterConference'),
@ -200,7 +229,7 @@ $urlList[] = Display::url(
);
$type = $plugin->get('launch_type');
$warningIntefaceMessage = '';
$warningInterfaceMessage = '';
$showClientOptions = false;
switch ($type) {
@ -215,7 +244,7 @@ switch ($type) {
case BBBPlugin::LAUNCH_TYPE_SET_BY_TEACHER:
if ($conferenceManager) {
$urlList = $plugin->getUrlInterfaceLinks($conferenceUrl);
$warningIntefaceMessage = Display::return_message($plugin->get_lang('ParticipantsWillUseSameInterface'));
$warningInterfaceMessage = Display::return_message($plugin->get_lang('ParticipantsWillUseSameInterface'));
$showClientOptions = true;
} else {
$meetingInfo = $bbb->getMeetingByName($videoConferenceName);
@ -235,35 +264,31 @@ switch ($type) {
$showClientOptions = true;
} else {
if ($meetingExists) {
$meetingInfo = $bbb->getMeetingByName($videoConferenceName);
$meetinUserInfo = $bbb->getMeetingParticipantInfo($meetingInfo['id'], api_get_user_id());
$urlList = $plugin->getUrlInterfaceLinks($conferenceUrl);
$showClientOptions = true;
/*if (empty($meetinUserInfo)) {
$url = $plugin->getUrlInterfaceLinks($conferenceUrl);
} else {
switch ($meetinUserInfo['interface']) {
case BBBPlugin::INTERFACE_FLASH:
$url = $plugin->getFlashUrl($conferenceUrl);
break;
case BBBPlugin::INTERFACE_HTML5:
$url = $plugin->getHtmlUrl($conferenceUrl);
break;
}
}*/
}
}
break;
}
$tpl = new Template($tool_name);
$tpl->assign('allow_to_edit', $conferenceManager);
$tpl->assign('meetings', $meetings);
$tpl->assign('conference_url', $conferenceUrl);
$tpl->assign('users_online', $usersOnline);
$tpl->assign('conference_manager', $conferenceManager);
$tpl->assign('max_users_limit', $maxUsers);
$tpl->assign('bbb_status', $status);
$tpl->assign('show_join_button', $showJoinButton);
$tpl->assign('message', $message);
$tpl->assign('form', $formToString);
$tpl->assign('enter_conference_links', $urlList);
$tpl->assign('warning_inteface_msg', $warningIntefaceMessage);
$tpl->assign('warning_inteface_msg', $warningInterfaceMessage);
$tpl->assign('show_client_options', $showClientOptions);
$listing_tpl = 'bbb/view/listing.tpl';
$content = $tpl->fetch($listing_tpl);
$actionLinks = '';
if (api_is_platform_admin()) {
$actionLinks .= Display::toolbarButton(

@ -1,97 +0,0 @@
<div class ="row">
{% if bbb_status == true %}
<div class ="col-md-12" style="text-align:center">
{{ form }}
{% if show_join_button == true %}
<p>
<a href="{{ conference_url }}" target="_blank" class="btn btn-primary btn-large">
{{ 'EnterConference'| get_plugin_lang('BBBPlugin') }}
</a>
<span id="users_online" class="label label-warning">
{{ 'XUsersOnLine'| get_plugin_lang('BBBPlugin') | format(users_online) }}
</span>
</p>
{% if max_users_limit > 0 %}
{% if conference_manager == true %}
<p>{{ 'MaxXUsersWarning' | get_plugin_lang('BBBPlugin') | format(max_users_limit) }}</p>
{% elseif users_online >= max_users_limit/2 %}
<p>{{ 'MaxXUsersWarning' | get_plugin_lang('BBBPlugin') | format(max_users_limit) }}</p>
{% endif %}
{% endif %}
<div class="well">
<strong>{{ 'UrlMeetingToShare'| get_plugin_lang('BBBPlugin') }}</strong>
<input type="text" class="form-control text-center" readonly value="{{ conference_url }}">
</div>
{% elseif max_users_limit > 0 %}
{% if conference_manager == true %}
<p>{{ 'MaxXUsersReachedManager' | get_plugin_lang('BBBPlugin') | format(max_users_limit) }}</p>
{% elseif users_online > 0 %}
<p>{{ 'MaxXUsersReached' | get_plugin_lang('BBBPlugin') | format(max_users_limit) }}</p>
{% endif %}
{% endif %}
</div>
<div class ="col-md-12">
<div class="page-header">
<h2>{{ 'RecordList'| get_plugin_lang('BBBPlugin') }}</h2>
</div>
<table class="table">
<tr>
<!-- th>#</th -->
<th>{{ 'CreatedAt'| get_plugin_lang('BBBPlugin') }}</th>
<th>{{ 'Status'| get_lang }}</th>
<th>{{ 'Records'| get_plugin_lang('BBBPlugin') }}</th>
{% if allow_to_edit %}
<th>{{ 'Actions'| get_lang }}</th>
{% endif %}
</tr>
{% for meeting in meetings %}
<tr>
<!-- td>{{ meeting.id }}</td -->
{% if meeting.visibility == 0 %}
<td class="muted">{{ meeting.created_at }}</td>
{% else %}
<td>{{ meeting.created_at }}</td>
{% endif %}
<td>
{% if meeting.status == 1 %}
<span class="label label-success">{{ 'MeetingOpened'|get_plugin_lang('BBBPlugin') }}</span>
{% else %}
<span class="label label-info">{{ 'MeetingClosed'|get_plugin_lang('BBBPlugin') }}</span>
{% endif %}
</td>
<td>
{% if meeting.record == 1 %}
{# Record list #}
{{ meeting.show_links }}
{% else %}
{{ 'NoRecording'|get_plugin_lang('BBBPlugin') }}
{% endif %}
</td>
{% if allow_to_edit %}
<td>
{% if meeting.status == 1 %}
<a class="btn btn-default" href="{{ meeting.end_url }} ">
{{ 'CloseMeeting'|get_plugin_lang('BBBPlugin') }}
</a>
{% else %}
{{ meeting.action_links }}
{% endif %}
</td>
{% endif %}
</tr>
{% endfor %}
</table>
</div>
{% else %}
<div class ="col-md-12" style="text-align:center">
{{ 'ServerIsNotRunning' | get_plugin_lang('BBBPlugin') | return_message('warning') }}
</div>
{% endif %}
</div>

@ -13,10 +13,6 @@ require_once __DIR__.'/config.php';
$logInfo = [
'tool' => 'Videoconference',
'tool_id' => 0,
'tool_id_detail' => 0,
'action' => '',
'action_details' => '',
];
Event::registerLog($logInfo);
@ -68,7 +64,6 @@ if ($bbb->pluginEnabled) {
$meetingParams = [];
$meetingParams['meeting_name'] = $bbb->getCurrentVideoConferenceName();
$meetingParams['interface'] = $interface;
if ($bbb->meetingExists($meetingParams['meeting_name'])) {
$joinUrl = $bbb->joinMeeting($meetingParams['meeting_name']);
if ($joinUrl) {

@ -0,0 +1,48 @@
{{ search_form }}
<table class="table table-hover table-striped">
<thead>
<tr>
<th>{{ 'CreatedAt'|get_lang }}</th>
<th>{{ 'Status'|get_lang }}</th>
<th>{{ 'Records'|get_plugin_lang('BBBPlugin') }}</th>
<th>{{ 'Course'|get_lang }}</th>
<th>{{ 'Session'|get_lang }}</th>
<th>{{ 'Participants'|get_lang }}</th>
<th>{{ 'Actions'|get_lang }}</th>
</tr>
</thead>
<tbody>
{% for meeting in meetings %}
<tr id="meeting-{{ meeting.id }}">
{% if meeting.visibility == 0 %}
<td class="muted">{{ meeting.created_at }}</td>
{% else %}
<td>{{ meeting.created_at }}</td>
{% endif %}
<td>
{% if meeting.status == 1 %}
<span class="label label-success">{{ 'MeetingOpened'|get_plugin_lang('BBBPlugin') }}</span>
{% else %}
<span class="label label-info">{{ 'MeetingClosed'|get_plugin_lang('BBBPlugin') }}</span>
{% endif %}
</td>
<td>
{% if meeting.record == 1 %}
{# Record list #}
{{ meeting.show_links }}
{% else %}
{{ 'NoRecording'|get_plugin_lang('BBBPlugin') }}
{% endif %}
</td>
<td>{{ meeting.course ?: '-' }}</td>
<td>{{ meeting.session ?: '-' }}</td>
<td>
{{ meeting.participants ? meeting.participants|join('<br>') : '-' }}
</td>
<td>
{{ meeting.action_links }}
</td>
</tr>
{% endfor %}
</tbody>
</table>

@ -0,0 +1,173 @@
<style type="text/css">
.conference .url{
padding: 5px;
margin-bottom: 5px;
}
.conference .share{
padding: 10px;
margin-top: 5px;
margin-bottom: 5px;
font-weight: bold;
}
</style>
<div class ="row">
{% if bbb_status == true %}
<div class ="col-md-12" style="text-align:center">
{{ form }}
{% if show_join_button == true %}
{% if show_client_options %}
<div class="row">
<div class="col-md-6">
<div class="panel panel-default conference">
<div class="panel-body">
<div class="url">
<a class="btn btn-default" href="{{ enter_conference_links.0.url }}">
<img src="{{ enter_conference_links.0.icon }}" /><br>
{{ enter_conference_links.0.text }}
</a>
</div>
<div class="share">
{{ 'UrlMeetingToShare'| get_plugin_lang('BBBPlugin') }}
</div>
<div class="form-inline">
<div class="form-group">
<input id="share_button_flash" type="text"
style="width:300px"
class="form-control" readonly value="{{ conference_url }}&interface=0">
<button onclick="copyTextToClipBoard('share_button_flash');" class="btn btn-default">
<span class="fa fa-copy"></span> {{ 'CopyTextToClipboard' | get_lang }}
</button>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-6">
<div class="panel panel-default conference">
<div class="panel-body">
<div class="url">
<a class="btn btn-default" href="{{ enter_conference_links.1.url }}">
<img src="{{ enter_conference_links.1.icon }}" /><br>
{{ enter_conference_links.1.text }}
</a>
</div>
<div class="share">
{{ 'UrlMeetingToShare'| get_plugin_lang('BBBPlugin') }}
</div>
<div class="form-inline">
<div class="form-group">
<input id="share_button_html" type="text"
style="width:300px"
class="form-control" readonly value="{{ conference_url }}&interface=1">
<button onclick="copyTextToClipBoard('share_button_html');" class="btn btn-default">
<span class="fa fa-copy"></span> {{ 'CopyTextToClipboard' | get_lang }}
</button>
</div>
</div>
</div>
</div>
</div>
</div>
{% else %}
{{ enter_conference_links.0 }}
<br />
<strong>{{ 'UrlMeetingToShare'| get_plugin_lang('BBBPlugin') }}</strong>
<div class="well">
<div class="form-inline">
<div class="form-group">
<input id="share_button"
type="text"
style="width:600px"
class="form-control" readonly value="{{ conference_url }}">
<button onclick="copyTextToClipBoard('share_button');" class="btn btn-default">
<span class="fa fa-copy"></span> {{ 'CopyTextToClipboard' | get_lang }}
</button>
</div>
</div>
</div>
{% endif %}
<p>
<span id="users_online" class="label label-warning">
{{ 'XUsersOnLine'| get_plugin_lang('BBBPlugin') | format(users_online) }}
</span>
</p>
{{ warning_inteface_msg }}
{% if max_users_limit > 0 %}
{% if conference_manager == true %}
<p>{{ 'MaxXUsersWarning' | get_plugin_lang('BBBPlugin') | format(max_users_limit) }}</p>
{% elseif users_online >= max_users_limit/2 %}
<p>{{ 'MaxXUsersWarning' | get_plugin_lang('BBBPlugin') | format(max_users_limit) }}</p>
{% endif %}
{% endif %}
</div>
{% elseif max_users_limit > 0 %}
{% if conference_manager == true %}
<p>{{ 'MaxXUsersReachedManager' | get_plugin_lang('BBBPlugin') | format(max_users_limit) }}</p>
{% elseif users_online > 0 %}
<p>{{ 'MaxXUsersReached' | get_plugin_lang('BBBPlugin') | format(max_users_limit) }}</p>
{% endif %}
{% endif %}
</div>
<div class ="col-md-12">
<div class="page-header">
<h2>{{ 'RecordList'| get_plugin_lang('BBBPlugin') }}</h2>
</div>
<table class="table">
<tr>
<th>{{ 'CreatedAt'| get_plugin_lang('BBBPlugin') }}</th>
<th>{{ 'Status'| get_lang }}</th>
<th>{{ 'Records'| get_plugin_lang('BBBPlugin') }}</th>
{% if allow_to_edit %}
<th>{{ 'Actions'| get_lang }}</th>
{% endif %}
</tr>
{% for meeting in meetings %}
<tr>
<!-- td>{{ meeting.id }}</td -->
{% if meeting.visibility == 0 %}
<td class="muted">{{ meeting.created_at }}</td>
{% else %}
<td>{{ meeting.created_at }}</td>
{% endif %}
<td>
{% if meeting.status == 1 %}
<span class="label label-success">{{ 'MeetingOpened'|get_plugin_lang('BBBPlugin') }}</span>
{% else %}
<span class="label label-info">{{ 'MeetingClosed'|get_plugin_lang('BBBPlugin') }}</span>
{% endif %}
</td>
<td>
{% if meeting.record == 1 %}
{# Record list #}
{{ meeting.show_links }}
{% else %}
{{ 'NoRecording'|get_plugin_lang('BBBPlugin') }}
{% endif %}
</td>
{% if allow_to_edit %}
<td>
{% if meeting.status == 1 %}
<a class="btn btn-default" href="{{ meeting.end_url }} ">
{{ 'CloseMeeting'|get_plugin_lang('BBBPlugin') }}
</a>
{% else %}
{% endif %}
{{ meeting.action_links }}
</td>
{% endif %}
</tr>
{% endfor %}
</table>
</div>
{% else %}
<div class ="col-md-12" style="text-align:center">
{{ 'ServerIsNotRunning' | get_plugin_lang('BBBPlugin') | return_message('warning') }}
</div>
{% endif %}
</div>

@ -7,15 +7,9 @@
* @package chamilo.plugin.buycourses
*/
$plugin = BuyCoursesPlugin::create();
$guess_enable = $plugin->get('unregistered_users_enable');
if ($guess_enable == "true" || isset($_SESSION['_user'])) {
// If the user is NOT an administrator, redirect it to course/session buy list
if (!api_is_platform_admin()) {
header('Location: src/course_panel.php');
exit;
}
$allow = $plugin->get('unregistered_users_enable');
if (($allow === 'true' && api_is_anonymous()) || !api_is_anonymous()) {
$tpl = new Template();
$content = $tpl->fetch('buycourses/view/index.tpl');
$tpl->assign('content', $content);

@ -31,6 +31,7 @@ $strings['ExpectionPlace'] = "Expection place";
$strings['DateExpediction'] = "Expediction date";
$strings['UseDateEndAccessSession'] = "Use end date of session access";
$strings['UseDateDownloadCertificate'] = "Use certificate download date";
$strings['UseDateGenerationCertificate'] = "Use certificate generation date";
$strings['UseCustomDate'] = "Use custom date";
$strings['LogosSeal'] = "Logos / Seals";
$strings['LogoLeft'] = "Logo left";

@ -31,6 +31,7 @@ $strings['ExpectionPlace'] = "Lugar expedición";
$strings['DateExpediction'] = "Fecha expedición";
$strings['UseDateEndAccessSession'] = "Usar fecha fin de acceso de la sesión";
$strings['UseDateDownloadCertificate'] = "Usar fecha de descarga del certificado";
$strings['UseDateGenerationCertificate'] = "Usar fecha de generación del certificado";
$strings['UseCustomDate'] = "Usar fechas personalizadas";
$strings['LogosSeal'] = "Logos / Sellos";
$strings['LogoLeft'] = "Logo izquierda";

@ -175,10 +175,9 @@ if ($form->validate()) {
$infoCertificateDefault = Database::select(
'*',
$table,
['where' => ['certificate_default = ? ' => 1]],
['where' => ['access_url_id = ? AND certificate_default = ? ' => [$accessUrlId, 1]]],
'first'
);
if (!empty($infoCertificateDefault)) {
foreach ($fieldList as $field) {
if (!empty($infoCertificateDefault[$field]) && !$checkLogo[$field]) {
@ -454,7 +453,7 @@ $form->addText(
);
$group = [];
$option1 = &$form->createElement(
$option = &$form->createElement(
'radio',
'type_date_expediction',
'',
@ -466,9 +465,9 @@ $option1 = &$form->createElement(
(($sessionId == 0) ? 'disabled' : ''),
]
);
$group[] = $option1;
$group[] = $option;
$option2 = &$form->createElement(
$option = &$form->createElement(
'radio',
'type_date_expediction',
'',
@ -479,9 +478,22 @@ $option2 = &$form->createElement(
'onclick' => 'javascript: typeDateExpedictionSwitchRadioButton();',
]
);
$group[] = $option2;
$group[] = $option;
$option4 = &$form->createElement(
$option = &$form->createElement(
'radio',
'type_date_expediction',
'',
get_lang('UseDateGenerationCertificate'),
4,
[
'id' => 'type_date_expediction_4',
'onclick' => 'javascript: typeDateExpedictionSwitchRadioButton();',
]
);
$group[] = $option;
$option = &$form->createElement(
'radio',
'type_date_expediction',
'',
@ -492,9 +504,9 @@ $option4 = &$form->createElement(
'onclick' => 'javascript: typeDateExpedictionSwitchRadioButton();',
]
);
$group[] = $option4;
$group[] = $option;
$option3 = &$form->createElement(
$option = &$form->createElement(
'radio',
'type_date_expediction',
'',
@ -505,7 +517,7 @@ $option3 = &$form->createElement(
'onclick' => 'javascript: typeDateExpedictionSwitchRadioButton();',
]
);
$group[] = $option3;
$group[] = $option;
$form->addGroup(
$group,
@ -938,6 +950,7 @@ function checkInstanceImage($certificateId, $imagePath, $field, $type = 'certifi
$table = Database::get_main_table(CustomCertificatePlugin::TABLE_CUSTOMCERTIFICATE);
$imagePath = Database::escape_string($imagePath);
$field = Database::escape_string($field);
$certificateId = (int) $certificateId;
$sql = "SELECT * FROM $table WHERE $field = '$imagePath'";
$res = Database::query($sql);

@ -183,19 +183,19 @@ foreach ($userList as $userInfo) {
$htmlText .= '</tr>';
$htmlText .= '</table>';
$all_user_info = DocumentManager::get_all_info_to_certificate(
$allUserInfo = DocumentManager::get_all_info_to_certificate(
$studentId,
$courseCode,
true
false
);
$myContentHtml = $infoCertificate['content_course'];
$myContentHtml = str_replace(chr(13).chr(10).chr(13).chr(10), chr(13).chr(10), $myContentHtml);
$info_to_be_replaced_in_content_html = $all_user_info[0];
$info_to_replace_in_content_html = $all_user_info[1];
$infoToBeReplacedInContentHtml = $allUserInfo[0];
$infoToReplaceInContentHtml = $allUserInfo[1];
$myContentHtml = str_replace(
$info_to_be_replaced_in_content_html,
$info_to_replace_in_content_html,
$infoToBeReplacedInContentHtml,
$infoToReplaceInContentHtml,
$myContentHtml
);
@ -253,6 +253,8 @@ foreach ($userList as $userInfo) {
'............'
);
}
} elseif ($infoCertificate['type_date_expediction'] == 4) {
$dateExpediction .= $plugin->get_lang('to').$infoToReplaceInContentHtml[9]; //date_certificate_no_time
} else {
if (!empty($sessionInfo)) {
$dateInfo = api_get_local_time($sessionInfo['access_end_date']);

@ -110,7 +110,7 @@ try {
$fileData = [];
$fileData[] = sprintf(
"1 %s %s%s",
'1 %s %s%s',
$fieldProvider ? $fieldProvider['value'] : null,
$values['course'],
$dateStart->format('m/d/Y')

@ -4,7 +4,7 @@ Redirection plugin
Chamilo plugin for the redirection of specific users after they login and
redirect from the index.php to the selected URL.
Requires the addition of the following in configuration.php:
1. Requires the addition of the following in configuration.php:
```
$_configuration['plugin_redirection_enabled'] = true;
@ -13,4 +13,6 @@ $_configuration['plugin_redirection_enabled'] = true;
This setting is defined in configuration.php rather than in settings_current to reduce the
load, as it is used on every login.
2. Add the plugin in the "menu administration" region.
@TODO Check the load difference for *just* checking it in settings_current rather than building the plugin object

@ -87,6 +87,20 @@ class RedirectionPlugin extends Plugin
return Database::fetch_array($result, 'ASSOC');
}
/**
* Deletes redirection from user.
*
* @param int $userId
*/
public static function deleteUserRedirection($userId)
{
$table = Database::get_main_table('plugin_redirection');
Database::delete(
$table,
['user_id = ?' => [$userId]]
);
}
/**
* Deletes an existing redirection.
*

@ -133,7 +133,7 @@ class StudentFollowUpPlugin extends Plugin
}
// Check if course session coach
$sessions = SessionManager::get_sessions_by_user($studentId);
$sessions = SessionManager::get_sessions_by_user($studentId, false, true);
if (!empty($sessions)) {
foreach ($sessions as $session) {
$sessionId = $session['session_id'];
@ -146,7 +146,6 @@ class StudentFollowUpPlugin extends Plugin
break;
}
foreach ($session['courses'] as $course) {
//$isCourseCoach = api_is_coach($sessionId, $course['real_id']);
$coachList = SessionManager::getCoachesByCourseSession(
$sessionId,
$course['real_id']
@ -174,18 +173,27 @@ class StudentFollowUpPlugin extends Plugin
/**
* @param string $status
* @param int $currentUserId
* @param int $sessionId
* @param int $start
* @param int $limit
*
* @return array
*/
public static function getUsers($status, $currentUserId, $start, $limit)
public static function getUsers($status, $currentUserId, $sessionId, $start, $limit)
{
$sessions = [];
$courses = [];
$sessionsFull = [];
switch ($status) {
case COURSEMANAGER:
$sessions = SessionManager::get_sessions_by_user($currentUserId);
$sessions = array_column($sessions, 'session_id');
/*$sessions = SessionManager::get_sessions_by_user($currentUserId);
$sessions = array_column($sessions, 'session_id');*/
$sessionsFull = SessionManager::getSessionsCoachedByUser($currentUserId);
$sessions = array_column($sessionsFull, 'id');
if (!empty($sessionId)) {
$sessions = [$sessionId];
}
// Get session courses where I'm coach
$courseList = SessionManager::getCoursesListByCourseCoach($currentUserId);
$courses = [];
@ -195,8 +203,12 @@ class StudentFollowUpPlugin extends Plugin
}
break;
case DRH:
$sessions = SessionManager::get_sessions_followed_by_drh($currentUserId);
$sessions = array_column($sessions, 'id');
$sessionsFull = SessionManager::get_sessions_followed_by_drh($currentUserId);
$sessions = array_column($sessionsFull, 'id');
if (!empty($sessionId)) {
$sessions = [$sessionId];
}
$courses = [];
foreach ($sessions as $sessionId) {
$sessionDrhInfo = SessionManager::getSessionFollowedByDrh(
@ -230,7 +242,10 @@ class StudentFollowUpPlugin extends Plugin
$userList = array_unique($userList);
}*/
return $userList;
return [
'users' => $userList,
'sessions' => $sessionsFull,
];
}
/**

@ -5,3 +5,6 @@ $strings['plugin_comment'] = "Care system (Zorgdossier) [CS]
Career follow system (Structuurschema) [CFS]
Competence based evaluation system (Competentie evaluatie systeem) [CBES]";
$strings['tool_enable'] = 'Enable plugin';
$strings['CareDetailView'] = 'Mijn begeleidingen';
$strings['Private'] = 'Niet gedeeld met lesgevers';
$strings['Public'] = 'Gedeeld met lesgevers';

@ -11,37 +11,60 @@ api_block_anonymous_users();
$plugin = StudentFollowUpPlugin::create();
$currentUserId = api_get_user_id();
$currentPage = isset($_GET['page']) ? (int) $_GET['page'] : 1;
$keyword = isset($_GET['keyword']) ? $_GET['keyword'] : '';
$currentPage = isset($_REQUEST['page']) ? (int) $_REQUEST['page'] : 1;
$keyword = isset($_REQUEST['keyword']) ? Security::remove_XSS($_REQUEST['keyword']) : '';
$sessionId = isset($_REQUEST['session_id']) ? (int) $_REQUEST['session_id'] : 0;
$selectedTag = isset($_REQUEST['tag']) ? Security::remove_XSS($_REQUEST['tag']) : '';
$totalItems = 0;
$items = [];
$tags = [];
$showPrivate = false;
$pageSize = StudentFollowUpPlugin::getPageSize();
$firstResults = $pageSize * ($currentPage - 1);
$pagesCount = 0;
$isAdmin = api_is_platform_admin();
$userList = [];
if (!api_is_platform_admin()) {
if (!$isAdmin) {
$status = COURSEMANAGER;
if (api_is_drh()) {
$status = DRH;
}
$userList = StudentFollowUpPlugin::getUsers(
$data = StudentFollowUpPlugin::getUsers(
$status,
$currentUserId,
$sessionId,
$firstResults,
$pageSize
);
$userList = $data['users'];
$fullSessionList = $data['sessions'];
} else {
$fullSessionList = SessionManager::getSessionsCoachedByUser($currentUserId);
}
if (!empty($sessionId)) {
$userList = SessionManager::get_users_by_session($sessionId);
$userList = array_column($userList, 'user_id');
}
$tagList = [];
if (!empty($userList) || api_is_platform_admin()) {
if (!empty($userList) || $isAdmin) {
$em = Database::getManager();
$qb = $em->createQueryBuilder();
$criteria = Criteria::create();
if (!api_is_platform_admin()) {
if (!$isAdmin) {
$criteria->where(Criteria::expr()->in('user', $userList));
}
if (!empty($sessionId)) {
$criteria->where(Criteria::expr()->in('user', $userList));
}
if ($showPrivate == false) {
if ($showPrivate === false) {
$criteria->andWhere(Criteria::expr()->eq('private', false));
}
@ -58,9 +81,9 @@ if (!empty($userList) || api_is_platform_admin()) {
;
if (!empty($keyword)) {
$keyword = explode(' ', $keyword);
if (is_array($keyword)) {
foreach ($keyword as $key) {
$keywordToArray = explode(' ', $keyword);
if (is_array($keywordToArray)) {
foreach ($keywordToArray as $key) {
$key = trim($key);
if (empty($key)) {
continue;
@ -78,15 +101,45 @@ if (!empty($userList) || api_is_platform_admin()) {
}
}
$queryBuilderOriginal = clone $qb;
if (!empty($selectedTag)) {
$qb->andWhere('p.tags LIKE :tags ');
$qb->setParameter('tags', "%$selectedTag%");
}
$query = $qb->getQuery();
$items = new Paginator($query);
$queryBuilderOriginal->select('p.tags')
->distinct(false)
->setFirstResult(null)
->setMaxResults(null)
->groupBy('p.id')
;
$tags = $queryBuilderOriginal->getQuery()->getResult();
//var_dump($queryBuilderOriginal->getQuery()->getSQL());
$tagList = [];
foreach ($tags as $tag) {
$itemTags = $tag['tags'];
foreach ($itemTags as $itemTag) {
if (in_array($itemTag, array_keys($tagList))) {
$tagList[$itemTag]++;
} else {
$tagList[$itemTag] = 1;
}
}
}
$totalItems = $items->count();
$pagesCount = ceil($totalItems / $pageSize);
}
$pagination = '';
$url = api_get_self().'?';
if ($totalItems > 1) {
$url = api_get_self().'?session_id='.$sessionId.'&tag='.$selectedTag.'&keyword='.$keyword.'&';
if ($totalItems > 1 && $pagesCount > 1) {
$pagination .= '<ul class="pagination">';
for ($i = 0; $i < $pagesCount; $i++) {
$newPage = $i + 1;
@ -100,17 +153,42 @@ if ($totalItems > 1) {
}
// Create a search-box
$form = new FormValidator('search_simple', 'get', null, null, null, 'inline');
$form = new FormValidator('search_simple', 'get', null, null, null, FormValidator::LAYOUT_HORIZONTAL);
$form->addText(
'keyword',
get_lang('Search'),
false,
[
'aria-label' => get_lang("SearchUsers"),
'aria-label' => get_lang('SearchUsers'),
]
);
if (!empty($fullSessionList)) {
$options = array_column($fullSessionList, 'name', 'id');
$options[0] = get_lang('SelectAnOption');
ksort($options);
$form->addSelect('session_id', get_lang('Session'), $options);
}
if (!empty($tagList)) {
$tagOptions = [];
arsort($tagList);
foreach ($tagList as $tag => $counter) {
$tagOptions[$tag] = $tag.' ('.$counter.')';
}
$form->addSelect('tag', get_lang('Tags'), $tagOptions, ['placeholder' => get_lang('SelectAnOption')]);
}
$form->addButtonSearch(get_lang('Search'));
$defaults = [
'session_id' => $sessionId,
'keyword' => $keyword,
'tag' => $selectedTag,
];
$form->setDefaults($defaults);
$tpl = new Template($plugin->get_lang('plugin_title'));
$tpl->assign('users', $items);
$tpl->assign('form', $form->returnForm());
@ -121,7 +199,5 @@ $tpl->assign('my_students_url', $url);
$tpl->assign('pagination', $pagination);
$tpl->assign('care_title', $plugin->get_lang('CareDetailView'));
$content = $tpl->fetch('/'.$plugin->get_name().'/view/my_students.html.twig');
// Assign into content
$tpl->assign('content', $content);
// Display
$tpl->display_one_col_template();

@ -3,8 +3,8 @@
{% if users %}
<table class="data_table">
<tr>
<th>Student</th>
<th>Action</th>
<th>{{'Student' | get_lang }}</th>
<th>{{'Action' | get_lang }}</th>
</tr>
{% for user in users %}
<tr>

@ -52,7 +52,17 @@
{% endif %}
{% if post.private %}
<p><span class="label label-warning">Private</span></p>
<p>
<span class="label label-warning">
{{ 'Private'|get_plugin_lang('StudentFollowUpPlugin') }}
</span>
</p>
{% else %}
<p>
<span class="label label-info">
{{ 'Public'|get_plugin_lang('StudentFollowUpPlugin') }}
</span>
</p>
{% endif %}
</div>
</div>

@ -1,6 +1,9 @@
# Speech authentication with Whispeak
Speech authentication with Whispeak
===================================
Instructions:
-------------
1. Install plugin in Chamilo.
2. Set the plugin configuration with the token and API url. And enable the plugin.
3. Set the `login_bottom` region to the plugin.

@ -48,7 +48,7 @@
</div>
<script>
$(document).on('ready', function () {
$(function () {
{% block config_data %}
RecordAudio.init(
{

Loading…
Cancel
Save