PluginProbe ʕ •ᴥ•ʔ
Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress / 3.1
Paid Membership Plugin, Ecommerce, User Registration Form, Login Form, User Profile & Restrict Content – ProfilePress v3.1
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 / AbstractMemberDirectoryTheme.php
wp-user-avatar / src / Themes / DragDrop Last commit date
EditProfile 5 years ago Login 5 years ago MemberDirectory 5 years ago PasswordReset 5 years ago Registration 5 years ago UserProfile 5 years ago AbstractBuildScratch.php 5 years ago AbstractMemberDirectoryTheme.php 5 years ago AbstractTheme.php 5 years ago FieldListing.php 5 years ago MemberDirectoryListing.php 5 years ago ProfileFieldListing.php 5 years ago ThemeInterface.php 5 years ago ThemesRepository.php 5 years ago index.php 5 years ago
AbstractMemberDirectoryTheme.php
987 lines
1 <?php
2
3 namespace ProfilePress\Core\Themes\DragDrop;
4
5 use ProfilePress\Core\Classes\ExtensionManager as EM;
6 use ProfilePress\Core\Classes\PROFILEPRESS_sql;
7 use ProfilePress\Core\ShortcodeParser\Builder\FieldsShortcodeCallback;
8 use WP_User_Query;
9
10 abstract class AbstractMemberDirectoryTheme extends AbstractTheme
11 {
12 public function __construct($form_id, $form_type)
13 {
14 parent::__construct($form_id, $form_type);
15
16 add_action('ppress_drag_drop_builder_admin_page', [$this, 'js_script']);
17 }
18
19 abstract function form_wrapper_class();
20
21 abstract function directory_structure();
22
23 public function form_structure()
24 {
25 $wp_user_query = $this->wp_user_query();
26
27 $total_users_found = $wp_user_query['total_users_found'];
28
29 $query_params = $this->search_filter_query_params();
30
31 ob_start();
32
33 printf('[pp-form-wrapper class="%s"]', $this->form_wrapper_class());
34
35 $this->search_filter_sort_structure();
36
37 if ( ! $this->is_result_after_search_enabled() || (isset($query_params['ppmd-search']) && $query_params['ppmd-search'] == $this->form_id)) {
38
39 if (0 === $total_users_found) { ?>
40
41 <div class="ppressmd-members-total-wrap">
42 <div class="ppressmd-members-total">
43 <?= $this->get_no_result_text() ?>
44 </div>
45 </div>
46
47 <?php
48
49 } else {
50
51 if ( ! empty($query_params[$this->search_query_key()])) { ?>
52
53 <div class="ppressmd-members-total-wrap">
54 <div class="ppressmd-members-total">
55
56 <?= str_replace(
57 '{total_users}', $total_users_found,
58 $total_users_found > 1 ? $this->get_results_text() : $this->get_single_result_text()
59 ) ?>
60 </div>
61 </div>
62
63 <?php
64 }
65 }
66
67 if ($total_users_found > 0) {
68
69 $this->directory_structure();
70
71 $this->display_pagination($total_users_found);
72 }
73 }
74
75 echo '[/pp-form-wrapper]';
76
77 return ob_get_clean();
78 }
79
80 public function js_script()
81 {
82 ?>
83 <script type="text/javascript">
84 (function ($) {
85
86 var run = function () {
87 $('#ppress_md_enable_custom_sort').change(function () {
88 $('.ppress_md_sort_method_fields_wrap').toggle(this.checked);
89 }).trigger('change');
90
91 $('#ppress_md_enable_search').change(function () {
92 $('.ppress_md_search_fields_wrap').toggle(this.checked);
93 $('.ppress_md_enable_filters_wrap').toggle(this.checked);
94 $('.ppress_md_filter_fields_wrap').toggle(this.checked);
95 }).trigger('change');
96 };
97
98 $(run);
99
100 })(jQuery);
101 </script>
102 <?php
103 }
104
105 public function default_metabox_settings()
106 {
107 $data = parent::default_metabox_settings();
108 $data['ppress_md_user_roles'] = [];
109 $data['ppress_md_specific_users'] = '';
110 $data['ppress_md_exclude_users'] = '';
111
112 $data['ppress_md_sort_default'] = 'newest';
113 $data['ppress_md_enable_custom_sort'] = 'false';
114 $data['ppress_md_sort_method_fields'] = [];
115
116 $data['ppress_md_enable_search'] = 'true';
117 $data['ppress_md_search_fields'] = ['pp_email_address', 'pp_website_url', 'pp_display_name', 'first_name', 'last_name'];
118 $data['ppress_md_enable_filters'] = 'false';
119 $data['ppress_md_filter_fields'] = [];
120
121 $data['ppress_md_enable_result_after_search'] = 'false';
122 $data['ppress_md_result_number_per_page'] = '9';
123 $data['ppress_md_results_text'] = sprintf(esc_html__('%s Members', 'wp-user-avatar'), '{total_users}');
124 $data['ppress_md_single_result_text'] = sprintf(esc_html__('%s Member', 'wp-user-avatar'), '{total_users}');
125 $data['ppress_md_no_result_text'] = esc_html__('We could not find any user that matches your search criteria', 'wp-user-avatar');
126
127 $data['ppress_md_search_filter_field_text_color'] = '#666666';
128 $data['ppress_md_search_filter_field_border_color'] = '#dddddd';
129
130 $data['ppress_md_pagination_link_color'] = '#666666';
131 $data['ppress_md_pagination_active_link_color'] = '#ffffff';
132 $data['ppress_md_pagination_active_background_color'] = '#007bff';
133
134 return $data;
135 }
136
137 public function appearance_settings($settings)
138 {
139 $settings[] = [
140 'id' => 'ppress_md_user_roles',
141 'type' => 'select2',
142 'label' => esc_html__('User Roles to Display', 'wp-user-avatar'),
143 'description' => esc_html__('If you do not want to show all members, select the user roles to appear in this directory.', 'wp-user-avatar'),
144 'options' => ppress_wp_roles_key_value(false),
145 'priority' => 5
146 ];
147
148 $settings[] = [
149 'id' => 'ppress_md_specific_users',
150 'type' => 'textarea',
151 'placeholder' => esc_html__('Example: 1, 6, 32', 'wp-user-avatar'),
152 'label' => esc_html__('Comma Separated List of Users ID to Only Show', 'wp-user-avatar'),
153 'priority' => 10
154 ];
155
156 $settings[] = [
157 'id' => 'ppress_md_exclude_users',
158 'type' => 'textarea',
159 'placeholder' => esc_html__('Example: 1, 6, 32', 'wp-user-avatar'),
160 'label' => esc_html__('Comma Separated List of Users ID to Exclude', 'wp-user-avatar'),
161 'priority' => 10
162 ];
163
164 return $settings;
165 }
166
167 public function color_settings($settings)
168 {
169 $settings2 = [
170 [
171 'id' => 'ppress_md_search_filter_field_text_color',
172 'type' => 'color',
173 'label' => esc_html__('Search & Filter Fields Text', 'wp-user-avatar')
174 ],
175 [
176 'id' => 'ppress_md_search_filter_field_border_color',
177 'type' => 'color',
178 'label' => esc_html__('Search & Filter Fields Border', 'wp-user-avatar')
179 ],
180 [
181 'id' => 'ppress_md_pagination_link_color',
182 'type' => 'color',
183 'label' => esc_html__('Pagination Links', 'wp-user-avatar')
184 ],
185 [
186 'id' => 'ppress_md_pagination_active_link_color',
187 'type' => 'color',
188 'label' => esc_html__('Pagination Active Link Color', 'wp-user-avatar')
189 ],
190 [
191 'id' => 'ppress_md_pagination_active_background_color',
192 'type' => 'color',
193 'label' => esc_html__('Pagination Active Link Background', 'wp-user-avatar')
194 ]
195 ];
196
197 return array_merge($settings, $settings2);
198 }
199
200 public function metabox_settings($settings, $form_type, $DragDropBuilderInstance)
201 {
202 $sorting_options = [esc_html__('Standard Fields', 'wp-user-avatar') => $this->md_standard_sort_fields()];
203
204 $search_options = [
205 esc_html__('Standard', 'wp-user-avatar') => [
206 'pp_email_address' => esc_html__('Email Address', 'wp-user-avatar'),
207 'pp_website_url' => esc_html__('Website', 'wp-user-avatar'),
208 'pp_display_name' => esc_html__('Display Name', 'wp-user-avatar'),
209 'first_name' => esc_html__('First Name', 'wp-user-avatar'),
210 'last_name' => esc_html__('Last Name', 'wp-user-avatar'),
211 'description' => esc_html__('Biography', 'wp-user-avatar')
212 ]
213 ];
214
215 if (EM::is_enabled(EM::CUSTOM_FIELDS)) {
216 $cf_label = esc_html__('Custom Fields', 'wp-user-avatar');
217 $sorting_options[$cf_label] = ppress_custom_fields_key_value_pair(true);
218 $search_options[$cf_label] = ppress_custom_fields_key_value_pair(true);
219 }
220
221 $new_settings['ppress_md_sorting'] = [
222 'tab_title' => esc_html__('Sorting', 'wp-user-avatar'),
223 [
224 'id' => 'ppress_md_sort_default',
225 'label' => esc_html__('Default Sorting method', 'wp-user-avatar'),
226 'type' => 'select',
227 'options' => $sorting_options
228 ],
229 [
230 'id' => 'ppress_md_enable_custom_sort',
231 'label' => esc_html__('Enable custom sorting', 'wp-user-avatar'),
232 'type' => 'checkbox',
233 ],
234 [
235 'id' => 'ppress_md_sort_method_fields',
236 'label' => esc_html__('Sorting Method Fields', 'wp-user-avatar'),
237 'type' => 'select2',
238 'options' => $sorting_options,
239 'description' => esc_html__('Fields to show in sorting dropdown menu', 'wp-user-avatar')
240 ],
241 ];
242
243 $new_settings['ppress_md_search'] = [
244 'tab_title' => esc_html__('Search', 'wp-user-avatar'),
245 [
246 'id' => 'ppress_md_enable_search',
247 'label' => esc_html__('Display Search Form', 'wp-user-avatar'),
248 'type' => 'checkbox'
249 ],
250 [
251 'id' => 'ppress_md_search_fields',
252 'label' => esc_html__('Search Fields', 'wp-user-avatar'),
253 'type' => 'select2',
254 'options' => $search_options,
255 'description' => esc_html__('Select fields to search in.', 'wp-user-avatar')
256 ]
257 ];
258
259
260 if (EM::is_enabled(EM::CUSTOM_FIELDS)) {
261
262 $new_settings['ppress_md_search'][] = [
263 'id' => 'ppress_md_enable_filters',
264 'label' => esc_html__('Enable Filters', 'wp-user-avatar'),
265 'type' => 'checkbox',
266 'description' => esc_html__('If enabled, users will be able to filter members in this directory', 'wp-user-avatar')
267 ];
268
269 $new_settings['ppress_md_search'][] = [
270 'id' => 'ppress_md_filter_fields',
271 'label' => esc_html__('Filter Fields', 'wp-user-avatar'),
272 'type' => 'select2',
273 'options' => array_reduce(PROFILEPRESS_sql::get_profile_custom_fields_by_types(['select', 'checkbox', 'radio', 'country', 'date']), function ($carry, $item) {
274 $carry[$item->field_key] = ppress_woocommerce_field_transform($item->field_key, $item->label_name);
275
276 return $carry;
277 }, []),
278 'description' => esc_html__('Select custom fields that members can be filtered by. Only Select, Checkbox, Radio, Country and Date/Time fields are supported.', 'wp-user-avatar')
279 ];
280 }
281
282 if ( ! EM::is_enabled(EM::CUSTOM_FIELDS)) {
283 $upgrade_url = 'https://profilepress.net/pricing/?utm_source=wp_dashboard&utm_medium=upgrade&utm_campaign=md_custom_field_upsell';
284 $new_settings['ppress_md_search'][] = [
285 'id' => 'ppress_md_search_filter_upsell',
286 'label' => '',
287 'type' => 'custom',
288 'content' => sprintf(
289 esc_html__('%sUpgrade to ProfilePress premium%s if you don\'t have the custom field addon so you can enable search and filtering by custom fields.', 'wp-user-avatar'),
290 '<a href="' . $upgrade_url . '" target="_blank">', '</a>'
291 ),
292 ];
293 }
294
295 $new_settings['ppress_md_result_pagination'] = [
296 'tab_title' => esc_html__('Result & Pagination', 'wp-user-avatar'),
297 [
298 'id' => 'ppress_md_enable_result_after_search',
299 'label' => esc_html__('Show Results Only After a Search', 'wp-user-avatar'),
300 'type' => 'checkbox',
301 'description' => esc_html__('Enable to only show members after a search is performed', 'wp-user-avatar')
302 ],
303 [
304 'id' => 'ppress_md_result_number_per_page',
305 'label' => esc_html__('Number of Members per Page', 'wp-user-avatar'),
306 'type' => 'number'
307 ],
308 [
309 'id' => 'ppress_md_results_text',
310 'label' => esc_html__('Results Text', 'wp-user-avatar'),
311 'type' => 'text'
312 ],
313 [
314 'id' => 'ppress_md_single_result_text',
315 'label' => esc_html__('Single Result Text', 'wp-user-avatar'),
316 'type' => 'text'
317 ],
318 [
319 'id' => 'ppress_md_no_result_text',
320 'label' => esc_html__('No Result Text', 'wp-user-avatar'),
321 'type' => 'text'
322 ],
323 ];
324
325 return $new_settings;
326 }
327
328 protected function search_query_key()
329 {
330 return 'search-' . $this->form_id;
331 }
332
333 protected function get_current_page()
334 {
335 return absint(max(1, ppressGET_var('mdpage' . $this->form_id, 1, true)));
336 }
337
338 protected function wp_user_query()
339 {
340 static $cache = [];
341
342 if ( ! isset($cache[$this->form_id])) {
343
344 $search_q_key = self::search_query_key();
345
346 $current_page = $this->get_current_page();
347
348 $users_per_page = $this->get_default_result_number_per_page();
349
350 $roles = array_filter($this->get_meta('ppress_md_user_roles'));
351
352 $include_user_ids = $this->get_meta('ppress_md_specific_users');
353
354 $exclude_user_ids = $this->get_meta('ppress_md_exclude_users');
355
356 $default_sort_method = $this->get_meta('ppress_md_sort_default');
357
358 if (empty($include_user_ids)) {
359 $include_user_ids = [];
360 } else {
361 $include_user_ids = array_map(function ($val) {
362 return absint(trim($val));
363 }, explode(',', $include_user_ids));
364 }
365
366 if (empty($exclude_user_ids)) {
367 $exclude_user_ids = [];
368 } else {
369 $exclude_user_ids = array_map(function ($val) {
370 return absint(trim($val));
371 }, explode(',', $exclude_user_ids));
372 }
373
374 $search_field_harsh_map = [
375 'pp_email_address' => 'user_email',
376 'pp_website_url' => 'user_url',
377 'pp_display_name' => 'display_name'
378 ];
379
380 $db_search_fields = array_filter($this->get_meta('ppress_md_search_fields'), function ($item) {
381 return ! empty($item);
382 });
383
384 // get meta fields to be used by meta query of WP_User_Query
385 $meta_fields = array_filter($db_search_fields, function ($item) use ($search_field_harsh_map) {
386 return ! in_array($item, array_keys($search_field_harsh_map));
387 });
388
389 // get wp_user table columns to use in searching for users
390 $search_columns = array_reduce($db_search_fields, function ($carry, $item) use ($search_field_harsh_map) {
391
392 if (isset($search_field_harsh_map[$item])) {
393 $carry[] = $search_field_harsh_map[$item];
394 }
395
396 return $carry;
397
398 }, ['user_login']);
399
400 $sort_method = ppress_var($_GET, 'sortby' . $this->form_id, $default_sort_method, true);
401
402 $query_params = $this->search_filter_query_params();
403
404 $offset = $current_page > 1 ? ($current_page - 1) * $users_per_page : 0;
405
406 $wp_user_query = $this->member_directory_users([
407 'number' => $users_per_page,
408 'paged' => $current_page,
409 'offset' => $offset,
410 'roles' => $roles,
411 'include_user_ids' => $include_user_ids,
412 'exclude_user_ids' => $exclude_user_ids,
413 'sort_method' => $sort_method,
414 'search_columns' => $search_columns,
415 'search_meta_fields' => $meta_fields,
416 'filter_meta_fields' => isset($query_params['filters']) ? array_filter(
417 array_map('ppress_recursive_trim', ppress_var($query_params, 'filters', [], true))
418 ) : [],
419 'is_search_query' => ! empty($query_params['ppmd-search']),
420 'search_q' => isset($query_params[$search_q_key]) ? sanitize_text_field($query_params[$search_q_key]) : ''
421 ]);
422
423 $users = (array)$wp_user_query->get_results();
424
425 $total_users_found = $wp_user_query->get_total();
426
427 $cache[$this->form_id] = [
428 'users' => $users,
429 'total_users_found' => $total_users_found
430 ];
431 }
432
433 return $cache[$this->form_id];
434 }
435
436 protected function search_filter_query_params()
437 {
438
439 static $cache = [];
440
441 if ( ! isset($cache[$this->form_id])) {
442
443 $query_params = [];
444
445 if (empty($query_params) && ! empty($_GET['filter' . $this->form_id])) {
446 $query_params = json_decode(base64_decode($_GET['filter' . $this->form_id]), true);
447 }
448
449 $cache[$this->form_id] = $query_params;
450 }
451
452 return $cache[$this->form_id];
453 }
454
455 public function md_standard_sort_fields()
456 {
457 return [
458 'newest' => esc_html__('Newest Users First', 'wp-user-avatar'),
459 'oldest' => esc_html__('Oldest Users First', 'wp-user-avatar'),
460 'display-name' => esc_html__('Display Name', 'wp-user-avatar'),
461 'first-name' => esc_html__('First Name', 'wp-user-avatar'),
462 'last-name' => esc_html__('Last Name', 'wp-user-avatar'),
463 'username' => esc_html__('Username', 'wp-user-avatar')
464 ];
465 }
466
467 public function get_sort_field_label($field)
468 {
469 $pp_custom_fields = ppress_custom_fields_key_value_pair(true);
470
471 return ppress_var($this->md_standard_sort_fields(), $field, ppress_var($pp_custom_fields, $field));
472 }
473
474 /**
475 * @param array $parsed_args
476 *
477 * @return WP_User_Query
478 */
479 public function member_directory_users($parsed_args = [])
480 {
481 $parsed_args = apply_filters('ppress_member_directory_parsed_args', wp_parse_args($parsed_args, [
482 'number' => 9,
483 'paged' => 1,
484 'offset' => 0,
485 'roles' => [],
486 'include_user_ids' => [],
487 'exclude_user_ids' => [],
488 'count_total' => true,
489 'sort_method' => 'newest',
490 'is_search_query' => false,
491 'search_q' => '',
492 'search_columns' => ['user_login', 'user_email', 'user_url', 'display_name'],
493 'search_meta_fields' => ['first_name', 'last_name'],
494 'filter_meta_fields' => []
495 ]));
496
497 $args = [
498 'number' => $parsed_args['number'],
499 'paged' => $parsed_args['paged'],
500 'offset' => $parsed_args['offset'],
501 'role__in' => $parsed_args['roles'],
502 'include' => $parsed_args['include_user_ids'],
503 'exclude' => $parsed_args['exclude_user_ids']
504 ];
505
506 // no check for username because it's the default orderby
507 switch ($parsed_args['sort_method']) {
508 case 'newest':
509 $args['orderby'] = 'user_registered';
510 $args['order'] = 'DESC';
511 break;
512 case 'oldest':
513 $args['orderby'] = 'user_registered';
514 break;
515 case 'username':
516 $args['orderby'] = 'user_login';
517 break;
518 case 'display-name':
519 $args['meta_key'] = 'display_name';
520 $args['orderby'] = 'meta_value';
521 $args['order'] = 'ASC';
522 break;
523 case 'first-name':
524 $args['meta_key'] = 'first_name';
525 $args['orderby'] = 'meta_value';
526 $args['order'] = 'ASC';
527 break;
528 case 'last-name':
529 $args['meta_key'] = 'last_name';
530 $args['orderby'] = 'meta_value';
531 $args['order'] = 'ASC';
532 break;
533 }
534
535 if ($parsed_args['is_search_query'] === true) {
536
537 $search_term = sanitize_text_field($parsed_args['search_q']);
538
539 $search_columns = $parsed_args['search_columns'];
540
541 $filter_meta_fields = $parsed_args['filter_meta_fields'];
542
543 $args['search'] = '*' . $search_term . '*';
544
545 // we need to empty out the search column so wp user query doesn't restrict the search only
546 // to supplied search columns. We want to also check usermeta too.
547 add_filter('user_search_columns', '__return_empty_array', 999999999);
548
549 add_action('pre_user_query', function ($query) {
550 // removes "AND ()" from query which causes the sql to be invalid.
551 // SELECT DISTINCT wp_users.* FROM wp_users INNER JOIN wp_usermeta ON
552 // ( wp_users.ID = wp_usermeta.user_id ) WHERE 1=1 AND ( wp_users.user_nicename LIKE '%little%' OR
553 // wp_users.user_email LIKE '%little%' OR ( ( wp_usermeta.meta_key = 'first_name' AND wp_usermeta.meta_value LIKE '%little%' )
554 // OR ( wp_usermeta.meta_key = 'last_name' AND wp_usermeta.meta_value LIKE '%little%' ) OR ( wp_usermeta.meta_key = 'twitter'
555 // AND wp_usermeta.meta_value LIKE '%little%' ) ) ) AND () ORDER BY user_registered DESC
556 $query->query_where = str_replace('AND ()', '', $query->query_where);
557 });
558
559 /**
560 * Modifies the query so we can tactically include searching of $search_columns in wp_users table
561 * @see https://wordpress.stackexchange.com/a/248674/59917
562 */
563 add_filter('get_meta_sql', function ($sql) use ($search_term, $search_columns, $filter_meta_fields) {
564
565 global $wpdb;
566
567 // Only run once:
568 static $nr = 0;
569
570 if (0 != $nr++) return $sql;
571
572 $OR_placeholders = [];
573 $queries = [];
574
575
576 if (is_array($search_columns) && ! empty($search_columns)) {
577
578 foreach ($search_columns as $search_column) {
579
580 $OR_placeholders[] = '%s';
581
582 $queries[] = $wpdb->prepare("{$wpdb->users}.$search_column LIKE %s", '%' . $wpdb->esc_like($search_term) . '%');
583 }
584 }
585
586 $OR_placeholders[] = '%s';
587
588 $queries[] = ppress_mb_function(
589 ['mb_substr', 'substr'],
590 [
591 $sql['where'],
592 5,
593 ppress_mb_function(['mb_strlen', 'strlen'], [$sql['where']])
594 ]
595 );
596
597
598 $filter_queries = '';
599
600 /** @see https://stackoverflow.com/a/65653006/2648410 */
601 if ( ! empty($filter_meta_fields)) {
602
603 foreach ($filter_meta_fields as $meta_key => $meta_value) {
604
605 $filter_queries .= "AND EXISTS ( SELECT 1 FROM {$wpdb->usermeta} WHERE {$wpdb->users}.ID = {$wpdb->usermeta}.user_id AND ";
606
607 $filter_queries .= '(';
608
609 if (is_array($meta_value)) {
610
611 $meta_value_count = count($meta_value);
612
613 foreach ($meta_value as $index2 => $value) {
614 $index2++;
615
616 $filter_queries .= $wpdb->prepare(
617 "({$wpdb->usermeta}.meta_key = '$meta_key' AND {$wpdb->usermeta}.meta_value LIKE %s)",
618 '%' . $wpdb->esc_like($value) . '%'
619 );
620
621 if ($index2 != $meta_value_count) {
622 $filter_queries .= ' OR ';
623 }
624 }
625
626 } else {
627
628 $filter_queries .= $wpdb->prepare(
629 "({$wpdb->usermeta}.meta_key = '$meta_key' AND {$wpdb->usermeta}.meta_value = %s)",
630 $meta_value
631 );
632 }
633
634 $filter_queries .= ')';
635
636 $filter_queries .= ')';
637 }
638 }
639
640 $queries[] = $filter_queries;
641
642 $where = vsprintf(
643 " AND ((" . implode(' OR ', $OR_placeholders) . " ) %s )",
644 $queries
645 );
646
647 $sql['where'] = $where;
648
649 return $sql;
650 });
651
652 if (is_array($parsed_args['search_meta_fields']) && ! empty($parsed_args['search_meta_fields'])) {
653
654 $args['meta_query'][0]['relation'] = 'OR';
655
656 foreach ($parsed_args['search_meta_fields'] as $search_meta_field) {
657
658 $args['meta_query'][0][] = [
659 'key' => $search_meta_field,
660 'value' => $search_term,
661 'compare' => 'LIKE'
662 ];
663 }
664 }
665 }
666
667 return new WP_User_Query(apply_filters('ppress_member_directory_wp_user_args', $args, $this->form_id, $this));
668 }
669
670 protected function sort_method_dropdown_menu()
671 {
672 $sortby_query_key = 'sortby' . $this->form_id;
673
674 $custom_sort_enabled = $this->get_meta('ppress_md_enable_custom_sort') == 'true';
675
676 $default_sort_field = ppress_var($_GET, $sortby_query_key, $this->get_meta('ppress_md_sort_default'), true);
677
678 $custom_sort_fields = array_filter($this->get_meta('ppress_md_sort_method_fields'), function ($item) use ($default_sort_field) {
679 return ! empty($item) && $item != $default_sort_field;
680 });
681
682 if ( ! $custom_sort_enabled || empty($custom_sort_fields)) return;
683
684 ?>
685 <div class="ppressmd-member-directory-sorting">
686 <span><?= esc_html__('Sort by', 'wp-user-avatar') ?>:&nbsp;</span>
687 <div class="ppressmd-member-directory-sorting-a">
688 <a href=" <?= esc_url(add_query_arg([$sortby_query_key => $default_sort_field])) ?>" class="ppressmd-member-directory-sorting-a-text">
689 <?= $this->get_sort_field_label($default_sort_field) ?>
690 <span class="ppress-material-icons">keyboard_arrow_down</span>
691 </a>
692
693 <div class="ppressmd-new-dropdown">
694 <ul>
695 <?php foreach ($custom_sort_fields as $field) : ?>
696 <li>
697 <a href="<?= esc_url(add_query_arg([$sortby_query_key => $field])) ?>">
698 <?= $this->get_sort_field_label($field) ?>
699 </a>
700 </li>
701 <?php endforeach; ?>
702 </ul>
703 </div>
704 </div>
705 </div>
706 <?php
707 }
708
709 protected function filter_structure($show_filter_fields = false)
710 {
711 $filter_enabled = $this->get_meta('ppress_md_enable_filters') == 'true';
712
713 $filter_fields = array_filter($this->get_meta('ppress_md_filter_fields'), function ($item) {
714 return ! empty($item);
715 });
716
717 if ( ! $filter_enabled || empty($filter_fields)) return;
718
719 if ( ! $show_filter_fields) : ?>
720 <span class="ppressmd-member-directory-filters">
721 <span class="ppressmd-member-directory-filters-a">
722 <a href="#">
723 <?= esc_html__('More Filters', 'wp-user-avatar') ?>
724 <span class="ppress-material-icons ppress-up">keyboard_arrow_up</span>
725 <span class="ppress-material-icons ppress-down">keyboard_arrow_down</span>
726 </a>
727 </span>
728 </span>
729 <?php endif;
730
731 if ($show_filter_fields) : $query_params = $this->search_filter_query_params(); ?>
732
733 <div class="ppressmd-member-directory-header-row ppressmd-member-directory-filters-bar">
734
735 <div class="ppressmd-search ppressmd-search-invisible">
736
737 <?php foreach ($filter_fields as $field_key) : ?>
738
739 <?php $custom_field = PROFILEPRESS_sql::get_profile_custom_field_by_key($field_key); ?>
740
741 <div class="ppressmd-search-filter ppressmd-text-filter-type">
742
743 <?php
744
745 switch ($custom_field['type']) {
746 case 'select' :
747 case 'checkbox' :
748 case 'radio' :
749 case 'country' :
750
751 if ( ! empty($custom_field['options'])) {
752
753 $is_multiple = false;
754
755 if ($custom_field['type'] == 'select') {
756 $is_multiple = ppress_is_select_field_multi_selectable($field_key);
757
758 }
759
760 if ($custom_field['type'] == 'checkbox') $is_multiple = true;
761
762 printf(
763 '<select name="%s" data-placeholder="%s" class="ppressmd-form-field ppmd-select2"%s>',
764 $is_multiple ? 'filters[' . $field_key . '][]' : 'filters[' . $field_key . ']',
765 $custom_field['label_name'],
766 $is_multiple ? ' multiple' : ' data-allow-clear="true"'
767 );
768
769 $options = array_map('trim', explode(',', $custom_field['options']));
770
771 if ( ! $is_multiple) {
772 echo '<option></option>';
773 }
774
775 foreach ($options as $option) {
776 $bucket = ppress_var(ppress_var($query_params, 'filters', []), $field_key);
777 printf(
778 '<option value="%1$s" %2$s>%1$s</option>',
779 $option,
780 ! $is_multiple ? selected($option, $bucket, false) : (is_array($bucket) && in_array($option, $bucket) ? 'selected=selected' : '')
781 );
782 }
783
784 echo '</select>';
785 }
786 break;
787
788 case 'date' :
789
790 $dateFormat = ! empty($custom_field['options']) ? $custom_field['options'] : 'Y-m-d';
791
792 $hasTime = FieldsShortcodeCallback::hasTime($dateFormat);
793 $time24 = false;
794
795 if ($hasTime && strpos($dateFormat, 'H') !== false) {
796 $time24 = true;
797 }
798
799 $config = apply_filters('ppress_frontend_flatpickr_date_config', [
800 'dateFormat' => $dateFormat,
801 'enableTime' => $hasTime,
802 'noCalendar' => ! FieldsShortcodeCallback::hasDate($dateFormat),
803 'disableMobile' => true,
804 'time_24hr' => $time24
805 ]);
806
807 printf(
808 '<input type="text" name="%1$s" placeholder="%2$s" value="%4$s" class="ppressmd-form-field ppmd-date" data-config="%3$s">',
809 'filters[' . $custom_field['field_key'] . ']',
810 $custom_field['label_name'],
811 esc_attr(json_encode($config)),
812 ppress_var(ppress_var($query_params, 'filters', []), $field_key)
813 );
814 break;
815 }
816 ?>
817
818 </div>
819
820 <?php endforeach; ?>
821
822 </div>
823
824 </div>
825 <?php endif;
826 }
827
828 protected function search_form()
829 {
830 if ($this->get_meta('ppress_md_enable_search') != 'true') return;
831
832 $search_string = esc_html__('Search', 'wp-user-avatar');
833
834 $entered_search_term = ppress_var($this->search_filter_query_params(), 'search-' . $this->form_id, '');
835
836 ?>
837 <div class="ppressmd-member-directory-header-row ppressmd-member-directory-search-row">
838 <div class="ppressmd-member-directory-search-line">
839 <label>
840 <input name="search-<?= $this->form_id ?>" type="search" class="ppressmd-search-line" placeholder="<?= $search_string ?>" value="<?= $entered_search_term ?>">
841 </label>
842 <input type="submit" class="ppressmd-do-search ppressmd-button" value="<?= $search_string ?>">
843 </div>
844 </div>
845 <?php
846 }
847
848 protected function display_pagination($total_users_found)
849 {
850 $current_page = $this->get_current_page();
851
852 $users_per_page = $this->get_default_result_number_per_page();
853
854 $total_pages = ceil($total_users_found / $users_per_page);
855
856 if ($total_pages > 1) {
857
858 echo '<div class="ppmd-pagination-wrap">';
859
860 // from https://wordpress.stackexchange.com/questions/275527/paginate-links-ignore-my-format
861 echo paginate_links([
862 //'base' => '%_%', somehow with this enabled, pagination breaks where page 1 link becomes the current page url
863 'total' => $total_pages,
864 'current' => $current_page,
865 'format' => '?mdpage' . $this->form_id . '=%#%',
866 'prev_text' => '<span class="ppress-material-icons">keyboard_arrow_left</span>',
867 'next_text' => '<span class="ppress-material-icons">keyboard_arrow_right</span>',
868 ]);
869
870 echo '</div>';
871 }
872 }
873
874 protected function search_filter_sort_structure()
875 {
876 $is_filters_expanded = false;
877
878 $query_params = $this->search_filter_query_params();
879
880 if (isset($query_params['filters']) && ! empty(array_filter($query_params['filters']))) {
881 $is_filters_expanded = true;
882 }
883 ?>
884
885 <div class="ppressmd-member-directory-header ppressmd-form<?= $is_filters_expanded ? ' ppmd-filters-expand' : ''; ?>">
886
887 <form action="<?= ppress_get_current_url_query_string() ?>" method="get">
888
889 <?php $this->search_form(); ?>
890
891 <div class="ppressmd-member-directory-header-row">
892 <div class="ppressmd-member-directory-nav-line">
893
894 <?php $this->sort_method_dropdown_menu(); ?>
895
896 <?php $this->filter_structure(); ?>
897
898 </div>
899 </div>
900
901 <?php $this->filter_structure(true) ?>
902
903 <input type="hidden" name="ppmd-search" value="<?= $this->form_id ?>">
904
905 </form>
906
907 </div>
908 <?php
909 }
910
911 protected function is_result_after_search_enabled()
912 {
913 return $this->get_meta('ppress_md_enable_result_after_search') == 'true';
914 }
915
916 protected function get_results_text()
917 {
918 return $this->get_meta('ppress_md_results_text');
919 }
920
921 protected function get_single_result_text()
922 {
923 return $this->get_meta('ppress_md_single_result_text');
924 }
925
926 protected function get_no_result_text()
927 {
928 return $this->get_meta('ppress_md_no_result_text');
929 }
930
931 protected function get_default_result_number_per_page()
932 {
933 static $cache = [];
934
935 if ( ! isset($cache[$this->form_id])) {
936 $cache[$this->form_id] = absint($this->get_meta('ppress_md_result_number_per_page'));
937 }
938
939 return $cache[$this->form_id];
940 }
941
942 /**
943 * @return MemberDirectoryListing
944 */
945 protected function directory_listing($user_id = false)
946 {
947 return (new MemberDirectoryListing($this->form_id, $user_id))->defaults($this->default_fields_settings());
948 }
949
950 public function form_css()
951 {
952 $form_id = $this->form_id;
953 $form_type = $this->form_type;
954
955 $search_filter_field_text_color = $this->get_meta('ppress_md_search_filter_field_text_color');
956 $search_filter_field_border_color = $this->get_meta('ppress_md_search_filter_field_border_color');
957
958 $pagination_link_color = $this->get_meta('ppress_md_pagination_link_color');
959 $pagination_active_link_color = $this->get_meta('ppress_md_pagination_active_link_color');
960 $pagination_active_bg_color = $this->get_meta('ppress_md_pagination_active_background_color');
961
962 return <<<CSS
963 #pp-$form_type-$form_id.pp-member-directory .ppressmd-member-directory-header .ppressmd-member-directory-header-row .ppressmd-member-directory-search-line label .ppressmd-search-line,
964 #pp-$form_type-$form_id.pp-member-directory .ppressmd-member-directory-header .ppressmd-member-directory-header-row .ppressmd-search .ppressmd-search-filter.ppressmd-text-filter-type input:not(.select2-search__field),
965 #pp-$form_type-$form_id.pp-member-directory .ppressmd-member-directory-header .select2.select2-container .select2-selection {
966 border: 1px solid $search_filter_field_border_color !important;
967 }
968
969 #pp-$form_type-$form_id.pp-member-directory .ppressmd-member-directory-header .ppressmd-member-directory-header-row .ppressmd-search .ppressmd-search-filter.ppressmd-text-filter-type input,
970 #pp-$form_type-$form_id.pp-member-directory .ppressmd-member-directory-header .ppressmd-member-directory-header-row .ppressmd-member-directory-search-line label .ppressmd-search-line,
971 #pp-$form_type-$form_id.pp-member-directory .ppressmd-member-directory-header .ppressmd-member-directory-header-row .ppressmd-member-directory-nav-line .ppress-material-icons,
972 #pp-$form_type-$form_id.pp-member-directory .ppressmd-member-directory-header .select2.select2-container .select2-selection__rendered {
973 color: $search_filter_field_text_color !important;
974 }
975
976 #pp-$form_type-$form_id.pp-member-directory .ppmd-pagination-wrap .page-numbers {
977 color: $pagination_link_color !important;
978 }
979
980 #pp-$form_type-$form_id.pp-member-directory .ppmd-pagination-wrap .page-numbers.current {
981 background: $pagination_active_bg_color !important;
982 color: $pagination_active_link_color !important;
983 }
984 CSS;
985
986 }
987 }