예제 #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 '';
     }
     // Do the job.
     $worker = new CronWorker($this->getCache());
     $success = $worker->run();
     // Determine use or not quiet mode
     $quiet = $request->query->has('q');
     if (!$quiet) {
         if ($success) {
             // Everything is fine.
             return 'All cron jobs done.';
         }
         // Prepare error message for system's error log.
         $error_message = "Cron job failed. Here are the errors:\n";
         foreach ($worker->getErrors() as $error) {
             $error_message .= '    ' . $error . "\n";
         }
         trigger_error($error_message, E_USER_WARNING);
         // Let the client know about the problem.
         return 'Cron job failed.';
     }
 }
예제 #2
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.';
        }
    }
예제 #3
0
 /**
  * Renders operator's home page.
  *
  * @param Request $request Incoming request
  * @return string Rendered page content.
  */
 public function dashboardAction(Request $request)
 {
     $operator = $this->getOperator();
     $is_online = is_operator_online($operator['operatorid']);
     $page = array('version' => MIBEW_VERSION, 'localeLinks' => get_locale_links(), 'needUpdate' => version_compare(Settings::get('dbversion'), MIBEW_VERSION, '<'), 'profilePage' => $this->generateUrl('operator_edit', array('operator_id' => $operator['operatorid'])), 'isOnline' => $is_online, 'warnOffline' => true, 'title' => getlocal('Home'), 'menuid' => 'main');
     $page = array_merge($page, prepare_menu($operator));
     return $this->render('index', $page);
 }
예제 #4
0
function get_user_addr($addr)
{
    if (Settings::get('geolink') && preg_match("/(\\d+\\.\\d+\\.\\d+\\.\\d+)/", $addr, $matches)) {
        $user_ip = $matches[1];
        return get_popup(str_replace("{ip}", $user_ip, Settings::get('geolink')), '', htmlspecialchars($addr), "GeoLocation", "ip{$user_ip}", Settings::get('geolinkparams'));
    }
    return htmlspecialchars($addr);
}
예제 #5
0
 /**
  * Generates JavaScript code that starts client side application.
  *
  * @param Request $request Incoming request.
  * @param array $operator Current operator.
  * @return string JavaScript code that starts "users" client side
  *   application.
  */
 protected function startJsApplication(Request $request, $operator)
 {
     // Load dialogs style options
     $chat_style = new ChatStyle(ChatStyle::getCurrentStyle());
     $chat_style_config = $style_config = $chat_style->getConfigurations();
     // Load page style options
     $page_style_config = $style_config = $this->getStyle()->getConfigurations();
     return sprintf('jQuery(document).ready(function() {Mibew.Application.start(%s);});', json_encode(array('server' => array('url' => $this->generateUrl('users_update'), 'requestsFrequency' => Settings::get('updatefrequency_operator')), 'agent' => array('id' => $operator['operatorid']), 'page' => array('mibewBasePath' => $request->getBasePath(), 'mibewBaseUrl' => $request->getBaseUrl(), 'showOnlineOperators' => Settings::get('showonlineoperators') == '1', 'showVisitors' => Settings::get('enabletracking') == '1', 'showPopup' => Settings::get('enablepopupnotification') == '1', 'threadTag' => $page_style_config['users']['thread_tag'], 'visitorTag' => $page_style_config['users']['visitor_tag'], 'agentLink' => $request->getBaseUrl() . '/operator/chat', 'geoLink' => Settings::get('geolink'), 'trackedLink' => $request->getBaseUrl() . '/operator/history/user-track', 'banLink' => $request->getBaseUrl() . '/operator/ban', 'inviteLink' => $request->getBaseUrl() . '/operator/invite', 'chatWindowParams' => $chat_style_config['chat']['window'], 'geoWindowParams' => Settings::get('geolinkparams'), 'trackedUserWindowParams' => $page_style_config['tracked']['user_window'], 'trackedVisitorWindowParams' => $page_style_config['tracked']['visitor_window'], 'banWindowParams' => $page_style_config['ban']['window'], 'inviteWindowParams' => $chat_style_config['chat']['window']))));
 }
