mirror of
				https://github.com/Mibew/java.git
				synced 2025-10-31 18:41:09 +03:00 
			
		
		
		
	Create Thread class and tests for it
This commit is contained in:
		
							parent
							
								
									1a963cff00
								
							
						
					
					
						commit
						03ffd84e76
					
				
							
								
								
									
										1226
									
								
								src/messenger/tests/server_side/webim/classes/ThreadTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1226
									
								
								src/messenger/tests/server_side/webim/classes/ThreadTest.php
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										727
									
								
								src/messenger/webim/libs/classes/thread.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										727
									
								
								src/messenger/webim/libs/classes/thread.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,727 @@ | ||||
| <?php | ||||
| /* | ||||
|  * Copyright 2005-2013 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. | ||||
|  */ | ||||
| 
 | ||||
| /** | ||||
|  * Represents a chat thread | ||||
|  * | ||||
|  * @todo Think about STATE_* and KIND_* constant systems and may be simplifies them. | ||||
|  */ | ||||
| Class Thread { | ||||
| 
 | ||||
| 	/** | ||||
| 	 * User in the users queue | ||||
| 	 */ | ||||
| 	const STATE_QUEUE = 0; | ||||
| 	/** | ||||
| 	 * User waiting for operator | ||||
| 	 */ | ||||
| 	const STATE_WAITING = 1; | ||||
| 	/** | ||||
| 	 * Conversation in progress | ||||
| 	 */ | ||||
| 	const STATE_CHATTING = 2; | ||||
| 	/** | ||||
| 	 * Thread closed | ||||
| 	 */ | ||||
| 	const STATE_CLOSED = 3; | ||||
| 	/** | ||||
| 	 * Thread just created | ||||
| 	 */ | ||||
| 	const STATE_LOADING = 4; | ||||
| 	/** | ||||
| 	 * User left message without starting a conversation | ||||
| 	 */ | ||||
| 	const STATE_LEFT = 5; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Message sent by user | ||||
| 	 */ | ||||
| 	const KIND_USER = 1; | ||||
| 	/** | ||||
| 	 * Message sent by operator | ||||
| 	 */ | ||||
| 	const KIND_AGENT = 2; | ||||
| 	/** | ||||
| 	 * Hidden system message to operator | ||||
| 	 */ | ||||
| 	const KIND_FOR_AGENT = 3; | ||||
| 	/** | ||||
| 	 * System messages for user and operator | ||||
| 	 */ | ||||
| 	const KIND_INFO = 4; | ||||
| 	/** | ||||
| 	 * Message for user if operator have connection problems | ||||
| 	 */ | ||||
| 	const KIND_CONN = 5; | ||||
| 	/** | ||||
| 	 * System message about some events (like rename). | ||||
| 	 */ | ||||
| 	const KIND_EVENTS = 6; | ||||
| 	/** | ||||
| 	 * Message with operators avatar | ||||
| 	 */ | ||||
| 	const KIND_AVATAR = 7; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Messaging window connection timeout. | ||||
| 	 */ | ||||
| 	const CONNECTION_TIMEOUT = 30; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Contain mapping of thread object properties to fields in database. | ||||
| 	 * | ||||
| 	 * Keys are object properties and vlues are {chatthread} table fields. Properties are available via magic __get | ||||
| 	 * and __set methods. Real values are stored in the Thread::$threadInfo array. | ||||
| 	 * | ||||
| 	 * Thread object have following properties: | ||||
| 	 *  - 'id': id of the thread | ||||
| 	 *  - 'lastRevision': last revision number | ||||
| 	 *  - 'state': state of the thread. See Thread::STATE_* | ||||
| 	 *  - 'lastToken': last chat token | ||||
| 	 *  - 'nextAgent': id of the next agent(agent that change current agent in the chat) | ||||
| 	 *  - 'groupId': id of the group related to the thread | ||||
| 	 *  - 'shownMessageId': last id of shown message | ||||
| 	 *  - 'messageCount': count of user's messages related to the thread | ||||
| 	 *  - 'created': unix timestamp of the thread creation | ||||
| 	 *  - 'modified': unix timestamp of the thread's last modification | ||||
| 	 *  - 'chatStarted': unix timestamp of related to thread chat started | ||||
| 	 *  - 'agentId': id of an operator who take part in the chat | ||||
| 	 *  - 'agentName': name of an operator who take part in the chat | ||||
| 	 *  - 'agentTyping': "1" if operator typing at last ping time and "0" otherwise | ||||
| 	 *  - 'lastPingAgent': unix timestamp of last operator ping | ||||
| 	 *  - 'locale': locale code of the chat related to thread | ||||
| 	 *  - 'userId': id of an user who take part in the chat | ||||
| 	 *  - 'userName': name of an user who take part in the chat | ||||
| 	 *  - 'userTyping': "1" if user typing at last ping time and "0" otherwise | ||||
| 	 *  - 'lastPingUser': unix timestamp of last user ping | ||||
| 	 *  - 'remote': user's IP | ||||
| 	 *  - 'referer': content of HTTP Referer header for user | ||||
| 	 *  - 'userAgent': content of HTTP User-agent header for user | ||||
| 	 * | ||||
| 	 * @var array | ||||
| 	 * | ||||
| 	 * @see Thread::__get() | ||||
| 	 * @see Thread::__set() | ||||
| 	 * @see Thread::$threadInfo | ||||
| 	 */ | ||||
| 	protected $propertyMap = array( | ||||
| 		'id' => 'threadid', | ||||
| 
 | ||||
| 		'lastRevision' => 'lrevision', | ||||
| 		'state' => 'istate', | ||||
| 		'lastToken' => 'ltoken', | ||||
| 
 | ||||
| 		'nextAgent' => 'nextagent', | ||||
| 		'groupId' => 'groupid', | ||||
| 
 | ||||
| 		'shownMessageId' => 'shownmessageid', | ||||
| 		'messageCount' => 'messageCount', | ||||
| 
 | ||||
| 		'created' => 'dtmcreated', | ||||
| 		'modified' => 'dtmmodified', | ||||
| 		'chatStarted' => 'dtmchatstarted', | ||||
| 
 | ||||
| 		'agentId' => 'agentId', | ||||
| 		'agentName' => 'agentName', | ||||
| 		'agentTyping' => 'agentTyping', | ||||
| 		'lastPingAgent' => 'lastpingagent', | ||||
| 
 | ||||
| 		'locale' => 'locale', | ||||
| 
 | ||||
| 		'userId' => 'userid', | ||||
| 		'userName' => 'userName', | ||||
| 		'userTyping' => 'userTyping', | ||||
| 		'lastPingUser' => 'lastpinguser', | ||||
| 
 | ||||
| 		'remote' => 'remote', | ||||
| 		'referer' => 'referer', | ||||
| 		'userAgent' => 'userAgent' | ||||
| 	); | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Contain loaded from database information about thread | ||||
| 	 * | ||||
| 	 * Do not use this property manually! | ||||
| 	 * @var array | ||||
| 	 */ | ||||
| 	protected $threadInfo; | ||||
| 
 | ||||
| 	/** | ||||
| 	 * List of modified fields. | ||||
| 	 * | ||||
| 	 * Do not use this property manually! | ||||
| 	 * @var array | ||||
| 	 */ | ||||
| 	protected $updatedFields = array(); | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Load thread from database or create a new one in the database if $id is empty. | ||||
| 	 * | ||||
| 	 * @param int $id ID of the thread to load | ||||
| 	 */ | ||||
| 	protected function __construct($id = null) { | ||||
| 		// Get database object
 | ||||
| 		$db = Database::getInstance(); | ||||
| 
 | ||||
| 		if (! empty($id)) { | ||||
| 			// Load thread
 | ||||
| 			$thread_info = $db->query( | ||||
| 				"select * from {chatthread} where threadid = :threadid", | ||||
| 				array( | ||||
| 					':threadid' => $id | ||||
| 				), | ||||
| 				array('return_rows' => Database::RETURN_ONE_ROW) | ||||
| 			); | ||||
| 
 | ||||
| 			if (! $thread_info) { | ||||
| 				return; | ||||
| 			} | ||||
| 
 | ||||
| 			$this->threadInfo = $thread_info; | ||||
| 		} else { | ||||
| 			// Create thread
 | ||||
| 			$db->query("insert into {chatthread} (threadid) values (NULL)"); | ||||
| 			// Set initial values
 | ||||
| 			// In this case Thread::$threadInfo array use because id of a thread should not be update
 | ||||
| 			$this->threadInfo['threadid'] = $db->insertedId(); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Create new empty thread in database | ||||
| 	 * | ||||
| 	 * @return boolean|Thread Returns an object of the Thread class or boolean false on failure | ||||
| 	 */ | ||||
| 	public static function create() { | ||||
| 		// Create new empty thread
 | ||||
| 		$thread = new self(); | ||||
| 		// Check if something went wrong
 | ||||
| 		if (empty($thread->id)) { | ||||
| 			return false; | ||||
| 		} | ||||
| 		return $thread; | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Load thread from database | ||||
| 	 * | ||||
| 	 * @param int $id ID of the thread to load | ||||
| 	 * @return boolean|Thread Returns an object of the Thread class or boolean false on failure | ||||
| 	 */ | ||||
| 	public static function load($id) { | ||||
| 		// Check $id
 | ||||
| 		if (empty($id)) { | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		// Load thread
 | ||||
| 		$thread = new self($id); | ||||
| 
 | ||||
| 		// Check if something went wrong
 | ||||
| 		if ($thread->id != $id) { | ||||
| 			return false; | ||||
| 		} | ||||
| 		return $thread; | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Reopen thread and send message about it | ||||
| 	 * | ||||
| 	 * @return boolean|Thread Boolean FALSE on failure or thread object on success | ||||
| 	 */ | ||||
| 	public static function reopen($id) { | ||||
| 		// Load thread
 | ||||
| 		$thread = self::load($id); | ||||
| 		// Check if user and agent gone
 | ||||
| 		if (Settings::get('thread_lifetime') != 0 && | ||||
| 				abs($thread->lastPingUser - time()) > Settings::get('thread_lifetime') && | ||||
| 				abs($thread->lastPingAgent - time()) > Settings::get('thread_lifetime')) { | ||||
| 			unset($thread); | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		// Check if thread closed
 | ||||
| 		if ($thread->state == self::STATE_CLOSED || $thread->state == self::STATE_LEFT) { | ||||
| 			unset($thread); | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		// Reopen thread
 | ||||
| 		if ($thread->state == self::STATE_WAITING) { | ||||
| 			$thread->nextAgent = 0; | ||||
| 			$thread->save(); | ||||
| 		} | ||||
| 
 | ||||
| 		// Send message
 | ||||
| 		$thread->postMessage(self::KIND_EVENTS, getstring_("chat.status.user.reopenedthread", $thread->locale)); | ||||
| 		return $thread; | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Close all old threads that were not closed by some reasons | ||||
| 	 */ | ||||
| 	public static function closeOldThreads() { | ||||
| 		if (Settings::get('thread_lifetime') == 0) { | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		$db = Database::getInstance(); | ||||
| 
 | ||||
| 		$query = "update {chatthread} set lrevision = :next_revision, " . | ||||
| 			"dtmmodified = :now, istate = :state_closed " . | ||||
| 			"where istate <> :state_closed and istate <> :state_left " . | ||||
| 			"and ((lastpingagent <> 0 and lastpinguser <> 0 and " . | ||||
| 			"(ABS(:now - lastpinguser) > :thread_lifetime and " . | ||||
| 			"ABS(:now - lastpingagent) > :thread_lifetime)) or " . | ||||
| 			"(lastpingagent = 0 and lastpinguser <> 0 and " . | ||||
| 			"ABS(:now - lastpinguser) > :thread_lifetime))"; | ||||
| 
 | ||||
| 		$db->query( | ||||
| 			$query, | ||||
| 			array( | ||||
| 				':next_revision' => self::nextRevision(), | ||||
| 				':now' => time(), | ||||
| 				':state_closed' => self::STATE_CLOSED, | ||||
| 				':state_left' => self::STATE_LEFT, | ||||
| 				':thread_lifetime' => Settings::get('thread_lifetime') | ||||
| 			) | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Return next revision number (last revision number plus one) | ||||
| 	 * | ||||
| 	 * @return int revision number | ||||
| 	 */ | ||||
| 	protected static function nextRevision() { | ||||
| 		$db = Database::getInstance(); | ||||
| 		$db->query("update {chatrevision} set id=LAST_INSERT_ID(id+1)"); | ||||
| 		$val = $db->insertedId(); | ||||
| 		return $val; | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Implementation of the magic __get method | ||||
| 	 * | ||||
| 	 * Check if variable with name $name exists in the Thread::$propertyMap array. | ||||
| 	 * If it does not exist triggers an error with E_USER_NOTICE level and returns false. | ||||
| 	 * | ||||
| 	 * @param string $name property name | ||||
| 	 * @return mixed | ||||
| 	 * @see Thread::$propertyMap | ||||
| 	 */ | ||||
| 	public function __get($name) { | ||||
| 		// Check property existance
 | ||||
| 		if (! array_key_exists($name, $this->propertyMap)) { | ||||
| 			trigger_error("Undefined property '{$name}'", E_USER_NOTICE); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 
 | ||||
| 		$field_name = $this->propertyMap[$name]; | ||||
| 		return $this->threadInfo[$field_name]; | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Implementation of the magic __set method | ||||
| 	 * | ||||
| 	 * Check if variable with name $name exists in the Thread::$propertyMap array before setting. | ||||
| 	 * If it does not exist triggers an error with E_USER_NOTICE level and value will NOT set. | ||||
| 	 * | ||||
| 	 * @param string $name Property name | ||||
| 	 * @param mixed $value Property value | ||||
| 	 * @return mixed | ||||
| 	 * @see Thread::$propertyMap | ||||
| 	 */ | ||||
| 	public function __set($name, $value) { | ||||
| 		if (empty($this->propertyMap[$name])) { | ||||
| 			trigger_error("Undefined property '{$name}'", E_USER_NOTICE); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		$field_name = $this->propertyMap[$name]; | ||||
| 		$this->threadInfo[$field_name] = $value; | ||||
| 
 | ||||
| 		if (! in_array($field_name, $this->updatedFields)) { | ||||
| 			$this->updatedFields[] = $field_name; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Implementation of the magic __isset method | ||||
| 	 * | ||||
| 	 * Check if variable with $name exists. | ||||
| 	 * | ||||
| 	 * param string $name Variable name | ||||
| 	 * return boolean True if variable exists and false otherwise | ||||
| 	 */ | ||||
| 	public function __isset($name) { | ||||
| 		if (!array_key_exists($name, $this->propertyMap)) { | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		$property_name = $this->propertyMap[$name]; | ||||
| 		return isset($this->threadInfo[$property_name]); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Remove thread from database | ||||
| 	 */ | ||||
| 	public function delete() { | ||||
| 		$db = Database::getInstance(); | ||||
| 		$db->query( | ||||
| 			"DELETE FROM {chatthread} WHERE threadid = :id LIMIT 1", | ||||
| 			array(':id' => $this->id) | ||||
| 		); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * 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) { | ||||
| 		// 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) > self::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 = getstring_("chat.status.operator.dead", $this->locale); | ||||
| 					$this->postMessage( | ||||
| 						self::KIND_CONN, | ||||
| 						$message_to_post, | ||||
| 						null, | ||||
| 						null, | ||||
| 						$last_ping_other_side + self::CONNECTION_TIMEOUT | ||||
| 					); | ||||
| 
 | ||||
| 					// And update thread
 | ||||
| 					$this->state = self::STATE_WAITING; | ||||
| 					$this->nextAgent = 0; | ||||
| 				} | ||||
| 			} else { | ||||
| 				// _Other_ side is user
 | ||||
| 				// Update user's last ping time
 | ||||
| 				$this->lastPingUser = 0; | ||||
| 
 | ||||
| 				// And send a message to operator
 | ||||
| 				$message_to_post = getstring_("chat.status.user.dead", $this->locale); | ||||
| 				$this->postMessage( | ||||
| 					self::KIND_FOR_AGENT, | ||||
| 					$message_to_post, | ||||
| 					null, | ||||
| 					null, | ||||
| 					$last_ping_other_side + self::CONNECTION_TIMEOUT | ||||
| 				); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		$this->save(); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Save the thread to the database | ||||
| 	 */ | ||||
| 	public function save(){ | ||||
| 		$db = Database::getInstance(); | ||||
| 
 | ||||
| 		$query = "update {chatthread} t " . | ||||
| 			"set lrevision = ?, dtmmodified = ?"; | ||||
| 
 | ||||
| 		$values = array(); | ||||
| 		$values[] = $this->nextRevision(); | ||||
| 		$values[] = time(); | ||||
| 
 | ||||
| 		foreach ($this->updatedFields as $field_name) { | ||||
| 			$query .= ", {$field_name} = ?" ; | ||||
| 			$values[] = $this->threadInfo[$field_name]; | ||||
| 		} | ||||
| 
 | ||||
| 		$query .= " where threadid = ?"; | ||||
| 		$values[] = $this->id; | ||||
| 		$db->query($query, $values); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Check if thread is reassigned for another operator | ||||
| 	 * | ||||
| 	 * Updates thread info, send events messages and avatar message to user | ||||
| 	 * @global string $home_locale | ||||
| 	 * @param array $operator Operator for test | ||||
| 	 */ | ||||
| 	public function checkForReassign($operator) { | ||||
| 		global $home_locale; | ||||
| 
 | ||||
| 		$operator_name = ($this->locale == $home_locale) ? $operator['vclocalename'] : $operator['vccommonname']; | ||||
| 
 | ||||
| 		if ($this->state == self::STATE_WAITING && | ||||
| 			($this->nextAgent == $operator['operatorid'] || $this->agentId == $operator['operatorid'])) { | ||||
| 
 | ||||
| 			// Prepare message
 | ||||
| 			if ($this->nextAgent == $operator['operatorid']) { | ||||
| 				$message_to_post = getstring2_( | ||||
| 					"chat.status.operator.changed", | ||||
| 					array($operator_name, $this->agentName), | ||||
| 					$this->locale | ||||
| 				); | ||||
| 			} else { | ||||
| 				$message_to_post = getstring2_("chat.status.operator.returned", array($operator_name), $this->locale); | ||||
| 			} | ||||
| 
 | ||||
| 			// Update thread info
 | ||||
| 			$this->state = self::STATE_CHATTING; | ||||
| 			$this->nextAgent = 0; | ||||
| 			$this->agentId = $operator['operatorid']; | ||||
| 			$this->agentName = $operator_name; | ||||
| 			$this->save(); | ||||
| 
 | ||||
| 			// Send messages
 | ||||
| 			$this->postMessage(self::KIND_EVENTS, $message_to_post); | ||||
| 			$this->postMessage(self::KIND_AVATAR, $operator['vcavatar'] ? $operator['vcavatar'] : ""); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Load messages from database corresponding to the thread those ID's more than $lastid | ||||
| 	 * | ||||
| 	 * @param boolean $is_user Boolean TRUE if messages loads for user and boolean FALSE if they loads for operator. | ||||
| 	 * @param int $lastid ID of the last loaded message. | ||||
| 	 * @return array Array of messages | ||||
| 	 * @see Thread::postMessage() | ||||
| 	 */ | ||||
| 	public function getMessages($is_user, &$last_id) { | ||||
| 
 | ||||
| 		$db = Database::getInstance(); | ||||
| 
 | ||||
| 		// Load messages
 | ||||
| 		$messages = $db->query( | ||||
| 			"select messageid,ikind,dtmcreated as created,tname,tmessage from {chatmessage} " . | ||||
| 			"where threadid = :threadid and messageid > :lastid " . | ||||
| 			($is_user ? "and ikind <> " . self::KIND_FOR_AGENT : "") . | ||||
| 			" order by messageid", | ||||
| 			array( | ||||
| 				':threadid' => $this->id, | ||||
| 				':lastid' => $last_id | ||||
| 			), | ||||
| 			array('return_rows' => Database::RETURN_ALL_ROWS) | ||||
| 		); | ||||
| 
 | ||||
| 		foreach ($messages as $msg) { | ||||
| 			// Get last message ID
 | ||||
| 			if ($msg['messageid'] > $last_id) { | ||||
| 				$last_id = $msg['messageid']; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		return $messages; | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Send the messsage | ||||
| 	 * | ||||
| 	 * @param int $kind Message kind. One of the Thread::KIND_* | ||||
| 	 * @param string $message Message body | ||||
| 	 * @param string|null $from Sender name | ||||
| 	 * @param int|null $opid operator id. Use NULL for system messages | ||||
| 	 * @param int|null $time unix timestamp of the send time. Use NULL for current time. | ||||
| 	 * @return int Message ID | ||||
| 	 * | ||||
| 	 * @see Thread::KIND_USER | ||||
| 	 * @see Thread::KIND_AGENT | ||||
| 	 * @see Thread::KIND_FOR_AGENT | ||||
| 	 * @see Thread::KIND_INFO | ||||
| 	 * @see Thread::KIND_CONN | ||||
| 	 * @see Thread::KIND_EVENTS | ||||
| 	 * @see Thread::KIND_AVATAR | ||||
| 	 * @see Thread::getMessages() | ||||
| 	 */ | ||||
| 	public function postMessage($kind, $message, $from = null, $opid = null, $time = null) { | ||||
| 		$db = Database::getInstance(); | ||||
| 
 | ||||
| 		$query = "INSERT INTO {chatmessage} " . | ||||
| 			"(threadid,ikind,tmessage,tname,agentId,dtmcreated) " . | ||||
| 			"VALUES (:threadid,:kind,:message,:name,:agentid,:created)"; | ||||
| 
 | ||||
| 		$values = array( | ||||
| 			':threadid' => $this->id, | ||||
| 			':kind' => $kind, | ||||
| 			':message' => $message, | ||||
| 			':name' => ($from ? $from : "null"), | ||||
| 			':agentid' => ($opid ? $opid : 0), | ||||
| 			':created' => ($time ? $time : time()) | ||||
| 		); | ||||
| 
 | ||||
| 		$db->query($query, $values); | ||||
| 		return $db->insertedId(); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Close thread and send closing messages to the conversation members | ||||
| 	 * | ||||
| 	 * @param boolean $is_user Boolean TRUE if user initiate thread closing or boolean FALSE otherwise | ||||
| 	 */ | ||||
| 	public function close($is_user) { | ||||
| 		$db = Database::getInstance(); | ||||
| 
 | ||||
| 		// Get messages count
 | ||||
| 		list($message_count) = $db->query( | ||||
| 			"SELECT COUNT(*) FROM {chatmessage} WHERE {chatmessage}.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->messageCount = $message_count; | ||||
| 			$this->save(); | ||||
| 		} | ||||
| 
 | ||||
| 		// Send message about closing
 | ||||
| 		$message = ''; | ||||
| 		if ($is_user) { | ||||
| 			$message = getstring2_("chat.status.user.left", array($this->userName), $this->locale); | ||||
| 		} else { | ||||
| 			$message = getstring2_("chat.status.operator.left", array($this->agentName), $this->locale); | ||||
| 		} | ||||
| 		$this->postMessage(self::KIND_EVENTS, $message); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Assign operator to thread | ||||
| 	 * | ||||
| 	 * @global string $home_locale | ||||
| 	 * @param array $operator Operator who try to take thread | ||||
| 	 * @return boolean Boolean TRUE on success or FALSE on failure | ||||
| 	 */ | ||||
| 	public function take($operator) { | ||||
| 		global $home_locale; | ||||
| 
 | ||||
| 		$take_thread = false; | ||||
| 		$message = ''; | ||||
| 		$operator_name = ($this->locale == $home_locale) ? $operator['vclocalename'] : $operator['vccommonname']; | ||||
| 
 | ||||
| 		if ($this->state == self::STATE_QUEUE || $this->state == self::STATE_WAITING || $this->state == self::STATE_LOADING) { | ||||
| 			// User waiting
 | ||||
| 			$take_thread = true; | ||||
| 			if ($this->state == self::STATE_WAITING) { | ||||
| 				if ($operator['operatorid'] != $this->agentId) { | ||||
| 					$message = getstring2_( | ||||
| 						"chat.status.operator.changed", | ||||
| 						array($operator_name, $this->agentName), | ||||
| 						$this->locale | ||||
| 					); | ||||
| 				} else { | ||||
| 					$message = getstring2_("chat.status.operator.returned", array($operator_name), $this->locale); | ||||
| 				} | ||||
| 			} else { | ||||
| 				$message = getstring2_("chat.status.operator.joined", array($operator_name), $this->locale); | ||||
| 			} | ||||
| 		} elseif ($this->state == self::STATE_CHATTING) { | ||||
| 			// User chatting
 | ||||
| 			if ($operator['operatorid'] != $this->agentId) { | ||||
| 				$take_thread = true; | ||||
| 				$message = getstring2_( | ||||
| 					"chat.status.operator.changed", | ||||
| 					array($operator_name, $this->agentName), | ||||
| 					$this->locale | ||||
| 				); | ||||
| 			} | ||||
| 		} else { | ||||
| 			// Thread closed
 | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		// Change operator and update chat info
 | ||||
| 		if ($take_thread) { | ||||
| 			$this->state = self::STATE_CHATTING; | ||||
| 			$this->nextAgent = 0; | ||||
| 			$this->agentId = $operator['operatorid']; | ||||
| 			$this->agentName = $operator_name; | ||||
| 			if (empty($this->chatStarted)) { | ||||
| 				$this->chatStarted = time(); | ||||
| 			} | ||||
| 			$this->save(); | ||||
| 		} | ||||
| 
 | ||||
| 		// Send message
 | ||||
| 		if ($message) { | ||||
| 			$this->postMessage(self::KIND_EVENTS, $message); | ||||
| 			$this->postMessage(self::KIND_AVATAR, $operator['vcavatar'] ? $operator['vcavatar'] : ""); | ||||
| 		} | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
| 	 * Change user name in the conversation | ||||
| 	 * | ||||
| 	 * @param string $new_name New user name | ||||
| 	 */ | ||||
| 	public function renameUser($new_name) { | ||||
| 		// Rename only if a new name is realy new
 | ||||
| 		if ($this->userName != $new_name) { | ||||
| 			// Rename user
 | ||||
| 			$this->userName = $new_name; | ||||
| 			$this->save(); | ||||
| 
 | ||||
| 			// Send message about renaming
 | ||||
| 			$message = getstring2_( | ||||
| 				"chat.status.user.changedname", | ||||
| 				array($this->userName, $new_name), | ||||
| 				$this->locale | ||||
| 			); | ||||
| 			$this->postMessage(self::KIND_EVENTS, $message); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| ?>
 | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user