Beispiel #1
0
    /**
     * Runs the cron.
     *
     * Triggers {@link \Mibew\EventDispatcher\Events::CRON_RUN} event.
     *
     * @param Request $request Incoming request.
     * @return string Rendered page content.
     */
    public function runAction(Request $request)
    {
        $cron_key = $request->query->get('cron_key', '');

        // Check cron security key
        if ($cron_key != Settings::get('cron_key')) {
            // Return an empty response
            return '';
        }

        // Determine use or not quiet mode
        $quiet = $request->query->has('q');

        set_time_limit(0);

        // Remove stale cached items
        $this->getCache()->purge();

        // Run cron jobs of the core
        calculate_thread_statistics();
        calculate_operator_statistics();
        calculate_page_statistics();

        // Trigger cron event
        $dispatcher = EventDispatcher::getInstance();
        $dispatcher->triggerEvent(Events::CRON_RUN);

        // Update time of last cron run
        Settings::set('_last_cron_run', time());

        if (!$quiet) {
            // TODO: May be localize it
            return 'All cron jobs done.';
        }
    }
Beispiel #2
0
 /**
  * The main entry point of a plugin.
  */
 public function run()
 {
     // Attach CSS and JS files of the plugin to chat window.
     $dispatcher = EventDispatcher::getInstance();
     $dispatcher->attachListener(Events::PAGE_ADD_CSS, $this, 'attachCssFiles');
     $dispatcher->attachListener(Events::PAGE_ADD_JS, $this, 'attachJsFiles');
     $dispatcher->attachListener(Events::PAGE_ADD_JS_PLUGIN_OPTIONS, $this, 'attachPluginOptions');
 }
Beispiel #3
0
 /**
  * Performs all periodical actions.
  *
  * @return boolean True if all periodical actions are done and false
  * otherwise.
  */
 public function run()
 {
     try {
         set_time_limit(0);
         // Remove stale cached items
         $this->cache->purge();
         // Run cron jobs of the core
         calculate_thread_statistics();
         calculate_operator_statistics();
         calculate_page_statistics();
         // Trigger cron event
         $dispatcher = EventDispatcher::getInstance();
         $dispatcher->triggerEvent(Events::CRON_RUN);
         // Update time of last cron run
         Settings::set('_last_cron_run', time());
     } catch (\Exception $e) {
         $this->log[] = $e->getMessage();
         return false;
     }
     return true;
 }
Beispiel #4
0
/**
 * Updates set of groups the operator belongs to.
 *
 * Triggers {@link \Mibew\EventDispatcher\Events::GROUP_UPDATE_OPERATORS} event.
 *
 * @param int $operator_id ID of the operator.
 * @param array $new_value List of operator's groups IDs.
 */
function update_operator_groups($operator_id, $new_value)
{
    // Get difference of groups the operator belongs to before and after the
    // update.
    $original_groups = get_operator_group_ids($operator_id);
    $groups_union = array_unique(array_merge($original_groups, $new_value));
    $groups_intersect = array_intersect($original_groups, $new_value);
    $updated_groups = array_diff($groups_union, $groups_intersect);

    // Get members of all updated groups. It will be used to trigger the
    // "update" event later.
    $original_relations = array();
    foreach ($updated_groups as $group_id) {
        $original_relations[$group_id] = get_group_members($group_id);
    }

    // Update group members
    $db = Database::getInstance();
    $db->query(
        "DELETE FROM {operatortoopgroup} WHERE operatorid = ?",
        array($operator_id)
    );

    foreach ($new_value as $group_id) {
        $db->query(
            "INSERT INTO {operatortoopgroup} (groupid, operatorid) VALUES (?,?)",
            array($group_id, $operator_id)
        );
    }

    // Trigger the "update" event
    foreach ($original_relations as $group_id => $operators) {
        $args = array(
            'group' => group_by_id($group_id),
            'original_operators' => $operators,
            'operators' => get_group_members($group_id),
        );
        EventDispatcher::getInstance()->triggerEvent(Events::GROUP_UPDATE_OPERATORS, $args);
    }
}
Beispiel #5
0
/**
 * 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;
}
Beispiel #6
0
/**
 * Remove old visitors.
 *
 * Triggers {@link \Mibew\EventDispatcher\Events::VISITOR_DELETE_OLD} event.
 */