예제 #6
0
 /**
  * Builds list of the styles tabs.
  *
  * @param Request $request Current request.
  * @return array Tabs list. The keys of the array are tabs titles and the
  *   values are tabs URLs.
  */
 protected function buildTabs(Request $request)
 {
     $tabs = array();
     $type = $request->attributes->get('type');
     $tabs[getlocal("Operator pages themes preview")] = $type != self::TYPE_PAGE ? $this->generateUrl('style_preview', array('type' => self::TYPE_PAGE)) : '';
     $tabs[getlocal("Chat themes preview")] = $type != self::TYPE_CHAT ? $this->generateUrl('style_preview', array('type' => self::TYPE_CHAT)) : '';
     if (Settings::get('enabletracking')) {
         $tabs[getlocal("Invitation themes preview")] = $type != self::TYPE_INVITATION ? $this->generateUrl('style_preview', array('type' => self::TYPE_INVITATION)) : '';
     }
     return $tabs;
 }
예제 #7
0
 /**
  * Processes submitting of the form which is generated in
  * {@link \Mibew\Controller\Settings\FeaturesController::showFormAction()}
  * method.
  *
  * @param Request $request Incoming request.
  * @return string Rendered page content.
  */
 public function submitFormAction(Request $request)
 {
     csrf_check_token($request);
     // Update options in the database.
     $options = $this->getOptionsList();
     foreach ($options as $opt) {
         $value = $request->request->get($opt) == 'on' ? '1' : '0';
         Settings::set($opt, $value);
     }
     // Redirect the current operator to the same page using GET method.
     $redirect_to = $this->generateUrl('settings_features', array('stored' => true));
     return $this->redirect($redirect_to);
 }
 /**
  * Processes submitting of the form which is generated in
  * {@link \Mibew\Controller\TranslationImportController::showFormAction()}
  * method.
  *
  * @param Request $request Incoming request.
  * @return string Rendered page content.
  */
 public function submitFormAction(Request $request)
 {
     csrf_check_token($request);
     $errors = array();
     $target = $request->request->get('target');
     if (!preg_match("/^[\\w-]{2,5}\$/", $target)) {
         $target = get_current_locale();
     }
     $override = (bool) $request->request->get('override', false);
     // Validate uploaded file
     $file = $request->files->get('translation_file');
     if ($file) {
         // Process uploaded file.
         $orig_filename = $file->getClientOriginalName();
         $file_size = $file->getSize();
         if ($file_size == 0 || $file_size > Settings::get('max_uploaded_file_size')) {
             $errors[] = failed_uploading_file($orig_filename, "Uploaded file size exceeded");
         } elseif ($file->getClientOriginalExtension() != 'po') {
             $errors[] = failed_uploading_file($orig_filename, "Invalid file type");
         }
     } else {
         $errors[] = getlocal("No file selected");
     }
     // Try to process uploaded file
     if (count($errors) == 0) {
         try {
             // Try to import new messages.
             import_messages($target, $file->getRealPath(), $override);
             // Remove cached client side translations.
             $this->getCache()->getItem('translation/js/' . $target)->clear();
             // The file is not needed any more. Remove it.
             unlink($file->getRealPath());
         } catch (\Exception $e) {
             $errors[] = $e->getMessage();
         }
     }
     if (count($errors) != 0) {
         $request->attributes->set('errors', $errors);
         // The form should be rebuild. Invoke appropriate action.
         return $this->showFormAction($request);
     }
     // Redirect the operator to the same page using GET method.
     $redirect_to = $this->generateUrl('translation_import', array('stored' => true));
     return $this->redirect($redirect_to);
 }
예제 #9
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;
 }
예제 #10
0
 /**
  * Generates HTML markup for Mibew widget.
  *
  * @return \Canteen\HTML5\Fragment
  */
 protected function getWidgetCode()
 {
     $widget_data = array();
     // Get actual invitation style instance
     $style_name = $this->getOption('invitation_style') ? $this->getOption('invitation_style') : InvitationStyle::getCurrentStyle();
     $style = new InvitationStyle($style_name);
     // URL of file with additional CSS rules for invitation popup
     $widget_data['inviteStyle'] = $this->generateAssetUrl($style->getFilesPath() . '/invite.css');
     // Time between requests to the server in milliseconds
     $widget_data['requestTimeout'] = Settings::get('updatefrequency_tracking') * 1000;
     // URL for requests
     $widget_data['requestURL'] = $this->generateUrl('widget_gateway');
     // Locale for invitation
     $widget_data['locale'] = $this->getOption('locale');
     // Name of the cookie to track user. It is used if third-party cookie
     // blocked
     $widget_data['visitorCookieName'] = VISITOR_COOKIE_NAME;
     $markup = HTML5\html('fragment');
     $markup->addChild(HTML5\html('div#mibew-invitation'));
     $markup->addChild(HTML5\html('script')->setAttributes(array('type' => 'text/javascript', 'src' => $this->generateAssetUrl('js/compiled/widget.js'))));
     $markup->addChild(HTML5\html('script')->setAttribute('type', 'text/javascript')->addChild('Mibew.Widget.init(' . json_encode($widget_data) . ')'));
     return $markup;
 }
