number)) return $conference->number; $idLength = strlen((string)$conference->id); if($idLength > 8) { $number = $idLength . $conference->id; } else { /* Pad the number to length of 9 with openedDate if id is not long enough. */ $dateNum = str_replace('-', '', current(explode(' ', $conference->openedDate))); $number = $idLength . substr($dateNum, 0, 8 - $idLength) . $conference->id; } return $number; } /** * Set the number of a conference. * * @param int $id conference id * @param string $number conference number * @access public * @return bool */ public function setNumber($id, $number) { $this->dao->update(TABLE_IM_CONFERENCE)->set('number')->eq($number)->where('id')->eq($id)->exec(); return !dao::isError(); } /** * Extract id from number. * * @param string $number * @access public * @return int */ public function extractIdFromNumber($number) { $len = strlen($number); /* Use first chars as length of id. */ if($len > 10) { $id = substr($number, 2); } else { $idLength = intval(substr($number, 0, 1)); $id = substr($number, $len - $idLength, $idLength); } return intval($id); } /** * Get a conference by number. * The number is generated by the getNumber method. * * @param int $number * @access public * @return object */ public function getByNumber($number) { $id = $this->extractIdFromNumber($number); return $this->getById($id); } /** * Get the old conference by the id of chat. * @deprecated * @param string $chatID * @access public * @return object */ public function getByChatID($chatID) { return $this->dao->select('*')->from(TABLE_IM_CONFERENCE) ->where('number')->eq('') ->andWhere('cgid')->eq($chatID) ->orderBy('id_asc') ->fetch(); } /** * Get the latest conference by the id of chat. * * @param string $cgid * @access public * @return object */ public function getLatestByChatID($cgid) { return $this->dao->select('*')->from(TABLE_IM_CONFERENCE) ->where('cgid')->eq($cgid) ->andWhere('status')->eq('open') ->orderBy('id_desc') ->fetch(); } /** * Get a conference by id. * * @param int $id * @access public * @return object */ public function getById($id) { return $this->dao->select('*')->from(TABLE_IM_CONFERENCE) ->where('id')->eq($id) ->fetch(); } /** * get not started scheduled conferences. * @return object */ public function getNotStartedSchedule() { return $this->dao->select('*')->from(TABLE_IM_CONFERENCE) ->where('status')->eq('notStarted') ->andWhere('type')->eq('scheduled') /* notStarted conferences notify count will be 1 when create. */ ->andWhere('sentNotify')->eq(1) ->fetchAll(); } /** * get will start scheduled conferences. * @return object */ public function getWillStartSchedule() { return $this->dao->select('*')->from(TABLE_IM_CONFERENCE) ->where('status')->eq('notStarted') ->andWhere('type')->eq('scheduled') /* will start conference notify count should be 2 */ ->andWhere('sentNotify')->in(2) ->fetchAll(); } /** * Get conference by condition. * @param string $condition * @param array $options * @param int $userID * @return array */ public function getByCondition($condition, $options, $pager, $userID) { if(!is_object($options) && is_array($options)) $options = (object)$options; $isCreateByMe = property_exists($options, 'isCreateByMe') ? $options->isCreateByMe : false; $conferences = array(); switch($condition) { case 'immediate': $chatList = $this->loadModel('im')->chatGetGidListByUserID($userID); $conferences = $this->dao->select('t1.*, t2.hide, t2.user')->from(TABLE_IM_CONFERENCE)->alias('t1') ->leftJoin(TABLE_IM_CONFERENCEUSER)->alias('t2') ->on('t2.conference=t1.id and t2.user')->eq($userID) ->where('(t2.user is null') ->orWhere('t2.user')->eq($userID) ->andWhere('t2.hide')->ne(1) ->markRight(1) ->andWhere('t1.number')->ne('') ->andWhere('t1.type')->eq('default') ->beginIF($isCreateByMe)->andWhere('t1.openedBy')->eq($userID) ->fi() ->andWhere('t1.participants', true)->like("%,$userID,%") ->orWhere('t1.invitee')->like("%,$userID,%") ->orWhere('t1.subscribers')->like("%,$userID,%") ->orWhere('t1.cgid')->in($chatList) ->markRight(1) ->orderBy('id_desc') ->page($pager) ->fetchAll(); break; case 'scheduled': $conferences = $this->dao->select('t1.*, t2.hide, t2.user')->from(TABLE_IM_CONFERENCE)->alias('t1') ->leftJoin(TABLE_IM_CONFERENCEUSER)->alias('t2') ->on('t2.conference=t1.id and t2.user')->eq($userID) ->where('(t2.user is null') ->orWhere('t2.user')->eq($userID) ->andWhere('t2.hide')->ne(1) ->markRight(1) ->andWhere('t1.type')->eq('scheduled') ->beginIF($isCreateByMe)->andWhere('t1.openedBy')->eq($userID) ->fi() ->andWhere('t1.invitee', true)->like("%,$userID,%") ->orWhere('t1.subscribers')->like("%,$userID,%") ->markRight(1) ->orderBy('id_desc') ->page($pager) ->fetchAll(); break; } $thisClass = $this; return array_map(function($conference) use ($thisClass) { return $thisClass->format($conference); }, $conferences); } /** * Format conference data. * * @param object $conference * @access public * @return object */ public function format($conference) { if(empty($conference)) return $conference; foreach(array('openedDate', 'startTime', 'endTime') as $key) $conference->$key = empty($conference->$key) || $conference->$key == null ? 0 : strtotime($conference->$key); if(!empty($conference->number) && !empty($conference->rid) && empty($conference->room)) { $conference->room = $conference->rid; unset($conference->rid); } return $conference; } // /** // * Generate room password. // * // * @access private // * @return string // */ // private function generateRoomPassword() // { // return sprintf("%06d", mt_rand(0, 999999)); // } /** * Create a detached conference. * * @param string $chatID * @param string $invitee * @param string $topic * @param string $type * @param string $password * @param string $startTime * @param string $endTime * @param int $reminderTime * @param string $note * @param int $userID * @param bool $isPrivate * @param bool $isInner * @access public * @return object|boolean */ public function createDetached($chatID, $invitee, $type = 'default', $topic = '', $password = '', $startTime = null, $endTime = null, $reminderTime = 0, $note = '', $isPrivate = false, $isInner = true, $userID = 0) { $conferenceData = new stdClass(); $conferenceData->openedDate = helper::now(); $this->dao->insert(TABLE_IM_CONFERENCE)->data($conferenceData)->exec(); if(dao::isError()) return false; $id = $this->dao->lastInsertID(); if(empty($id)) return false; $conference = $this->getById($id); if(empty($conference)) return false; $number = $this->getNumber($conference); $this->setNumber($id, $number); $conferenceData = new stdClass(); $conferenceData->cgid = $chatID; $conferenceData->rid = $number; $conferenceData->status = $type == 'default' ? 'open' : 'notStarted'; $conferenceData->openedBy = (int)$userID; $conferenceData->openedDate = helper::now(); $conferenceData->subscribers = ",$userID,"; $conferenceData->participants = $type == 'default' ? ",$userID," : '' ; $conferenceData->invitee = empty($invitee) ? '' : ",$invitee,"; $conferenceData->topic = $topic; $conferenceData->type = $type; // $conferenceData->password = empty($password) ? $this->generateRoomPassword() : $password; $conferenceData->password = ''; if(!empty($startTime)) $conferenceData->startTime = $startTime; if(!empty($endTime)) $conferenceData->endTime = $endTime; $conferenceData->number = $number; $conferenceData->reminderTime = $reminderTime; $conferenceData->note = $note; $conferenceData->moderators = ",$userID,"; $conferenceData->isPrivate = $isPrivate === true ? '1' : '0'; $conferenceData->isInner = $isInner === true ? '1' : '0'; $this->dao->update(TABLE_IM_CONFERENCE)->data($conferenceData)->where('id')->eq($id)->exec(); $conference = $this->getById($id); if(dao::isError()) return false; return $conference; } /** * set the scheduled conference status to open, and return the conference. * @param $conference * @return object */ public function startSchedule($conference) { $this->dao->update(TABLE_IM_CONFERENCE) ->set('status')->eq('open') ->where('number')->eq($conference->number) ->exec(); if(dao::isError()) return false; return $this->dao->select('*')->from(TABLE_IM_CONFERENCE) ->where('number')->eq($conference->number) ->fetch(); } /** * Close a conference. * * @param string $conferenceId * @access public * @return boolean */ public function close($conferenceId) { $conference = $this->getById($conferenceId); if($conference->status == 'closed') return true; $this->dao->update(TABLE_IM_CONFERENCE) ->set('status')->eq('closed') ->set('participants')->eq('') ->where('id')->eq($conference->id) ->exec(); $this->dao->update(TABLE_IM_CONFERENCEINVITE) ->set('status')->eq('rejected') ->where('conferenceID')->eq($conference->id) ->exec(); return !dao::isError(); } /** * cancel a conference. * * @param string $conferenceId * @access public * @return boolean */ public function cancel($conferenceId) { $conference = $this->getById($conferenceId); if($conference->status == 'canceled') return true; $this->dao->update(TABLE_IM_CONFERENCE) ->set('status')->eq('canceled') ->set('participants')->eq('') ->where('id')->eq($conference->id) ->exec(); return !dao::isError(); } /** * hide a conference. * * @param string $conferenceNumber * @param int $userID * @access public * @return bool */ public function hide($conferenceNumber, $userID) { $conferenceId = $this->extractIdFromNumber($conferenceNumber); $data = $this->dao->select('*')->from(TABLE_IM_CONFERENCEUSER)->where('conference')->eq($conferenceId)->andWhere('user')->eq($userID)->fetch(); if(empty($data)) { $this->dao->insert(TABLE_IM_CONFERENCEUSER) ->set('conference')->eq($conferenceId) ->set('user')->eq($userID) ->set('hide')->eq(1) ->exec(); } else { $this->dao->update(TABLE_IM_CONFERENCEUSER) ->set('hide')->eq(1) ->where('conference')->eq($conferenceId) ->andWhere('user')->eq($userID) ->exec(); } return !dao::isError(); } /** * update a conference. * * @param object $conference * @return bool */ public function update($conference) { $this->dao->update(TABLE_IM_CONFERENCE) ->data($conference) ->where('number')->eq($conference->number) ->exec(); return !dao::isError(); } /** * after sent notify, update the sentNotify field. * @param object $conference * @param int $times * @return void */ public function updateSentNotifyTimes($conference, $times) { $this->dao->update(TABLE_IM_CONFERENCE) ->set('sentNotify')->eq($times) ->where('number')->eq($conference->number) ->exec(); } /** * Add invitee into a conference. * * @param string $conferenceNumber * @param array $newInvitee * @access public * @return string|boolean */ public function addInvitee($conferenceNumber, $newInvitee) { $conference = $this->getByNumber($conferenceNumber); foreach($newInvitee as $uid) { $hasInviteRecord = $this->dao->select('id')->from(TABLE_IM_CONFERENCEINVITE) ->where('conferenceID')->eq($conference->id) ->andWhere('inviteeID')->eq($uid) ->fetch(); if(empty($hasInviteRecord)) { $now = helper::now(); $data = new stdClass(); $data->conferenceID = $conference->id; $data->inviteeID = $uid; $data->status = 'pending'; $data->createdDate = $now; $data->updatedDate = $now; $this->dao->insert(TABLE_IM_CONFERENCEINVITE) ->data($data) ->exec(); } else { $this->dao->update(TABLE_IM_CONFERENCEINVITE) ->set('status')->eq('pending') ->set('updatedDate')->eq(helper::now()) ->where('id')->eq($hasInviteRecord->id); } } $invitee = explode(',', $conference->invitee); $invitee = array_filter($invitee); $invitee = array_merge($invitee, $newInvitee); $invitee = array_unique($invitee); $invitee = implode(',', $invitee); $invitee = empty($invitee) ? '' : ",$invitee,"; $this->dao->update(TABLE_IM_CONFERENCE) ->set('invitee')->eq($invitee) ->where('id')->eq($conference->id) ->exec(); if(dao::isError()) return false; return $invitee; } /** * Add participant into a conference. * * @param string $conferenceNumber * @param int $userID * @access public * @return string|boolean */ public function addParticipant($conferenceNumber, $userID) { $conference = $this->getByNumber($conferenceNumber); $participants = explode(',', $conference->participants); $participants = array_filter($participants); $participants[] = $userID; $participants = array_unique($participants); $participants = implode(',', $participants); $participants = empty($participants) ? '' : ",$participants,"; $this->dao->update(TABLE_IM_CONFERENCE) ->set('participants')->eq($participants) ->where('id')->eq($conference->id) ->exec(); if(dao::isError()) return false; return $participants; } /** * Remove participant from a conference. * * @param string $conferenceNumber * @param int $userID * @access public * @return string|false */ public function removeParticipant($conferenceNumber, $userID) { $conference = $this->getByNumber($conferenceNumber); if($conference->participants == '') return false; $participants = explode(',', $conference->participants); $participants = array_filter($participants); $participants = array_diff($participants, array($userID)); $participants = implode(',', $participants); $participants = empty($participants) ? '' : ",$participants,"; $this->dao->update(TABLE_IM_CONFERENCE) ->set('participants')->eq($participants) ->where('id')->eq($conference->id) ->exec(); if(dao::isError()) return false; return $participants; } public function addModerator($conferenceNumber, $userID) { $conference = $this->getByNumber($conferenceNumber); if(empty($conference)) return false; $moderators = explode(',', $conference->moderators); $moderators = array_filter($moderators); $moderators = array_unique(array_merge($moderators, array($userID))); $moderators = implode(',', $moderators); $moderators = empty($moderators) ? '' : ",$moderators,"; $this->dao->update(TABLE_IM_CONFERENCE) ->set('moderators')->eq($moderators) ->where('id')->eq($conference->id) ->exec(); return true; } public function addSubscriber($conferenceNumber, $userID) { $conference = $this->dao->select('subscribers')->from(TABLE_IM_CONFERENCE) ->where('number')->eq($conferenceNumber) ->fetch(); if (!empty($conference)) { $subscribers = explode(',', $conference->subscribers); $subscribers = array_filter($subscribers); $subscribers = array_unique(array_merge($subscribers, array($userID))); $subscribers = ',' . implode(',', $subscribers) . ','; $this->dao->update(TABLE_IM_CONFERENCE) ->set('subscribers')->eq($subscribers) ->where('number')->eq($conferenceNumber) ->exec(); } } /** * Remove the user from all related conferences and close if necessary. * * @param int $userID * @access public * @return string[] */ public function removeUserFromConferences($userID) { $conferences = $this->dao->select('*')->from(TABLE_IM_CONFERENCE) ->where('participants')->like("%,$userID,%") ->fetchAll(); $closedConferences = []; foreach($conferences as $conference) { $participants = explode(',', $conference->participants); $participants = array_filter($participants); $participants = array_values($participants); $participants = array_map(function($v){ return (int)$v; }, $participants); if(in_array($userID, $participants)) { $leftParticipants = $this->removeParticipant($conference->number, $userID); if($leftParticipants == '') $closedConferences[] = $conference->number; } } return $closedConferences; } /** * Try to remove user from a conference of a chat. * * @deprecated * @param string $chatID * @param int $userID * @access public * @return object|boolean */ public function removeUserFromChat($chatID, $userID) { /* Ignore conference number. */ if(is_numeric($chatID)) return false; $conference = $this->getByChatID($chatID); if(empty($conference)) return false; $participants = explode(',', $conference->participants); $participants = array_filter($participants); $participants = array_diff($participants, array($userID)); if(count($participants) == 0) { $this->close($conference->id, $userID); return false; } $participants = implode(',', $participants); $conference->participants = $participants; return $conference; } /** * Reset status of conferences. * * @access public * @return void */ public function resetStatus() { $this->dao->update(TABLE_IM_CONFERENCE) ->set('status')->eq('closed') ->set('participants')->eq('') ->where('status')->eq('open') ->exec(); } /** * Check if there is a participant limit in license. * If there is no limit, return 'unlimited'. * Otherwise, return the limit number. * * @access public * @return int */ public function getConferenceLimit() { $limit = extCommonModel::getLicensePropertyValue('unlimitedParticipants'); // fallback for older value. if($limit === '1') $limit = null; if($limit === '0') $limit = 3; return $limit; } /** * accept a conference invite. * * @param int $conferenceID * @param int $invitee * @return bool */ public function acceptInvite($conferenceID, $invitee) { $this->dao->update(TABLE_IM_CONFERENCEINVITE) ->set('status')->eq('accepted') ->set('updatedDate')->eq(helper::now()) ->where('conferenceID')->eq($conferenceID) ->andWhere('inviteeID')->eq($invitee) ->exec(); return !dao::isError(); } /** * reject a conference invite. * * @param int $conferenceID * @param int $invitee * @return bool */ public function rejectInvite($conferenceID, $invitee) { $this->dao->update(TABLE_IM_CONFERENCEINVITE) ->set('status')->eq('rejected') ->set('updatedDate')->eq(helper::now()) ->where('conferenceID')->eq($conferenceID) ->andWhere('inviteeID')->eq($invitee) ->exec(); return !dao::isError(); } /** * get pending invites. * * @param int $userID * @return array */ public function getPendingInvites($userID) { return $this->dao->select('t1.*, t2.*')->from(TABLE_IM_CONFERENCEINVITE)->alias('t1') ->leftJoin(TABLE_IM_CONFERENCE)->alias('t2')->on('t1.conferenceID = t2.id') ->where('t1.inviteeID')->eq($userID) ->andWhere('t1.status')->eq('pending') ->andWhere('t2.status')->eq('open') ->fetchAll(); } /** * get the jwt secret for jitsi meet authentication. * * @return string */ public function getJwtSecret() { return $this->dao->select('value')->from(TABLE_CONFIG) ->where('owner')->eq('system') ->andWhere('module')->eq('jitsi') ->andWhere('section')->eq('jwt') ->andWhere('`key`')->eq('secret') ->fetch('value'); } }