bbb.lib.php 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. /**
  4. * Class bbb
  5. * This script initiates a video conference session, calling the BigBlueButton
  6. * API
  7. * @package chamilo.plugin.bigbluebutton
  8. *
  9. * BigBlueButton-Chamilo connector class
  10. */
  11. //namespace Chamilo\Plugin\BBB;
  12. /**
  13. * Class bbb
  14. * @package Chamilo\Plugin\BBB
  15. */
  16. class bbb
  17. {
  18. public $url;
  19. public $salt;
  20. public $api;
  21. public $userCompleteName = '';
  22. public $protocol = 'http://';
  23. public $debug = false;
  24. public $logoutUrl = '';
  25. public $pluginEnabled = false;
  26. public $enableGlobalConference = false;
  27. public $enableGlobalConferencePerUser = false;
  28. public $isGlobalConference = false;
  29. public $groupSupport = false;
  30. public $userSupport = false;
  31. public $accessUrl = 1;
  32. public $userId = 0;
  33. public $plugin;
  34. private $courseCode;
  35. private $courseId;
  36. private $sessionId;
  37. private $groupId;
  38. private $maxUsersLimit;
  39. /**
  40. * Constructor (generates a connection to the API and the Chamilo settings
  41. * required for the connection to the video conference server)
  42. * @param string $host
  43. * @param string $salt
  44. * @param bool $isGlobalConference
  45. * @param int $isGlobalPerUser
  46. */
  47. public function __construct(
  48. $host = '',
  49. $salt = '',
  50. $isGlobalConference = false,
  51. $isGlobalPerUser = 0
  52. ) {
  53. $this->courseCode = api_get_course_id();
  54. $this->courseId = api_get_course_int_id();
  55. $this->sessionId = api_get_session_id();
  56. $this->groupId = api_get_group_id();
  57. // Initialize video server settings from global settings
  58. $this->plugin = BBBPlugin::create();
  59. $bbbPluginEnabled = $this->plugin->get('tool_enable');
  60. $bbb_host = !empty($host) ? $host : $this->plugin->get('host');
  61. $bbb_salt = !empty($salt) ? $salt : $this->plugin->get('salt');
  62. $this->table = Database::get_main_table('plugin_bbb_meeting');
  63. $this->enableGlobalConference = $this->plugin->get('enable_global_conference') === 'true' ? true : false;
  64. $this->isGlobalConference = (bool) $isGlobalConference;
  65. $columns = Database::listTableColumns($this->table);
  66. $this->groupSupport = isset($columns['group_id']) ? true : false;
  67. $this->userSupport = isset($columns['user_id']) ? true : false;
  68. $this->accessUrl = api_get_current_access_url_id();
  69. $this->enableGlobalConferencePerUser = false;
  70. if ($this->userSupport && !empty($isGlobalPerUser)) {
  71. $this->enableGlobalConferencePerUser = $this->plugin->get('enable_global_conference_per_user') === 'true' ? true : false;
  72. $this->userId = $isGlobalPerUser;
  73. }
  74. if ($this->groupSupport) {
  75. // Plugin check
  76. $this->groupSupport = $this->plugin->get('enable_conference_in_course_groups') === 'true' ? true : false;
  77. if ($this->groupSupport) {
  78. // Platform check
  79. $bbbSetting = api_get_setting('bbb_enable_conference_in_course_groups');
  80. $bbbSetting = isset($bbbSetting['bbb']) ? $bbbSetting['bbb'] === 'true' : false;
  81. if ($bbbSetting) {
  82. // Course check
  83. $courseInfo = api_get_course_info();
  84. if ($courseInfo) {
  85. $this->groupSupport = api_get_course_setting('bbb_enable_conference_in_groups', $courseInfo['code']) === '1';
  86. }
  87. }
  88. }
  89. }
  90. $this->maxUsersLimit = $this->plugin->get('max_users_limit');
  91. if ($bbbPluginEnabled === 'true') {
  92. $userInfo = api_get_user_info();
  93. if (empty($userInfo) && !empty($isGlobalPerUser)) {
  94. // If we are following a link to a global "per user" conference
  95. // then generate a random guest name to join the conference
  96. // because there is no part of the process where we give a name
  97. $this->userCompleteName = 'Guest'.rand(1000, 9999);
  98. } else {
  99. $this->userCompleteName = $userInfo['complete_name'];
  100. }
  101. $this->salt = $bbb_salt;
  102. $info = parse_url($bbb_host);
  103. $this->url = $bbb_host.'/bigbluebutton/';
  104. if (isset($info['scheme'])) {
  105. $this->protocol = $info['scheme'].'://';
  106. $this->url = str_replace($this->protocol, '', $this->url);
  107. $urlWithProtocol = $bbb_host;
  108. } else {
  109. // We asume it's an http, if user wants to use https host must include the protocol.
  110. $urlWithProtocol = 'http://'.$bbb_host;
  111. }
  112. // Setting BBB api
  113. define('CONFIG_SECURITY_SALT', $this->salt);
  114. define('CONFIG_SERVER_URL_WITH_PROTOCOL', $urlWithProtocol);
  115. define('CONFIG_SERVER_BASE_URL', $this->url);
  116. $this->api = new BigBlueButtonBN();
  117. $this->pluginEnabled = true;
  118. $this->logoutUrl = $this->getListingUrl();
  119. }
  120. }
  121. /**
  122. * Force the course, session and/or group IDs
  123. * @param string $courseCode
  124. * @param int $sessionId
  125. * @param int $groupId
  126. */
  127. public function forceCIdReq($courseCode, $sessionId = 0, $groupId = 0)
  128. {
  129. $this->courseCode = $courseCode;
  130. $this->sessionId = intval($sessionId);
  131. $this->groupId = intval($groupId);
  132. }
  133. /**
  134. * @return bool
  135. */
  136. public function isGlobalConferenceEnabled()
  137. {
  138. return $this->enableGlobalConference;
  139. }
  140. /**
  141. * @return bool
  142. */
  143. public function isGlobalConferencePerUserEnabled()
  144. {
  145. return $this->enableGlobalConferencePerUser;
  146. }
  147. /**
  148. * @return bool
  149. */
  150. public function isGlobalConference()
  151. {
  152. if ($this->isGlobalConferenceEnabled() === false) {
  153. return false;
  154. }
  155. return (bool) $this->isGlobalConference;
  156. }
  157. /**
  158. * @return bool
  159. */
  160. public function hasGroupSupport()
  161. {
  162. return $this->groupSupport;
  163. }
  164. /**
  165. * Checks whether a user is teacher in the current course
  166. * @return bool True if the user can be considered a teacher in this course, false otherwise
  167. */
  168. public function isConferenceManager()
  169. {
  170. if (api_is_coach() || api_is_platform_admin()) {
  171. return true;
  172. }
  173. if ($this->isGlobalConferencePerUserEnabled()) {
  174. $currentUserId = api_get_user_id();
  175. if ($this->userId === $currentUserId) {
  176. return true;
  177. } else {
  178. return false;
  179. }
  180. }
  181. $courseInfo = api_get_course_info();
  182. if (!empty($courseInfo)) {
  183. return api_is_course_admin();
  184. }
  185. return false;
  186. }
  187. /**
  188. * Gets the global limit of users in a video-conference room.
  189. * This value can be overridden by course-specific values
  190. * @return int Maximum number of users set globally
  191. */
  192. public function getMaxUsersLimit()
  193. {
  194. $limit = $this->maxUsersLimit;
  195. if ($limit <= 0) {
  196. $limit = 0;
  197. }
  198. $courseLimit = 0;
  199. $sessionLimit = 0;
  200. // Check the extra fields for this course and session
  201. // Session limit takes priority over course limit
  202. // Course limit takes priority over global limit
  203. if (!empty($this->courseId)) {
  204. $extraField = new ExtraField('course');
  205. $fieldId = $extraField->get_all(
  206. array('variable = ?' => 'plugin_bbb_course_users_limit')
  207. );
  208. $extraValue = new ExtraFieldValue('course');
  209. $value = $extraValue->get_values_by_handler_and_field_id($this->courseId, $fieldId[0]['id']);
  210. if (!empty($value['value'])) {
  211. $courseLimit = (int) $value['value'];
  212. }
  213. }
  214. if (!empty($this->sessionId)) {
  215. $extraField = new ExtraField('session');
  216. $fieldId = $extraField->get_all(
  217. array('variable = ?' => 'plugin_bbb_session_users_limit')
  218. );
  219. $extraValue = new ExtraFieldValue('session');
  220. $value = $extraValue->get_values_by_handler_and_field_id($this->sessionId, $fieldId[0]['id']);
  221. if (!empty($value['value'])) {
  222. $sessionLimit = (int) $value['value'];
  223. }
  224. }
  225. if (!empty($sessionLimit)) {
  226. return $sessionLimit;
  227. } elseif (!empty($courseLimit)) {
  228. return $courseLimit;
  229. }
  230. return (int) $limit;
  231. }
  232. /**
  233. * Sets the global limit of users in a video-conference room.
  234. * @param int Maximum number of users (globally)
  235. */
  236. public function setMaxUsersLimit($max)
  237. {
  238. if ($max < 0) {
  239. $max = 0;
  240. }
  241. $this->maxUsersLimit = intval($max);
  242. }
  243. /**
  244. * See this file in you BBB to set up default values
  245. * @param array $params Array of parameters that will be completed if not containing all expected variables
  246. /var/lib/tomcat6/webapps/bigbluebutton/WEB-INF/classes/bigbluebutton.properties
  247. *
  248. More record information:
  249. http://code.google.com/p/bigbluebutton/wiki/RecordPlaybackSpecification
  250. # Default maximum number of users a meeting can have.
  251. # Doesn't get enforced yet but is the default value when the create
  252. # API doesn't pass a value.
  253. defaultMaxUsers=20
  254. # Default duration of the meeting in minutes.
  255. # Current default is 0 (meeting doesn't end).
  256. defaultMeetingDuration=0
  257. # Remove the meeting from memory when the end API is called.
  258. # This allows 3rd-party apps to recycle the meeting right-away
  259. # instead of waiting for the meeting to expire (see below).
  260. removeMeetingWhenEnded=false
  261. # The number of minutes before the system removes the meeting from memory.
  262. defaultMeetingExpireDuration=1
  263. # The number of minutes the system waits when a meeting is created and when
  264. # a user joins. If after this period, a user hasn't joined, the meeting is
  265. # removed from memory.
  266. defaultMeetingCreateJoinDuration=5
  267. *
  268. * @return mixed
  269. */
  270. public function createMeeting($params)
  271. {
  272. $courseCode = api_get_course_id();
  273. $params['c_id'] = api_get_course_int_id();
  274. $params['session_id'] = api_get_session_id();
  275. if ($this->hasGroupSupport()) {
  276. $params['group_id'] = api_get_group_id();
  277. }
  278. if ($this->isGlobalConferencePerUserEnabled() && !empty($this->userId)) {
  279. $params['user_id'] = (int) $this->userId;
  280. }
  281. $params['attendee_pw'] = isset($params['attendee_pw']) ? $params['attendee_pw'] : $this->getUserMeetingPassword();
  282. $attendeePassword = $params['attendee_pw'];
  283. $params['moderator_pw'] = isset($params['moderator_pw']) ? $params['moderator_pw'] : $this->getModMeetingPassword();
  284. $moderatorPassword = $params['moderator_pw'];
  285. $params['record'] = api_get_course_setting('big_blue_button_record_and_store', $courseCode) == 1 ? true : false;
  286. $max = api_get_course_setting('big_blue_button_max_students_allowed', $courseCode);
  287. $max = isset($max) ? $max : -1;
  288. $params['status'] = 1;
  289. // Generate a pseudo-global-unique-id to avoid clash of conferences on
  290. // the same BBB server with several Chamilo portals
  291. $params['remote_id'] = uniqid(true, true);
  292. // Each simultaneous conference room needs to have a different
  293. // voice_bridge composed of a 5 digits number, so generating a random one
  294. $params['voice_bridge'] = rand(10000, 99999);
  295. if ($this->debug) {
  296. error_log("enter create_meeting ".print_r($params, 1));
  297. }
  298. $params['created_at'] = api_get_utc_datetime();
  299. $params['access_url'] = $this->accessUrl;
  300. // Check interface feature is installed
  301. $interfaceFeature = $this->plugin->get('interface');
  302. if ($interfaceFeature === false) {
  303. if (isset($params['interface'])) {
  304. unset($params['interface']);
  305. }
  306. }
  307. $id = Database::insert($this->table, $params);
  308. if ($id) {
  309. if ($this->debug) {
  310. error_log("create_meeting: $id ");
  311. }
  312. $meetingName = isset($params['meeting_name']) ? $params['meeting_name'] : $this->getCurrentVideoConferenceName();
  313. $welcomeMessage = isset($params['welcome_msg']) ? $params['welcome_msg'] : null;
  314. $record = isset($params['record']) && $params['record'] ? 'true' : 'false';
  315. $duration = isset($params['duration']) ? intval($params['duration']) : 0;
  316. // This setting currently limits the maximum conference duration,
  317. // to avoid lingering sessions on the video-conference server #6261
  318. $duration = 300;
  319. $bbbParams = array(
  320. 'meetingId' => $params['remote_id'], // REQUIRED
  321. 'meetingName' => $meetingName, // REQUIRED
  322. 'attendeePw' => $attendeePassword, // Match this value in getJoinMeetingURL() to join as attendee.
  323. 'moderatorPw' => $moderatorPassword, // Match this value in getJoinMeetingURL() to join as moderator.
  324. 'welcomeMsg' => $welcomeMessage, // ''= use default. Change to customize.
  325. 'dialNumber' => '', // The main number to call into. Optional.
  326. 'voiceBridge' => $params['voice_bridge'], // PIN to join voice. Required.
  327. 'webVoice' => '', // Alphanumeric to join voice. Optional.
  328. 'logoutUrl' => $this->logoutUrl,
  329. 'maxParticipants' => $max, // Optional. -1 = unlimitted. Not supported in BBB. [number]
  330. 'record' => $record, // New. 'true' will tell BBB to record the meeting.
  331. 'duration' => $duration, // Default = 0 which means no set duration in minutes. [number]
  332. //'meta_category' => '', // Use to pass additional info to BBB server. See API docs.
  333. );
  334. if ($this->debug) {
  335. error_log("create_meeting params: ".print_r($bbbParams, 1));
  336. }
  337. $status = false;
  338. $meeting = null;
  339. while ($status === false) {
  340. $result = $this->api->createMeetingWithXmlResponseArray(
  341. $bbbParams
  342. );
  343. if (isset($result) && strval($result['returncode']) == 'SUCCESS') {
  344. if ($this->debug) {
  345. error_log(
  346. "create_meeting result: ".print_r($result, 1)
  347. );
  348. }
  349. $meeting = $this->joinMeeting($meetingName, true);
  350. return $meeting;
  351. }
  352. }
  353. return false;
  354. }
  355. return false;
  356. }
  357. /**
  358. * Save a participant in a meeting room
  359. * @param int $meetingId
  360. * @param int $participantId
  361. * @param int $interface
  362. *
  363. * @return false|int The last inserted ID. Otherwise return false
  364. */
  365. public function saveParticipant($meetingId, $participantId, $interface = 0)
  366. {
  367. $params = [
  368. 'meeting_id' => $meetingId,
  369. 'participant_id' => $participantId,
  370. 'in_at' => api_get_utc_datetime(),
  371. 'out_at' => api_get_utc_datetime()
  372. ];
  373. if ($this->plugin->get('interface') !== false) {
  374. $params['interface'] = $interface;
  375. }
  376. return Database::insert(
  377. 'plugin_bbb_room',
  378. $params
  379. );
  380. }
  381. /**
  382. * @param string $meetingName
  383. *
  384. * @return array
  385. */
  386. public function getMeetingByName($meetingName)
  387. {
  388. if (empty($meetingName)) {
  389. return [];
  390. }
  391. $courseId = api_get_course_int_id();
  392. $sessionId = api_get_session_id();
  393. $conditions = array(
  394. 'where' => array(
  395. 'c_id = ? AND session_id = ? AND meeting_name = ? AND status = 1 AND access_url = ?' =>
  396. array($courseId, $sessionId, $meetingName, $this->accessUrl)
  397. )
  398. );
  399. if ($this->hasGroupSupport()) {
  400. $groupId = api_get_group_id();
  401. $conditions = array(
  402. 'where' => array(
  403. 'c_id = ? AND session_id = ? AND meeting_name = ? AND group_id = ? AND status = 1 AND access_url = ?' =>
  404. array(
  405. $courseId,
  406. $sessionId,
  407. $meetingName,
  408. $groupId,
  409. $this->accessUrl
  410. )
  411. )
  412. );
  413. }
  414. $meetingData = Database::select(
  415. '*',
  416. $this->table,
  417. $conditions,
  418. 'first'
  419. );
  420. if ($this->debug) {
  421. error_log('meeting_exists '.print_r($meetingData, 1));
  422. }
  423. return $meetingData;
  424. }
  425. /**
  426. * Tells whether the given meeting exists and is running
  427. * (using course code as name)
  428. * @param string $meetingName Meeting name (usually the course code)
  429. *
  430. * @return bool True if meeting exists, false otherwise
  431. * @assert ('') === false
  432. * @assert ('abcdefghijklmnopqrstuvwxyzabcdefghijklmno') === false
  433. */
  434. public function meetingExists($meetingName)
  435. {
  436. $meetingData = $this->getMeetingByName($meetingName);
  437. return !empty($meetingData);
  438. }
  439. /**
  440. * Returns a meeting "join" URL
  441. * @param string The name of the meeting (usually the course code)
  442. * @return mixed The URL to join the meeting, or false on error
  443. * @todo implement moderator pass
  444. * @assert ('') === false
  445. * @assert ('abcdefghijklmnopqrstuvwxyzabcdefghijklmno') === false
  446. */
  447. public function joinMeeting($meetingName)
  448. {
  449. if ($this->debug) {
  450. error_log("joinMeeting: $meetingName");
  451. }
  452. if (empty($meetingName)) {
  453. return false;
  454. }
  455. $manager = $this->isConferenceManager();
  456. if ($manager) {
  457. $pass = $this->getModMeetingPassword();
  458. } else {
  459. $pass = $this->getUserMeetingPassword();
  460. }
  461. $meetingData = Database::select(
  462. '*',
  463. $this->table,
  464. array(
  465. 'where' => array(
  466. 'meeting_name = ? AND status = 1 AND access_url = ?' => array(
  467. $meetingName,
  468. $this->accessUrl
  469. )
  470. )
  471. ),
  472. 'first'
  473. );
  474. if (empty($meetingData) || !is_array($meetingData)) {
  475. if ($this->debug) {
  476. error_log("meeting does not exist: $meetingName");
  477. }
  478. return false;
  479. }
  480. $params = array(
  481. 'meetingId' => $meetingData['remote_id'],
  482. // -- REQUIRED - The unique id for the meeting
  483. 'password' => $this->getModMeetingPassword()
  484. // -- REQUIRED - The moderator password for the meeting
  485. );
  486. $meetingInfoExists = false;
  487. $meetingIsRunningInfo = $this->getMeetingInfo($params);
  488. if ($this->debug) {
  489. error_log('Searching meeting with params:');
  490. error_log(print_r($params, 1));
  491. error_log('Result:');
  492. error_log(print_r($meetingIsRunningInfo, 1));
  493. }
  494. if ($meetingIsRunningInfo === false) {
  495. // checking with the remote_id didn't work, so just in case and
  496. // to provide backwards support, check with the id
  497. $params = array(
  498. 'meetingId' => $meetingData['id'],
  499. // -- REQUIRED - The unique id for the meeting
  500. 'password' => $this->getModMeetingPassword()
  501. // -- REQUIRED - The moderator password for the meeting
  502. );
  503. $meetingIsRunningInfo = $this->getMeetingInfo($params);
  504. if ($this->debug) {
  505. error_log('Searching meetingId with params:');
  506. error_log(print_r($params, 1));
  507. error_log('Result:');
  508. error_log(print_r($meetingIsRunningInfo, 1));
  509. }
  510. }
  511. if (strval($meetingIsRunningInfo['returncode']) == 'SUCCESS' &&
  512. isset($meetingIsRunningInfo['meetingName']) &&
  513. !empty($meetingIsRunningInfo['meetingName'])
  514. ) {
  515. $meetingInfoExists = true;
  516. }
  517. if ($this->debug) {
  518. error_log(
  519. "meeting is running: ".intval($meetingInfoExists)
  520. );
  521. }
  522. $url = false;
  523. if ($meetingInfoExists) {
  524. $joinParams = [
  525. 'meetingId' => $meetingData['remote_id'], // -- REQUIRED - A unique id for the meeting
  526. 'username' => $this->userCompleteName, //-- REQUIRED - The name that will display for the user in the meeting
  527. 'password' => $pass, //-- REQUIRED - The attendee or moderator password, depending on what's passed here
  528. //'createTime' => api_get_utc_datetime(), //-- OPTIONAL - string. Leave blank ('') unless you set this correctly.
  529. 'userID' => api_get_user_id(), //-- OPTIONAL - string
  530. 'webVoiceConf' => '', // -- OPTIONAL - string
  531. 'interface' => $this->checkInterface($meetingData),
  532. ];
  533. $url = $this->api->getJoinMeetingURL($joinParams);
  534. $url = $this->protocol.$url;
  535. }
  536. if ($this->debug) {
  537. error_log("return url :".$url);
  538. }
  539. return $url;
  540. }
  541. /**
  542. * Get information about the given meeting
  543. * @param array ...?
  544. * @return mixed Array of information on success, false on error
  545. * @assert (array()) === false
  546. */
  547. public function getMeetingInfo($params)
  548. {
  549. try {
  550. $result = $this->api->getMeetingInfoWithXmlResponseArray($params);
  551. if ($result == null) {
  552. if ($this->debug) {
  553. error_log("Failed to get any response. Maybe we can't contact the BBB server.");
  554. }
  555. } else {
  556. return $result;
  557. }
  558. } catch (Exception $e) {
  559. if ($this->debug) {
  560. error_log('Caught exception: ', $e->getMessage(), "\n");
  561. }
  562. }
  563. return false;
  564. }
  565. /**
  566. * @param int $courseId
  567. * @param int $sessionId
  568. * @param int $status
  569. *
  570. * @return array
  571. */
  572. public function getAllMeetingsInCourse($courseId, $sessionId, $status)
  573. {
  574. $conditions = array(
  575. 'where' => array(
  576. 'status = ? AND c_id = ? AND session_id = ? ' => array(
  577. $status,
  578. $courseId,
  579. $sessionId,
  580. ),
  581. ),
  582. );
  583. $meetingList = Database::select(
  584. '*',
  585. $this->table,
  586. $conditions
  587. );
  588. return $meetingList;
  589. }
  590. /**
  591. * Gets all the course meetings saved in the plugin_bbb_meeting table
  592. * @param int $courseId
  593. * @param int $sessionId
  594. * @param int $groupId
  595. * @param bool $isAdminReport Optional. Set to true then the report is for admins
  596. * @param array $dateRange Optional
  597. * @return array Array of current open meeting rooms
  598. */
  599. public function getMeetings(
  600. $courseId = 0,
  601. $sessionId = 0,
  602. $groupId = 0,
  603. $isAdminReport = false,
  604. $dateRange = []
  605. ) {
  606. $em = Database::getManager();
  607. $manager = $this->isConferenceManager();
  608. $conditions = [];
  609. if ($courseId || $sessionId || $groupId) {
  610. $conditions = array(
  611. 'where' => array(
  612. 'c_id = ? AND session_id = ? ' => array($courseId, $sessionId),
  613. ),
  614. );
  615. if ($this->hasGroupSupport()) {
  616. $conditions = array(
  617. 'where' => array(
  618. 'c_id = ? AND session_id = ? AND group_id = ? ' => array(
  619. $courseId,
  620. $sessionId,
  621. $groupId
  622. )
  623. )
  624. );
  625. }
  626. if ($this->isGlobalConferencePerUserEnabled()) {
  627. $conditions = array(
  628. 'where' => array(
  629. 'c_id = ? AND session_id = ? AND user_id = ?' => array(
  630. $courseId,
  631. $sessionId,
  632. $this->userId
  633. ),
  634. ),
  635. );
  636. }
  637. }
  638. if (!empty($dateRange)) {
  639. $dateStart = date_create($dateRange['search_meeting_start']);
  640. $dateStart = date_format($dateStart, 'Y-m-d H:i:s');
  641. $dateEnd = date_create($dateRange['search_meeting_end']);
  642. $dateEnd = $dateEnd->add(new DateInterval('P1D'));
  643. $dateEnd = date_format($dateEnd, 'Y-m-d H:i:s');
  644. $conditions = array(
  645. 'where' => array(
  646. 'created_at BETWEEN ? AND ? ' => array($dateStart, $dateEnd),
  647. ),
  648. );
  649. }
  650. $conditions['order'] = "created_at ASC";
  651. $meetingList = Database::select(
  652. '*',
  653. $this->table,
  654. $conditions
  655. );
  656. $isGlobal = $this->isGlobalConference();
  657. $newMeetingList = array();
  658. foreach ($meetingList as $meetingDB) {
  659. $item = array();
  660. $courseId = $meetingDB['c_id'];
  661. $courseInfo = api_get_course_info_by_id($courseId);
  662. $courseCode = '';
  663. if (!empty($courseInfo)) {
  664. $courseCode = $courseInfo['code'];
  665. }
  666. if ($manager) {
  667. $pass = $this->getUserMeetingPassword($courseCode);
  668. } else {
  669. $pass = $this->getModMeetingPassword($courseCode);
  670. }
  671. $meetingBBB = $this->getMeetingInfo(
  672. [
  673. 'meetingId' => $meetingDB['remote_id'],
  674. 'password' => $pass
  675. ]
  676. );
  677. if ($meetingBBB === false) {
  678. //checking with the remote_id didn't work, so just in case and
  679. // to provide backwards support, check with the id
  680. $params = array(
  681. 'meetingId' => $meetingDB['id'],
  682. // -- REQUIRED - The unique id for the meeting
  683. 'password' => $pass
  684. // -- REQUIRED - The moderator password for the meeting
  685. );
  686. $meetingBBB = $this->getMeetingInfo($params);
  687. }
  688. if ($meetingDB['visibility'] == 0 && $this->isConferenceManager() === false) {
  689. continue;
  690. }
  691. $meetingBBB['end_url'] = $this->endUrl($meetingDB);
  692. if (isset($meetingBBB['returncode']) && (string) $meetingBBB['returncode'] == 'FAILED') {
  693. if ($meetingDB['status'] == 1 && $this->isConferenceManager()) {
  694. $this->endMeeting($meetingDB['id'], $courseCode);
  695. }
  696. } else {
  697. $meetingBBB['add_to_calendar_url'] = $this->addToCalendarUrl($meetingDB);
  698. }
  699. if ($meetingDB['record'] == 1) {
  700. // backwards compatibility (when there was no remote ID)
  701. $mId = $meetingDB['remote_id'];
  702. if (empty($mId)) {
  703. $mId = $meetingDB['id'];
  704. }
  705. if (empty($mId)) {
  706. // if the id is still empty (should *never* occur as 'id' is
  707. // the table's primary key), skip this conference
  708. continue;
  709. }
  710. $record = [];
  711. if (empty($meetingDB['video_url'])) {
  712. $recordingParams = ['meetingId' => $mId];
  713. $records = $this->api->getRecordingsWithXmlResponseArray($recordingParams);
  714. if (!empty($records)) {
  715. if (!isset($records['messageKey']) || $records['messageKey'] != 'noRecordings') {
  716. $record = end($records);
  717. if (!is_array($record) || !isset($record['recordId'])) {
  718. continue;
  719. }
  720. $this->updateMeetingVideoUrl($meetingDB['id'], $record['playbackFormatUrl']);
  721. if (!$this->isConferenceManager()) {
  722. $record = [];
  723. }
  724. }
  725. }
  726. } else {
  727. $record['playbackFormatUrl'] = $meetingDB['video_url'];
  728. }
  729. $recordLink = isset($record['playbackFormatUrl']) && !empty($record['playbackFormatUrl'])
  730. ? Display::url(
  731. $this->plugin->get_lang('ViewRecord'),
  732. $record['playbackFormatUrl'],
  733. ['target' => '_blank']
  734. )
  735. : $this->plugin->get_lang('NoRecording');
  736. if ($isAdminReport) {
  737. $this->forceCIdReq(
  738. $courseInfo['code'],
  739. $meetingDB['session_id'],
  740. $meetingDB['group_id']
  741. );
  742. }
  743. $actionLinks = $this->getActionLinks(
  744. $meetingDB,
  745. $record,
  746. $isGlobal,
  747. $isAdminReport
  748. );
  749. $item['show_links'] = $recordLink;
  750. } else {
  751. $actionLinks = $this->getActionLinks(
  752. $meetingDB,
  753. [],
  754. $isGlobal,
  755. $isAdminReport
  756. );
  757. $item['show_links'] = $this->plugin->get_lang('NoRecording');
  758. }
  759. $item['action_links'] = implode(PHP_EOL, $actionLinks);
  760. $item['created_at'] = api_convert_and_format_date($meetingDB['created_at']);
  761. // created_at
  762. $meetingDB['created_at'] = $item['created_at']; //avoid overwrite in array_merge() below
  763. $item['publish_url'] = $this->publishUrl($meetingDB);
  764. $item['unpublish_url'] = $this->unPublishUrl($meetingBBB);
  765. if ($meetingDB['status'] == 1) {
  766. $joinParams = [
  767. 'meetingId' => $meetingDB['remote_id'], //-- REQUIRED - A unique id for the meeting
  768. 'username' => $this->userCompleteName, //-- REQUIRED - The name that will display for the user in the meeting
  769. 'password' => $pass, //-- REQUIRED - The attendee or moderator password, depending on what's passed here
  770. 'createTime' => '', //-- OPTIONAL - string. Leave blank ('') unless you set this correctly.
  771. 'userID' => '', // -- OPTIONAL - string
  772. 'webVoiceConf' => '', // -- OPTIONAL - string
  773. 'interface' => $this->checkInterface($meetingDB),
  774. ];
  775. $item['go_url'] = $this->protocol.$this->api->getJoinMeetingURL($joinParams);
  776. }
  777. $item = array_merge($item, $meetingDB, $meetingBBB);
  778. $item['course'] = $em->find('ChamiloCoreBundle:Course', $item['c_id']);
  779. $item['session'] = $em->find('ChamiloCoreBundle:Session', $item['session_id']);
  780. $newMeetingList[] = $item;
  781. }
  782. return $newMeetingList;
  783. }
  784. /**
  785. * @param $meetingInfo
  786. *
  787. * @return int
  788. */
  789. public function checkInterface($meetingInfo)
  790. {
  791. $interface = BBBPlugin::LAUNCH_TYPE_DEFAULT;
  792. $type = $this->plugin->get('launch_type');
  793. switch ($type) {
  794. case BBBPlugin::LAUNCH_TYPE_DEFAULT:
  795. $interface = $this->plugin->get('interface');
  796. break;
  797. case BBBPlugin::LAUNCH_TYPE_SET_BY_TEACHER:
  798. if (isset($meetingInfo['interface'])) {
  799. $interface = $meetingInfo['interface'];
  800. }
  801. break;
  802. case BBBPlugin::LAUNCH_TYPE_SET_BY_STUDENT:
  803. if (isset($meetingInfo['id'])) {
  804. $roomInfo = $this->getMeetingParticipantInfo($meetingInfo['id'], api_get_user_id());
  805. if (!empty($roomInfo) && isset($roomInfo['interface'])) {
  806. $interface = $roomInfo['interface'];
  807. } else {
  808. if (isset($_REQUEST['interface'])) {
  809. $interface = isset($_REQUEST['interface']) ? (int) $_REQUEST['interface'] : 0;
  810. }
  811. }
  812. }
  813. break;
  814. }
  815. return $interface;
  816. }
  817. /**
  818. * Function disabled
  819. */
  820. public function publishMeeting($id)
  821. {
  822. //return BigBlueButtonBN::setPublishRecordings($id, 'true', $this->url, $this->salt);
  823. if (empty($id)) {
  824. return false;
  825. }
  826. $id = intval($id);
  827. Database::update($this->table, array('visibility' => 1), array('id = ? ' => $id));
  828. return true;
  829. }
  830. /**
  831. * Function disabled
  832. */
  833. public function unpublishMeeting($id)
  834. {
  835. //return BigBlueButtonBN::setPublishRecordings($id, 'false', $this->url, $this->salt);
  836. if (empty($id)) {
  837. return false;
  838. }
  839. $id = intval($id);
  840. Database::update($this->table, array('visibility' => 0), array('id = ?' => $id));
  841. return true;
  842. }
  843. /**
  844. * Closes a meeting (usually when the user click on the close button from
  845. * the conferences listing.
  846. * @param string The internal ID of the meeting (id field for this meeting)
  847. * @param string $courseCode
  848. *
  849. * @return void
  850. * @assert (0) === false
  851. */
  852. public function endMeeting($id, $courseCode = null)
  853. {
  854. if (empty($id)) {
  855. return false;
  856. }
  857. $meetingData = Database::select(
  858. '*',
  859. $this->table,
  860. array('where' => array('id = ?' => array($id))),
  861. 'first'
  862. );
  863. $manager = $this->isConferenceManager();
  864. if ($manager) {
  865. $pass = $this->getUserMeetingPassword($courseCode);
  866. } else {
  867. $pass = $this->getModMeetingPassword($courseCode);
  868. }
  869. $endParams = array(
  870. 'meetingId' => $meetingData['remote_id'], // REQUIRED - We have to know which meeting to end.
  871. 'password' => $pass, // REQUIRED - Must match moderator pass for meeting.
  872. );
  873. $this->api->endMeetingWithXmlResponseArray($endParams);
  874. Database::update(
  875. $this->table,
  876. array('status' => 0, 'closed_at' => api_get_utc_datetime()),
  877. array('id = ? ' => $id)
  878. );
  879. }
  880. /**
  881. * Gets the password for a specific meeting for the current user
  882. * @param string $courseCode
  883. * @return string A moderator password if user is teacher, or the course code otherwise
  884. *
  885. */
  886. public function getUserMeetingPassword($courseCode = null)
  887. {
  888. if ($this->isGlobalConferencePerUserEnabled()) {
  889. return 'url_'.$this->userId.'_'.api_get_current_access_url_id();
  890. }
  891. if ($this->isGlobalConference()) {
  892. return 'url_'.api_get_current_access_url_id();
  893. }
  894. $courseCode = empty($courseCode) ? api_get_course_id() : $courseCode;
  895. return $courseCode;
  896. }
  897. /**
  898. * Generated a moderator password for the meeting.
  899. *
  900. * @param string $courseCode
  901. *
  902. * @return string A password for the moderation of the videoconference
  903. */
  904. public function getModMeetingPassword($courseCode = null)
  905. {
  906. if ($this->isGlobalConferencePerUserEnabled()) {
  907. return 'url_'.$this->userId.'_'.api_get_current_access_url_id().'_mod';
  908. }
  909. if ($this->isGlobalConference()) {
  910. return 'url_'.api_get_current_access_url_id().'_mod';
  911. }
  912. $courseCode = empty($courseCode) ? api_get_course_id() : $courseCode;
  913. return $courseCode.'mod';
  914. }
  915. /**
  916. * Get users online in the current course room.
  917. *
  918. * @return int The number of users currently connected to the videoconference
  919. * @assert () > -1
  920. */
  921. public function getUsersOnlineInCurrentRoom()
  922. {
  923. $courseId = api_get_course_int_id();
  924. $sessionId = api_get_session_id();
  925. $conditions = array(
  926. 'where' => array(
  927. 'c_id = ? AND session_id = ? AND status = 1 AND access_url = ?' => array(
  928. $courseId,
  929. $sessionId,
  930. $this->accessUrl
  931. ),
  932. ),
  933. );
  934. if ($this->hasGroupSupport()) {
  935. $groupId = api_get_group_id();
  936. $conditions = array(
  937. 'where' => array(
  938. 'c_id = ? AND session_id = ? AND group_id = ? AND status = 1 AND access_url = ?' => array(
  939. $courseId,
  940. $sessionId,
  941. $groupId,
  942. $this->accessUrl
  943. ),
  944. ),
  945. );
  946. }
  947. if ($this->isGlobalConferencePerUserEnabled()) {
  948. $conditions = array(
  949. 'where' => array(
  950. 'user_id = ? AND status = 1 AND access_url = ?' => array(
  951. $this->userId,
  952. $this->accessUrl
  953. ),
  954. ),
  955. );
  956. }
  957. $meetingData = Database::select(
  958. '*',
  959. $this->table,
  960. $conditions,
  961. 'first'
  962. );
  963. if (empty($meetingData)) {
  964. return 0;
  965. }
  966. $pass = $this->getModMeetingPassword();
  967. $info = $this->getMeetingInfo(array('meetingId' => $meetingData['remote_id'], 'password' => $pass));
  968. if ($info === false) {
  969. //checking with the remote_id didn't work, so just in case and
  970. // to provide backwards support, check with the id
  971. $params = array(
  972. 'meetingId' => $meetingData['id'],
  973. // -- REQUIRED - The unique id for the meeting
  974. 'password' => $pass
  975. // -- REQUIRED - The moderator password for the meeting
  976. );
  977. $info = $this->getMeetingInfo($params);
  978. }
  979. if (!empty($info) && isset($info['participantCount'])) {
  980. return $info['participantCount'];
  981. }
  982. return 0;
  983. }
  984. /**
  985. * Deletes a recording of a meeting
  986. *
  987. * @param int $id ID of the recording
  988. *
  989. * @return bool
  990. *
  991. * @assert () === false
  992. * @todo Also delete links and agenda items created from this recording
  993. */
  994. public function deleteRecording($id)
  995. {
  996. if (empty($id)) {
  997. return false;
  998. }
  999. $meetingData = Database::select(
  1000. '*',
  1001. $this->table,
  1002. array('where' => array('id = ?' => array($id))),
  1003. 'first'
  1004. );
  1005. $delete = false;
  1006. // Check if there are recordings for this meeting
  1007. $recordings = $this->api->getRecordings(['meetingId' => $meetingData['remote_id']]);
  1008. if (!empty($recordings) && isset($recordings['messageKey']) && $recordings['messageKey'] == 'noRecordings') {
  1009. $delete = true;
  1010. } else {
  1011. $recordsToDelete = [];
  1012. if (!empty($recordings['records'])) {
  1013. foreach ($recordings['records'] as $record) {
  1014. $recordsToDelete[] = $record['recordId'];
  1015. }
  1016. $recordingParams = ['recordId' => implode(',', $recordsToDelete)];
  1017. $result = $this->api->deleteRecordingsWithXmlResponseArray($recordingParams);
  1018. if (!empty($result) && isset($result['deleted']) && $result['deleted'] === 'true') {
  1019. $delete = true;
  1020. }
  1021. }
  1022. }
  1023. if ($delete) {
  1024. Database::delete(
  1025. 'plugin_bbb_room',
  1026. array('meeting_id = ?' => array($id))
  1027. );
  1028. Database::delete(
  1029. $this->table,
  1030. array('id = ?' => array($id))
  1031. );
  1032. }
  1033. return $delete;
  1034. }
  1035. /**
  1036. * Creates a link in the links tool from the given videoconference recording
  1037. * @param int $id ID of the item in the plugin_bbb_meeting table
  1038. * @param string Hash identifying the recording, as provided by the API
  1039. * @return mixed ID of the newly created link, or false on error
  1040. * @assert (null, null) === false
  1041. * @assert (1, null) === false
  1042. * @assert (null, 'abcdefabcdefabcdefabcdef') === false
  1043. */
  1044. public function copyRecordingToLinkTool($id)
  1045. {
  1046. if (empty($id)) {
  1047. return false;
  1048. }
  1049. //$records = BigBlueButtonBN::getRecordingsUrl($id);
  1050. $meetingData = Database::select(
  1051. '*',
  1052. $this->table,
  1053. array('where' => array('id = ?' => array($id))),
  1054. 'first'
  1055. );
  1056. $records = $this->api->getRecordingsWithXmlResponseArray(
  1057. array('meetingId' => $meetingData['remote_id'])
  1058. );
  1059. if (!empty($records)) {
  1060. if (isset($records['message']) && !empty($records['message'])) {
  1061. if ($records['messageKey'] == 'noRecordings') {
  1062. $recordArray[] = $this->plugin->get_lang('NoRecording');
  1063. } else {
  1064. //$recordArray[] = $records['message'];
  1065. }
  1066. return false;
  1067. } else {
  1068. $record = $records[0];
  1069. if (is_array($record) && isset($record['recordId'])) {
  1070. $url = $record['playbackFormatUrl'];
  1071. $link = new Link();
  1072. $params['url'] = $url;
  1073. $params['title'] = $meetingData['meeting_name'];
  1074. $id = $link->save($params);
  1075. return $id;
  1076. }
  1077. }
  1078. }
  1079. return false;
  1080. }
  1081. /**
  1082. * Checks if the video conference server is running.
  1083. * Function currently disabled (always returns 1)
  1084. * @return bool True if server is running, false otherwise
  1085. * @assert () === false
  1086. */
  1087. public function isServerRunning()
  1088. {
  1089. return true;
  1090. //return BigBlueButtonBN::isServerRunning($this->protocol.$this->url);
  1091. }
  1092. /**
  1093. * Get active session in the all platform
  1094. */
  1095. public function getActiveSessionsCount()
  1096. {
  1097. $meetingList = Database::select(
  1098. 'count(id) as count',
  1099. $this->table,
  1100. array('where' => array('status = ? AND access_url = ?' => array(1, $this->accessUrl))),
  1101. 'first'
  1102. );
  1103. return $meetingList['count'];
  1104. }
  1105. /**
  1106. * @param string $url
  1107. */
  1108. public function redirectToBBB($url)
  1109. {
  1110. if (file_exists(__DIR__.'/../config.vm.php')) {
  1111. // Using VM
  1112. echo Display::url($this->plugin->get_lang('ClickToContinue'), $url);
  1113. exit;
  1114. } else {
  1115. // Classic
  1116. header("Location: $url");
  1117. exit;
  1118. }
  1119. }
  1120. /**
  1121. * @return string
  1122. */
  1123. public function getUrlParams()
  1124. {
  1125. if (empty($this->courseCode)) {
  1126. if ($this->isGlobalConferencePerUserEnabled()) {
  1127. return 'global=1&user_id='.$this->userId;
  1128. }
  1129. if ($this->isGlobalConference()) {
  1130. return 'global=1';
  1131. }
  1132. return '';
  1133. }
  1134. return http_build_query([
  1135. 'cidReq' => $this->courseCode,
  1136. 'id_session' => $this->sessionId,
  1137. 'gidReq' => $this->groupId
  1138. ]);
  1139. }
  1140. /**
  1141. * @return string
  1142. */
  1143. public function getCurrentVideoConferenceName()
  1144. {
  1145. if ($this->isGlobalConferencePerUserEnabled()) {
  1146. return 'url_'.$this->userId.'_'.api_get_current_access_url_id();
  1147. }
  1148. if ($this->isGlobalConference()) {
  1149. return 'url_'.api_get_current_access_url_id();
  1150. }
  1151. if ($this->hasGroupSupport()) {
  1152. return api_get_course_id().'-'.api_get_session_id().'-'.api_get_group_id();
  1153. }
  1154. return api_get_course_id().'-'.api_get_session_id();
  1155. }
  1156. /**
  1157. * @return string
  1158. */
  1159. public function getConferenceUrl()
  1160. {
  1161. return api_get_path(WEB_PLUGIN_PATH).'bbb/start.php?launch=1&'.$this->getUrlParams();
  1162. }
  1163. /**
  1164. * @return string
  1165. */
  1166. public function getListingUrl()
  1167. {
  1168. return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams();
  1169. }
  1170. /**
  1171. * @param array $meeting
  1172. * @return string
  1173. */
  1174. public function endUrl($meeting)
  1175. {
  1176. if (!isset($meeting['id'])) {
  1177. return '';
  1178. }
  1179. return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().'&action=end&id='.$meeting['id'];
  1180. }
  1181. /**
  1182. * @param array $meeting
  1183. * @param array $record
  1184. * @return string
  1185. */
  1186. public function addToCalendarUrl($meeting, $record = [])
  1187. {
  1188. $url = isset($record['playbackFormatUrl']) ? $record['playbackFormatUrl'] : '';
  1189. return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().'&action=add_to_calendar&id='.$meeting['id'].'&start='.api_strtotime($meeting['created_at']).'&url='.$url;
  1190. }
  1191. /**
  1192. * @param array $meeting
  1193. * @return string
  1194. */
  1195. public function publishUrl($meeting)
  1196. {
  1197. if (!isset($meeting['id'])) {
  1198. return '';
  1199. }
  1200. return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().'&action=publish&id='.$meeting['id'];
  1201. }
  1202. /**
  1203. * @param array $meeting
  1204. * @return string
  1205. */
  1206. public function unPublishUrl($meeting)
  1207. {
  1208. if (!isset($meeting['id'])) {
  1209. return null;
  1210. }
  1211. return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().'&action=unpublish&id='.$meeting['id'];
  1212. }
  1213. /**
  1214. * @param array $meeting
  1215. * @return string
  1216. */
  1217. public function deleteRecordUrl($meeting)
  1218. {
  1219. if (!isset($meeting['id'])) {
  1220. return '';
  1221. }
  1222. return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().'&action=delete_record&id='.$meeting['id'];
  1223. }
  1224. /**
  1225. * @param array $meeting
  1226. * @return string
  1227. */
  1228. public function copyToRecordToLinkTool($meeting)
  1229. {
  1230. if (!isset($meeting['id'])) {
  1231. return '';
  1232. }
  1233. return api_get_path(WEB_PLUGIN_PATH).'bbb/listing.php?'.$this->getUrlParams().'&action=copy_record_to_link_tool&id='.$meeting['id'];
  1234. }
  1235. /**
  1236. * Get the meeting info from DB by its name
  1237. * @param string $name
  1238. *
  1239. * @return array
  1240. */
  1241. public function findMeetingByName($name)
  1242. {
  1243. $meetingData = Database::select(
  1244. '*',
  1245. 'plugin_bbb_meeting',
  1246. array('where' => array('meeting_name = ? AND status = 1 ' => $name)),
  1247. 'first'
  1248. );
  1249. return $meetingData;
  1250. }
  1251. /**
  1252. * Get the meeting info from DB by its name
  1253. * @param int $id
  1254. *
  1255. * @return array
  1256. */
  1257. public function getMeeting($id)
  1258. {
  1259. $meetingData = Database::select(
  1260. '*',
  1261. 'plugin_bbb_meeting',
  1262. array('where' => array('id = ?' => $id)),
  1263. 'first'
  1264. );
  1265. return $meetingData;
  1266. }
  1267. /**
  1268. * @param int $meetingId
  1269. * @return array
  1270. */
  1271. public function findConnectedMeetingParticipants($meetingId)
  1272. {
  1273. $meetingData = Database::select(
  1274. '*',
  1275. 'plugin_bbb_room',
  1276. array('where' => array('meeting_id = ? AND in_at IS NOT NULL' => $meetingId))
  1277. );
  1278. $participantIds = [];
  1279. $return = [];
  1280. foreach ($meetingData as $participantInfo) {
  1281. if (in_array($participantInfo['participant_id'], $participantIds)) {
  1282. continue;
  1283. }
  1284. $participantIds[] = $participantInfo['participant_id'];
  1285. $return[] = [
  1286. 'id' => $participantInfo['id'],
  1287. 'meeting_id' => $participantInfo['meeting_id'],
  1288. 'participant' => api_get_user_entity($participantInfo['participant_id']),
  1289. 'in_at' => $participantInfo['in_at'],
  1290. 'out_at' => $participantInfo['out_at']
  1291. ];
  1292. }
  1293. return $return;
  1294. }
  1295. /**
  1296. * @param int $meetingId
  1297. * @param int $userId
  1298. *
  1299. * @return array
  1300. */
  1301. public function getMeetingParticipantInfo($meetingId, $userId)
  1302. {
  1303. $meetingData = Database::select(
  1304. '*',
  1305. 'plugin_bbb_room',
  1306. array('where' => array('meeting_id = ? AND participant_id = ?' => [$meetingId, $userId])),
  1307. 'first'
  1308. );
  1309. if ($meetingData) {
  1310. return $meetingData;
  1311. }
  1312. return [];
  1313. }
  1314. /**
  1315. * @param array $meetingInfo
  1316. * @param array $recordInfo
  1317. * @param bool $isGlobal
  1318. * @param bool $isAdminReport
  1319. * @return array
  1320. */
  1321. private function getActionLinks(
  1322. $meetingInfo,
  1323. $recordInfo,
  1324. $isGlobal = false,
  1325. $isAdminReport = false
  1326. ) {
  1327. $isVisible = $meetingInfo['visibility'] != 0;
  1328. $linkVisibility = $isVisible
  1329. ? Display::url(
  1330. Display::return_icon('visible.png', get_lang('MakeInvisible')),
  1331. $this->unPublishUrl($meetingInfo)
  1332. )
  1333. : Display::url(
  1334. Display::return_icon('invisible.png', get_lang('MakeVisible')),
  1335. $this->publishUrl($meetingInfo)
  1336. );
  1337. $links = [];
  1338. if (empty($recordInfo)) {
  1339. if (!$isAdminReport) {
  1340. $links[] = Display::url(
  1341. Display::return_icon('delete.png', get_lang('Delete')),
  1342. $this->deleteRecordUrl($meetingInfo)
  1343. );
  1344. $links[] = $linkVisibility;
  1345. return $links;
  1346. } else {
  1347. $links[] = Display::url(
  1348. Display::return_icon('course_home.png', get_lang('GoToCourse')),
  1349. $this->getListingUrl()
  1350. );
  1351. return $links;
  1352. }
  1353. }
  1354. if (!$isGlobal) {
  1355. $links[] = Display::url(
  1356. Display::return_icon('link.gif', get_lang('UrlMeetingToShare')),
  1357. $this->copyToRecordToLinkTool($meetingInfo)
  1358. );
  1359. $links[] = Display::url(
  1360. Display::return_icon('agenda.png', get_lang('AddToCalendar')),
  1361. $this->addToCalendarUrl($meetingInfo, $recordInfo)
  1362. );
  1363. }
  1364. $hide = $this->plugin->get('disable_download_conference_link') === 'true' ? true : false;
  1365. if ($hide == false) {
  1366. if ($meetingInfo['has_video_m4v']) {
  1367. $links[] = Display::url(
  1368. Display::return_icon('save.png', get_lang('DownloadFile')),
  1369. $recordInfo['playbackFormatUrl'].'/capture.m4v',
  1370. ['target' => '_blank']
  1371. );
  1372. } else {
  1373. $links[] = Display::url(
  1374. Display::return_icon('save.png', get_lang('DownloadFile')),
  1375. '#',
  1376. [
  1377. 'id' => "btn-check-meeting-video-{$meetingInfo['id']}",
  1378. 'class' => 'check-meeting-video',
  1379. 'data-id' => $meetingInfo['id']
  1380. ]
  1381. );
  1382. }
  1383. }
  1384. if (!$isAdminReport) {
  1385. $links[] = Display::url(
  1386. Display::return_icon('delete.png', get_lang('Delete')),
  1387. $this->deleteRecordUrl($meetingInfo)
  1388. );
  1389. $links[] = $linkVisibility;
  1390. } else {
  1391. $links[] = Display::url(
  1392. Display::return_icon('course_home.png', get_lang('GoToCourse')),
  1393. $this->getListingUrl()
  1394. );
  1395. }
  1396. return $links;
  1397. }
  1398. /**
  1399. * @param int $meetingId
  1400. * @param string $videoUrl
  1401. * @return bool|int
  1402. */
  1403. public function updateMeetingVideoUrl($meetingId, $videoUrl)
  1404. {
  1405. return Database::update(
  1406. 'plugin_bbb_meeting',
  1407. ['video_url' => $videoUrl],
  1408. ['id = ?' => intval($meetingId)]
  1409. );
  1410. }
  1411. /**
  1412. * Check if the meeting has a capture.m4v video file. If exists then the has_video_m4v field is updated
  1413. * @param int $meetingId
  1414. * @return bool
  1415. */
  1416. public function checkDirectMeetingVideoUrl($meetingId)
  1417. {
  1418. $meetingInfo = Database::select(
  1419. '*',
  1420. 'plugin_bbb_meeting',
  1421. [
  1422. 'where' => ['id = ?' => intval($meetingId)]
  1423. ],
  1424. 'first'
  1425. );
  1426. if (!isset($meetingInfo['video_url'])) {
  1427. return false;
  1428. }
  1429. $hasCapture = SocialManager::verifyUrl($meetingInfo['video_url'].'/capture.m4v');
  1430. if ($hasCapture) {
  1431. return Database::update(
  1432. 'plugin_bbb_meeting',
  1433. ['has_video_m4v' => true],
  1434. ['id = ?' => intval($meetingId)]
  1435. );
  1436. }
  1437. return $hasCapture;
  1438. }
  1439. /**
  1440. * @param array $userInfo
  1441. * @return bool
  1442. */
  1443. public static function showGlobalConferenceLink($userInfo)
  1444. {
  1445. if (empty($userInfo)) {
  1446. return false;
  1447. }
  1448. $setting = api_get_plugin_setting('bbb', 'enable_global_conference');
  1449. $settingLink = api_get_plugin_setting('bbb', 'enable_global_conference_link');
  1450. if ($setting === 'true' && $settingLink === 'true') {
  1451. //$content = Display::url(get_lang('LaunchVideoConferenceRoom'), $url);
  1452. $allowedRoles = api_get_plugin_setting(
  1453. 'bbb',
  1454. 'global_conference_allow_roles'
  1455. );
  1456. if (api_is_platform_admin()) {
  1457. $userInfo['status'] = PLATFORM_ADMIN;
  1458. }
  1459. $showGlobalLink = true;
  1460. if (!empty($allowedRoles)) {
  1461. if (!in_array($userInfo['status'], $allowedRoles)) {
  1462. $showGlobalLink = false;
  1463. }
  1464. }
  1465. return $showGlobalLink;
  1466. }
  1467. }
  1468. }