Merge branch '17288-plugin-zoom' of github.com:sebastiendu/chamilo-lms into 17288

pull/3376/head
Yannick Warnier 5 years ago
commit 1fc5d992c8
  1. 1
      plugin/zoom/README.md
  2. 33
      plugin/zoom/admin.php
  3. 4
      plugin/zoom/config.php
  4. 1
      plugin/zoom/index.php
  5. 8
      plugin/zoom/install.php
  6. 108
      plugin/zoom/lang/english.php
  7. 107
      plugin/zoom/lang/french.php
  8. 31
      plugin/zoom/lib/API/BaseMeetingTrait.php
  9. 34
      plugin/zoom/lib/API/Client.php
  10. 43
      plugin/zoom/lib/API/CreatedRegistration.php
  11. 19
      plugin/zoom/lib/API/CustomQuestion.php
  12. 40
      plugin/zoom/lib/API/GlobalDialInNumber.php
  13. 94
      plugin/zoom/lib/API/JWTClient.php
  14. 109
      plugin/zoom/lib/API/JsonDeserializableTrait.php
  15. 89
      plugin/zoom/lib/API/Meeting.php
  16. 35
      plugin/zoom/lib/API/MeetingInfo.php
  17. 162
      plugin/zoom/lib/API/MeetingInfoGet.php
  18. 53
      plugin/zoom/lib/API/MeetingInstance.php
  19. 55
      plugin/zoom/lib/API/MeetingInstances.php
  20. 60
      plugin/zoom/lib/API/MeetingList.php
  21. 45
      plugin/zoom/lib/API/MeetingListItem.php
  22. 102
      plugin/zoom/lib/API/MeetingRegistrant.php
  23. 55
      plugin/zoom/lib/API/MeetingRegistrantList.php
  24. 30
      plugin/zoom/lib/API/MeetingRegistrantListItem.php
  25. 141
      plugin/zoom/lib/API/MeetingSettings.php
  26. 70
      plugin/zoom/lib/API/Pagination.php
  27. 73
      plugin/zoom/lib/API/PaginationToken.php
  28. 60
      plugin/zoom/lib/API/ParticipantList.php
  29. 23
      plugin/zoom/lib/API/ParticipantListItem.php
  30. 78
      plugin/zoom/lib/API/PastMeeting.php
  31. 118
      plugin/zoom/lib/API/RecordingFile.php
  32. 68
      plugin/zoom/lib/API/RecordingList.php
  33. 91
      plugin/zoom/lib/API/RecordingMeeting.php
  34. 30
      plugin/zoom/lib/API/TrackingField.php
  35. 69
      plugin/zoom/lib/CourseMeeting.php
  36. 57
      plugin/zoom/lib/CourseMeetingInfoGet.php
  37. 26
      plugin/zoom/lib/CourseMeetingList.php
  38. 31
      plugin/zoom/lib/CourseMeetingListItem.php
  39. 142
      plugin/zoom/lib/CourseMeetingTrait.php
  40. 74
      plugin/zoom/lib/DisplayableMeetingTrait.php
  41. 28
      plugin/zoom/lib/File.php
  42. 65
      plugin/zoom/lib/Recording.php
  43. 51
      plugin/zoom/lib/RecordingList.php
  44. 51
      plugin/zoom/lib/UserMeetingRegistrant.php
  45. 43
      plugin/zoom/lib/UserMeetingRegistrantList.php
  46. 30
      plugin/zoom/lib/UserMeetingRegistrantListItem.php
  47. 89
      plugin/zoom/lib/UserMeetingRegistrantTrait.php
  48. 1043
      plugin/zoom/lib/zoom_plugin.class.php
  49. 68
      plugin/zoom/meeting.php
  50. 13
      plugin/zoom/meeting_from_admin.php
  51. 13
      plugin/zoom/meeting_from_start.php
  52. 4
      plugin/zoom/plugin.php
  53. 41
      plugin/zoom/start.php
  54. 8
      plugin/zoom/uninstall.php
  55. 55
      plugin/zoom/view/admin.tpl
  56. 78
      plugin/zoom/view/meeting.tpl
  57. 44
      plugin/zoom/view/start.tpl

@ -0,0 +1 @@
This plugin allows teachers to launch a Zoom conference at any time and students to join it.

@ -0,0 +1,33 @@
<?php
/* For license terms, see /license.txt */
use Chamilo\PluginBundle\Zoom\CourseMeetingList;
$course_plugin = 'zoom'; // needed in order to load the plugin lang variables
$cidReset = true;
require_once __DIR__.'/config.php';
api_protect_admin_script();
$tool_name = get_lang('ZoomVideoConferences');
$plugin = ZoomPlugin::create();
// the section (for the tabs)
$this_section = SECTION_PLATFORM_ADMIN;
$form = $plugin->getAdminSearchForm();
$startDate = new DateTime($form->getElement('start')->getValue());
$endDate = new DateTime($form->getElement('end')->getValue());
$type = $form->getElement('type')->getValue();
$tpl = new Template($tool_name);
$tpl->assign('meetings', $plugin->getPeriodMeetings($type, $startDate, $endDate));
if ($plugin->get('enableCloudRecording')) {
$tpl->assign('recordings', $plugin->getRecordings($startDate, $endDate));
}
$tpl->assign('search_form', $form->returnForm());
$tpl->assign('content', $tpl->fetch('zoom/view/admin.tpl'));
$tpl->display_one_col_template();

@ -0,0 +1,4 @@
<?php
/* For licensing terms, see /license.txt */
require_once __DIR__.'/../../main/inc/global.inc.php';

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

@ -0,0 +1,108 @@
<?php
/* License: see /license.txt */
// Needed in order to show the plugin title
$strings['plugin_title'] = "Zoom Videoconference";
$strings['plugin_comment'] = "Zoom Videoconference integration in courses and sessions";
$strings['tool_enable'] = 'Zoom videoconference tool enabled';
$strings['apiKey'] = 'API Key';
$strings['apiSecret'] = 'API Secret';
$strings['enableParticipantRegistration'] = 'Enable participant registration';
$strings['enableCloudRecording'] = 'Enable cloud recording';
$strings['enableGlobalConference'] = 'Enable global conference';
$strings['enableGlobalConferencePerUser'] = 'Enable global conference per user';
$strings['globalConferenceAllowRoles'] = "Global conference link only visible for these user roles";
$strings['tool_enable_help'] = "Choose whether you want to enable the Zoom videoconference tool.
Once enabled, it will show as an additional course tool in all courses' homepage :
teachers will be able to <strong>launch</strong> a conference and student to <strong>join</strong> it.
<br/>
This plugin requires a Zoom account to manage meetings.
The Zoom API uses JSON Web Tokens (JWT) to authenticate account-level access.
<br/>
JWT apps provide an <strong>API <em>Key</em> and <em>Secret</em></strong> required to authenticate with JWT.
To get them, create a <em>JWT App</em> :
<br/>1. log into <a href=\"https://zoom.us/profile\">your Zoom profile page</a>
<br/>2. click on <em>Advanced / Application Marketplace</em>
<br/>3. click on <em><a href=\"https://marketplace.zoom.us/develop/create\">Develop / build App</a></em>
<br/>4. choose <em>JWT / Create</em>
<br/>5. fill in information about your \"App\"
(application and company names, contact name and email address)
<br/>6. click on <em>Continue</em>
Locate your API Key and Secret in the App Credentials page.
<br/>
<strong>Attention</strong>:
<br/>Zoom is <em>NOT</em> free software and specific rules apply to personal data protection.
Please check with Zoom and make sure they satisfy you and learning users.";
$strings['enableParticipantRegistration_help'] = "Requires a paying Zoom profile.
Will not work for a <em>basic</em> profile.";
$strings['enableCloudRecording_help'] = "Requires a paying Zoom profile.
Will not work for a <em>basic</em> profile.";
// please keep these lines alphabetically sorted
$strings['AllCourseUsersWereRegistered'] = "All course students were registered";
$strings['Agenda'] = "Agenda";
$strings['CopyingJoinURL'] = "Copying join URL";
$strings['CopyJoinAsURL'] = "Copy 'join as' URL";
$strings['CopyToCourse'] = "Copy to course";
$strings['CouldNotCopyJoinURL'] = "Could not copy join URL";
$strings['Course'] = "Cours";
$strings['CreatedAt'] = "Created at";
$strings['CreateLinkInCourse'] = "Create link(s) in course";
$strings['DeleteMeeting'] = "Delete meeting";
$strings['DeleteFile'] = "Delete file(s)";
$strings['Details'] = "Details";
$strings['DoIt'] = "Do it";
$strings['Duration'] = "Duration";
$strings['DurationFormat'] = "%hh%I";
$strings['DurationInMinutes'] = "Duration (in minutes)";
$strings['EndDate'] = "End Date";
$strings['Files'] = "Files";
$strings['Finished'] = "finished";
$strings['FileWasCopiedToCourse'] = "The file was copied to the course";
$strings['FileWasDeleted'] = "The file was deleted";
$strings['InstantMeeting'] = "Instant meeting";
$strings['Join'] = "Join";
$strings['JoinMeetingAsMyself'] = "Join meeting as myself";
$strings['JoinURLCopied'] = "Join URL copied";
$strings['JoinURLToSendToParticipants'] = "Join URL to send to participants";
$strings['LiveMeetings'] = "Live meetings";
$strings['LinkToFileWasCreatedInCourse'] = "A link to the file was added to the course";
$strings['MeetingDeleted'] = "Meeting deleted";
$strings['MeetingsFound'] = "Meetings found";
$strings['MeetingUpdated'] = "Meeting updated";
$strings['NewMeetingCreated'] = "New meeting created";
$strings['Password'] = "Password";
$strings['RecurringWithFixedTime'] = "Recurring with fixed time";
$strings['RecurringWithNoFixedTime'] = "Recurring with no fixed time";
$strings['RegisterAllCourseUsers'] = "Register all course users";
$strings['RegisteredUserListWasUpdated'] = "Registered user list updated";
$strings['RegisteredUsers'] = "Registered users";
$strings['RegisterNoUser'] = "Register no user";
$strings['RegisterTheseGroupMembers'] = "Register these group members";
$strings['ScheduleAMeeting'] = "Schedule a meeting";
$strings['ScheduledMeeting'] = "Scheduled meeting";
$strings['ScheduledMeetings'] = "Scheduled Meetings";
$strings['ScheduleTheMeeting'] = "Schedule the meeting";
$strings['Search'] = "Search";
$strings['Session'] = "Session";
$strings['StartDate'] = "Start Date";
$strings['Started'] = "started";
$strings['StartInstantMeeting'] = "Start instant meeting";
$strings['StartMeeting'] = "Start meeting";
$strings['StartTime'] = "Start time";
$strings['Topic'] = "Topic";
$strings['TopicAndAgenda'] = "Topic and agenda";
$strings['Type'] = "Type";
$strings['UpcomingMeetings'] = "Upcoming meetings";
$strings['UpdateMeeting'] = "Update meeting";
$strings['UpdateRegisteredUserList'] = "Update registered user list";
$strings['UserRegistration'] = "User registration";
$strings['Y-m-d H:i'] = "Y-m-d H:i";
$strings['Waiting'] = "waiting";
$strings['XRecordingOfMeetingXFromXDurationXDotX'] = "%s recording of meeting %s from %s (%s).%s";
$strings['ZoomVideoConferences'] = "Zoom Video Conferences";

