/** * 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.'; } }
/** * 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'); }
/** * 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; }
/** * 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); } }
/** * 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; }
/** * 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']); }
/** * 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); } }
/** * 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); }
/** * 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']; }
/** * 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']); }
/** * 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); }
/** * 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); }
/** * 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); } }
/** * 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(); } }
/** * 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; }