예제 #11
0
/**
 * Check if group is away
 *
 * @param array $group Associative group array. Should contain 'ilastseenaway'
 *   key.
 * @return bool
 */
function group_is_away($group)
{
    return $group['ilastseenaway'] !== null
        && $group['ilastseenaway'] < Settings::get('online_timeout');
}
예제 #12
0
 /**
  * Processes submitting of the form which is generated in
  * {@link \Mibew\Controller\Settings\CommonController::showFormAction()}
  * method.
  *
  * @param Request $request Incoming request.
  * @return string Rendered page content.
  * @throws BadRequestException If one or more parameters of the request have
  *   wrong format.
  */
 public function submitFormAction(Request $request)
 {
     csrf_check_token($request);
     $errors = array();
     $params = array();
     $params['email'] = $request->request->get('email');
     $params['title'] = $request->request->get('title');
     $params['logo'] = $request->request->get('logo');
     $params['hosturl'] = $request->request->get('hosturl');
     $params['usernamepattern'] = $request->request->get('usernamepattern');
     $params['chattitle'] = $request->request->get('chattitle');
     $params['geolink'] = $request->request->get('geolink');
     $params['geolinkparams'] = $request->request->get('geolinkparams');
     $params['cron_key'] = $request->request->get('cronkey');
     $send_key = $request->request->get('sendmessagekey');
     if (!preg_match("/^c?enter\$/", $send_key)) {
         throw new BadRequestException('Wrong format of "sendmessagekey" field.');
     }
     $params['sendmessagekey'] = $send_key;
     $params['left_messages_locale'] = $request->request->get('leftmessageslocale');
     if (!in_array($params['left_messages_locale'], get_available_locales())) {
         $params['left_messages_locale'] = get_home_locale();
     }
     if ($params['email'] && !MailUtils::isValidAddress($params['email'])) {
         $errors[] = getlocal('Enter a valid email address');
     }
     if ($params['geolinkparams']) {
         foreach (explode(',', $params['geolinkparams']) as $one_param) {
             $wrong_param = !preg_match("/^\\s*(toolbar|scrollbars|location|status|menubar|width|height|resizable)=\\d{1,4}\$/", $one_param);
             if ($wrong_param) {
                 $errors[] = "Wrong link parameter: \"{$one_param}\", " . "should be one of 'toolbar, scrollbars, location, " . "status, menubar, width, height or resizable'";
             }
         }
     }
     if (preg_match("/^[0-9A-Za-z]*\$/", $params['cron_key']) == 0) {
         $errors[] = getlocal('Use only Latin letters(upper and lower case) and numbers in cron key.');
     }
     // Load styles configs
     $chat_style = $request->request->get('chat_style', ChatStyle::getDefaultStyle());
     $chat_style_list = ChatStyle::getAvailableStyles();
     if (!in_array($chat_style, $chat_style_list)) {
         $chat_style = $chat_style_list[0];
     }
     $page_style = $request->request->get('page_style', PageStyle::getDefaultStyle());
     $page_style_list = PageStyle::getAvailableStyles();
     if (!in_array($page_style, $page_style_list)) {
         $page_style = $page_style_list[0];
     }
     if (Settings::get('enabletracking')) {
         $invitation_style = $request->request->get('invitation_style', InvitationStyle::getDefaultStyle());
         $invitation_style_list = InvitationStyle::getAvailableStyles();
         if (!in_array($invitation_style, $invitation_style_list)) {
             $invitation_style = $invitation_style_list[0];
         }
     }
     if (count($errors) != 0) {
         $request->attributes->set('errors', $errors);
         // The form should be rebuild. Invoke appropriate action.
         return $this->showFormAction($request);
     }
     // Update system settings
     foreach ($params as $key => $value) {
         Settings::set($key, $value);
     }
     // Update styles params
     ChatStyle::setDefaultStyle($chat_style);
     PageStyle::setDefaultStyle($page_style);
     if (Settings::get('enabletracking')) {
         InvitationStyle::setDefaultStyle($invitation_style);
     }
     // Redirect the user to the same page using GET method
     $redirect_to = $this->generateUrl('settings_common', array('stored' => true));
     return $this->redirect($redirect_to);
 }
