PluginProbe
wpForo Forum / 3.1.6
wpForo Forum v3.1.6
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 1.4.11 1.4.12 1.4.13 All 138 releases
wpforo / classes / Members.php

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

4,395 lines 182.7 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 $needle = preg_quote( preg_quote( $needle ) );
1673 if( in_array( $f['type'], [ 'text', 'search', 'textarea' ], true ) ) {
1674 $wheres[] = "`fields` REGEXP '[{,]\"" . $field . "\":(\\\[[^\\\[]*)?\"[^\"]*" . esc_sql(
1675 $needle
1676 ) . "[^\"]*\"'";
1677 } else {
1678 $wheres[] = "`fields` REGEXP '[{,]\"" . $field . "\":(\\\[[^\\\[]*)?\"" . esc_sql(
1679 $needle
1680 ) . "\"'";
1681 }
1682 }
1683 }
1684
1685 if( ! empty( $wheres ) ) {
1686 $sql .= " WHERE " . implode( " OR ", $wheres );
1687 if( ! is_null( $orderby ) ) {
1688 $sql .= " ORDER BY " . implode( ', ', (array) $orderby );
1689 }
1690 if( $limit ) $sql .= " LIMIT " . intval( $limit );
1691
1692 return WPF()->db->get_col( $sql );
1693 } else {
1694 return [];
1695 }
1696 } else {
1697 return [];
1698 }
1699
1700 }
1701
1702 public function filter( $args, $limit = null, $orderby = null ) {
1703 if( $args && is_array( $args ) ) {
1704 $sql = "SELECT `ID` FROM `" . WPF()->db->users . "` u
1705 INNER JOIN `" . WPF()->tables->profiles . "` p ON p.`userid` = u.`ID`";
1706 $wheres = [];
1707
1708 foreach( $args as $field => $needle ) {
1709 $f = $this->get_field( $field );
1710 $field = sanitize_text_field( $field );
1711 if( $f['isDefault'] ) {
1712 if( is_scalar( $needle ) ) {
1713 $needle = sanitize_text_field( $needle );
1714 $wheres[] = "`" . esc_sql( $field ) . "` LIKE '%" . esc_sql( $needle ) . "%'";
1715 } elseif( is_array( $needle ) ) {
1716 foreach( $needle as $n ) {
1717 $n = sanitize_text_field( $n );
1718 $wheres[] = "`" . esc_sql( $field ) . "` LIKE '%" . esc_sql( $n ) . "%'";
1719 }
1720 }
1721 } else {
1722 if( in_array( $f['type'], [ 'text', 'search', 'textarea' ], true ) ) {
1723 if( is_scalar( $needle ) ) {
1724 $needle = preg_quote( preg_quote( wpforo_encode( $needle ) ) );
1725 $wheres[] = "`fields` REGEXP '[{,]\"" . $field . "\":(\\\[[^\\\[]*)?\"[^\"]*" . esc_sql(
1726 $needle
1727 ) . "[^\"]*\"'";
1728 } elseif( is_array( $needle ) ) {
1729 foreach( $needle as $n ) {
1730 $n = preg_quote( preg_quote( wpforo_encode( $n ) ) );
1731 $wheres[] = "`fields` REGEXP '[{,]\"" . $field . "\":(\\\[[^\\\[]*)?\"[^\"]*" . esc_sql(
1732 $n
1733 ) . "[^\"]*\"'";
1734 }
1735 }
1736 } else {
1737 if( is_scalar( $needle ) ) {
1738 $needle = preg_quote( preg_quote( wpforo_encode( $needle ) ) );
1739 $wheres[] = "`fields` REGEXP '[{,]\"" . $field . "\":(\\\[[^\\\[]*)?\"" . esc_sql(
1740 $needle
1741 ) . "\"'";
1742 } elseif( is_array( $needle ) ) {
1743 foreach( $needle as $n ) {
1744 $n = preg_quote( preg_quote( wpforo_encode( $n ) ) );
1745 $wheres[] = "`fields` REGEXP '[{,]\"" . $field . "\":(\\\[[^\\\[]*)?\"" . esc_sql(
1746 $n
1747 ) . "\"'";
1748 }
1749 }
1750 }
1751 }
1752 }
1753
1754 if( $wheres ) {
1755 $sql .= " WHERE " . implode( " AND ", $wheres );
1756 if( ! is_null( $orderby ) ) {
1757 $sql .= " ORDER BY " . implode( ', ', (array) $orderby );
1758 }
1759 if( $limit ) $sql .= " LIMIT " . intval( $limit );
1760
1761 return WPF()->db->get_col( $sql );
1762 }
1763 }
1764
1765 return [];
1766 }
1767
1768 public function ban( $userid ) {
1769 if( $userid == WPF()->current_userid ) {
1770 WPF()->notice->add( 'You can\'t make yourself banned user', 'error' );
1771
1772 return false;
1773 }
1774 if( ! WPF()->usergroup->can( 'bm' ) || ! WPF()->perm->user_can_manage_user(
1775 WPF()->current_userid,
1776 intval( $userid )
1777 ) ) {
1778 WPF()->notice->add( 'Permission denied for this action', 'error' );
1779
1780 return false;
1781 }
1782 if( false !== WPF()->db->update(
1783 WPF()->tables->profiles,
1784 [ 'status' => 'banned' ],
1785 [ 'userid' => intval( $userid ) ],
1786 [ '%s' ],
1787 [ '%d' ]
1788 ) ) {
1789 do_action( 'wpforo_after_ban_user', $userid );
1790 $this->reset( $userid );
1791 WPF()->notice->add( 'User successfully banned from wpforo', 'success' );
1792
1793 return true;
1794 }
1795
1796 WPF()->notice->add( 'User ban action error', 'error' );
1797
1798 return false;
1799 }
1800
1801 public function unban( $userid ) {
1802 if( ! WPF()->usergroup->can( 'bm' ) || ! WPF()->perm->user_can_manage_user(
1803 WPF()->current_userid,
1804 intval( $userid )
1805 ) ) {
1806 WPF()->notice->add( 'Permission denied for this action', 'error' );
1807
1808 return false;
1809 }
1810 if( false !== WPF()->db->update(
1811 WPF()->tables->profiles,
1812 [ 'status' => 'active' ],
1813 [ 'userid' => intval( $userid ) ],
1814 [ '%s' ],
1815 [ '%d' ]
1816 ) ) {
1817 do_action( 'wpforo_after_unban_user', $userid );
1818 $this->reset( $userid );
1819 WPF()->notice->add( 'User successfully unbanned from wpforo', 'success' );
1820
1821 return true;
1822 }
1823
1824 WPF()->notice->add( 'User unban action error', 'error' );
1825
1826 return false;
1827 }
1828
1829 public function activate( $userid ) {
1830 if( false !== WPF()->db->update(
1831 WPF()->tables->profiles,
1832 [ 'status' => 'active' ],
1833 [ 'userid' => intval( $userid ) ],
1834 [ '%s' ],
1835 [ '%d' ]
1836 ) ) {
1837 do_action( 'wpforo_after_activate_user', $userid );
1838 $this->reset( $userid );
1839 WPF()->notice->add( 'User successfully activated from wpforo', 'success' );
1840
1841 return true;
1842 }
1843
1844 WPF()->notice->add( 'User activate action error', 'error' );
1845
1846 return false;
1847 }
1848
1849 public function deactivate( $userid ) {
1850 if( false !== WPF()->db->update(
1851 WPF()->tables->profiles,
1852 [ 'status' => 'inactive' ],
1853 [ 'userid' => intval( $userid ) ],
1854 [ '%s' ],
1855 [ '%d' ]
1856 ) ) {
1857 do_action( 'wpforo_after_deactivate_user', $userid );
1858 $this->reset( $userid );
1859 WPF()->notice->add( 'User successfully deactivated from wpforo', 'success' );
1860
1861 return true;
1862 }
1863
1864 WPF()->notice->add( 'User deactivate action error', 'error' );
1865
1866 return false;
1867 }
1868
1869 /**
1870 *
1871 * @param int $userid
1872 * @param int $reassign
1873 *
1874 * @return bool true | false if user successfully deleted
1875 */
1876 public function delete( $userid, $reassign = null ) {
1877 if( ! ( $userid = wpforo_bigintval( $userid ) ) ) return false;
1878 do_action( 'wpforo_before_delete_user', $userid, $reassign );
1879
1880 if( false !== WPF()->db->delete( WPF()->tables->profiles, [ 'userid' => $userid ], [ '%d' ] ) ) {
1881 do_action( 'wpforo_after_delete_user', $userid, $reassign );
1882
1883 WPF()->notice->add( 'User successfully deleted', 'success' );
1884
1885 return true;
1886 }
1887
1888 WPF()->notice->add( 'User delete error', 'error' );
1889
1890 return false;
1891 }
1892
1893 /**
1894 * @deprecated since 2.1.6 version instead of this use $this->_get_avatar() method
1895 */
1896 public function _avatar( $user, $attr = '', $size = 96 ) {
1897 return $this->_get_avatar( $user, $size, $attr );
1898 }
1899
1900 /**
1901 * @deprecated since 2.1.6 version instead of this use $this->get_avatar() method
1902 */
1903 public function avatar( $user, $attr = '', $size = 96 ) {
1904 return wpforo_ram_get( [ $this, '_avatar' ], $user, $attr, $size );
1905 }
1906
1907 public function _get_avatar( $user, $size = 96, $attr = '' ) {
1908 return $this->get_avatar_html( $this->get_avatar_url( $user ), $user, $size, $attr );
1909 }
1910
1911 public function get_avatar( $user, $size = 96, $attr = '' ) {
1912 return wpforo_ram_get( [ $this, '_get_avatar' ], $user, $size, $attr );
1913 }
1914
1915 public function _get_avatar_url( $user ) {
1916 $cache = WPF()->cache->on( 'avatar' );
1917
1918 if( is_scalar( $user ) ) {
1919 $userid = wpforo_bigintval( $user );
1920 $cache_avatar = apply_filters( 'wpforo_avatar_cache', true, $userid );
1921 if( $cache && $cache_avatar && $avatar_url = WPF()->cache->get_item( $userid, 'avatar' ) ) {
1922 return str_replace( '#', '', (string) $avatar_url );
1923 }
1924 $avatar_url = (string) WPF()->db->get_var(
1925 WPF()->db->prepare(
1926 "SELECT `avatar` FROM `" . WPF()->tables->profiles . "` WHERE `userid` = %d",
1927 wpforo_bigintval( $userid )
1928 )
1929 );
1930 } else {
1931 $avatar_url = (string) wpfval( $user, 'avatar' );
1932 $userid = wpforo_bigintval( wpfval( $user, 'userid' ) );
1933 }
1934
1935 if( $cache && $userid ) {
1936 WPF()->cache->create( 'item', [ $userid => $avatar_url ?: '#' ], 'avatar' );
1937 }
1938
1939 return apply_filters( 'wpforo_avatar_url', $avatar_url, $userid );
1940 }
1941
1942 public function get_avatar_url( $user ) {
1943 return wpforo_ram_get( [ $this, '_get_avatar_url' ], $user );
1944 }
1945
1946 public function get_avatar_html( $url, $user = [], $size = 96, $attr = '' ) {
1947 $size = intval( $size );
1948 if( $url && wpforo_setting( 'profiles', 'custom_avatars' ) ) {
1949 $url = apply_filters( 'wpforo_avatar_url', $url, $user );
1950 $dname = ! is_scalar( $user ) ? wpforo_user_dname( $user ) : $this->get_member( $user )['dname'];
1951 if( strpos( (string) $attr, 'alt=' ) === false ) $attr .= ' ' . sprintf( 'alt="%1$s"', esc_attr( $dname ) );
1952 if( strpos( (string) $attr, 'title=' ) === false ) {
1953 $attr .= ' ' . sprintf(
1954 'title="%1$s"',
1955 esc_attr( $dname )
1956 );
1957 }
1958 if( strpos( (string) $attr, 'height=' ) === false ) $attr .= ' ' . sprintf( 'height="%1$d"', $size );
1959 if( strpos( (string) $attr, 'width=' ) === false ) $attr .= ' ' . sprintf( 'width="%1$d"', $size );
1960 $img = '<img class="avatar" src="' . esc_url( (string) $url ) . '" ' . $attr . ' >';
1961 } else {
1962 $userid = is_scalar( $user ) ? wpforo_bigintval( $user ) : ( wpforo_bigintval(
1963 wpfval( $user, 'userid' )
1964 ) ?: (string) wpfval( $user, 'user_email' ) );
1965 $img = get_avatar( $userid, $size );
1966 if( $attr ) $img = str_replace( '<img', '<img ' . $attr, $img );
1967 }
1968
1969 return $img;
1970 }
1971
1972 /**
1973 * @param array|int|string $arg
1974 * @param string $template
1975 *
1976 * @return string
1977 */
1978 public function get_profile_url( $arg, $template = 'profile', $ram_cache = true ) {
1979 $user = is_scalar( $arg ) ? ( $ram_cache ? $this->get_member(
1980 intval( basename( (string) $arg ) )
1981 ) : $this->_get_member(
1982 intval( basename( (string) $arg ) )
1983 ) ) ?: [ 'user_nicename' => basename( (string) $arg ) ] : $arg;
1984 if( wpfkey( $user, 'userid' ) && wpfkey( $user, 'user_nicename' ) ) {
1985 $user_slug = ( wpforo_setting(
1986 'profiles',
1987 'url_structure'
1988 ) === 'id' ? $user['userid'] : $user['user_nicename'] );
1989 if( $template === 'profile' ) {
1990 $profile_url = wpforo_url( $user_slug, 'member' );
1991 } else {
1992 $template_slug = wpforo_settings_get_slug( $template );
1993 $profile_url = wpforo_url( "$user_slug/$template_slug", 'member' );
1994 }
1995 } else {
1996 $profile_url = wpforo_home_url();
1997 }
1998
1999 return apply_filters( 'wpforo_member_profile_url', $profile_url, $user, $template );
2000 }
2001
2002 /**
2003 * @param float $points
2004 *
2005 * @return array
2006 */
2007 public function calc_rating( float $points ): array {
2008 $rating = $this->default->member['rating'];
2009
2010 $rating['level'] = $this->rating_level( floatval( $points ), false );
2011 $rating['percent'] = $rating['level'] * 10;
2012 $rating['title'] = $this->rating( $rating['level'], 'title' );
2013 $rating['color'] = $this->rating( $rating['level'], 'color' );
2014 $rating['badge'] = $this->rating( $rating['level'], 'icon' );
2015
2016 return $rating;
2017 }
2018
2019 /**
2020 * @param $member
2021 *
2022 * @return float
2023 */
2024 private function calc_points( $member ): float {
2025 $topic_points = intval( $member['topics'] ) * wpforo_setting( 'rating', 'topic_points' );
2026 $post_points = intval( $member['posts'] ) * wpforo_setting( 'rating', 'post_points' );
2027 $like_points = intval( wpfval( $member, 'reactions_in', 'up' ) ) * wpforo_setting( 'rating', 'like_points' );
2028 $dislike_points = intval( wpfval( $member, 'reactions_in', 'down' ) ) * wpforo_setting(
2029 'rating',
2030 'dislike_points'
2031 );
2032
2033 $topic_points = (float) apply_filters( 'wpforo_member_get_points_topic_points', $topic_points, $member );
2034 $post_points = (float) apply_filters( 'wpforo_member_get_points_post_points', $post_points, $member );
2035 $like_points = (float) apply_filters( 'wpforo_member_get_points_like_points', $like_points, $member );
2036 $dislike_points = (float) apply_filters( 'wpforo_member_get_points_dislike_points', $dislike_points, $member );
2037 $other_points = (float) apply_filters( 'wpforo_member_get_points_other_points', 0, $member );
2038
2039 return $topic_points + $post_points + $like_points + $dislike_points + $other_points;
2040 }
2041
2042 public function get_count( $args = [] ): int {
2043 $sql = "SELECT SQL_NO_CACHE COUNT(*) FROM `" . WPF()->tables->profiles . "` p
2044 INNER JOIN `" . WPF()->db->users . "` u ON u.`ID` = p.`userid` WHERE p.`status` NOT LIKE 'trashed'";
2045 if( $args ) {
2046 $wheres = [];
2047 foreach( $args as $key => $value ) {
2048 if( is_array( $value ) ) {
2049 $wheres[] = "$key IN('" . implode( "','", array_map( 'esc_sql', $value ) ) . "')";
2050 } else {
2051 $wheres[] = "$key = '" . esc_sql( $value ) . "'";
2052 }
2053 }
2054 if( $wheres ) $sql .= " AND " . implode( ' AND ', $wheres );
2055 }
2056
2057 return (int) WPF()->db->get_var( $sql );
2058 }
2059
2060 public function _is_online( $userid, $duration = null ) {
2061 if( ! $duration ) $duration = wpforo_setting( 'profiles', 'online_status_timeout' );
2062 $sql = "SELECT `online_time` FROM `" . WPF()->tables->profiles . "` WHERE `userid` = %d";
2063 $sql = WPF()->db->prepare( $sql, $userid );
2064 $online_time = intval( WPF()->db->get_var( $sql ) );
2065 $current_time = time();
2066
2067 return ( $current_time - $online_time ) < $duration;
2068 }
2069
2070 public function is_online( $userid, $duration = null ) {
2071 return wpforo_ram_get( [ $this, '_is_online' ], $userid, $duration );
2072 }
2073
2074 public function show_online_indicator( $userid, $ico = true ) {
2075 if( $this->is_online( $userid ) ) : ?>
2076
2077 <?php if( $ico ) : ?>
2078 <i class="fas fa-circle wpfsx wpfcl-8" title="<?php wpforo_phrase( 'Online' ) ?>"></i>
2079 <?php else : wpforo_phrase( 'Online' ); endif ?>
2080
2081 <?php else : ?>
2082
2083 <?php if( $ico ) : ?>
2084 <i class="fas fa-circle wpfsx wpfcl-0" title="<?php wpforo_phrase( 'Offline' ) ?>"></i>
2085 <?php else : wpforo_phrase( 'Offline' ); endif ?>
2086
2087 <?php endif;
2088 }
2089
2090 public function online_members_count( $duration = null ) {
2091 if( ! ( $duration = intval( $duration ) ) ) {
2092 $duration = (int) wpforo_setting(
2093 'profiles',
2094 'online_status_timeout'
2095 );
2096 }
2097 $online_timeframe = time() - $duration;
2098 $key = [ 'member', 'online_members_count', $online_timeframe ];
2099 if( WPF()->ram_cache->exists( $key ) ) return WPF()->ram_cache->get( $key );
2100 $sql = "SELECT COUNT(DISTINCT `userid`, `ip`) AS total FROM `" . WPF()->tables->visits . "` WHERE `time` > %d";
2101 $var = (int) WPF()->db->get_var( WPF()->db->prepare( $sql, $online_timeframe ) );
2102 if( ! $var ) {
2103 $sql = "SELECT COUNT(*) FROM `" . WPF()->tables->profiles . "` WHERE `online_time` > %d";
2104 $var = (int) WPF()->db->get_var( WPF()->db->prepare( $sql, $online_timeframe ) );
2105 }
2106 WPF()->ram_cache->set( $key, $var );
2107
2108 return $var;
2109 }
2110
2111 public function get_online_members( $count = 1, $groupids = [], $duration = null ) {
2112 if( ! $duration ) $duration = wpforo_setting( 'profiles', 'online_status_timeout' );
2113 $current_time = time();
2114 $online_timeframe = $current_time - $duration;
2115 $groupids = array_filter((array)$groupids);
2116 $args = [
2117 'groupids' => $groupids,
2118 'online_time' => $online_timeframe, // $current_time - $duration
2119 'orderby' => 'userid', // forumid, order, parentid
2120 'row_count' => $count,
2121 'order' => 'ASC', // ASC DESC
2122 ];
2123
2124 return $this->get_members( $args );
2125 }
2126
2127 public function levels() {
2128 $max_rating_levels = (int) apply_filters( 'wpforo_max_rating_levels', 10 );
2129
2130 return range( 0, $max_rating_levels );
2131 }
2132
2133 public function rating( $level = null, $var = '' ) {
2134 if( is_null( $level ) ) return wpforo_setting( 'rating', 'levels' );
2135 $_levels = wpforo_setting( 'rating', 'levels' );
2136 if( ! wpfkey( $_levels, $level ) ) $level = 0;
2137 if( ! $var ) return wpforo_setting( 'rating', 'levels', $level );
2138
2139 return wpforo_setting( 'rating', 'levels', $level, $var );
2140 }
2141
2142 /**
2143 * @param int $points
2144 * @param bool $percent
2145 *
2146 * @return int
2147 */
2148 public function rating_level( $points, $percent = true ) {
2149 $points = intval( $points );
2150 $max_rating_levels = (int) apply_filters( 'wpforo_max_rating_levels', 10 );
2151 $increment = 100 / $max_rating_levels;
2152 for( $level = 1; $level <= $max_rating_levels; $level ++ ) {
2153 if( $points < $this->rating( $level, 'points' ) ) {
2154 $bar = ( $level - 1 ) * $increment;
2155 break;
2156 }
2157 }
2158 // If the points are greater than or equal to the highest level's points, set $bar to 100
2159 if( ! isset( $bar ) ) $bar = 100;
2160
2161 return $percent ? $bar : (int) floor( $bar / ( 100 / $max_rating_levels ) );
2162 }
2163
2164
2165 public function rating_badge( $level = 0, $view = 'short' ) {
2166
2167 $max_rating_levels = (int) apply_filters( 'wpforo_max_rating_levels', 10 );
2168 $level = ( $level > $max_rating_levels ) ? floor( $level / $max_rating_levels ) : $level;
2169
2170 if( $level === 0 ) {
2171 return '<i class="' . $this->rating( $level, 'icon' ) . '"></i>';
2172 } elseif( $level > 0 && $level < 6 ) {
2173 if( $view == 'full' ) {
2174 return str_repeat( ' <i class="' . $this->rating( $level, 'icon' ) . '"></i> ', $level );
2175 } else {
2176 return '<span>' . esc_html( $level ) . '</span> <i class="' . $this->rating(
2177 $level,
2178 'icon'
2179 ) . '"></i>';
2180 }
2181 } elseif( $level > 5 && $level < 9 ) {
2182 if( $view == 'full' ) {
2183 return str_repeat( ' <i class="' . $this->rating( $level, 'icon' ) . '"></i> ', ( $level - 5 ) );
2184 } else {
2185 return '<span>' . esc_html( $level - 5 ) . '</span> <i class="' . $this->rating(
2186 $level,
2187 'icon'
2188 ) . '"></i>';
2189 }
2190 }
2191
2192 if( $max_rating_levels > 10 ) {
2193 if( $level < 12 ) {
2194 if( $view == 'full' ) {
2195 return str_repeat( ' <i class="' . $this->rating( $level, 'icon' ) . '"></i> ', ( $level - 8 ) );
2196 } else {
2197 return '<span>' . esc_html( $level - 8 ) . '</span> <i class="' . $this->rating(
2198 $level,
2199 'icon'
2200 ) . '"></i>';
2201 }
2202 } elseif( $level < 15 ) {
2203 if( $view == 'full' ) {
2204 return str_repeat( ' <i class="' . $this->rating( $level, 'icon' ) . '"></i> ', ( $level - 11 ) );
2205 } else {
2206 return '<span>' . esc_html( $level - 11 ) . '</span> <i class="' . $this->rating(
2207 $level,
2208 'icon'
2209 ) . '"></i>';
2210 }
2211 }
2212 } else {
2213 if( $level > 8 ) {
2214 return '<i class="' . $this->rating( $level, 'icon' ) . '"></i>';
2215 }
2216 }
2217
2218 return '';
2219 }
2220
2221 public function reset( $userid ) {
2222 if( ! $userid ) return;
2223 wpforo_clean_cache( 'avatar', $userid );
2224 delete_user_meta( intval( $userid ), '_wpf_member_obj' );
2225 if( wpforo_setting( 'seo', 'seo_profile' ) ) WPF()->seo->clear_cache();
2226 }
2227
2228 public function clear_db_cache() {
2229 WPF()->db->query( "DELETE FROM `" . WPF()->db->usermeta . "` WHERE `meta_key` = '_wpf_member_obj'" );
2230 }
2231
2232 private function update_online_time( $userid = null ) {
2233 if( ! $userid ) $userid = WPF()->current_userid;
2234 if( ! $userid ) return false;
2235 $current_timestamp = time();
2236 $sql = "UPDATE `" . WPF()->tables->profiles . "` SET `online_time` = %d WHERE `userid` = %d";
2237 $sql = WPF()->db->prepare( $sql, $current_timestamp, wpforo_bigintval( $userid ) );
2238 if( false !== WPF()->db->query( $sql ) ) return $current_timestamp;
2239
2240 return false;
2241 }
2242
2243 public function init_current_user() {
2244 WPF()->wp_current_user = $current_user = wp_get_current_user();
2245 if( $current_user->exists() ) {
2246 WPF()->current_userid = $current_user->ID;
2247 WPF()->current_user_login = $current_user->user_login;
2248 WPF()->current_user_email = $current_user->user_email;
2249 WPF()->current_user_display_name = $current_user->display_name;
2250
2251 $user = $this->get_member( $current_user->ID );
2252 if( ! wpfkey( $user, 'groupid' ) ) {
2253 $this->synchronize_user( $current_user->ID );
2254 $user = $this->get_member( $current_user->ID );
2255 }
2256 $user_meta = get_user_meta( $current_user->ID );
2257 WPF()->current_user = $user;
2258 WPF()->current_usermeta = $user_meta;
2259 WPF()->current_user_groupid = WPF()->current_user['groupid'];
2260 WPF()->current_user_secondary_groupids = WPF()->current_user['secondary_groupids'];
2261 WPF()->current_user_groupids = WPF()->current_user['groupids'];
2262 WPF()->current_user_status = (string) wpfval( $user, 'status' );
2263 $this->update_online_time();
2264 } elseif( $guest = $this->get_guest_cookies() ) {
2265 WPF()->current_userid = 0;
2266 WPF()->current_user_login = '';
2267 WPF()->current_user_email = $guest['email'];
2268 WPF()->current_user_display_name = $guest['name'];
2269 WPF()->current_user = $this->get_guest( $guest );
2270 WPF()->current_usermeta = [];
2271 WPF()->current_user_groupid = 4;
2272 WPF()->current_user_groupids = [ 4 ];
2273 WPF()->current_user_secondary_groupids = [];
2274 WPF()->current_user_status = '';
2275 WPF()->current_user_accesses = [];
2276 }
2277
2278 WPF()->usergroup->init_current();
2279
2280 if( function_exists( 'wp_get_session_token' ) && function_exists( 'wp_parse_auth_cookie' ) ) {
2281 WPF()->session_token = wp_get_session_token();
2282 }
2283 if( ! WPF()->session_token ) {
2284 $secret_key = defined( 'SECRET_KEY' ) && SECRET_KEY ? SECRET_KEY : 'wpforo';
2285 WPF()->session_token = hash_hmac(
2286 'sha256',
2287 md5( (string) wpfval( $_SERVER, 'HTTP_USER_AGENT' ) ) . md5(
2288 (string) wpfval( $_SERVER, 'REMOTE_ADDR' )
2289 ) . md5(
2290 (string) wpfval( $_SERVER, 'SERVER_ADDR' )
2291 ),
2292 $secret_key
2293 );
2294 }
2295
2296 do_action(
2297 'wpforo_after_init_current_user',
2298 WPF()->current_user,
2299 WPF()->current_usermeta,
2300 WPF()->current_user_groupid,
2301 WPF()->current_user_secondary_groupids,
2302 WPF()->current_user_groupids
2303 );
2304 }
2305
2306 public function blog_posts( $userid ) {
2307 if( isset( $userid ) && $userid ) return count_user_posts( $userid );
2308
2309 return 0;
2310 }
2311
2312 public function blog_comments( $userid, $user_email ) {
2313 global $wpdb;
2314
2315 $userid = intval( $userid );
2316
2317 // Match the author by WP user id and/or by email (for guest/imported comments).
2318 // A comment made while logged in has both columns set, but COUNT(*) still counts
2319 // that single row once, so the OR never double-counts.
2320 $author_conditions = [];
2321 $params = [];
2322 if( $userid ) {
2323 $author_conditions[] = 'c.user_id = %d';
2324 $params[] = $userid;
2325 }
2326 if( $user_email && is_email( $user_email ) ) {
2327 $author_conditions[] = 'c.comment_author_email = %s';
2328 $params[] = $user_email;
2329 }
2330 if( empty( $author_conditions ) ) return 0;
2331
2332 // WordPress stores every kind of comment in the same table (pingbacks, trackbacks,
2333 // reviews, comments on pages/CPTs, etc.). Restrict to real, approved, public blog
2334 // post comments only:
2335 // - comment_approved = '1' -> approved only (not spam/trash/pending)
2336 // - comment_type IN ('','comment') -> regular comments (not pingback/trackback/review)
2337 // - p.post_type = 'post' -> blog posts, not pages/products/other CPTs
2338 // - p.post_status = 'publish' -> public posts only
2339 $sql = "SELECT COUNT(*)
2340 FROM {$wpdb->comments} AS c
2341 INNER JOIN {$wpdb->posts} AS p ON p.ID = c.comment_post_ID
2342 WHERE ( " . implode( ' OR ', $author_conditions ) . " )
2343 AND c.comment_approved = '1'
2344 AND c.comment_type IN ( '', 'comment' )
2345 AND p.post_type = 'post'
2346 AND p.post_status = 'publish'";
2347
2348 return (int) $wpdb->get_var( $wpdb->prepare( $sql, $params ) );
2349 }
2350
2351 public function show_delete_form( $current_user, $userids ) {
2352 if( empty( $current_user ) || empty( $userids ) ) return;
2353
2354 $userids = array_diff( $userids, [ $current_user->ID ] );
2355 $users_have_content = false;
2356 if( WPF()->db->get_var(
2357 "SELECT `postid` FROM `" . WPF()->tables->posts . "` WHERE `userid` IN( " . implode(
2358 ',',
2359 array_map( 'intval', $userids )
2360 ) . " ) LIMIT 1"
2361 ) ) {
2362 $users_have_content = true;
2363 }
2364 ?>
2365 <hr/><strong>#wpForo</strong>
2366 <?php if( ! $users_have_content ) : ?>
2367 <input type="hidden" name="wpforo_user_delete_option" value="delete"/>
2368 <?php else: ?>
2369 <?php if( 1 == count( $userids ) ) : ?>
2370 <fieldset>
2371 <legend><?php _e( 'What should be done with wpForo content owned by this user?', 'wpforo' ); ?></legend>
2372 <?php else : ?>
2373 <fieldset>
2374 <legend><?php _e(
2375 'What should be done with wpForo content owned by these users?',
2376 'wpforo'
2377 ); ?></legend>
2378 <?php endif; ?>
2379 <ul style="list-style:none;">
2380 <li><label><input type="radio" id="wpforo_delete_option0" name="wpforo_user_delete_option"
2381 value="delete">
2382 <?php _e( 'Delete all wpForo content.', 'wpforo' ); ?></label></li>
2383 <li><input type="radio" id="wpforo_delete_option1" name="wpforo_user_delete_option" value="reassign">
2384 <label for="wpforo_delete_option1"><?php _e( 'Attribute all content to:' ) ?></label>
2385 <?php
2386 wp_dropdown_users( [
2387 'name' => 'wpforo_reassign_userid',
2388 'exclude' => $userids,
2389 'show' => 'display_name_with_login',
2390 ] );
2391 ?>
2392 </li>
2393 </ul></fieldset>
2394 <script type="text/javascript">
2395 jQuery(document).ready(function ($) {
2396 $('#wpforo_reassign_userid').on('focus', function () {
2397 $('#wpforo_delete_option1').prop('checked', true).trigger('change');
2398 });
2399 });
2400 </script>
2401 <?php endif;
2402 }
2403
2404 public function autoban( $userid ) {
2405 if( ! WPF()->usergroup->can( 'em' ) ) {
2406 WPF()->db->update(
2407 WPF()->tables->profiles,
2408 [ 'status' => 'banned' ],
2409 [ 'userid' => intval( $userid ) ],
2410 [ '%s' ],
2411 [ '%d' ]
2412 );
2413 }
2414 }
2415
2416 public function member_approved_posts( $member = [] ) {
2417 if( is_numeric( $member ) ) {
2418 if( $userid = wpforo_bigintval( $member ) ) {
2419 if( $userid === WPF()->current_userid ) return WPF()->current_user['posts'];
2420
2421 return (int) WPF()->db->get_var(
2422 WPF()->db->prepare(
2423 "SELECT COUNT(*) as posts
2424 FROM `" . WPF()->tables->posts . "`
2425 WHERE `status` = 0
2426 AND `userid` = %d",
2427 $userid
2428 )
2429 );
2430 }
2431 }
2432
2433 return (int) wpfval( $member, 'posts' );
2434 }
2435
2436 public function current_user_is_new() {
2437 if( WPF()->usergroup->can( 'em' ) ) {
2438 //This is an admin or moderator. The number of posts doesn't matter.
2439 return false;
2440 } else {
2441 $new_user_max_posts = wpforo_setting( 'antispam', 'new_user_max_posts' );
2442 $posts = $this->member_approved_posts( WPF()->current_userid );
2443 if( $new_user_max_posts && $posts <= $new_user_max_posts ) {
2444 return true;
2445 } else {
2446 return false;
2447 }
2448 }
2449 }
2450
2451 /**
2452 * @return int
2453 */
2454 public function banned_count() {
2455 $key = [ 'member', 'banned_count' ];
2456 if( WPF()->ram_cache->exists( $key ) ) return WPF()->ram_cache->get( $key );
2457 $var = (int) WPF()->db->get_var(
2458 "SELECT count(*) FROM `" . WPF()->tables->profiles . "` WHERE `status` = 'banned'"
2459 );
2460 WPF()->ram_cache->set( $key, $var );
2461
2462 return $var;
2463 }
2464
2465 public function _get_guest_posts( $email ) {
2466 $sqls = [];
2467 foreach( WPF()->get_active_boards_tables( 'posts' ) as $table ) {
2468 $sqls[] = WPF()->db->prepare(
2469 "SELECT *
2470 FROM `$table`
2471 WHERE `status` = 0
2472 AND `email` = %s",
2473 $email
2474 );
2475 }
2476
2477 if( $sqls ) {
2478 return (array) WPF()->db->get_results(
2479 implode( ' UNION ALL ', $sqls ) . " ORDER BY `created` ASC, `postid` ASC",
2480 ARRAY_A
2481 );
2482 }
2483
2484 return [];
2485 }
2486
2487 public function get_guest_posts( $email ) {
2488 return wpforo_ram_get( [ $this, '_get_guest_posts' ], $email );
2489 }
2490
2491 public function _get_guest( $args = [] ) {
2492 if( ! wpfval( $args, 'name' ) ) $args['name'] = wpforo_phrase( 'Anonymous', false );
2493 if( ! wpfval( $args, 'email' ) ) $args['email'] = 'anonymous@example.com';
2494
2495 $args['name'] = strip_tags( (string) $args['name'] );
2496 $args['email'] = sanitize_email( $args['email'] );
2497 $args['user_registered'] = current_time( 'mysql', 1 );
2498 $args['posts'] = 0;
2499
2500 if( $args['email'] && $args['email'] !== 'anonymous@example.com' ) {
2501 if( $posts = $this->get_guest_posts( $args['email'] ) ) {
2502 $args['posts'] = count( $posts );
2503 if( $first_post_created = wpfval(
2504 $posts,
2505 0,
2506 'created'
2507 ) ) {
2508 $args['user_registered'] = $first_post_created;
2509 }
2510 }
2511 }
2512
2513 $guest = wpforo_array_args_cast_and_merge(
2514 [
2515 'groupid' => 4,
2516 'group_name' => wpforo_phrase( 'Guest', false ),
2517 'user_login' => $args['name'],
2518 'user_nicename' => sanitize_text_field( $args['name'] ),
2519 'user_email' => $args['email'],
2520 'user_registered' => $args['user_registered'],
2521 'display_name' => $args['name'],
2522 'posts' => $args['posts'],
2523 'status' => 'active',
2524 ],
2525 $this->default->member
2526 );
2527
2528 return $this->decode( $guest );
2529 }
2530
2531 public function get_guest( $args = [] ) {
2532 return wpforo_ram_get( [ $this, '_get_guest' ], $args );
2533 }
2534
2535 public function init_fields() {
2536 if( ! empty( $this->fields ) ) return;
2537
2538 $this->init_countries();
2539 $this->init_timezones();
2540
2541 $this->fields = apply_filters( 'wpforo_member_before_init_fields', $this->fields );
2542
2543 $usergroupids = [];
2544 $usergroups = WPF()->usergroup->get_usergroups();
2545 foreach( $usergroups as $usergroup ) {
2546 $usergroupids[] = $usergroup['groupid'];
2547 }
2548 $usergroupids_can_edit_fields = WPF()->usergroup->get_groupids_by_can( 'em' );
2549 $usergroupids_can_view_social_net = WPF()->usergroup->get_groupids_by_can( 'vmsn' );
2550
2551 /**
2552 * start init tinymce settings
2553 */
2554 $wp_editor_settings = WPF()->tpl->editor_buttons();
2555 $wp_editor_settings['tinymce']['toolbar1'] = 'bold,italic,link,unlink,undo,redo,source_code,emoticons';
2556 $wp_editor_settings['plugins'] = '';
2557 unset(
2558 $wp_editor_settings['external_plugins']['wpforo_pre_button'], $wp_editor_settings['external_plugins']['wpforo_spoiler_button']
2559 );
2560 $wp_editor_settings = apply_filters( 'wpforo_members_init_fields_tinymce_settings', $wp_editor_settings );
2561
2562 $this->fields['user_login'] = [
2563 'fieldKey' => 'user_login',
2564 'type' => 'text',
2565 'isDefault' => 1,
2566 'isRemovable' => 0,
2567 'isRequired' => 1,
2568 'isEditable' => 0,
2569 'label' => wpforo_phrase( 'Username', false ),
2570 'title' => wpforo_phrase( 'Username', false ),
2571 'placeholder' => wpforo_phrase( 'Username', false ),
2572 'description' => wpforo_phrase( 'Length must be between 3 characters and 15 characters.', false ),
2573 'minLength' => 3,
2574 'maxLength' => 30,
2575 'faIcon' => 'fas fa-user',
2576 'name' => 'user_login',
2577 'cantBeInactive' => [ 'register' ],
2578 'canEdit' => [],
2579 'canView' => WPF()->usergroup->get_groupids_by_can( 'vmu' ),
2580 'can' => 'vmu',
2581 'isSearchable' => 0,
2582 ];
2583
2584 $this->fields['user_email'] = [
2585 'fieldKey' => 'user_email',
2586 'type' => 'email',
2587 'isDefault' => 1,
2588 'isRemovable' => 0,
2589 'isRequired' => 1,
2590 'isEditable' => 1,
2591 'label' => wpforo_phrase( 'Email', false ),
2592 'title' => wpforo_phrase( 'Email', false ),
2593 'placeholder' => wpforo_phrase( 'Email', false ),
2594 'minLength' => 0,
2595 'maxLength' => 0,
2596 'faIcon' => 'fas fa-envelope',
2597 'name' => 'user_email',
2598 'cantBeInactive' => [ 'register' ],
2599 'canEdit' => $usergroupids_can_edit_fields,
2600 'canView' => WPF()->usergroup->get_groupids_by_can( 'vmm' ),
2601 'can' => 'vmm',
2602 'isSearchable' => 1,
2603 ];
2604
2605 $this->fields['user_pass'] = [
2606 'fieldKey' => 'user_pass',
2607 'type' => 'password',
2608 'isDefault' => 1,
2609 'isRemovable' => 0,
2610 'isRequired' => 1,
2611 'isEditable' => 1,
2612 'label' => wpforo_phrase( 'Password', false ),
2613 'title' => wpforo_phrase( 'Password', false ),
2614 'placeholder' => wpforo_phrase( 'Password', false ),
2615 'description' => wpforo_phrase( 'Must be minimum 6 characters.', false ),
2616 'minLength' => 6,
2617 'maxLength' => 20,
2618 'faIcon' => 'fas fa-key',
2619 'name' => 'user_pass',
2620 'cantBeInactive' => [
2621 'account',
2622 'register',
2623 ],
2624 'canEdit' => $usergroupids_can_edit_fields,
2625 'canView' => [],
2626 'can' => '',
2627 'isSearchable' => 0,
2628 ];
2629
2630 $this->fields['display_name'] = [
2631 'fieldKey' => 'display_name',
2632 'type' => 'text',
2633 'isDefault' => 1,
2634 'isRemovable' => 0,
2635 'isRequired' => 1,
2636 'isEditable' => 1,
2637 'label' => wpforo_phrase( 'Display Name', false ),
2638 'title' => wpforo_phrase( 'Display Name', false ),
2639 'placeholder' => wpforo_phrase( 'Display Name', false ),
2640 'faIcon' => 'fas fa-user',
2641 'name' => 'display_name',
2642 'cantBeInactive' => [],
2643 'canEdit' => $usergroupids_can_edit_fields,
2644 'canView' => $usergroupids,
2645 'can' => '',
2646 'isSearchable' => 1,
2647 ];
2648
2649 $this->fields['user_nicename'] = [
2650 'fieldKey' => 'user_nicename',
2651 'type' => 'text',
2652 'isDefault' => 1,
2653 'isRemovable' => 0,
2654 'isRequired' => 1,
2655 'isEditable' => 1,
2656 'label' => wpforo_phrase( 'Nickname', false ),
2657 'title' => wpforo_phrase( 'Nickname', false ),
2658 'placeholder' => wpforo_phrase( 'Nickname', false ),
2659 'description' => wpforo_phrase( 'URL Address Identifier', false ),
2660 'minLength' => 0,
2661 'maxLength' => 0,
2662 'faIcon' => 'fas fa-link',
2663 'name' => 'user_nicename',
2664 'cantBeInactive' => [],
2665 'canEdit' => $usergroupids_can_edit_fields,
2666 'canView' => $usergroupids,
2667 'can' => '',
2668 'isSearchable' => 1,
2669 ];
2670
2671 $this->fields['first_name'] = [
2672 'fieldKey' => 'first_name',
2673 'type' => 'text',
2674 'isDefault' => 1,
2675 'isRemovable' => 0,
2676 'isRequired' => 0,
2677 'isEditable' => 1,
2678 'label' => wpforo_phrase( 'First Name', false ),
2679 'title' => wpforo_phrase( 'First Name', false ),
2680 'placeholder' => wpforo_phrase( 'First Name', false ),
2681 'faIcon' => 'fas fa-address-card',
2682 'name' => 'first_name',
2683 'cantBeInactive' => [],
2684 'canEdit' => $usergroupids_can_edit_fields,
2685 'canView' => $usergroupids,
2686 'can' => '',
2687 'isSearchable' => 0,
2688 ];
2689
2690 $this->fields['last_name'] = [
2691 'fieldKey' => 'last_name',
2692 'type' => 'text',
2693 'isDefault' => 1,
2694 'isRemovable' => 0,
2695 'isRequired' => 0,
2696 'isEditable' => 1,
2697 'label' => wpforo_phrase( 'Last Name', false ),
2698 'title' => wpforo_phrase( 'Last Name', false ),
2699 'placeholder' => wpforo_phrase( 'Last Name', false ),
2700 'faIcon' => 'fas fa-address-card',
2701 'name' => 'last_name',
2702 'cantBeInactive' => [],
2703 'canEdit' => $usergroupids_can_edit_fields,
2704 'canView' => $usergroupids,
2705 'can' => '',
2706 'isSearchable' => 0,
2707 ];
2708
2709 $this->fields['title'] = [
2710 'fieldKey' => 'title',
2711 'type' => 'text',
2712 'isDefault' => 1,
2713 'isRemovable' => 0,
2714 'isRequired' => 1,
2715 'isEditable' => 1,
2716 'label' => wpforo_phrase( 'Title', false ),
2717 'title' => wpforo_phrase( 'Title', false ),
2718 'placeholder' => wpforo_phrase( 'Title', false ),
2719 'minLength' => 0,
2720 'maxLength' => 0,
2721 'faIcon' => 'fas fa-user',
2722 'name' => 'title',
2723 'cantBeInactive' => [],
2724 'canEdit' => $usergroupids_can_edit_fields,
2725 'canView' => WPF()->usergroup->get_groupids_by_can( 'vmt' ),
2726 'can' => 'vmt',
2727 'isSearchable' => 1,
2728 ];
2729
2730 $this->fields['groupid'] = [
2731 'fieldKey' => 'groupid',
2732 'type' => 'usergroup',
2733 'isDefault' => 1,
2734 'isRemovable' => 0,
2735 'isRequired' => 1,
2736 'isEditable' => 1,
2737 'label' => wpforo_phrase( 'User Group', false ),
2738 'title' => wpforo_phrase( 'User Group', false ),
2739 'placeholder' => wpforo_phrase( 'User Group', false ),
2740 'faIcon' => 'fas fa-users',
2741 'name' => 'groupid',
2742 'allowedGroupIds' => [ 3, 5 ],
2743 'cantBeInactive' => [],
2744 'canEdit' => $usergroupids_can_edit_fields,
2745 'canView' => $usergroupids,
2746 'can' => '',
2747 'isSearchable' => 1,
2748 ];
2749
2750 $this->fields['secondary_groupids'] = [
2751 'fieldKey' => 'secondary_groupids',
2752 'type' => 'secondary_groups',
2753 'isWrapItem' => 1,
2754 'isDefault' => 1,
2755 'isRemovable' => 0,
2756 'isRequired' => 0,
2757 'isEditable' => 1,
2758 'label' => wpforo_phrase( 'User Groups Secondary', false ),
2759 'title' => wpforo_phrase( 'User Groups Secondary', false ),
2760 'placeholder' => '',
2761 'faIcon' => '',
2762 'values' => '',
2763 'name' => 'secondary_groupids',
2764 'allowedGroupIds' => WPF()->usergroup->get_secondary_groupids(),
2765 'cantBeInactive' => [],
2766 'canEdit' => $usergroupids_can_edit_fields,
2767 'canView' => $usergroupids,
2768 'can' => '',
2769 'isSearchable' => 1,
2770 ];
2771
2772 $this->fields['avatar'] = [
2773 'fieldKey' => 'avatar',
2774 'type' => 'avatar',
2775 'isDefault' => 1,
2776 'isRemovable' => 0,
2777 'isRequired' => 0,
2778 'isEditable' => 1,
2779 'label' => wpforo_phrase( 'Avatar', false ),
2780 'title' => wpforo_phrase( 'Avatar', false ),
2781 'placeholder' => wpforo_phrase( 'Avatar', false ),
2782 'name' => 'avatar',
2783 'cantBeInactive' => [],
2784 'canEdit' => $usergroupids_can_edit_fields,
2785 'canView' => WPF()->usergroup->get_groupids_by_can( 'va' ),
2786 'can' => 'va',
2787 'isSearchable' => 0,
2788 ];
2789
2790 $this->fields['user_url'] = [
2791 'fieldKey' => 'user_url',
2792 'type' => 'url',
2793 'isDefault' => 1,
2794 'isRemovable' => 0,
2795 'isRequired' => 0,
2796 'isEditable' => 1,
2797 'label' => wpforo_phrase( 'Website', false ),
2798 'title' => wpforo_phrase( 'Website', false ),
2799 'placeholder' => wpforo_phrase( 'Website', false ),
2800 'faIcon' => 'fas fa-sitemap',
2801 'name' => 'user_url',
2802 'cantBeInactive' => [],
2803 'canEdit' => $usergroupids_can_edit_fields,
2804 'canView' => WPF()->usergroup->get_groupids_by_can( 'vmw' ),
2805 'can' => 'vmw',
2806 'isSearchable' => 1,
2807 ];
2808
2809 $this->fields['facebook'] = [
2810 'fieldKey' => 'facebook',
2811 'type' => 'url',
2812 'isDefault' => 0,
2813 'isRemovable' => 1,
2814 'isRequired' => 0,
2815 'isEditable' => 1,
2816 'label' => wpforo_phrase( 'Facebook', false ),
2817 'title' => wpforo_phrase( 'Facebook', false ),
2818 'placeholder' => wpforo_phrase( 'Facebook', false ),
2819 'faIcon' => 'fab fa-facebook',
2820 'name' => 'facebook',
2821 'cantBeInactive' => [],
2822 'canEdit' => $usergroupids_can_edit_fields,
2823 'canView' => $usergroupids_can_view_social_net,
2824 'can' => 'vmsn',
2825 'isSearchable' => 1,
2826 ];
2827
2828 $this->fields['twitter'] = [
2829 'fieldKey' => 'twitter',
2830 'type' => 'url',
2831 'isDefault' => 0,
2832 'isRemovable' => 1,
2833 'isRequired' => 0,
2834 'isEditable' => 1,
2835 'label' => wpforo_phrase( 'X.com', false ),
2836 'title' => wpforo_phrase( 'X.com', false ),
2837 'placeholder' => wpforo_phrase( 'X.com', false ),
2838 'faIcon' => 'fa-brands fa-square-x-twitter',
2839 'name' => 'twitter',
2840 'cantBeInactive' => [],
2841 'canEdit' => $usergroupids_can_edit_fields,
2842 'canView' => $usergroupids_can_view_social_net,
2843 'can' => 'vmsn',
2844 'isSearchable' => 1,
2845 ];
2846
2847 $this->fields['youtube'] = [
2848 'fieldKey' => 'youtube',
2849 'type' => 'url',
2850 'isDefault' => 0,
2851 'isRemovable' => 1,
2852 'isRequired' => 0,
2853 'isEditable' => 1,
2854 'label' => wpforo_phrase( 'YouTube', false ),
2855 'title' => wpforo_phrase( 'YouTube', false ),
2856 'placeholder' => wpforo_phrase( 'YouTube', false ),
2857 'faIcon' => 'fab fa-youtube',
2858 'name' => 'youtube',
2859 'cantBeInactive' => [],
2860 'canEdit' => $usergroupids_can_edit_fields,
2861 'canView' => $usergroupids_can_view_social_net,
2862 'can' => 'vmsn',
2863 'isSearchable' => 1,
2864 ];
2865
2866 $this->fields['vkontakte'] = [
2867 'fieldKey' => 'vkontakte',
2868 'type' => 'url',
2869 'isDefault' => 0,
2870 'isRemovable' => 1,
2871 'isRequired' => 0,
2872 'isEditable' => 1,
2873 'label' => wpforo_phrase( 'VKontakte', false ),
2874 'title' => wpforo_phrase( 'VKontakte', false ),
2875 'placeholder' => wpforo_phrase( 'VKontakte', false ),
2876 'faIcon' => 'fab fa-vk',
2877 'name' => 'vkontakte',
2878 'cantBeInactive' => [],
2879 'canEdit' => $usergroupids_can_edit_fields,
2880 'canView' => $usergroupids_can_view_social_net,
2881 'can' => 'vmsn',
2882 'isSearchable' => 1,
2883 ];
2884
2885 $this->fields['linkedin'] = [
2886 'fieldKey' => 'linkedin',
2887 'type' => 'url',
2888 'isDefault' => 0,
2889 'isRemovable' => 1,
2890 'isRequired' => 0,
2891 'isEditable' => 1,
2892 'label' => wpforo_phrase( 'LinkedIn', false ),
2893 'title' => wpforo_phrase( 'LinkedIn', false ),
2894 'placeholder' => wpforo_phrase( 'LinkedIn', false ),
2895 'faIcon' => 'fab fa-linkedin-in',
2896 'name' => 'linkedin',
2897 'cantBeInactive' => [],
2898 'canEdit' => $usergroupids_can_edit_fields,
2899 'canView' => $usergroupids_can_view_social_net,
2900 'can' => 'vmsn',
2901 'isSearchable' => 1,
2902 ];
2903
2904 $this->fields['telegram'] = [
2905 'fieldKey' => 'telegram',
2906 'type' => 'text',
2907 'isDefault' => 0,
2908 'isRemovable' => 1,
2909 'isRequired' => 0,
2910 'isEditable' => 1,
2911 'label' => wpforo_phrase( 'Telegram', false ),
2912 'title' => wpforo_phrase( 'Telegram', false ),
2913 'placeholder' => wpforo_phrase( 'Telegram', false ),
2914 'faIcon' => 'fab fa-telegram-plane',
2915 'name' => 'telegram',
2916 'cantBeInactive' => [],
2917 'canEdit' => $usergroupids_can_edit_fields,
2918 'canView' => $usergroupids_can_view_social_net,
2919 'can' => 'vmsn',
2920 'isSearchable' => 1,
2921 ];
2922
2923 $this->fields['instagram'] = [
2924 'fieldKey' => 'instagram',
2925 'type' => 'url',
2926 'isDefault' => 0,
2927 'isRemovable' => 1,
2928 'isRequired' => 0,
2929 'isEditable' => 1,
2930 'label' => wpforo_phrase( 'Instagram', false ),
2931 'title' => wpforo_phrase( 'Instagram', false ),
2932 'placeholder' => wpforo_phrase( 'Instagram', false ),
2933 'faIcon' => 'fab fa-instagram',
2934 'name' => 'instagram',
2935 'cantBeInactive' => [],
2936 'canEdit' => $usergroupids_can_edit_fields,
2937 'canView' => $usergroupids_can_view_social_net,
2938 'can' => 'vmsn',
2939 'isSearchable' => 1,
2940 ];
2941
2942 $this->fields['skype'] = [
2943 'fieldKey' => 'skype',
2944 'type' => 'text',
2945 'isDefault' => 0,
2946 'isRemovable' => 1,
2947 'isRequired' => 0,
2948 'isEditable' => 1,
2949 'label' => wpforo_phrase( 'Skype', false ),
2950 'title' => wpforo_phrase( 'Skype', false ),
2951 'placeholder' => wpforo_phrase( 'Skype', false ),
2952 'faIcon' => 'fab fa-skype',
2953 'name' => 'skype',
2954 'cantBeInactive' => [],
2955 'canEdit' => $usergroupids_can_edit_fields,
2956 'canView' => $usergroupids_can_view_social_net,
2957 'can' => 'vmsn',
2958 'isSearchable' => 1,
2959 ];
2960
2961 $this->fields['location'] = [
2962 'fieldKey' => 'location',
2963 'type' => 'select',
2964 'isDefault' => 1,
2965 'isRemovable' => 0,
2966 'isRequired' => 0,
2967 'isEditable' => 1,
2968 'label' => wpforo_phrase( 'Location', false ),
2969 'title' => wpforo_phrase( 'Location', false ),
2970 'placeholder' => wpforo_phrase( 'Location', false ),
2971 'faIcon' => 'fas fa-globe',
2972 'values' => $this->countries,
2973 'name' => 'location',
2974 'cantBeInactive' => [],
2975 'canEdit' => $usergroupids_can_edit_fields,
2976 'canView' => WPF()->usergroup->get_groupids_by_can( 'vml' ),
2977 'can' => 'vml',
2978 'isSearchable' => 1,
2979 ];
2980
2981 $this->fields['timezone'] = [
2982 'fieldKey' => 'timezone',
2983 'type' => 'select',
2984 'isDefault' => 1,
2985 'isRemovable' => 0,
2986 'isRequired' => 0,
2987 'isEditable' => 1,
2988 'label' => wpforo_phrase( 'Timezone', false ),
2989 'title' => wpforo_phrase( 'Timezone', false ),
2990 'placeholder' => wpforo_phrase( 'Timezone', false ),
2991 'faIcon' => 'fas fa-globe',
2992 'values' => $this->timezones,
2993 'name' => 'timezone',
2994 'cantBeInactive' => [],
2995 'canEdit' => $usergroupids_can_edit_fields,
2996 'canView' => $usergroupids,
2997 'can' => '',
2998 'isSearchable' => 1,
2999 ];
3000
3001 $this->fields['occupation'] = [
3002 'fieldKey' => 'occupation',
3003 'type' => 'text',
3004 'isDefault' => 1,
3005 'isRemovable' => 0,
3006 'isRequired' => 0,
3007 'isEditable' => 1,
3008 'label' => wpforo_phrase( 'Occupation', false ),
3009 'title' => wpforo_phrase( 'Occupation', false ),
3010 'placeholder' => wpforo_phrase( 'Occupation', false ),
3011 'faIcon' => 'fas fa-address-card',
3012 'name' => 'occupation',
3013 'cantBeInactive' => [],
3014 'canEdit' => $usergroupids_can_edit_fields,
3015 'canView' => WPF()->usergroup->get_groupids_by_can( 'vmo' ),
3016 'can' => 'vmo',
3017 'isSearchable' => 1,
3018 ];
3019
3020 $this->fields['signature'] = [
3021 'fieldKey' => 'signature',
3022 'type' => 'tinymce',
3023 'wp_editor_settings' => $wp_editor_settings,
3024 'isDefault' => 1,
3025 'isRemovable' => 0,
3026 'isRequired' => 0,
3027 'isEditable' => 1,
3028 'label' => wpforo_phrase( 'Signature', false ),
3029 'title' => wpforo_phrase( 'Signature', false ),
3030 'placeholder' => wpforo_phrase( 'Signature', false ),
3031 'faIcon' => 'fas fa-address-card',
3032 'name' => 'signature',
3033 'cantBeInactive' => [],
3034 'canEdit' => $usergroupids_can_edit_fields,
3035 'canView' => WPF()->usergroup->get_groupids_by_can( 'vms' ),
3036 'can' => 'vms',
3037 'isSearchable' => 1,
3038 ];
3039
3040 $this->fields['about'] = [
3041 'fieldKey' => 'about',
3042 'type' => 'tinymce',
3043 'wp_editor_settings' => $wp_editor_settings,
3044 'isDefault' => 1,
3045 'isRemovable' => 0,
3046 'isRequired' => 0,
3047 'isEditable' => 1,
3048 'label' => wpforo_phrase( 'About Me', false ),
3049 'title' => wpforo_phrase( 'About Me', false ),
3050 'placeholder' => wpforo_phrase( 'About Me', false ),
3051 'faIcon' => 'fas fa-address-card',
3052 'name' => 'about',
3053 'cantBeInactive' => [],
3054 'canEdit' => $usergroupids_can_edit_fields,
3055 'canView' => WPF()->usergroup->get_groupids_by_can( 'vmam' ),
3056 'can' => 'vmam',
3057 'isSearchable' => 1,
3058 ];
3059
3060 $this->fields['html_soc_net'] = [
3061 'fieldKey' => 'html_soc_net',
3062 'type' => 'html',
3063 'isDefault' => 1,
3064 'isRemovable' => 0,
3065 'isRequired' => 0,
3066 'isEditable' => 1,
3067 'label' => wpforo_phrase( 'Social Networks', false ),
3068 'title' => wpforo_phrase( 'Social Networks', false ),
3069 'placeholder' => wpforo_phrase( 'Social Networks', false ),
3070 'description' => wpforo_phrase( 'Social Networks', false ),
3071 'html' => '<div class="wpf-label">' . wpforo_phrase( 'Social Networks', false ) . '</div>',
3072 'name' => 'html_soc_net',
3073 'cantBeInactive' => [],
3074 'canEdit' => $usergroupids_can_edit_fields,
3075 'canView' => $usergroupids_can_view_social_net,
3076 'can' => 'vmsn',
3077 'isSearchable' => 0,
3078 ];
3079
3080 $this->fields = apply_filters( 'wpforo_member_after_init_fields', $this->fields );
3081 }
3082
3083 private function init_countries() {
3084 $this->countries = [
3085 "Afghanistan",
3086 "�
3087 land Islands",
3088 "Albania",
3089 "Algeria",
3090 "American Samoa",
3091 "Andorra",
3092 "Angola",
3093 "Anguilla",
3094 "Antarctica",
3095 "Antigua and Barbuda",
3096 "Argentina",
3097 "Armenia",
3098 "Aruba",
3099 "Australia",
3100 "Austria",
3101 "Azerbaijan",
3102 "Bahamas",
3103 "Bahrain",
3104 "Bangladesh",
3105 "Barbados",
3106 "Belarus",
3107 "Belgium",
3108 "Belize",
3109 "Benin",
3110 "Bermuda",
3111 "Bhutan",
3112 "Bolivia",
3113 "Bosnia and Herzegovina",
3114 "Botswana",
3115 "Bouvet Island",
3116 "Brazil",
3117 "British Indian Ocean Territory",
3118 "Brunei Darussalam",
3119 "Bulgaria",
3120 "Burkina Faso",
3121 "Burundi",
3122 "Cambodia",
3123 "Cameroon",
3124 "Canada",
3125 "Cape Verde",
3126 "Cayman Islands",
3127 "Central African Republic",
3128 "Chad",
3129 "Chile",
3130 "China",
3131 "Christmas Island",
3132 "Cocos (Keeling) Islands",
3133 "Colombia",
3134 "Comoros",
3135 "Congo",
3136 "Congo, The Democratic Republic of The",
3137 "Cook Islands",
3138 "Costa Rica",
3139 "Cote D'ivoire",
3140 "Croatia",
3141 "Cuba",
3142 "Cyprus",
3143 "Czech Republic",
3144 "Denmark",
3145 "Djibouti",
3146 "Dominica",
3147 "Dominican Republic",
3148 "Ecuador",
3149 "Egypt",
3150 "El Salvador",
3151 "Equatorial Guinea",
3152 "Eritrea",
3153 "Estonia",
3154 "Ethiopia",
3155 "Falkland Islands (Malvinas)",
3156 "Faroe Islands",
3157 "Fiji",
3158 "Finland",
3159 "France",
3160 "French Guiana",
3161 "French Polynesia",
3162 "French Southern Territories",
3163 "Gabon",
3164 "Gambia",
3165 "Georgia",
3166 "Germany",
3167 "Ghana",
3168 "Gibraltar",
3169 "Greece",
3170 "Greenland",
3171 "Grenada",
3172 "Guadeloupe",
3173 "Guam",
3174 "Guatemala",
3175 "Guernsey",
3176 "Guinea",
3177 "Guinea-bissau",
3178 "Guyana",
3179 "Haiti",
3180 "Heard Island and Mcdonald Islands",
3181 "Holy See (Vatican City State)",
3182 "Honduras",
3183 "Hong Kong",
3184 "Hungary",
3185 "Iceland",
3186 "India",
3187 "Indonesia",
3188 "Iran, Islamic Republic of",
3189 "Iraq",
3190 "Ireland",
3191 "Isle of Man",
3192 "Israel",
3193 "Italy",
3194 "Jamaica",
3195 "Japan",
3196 "Jersey",
3197 "Jordan",
3198 "Kazakhstan",
3199 "Kenya",
3200 "Kiribati",
3201 "Korea, Democratic People's Republic of",
3202 "Korea, Republic of",
3203 "Kuwait",
3204 "Kyrgyzstan",
3205 "Lao People's Democratic Republic",
3206 "Latvia",
3207 "Lebanon",
3208 "Lesotho",
3209 "Liberia",
3210 "Libyan Arab Jamahiriya",
3211 "Liechtenstein",
3212 "Lithuania",
3213 "Luxembourg",
3214 "Macao",
3215 "Macedonia, The Former Yugoslav Republic of",
3216 "Madagascar",
3217 "Malawi",
3218 "Malaysia",
3219 "Maldives",
3220 "Mali",
3221 "Malta",
3222 "Marshall Islands",
3223 "Martinique",
3224 "Mauritania",
3225 "Mauritius",
3226 "Mayotte",
3227 "Mexico",
3228 "Micronesia, Federated States of",
3229 "Moldova, Republic of",
3230 "Monaco",
3231 "Mongolia",
3232 "Montenegro",
3233 "Montserrat",
3234 "Morocco",
3235 "Mozambique",
3236 "Myanmar",
3237 "Namibia",
3238 "Nauru",
3239 "Nepal",
3240 "Netherlands",
3241 "Netherlands Antilles",
3242 "New Caledonia",
3243 "New Zealand",
3244 "Nicaragua",
3245 "Niger",
3246 "Nigeria",
3247 "Niue",
3248 "Norfolk Island",
3249 "Northern Mariana Islands",
3250 "Norway",
3251 "Oman",
3252 "Pakistan",
3253 "Palau",
3254 "Palestinian Territory, Occupied",
3255 "Panama",
3256 "Papua New Guinea",
3257 "Paraguay",
3258 "Peru",
3259 "Philippines",
3260 "Pitcairn",
3261 "Poland",
3262 "Portugal",
3263 "Puerto Rico",
3264 "Qatar",
3265 "Reunion",
3266 "Romania",
3267 "Russian Federation",
3268 "Rwanda",
3269 "Saint Helena",
3270 "Saint Kitts and Nevis",
3271 "Saint Lucia",
3272 "Saint Pierre and Miquelon",
3273 "Saint Vincent and The Grenadines",
3274 "Samoa",
3275 "San Marino",
3276 "Sao Tome and Principe",
3277 "Saudi Arabia",
3278 "Senegal",
3279 "Serbia",
3280 "Seychelles",
3281 "Sierra Leone",
3282 "Singapore",
3283 "Slovakia",
3284 "Slovenia",
3285 "Solomon Islands",
3286 "Somalia",
3287 "South Africa",
3288 "South Georgia and The South Sandwich Islands",
3289 "Spain",
3290 "Sri Lanka",
3291 "Sudan",
3292 "Suriname",
3293 "Svalbard and Jan Mayen",
3294 "Swaziland",
3295 "Sweden",
3296 "Switzerland",
3297 "Syrian Arab Republic",
3298 "Taiwan, Province of China",
3299 "Tajikistan",
3300 "Tanzania, United Republic of",
3301 "Thailand",
3302 "Timor-leste",
3303 "Togo",
3304 "Tokelau",
3305 "Tonga",
3306 "Trinidad and Tobago",
3307 "Tunisia",
3308 "Turkey",
3309 "Turkmenistan",
3310 "Turks and Caicos Islands",
3311 "Tuvalu",
3312 "Uganda",
3313 "Ukraine",
3314 "United Arab Emirates",
3315 "United Kingdom",
3316 "United States",
3317 "United States Minor Outlying Islands",
3318 "Uruguay",
3319 "Uzbekistan",
3320 "Vanuatu",
3321 "Venezuela",
3322 "Viet Nam",
3323 "Virgin Islands, British",
3324 "Virgin Islands, U.S.",
3325 "Wallis and Futuna",
3326 "Western Sahara",
3327 "Yemen",
3328 "Zambia",
3329 "Zimbabwe",
3330 ];
3331 }
3332
3333 private function init_timezones() {
3334 $this->timezones = timezone_identifiers_list();
3335 $offset_range = [
3336 - 12,
3337 - 11.5,
3338 - 11,
3339 - 10.5,
3340 - 10,
3341 - 9.5,
3342 - 9,
3343 - 8.5,
3344 - 8,
3345 - 7.5,
3346 - 7,
3347 - 6.5,
3348 - 6,
3349 - 5.5,
3350 - 5,
3351 - 4.5,
3352 - 4,
3353 - 3.5,
3354 - 3,
3355 - 2.5,
3356 - 2,
3357 - 1.5,
3358 - 1,
3359 - 0.5,
3360 0,
3361 0.5,
3362 1,
3363 1.5,
3364 2,
3365 2.5,
3366 3,
3367 3.5,
3368 4,
3369 4.5,
3370 5,
3371 5.5,
3372 5.75,
3373 6,
3374 6.5,
3375 7,
3376 7.5,
3377 8,
3378 8.5,
3379 8.75,
3380 9,
3381 9.5,
3382 10,
3383 10.5,
3384 11,
3385 11.5,
3386 12,
3387 12.75,
3388 13,
3389 13.75,
3390 14,
3391 ];
3392 foreach( $offset_range as $offset ) {
3393 if( 0 <= $offset ) {
3394 $offset_name = '+' . $offset;
3395 } else {
3396 $offset_name = (string) $offset;
3397 }
3398
3399 $offset_value = $offset_name;
3400 $offset_value = 'UTC' . $offset_value;
3401 $this->timezones[] = 'UTC/' . $offset_value;
3402 }
3403
3404 $zones = $this->timezones;
3405 $timezones = [];
3406 foreach( $zones as $zone ) {
3407 if( strpos( (string) $zone, '/' ) === false ) continue;
3408
3409 $zone = str_replace( '_', ' ', $zone );
3410
3411 $group = function_exists( 'mb_substr' ) ? mb_substr(
3412 (string) $zone,
3413 0,
3414 strpos( (string) $zone, '/' )
3415 ) : substr( (string) $zone, 0, strpos( (string) $zone, '/' ) );
3416 $index = function_exists( 'mb_strlen' ) ? mb_strlen( (string) $group ) + 1 : strlen(
3417 (string) $group
3418 ) + 1;
3419 $optionValue = substr( (string) $zone, $index );
3420
3421 if( strpos( (string) $optionValue, 'UTC' ) !== false ) {
3422 $optionTitle = str_replace( [ '.25', '.5', '.75', ], [ ':15', ':30', ':45', ], $optionValue );
3423 $optionValue = "$optionValue=>$optionTitle";
3424 } else {
3425 $optionValue = "$zone=>$optionValue";
3426 }
3427
3428 $timezones[ $group ][] = $optionValue;
3429 }
3430
3431 $this->timezones = $timezones;
3432 }
3433
3434 public function get_fields( $only_defaults = false ) {
3435 $this->init_fields();
3436 $fields = $this->fields;
3437 if( $only_defaults ) {
3438 foreach( $fields as $k => $v ) if( ! wpfval( $v, 'isDefault' ) ) unset( $fields[ $k ] );
3439 } else {
3440 $this->fields = $fields = apply_filters( 'wpforo_get_fields', $fields );
3441 }
3442
3443 return $fields;
3444 }
3445
3446 public function get_field( $key ) {
3447 if( is_string( $key ) ) {
3448 $this->init_fields();
3449
3450 return (array) wpfval( $this->fields, $key );
3451 } elseif( $this->is_field( $key ) ) {
3452 return $key;
3453 }
3454
3455 return [];
3456 }
3457
3458 public function is_field( $field ) {
3459 return wpfval( $field, 'fieldKey' ) && wpfval( $field, 'type' ) && wpfkey( $field, 'isDefault' );
3460 }
3461
3462 public function get_field_key( $field ) {
3463 return is_string( $field ) ? $field : (string) wpfval( $field, 'fieldKey' );
3464 }
3465
3466 public function fields_structure_full_array( $fields, $need_password = null ) {
3467 if( is_string( $fields ) ) $fields = maybe_unserialize( $fields );
3468 $fs = [ [ [] ] ];
3469 if( ! is_array( $fields ) ) return $fs;
3470 if( is_null( $need_password ) ) {
3471 foreach( $fields as $kr => $row ) {
3472 if( is_array( $row ) ) {
3473 foreach( $row as $kc => $cols ) {
3474 if( is_array( $cols ) ) {
3475 foreach( $cols as $field ) {
3476 $field_key = $this->get_field_key( $field );
3477 $fs[ $kr ][ $kc ][ $field_key ] = $this->get_field( $field );
3478 }
3479 }
3480 }
3481 }
3482 }
3483 } else {
3484 $has_password = false;
3485 foreach( $fields as $kr => $row ) {
3486 if( is_array( $row ) ) {
3487 foreach( $row as $kc => $cols ) {
3488 if( is_array( $cols ) ) {
3489 foreach( $cols as $field ) {
3490 $field_key = $this->get_field_key( $field );
3491 if( $field_key === 'user_pass' ) {
3492 if( $need_password ) {
3493 $has_password = true;
3494 $fs[ $kr ][ $kc ][ $field_key ] = $this->get_field( $field );
3495 }
3496 } else {
3497 $fs[ $kr ][ $kc ][ $field_key ] = $this->get_field( $field );
3498 }
3499 }
3500 }
3501 }
3502 }
3503 }
3504 if( $need_password && ! $has_password ) $fs[][][] = $this->get_field( 'user_pass' );
3505 }
3506
3507 return $fs;
3508 }
3509
3510 public function get_register_fields_structure( $only_defaults = false ) {
3511 $need_password = ! wpforo_setting( 'authorization', 'user_register_email_confirm' );
3512 $regform = [ 'user_login', 'user_email' ];
3513 if( $need_password ) $regform[] = 'user_pass';
3514 $fields = [ [ $regform ] ];
3515 if( ! $only_defaults ) $fields = apply_filters( 'wpforo_get_register_fields', $fields );
3516
3517 return $fields;
3518 }
3519
3520 public function get_register_fields( $only_defaults = false ) {
3521 return $this->fields_structure_full_array(
3522 $this->get_register_fields_structure( $only_defaults ),
3523 ! wpforo_setting( 'authorization', 'user_register_email_confirm' )
3524 );
3525 }
3526
3527 public function get_account_fields_structure( $only_defaults = false ) {
3528 $fields = [
3529 [
3530 [
3531 'user_login',
3532 'display_name',
3533 'user_nicename',
3534 'user_email',
3535 'title',
3536 'groupid',
3537 'avatar',
3538 'about',
3539 'user_url',
3540 'occupation',
3541 'signature',
3542 ],
3543 ],
3544 [
3545 [
3546 'html_soc_net',
3547 ],
3548 [
3549 'facebook',
3550 'linkedin',
3551 'instagram',
3552 'vkontakte',
3553 ],
3554 [
3555 'twitter',
3556 'youtube',
3557 'telegram',
3558 'skype',
3559 ],
3560 ],
3561 [
3562 [
3563 'location',
3564 'timezone',
3565 'user_pass',
3566 ],
3567 ],
3568 ];
3569 if( ! $only_defaults ) $fields = apply_filters( 'wpforo_get_account_fields', $fields );
3570
3571 return $fields;
3572 }
3573
3574 public function get_account_fields( $only_defaults = false ) {
3575 return $this->fields_structure_full_array( $this->get_account_fields_structure( $only_defaults ) );
3576 }
3577
3578 public function get_profile_fields_structure( $only_defaults = false ) {
3579 $fields = [
3580 [
3581 [
3582 'about',
3583 'user_url',
3584 ],
3585 ],
3586 [
3587 [
3588 'location',
3589 'timezone',
3590 'occupation',
3591 'signature',
3592 ],
3593 ],
3594 [
3595 [
3596 'html_soc_net',
3597 ],
3598 ],
3599 [
3600 [
3601 'facebook',
3602 'linkedin',
3603 'instagram',
3604 'vkontakte',
3605 ],
3606 [
3607 'twitter',
3608 'youtube',
3609 'telegram',
3610 'skype',
3611 ],
3612 ],
3613 ];
3614 if( ! $only_defaults ) $fields = apply_filters( 'wpforo_get_profile_fields', $fields );
3615
3616 return $fields;
3617 }
3618
3619 public function get_profile_fields( $only_defaults = false ) {
3620 return $this->fields_structure_full_array( $this->get_profile_fields_structure( $only_defaults ) );
3621 }
3622
3623 public function get_search_fields_structure( $only_defaults = false ) {
3624 $fields = [ [ [ 'display_name', 'user_nicename' ] ] ];
3625 if( ! $only_defaults ) $fields = apply_filters( 'wpforo_get_search_fields', $fields );
3626
3627 return $fields;
3628 }
3629
3630 public function get_search_fields( $only_defaults = false ) {
3631 $fields = $this->fields_structure_full_array( $this->get_search_fields_structure( $only_defaults ) );
3632 foreach( $fields as $row_key => $row ) {
3633 foreach( $row as $col_key => $col ) {
3634 foreach( $col as $field_key => $field ) {
3635 if( $this->is_field( $field ) ) {
3636 $fields[ $row_key ][ $col_key ][ $field_key ]['isRequired'] = 0;
3637 $fields[ $row_key ][ $col_key ][ $field_key ]['class'] = 'wpf-member-search-field';
3638 if( in_array( $field['type'], [ 'text', 'textarea', 'email', 'url' ], true ) ) {
3639 $fields[ $row_key ][ $col_key ][ $field_key ]['type'] = 'search';
3640 }
3641 }
3642 }
3643 }
3644 }
3645
3646 return $fields;
3647 }
3648
3649 public function get_search_fields_names( $only_defaults = false ) {
3650 $names = [];
3651 $fields = $this->get_search_fields( $only_defaults );
3652
3653 foreach( $fields as $row ) {
3654 foreach( $row as $col ) {
3655 foreach( $col as $field ) {
3656 if( $only_defaults && ! $field['isDefault'] ) continue;
3657 $names[] = $field['name'];
3658 }
3659 }
3660 }
3661
3662 return $names;
3663 }
3664
3665 public function get_participant_fields_list( $fields, $only_defaults = false ): array {
3666 $names = [];
3667
3668 foreach( $fields as $row ) {
3669 foreach( $row as $col ) {
3670 foreach( $col as $field ) {
3671 if( $only_defaults && ! $field['isDefault'] ) continue;
3672 $names[] = $field['name'];
3673 }
3674 }
3675 }
3676
3677 return $names;
3678 }
3679
3680 public function set_guest_cookies( $args ) {
3681 if( ! wpforo_setting( 'legal', 'cookies' ) ) return;
3682 if( isset( $args['name'] ) && isset( $args['email'] ) ) {
3683 $comment_cookie_lifetime = apply_filters( 'comment_cookie_lifetime', 30000000 );
3684 $secure = ( 'https' === parse_url( home_url(), PHP_URL_SCHEME ) );
3685 setcookie(
3686 'comment_author_' . COOKIEHASH,
3687 $args['name'],
3688 time() + $comment_cookie_lifetime,
3689 COOKIEPATH,
3690 COOKIE_DOMAIN,
3691 $secure
3692 );
3693 setcookie(
3694 'comment_author_email_' . COOKIEHASH,
3695 $args['email'],
3696 time() + $comment_cookie_lifetime,
3697 COOKIEPATH,
3698 COOKIE_DOMAIN,
3699 $secure
3700 );
3701
3702 WPF()->current_user_display_name = $args['name'];
3703 WPF()->current_user_email = $args['email'];
3704 }
3705 }
3706
3707 public function get_guest_cookies() {
3708 $guest = [ 'name' => '', 'email' => '' ];
3709 if( wpforo_setting( 'legal', 'cookies' ) ) {
3710 if( ! WPF()->current_userid && WPF()->current_user_email ) {
3711 $guest['name'] = WPF()->current_user_display_name;
3712 $guest['email'] = WPF()->current_user_email;
3713 } else {
3714 $guest_cookies = wp_get_current_commenter();
3715 $guest['name'] = ( isset( $guest_cookies['comment_author'] ) ) ? $guest_cookies['comment_author'] : '';
3716 $guest['email'] = ( isset( $guest_cookies['comment_author_email'] ) ) ? $guest_cookies['comment_author_email'] : '';
3717 }
3718 }
3719
3720 return $guest;
3721 }
3722
3723 public function set_is_email_confirmed( $userid, $status ) {
3724 if( false !== WPF()->db->update(
3725 WPF()->tables->profiles,
3726 [ 'is_email_confirmed' => intval( $status ) ],
3727 [ 'userid' => wpforo_bigintval( $userid ) ],
3728 [ '%d' ],
3729 [ '%d' ]
3730 ) ) {
3731 WPF()->notice->add( 'Email has been confirmed', 'success' );
3732
3733 return true;
3734 }
3735 WPF()->notice->add( 'Email confirm error', 'error' );
3736
3737 return false;
3738 }
3739
3740 public function get_is_email_confirmed( $userid ) {
3741 $sql = "SELECT `is_email_confirmed` FROM `" . WPF()->tables->profiles . "` WHERE `userid` = %d";
3742
3743 return (bool) WPF()->db->get_var( WPF()->db->prepare( $sql, $userid ) );
3744 }
3745
3746 /**
3747 * @param int $userid
3748 *
3749 * @return int
3750 */
3751 public function get_groupid( $userid ) {
3752 return (int) WPF()->db->get_var(
3753 WPF()->db->prepare(
3754 "SELECT `groupid`
3755 FROM `" . WPF()->tables->profiles . "`
3756 WHERE `userid` = %d",
3757 $userid
3758 )
3759 );
3760 }
3761
3762
3763 /**
3764 * @param int $userid
3765 * @param boolean $array
3766 *
3767 * @return string|array|null
3768 */
3769 public function get_secondary_groupids( $userid, $array = true ) {
3770 $secondary_groupids = WPF()->db->get_var(
3771 WPF()->db->prepare(
3772 "SELECT `secondary_groupids`
3773 FROM `" . WPF()->tables->profiles . "`
3774 WHERE `userid` = %d",
3775 $userid
3776 )
3777 );
3778
3779 if( $array && ! empty( $secondary_groupids ) ) return explode( ',', $secondary_groupids );
3780
3781 return $secondary_groupids;
3782 }
3783
3784 public function set_groupid( $userid, $groupid ) {
3785 $userid = wpforo_bigintval( $userid );
3786 $groupid = intval( $groupid );
3787 if( $userid && $groupid ) {
3788 $sql = "UPDATE `" . WPF()->tables->profiles . "` SET `groupid` = %d WHERE `userid` = %d";
3789 if( false !== WPF()->db->query( WPF()->db->prepare( $sql, $groupid, $userid ) ) ) {
3790 $this->reset( $userid );
3791 do_action( 'wpforo_set_groupid', $userid, $groupid );
3792
3793 return true;
3794 }
3795 }
3796
3797 return false;
3798 }
3799
3800 /**
3801 * @param int $userid
3802 * @param array|int $groupids
3803 *
3804 * @return bool
3805 */
3806 public function set_secondary_groupids( $userid, $groupids ) {
3807 $userid = wpforo_bigintval( $userid );
3808 if( $userid ) {
3809 $groupids = implode( ',', array_unique( array_filter( array_map( 'intval', (array) $groupids ) ) ) );
3810 $sql = "UPDATE `" . WPF()->tables->profiles . "` SET `secondary_groupids` = %s WHERE `userid` = %d";
3811 if( false !== WPF()->db->query( WPF()->db->prepare( $sql, $groupids, $userid ) ) ) {
3812 $this->reset( $userid );
3813
3814 return true;
3815 }
3816 }
3817
3818 return false;
3819 }
3820
3821 /**
3822 * @param int $userid
3823 * @param array|int $groupids
3824 *
3825 * @return bool
3826 */
3827 public function append_secondary_groupids( $userid, $groupids ) {
3828 $userid = wpforo_bigintval( $userid );
3829 if( $userid ) {
3830 if( $member = $this->get_member( $userid ) ) {
3831 $groupids = array_filter( array_map( 'intval', (array) $groupids ) );
3832 $groupids = array_merge( $member['secondary_groupids'], $groupids );
3833
3834 return $this->set_secondary_groupids( $userid, $groupids );
3835 }
3836 }
3837
3838 return false;
3839 }
3840
3841 public function after_register_new_user( $userid ) {
3842 if( wpforo_setting( 'authorization', 'manually_approval' ) || (int) trim(
3843 (string) get_user_meta( $userid, 'default_password_nag', true )
3844 ) ) {
3845 $this->update_profile_fields( $userid, [ 'status' => 'inactive' ], false );
3846 }
3847 }
3848
3849 public function after_password_reset( $user ) {
3850 $status = $this->get_status( $user->ID );
3851 if( $status !== 'banned' ) {
3852 $data = [ 'status' => 'active', 'is_email_confirmed' => 1 ];
3853 if( wpforo_setting( 'authorization', 'manually_approval' ) ) $data['status'] = $status;
3854 $this->update_profile_fields( $user->ID, $data, false );
3855 $this->reset( $user->ID );
3856 }
3857 }
3858
3859 public function wp_login( $user_login, $user ) {
3860 $this->inactive_to_active( wpforo_bigintval( $user->ID ), $user_login );
3861 }
3862
3863 public function get_distinct_status() {
3864 $sql = "SELECT DISTINCT `status` as statuses FROM `" . WPF()->tables->profiles . "`";
3865
3866 return WPF()->db->get_col( $sql );
3867 }
3868
3869 /**
3870 * @param array $args
3871 *
3872 * @return array selected userids
3873 */
3874 public function get_userids( $args ) {
3875 $sql = "SELECT `userid` FROM `" . WPF()->tables->profiles . "`";
3876 if( $args ) {
3877 $wheres = [];
3878 foreach( $args as $field => $value ) {
3879 $wheres[] = "`" . $field . "` = '" . esc_sql( $value ) . "'";
3880 }
3881 if( $wheres ) $sql .= " WHERE " . implode( " AND ", $wheres );
3882 }
3883
3884 $userids = WPF()->db->get_col( $sql );
3885
3886 return apply_filters( 'wpforo_member_after_get_userids', $userids );
3887 }
3888
3889 public function get_inlist_enabled_statuses() {
3890 return wpforo_setting( 'members', 'hide_inactive' ) ? [ 'active' ] : [ 'active', 'inactive', 'banned' ];
3891 }
3892
3893 /**
3894 * @param int $userid
3895 *
3896 * @return int
3897 */
3898 public function calc_approved_posts( $userid ) {
3899 if( $userid = wpforo_bigintval( $userid ) ) {
3900 $sqls = [];
3901 foreach( WPF()->get_active_boards_tables( 'posts' ) as $table ) {
3902 $sqls[] = WPF()->db->prepare(
3903 "SELECT COUNT(*)
3904 FROM `$table`
3905 WHERE `status` = 0
3906 AND `userid` = %d",
3907 $userid
3908 );
3909 }
3910
3911 if( $sqls ) {
3912 return (int) WPF()->db->get_var(
3913 "SELECT SUM(`COUNT(*)`) FROM (" . implode( ' UNION ALL ', $sqls ) . ") AS temp"
3914 );
3915 }
3916 }
3917
3918 return 0;
3919 }
3920
3921 /**
3922 * @param int $userid
3923 *
3924 * @return int
3925 */
3926 public function calc_approved_topics( $userid ) {
3927 if( $userid = wpforo_bigintval( $userid ) ) {
3928 $sqls = [];
3929 foreach( WPF()->get_active_boards_tables( 'topics' ) as $table ) {
3930 $sqls[] = WPF()->db->prepare(
3931 "SELECT COUNT(*)
3932 FROM `$table`
3933 WHERE `status` = 0
3934 AND `userid` = %d",
3935 $userid
3936 );
3937 }
3938
3939 if( $sqls ) {
3940 return (int) WPF()->db->get_var(
3941 "SELECT SUM(`COUNT(*)`) FROM (" . implode( ' UNION ALL ', $sqls ) . ") AS temp"
3942 );
3943 }
3944 }
3945
3946 return 0;
3947 }
3948
3949 /**
3950 * @param int $userid
3951 *
3952 * @return int
3953 */
3954 public function calc_approved_questions( $userid ) {
3955 if( $userid = wpforo_bigintval( $userid ) ) {
3956 $sqls = [];
3957 foreach( WPF()->get_active_boards_tables( 'topics' ) as $table ) {
3958 if( $forumids = Forums::get_forumids( str_replace( 'topics', 'forums', $table ), 3 ) ) {
3959 $sqls[] = WPF()->db->prepare(
3960 "SELECT COUNT(*)
3961 FROM `$table`
3962 WHERE `status` = 0
3963 AND `userid` = %d
3964 AND `forumid` IN( " . implode( ',', $forumids ) . " )",
3965 $userid
3966 );
3967 }
3968 }
3969
3970 if( $sqls ) {
3971 return (int) WPF()->db->get_var(
3972 "SELECT SUM(`COUNT(*)`) FROM (" . implode( ' UNION ALL ', $sqls ) . ") AS temp"
3973 );
3974 }
3975 }
3976
3977 return 0;
3978 }
3979
3980 /**
3981 * @param int $userid
3982 *
3983 * @return int
3984 */
3985 public function calc_approved_answers( $userid ) {
3986 if( $userid = wpforo_bigintval( $userid ) ) {
3987 $sqls = [];
3988 foreach( WPF()->get_active_boards_tables( 'posts' ) as $table ) {
3989 if( $forumids = Forums::get_forumids( str_replace( 'posts', 'forums', $table ), 3 ) ) {
3990 $sqls[] = WPF()->db->prepare(
3991 "SELECT COUNT(*)
3992 FROM `$table`
3993 WHERE `status` = 0
3994 AND `is_first_post` = 0
3995 AND `parentid` = 0
3996 AND `userid` = %d
3997 AND `forumid` IN( " . implode( ',', $forumids ) . " )",
3998 $userid
3999 );
4000 }
4001 }
4002
4003 if( $sqls ) {
4004 return (int) WPF()->db->get_var(
4005 "SELECT SUM(`COUNT(*)`) FROM (" . implode( ' UNION ALL ', $sqls ) . ") AS temp"
4006 );
4007 }
4008 }
4009
4010 return 0;
4011 }
4012
4013 /**
4014 * @param int $userid
4015 *
4016 * @return int
4017 */
4018 public function calc_approved_comments( $userid ) {
4019 if( $userid = wpforo_bigintval( $userid ) ) {
4020 $sqls = [];
4021 foreach( WPF()->get_active_boards_tables( 'posts' ) as $table ) {
4022 if( $forumids = Forums::get_forumids( str_replace( 'posts', 'forums', $table ), 3 ) ) {
4023 $sqls[] = WPF()->db->prepare(
4024 "SELECT COUNT(*)
4025 FROM `$table`
4026 WHERE `status` = 0
4027 AND `is_first_post` = 0
4028 AND `parentid` <> 0
4029 AND `userid` = %d
4030 AND `forumid` IN( " . implode( ',', $forumids ) . " )",
4031 $userid
4032 );
4033 }
4034 }
4035
4036 if( $sqls ) {
4037 return (int) WPF()->db->get_var(
4038 "SELECT SUM(`COUNT(*)`) FROM (" . implode( ' UNION ALL ', $sqls ) . ") AS temp"
4039 );
4040 }
4041 }
4042
4043 return 0;
4044 }
4045
4046 /**
4047 * @param int $userid
4048 *
4049 * @return array
4050 */
4051 public function calc_reactions_in( int $userid ): array {
4052 $reactions_in = array_map( '__return_zero', array_flip( Reactions::get_type_list() ) );
4053 if( $userid = wpforo_bigintval( $userid ) ) {
4054 $sqls = [];
4055 foreach( WPF()->get_active_boards_tables( 'reactions' ) as $table ) {
4056 $sqls[] = WPF()->db->prepare(
4057 "SELECT `type`
4058 FROM `$table`
4059 WHERE `post_userid` = %d",
4060 $userid
4061 );
4062 }
4063
4064 if( $sqls ) {
4065 $r = (array) WPF()->db->get_results(
4066 "SELECT `type`, COUNT(`type`) as `count` FROM (" . implode(
4067 ' UNION ALL ',
4068 $sqls
4069 ) . ") AS temp GROUP BY `type`",
4070 ARRAY_A
4071 );
4072 foreach( $r as $_r ) $reactions_in[ $_r['type'] ] = intval( $_r['count'] );
4073 }
4074 }
4075
4076 return $reactions_in;
4077 }
4078
4079 /**
4080 * @param int $userid
4081 *
4082 * @return array
4083 */
4084 public function calc_reactions_out( int $userid ): array {
4085 $reactions_out = array_map( '__return_zero', array_flip( Reactions::get_type_list() ) );
4086 if( $userid = wpforo_bigintval( $userid ) ) {
4087 $sqls = [];
4088 foreach( WPF()->get_active_boards_tables( 'reactions' ) as $table ) {
4089 $sqls[] = WPF()->db->prepare(
4090 "SELECT `type`
4091 FROM `$table`
4092 WHERE `userid` = %d",
4093 $userid
4094 );
4095 }
4096
4097 if( $sqls ) {
4098 $r = (array) WPF()->db->get_results(
4099 "SELECT `type`, COUNT(`type`) as `count` FROM (" . implode(
4100 ' UNION ALL ',
4101 $sqls
4102 ) . ") AS temp GROUP BY `type`",
4103 ARRAY_A
4104 );
4105 foreach( $r as $_r ) $reactions_out[ $_r['type'] ] = intval( $_r['count'] );
4106 }
4107 }
4108
4109 return $reactions_out;
4110 }
4111
4112 /**
4113 * @param int $userid
4114 *
4115 * @return void
4116 */
4117 public function rebuild_stats( int $userid ) {
4118 $posts = $this->calc_approved_posts( $userid );
4119 $topics = $this->calc_approved_topics( $userid );
4120 $questions = $this->calc_approved_questions( $userid );
4121 $answers = $this->calc_approved_answers( $userid );
4122 $comments = $this->calc_approved_comments( $userid );
4123 $reactions_in = $this->calc_reactions_in( $userid );
4124 $reactions_out = $this->calc_reactions_out( $userid );
4125 $points = $this->calc_points(
4126 compact( 'posts', 'topics', 'questions', 'answers', 'comments', 'reactions_in', 'reactions_out' )
4127 );
4128
4129 if( WPF()->db->update(
4130 WPF()->tables->profiles,
4131 [
4132 'posts' => $posts,
4133 'topics' => $topics,
4134 'questions' => $questions,
4135 'answers' => $answers,
4136 'comments' => $comments,
4137 'reactions_in' => wp_json_encode( $reactions_in ),
4138 'reactions_out' => wp_json_encode( $reactions_out ),
4139 'points' => $points,
4140 ],
4141 [ 'userid' => $userid ],
4142 [ '%d', '%d', '%d', '%d', '%d', '%s', '%s', '%d' ],
4143 '%d'
4144 ) ) {
4145 $this->reset( $userid );
4146 }
4147 }
4148
4149 public function after_add_topic( $topic ) {
4150 if( ! intval( $topic['status'] ) ) $this->rebuild_stats( $topic['userid'] );
4151 }
4152
4153 public function after_delete_topic( $topic ) {
4154 if( ! intval( $topic['status'] ) ) $this->rebuild_stats( $topic['userid'] );
4155 }
4156
4157 public function after_topic_status_update( $topic ) {
4158 $this->rebuild_stats( $topic['userid'] );
4159 }
4160
4161 public function before_merge_topic( $target, $current, $postids ) {
4162 $sql = "SELECT DISTINCT `userid` FROM `" . WPF()->tables->posts . "` WHERE `topicid` = %d";
4163 $sql = WPF()->db->prepare( $sql, $current['topicid'] );
4164 if( $postids ) $sql .= " AND `postid` IN(" . implode( ',', $postids ) . ")";
4165 $this->touched_userids = array_merge( $this->touched_userids, WPF()->db->get_col( $sql ) );
4166 }
4167
4168 public function after_merge_topic() {
4169 foreach( array_unique( array_filter( $this->touched_userids ) ) as $userid ) $this->rebuild_stats( $userid );
4170 }
4171
4172 public function after_move_topic( $topic ) {
4173 $sql = "SELECT DISTINCT `userid` FROM `" . WPF()->tables->posts . "` WHERE `topicid` = %d";
4174 $sql = WPF()->db->prepare( $sql, $topic['topicid'] );
4175 foreach( WPF()->db->get_col( $sql ) as $userid ) $this->rebuild_stats( $userid );
4176 }
4177
4178 public function after_add_post( $post ) {
4179 if( ! intval( $post['status'] ) ) $this->rebuild_stats( $post['userid'] );
4180 }
4181
4182 public function after_delete_post( $post ) {
4183 if( ! intval( $post['status'] ) ) $this->rebuild_stats( $post['userid'] );
4184 }
4185
4186 public function after_post_status_update( $post ) {
4187 $this->rebuild_stats( $post['userid'] );
4188 }
4189
4190 public function after_add_reaction( $reaction ) {
4191 $this->rebuild_stats( $reaction['userid'] );
4192 $this->rebuild_stats( $reaction['post_userid'] );
4193 }
4194
4195 public function after_edit_reaction( $fields, $where ) {
4196 $field_userid = wpfval( $fields, 'userid' );
4197 $field_post_userid = wpfval( $fields, 'post_userid' );
4198 if( $field_userid || $field_post_userid ) {
4199 $userids = [
4200 $field_userid,
4201 $field_post_userid,
4202 wpfval( $where, 'userid' ),
4203 wpfval( $where, 'post_userid' ),
4204 ];
4205 $userids = array_filter( $userids );
4206 $userids = array_unique( $userids );
4207 foreach( $userids as $userid ) $this->rebuild_stats( $userid );
4208 }
4209 }
4210
4211 public function before_delete_reaction( $args, $operator ) {
4212 $reactions = WPF()->reaction->get_reactions( $args, $operator );
4213 foreach( $reactions as $reaction ) {
4214 $this->touched_userids[] = $reaction['userid'];
4215 $this->touched_userids[] = $reaction['post_userid'];
4216 }
4217 }
4218
4219 public function after_delete_reaction() {
4220 foreach( array_unique( array_filter( $this->touched_userids ) ) as $userid ) $this->rebuild_stats( $userid );
4221 }
4222
4223 public function after_delete_user( $userid, $reassign ) {
4224 if( $reassign ) $this->rebuild_stats( $reassign );
4225 }
4226
4227 public function after_activate_user( $userid ) {
4228 $user = get_user_by( 'ID', $userid );
4229 if( $user ) {
4230 $sbj = wpforo_setting( 'email', 'after_user_approve_email_subject' );
4231 $msg = wpforo_setting( 'email', 'after_user_approve_email_message' );
4232 $blogname = get_option( 'blogname', '' );
4233 $login_url = wpforo_login_url( '' );
4234 $login_link = sprintf( '<a href="%1$s">%2$s</a>', $login_url, $blogname );
4235
4236 $sbj = str_replace( [ '[blogname]', '[user_login]', '[login_link]', '[login_url]' ],
4237 [ $blogname, $user->user_login, $login_link, $login_url ],
4238 $sbj );
4239 $msg = str_replace( [ '[blogname]', '[user_login]', '[login_link]', '[login_url]' ],
4240 [ $blogname, $user->user_login, $login_link, $login_url ],
4241 $msg );
4242 wpforo_send_email( $user->user_email, $sbj, $msg, '', 'user_approved', (int) $user->ID );
4243 }
4244 }
4245
4246 public function get_activity_url( $filter = '', $boardid = null, $arg = null ) {
4247 if( ! $arg ) $arg = WPF()->current_object['user'];
4248 $url = rtrim( $this->get_profile_url( $arg, 'activity' ), '/' );
4249 if( is_wpforo_multiboard() ) {
4250 if( is_null( $boardid ) ) {
4251 $boardid = wpfkey( WPF()->GET, 'boardid' ) ? (int) wpfval(
4252 WPF()->GET,
4253 'boardid'
4254 ) : WPF()->board->get_current( 'boardid' );
4255 }
4256 if( $filter ) {
4257 $url .= sprintf( '/?boardid=%1$d&filter=%2$s', $boardid, $filter );
4258 } else {
4259 $url .= sprintf( '/?boardid=%1$d', $boardid );
4260 }
4261 } elseif( $filter ) {
4262 $url .= sprintf( '/?filter=%1$s', $filter );
4263 }
4264
4265 return WPF()->user_trailingslashit( $url );
4266 }
4267
4268 public function get_favored_url( $filter = '', $boardid = null, $arg = null ) {
4269 if( ! $arg ) $arg = WPF()->current_object['user'];
4270 $url = rtrim( $this->get_profile_url( $arg, 'favored' ), '/' );
4271 if( is_wpforo_multiboard() ) {
4272 if( is_null( $boardid ) ) {
4273 $boardid = wpfkey( WPF()->GET, 'boardid' ) ? (int) wpfval(
4274 WPF()->GET,
4275 'boardid'
4276 ) : WPF()->board->get_current( 'boardid' );
4277 }
4278 if( $filter ) {
4279 $url .= sprintf( '/?boardid=%1$d&filter=%2$s', $boardid, $filter );
4280 } else {
4281 $url .= sprintf( '/?boardid=%1$d', $boardid );
4282 }
4283 } elseif( $filter ) {
4284 $url .= sprintf( '/?filter=%1$s', $filter );
4285 }
4286
4287 return WPF()->user_trailingslashit( $url );
4288 }
4289
4290 public function get_newest_member( $status_filter = true ) {
4291 if( ! ( $visible_usergroup_ids = WPF()->usergroup->get_visible_usergroup_ids() ) ) return [];
4292
4293 $groupids = implode( ", ", $visible_usergroup_ids );
4294
4295 if( $status_filter ) {
4296 $status = implode( "', '", $this->get_inlist_enabled_statuses() );
4297 $sql = "SELECT `userid` FROM `" . WPF()->tables->profiles . "`
4298 WHERE `status` IN ('" . $status . "')
4299 AND `groupid` IN (" . $groupids . ")
4300 ORDER BY `userid` DESC LIMIT 1";
4301 } else {
4302 $sql = "SELECT `userid` FROM `" . WPF()->tables->profiles . "`
4303 WHERE `groupid` IN (" . $groupids . ")
4304 ORDER BY `userid` DESC LIMIT 1";
4305 }
4306
4307 $memberid = WPF()->db->get_var( $sql );
4308 $member = wpforo_member( $memberid );
4309
4310 if( empty( $member ) ) {
4311
4312 WPF()->db->query(
4313 "DELETE p FROM `" . WPF()->tables->profiles . "` p LEFT JOIN `" . WPF()->db->users . "` u ON u.ID = p.userid WHERE u.ID IS NULL"
4314 );
4315
4316 $memberid = WPF()->db->get_var( $sql );
4317 $member = wpforo_member( $memberid );
4318
4319 if( empty( $member ) ) {
4320 if( $status_filter ) {
4321 $members = $this->get_members(
4322 [
4323 'orderby' => 'userid',
4324 'status' => [ 'active' ],
4325 'order' => 'DESC',
4326 'row_count' => 1,
4327 'groupids' => $visible_usergroup_ids,
4328 ]
4329 );
4330 } else {
4331 $members = $this->get_members(
4332 [
4333 'orderby' => 'userid',
4334 'order' => 'DESC',
4335 'row_count' => 1,
4336 'groupids' => $visible_usergroup_ids,
4337 ]
4338 );
4339 }
4340 if( isset( $members[0] ) && ! empty( $members[0] ) ) {
4341 $member = $members[0];
4342 } else {
4343 $member = [];
4344 }
4345 }
4346 }
4347 $member['profile_url'] = wpfval( $member, 'profile_url' ) ?: $this->get_profile_url( $memberid );
4348
4349 return $member;
4350 }
4351
4352 public function buffer_before_front_delete_user( $user ) {
4353 // Buffer email content before the user is deleted so shortcodes resolve to the real user, not guest
4354 if( wpfval( $user, 'userid' ) === WPF()->current_userid ) {
4355 if( wpforo_setting( 'authorization', 'send_email_after_user_delete' ) ) {
4356 $sbj = wpforo_setting( 'email', 'after_user_delete_notification_email_admin_subject' );
4357 $sbj = str_replace(
4358 [ '[datetime]', '[blogname]' ],
4359 [ _wpforo_date( 0, 'mysql', 'wp' ), '[' . get_bloginfo( 'name' ) . ']' ],
4360 $sbj
4361 );
4362 $sbj = wpforo_apply_email_shortcodes( $sbj, [], [], [], $user, '' );
4363
4364 $msg = wpforo_setting( 'email', 'after_user_delete_notification_email_admin_message' );
4365 $msg = str_replace(
4366 [ '[datetime]', '[blogname]' ],
4367 [ _wpforo_date( 0, 'mysql', 'wp' ), '[' . get_bloginfo( 'name' ) . ']' ],
4368 $msg
4369 );
4370 $msg = wpforo_apply_email_shortcodes( $msg, [], [], [], $user, '' );
4371 $msg = wpautop( $msg );
4372
4373 $this->front_delete_email_buffer = [
4374 'subject' => $sbj,
4375 'message' => $msg,
4376 ];
4377 }
4378 }
4379 }
4380
4381 public function after_front_delete_user( $user ) {
4382 if( wpfval( $user, 'userid' ) === WPF()->current_userid ) {
4383 if( wpforo_setting( 'authorization', 'send_email_after_user_delete' ) ) {
4384 // Prefer buffered email if available
4385 if( is_array( $this->front_delete_email_buffer ) ) {
4386 $sbj = (string) wpfval( $this->front_delete_email_buffer, 'subject' );
4387 $msg = (string) wpfval( $this->front_delete_email_buffer, 'message' );
4388 wpforo_send_email( wpforo_setting( 'email', 'admin_emails' ), $sbj, $msg, '', 'admin_notification' );
4389 $this->front_delete_email_buffer = null; // clear buffer
4390 }
4391 }
4392 }
4393 }
4394 }
4395