PluginProbe
wpForo Forum / 3.1.4
wpForo Forum v3.1.4
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.4, at classes/Members.php

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