"admin", CAN_VIEWSTATISTICS => "statistics", CAN_TAKEOVER => "takeover", CAN_VIEWTHREADS => "viewthreads", CAN_MODIFYPROFILE => "modifyprofile", ); } /** * Map numerical permissions ids onto its descriptions. * * The descriptions are localized. * * @return array Array whose keys are numerical permission ids and values are * localized permission descriptions. */ function permission_descriptions() { return array( CAN_ADMINISTRATE => getlocal('System administration: settings, operators management, button generation'), CAN_VIEWSTATISTICS => getlocal('Ability to view system statistics'), CAN_TAKEOVER => getlocal('Take over chat thread'), CAN_VIEWTHREADS => getlocal('View another operator\'s chat thread'), CAN_MODIFYPROFILE => getlocal('Ability to modify profile'), ); } /** * Set new permissions to operator * @param int $operator_id Operator ID * @param int $perm New permissions value */ function update_operator_permissions($operator_id, $perm) { $operator = operator_by_id($operator_id); $operator['iperm'] = $perm; update_operator($operator); } function operator_by_login($login) { $db = Database::getInstance(); return $db->query( "SELECT * FROM {operator} WHERE vclogin = ?", array($login), array('return_rows' => Database::RETURN_ONE_ROW) ); } function operator_by_email($mail) { $db = Database::getInstance(); return $db->query( "SELECT * FROM {operator} WHERE vcemail = ?", array($mail), array('return_rows' => Database::RETURN_ONE_ROW) ); } function operator_by_id($id) { $db = Database::getInstance(); return $db->query( "SELECT * FROM {operator} WHERE operatorid = ?", array($id), array('return_rows' => Database::RETURN_ONE_ROW) ); } /** * Load operator info by specified operators code * * @param string $code Operators code * @return array|boolean Operators info array or boolean false if there is no * operator with specified code. */ function operator_by_code($code) { $db = Database::getInstance(); return $db->query( "SELECT * FROM {operator} WHERE code = ?", array($code), array('return_rows' => Database::RETURN_ONE_ROW) ); } /** * Get list of operators taking into account $options * @param array $options Associative array of options. It can contains following * keys: * - 'sort': an associative array of sorting options. * - 'isolated_operator_id': id of current operators. If it set - function * would return only operators from adjacent groups. * * 'sort' array must contains two keys: 'by' and 'desc'. * 'by' means the field by which operators would be sort and can take * following values: 'commonname', 'localename', 'login', 'lastseen'. 'desc' * means order in which operators would be sort. If it's 'true' operators * would be sort in descending order and in ascending order overwise. * */ function get_operators_list($options = array()) { $db = Database::getInstance(); if (!empty($options['sort']) && isset($options['sort']['by']) && isset($options['sort']['desc'])) { switch ($options['sort']['by']) { case 'commonname': $orderby = 'vccommonname'; break; case 'localename': $orderby = 'vclocalename'; break; case 'lastseen': $orderby = 'time'; break; default: $orderby = 'vclogin'; break; } $orderby = $orderby . ' ' . ($options['sort']['desc'] ? 'DESC' : 'ASC'); } else { $orderby = "vclogin"; } $query = "SELECT DISTINCT " . "{operator}.operatorid, " . "vclogin, " . "vclocalename, " . "vccommonname, " . "code, " . "istatus, " . "idisabled, " . "(:now - dtmlastvisited) AS time " . "FROM {operator}" . (empty($options['isolated_operator_id']) ? "" : ", {operatortoopgroup} " . "WHERE {operator}.operatorid = {operatortoopgroup}.operatorid " . "AND {operatortoopgroup}.groupid IN (" . "SELECT g.groupid FROM {opgroup} g, " . "(SELECT {opgroup}.groupid, {opgroup}.parent FROM {opgroup}, {operatortoopgroup} " . "WHERE {opgroup}.groupid = {operatortoopgroup}.groupid " . "AND {operatortoopgroup}.operatorid = :operatorid) i " . "WHERE g.groupid = i.parent " . "OR g.parent = i.groupid " . "OR (g.parent = i.parent AND g.parent IS NOT NULL) " . "OR g.groupid = i.groupid " . ")") . " ORDER BY " . $orderby; $values = array( ':now' => time(), ); if (!empty($options['isolated_operator_id'])) { $values[':operatorid'] = $options['isolated_operator_id']; } $operators = $db->query( $query, $values, array('return_rows' => Database::RETURN_ALL_ROWS) ); return $operators; } /* * Get list of all operators * * @return array|null Operators list. Each its element contains (operatorid * integer, vclogin string, vclocalename string, vccommonname string, istatus * boolean, code string, idisabled integer, time integer) */ function operator_get_all() { $db = Database::getInstance(); return $operators = $db->query( ("SELECT operatorid, vclogin, vclocalename, vccommonname, istatus, " . "code, idisabled, (:now - dtmlastvisited) AS time " . "FROM {operator} ORDER BY vclogin"), array(':now' => time()), array('return_rows' => Database::RETURN_ALL_ROWS) ); } function operator_is_online($operator) { return $operator['time'] < Settings::get('online_timeout'); } function operator_is_available($operator) { return ($operator['istatus'] == 0 && $operator['time'] < Settings::get('online_timeout')) ? "1" : ""; } function operator_is_away($operator) { return ($operator['istatus'] != 0 && $operator['time'] < Settings::get('online_timeout')) ? "1" : ""; } function operator_is_disabled($operator) { return $operator['idisabled'] == '1'; } /** * Update existing operator's info. * * Triggers {@link \Mibew\EventDispatcher\Events::OPERATOR_UPDATE} event. * * @param array $operator Associative array of operator's fields. This array * must contain the following keys: * - operatorid, * - vclogin, * - vcpassword, * - vclocalename, * - vccommonname, * - vcemail, * - dtmlastvisited, * - istatus, * - idisabled, * - vcavatar, * - iperm, * - dtmrestore, * - vcrestoretoken, * - code * * @throws \InvalidArgumentException if not all operator's fields are in place. */ function update_operator($operator) { if (!check_operator_fields($operator)) { throw new \InvalidArgumentException('Not all operator fields are specified'); } // Get the original operator to trigger the "update" event later $original_operator = operator_by_id($operator['operatorid']); Database::getInstance()->query( ('UPDATE {operator} SET vclogin = :login, vcpassword=:password, ' . 'vclocalename = :local_name, vccommonname = :common_name, ' . 'vcemail = :email, dtmlastvisited = :last_visited, ' . 'istatus = :status, idisabled = :disabled, vcavatar = :avatar, ' . 'iperm = :permissions, dtmrestore = :restore_time, ' . 'vcrestoretoken = :restore_token, code = :code ' . 'WHERE operatorid = :id'), array( ':id' => $operator['operatorid'], ':login' => $operator['vclogin'], ':password' => $operator['vcpassword'], ':local_name' => $operator['vclocalename'], ':common_name' => $operator['vccommonname'], ':email' => $operator['vcemail'], ':last_visited' => $operator['dtmlastvisited'], ':status' => $operator['istatus'], ':disabled' => $operator['idisabled'], ':avatar' => $operator['vcavatar'], ':permissions' => $operator['iperm'], ':restore_time' => $operator['dtmrestore'], ':restore_token' => $operator['vcrestoretoken'], ':code' => $operator['code'], ) ); $args = array( 'operator' => $operator, 'original_operator' => $original_operator, ); EventDispatcher::getInstance()->triggerEvent(Events::OPERATOR_UPDATE, $args); } function update_operator_avatar($operator_id, $avatar) { $operator = operator_by_id($operator_id); $operator['vcavatar'] = $avatar; update_operator($operator); } /** * Create new operator * * Triggers {@link \Mibew\EventDispatcher\Events::OPERATOR_CREATE} event. * * @param string $login Operator's login * @param string $email Operator's * @param string $password Operator's password * @param string $locale_name Operator's local name * @param string $common_name Operator's international name * @param string $avatar Operator's avatar * @param string $code Operator's code which use to start chat with specified * operator * @return array Operator's array */ function create_operator( $login, $email, $password, $locale_name, $common_name, $avatar, $code ) { $db = Database::getInstance(); $db->query( ("INSERT INTO {operator} (" . "vclogin, vcpassword, vclocalename, vccommonname, vcavatar, " . "vcemail, code " . ") VALUES (" . ":login, :pass, :localename, :commonname, :avatar, " . ":email, :code" . ")"), array( ':login' => $login, ':pass' => calculate_password_hash($login, $password), ':localename' => $locale_name, ':commonname' => $common_name, ':avatar' => $avatar, ':email' => $email, ':code' => $code, ) ); $id = $db->insertedId(); $new_operator = $db->query( "SELECT * FROM {operator} WHERE operatorid = ?", array($id), array('return_rows' => Database::RETURN_ONE_ROW) ); $event = array('operator' => $new_operator); EventDispatcher::getInstance()->triggerEvent(Events::OPERATOR_CREATE, $event); return $new_operator; } /** * Delete operator * * This function remove operator and associations with groups for this operator * from datatabse. * It triggers {@link \Mibew\EventDispatcher\Events::OPERATOR_DELETE} event. * * @param int $operator_id Operator ID */ function delete_operator($operator_id) { $db = Database::getInstance(); $db->query( "DELETE FROM {operatortoopgroup} WHERE operatorid = ?", array($operator_id) ); $db->query( "DELETE FROM {operator} WHERE operatorid = ?", array($operator_id) ); // Trigger 'operatorDelete' event $dispatcher = EventDispatcher::getInstance(); $args = array('id' => $operator_id); $dispatcher->triggerEvent(Events::OPERATOR_DELETE, $args); } /** * Set current status of the operator('available' or 'away') * * @param int $operator_id Id of the operator * @param int $istatus Operator status: '0' means 'available' and '1' means * 'away' */ function notify_operator_alive($operator_id, $istatus) { $operator = operator_by_id($operator_id); $operator['istatus'] = $istatus; $operator['dtmlastvisited'] = time(); update_operator($operator); } /** * Indicates if at least one operator of the group is online * @param int $group_id Id of the group * @return boolean true if the group have online operators and false otherwise */ function has_online_operators($group_id = "") { $db = Database::getInstance(); $query = "SELECT count(*) AS total, MIN(:now - dtmlastvisited) AS time " . "FROM {operator}"; $values = array(':now' => time()); if ($group_id) { $query .= ", {operatortoopgroup}, {opgroup} " . "WHERE {opgroup}.groupid = {operatortoopgroup}.groupid " . "AND ({opgroup}.groupid = :groupid OR {opgroup}.parent = :groupid) " . "AND {operator}.operatorid = {operatortoopgroup}.operatorid " . "AND istatus = 0"; $values[':groupid'] = $group_id; } else { if (Settings::get('enablegroups') == 1) { // If the groups and prechat survey are enabled and a button was // generated not for concrete group a user must select a group to // chat with. All groups will be checked for online operators. If // only operators, who do not related with groups, are online a user // cannot complete prechat survey because there will be no online // groups. The following code fixes this strange behaviour. $query .= ", {operatortoopgroup} " . "WHERE {operator}.operatorid = {operatortoopgroup}.operatorid " . "AND istatus = 0"; } else { $query .= " WHERE istatus = 0"; } } $row = $db->query( $query, $values, array('return_rows' => Database::RETURN_ONE_ROW) ); return ($row['time'] < Settings::get('online_timeout')) && ($row['total'] > 0); } /** * Gets list of operators that are currently online. * * @param int|null $group_id ID of the group online operators should belong to * or null to search within all groups. * @return Array List of online operators. Each item of the list is an array * with operators info. For details of its structure See description of * {@link operator_by_id()} function's return value. */ function get_online_operators($group_id = null) { $db = Database::getInstance(); $groups = false; if (!$group_id) { // We should not care about groups. Just return all online operators. $groups = $db->query( ('SELECT * FROM {operator} ' . 'WHERE istatus = 0 AND (:now - dtmlastvisited) < :timeout'), array( ':now' => time(), ':timeout' => Settings::get('online_timeout'), ), array('return_rows' => Database::RETURN_ALL_ROWS) ); } else { $groups = $db->query( ('SELECT o.* FROM {operator} o, {operatortoopgroup} r, {opgroup} g ' // The operator should be online . 'WHERE o.istatus = 0 AND (:now - o.dtmlastvisited) < :timeout ' // And it should belong to the specified group or to one of // its children. . 'AND o.operatorid = r.operatorid ' . 'AND r.groupid = g.groupid ' . 'AND (g.groupid = :group_id OR g.parent = :group_id)'), array( ':now' => time(), ':timeout' => Settings::get('online_timeout'), ':group_id' => $group_id, ), array('return_rows' => Database::RETURN_ALL_ROWS) ); } return $groups ? $groups : array(); } /** * Indicates if operator online or not * * @param int $operator_id Id of the operator * @return boolean true if operator is online and false otherwise */ function is_operator_online($operator_id) { $db = Database::getInstance(); $row = $db->query( ("SELECT count(*) AS total, " . "MIN(:now - dtmlastvisited) AS time " . "FROM {operator} WHERE operatorid = :operatorid"), array( ':now' => time(), ':operatorid' => $operator_id, ), array('return_rows' => Database::RETURN_ONE_ROW) ); return ($row['time'] < Settings::get('online_timeout')) && ($row['total'] == 1); } /** * Returns name of the operator. Choose between vclocalname and vccommonname * * @param array $operator Operator's array * @return string Operator's name */ function get_operator_name($operator) { if (get_home_locale() == get_current_locale()) { return $operator['vclocalename']; } else { return $operator['vccommonname']; } } function setup_redirect_links(UrlGeneratorInterface $url_generator, $threadid, $operator, $token) { $result = array(); $operator_in_isolation = in_isolation($operator); $list_options = $operator_in_isolation ? array('isolated_operator_id' => $operator['operatorid']) : array(); $operators = get_operators_list($list_options); $operators_count = count($operators); $groups_count = 0; $groups = array(); if (Settings::get('enablegroups') == "1") { $groupslist = $operator_in_isolation ? get_groups_for_operator($operator, true) : get_groups(true); foreach ($groupslist as $group) { if ($group['inumofagents'] == 0) { continue; } $groups[] = $group; } $groups_count = count($groups); } $p = pagination_info(max($operators_count, $groups_count), 8); $result['pagination'] = $p; $operators = array_slice($operators, $p['start'], $p['end'] - $p['start']); $groups = array_slice($groups, $p['start'], $p['end'] - $p['start']); $agent_list = ""; $params = array('thread_id' => $threadid, 'token' => $token); foreach ($operators as $agent) { $params['nextAgent'] = $agent['operatorid']; $status = $agent['time'] < Settings::get('online_timeout') ? ($agent['istatus'] == 0 ? getlocal("(online)") : getlocal("(away)")) : ""; $agent_list .= "