@ -0,0 +1,107 @@
<?php
/* License: see /license.txt */
// Needed in order to show the plugin title
$strings['plugin_title'] = "Conférence vidéo Zoom";
$strings['plugin_comment'] = "Intégration de conférences vidéo Zoom dans les cours et les sessions";
$strings['tool_enable'] = 'Outil de conférence vidéos Zoom activé';
$strings['apiKey'] = "Clé d'API (<em>API Key</em>)";
$strings['apiSecret'] = "Code secret d'API (<em>API Secret</em>)";
$strings['enableParticipantRegistration'] = "Activer l'inscription des participants";
$strings['enableCloudRecording'] = "Activer l'enregistrement sur les serveurs de Zoom";
$strings['enableGlobalConference'] = "Activer les conférences globales";
$strings['enableGlobalConferencePerUser'] = "Activer les conférences globales par utilisateur";
$strings['globalConferenceAllowRoles'] = "Visibilité du lien de vidéo conférence global pour les profils suivant";
$strings['tool_enable_help'] = "Choisissez si vous voulez activer l'outil de conférence vidéo Zoom.
Une fois activé, il apparaitra dans les pages d'accueil de tous les cours :
les enseignants pourront <strong>démarrer</strong> une conférence et les étudiants la <strong>rejoindre</strong>.
<br/>
Ce plugin requiert un compte Zoom pour gérer les conférences.
L'API de Zoom utilise les <em>JSON Web Tokens (JWT)</em> pour autoriser l'accès à un compte.
<strong>Une <em>clé</em> et un <em>code secret</em> d'API sont requis</strong> pour s'authentifier avec JWT.
Pour les obtenir, créez une <em>JWT app</em> :
<br/>1. logguez vous sur <a href=\"https://zoom.us/profile\">Votre profil Zoom</a>
<br/>2. cliquez sur <em>Avancé / Marketplace d'application</em>
<br/>3. cliquez sur <em><a href=\"https://marketplace.zoom.us/develop/create\">Develop / build App</a></em>
<br/>4. choisissez <em>JWT / Create</em>
<br/>5. saisissez quelques informations sur votre \"App\"
(noms de l'application, de l'entreprise, nom et adresse de courriel de contact)
<br/>6. cliquez sur <em>Continue</em>
<br/>La page <em>App Credentials</em> affiche la clé (API Key) and le code secret (API Secret) à saisir ici.
<br/>
<strong>Attention</strong> :
<br/>Zoom n'est <em>PAS</em> un logiciel libre
et des règles spécifiques de protection des données personnelles s'y appliquent.
Merci de vérifier auprès de Zoom qu'elles sont satisfaisantes pour vous et les apprenants qui l'utiliseront.";
$strings['enableParticipantRegistration_help'] = "Nécessite un profil Zoom payant.
Ne fonctionnera pas pour un profil <em>de base</em>.";
$strings['enableCloudRecording_help'] = "Nécessite un profil Zoom payant.
Ne fonctionnera pas pour un profil <em>de base</em>.";
// please keep these lines alphabetically sorted
$strings['AllCourseUsersWereRegistered'] = "Tous les étudiants du cours sont inscrits";
$strings['Agenda'] = "Ordre du jour";
$strings['CopyingJoinURL'] = "Copie de l'URL pour rejoindre en cours";
$strings['CopyJoinAsURL'] = "Copier l'URL pour 'rejoindre en tant que'";
$strings['CopyToCourse'] = "Copier dans le cours";
$strings['CouldNotCopyJoinURL'] = "Échec de la copie de l'URL pour rejoindre";
$strings['Course'] = "Cours";
$strings['CreatedAt'] = "Créé à";
$strings['CreateLinkInCourse'] = "Créer dans le cours un ou des lien(s) vers le(s) fichier(s)";
$strings['DeleteMeeting'] = "Effacer la conférence";
$strings['DeleteFile'] = "Supprimer ce(s) fichier(s)";
$strings['Details'] = "Détail";
$strings['DoIt'] = "Fais-le";
$strings['Duration'] = "Durée";
$strings['DurationFormat'] = "%hh%I";
$strings['DurationInMinutes'] = "Durée (en minutes)";
$strings['EndDate'] = "Date de fin";
$strings['Files'] = "Fichiers";
$strings['Finished'] = "terminée";
$strings['FileWasCopiedToCourse'] = "Le fichier a été copié dans le cours";
$strings['FileWasDeleted'] = "Le fichier a été effacé";
$strings['InstantMeeting'] = "Conférence instantanée";
$strings['Join'] = "Rejoindre";
$strings['JoinMeetingAsMyself'] = "Rejoindre la conférence en tant que moi-même";
$strings['JoinURLCopied'] = "URL pour rejoindre copiée";
$strings['JoinURLToSendToParticipants'] = "URL pour assister à la conférence (à envoyer aux participants)";
$strings['LiveMeetings'] = "Conférences en cours";
$strings['LinkToFileWasCreatedInCourse'] = "A link to the file was added to the course";
$strings['MeetingDeleted'] = "Conférence effacée";
$strings['MeetingsFound'] = "Conférences trouvées";
$strings['MeetingUpdated'] = "Conférence mise à jour";
$strings['NewMeetingCreated'] = "Nouvelle conférence créée";
$strings['Password'] = "Mot de passe";
$strings['RecurringWithFixedTime'] = "Recurrent, à heure fixe";
$strings['RecurringWithNoFixedTime'] = "Recurrent, sans heure fixe";
$strings['RegisterAllCourseUsers'] = "Inscrire tous les utilisateurs du cours";
$strings['RegisteredUserListWasUpdated'] = "Liste des utilisateurs inscrits mise à jour";
$strings['RegisteredUsers'] = "Utilisateurs inscrits";
$strings['RegisterNoUser'] = "N'inscrire aucun utilisateur";
$strings['RegisterTheseGroupMembers'] = "Inscrire les membres de ces groupes";
$strings['ScheduleAMeeting'] = "Programmer une conférence";
$strings['ScheduledMeeting'] = "Conférence programmée";
$strings['ScheduledMeetings'] = "Conférences programmées";
$strings['ScheduleTheMeeting'] = "Programmer la conférence";
$strings['Search'] = "Rechercher";
$strings['Session'] = "Session";
$strings['StartDate'] = "Date de début";
$strings['Started'] = "démarrée";
$strings['StartInstantMeeting'] = "Démarrer une conférence instantanée";
$strings['StartMeeting'] = "Démarrer la conférence";
$strings['StartTime'] = "Heure de début";
$strings['Topic'] = "Objet";
$strings['TopicAndAgenda'] = "Objet et ordre du jour";
$strings['Type'] = "Type";
$strings['UpcomingMeeting'] = "Conférences à venir";
$strings['UpdateMeeting'] = "Mettre à jour la conférence";
$strings['UpdateRegisteredUserList'] = "Mettre à jour la liste des utilisateurs inscrits";
$strings['UserRegistration'] = "Inscription des utilisateurs";
$strings['Y-m-d H:i'] = "d/m/Y à H\hi";
$strings['Waiting'] = "en attente";
$strings['XRecordingOfMeetingXFromXDurationXDotX'] = "Enregistrement (%s) de la conférence %s de %s (%s).%s";
$strings['ZoomVideoConferences'] = "Conférences vidéo Zoom";

@ -0,0 +1,31 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
/**
* Trait BaseMeetingTrait.
* Common meeting properties definitions.
*
* @package Chamilo\PluginBundle\Zoom\API
*/
trait BaseMeetingTrait
{
/** @var string */
public $topic;
/** @var int */
public $type;
/** @var string "yyyy-MM-dd'T'HH:mm:ss'Z'" for GMT, same without 'Z' for local time (as set on zoom account) */
public $start_time;
/** @var int in minutes, for scheduled meetings only */
public $duration;
/** @var string the timezone for start_time */
public $timezone;
/** @var string description */
public $agenda;
}

@ -0,0 +1,34 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
/**
* Interface Client.
* Two implementations are currently possible : OAuth and JWT.
*
* @see https://marketplace.zoom.us/docs/api-reference/zoom-api
*
* @package Chamilo\PluginBundle\Zoom\API
*/
interface Client
{
/**
* Sends a Zoom API-compliant HTTP request and retrieves the response.
*
* On success, returns the body of the response
* On error, throws an exception with an detailed error message
*
* @param string $httpMethod GET, POST, PUT, DELETE ...
* @param string $relativePath to append to https://api.zoom.us/v2/
* @param array $parameters request query parameters
* @param object $requestBody json-encoded body of the request
*
* @throws Exception describing the error (message and code)
*
* @return string response body (not json-decoded)
*/
public function send($httpMethod, $relativePath, $parameters = [], $requestBody = null);
}

