mirror of
https://github.com/Mibew/mibew.git
synced 2025-04-24 23:36:07 +03:00
353 lines
11 KiB
PHP
353 lines
11 KiB
PHP
<?php
|
|
/*
|
|
* This file is a part of Mibew Messenger.
|
|
*
|
|
* Copyright 2005-2023 the original author or authors.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
// Import namespaces and classes of the core
|
|
use Mibew\EventDispatcher\EventDispatcher;
|
|
use Mibew\EventDispatcher\Events;
|
|
use Mibew\Database;
|
|
use Mibew\ProcessLock;
|
|
use Mibew\Settings;
|
|
use Mibew\Thread;
|
|
|
|
/**
|
|
* Check invitation state for specified visitior
|
|
*
|
|
* @param int $visitor_id ID of the visitor to check
|
|
* @return array Array of invitation info. It contains following items:
|
|
* - 'invited': boolean, indicates if the visitor was invited by an operator;
|
|
* - 'threadid': int, ID of the thread, related with visitor or boolean false
|
|
* if visitor with specfied ID does not exist.
|
|
*/
|
|
function invitation_state($visitor_id)
|
|
{
|
|
$db = Database::getInstance();
|
|
$db_result = $db->query(
|
|
("SELECT t.threadid, t.invitationstate, t.istate "
|
|
. "FROM {sitevisitor} v, {thread} t "
|
|
. "WHERE visitorid = ? "
|
|
. "AND t.threadid = v.threadid"),
|
|
array($visitor_id),
|
|
array('return_rows' => Database::RETURN_ONE_ROW)
|
|
);
|
|
|
|
$ret = array();
|
|
if (!$db_result) {
|
|
$ret['invited'] = false;
|
|
$ret['threadid'] = false;
|
|
} else {
|
|
$ret['invited'] = ($db_result['istate'] == Thread::STATE_INVITED)
|
|
&& ($db_result['invitationstate'] == Thread::INVITATION_WAIT);
|
|
$ret['threadid'] = $db_result['threadid'];
|
|
}
|
|
|
|
return $ret;
|
|
}
|
|
|
|
/**
|
|
* Invite visitor by operator
|
|
*
|
|
* Triggers {@link \Mibew\EventDispatcher\Events::INVITATION_CREATE} event.
|
|
*
|
|
* @param int $visitor_id ID of the visitor, who must be invited.
|
|
* @param array $operator Info for operator who invite the visitor
|
|
* @return Thread|boolean Thread object related with invitation or boolean
|
|
* false on failure
|
|
*/
|
|
function invitation_invite($visitor_id, $operator)
|
|
{
|
|
// Check if visitor already invited
|
|
$invitation_state = invitation_state($visitor_id);
|
|
if ($invitation_state['invited']) {
|
|
return false;
|
|
}
|
|
|
|
// Get visitor info
|
|
$visitor = track_get_visitor_by_id($visitor_id);
|
|
|
|
// Get last page visited by the visitor
|
|
$visitor_path = track_get_path($visitor);
|
|
ksort($visitor_path);
|
|
$last_visited_page = array_pop($visitor_path);
|
|
|
|
// Get visitor details
|
|
$visitor_details = track_retrieve_details($visitor);
|
|
|
|
// Get some operator's info
|
|
$operator_name = get_operator_name($operator);
|
|
|
|
// Create thread for invitation
|
|
$thread = new Thread();
|
|
|
|
// Populate thread and save it
|
|
$thread->agentId = $operator['operatorid'];
|
|
$thread->agentName = $operator_name;
|
|
$thread->userName = $visitor['username'];
|
|
$thread->remote = $visitor_details['remote_host'];
|
|
$thread->referer = $last_visited_page;
|
|
// User's locale is unknown, set operator locale to the thread
|
|
$thread->locale = get_current_locale();
|
|
$thread->userId = $visitor['userid'];
|
|
$thread->userAgent = $visitor_details['user_agent'];
|
|
$thread->state = Thread::STATE_INVITED;
|
|
$thread->invitationState = Thread::INVITATION_WAIT;
|
|
$thread->save();
|
|
|
|
$db = Database::getInstance();
|
|
$db->query(
|
|
("UPDATE {sitevisitor} set "
|
|
. "invitations = invitations + 1, "
|
|
. "threadid = :thread_id "
|
|
. "WHERE visitorid = :visitor_id"),
|
|
array(
|
|
':thread_id' => $thread->id,
|
|
':visitor_id' => $visitor_id,
|
|
)
|
|
);
|
|
|
|
// Send some messages
|
|
$thread->postMessage(
|
|
Thread::KIND_FOR_AGENT,
|
|
getlocal(
|
|
'Operator {0} invites visitor at {1} page',
|
|
array(safe_htmlspecialchars($operator_name), safe_htmlspecialchars($last_visited_page)),
|
|
get_current_locale(),
|
|
true
|
|
)
|
|
);
|
|
$thread->postMessage(
|
|
Thread::KIND_AGENT,
|
|
getlocal('Hello, how can I help you?', null, get_current_locale(), true),
|
|
array(
|
|
'name' => $operator_name,
|
|
'operator_id' => $operator['operatorid'],
|
|
)
|
|
);
|
|
|
|
// Let plugins know about the invitation.
|
|
$args = array('invitation' => $thread);
|
|
EventDispatcher::getInstance()->triggerEvent(Events::INVITATION_CREATE, $args);
|
|
|
|
return $thread;
|
|
}
|
|
|
|
/**
|
|
* Invitation was accepted by visitor
|
|
*
|
|
* Triggers {@link \Mibew\EventDispatcher\Events::INVITATION_ACCEPT} event.
|
|
*
|
|
* @param int $visitor_id ID of the visitor who accept invitation
|
|
* @return Thread|boolean Thread object or boolean false on failure
|
|
*/
|
|
function invitation_accept($visitor_id)
|
|
{
|
|
// Check if visitor was invited
|
|
$invitation_state = invitation_state($visitor_id);
|
|
if (!$invitation_state['invited']) {
|
|
// Visitor was not invited
|
|
return false;
|
|
}
|
|
|
|
// Get thread related with the visitor
|
|
$db = Database::getInstance();
|
|
$result = $db->query(
|
|
"SELECT threadid FROM {sitevisitor} WHERE visitorid = :visitor_id",
|
|
array(':visitor_id' => $visitor_id),
|
|
array('return_rows' => Database::RETURN_ONE_ROW)
|
|
);
|
|
|
|
if (empty($result['threadid'])) {
|
|
// Something went wrong. There is no thread related with the visitor.
|
|
return false;
|
|
}
|
|
|
|
$thread = Thread::load($result['threadid']);
|
|
if (!$thread) {
|
|
// Something went wrong. Cannot load thread.
|
|
return false;
|
|
}
|
|
|
|
// Update thread info
|
|
$thread->state = Thread::STATE_CHATTING;
|
|
$thread->invitationState = Thread::INVITATION_ACCEPTED;
|
|
$thread->chatStarted = time();
|
|
$thread->save();
|
|
|
|
// Update visitor info
|
|
$db->query(
|
|
("UPDATE {sitevisitor} SET chats = chats + 1 "
|
|
. "WHERE visitorid = :visitor_id"),
|
|
array(':visitor_id' => $visitor_id)
|
|
);
|
|
|
|
$args = array('invitation' => $thread);
|
|
EventDispatcher::getInstance()->triggerEvent(Events::INVITATION_ACCEPT, $args);
|
|
|
|
return $thread;
|
|
}
|
|
|
|
/**
|
|
* Inviation was rejected by visitor
|
|
*
|
|
* Triggers {@link \Mibew\EventDispatcher\Events::INVITATION_REJECT} event.
|
|
*
|
|
* @param int $visitor_id ID of the visitor
|
|
*/
|
|
function invitation_reject($visitor_id)
|
|
{
|
|
$visitor = track_get_visitor_by_id($visitor_id);
|
|
|
|
// Send message to operator
|
|
$thread = Thread::load($visitor['threadid']);
|
|
if ($thread) {
|
|
$thread->postMessage(
|
|
Thread::KIND_FOR_AGENT,
|
|
getlocal('Visitor rejected invitation', null, get_current_locale(), true)
|
|
);
|
|
}
|
|
|
|
$db = Database::getInstance();
|
|
$db->query(
|
|
("UPDATE {sitevisitor} v, {thread} t SET "
|
|
. "v.threadid = NULL, "
|
|
. "t.invitationstate = :invitation_rejected, "
|
|
. "t.istate = :state_closed, "
|
|
. "t.dtmclosed = :now "
|
|
. "WHERE t.threadid = v.threadid "
|
|
. "AND visitorid = :visitor_id"),
|
|
array(
|
|
':invitation_rejected' => Thread::INVITATION_REJECTED,
|
|
':state_closed' => Thread::STATE_CLOSED,
|
|
':visitor_id' => $visitor_id,
|
|
':now' => time(),
|
|
)
|
|
);
|
|
|
|
$args = array('invitation' => $thread);
|
|
EventDispatcher::getInstance()->triggerEvent(Events::INVITATION_REJECT, $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();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|