openmeetings.class.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. <?php
  2. /**
  3. * Chamilo-OpenMeetings integration plugin library, defining methods to connect
  4. * to OpenMeetings from Chamilo by calling its web services
  5. * @package chamilo.plugin.openmeetings
  6. */
  7. namespace Chamilo\Plugin\OpenMeetings;
  8. include_once __DIR__.'/session.class.php';
  9. include_once __DIR__.'/room.class.php';
  10. include_once __DIR__.'/user.class.php';
  11. /**
  12. * Open Meetings-Chamilo connector class
  13. */
  14. class OpenMeetings
  15. {
  16. public $url;
  17. public $user;
  18. public $pass;
  19. public $api;
  20. public $user_complete_name = null;
  21. public $protocol = 'http://';
  22. public $debug = false;
  23. public $logout_url = null;
  24. public $plugin_enabled = false;
  25. public $sessionId = "";
  26. public $roomName = '';
  27. public $chamiloCourseId;
  28. public $chamiloSessionId;
  29. public $externalType;
  30. /**
  31. * Constructor (generates a connection to the API and the Chamilo settings
  32. * required for the connection to the video conference server)
  33. */
  34. public function __construct()
  35. {
  36. global $_configuration;
  37. // initialize video server settings from global settings
  38. $plugin = \OpenMeetingsPlugin::create();
  39. $om_plugin = (bool) $plugin->get('tool_enable');
  40. $om_host = $plugin->get('host');
  41. $om_user = $plugin->get('user');
  42. $om_pass = $plugin->get('pass');
  43. $accessUrl = api_get_access_url($_configuration['access_url']);
  44. $this->externalType = substr($accessUrl['url'], strpos($accessUrl['url'], '://') + 3, -1);
  45. if (strcmp($this->externalType, 'localhost') == 0) {
  46. $this->externalType = substr(api_get_path(WEB_PATH), strpos(api_get_path(WEB_PATH), '://') + 3, -1);
  47. }
  48. $this->externalType = 'chamilolms.'.$this->externalType;
  49. $this->table = \Database::get_main_table('plugin_openmeetings');
  50. if ($om_plugin) {
  51. $user_info = api_get_user_info();
  52. $this->user_complete_name = $user_info['complete_name'];
  53. $this->user = $om_user;
  54. $this->pass = $om_pass;
  55. $this->url = $om_host;
  56. // Setting OM api
  57. define('CONFIG_OPENMEETINGS_USER', $this->user);
  58. define('CONFIG_OPENMEETINGS_PASS', $this->pass);
  59. define('CONFIG_OPENMEETINGS_SERVER_URL', $this->url);
  60. $this->gateway = new \OpenMeetingsGateway($this->url, $this->user, $this->pass);
  61. $this->plugin_enabled = $om_plugin;
  62. // The room has a name composed of C + course ID + '-' + session ID
  63. $this->chamiloCourseId = api_get_course_int_id();
  64. $this->chamiloSessionId = api_get_session_id();
  65. $this->roomName = 'C'.$this->chamiloCourseId.'-'.$this->chamiloSessionId;
  66. $return = $this->gateway->loginUser();
  67. if ($return == 0) {
  68. $msg = 'Could not initiate session with server through OpenMeetingsGateway::loginUser()';
  69. error_log(__FILE__.'+'.__LINE__.': '.$msg);
  70. die($msg);
  71. }
  72. $this->sessionId = $this->gateway->sessionId;
  73. }
  74. }
  75. /**
  76. * Checks whether a user is teacher in the current course
  77. * @return bool True if the user can be considered a teacher in this course, false otherwise
  78. */
  79. public function isTeacher()
  80. {
  81. return api_is_course_admin() || api_is_coach() || api_is_platform_admin();
  82. }
  83. /*
  84. * Creating a Room for the meeting
  85. * @return bool True if the user is correct and false when is incorrect
  86. */
  87. public function createMeeting($params)
  88. {
  89. global $_configuration;
  90. // First, try to see if there is an active room for this course and session.
  91. $roomId = null;
  92. $meetingData = \Database::select(
  93. '*',
  94. $this->table,
  95. array(
  96. 'where' =>
  97. array(
  98. 'c_id = ?' => $this->chamiloCourseId,
  99. ' AND session_id = ? ' => $this->chamiloSessionId,
  100. ' AND status <> ? ' => 2,
  101. )
  102. ),
  103. 'first'
  104. );
  105. if ($meetingData != false && count($meetingData) > 0) {
  106. // There has been a room in the past for this course. It should
  107. // still be on the server, so update (instead of creating a new one)
  108. // This fills the following attributes: status, name, comment, chamiloCourseId, chamiloSessionId
  109. $room = new Room();
  110. $room->loadRoomId($meetingData['room_id']);
  111. $roomArray = (array) $room;
  112. $roomArray['SID'] = $this->sessionId;
  113. $roomId = $this->gateway->updateRoomWithModeration($room);
  114. if ($roomId != $meetingData['room_id']) {
  115. $msg = 'Something went wrong: the updated room ID ('.$roomId.') is not the same as the one we had ('.$meetingData['room_id'].')';
  116. die($msg);
  117. }
  118. } else {
  119. $room = new Room();
  120. $room->SID = $this->sessionId;
  121. $room->name = $this->roomName;
  122. //$room->roomtypes_id = $room->roomtypes_id;
  123. $room->comment = urlencode(get_lang('Course').': '.$params['meeting_name'].' - '.$_configuration['software_name']);
  124. //$room->numberOfPartizipants = $room->numberOfPartizipants;
  125. $room->ispublic = boolval($room->getString('isPublic', 'false'));
  126. //$room->appointment = $room->getString('appointment');
  127. //$room->isDemoRoom = $room->getString('isDemoRoom');
  128. //$room->demoTime = $room->demoTime;
  129. //$room->isModeratedRoom = $room->getString('isModeratedRoom');
  130. $roomId = $this->gateway->createRoomWithModAndType($room);
  131. }
  132. if (!empty($roomId)) {
  133. /*
  134. // Find the biggest room_id so far, and create a new one
  135. if (empty($roomId)) {
  136. $roomData = \Database::select('MAX(room_id) as room_id', $this->table, array(), 'first');
  137. $roomId = $roomData['room_id'] + 1;
  138. }*/
  139. $params['status'] = '1';
  140. $params['meeting_name'] = $room->name;
  141. $params['created_at'] = api_get_utc_datetime();
  142. $params['room_id'] = $roomId;
  143. $params['c_id'] = api_get_course_int_id();
  144. $params['session_id'] = api_get_session_id();
  145. $params['record'] = ($room->allowRecording ? 1 : 0);
  146. $id = \Database::insert($this->table, $params);
  147. $this->joinMeeting($id);
  148. } else {
  149. return -1;
  150. }
  151. }
  152. /**
  153. * Returns a meeting "join" URL
  154. * @param string The name of the meeting (usually the course code)
  155. * @return mixed The URL to join the meeting, or false on error
  156. * @todo implement moderator pass
  157. * @assert ('') === false
  158. * @assert ('abcdefghijklmnopqrstuvwxyzabcdefghijklmno') === false
  159. */
  160. public function joinMeeting($meetingId)
  161. {
  162. if (empty($meetingId)) {
  163. return false;
  164. }
  165. $meetingData = \Database::select(
  166. '*',
  167. $this->table,
  168. array('where' => array('id = ? AND status = 1 ' => $meetingId)),
  169. 'first'
  170. );
  171. if (empty($meetingData)) {
  172. if ($this->debug) {
  173. error_log("meeting does not exist: $meetingId ");
  174. }
  175. return false;
  176. }
  177. $params = array('room_id' => $meetingData['room_id']);
  178. $returnVal = $this->setUserObjectAndGenerateRoomHashByURLAndRecFlag($params);
  179. $iframe = $this->url."/?"."secureHash=".$returnVal;
  180. printf("<iframe src='%s' width='%s' height = '%s' />", $iframe, "100%", 640);
  181. }
  182. /**
  183. * Checks if the videoconference server is running.
  184. * Function currently disabled (always returns 1)
  185. * @return bool True if server is running, false otherwise
  186. * @assert () === false
  187. */
  188. public function isServerRunning()
  189. {
  190. // Always return true for now as this requires the openmeetings object
  191. // to have been instanciated and this includes a loginUser() which
  192. // connects to the server
  193. return true;
  194. }
  195. /**
  196. * Gets the password for a specific meeting for the current user
  197. * @return string A moderator password if user is teacher, or the course code otherwise
  198. */
  199. public function getMeetingUserPassword()
  200. {
  201. if ($this->isTeacher()) {
  202. return $this->getMeetingModerationPassword();
  203. } else {
  204. return api_get_course_id();
  205. }
  206. }
  207. /**
  208. * Generated a moderator password for the meeting
  209. * @return string A password for the moderation of the video conference
  210. */
  211. public function getMeetingModerationPassword()
  212. {
  213. return api_get_course_id().'mod';
  214. }
  215. /**
  216. * Get information about the given meeting
  217. * @param array ...?
  218. * @return mixed Array of information on success, false on error
  219. * @assert (array()) === false
  220. */
  221. public function getMeetingInfo($params)
  222. {
  223. try {
  224. $result = $this->api->getMeetingInfoArray($params);
  225. if ($result == null) {
  226. if ($this->debug) {
  227. error_log(__FILE__.'+'.__LINE__." Failed to get any response. Maybe we can't contact the OpenMeetings server.");
  228. }
  229. } else {
  230. return $result;
  231. }
  232. } catch (Exception $e) {
  233. if ($this->debug) {
  234. error_log(__FILE__.'+'.__LINE__.' Caught exception: ', $e->getMessage(), "\n");
  235. }
  236. }
  237. return false;
  238. }
  239. /**
  240. * @param array $params Array of parameters
  241. * @return mixed
  242. */
  243. public function setUserObjectAndGenerateRecordingHashByURL($params)
  244. {
  245. $username = $_SESSION['_user']['username'];
  246. $firstname = $_SESSION['_user']['firstname'];
  247. $lastname = $_SESSION['_user']['lastname'];
  248. $userId = $_SESSION['_user']['user_id'];
  249. $systemType = 'chamilo';
  250. $room_id = $params['room_id'];
  251. $urlWsdl = $this->url."/services/UserService?wsdl";
  252. $omServices = new \SoapClient($urlWsdl);
  253. $objRec = new User();
  254. $objRec->SID = $this->sessionId;
  255. $objRec->username = $username;
  256. $objRec->firstname = $firstname;
  257. $objRec->lastname = $lastname;
  258. $objRec->externalUserId = $userId;
  259. $objRec->externalUserType = $systemType;
  260. $objRec->recording_id = $recording_id;
  261. $orFn = $omServices->setUserObjectAndGenerateRecordingHashByURL($objRec);
  262. return $orFn->return;
  263. }
  264. /**
  265. * @param Array $params Array of parameters
  266. * @return mixed
  267. */
  268. public function setUserObjectAndGenerateRoomHashByURLAndRecFlag($params)
  269. {
  270. $username = $_SESSION['_user']['username'];
  271. $firstname = $_SESSION['_user']['firstname'];
  272. $lastname = $_SESSION['_user']['lastname'];
  273. $profilePictureUrl = $_SESSION['_user']['avatar'];
  274. $email = $_SESSION['_user']['mail'];
  275. $userId = $_SESSION['_user']['user_id'];
  276. $systemType = 'Chamilo';
  277. $room_id = $params['room_id'];
  278. $becomeModerator = ($this->isTeacher() ? 1 : 0);
  279. $allowRecording = 1; //Provisional
  280. $urlWsdl = $this->url."/services/UserService?wsdl";
  281. $omServices = new \SoapClient($urlWsdl);
  282. $objRec = new User();
  283. $objRec->SID = $this->sessionId;
  284. $objRec->username = $username;
  285. $objRec->firstname = $firstname;
  286. $objRec->lastname = $lastname;
  287. $objRec->profilePictureUrl = $profilePictureUrl;
  288. $objRec->email = $email;
  289. $objRec->externalUserId = $userId;
  290. $objRec->externalUserType = $systemType;
  291. $objRec->room_id = $room_id;
  292. $objRec->becomeModeratorAsInt = $becomeModerator;
  293. $objRec->showAudioVideoTestAsInt = 1;
  294. $objRec->allowRecording = $allowRecording;
  295. $rcFn = $omServices->setUserObjectAndGenerateRoomHashByURLAndRecFlag($objRec);
  296. return $rcFn->return;
  297. }
  298. /**
  299. * Gets all the course meetings saved in the plugin_openmeetings table
  300. * @return array Array of current open meeting rooms
  301. */
  302. public function getCourseMeetings()
  303. {
  304. $newMeetingsList = array();
  305. $item = array();
  306. $meetingsList = \Database::select(
  307. '*',
  308. $this->table,
  309. array('where' =>
  310. array(
  311. 'c_id = ? ' => api_get_course_int_id(),
  312. ' AND session_id = ? ' => api_get_session_id(),
  313. ' AND status <> ? ' => 2 // status deleted
  314. )
  315. )
  316. );
  317. $room = new Room();
  318. $room->SID = $this->sessionId;
  319. if (!empty($meetingsList)) {
  320. foreach ($meetingsList as $meetingDb) {
  321. //$room->rooms_id = $meetingDb['room_id'];
  322. error_log(__FILE__.'+'.__LINE__.' Meetings found: '.print_r($meetingDb, 1));
  323. $remoteMeeting = array();
  324. $meetingDb['created_at'] = api_get_local_time($meetingDb['created_at']);
  325. $meetingDb['closed_at'] = (!empty($meetingDb['closed_at']) ? api_get_local_time($meetingDb['closed_at']) : '');
  326. // Fixed value for now
  327. $meetingDb['participantCount'] = 40;
  328. $rec = $this->gateway->getFlvRecordingByRoomId($meetingDb['room_id']);
  329. $links = array();
  330. // Links to videos look like these:
  331. // http://video2.openmeetings.com:5080/openmeetings/DownloadHandler?fileName=flvRecording_4.avi&moduleName=lzRecorderApp&parentPath=&room_id=&sid=dfc0cac396d384f59242aa66e5a9bbdd
  332. $link = $this->url.'/DownloadHandler?fileName=%s&moduleName=lzRecorderApp&parentPath=&room_id=%s&sid=%s';
  333. if (!empty($rec)) {
  334. $link1 = sprintf($link, $rec['fileHash'], $meetingDb['room_id'], $this->sessionId);
  335. $link2 = sprintf($link, $rec['alternateDownload'], $meetingDb['room_id'], $this->sessionId);
  336. $links[] = $rec['fileName'].' '.
  337. \Display::url('[.flv]', $link1, array('target' => '_blank')).' '.
  338. \Display::url('[.avi]', $link2, array('target' => '_blank'));
  339. }
  340. $item['show_links'] = implode('<br />', $links);
  341. // The following code is currently commented because the web service
  342. // says this is not allowed by the SOAP user.
  343. /*
  344. try {
  345. // Get the conference room object from OpenMeetings server - requires SID and rooms_id to be defined
  346. $objRoomId = $this->gateway->getRoomById($meetingDb['room_id']);
  347. if (empty($objRoomId->return)) {
  348. error_log(__FILE__.'+'.__LINE__.' Emptyyyyy ');
  349. //\Database::delete($this->table, "id = {$meetingDb['id']}");
  350. // Don't delete expired rooms, just mark as closed
  351. \Database::update($this->table, array('status' => 0, 'closed_at' => api_get_utc_datetime()), array('id = ? ' => $meetingDb['id']));
  352. continue;
  353. }
  354. //$objCurUs = $omServices->getRoomWithCurrentUsersById($objCurrentUsers);
  355. } catch (SoapFault $e) {
  356. error_log(__FILE__.'+'.__LINE__.' '.$e->faultstring);
  357. exit;
  358. }
  359. //if( empty($objCurUs->returnMeetingID) ) continue;
  360. $current_room = array(
  361. 'roomtype' => $objRoomId->return->roomtype->roomtypes_id,
  362. 'meetingName' => $objRoomId->return->name,
  363. 'meetingId' => $objRoomId->return->meetingID,
  364. 'createTime' => $objRoomId->return->rooms_id,
  365. 'showMicrophoneStatus' => $objRoomId->return->showMicrophoneStatus,
  366. 'attendeePw' => $objRoomId->return->attendeePW,
  367. 'moderatorPw' => $objRoomId->return->moderators,
  368. 'isClosed' => $objRoomId->return->isClosed,
  369. 'allowRecording' => $objRoomId->return->allowRecording,
  370. 'startTime' => $objRoomId->return->startTime,
  371. 'endTime' => $objRoomId->return->updatetime,
  372. 'participantCount' => count($objRoomId->return->currentusers),
  373. 'maxUsers' => $objRoomId->return->numberOfPartizipants,
  374. 'moderatorCount' => count($objRoomId->return->moderators)
  375. );
  376. // Then interate through attendee results and return them as part of the array:
  377. if (!empty($objRoomId->return->currentusers)) {
  378. foreach ($objRoomId->return->currentusers as $a)
  379. $current_room[] = array(
  380. 'userId' => $a->username,
  381. 'fullName' => $a->firstname . " " . $a->lastname,
  382. 'isMod' => $a->isMod
  383. );
  384. }
  385. $remoteMeeting = $current_room;
  386. */
  387. if (empty($remoteMeeting)) {
  388. /*
  389. error_log(__FILE__.'+'.__LINE__.' Empty remote Meeting for now');
  390. if ($meetingDb['status'] == 1 && $this->isTeacher()) {
  391. $this->endMeeting($meetingDb['id']);
  392. }
  393. */
  394. } else {
  395. $remoteMeeting['add_to_calendar_url'] = api_get_self().'?action=add_to_calendar&id='.$meetingDb['id'].'&start='.api_strtotime($meetingDb['startTime']);
  396. }
  397. $remoteMeeting['end_url'] = api_get_self().'?action=end&id='.$meetingDb['id'];
  398. $remoteMeeting['delete_url'] = api_get_self().'?action=delete&id='.$meetingDb['id'];
  399. //$record_array = array();
  400. // if ($meetingDb['record'] == 1) {
  401. // $recordingParams = array(
  402. // 'meetingId' => $meetingDb['id'], //-- OPTIONAL - comma separate if multiple ids
  403. // );
  404. //
  405. // $records = $this->api->getRecordingsWithXmlResponseArray($recordingParams);
  406. // if (!empty($records)) {
  407. // $count = 1;
  408. // if (isset($records['message']) && !empty($records['message'])) {
  409. // if ($records['messageKey'] == 'noRecordings') {
  410. // $record_array[] = get_lang('NoRecording');
  411. // } else {
  412. // //$record_array[] = $records['message'];
  413. // }
  414. // } else {
  415. // foreach ($records as $record) {
  416. // if (is_array($record) && isset($record['recordId'])) {
  417. // $url = Display::url(get_lang('ViewRecord'), $record['playbackFormatUrl'], array('target' => '_blank'));
  418. // if ($this->is_teacher()) {
  419. // $url .= Display::url(Display::return_icon('link.gif',get_lang('CopyToLinkTool')), api_get_self().'?action=copy_record_to_link_tool&id='.$meetingDb['id'].'&record_id='.$record['recordId']);
  420. // $url .= Display::url(Display::return_icon('agenda.png',get_lang('AddToCalendar')), api_get_self().'?action=add_to_calendar&id='.$meetingDb['id'].'&start='.api_strtotime($meetingDb['created_at']).'&url='.$record['playbackFormatUrl']);
  421. // $url .= Display::url(Display::return_icon('delete.png',get_lang('Delete')), api_get_self().'?action=delete_record&id='.$record['recordId']);
  422. // }
  423. // //$url .= api_get_self().'?action=publish&id='.$record['recordID'];
  424. // $count++;
  425. // $record_array[] = $url;
  426. // } else {
  427. //
  428. // }
  429. // }
  430. // }
  431. // }
  432. // //var_dump($record_array);
  433. // $item['show_links'] = implode('<br />', $record_array);
  434. //
  435. // }
  436. //
  437. //$item['created_at'] = api_convert_and_format_date($meetingDb['created_at']);
  438. // //created_at
  439. //
  440. // $item['publish_url'] = api_get_self().'?action=publish&id='.$meetingDb['id'];
  441. // $item['unpublish_url'] = api_get_self().'?action=unpublish&id='.$meetingDb['id'];
  442. //
  443. //if ($meetingDb['status'] == 1) {
  444. // $joinParams = array(
  445. // 'meetingId' => $meetingDb['id'], //-- REQUIRED - A unique id for the meeting
  446. // 'username' => $this->user_complete_name, //-- REQUIRED - The name that will display for the user in the meeting
  447. // 'password' => $pass, //-- REQUIRED - The attendee or moderator password, depending on what's passed here
  448. // 'createTime' => '', //-- OPTIONAL - string. Leave blank ('') unless you set this correctly.
  449. // 'userID' => '', // -- OPTIONAL - string
  450. // 'webVoiceConf' => '' // -- OPTIONAL - string
  451. // );
  452. // $returnVal = $this->setUserObjectAndGenerateRoomHashByURLAndRecFlag( array('room_id' => $meetingDb['id']) );
  453. // $joinUrl = CONFIG_OPENMEETINGS_SERVER_URL . "?" .
  454. // "secureHash=" . $returnVal;
  455. //
  456. // $item['go_url'] = $joinUrl;
  457. //}
  458. $item = array_merge($item, $meetingDb, $remoteMeeting);
  459. //error_log(__FILE__.'+'.__LINE__.' Item: '.print_r($item,1));
  460. $newMeetingsList[] = $item;
  461. } //end foreach $meetingsList
  462. }
  463. return $newMeetingsList;
  464. }
  465. /**
  466. * Send a command to the OpenMeetings server to close the meeting
  467. * @param int $meetingId
  468. * @return int
  469. */
  470. public function endMeeting($meetingId)
  471. {
  472. try {
  473. $room = new Room($meetingId);
  474. $room->SID = $this->sessionId;
  475. $room->room_id = intval($meetingId);
  476. $room->status = false;
  477. $urlWsdl = $this->url."/services/RoomService?wsdl";
  478. $ws = new \SoapClient($urlWsdl);
  479. $roomClosed = $ws->closeRoom($room);
  480. if ($roomClosed > 0) {
  481. \Database::update(
  482. $this->table,
  483. array(
  484. 'status' => 0,
  485. 'closed_at' => api_get_utc_datetime()
  486. ),
  487. array('id = ? ' => $meetingId)
  488. );
  489. }
  490. } catch (SoapFault $e) {
  491. error_log(__FILE__.'+'.__LINE__.' Warning: We have detected some problems: Fault: '.$e->faultstring);
  492. exit;
  493. return -1;
  494. }
  495. }
  496. /**
  497. * @param int $id
  498. * @return int
  499. */
  500. public function deleteMeeting($id)
  501. {
  502. try {
  503. $room = new Room();
  504. $room->loadRoomId($id);
  505. $this->gateway->deleteRoom($room);
  506. \Database::update(
  507. $this->table,
  508. array(
  509. 'status' => 2
  510. ),
  511. array('id = ? ' => $id)
  512. );
  513. return $id;
  514. } catch (SoapFault $e) {
  515. error_log(__FILE__.'+'.__LINE__.' Warning: We have detected some problems: Fault: '.$e->faultstring);
  516. exit;
  517. return -1;
  518. }
  519. }
  520. }