@ -0,0 +1,43 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
/**
* Class CreatedRegistration.
* An instance of this class is returned by the Zoom serveur upon recording a registrant to a meeting.
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class CreatedRegistration
{
use JsonDeserializableTrait;
/** @var int meeting ID */
public $id;
/** @var string Unique URL for this registrant to join the meeting.
* This URL should only be shared with the registrant for whom the API request was made.
* If the meeting was created with manual approval type (1), the join URL will not be returned in the response.
*/
public $join_url;
/** @var string Unique identifier of the registrant */
public $registrant_id;
/** @var string The start time for the meeting. */
public $start_time;
/** @var string Topic of the meeting. */
public $topic;
/**
* {@inheritdoc}
*/
protected function itemClass($propertyName)
{
throw new Exception("no such array property $propertyName");
}
}

@ -0,0 +1,19 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
/**
* Class CustomQuestion.
* A list of instances of this class is included in a MeetingRegistrant instance.
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class CustomQuestion
{
/** @var string */
public $title;
/** @var string */
public $value;
}

@ -0,0 +1,40 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
/**
* Class GlobalDialInNumber.
* A list of these is included in a meeting settings.
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class GlobalDialInNumber
{
use JsonDeserializableTrait;
/** @var string Country code. For example, BR. */
public $country;
/** @var string Full name of country. For example, Brazil. */
public $country_name;
/** @var string City of the number, if any. For example, Chicago. */
public $city;
/** @var string Phone number. For example, +1 2332357613. */
public $number;
/** @var string Type of number. Either "toll" or "tollfree". */
public $type;
/**
* {@inheritdoc}
*/
public function itemClass($propertyName)
{
throw new Exception("No such array property $propertyName");
}
}

@ -0,0 +1,94 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
use Firebase\JWT\JWT;
/**
* Class JWTClient.
*
* @see https://marketplace.zoom.us/docs/guides/auth/jwt
*
* @package Chamilo\PluginBundle\Zoom
*/
class JWTClient implements Client
{
public $token;
/**
* JWTClient constructor.
* Requires JWT app credentials.
*
* @param string $apiKey JWT API Key
* @param string $apiSecret JWT API Secret
*/
public function __construct($apiKey, $apiSecret)
{
$this->token = JWT::encode(
[
'iss' => $apiKey,
'exp' => (time() + 60) * 1000, // will expire in one minute
],
$apiSecret
);
}
/**
* {@inheritdoc}
*/
public function send($httpMethod, $relativePath, $parameters = [], $requestBody = null)
{
$options = [
CURLOPT_CUSTOMREQUEST => $httpMethod,
CURLOPT_ENCODING => '',
CURLOPT_HTTPHEADER => [
'authorization: Bearer '.$this->token,
'content-type: application/json',
],
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_MAXREDIRS => 10,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
];
if (!is_null($requestBody)) {
$jsonRequestBody = json_encode($requestBody);
if (false === $jsonRequestBody) {
throw new Exception('Could not generate JSON request body');
}
$options[CURLOPT_POSTFIELDS] = $jsonRequestBody;
}
$url = "https://api.zoom.us/v2/$relativePath";
if (!empty($parameters)) {
$url .= '?'.http_build_query($parameters);
}
$curl = curl_init($url);
if (false === $curl) {
throw new Exception("curl_init returned false");
}
curl_setopt_array($curl, $options);
$responseBody = curl_exec($curl);
$responseCode = curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
$curlError = curl_error($curl);
curl_close($curl);
if ($curlError) {
throw new Exception("cURL Error: $curlError");
}
if (false === $responseBody || !is_string($responseBody)) {
throw new Exception('cURL Error');
}
if (empty($responseCode)
|| $responseCode < 200
|| $responseCode >= 300
) {
throw new Exception($responseBody, $responseCode);
}
return $responseBody;
}
}

@ -0,0 +1,109 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
/**
* Trait JsonDeserializableTrait.
* Utility fonctions to help convert server-generated JSON to API class instances.
*
* @package Chamilo\PluginBundle\Zoom\API
*/
trait JsonDeserializableTrait
{
/**
* Builds a class instance from the Json description of the object.
*
* @param string $json
*
* @throws Exception on JSON-decode error or unexpected object property
*
* @return static
*/
public static function fromJson($json)
{
if (empty($json)) {
throw new Exception('Cannot JSON-decode empty string');
}
$object = json_decode($json);
if (is_null($object)) {
throw new Exception('Could not decode JSON: '.$json);
}
$instance = new static();
static::recursivelyCopyObjectProperties($object, $instance);
return $instance;
}
/**
* Returns the class name of the items to be found in the named array property.
*
* To override in classes that have a property of type array
*
* @param string $propertyName array property name
*
* @throws Exception if not implemented for this propertyName
*
* @return string class name of the items to be found in the named array property
*/
abstract public function itemClass($propertyName);
/**
* Initializes properties that can be calculated from json-decoded properties.
*
* Called at the end of method recursivelyCopyObjectProperties()
* and indirectly at the end of static method fromJson().
*
* By default it does nothing.
*/
public function initializeExtraProperties()
{
// default does nothing
}
/**
* Copies values from another object properties to an instance, recursively.
*
* @param object $source source object
* @param object $destination specific class instance, with already initialized properties
*
* @throws Exception when the source object has an unexpected property
*/
protected static function recursivelyCopyObjectProperties($source, &$destination)
{
foreach (get_object_vars($source) as $name => $value) {
if (property_exists($destination, $name)) {
if (is_object($value)) {
if (is_object($destination->$name)) {
static::recursivelyCopyObjectProperties($value, $destination->$name);
} else {
throw new Exception("Source property $name is an object, which is not expected");
}
} elseif (is_array($value)) {
if (is_array($destination->$name)) {
$itemClass = $destination->itemClass($name);
foreach ($value as $sourceItem) {
if ('string' === $itemClass) {
$destination->$name[] = $sourceItem;
} else {
$item = new $itemClass();
static::recursivelyCopyObjectProperties($sourceItem, $item);
$destination->$name[] = $item;
}
}
} else {
throw new Exception("Source property $name is an array, which is not expected");
}
} else {
$destination->$name = $value;
}
} else {
throw new Exception("Source object has property $name, which was not expected.");
}
}
$destination->initializeExtraProperties();
}
}

@ -0,0 +1,89 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
/**
* Class Meeting, minimal meeting definition required to create one from scratch or update an existing one
* Also referred to as MeetingUpdate in the API documentation
* Does not represent an actual created meeting.
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class Meeting
{
use BaseMeetingTrait;
use JsonDeserializableTrait;
const TYPE_INSTANT = 1;
const TYPE_SCHEDULED = 2;
const TYPE_RECURRING_WITH_NO_FIXED_TIME = 3;
const TYPE_RECURRING_WITH_FIXED_TIME = 8;
/** @var string password to join. [a-z A-Z 0-9 @ - _ *]. Max of 10 characters. */
public $password;
/** @var TrackingField[] Tracking fields */
public $tracking_fields;
/** @var object, only for a recurring meeting with fixed time (type 8) */
public $recurrence;
/** @var MeetingSettings */
public $settings;
/**
* Meeting constructor.
*/
protected function __construct()
{
$this->tracking_fields = [];
$this->settings = new MeetingSettings();
}
/**
* {@inheritdoc}
*/
public function itemClass($propertyName)
{
if ('tracking_fields' === $propertyName) {
return TrackingField::class;
}
throw new Exception("no such array property $propertyName");
}
/**
* Creates a meeting on the server and returns the resulting MeetingInfoGet.
*
* @param Client $client an API client
*
* @throws Exception describing the error (message and code)
*
* @return MeetingInfoGet meeting
*/
public function create($client)
{
return MeetingInfoGet::fromJson($client->send('POST', 'users/me/meetings', [], $this));
}
/**
* Creates a Meeting instance from a topic.
*
* @param string $topic
* @param int $type
*
* @throws Exception
*
* @return static
*/
protected static function fromTopicAndType($topic, $type = self::TYPE_SCHEDULED)
{
$instance = new static();
$instance->topic = $topic;
$instance->type = $type;
return $instance;
}
}

@ -0,0 +1,35 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
/**
* Class MeetingInfo
* Used to define MeetingInfoGet
* Does not seem to be used directly.
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class MeetingInfo extends Meeting
{
/** @var string */
public $created_at;
/** @var string, allows host to start the meeting as the host (without password) - not to be shared */
public $start_url;
/** @var string, for participants to join the meeting - to share with users to invite */
public $join_url;
/** @var string undocumented */
public $registration_url;
/** @var string H.323/SIP room system password */
public $h323_password;
/** @var int Personal Meeting Id. Only used for scheduled meetings and recurring meetings with no fixed time */
public $pmi;
/** @var object[] */
public $occurrences;
}

@ -0,0 +1,162 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
/**
* Class MeetingInfoGet
* Full Meeting as returned by the server, with unique identifiers and current status.
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class MeetingInfoGet extends MeetingInfo
{
/** @var string unique meeting instance ID */
public $uuid;
/** @var string meeting number */
public $id;
/** @var string host Zoom user id */
public $host_id;
/** @var string meeting status, either "waiting", "started" or "finished" */
public $status;
/** @var string undocumented */
public $pstn_password;
/** @var string Encrypted password for third party endpoints (H323/SIP). */
public $encrypted_password;
/**
* Retrieves a meeting from its numeric identifier.
*
* @param Client $client
* @param int $id
*
* @throws Exception
*
* @return static the meeting
*/
public static function fromId($client, $id)
{
return static::fromJson($client->send('GET', "meetings/$id"));
}
/**
* Updates the meeting on server.
*
* @param Client $client
*
* @throws Exception
*/
public function update($client)
{
$client->send('PATCH', 'meetings/'.$this->id, [], $this);
}
/**
* Ends the meeting on server.
*
* @param Client $client
*
* @throws Exception
*/
public function endNow($client)
{
$client->send('PUT', "meetings/$this->id/status", [], (object) ['action' => 'end']);
}
/**
* Deletes the meeting on server.
*
* @param Client $client
*
* @throws Exception
*/
public function delete($client)
{
$client->send('DELETE', "meetings/$this->id");
}
/**
* Adds a registrant to the meeting.
*
* @param Client $client
* @param MeetingRegistrant $registrant with at least 'email' and 'first_name'.
* 'last_name' will also be recorded by Zoom.
* Other properties remain ignored, or not returned by Zoom
* (at least while using profile "Pro")
* @param string $occurrenceIds separated by comma
*
* @throws Exception
*
* @return CreatedRegistration with unique join_url and registrant_id properties
*/
public function addRegistrant($client, $registrant, $occurrenceIds = '')
{
return CreatedRegistration::fromJson(
$client->send(
'POST',
"meetings/$this->id/registrants",
empty($occurrenceIds) ? [] : ['occurrence_ids' => $occurrenceIds],
$registrant
)
);
}
/**
* Removes registrants from the meeting.
*
* @param Client $client
* @param MeetingRegistrant[] $registrants registrants to remove (id and email)
* @param string $occurrenceIds separated by comma
*
* @throws Exception
*/
public function removeRegistrants($client, $registrants, $occurrenceIds = '')
{
if (!empty($registrants)) {
$client->send(
'PUT',
"meetings/$this->id/registrants/status",
empty($occurrenceIds) ? [] : ['occurrence_ids' => $occurrenceIds],
(object) [
'action' => 'cancel',
'registrants' => $registrants,
]
);
}
}
/**
* Retrieves meeting registrants.
*
* @param Client $client
*
* @throws Exception
*
* @return MeetingRegistrantListItem[] the meeting registrants
*/
public function getRegistrants($client)
{
return MeetingRegistrantList::loadMeetingRegistrants($client, $this->id);
}
/**
* Retrieves the meeting's instances.
*
* @param Client $client
*
* @throws Exception
*
* @return MeetingInstance[]
*/
public function getInstances($client)
{
return MeetingInstances::fromMeetingId($client, $this->id)->meetings;
}
}