예제 #13
0
 /**
  * Releases the lock
  */
 public function release()
 {
     Settings::set($this->getInternalName(), '0');
 }
예제 #14
0
/**
 * Prepare values to render page menu.
 *
 * @param array $operator An array with operators data.
 * @param boolean $has_right Restricts access to menu items. If it equals to
 *   FALSE only "Home", "Visitors", and "Chat history" items will be displayed.
 *   Otherwise items set depends on operator's permissions and system settings.
 *   Default value is TRUE.
 * @return array
 */
function prepare_menu($operator, $has_right = true)
{
    $result = array();

    $result['showMenu'] = true;
    $result['operator'] = get_operator_name($operator);
    if ($has_right) {
        $result['showban'] = Settings::get('enableban') == "1";
        $result['showstat'] = Settings::get('enablestatistics') == "1";
        $result['showadmin'] = is_capable(CAN_ADMINISTRATE, $operator);
        $result['currentopid'] = $operator['operatorid'];
    }

    return $result;
}
예제 #15
0
 /**
  * Generates a page with Mibew button code form.
  *
  * @param Request $request Incoming request
  * @return Response Rendered content of the page.
  */
 public function generateAction(Request $request)
 {
     $operator = $this->getOperator();
     $page = array('errors' => array());
     $image_locales_map = $this->getImageLocalesMap(MIBEW_FS_ROOT . '/locales');
     $image = $request->query->get('i', 'mibew');
     if (!isset($image_locales_map[$image])) {
         $page['errors'][] = 'Unknown image: ' . $image;
         $avail = array_keys($image_locales_map);
         $image = $avail[0];
     }
     $image_locales = $image_locales_map[$image];
     $style_list = ChatStyle::getAvailableStyles();
     $style_list[''] = getlocal('-from general settings-');
     $style = $request->query->get('style', '');
     if ($style && !in_array($style, $style_list)) {
         $style = '';
     }
     $invitation_style_list = InvitationStyle::getAvailableStyles();
     $invitation_style_list[''] = getlocal('-from general settings-');
     $invitation_style = $request->query->get('invitationstyle', '');
     if ($invitation_style && !in_array($invitation_style, $invitation_style_list)) {
         $invitation_style = '';
     }
     $locales_list = get_available_locales();
     $group_id = $request->query->getInt('group');
     if ($group_id && !group_by_id($group_id)) {
         $page['errors'][] = getlocal("No such group");
         $group_id = false;
     }
     $show_host = $request->query->get('hostname') == 'on';
     $force_secure = $request->query->get('secure') == 'on';
     $mod_security = $request->query->get('modsecurity') == 'on';
     $force_windows = $request->query->get('forcewindows') == 'on';
     $code_type = $request->query->get('codetype', 'button');
     if (!in_array($code_type, array('button', 'operator_code', 'text_link'))) {
         throw new BadRequestException('Wrong value of "codetype" param.');
     }
     $lang = $request->query->get('lang', '');
     if (!preg_match("/^[\\w-]{2,5}\$/", $lang)) {
         $lang = '';
     }
     $operator_code = $code_type == 'operator_code';
     $generate_button = $code_type == 'button';
     $button_generator_options = array('chat_style' => $style, 'group_id' => $group_id, 'show_host' => $show_host, 'force_secure' => $force_secure, 'mod_security' => $mod_security, 'prefer_iframe' => !$force_windows);
     if ($operator_code) {
         $button_generator = new OperatorCodeFieldGenerator($this->getRouter(), $this->getAssetManager()->getUrlGenerator(), $button_generator_options);
     } elseif ($generate_button) {
         // Make sure locale exists
         if (!$lang || !in_array($lang, $image_locales)) {
             $lang = in_array(get_current_locale(), $image_locales) ? get_current_locale() : $image_locales[0];
         }
         $button_generator = new ImageButtonGenerator($this->getRouter(), $this->getAssetManager()->getUrlGenerator(), $button_generator_options);
         // Set generator-specific options
         $button_generator->setOption('image', $image);
     } else {
         // Make sure locale exists
         if (!$lang || !in_array($lang, $locales_list)) {
             $lang = in_array(get_current_locale(), $locales_list) ? get_current_locale() : $locales_list[0];
         }
         $button_generator = new TextButtonGenerator($this->getRouter(), $this->getAssetManager()->getUrlGenerator(), $button_generator_options);
         // Set generator-specific options
         $button_generator->setOption('caption', getlocal('Click to chat'));
     }
     // Set verified locale code to a button generator
     $button_generator->setOption('locale', $lang);
     $page['buttonCode'] = $button_generator->generate();
     $page['availableImages'] = array_keys($image_locales_map);
     $page['availableLocales'] = $generate_button ? $image_locales : $locales_list;
     $page['availableChatStyles'] = $style_list;
     $page['availableInvitationStyles'] = $invitation_style_list;
     $page['groups'] = $this->getGroupsList();
     $page['availableCodeTypes'] = array('button' => getlocal('button'), 'operator_code' => getlocal('operator code field'), 'text_link' => getlocal('text link'));
     $page['formgroup'] = $group_id;
     $page['formstyle'] = $style;
     $page['forminvitationstyle'] = $invitation_style;
     $page['formimage'] = $image;
     $page['formlang'] = $lang;
     $page['formhostname'] = $show_host;
     $page['formsecure'] = $force_secure;
     $page['formmodsecurity'] = $mod_security;
     $page['formcodetype'] = $code_type;
     $page['formforcewindows'] = $force_windows;
     $page['enabletracking'] = Settings::get('enabletracking');
     $page['operator_code'] = $operator_code;
     $page['generateButton'] = $generate_button;
     $page['title'] = getlocal("Button HTML code generation");
     $page['menuid'] = "getcode";
     $page = array_merge($page, prepare_menu($operator));
     return $this->render('button_code', $page);
 }