function track_remove_old_visitors()
{
    $lock = new ProcessLock('visitors_remove_old');
    if ($lock->get()) {
        // Freeze the time for the whole process
        $now = time();
        $db = Database::getInstance();
        // Remove associations of visitors with closed threads
        $db->query("UPDATE {sitevisitor} SET threadid = NULL " . "WHERE threadid IS NOT NULL AND " . "(SELECT count(*) FROM {thread} " . "WHERE threadid = {sitevisitor}.threadid " . "AND istate <> " . Thread::STATE_CLOSED . " " . "AND istate <> " . Thread::STATE_LEFT . ") = 0 ");
        // Get all visitors that will be removed. They will be used in the
        // "delete" event later.
        $rows = $db->query("SELECT visitorid FROM {sitevisitor} " . "WHERE (:now - lasttime) > :lifetime " . "AND threadid IS NULL", array(':lifetime' => Settings::get('tracking_lifetime'), ':now' => $now), array('return_rows' => Database::RETURN_ALL_ROWS));
        $removed_visitors = array();
        foreach ($rows as $row) {
            $removed_visitors[] = $row['visitorid'];
        }
        // Remove old visitors
        $db->query("DELETE FROM {sitevisitor} " . "WHERE (:now - lasttime) > :lifetime " . "AND threadid IS NULL", array(':lifetime' => Settings::get('tracking_lifetime'), ':now' => $now));
        // Trigger the "delete" event only if visitors was removed.
        if (count($removed_visitors) > 0) {
            $args = array('visitors' => $removed_visitors);
            EventDispatcher::getInstance()->triggerEvent(Events::VISITOR_DELETE_OLD, $args);
        }
        // Release the lock
        $lock->release();
    }
}
 /**
  * Process function
  *
  * @param array $function 'Function' array. See Mibew API for details
  * @param \Mibew\API\ExecutionContext &$context Execution context
  * @return boolean False if function returns errorCode and errorCode isn't 0
  *   and true otherwise.
  */
 protected function processFunction($function, \Mibew\API\ExecutionContext &$context)
 {
     // Get function arguments with replaced references
     $arguments = $context->getArgumentsList($function);
     $call_vars = array('function' => $function['function'], 'arguments' => $arguments, 'results' => array());
     // Call processor function
     $this->processorCall($call_vars);
     // Trigger FunctionCall event
     $call_vars['request_processor'] = $this;
     $dispatcher = EventDispatcher::getInstance();
     $dispatcher->triggerEvent($this->eventPrefix . 'FunctionCall', $call_vars);
     // Get results
     $results = $call_vars['results'];
     // Add function results to execution context
     $context->storeFunctionResults($function, $results);
     // Check errorCode
     return empty($results['errorCode']);
 }
Beispiel #8
0
 /**
  * Close thread and send closing messages to the conversation members
  *
  * Triggers {@link \Mibew\EventDispatcher\Events::THREAD_CLOSE} event.
  *
  * @param boolean $is_user Boolean TRUE if user initiate thread closing or
  *   boolean FALSE otherwise
  */
 public function close($is_user)
 {
     // Send message about closing
     if ($is_user) {
         $this->postMessage(self::KIND_EVENTS, getlocal("Visitor {0} left the chat", array($this->userName), $this->locale, true));
     } else {
         if ($this->state == self::STATE_INVITED) {
             $this->postMessage(self::KIND_FOR_AGENT, getlocal('Operator canceled invitation', null, $this->locale, true));
         } else {
             $this->postMessage(self::KIND_EVENTS, getlocal("Operator {0} left the chat", array($this->agentName), $this->locale, true));
         }
     }
     // Get messages count
     $db = Database::getInstance();
     list($message_count) = $db->query("SELECT COUNT(*) FROM {message} " . "WHERE {message}.threadid = :threadid AND ikind = :kind_user", array(':threadid' => $this->id, ':kind_user' => Thread::KIND_USER), array('return_rows' => Database::RETURN_ONE_ROW, 'fetch_type' => Database::FETCH_NUM));
     // Close thread if it's not already closed
     if ($this->state != self::STATE_CLOSED) {
         $this->state = self::STATE_CLOSED;
         $this->closed = time();
         $this->messageCount = $message_count;
         $this->save();
         $args = array('thread' => $this);
         EventDispatcher::getInstance()->triggerEvent(Events::THREAD_CLOSE, $args);
     }
 }