@ -0,0 +1,53 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
/**
* Class MeetingInstance
* A meeting (numerical id) can have one or more instances (string UUID).
* Each instance has its own start time, participants and recording files.
*
* @see MeetingInstances
* @see PastMeeting for the full record
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class MeetingInstance
{
/** @var string */
public $uuid;
/** @var string */
public $start_time;
/**
* Retrieves the recording of the instance.
*
* @param Client $client
*
* @throws Exception with code 404 when there is no recording for this meeting
*
* @return RecordingMeeting the recording
*/
public function getRecordings($client)
{
return RecordingMeeting::fromJson($client->send('GET', 'meetings/'.htmlentities($this->uuid).'/recordings'));
}
/**
* Retrieves the instance's participants.
*
* @param Client $client
*
* @throws Exception
*
* @return ParticipantListItem[]
*/
public function getParticipants($client)
{
return ParticipantList::loadInstanceParticipants($client, $this->uuid);
}
}

@ -0,0 +1,55 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
/**
* Class MeetingInstances. The list of one meeting's ended instances.
*
* @see MeetingInstance
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class MeetingInstances
{
use JsonDeserializableTrait;
/** @var MeetingInstance[] List of ended meeting instances. */
public $meetings;
/**
* MeetingInstances constructor.
*/
public function __construct()
{
$this->meetings = [];
}
/**
* Retrieves a meeting's instances.
*
* @param Client $client
* @param int $meetingId
*
* @throws Exception
*
* @return MeetingInstances the meeting's instances
*/
public static function fromMeetingId($client, $meetingId)
{
return static::fromJson($client->send('GET', "past_meetings/$meetingId/instances"));
}
/**
* {@inheritdoc}
*/
public function itemClass($propertyName)
{
if ('meetings' === $propertyName) {
return MeetingInstance::class;
}
throw new Exception("No such array property $propertyName");
}
}

@ -0,0 +1,60 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
/**
* Class MeetingList. Lists Meetings.
*
* @see MeetingListItem
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class MeetingList
{
use Pagination;
const TYPE_SCHEDULED = 'scheduled'; // all valid past meetings (unexpired),
// live meetings and upcoming scheduled meetings.
const TYPE_LIVE = 'live'; // all the ongoing meetings.
const TYPE_UPCOMING = 'upcoming'; // all upcoming meetings, including live meetings.
/** @var MeetingListItem[] */
public $meetings;
/**
* MeetingList constructor.
*/
public function __construct()
{
$this->meetings = [];
}
/**
* Retrieves all meetings of a type.
*
* @param Client $client
* @param int $type TYPE_SCHEDULED, TYPE_LIVE or TYPE_UPCOMING
*
* @throws Exception
*
* @return MeetingListItem[] all meetings
*/
public static function loadMeetings($client, $type)
{
return static::loadItems('meetings', $client, 'users/me/meetings', ['type' => $type]);
}
/**
* {@inheritdoc}
*/
public function itemClass($propertyName)
{
if ('meetings' === $propertyName) {
return MeetingListItem::class;
}
throw new Exception("No such array property $propertyName");
}
}

@ -0,0 +1,45 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
/**
* Class MeetingListItem. Item of a list of meetings.
*
* @see MeetingList
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class MeetingListItem
{
use BaseMeetingTrait;
use JsonDeserializableTrait;
/** @var string unique meeting instance ID */
public $uuid;
/** @var string meeting number */
public $id;
/** @var string host Zoom user id */
public $host_id;
/** @var string */
public $created_at;
/** @var string */
public $join_url;
/** @var string truncated to 250 characters */
// public $agenda;
/**
* {@inheritdoc}
*/
public function itemClass($propertyName)
{
throw new Exception("no such array property $propertyName");
}
}

@ -0,0 +1,102 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
/**
* Class MeetingRegistrant.
* Structure of the information to send the server in order to register someone to a meeting.
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class MeetingRegistrant
{
use JsonDeserializableTrait;
/** @var string */
public $email;
/** @var string */
public $first_name;
/** @var string */
public $last_name;
/** @var string */
public $address;
/** @var string */
public $city;
/** @var string */
public $country;
/** @var string */
public $zip;
/** @var string */
public $state;
/** @var string */
public $phone;
/** @var string */
public $industry;
/** @var string */
public $org;
/** @var string */
public $job_title;
/** @var string */
public $purchasing_time_frame;
/** @var string */
public $role_in_purchase_process;
/** @var string */
public $no_of_employees;
/** @var string */
public $comments;
/** @var object[] title => value */
public $custom_questions;
/**
* MeetingRegistrant constructor.
*/
public function __construct()
{
$this->custom_questions = [];
}
/**
* @param string $email
* @param string $firstName
*
* @return MeetingRegistrant
*/
public static function fromEmailAndFirstName($email, $firstName)
{
$instance = new static();
$instance->first_name = $firstName;
$instance->email = $email;
return $instance;
}
/**
* {@inheritdoc}
*/
public function itemClass($propertyName)
{
if ('custom_questions' == $propertyName) {
return CustomQuestion::class;
}
throw new Exception("no such array property $propertyName");
}
}

@ -0,0 +1,55 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
/**
* Class MeetingRegistrantList. List of meeting registrants.
*
* @see MeetingRegistrantListItem
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class MeetingRegistrantList
{
use Pagination;
/** @var MeetingRegistrantListItem[] */
public $registrants;
/**
* MeetingRegistrantList constructor.
*/
public function __construct()
{
$this->registrants = [];
}
/**
* Retrieves all registrant for a meeting.
*
* @param Client $client
* @param int $meetingId
*
* @throws Exception
*
* @return MeetingRegistrantListItem[] all registrants of the meeting
*/
public static function loadMeetingRegistrants($client, $meetingId)
{
return static::loadItems('registrants', $client, "meetings/$meetingId/registrants");
}
/**
* {@inheritdoc}
*/
public function itemClass($propertyName)
{
if ('registrants' === $propertyName) {
return MeetingRegistrantListItem::class;
}
throw new Exception("no such array property $propertyName");
}
}

@ -0,0 +1,30 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
/**
* Class MeetingRegistrantListItem. Item in a list of meeting registrants.
*
* @see MeetingRegistrantList
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class MeetingRegistrantListItem extends MeetingRegistrant
{
/** @var string Registrant ID. */
public $id;
/** @var string The status of the registrant's registration.
* `approved`: User has been successfully approved for the webinar.
* `pending`: The registration is still pending.
* `denied`: User has been denied from joining the webinar.
*/
public $status;
/** @var string The time at which the registrant registered. */
public $create_time;
/** @var string The URL using which an approved registrant can join the webinar. */
public $join_url;
}

@ -0,0 +1,141 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
/**
* Class MeetingSettings. An instance of this class is included in each Meeting instance.
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class MeetingSettings
{
use JsonDeserializableTrait;
const APPROVAL_TYPE_AUTOMATICALLY_APPROVE = 0;
const APPROVAL_TYPE_MANUALLY_APPROVE = 1;
const APPROVAL_TYPE_NO_REGISTRATION_REQUIRED = 2;
const REGISTRATION_TYPE_REGISTER_ONCE_ATTEND_ANY = 1;
const REGISTRATION_TYPE_REGISTER_EACH = 2;
const REGISTRATION_TYPE_REGISTER_ONCE_CHOOSE = 3;
/** @var bool Start video when the host joins the meeting */
public $host_video;
/** @var bool Start video when participants join the meeting */
public $participant_video;
/** @var bool Host meeting in China */
public $cn_meeting;
/** @var bool Host meeting in India */
public $in_meeting;
/** @var bool Allow participants to join the meeting before the host starts the meeting.
* Only used for scheduled or recurring meetings.
*/
public $join_before_host;
/** @var bool Mute participants upon entry */
public $mute_upon_entry;
/** @var bool Add watermark when viewing a shared screen */
public $watermark;
/** @var bool Use a personal meeting ID.
* Only used for scheduled meetings and recurring meetings with no fixed time.
*/
public $use_pmi;
/** @var int Enable registration and set approval for the registration.
* Note that this feature requires the host to be of **Licensed** user type.
* **Registration cannot be enabled for a basic user.**
*/
public $approval_type;
/** @var int Used for recurring meeting with fixed time only. */
public $registration_type;
/** @var string either both, telephony or voip */
public $audio;
/** @var string either local, cloud or none */
public $auto_recording;
/** @var bool @deprecated only signed in users can join this meeting */
public $enforce_login;
/** @var string @deprecated only signed in users with specified domains can join meetings */
public $enforce_login_domains;
/** @var string Alternative host's emails or IDs: multiple values separated by a comma. */
public $alternative_hosts;
/** @var bool Close registration after event date */
public $close_registration;
/** @var bool Enable waiting room */
public $waiting_room;
/** @var string[] List of global dial-in countries */
public $global_dial_in_countries;
/** @var GlobalDialInNumber[] Global Dial-in Countries/Regions */
public $global_dial_in_numbers;
/** @var string Contact name for registration */
public $contact_name;
/** @var string Contact email for registration */
public $contact_email;
/** @var bool Send confirmation email to registrants upon successful registration */
public $registrants_confirmation_email;
/** @var bool Send email notifications to registrants about approval, cancellation, denial of the registration.
* The value of this field must be set to true in order to use the `registrants_confirmation_email` field.
*/
public $registrants_email_notification;
/** @var bool Only authenticated users can join meetings. */
public $meeting_authentication;
/** @var string Meeting authentication option id. */
public $authentication_option;
/** @var string
* @see https://support.zoom.us/hc/en-us/articles/360037117472-Authentication-Profiles-for-Meetings-and-Webinars#h_5c0df2e1-cfd2-469f-bb4a-c77d7c0cca6f
*/
public $authentication_domains;
/** @var string
* @see https://support.zoom.us/hc/en-us/articles/360037117472-Authentication-Profiles-for-Meetings-and-Webinars#h_5c0df2e1-cfd2-469f-bb4a-c77d7c0cca6f
*/
public $authentication_name;
/**
* MeetingSettings constructor.
*/
public function __construct()
{
$this->global_dial_in_countries = [];
$this->global_dial_in_numbers = [];
}
/**
* {@inheritdoc}
*/
public function itemClass($propertyName)
{
if ('global_dial_in_countries' === $propertyName) {
return 'string';
}
if ('global_dial_in_numbers' === $propertyName) {
return GlobalDialInNumber::class;
}
throw new Exception("No such array property $propertyName");
}
}

