File: /var/www/gosurya-id/wp-content/themes/my-listing/includes/extensions/messages/reply.php
<?php
namespace MyListing\Ext\Messages;
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
use \MyListing\Src\User;
class Reply {
use \MyListing\Src\Traits\Instantiatable;
/**
* Sender and receivers lists
* @var array
*/
static $users_list = [];
/**
* Message sender information
* @var object
*/
protected $sender = [];
/**
* Message receiver information
* @var object
*/
protected $receiver = [];
/**
* Current user data
* @var object
*/
protected $current_user = [];
/**
* Current user id
* @var integer
*/
protected $current_user_id = 0;
/**
* Weather the current user is sender or receiver
* @var boolen|string
*/
protected $current_user_role = false;
/**
* Current message data
* @var object
*/
private $_message = [];
/**
* Message table name
* @var string
*/
private $_message_table = '';
/**
* Constructor to bind call events
*/
public function __construct( $message ) {
if ( ! is_object( $message ) && ! intval( $message ) ) {
throw new \Exception(
esc_html__('Invalid message', 'my-listing')
);
}
$this->_message_table = Reply::get_table_name();
$this->_message = $message;
// Fetch message data
$this->_fetch();
// Fetch current user information
$this->current_user_id = get_current_user_id();
// Set current user role
$this->_set_current_user_role();
$this->current_user = $this->_normalize_user_data( $this->current_user_id );
// Normalize Users data
$this->sender = $this->_normalize_user_data( $this->_message->sender_id );
$this->receiver = $this->_normalize_user_data( $this->_message->receiver_id );
return $this;
}
public function __get( $name ) {
switch( $name ) {
case 'sender_id' :
return $this->_message->sender_id;
break;
case 'receiver_id' :
return $this->_message->receiver_id;
break;
default :
if ( isset( $this->_message->{$name} ) ) {
return $this->_message->{$name};
}
throw new \Exception(
sprintf(
esc_html__('The property %s is not accessible.', 'my-listing')
)
);
break;
}
}
public function set_sender_data() {
$sender_user = get_user_by( 'id');
}
protected function _delete() {
global $wpdb;
// Authorization Test
if ( ! $this->current_user_role ) {
throw new Exception(
esc_html__('You are not authorized to delete this message.', 'my-listing')
);
}
// Do nothing if already deleted for this user
$delete_column = $this->current_user_role . '_delete_status';
if ( $this->{ $delete_column } ) {
return true;
}
// Delete message permanently if deleted for both users
$toggle_user_role = ( 'sender' == $this->current_user_role ) ? 'receiver' : 'sender';
$deleted_for_all = $this->{$toggle_user_role . '_delete_status'};
if ( $deleted_for_all ) {
return $wpdb->delete( $this->_message_table, ['message_id' => $this->message_id], ['%d'] );
}
return $wpdb->update( $this->_message_table, [ $delete_column => '1' ], [ 'message_id' => $this->message_id ] );
}
private function _fetch() {
global $wpdb;
if ( is_object( $this->_message ) ) {
return true;
}
$this->_message = $wpdb->get_row(
$wpdb->prepare(
"SELECT message_id, sender_id, sender_data, sender_delete_status,
receiver_id, receiver_data, receiver_delete_status, seen,
send_time, CONVERT_TZ( send_time, @@session.time_zone, '+00:00' ) AS `utc_datetime`, message
FROM {$this->_message_table} WHERE message_id = %d",
$this->_message
)
);
if ( ! $this->_message ) {
throw new \Exception(
esc_html__('The message is no longer available.', 'my-listing')
);
}
}
private function _normalize_user_data( $user_id ) {
// Fetch user only if it's not available in cache
if ( ! isset( Reply::$users_list[ $user_id ] ) ) {
$user = get_user_by( 'id', $user_id );
if ( ! $user ) {
$user_role = 'sender';
if ( $this->_message->receiver_id === $user_id ) {
$user_role = 'receiver';
}
$user = (object) apply_filters('mylisting/messages/deleted_user',
wp_parse_args(
maybe_unserialize( $this->_message->{$user_role . '_data'} ),
[
'ID' => $user_id,
'display_name' => esc_html__('USER DELETED', 'my-listing'),
'user_login' => false,
'email' => false,
]
)
);
}
Reply::$users_list[ $user_id ] = $user;
}
return Reply::$users_list[ $user_id ];
}
private function _set_current_user_role() {
if ( $this->sender_id != $this->current_user_id && $this->receiver_id != $this->current_user_id ) {
return false;
}
$this->current_user_role = ( $this->sender_id == $this->current_user_id ) ? 'sender' : 'receiver';
}
public static function delete_message( $message_id ) {
return ( new Reply( $message_id ) )->_delete();
}
public static function delete_conversation( $opponent_id, $listing_id, $current_user_id = 0 ) {
global $wpdb;
if ( ! $current_user_id ) {
$current_user_id = get_current_user_id();
}
$table_name = Reply::get_table_name();
$wpdb->query(
$wpdb->prepare(
"UPDATE {$table_name} SET
receiver_delete_status = CASE WHEN receiver_id = %d THEN 1 ELSE receiver_delete_status END,
sender_delete_status = CASE WHEN sender_id = %d THEN 1 ELSE sender_delete_status END
WHERE ( ( receiver_id = %d AND sender_id = %d ) OR ( sender_id = %d AND receiver_id = %d ) ) AND listing_id = %d
",
$current_user_id,
$current_user_id,
$current_user_id,
$opponent_id,
$current_user_id,
$opponent_id,
$listing_id
)
);
// DELETE GARBAGE
return $wpdb->query(
"DELETE FROM {$table_name} WHERE sender_delete_status = 1 AND receiver_delete_status = 1"
);
}
public static function unread_messages( $user_id, $time = '' ) {
global $wpdb;
$table_name = Reply::get_table_name();
$messages = $wpdb->get_results(
$wpdb->prepare(
"SELECT message_id, sender_id, sender_data, sender_delete_status,
receiver_id, receiver_data, receiver_delete_status, seen,
send_time, CONVERT_TZ( send_time, @@session.time_zone, '+00:00' ) AS `utc_datetime`, message
FROM {$table_name} WHERE receiver_id = %d AND seen = 0
ORDER BY seen ASC, send_time DESC",
$user_id
)
);
return Reply::_normalize_messages( $messages );
}
public static function read_messages( $user_id, $start_time = null, $user_start = 0, $user_end = 10 ) {
global $wpdb;
$table_name = Reply::get_table_name();
$time_query = '';
if ( $start_time ) {
$time_query .= " AND send_time > %s";
}
$query_args = [
"SELECT SQL_CALC_FOUND_ROWS a.message_id, a.sender_id, a.sender_data, a.sender_delete_status,
a.receiver_id, a.receiver_data, a.receiver_delete_status, a.listing_id, a.seen,
a.send_time, CONVERT_TZ( send_time, @@session.time_zone, '+00:00' ) AS `utc_datetime`, a.message
FROM {$table_name} a
INNER JOIN (
SELECT MAX( message_id ) as message_id
FROM {$table_name}
WHERE ( sender_id = %d OR receiver_id = %d ) AND
CASE
WHEN receiver_id = %d THEN receiver_delete_status=0
WHEN sender_id = %d THEN sender_delete_status=0
ELSE TRUE
END {$time_query}
GROUP BY listing_id, LEAST( sender_id, receiver_id ), GREATEST( sender_id, receiver_id )
) b on a.message_id = b.message_id
ORDER BY seen ASC, send_time DESC
LIMIT %d, %d",
$user_id,
$user_id,
$user_id,
$user_id
];
if ( $time_query ) {
$query_args[] = $start_time;
}
$query_args[] = $user_start;
$query_args[] = $user_end;
$messages = $wpdb->get_results(
call_user_func_array([$wpdb, 'prepare'], $query_args)
);
// @TODO: multiple ajax requests to load more messages - if
// in the worst case the messages are more than 500
$found_rows = $wpdb->get_col("SELECT FOUND_ROWS()", 0);
$messages_list = Reply::_normalize_messages( $messages );
$messages_list['fr'] = $found_rows[0];
return $messages_list;
}
public static function read_conversation( $args ) {
global $wpdb;
// Normalize Query data
$args = apply_filters('mylisting/messages/read_conversation', wp_parse_args( $args, [
'opponent_id' => 0,
'listing_id' => 0,
'current_user_id' => get_current_user_id(),
'offset' => 0,
'onload_limit' => \MyListing\Ext\Messages\Messages::instance()->onload_message_limit,
'onscroll_limit' => \MyListing\Ext\Messages\Messages::instance()->onscroll_message_limit
]) );
$load_message_limit = $args['onload_limit'];
if ( intval( $args['offset'] ) ) {
$offset = absint( $args['offset'] );
$load_message_limit = $args['onscroll_limit'];
}
$table_name = Reply::get_table_name();
$messages = $wpdb->get_results(
$wpdb->prepare(
"SELECT message_id, sender_id, sender_data, sender_delete_status,
receiver_id, receiver_data, receiver_delete_status, listing_id, seen,
send_time, CONVERT_TZ( send_time, @@session.time_zone, '+00:00' ) AS `utc_datetime`, message
FROM {$table_name} WHERE
listing_id = %d AND
(
( sender_id = %d AND receiver_id = %d AND sender_delete_status = 0 )
OR ( receiver_id = %d AND sender_id = %d AND receiver_delete_status = 0 )
)
ORDER BY message_id DESC
LIMIT %d, %d",
$args['listing_id'],
$args['current_user_id'],
$args['opponent_id'],
$args['current_user_id'],
$args['opponent_id'],
$args['offset'],
$load_message_limit
)
);
return Reply::_normalize_messages( $messages );
}
public static function mark_all_seen( $user_id = 0, $listing_id = 0 ) {
global $wpdb;
if ( ! $user_id ) {
$user_id = get_current_user_id();
}
$table_name = Reply::get_table_name();
return $wpdb->update(
$table_name,
['seen' => 1],
['receiver_id' => $user_id, 'listing_id' => $listing_id],
['%d'],
['%d']
);
}
public static function mark_as_seen( $opponent_id, $listing_id, $user_id = 0 ) {
global $wpdb;
if ( ! $user_id ) {
$user_id = get_current_user_id();
}
$table_name = Reply::get_table_name();
return $wpdb->update(
$table_name,
[ 'seen' => 1 ],
[ 'sender_id' => $opponent_id, 'receiver_id' => $user_id, 'listing_id' => $listing_id ],
['%d', '%d'],
['%d']
);
}
public static function block_user( $opponent_id, $user_id = null ) {
if ( ! $user_id ) {
$user_id = get_current_user_id();
}
$opponent = get_user_by( 'ID', $opponent_id );
if ( ! $opponent ) {
throw new Exception(
esc_html__('Error Processing Request', 'my-listing')
);
}
$user_block_list = Reply::_get_block_list( $user_id );
// Do not add if the user already exists in block list
if ( ! in_array( $opponent->ID, $user_block_list ) ) {
$user_block_list[] = $opponent->ID;
Reply::_update_block_list( $user_id, $user_block_list );
}
}
public static function unblock_user( $opponent_id, $user_id = null ) {
$opponent = get_user_by( 'ID', $opponent_id );
if ( ! $opponent ) {
throw new Exception(
esc_html__('Error Processing Request', 'my-listing')
);
}
if ( ! $user_id ) {
$user_id = get_current_user_id();
}
$user_block_list = Reply::_get_block_list( $user_id );
// Do it only if the user exists
if ( in_array( $opponent->ID, $user_block_list ) ) {
$block_index = array_search( $opponent->ID, $user_block_list );
unset( $user_block_list[ $block_index ] );
Reply::_update_block_list( $user_id, $user_block_list );
}
return true;
}
public static function get_table_name() {
return \MyListing\Ext\Messages\Messages::instance()->get_table_name();
}
private static function _get_block_list( $user_id ) {
$user_block_list = get_user_meta( $user_id, '_ml_user_block_list', true );
if ( ! $user_block_list || ! is_array( $user_block_list ) ) {
$user_block_list = [];
}
return $user_block_list;
}
private static function _update_block_list( $user_id, $block_list ) {
if ( ! add_user_meta( $user_id, '_ml_user_block_list', $block_list, true ) ) {
update_user_meta( $user_id, '_ml_user_block_list', $block_list );
}
}
private static function _normalize_messages( $messages ) {
// All messages are within a time-frame so take the first and
// last message time for this range
$messages_list = [
'st' => 0, // start time
'et' => 0, // end time
'ml' => [] // messages list
];
$block_nonce_keys = [];
$posts_data = [];
foreach ( $messages as $index => $message ) {
$this_message = new Reply( $message );
$opponent = 'receiver' === $this_message->current_user_role
? $this_message->sender
: $this_message->receiver;
if ( ! isset( $posts_data[ $this_message->listing_id ] ) ) {
$posts_data[ $this_message->listing_id ] = Reply::_message_post_data( $this_message->listing_id);
}
// Sender block list
$sender_block_list = Reply::_get_block_list( $this_message->current_user->ID );
$sender = new User( $this_message->sender_id );
$messages_list['ml'][ $this_message->message_id ] = [
'id' => $this_message->message_id,
'pid' => $this_message->listing_id,
'pdata' => $posts_data[ $this_message->listing_id ],
// Opponent User Info
'op' => [
'id' => $opponent->ID,
'login' => $opponent->user_login,
'name' => wp_specialchars_decode( $opponent->display_name ),
'avatar' => get_avatar( $opponent->ID ) ?: '',
'uri' => esc_url( ( new User( $opponent->ID ) )->get_link() ),
'blocked'=> in_array( $opponent->ID, $sender_block_list ),
'seckey' => isset( $block_nonce_keys[ $opponent->ID ] )
? $block_nonce_keys[ $opponent->ID ]
: wp_create_nonce("block-user-{$opponent->ID}")
],
'sender' => $this_message->sender_id,
'sender_name'=> wp_specialchars_decode( $sender->display_name ),
'sender_uri' => esc_url( $sender->get_link() ),
'seen' => $this_message->seen,
'utime' => strtotime($this_message->utc_datetime),
// 'utime' => strtotime($this_message->send_time), // Message time
//'time' => $this_message->send_time,
'message' => wp_kses( stripslashes( $this_message->message ), ['br', 'a'] ),
// Nonce
'dm' => wp_create_nonce("delete-message-{$this_message->message_id}"), // Delete Message
'dcn' => wp_create_nonce("delete-conversation-{$opponent->ID}"), // Delete Conversation
];
}
if ( $messages ) {
$messages_list['st'] = strtotime( $messages[0]->send_time );
// Last message time - same if there is only one message
end( $messages );
$messages_list['et'] = strtotime( current( $messages )->send_time );
}
return $messages_list;
}
private static function _message_post_data( $post_id ) {
if ( ! $post_id ) {
return [];
}
$post_data = [
'id' => 0,
'image' => '',
'title' => '',
'link' => '',
'author'=> 0
];
$listing = \MyListing\Src\Listing::get( $post_id );
if ( $listing ) {
$post_data = [
'id' => $listing->get_id(),
'image' => $listing->get_logo() ? : c27()->image( 'marker.jpg' ),
'title' => $listing->get_name(),
'link' => $listing->get_link(),
'author'=> $listing->get_author_id()
];
}
return $post_data;
}
}