Beispiel #9
0
 /**
  * Return updated visitors list. API function.
  *
  * Triggers
  * {@link \Mibew\EventDispatcher\Events::USERS_UPDATE_VISITORS_LOAD} and
  * {@link \Mibew\EventDispatcher\Events::USERS_UPDATE_VISITORS_ALTER}
  * events.
  *
  * @param array $args Associative array of arguments. It must contains the
  *   following keys:
  *    - 'agentId': Id of the agent related to users window
  *
  * @return array Array of results. It contains the following keys:
  *  - 'visitors': array of visitors on the site
  */
 protected function apiUpdateVisitors($args)
 {
     // Check access
     $this->checkOperator($args['agentId']);
     // Close old invitations
     invitation_close_old();
     // Remove old visitors
     track_remove_old_visitors();
     // Get instance of event dispatcher
     $dispatcher = EventDispatcher::getInstance();
     // Trigger load event
     $arguments = array('visitors' => false);
     $dispatcher->triggerEvent(Events::USERS_UPDATE_VISITORS_LOAD, $arguments);
     // Check if visiors list loaded by plugins
     if (!is_array($arguments['visitors'])) {
         // Load visitors list
         $db = Database::getInstance();
         // Load visitors
         $query = "SELECT v.visitorid, " . "v.userid, " . "v.username, " . "v.firsttime, " . "v.lasttime, " . "v.entry, " . "v.details, " . "t.invitationstate, " . "t.dtmcreated AS invitationtime, " . "t.agentId AS invitedby, " . "v.invitations, " . "v.chats " . "FROM {sitevisitor} v " . "LEFT OUTER JOIN {thread} t " . "ON t.threadid = v.threadid " . "WHERE v.threadid IS NULL " . "OR (t.istate = :state_invited " . "AND t.invitationstate = :invitation_wait)" . "ORDER BY t.invitationstate, v.lasttime DESC, v.invitations";
         $query .= Settings::get('visitors_limit') == '0' ? "" : " LIMIT " . Settings::get('visitors_limit');
         $rows = $db->query($query, array(':state_invited' => Thread::STATE_INVITED, ':invitation_wait' => Thread::INVITATION_WAIT), array('return_rows' => Database::RETURN_ALL_ROWS));
         $visitors = array();
         foreach ($rows as $row) {
             // Get visitor details
             $details = track_retrieve_details($row);
             // Get user agent
             $user_agent = get_user_agent_version($details['user_agent']);
             // Get user ip
             if (preg_match("/(\\d+\\.\\d+\\.\\d+\\.\\d+)/", $details['remote_host'], $matches) != 0) {
                 $user_ip = $matches[1];
             } else {
                 $user_ip = false;
             }
             // Get invitation info
             $row['invited'] = $row['invitationstate'] == Thread::INVITATION_WAIT;
             if ($row['invited']) {
                 $agent_name = get_operator_name(operator_by_id($row['invitedby']));
                 $invitation_info = array('time' => $row['invitationtime'], 'agentName' => $agent_name);
             } else {
                 $invitation_info = false;
             }
             // Create resulting visitor structure
             $visitors[] = array('id' => (int) $row['visitorid'], 'userId' => $row['userid'], 'userName' => $row['username'], 'userAgent' => $user_agent, 'userIp' => $user_ip, 'remote' => $details['remote_host'], 'firstTime' => $row['firsttime'], 'lastTime' => $row['lasttime'], 'invitations' => (int) $row['invitations'], 'chats' => (int) $row['chats'], 'invitationInfo' => $invitation_info);
         }
     } else {
         $visitors = $arguments['visitors'];
     }
     // Provide ability to alter visitors list
     $arguments = array('visitors' => $visitors);
     $dispatcher->triggerEvent(Events::USERS_UPDATE_VISITORS_ALTER, $arguments);
     // Send results back to the client. "array_values" function should be
     // used to avoid problems with JSON conversion. If there will be gaps in
     // keys (the keys are not serial) JSON Object will be produced instead
     // of an Array.
     return array('visitors' => array_values($arguments['visitors']));
 }
 /**
  * {@inheritdoc}
  *
  * Triggers {@link \Mibew\EventDispatcher\Events::OPERATOR_LOGOUT} event.
  */
 public function logoutOperator()
 {
     parent::logoutOperator();
     $this->remember = false;
     // Trigger logout event
     $dispatcher = EventDispatcher::getInstance();
     $dispatcher->triggerEvent(Events::OPERATOR_LOGOUT);
 }
