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

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

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