예제 #16
0
 /**
  * Return updated operators list. API function
  *
  * @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:
  *    - 'operators': array of online operators
  */
 protected function apiUpdateOperators($args)
 {
     // Check access and get operators info
     $operator = $this->checkOperator($args['agentId']);
     // Return empty array if show operators option disabled
     if (Settings::get('showonlineoperators') != '1') {
         return array('operators' => array());
     }
     // Check if curent operator is in isolation
     $list_options = in_isolation($operator) ? array('isolated_operator_id' => $operator['operatorid']) : array();
     // Get operators list
     $operators = get_operators_list($list_options);
     // Create resulting list of operators
     $result_list = array();
     foreach ($operators as $item) {
         if (!operator_is_online($item)) {
             continue;
         }
         $result_list[] = array('id' => (int) $item['operatorid'], 'name' => htmlspecialchars($item['vclocalename']), 'away' => (bool) operator_is_away($item));
     }
     // Send operators list to the client side
     return array('operators' => $result_list);
 }
예제 #17
0
    /**
     * 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);
    }
예제 #18
0
 /**
  * Retrives an instance of Update Checker attached to the worker.
  *
  * If there was no attached checker it creates a new one.
  *
  * @return UpdateChecker
  */
 protected function getUpdateChecker()
 {
     if (is_null($this->updateChecker)) {
         $this->updateChecker = new UpdateChecker();
         $id = Settings::get('_instance_id');
         if ($id) {
             $this->updateChecker->setInstanceId($id);
         }
     }
     return $this->updateChecker;
 }