Beispiel #11
0
 /**
  * Performs all periodical actions.
  *
  * @return boolean True if all periodical actions are done and false
  * otherwise.
  */
 public function run()
 {
     try {
         set_time_limit(0);
         // Update time of last cron run
         Settings::set('_last_cron_run', time());
         // Remove stale cached items
         $this->cache->purge();
         // Run cron jobs of the core
         calculate_thread_statistics();
         calculate_operator_statistics();
         calculate_page_statistics();
         // Trigger cron event
         $dispatcher = EventDispatcher::getInstance();
         $dispatcher->triggerEvent(Events::CRON_RUN);
         if (Settings::get('autocheckupdates') == '1') {
             // Run the update checker
             $update_checker = $this->getUpdateChecker();
             if (!$update_checker->run()) {
                 $this->errors = array_merge($this->errors, $update_checker->getErrors());
                 return false;
             }
         }
     } catch (\Exception $e) {
         $this->log[] = $e->getMessage();
         return false;
     }
     return true;
 }
 /**
  * {@inheritdoc}
  */
 public function generate()
 {
     $args = array('button' => $this->doGenerate(), 'generator' => $this);
     EventDispatcher::getInstance()->triggerEvent(Events::BUTTON_GENERATE, $args);
     return (string) $args['button'];
 }
Beispiel #13
0
 /**
  * Gets additional CSS assets by triggering some events.
  *
  * Triggers {@link \Mibew\EventDispatcher\Events::PAGE_ADD_CSS} event.
  *
  * @return Package Assets list.
  */
 protected function triggerCssEvent()
 {
     $event = array('request' => $this->getRequest(), 'css' => array());
     EventDispatcher::getInstance()->triggerEvent(Events::PAGE_ADD_CSS, $event);
     return $this->normalizeAssets($event['css']);
 }
Beispiel #14
0
 /**
  * Builds response for a not found page.
  *
  * Triggers {@link \Mibew\EventDispatcher\Events::RESOURCE_NOT_FOUND}
  * event.
  *
  * @param Request $request Incoming request
  * @return Response
  */
 protected function buildNotFoundResponse(Request $request)
 {
     // Trigger fail
     $args = array('request' => $request, 'response' => false);
     $dispatcher = EventDispatcher::getInstance();
     $dispatcher->triggerEvent(Events::RESOURCE_NOT_FOUND, $args);
     if ($args['response'] && $args['response'] instanceof Response) {
         // If one of event listeners returned the response object send it
         // to the client.
         return $args['response'];
     }
     return new Response('Not Found', 404);
 }
Beispiel #15
0
/**
 * Deletes a group with specified ID.
 *
 * Triggers {@link \Mibew\EventDispatcher\Events::GROUP_DELETE} event.
 *
 * @param int $group_id ID of the group that should be deleted.
 */
function delete_group($group_id)
{
    $db = Database::getInstance();
    $db->query("DELETE FROM {opgroup} WHERE groupid = ?", array($group_id));
    $db->query("DELETE FROM {operatortoopgroup} WHERE groupid = ?", array($group_id));
    $db->query("UPDATE {thread} SET groupid = 0 WHERE groupid = ?", array($group_id));

    $args = array('id' => $group_id);
    EventDispatcher::getInstance()->triggerEvent(Events::GROUP_DELETE, $args);
}
Beispiel #16
0
 /**
  * Save the ban to the database.
  *
  * Triggers {@link \Mibew\EventDispatcher\Events::BAN_CREATE} event.
  */
 public function save()
 {
     $db = Database::getInstance();
     if (!$this->id) {
         // This ban is new.
         $db->query("INSERT INTO {ban} (dtmcreated, dtmtill, address, comment) " . "VALUES (:created, :till, :address, :comment)", array(':created' => (int) $this->created, ':till' => (int) $this->till, ':address' => $this->address, ':comment' => $this->comment));
         $this->id = $db->insertedId();
         $args = array('ban' => $this);
         EventDispatcher::getInstance()->triggerEvent(Events::BAN_CREATE, $args);
     } else {
         // Get the original state of the ban for "update" event.
         $original_ban = Ban::load($this->id);
         // Update existing ban
         $db->query("UPDATE {ban} SET dtmtill = :till, address = :address, " . "comment = :comment WHERE banid = :id", array(':id' => $this->id, ':till' => (int) $this->till, ':address' => $this->address, ':comment' => $this->comment));
         $args = array('ban' => $this, 'original_ban' => $original_ban);
         EventDispatcher::getInstance()->triggerEvent(Events::BAN_UPDATE, $args);
     }
 }
Beispiel #17
0
/**
 * 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();
    }
}
Beispiel #18
0
 /**
  * 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;
 }