PluginProbe ʕ •ᴥ•ʔ
Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress / 4.17.1
Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress v4.17.1
4.17.1 4.17.0 4.16.19 4.16.18 4.16.17 4.16.16 trunk 1.0 1.0.1 1.0.2 1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.5a 1.1.6 1.1.7 1.2 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.3 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.4 1.4.1 1.4.2 1.5 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.7 1.7.1 1.7.2 1.8 1.8.1 1.8.10 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 1.8.9 1.9 1.9.1 1.9.10 1.9.11 1.9.12 1.9.13 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.1.9 2.2.10 2.2.11 2.2.12 2.2.13 2.2.14 2.2.15 2.2.16 2.2.2 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 3.0 3.1 3.1.1 3.1.10 3.1.11 3.1.12 3.1.13 3.1.14 3.1.15 3.1.16 3.1.17 3.1.18 3.1.19 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.10 3.2.11 3.2.12 3.2.13 3.2.14 3.2.15 3.2.16 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8 3.2.9 4.0.0 4.0.1 4.0.2 4.0.3 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.10.0 4.10.1 4.10.2 4.10.3 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.13.3 4.13.4 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.15.0 4.15.1 4.15.10 4.15.11 4.15.12 4.15.13 4.15.14 4.15.15 4.15.16 4.15.17 4.15.18 4.15.19 4.15.2 4.15.20 4.15.20.1 4.15.21 4.15.22 4.15.23 4.15.24 4.15.25 4.15.3 4.15.4 4.15.5 4.15.6 4.15.7 4.15.8 4.15.9 4.16.0 4.16.1 4.16.10 4.16.11 4.16.12 4.16.13 4.16.14 4.16.15 4.16.2 4.16.3 4.16.4 4.16.5 4.16.6 4.16.7 4.16.8 4.16.9 4.2.0 4.3.0 4.3.1 4.3.2 4.4.0 4.4.1 4.5.0 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.7.0 4.8.0 4.9.0
wp-user-avatar / src / Themes / DragDrop / MemberDirectoryTrait.php
wp-user-avatar / src / Themes / DragDrop Last commit date
EditProfile 5 years ago Login 3 years ago MemberDirectory 9 months ago PasswordReset 5 years ago Registration 3 years ago UserProfile 1 year ago AbstractBuildScratch.php 9 months ago AbstractMemberDirectoryTheme.php 5 months ago AbstractTheme.php 1 year ago FieldListing.php 9 months ago MemberDirectoryListing.php 1 day ago MemberDirectoryTrait.php 1 day ago ProfileFieldListing.php 4 months ago ThemeInterface.php 5 years ago ThemesRepository.php 1 year ago index.php 5 years ago
MemberDirectoryTrait.php
421 lines
1 <?php
2
3 namespace ProfilePress\Core\Themes\DragDrop;
4
5 use ProfilePress\Core\Classes\PROFILEPRESS_sql;
6 use ProfilePress\Core\Membership\CheckoutFields;
7 use WP_User;
8 use WP_User_Query;
9
10 trait MemberDirectoryTrait
11 {
12 protected int $_form_id;
13
14 protected array $args;
15
16 public function initializeMemberDirectoryTrait($_form_id, $args)
17 {
18 $this->_form_id = $_form_id;
19
20 $this->args = $args;
21 }
22
23 protected function search_query_key(): string
24 {
25 return 'search-' . $this->_form_id;
26 }
27
28 protected function search_filter_query_params()
29 {
30 static $cache = [];
31
32 if ( ! isset($cache[$this->_form_id])) {
33
34 $query_params = [];
35
36 if (empty($query_params) && ! empty($_GET['filter' . $this->_form_id])) {
37 $query_params = json_decode(base64_decode($_GET['filter' . $this->_form_id]), true);
38 }
39
40 $cache[$this->_form_id] = $query_params;
41 }
42
43 return $cache[$this->_form_id];
44 }
45
46 protected function wp_user_query()
47 {
48 static $cache = [];
49
50 if ( ! isset($cache[$this->_form_id])) {
51
52 $search_q_key = self::search_query_key();
53
54 $current_page = $this->get_current_page();
55
56 $users_per_page = $this->args['users_per_page'] ?: 9;
57
58 $roles = array_filter($this->args['user_roles'] ?? []);
59
60 $include_user_ids = $this->args['specific_users'] ?? '';
61
62 $exclude_user_ids = $this->args['exclude_users'] ?? '';
63
64 $default_sort_method = $this->args['sort_default'] ?? 'newest';
65
66 if (empty($include_user_ids)) {
67 $include_user_ids = [];
68 } else {
69 $include_user_ids = array_map(function ($val) {
70 return absint(trim($val));
71 }, explode(',', $include_user_ids));
72 }
73
74 if (empty($exclude_user_ids)) {
75 $exclude_user_ids = [];
76 } else {
77 $exclude_user_ids = array_map(function ($val) {
78 return absint(trim($val));
79 }, explode(',', $exclude_user_ids));
80 }
81
82 $search_field_harsh_map = [
83 'pp_email_address' => 'user_email',
84 'pp_website_url' => 'user_url',
85 'pp_display_name' => 'display_name'
86 ];
87
88 $db_search_fields = array_filter($this->args['search_fields'] ?? [], function ($item) {
89 return ! empty($item);
90 });
91
92 // get meta fields to be used by meta query of WP_User_Query
93 $meta_fields = array_filter($db_search_fields, function ($item) use ($search_field_harsh_map) {
94 return ! in_array($item, array_keys($search_field_harsh_map));
95 });
96
97 // get wp_user table columns to use in searching for users
98 $search_columns = array_reduce($db_search_fields, function ($carry, $item) use ($search_field_harsh_map) {
99
100 if (isset($search_field_harsh_map[$item])) {
101 $carry[] = $search_field_harsh_map[$item];
102 }
103
104 return $carry;
105
106 }, ['user_login']);
107
108 $sort_method = ppress_var($_GET, 'sortby' . $this->_form_id, $default_sort_method, true);
109
110 $query_params = $this->search_filter_query_params();
111
112 $offset = $current_page > 1 ? ($current_page - 1) * $users_per_page : 0;
113
114 $filter_meta_fields = isset($query_params['filters']) ? array_filter(
115 array_map('ppress_recursive_trim', ppress_var($query_params, 'filters', [], true))
116 ) : [];
117
118 if ( ! empty($filter_meta_fields['ppress_user_role'])) {
119 $roles = [sanitize_text_field($filter_meta_fields['ppress_user_role'])];
120 }
121
122 $wp_user_query = $this->member_directory_users([
123 'number' => $users_per_page,
124 'paged' => $current_page,
125 'offset' => $offset,
126 'roles' => $roles,
127 'include_user_ids' => $include_user_ids,
128 'exclude_user_ids' => $exclude_user_ids,
129 'sort_method' => $sort_method,
130 'search_columns' => $search_columns,
131 'search_meta_fields' => $meta_fields,
132 'filter_meta_fields' => $filter_meta_fields,
133 'is_search_query' => ! empty($query_params['ppmd-search']),
134 'search_q' => isset($query_params[$search_q_key]) ? sanitize_text_field($query_params[$search_q_key]) : ''
135 ]);
136
137 $users = $wp_user_query->get_results();
138
139 $total_users_found = $wp_user_query->get_total();
140
141 if (
142 apply_filters('ppress_member_directory_enable_user_role_array_filtering', false) &&
143 ! empty($roles) &&
144 ! empty($query_params['ppmd-search']) &&
145 is_array($users) &&
146 ! empty($users)
147 ) {
148
149 /**
150 * @var int $key
151 * @var WP_User $user
152 */
153 foreach ($users as $key => $user) {
154
155 if (empty(array_intersect($user->roles, $roles))) {
156 unset($users[$key]);
157 }
158 }
159
160 $total_users_found = count($users);
161
162 // only necessary because if filtering by role, LIMIT is removed from query.
163 // limit is removed in pre_user_query action callback.
164 $users = array_slice($users, $offset, $users_per_page);
165 }
166
167 $cache[$this->_form_id] = [
168 'users' => $users,
169 'total_users_found' => $total_users_found
170 ];
171 }
172
173 return $cache[$this->_form_id];
174 }
175
176 public function member_directory_users(array $parsed_args = []): WP_User_Query
177 {
178 $parsed_args = apply_filters('ppress_member_directory_parsed_args', wp_parse_args($parsed_args, [
179 'number' => 9,
180 'paged' => 1,
181 'offset' => 0,
182 'roles' => [],
183 'include_user_ids' => [],
184 'exclude_user_ids' => [],
185 'count_total' => true,
186 'sort_method' => 'newest',
187 'is_search_query' => false,
188 'search_q' => '',
189 'search_columns' => ['user_login', 'user_email', 'user_url', 'display_name'],
190 'search_meta_fields' => ['first_name', 'last_name'],
191 'filter_meta_fields' => []
192 ]));
193
194 $args = [
195 'number' => $parsed_args['number'],
196 'paged' => $parsed_args['paged'],
197 'offset' => $parsed_args['offset'],
198 'role__in' => $parsed_args['roles'],
199 'include' => $parsed_args['include_user_ids'],
200 'exclude' => $parsed_args['exclude_user_ids']
201 ];
202
203 // no check for username because it's the default orderby
204 switch ($parsed_args['sort_method']) {
205 case 'newest':
206 $args['orderby'] = 'user_registered';
207 $args['order'] = 'DESC';
208 break;
209 case 'oldest':
210 $args['orderby'] = 'user_registered';
211 break;
212 case 'username':
213 $args['orderby'] = 'user_login';
214 break;
215 case 'first-name':
216 $args['meta_key'] = 'first_name';
217 $args['orderby'] = 'meta_value';
218 $args['order'] = 'ASC';
219 break;
220 case 'last-name':
221 $args['meta_key'] = 'last_name';
222 $args['orderby'] = 'meta_value';
223 $args['order'] = 'ASC';
224 break;
225 default:
226 $args['meta_key'] = $parsed_args['sort_method'];
227 $args['orderby'] = 'meta_value';
228 $args['order'] = 'ASC';
229 }
230
231 if ($parsed_args['is_search_query'] === true) {
232
233 $search_term = sanitize_text_field($parsed_args['search_q']);
234
235 $search_columns = $parsed_args['search_columns'];
236
237 $roles = $parsed_args['roles'];
238
239 $filter_meta_fields = array_filter($parsed_args['filter_meta_fields'], function ($v, $k) {
240 return $k != 'ppress_user_role';
241 }, ARRAY_FILTER_USE_BOTH);
242
243 if ( ! empty($search_term)) {
244 $args['search'] = '*' . $search_term . '*';
245 }
246
247 // we need to empty out the search column so wp user query doesn't restrict the search only
248 // to supplied search columns. We want to also check usermeta too.
249 add_filter('user_search_columns', '__return_empty_array', 999999999);
250
251 add_action('pre_user_query', function ($query) use ($roles) {
252 // removes "AND ()" from query which causes the sql to be invalid.
253 // SELECT DISTINCT wp_users.* FROM wp_users INNER JOIN wp_usermeta ON
254 // ( wp_users.ID = wp_usermeta.user_id ) WHERE 1=1 AND ( wp_users.user_nicename LIKE '%little%' OR
255 // wp_users.user_email LIKE '%little%' OR ( ( wp_usermeta.meta_key = 'first_name' AND wp_usermeta.meta_value LIKE '%little%' )
256 // OR ( wp_usermeta.meta_key = 'last_name' AND wp_usermeta.meta_value LIKE '%little%' ) OR ( wp_usermeta.meta_key = 'twitter'
257 // AND wp_usermeta.meta_value LIKE '%little%' ) ) ) AND () ORDER BY user_registered DESC
258 $query->query_where = str_replace('AND ()', '', $query->query_where);
259
260 if ( apply_filters('ppress_member_directory_enable_user_role_array_filtering', false) && ! empty($roles)) {
261 // remove query LIMIT so we can get actual total number of result when filitering by roles
262 unset($query->query_limit);
263 }
264 });
265
266 /**
267 * Modifies the query so we can tactically include searching of $search_columns in wp_users table
268 * @see https://wordpress.stackexchange.com/a/248674/59917
269 */
270 add_filter('get_meta_sql', function ($sql) use ($search_term, $search_columns, $filter_meta_fields) {
271
272 global $wpdb;
273
274 // Only run once:
275 static $nr = 0;
276
277 if (0 != $nr++) return $sql;
278
279 $OR_placeholders = [];
280 $queries = [];
281
282
283 if (is_array($search_columns) && ! empty($search_columns)) {
284
285 foreach ($search_columns as $search_column) {
286
287 if ( ! empty($search_term)) {
288 $OR_placeholders[] = '%s';
289 $queries[] = $wpdb->prepare("{$wpdb->users}.$search_column LIKE %s", '%' . $wpdb->esc_like($search_term) . '%');
290 }
291 }
292 }
293
294 $OR_placeholders[] = '%s';
295
296 $queries[] = ppress_mb_function(
297 ['mb_substr', 'substr'],
298 [
299 $sql['where'],
300 5,
301 ppress_mb_function(['mb_strlen', 'strlen'], [$sql['where']])
302 ]
303 );
304
305 $filter_queries = '';
306
307 /** @see https://stackoverflow.com/a/65653006/2648410 */
308 if ( ! empty($filter_meta_fields)) {
309
310 foreach ($filter_meta_fields as $meta_key => $meta_value) {
311
312 $filter_queries .= "AND EXISTS ( SELECT 1 FROM {$wpdb->usermeta} WHERE {$wpdb->users}.ID = {$wpdb->usermeta}.user_id AND ";
313
314 $filter_queries .= '(';
315
316 if (is_array($meta_value)) {
317
318 $meta_value_count = count($meta_value);
319
320 foreach ($meta_value as $index2 => $value) {
321 $index2++;
322
323 $filter_queries .= $wpdb->prepare(
324 "({$wpdb->usermeta}.meta_key = %s AND {$wpdb->usermeta}.meta_value LIKE %s)",
325 $meta_key,
326 '%' . $wpdb->esc_like($value) . '%'
327 );
328
329 if ($index2 != $meta_value_count) {
330 $filter_queries .= ' OR ';
331 }
332 }
333
334 } else {
335
336 $filter_queries .= $wpdb->prepare(
337 "({$wpdb->usermeta}.meta_key = %s AND {$wpdb->usermeta}.meta_value = %s)",
338 $meta_key,
339 $meta_value
340 );
341 }
342
343 $filter_queries .= ')';
344
345 $filter_queries .= ')';
346 }
347 }
348
349 $queries[] = $filter_queries;
350
351 $where = vsprintf(
352 " AND ((" . implode(' OR ', $OR_placeholders) . " ) %s )",
353 $queries
354 );
355
356 $sql['where'] = $where;
357
358 return $sql;
359 });
360
361 if (is_array($parsed_args['search_meta_fields']) && ! empty($parsed_args['search_meta_fields']) && ! empty($search_term)) {
362
363 $args['meta_query'][0]['relation'] = 'OR';
364
365 foreach ($parsed_args['search_meta_fields'] as $search_meta_field) {
366
367 $clause = ['key' => $search_meta_field, 'value' => $search_term, 'compare' => 'LIKE'];
368
369 // country fields store the country code, so match the searched name against the codes it resolves to.
370 if ($search_meta_field == CheckoutFields::BILLING_COUNTRY || PROFILEPRESS_sql::get_field_type($search_meta_field) == 'country') {
371
372 $codes = array_keys(array_filter(ppress_array_of_world_countries(), function ($title) use ($search_term) {
373 return stripos($title, $search_term) !== false;
374 }));
375
376 if ( ! empty($codes)) $clause = ['key' => $search_meta_field, 'value' => $codes, 'compare' => 'IN'];
377 }
378
379 $args['meta_query'][0][] = $clause;
380 }
381 }
382 }
383
384 return new WP_User_Query(apply_filters('ppress_member_directory_wp_user_args', $args, $this->_form_id, $this));
385 }
386
387 protected function get_current_page(): int
388 {
389 return absint(max(1, ppressGET_var('mdpage' . $this->_form_id, 1, true)));
390 }
391
392 protected function display_pagination(
393 $total_users_found,
394 $users_per_page,
395 $prev_text = '<span class="ppress-material-icons">keyboard_arrow_left</span>',
396 $next_text = '<span class="ppress-material-icons">keyboard_arrow_right</span>'
397 )
398 {
399 $current_page = $this->get_current_page();
400
401 $total_pages = ceil($total_users_found / ($users_per_page ?: 9));
402
403 if ($total_pages > 1) {
404
405 echo '<div class="ppmd-pagination-wrap">';
406
407 // from https://wordpress.stackexchange.com/questions/275527/paginate-links-ignore-my-format
408 echo paginate_links([
409 //'base' => '%_%', somehow with this enabled, pagination breaks where page 1 link becomes the current page url
410 'total' => $total_pages,
411 'current' => $current_page,
412 'format' => '?mdpage' . $this->_form_id . '=%#%',
413 'prev_text' => $prev_text,
414 'next_text' => $next_text
415 ]);
416
417 echo '</div>';
418 }
419 }
420 }
421