PluginProbe
wpForo Forum / 2.3.1
wpForo Forum v2.3.1
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.3.1, at classes/Members.php

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