예제 #19
0
 /**
  * Processes submitting of the form which is generated in
  * {@link \Mibew\Controller\Settings\PerformanceController::showFormAction()}
  * method.
  *
  * @param Request $request Incoming request.
  * @return string Rendered page content.
  */
 public function submitFormAction(Request $request)
 {
     csrf_check_token($request);
     $errors = array();
     $params = array();
     $params['online_timeout'] = $request->request->get('onlinetimeout');
     if (!is_numeric($params['online_timeout'])) {
         $errors[] = wrong_field("Operator online time threshold");
     }
     $params['connection_timeout'] = $request->request->get('connectiontimeout');
     if (!is_numeric($params['connection_timeout'])) {
         $errors[] = wrong_field("Connection timeout for messaging window");
     }
     $params['updatefrequency_operator'] = $request->request->get('frequencyoperator');
     if (!is_numeric($params['updatefrequency_operator'])) {
         $errors[] = wrong_field("Operator's console refresh time");
     }
     $params['updatefrequency_chat'] = $request->request->get('frequencychat');
     if (!is_numeric($params['updatefrequency_chat'])) {
         $errors[] = wrong_field("Chat refresh time");
     }
     $params['max_connections_from_one_host'] = $request->request->get('onehostconnections');
     if (!is_numeric($params['max_connections_from_one_host'])) {
         $errors[] = getlocal("\"Max number of threads\" field should be a number");
     }
     $params['thread_lifetime'] = $request->request->get('threadlifetime');
     if (!is_numeric($params['thread_lifetime'])) {
         $errors[] = getlocal("\"Thread lifetime\" field should be a number");
     }
     if (Settings::get('enabletracking')) {
         $params['updatefrequency_tracking'] = $request->request->get('frequencytracking');
         if (!is_numeric($params['updatefrequency_tracking'])) {
             $errors[] = wrong_field("Tracking refresh time");
         }
         $params['visitors_limit'] = $request->request->get('visitorslimit');
         if (!is_numeric($params['visitors_limit'])) {
             $errors[] = wrong_field("Limit for tracked visitors list");
         }
         $params['invitation_lifetime'] = $request->request->get('invitationlifetime');
         if (!is_numeric($params['invitation_lifetime'])) {
             $errors[] = wrong_field("Invitation lifetime");
         }
         $params['tracking_lifetime'] = $request->request->get('trackinglifetime');
         if (!is_numeric($params['tracking_lifetime'])) {
             $errors[] = wrong_field("Track lifetime");
         }
     }
     $params['max_uploaded_file_size'] = $request->request->get('maxuploadedfilesize');
     if (!is_numeric($params['max_uploaded_file_size'])) {
         $errors[] = wrong_field("Maximum size of uploaded files");
     }
     if (count($errors) != 0) {
         $request->attributes->set('errors', $errors);
         // The form should be rebuild. Invoke appropriate action.
         return $this->showFormAction($request);
     }
     // Update settings in the database
     foreach ($params as $key => $value) {
         Settings::set($key, $value);
     }
     // Redirect the current operator to the same page using get method.
     $redirect_to = $this->generateUrl('settings_performance', array('stored' => true));
     return $this->redirect($redirect_to);
 }
예제 #20
0
/**
 * Prepare data to dispaly invitation
 *
 * @param Thread $thread Thread object related with invitation
 * @return array Array of invitation data
 */