@ -0,0 +1,70 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
/**
* Trait Pagination
* properties for Pagination objects, which are paginated lists of items,
* retrieved in chunks from the server over one or several API calls, one per page.
*
* @package Chamilo\PluginBundle\Zoom\API
*/
trait Pagination
{
use JsonDeserializableTrait;
/** @var int The number of pages returned for the request made. */
public $page_count;
/** @var int The page number of the current results, counting from 1 */
public $page_number;
/** @var int The number of records returned with a single API call. Default 30, max 300. */
public $page_size;
/** @var int The total number of all the records available across pages. */
public $total_records;
/**
* Retrieves all items from the server, possibly generating several API calls.
*
* @param string $arrayPropertyName item array property name
* @param Client $client
* @param string $relativePath relative path to pass to Client::send
* @param array $parameters parameter array to pass to Client::send
*
* @throws Exception
*
* @return array united list of items
*/
protected static function loadItems($arrayPropertyName, $client, $relativePath, $parameters = [])
{
$items = [];
$pageCount = 1;
$pageSize = 300;
$totalRecords = 0;
for ($pageNumber = 1; $pageNumber <= $pageCount; $pageNumber++) {
$response = static::fromJson(
$client->send(
'GET',
$relativePath,
array_merge(['page_size' => $pageSize, 'page_number' => $pageNumber], $parameters)
)
);
$items = array_merge($items, $response->$arrayPropertyName);
if (0 === $totalRecords) {
$pageCount = $response->page_count;
$pageSize = $response->page_size;
$totalRecords = $response->total_records;
}
}
if (count($items) !== $totalRecords) {
error_log('Zoom announced '.$totalRecords.' records but returned '.count($items));
}
return $items;
}
}

@ -0,0 +1,73 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
/**
* Trait PaginationToken
* properties for PaginationToken objects, which are paginated lists of items,
* retrieved in chunks from the server over one or several API calls, one per page.
*
* @package Chamilo\PluginBundle\Zoom\API
*/
trait PaginationToken
{
use JsonDeserializableTrait;
/** @var int The number of pages returned for the request made. */
public $page_count;
/** @var int The number of records returned within a single API call. Default 30, max 300. */
public $page_size;
/** @var int The number of all records available across pages. */
public $total_records;
/** @var string The next page token is used to paginate through large result sets.
* A next page token will be returned whenever the set of available results exceeds the current page size.
* The expiration period for this token is 15 minutes.
*/
public $next_page_token;
/**
* Retrieves all items from the server, possibly generating several API calls.
*
* @param string $arrayPropertyName item array property name
* @param Client $client
* @param string $relativePath relative path to pass to Client::send
* @param array $parameters parameter array to pass to Client::send
*
* @throws Exception
*
* @return array united list of items
*/
protected static function loadItems($arrayPropertyName, $client, $relativePath, $parameters = [])
{
$items = [];
$pageSize = 300;
$totalRecords = 0;
$nextPageToken = '';
do {
$response = static::fromJson(
$client->send(
'GET',
$relativePath,
array_merge(['page_size' => $pageSize, 'next_page_token' => $nextPageToken], $parameters)
)
);
$items = array_merge($items, $response->$arrayPropertyName);
$nextPageToken = $response->next_page_token;
if (0 === $totalRecords) {
$pageSize = $response->page_size;
$totalRecords = $response->total_records;
}
} while (!empty($nextPagetoken));
if (count($items) !== $totalRecords) {
error_log('Zoom announced '.$totalRecords.' records but returned '.count($items));
}
return $items;
}
}

@ -0,0 +1,60 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
/**
* Class ParticipantList
* List of past meeting instance participants.
*
* @see ParticipantListItem;
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class ParticipantList
{
use PaginationToken;
/** @var ParticipantListItem[] */
public $participants;
/**
* ParticipantList constructor.
*/
public function __construct()
{
$this->participants = [];
}
/**
* Retrieves a meeting instance's participants.
*
* @param Client $client
* @param string $instanceUUID
*
* @throws Exception
*
* @return ParticipantListItem[] participants
*/
public static function loadInstanceParticipants($client, $instanceUUID)
{
return static::loadItems(
'participants',
$client,
'past_meetings/'.htmlentities($instanceUUID).'/participants'
);
}
/**
* {@inheritdoc}
*/
public function itemClass($propertyName)
{
if ('participants' === $propertyName) {
return ParticipantListItem::class;
}
throw new Exception("No such array property $propertyName");
}
}

@ -0,0 +1,23 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
/**
* Class ParticipantListItem. Item in a list of past meeting instance participants.
*
* @see ParticipantList
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class ParticipantListItem
{
/** @var string participant UUID */
public $id;
/** @var string display name */
public $name;
/** @var string Email address of the user; will be returned if the user logged into Zoom to join the meeting. */
public $user_email;
}

@ -0,0 +1,78 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
/**
* Class PastMeeting.
* A past meeting, really a past meeting instance, as returned from the server.
*
* Each past meeting instance is identified by its own UUID.
* Many past meeting instances can be part of the same meeting, identified by property 'id'.
* Each instance has its own start time, participants and recording files.
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class PastMeeting extends Meeting
{
/** @var string unique meeting instance ID */
public $uuid;
/** @var string meeting number */
public $id;
/** @var string host Zoom user id */
public $host_id;
/** @var string user display name */
public $user_name;
/** @var string */
public $user_email;
/** @var string "yyyy-MM-dd'T'HH:mm:ss'Z'" (GMT) */
public $start_time;
/** @var string "yyyy-MM-dd'T'HH:mm:ss'Z'" (GMT) */
public $end_time;
/** @var int sum of meeting minutes from all participants in the meeting. */
public $total_minutes;
/** @var int number of meeting participants */
public $participants_count;
/** @var string undocumented */
public $dept;
/**
* Retrieves a past meeting instance from its identifier.
*
* @param Client $client
* @param string $uuid
*
* @throws Exception
*
* @return PastMeeting the past meeting
*/
public static function fromUUID($client, $uuid)
{
return static::fromJson($client->send('GET', 'past_meetings/'.htmlentities($uuid)));
}
/**
* Retrieves information on participants from a past meeting instance.
*
* @param Client $client
*
* @throws Exception
*
* @return ParticipantListItem[] participants
*/
public function getParticipants($client)
{
return ParticipantList::loadInstanceParticipants($client, $this->uuid);
}
}

@ -0,0 +1,118 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
/**
* Class RecordingFile. A video, audio or text file, part of a past meeting instance recording.
*
* @see RecordingMeeting
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class RecordingFile
{
use JsonDeserializableTrait;
/** @var string The recording file ID. Included in the response of general query. */
public $id;
/** @var string The meeting ID. */
public $meeting_id;
/** @var string The recording start time. */
public $recording_start;
/** @var string The recording end time. Response in general query. */
public $recording_end;
/** @var string The recording file type. The value of this field could be one of the following:<br>
* `MP4`: Video file of the recording.<br>
* `M4A` Audio-only file of the recording.<br>
* `TIMELINE`: Timestamp file of the recording.
* To get a timeline file, the "Add a timestamp to the recording" setting must be enabled in the recording settings
* (https://support.zoom.us/hc/en-us/articles/203741855-Cloud-recording#h_3f14c3a4-d16b-4a3c-bbe5-ef7d24500048).
* The time will display in the host's timezone, set on their Zoom profile.
* `TRANSCRIPT`: Transcription file of the recording.
* `CHAT`: A TXT file containing in-meeting chat messages that were sent during the meeting.
* `CC`: File containing closed captions of the recording.
* A recording file object with file type of either `CC` or `TIMELINE` **does not have** the following properties:
* `id`, `status`, `file_size`, `recording_type`, and `play_url`.
*/
public $file_type;
/** @var int The recording file size. */
public $file_size;
/** @var string The URL using which a recording file can be played. */
public $play_url;
/** @var string The URL using which the recording file can be downloaded.
* To access a private or password protected cloud recording, you must use a [Zoom JWT App Type]
* (https://marketplace.zoom.us/docs/guides/getting-started/app-types/create-jwt-app).
* Use the generated JWT token as the value of the `access_token` query parameter
* and include this query parameter at the end of the URL as shown in the example.
* Example: `https://api.zoom.us/recording/download/{{ Download Path }}?access_token={{ JWT Token }}`
*/
public $download_url;
/** @var string The recording status. "completed". */
public $status;
/** @var string The time at which recording was deleted. Returned in the response only for trash query. */
public $deleted_time;
/** @var string The recording type. The value of this field can be one of the following:
* `shared_screen_with_speaker_view(CC)`
* `shared_screen_with_speaker_view`
* `shared_screen_with_gallery_view`
* `speaker_view`
* `gallery_view`
* `shared_screen`
* `audio_only`
* `audio_transcript`
* `chat_file`
* `TIMELINE`
*/
public $recording_type;
/**
* Builds the recording file download URL with the access_token query parameter.
*
* @see RecordingFile::$download_url
*
* @param string $token
*
* @return string full URL
*/
public function getFullDownloadURL($token)
{
return $this->download_url.'?access_token='.$token;
}
/**
* Deletes the file.
*
* @param Client $client
*
* @throws Exception
*/
public function delete($client)
{
$client->send(
'DELETE',
"/meetings/$this->meeting_id/recordings/$this->id",
['action' => 'delete']
);
}
/**
* {@inheritdoc}
*/
public function itemClass($propertyName)
{
throw new Exception("No such array property $propertyName");
}
}

