/** * Starts chat process. * * @param Request $request Incoming request. * @return string|\Symfony\Component\HttpFoundation\RedirectResponse Rendered * page content or a redirect response. */ public function startAction(Request $request) { $operator = $this->getOperator(); $thread_id = $request->attributes->getInt('thread_id'); // Check if the thread can be loaded. $thread = Thread::load($thread_id); if (!$thread || !isset($thread->lastToken)) { return $this->showErrors(array(getlocal('Wrong thread'))); } $view_only = $request->query->get('viewonly') == 'true'; $force_take = $request->query->get('force') == 'true'; $try_take_over = !$view_only && $thread->state == Thread::STATE_CHATTING && $operator['operatorid'] != $thread->agentId; if ($try_take_over) { if (!is_capable(CAN_TAKEOVER, $operator)) { return $this->showErrors(array(getlocal('Cannot take over'))); } if ($force_take == false) { $link = $this->generateUrl('chat_operator_start', array('thread_id' => $thread_id, 'force' => 'true')); $page = array('user' => $thread->userName, 'agent' => $thread->agentName, 'link' => $link, 'title' => getlocal('Change operator')); // Show confirmation page. return $this->render('confirm', $page); } } if (!$view_only) { if (!$thread->take($operator)) { return $this->showErrors(array(getlocal('Cannot take thread'))); } } elseif (!is_capable(CAN_VIEWTHREADS, $operator)) { return $this->showErrors(array(getlocal('Cannot view threads'))); } // Redrect the operator to initialized chat page $redirect_to = $this->generateUrl('chat_operator', array('thread_id' => intval($thread_id), 'token' => urlencode($thread->lastToken))); return $this->redirect($redirect_to); }
/** * Process submitting of the mail form. * * @param Request $request Incoming request. * @return string Rendered page content. * @throws NotFoundException If the thread with specified ID and token is * not found. */ public function submitFormAction(Request $request) { $errors = array(); $thread_id = $request->attributes->get('thread_id'); $token = $request->attributes->get('token'); // Try to load the thread $thread = Thread::load($thread_id, $token); if (!$thread) { throw new NotFoundException('The thread is not found.'); } $email = $request->request->get('email'); $group = $thread->groupId ? group_by_id($thread->groupId) : null; if (!$email) { $errors[] = no_field('Your email'); } elseif (!MailUtils::isValidAddress($email)) { $errors[] = wrong_field('Your email'); } if (count($errors) > 0) { $request->attributes->set('errors', $errors); // Render the mail form again return $this->showFormAction($request); } $history = ''; $last_id = -1; $messages = $thread->getMessages(true, $last_id); foreach ($messages as $msg) { $history .= message_to_text($msg); } // Load mail templates and substitute placeholders there. $mail_template = MailTemplate::loadByName('user_history', get_current_locale()); if ($mail_template) { $this->sendMail(MailUtils::buildMessage($email, MIBEW_MAILBOX, $mail_template->buildSubject(), $mail_template->buildBody(array($thread->userName, $history, Settings::get('title'), Settings::get('hosturl'))))); } else { trigger_error('Cannot send e-mail because "user_history" mail template cannot be loaded.', E_USER_WARNING); } $page = setup_logo($group); $page['email'] = $email; return $this->render('mailsent', $page); }
/** * Close old invitations. * * Triggers {@link \Mibew\EventDispatcher\Events::INVITATION_IGNORE} event. */ function invitation_close_old() { // Run only one instance of cleaning process. $lock = new ProcessLock('invitations_close_old'); if ($lock->get()) { // Freeze the time for the whole cleaning process. $now = time(); $db = Database::getInstance(); // Remove links between visitors and invitations that will be closed. $db->query("UPDATE {sitevisitor} v, {thread} t SET " . "v.threadid = NULL " . "WHERE t.istate = :state_invited " . "AND t.invitationstate = :invitation_wait " . "AND (:now - t.dtmcreated) > :lifetime", array(':invitation_wait' => Thread::INVITATION_WAIT, ':state_invited' => Thread::STATE_INVITED, ':lifetime' => Settings::get('invitation_lifetime'), ':now' => $now)); // Get all invitations to close $threads = $db->query("SELECT * FROM {thread} " . "WHERE istate = :state_invited " . "AND invitationstate = :invitation_wait " . "AND (:now - dtmcreated) > :lifetime", array(':invitation_wait' => Thread::INVITATION_WAIT, ':state_invited' => Thread::STATE_INVITED, ':lifetime' => Settings::get('invitation_lifetime'), ':now' => $now), array('return_rows' => Database::RETURN_ALL_ROWS)); // Close the invitations foreach ($threads as $thread_info) { $thread = Thread::createFromDbInfo($thread_info); $thread->invitationState = Thread::INVITATION_IGNORED; $thread->state = Thread::STATE_CLOSED; $thread->closed = $now; $thread->save(); // Notify the operator about autoclosing $thread->postMessage(Thread::KIND_FOR_AGENT, getlocal('Visitor ignored invitation and it was closed automatically', null, $thread->locale, true)); $args = array('invitation' => $thread); EventDispatcher::getInstance()->triggerEvent(Events::INVITATION_IGNORE, $args); unset($thread); } // Release the lock $lock->release(); } }
/** * Process chat in an invitation block. * * @param Request $request Incoming request. * @return string|\Symfony\Component\HttpFoundation\RedirectResponse Rendered * page content or a redirect response. */ public function invitationAction(Request $request) { // Check if an user tries to use invitation functionality when it's // disabled. if (!Settings::get('enabletracking')) { return $this->redirect($this->generateUrl('chat_user_start')); } // Check if we should force the user to use SSL. $ssl_redirect = $this->sslRedirect($request); if ($ssl_redirect !== false) { return $ssl_redirect; } // Check if user invited to chat. $invitation_state = invitation_state($_SESSION[SESSION_PREFIX . 'visitorid']); if (!$invitation_state['invited'] || !$invitation_state['threadid']) { return $this->redirect($this->generateUrl('chat_user_start')); } $thread = Thread::load($invitation_state['threadid']); // Store own thread ids to restrict access for other people if (!isset($_SESSION[SESSION_PREFIX . 'own_threads'])) { $_SESSION[SESSION_PREFIX . 'own_threads'] = array(); } $_SESSION[SESSION_PREFIX . 'own_threads'][] = $thread->id; // Prepare page $page = setup_invitation_view($thread); // Build js application options $page['invitationOptions'] = $page['invitation']; // Initialize client side application $this->getAssetManager()->attachJs('js/compiled/chat_app.js'); $this->getAssetManager()->attachJs( $this->startJsApplication($request, $page), AssetManagerInterface::INLINE, 1000 ); // Expand page return $this->render('chat', $page); }
/** * Process submitted leave message form. * * Send message to operator email and create special meil thread. * @param array $args Associative array of arguments. It must contains the * following keys: * - 'threadId': for this function this param equals to null; * - 'token': for this function this param equals to null; * - 'name': string, user name; * - 'email': string, user email; * - 'message': string, user message; * - 'info': string, some info about user; * - 'referrer': string, page user came from; * - 'captcha': string, captcha value; * - 'groupId': selected group id. * * @throws \Mibew\RequestProcessor\ThreadProcessorException Can throw an * exception if captcha or email is wrong. */ protected function apiProcessLeaveMessage($args) { // Check captcha if (Settings::get('enablecaptcha') == '1' && can_show_captcha()) { $captcha = $args['captcha']; $original = isset($_SESSION[SESSION_PREFIX . 'mibew_captcha']) ? $_SESSION[SESSION_PREFIX . 'mibew_captcha'] : ''; unset($_SESSION[SESSION_PREFIX . 'mibew_captcha']); if (empty($original) || empty($captcha) || $captcha != $original) { throw new ThreadProcessorException( getlocal('The letters you typed don\'t match the letters that were shown in the picture.'), ThreadProcessorException::ERROR_WRONG_CAPTCHA ); } } // Get form fields $email = $args['email']; $name = $args['name']; $message = $args['message']; $info = $args['info']; $referrer = $args['referrer']; if (!MailUtils::isValidAddress($email)) { throw new ThreadProcessorException( wrong_field("Your email"), ThreadProcessorException::ERROR_WRONG_EMAIL ); } // Verify group id $group_id = ''; if (Settings::get('enablegroups') == '1') { if (preg_match("/^\d{1,8}$/", $args['groupId']) != 0) { $group = group_by_id($args['groupId']); if ($group) { $group_id = $args['groupId']; } } } // Create thread for left message $remote_host = get_remote_host(); $user_browser = $_SERVER['HTTP_USER_AGENT']; $visitor = visitor_from_request(); // Get message locale $message_locale = Settings::get('left_messages_locale'); if (!locale_is_available($message_locale)) { $message_locale = get_home_locale(); } // Create thread $thread = new Thread(); $thread->groupId = $group_id; $thread->userName = $name; $thread->remote = $remote_host; $thread->referer = $referrer; $thread->locale = get_current_locale(); $thread->userId = $visitor['id']; $thread->userAgent = $user_browser; $thread->state = Thread::STATE_LEFT; $thread->closed = time(); $thread->save(); // Send some messages if ($referrer) { $thread->postMessage( Thread::KIND_FOR_AGENT, getlocal('Vistor came from page {0}', array($referrer), get_current_locale(), true) ); } if ($email) { $thread->postMessage( Thread::KIND_FOR_AGENT, getlocal('E-Mail: {0}', array($email), get_current_locale(), true) ); } if ($info) { $thread->postMessage( Thread::KIND_FOR_AGENT, getlocal('Info: {0}', array($info), get_current_locale(), true) ); } $thread->postMessage(Thread::KIND_USER, $message, array('name' => $name)); // Get email for message $inbox_mail = get_group_email($group_id); if (empty($inbox_mail)) { $inbox_mail = Settings::get('email'); } // Send email if ($inbox_mail) { // Prepare message to send by email $mail_template = MailTemplate::loadByName('leave_message', $message_locale); if (!$mail_template) { trigger_error( 'Cannot send e-mail because "leave_message" mail template cannot be loaded.', E_USER_WARNING ); return; } $subject = $mail_template->buildSubject(array($args['name'])); $body = $mail_template->buildBody(array( $args['name'], $email, $message, ($info ? $info . "\n" : ""), )); // Send $this->getMailerFactory()->getMailer()->send( MailUtils::buildMessage($inbox_mail, $email, $subject, $body) ); } }
/** * Start chat thread for user * * @param int $group_id Id of group related to thread * @param array $requested_operator Array of requested operator info * @param string $visitor_id Id of the visitor * @param string $visitor_name Name of the visitor * @param string $referrer Page user came from * @param string $info User info * * @return Thread thread object */ function chat_start_for_user($group_id, $requested_operator, $visitor_id, $visitor_name, $referrer, $info) { // Get user info $remote_host = get_remote_host(); $user_browser = $_SERVER['HTTP_USER_AGENT']; // Check connection limit if (Thread::connectionLimitReached($remote_host)) { die("number of connections from your IP is exceeded, try again later"); } // Check if visitor was invited to chat $is_invited = false; if (Settings::get('enabletracking')) { $invitation_state = invitation_state($_SESSION[SESSION_PREFIX . 'visitorid']); if ($invitation_state['invited']) { $is_invited = true; } } // Get info about requested operator $requested_operator_online = false; if ($requested_operator) { $requested_operator_online = is_operator_online($requested_operator['operatorid']); } // Get thread object if ($is_invited) { // Get thread from invitation $thread = invitation_accept($_SESSION[SESSION_PREFIX . 'visitorid']); if (!$thread) { die("Cannot start thread"); } } else { // Create thread $thread = new Thread(); $thread->state = Thread::STATE_LOADING; $thread->agentId = 0; if ($requested_operator && $requested_operator_online) { $thread->nextAgent = $requested_operator['operatorid']; } } // Update thread fields $thread->groupId = $group_id; $thread->userName = $visitor_name; $thread->remote = $remote_host; $thread->referer = $referrer; $thread->locale = get_current_locale(); $thread->userId = $visitor_id; $thread->userAgent = $user_browser; $thread->save(); $_SESSION[SESSION_PREFIX . 'threadid'] = $thread->id; // Store own thread ids to restrict access for other people if (!isset($_SESSION[SESSION_PREFIX . 'own_threads'])) { $_SESSION[SESSION_PREFIX . 'own_threads'] = array(); } $_SESSION[SESSION_PREFIX . 'own_threads'][] = $thread->id; // Bind thread to the visitor if (Settings::get('enabletracking')) { track_visitor_bind_thread($visitor_id, $thread); } // Send several messages if ($is_invited) { $operator = operator_by_id($thread->agentId); $operator_name = get_operator_name($operator); $thread->postMessage(Thread::KIND_FOR_AGENT, getlocal('Visitor accepted invitation from operator {0}', array($operator_name), get_current_locale(), true)); } else { if ($referrer) { $thread->postMessage(Thread::KIND_FOR_AGENT, getlocal('Vistor came from page {0}', array($referrer), get_current_locale(), true)); } if ($requested_operator && !$requested_operator_online) { $thread->postMessage(Thread::KIND_INFO, getlocal('Thank you for contacting us. We are sorry, but requested operator <strong>{0}</strong> is offline. Another operator will be with you shortly.', array(get_operator_name($requested_operator)), get_current_locale(), true)); } else { $thread->postMessage(Thread::KIND_INFO, getlocal('Thank you for contacting us. An operator will be with you shortly.', null, get_current_locale(), true)); } } // TODO: May be move sending this message somewhere else? if ($info) { $thread->postMessage(Thread::KIND_FOR_AGENT, getlocal('Info: {0}', array($info), get_current_locale(), true)); } // Let plugins know that user is ready to chat. $dispatcher = EventDispatcher::getInstance(); $event_args = array('thread' => $thread); $dispatcher->triggerEvent(Events::THREAD_USER_IS_READY, $event_args); return $thread; }
/** * Builds a page with form for add/edit ban. * * @param Request $request Incoming request. * @return string Rendered page content. * @throws NotFoundException If the ban with specified ID is not found in * the system. * @throws BadRequestException If "thread" GET param is specified but has a * wrong format. */ public function showEditFormAction(Request $request) { $operator = $this->getOperator(); $page = array('banId' => '', 'saved' => false, 'thread' => '', 'threadid' => '', 'errors' => $request->attributes->get('errors', array())); if ($request->attributes->has('ban_id')) { $ban_id = $request->attributes->getInt('ban_id'); // Retrieve ban information from the database $ban = Ban::load($ban_id); if (!$ban) { throw new NotFoundException('The ban is not found.'); } $page['banId'] = $ban->id; $page['formaddress'] = $ban->address; $page['formdays'] = round(($ban->till - time()) / 86400); $page['formcomment'] = $ban->comment; } elseif ($request->query->has('thread')) { // Prepopulate form using thread data $thread_id = $request->query->has('thread'); if (!preg_match("/^\\d{1,10}\$/", $thread_id)) { throw new BadRequestException('Wrong value of "thread" argument.'); } $thread = Thread::load($thread_id); if ($thread) { $page['thread'] = htmlspecialchars($thread->userName); $page['threadid'] = $thread_id; $page['formaddress'] = $thread->remote; $page['formdays'] = 15; } } // Override form fields from the request if it is needed if ($request->isMethod('POST')) { $page['formaddress'] = $request->request->get('address'); $page['formdays'] = $request->request->get('days'); $page['formcomment'] = $request->request->get('comment'); $page['threadid'] = $request->request->get('threadid'); } $page['title'] = getlocal('Block address'); $page['formaction'] = $request->getBaseUrl() . $request->getPathInfo(); $page = array_merge($page, prepare_menu($operator, false)); return $this->render('ban', $page); }
/** * Returns content of the chat button. * * @param Request $request * @return string Rendered page content */ public function indexAction(Request $request) { $referer = $request->server->get('HTTP_REFERER', ''); // We need to display message about visited page only if the visitor // really change it. $new_page = empty($_SESSION[SESSION_PREFIX . 'last_visited_page']) || $_SESSION[SESSION_PREFIX . 'last_visited_page'] != $referer; // Display message about page change if ($referer && isset($_SESSION[SESSION_PREFIX . 'threadid']) && $new_page) { $thread = Thread::load($_SESSION[SESSION_PREFIX . 'threadid']); if ($thread && $thread->state != Thread::STATE_CLOSED) { $msg = getlocal("Visitor navigated to {0}", array($referer), $thread->locale, true); $thread->postMessage(Thread::KIND_FOR_AGENT, $msg); } } $_SESSION[SESSION_PREFIX . 'last_visited_page'] = $referer; $image = $request->query->get('i', ''); if (!preg_match("/^\\w+\$/", $image)) { $image = 'mibew'; } $lang = $request->query->get('lang', ''); if (!preg_match("/^[\\w-]{2,5}\$/", $lang)) { $lang = ''; } if (!$lang || !locale_is_available($lang)) { $lang = get_current_locale(); } $group_id = $request->query->get('group', ''); if (!preg_match("/^\\d{1,8}\$/", $group_id)) { $group_id = false; } if ($group_id) { if (Settings::get('enablegroups') == '1') { $group = group_by_id($group_id); if (!$group) { $group_id = false; } } else { $group_id = false; } } // Get image file content $image_postfix = has_online_operators($group_id) ? "on" : "off"; $file_name = "locales/{$lang}/button/{$image}_{$image_postfix}.png"; $content_type = 'image/png'; if (!is_readable($file_name)) { // Fall back to .gif image $file_name = "locales/{$lang}/button/{$image}_{$image_postfix}.gif"; $content_type = 'image/gif'; } $fh = fopen($file_name, 'rb'); if ($fh) { // Create response with image in body $file_size = filesize($file_name); $content = fread($fh, $file_size); fclose($fh); $response = new Response($content, 200); // Set correct content info $response->headers->set('Content-Type', $content_type); $response->headers->set('Content-Length', $file_size); } else { $response = new Response('Not found', 404); } // Disable caching $response->headers->addCacheControlDirective('no-cache', true); $response->headers->addCacheControlDirective('no-store', true); $response->headers->addCacheControlDirective('must-revalidate', true); $response->setExpires(new \DateTime('yesterday noon')); $response->headers->set('Pragma', 'no-cache'); return $response; }
/** * Generates a page with a user history. * * @param Request $request * @return string Rendered page content */ public function userAction(Request $request) { $operator = $this->getOperator(); $user_id = $request->attributes->get('user_id', ''); $page = array(); if (!empty($user_id)) { $db = Database::getInstance(); $query = "SELECT {thread}.* " . "FROM {thread} " . "WHERE userid=:user_id " . "AND (invitationstate = :invitation_accepted " . "OR invitationstate = :invitation_not_invited) " . "ORDER BY dtmcreated DESC"; $found = $db->query($query, array(':user_id' => $user_id, ':invitation_accepted' => Thread::INVITATION_ACCEPTED, ':invitation_not_invited' => Thread::INVITATION_NOT_INVITED), array('return_rows' => Database::RETURN_ALL_ROWS)); } else { $found = null; } $page = array_merge($page, prepare_menu($operator)); // Setup pagination $pagination = setup_pagination($found, 6); $page['pagination'] = $pagination['info']; $page['pagination.items'] = $pagination['items']; if (!empty($page['pagination.items'])) { foreach ($page['pagination.items'] as $key => $item) { $thread = Thread::createFromDbInfo($item); $page['pagination.items'][$key] = array('threadId' => $thread->id, 'userName' => $thread->userName, 'userAddress' => get_user_addr($thread->remote), 'agentName' => $thread->agentName, 'chatTime' => $thread->modified - $thread->created, 'chatCreated' => $thread->created); } } $page['title'] = getlocal("Visit history"); $page['menuid'] = "history"; return $this->render('history_user', $page); }
/** * Redirects a chat thread to the operator with the specified ID. * * @param \Mibew\Thread $thread Chat thread to redirect. * @param int $group_id ID of the target operator. * @return boolean True if the thread was redirected and false on failure. */ protected function redirectToOperator(Thread $thread, $operator_id) { if ($thread->state != Thread::STATE_CHATTING) { // We can redirect only threads which are in proggress now. return false; } // Redirect the thread $thread->state = Thread::STATE_WAITING; $thread->nextAgent = $operator_id; $thread->agentId = 0; // Check if the target operator belongs to the current thread's group. // If not reset the current thread's group. if ($thread->groupId != 0) { $db = Database::getInstance(); list($groups_count) = $db->query("SELECT count(*) AS count " . "FROM {operatortoopgroup} " . "WHERE operatorid = ? AND groupid = ?", array($operator_id, $thread->groupId), array('return_rows' => Database::RETURN_ONE_ROW, 'fetch_type' => Database::FETCH_NUM)); if ($groups_count === 0) { $thread->groupId = 0; } } $thread->save(); // Send notification message $thread->postMessage(Thread::KIND_EVENTS, getlocal('Operator {0} redirected you to another operator. Please wait a while.', array(get_operator_name($this->getOperator())), $thread->locale, true)); return true; }
/** * Update chat window state. API function * Call periodically by chat window. * * @param array $args Associative array of arguments. It must contains the * following keys: * - 'agentId': Id of the agent related to users window */ protected function apiUpdate($args) { // Check access and get operator array $operator = $this->checkOperator($args['agentId']); // Update operator status notify_operator_alive($operator['operatorid'], $operator['istatus']); // Close old threads Thread::closeOldThreads(); // Load stored requests $stored_requests = $this->getRequestsFromBuffer('users_' . $args['agentId']); if ($stored_requests !== false) { $this->responses = array_merge($this->responses, $stored_requests); } }
/** * Save the thread to the database * * Triggers {@link \Mibew\EventDispatcher\Events::THREAD_UPDATE} and * {@link \Mibew\EventDispatcher\Events::THREAD_CREATE} events. * * @param boolean $update_revision Indicates if last modified time and last * revision should be updated. */ public function save($update_revision = true) { // Update modification time and revision number only if needed if ($update_revision) { $this->lastRevision = $this->nextRevision(); $this->modified = time(); } $db = Database::getInstance(); if (!$this->id) { $db->query('INSERT INTO {thread} (' . 'username, userid, agentname, agentid, ' . 'dtmcreated, dtmchatstarted, dtmmodified, dtmclosed, ' . 'lrevision, istate, invitationstate, ltoken, remote, ' . 'referer, nextagent, locale, lastpinguser, ' . 'lastpingagent, usertyping, agenttyping, ' . 'shownmessageid, useragent, messagecount, groupid' . ') VALUES (' . ':user_name, :user_id, :agent_name, :agent_id, ' . ':created, :chat_started, :modified, :closed, ' . ':revision, :state, :invitation_state, :token, :remote, ' . ':referer, :next_agent, :locale, :last_ping_user, ' . ':last_ping_agent, :user_typing, :agent_typing, ' . ':shown_message_id, :user_agent, :message_count, :group_id ' . ')', array(':user_name' => $this->userName, ':user_id' => $this->userId, ':agent_name' => $this->agentName, ':agent_id' => $this->agentId, ':created' => $this->created, ':chat_started' => $this->chatStarted, ':modified' => $this->modified, ':closed' => $this->closed, ':revision' => $this->lastRevision, ':state' => $this->state, ':invitation_state' => $this->invitationState, ':token' => $this->lastToken, ':remote' => $this->remote, ':referer' => $this->referer, ':next_agent' => $this->nextAgent, ':locale' => $this->locale, ':last_ping_user' => $this->lastPingUser, ':last_ping_agent' => $this->lastPingAgent, ':user_typing' => $this->userTyping, ':agent_typing' => $this->agentTyping, ':shown_message_id' => $this->shownMessageId, ':user_agent' => $this->userAgent, ':message_count' => $this->messageCount, ':group_id' => $this->groupId)); $this->id = $db->insertedId(); $args = array('thread' => $this); EventDispatcher::getInstance()->triggerEvent(Events::THREAD_CREATE, $args); } else { // Get the original state of the thread to trigger event later. $original_thread = Thread::load($this->id); $db->query('UPDATE {thread} SET ' . 'username = :user_name, userid = :user_id, ' . 'agentname = :agent_name, agentid = :agent_id, ' . 'dtmcreated = :created, dtmchatstarted = :chat_started, ' . 'dtmmodified = :modified, dtmclosed = :closed, ' . 'lrevision = :revision, istate = :state, ' . 'invitationstate = :invitation_state, ltoken = :token, ' . 'remote = :remote, referer = :referer, ' . 'nextagent = :next_agent, locale = :locale, ' . 'lastpinguser = :last_ping_user, ' . 'lastpingagent = :last_ping_agent, ' . 'usertyping = :user_typing, agenttyping = :agent_typing, ' . 'shownmessageid = :shown_message_id, ' . 'useragent = :user_agent, messagecount = :message_count, ' . 'groupid = :group_id ' . 'WHERE threadid = :thread_id', array(':thread_id' => $this->id, ':user_name' => $this->userName, ':user_id' => $this->userId, ':agent_name' => $this->agentName, ':agent_id' => $this->agentId, ':created' => $this->created, ':chat_started' => $this->chatStarted, ':modified' => $this->modified, ':closed' => $this->closed, ':revision' => $this->lastRevision, ':state' => $this->state, ':invitation_state' => $this->invitationState, ':token' => $this->lastToken, ':remote' => $this->remote, ':referer' => $this->referer, ':next_agent' => $this->nextAgent, ':locale' => $this->locale, ':last_ping_user' => $this->lastPingUser, ':last_ping_agent' => $this->lastPingAgent, ':user_typing' => $this->userTyping, ':agent_typing' => $this->agentTyping, ':shown_message_id' => $this->shownMessageId, ':user_agent' => $this->userAgent, ':message_count' => $this->messageCount, ':group_id' => $this->groupId)); $args = array('thread' => $this, 'original_thread' => $original_thread); EventDispatcher::getInstance()->triggerEvent(Events::THREAD_UPDATE, $args); } }
/** * Provides a gateway for widget requests. * * Triggers {@link \Mibew\EventDispatcher\Events::VISITOR_TRACK} event. * * @param Request $request * @return string Rendered page content */ public function indexAction(Request $request) { $operator = array(); $response_data = array('load' => array(), 'handlers' => array(), 'dependencies' => array(), 'data' => array()); $tracking_allowed = Settings::get('enabletracking') == '1' && (Settings::get('trackoperators') == '1' || !$this->getOperator()); if ($tracking_allowed) { $entry = $request->query->get('entry', ''); $referer = $request->server->get('HTTP_REFERER', ''); $user_id = $request->query->get('user_id', false); // Check if session was started if (isset($_SESSION[SESSION_PREFIX . 'visitorid']) && preg_match('/^[0-9]+$/', $_SESSION[SESSION_PREFIX . 'visitorid'])) { // Session was started. Just track the visitor. $visitor_id = track_visitor($_SESSION[SESSION_PREFIX . 'visitorid'], $entry, $referer); $visitor = track_get_visitor_by_id($visitor_id); } else { $visitor = track_get_visitor_by_user_id($user_id); if ($visitor !== false) { // Session is not started but the visitor exists in // database. Probably third-party cookies are disabled by // the browser. Use tracking by local cookie at target site. $visitor_id = track_visitor($visitor['visitorid'], $entry, $referer); } else { // Start tracking session $visitor_id = track_visitor_start($entry, $referer); $visitor = track_get_visitor_by_id($visitor_id); } } if ($visitor_id) { $_SESSION[SESSION_PREFIX . 'visitorid'] = $visitor_id; } if ($user_id === false) { // Update local cookie value at target site $response_data['handlers'][] = 'updateUserId'; $response_data['dependencies']['updateUserId'] = array(); $response_data['data']['user']['id'] = $visitor['userid']; } // Provide an ability for others to make something on visitor // tracking $event_arguments = array('visitor' => $visitor); EventDispatcher::getInstance()->triggerEvent(Events::VISITOR_TRACK, $event_arguments); // Get invitation state $invitation_state = invitation_state($visitor_id); // Check if invitation is closed if (!$invitation_state['invited'] && !empty($_SESSION[SESSION_PREFIX . 'invitation_threadid'])) { $response_data['handlers'][] = 'invitationClose'; $response_data['dependencies']['invitationClose'] = array(); unset($_SESSION[SESSION_PREFIX . 'invitation_threadid']); } // Check if the visitor is just invited to chat $is_invited = $invitation_state['invited'] && (empty($_SESSION[SESSION_PREFIX . 'invitation_threadid']) ? true : $_SESSION[SESSION_PREFIX . 'invitation_threadid'] != $invitation_state['threadid']); if ($is_invited) { // Load invitation thread $thread = Thread::load($invitation_state['threadid']); // Get operator info $operator = operator_by_id($thread->agentId); $locale = $request->query->get('locale', ''); $operator_name = $locale == get_home_locale() ? $operator['vclocalename'] : $operator['vccommonname']; $avatar_url = $operator['vcavatar'] ? $this->asset($operator['vcavatar'], AssetUrlGeneratorInterface::ABSOLUTE_URL) : false; // Show invitation dialog at widget side $response_data['handlers'][] = 'invitationCreate'; $response_data['dependencies']['invitationCreate'] = array(); $response_data['data']['invitation'] = array('operatorName' => htmlspecialchars($operator_name), 'avatarUrl' => htmlspecialchars($avatar_url), 'threadUrl' => $this->generateUrl('chat_user_invitation', array(), UrlGeneratorInterface::ABSOLUTE_URL), 'acceptCaption' => getlocal('Answer')); $_SESSION[SESSION_PREFIX . 'invitation_threadid'] = $thread->id; } // Check if the visitor rejects invitation if ($invitation_state['invited'] && $request->query->get('invitation_rejected')) { invitation_reject($visitor_id); } $event_arguments = array('visitor' => $visitor, 'request' => $request, 'response' => $response_data, 'route_url_generator' => $this->getRouter(), 'asset_url_generator' => $this->getAssetManager()->getUrlGenerator()); EventDispatcher::getInstance()->triggerEvent(Events::WIDGET_RESPONSE_ALTER, $event_arguments); $response_data = $event_arguments['response']; } // Builds JSONP response $response = new JsonResponse($response_data); $response->setCallback("Mibew.Objects.widget.onResponse"); // Add headers to overcome third-party cookies problem. $response->headers->set('P3P', 'CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"'); return $response; }