init_defaults(); $this->init_classes(); $this->init_hooks(); } private function init_defaults() { $this->default = new stdClass(); $this->default->follow = [ 'followid' => 0, 'itemid' => 0, 'itemtype' => '', 'confirmkey' => '', 'userid' => 0, 'active' => 0, 'name' => '', 'email' => '', 'confirm_link' => '', 'unfollow_link' => '', ]; $this->default->follow_format = [ 'followid' => '%d', 'itemid' => '%d', 'itemtype' => '%s', 'confirmkey' => '%s', 'userid' => '%d', 'active' => '%d', 'name' => '%s', 'email' => '%s', ]; $this->default->sql_select_args = [ 'followid' => null, 'itemid' => null, 'itemtype' => null, 'itemtype_include' => [], 'itemtype_exclude' => [], 'confirmkey' => null, 'userid' => null, 'active' => null, 'name' => null, 'email' => null, 'orderby' => null, 'offset' => null, 'row_count' => null, ]; } private function init_classes() { $this->Template = new Template(); $this->Actions = new Actions(); } private function init_hooks() { add_filter( 'wpforo_init_member_templates', [ $this, 'add_templates' ] ); add_filter( 'wpforo_after_init_current_object', [ $this, 'init_current_object' ] ); } public function add_templates( $templates ) { $templates['followers'] = [ 'type' => 'callback', 'key' => 'followers', 'menu_shortcode' => 'wpforo-profile-followers', 'ico' => '', 'title' => 'Followers', 'is_default' => 1, 'can' => 'vprs', 'add_in_member_menu' => 0, 'add_in_member_buttons' => 0, 'callback_for_page' => function() { require wpftpl( 'profile-followers.php' ); }, ]; $templates['following'] = [ 'type' => 'callback', 'key' => 'following', 'menu_shortcode' => 'wpforo-profile-following', 'ico' => '', 'title' => 'Following', 'is_default' => 1, 'can' => 'vprs', 'add_in_member_menu' => 0, 'add_in_member_buttons' => 0, 'callback_for_page' => function() { require wpftpl( 'profile-following.php' ); }, ]; return $templates; } public function init_current_object( $current_object ) { if( ! $current_object['is_404'] ) { if( $current_object['template'] === 'followers' ) { $args = [ 'offset' => ( $current_object['paged'] - 1 ) * $current_object['items_per_page'], 'row_count' => $current_object['items_per_page'], 'itemid' => $current_object['userid'], 'itemtype' => 'user', 'order' => 'DESC', ]; $current_object['items_count'] = 0; $current_object['follows'] = $this->get_follows( $args, $current_object['items_count'] ); } elseif( $current_object['template'] === 'following' ) { $args = [ 'offset' => ( $current_object['paged'] - 1 ) * $current_object['items_per_page'], 'row_count' => $current_object['items_per_page'], 'userid' => $current_object['userid'], 'itemtype' => 'user', 'order' => 'DESC', ]; $current_object['items_count'] = 0; $current_object['follows'] = $this->get_follows( $args, $current_object['items_count'] ); } } return $current_object; } private function get_confirm_key() { return substr( md5( rand() . time() ), 0, 32 ); } /** * @param $follow * * @return array */ public function decode( $follow ) { $follow = array_merge( $this->default->follow, (array) $follow ); $follow['followid'] = wpforo_bigintval( $follow['followid'] ); $follow['itemid'] = wpforo_bigintval( $follow['itemid'] ); $follow['userid'] = wpforo_bigintval( $follow['userid'] ); $follow['active'] = (bool) intval( $follow['active'] ); $follow['name'] = trim( strip_tags( (string) $follow['name'] ) ); $follow['email'] = sanitize_email( $follow['email'] ); if( ! ( $follow['itemtype'] = trim( strip_tags( (string) $follow['itemtype'] ) ) ) ) $follow['itemtype'] = 'user'; if( ! ( $follow['confirmkey'] = trim( (string) $follow['confirmkey'] ) ) ) $follow['confirmkey'] = $this->get_confirm_key(); $follow['confirm_link'] = $this->get_confirm_link( $follow['confirmkey'] ); $follow['unfollow_link'] = $this->get_unfollow_link( $follow['confirmkey'] ); return $follow; } /** * @param $follow * * @return array */ private function encode( $follow ) { $follow = $this->decode( $follow ); $follow['active'] = intval( $follow['active'] ); return $follow; } /** * @param $follow * * @return false|int */ public function add( $follow ) { $follow = $this->encode( $follow ); unset( $follow['followid'] ); $follow = wpforo_array_ordered_intersect_key( $follow, $this->default->follow_format ); if( WPF()->db->insert( WPF()->tables->follows, $follow, wpforo_array_ordered_intersect_key( $this->default->follow_format, $follow ) ) ) { $follow['followid'] = WPF()->db->insert_id; do_action( 'wpforo_after_add_follow', $follow ); return $follow['followid']; } return false; } /** * @param array $fields * @param array|int|string $where * * @return bool */ public function edit( $fields, $where ) { if( is_numeric( $where ) ) { $where = [ 'followid' => wpforo_bigintval( $where ) ]; } elseif( is_string( $where ) ) { $where = [ 'confirmkey' => $where ]; } $fields = wpforo_array_ordered_intersect_key( $fields, $this->default->follow_format ); if( false !== WPF()->db->update( WPF()->tables->follows, $fields = wpforo_array_ordered_intersect_key( $this->encode( $fields ), $fields ), $where = wpforo_array_ordered_intersect_key( $where, $this->default->follow_format ), wpforo_array_ordered_intersect_key( $this->default->follow_format, $fields ), wpforo_array_ordered_intersect_key( $this->default->follow_format, $where ) ) ) { do_action( 'wpforo_after_edit_follow', $fields, $where ); return true; } return false; } /** * @param array|int|string $args * @param string $operator * * @return bool */ public function delete( $args, string $operator = 'AND' ): bool { if( is_numeric( $args ) ) { $args = [ 'followid' => wpforo_bigintval( $args ) ]; } elseif( is_string( $args ) ) { $args = [ 'confirmkey' => $args ]; } $operator = trim( strtoupper( (string) $operator ) ); if( ! in_array( $operator, [ 'AND', 'OR' ], true ) ) $operator = 'AND'; do_action( 'wpforo_before_delete_follow', $args, $operator ); $sql = "DELETE FROM " . WPF()->tables->follows; if( $wheres = $this->build_sql_wheres( $args ) ) $sql .= " WHERE " . implode( " $operator ", $wheres ); $args = $this->parse_args( $args ); if( $args['orderby'] ) $sql .= " ORDER BY " . $args['orderby']; if( $args['row_count'] ) $sql .= " LIMIT " . intval( $args['row_count'] ); $r = WPF()->db->query( $sql ); do_action( 'wpforo_after_delete_follow', $args, $operator ); return false !== $r; } private function parse_args( $args ) { $args = wpforo_parse_args( $args, $this->default->sql_select_args ); $args = wpforo_array_ordered_intersect_key( $args, $this->default->sql_select_args ); $args['itemtype_include'] = wpforo_parse_args( $args['itemtype_include'] ); $args['itemtype_exclude'] = wpforo_parse_args( $args['itemtype_exclude'] ); $args['orderby'] = sanitize_sql_orderby( (string) $args['orderby'] ); return $args; } private function build_sql_wheres( $args ) { $args = $this->parse_args( $args ); $wheres = []; if( ! is_null( $args['followid'] ) ) $wheres[] = "`followid` = '" . wpforo_bigintval( $args['followid'] ) . "'"; if( ! is_null( $args['itemid'] ) ) $wheres[] = "`itemid` = '" . wpforo_bigintval( $args['itemid'] ) . "'"; if( ! is_null( $args['itemtype'] ) ) $wheres[] = "`itemtype` = '" . esc_sql( $args['itemtype'] ) . "'"; if( ! is_null( $args['confirmkey'] ) ) $wheres[] = "`confirmkey` = '" . esc_sql( $args['confirmkey'] ) . "'"; if( ! is_null( $args['userid'] ) ) $wheres[] = "`userid` = '" . wpforo_bigintval( $args['userid'] ) . "'"; if( ! is_null( $args['active'] ) ) $wheres[] = "`active` = '" . intval( $args['active'] ) . "'"; if( ! is_null( $args['name'] ) ) $wheres[] = "`name` = '" . esc_sql( $args['name'] ) . "'"; if( ! is_null( $args['email'] ) ) $wheres[] = "`email` = '" . esc_sql( $args['email'] ) . "'"; if( ! empty( $args['itemtype_include'] ) ) $wheres[] = "`itemtype` IN('" . implode( "','", array_map( 'trim', $args['itemtype_include'] ) ) . "')"; if( ! empty( $args['itemtype_exclude'] ) ) $wheres[] = "`itemtype` NOT IN(" . implode( "','", array_map( 'trim', $args['itemtype_exclude'] ) ) . "')"; return $wheres; } /** * @param $args * @param $select * @param $operator * * @return string */ private function build_sql_select( $args, $select = '', $operator = 'AND' ) { if( ! $select ) $select = '*'; $operator = trim( strtoupper( (string) $operator ) ); if( ! in_array( $operator, [ 'AND', 'OR' ], true ) ) $operator = 'AND'; $sql = "SELECT $select FROM " . WPF()->tables->follows; if( $wheres = $this->build_sql_wheres( $args ) ) $sql .= " WHERE " . implode( " $operator ", $wheres ); $args = $this->parse_args( $args ); if( $args['orderby'] ) $sql .= " ORDER BY " . $args['orderby']; if( $args['row_count'] ) $sql .= " LIMIT " . intval( $args['offset'] ) . "," . intval( $args['row_count'] ); return $sql; } /** * @param array|numeric $args * * @return array */ public function _get_follow( $args, $operator = 'AND' ) { if( is_numeric( $args ) ) $args = [ 'followid' => wpforo_bigintval( $args ) ]; if( ! wpfkey( $args, 'orderby' ) ) $args['orderby'] = '`followid` DESC'; $follow = (array) WPF()->db->get_row( $this->build_sql_select( $args, '', $operator ), ARRAY_A ); if( $follow ) $follow = $this->decode( $follow ); return $follow; } public function get_follow( $args, $operator = 'AND' ) { return wpforo_ram_get( [ $this, '_get_follow' ], $args, $operator ); } /** * @param array $args * * @return array */ public function _get_follows( $args = [], $operator = 'AND' ) { return array_map( [ $this, 'decode' ], (array) WPF()->db->get_results( $this->build_sql_select( $args, '', $operator ), ARRAY_A ) ); } public function get_follows( $args = [], $operator = 'AND' ) { return wpforo_ram_get( [ $this, '_get_follows' ], $args, $operator ); } public function _get_follows_col( $col, $args = [], $operator = 'AND' ) { $r = WPF()->db->get_col( $this->build_sql_select( $args, "`$col`", $operator ) ); if( $this->default->follow_format[ $col ] === '%d' ) $r = array_map( 'wpforo_bigintval', $r ); return $r; } public function get_follows_col( $col, $args = [], $operator = 'AND' ) { return wpforo_ram_get( [ $this, '_get_follows_col' ], $col, $args, $operator ); } /** * @param array $args * * @return int */ public function _get_count( $args = [], $operator = 'AND' ) { return (int) WPF()->db->get_var( $this->build_sql_select( $args, 'COUNT(*)', $operator ) ); } public function get_count( $args = [], $operator = 'AND' ) { return wpforo_ram_get( [ $this, '_get_count' ], $args, $operator ); } public function user_is_following_this( $userid, $itemid, $itemtype = 'user' ) { return (bool) $this->get_follow( [ 'userid' => $userid, 'itemid' => $itemid, 'itemtype' => $itemtype, 'active' => true ] ); } private function get_confirm_link( $confirmkey ) { return wpforo_url( "?wpfaction=followconfirm&key=$confirmkey", 'member' ); } private function get_unfollow_link( $confirmkey ) { return wpforo_url( "?wpfaction=unfollow&key=$confirmkey", 'member' ); } /** * @param int $userid * @param int $itemid * @param string $itemtype * * @return bool|int */ public function follow( $userid, $itemid, $itemtype = 'user' ) { if( ! $this->user_is_following_this( $userid, $itemid, $itemtype ) ) { return $this->add( [ 'itemid' => $itemid, 'itemtype' => $itemtype, 'userid' => $userid, 'active' => true, ] ); } return true; } /** * @param int $userid * @param int $itemid * @param string $itemtype * * @return bool */ public function unfollow( $userid, $itemid, string $itemtype = 'user' ): bool { return $this->delete( [ 'userid' => $userid, 'itemid' => $itemid, 'itemtype' => $itemtype ] ); } /** * @param string $confirmkey * * @return bool */ public function unfollow_by_key( string $confirmkey ): bool { return $this->delete( $confirmkey ); } public function confirm( $confirmkey ) { return $this->edit( [ 'active' => true ], $confirmkey ); } }