@ -0,0 +1,68 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use DateTime;
use Exception;
/**
* Class RecordingList. A list of past meeting instance recordings generated between two dates.
*
* @see RecordingMeeting
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class RecordingList
{
use PaginationToken;
/** @var string Start Date */
public $from;
/** @var string End Date */
public $to;
/** @var RecordingMeeting[] List of recordings */
public $meetings;
public function __construct()
{
$this->meetings = [];
}
/**
* Retrieves all recordings from a period of time.
*
* @param Client $client
* @param DateTime $startDate first day of the period
* @param DateTime $endDate last day of the period
*
* @throws Exception
*
* @return RecordingMeeting[] all recordings from that period
*/
public static function loadPeriodRecordings($client, $startDate, $endDate)
{
return static::loadItems(
'meetings',
$client,
'users/me/recordings',
[
'from' => $startDate->format('Y-m-d'),
'to' => $endDate->format('Y-m-d'),
]
);
}
/**
* {@inheritdoc}
*/
public function itemClass($propertyName)
{
if ('meetings' === $propertyName) {
return RecordingMeeting::class;
}
throw new Exception("No such array property $propertyName");
}
}

@ -0,0 +1,91 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
/**
* Class RecordingMeeting.
* A meeting instance can be recorded, hence creating an instance of this class.
* Contains a list of recording files.
*
* @see PastMeeting
* @see RecordingFile
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class RecordingMeeting
{
use JsonDeserializableTrait;
/** @var string Unique Meeting Identifier. Each instance of the meeting will have its own UUID. */
public $uuid;
/** @var string Meeting ID - also known as the meeting number. */
public $id;
/** @var string Unique Identifier of the user account. */
public $account_id;
/** @var string ID of the user set as host of meeting. */
public $host_id;
/** @var string Meeting topic. */
public $topic;
/** @var int undocumented */
public $type;
/** @var string The time at which the meeting started. */
public $start_time;
/** @var string undocumented */
public $timezone;
/** @var int Meeting duration. */
public $duration;
/** @var string Total size of the recording. */
public $total_size;
/** @var string Number of recording files returned in the response of this API call. */
public $recording_count;
/** @var string undocumented */
public $share_url;
/** @var RecordingFile[] List of recording file. */
public $recording_files;
/**
* RecordingMeeting constructor.
*/
public function __construct()
{
$this->recording_files = [];
}
/**
* Deletes the recording on the server.
*
* @param Client $client
*
* @throws Exception
*/
public function delete($client)
{
$client->send('DELETE', 'meetings/'.htmlentities($this->uuid).'/recordings', ['action' => 'delete']);
}
/**
* {@inheritdoc}
*/
public function itemClass($propertyName)
{
if ('recording_files' === $propertyName) {
return RecordingFile::class;
}
throw new Exception("No such array property $propertyName");
}
}

@ -0,0 +1,30 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom\API;
use Exception;
/**
* Class TrackingField. Instances of this class can be listed in a meeting object.
*
* @package Chamilo\PluginBundle\Zoom\API
*/
class TrackingField
{
use JsonDeserializableTrait;
/** @var string Tracking fields type */
public $field;
/** @var string Tracking fields value */
public $value;
/**
* {@inheritdoc}
*/
public function itemClass($propertyName)
{
throw new Exception("no such array property $propertyName");
}
}

@ -0,0 +1,69 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom;
use Exception;
/**
* Class CourseMeeting. A remote Zoom meeting linked to a local course.
* An instance of this class is required to create a remote meeting from scratch.
*
* @package Chamilo\PluginBundle\Zoom
*/
class CourseMeeting extends API\Meeting
{
use CourseMeetingTrait;
use DisplayableMeetingTrait;
/**
* Creates a CourseMeeting instance from a topic.
*
* @param int $courseId
* @param int $sessionId
* @param string $topic
* @param int $type
*
* @throws Exception
*
* @return static
*/
public static function fromCourseSessionTopicAndType($courseId, $sessionId, $topic, $type)
{
$instance = static::fromTopicAndType($topic, $type);
$instance->setCourseAndSessionId($courseId, $sessionId);
$instance->initializeDisplayableProperties();
return $instance;
}
/**
* {@inheritdoc}
*
* @throws Exception
*/
public function initializeExtraProperties()
{
parent::initializeExtraProperties();
$this->decodeAndRemoveTag();
$this->initializeDisplayableProperties();
}
/**
* {@inheritdoc}
*
* Creates a tagged meeting
*
* @return CourseMeetingInfoGet
*/
public function create($client)
{
$new = new CourseMeetingInfoGet();
$this->tagAgenda();
static::recursivelyCopyObjectProperties(parent::create($client), $new);
$this->untagAgenda();
return $new;
}
}

@ -0,0 +1,57 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom;
use Exception;
/**
* Class CourseMeetingInfoGet. A full Meeting as returned by the server and locally linked to a course.
*
* @package Chamilo\PluginBundle\Zoom
*/
class CourseMeetingInfoGet extends API\MeetingInfoGet
{
use CourseMeetingTrait;
use DisplayableMeetingTrait;
/**
* {@inheritdoc}
*
* @throws Exception
*/
public function initializeExtraProperties()
{
parent::initializeExtraProperties();
$this->decodeAndRemoveTag();
$this->initializeDisplayableProperties();
}
/**
* Updates the meeting on server, tagging it so to remember its course and session.
*
* @param API\Client $client
*
* @throws Exception
*/
public function update($client)
{
$this->tagAgenda();
parent::update($client);
$this->untagAgenda();
}
/**
* Retrieves meeting registrants.
*
* @param API\Client $client
*
* @throws Exception
*
* @return UserMeetingRegistrantListItem[]
*/
public function getUserRegistrants($client)
{
return UserMeetingRegistrantList::loadUserMeetingRegistrants($client, $this->id);
}
}

@ -0,0 +1,26 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom;
/**
* Class CourseMeetingList. A List of course meetings.
*
* @see CourseMeetingListItem
*
* @package Chamilo\PluginBundle\Zoom
*/
class CourseMeetingList extends API\MeetingList
{
/**
* {@inheritdoc}
*/
public function itemClass($propertyName)
{
if ('meetings' === $propertyName) {
return CourseMeetingListItem::class;
}
return parent::itemClass($propertyName);
}
}

@ -0,0 +1,31 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom;
use Exception;
/**
* Class CourseMeetingListItem. An item from a course meeting list.
*
* @see CourseMeetingList
*
* @package Chamilo\PluginBundle\Zoom
*/
class CourseMeetingListItem extends API\MeetingListItem
{
use CourseMeetingTrait;
use DisplayableMeetingTrait;
/**
* {@inheritdoc}
*
* @throws Exception
*/
public function initializeExtraProperties()
{
parent::initializeExtraProperties();
$this->decodeAndRemoveTag();
$this->initializeDisplayableProperties();
}
}

@ -0,0 +1,142 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom;
use Chamilo\CoreBundle\Entity\Course;
use Chamilo\CoreBundle\Entity\Session;
use Chamilo\CoreBundle\Entity\SessionRelCourseRelUser;
use Chamilo\UserBundle\Entity\User;
use Database;
/**
* Trait CourseMeetingTrait.
* A Zoom meeting linked to a (course, session) pair.
* The course and session IDs are stored in the meeting agenda on write operations, read and removed on retrieval.
*
* @package Chamilo\PluginBundle\Zoom
*/
trait CourseMeetingTrait
{
use API\BaseMeetingTrait;
/** @var bool whether the agenda contains the course and session identifiers */
public $isTaggedWithCourseId;
/** @var int meeting course id as found in the agenda field */
public $courseId;
/** @var Course meeting course */
public $course;
/** @var int meeting session id as found in the agenda field */
public $sessionId;
/** @var Session meeting session */
public $session;
public function loadCourse()
{
$this->course = Database::getManager()->getRepository('ChamiloCoreBundle:Course')->find($this->courseId);
}
public function loadSession()
{
$this->session = $this->sessionId
? Database::getManager()->getRepository('ChamiloCoreBundle:Session')->find($this->sessionId)
: null;
}
public function setCourseAndSessionId($courseId, $sessionId)
{
$this->courseId = $courseId;
$this->sessionId = $sessionId;
}
public function tagAgenda()
{
$this->agenda = $this->getUntaggedAgenda().$this->getTag();
}
public function untagAgenda()
{
$this->agenda = $this->getUntaggedAgenda();
}
/**
* Builds the list of users that can register into this meeting.
*
* @return User[] the list of users
*/
public function getCourseAndSessionUsers()
{
if ($this->sessionId && is_null($this->session)) {
$this->loadSession();
}
if (is_null($this->course)) {
$this->loadCourse();
}
$users = [];
if (is_null($this->session)) {
$users = Database::getManager()->getRepository(
'ChamiloCoreBundle:Course'
)->getSubscribedUsers($this->course)->getQuery()->getResult();
} else {
$subscriptions = $this->session->getUserCourseSubscriptionsByStatus($this->course, Session::STUDENT);
if ($subscriptions) {
/** @var SessionRelCourseRelUser $sessionCourseUser */
foreach ($subscriptions as $sessionCourseUser) {
$users[$sessionCourseUser->getUser()->getUserId()] = $sessionCourseUser->getUser();
}
}
}
return $users;
}
/**
* @param int $courseId
* @param int $sessionId
*
* @return bool whether both values match this CourseMeeting
*/
public function matches($courseId, $sessionId)
{
return $courseId == $this->courseId && $sessionId == $this->sessionId;
}
protected function decodeAndRemoveTag()
{
$this->isTaggedWithCourseId = preg_match(self::getTagPattern(), $this->agenda, $matches);
if ($this->isTaggedWithCourseId) {
$this->setCourseAndSessionId($matches['courseId'], $matches['sessionId']);
$this->untagAgenda();
} else {
$this->setCourseAndSessionId(0, 0);
}
$this->course = null;
$this->session = null;
}
protected function getUntaggedAgenda()
{
return str_replace($this->getTag(), '', $this->agenda);
}
/**
* @return string a tag to append to a meeting agenda so to link it to a (course, session) tuple
*/
private function getTag()
{
return "\n(course $this->courseId, session $this->sessionId)";
}
private static function getTagPattern()
{
return '/course (?P<courseId>\d+), session (?P<sessionId>\d+)/m';
}
}

