PluginProbe
wpForo Forum / 2.4.2
wpForo Forum v2.4.2
3.1.5 3.1.4 3.1.2 3.1.1 3.1.0 3.0.9 3.0.8 3.0.7 trunk 1.0.0 1.0.1 1.0.2 1.1.0 1.1.1 1.1.2 1.2.0 1.3.0 1.3.1 1.4.0 1.4.1 1.4.10 1.4.11 1.4.12 1.4.13 1.4.2 All 137 releases
wpforo / classes / Members.php

Members.php in wpForo Forum 2.4.2, at classes/Members.php

3,855 lines 134.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace wpforo\classes;
4
5 use stdClass;
6 use WP_Error;
7 use wpforo\admin\listtables\Members as MembersListTable;
8 use wpforo\modules\reactions\Reactions;
9
10 // Exit if accessed directly
11 if( ! defined( 'ABSPATH' ) ) exit;
12
13
14 class Members {
15 private $default;
16 private $fields;
17 private $countries;
18 private $timezones;
19 public $login_min_length;
20 public $login_max_length;
21 public $pass_min_length;
22 public $pass_max_length;
23 public $list_table;
24 private $touched_userids = [];
25
26 function __construct() {
27 $this->init_defaults();
28 $this->init_hooks();
29 }
30
31 private function init_defaults() {
32 $this->login_min_length = 3;
33 $this->login_max_length = 30;
34 $this->pass_min_length = 6;
35 $this->pass_max_length = 20;
36 $this->default = new stdClass();
37 $this->default->member = [
38 'user_login' => '',
39 'user_nicename' => '',
40 'user_email' => '',
41 'user_url' => '',
42 'user_registered' => '0000-00-00 00:00:00',
43 'user_activation_key' => '',
44 'display_name' => '',
45
46 'first_name' => '',
47 'last_name' => '',
48
49 'userid' => 0,
50 'title' => '',
51 'groupid' => 0,
52 'secondary_groupids' => [],
53 'groupids' => [],
54 'avatar' => '',
55 'cover' => '',
56
57 'posts' => 0,
58 'topics' => 0,
59 'questions' => 0,
60 'answers' => 0,
61 'comments' => 0,
62 'reactions_in' => [],
63 'reactions_out' => [],
64 'points' => 0,
65 'custom_points' => 0,
66
67 'online_time' => 0,
68 'timezone' => '',
69 'location' => '',
70 'signature' => '',
71 'occupation' => '',
72 'about' => '',
73 'status' => 'inactive',
74 'is_email_confirmed' => 0,
75 'is_mention_muted' => 0,
76
77 'fields' => [],
78
79 'group_name' => '',
80 'group_color' => '',
81
82 'profile_url' => '',
83 'dname' => '',
84 'rating' => [
85 'level' => 0,
86 'percent' => 0,
87 'color' => $this->rating( 0, 'color' ),
88 'title' => $this->rating( 0, 'title' ),
89 'badge' => $this->rating( 0, 'icon' ),
90 ],
91 ];
92 $this->default->action = [
93 'label' => 'Action Button',
94 'ico' => '<svg height="12" width="12" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M224 256A128 128 0 1 0 224 0a128 128 0 1 0 0 256zm-45.7 48C79.8 304 0 383.8 0 482.3C0 498.7 13.3 512 29.7 512H418.3c16.4 0 29.7-13.3 29.7-29.7C448 383.8 368.2 304 269.7 304H178.3z"/></svg>',
95 'callback_for_can' => '',
96 'callback_for_get_url' => '',
97 'data' => [],
98 'confirm_msg' => '',
99 ];
100
101 $this->login_min_length = apply_filters( 'wpforo_login_min_length', $this->login_min_length );
102 $this->login_max_length = apply_filters( 'wpforo_login_max_length', $this->login_max_length );
103 $this->pass_min_length = apply_filters( 'wpforo_pass_min_length', $this->pass_min_length );
104 $this->pass_max_length = apply_filters( 'wpforo_pass_max_length', $this->pass_max_length );
105 }
106
107 private function init_hooks() {
108 if( is_admin() ) add_action( 'wpforo_after_init', [ $this, 'init_list_table' ] );
109 add_action( 'delete_user_form', [ $this, 'show_delete_form' ], 10, 2 );
110 add_action( 'register_new_user', [ $this, 'after_register_new_user' ] );
111 add_action( 'after_password_reset', [ $this, 'after_password_reset' ] );
112 add_action( 'wp_login', [ $this, 'wp_login' ], 10, 2 );
113 add_action( 'set_current_user', [ $this, 'init_current_user' ] );
114 add_action( 'clean_user_cache', function( $userid ) { $this->reset( $userid ); } );
115 add_action( 'init', function() { if( ! WPF()->current_userid ) $this->init_current_user(); } );
116 add_action( 'set_logged_in_cookie', function( $logged_in_cookie ) {
117 $cookie = explode( '|', $logged_in_cookie );
118 WPF()->session_token = (string) wpfval( $cookie, 2 );
119 } );
120
121 add_action( 'wpforo_after_add_topic', [ $this, 'after_add_topic' ] );
122 add_action( 'wpforo_after_delete_topic', [ $this, 'after_delete_topic' ] );
123 add_action( 'wpforo_topic_status_update', [ $this, 'after_topic_status_update' ] );
124 add_action( 'wpforo_before_merge_topic', [ $this, 'before_merge_topic' ], 10, 3 );
125 add_action( 'wpforo_after_merge_topic', [ $this, 'after_merge_topic' ] );
126 add_action( 'wpforo_after_move_topic', [ $this, 'after_move_topic' ] );
127
128 add_action( 'wpforo_after_add_post', [ $this, 'after_add_post' ] );
129 add_action( 'wpforo_after_delete_post', [ $this, 'after_delete_post' ] );
130 add_action( 'wpforo_post_status_update', [ $this, 'after_post_status_update' ] );
131
132 add_action( 'wpforo_after_add_reaction', [ $this, 'after_add_reaction' ] );
133 add_action( 'wpforo_after_edit_reaction', [ $this, 'after_edit_reaction' ], 10, 2 );
134 add_action( 'wpforo_before_delete_reaction', [ $this, 'before_delete_reaction' ], 10, 2 );
135 add_action( 'wpforo_after_delete_reaction', [ $this, 'after_delete_reaction' ] );
136
137 add_action( 'wpforo_after_delete_user', [ $this, 'after_delete_user' ], 11, 2 );
138
139 add_action( 'wpforo_after_activate_user', [ $this, 'after_activate_user' ] );
140
141 add_action( 'wpforo_after_front_delete_user', [ $this, 'after_front_delete_user' ] );
142 }
143
144 public function init_list_table() {
145 if( wpfval( $_GET, 'page' ) === wpforo_prefix_slug( 'members' ) ) {
146 $this->list_table = new MembersListTable();
147 $this->list_table->prepare_items();
148 }
149 }
150
151 /**
152 * @param array $member
153 *
154 * @return array
155 */
156 public function decode( $member ) {
157 $reaction_types = array_map( '__return_zero', array_flip( Reactions::get_type_list() ) );
158 $member = array_merge( $this->default->member, (array) $member );
159 $member['userid'] = wpforo_bigintval( $member['userid'] );
160 $member['first_name'] = trim( strip_tags( (string) $member['first_name'] ) );
161 $member['last_name'] = trim( strip_tags( (string) $member['last_name'] ) );
162 $member['groupid'] = intval( $member['groupid'] );
163 $member['secondary_groupids'] = array_map( 'intval', (array) ( is_scalar( $member['secondary_groupids'] ) ? explode( ',', $member['secondary_groupids'] ) : $member['secondary_groupids'] ) );
164 $member['secondary_groupids'] = array_diff( $member['secondary_groupids'], (array) $member['groupid'] );
165 $member['groupids'] = array_unique( array_filter( array_merge( (array) $member['groupid'], $member['secondary_groupids'] ) ) );
166 $member['avatar'] = preg_replace( '#^https?://#iu', '//', esc_url( (string) $member['avatar'] ) );
167 $member['cover'] = preg_replace( '#^https?://#iu', '//', esc_url( (string) $member['cover'] ) );
168 $member['topics'] = intval( $member['topics'] );
169 $member['posts'] = intval( $member['posts'] );
170 $member['questions'] = intval( $member['questions'] );
171 $member['answers'] = intval( $member['answers'] );
172 $member['comments'] = intval( $member['comments'] );
173
174 sort( $member['secondary_groupids'], SORT_NUMERIC );
175 sort( $member['groupids'], SORT_NUMERIC );
176
177 $member['reactions_in'] = array_map( 'intval', (array) ( wpforo_is_json( $member['reactions_in'] ) ? json_decode( $member['reactions_in'], true ) : $member['reactions_in'] ) );
178 $member['reactions_in']['__ALL__'] = 0;
179 foreach( $member['reactions_in'] as $r ) $member['reactions_in']['__ALL__'] += $r;
180 $member['reactions_in'] = array_merge( $reaction_types, $member['reactions_in'] );
181
182 $member['reactions_out'] = array_map( 'intval', (array) ( wpforo_is_json( $member['reactions_out'] ) ? json_decode( $member['reactions_out'], true ) : $member['reactions_out'] ) );
183 $member['reactions_out']['__ALL__'] = 0;
184 foreach( $member['reactions_out'] as $r ) $member['reactions_out']['__ALL__'] += $r;
185 $member['reactions_out'] = array_merge( $reaction_types, $member['reactions_out'] );
186
187 $member['custom_points'] = floatval( $member['custom_points'] );
188 $member['points'] = $member['custom_points'] ?: $this->calc_points( $member );
189 $member['rating'] = $this->calc_rating( $member['points'] );
190
191 $member['online_time'] = (int) ( $member['online_time'] && ! is_numeric( $member['online_time'] ) ? strtotime( (string) $member['online_time'] . ' GMT' ) : $member['online_time'] );
192 $member['is_email_confirmed'] = (bool) (int) $member['is_email_confirmed'];
193 $member['is_mention_muted'] = (bool) (int) $member['is_mention_muted'];
194
195 $member['title'] = trim( strip_tags( (string) $member['title'] ) );
196 $member['profile_url'] = $this->get_profile_url( $member );
197 $member['dname'] = wpforo_user_dname( $member );
198
199 $member['fields'] = (array) ( wpforo_is_json( $member['fields'] ) ? json_decode( $member['fields'], true ) : $member['fields'] );
200 // $member = wpforo_array_ordered_intersect_key( $member, $this->default->member );
201
202 if( ! $member['userid'] ) $member['timezone'] = '';
203
204 return array_merge( $member['fields'], $member );
205 }
206
207 /** @TODO use this function for member all insert|update queries
208 * @param $member
209 *
210 * @return array
211 */
212 private function encode( $member ) {
213 $member = $this->decode( $member );
214 $member['secondary_groupids'] = implode( ',', $member['secondary_groupids'] );
215 $member['groupids'] = implode( ',', $member['groupids'] );
216 unset( $member['reactions_in']['__ALL__'], $member['reactions_out']['__ALL__'] );
217 $member['reactions_in'] = json_encode( $member['reactions_in'] );
218 $member['reactions_out'] = json_encode( $member['reactions_out'] );
219 $member['is_email_confirmed'] = intval( $member['is_email_confirmed'] );
220 $member['is_mention_muted'] = intval( $member['is_mention_muted'] );
221 $member['fields'] = json_encode( $member['fields'] );
222
223 return $member;
224 }
225
226 private function fix_action( $action ) {
227 return array_merge( $this->default->action, $action );
228 }
229
230 private function _get_actions( $user ) {
231 $userid = wpforo_bigintval( $user['userid'] );
232 $actions = [
233 'edit' => [
234 'label' => wpforo_phrase( 'Edit Account Information', false ),
235 'ico' => '<svg height="12" width="12" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M224 0a128 128 0 1 1 0 256A128 128 0 1 1 224 0zM178.3 304h91.4c11.8 0 23.4 1.2 34.5 3.3c-2.1 18.5 7.4 35.6 21.8 44.8c-16.6 10.6-26.7 31.6-20 53.3c4 12.9 9.4 25.5 16.4 37.6s15.2 23.1 24.4 33c15.7 16.9 39.6 18.4 57.2 8.7v.9c0 9.2 2.7 18.5 7.9 26.3H29.7C13.3 512 0 498.7 0 482.3C0 383.8 79.8 304 178.3 304zM436 218.2c0-7 4.5-13.3 11.3-14.8c10.5-2.4 21.5-3.7 32.7-3.7s22.2 1.3 32.7 3.7c6.8 1.5 11.3 7.8 11.3 14.8v17.7c0 7.8 4.8 14.8 11.6 18.7c6.8 3.9 15.1 4.5 21.8 .6l13.8-7.9c6.1-3.5 13.7-2.7 18.5 2.4c7.6 8.1 14.3 17.2 20.1 27.2s10.3 20.4 13.5 31c2.1 6.7-1.1 13.7-7.2 17.2l-14.4 8.3c-6.5 3.7-10 10.9-10 18.4s3.5 14.7 10 18.4l14.4 8.3c6.1 3.5 9.2 10.5 7.2 17.2c-3.3 10.6-7.8 21-13.5 31s-12.5 19.1-20.1 27.2c-4.8 5.1-12.5 5.9-18.5 2.4l-13.8-7.9c-6.7-3.9-15.1-3.3-21.8 .6c-6.8 3.9-11.6 10.9-11.6 18.7v17.7c0 7-4.5 13.3-11.3 14.8c-10.5 2.4-21.5 3.7-32.7 3.7s-22.2-1.3-32.7-3.7c-6.8-1.5-11.3-7.8-11.3-14.8V467.8c0-7.9-4.9-14.9-11.7-18.9c-6.8-3.9-15.2-4.5-22-.6l-13.5 7.8c-6.1 3.5-13.7 2.7-18.5-2.4c-7.6-8.1-14.3-17.2-20.1-27.2s-10.3-20.4-13.5-31c-2.1-6.7 1.1-13.7 7.2-17.2l14-8.1c6.5-3.8 10.1-11.1 10.1-18.6s-3.5-14.8-10.1-18.6l-14-8.1c-6.1-3.5-9.2-10.5-7.2-17.2c3.3-10.6 7.7-21 13.5-31s12.5-19.1 20.1-27.2c4.8-5.1 12.4-5.9 18.5-2.4l13.6 7.8c6.8 3.9 15.2 3.3 22-.6c6.9-3.9 11.7-11 11.7-18.9V218.2zm92.1 133.5a48.1 48.1 0 1 0 -96.1 0 48.1 48.1 0 1 0 96.1 0z"/></svg>',
236 'callback_for_can' => function() {
237 return WPF()->perm->user_can_edit_account();
238 },
239 'callback_for_get_url' => function() use ( $user ) {
240 return WPF()->member->get_profile_url( $user, 'account' );
241 },
242 ],
243 'edit_dash' => [
244 'label' => wpforo_phrase( 'Edit User in Dashboard', false ),
245 'ico' => '<svg height="12" width="12" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M224 256A128 128 0 1 0 224 0a128 128 0 1 0 0 256zm-45.7 48C79.8 304 0 383.8 0 482.3C0 498.7 13.3 512 29.7 512H322.8c-3.1-8.8-3.7-18.4-1.4-27.8l15-60.1c2.8-11.3 8.6-21.5 16.8-29.7l40.3-40.3c-32.1-31-75.7-50.1-123.9-50.1H178.3zm435.5-68.3c-15.6-15.6-40.9-15.6-56.6 0l-29.4 29.4 71 71 29.4-29.4c15.6-15.6 15.6-40.9 0-56.6l-14.4-14.4zM375.9 417c-4.1 4.1-7 9.2-8.4 14.9l-15 60.1c-1.4 5.5 .2 11.2 4.2 15.2s9.7 5.6 15.2 4.2l60.1-15c5.6-1.4 10.8-4.3 14.9-8.4L576.1 358.7l-71-71L375.9 417z"/></svg>',
246 'callback_for_can' => function() {
247 return current_user_can( 'administrator' );
248 },
249 'callback_for_get_url' => function() use ( $userid ) {
250 return admin_url( "user-edit.php?user_id=" . $userid );
251 },
252 ],
253 'ban' => [
254 'label' => ( $user['status'] === 'banned' ? wpforo_phrase( 'Unban User', false ) : wpforo_phrase( 'Ban User', false ) ),
255 'ico' => '<svg height="12" width="12" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M224 256A128 128 0 1 0 224 0a128 128 0 1 0 0 256zm-45.7 48C79.8 304 0 383.8 0 482.3C0 498.7 13.3 512 29.7 512H392.6c-5.4-9.4-8.6-20.3-8.6-32V352c0-2.1 .1-4.2 .3-6.3c-31-26-71-41.7-114.6-41.7H178.3zM528 240c17.7 0 32 14.3 32 32v48H496V272c0-17.7 14.3-32 32-32zm-80 32v48c-17.7 0-32 14.3-32 32V480c0 17.7 14.3 32 32 32H608c17.7 0 32-14.3 32-32V352c0-17.7-14.3-32-32-32V272c0-44.2-35.8-80-80-80s-80 35.8-80 80z"/></svg>',
256 'callback_for_can' => function() use ( $userid ) {
257 return $userid !== WPF()->current_userid && ( WPF()->usergroup->can( 'bm' ) || wpforo_current_user_is( 'admin' ) );
258 },
259 'callback_for_get_url' => function() use ( $userid ) {
260 return '';
261 },
262 'data' => [
263 'currentstate' => (int) ( $user['status'] === 'banned' ),
264 'active_label' => wpforo_phrase( 'Unban User', false ),
265 'inactive_label' => wpforo_phrase( 'Ban User', false ),
266 ],
267 'confirm_msg' => wpforo_phrase( 'Please confirm you want to do this action?', false ),
268 ],
269 'delete' => [
270 'label' => wpforo_phrase( 'Delete Account', false ),
271 'ico' => '<svg height="12" width="12" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M96 128a128 128 0 1 1 256 0A128 128 0 1 1 96 128zM0 482.3C0 383.8 79.8 304 178.3 304h91.4C368.2 304 448 383.8 448 482.3c0 16.4-13.3 29.7-29.7 29.7H29.7C13.3 512 0 498.7 0 482.3zM471 143c9.4-9.4 24.6-9.4 33.9 0l47 47 47-47c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-47 47 47 47c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0l-47-47-47 47c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l47-47-47-47c-9.4-9.4-9.4-24.6 0-33.9z"/></svg>',
272 'callback_for_can' => function() use ( $userid ) {
273 $self = $userid === WPF()->current_userid && ! is_super_admin( $userid ) && apply_filters( 'wpforo_can_user_self_delete', true );
274 $not_self = $userid !== WPF()->current_userid && ( current_user_can( 'administrator' ) || WPF()->usergroup->can( 'dm' ) ) && WPF()->perm->user_can_manage_user(
275 WPF()->current_userid,
276 $userid
277 );
278
279 return $self || $not_self;
280 },
281 'callback_for_get_url' => function() use ( $userid ) {
282 return wp_nonce_url( trim( WPF()->member->get_profile_url( $userid ), '/' ) . '/?wpfaction=user_delete', 'user_delete', '_wpfnonce' );
283 },
284 'confirm_msg' => wpforo_phrase( 'Please confirm you want to do this action?', false ),
285 ],
286 'delete_dash' => [
287 'label' => wpforo_phrase( 'Delete User in Dashboard', false ),
288 'ico' => '<svg height="12" width="12" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L381.9 274c48.5-23.2 82.1-72.7 82.1-130C464 64.5 399.5 0 320 0C250.4 0 192.4 49.3 178.9 114.9L38.8 5.1zM545.5 512H528L284.3 320h-59C136.2 320 64 392.2 64 481.3c0 17 13.8 30.7 30.7 30.7H545.3l.3 0z"/></svg>',
289 'callback_for_can' => function() use ( $userid ) {
290 return ( current_user_can( 'administrator' ) && WPF()->perm->user_can_manage_user( WPF()->current_userid, $userid ) && $userid !== WPF()->current_userid );
291 },
292 'callback_for_get_url' => function() use ( $userid ) {
293 return admin_url( wp_nonce_url( "users.php?action=delete&user=" . $userid, 'bulk-users' ) );
294 },
295 ],
296 ];
297
298 return array_map( [ $this, 'fix_action' ], apply_filters( 'wpforo_member_get_actions', $actions, $user ) );
299 }
300
301 public function get_action( $user, $key ) {
302 $actions = $this->_get_actions( $user );
303
304 return wpfval( $actions, $key );
305 }
306
307 public function get_actions( $user ) {
308 $actions = $this->_get_actions( $user );
309
310 return array_filter( $actions, function( $action ) use ( $user ) {
311 if( is_callable( $action['callback_for_can'] ) ) return call_user_func( $action['callback_for_can'] );
312
313 return true;
314 } );
315 }
316
317 private function add_profile( $args ) {
318 if( ! $userid = wpforo_bigintval( wpfval( $args, 'userid' ) ) ) return false;
319 $sql = "INSERT IGNORE INTO `" . WPF()->tables->profiles . "` (`userid`, `title`, `groupid`, `timezone`, `about`) VALUES ( %d, %s, %d, %s, %s )";
320 $sql = WPF()->db->prepare(
321 $sql,
322 $userid,
323 sanitize_text_field( wpfval( $args, 'title' ) ) ?: wpforo_setting( 'profiles', 'default_title' ),
324 (int) wpfval( $args, 'groupid' ) ?: WPF()->usergroup->default_groupid,
325 sanitize_text_field( wpfval( $args, 'timezone' ) ) ?: '',
326 stripslashes( wpforo_kses( trim( (string) wpfval( $args, 'about' ) ), 'user_description' ) ) ?: ''
327 );
328
329 $r = WPF()->db->query( $sql );
330 $this->reset( $userid );
331
332 return $r;
333 }
334
335 function create( $data ) {
336 if( ! wpforo_setting( 'authorization', 'user_register' ) ) {
337 WPF()->notice->add( 'User registration is disabled.', 'error' );
338
339 return false;
340 }
341
342 $user_fields = [];
343 if( ! empty( $data ) ) {
344 if( wpfval( $data, 'wpfreg' ) ) {
345 $user_fields = $data['wpfreg'];
346 } else {
347 $user_fields = $data;
348 }
349 }
350
351 //-- START -- copied from update code
352 //Define $user
353 $user = $user_fields;
354
355 //Define $userid
356 $userid = intval( wpfval( $user, 'userid' ) );
357
358 //Check custom fields and merge to $user array
359 if( wpfval( $data, 'data' ) && is_array( $data['data'] ) && ! empty( $data['data'] ) ) {
360 $custom_fields = $data['data'];
361 $user = array_merge( $custom_fields, $user );
362 }
363
364 //Check file uploading custom fields and merge to $user array
365 $file_data = isset( $_FILES['data']['name'] ) && $_FILES['data']['name'] && is_array( $_FILES['data']['name'] ) ? array_filter( $_FILES['data']['name'] ) : [];
366 $file_fields = WPF()->form->prepare_file_args( $file_data, $userid );
367 if( wpfval( $file_fields, 'fields' ) ) {
368 $user = array_merge( $file_fields['fields'], $user );
369 }
370
371 //Validate fields
372 $form_fields = $this->get_register_fields();
373 $result = WPF()->form->validate( $user, $form_fields );
374 if( wpfval( $result, 'error' ) ) {
375 if( wpforo_is_admin() && wpfval( $result['error'], 0 ) ) {
376 wp_die( $result['error'][0] );
377 } else {
378 WPF()->notice->add( $result['error'], 'error' );
379
380 return false;
381 }
382 }
383 //-- END -- copied from update code
384
385 $user_fields = apply_filters( 'wpforo_create_profile', $user_fields );
386
387 if( ! $user_fields ) {
388 WPF()->notice->add( 'Empty fields', 'error' );
389
390 return false;
391 } elseif( wpfval( $user_fields, 'error' ) ) {
392 WPF()->notice->add( sanitize_text_field( $user_fields['error'] ), 'error' );
393
394 return false;
395 }
396
397 if( ! wpforo_setting( 'authorization', 'user_register_email_confirm' ) && ! empty( $user_fields ) && is_array( $user_fields ) && ! empty( $user_fields['user_pass1'] ) ) {
398
399 remove_action( 'register_new_user', 'wp_send_new_user_notifications' );
400 remove_action( 'register_new_user', 'wpforo_send_new_user_notifications' );
401 add_action( 'register_new_user', function( $user_id ) {
402 wpforo_send_new_user_notifications( $user_id, 'admin' );
403 } );
404
405 do_action( 'wpforo_create_profile_before', $user_fields );
406
407 $errors = new WP_Error();
408
409 extract( $user_fields, EXTR_OVERWRITE );
410 $sanitized_user_login = sanitize_user( $user_login );
411 $user_email = apply_filters( 'user_registration_email', $user_email );
412 $user_pass1 = trim( substr( (string) $user_pass1, 0, 100 ) );
413 $user_pass2 = trim( substr( (string) $user_pass2, 0, 100 ) );
414 $illegal_user_logins = array_map( 'strtolower', (array) apply_filters( 'illegal_user_logins', [] ) );
415
416 if( $sanitized_user_login == '' ) {
417 $errors->add( 'empty_username', __( '<strong>ERROR</strong>: Please enter a username.' ) );
418 WPF()->notice->add( 'Username is missed.', 'error' );
419
420 return false;
421 } elseif( ! validate_username( $user_login ) ) {
422 $errors->add( 'invalid_username', __( '<strong>ERROR</strong>: This username is invalid because it uses illegal characters. Please enter a valid username.' ) );
423 WPF()->notice->add( 'Illegal character in username.', 'error' );
424
425 return false;
426 } elseif( strlen( (string) $user_login ) < $this->login_min_length || strlen( (string) $user_login ) > $this->login_max_length ) {
427 WPF()->notice->add( 'Username length must be between %d characters and %d characters.', 'error', [ $this->login_min_length, $this->login_max_length ] );
428
429 return false;
430 } elseif( username_exists( $sanitized_user_login ) ) {
431 $errors->add( 'username_exists', __( '<strong>ERROR</strong>: This username is already registered. Please choose another one.' ) );
432 WPF()->notice->add( 'Username exists. Please insert another.', 'error' );
433
434 return false;
435 } elseif( in_array( strtolower( $sanitized_user_login ), $illegal_user_logins ) ) {
436 $errors->add( 'invalid_username', __( '<strong>ERROR</strong>: Sorry, that username is not allowed.' ) );
437 WPF()->notice->add( 'ERROR: invalid_username. Sorry, that username is not allowed. Please insert another.', 'error' );
438
439 return false;
440 } elseif( $user_email == '' ) {
441 $errors->add( 'empty_email', __( '<strong>ERROR</strong>: Please type your email address.' ) );
442 WPF()->notice->add( 'Insert your Email address.', 'error' );
443
444 return false;
445 } elseif( ! is_email( $user_email ) ) {
446 $errors->add( 'invalid_email', __( '<strong>ERROR</strong>: The email address isn&#8217;t correct.' ) );
447 WPF()->notice->add( 'Invalid Email address', 'error' );
448
449 return false;
450 } elseif( email_exists( $user_email ) ) {
451 $errors->add( 'email_exists', __( '<strong>ERROR</strong>: This email is already registered, please choose another one.' ) );
452 WPF()->notice->add( 'Email address exists. Please insert another.', 'error' );
453
454 return false;
455 } elseif( strlen( $user_pass1 ) < $this->pass_min_length || strlen( $user_pass1 ) > $this->pass_max_length ) {
456 WPF()->notice->add( 'Password length must be between %d characters and %d characters.', 'error', [ $this->pass_min_length, $this->pass_max_length ] );
457
458 return false;
459 } elseif( $user_pass1 != $user_pass2 ) {
460 WPF()->notice->add( 'Password mismatch.', 'error' );
461
462 return false;
463 } else {
464 do_action( 'register_post', $sanitized_user_login, $user_email, $errors );
465 $errors = apply_filters( 'registration_errors', $errors, $sanitized_user_login, $user_email );
466 if( $errors->get_error_code() ) {
467 $user_fields = [];
468 foreach( $errors->errors as $u_err ) $user_fields[] = $u_err[0];
469 WPF()->notice->add( $user_fields, 'error' );
470
471 return false;
472 }
473 $user_id = wp_create_user( $sanitized_user_login, $user_pass1, $user_email );
474 if( ! is_wp_error( $user_id ) && $user_id ) {
475 do_action( 'register_new_user', $user_id );
476 do_action( 'wpforo_create_user_after', $data );
477 wp_signon( [ 'user_login' => $sanitized_user_login, 'user_password' => $user_pass1 ] );
478 WPF()->notice->clear();
479 WPF()->notice->add( 'Success!', 'success' );
480
481 return $user_id;
482 }
483 }
484 } elseif( wpforo_setting( 'authorization', 'user_register_email_confirm' ) && ! empty( $user_fields['user_login'] ) && ! empty( $user_fields['user_email'] ) ) {
485 if( strlen( (string) $user_fields['user_login'] ) < $this->login_min_length || strlen( (string) $user_fields['user_login'] ) > $this->login_max_length ) {
486 WPF()->notice->add( 'Username length must be between %d characters and %d characters.', 'error', [ $this->login_min_length, $this->login_max_length ] );
487
488 return false;
489 }
490 if( is_multisite() && apply_filters( 'wpforo_mu_signup', false ) ) {
491 wpmu_signup_user( $user_fields['user_login'], $user_fields['user_email'] );
492 WPF()->notice->clear();
493 WPF()->notice->add( 'Success! Please check your mail for confirmation.', 'success' );
494
495 return true;
496 } else {
497 $user_id = register_new_user( $user_fields['user_login'], $user_fields['user_email'] );
498 }
499 if( ! is_wp_error( $user_id ) && $user_id ) {
500 do_action( 'wpforo_create_user_after', $data );
501 WPF()->notice->clear();
502 WPF()->notice->add( 'Success! Please check your mail for confirmation.', 'success' );
503
504 return $user_id;
505 }
506 }
507
508 if( ! empty( $user_id->errors ) ) {
509 $user_fields = [];
510 foreach( $user_id->errors as $u_err ) $user_fields[] = $u_err[0];
511 WPF()->notice->add( $user_fields, 'error' );
512
513 return false;
514 }
515
516 WPF()->notice->add( 'Registration Error', 'error' );
517
518 return false;
519 }
520
521 /**
522 * Updates user data
523 *
524 * @param array $data - User data as a simple array( field => value ) OR
525 * $data['member'] = array( field => value ) user and profile fields - Account form
526 * $data['wpfreg'] = array( field => value ) user and profile fields - Registration form
527 * $data['data'] = array( field => value ) user custom fields
528 *
529 * $data['member']['userid'] or $data['userid'] is required
530 *
531 * @param string|array $type User data update types (comma separated)
532 * $type = 'full' user fields, profile fields, custom fields
533 * $type = 'user' only user fields (wp_users table)
534 * $type = 'profile' only profile fields (wp_wpforo_profiles table)
535 * $type = 'custom_fields' only custom fields (wp_wpforo_profiles table > fields column)
536 * $type = 'profile, custom_fields'
537 *
538 * @param boolean $check_permissions Whether check the current editor permissions or not
539 *
540 * @return false|array wpForo User array
541 * @since 1.5.0
542 *
543 */
544 public function update( $data, $type = 'full', $check_permissions = true ) {
545 $type = (array) $type;
546
547 switch( WPF()->current_object['template'] ) {
548 case 'register':
549 $form = 'wpfreg';
550 $form_fields = $this->get_register_fields();
551 break;
552 default:
553 $form = 'member';
554 $form_fields = $this->get_account_fields();
555 break;
556 }
557 if( ! $form_fields ) {
558 WPF()->notice->add( 'Form template not found', 'error' );
559
560 return false;
561 }
562
563 if( ! wpfkey( $data, $form ) ) {
564 if( in_array( 'custom_fields', $type ) && count( $type ) === 1 ) {
565 if( ! wpfval( $data, 'data' ) ) {
566 $data['data'] = $data;
567 if( wpfkey( $data, 'data', 'userid' ) ) unset( $data['data']['userid'] );
568 }
569 } else {
570 $data[ $form ] = $data;
571 }
572 }
573 if( wpfval( $data, 'userid' ) && ! wpfval( $data, $form, 'userid' ) ) $data[ $form ]['userid'] = $data['userid'];
574
575 if( wpfval( $data, $form, 'userid' ) ) {
576 $result_user = true;
577 $result_fields = true;
578 $result_profile = true;
579 $custom_fields = [];
580
581 //Define $user
582 $user = $data[ $form ];
583
584 //Define $userid
585 $userid = intval( $data[ $form ]['userid'] );
586
587 //Check profile editor permissions
588 if( $check_permissions ) WPF()->perm->can_edit_user( $userid );
589
590 //Check custom fields and merge to $user array
591 if( wpfkey( $data, 'data' ) && is_array( $data['data'] ) && ! empty( $data['data'] ) ) {
592 $custom_fields = $data['data'];
593 $user = array_merge( $custom_fields, $user );
594 }
595
596 //Check file uploading custom fields and merge to $user array
597 $file_data = isset( $_FILES['data']['name'] ) && $_FILES['data']['name'] && is_array( $_FILES['data']['name'] ) ? array_filter( $_FILES['data']['name'], function( $key ) {
598 if( trim( (string) $key ) && ( $field = $this->get_field( $key ) ) ) {
599 if( wpfval( $field, 'type' ) === 'file' ) {
600 $mime_type = wpfval( $_FILES, 'data', 'type', $key );
601 $mime_types = wp_get_mime_types();
602 $allowed_mime_types = get_allowed_mime_types();
603 $name = wpfval( $_FILES, 'data', 'name', $key );
604 $ext = pathinfo( $name, PATHINFO_EXTENSION );
605 $size = intval( $field['fileSize'] );
606 $fileExtensions = array_filter( (array) ( is_scalar( $field['fileExtensions'] ) ? explode( ',', trim( (string) $field['fileExtensions'] ) ) : $field['fileExtensions'] ) );
607 if( $fileExtensions ) {
608 if( in_array( $ext, $fileExtensions ) ) {
609 $extensions = explode( '|', array_search( $mime_type, $mime_types ) );
610 $e = in_array( $ext, $extensions );
611 } else {
612 $e = false;
613 }
614 } else {
615 $extensions = explode( '|', array_search( $mime_type, $allowed_mime_types ) );
616 $e = in_array( $ext, $extensions );
617 }
618
619 return ! empty( $e ) && wpfval( $_FILES, 'data', 'size', $key ) <= ( $size * 1024 * 1024 );
620 }
621 }
622
623 return false;
624 }, ARRAY_FILTER_USE_KEY ) : [];
625 $file_fields = WPF()->form->prepare_file_args( $file_data, $userid );
626 if( wpfval( $file_fields, 'fields' ) ) {
627 $user = array_merge( $file_fields['fields'], $user );
628 $custom_fields = ( ! empty( $custom_fields ) ) ? array_merge( $custom_fields, $file_fields['fields'] ) : $file_fields['fields'];
629 }
630
631 //Hooks
632 $user = apply_filters( 'wpforo_update_profile', $user );
633 do_action( 'wpforo_update_profile_before', $user );
634 if( wpfval( $user, 'error' ) || empty( $user ) ) {
635 $error_message = ( wpfval( $user, 'error_message' ) ) ? sanitize_text_field(
636 $user['error_message']
637 ) : 'Unknown error in profile editing hook. Please disable all plugins and check it again.';
638 WPF()->notice->add( $error_message, 'error' );
639
640 return false;
641 }
642
643 //Validate avatar_url
644 if( wpfval( $user, 'avatar_type' ) === 'remote' && wpfval( $user, 'avatar_url' ) ) {
645 if( ! is_avatar_url( $user['avatar_url'] ) ) {
646 WPF()->notice->add( 'The avatar URL is not an image file.', 'error' );
647
648 return false;
649 }
650 }
651
652 //Validate fields
653 $result = WPF()->form->validate( $user, $form_fields );
654 if( wpfval( $result, 'error' ) ) {
655 if( is_admin() && wpfval( $result['error'], 0 ) ) {
656 wp_die( $result['error'][0] );
657 } else {
658 WPF()->notice->add( $result['error'], 'error' );
659
660 return false;
661 }
662 }
663
664 if( $gid = (int) wpfval( $user, 'groupid' ) ) {
665 $err = '';
666 if( WPF()->current_object['template'] !== 'register' ) {
667 if( WPF()->form->owner() || ! wpforo_current_user_is( 'admin' ) ) {
668 $err = 'You have no permission to edit Usergroup field';
669 }
670 } else {
671 if( in_array( $gid, [ 1, 2, 4 ], true ) ) {
672 $err = 'Admin and Moderator Usergroups are not permitted';
673 } else {
674 $f = $this->get_field( 'groupid' );
675 if( wpfval( $f, 'allowedGroupIds' ) ) {
676 $allowedGroupIds = wpforo_parse_args( $f['allowedGroupIds'] );
677 if( ! in_array( $gid, $allowedGroupIds, true ) ) {
678 $err = 'The selected Usergroup cannot be set';
679 }
680 } else {
681 $err = 'The selected Usergroup is not found in allowed list';
682 }
683 }
684 }
685
686 if( $err ) {
687 unset( $user['groupid'] );
688 }
689 }
690
691 if( $sgids = array_filter( array_map( 'intval', (array) wpfval( $user, 'secondary_groupids' ) ) ) ) {
692 $f = $this->get_field( 'secondary_groupids' );
693 if( WPF()->current_object['template'] === 'register' || ( WPF()->form->owner() && $f['isEditable'] ) || ( ! WPF()->form->owner() && in_array(
694 WPF()->current_user_groupid,
695 (array) $f['canEdit']
696 ) ) || wpforo_current_user_is( 'admin' ) ) {
697 if( ! empty( $sgids ) ) {
698 $secondary_groupids = WPF()->usergroup->get_secondary_groupids();
699 foreach( $sgids as $sgid ) {
700 if( in_array( $sgid, [ 1, 2, 4 ], true ) ) {
701 $user['secondary_groupids'] = array_diff( $user['secondary_groupids'], [ $sgid ] );
702 }
703 if( ! in_array( $sgid, $secondary_groupids ) ) {
704 $user['secondary_groupids'] = array_diff( $user['secondary_groupids'], [ $sgid ] );
705 }
706 }
707 }
708 } else {
709 unset( $user['secondary_groupids'] );
710 }
711 }
712
713 //Sanitize fields
714 $user = WPF()->form->sanitize( $user, $form_fields );
715
716 //Update User Fields
717 if( ! empty( $user ) && ( in_array( 'full', $type ) || in_array( 'user', $type ) ) ) {
718 $result_user = $this->update_user_fields( $userid, $user, false );
719 }
720
721 //Update Profile Fields
722 if( ! empty( $user ) && ( in_array( 'full', $type ) || in_array( 'profile', $type ) ) ) {
723 $result_profile = $this->update_profile_fields( $userid, $user, false );
724 }
725
726 //Password field validation and update
727 $result_password = WPF()->form->validate_password( $user );
728 if( $result_password === false ) {
729 $result_password = true;
730 }
731 if( wpfval( $result_password, 'error' ) ) {
732 WPF()->notice->add( $result_password['error'], 'error' );
733 $result_password = false;
734 }
735 if( $result_password && wpfval( $user, 'old_pass' ) && wpfval( $user, 'user_pass1' ) ) {
736 $result_password = $this->change_password( $user['old_pass'], $user['user_pass1'], $userid );
737 }
738
739 //Upload avatar
740 if( wpfval( $user, 'avatar_type' ) === 'custom' ) {
741 $this->upload_avatar( $userid );
742 }
743
744 //Update Custom Fields
745 if( ! empty( $custom_fields ) && ( in_array( 'full', $type ) || in_array( 'custom_fields', $type ) ) ) {
746 $result_fields = $this->update_custom_fields( $userid, $custom_fields, false );
747 }
748
749 //Upload Files from Custom Fields
750 if( wpfval( $file_fields, 'files' ) ) {
751 $this->upload_files( $file_fields['files'] );
752 }
753
754 //Reset this user cache
755 $this->reset( $userid );
756
757 if( $result_user === false || $result_profile === false || $result_fields === false || $result_password === false ) {
758 return false;
759 } else {
760 WPF()->notice->add( 'Profile updated successfully', 'success' );
761 do_action( 'wpforo_update_profile_after', $user );
762
763 return $user;
764 }
765 }
766
767 return false;
768 }
769
770 public function update_user_fields( $userid, $data, $check_permissions = true ) {
771 $result_user = true;
772 if( $check_permissions ) WPF()->perm->can_edit_user( $userid );
773
774 //User Fields
775 if( wpfkey( $data, 'display_name' ) ) {
776 $user_fields['display_name'] = $data['display_name'];
777 $user_fields_types[] = '%s';
778 }
779 if( wpfkey( $data, 'user_email' ) ) {
780 $user_fields['user_email'] = $data['user_email'];
781 $user_fields_types[] = '%s';
782 }
783 if( wpfkey( $data, 'user_nicename' ) ) {
784 if( ! wpfval( $data, 'user_nicename' ) ) {
785 $user_info = get_userdata( $userid );
786 $data['user_nicename'] = sanitize_title( sanitize_user( $user_info->user_nicename, true ) );
787 }
788 $user_fields['user_nicename'] = $data['user_nicename'];
789 $user_fields_types[] = '%s';
790 update_user_meta( $userid, 'nickname', $data['user_nicename'] );
791 }
792 if( wpfkey( $data, 'first_name' ) ) {
793 update_user_meta( $userid, 'first_name', $data['first_name'] );
794 }
795 if( wpfkey( $data, 'last_name' ) ) {
796 update_user_meta( $userid, 'last_name', $data['last_name'] );
797 }
798 if( wpfkey( $data, 'user_url' ) ) {
799 $user_fields['user_url'] = $data['user_url'];
800 $user_fields_types[] = '%s';
801 }
802
803 if( ! empty( $user_fields ) && ! empty( $user_fields_types ) ) {
804 $result_user = WPF()->db->update( WPF()->db->users, $user_fields, [ 'ID' => $userid ], $user_fields_types, [ '%d' ] );
805 if( false === $result_user ) {
806 WPF()->notice->add( 'User data update failed', 'error' );
807 if( WPF()->db->last_error ) {
808 WPF()->notice->add( sanitize_text_field( WPF()->db->last_error ), 'error' );
809 }
810 } else {
811 clean_user_cache( $userid );
812 }
813 }
814
815 if( $result_user ) $this->reset( $userid );
816
817 return $result_user;
818 }
819
820 public function update_profile_field( $userid, $key, $value ) {
821 $r = false;
822 if( $key && ! is_null( $value ) ) {
823 $member = $this->encode( [ $key => $value ] );
824 if( wpfkey( $member, $key ) ) {
825 $r = WPF()->db->update(
826 WPF()->tables->profiles,
827 [ $key => $value ],
828 [ 'userid' => $userid ],
829 [ wpforo_db_data_format( $member[ $key ] ) ], [ '%d' ]
830 );
831
832 if( $r ) $this->reset( $userid );
833 }
834 }
835
836 return $r;
837 }
838
839 public function update_profile_fields( $userid, $data, $check_permissions = true ) {
840 $result_profile = true;
841
842 if( $check_permissions ) {
843 WPF()->perm->can_edit_user( $userid );
844 }
845
846 $member = $this->encode( $data );
847
848 $profile_fields = [];
849 $profile_fields_types = [];
850
851 //Profile Fields
852 if( wpfkey( $data, 'groupid' ) ) {
853 $profile_fields['groupid'] = $member['groupid'];
854 $profile_fields_types[] = '%d';
855 }
856 if( wpfkey( $data, 'title' ) ) {
857 $profile_fields['title'] = $member['title'];
858 $profile_fields_types[] = '%s';
859 }
860 if( wpfkey( $data, 'signature' ) ) {
861 $profile_fields['signature'] = $member['signature'];
862 $profile_fields_types[] = '%s';
863 }
864 if( wpfkey( $data, 'about' ) ) {
865 $profile_fields['about'] = $member['about'];
866 $profile_fields_types[] = '%s';
867 update_user_meta( $userid, 'description', $member['about'] );
868 }
869 if( wpfkey( $data, 'occupation' ) ) {
870 $profile_fields['occupation'] = $member['occupation'];
871 $profile_fields_types[] = '%s';
872 }
873 if( wpfkey( $data, 'location' ) ) {
874 $profile_fields['location'] = $member['location'];
875 $profile_fields_types[] = '%s';
876 }
877 if( wpfkey( $data, 'timezone' ) ) {
878 $profile_fields['timezone'] = $member['timezone'];
879 $profile_fields_types[] = '%s';
880 }
881 if( wpfkey( $data, 'avatar_type' ) && $data['avatar_type'] !== 'gravatar' && wpfval( $data, 'avatar_url' ) ) {
882 $profile_fields['avatar'] = esc_url( trim( (string) $data['avatar_url'] ) );
883 $profile_fields_types[] = '%s';
884 }
885 if( wpfkey( $data, 'avatar_type' ) && $data['avatar_type'] === 'gravatar' ) {
886 $profile_fields['avatar'] = '';
887 $profile_fields_types[] = '%s';
888 }
889 if( wpfkey( $data, 'cover' ) ) {
890 $profile_fields['cover'] = $member['cover'];
891 $profile_fields_types[] = '%s';
892 }
893 if( wpfkey( $data, 'secondary_groupids' ) ) {
894 $profile_fields['secondary_groupids'] = $member['secondary_groupids'];
895 $profile_fields_types[] = '%s';
896 }
897 if( wpfkey( $data, 'custom_points' ) ) {
898 $profile_fields['custom_points'] = $member['custom_points'];
899 $profile_fields_types[] = '%d';
900 }
901 if( wpfkey( $data, 'online_time' ) ) {
902 $profile_fields['online_time'] = $member['online_time'];
903 $profile_fields_types[] = '%d';
904 }
905 if( wpfkey( $data, 'status' ) ) {
906 $profile_fields['status'] = $member['status'];
907 $profile_fields_types[] = '%s';
908 }
909 if( wpfkey( $data, 'is_email_confirmed' ) ) {
910 $profile_fields['is_email_confirmed'] = $member['is_email_confirmed'];
911 $profile_fields_types[] = '%d';
912 }
913 if( wpfkey( $data, 'is_mention_muted' ) ) {
914 $profile_fields['is_mention_muted'] = $member['is_mention_muted'];
915 $profile_fields_types[] = '%d';
916 }
917
918 $profile_fields = apply_filters( 'wpforo_before_update_profile_fields', $profile_fields, $userid, $data, $check_permissions );
919
920 if( ! empty( $profile_fields ) ) {
921 $result_profile = WPF()->db->update( WPF()->tables->profiles, $profile_fields, [ 'userid' => intval( $userid ) ], $profile_fields_types, [ '%d' ] );
922 if( false === $result_profile ) {
923 WPF()->notice->add( 'User profile update failed', 'error' );
924 if( WPF()->db->last_error ) {
925 WPF()->notice->add( sanitize_text_field( WPF()->db->last_error ), 'error' );
926 }
927 }
928 }
929 do_action( 'wpforo_update_profile_fields', $userid, $data, $result_profile );
930
931 if( $result_profile ) $this->reset( $userid );
932
933 return $result_profile;
934 }
935
936 public function update_custom_field( $userid, $field_name, $field_value = null ) {
937 $result = false;
938 $fields = $this->get_custom_fields( $userid );
939 if( ! empty( $fields ) && $field_name && ! is_null( $field_value ) ) {
940 foreach( $fields as $name => $value ) {
941 if( $name == $field_name ) {
942 $fields[ $name ] = $field_value;
943 }
944 }
945 $custom_fields = array_filter( $fields );
946 $custom_fields = wpforo_unslashe( $custom_fields );
947 $custom_fields = wpforo_decode( $custom_fields );
948 $custom_fields = wpforo_encode( $custom_fields );
949 $fields_json = json_encode( $custom_fields, JSON_UNESCAPED_UNICODE );
950 $sql = "UPDATE `" . WPF()->tables->profiles . "` SET `fields` = %s WHERE `userid` = %d;";
951 $sql = WPF()->db->prepare( $sql, $fields_json, $userid );
952 $result = WPF()->db->query( $sql );
953 }
954
955 if( $result ) $this->reset( $userid );
956
957 return $result;
958 }
959
960 public function update_custom_fields( $userid, $custom_fields, $check_permissions = true ) {
961
962 $result_fields = true;
963
964 if( ! empty( $custom_fields ) ) {
965 if( $check_permissions ) {
966 WPF()->perm->can_edit_user( $userid );
967 }
968 $custom_fields = wpforo_unslashe( $custom_fields );
969 $custom_fields = wpforo_decode( $custom_fields );
970 $custom_fields = wpforo_encode( $custom_fields );
971 $data_old = $this->get_custom_fields( $userid );
972 if( $data_old && is_array( $data_old ) ) {
973 $custom_fields = wp_parse_args( $custom_fields, $data_old );
974 }
975 $fields_json = json_encode( $custom_fields, JSON_UNESCAPED_UNICODE );
976 $sql = "UPDATE `" . WPF()->tables->profiles . "` SET `fields` = %s WHERE `userid` = %d;";
977 $sql = WPF()->db->prepare( $sql, $fields_json, $userid );
978 $result_fields = WPF()->db->query( $sql );
979 if( false === $result_fields ) {
980 WPF()->notice->add( 'User custom field update failed', 'error' );
981 if( WPF()->db->last_error ) {
982 WPF()->notice->add( sanitize_text_field( WPF()->db->last_error ), 'error' );
983 }
984 }
985 }
986
987 if( $result_fields ) $this->reset( $userid );
988
989 return $result_fields;
990 }
991
992 public function upload_avatar( $userid = 0 ) {
993 $userid = intval( $userid );
994
995 $_FILES['avatar'] = wpforo_check_for_svg( $_FILES['avatar'] );
996
997 if( wpfval( WPF()->current_object, 'template' ) ) {
998 $template = WPF()->current_object['template'];
999 if( $template === 'account' ) {
1000 if( ! WPF()->usergroup->can( 'upa' ) ) return false;
1001 }
1002 }
1003
1004 if( ! $user = $this->get_member( $userid ) ) return false;
1005 $user_nicename = urldecode( (string) $user['user_nicename'] );
1006 if( isset( $_FILES['avatar'] ) && ! empty( $_FILES['avatar'] ) && isset( $_FILES['avatar']['name'] ) && $_FILES['avatar']['name'] ) {
1007 $name = sanitize_file_name( $_FILES['avatar']['name'] ); //myimg.png
1008 $tmp_name = sanitize_text_field( $_FILES['avatar']['tmp_name'] ); //D:\wamp\tmp\php986B.tmp
1009 $error = sanitize_text_field( $_FILES['avatar']['error'] ); //0
1010 $size = intval( $_FILES['avatar']['size'] ); //6112
1011
1012 $upload_max_filesize = apply_filters( 'wpforo_avatar_upload_max_filesize', 2 * 1048576 );
1013 if( $size > $upload_max_filesize ) {
1014 WPF()->notice->clear();
1015 WPF()->notice->add( 'Avatar image is too big maximum allowed size is %s', 'error', wpforo_print_size( $upload_max_filesize ) );
1016
1017 return false;
1018 }
1019
1020 if( $error ) {
1021 $error = wpforo_file_upload_error( $error );
1022 WPF()->notice->clear();
1023 WPF()->notice->add( $error, 'error' );
1024
1025 return false;
1026 }
1027
1028 $avatar_dir = WPF()->folders['avatars']['dir'];
1029 if( ! is_dir( $avatar_dir ) ) wp_mkdir_p( $avatar_dir );
1030
1031 $ext = pathinfo( $name, PATHINFO_EXTENSION );
1032 if( ! wpforo_is_image( $ext ) ) {
1033 WPF()->notice->clear();
1034 WPF()->notice->add( 'Incorrect file format. Allowed formats: jpeg, jpg, png, gif.', 'error' );
1035
1036 return false;
1037 }
1038
1039 $fnm = pathinfo( $user_nicename, PATHINFO_FILENAME );
1040 $fnm = str_replace( ' ', '-', $fnm );
1041 while( strpos( (string) $fnm, '--' ) !== false ) $fnm = str_replace( '--', '-', $fnm );
1042 $fnm = preg_replace( "/[^-a-zA-Z0-9]/", "", (string) $fnm );
1043 $fnm = trim( (string) $fnm, "-" );
1044
1045 $avatar_fname = $fnm . ( $fnm ? '_' : '' ) . $userid . "." . strtolower( (string) $ext );
1046 $avatar_fname_orig = $fnm . ( $fnm ? '_' : '' ) . $userid . "." . $ext;
1047 $avatar_path = $avatar_dir . DIRECTORY_SEPARATOR . $avatar_fname;
1048 $avatar_path_orig = $avatar_dir . DIRECTORY_SEPARATOR . $avatar_fname_orig;
1049
1050 if( is_dir( $avatar_dir ) ) {
1051 if( move_uploaded_file( $tmp_name, $avatar_path ) ) {
1052 $image = wp_get_image_editor( $avatar_path );
1053 if( ! is_wp_error( $image ) ) {
1054 $image->resize( 150, 150, true );
1055 $saved = $image->save( $avatar_path );
1056 if( ! is_wp_error( $saved ) && $avatar_fname != $avatar_fname_orig ) {
1057 if( defined( PHP_OS ) && strtoupper( substr( PHP_OS, 0, 3 ) ) !== 'WIN' ) unlink( $avatar_path_orig );
1058 }
1059 }
1060 WPF()->db->update( WPF()->tables->profiles, [ 'avatar' => WPF()->folders['avatars']['url//'] . "/" . $avatar_fname ], [ 'userid' => intval( $userid ) ], [ '%s' ], [ '%d' ] );
1061 $this->reset( $userid );
1062
1063 return true;
1064 }
1065 }
1066 }
1067
1068 return false;
1069 }
1070
1071 public function upload_files( $file_fields ) {
1072 if( ! empty( $file_fields ) ) {
1073 foreach( $file_fields as $field_name => $file_path ) {
1074 if( wpfval( $_FILES, 'data', 'tmp_name', $field_name ) && ! move_uploaded_file( $_FILES['data']['tmp_name'][ $field_name ], $file_path ) ) {
1075 WPF()->notice->add( 'Sorry, there was an error uploading attached file', 'error' );
1076 }
1077 }
1078 }
1079 }
1080
1081 public function get_custom_field( $userid, $field_name ) {
1082 $field_value = '';
1083 if( $userid ) {
1084 $sql = WPF()->db->prepare( "SELECT `fields` FROM `" . WPF()->tables->profiles . "` WHERE userid = %d", $userid );
1085 $fields = WPF()->db->get_var( $sql );
1086 if( $fields ) {
1087 $data = (array) json_decode( $fields, true );
1088 if( ! empty( $data ) ) {
1089 $data = wpforo_unslashe( $data );
1090 if( wpfkey( $data, $field_name ) ) {
1091 $field_value = $data[ $field_name ];
1092 }
1093 }
1094 }
1095 }
1096
1097 return $field_value;
1098 }
1099
1100 public function get_custom_fields( $userid ) {
1101 $data = [];
1102 if( $userid ) {
1103 $sql = WPF()->db->prepare( "SELECT `fields` FROM `" . WPF()->tables->profiles . "` WHERE userid = %d", $userid );
1104 $fields = WPF()->db->get_var( $sql );
1105 if( $fields ) {
1106 $data = (array) json_decode( $fields, true );
1107 }
1108 $data = wpforo_unslashe( $data );
1109 }
1110
1111 return $data;
1112 }
1113
1114 public function change_password( $old_passw, $new_passw, $userid ) {
1115 if( ! ( $userid = wpforo_bigintval( $userid ) ) || ! ( $user = $this->get_member( $userid ) ) ) {
1116 WPF()->notice->clear();
1117 WPF()->notice->add( 'Userid is wrong', 'error' );
1118
1119 return false;
1120 }
1121
1122 $user['user_pass'] = ( $userdata = get_userdata( $userid ) ) ? $userdata->user_pass : '';
1123
1124 if( ! apply_filters( 'wpforo_change_password_validate', true, $old_passw, $new_passw, $user ) ) return false;
1125
1126 if( wp_check_password( $old_passw, $user['user_pass'], $userid ) ) {
1127 wp_set_password( $new_passw, $userid );
1128 if( ! wpforo_is_owner( $userid ) ) $this->inactive_to_active( $userid );
1129
1130 /**
1131 * Login user after change password with new pass
1132 */
1133 if( wpforo_is_owner( $userid ) ) {
1134 wp_signon( [ 'user_login' => sanitize_user( $user['user_login'] ), 'user_password' => $new_passw ] );
1135 }
1136
1137 WPF()->notice->add( 'Password successfully changed', 'success' );
1138
1139 return true;
1140 }
1141
1142 WPF()->notice->clear();
1143 WPF()->notice->add( 'Old password is wrong', 'error' );
1144
1145 return false;
1146 }
1147
1148 public function get_status( $userid ) {
1149 return WPF()->db->get_var(
1150 WPF()->db->prepare(
1151 "SELECT `status`
1152 FROM " . WPF()->tables->profiles . "
1153 WHERE `userid` = %d",
1154 $userid
1155 )
1156 );
1157 }
1158
1159 public function inactive_to_active( $userid, $user_login = '', $raw = false ) {
1160 if( $this->get_status( $userid ) === 'inactive' ) {
1161 if( $raw || ! wpforo_setting( 'authorization', 'manually_approval' ) ) {
1162 $this->update_profile_fields( $userid, [ 'status' => 'active' ], false );
1163 } else {
1164 wp_safe_redirect( wpforo_url( '', 'cantlogin' ) );
1165 exit();
1166 }
1167 }
1168 }
1169
1170 public function synchronize_user( $userid, $roles_usergroups = [] ) {
1171 $groupid = false;
1172 if( ! $userid || ! ( $user = get_userdata( $userid ) ) ) return false;
1173 $user->roles = (array) $user->roles;
1174
1175 //Don't synchronize User Roles with Usergroups if the option is disabled
1176 if( wpforo_setting( 'authorization', 'role_synch' ) ) {
1177 if( ! $roles_usergroups ) $roles_usergroups = WPF()->usergroup->get_role_usergroup_relation();
1178 if( ! empty( $roles_usergroups ) && ! empty( $user->roles ) ) {
1179 foreach( $user->roles as $role ) {
1180 if( isset( $roles_usergroups[ $role ] ) ) {
1181 $groupid = $roles_usergroups[ $role ];
1182 break;
1183 }
1184 }
1185 }
1186 }
1187
1188 if( ! $groupid ) {
1189 if( is_super_admin( $userid ) || in_array( 'administrator', $user->roles ) ) {
1190 $groupid = 1;
1191 } elseif( in_array( 'editor', $user->roles ) ) {
1192 $groupid = 2;
1193 } elseif( in_array( 'customer', $user->roles ) ) {
1194 $groupid = 5;
1195 } else {
1196 $groupid = WPF()->usergroup->default_groupid;
1197 }
1198 }
1199 $insert_groupid = ( isset( $_POST['wpforo_usergroup'] ) && ! wpforo_setting( 'authorization', 'role_synch' ) ) ? intval( $_POST['wpforo_usergroup'] ) : $groupid;
1200 $insert_timezone = ( isset( $_POST['wpforo_usertimezone'] ) ) ? sanitize_text_field( $_POST['wpforo_usertimezone'] ) : '';
1201 $about = get_user_meta( $userid, 'description', true );
1202 $return = $this->add_profile( [
1203 'userid' => wpforo_bigintval( $userid ),
1204 'groupid' => intval( $insert_groupid ),
1205 'timezone' => sanitize_text_field( $insert_timezone ),
1206 'about' => stripslashes( wpforo_kses( trim( (string) $about ), 'user_description' ) ),
1207 ] );
1208
1209 if( $return !== false && ( $secondary_groupids = wpfval( $_POST, 'wpforo_secondary_groupids' ) ) ) {
1210 $this->set_secondary_groupids( $userid, $secondary_groupids );
1211 }
1212
1213 return $return;
1214 }
1215
1216 public function synchronize_users( $limit = null ) {
1217
1218 if( is_multisite() ) {
1219 $sql = "SELECT `user_id` FROM `" . WPF()->db->usermeta . "` WHERE `meta_key` LIKE '" . WPF()->blog_prefix . "capabilities' AND `user_id` NOT IN( SELECT `userid` FROM `" . WPF(
1220 )->tables->profiles . "` ) ORDER BY `user_id` ASC";
1221 } else {
1222 $sql = "SELECT `ID` as user_id FROM `" . WPF()->db->users . "` WHERE `ID` NOT IN( SELECT `userid` FROM `" . WPF()->tables->profiles . "` ) ORDER BY `ID` ASC";
1223 }
1224 if( ! is_null( $limit ) ) {
1225 $sql .= " LIMIT " . intval( $limit );
1226 }
1227
1228 $userids = WPF()->db->get_col( $sql );
1229 if( ! empty( $userids ) ) {
1230 $roles_usergroups = WPF()->usergroup->get_role_usergroup_relation();
1231 foreach( $userids as $userid ) {
1232 $this->synchronize_user( $userid, $roles_usergroups );
1233 }
1234
1235 return false;
1236 }
1237
1238 ## -- START -- delete profiles where not participant on multisite blog
1239 if( is_multisite() ) {
1240 $sql = "DELETE FROM `" . WPF()->tables->profiles . "` WHERE `userid` NOT IN( SELECT `user_id` FROM `" . WPF()->db->usermeta . "` WHERE `meta_key` LIKE '" . WPF(
1241 )->blog_prefix . "capabilities' )";
1242 WPF()->db->query( $sql );
1243 }
1244
1245 ## -- END -- delete profiles where not participant on multisite blog
1246
1247 return true;
1248 }
1249
1250 public function _get_member( $args ) {
1251 if( ! $args ) return $this->get_guest();
1252
1253 $default = [
1254 'userid' => null, // $userid
1255 'user_nicename' => '', // $user_nicename
1256 ];
1257
1258 if( wpforo_is_id( $args ) ) {
1259 $args = [ 'userid' => $args ];
1260 } elseif( ! is_array( $args ) ) {
1261 $args = [ 'user_nicename' => $args ];
1262 }
1263
1264 $args = wpforo_parse_args( $args, $default );
1265
1266 extract( $args );
1267
1268 $userid = wpforo_bigintval( $userid );
1269
1270 $user_meta_obj = true;
1271 if( $user_nicename ) {
1272 $user_obj = get_user_by( 'user_nicename', $user_nicename );
1273 if( ! empty( $user_obj ) ) $userid = $user_obj->ID;
1274 }
1275 $member = get_user_meta( $userid, '_wpf_member_obj', true );
1276
1277 if( empty( $member ) ) {
1278 $user_meta_obj = false;
1279 $sql = "SELECT *,
1280 ug.`name` AS group_name,
1281 ug.`color` AS group_color,
1282 fn.`meta_value` AS first_name,
1283 ln.`meta_value` AS last_name
1284 FROM `" . WPF()->db->users . "` u
1285 INNER JOIN `" . WPF()->tables->profiles . "` p ON p.`userid` = u.`ID`
1286 LEFT JOIN `" . WPF()->tables->usergroups . "` ug ON ug.`groupid` = p.`groupid`
1287 LEFT JOIN `" . WPF()->db->usermeta . "` fn ON fn.`user_id` = u.`ID` AND fn.`meta_key` LIKE 'first_name'
1288 LEFT JOIN `" . WPF()->db->usermeta . "` ln ON ln.`user_id` = u.`ID` AND ln.`meta_key` LIKE 'last_name'";
1289 $wheres = [];
1290 if( $userid ) $wheres[] = "`ID` = $userid";
1291 if( $user_nicename ) $wheres[] = "`user_nicename` = '" . esc_sql( $user_nicename ) . "'";
1292 if( ! empty( $wheres ) ) $sql .= " WHERE " . implode( " AND ", $wheres );
1293 $member = WPF()->db->get_row( $sql, ARRAY_A );
1294 }
1295
1296 if( ! empty( $member ) && ! $user_meta_obj ) update_user_meta( $userid, '_wpf_member_obj', $member );
1297
1298 if( $member ) $member = $this->decode( $member );
1299
1300 return $member;
1301 }
1302
1303 public function get_member( $args ) {
1304 return wpforo_ram_get( [ $this, '_get_member' ], $args );
1305 }
1306
1307 public function get_members( $args = [], &$items_count = 0 ) {
1308 $default = [
1309 'include' => [], // array( 2, 10, 25 )
1310 'exclude' => [], // array( 2, 10, 25 )
1311 'status' => [ 'active', 'inactive', 'banned' ], // 'active', 'blocked', 'trashed', 'spamer'
1312 'groupid' => null, // groupid
1313 'online_time' => null, // groupid
1314 'orderby' => 'userid', //
1315 'order' => 'ASC', // ASC DESC
1316 'offset' => 0, // OFFSET
1317 'row_count' => null, // ROW COUNT
1318 'groupids' => [], // array( 1, 2 )
1319 ];
1320
1321 $args = wpforo_parse_args( $args, $default );
1322 extract( $args, EXTR_OVERWRITE );
1323
1324 $include = wpforo_parse_args( $include );
1325 $exclude = wpforo_parse_args( $exclude );
1326
1327 $sql = "SELECT *,
1328 ug.`name` AS group_name,
1329 ug.`color` AS group_color,
1330 fn.`meta_value` AS first_name,
1331 ln.`meta_value` AS last_name
1332 FROM `" . WPF()->db->users . "` u
1333 INNER JOIN `" . WPF()->tables->profiles . "` p ON p.`userid` = u.`ID`
1334 LEFT JOIN `" . WPF()->tables->usergroups . "` ug ON ug.`groupid` = p.`groupid`
1335 LEFT JOIN `" . WPF()->db->usermeta . "` fn ON fn.`user_id` = u.`ID` AND fn.`meta_key` LIKE 'first_name'
1336 LEFT JOIN `" . WPF()->db->usermeta . "` ln ON ln.`user_id` = u.`ID` AND fn.`meta_key` LIKE 'last_name'";
1337
1338 $sql_count = "SELECT count(*)
1339 FROM `" . WPF()->db->users . "` u
1340 INNER JOIN `" . WPF()->tables->profiles . "` p ON p.`userid` = u.`ID`
1341 LEFT JOIN `" . WPF()->tables->usergroups . "` ug ON ug.`groupid` = p.`groupid`";
1342
1343
1344 $wheres = [];
1345 if( ! empty( $include ) ) $wheres[] = " u.`ID` IN(" . implode( ', ', array_map( 'intval', $include ) ) . ")";
1346 if( ! empty( $exclude ) ) $wheres[] = " u.`ID` NOT IN(" . implode( ', ', array_map( 'intval', $exclude ) ) . ")";
1347 if( ! empty( $status ) ) $wheres[] = " p.`status` IN('" . implode( "','", array_map( 'esc_sql', array_map( 'sanitize_text_field', $status ) ) ) . "')";
1348 if( ! empty( $groupids ) ) {
1349 $wheres[] = " (p.`groupid` IN(" . implode( ', ', array_map( 'intval', $groupids ) ) . ") OR CONCAT(',', p.`secondary_groupids`, ',') REGEXP ',(" . implode(
1350 '|',
1351 array_map(
1352 'intval',
1353 $groupids
1354 )
1355 ) . "),' )";
1356 }
1357 if( ! is_null( $groupid ) ) $wheres[] = " (p.`groupid` = " . intval( $groupid ) . " OR FIND_IN_SET(" . intval( $groupid ) . ", p.`secondary_groupids`) ) ";
1358 if( ! is_null( $online_time ) ) $wheres[] = " p.`online_time` > " . intval( $online_time );
1359
1360 if( ! empty( $wheres ) ) {
1361 $sql .= " WHERE " . implode( " AND ", $wheres );
1362 $sql_count .= " WHERE " . implode( " AND ", $wheres );
1363 }
1364
1365 $item_count_sql = preg_replace( '#ORDER.+$#is', '', $sql_count );
1366 if( $item_count_sql ) $items_count = WPF()->db->get_var( $item_count_sql );
1367
1368 if( $orderby === 'groupid' ) $orderby = 'p.`groupid`';
1369 $sql .= esc_sql( " ORDER BY $orderby " . $order );
1370 if( $row_count ) $sql .= esc_sql( " LIMIT $offset,$row_count" );
1371
1372 return array_map( [ $this, 'decode' ], WPF()->db->get_results( $sql, ARRAY_A ) );
1373 }
1374
1375 public function search( $needle, $fields = [], $limit = null, $orderby = null ) {
1376 if( $needle ) {
1377 $needle = sanitize_text_field( $needle );
1378 if( empty( $fields ) ) {
1379 $fields = [
1380 'title',
1381 'user_nicename',
1382 'user_email',
1383 'signature',
1384 ];
1385 }
1386
1387 $sql = "SELECT `ID` FROM `" . WPF()->db->users . "` u
1388 INNER JOIN `" . WPF()->tables->profiles . "` p ON p.`userid` = u.`ID`";
1389 $wheres = [];
1390
1391 foreach( $fields as $field ) {
1392 $f = $this->get_field( $field );
1393 $field = sanitize_text_field( $field );
1394 if( $f['isDefault'] ) {
1395 if( $f['fieldKey'] === 'title' ) {
1396 $wheres[] = "`" . esc_sql( $field ) . "` LIKE '" . esc_sql( $needle ) . "'";
1397 } else {
1398 $wheres[] = "`" . esc_sql( $field ) . "` LIKE '%" . esc_sql( $needle ) . "%'";
1399 }
1400 } else {
1401 $needle = preg_quote( preg_quote( $needle ) );
1402 if( in_array( $f['type'], [ 'text', 'search', 'textarea' ], true ) ) {
1403 $wheres[] = "`fields` REGEXP '[{,]\"" . $field . "\":(\\\[[^\\\[]*)?\"[^\"]*" . esc_sql( $needle ) . "[^\"]*\"'";
1404 } else {
1405 $wheres[] = "`fields` REGEXP '[{,]\"" . $field . "\":(\\\[[^\\\[]*)?\"" . esc_sql( $needle ) . "\"'";
1406 }
1407 }
1408 }
1409
1410 if( ! empty( $wheres ) ) {
1411 $sql .= " WHERE " . implode( " OR ", $wheres );
1412 if( ! is_null( $orderby ) ) {
1413 $sql .= " ORDER BY " . implode( ', ', (array) $orderby );
1414 }
1415 if( $limit ) $sql .= " LIMIT " . intval( $limit );
1416
1417 return WPF()->db->get_col( $sql );
1418 } else {
1419 return [];
1420 }
1421 } else {
1422 return [];
1423 }
1424
1425 }
1426
1427 public function filter( $args, $limit = null, $orderby = null ) {
1428 if( $args && is_array( $args ) ) {
1429 $sql = "SELECT `ID` FROM `" . WPF()->db->users . "` u
1430 INNER JOIN `" . WPF()->tables->profiles . "` p ON p.`userid` = u.`ID`";
1431 $wheres = [];
1432
1433 foreach( $args as $field => $needle ) {
1434 $f = $this->get_field( $field );
1435 $field = sanitize_text_field( $field );
1436 if( $f['isDefault'] ) {
1437 if( is_scalar( $needle ) ) {
1438 $needle = sanitize_text_field( $needle );
1439 $wheres[] = "`" . esc_sql( $field ) . "` LIKE '%" . esc_sql( $needle ) . "%'";
1440 } elseif( is_array( $needle ) ) {
1441 foreach( $needle as $n ) {
1442 $n = sanitize_text_field( $n );
1443 $wheres[] = "`" . esc_sql( $field ) . "` LIKE '%" . esc_sql( $n ) . "%'";
1444 }
1445 }
1446 } else {
1447 if( in_array( $f['type'], [ 'text', 'search', 'textarea' ], true ) ) {
1448 if( is_scalar( $needle ) ) {
1449 $needle = preg_quote( preg_quote( wpforo_encode( $needle ) ) );
1450 $wheres[] = "`fields` REGEXP '[{,]\"" . $field . "\":(\\\[[^\\\[]*)?\"[^\"]*" . esc_sql( $needle ) . "[^\"]*\"'";
1451 } elseif( is_array( $needle ) ) {
1452 foreach( $needle as $n ) {
1453 $n = preg_quote( preg_quote( wpforo_encode( $n ) ) );
1454 $wheres[] = "`fields` REGEXP '[{,]\"" . $field . "\":(\\\[[^\\\[]*)?\"[^\"]*" . esc_sql( $n ) . "[^\"]*\"'";
1455 }
1456 }
1457 } else {
1458 if( is_scalar( $needle ) ) {
1459 $needle = preg_quote( preg_quote( wpforo_encode( $needle ) ) );
1460 $wheres[] = "`fields` REGEXP '[{,]\"" . $field . "\":(\\\[[^\\\[]*)?\"" . esc_sql( $needle ) . "\"'";
1461 } elseif( is_array( $needle ) ) {
1462 foreach( $needle as $n ) {
1463 $n = preg_quote( preg_quote( wpforo_encode( $n ) ) );
1464 $wheres[] = "`fields` REGEXP '[{,]\"" . $field . "\":(\\\[[^\\\[]*)?\"" . esc_sql( $n ) . "\"'";
1465 }
1466 }
1467 }
1468 }
1469 }
1470
1471 if( $wheres ) {
1472 $sql .= " WHERE " . implode( " AND ", $wheres );
1473 if( ! is_null( $orderby ) ) {
1474 $sql .= " ORDER BY " . implode( ', ', (array) $orderby );
1475 }
1476 if( $limit ) $sql .= " LIMIT " . intval( $limit );
1477
1478 return WPF()->db->get_col( $sql );
1479 }
1480 }
1481
1482 return [];
1483 }
1484
1485 public function ban( $userid ) {
1486 if( $userid == WPF()->current_userid ) {
1487 WPF()->notice->add( 'You can\'t make yourself banned user', 'error' );
1488
1489 return false;
1490 }
1491 if( ! WPF()->usergroup->can( 'bm' ) || ! WPF()->perm->user_can_manage_user( WPF()->current_userid, intval( $userid ) ) ) {
1492 WPF()->notice->add( 'Permission denied for this action', 'error' );
1493
1494 return false;
1495 }
1496 if( false !== WPF()->db->update( WPF()->tables->profiles, [ 'status' => 'banned' ], [ 'userid' => intval( $userid ) ], [ '%s' ], [ '%d' ] ) ) {
1497 do_action( 'wpforo_after_ban_user', $userid );
1498 $this->reset( $userid );
1499 WPF()->notice->add( 'User successfully banned from wpforo', 'success' );
1500
1501 return true;
1502 }
1503
1504 WPF()->notice->add( 'User ban action error', 'error' );
1505
1506 return false;
1507 }
1508
1509 public function unban( $userid ) {
1510 if( ! WPF()->usergroup->can( 'bm' ) || ! WPF()->perm->user_can_manage_user( WPF()->current_userid, intval( $userid ) ) ) {
1511 WPF()->notice->add( 'Permission denied for this action', 'error' );
1512
1513 return false;
1514 }
1515 if( false !== WPF()->db->update( WPF()->tables->profiles, [ 'status' => 'active' ], [ 'userid' => intval( $userid ) ], [ '%s' ], [ '%d' ] ) ) {
1516 do_action( 'wpforo_after_unban_user', $userid );
1517 $this->reset( $userid );
1518 WPF()->notice->add( 'User successfully unbanned from wpforo', 'success' );
1519
1520 return true;
1521 }
1522
1523 WPF()->notice->add( 'User unban action error', 'error' );
1524
1525 return false;
1526 }
1527
1528 public function activate( $userid ) {
1529 if( false !== WPF()->db->update( WPF()->tables->profiles, [ 'status' => 'active' ], [ 'userid' => intval( $userid ) ], [ '%s' ], [ '%d' ] ) ) {
1530 do_action( 'wpforo_after_activate_user', $userid );
1531 $this->reset( $userid );
1532 WPF()->notice->add( 'User successfully activated from wpforo', 'success' );
1533
1534 return true;
1535 }
1536
1537 WPF()->notice->add( 'User activate action error', 'error' );
1538
1539 return false;
1540 }
1541
1542 public function deactivate( $userid ) {
1543 if( false !== WPF()->db->update( WPF()->tables->profiles, [ 'status' => 'inactive' ], [ 'userid' => intval( $userid ) ], [ '%s' ], [ '%d' ] ) ) {
1544 do_action( 'wpforo_after_deactivate_user', $userid );
1545 $this->reset( $userid );
1546 WPF()->notice->add( 'User successfully deactivated from wpforo', 'success' );
1547
1548 return true;
1549 }
1550
1551 WPF()->notice->add( 'User deactivate action error', 'error' );
1552
1553 return false;
1554 }
1555
1556 /**
1557 *
1558 * @param int $userid
1559 * @param int $reassign
1560 *
1561 * @return bool true | false if user successfully deleted
1562 */
1563 public function delete( $userid, $reassign = null ) {
1564 if( ! ( $userid = wpforo_bigintval( $userid ) ) ) return false;
1565 do_action( 'wpforo_before_delete_user', $userid, $reassign );
1566
1567 if( false !== WPF()->db->delete( WPF()->tables->profiles, [ 'userid' => $userid ], [ '%d' ] ) ) {
1568 do_action( 'wpforo_after_delete_user', $userid, $reassign );
1569
1570 WPF()->notice->add( 'User successfully deleted', 'success' );
1571
1572 return true;
1573 }
1574
1575 WPF()->notice->add( 'User delete error', 'error' );
1576
1577 return false;
1578 }
1579
1580 /**
1581 * @deprecated since 2.1.6 version instead of this use $this->_get_avatar() method
1582 */
1583 public function _avatar( $user, $attr = '', $size = 96 ) {
1584 return $this->_get_avatar( $user, $size, $attr );
1585 }
1586
1587 /**
1588 * @deprecated since 2.1.6 version instead of this use $this->get_avatar() method
1589 */
1590 public function avatar( $user, $attr = '', $size = 96 ) {
1591 return wpforo_ram_get( [ $this, '_avatar' ], $user, $attr, $size );
1592 }
1593
1594 public function _get_avatar( $user, $size = 96, $attr = '' ) {
1595 return $this->get_avatar_html( $this->get_avatar_url( $user ), $user, $size, $attr );
1596 }
1597
1598 public function get_avatar( $user, $size = 96, $attr = '' ) {
1599 return wpforo_ram_get( [ $this, '_get_avatar' ], $user, $size, $attr );
1600 }
1601
1602 public function _get_avatar_url( $user ) {
1603 $cache = WPF()->cache->on( 'avatar' );
1604
1605 if( is_scalar( $user ) ) {
1606 $userid = wpforo_bigintval( $user );
1607 $cache_avatar = apply_filters( 'wpforo_avatar_cache', true, $userid );
1608 if( $cache && $cache_avatar && $avatar_url = WPF()->cache->get_item( $userid, 'avatar' ) ) {
1609 return str_replace( '#', '', (string) $avatar_url );
1610 }
1611 $avatar_url = (string) WPF()->db->get_var( WPF()->db->prepare( "SELECT `avatar` FROM `" . WPF()->tables->profiles . "` WHERE `userid` = %d", wpforo_bigintval( $userid ) ) );
1612 } else {
1613 $avatar_url = (string) wpfval( $user, 'avatar' );
1614 $userid = wpforo_bigintval( wpfval( $user, 'userid' ) );
1615 }
1616
1617 if( $cache && $userid ) {
1618 WPF()->cache->create( 'item', [ $userid => $avatar_url ?: '#' ], 'avatar' );
1619 }
1620
1621 return apply_filters( 'wpforo_avatar_url', $avatar_url, $userid );
1622 }
1623
1624 public function get_avatar_url( $user ) {
1625 return wpforo_ram_get( [ $this, '_get_avatar_url' ], $user );
1626 }
1627
1628 public function get_avatar_html( $url, $user = [], $size = 96, $attr = '' ) {
1629 $size = intval( $size );
1630 if( $url && wpforo_setting( 'profiles', 'custom_avatars' ) ) {
1631 $url = apply_filters( 'wpforo_avatar_url', $url, $user );
1632 $dname = ! is_scalar( $user ) ? wpforo_user_dname( $user ) : $this->get_member( $user )['dname'];
1633 if( strpos( (string) $attr, 'alt=' ) === false ) $attr .= ' ' . sprintf( 'alt="%1$s"', esc_attr( $dname ) );
1634 if( strpos( (string) $attr, 'title=' ) === false ) $attr .= ' ' . sprintf( 'title="%1$s"', esc_attr( $dname ) );
1635 if( strpos( (string) $attr, 'height=' ) === false ) $attr .= ' ' . sprintf( 'height="%1$d"', $size );
1636 if( strpos( (string) $attr, 'width=' ) === false ) $attr .= ' ' . sprintf( 'width="%1$d"', $size );
1637 $img = '<img class="avatar" src="' . esc_url( (string) $url ) . '" ' . $attr . ' >';
1638 } else {
1639 $userid = is_scalar( $user ) ? wpforo_bigintval( $user ) : ( wpforo_bigintval( wpfval( $user, 'userid' ) ) ?: (string) wpfval( $user, 'user_email' ) );
1640 $img = get_avatar( $userid, $size );
1641 if( $attr ) $img = str_replace( '<img', '<img ' . $attr, $img );
1642 }
1643
1644 return $img;
1645 }
1646
1647 /**
1648 * @param array|int|string $arg
1649 * @param string $template
1650 *
1651 * @return string
1652 */
1653 public function get_profile_url( $arg, $template = 'profile', $ram_cache = true ) {
1654 $user = is_scalar( $arg ) ? ( $ram_cache ? $this->get_member( intval( basename( (string) $arg ) ) ) : $this->_get_member(
1655 intval( basename( (string) $arg ) )
1656 ) ) ?: [ 'user_nicename' => basename( (string) $arg ) ] : $arg;
1657 if( wpfkey( $user, 'userid' ) && wpfkey( $user, 'user_nicename' ) ) {
1658 $user_slug = ( wpforo_setting( 'profiles', 'url_structure' ) === 'id' ? $user['userid'] : $user['user_nicename'] );
1659 if( $template === 'profile' ) {
1660 $profile_url = wpforo_url( $user_slug, 'member' );
1661 } else {
1662 $template_slug = wpforo_settings_get_slug( $template );
1663 $profile_url = wpforo_url( "$user_slug/$template_slug", 'member' );
1664 }
1665 } else {
1666 $profile_url = wpforo_home_url();
1667 }
1668
1669 return apply_filters( 'wpforo_member_profile_url', $profile_url, $user, $template );
1670 }
1671
1672 /**
1673 * @param float $points
1674 *
1675 * @return array
1676 */
1677 public function calc_rating( float $points ): array {
1678 $rating = $this->default->member['rating'];
1679
1680 $rating['level'] = $this->rating_level( floatval( $points ), false );
1681 $rating['percent'] = $rating['level'] * 10;
1682 $rating['title'] = $this->rating( $rating['level'], 'title' );
1683 $rating['color'] = $this->rating( $rating['level'], 'color' );
1684 $rating['badge'] = $this->rating( $rating['level'], 'icon' );
1685
1686 return $rating;
1687 }
1688
1689 /**
1690 * @param $member
1691 *
1692 * @return float
1693 */
1694 private function calc_points( $member ): float {
1695 $topic_points = intval( $member['topics'] ) * wpforo_setting( 'rating', 'topic_points' );
1696 $post_points = intval( $member['posts'] ) * wpforo_setting( 'rating', 'post_points' );
1697 $like_points = intval( wpfval( $member, 'reactions_in', 'up' ) ) * wpforo_setting( 'rating', 'like_points' );
1698 $dislike_points = intval( wpfval( $member, 'reactions_in', 'down' ) ) * wpforo_setting( 'rating', 'dislike_points' );
1699
1700 $topic_points = (float) apply_filters( 'wpforo_member_get_points_topic_points', $topic_points, $member );
1701 $post_points = (float) apply_filters( 'wpforo_member_get_points_post_points', $post_points, $member );
1702 $like_points = (float) apply_filters( 'wpforo_member_get_points_like_points', $like_points, $member );
1703 $dislike_points = (float) apply_filters( 'wpforo_member_get_points_dislike_points', $dislike_points, $member );
1704 $other_points = (float) apply_filters( 'wpforo_member_get_points_other_points', 0, $member );
1705
1706 return $topic_points + $post_points + $like_points + $dislike_points + $other_points;
1707 }
1708
1709 public function get_count( $args = [] ): int {
1710 $sql = "SELECT SQL_NO_CACHE COUNT(*) FROM `" . WPF()->tables->profiles . "` p
1711 INNER JOIN `" . WPF()->db->users . "` u ON u.`ID` = p.`userid` WHERE p.`status` NOT LIKE 'trashed'";
1712 if( $args ) {
1713 $wheres = [];
1714 foreach( $args as $key => $value ) {
1715 if( is_array( $value ) ) {
1716 $wheres[] = "$key IN('" . implode( "','", array_map( 'esc_sql', $value ) ) . "')";
1717 } else {
1718 $wheres[] = "$key = '" . esc_sql( $value ) . "'";
1719 }
1720 }
1721 if( $wheres ) $sql .= " AND " . implode( ' AND ', $wheres );
1722 }
1723
1724 return (int) WPF()->db->get_var( $sql );
1725 }
1726
1727 public function _is_online( $userid, $duration = null ) {
1728 if( ! $duration ) $duration = wpforo_setting( 'profiles', 'online_status_timeout' );
1729 $sql = "SELECT `online_time` FROM `" . WPF()->tables->profiles . "` WHERE `userid` = %d";
1730 $sql = WPF()->db->prepare( $sql, $userid );
1731 $online_time = intval( WPF()->db->get_var( $sql ) );
1732 $current_time = time();
1733
1734 return ( $current_time - $online_time ) < $duration;
1735 }
1736
1737 public function is_online( $userid, $duration = null ) {
1738 return wpforo_ram_get( [ $this, '_is_online' ], $userid, $duration );
1739 }
1740
1741 public function show_online_indicator( $userid, $ico = true ) {
1742 if( $this->is_online( $userid ) ) : ?>
1743
1744 <?php if( $ico ) : ?>
1745 <i class="fas fa-circle wpfsx wpfcl-8" title="<?php wpforo_phrase( 'Online' ) ?>"></i>
1746 <?php else : wpforo_phrase( 'Online' ); endif ?>
1747
1748 <?php else : ?>
1749
1750 <?php if( $ico ) : ?>
1751 <i class="fas fa-circle wpfsx wpfcl-0" title="<?php wpforo_phrase( 'Offline' ) ?>"></i>
1752 <?php else : wpforo_phrase( 'Offline' ); endif ?>
1753
1754 <?php endif;
1755 }
1756
1757 public function online_members_count( $duration = null ) {
1758 if( ! ( $duration = intval( $duration ) ) ) $duration = (int) wpforo_setting( 'profiles', 'online_status_timeout' );
1759 $online_timeframe = time() - $duration;
1760 $key = [ 'member', 'online_members_count', $online_timeframe ];
1761 if( WPF()->ram_cache->exists( $key ) ) return WPF()->ram_cache->get( $key );
1762 $sql = "SELECT COUNT(DISTINCT `userid`, `ip`) AS total FROM `" . WPF()->tables->visits . "` WHERE `time` > %d";
1763 $var = (int) WPF()->db->get_var( WPF()->db->prepare( $sql, $online_timeframe ) );
1764 if( ! $var ) {
1765 $sql = "SELECT COUNT(*) FROM `" . WPF()->tables->profiles . "` WHERE `online_time` > %d";
1766 $var = (int) WPF()->db->get_var( WPF()->db->prepare( $sql, $online_timeframe ) );
1767 }
1768 WPF()->ram_cache->set( $key, $var );
1769
1770 return $var;
1771 }
1772
1773 public function get_online_members( $count = 1, $groupids = [], $duration = null ) {
1774 if( ! $duration ) $duration = wpforo_setting( 'profiles', 'online_status_timeout' );
1775 $current_time = time();
1776 $online_timeframe = $current_time - $duration;
1777 $groupids = array_filter( wpforo_parse_args( $groupids ) );
1778 $args = [
1779 'groupids' => $groupids,
1780 'online_time' => $online_timeframe, // $current_time - $duration
1781 'orderby' => 'userid', // forumid, order, parentid
1782 'row_count' => $count,
1783 'order' => 'ASC', // ASC DESC
1784 ];
1785
1786 return $this->get_members( $args );
1787 }
1788
1789 public function levels() {
1790 $max_rating_levels = (int) apply_filters( 'wpforo_max_rating_levels', 10 );
1791
1792 return range( 0, $max_rating_levels );
1793 }
1794
1795 public function rating( $level = null, $var = '' ) {
1796 if( is_null( $level ) ) return wpforo_setting( 'rating', 'levels' );
1797 $_levels = wpforo_setting( 'rating', 'levels' );
1798 if( ! wpfkey( $_levels, $level ) ) $level = 0;
1799 if( ! $var ) return wpforo_setting( 'rating', 'levels', $level );
1800
1801 return wpforo_setting( 'rating', 'levels', $level, $var );
1802 }
1803
1804 /**
1805 * @param int $points
1806 * @param bool $percent
1807 *
1808 * @return int
1809 */
1810 public function rating_level( $points, $percent = true ) {
1811 $points = intval( $points );
1812 $max_rating_levels = (int) apply_filters( 'wpforo_max_rating_levels', 10 );
1813 $increment = 100 / $max_rating_levels;
1814 for( $level = 1; $level <= $max_rating_levels; $level ++ ) {
1815 if( $points < $this->rating( $level, 'points' ) ) {
1816 $bar = ( $level - 1 ) * $increment;
1817 break;
1818 }
1819 }
1820 // If the points are greater than or equal to the highest level's points, set $bar to 100
1821 if( ! isset( $bar ) ) $bar = 100;
1822
1823 return $percent ? $bar : (int) floor( $bar / ( 100 / $max_rating_levels ) );
1824 }
1825
1826
1827 public function rating_badge( $level = 0, $view = 'short' ) {
1828
1829 $max_rating_levels = (int) apply_filters( 'wpforo_max_rating_levels', 10 );
1830 $level = ( $level > $max_rating_levels ) ? floor( $level / $max_rating_levels ) : $level;
1831
1832 if( $level === 0 ) {
1833 return '<i class="' . $this->rating( $level, 'icon' ) . '"></i>';
1834 } elseif( $level > 0 && $level < 6 ) {
1835 if( $view == 'full' ) {
1836 return str_repeat( ' <i class="' . $this->rating( $level, 'icon' ) . '"></i> ', $level );
1837 } else {
1838 return '<span>' . esc_html( $level ) . '</span> <i class="' . $this->rating( $level, 'icon' ) . '"></i>';
1839 }
1840 } elseif( $level > 5 && $level < 9 ) {
1841 if( $view == 'full' ) {
1842 return str_repeat( ' <i class="' . $this->rating( $level, 'icon' ) . '"></i> ', ( $level - 5 ) );
1843 } else {
1844 return '<span>' . esc_html( $level - 5 ) . '</span> <i class="' . $this->rating( $level, 'icon' ) . '"></i>';
1845 }
1846 }
1847
1848 if( $max_rating_levels > 10 ) {
1849 if( $level < 12 ) {
1850 if( $view == 'full' ) {
1851 return str_repeat( ' <i class="' . $this->rating( $level, 'icon' ) . '"></i> ', ( $level - 8 ) );
1852 } else {
1853 return '<span>' . esc_html( $level - 8 ) . '</span> <i class="' . $this->rating( $level, 'icon' ) . '"></i>';
1854 }
1855 } elseif( $level < 15 ) {
1856 if( $view == 'full' ) {
1857 return str_repeat( ' <i class="' . $this->rating( $level, 'icon' ) . '"></i> ', ( $level - 11 ) );
1858 } else {
1859 return '<span>' . esc_html( $level - 11 ) . '</span> <i class="' . $this->rating( $level, 'icon' ) . '"></i>';
1860 }
1861 }
1862 } else {
1863 if( $level > 8 ) {
1864 return '<i class="' . $this->rating( $level, 'icon' ) . '"></i>';
1865 }
1866 }
1867
1868 return '';
1869 }
1870
1871 public function reset( $userid ) {
1872 if( ! $userid ) return;
1873 wpforo_clean_cache( 'avatar', $userid );
1874 delete_user_meta( intval( $userid ), '_wpf_member_obj' );
1875 if( wpforo_setting( 'seo', 'seo_profile' ) ) WPF()->seo->clear_cache();
1876 }
1877
1878 public function clear_db_cache() {
1879 WPF()->db->query( "DELETE FROM `" . WPF()->db->usermeta . "` WHERE `meta_key` = '_wpf_member_obj'" );
1880 }
1881
1882 private function update_online_time( $userid = null ) {
1883 if( ! $userid ) $userid = WPF()->current_userid;
1884 if( ! $userid ) return false;
1885 $current_timestamp = time();
1886 $sql = "UPDATE `" . WPF()->tables->profiles . "` SET `online_time` = %d WHERE `userid` = %d";
1887 $sql = WPF()->db->prepare( $sql, $current_timestamp, wpforo_bigintval( $userid ) );
1888 if( false !== WPF()->db->query( $sql ) ) return $current_timestamp;
1889
1890 return false;
1891 }
1892
1893 public function init_current_user() {
1894 WPF()->wp_current_user = $current_user = wp_get_current_user();
1895 if( $current_user->exists() ) {
1896 WPF()->current_userid = $current_user->ID;
1897 WPF()->current_user_login = $current_user->user_login;
1898 WPF()->current_user_email = $current_user->user_email;
1899 WPF()->current_user_display_name = $current_user->display_name;
1900
1901 $user = $this->get_member( $current_user->ID );
1902 if( ! wpfkey( $user, 'groupid' ) ) {
1903 $this->synchronize_user( $current_user->ID );
1904 $user = $this->get_member( $current_user->ID );
1905 }
1906 $user_meta = get_user_meta( $current_user->ID );
1907 WPF()->current_user = $user;
1908 WPF()->current_usermeta = $user_meta;
1909 WPF()->current_user_groupid = WPF()->current_user['groupid'];
1910 WPF()->current_user_secondary_groupids = WPF()->current_user['secondary_groupids'];
1911 WPF()->current_user_groupids = WPF()->current_user['groupids'];
1912 WPF()->current_user_status = (string) wpfval( $user, 'status' );
1913 $this->update_online_time();
1914 } elseif( $guest = $this->get_guest_cookies() ) {
1915 WPF()->current_userid = 0;
1916 WPF()->current_user_login = '';
1917 WPF()->current_user_email = $guest['email'];
1918 WPF()->current_user_display_name = $guest['name'];
1919 WPF()->current_user = $this->get_guest( $guest );
1920 WPF()->current_usermeta = [];
1921 WPF()->current_user_groupid = 4;
1922 WPF()->current_user_groupids = [ 4 ];
1923 WPF()->current_user_secondary_groupids = [];
1924 WPF()->current_user_status = '';
1925 WPF()->current_user_accesses = [];
1926 }
1927
1928 WPF()->usergroup->init_current();
1929
1930 if( function_exists( 'wp_get_session_token' ) && function_exists( 'wp_parse_auth_cookie' ) ) WPF()->session_token = wp_get_session_token();
1931 if( ! WPF()->session_token ) {
1932 $secret_key = defined( 'SECRET_KEY' ) && SECRET_KEY ? SECRET_KEY : 'wpforo';
1933 WPF()->session_token = hash_hmac(
1934 'sha256',
1935 md5( (string) wpfval( $_SERVER, 'HTTP_USER_AGENT' ) ) . md5( (string) wpfval( $_SERVER, 'REMOTE_ADDR' ) ) . md5(
1936 (string) wpfval( $_SERVER, 'SERVER_ADDR' )
1937 ),
1938 $secret_key
1939 );
1940 }
1941
1942 do_action( 'wpforo_after_init_current_user', WPF()->current_user, WPF()->current_usermeta, WPF()->current_user_groupid, WPF()->current_user_secondary_groupids, WPF()->current_user_groupids );
1943 }
1944
1945 public function blog_posts( $userid ) {
1946 if( isset( $userid ) && $userid ) return count_user_posts( $userid );
1947
1948 return 0;
1949 }
1950
1951 public function blog_comments( $userid, $user_email ) {
1952 global $wpdb;
1953 if( ! $userid || ! $user_email ) return 0;
1954
1955 return (int) $wpdb->get_var( "SELECT COUNT(*) FROM " . $wpdb->comments . " WHERE `user_id` = " . intval( $userid ) . " OR `comment_author_email` = '" . esc_sql( $user_email ) . "'" );
1956 }
1957
1958 public function show_delete_form( $current_user, $userids ) {
1959 if( empty( $current_user ) || empty( $userids ) ) return;
1960
1961 $userids = array_diff( $userids, [ $current_user->ID ] );
1962 $users_have_content = false;
1963 if( WPF()->db->get_var( "SELECT `postid` FROM `" . WPF()->tables->posts . "` WHERE `userid` IN( " . implode( ',', array_map( 'intval', $userids ) ) . " ) LIMIT 1" ) ) {
1964 $users_have_content = true;
1965 }
1966 ?>
1967 <hr/><strong>#wpForo</strong>
1968 <?php if( ! $users_have_content ) : ?>
1969 <input type="hidden" name="wpforo_user_delete_option" value="delete"/>
1970 <?php else: ?>
1971 <?php if( 1 == count( $userids ) ) : ?>
1972 <fieldset>
1973 <legend><?php _e( 'What should be done with wpForo content owned by this user?', 'wpforo' ); ?></legend>
1974 <?php else : ?>
1975 <fieldset>
1976 <legend><?php _e( 'What should be done with wpForo content owned by these users?', 'wpforo' ); ?></legend>
1977 <?php endif; ?>
1978 <ul style="list-style:none;">
1979 <li><label><input type="radio" id="wpforo_delete_option0" name="wpforo_user_delete_option" value="delete">
1980 <?php _e( 'Delete all wpForo content.', 'wpforo' ); ?></label></li>
1981 <li><input type="radio" id="wpforo_delete_option1" name="wpforo_user_delete_option" value="reassign">
1982 <label for="wpforo_delete_option1"><?php _e( 'Attribute all content to:' ) ?></label>
1983 <?php
1984 wp_dropdown_users( [
1985 'name' => 'wpforo_reassign_userid',
1986 'exclude' => $userids,
1987 'show' => 'display_name_with_login',
1988 ] );
1989 ?>
1990 </li>
1991 </ul></fieldset>
1992 <script type="text/javascript">
1993 jQuery(document).ready(function ($) {
1994 $('#wpforo_reassign_userid').on('focus', function () {
1995 $('#wpforo_delete_option1').prop('checked', true).trigger('change');
1996 });
1997 });
1998 </script>
1999 <?php endif;
2000 }
2001
2002 public function autoban( $userid ) {
2003 if( ! WPF()->usergroup->can( 'em' ) ) {
2004 WPF()->db->update( WPF()->tables->profiles, [ 'status' => 'banned' ], [ 'userid' => intval( $userid ) ], [ '%s' ], [ '%d' ] );
2005 }
2006 }
2007
2008 public function member_approved_posts( $member = [] ) {
2009 if( is_numeric( $member ) ) {
2010 if( $userid = wpforo_bigintval( $member ) ) {
2011 if( $userid === WPF()->current_userid ) return WPF()->current_user['posts'];
2012
2013 return (int) WPF()->db->get_var(
2014 WPF()->db->prepare(
2015 "SELECT COUNT(*) as posts
2016 FROM `" . WPF()->tables->posts . "`
2017 WHERE `status` = 0
2018 AND `userid` = %d",
2019 $userid
2020 )
2021 );
2022 }
2023 }
2024
2025 return (int) wpfval( $member, 'posts' );
2026 }
2027
2028 public function current_user_is_new() {
2029 if( WPF()->usergroup->can( 'em' ) ) {
2030 //This is an admin or moderator. The number of posts doesn't matter.
2031 return false;
2032 } else {
2033 $new_user_max_posts = wpforo_setting( 'antispam', 'new_user_max_posts' );
2034 $posts = $this->member_approved_posts( WPF()->current_userid );
2035 if( $new_user_max_posts && $posts <= $new_user_max_posts ) {
2036 return true;
2037 } else {
2038 return false;
2039 }
2040 }
2041 }
2042
2043 /**
2044 * @return int
2045 */
2046 public function banned_count() {
2047 $key = [ 'member', 'banned_count' ];
2048 if( WPF()->ram_cache->exists( $key ) ) return WPF()->ram_cache->get( $key );
2049 $var = (int) WPF()->db->get_var( "SELECT count(*) FROM `" . WPF()->tables->profiles . "` WHERE `status` = 'banned'" );
2050 WPF()->ram_cache->set( $key, $var );
2051
2052 return $var;
2053 }
2054
2055 public function _get_guest_posts( $email ) {
2056 $sqls = [];
2057 foreach( WPF()->get_active_boards_tables( 'posts' ) as $table ) {
2058 $sqls[] = WPF()->db->prepare(
2059 "SELECT *
2060 FROM `$table`
2061 WHERE `status` = 0
2062 AND `email` = %s",
2063 $email
2064 );
2065 }
2066
2067 if( $sqls ) {
2068 return (array) WPF()->db->get_results(
2069 implode( ' UNION ALL ', $sqls ) . " ORDER BY `created` ASC, `postid` ASC",
2070 ARRAY_A
2071 );
2072 }
2073
2074 return [];
2075 }
2076
2077 public function get_guest_posts( $email ) {
2078 return wpforo_ram_get( [ $this, '_get_guest_posts' ], $email );
2079 }
2080
2081 public function _get_guest( $args = [] ) {
2082 if( ! wpfval( $args, 'name' ) ) $args['name'] = wpforo_phrase( 'Anonymous', false );
2083 if( ! wpfval( $args, 'email' ) ) $args['email'] = 'anonymous@example.com';
2084
2085 $args['name'] = strip_tags( (string) $args['name'] );
2086 $args['email'] = sanitize_email( $args['email'] );
2087 $args['user_registered'] = current_time( 'mysql', 1 );
2088 $args['posts'] = 0;
2089
2090 if( $args['email'] && $args['email'] !== 'anonymous@example.com' ) {
2091 if( $posts = $this->get_guest_posts( $args['email'] ) ) {
2092 $args['posts'] = count( $posts );
2093 if( $first_post_created = wpfval( $posts, 0, 'created' ) ) $args['user_registered'] = $first_post_created;
2094 }
2095 }
2096
2097 $guest = wpforo_array_args_cast_and_merge(
2098 [
2099 'groupid' => 4,
2100 'group_name' => wpforo_phrase( 'Guest', false ),
2101 'user_login' => $args['name'],
2102 'user_nicename' => sanitize_text_field( $args['name'] ),
2103 'user_email' => $args['email'],
2104 'user_registered' => $args['user_registered'],
2105 'display_name' => $args['name'],
2106 'posts' => $args['posts'],
2107 'status' => 'active',
2108 ],
2109 $this->default->member
2110 );
2111
2112 return $this->decode( $guest );
2113 }
2114
2115 public function get_guest( $args = [] ) {
2116 return wpforo_ram_get( [ $this, '_get_guest' ], $args );
2117 }
2118
2119 public function init_fields() {
2120 if( ! empty( $this->fields ) ) return;
2121
2122 $this->init_countries();
2123 $this->init_timezones();
2124
2125 $this->fields = apply_filters( 'wpforo_member_before_init_fields', $this->fields );
2126
2127 $usergroupids = [];
2128 $usergroups = WPF()->usergroup->get_usergroups();
2129 foreach( $usergroups as $usergroup ) {
2130 $usergroupids[] = $usergroup['groupid'];
2131 }
2132 $usergroupids_can_edit_fields = WPF()->usergroup->get_groupids_by_can( 'em' );
2133 $usergroupids_can_view_social_net = WPF()->usergroup->get_groupids_by_can( 'vmsn' );
2134
2135 /**
2136 * start init tinymce settings
2137 */
2138 $wp_editor_settings = WPF()->tpl->editor_buttons();
2139 $wp_editor_settings['tinymce']['toolbar1'] = 'bold,italic,link,unlink,undo,redo,source_code,emoticons';
2140 $wp_editor_settings['plugins'] = '';
2141 unset(
2142 $wp_editor_settings['external_plugins']['wpforo_pre_button'], $wp_editor_settings['external_plugins']['wpforo_spoiler_button']
2143 );
2144 $wp_editor_settings = apply_filters( 'wpforo_members_init_fields_tinymce_settings', $wp_editor_settings );
2145
2146 $this->fields['user_login'] = [
2147 'fieldKey' => 'user_login',
2148 'type' => 'text',
2149 'isDefault' => 1,
2150 'isRemovable' => 0,
2151 'isRequired' => 1,
2152 'isEditable' => 0,
2153 'label' => wpforo_phrase( 'Username', false ),
2154 'title' => wpforo_phrase( 'Username', false ),
2155 'placeholder' => wpforo_phrase( 'Username', false ),
2156 'description' => wpforo_phrase( 'Length must be between 3 characters and 15 characters.', false ),
2157 'minLength' => 3,
2158 'maxLength' => 30,
2159 'faIcon' => 'fas fa-user',
2160 'name' => 'user_login',
2161 'cantBeInactive' => [ 'register' ],
2162 'canEdit' => [],
2163 'canView' => WPF()->usergroup->get_groupids_by_can( 'vmu' ),
2164 'can' => 'vmu',
2165 'isSearchable' => 0,
2166 ];
2167
2168 $this->fields['user_email'] = [
2169 'fieldKey' => 'user_email',
2170 'type' => 'email',
2171 'isDefault' => 1,
2172 'isRemovable' => 0,
2173 'isRequired' => 1,
2174 'isEditable' => 1,
2175 'label' => wpforo_phrase( 'Email', false ),
2176 'title' => wpforo_phrase( 'Email', false ),
2177 'placeholder' => wpforo_phrase( 'Email', false ),
2178 'minLength' => 0,
2179 'maxLength' => 0,
2180 'faIcon' => 'fas fa-envelope',
2181 'name' => 'user_email',
2182 'cantBeInactive' => [ 'register' ],
2183 'canEdit' => $usergroupids_can_edit_fields,
2184 'canView' => WPF()->usergroup->get_groupids_by_can( 'vmm' ),
2185 'can' => 'vmm',
2186 'isSearchable' => 1,
2187 ];
2188
2189 $this->fields['user_pass'] = [
2190 'fieldKey' => 'user_pass',
2191 'type' => 'password',
2192 'isDefault' => 1,
2193 'isRemovable' => 0,
2194 'isRequired' => 1,
2195 'isEditable' => 1,
2196 'label' => wpforo_phrase( 'Password', false ),
2197 'title' => wpforo_phrase( 'Password', false ),
2198 'placeholder' => wpforo_phrase( 'Password', false ),
2199 'description' => wpforo_phrase( 'Must be minimum 6 characters.', false ),
2200 'minLength' => 6,
2201 'maxLength' => 20,
2202 'faIcon' => 'fas fa-key',
2203 'name' => 'user_pass',
2204 'cantBeInactive' => [
2205 'account',
2206 'register',
2207 ],
2208 'canEdit' => $usergroupids_can_edit_fields,
2209 'canView' => [],
2210 'can' => '',
2211 'isSearchable' => 0,
2212 ];
2213
2214 $this->fields['display_name'] = [
2215 'fieldKey' => 'display_name',
2216 'type' => 'text',
2217 'isDefault' => 1,
2218 'isRemovable' => 0,
2219 'isRequired' => 1,
2220 'isEditable' => 1,
2221 'label' => wpforo_phrase( 'Display Name', false ),
2222 'title' => wpforo_phrase( 'Display Name', false ),
2223 'placeholder' => wpforo_phrase( 'Display Name', false ),
2224 'faIcon' => 'fas fa-user',
2225 'name' => 'display_name',
2226 'cantBeInactive' => [],
2227 'canEdit' => $usergroupids_can_edit_fields,
2228 'canView' => $usergroupids,
2229 'can' => '',
2230 'isSearchable' => 1,
2231 ];
2232
2233 $this->fields['user_nicename'] = [
2234 'fieldKey' => 'user_nicename',
2235 'type' => 'text',
2236 'isDefault' => 1,
2237 'isRemovable' => 0,
2238 'isRequired' => 1,
2239 'isEditable' => 1,
2240 'label' => wpforo_phrase( 'Nickname', false ),
2241 'title' => wpforo_phrase( 'Nickname', false ),
2242 'placeholder' => wpforo_phrase( 'Nickname', false ),
2243 'description' => wpforo_phrase( 'URL Address Identifier', false ),
2244 'minLength' => 0,
2245 'maxLength' => 0,
2246 'faIcon' => 'fas fa-link',
2247 'name' => 'user_nicename',
2248 'cantBeInactive' => [],
2249 'canEdit' => $usergroupids_can_edit_fields,
2250 'canView' => $usergroupids,
2251 'can' => '',
2252 'isSearchable' => 1,
2253 ];
2254
2255 $this->fields['first_name'] = [
2256 'fieldKey' => 'first_name',
2257 'type' => 'text',
2258 'isDefault' => 1,
2259 'isRemovable' => 0,
2260 'isRequired' => 0,
2261 'isEditable' => 1,
2262 'label' => wpforo_phrase( 'First Name', false ),
2263 'title' => wpforo_phrase( 'First Name', false ),
2264 'placeholder' => wpforo_phrase( 'First Name', false ),
2265 'faIcon' => 'fas fa-address-card',
2266 'name' => 'first_name',
2267 'cantBeInactive' => [],
2268 'canEdit' => $usergroupids_can_edit_fields,
2269 'canView' => $usergroupids,
2270 'can' => '',
2271 'isSearchable' => 0,
2272 ];
2273
2274 $this->fields['last_name'] = [
2275 'fieldKey' => 'last_name',
2276 'type' => 'text',
2277 'isDefault' => 1,
2278 'isRemovable' => 0,
2279 'isRequired' => 0,
2280 'isEditable' => 1,
2281 'label' => wpforo_phrase( 'Last Name', false ),
2282 'title' => wpforo_phrase( 'Last Name', false ),
2283 'placeholder' => wpforo_phrase( 'Last Name', false ),
2284 'faIcon' => 'fas fa-address-card',
2285 'name' => 'last_name',
2286 'cantBeInactive' => [],
2287 'canEdit' => $usergroupids_can_edit_fields,
2288 'canView' => $usergroupids,
2289 'can' => '',
2290 'isSearchable' => 0,
2291 ];
2292
2293 $this->fields['title'] = [
2294 'fieldKey' => 'title',
2295 'type' => 'text',
2296 'isDefault' => 1,
2297 'isRemovable' => 0,
2298 'isRequired' => 1,
2299 'isEditable' => 1,
2300 'label' => wpforo_phrase( 'Title', false ),
2301 'title' => wpforo_phrase( 'Title', false ),
2302 'placeholder' => wpforo_phrase( 'Title', false ),
2303 'minLength' => 0,
2304 'maxLength' => 0,
2305 'faIcon' => 'fas fa-user',
2306 'name' => 'title',
2307 'cantBeInactive' => [],
2308 'canEdit' => $usergroupids_can_edit_fields,
2309 'canView' => WPF()->usergroup->get_groupids_by_can( 'vmt' ),
2310 'can' => 'vmt',
2311 'isSearchable' => 1,
2312 ];
2313
2314 $this->fields['groupid'] = [
2315 'fieldKey' => 'groupid',
2316 'type' => 'usergroup',
2317 'isDefault' => 1,
2318 'isRemovable' => 0,
2319 'isRequired' => 1,
2320 'isEditable' => 1,
2321 'label' => wpforo_phrase( 'User Group', false ),
2322 'title' => wpforo_phrase( 'User Group', false ),
2323 'placeholder' => wpforo_phrase( 'User Group', false ),
2324 'faIcon' => 'fas fa-users',
2325 'name' => 'groupid',
2326 'allowedGroupIds' => [ 3, 5 ],
2327 'cantBeInactive' => [],
2328 'canEdit' => $usergroupids_can_edit_fields,
2329 'canView' => $usergroupids,
2330 'can' => '',
2331 'isSearchable' => 1,
2332 ];
2333
2334 $this->fields['secondary_groupids'] = [
2335 'fieldKey' => 'secondary_groupids',
2336 'type' => 'secondary_groups',
2337 'isWrapItem' => 1,
2338 'isDefault' => 1,
2339 'isRemovable' => 0,
2340 'isRequired' => 0,
2341 'isEditable' => 1,
2342 'label' => wpforo_phrase( 'User Groups Secondary', false ),
2343 'title' => wpforo_phrase( 'User Groups Secondary', false ),
2344 'placeholder' => '',
2345 'faIcon' => '',
2346 'values' => '',
2347 'name' => 'secondary_groupids',
2348 'allowedGroupIds' => WPF()->usergroup->get_secondary_groupids(),
2349 'cantBeInactive' => [],
2350 'canEdit' => $usergroupids_can_edit_fields,
2351 'canView' => $usergroupids,
2352 'can' => '',
2353 'isSearchable' => 1,
2354 ];
2355
2356 $this->fields['avatar'] = [
2357 'fieldKey' => 'avatar',
2358 'type' => 'avatar',
2359 'isDefault' => 1,
2360 'isRemovable' => 0,
2361 'isRequired' => 0,
2362 'isEditable' => 1,
2363 'label' => wpforo_phrase( 'Avatar', false ),
2364 'title' => wpforo_phrase( 'Avatar', false ),
2365 'placeholder' => wpforo_phrase( 'Avatar', false ),
2366 'name' => 'avatar',
2367 'cantBeInactive' => [],
2368 'canEdit' => $usergroupids_can_edit_fields,
2369 'canView' => WPF()->usergroup->get_groupids_by_can( 'va' ),
2370 'can' => 'va',
2371 'isSearchable' => 0,
2372 ];
2373
2374 $this->fields['user_url'] = [
2375 'fieldKey' => 'user_url',
2376 'type' => 'url',
2377 'isDefault' => 1,
2378 'isRemovable' => 0,
2379 'isRequired' => 0,
2380 'isEditable' => 1,
2381 'label' => wpforo_phrase( 'Website', false ),
2382 'title' => wpforo_phrase( 'Website', false ),
2383 'placeholder' => wpforo_phrase( 'Website', false ),
2384 'faIcon' => 'fas fa-sitemap',
2385 'name' => 'user_url',
2386 'cantBeInactive' => [],
2387 'canEdit' => $usergroupids_can_edit_fields,
2388 'canView' => WPF()->usergroup->get_groupids_by_can( 'vmw' ),
2389 'can' => 'vmw',
2390 'isSearchable' => 1,
2391 ];
2392
2393 $this->fields['facebook'] = [
2394 'fieldKey' => 'facebook',
2395 'type' => 'url',
2396 'isDefault' => 0,
2397 'isRemovable' => 1,
2398 'isRequired' => 0,
2399 'isEditable' => 1,
2400 'label' => wpforo_phrase( 'Facebook', false ),
2401 'title' => wpforo_phrase( 'Facebook', false ),
2402 'placeholder' => wpforo_phrase( 'Facebook', false ),
2403 'faIcon' => 'fab fa-facebook',
2404 'name' => 'facebook',
2405 'cantBeInactive' => [],
2406 'canEdit' => $usergroupids_can_edit_fields,
2407 'canView' => $usergroupids_can_view_social_net,
2408 'can' => 'vmsn',
2409 'isSearchable' => 1,
2410 ];
2411
2412 $this->fields['twitter'] = [
2413 'fieldKey' => 'twitter',
2414 'type' => 'url',
2415 'isDefault' => 0,
2416 'isRemovable' => 1,
2417 'isRequired' => 0,
2418 'isEditable' => 1,
2419 'label' => wpforo_phrase( 'X.com', false ),
2420 'title' => wpforo_phrase( 'X.com', false ),
2421 'placeholder' => wpforo_phrase( 'X.com', false ),
2422 'faIcon' => 'fa-brands fa-square-x-twitter',
2423 'name' => 'twitter',
2424 'cantBeInactive' => [],
2425 'canEdit' => $usergroupids_can_edit_fields,
2426 'canView' => $usergroupids_can_view_social_net,
2427 'can' => 'vmsn',
2428 'isSearchable' => 1,
2429 ];
2430
2431 $this->fields['youtube'] = [
2432 'fieldKey' => 'youtube',
2433 'type' => 'url',
2434 'isDefault' => 0,
2435 'isRemovable' => 1,
2436 'isRequired' => 0,
2437 'isEditable' => 1,
2438 'label' => wpforo_phrase( 'YouTube', false ),
2439 'title' => wpforo_phrase( 'YouTube', false ),
2440 'placeholder' => wpforo_phrase( 'YouTube', false ),
2441 'faIcon' => 'fab fa-youtube',
2442 'name' => 'youtube',
2443 'cantBeInactive' => [],
2444 'canEdit' => $usergroupids_can_edit_fields,
2445 'canView' => $usergroupids_can_view_social_net,
2446 'can' => 'vmsn',
2447 'isSearchable' => 1,
2448 ];
2449
2450 $this->fields['vkontakte'] = [
2451 'fieldKey' => 'vkontakte',
2452 'type' => 'url',
2453 'isDefault' => 0,
2454 'isRemovable' => 1,
2455 'isRequired' => 0,
2456 'isEditable' => 1,
2457 'label' => wpforo_phrase( 'VKontakte', false ),
2458 'title' => wpforo_phrase( 'VKontakte', false ),
2459 'placeholder' => wpforo_phrase( 'VKontakte', false ),
2460 'faIcon' => 'fab fa-vk',
2461 'name' => 'vkontakte',
2462 'cantBeInactive' => [],
2463 'canEdit' => $usergroupids_can_edit_fields,
2464 'canView' => $usergroupids_can_view_social_net,
2465 'can' => 'vmsn',
2466 'isSearchable' => 1,
2467 ];
2468
2469 $this->fields['linkedin'] = [
2470 'fieldKey' => 'linkedin',
2471 'type' => 'url',
2472 'isDefault' => 0,
2473 'isRemovable' => 1,
2474 'isRequired' => 0,
2475 'isEditable' => 1,
2476 'label' => wpforo_phrase( 'LinkedIn', false ),
2477 'title' => wpforo_phrase( 'LinkedIn', false ),
2478 'placeholder' => wpforo_phrase( 'LinkedIn', false ),
2479 'faIcon' => 'fab fa-linkedin-in',
2480 'name' => 'linkedin',
2481 'cantBeInactive' => [],
2482 'canEdit' => $usergroupids_can_edit_fields,
2483 'canView' => $usergroupids_can_view_social_net,
2484 'can' => 'vmsn',
2485 'isSearchable' => 1,
2486 ];
2487
2488 $this->fields['telegram'] = [
2489 'fieldKey' => 'telegram',
2490 'type' => 'text',
2491 'isDefault' => 0,
2492 'isRemovable' => 1,
2493 'isRequired' => 0,
2494 'isEditable' => 1,
2495 'label' => wpforo_phrase( 'Telegram', false ),
2496 'title' => wpforo_phrase( 'Telegram', false ),
2497 'placeholder' => wpforo_phrase( 'Telegram', false ),
2498 'faIcon' => 'fab fa-telegram-plane',
2499 'name' => 'telegram',
2500 'cantBeInactive' => [],
2501 'canEdit' => $usergroupids_can_edit_fields,
2502 'canView' => $usergroupids_can_view_social_net,
2503 'can' => 'vmsn',
2504 'isSearchable' => 1,
2505 ];
2506
2507 $this->fields['instagram'] = [
2508 'fieldKey' => 'instagram',
2509 'type' => 'url',
2510 'isDefault' => 0,
2511 'isRemovable' => 1,
2512 'isRequired' => 0,
2513 'isEditable' => 1,
2514 'label' => wpforo_phrase( 'Instagram', false ),
2515 'title' => wpforo_phrase( 'Instagram', false ),
2516 'placeholder' => wpforo_phrase( 'Instagram', false ),
2517 'faIcon' => 'fab fa-instagram',
2518 'name' => 'instagram',
2519 'cantBeInactive' => [],
2520 'canEdit' => $usergroupids_can_edit_fields,
2521 'canView' => $usergroupids_can_view_social_net,
2522 'can' => 'vmsn',
2523 'isSearchable' => 1,
2524 ];
2525
2526 $this->fields['skype'] = [
2527 'fieldKey' => 'skype',
2528 'type' => 'text',
2529 'isDefault' => 0,
2530 'isRemovable' => 1,
2531 'isRequired' => 0,
2532 'isEditable' => 1,
2533 'label' => wpforo_phrase( 'Skype', false ),
2534 'title' => wpforo_phrase( 'Skype', false ),
2535 'placeholder' => wpforo_phrase( 'Skype', false ),
2536 'faIcon' => 'fab fa-skype',
2537 'name' => 'skype',
2538 'cantBeInactive' => [],
2539 'canEdit' => $usergroupids_can_edit_fields,
2540 'canView' => $usergroupids_can_view_social_net,
2541 'can' => 'vmsn',
2542 'isSearchable' => 1,
2543 ];
2544
2545 $this->fields['location'] = [
2546 'fieldKey' => 'location',
2547 'type' => 'select',
2548 'isDefault' => 1,
2549 'isRemovable' => 0,
2550 'isRequired' => 0,
2551 'isEditable' => 1,
2552 'label' => wpforo_phrase( 'Location', false ),
2553 'title' => wpforo_phrase( 'Location', false ),
2554 'placeholder' => wpforo_phrase( 'Location', false ),
2555 'faIcon' => 'fas fa-globe',
2556 'values' => $this->countries,
2557 'name' => 'location',
2558 'cantBeInactive' => [],
2559 'canEdit' => $usergroupids_can_edit_fields,
2560 'canView' => WPF()->usergroup->get_groupids_by_can( 'vml' ),
2561 'can' => 'vml',
2562 'isSearchable' => 1,
2563 ];
2564
2565 $this->fields['timezone'] = [
2566 'fieldKey' => 'timezone',
2567 'type' => 'select',
2568 'isDefault' => 1,
2569 'isRemovable' => 0,
2570 'isRequired' => 0,
2571 'isEditable' => 1,
2572 'label' => wpforo_phrase( 'Timezone', false ),
2573 'title' => wpforo_phrase( 'Timezone', false ),
2574 'placeholder' => wpforo_phrase( 'Timezone', false ),
2575 'faIcon' => 'fas fa-globe',
2576 'values' => $this->timezones,
2577 'name' => 'timezone',
2578 'cantBeInactive' => [],
2579 'canEdit' => $usergroupids_can_edit_fields,
2580 'canView' => $usergroupids,
2581 'can' => '',
2582 'isSearchable' => 1,
2583 ];
2584
2585 $this->fields['occupation'] = [
2586 'fieldKey' => 'occupation',
2587 'type' => 'text',
2588 'isDefault' => 1,
2589 'isRemovable' => 0,
2590 'isRequired' => 0,
2591 'isEditable' => 1,
2592 'label' => wpforo_phrase( 'Occupation', false ),
2593 'title' => wpforo_phrase( 'Occupation', false ),
2594 'placeholder' => wpforo_phrase( 'Occupation', false ),
2595 'faIcon' => 'fas fa-address-card',
2596 'name' => 'occupation',
2597 'cantBeInactive' => [],
2598 'canEdit' => $usergroupids_can_edit_fields,
2599 'canView' => WPF()->usergroup->get_groupids_by_can( 'vmo' ),
2600 'can' => 'vmo',
2601 'isSearchable' => 1,
2602 ];
2603
2604 $this->fields['signature'] = [
2605 'fieldKey' => 'signature',
2606 'type' => 'tinymce',
2607 'wp_editor_settings' => $wp_editor_settings,
2608 'isDefault' => 1,
2609 'isRemovable' => 0,
2610 'isRequired' => 0,
2611 'isEditable' => 1,
2612 'label' => wpforo_phrase( 'Signature', false ),
2613 'title' => wpforo_phrase( 'Signature', false ),
2614 'placeholder' => wpforo_phrase( 'Signature', false ),
2615 'faIcon' => 'fas fa-address-card',
2616 'name' => 'signature',
2617 'cantBeInactive' => [],
2618 'canEdit' => $usergroupids_can_edit_fields,
2619 'canView' => WPF()->usergroup->get_groupids_by_can( 'vms' ),
2620 'can' => 'vms',
2621 'isSearchable' => 1,
2622 ];
2623
2624 $this->fields['about'] = [
2625 'fieldKey' => 'about',
2626 'type' => 'tinymce',
2627 'wp_editor_settings' => $wp_editor_settings,
2628 'isDefault' => 1,
2629 'isRemovable' => 0,
2630 'isRequired' => 0,
2631 'isEditable' => 1,
2632 'label' => wpforo_phrase( 'About Me', false ),
2633 'title' => wpforo_phrase( 'About Me', false ),
2634 'placeholder' => wpforo_phrase( 'About Me', false ),
2635 'faIcon' => 'fas fa-address-card',
2636 'name' => 'about',
2637 'cantBeInactive' => [],
2638 'canEdit' => $usergroupids_can_edit_fields,
2639 'canView' => WPF()->usergroup->get_groupids_by_can( 'vmam' ),
2640 'can' => 'vmam',
2641 'isSearchable' => 1,
2642 ];
2643
2644 $this->fields['html_soc_net'] = [
2645 'fieldKey' => 'html_soc_net',
2646 'type' => 'html',
2647 'isDefault' => 1,
2648 'isRemovable' => 0,
2649 'isRequired' => 0,
2650 'isEditable' => 1,
2651 'label' => wpforo_phrase( 'Social Networks', false ),
2652 'title' => wpforo_phrase( 'Social Networks', false ),
2653 'placeholder' => wpforo_phrase( 'Social Networks', false ),
2654 'description' => wpforo_phrase( 'Social Networks', false ),
2655 'html' => '<div class="wpf-label">' . wpforo_phrase( 'Social Networks', false ) . '</div>',
2656 'name' => 'html_soc_net',
2657 'cantBeInactive' => [],
2658 'canEdit' => $usergroupids_can_edit_fields,
2659 'canView' => $usergroupids_can_view_social_net,
2660 'can' => 'vmsn',
2661 'isSearchable' => 0,
2662 ];
2663
2664 $this->fields = apply_filters( 'wpforo_member_after_init_fields', $this->fields );
2665 }
2666
2667 private function init_countries() {
2668 $this->countries = [
2669 "Afghanistan",
2670 "�
2671 land Islands",
2672 "Albania",
2673 "Algeria",
2674 "American Samoa",
2675 "Andorra",
2676 "Angola",
2677 "Anguilla",
2678 "Antarctica",
2679 "Antigua and Barbuda",
2680 "Argentina",
2681 "Armenia",
2682 "Aruba",
2683 "Australia",
2684 "Austria",
2685 "Azerbaijan",
2686 "Bahamas",
2687 "Bahrain",
2688 "Bangladesh",
2689 "Barbados",
2690 "Belarus",
2691 "Belgium",
2692 "Belize",
2693 "Benin",
2694 "Bermuda",
2695 "Bhutan",
2696 "Bolivia",
2697 "Bosnia and Herzegovina",
2698 "Botswana",
2699 "Bouvet Island",
2700 "Brazil",
2701 "British Indian Ocean Territory",
2702 "Brunei Darussalam",
2703 "Bulgaria",
2704 "Burkina Faso",
2705 "Burundi",
2706 "Cambodia",
2707 "Cameroon",
2708 "Canada",
2709 "Cape Verde",
2710 "Cayman Islands",
2711 "Central African Republic",
2712 "Chad",
2713 "Chile",
2714 "China",
2715 "Christmas Island",
2716 "Cocos (Keeling) Islands",
2717 "Colombia",
2718 "Comoros",
2719 "Congo",
2720 "Congo, The Democratic Republic of The",
2721 "Cook Islands",
2722 "Costa Rica",
2723 "Cote D'ivoire",
2724 "Croatia",
2725 "Cuba",
2726 "Cyprus",
2727 "Czech Republic",
2728 "Denmark",
2729 "Djibouti",
2730 "Dominica",
2731 "Dominican Republic",
2732 "Ecuador",
2733 "Egypt",
2734 "El Salvador",
2735 "Equatorial Guinea",
2736 "Eritrea",
2737 "Estonia",
2738 "Ethiopia",
2739 "Falkland Islands (Malvinas)",
2740 "Faroe Islands",
2741 "Fiji",
2742 "Finland",
2743 "France",
2744 "French Guiana",
2745 "French Polynesia",
2746 "French Southern Territories",
2747 "Gabon",
2748 "Gambia",
2749 "Georgia",
2750 "Germany",
2751 "Ghana",
2752 "Gibraltar",
2753 "Greece",
2754 "Greenland",
2755 "Grenada",
2756 "Guadeloupe",
2757 "Guam",
2758 "Guatemala",
2759 "Guernsey",
2760 "Guinea",
2761 "Guinea-bissau",
2762 "Guyana",
2763 "Haiti",
2764 "Heard Island and Mcdonald Islands",
2765 "Holy See (Vatican City State)",
2766 "Honduras",
2767 "Hong Kong",
2768 "Hungary",
2769 "Iceland",
2770 "India",
2771 "Indonesia",
2772 "Iran, Islamic Republic of",
2773 "Iraq",
2774 "Ireland",
2775 "Isle of Man",
2776 "Israel",
2777 "Italy",
2778 "Jamaica",
2779 "Japan",
2780 "Jersey",
2781 "Jordan",
2782 "Kazakhstan",
2783 "Kenya",
2784 "Kiribati",
2785 "Korea, Democratic People's Republic of",
2786 "Korea, Republic of",
2787 "Kuwait",
2788 "Kyrgyzstan",
2789 "Lao People's Democratic Republic",
2790 "Latvia",
2791 "Lebanon",
2792 "Lesotho",
2793 "Liberia",
2794 "Libyan Arab Jamahiriya",
2795 "Liechtenstein",
2796 "Lithuania",
2797 "Luxembourg",
2798 "Macao",
2799 "Macedonia, The Former Yugoslav Republic of",
2800 "Madagascar",
2801 "Malawi",
2802 "Malaysia",
2803 "Maldives",
2804 "Mali",
2805 "Malta",
2806 "Marshall Islands",
2807 "Martinique",
2808 "Mauritania",
2809 "Mauritius",
2810 "Mayotte",
2811 "Mexico",
2812 "Micronesia, Federated States of",
2813 "Moldova, Republic of",
2814 "Monaco",
2815 "Mongolia",
2816 "Montenegro",
2817 "Montserrat",
2818 "Morocco",
2819 "Mozambique",
2820 "Myanmar",
2821 "Namibia",
2822 "Nauru",
2823 "Nepal",
2824 "Netherlands",
2825 "Netherlands Antilles",
2826 "New Caledonia",
2827 "New Zealand",
2828 "Nicaragua",
2829 "Niger",
2830 "Nigeria",
2831 "Niue",
2832 "Norfolk Island",
2833 "Northern Mariana Islands",
2834 "Norway",
2835 "Oman",
2836 "Pakistan",
2837 "Palau",
2838 "Palestinian Territory, Occupied",
2839 "Panama",
2840 "Papua New Guinea",
2841 "Paraguay",
2842 "Peru",
2843 "Philippines",
2844 "Pitcairn",
2845 "Poland",
2846 "Portugal",
2847 "Puerto Rico",
2848 "Qatar",
2849 "Reunion",
2850 "Romania",
2851 "Russian Federation",
2852 "Rwanda",
2853 "Saint Helena",
2854 "Saint Kitts and Nevis",
2855 "Saint Lucia",
2856 "Saint Pierre and Miquelon",
2857 "Saint Vincent and The Grenadines",
2858 "Samoa",
2859 "San Marino",
2860 "Sao Tome and Principe",
2861 "Saudi Arabia",
2862 "Senegal",
2863 "Serbia",
2864 "Seychelles",
2865 "Sierra Leone",
2866 "Singapore",
2867 "Slovakia",
2868 "Slovenia",
2869 "Solomon Islands",
2870 "Somalia",
2871 "South Africa",
2872 "South Georgia and The South Sandwich Islands",
2873 "Spain",
2874 "Sri Lanka",
2875 "Sudan",
2876 "Suriname",
2877 "Svalbard and Jan Mayen",
2878 "Swaziland",
2879 "Sweden",
2880 "Switzerland",
2881 "Syrian Arab Republic",
2882 "Taiwan, Province of China",
2883 "Tajikistan",
2884 "Tanzania, United Republic of",
2885 "Thailand",
2886 "Timor-leste",
2887 "Togo",
2888 "Tokelau",
2889 "Tonga",
2890 "Trinidad and Tobago",
2891 "Tunisia",
2892 "Turkey",
2893 "Turkmenistan",
2894 "Turks and Caicos Islands",
2895 "Tuvalu",
2896 "Uganda",
2897 "Ukraine",
2898 "United Arab Emirates",
2899 "United Kingdom",
2900 "United States",
2901 "United States Minor Outlying Islands",
2902 "Uruguay",
2903 "Uzbekistan",
2904 "Vanuatu",
2905 "Venezuela",
2906 "Viet Nam",
2907 "Virgin Islands, British",
2908 "Virgin Islands, U.S.",
2909 "Wallis and Futuna",
2910 "Western Sahara",
2911 "Yemen",
2912 "Zambia",
2913 "Zimbabwe",
2914 ];
2915 }
2916
2917 private function init_timezones() {
2918 $this->timezones = timezone_identifiers_list();
2919 $offset_range = [
2920 - 12,
2921 - 11.5,
2922 - 11,
2923 - 10.5,
2924 - 10,
2925 - 9.5,
2926 - 9,
2927 - 8.5,
2928 - 8,
2929 - 7.5,
2930 - 7,
2931 - 6.5,
2932 - 6,
2933 - 5.5,
2934 - 5,
2935 - 4.5,
2936 - 4,
2937 - 3.5,
2938 - 3,
2939 - 2.5,
2940 - 2,
2941 - 1.5,
2942 - 1,
2943 - 0.5,
2944 0,
2945 0.5,
2946 1,
2947 1.5,
2948 2,
2949 2.5,
2950 3,
2951 3.5,
2952 4,
2953 4.5,
2954 5,
2955 5.5,
2956 5.75,
2957 6,
2958 6.5,
2959 7,
2960 7.5,
2961 8,
2962 8.5,
2963 8.75,
2964 9,
2965 9.5,
2966 10,
2967 10.5,
2968 11,
2969 11.5,
2970 12,
2971 12.75,
2972 13,
2973 13.75,
2974 14,
2975 ];
2976 foreach( $offset_range as $offset ) {
2977 if( 0 <= $offset ) {
2978 $offset_name = '+' . $offset;
2979 } else {
2980 $offset_name = (string) $offset;
2981 }
2982
2983 $offset_value = $offset_name;
2984 $offset_value = 'UTC' . $offset_value;
2985 $this->timezones[] = 'UTC/' . $offset_value;
2986 }
2987
2988 $zones = $this->timezones;
2989 $timezones = [];
2990 foreach( $zones as $zone ) {
2991 if( strpos( (string) $zone, '/' ) === false ) continue;
2992
2993 $zone = str_replace( '_', ' ', $zone );
2994
2995 $group = function_exists( 'mb_substr' ) ? mb_substr( (string) $zone, 0, strpos( (string) $zone, '/' ) ) : substr( (string) $zone, 0, strpos( (string) $zone, '/' ) );
2996 $index = function_exists( 'mb_strlen' ) ? mb_strlen( (string) $group ) + 1 : strlen( (string) $group ) + 1;
2997 $optionValue = substr( (string) $zone, $index );
2998
2999 if( strpos( (string) $optionValue, 'UTC' ) !== false ) {
3000 $optionTitle = str_replace( [ '.25', '.5', '.75', ], [ ':15', ':30', ':45', ], $optionValue );
3001 $optionValue = "$optionValue=>$optionTitle";
3002 } else {
3003 $optionValue = "$zone=>$optionValue";
3004 }
3005
3006 $timezones[ $group ][] = $optionValue;
3007 }
3008
3009 $this->timezones = $timezones;
3010 }
3011
3012 public function get_fields( $only_defaults = false ) {
3013 $this->init_fields();
3014 $fields = $this->fields;
3015 if( $only_defaults ) {
3016 foreach( $fields as $k => $v ) if( ! wpfval( $v, 'isDefault' ) ) unset( $fields[ $k ] );
3017 } else {
3018 $this->fields = $fields = apply_filters( 'wpforo_get_fields', $fields );
3019 }
3020
3021 return $fields;
3022 }
3023
3024 public function get_field( $key ) {
3025 if( is_string( $key ) ) {
3026 $this->init_fields();
3027
3028 return (array) wpfval( $this->fields, $key );
3029 } elseif( $this->is_field( $key ) ) {
3030 return $key;
3031 }
3032
3033 return [];
3034 }
3035
3036 public function is_field( $field ) {
3037 return wpfval( $field, 'fieldKey' ) && wpfval( $field, 'type' ) && wpfkey( $field, 'isDefault' );
3038 }
3039
3040 public function get_field_key( $field ) {
3041 return is_string( $field ) ? $field : (string) wpfval( $field, 'fieldKey' );
3042 }
3043
3044 public function fields_structure_full_array( $fields, $need_password = null ) {
3045 if( is_string( $fields ) ) $fields = maybe_unserialize( $fields );
3046 $fs = [ [ [] ] ];
3047 if( ! is_array( $fields ) ) return $fs;
3048 if( is_null( $need_password ) ) {
3049 foreach( $fields as $kr => $row ) {
3050 if( is_array( $row ) ) {
3051 foreach( $row as $kc => $cols ) {
3052 if( is_array( $cols ) ) {
3053 foreach( $cols as $field ) {
3054 $field_key = $this->get_field_key( $field );
3055 $fs[ $kr ][ $kc ][ $field_key ] = $this->get_field( $field );
3056 }
3057 }
3058 }
3059 }
3060 }
3061 } else {
3062 $has_password = false;
3063 foreach( $fields as $kr => $row ) {
3064 if( is_array( $row ) ) {
3065 foreach( $row as $kc => $cols ) {
3066 if( is_array( $cols ) ) {
3067 foreach( $cols as $field ) {
3068 $field_key = $this->get_field_key( $field );
3069 if( $field_key === 'user_pass' ) {
3070 if( $need_password ) {
3071 $has_password = true;
3072 $fs[ $kr ][ $kc ][ $field_key ] = $this->get_field( $field );
3073 }
3074 } else {
3075 $fs[ $kr ][ $kc ][ $field_key ] = $this->get_field( $field );
3076 }
3077 }
3078 }
3079 }
3080 }
3081 }
3082 if( $need_password && ! $has_password ) $fs[][][] = $this->get_field( 'user_pass' );
3083 }
3084
3085 return $fs;
3086 }
3087
3088 public function get_register_fields_structure( $only_defaults = false ) {
3089 $need_password = ! wpforo_setting( 'authorization', 'user_register_email_confirm' );
3090 $regform = [ 'user_login', 'user_email' ];
3091 if( $need_password ) $regform[] = 'user_pass';
3092 $fields = [ [ $regform ] ];
3093 if( ! $only_defaults ) $fields = apply_filters( 'wpforo_get_register_fields', $fields );
3094
3095 return $fields;
3096 }
3097
3098 public function get_register_fields( $only_defaults = false ) {
3099 return $this->fields_structure_full_array( $this->get_register_fields_structure( $only_defaults ), ! wpforo_setting( 'authorization', 'user_register_email_confirm' ) );
3100 }
3101
3102 public function get_account_fields_structure( $only_defaults = false ) {
3103 $fields = [
3104 [
3105 [
3106 'user_login',
3107 'display_name',
3108 'user_nicename',
3109 'user_email',
3110 'title',
3111 'groupid',
3112 'avatar',
3113 'about',
3114 'user_url',
3115 'occupation',
3116 'signature',
3117 ],
3118 ],
3119 [
3120 [
3121 'html_soc_net',
3122 ],
3123 [
3124 'facebook',
3125 'linkedin',
3126 'instagram',
3127 'vkontakte',
3128 ],
3129 [
3130 'twitter',
3131 'youtube',
3132 'telegram',
3133 'skype',
3134 ],
3135 ],
3136 [
3137 [
3138 'location',
3139 'timezone',
3140 'user_pass',
3141 ],
3142 ],
3143 ];
3144 if( ! $only_defaults ) $fields = apply_filters( 'wpforo_get_account_fields', $fields );
3145
3146 return $fields;
3147 }
3148
3149 public function get_account_fields( $only_defaults = false ) {
3150 return $this->fields_structure_full_array( $this->get_account_fields_structure( $only_defaults ) );
3151 }
3152
3153 public function get_profile_fields_structure( $only_defaults = false ) {
3154 $fields = [
3155 [
3156 [
3157 'about',
3158 'user_url',
3159 ],
3160 ],
3161 [
3162 [
3163 'location',
3164 'timezone',
3165 'occupation',
3166 'signature',
3167 ],
3168 ],
3169 [
3170 [
3171 'html_soc_net',
3172 ],
3173 ],
3174 [
3175 [
3176 'facebook',
3177 'linkedin',
3178 'instagram',
3179 'vkontakte',
3180 ],
3181 [
3182 'twitter',
3183 'youtube',
3184 'telegram',
3185 'skype',
3186 ],
3187 ],
3188 ];
3189 if( ! $only_defaults ) $fields = apply_filters( 'wpforo_get_profile_fields', $fields );
3190
3191 return $fields;
3192 }
3193
3194 public function get_profile_fields( $only_defaults = false ) {
3195 return $this->fields_structure_full_array( $this->get_profile_fields_structure( $only_defaults ) );
3196 }
3197
3198 public function get_search_fields_structure( $only_defaults = false ) {
3199 $fields = [ [ [ 'display_name', 'user_nicename' ] ] ];
3200 if( ! $only_defaults ) $fields = apply_filters( 'wpforo_get_search_fields', $fields );
3201
3202 return $fields;
3203 }
3204
3205 public function get_search_fields( $only_defaults = false ) {
3206 $fields = $this->fields_structure_full_array( $this->get_search_fields_structure( $only_defaults ) );
3207 foreach( $fields as $row_key => $row ) {
3208 foreach( $row as $col_key => $col ) {
3209 foreach( $col as $field_key => $field ) {
3210 if( $this->is_field( $field ) ) {
3211 $fields[ $row_key ][ $col_key ][ $field_key ]['isRequired'] = 0;
3212 $fields[ $row_key ][ $col_key ][ $field_key ]['class'] = 'wpf-member-search-field';
3213 if( in_array( $field['type'], [ 'text', 'textarea', 'email', 'url' ], true ) ) {
3214 $fields[ $row_key ][ $col_key ][ $field_key ]['type'] = 'search';
3215 }
3216 }
3217 }
3218 }
3219 }
3220
3221 return $fields;
3222 }
3223
3224 public function get_search_fields_names( $only_defaults = false ) {
3225 $names = [];
3226 $fields = $this->get_search_fields( $only_defaults );
3227
3228 foreach( $fields as $row ) {
3229 foreach( $row as $col ) {
3230 foreach( $col as $field ) {
3231 if( $only_defaults && ! $field['isDefault'] ) continue;
3232 $names[] = $field['name'];
3233 }
3234 }
3235 }
3236
3237 return $names;
3238 }
3239
3240 public function set_guest_cookies( $args ) {
3241 if( ! wpforo_setting( 'legal', 'cookies' ) ) return;
3242 if( isset( $args['name'] ) && isset( $args['email'] ) ) {
3243 $comment_cookie_lifetime = apply_filters( 'comment_cookie_lifetime', 30000000 );
3244 $secure = ( 'https' === parse_url( home_url(), PHP_URL_SCHEME ) );
3245 setcookie( 'comment_author_' . COOKIEHASH, $args['name'], time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, $secure );
3246 setcookie( 'comment_author_email_' . COOKIEHASH, $args['email'], time() + $comment_cookie_lifetime, COOKIEPATH, COOKIE_DOMAIN, $secure );
3247
3248 WPF()->current_user_display_name = $args['name'];
3249 WPF()->current_user_email = $args['email'];
3250 }
3251 }
3252
3253 public function get_guest_cookies() {
3254 $guest = [ 'name' => '', 'email' => '' ];
3255 if( wpforo_setting( 'legal', 'cookies' ) ) {
3256 if( ! WPF()->current_userid && WPF()->current_user_email ) {
3257 $guest['name'] = WPF()->current_user_display_name;
3258 $guest['email'] = WPF()->current_user_email;
3259 } else {
3260 $guest_cookies = wp_get_current_commenter();
3261 $guest['name'] = ( isset( $guest_cookies['comment_author'] ) ) ? $guest_cookies['comment_author'] : '';
3262 $guest['email'] = ( isset( $guest_cookies['comment_author_email'] ) ) ? $guest_cookies['comment_author_email'] : '';
3263 }
3264 }
3265
3266 return $guest;
3267 }
3268
3269 public function set_is_email_confirmed( $userid, $status ) {
3270 if( false !== WPF()->db->update( WPF()->tables->profiles, [ 'is_email_confirmed' => intval( $status ) ], [ 'userid' => wpforo_bigintval( $userid ) ], [ '%d' ], [ '%d' ] ) ) {
3271 WPF()->notice->add( 'Email has been confirmed', 'success' );
3272
3273 return true;
3274 }
3275 WPF()->notice->add( 'Email confirm error', 'error' );
3276
3277 return false;
3278 }
3279
3280 public function get_is_email_confirmed( $userid ) {
3281 $sql = "SELECT `is_email_confirmed` FROM `" . WPF()->tables->profiles . "` WHERE `userid` = %d";
3282
3283 return (bool) WPF()->db->get_var( WPF()->db->prepare( $sql, $userid ) );
3284 }
3285
3286 /**
3287 * @param int $userid
3288 *
3289 * @return int
3290 */
3291 public function get_groupid( $userid ) {
3292 return (int) WPF()->db->get_var(
3293 WPF()->db->prepare(
3294 "SELECT `groupid`
3295 FROM `" . WPF()->tables->profiles . "`
3296 WHERE `userid` = %d",
3297 $userid
3298 )
3299 );
3300 }
3301
3302
3303 /**
3304 * @param int $userid
3305 * @param boolean $array
3306 *
3307 * @return string|array|null
3308 */
3309 public function get_secondary_groupids( $userid, $array = true ) {
3310 $secondary_groupids = WPF()->db->get_var(
3311 WPF()->db->prepare(
3312 "SELECT `secondary_groupids`
3313 FROM `" . WPF()->tables->profiles . "`
3314 WHERE `userid` = %d",
3315 $userid
3316 )
3317 );
3318
3319 if( $array && ! empty( $secondary_groupids ) ) return explode( ',', $secondary_groupids );
3320
3321 return $secondary_groupids;
3322 }
3323
3324 public function set_groupid( $userid, $groupid ) {
3325 $userid = wpforo_bigintval( $userid );
3326 $groupid = intval( $groupid );
3327 if( $userid && $groupid ) {
3328 $sql = "UPDATE `" . WPF()->tables->profiles . "` SET `groupid` = %d WHERE `userid` = %d";
3329 if( false !== WPF()->db->query( WPF()->db->prepare( $sql, $groupid, $userid ) ) ) {
3330 $this->reset( $userid );
3331 do_action( 'wpforo_set_groupid', $userid, $groupid );
3332
3333 return true;
3334 }
3335 }
3336
3337 return false;
3338 }
3339
3340 /**
3341 * @param int $userid
3342 * @param array|int $groupids
3343 *
3344 * @return bool
3345 */
3346 public function set_secondary_groupids( $userid, $groupids ) {
3347 $userid = wpforo_bigintval( $userid );
3348 if( $userid ) {
3349 $groupids = implode( ',', array_unique( array_filter( array_map( 'intval', (array) $groupids ) ) ) );
3350 $sql = "UPDATE `" . WPF()->tables->profiles . "` SET `secondary_groupids` = %s WHERE `userid` = %d";
3351 if( false !== WPF()->db->query( WPF()->db->prepare( $sql, $groupids, $userid ) ) ) {
3352 $this->reset( $userid );
3353
3354 return true;
3355 }
3356 }
3357
3358 return false;
3359 }
3360
3361 /**
3362 * @param int $userid
3363 * @param array|int $groupids
3364 *
3365 * @return bool
3366 */
3367 public function append_secondary_groupids( $userid, $groupids ) {
3368 $userid = wpforo_bigintval( $userid );
3369 if( $userid ) {
3370 if( $member = $this->get_member( $userid ) ) {
3371 $groupids = array_filter( array_map( 'intval', (array) $groupids ) );
3372 $groupids = array_merge( $member['secondary_groupids'], $groupids );
3373
3374 return $this->set_secondary_groupids( $userid, $groupids );
3375 }
3376 }
3377
3378 return false;
3379 }
3380
3381 public function after_register_new_user( $userid ) {
3382 if( wpforo_setting( 'authorization', 'manually_approval' ) || (int) trim( (string) get_user_meta( $userid, 'default_password_nag', true ) ) ) {
3383 $this->update_profile_fields( $userid, [ 'status' => 'inactive' ], false );
3384 }
3385 }
3386
3387 public function after_password_reset( $user ) {
3388 $status = $this->get_status( $user->ID );
3389 if( $status !== 'banned' ) {
3390 $data = [ 'status' => 'active', 'is_email_confirmed' => 1 ];
3391 if( wpforo_setting( 'authorization', 'manually_approval' ) ) $data['status'] = $status;
3392 $this->update_profile_fields( $user->ID, $data, false );
3393 $this->reset( $user->ID );
3394 }
3395 }
3396
3397 public function wp_login( $user_login, $user ) {
3398 $this->inactive_to_active( wpforo_bigintval( $user->ID ), $user_login );
3399 }
3400
3401 public function get_distinct_status() {
3402 $sql = "SELECT DISTINCT `status` as statuses FROM `" . WPF()->tables->profiles . "`";
3403
3404 return WPF()->db->get_col( $sql );
3405 }
3406
3407 /**
3408 * @param array $args
3409 *
3410 * @return array selected userids
3411 */
3412 public function get_userids( $args ) {
3413 $sql = "SELECT `userid` FROM `" . WPF()->tables->profiles . "`";
3414 if( $args ) {
3415 $wheres = [];
3416 foreach( $args as $field => $value ) {
3417 $wheres[] = "`" . $field . "` = '" . esc_sql( $value ) . "'";
3418 }
3419 if( $wheres ) $sql .= " WHERE " . implode( " AND ", $wheres );
3420 }
3421
3422 $userids = WPF()->db->get_col( $sql );
3423
3424 return apply_filters( 'wpforo_member_after_get_userids', $userids );
3425 }
3426
3427 public function get_inlist_enabled_statuses() {
3428 return wpforo_setting( 'members', 'hide_inactive' ) ? [ 'active' ] : [ 'active', 'inactive', 'banned' ];
3429 }
3430
3431 /**
3432 * @param int $userid
3433 *
3434 * @return int
3435 */
3436 public function calc_approved_posts( $userid ) {
3437 if( $userid = wpforo_bigintval( $userid ) ) {
3438 $sqls = [];
3439 foreach( WPF()->get_active_boards_tables( 'posts' ) as $table ) {
3440 $sqls[] = WPF()->db->prepare(
3441 "SELECT COUNT(*)
3442 FROM `$table`
3443 WHERE `status` = 0
3444 AND `userid` = %d",
3445 $userid
3446 );
3447 }
3448
3449 if( $sqls ) {
3450 return (int) WPF()->db->get_var( "SELECT SUM(`COUNT(*)`) FROM (" . implode( ' UNION ALL ', $sqls ) . ") AS temp" );
3451 }
3452 }
3453
3454 return 0;
3455 }
3456
3457 /**
3458 * @param int $userid
3459 *
3460 * @return int
3461 */
3462 public function calc_approved_topics( $userid ) {
3463 if( $userid = wpforo_bigintval( $userid ) ) {
3464 $sqls = [];
3465 foreach( WPF()->get_active_boards_tables( 'topics' ) as $table ) {
3466 $sqls[] = WPF()->db->prepare(
3467 "SELECT COUNT(*)
3468 FROM `$table`
3469 WHERE `status` = 0
3470 AND `userid` = %d",
3471 $userid
3472 );
3473 }
3474
3475 if( $sqls ) {
3476 return (int) WPF()->db->get_var( "SELECT SUM(`COUNT(*)`) FROM (" . implode( ' UNION ALL ', $sqls ) . ") AS temp" );
3477 }
3478 }
3479
3480 return 0;
3481 }
3482
3483 /**
3484 * @param int $userid
3485 *
3486 * @return int
3487 */
3488 public function calc_approved_questions( $userid ) {
3489 if( $userid = wpforo_bigintval( $userid ) ) {
3490 $sqls = [];
3491 foreach( WPF()->get_active_boards_tables( 'topics' ) as $table ) {
3492 if( $forumids = Forums::get_forumids( str_replace( 'topics', 'forums', $table ), 3 ) ) {
3493 $sqls[] = WPF()->db->prepare(
3494 "SELECT COUNT(*)
3495 FROM `$table`
3496 WHERE `status` = 0
3497 AND `userid` = %d
3498 AND `forumid` IN( " . implode( ',', $forumids ) . " )",
3499 $userid
3500 );
3501 }
3502 }
3503
3504 if( $sqls ) {
3505 return (int) WPF()->db->get_var( "SELECT SUM(`COUNT(*)`) FROM (" . implode( ' UNION ALL ', $sqls ) . ") AS temp" );
3506 }
3507 }
3508
3509 return 0;
3510 }
3511
3512 /**
3513 * @param int $userid
3514 *
3515 * @return int
3516 */
3517 public function calc_approved_answers( $userid ) {
3518 if( $userid = wpforo_bigintval( $userid ) ) {
3519 $sqls = [];
3520 foreach( WPF()->get_active_boards_tables( 'posts' ) as $table ) {
3521 if( $forumids = Forums::get_forumids( str_replace( 'posts', 'forums', $table ), 3 ) ) {
3522 $sqls[] = WPF()->db->prepare(
3523 "SELECT COUNT(*)
3524 FROM `$table`
3525 WHERE `status` = 0
3526 AND `is_first_post` = 0
3527 AND `parentid` = 0
3528 AND `userid` = %d
3529 AND `forumid` IN( " . implode( ',', $forumids ) . " )",
3530 $userid
3531 );
3532 }
3533 }
3534
3535 if( $sqls ) {
3536 return (int) WPF()->db->get_var( "SELECT SUM(`COUNT(*)`) FROM (" . implode( ' UNION ALL ', $sqls ) . ") AS temp" );
3537 }
3538 }
3539
3540 return 0;
3541 }
3542
3543 /**
3544 * @param int $userid
3545 *
3546 * @return int
3547 */
3548 public function calc_approved_comments( $userid ) {
3549 if( $userid = wpforo_bigintval( $userid ) ) {
3550 $sqls = [];
3551 foreach( WPF()->get_active_boards_tables( 'posts' ) as $table ) {
3552 if( $forumids = Forums::get_forumids( str_replace( 'posts', 'forums', $table ), 3 ) ) {
3553 $sqls[] = WPF()->db->prepare(
3554 "SELECT COUNT(*)
3555 FROM `$table`
3556 WHERE `status` = 0
3557 AND `is_first_post` = 0
3558 AND `parentid` <> 0
3559 AND `userid` = %d
3560 AND `forumid` IN( " . implode( ',', $forumids ) . " )",
3561 $userid
3562 );
3563 }
3564 }
3565
3566 if( $sqls ) {
3567 return (int) WPF()->db->get_var( "SELECT SUM(`COUNT(*)`) FROM (" . implode( ' UNION ALL ', $sqls ) . ") AS temp" );
3568 }
3569 }
3570
3571 return 0;
3572 }
3573
3574 /**
3575 * @param int $userid
3576 *
3577 * @return array
3578 */
3579 public function calc_reactions_in( int $userid ): array {
3580 $reactions_in = array_map( '__return_zero', array_flip( Reactions::get_type_list() ) );
3581 if( $userid = wpforo_bigintval( $userid ) ) {
3582 $sqls = [];
3583 foreach( WPF()->get_active_boards_tables( 'reactions' ) as $table ) {
3584 $sqls[] = WPF()->db->prepare(
3585 "SELECT `type`
3586 FROM `$table`
3587 WHERE `post_userid` = %d",
3588 $userid
3589 );
3590 }
3591
3592 if( $sqls ) {
3593 $r = (array) WPF()->db->get_results( "SELECT `type`, COUNT(`type`) as `count` FROM (" . implode( ' UNION ALL ', $sqls ) . ") AS temp GROUP BY `type`", ARRAY_A );
3594 foreach( $r as $_r ) $reactions_in[ $_r['type'] ] = intval( $_r['count'] );
3595 }
3596 }
3597
3598 return $reactions_in;
3599 }
3600
3601 /**
3602 * @param int $userid
3603 *
3604 * @return array
3605 */
3606 public function calc_reactions_out( int $userid ): array {
3607 $reactions_out = array_map( '__return_zero', array_flip( Reactions::get_type_list() ) );
3608 if( $userid = wpforo_bigintval( $userid ) ) {
3609 $sqls = [];
3610 foreach( WPF()->get_active_boards_tables( 'reactions' ) as $table ) {
3611 $sqls[] = WPF()->db->prepare(
3612 "SELECT `type`
3613 FROM `$table`
3614 WHERE `userid` = %d",
3615 $userid
3616 );
3617 }
3618
3619 if( $sqls ) {
3620 $r = (array) WPF()->db->get_results( "SELECT `type`, COUNT(`type`) as `count` FROM (" . implode( ' UNION ALL ', $sqls ) . ") AS temp GROUP BY `type`", ARRAY_A );
3621 foreach( $r as $_r ) $reactions_out[ $_r['type'] ] = intval( $_r['count'] );
3622 }
3623 }
3624
3625 return $reactions_out;
3626 }
3627
3628 /**
3629 * @param int $userid
3630 *
3631 * @return void
3632 */
3633 public function rebuild_stats( int $userid ) {
3634 $posts = $this->calc_approved_posts( $userid );
3635 $topics = $this->calc_approved_topics( $userid );
3636 $questions = $this->calc_approved_questions( $userid );
3637 $answers = $this->calc_approved_answers( $userid );
3638 $comments = $this->calc_approved_comments( $userid );
3639 $reactions_in = $this->calc_reactions_in( $userid );
3640 $reactions_out = $this->calc_reactions_out( $userid );
3641 $points = $this->calc_points( compact( 'posts', 'topics', 'questions', 'answers', 'comments', 'reactions_in', 'reactions_out' ) );
3642
3643 if( WPF()->db->update(
3644 WPF()->tables->profiles,
3645 [
3646 'posts' => $posts,
3647 'topics' => $topics,
3648 'questions' => $questions,
3649 'answers' => $answers,
3650 'comments' => $comments,
3651 'reactions_in' => json_encode( $reactions_in ),
3652 'reactions_out' => json_encode( $reactions_out ),
3653 'points' => $points,
3654 ],
3655 [ 'userid' => $userid ],
3656 [ '%d', '%d', '%d', '%d', '%d', '%s', '%s', '%d' ],
3657 '%d'
3658 ) ) {
3659 $this->reset( $userid );
3660 }
3661 }
3662
3663 public function after_add_topic( $topic ) {
3664 if( ! intval( $topic['status'] ) ) $this->rebuild_stats( $topic['userid'] );
3665 }
3666
3667 public function after_delete_topic( $topic ) {
3668 if( ! intval( $topic['status'] ) ) $this->rebuild_stats( $topic['userid'] );
3669 }
3670
3671 public function after_topic_status_update( $topic ) {
3672 $this->rebuild_stats( $topic['userid'] );
3673 }
3674
3675 public function before_merge_topic( $target, $current, $postids ) {
3676 $sql = "SELECT DISTINCT `userid` FROM `" . WPF()->tables->posts . "` WHERE `topicid` = %d";
3677 $sql = WPF()->db->prepare( $sql, $current['topicid'] );
3678 if( $postids ) $sql .= " AND `postid` IN(" . implode( ',', $postids ) . ")";
3679 $this->touched_userids = array_merge( $this->touched_userids, WPF()->db->get_col( $sql ) );
3680 }
3681
3682 public function after_merge_topic() {
3683 foreach( array_unique( array_filter( $this->touched_userids ) ) as $userid ) $this->rebuild_stats( $userid );
3684 }
3685
3686 public function after_move_topic( $topic ) {
3687 $sql = "SELECT DISTINCT `userid` FROM `" . WPF()->tables->posts . "` WHERE `topicid` = %d";
3688 $sql = WPF()->db->prepare( $sql, $topic['topicid'] );
3689 foreach( WPF()->db->get_col( $sql ) as $userid ) $this->rebuild_stats( $userid );
3690 }
3691
3692 public function after_add_post( $post ) {
3693 if( ! intval( $post['status'] ) ) $this->rebuild_stats( $post['userid'] );
3694 }
3695
3696 public function after_delete_post( $post ) {
3697 if( ! intval( $post['status'] ) ) $this->rebuild_stats( $post['userid'] );
3698 }
3699
3700 public function after_post_status_update( $post ) {
3701 $this->rebuild_stats( $post['userid'] );
3702 }
3703
3704 public function after_add_reaction( $reaction ) {
3705 $this->rebuild_stats( $reaction['userid'] );
3706 $this->rebuild_stats( $reaction['post_userid'] );
3707 }
3708
3709 public function after_edit_reaction( $fields, $where ) {
3710 $field_userid = wpfval( $fields, 'userid' );
3711 $field_post_userid = wpfval( $fields, 'post_userid' );
3712 if( $field_userid || $field_post_userid ) {
3713 $userids = [ $field_userid, $field_post_userid, wpfval( $where, 'userid' ), wpfval( $where, 'post_userid' ) ];
3714 $userids = array_filter( $userids );
3715 $userids = array_unique( $userids );
3716 foreach( $userids as $userid ) $this->rebuild_stats( $userid );
3717 }
3718 }
3719
3720 public function before_delete_reaction( $args, $operator ) {
3721 $reactions = WPF()->reaction->get_reactions( $args, $operator );
3722 foreach( $reactions as $reaction ) {
3723 $this->touched_userids[] = $reaction['userid'];
3724 $this->touched_userids[] = $reaction['post_userid'];
3725 }
3726 }
3727
3728 public function after_delete_reaction() {
3729 foreach( array_unique( array_filter( $this->touched_userids ) ) as $userid ) $this->rebuild_stats( $userid );
3730 }
3731
3732 public function after_delete_user( $userid, $reassign ) {
3733 if( $reassign ) $this->rebuild_stats( $reassign );
3734 }
3735
3736 public function after_activate_user( $userid ) {
3737 $user = get_user_by( 'ID', $userid );
3738 if( $user ) {
3739 $sbj = wpforo_setting( 'email', 'after_user_approve_email_subject' );
3740 $msg = wpforo_setting( 'email', 'after_user_approve_email_message' );
3741 $blogname = get_option( 'blogname', '' );
3742 $login_url = wpforo_login_url( '' );
3743 $login_link = sprintf( '<a href="%1$s">%2$s</a>', $login_url, $blogname );
3744
3745 $sbj = str_replace( [ '[blogname]', '[user_login]', '[login_link]', '[login_url]' ], [ $blogname, $user->user_login, $login_link, $login_url ], $sbj );
3746 $msg = str_replace( [ '[blogname]', '[user_login]', '[login_link]', '[login_url]' ], [ $blogname, $user->user_login, $login_link, $login_url ], $msg );
3747 wpforo_send_email( $user->user_email, $sbj, $msg );
3748 }
3749 }
3750
3751 public function get_activity_url( $filter = '', $boardid = null, $arg = null ) {
3752 if( ! $arg ) $arg = WPF()->current_object['user'];
3753 $url = rtrim( $this->get_profile_url( $arg, 'activity' ), '/' );
3754 if( is_wpforo_multiboard() ) {
3755 if( is_null( $boardid ) ) $boardid = wpfkey( WPF()->GET, 'boardid' ) ? (int) wpfval( WPF()->GET, 'boardid' ) : WPF()->board->get_current( 'boardid' );
3756 if( $filter ) {
3757 $url .= sprintf( '/?boardid=%1$d&filter=%2$s', $boardid, $filter );
3758 } else {
3759 $url .= sprintf( '/?boardid=%1$d', $boardid );
3760 }
3761 } elseif( $filter ) {
3762 $url .= sprintf( '/?filter=%1$s', $filter );
3763 }
3764
3765 return WPF()->user_trailingslashit( $url );
3766 }
3767
3768 public function get_favored_url( $filter = '', $boardid = null, $arg = null ) {
3769 if( ! $arg ) $arg = WPF()->current_object['user'];
3770 $url = rtrim( $this->get_profile_url( $arg, 'favored' ), '/' );
3771 if( is_wpforo_multiboard() ) {
3772 if( is_null( $boardid ) ) $boardid = wpfkey( WPF()->GET, 'boardid' ) ? (int) wpfval( WPF()->GET, 'boardid' ) : WPF()->board->get_current( 'boardid' );
3773 if( $filter ) {
3774 $url .= sprintf( '/?boardid=%1$d&filter=%2$s', $boardid, $filter );
3775 } else {
3776 $url .= sprintf( '/?boardid=%1$d', $boardid );
3777 }
3778 } elseif( $filter ) {
3779 $url .= sprintf( '/?filter=%1$s', $filter );
3780 }
3781
3782 return WPF()->user_trailingslashit( $url );
3783 }
3784
3785 public function get_newest_member( $status_filter = true ) {
3786 if( ! ( $visible_usergroup_ids = WPF()->usergroup->get_visible_usergroup_ids() ) ) return [];
3787
3788 $groupids = implode( ", ", $visible_usergroup_ids );
3789
3790 if( $status_filter ) {
3791 $status = implode( "', '", $this->get_inlist_enabled_statuses() );
3792 $sql = "SELECT `userid` FROM `" . WPF()->tables->profiles . "`
3793 WHERE `status` IN ('" . $status . "')
3794 AND `groupid` IN (" . $groupids . ")
3795 ORDER BY `userid` DESC LIMIT 1";
3796 } else {
3797 $sql = "SELECT `userid` FROM `" . WPF()->tables->profiles . "`
3798 WHERE `groupid` IN (" . $groupids . ")
3799 ORDER BY `userid` DESC LIMIT 1";
3800 }
3801
3802 $memberid = WPF()->db->get_var( $sql );
3803 $member = wpforo_member( $memberid );
3804
3805 if( empty( $member ) ) {
3806
3807 WPF()->db->query( "DELETE p FROM `" . WPF()->tables->profiles . "` p LEFT JOIN `" . WPF()->db->users . "` u ON u.ID = p.userid WHERE u.ID IS NULL" );
3808
3809 $memberid = WPF()->db->get_var( $sql );
3810 $member = wpforo_member( $memberid );
3811
3812 if( empty( $member ) ) {
3813 if( $status_filter ) {
3814 $members = $this->get_members( [ 'orderby' => 'userid', 'status' => [ 'active' ], 'order' => 'DESC', 'row_count' => 1, 'groupids' => $visible_usergroup_ids ] );
3815 } else {
3816 $members = $this->get_members( [ 'orderby' => 'userid', 'order' => 'DESC', 'row_count' => 1, 'groupids' => $visible_usergroup_ids ] );
3817 }
3818 if( isset( $members[0] ) && ! empty( $members[0] ) ) {
3819 $member = $members[0];
3820 } else {
3821 $member = [];
3822 }
3823 }
3824 }
3825 $member['profile_url'] = wpfval( $member, 'profile_url' ) ?: $this->get_profile_url( $memberid );
3826
3827 return $member;
3828 }
3829
3830 public function after_front_delete_user( $user ) {
3831 if( $user['userid'] === WPF()->current_userid ) {
3832 if( wpforo_setting( 'authorization', 'send_email_after_user_delete' ) ) {
3833 $sbj = wpforo_setting( 'email', 'after_user_delete_notification_email_admin_subject' );
3834 $sbj = str_replace(
3835 [ '[datetime]', '[blogname]' ],
3836 [ _wpforo_date( 0, 'mysql', 'wp' ), '[' . get_bloginfo( 'name' ) . ']' ],
3837 $sbj
3838 );
3839 $sbj = wpforo_apply_email_shortcodes( $sbj, [], [], [], $user, '' );
3840
3841 $msg = wpforo_setting( 'email', 'after_user_delete_notification_email_admin_message' );
3842 $msg = str_replace(
3843 [ '[datetime]', '[blogname]' ],
3844 [ _wpforo_date( 0, 'mysql', 'wp' ), '[' . get_bloginfo( 'name' ) . ']' ],
3845 $msg
3846 );
3847 $msg = wpforo_apply_email_shortcodes( $msg, [], [], [], $user, '' );
3848 $msg = wpautop( $msg );
3849
3850 wpforo_send_email( wpforo_setting( 'email', 'admin_emails' ), $sbj, $msg );
3851 }
3852 }
3853 }
3854 }
3855