function setup_invitation_view(Thread $thread)
{
    $data = prepare_chat_app_data();
    // Set refresh frequency
    $data['frequency'] = Settings::get('updatefrequency_chat');
    // Create some empty arrays
    $data['invitation'] = array();
    $data['invitation']['thread'] = array('id' => $thread->id, 'token' => $thread->lastToken, 'agentId' => $thread->agentId, 'userId' => $thread->userId);
    $data['invitation']['user'] = array('name' => htmlspecialchars($thread->userName), 'canChangeName' => false, 'isAgent' => false);
    $data['startFrom'] = 'invitation';
    return $data;
}
예제 #21
0
 /**
  * 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;
 }
예제 #22
0
 /**
  * Processes submitting of the form which is generated in
  * {@link \Mibew\Controller\Operator\AvatarController::showFormAction()}
  * method.
  *
  * @param Request $request Incoming request.
  * @return string Rendered page content.
  * @throws NotFoundException If the operator with specified ID is not found
  *   in the system.
  */
 public function submitFormAction(Request $request)
 {
     csrf_check_token($request);
     $operator = $this->getOperator();
     $op_id = $request->attributes->getInt('operator_id');
     $errors = array();
     // Try to load the target operator.
     $op = operator_by_id($op_id);
     if (!$op) {
         throw new NotFoundException('The operator is not found');
     }
     $avatar = $op['vcavatar'];
     $file = $request->files->get('avatarFile');
     if ($file) {
         // Process uploaded file.
         $valid_types = array("gif", "jpg", "png", "tif", "jpeg");
         $ext = $file->getClientOriginalExtension();
         $orig_filename = $file->getClientOriginalName();
         $new_file_name = intval($op_id) . ".{$ext}";
         $file_size = $file->getSize();
         if ($file_size == 0 || $file_size > Settings::get('max_uploaded_file_size')) {
             $errors[] = failed_uploading_file($orig_filename, "Uploaded file size exceeded");
         } elseif (!in_array($ext, $valid_types)) {
             $errors[] = failed_uploading_file($orig_filename, "Invalid file type");
         } else {
             // Remove avatar if it already exists
             $avatar_local_dir = MIBEW_FS_ROOT . '/files/avatar/';
             $full_file_path = $avatar_local_dir . $new_file_name;
             if (file_exists($full_file_path)) {
                 unlink($full_file_path);
             }
             // Move uploaded file to avatar directory
             try {
                 $file->move($avatar_local_dir, $new_file_name);
                 $avatar = 'files/avatar/' . $new_file_name;
             } catch (Exception $e) {
                 $errors[] = failed_uploading_file($orig_filename, "Error moving file");
             }
         }
     } else {
         $errors[] = getlocal("No file selected");
     }
     if (count($errors) != 0) {
         $request->attributes->set('errors', $errors);
         // The form should be rebuild. Invoke appropriate action.
         return $this->showFormAction($request);
     }
     // Update path to avatar in the database
     update_operator_avatar($op['operatorid'], $avatar);
     // Operator's data are cached in the authentication manager thus we need
     // to update them manually.
     if ($avatar && $operator['operatorid'] == $op_id) {
         $operator['vcavatar'] = $avatar;
         $this->getAuthenticationManager()->setOperator($operator);
     }
     // Redirect the operator to the same page using GET method.
     $redirect_to = $this->generateUrl('operator_avatar', array('operator_id' => $op_id));
     return $this->redirect($redirect_to);
 }
 /**
  * Generates a page with user tracking information.
  *
  * @param Request $request
  * @return string Rendered page content
  */
 public function userTrackAction(Request $request)
 {
     if (Settings::get('enabletracking') == '0') {
         throw new BadRequestException('Tracking is disabled.');
     }
     if ($request->query->has('thread')) {
         $thread_id = $request->query->get('thread');
         if (!preg_match("/^\\d{1,8}\$/", $thread_id)) {
             throw new BadRequestException('Wrong thread ID.');
         }
         $visitor = track_get_visitor_by_thread_id($thread_id);
         if (!$visitor) {
             throw new BadRequestException('Wrong thread.');
         }
     } else {
         $visitor_id = $request->query->get('visitor');
         if (!preg_match("/^\\d{1,8}\$/", $visitor_id)) {
             throw new BadRequestException('Wrong visitor ID.');
         }
         $visitor = track_get_visitor_by_id($visitor_id);
         if (!$visitor) {
             throw new BadRequestException('Wrong visitor.');
         }
     }
     $path = track_get_path($visitor);
     $page['entry'] = htmlspecialchars($visitor['entry']);
     $page['history'] = array();
     ksort($path);
     foreach ($path as $k => $v) {
         $page['history'][] = array('date' => date_to_text($k), 'link' => htmlspecialchars($v));
     }
     $page['title'] = getlocal('Tracked path of visitor');
     $page['show_small_login'] = false;
     return $this->render('tracked', $page);
 }
예제 #24
0
    /**
     * 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)
            );
        }
    }
예제 #25
0
 /**
  * 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);
 }
예제 #26
0
 /**
  * Generates JavaScript code that starts client side application.
  *
  * @param Request $request Incoming request.
  * @param array $options Client side application options. At the moment the
  *   method accepts the following options:
  *   - "company": array, a set of company info. See {@link setup_logo()}
  *     for details.
  *   - "mibewHost": string, a URL which is used as a Mibew host. See
  *     {@link setup_logo()} for details.
  *   - "page.title": string, a value which will be used as a page title.
  *   - "startFrom": string, indicates what module should be invoked first.
  *   - "chatOptions": array, (optional) list of chat module options.
  *   - "surveyOptions": array, (optional) list of pre-chat survey module
  *     options.
  *   - "leaveMessageOptions": array, (optional) list of leave message module
  *     options.
  *   - "invitationOptions": array, (optional) list of invitation module
  *     options.
  * @return string JavaScript code that starts "users" client side
  *   application.
  * @todo The way options passed here should be reviewed. The method must get
  *   finite number of well-structured arguments.
  */
 protected function startJsApplication(Request $request, $options)
 {
     $app_settings = array('server' => array('url' => $this->generateUrl('chat_thread_update'), 'requestsFrequency' => Settings::get('updatefrequency_chat')), 'page' => array('style' => $this->getStyle()->getName(), 'mibewBasePath' => $request->getBasePath(), 'mibewBaseUrl' => $request->getBaseUrl(), 'stylePath' => $request->getBasePath() . '/' . $this->getStyle()->getFilesPath(), 'company' => isset($options['company']) ? $options['company'] : '', 'mibewHost' => isset($options['mibewHost']) ? $options['mibewHost'] : '', 'title' => isset($options['page.title']) ? $options['page.title'] : ''), 'startFrom' => $options['startFrom']);
     // Add module specific options
     $module_options_list = array('chatOptions', 'surveyOptions', 'leaveMessageOptions', 'invitationOptions');
     foreach ($module_options_list as $key) {
         if (isset($options[$key])) {
             $app_settings[$key] = $options[$key];
         }
     }
     return sprintf('jQuery(document).ready(function() {Mibew.Application.start(%s);});', json_encode($app_settings));
 }