@ -0,0 +1,74 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom;
use DateInterval;
use DateTime;
use DateTimeZone;
use Exception;
/**
* Trait DisplayableMeetingTrait. Extra properties to help formatting web views and time operations.
*
* @package Chamilo\PluginBundle\Zoom
*/
trait DisplayableMeetingTrait
{
use API\BaseMeetingTrait;
/** @var string meeting type name */
public $typeName;
/** @var DateTime meeting start time as a DateTime instance */
public $startDateTime;
/** @var string meeting formatted start time */
public $formattedStartTime;
/** @var DateInterval meeting duration as a DateInterval instance */
public $durationInterval;
/** @var string meeting formatted duration */
public $formattedDuration;
/** @var string */
public $statusName;
/**
* @throws Exception on unexpected start_time or duration
*/
public function initializeDisplayableProperties()
{
$this->typeName = [
API\Meeting::TYPE_INSTANT => get_lang('InstantMeeting'),
API\Meeting::TYPE_SCHEDULED => get_lang('ScheduledMeeting'),
API\Meeting::TYPE_RECURRING_WITH_NO_FIXED_TIME => get_lang('RecurringWithNoFixedTime'),
API\Meeting::TYPE_RECURRING_WITH_FIXED_TIME => get_lang('RecurringWithFixedTime'),
][$this->type];
if (property_exists($this, 'status')) {
$this->statusName = [
'waiting' => get_lang('Waiting'),
'started' => get_lang('Started'),
'finished' => get_lang('Finished'),
][$this->status];
}
$this->startDateTime = null;
$this->formattedStartTime = '';
$this->durationInterval = null;
$this->formattedDuration = '';
if (!empty($this->start_time)) {
$this->startDateTime = new DateTime($this->start_time);
$this->startDateTime->setTimezone(new DateTimeZone(date_default_timezone_get()));
$this->formattedStartTime = $this->startDateTime->format(get_lang('Y-m-d H:i'));
}
if (!empty($this->duration)) {
$now = new DateTime();
$later = new DateTime();
$later->add(new DateInterval('PT'.$this->duration.'M'));
$this->durationInterval = $later->diff($now);
$this->formattedDuration = $this->durationInterval->format(get_lang('DurationFormat'));
}
}
}

@ -0,0 +1,28 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom;
use Exception;
/**
* Class File. A RecordingFile with extra help properties for the web view.
*
* @package Chamilo\PluginBundle\Zoom
*/
class File extends API\RecordingFile
{
/** @var string */
public $formattedFileSize;
/**
* {@inheritdoc}
*
* @throws Exception
*/
public function initializeExtraProperties()
{
parent::initializeExtraProperties();
$this->formattedFileSize = format_file_size($this->file_size);
}
}

@ -0,0 +1,65 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom;
use DateInterval;
use DateTime;
use DateTimeZone;
use Exception;
/**
* Class Recording. A RecordingMeeting with extra help properties and a list of File instances
* (instead of RecordingFile instances).
*
* @package Chamilo\PluginBundle\Zoom
*/
class Recording extends API\RecordingMeeting
{
/** @var File[] List of recording file. */
public $recording_files;
/** @var DateTime */
public $startDateTime;
/** @var string */
public $formattedStartTime;
/** @var DateInterval */
public $durationInterval;
/** @var string */
public $formattedDuration;
/**
* {@inheritdoc}
*
* @throws Exception
*/
public function initializeExtraProperties()
{
parent::initializeExtraProperties();
$this->startDateTime = new DateTime($this->start_time);
$this->startDateTime->setTimezone(new DateTimeZone(date_default_timezone_get()));
$this->formattedStartTime = $this->startDateTime->format(get_lang('Y-m-d H:i'));
$now = new DateTime();
$later = new DateTime();
$later->add(new DateInterval('PT'.$this->duration.'M'));
$this->durationInterval = $later->diff($now);
$this->formattedDuration = $this->durationInterval->format(get_lang('DurationFormat'));
}
/**
* {@inheritdoc}
*/
public function itemClass($propertyName)
{
if ('recording_files' === $propertyName) {
return File::class;
}
return parent::itemClass($propertyName);
}
}

@ -0,0 +1,51 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom;
use DateTime;
use Exception;
/**
* Class RecordingList. A list of past meeting instance recordings generated between two dates.
*
* @package Chamilo\PluginBundle\Zoom
*/
class RecordingList extends API\RecordingList
{
/**
* {@inheritdoc}
*/
public function itemClass($propertyName)
{
if ('meetings' === $propertyName) {
return Recording::class;
}
return parent::itemClass($propertyName);
}
/**
* Retrieves all recordings from a period of time.
*
* @param API\Client $client
* @param DateTime $startDate first day of the period
* @param DateTime $endDate last day of the period
*
* @throws Exception
*
* @return Recording[] all recordings from that period
*/
public static function loadRecordings($client, $startDate, $endDate)
{
return static::loadItems(
'meetings',
$client,
'users/me/recordings',
[
'from' => $startDate->format('Y-m-d'),
'to' => $endDate->format('Y-m-d'),
]
);
}
}

@ -0,0 +1,51 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom;
use Chamilo\UserBundle\Entity\User;
use Exception;
/**
* Class UserMeetingRegistrant. Used to register a local user to a meeting.
*
* @package Chamilo\PluginBundle\Zoom
*/
class UserMeetingRegistrant extends API\MeetingRegistrant
{
use UserMeetingRegistrantTrait;
/**
* {@inheritdoc}
*
* @throws Exception
*/
public function initializeExtraProperties()
{
parent::initializeExtraProperties();
$this->decodeAndRemoveTag();
$this->computeFullName();
}
/**
* Creates a UserMeetingRegistrant instance from a user.
*
* @param User $user
*
* @throws Exception
*
* @return static
*/
public static function fromUser($user)
{
$instance = new static();
$instance->email = $user->getEmail();
$instance->first_name = $user->getFirstname();
$instance->last_name = $user->getLastname();
$instance->userId = $user->getId();
$instance->user = $user;
$instance->computeFullName();
return $instance;
}
}

@ -0,0 +1,43 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom;
use Exception;
/**
* Class UserMeetingRegistrantList. A list of users registered to a meeting.
*
* @see UserMeetingRegistrantListItem
*
* @package Chamilo\PluginBundle\Zoom
*/
class UserMeetingRegistrantList extends API\MeetingRegistrantList
{
/**
* {@inheritdoc}
*/
public function itemClass($propertyName)
{
if ('registrants' === $propertyName) {
return UserMeetingRegistrantListItem::class;
}
return parent::itemClass($propertyName);
}
/**
* Retrieves all registrant for a meeting.
*
* @param API\Client $client
* @param int $meetingId
*
* @throws Exception
*
* @return UserMeetingRegistrantListItem[] all registrants of the meeting
*/
public static function loadUserMeetingRegistrants($client, $meetingId)
{
return static::loadItems('registrants', $client, "meetings/$meetingId/registrants");
}
}

@ -0,0 +1,30 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom;
use Exception;
/**
* Class UserMeetingRegistrantListItem. An item of a user registrant list.
*
* @see UserMeetingRegistrantList
*
* @package Chamilo\PluginBundle\Zoom
*/
class UserMeetingRegistrantListItem extends API\MeetingRegistrantListItem
{
use UserMeetingRegistrantTrait;
/**
* {@inheritdoc}
*
* @throws Exception
*/
public function initializeExtraProperties()
{
parent::initializeExtraProperties();
$this->decodeAndRemoveTag();
$this->computeFullName();
}
}

