PluginProbe
wpForo Forum / 3.2.1
wpForo Forum v3.2.1
3.2.1 3.2.0 3.1.7 3.1.6 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 All 141 releases
wpforo / classes / Members.php

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

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