예제 #27
0
 /**
  * Builds list of the statistics tabs.
  *
  * @param Request $request Current request.
  * @return array Tabs list. The keys of the array are tabs titles and the
  *   values are tabs URLs.
  */
 protected function buildTabs(Request $request)
 {
     $tabs = array();
     $args = $request->query->all();
     $type = $request->attributes->get('type');
     $tabs[getlocal('Usage statistics for each date')] = $type != self::TYPE_BY_DATE ? $this->generateUrl('statistics', $args + array('type' => self::TYPE_BY_DATE)) : '';
     $tabs[getlocal('Threads by operator')] = $type != self::TYPE_BY_OPERATOR ? $this->generateUrl('statistics', $args + array('type' => self::TYPE_BY_OPERATOR)) : '';
     if (Settings::get('enabletracking')) {
         $tabs[getlocal('Chat threads by page')] = $type != self::TYPE_BY_PAGE ? $this->generateUrl('statistics', $args + array('type' => self::TYPE_BY_PAGE)) : '';
     }
     return $tabs;
 }
예제 #28
0
 /**
  * Ping the thread.
  *
  * Updates ping time for conversation members and sends messages about
  * connection problems.
  *
  * @param boolean $is_user Indicates user or operator pings thread. Boolean
  *   true for user and boolean false otherwise.
  * @param boolean $is_typing Indicates if user or operator is typing a
  *   message.
  */
 public function ping($is_user, $is_typing)
 {
     // Indicates if revision ID of the thread should be updated on save.
     // Update revision leads to rerender thread in threads list at client
     // side. Do it on every ping is too costly.
     $update_revision = false;
     // Last ping time of other side
     $last_ping_other_side = 0;
     // Update last ping time
     if ($is_user) {
         $last_ping_other_side = $this->lastPingAgent;
         $this->lastPingUser = time();
         $this->userTyping = $is_typing ? "1" : "0";
     } else {
         $last_ping_other_side = $this->lastPingUser;
         $this->lastPingAgent = time();
         $this->agentTyping = $is_typing ? "1" : "0";
     }
     // Update thread state for the first user ping
     if ($this->state == self::STATE_LOADING && $is_user) {
         $this->state = self::STATE_QUEUE;
         $this->save();
         return;
     }
     // Check if other side of the conversation have connection problems
     if ($last_ping_other_side > 0 && abs(time() - $last_ping_other_side) > Settings::get('connection_timeout')) {
         // Connection problems detected
         if ($is_user) {
             // _Other_ side is operator
             // Update operator's last ping time
             $this->lastPingAgent = 0;
             // Check if user chatting at the moment
             if ($this->state == self::STATE_CHATTING) {
                 // Send message to user
                 $message_to_post = getlocal('Your operator has connection issues. We have moved you to a priorty position in the queue. Sorry for keeping you waiting.', null, $this->locale, true);
                 $this->postMessage(self::KIND_CONN, $message_to_post, array('created' => $last_ping_other_side + Settings::get('connection_timeout')));
                 // And update thread
                 $this->state = self::STATE_WAITING;
                 $this->nextAgent = 0;
                 // Significant fields of the thread (state and nextAgent)
                 // are changed. Update revision ID on save.
                 $update_revision = true;
             }
         } else {
             // _Other_ side is user
             // Update user's last ping time
             $this->lastPingUser = 0;
             // And send a message to operator.
             if ($this->state == self::STATE_CHATTING) {
                 $message_to_post = getlocal('Visitor closed chat window', null, $this->locale, true);
                 $this->postMessage(self::KIND_FOR_AGENT, $message_to_post, array('created' => $last_ping_other_side + Settings::get('connection_timeout')));
             }
         }
     }
     $this->save($update_revision);
 }
예제 #29
0
 /**
  * Sets style which is used in the system by default
  *
  * @param string $style_name Name of a style
  */
 public static function setDefaultStyle($style_name)
 {
     Settings::set('invitation_style', $style_name);
 }
예제 #30
0
파일: chat.php 프로젝트: aburakovskiy/mibew
/**
 * 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;
}