@ -0,0 +1,89 @@
<?php
/* For licensing terms, see /license.txt */
namespace Chamilo\PluginBundle\Zoom;
use Chamilo\UserBundle\Entity\User;
use Database;
/**
* Trait UserMeetingRegistrantTrait.
* A Zoom meeting registrant linked to a local user.
* The user id is stored in the registrant's email address on write operations, read and removed on retrieval.
*
* @package Chamilo\PluginBundle\Zoom
*/
trait UserMeetingRegistrantTrait
{
/** @var bool whether the remote zoom record contains a local user's identifier */
public $isTaggedWithUserId;
/** @var int */
public $userId;
/** @var User */
public $user;
/** @var string */
public $fullName;
public function loadUser()
{
$this->user = Database::getManager()->getRepository('ChamiloUserBundle:User')->find($this->userId);
}
public function setUserId($userId)
{
$this->userId = $userId;
}
public function tagEmail()
{
$this->email = str_replace('@', $this->getTag(), $this->getUntaggedEmail());
}
public function untagEmail()
{
$this->email = $this->getUntaggedEmail();
}
public function matches($userId)
{
return $userId == $this->userId;
}
public function computeFullName()
{
$this->fullName = api_get_person_name($this->first_name, $this->last_name);
}
protected function decodeAndRemoveTag()
{
$this->isTaggedWithUserId = preg_match(self::getTagPattern(), $this->email, $matches);
if ($this->isTaggedWithUserId) {
$this->setUserId($matches['userId']);
$this->untagEmail();
} else {
$this->setUserId(0);
}
$this->user = null;
}
protected function getUntaggedEmail()
{
return str_replace($this->getTag(), '@', $this->email);
}
/**
* @return string a tag to append to a registrant comments so to link it to a user
*/
private function getTag()
{
return "+user_$this->userId@";
}
private static function getTagPattern()
{
return '/\+user_(?P<userId>\d+)@/m';
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,68 @@
<?php
/* For license terms, see /license.txt */
use Chamilo\PluginBundle\Zoom\API\MeetingSettings;
if (!isset($returnURL)) {
exit;
}
$course_plugin = 'zoom'; // needed in order to load the plugin lang variables
$logInfo = [
'tool' => 'Videoconference Zoom',
];
Event::registerLog($logInfo);
$interbreadcrumb[] = [ // used in templates
'url' => $returnURL,
'name' => get_lang('ZoomVideoConferences'),
];
if (!array_key_exists('meetingId', $_REQUEST)) {
throw new Exception('MeetingNotFound');
}
$plugin = ZoomPlugin::create();
$meeting = $plugin->getMeeting($_REQUEST['meetingId']);
$tpl = new Template($meeting->id);
if ($plugin->userIsConferenceManager()) {
// user can edit, start and delete meeting
$tpl->assign('isConferenceManager', true);
$tpl->assign('editMeetingForm', $plugin->getEditMeetingForm($meeting)->returnForm());
$tpl->assign('deleteMeetingForm', $plugin->getDeleteMeetingForm($meeting, $returnURL)->returnForm());
if ($plugin->get('enableParticipantRegistration')
&& MeetingSettings::APPROVAL_TYPE_NO_REGISTRATION_REQUIRED != $meeting->settings->approval_type) {
list($registerParticipantForm, $registrants) = $plugin->getRegisterParticipantForm($meeting);
$tpl->assign('registerParticipantForm', $registerParticipantForm->returnForm());
$tpl->assign('registrants', $registrants); // FIXME cache
}
if ($plugin->get('enableCloudRecording')
&& 'cloud' === $meeting->settings->auto_recording
// && 'finished' === $meeting->status
) {
list($fileForm, $recordings) = $plugin->getFileForm($meeting);
$tpl->assign('fileForm', $fileForm->returnForm());
$tpl->assign('recordings', $recordings);
}
} elseif (MeetingSettings::APPROVAL_TYPE_NO_REGISTRATION_REQUIRED != $meeting->settings->approval_type) {
$userId = api_get_user_id();
try {
foreach ($plugin->getRegistrants($meeting) as $registrant) {
if ($registrant->userId == $userId) {
$tpl->assign('currentUserJoinURL', $registrant->join_url);
break;
}
}
} catch (Exception $exception) {
Display::addFlash(
Display::return_message($exception->getMessage(), 'error')
);
}
}
$tpl->assign('meeting', $meeting);
$tpl->assign('content', $tpl->fetch('zoom/view/meeting.tpl'));
$tpl->display_one_col_template();

@ -0,0 +1,13 @@
<?php
/* For license terms, see /license.txt */
require_once __DIR__.'/config.php';
api_protect_admin_script();
$returnURL = 'admin.php';
// the section (for the tabs)
$this_section = SECTION_PLATFORM_ADMIN;
include "meeting.php";

@ -0,0 +1,13 @@
<?php
/* For license terms, see /license.txt */
require_once __DIR__.'/config.php';
api_protect_course_script(true);
$returnURL = 'start.php?cId='.api_get_course_id().'&sessionId='.api_get_session_id();
// the section (for the tabs)
$this_section = SECTION_COURSES;
include "meeting.php";

@ -0,0 +1,4 @@
<?php
/* For license terms, see /license.txt */
$plugin_info = ZoomPlugin::create()->get_info();

@ -0,0 +1,41 @@
<?php
/* For license terms, see /license.txt */
use Doctrine\Common\Collections\Criteria;
$course_plugin = 'zoom'; // needed in order to load the plugin lang variables
require_once __DIR__.'/config.php';
api_protect_course_script(true);
// the section (for the tabs)
$this_section = SECTION_COURSES;
$logInfo = [
'tool' => 'Videoconference Zoom',
];
Event::registerLog($logInfo);
$tool_name = get_lang('ZoomVideoconferences');
$tpl = new Template($tool_name);
$plugin = ZoomPlugin::create();
if ($plugin->userIsConferenceManager()) {
// user can create a new meeting
$tpl->assign('createInstantMeetingForm', $plugin->getCreateInstantMeetingForm()->returnForm());
$tpl->assign('scheduleMeetingForm', $plugin->getScheduleMeetingForm()->returnForm());
}
try {
$tpl->assign('scheduledMeetings', $plugin->getScheduledMeetings());
} catch (Exception $exception) {
Display::addFlash(
Display::return_message('Could not retrieve scheduled meeting list: '.$exception->getMessage(), 'error')
);
}
$tpl->assign('content', $tpl->fetch('zoom/view/start.tpl'));
$tpl->display_one_col_template();

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

@ -0,0 +1,55 @@
{{ search_form }}
<table class="table table-hover table-striped">
<caption>{{ 'MeetingsFound'|get_lang }}</caption>
<thead>
<tr>
<th>{{ 'StartTime'|get_lang }}</th>
<th>{{ 'Course'|get_lang }}</th>
<th>{{ 'Session'|get_lang }}</th>
<th>{{ 'Topic'|get_lang }}</th>
{% if recordings %}
<th>{{ 'Recordings'|get_lang }}</th>
{% endif %}
<th></th>
</tr>
</thead>
<tbody>
{% for meeting in meetings %}
<tr>
<td>{{ meeting.formattedStartTime }}</td>
<td>{{ meeting.course ? meeting.course.title : '-' }}</td>
<td>{{ meeting.session ? meeting.session.name : '-' }}</td>
<td>{{ meeting.topic }}</td>
{% if recordings %}
<td>
{% for recording in recordings %}
{% if recording.id == meeting.id %}
<dl>
<dt>
{{ recording.formattedStartTime }}
({{ recording.formattedDuration }})
</dt>
<dd>
<ul>
{% for file in recording.recording_files %}
<li>
{{ file.recording_type }}.{{ file.file_type }}
({{ file.formattedFileSize }})
</li>
{% endfor %}
</ul>
</dd>
</dl>
{% endif %}
{% endfor %}
</td>
{% endif %}
<td>
<a class="btn" href="meeting_from_admin.php?meetingId={{ meeting.id }}">
{{ 'Details'|get_lang }}
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>

@ -0,0 +1,78 @@
<p>{{ meeting.typeName }} {{ meeting.id }} ({{ meeting.statusName }})</p>
{% if isConferenceManager and meeting.status == 'waiting' %}
<p>
<a href="{{ meeting.start_url }}" target="_blank">
{{ 'StartMeeting'|get_lang }}
</a>
</p>
{% endif %}
{% if currentUserJoinURL %}
<p>
<a href="{{ currentUserJoinURL }}" target="_blank">
{{ 'JoinMeetingAsMyself'|get_lang }}
</a>
</p>
{% endif %}
{% if meeting.settings.approval_type == 2 %}
<p>
<label>
{{ 'JoinURLToSendToParticipants'|get_lang }}
<input readonly value="{{ meeting.join_url }}">
</label>
</p>
{% endif %}
{% if isConferenceManager %}
{{ editMeetingForm }}
{{ deleteMeetingForm }}
{{ registerParticipantForm }}
{{ fileForm }}
{% if registrants and meeting.settings.approval_type != 2 %}
<script>
function copyJoinURL(event, url) {
event.target.textContent = '{{ 'CopyingJoinURL'|get_lang|escape }}';
navigator.clipboard.writeText(url).then(
function() {
event.target.textContent = '{{ 'JoinURLCopied'|get_lang|escape }}';
}, function() {
event.target.textContent = '{{ 'CouldNotCopyJoinURL'|get_lang|escape }}' + ' ' + url;
}
);
}
</script>
<ul>
{% for registrant in registrants %}
<li>
<a onclick="copyJoinURL(event, '{{ registrant.join_url }}')">
{{ 'CopyJoinAsURL'|get_lang }}
</a>
{{ registrant.fullName }}
</li>
{% endfor %}
</ul>
{% endif %}
{% else %}
<h2>{{ meeting.topic }}</h2>
{% if meeting.agenda %}
<blockquote>{{ meeting.agenda| nl2br }}</blockquote>
{% endif %}
{% if meeting.type == 2 or meeting.type == 8 %}
<dl class="meeting_properties">
<dt>{{ 'StartTime'|get_lang }}</dt>
<dd>{{ meeting.formattedStartTime }}</dd>
<dt>{{ 'Duration'|get_lang }}</dt>
<dd>{{ meeting.formattedDuration }}</dd>
</dl>
{% endif %}
{% endif %}

@ -0,0 +1,44 @@
{% if createInstantMeetingForm %}
{{ createInstantMeetingForm }}
{% endif %}
{% if scheduledMeetings %}
<div class="page-header">
<h2>{{ 'ScheduledMeetings'|get_lang }}</h2>
</div>
<table class="table">
<tr>
<!-- th>{{ 'CreatedAt'|get_lang }}</th -->
<th>{{ 'StartTime'|get_lang }}</th>
<th>{{ 'Duration'|get_lang }}</th>
<!-- th>{{ 'Type'|get_lang }}</th -->
<th>{{ 'TopicAndAgenda'|get_lang }}</th>
<th></th>
</tr>
{% for meeting in scheduledMeetings %}
<tr>
<!-- td>{{ meeting.created_at }}</td -->
<td>{{ meeting.formattedStartTime }}</td>
<td>{{ meeting.formattedDuration }}</td>
<!-- td>{{ meeting.typeName }}</td -->
<td>
<strong>{{ meeting.topic }}</strong>
<p class="small">{{ meeting.agenda|nl2br }}</p>
</td>
<td>
<a class="btn" href="meeting_from_start.php?meetingId={{ meeting.id }}">
{{ 'Details'|get_lang }}
</a>
<a class="btn" href="{{ meeting.join_url }}">
{{ 'Join'|get_lang }}
</a>
</td>
</tr>
{% endfor %}
</table>
{% else %}
<!-- p>No scheduled meeting currently</p -->
{% endif %}
{% if scheduleMeetingForm %}
<h3>{{ 'ScheduleAMeeting'|get_lang }}</h3>
{{ scheduleMeetingForm }}
{% endif %}
Loading…
Cancel
Save