PluginProbe
WPSSO Core – Complete Schema Markup and Meta Tags / 22.7.0
WPSSO Core – Complete Schema Markup and Meta Tags v22.7.0
22.7.0 22.6.1 22.6.0 22.5.3 22.5.2 22.5.1 22.4.0 22.3.0 22.2.1 22.2.0 22.1.3 22.1.2 22.1.1 22.1.0 22.0.0 21.13.3 21.13.2 trunk 21.13.1
wpsso / lib / user.php

user.php in WPSSO Core – Complete Schema Markup and Meta Tags 22.7.0, at lib/user.php

2,137 lines 56.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 * License: GPLv3
4 * License URI: https://www.gnu.org/licenses/gpl.txt
5 * Copyright 2012-2026 Jean-Sebastien Morisset (https://wpsso.com/)
6 */
7
8 if ( ! defined( 'ABSPATH' ) ) {
9
10 die( 'These aren\'t the droids you\'re looking for.' );
11 }
12
13 if ( ! defined( 'WPSSO_PLUGINDIR' ) ) {
14
15 die( 'Do. Or do not. There is no try.' );
16 }
17
18 if ( ! class_exists( 'WpssoAbstractWpMeta' ) ) {
19
20 require_once WPSSO_PLUGINDIR . 'lib/abstract/wp-meta.php';
21 }
22
23 if ( ! class_exists( 'WpssoUser' ) ) {
24
25 class WpssoUser extends WpssoAbstractWpMeta {
26
27 private static $cache_user_prefs = array(); // Used by get_pref() and save_pref().
28
29 public function __construct( &$plugin ) {
30
31 $this->p =& $plugin;
32
33 if ( $this->p->debug->enabled ) {
34
35 $this->p->debug->mark();
36 }
37
38 /*
39 * This hook is fired once WordPress, plugins, and the theme are fully loaded and instantiated.
40 */
41 add_action( 'wp_loaded', array( $this, 'add_wp_callbacks' ) );
42 }
43
44 /*
45 * Add WordPress action and filter callbacks.
46 */
47 public function add_wp_callbacks() {
48
49 if ( $this->p->debug->enabled ) {
50
51 $this->p->debug->mark();
52 }
53
54 /*
55 * Since WPSSO Core v17.0.0.
56 *
57 * Register our user meta.
58 */
59 $this->register_meta( $object_type = 'user', WPSSO_META_NAME );
60
61 $is_admin = is_admin(); // Only check once.
62
63 $cm_fb_name = $this->p->options[ 'plugin_cm_fb_name' ];
64
65 if ( ! SucomUtilWP::role_exists( 'person' ) ) {
66
67 $role_label_transl = _x( 'Person', 'user role', 'wpsso' );
68
69 add_role( 'person', $role_label_transl, array() );
70 }
71
72 if ( ! empty( $this->p->options[ 'plugin_new_user_is_person' ] ) ) {
73
74 if ( is_multisite() ) {
75
76 add_action( 'wpmu_new_user', array( __CLASS__, 'add_role_by_id' ), 20, 1 );
77
78 } else add_action( 'user_register', array( __CLASS__, 'add_role_by_id' ), 20, 1 );
79 }
80
81 add_filter( 'user_contactmethods', array( $this, 'add_contact_methods' ), 20, 2 );
82 add_filter( 'user_' . $cm_fb_name . '_label', array( $this, 'modify_fb_contact_label' ), 20, 1 );
83
84 add_action( 'wpsso_add_person_role', array( $this, 'add_person_role' ), 10, 1 ); // For single scheduled task.
85 add_action( 'wpsso_remove_person_role', array( $this, 'remove_person_role' ), 10, 1 ); // For single scheduled task.
86
87 /*
88 * Hook a minimum number of admin actions to maximize performance. The user_id argument is
89 * always present when we're editing a user, but missing when viewing our own profile page.
90 */
91 if ( $is_admin ) {
92
93 if ( ! empty( $_GET ) ) { // Skip some action hooks if no query argument(s).
94
95 /*
96 * load_meta_page() priorities: 100 post, 200 user, 300 term.
97 *
98 * Sets the parent::$head_tags and parent::$head_info class properties.
99 */
100 add_action( 'current_screen', array( $this, 'load_meta_page' ), 200, 1 );
101
102 /*
103 * Fires after the 'About the User' settings table on the 'Edit User' screen.
104 */
105 add_action( 'edit_user_profile', array( $this, 'add_meta_boxes' ), 10, 1 );
106 }
107
108 add_action( 'admin_bar_menu', array( $this, 'add_admin_bar_menu_view_profile' ), WPSSO_TB_VIEW_PROFILE_MENU_ORDER, 1 );
109
110 add_filter( 'views_users', array( $this, 'add_person_view' ) );
111
112 /*
113 * Add edit table columns.
114 */
115 if ( $this->p->debug->enabled ) {
116
117 $this->p->debug->log( 'adding column filters for users' );
118 }
119
120 add_filter( 'manage_users_columns', array( $this, 'add_user_column_headings' ), WPSSO_ADD_COLUMN_PRIORITY, 1 );
121 add_filter( 'manage_users_sortable_columns', array( $this, 'add_sortable_columns' ), 10, 1 );
122 add_filter( 'manage_users_custom_column', array( $this, 'get_column_content' ), 10, 3 );
123
124 /*
125 * The 'parse_query' action is hooked once in the WpssoPost class to set the column orderby for
126 * post, term, and user edit tables.
127 *
128 * This comment is here as a reminder - do not uncomment the following 'parse_query' action hook.
129 *
130 * add_action( 'parse_query', array( $this, 'set_column_orderby' ), 10, 1 );
131 */
132
133 /*
134 * Maybe create or update the user column content.
135 */
136 add_action( 'get_user_metadata', array( $this, 'check_sortable_meta' ), 1000, 4 );
137
138 /*
139 * Hooks when editing a user.
140 */
141 add_action( 'edit_user_profile', array( $this, 'show_metaboxes' ), 20, 1 );
142
143 add_action( 'edit_user_profile_update', array( $this, 'save_about_section' ), -3000, 1 );
144 add_action( 'edit_user_profile_update', array( $this, 'sanitize_submit_cm' ), -2000, 1 );
145 add_action( 'edit_user_profile_update', array( $this, 'save_options' ), WPSSO_META_SAVE_PRIORITY, 1 );
146 add_action( 'edit_user_profile_update', array( $this, 'clear_cache' ), WPSSO_META_CLEAR_PRIORITY, 1 );
147 add_action( 'edit_user_profile_update', array( $this, 'refresh_cache' ), WPSSO_META_REFRESH_PRIORITY, 1 );
148
149 /*
150 * Hooks when editing personal profile.
151 */
152 add_action( 'personal_options_update', array( $this, 'save_about_section' ), -3000, 1 );
153 add_action( 'personal_options_update', array( $this, 'sanitize_submit_cm' ), -2000, 1 );
154 add_action( 'personal_options_update', array( $this, 'save_options' ), WPSSO_META_SAVE_PRIORITY, 1 );
155 add_action( 'personal_options_update', array( $this, 'clear_cache' ), WPSSO_META_CLEAR_PRIORITY, 1 );
156 add_action( 'personal_options_update', array( $this, 'refresh_cache' ), WPSSO_META_REFRESH_PRIORITY, 1 );
157
158 /*
159 * Use the 'show_password_fields' filter as an action to get more information about the user.
160 */
161 add_filter( 'show_password_fields', array( $this, 'pre_password_fields' ), -1000, 2 );
162 }
163 }
164
165 /*
166 * Get the $mod object for a user id.
167 */
168 public function get_mod( $user_id ) {
169
170 if ( $this->p->debug->enabled ) {
171
172 $this->p->debug->mark_caller();
173
174 $this->p->debug->log_args( array(
175 'user_id' => $user_id,
176 ) );
177 }
178
179 static $local_fifo = array();
180
181 /*
182 * Maybe return the array from the local cache.
183 */
184 if ( isset( $local_fifo[ $user_id ] ) ) {
185
186 if ( ! $this->md_cache_disabled ) {
187
188 if ( $this->p->debug->enabled ) {
189
190 $this->p->debug->log( 'exiting early: user id ' . $user_id . ' mod array from local cache' );
191 }
192
193 return $local_fifo[ $user_id ];
194
195 } else unset( $local_fifo[ $user_id ] );
196 }
197
198 /*
199 * Maybe limit the number of array elements.
200 */
201 $local_fifo = SucomUtil::array_slice_fifo( $local_fifo, WPSSO_CACHE_ARRAY_FIFO_MAX );
202
203 $mod = self::get_mod_defaults();
204
205 /*
206 * Common elements.
207 */
208 $mod[ 'id' ] = is_numeric( $user_id ) ? (int) $user_id : 0; // Cast as integer.
209 $mod[ 'name' ] = 'user';
210 $mod[ 'name_transl' ] = _x( 'user', 'module name', 'wpsso' );
211 $mod[ 'obj' ] =& $this;
212
213 /*
214 * WpssoUser elements.
215 */
216 $mod[ 'is_user' ] = true;
217 $mod[ 'is_archive' ] = true; // Required for WpssoUtil->get_url_paged().
218
219 if ( $mod[ 'id' ] ) { // Just in case.
220
221 $mod[ 'wp_obj' ] = SucomUtilWP::get_user_object( $mod[ 'id' ] ); // Optimize and fetch once.
222
223 if ( $mod[ 'wp_obj' ] instanceof WP_User ) { // Just in case.
224
225 $mod[ 'user_name' ] = (string) $mod[ 'wp_obj' ]->display_name;
226
227 } else $mod[ 'wp_obj' ] = false;
228 }
229
230 /*
231 * Filter the user mod array.
232 */
233 $mod = apply_filters( 'wpsso_get_user_mod', $mod, $user_id );
234
235 if ( $this->p->debug->enabled ) {
236
237 $this->p->debug->log_arr( 'mod', $mod );
238 }
239
240 /*
241 * Maybe save the array to the local cache.
242 */
243 if ( ! $this->md_cache_disabled ) {
244
245 $local_fifo[ $user_id ] = $mod;
246
247 if ( $this->p->debug->enabled ) {
248
249 $this->p->debug->log_size( 'local_fifo', $local_fifo );
250 }
251 }
252
253 return $mod;
254 }
255
256 public function get_mod_wp_object( array $mod ) {
257
258 if ( $mod[ 'wp_obj' ] instanceof WP_User ) {
259
260 return $mod[ 'wp_obj' ];
261 }
262
263 return SucomUtilWP::get_user_object( $mod[ 'id' ] );
264 }
265
266 /*
267 * Option handling methods:
268 *
269 * get_defaults()
270 * get_options()
271 * save_options()
272 * delete_options()
273 */
274 public function get_options( $user_id, $md_key = false, $filter_opts = true, $merge_defs = false ) {
275
276 if ( $this->p->debug->enabled ) {
277
278 $this->p->debug->mark_caller();
279
280 $this->p->debug->log_args( array(
281 'user_id' => $user_id,
282 'md_key' => $md_key,
283 'filter_opts' => $filter_opts,
284 'merge_defs' => $merge_defs, // Fallback to value in meta defaults.
285 ) );
286 }
287
288 $user_id = false === $user_id ? get_current_user_id() : $user_id;
289
290 if ( empty( $user_id ) ) {
291
292 if ( false !== $md_key ) {
293
294 return null;
295 }
296
297 return array();
298 }
299
300 static $local_fifo = array();
301
302 /*
303 * Use $user_id and $filter_opts to create the cache ID string, but do not add $merge_defs.
304 */
305 $cache_id = SucomUtil::get_assoc_salt( array( 'id' => $user_id, 'filter' => $filter_opts ) );
306
307 /*
308 * Maybe initialize a new local cache element. Use isset() instead of empty() to allow for an empty array.
309 */
310 if ( ! isset( $local_fifo[ $cache_id ] ) ) {
311
312 /*
313 * Maybe limit the number of array elements.
314 */
315 $local_fifo = SucomUtil::array_slice_fifo( $local_fifo, WPSSO_CACHE_ARRAY_FIFO_MAX );
316
317 $local_fifo[ $cache_id ] = null; // Create an element to reference.
318 }
319
320 $md_opts =& $local_fifo[ $cache_id ]; // Reference the local cache element.
321
322 if ( null === $md_opts ) { // Maybe read metadata into a new local cache element.
323
324 if ( $this->p->debug->enabled ) {
325
326 $this->p->debug->log( 'getting metadata for user id ' . $user_id );
327 }
328
329 $user_exists = SucomUtilWP::user_exists( $user_id );
330
331 if ( $user_exists ) {
332
333 $user_exists_id = $user_id;
334
335 $md_opts = self::get_meta( $user_exists_id, WPSSO_META_NAME, $single = true );
336
337 if ( ! is_array( $md_opts ) ) {
338
339 $md_opts = array(); // WPSSO_META_NAME not found.
340 }
341
342 } else {
343
344 $user_exists_id = 0;
345
346 $md_opts = apply_filters( 'wpsso_get_other_user_meta', array(), $user_id );
347 }
348
349 unset( $md_opts[ 'opt_filtered' ] ); // Just in case.
350
351 /*
352 * Check if options need to be upgraded and saved.
353 */
354 if ( $this->p->opt->is_upgrade_required( $md_opts ) ) {
355
356 $md_opts = $this->upgrade_options( $md_opts, $user_exists_id );
357
358 if ( $user_exists ) {
359
360 self::update_meta( $user_exists_id, WPSSO_META_NAME, $md_opts );
361
362 } else apply_filters( 'wpsso_update_other_user_meta', $md_opts, $user_id );
363 }
364 }
365
366 if ( $filter_opts ) {
367
368 if ( ! empty( $md_opts[ 'opt_filtered' ] ) ) { // Set before calling filters to prevent recursion.
369
370 if ( $this->p->debug->enabled ) {
371
372 $this->p->debug->log( 'skipping filters: options already filtered' );
373 }
374
375 } else {
376
377 if ( $this->p->debug->enabled ) {
378
379 $this->p->debug->log( 'setting opt_filtered to 1' );
380 }
381
382 $md_opts[ 'opt_filtered' ] = 1; // Set before calling filters to prevent recursion.
383
384 if ( $this->p->debug->enabled ) {
385
386 $this->p->debug->log( 'required call to WpssoUser->get_mod() for user ID ' . $user_id );
387 }
388
389 $mod = $this->get_mod( $user_id );
390
391 $filter_name = 'wpsso_get_md_options';
392
393 if ( $this->p->debug->enabled ) {
394
395 $this->p->debug->log( 'applying filters "' . $filter_name . '"' );
396 }
397
398 $md_opts = apply_filters( $filter_name, $md_opts, $mod );
399
400 $filter_name = 'wpsso_get_' . $mod[ 'name' ] . '_options';
401
402 if ( $this->p->debug->enabled ) {
403
404 $this->p->debug->log( 'applying filters "' . $filter_name . '"' );
405 }
406
407 $md_opts = apply_filters( $filter_name, $md_opts, $user_id, $mod );
408
409 $filter_name = 'wpsso_sanitize_md_options';
410
411 if ( $this->p->debug->enabled ) {
412
413 $this->p->debug->log( 'applying filters "' . $filter_name . '"' );
414 }
415
416 $md_opts = apply_filters( $filter_name, $md_opts, $mod );
417
418 /*
419 * Prevent users from modifying specific options.
420 */
421 $disable_keys = array(
422 'og_type',
423 'schema_type',
424 'canonical_url',
425 'redirect_url',
426 );
427
428 $filter_name = 'wpsso_get_user_options_keys_disabled';
429
430 if ( $this->p->debug->enabled ) {
431
432 $this->p->debug->log( 'applying filters "' . $filter_name . '"' );
433 }
434
435 $disable_keys = apply_filters( $filter_name, $disable_keys, $user_id, $mod );
436
437 if ( ! empty( $disable_keys ) ) {
438
439 foreach ( $disable_keys as $opt_key ) {
440
441 $md_opts[ $opt_key . ':disabled' ] = true;
442 }
443 }
444 }
445 }
446
447 /*
448 * Maybe save the array to the local cache.
449 */
450 if ( $this->md_cache_disabled ) {
451
452 $deref_md_opts = $local_fifo[ $cache_id ]; // Dereference.
453
454 unset( $local_fifo, $md_opts );
455
456 return $this->return_options( $user_id, $deref_md_opts, $md_key, $merge_defs );
457 }
458
459 return $this->return_options( $user_id, $md_opts, $md_key, $merge_defs );
460 }
461
462 /*
463 * Use $rel = false to extend WpssoAbstractWpMeta->save_options().
464 */
465 public function save_options( $user_id, $rel = false ) {
466
467 if ( $this->p->debug->enabled ) {
468
469 $this->p->debug->log_args( array(
470 'user_id' => $user_id,
471 ) );
472 }
473
474 if ( empty( $user_id ) ) { // Just in case.
475
476 if ( $this->p->debug->enabled ) {
477
478 $this->p->debug->log( 'exiting early: user id is empty' );
479 }
480
481 return;
482
483 } elseif ( ! $this->verify_submit_nonce() ) {
484
485 if ( $this->p->debug->enabled ) {
486
487 $this->p->debug->log( 'exiting early: verify_submit_nonce failed' );
488 }
489
490 return;
491
492 /*
493 * Check user capability for the user id.
494 */
495 } elseif ( ! $this->user_can_edit( $user_id ) ) {
496
497 if ( $this->p->debug->enabled ) {
498
499 $user_id = get_current_user_id();
500
501 $this->p->debug->log( 'exiting early: user id ' . $user_id . ' cannot edit use id ' . $user_id );
502 }
503
504 return;
505 }
506
507 $this->md_cache_disable(); // Disable the local cache.
508
509 $mod = $this->get_mod( $user_id );
510
511 $md_opts = $this->get_submit_opts( $mod ); // Merge previous + submitted options and then sanitize.
512
513 $this->md_cache_enable(); // Re-enable the local cache.
514
515 if ( false === $md_opts ) {
516
517 if ( $this->p->debug->enabled ) {
518
519 $this->p->debug->log( 'exiting early: returned submit options is false' );
520 }
521
522 return;
523 }
524
525 $md_opts = apply_filters( 'wpsso_save_md_options', $md_opts, $mod );
526
527 $md_opts = apply_filters( 'wpsso_save_' . $mod[ 'name' ] . '_options', $md_opts, $user_id, $mod );
528
529 return self::update_meta( $user_id, WPSSO_META_NAME, $md_opts );
530 }
531
532 /*
533 * Use $rel = false to extend WpssoAbstractWpMeta->delete_options().
534 */
535 public function delete_options( $user_id, $rel = false ) {
536
537 return self::delete_meta( $user_id, WPSSO_META_NAME );
538 }
539
540 /*
541 * Get all publicly accessible user ids in the 'creator' array.
542 */
543 public static function get_public_ids( array $users_args = array() ) {
544
545 $wpsso =& Wpsso::get_instance();
546
547 $mtime_start = microtime( $get_float = true );
548
549 $role_names = empty( $users_args[ 'role' ] ) ? $wpsso->cf[ 'wp' ][ 'roles' ][ 'creator' ] : array( $users_args[ 'role' ] );
550
551 $users_args = array_merge( $users_args, array( 'fields' => 'ids' ) );
552
553 $public_ids = array();
554
555 foreach ( $role_names as $name ) {
556
557 /*
558 * See https://developer.wordpress.org/reference/classes/wp_user_query/__construct/.
559 */
560 $query_args = array_merge( $users_args, array( 'role' => $name ) );
561
562 $users_ids = get_users( $query_args );
563
564 if ( is_array( $users_ids ) ) {
565
566 foreach ( $users_ids as $user_id ) {
567
568 if ( is_numeric( $user_id ) ) {
569
570 $public_ids[ $user_id ] = $user_id; // Prevents duplicates.
571 }
572 }
573 }
574 }
575
576 unset( $query_args, $users_ids );
577
578 /*
579 * Sort public user IDs with the newest user ID first.
580 *
581 * Note that rsort() assigns new keys to elements in the array.
582 *
583 * See https://www.php.net/manual/en/function.rsort.php.
584 */
585 rsort( $public_ids );
586
587 $mtime_total = microtime( $get_float = true ) - $mtime_start;
588
589 if ( $wpsso->debug->enabled ) {
590
591 $wpsso->debug->log( count( $public_ids ) . ' user IDs returned in ' . sprintf( '%0.3f secs', $mtime_total ) );
592 }
593
594 return apply_filters( 'wpsso_user_public_ids', $public_ids, $users_args );
595 }
596
597 /*
598 * Get the posts for a user.
599 *
600 * Returns an array of post IDs for a given $mod object.
601 *
602 * The 'posts_per_page' value should be set for an archive page before calling this method.
603 *
604 * See WpssoAbstractWpMeta->get_posts_mods().
605 */
606 public function get_posts_ids( array $mod ) {
607
608 if ( $this->p->debug->enabled ) {
609
610 $this->p->debug->mark();
611 }
612
613 if ( empty( $mod[ 'is_user' ] ) ) {
614
615 if ( $this->p->debug->enabled ) {
616
617 $this->p->debug->log( 'exiting early: ' . $mod[ 'name' ] . ' ID ' . $mod[ 'id' ] . ' is not a user' );
618 }
619
620 return array();
621 }
622
623 $posts_args = array_merge( array(
624 'has_password' => false,
625 'order' => 'DESC', // Newest first.
626 'orderby' => 'date',
627 'post_status' => 'publish', // Only 'publish' (not 'auto-draft', 'draft', 'future', 'inherit', 'pending', 'private', or 'trash').
628 'post_type' => 'post', // Return only posts authored by the user.
629 'author' => $mod[ 'id' ],
630 ), $mod[ 'posts_args' ], array( 'fields' => 'ids' ) ); // Return an array of post IDs.
631
632 if ( $this->p->debug->enabled ) {
633
634 $this->p->debug->log( 'getting posts for ' . $mod[ 'name' ] . ' ID ' . $mod[ 'id' ] );
635
636 $this->p->debug->log_arr( 'posts_args', $posts_args );
637 }
638
639 $mtime_start = microtime( $get_float = true );
640
641 $posts_ids = SucomUtilWP::get_posts( $posts_args ); // Alternative to get_posts() that does not exclude sticky posts.
642
643 $mtime_total = microtime( $get_float = true ) - $mtime_start;
644
645 if ( $this->p->debug->enabled ) {
646
647 $this->p->debug->log( count( $posts_ids ) . ' post IDs returned in ' . sprintf( '%0.3f secs', $mtime_total ) );
648 }
649
650 return $posts_ids;
651 }
652
653 public function add_user_column_headings( $columns ) {
654
655 if ( $this->p->debug->enabled ) {
656
657 $this->p->debug->mark();
658 }
659
660 return $this->add_column_headings( $columns, $opt_suffix = 'user_page' );
661 }
662
663 public function get_update_meta_cache( $user_id ) {
664
665 return SucomUtilWP::get_update_meta_cache( $user_id, $meta_type = 'user' );
666 }
667
668 /*
669 * Hooked into the current_screen action.
670 *
671 * Sets the parent::$head_tags and parent::$head_info class properties.
672 */
673 public function load_meta_page( $screen = false ) {
674
675 if ( $this->p->debug->enabled ) {
676
677 $this->p->debug->mark();
678 }
679
680 /*
681 * All meta modules set this property, so use it to optimize code execution.
682 */
683 if ( false !== parent::$head_tags || ! isset( $screen->id ) ) {
684
685 return;
686 }
687
688 if ( $this->p->debug->enabled ) {
689
690 $this->p->debug->log( 'screen id = ' . $screen->id );
691 }
692
693 switch ( $screen->id ) {
694
695 case ( 0 === strpos( $screen->id, 'users_page_wpsso-add-' ) ? true : false ): // Add user page.
696
697 $user_id = null;
698
699 $mod = $this->get_mod( null );
700
701 break;
702
703 case 'profile': // User profile page.
704 case 'user-edit': // User editing page.
705 case ( 0 === strpos( $screen->id, 'profile_page_' ) ? true : false ): // Your profile page.
706 case ( 0 === strpos( $screen->id, 'users_page_' . $this->p->id ) ? true : false ): // Profile SSO page.
707
708 /*
709 * Get the user id.
710 *
711 * Returns the current user id if the 'user_id' query argument is empty.
712 */
713 $user_id = SucomUtilWP::get_user_object( false, 'id' );
714
715 $mod = $this->get_mod( $user_id );
716
717 break;
718
719 default:
720
721 if ( $this->p->debug->enabled ) {
722
723 $this->p->debug->log( 'exiting early: not a recognized user page' );
724 }
725
726 return;
727 }
728
729 /*
730 * Define parent::$head_tags and signal to other 'current_screen' actions that this is a user page.
731 */
732 parent::$head_tags = array(); // Used by WpssoAbstractWpMeta->is_meta_page().
733
734 if ( $this->p->debug->enabled ) {
735
736 $this->p->debug->log( 'user id = ' . $user_id );
737 $this->p->debug->log( 'home url = ' . get_option( 'home' ) );
738 $this->p->debug->log( 'locale default = ' . SucomUtilWP::get_locale() );
739 $this->p->debug->log( 'locale current = ' . SucomUtilWP::get_locale( 'current' ) );
740 $this->p->debug->log( 'locale mod = ' . SucomUtilWP::get_locale( $mod ) );
741 $this->p->debug->log( SucomUtil::get_array_pretty( $mod ) );
742 }
743
744 if ( $user_id && ! empty( $this->p->options[ 'plugin_add_to_user_page' ] ) ) {
745
746 do_action( 'wpsso_admin_user_head', $mod, $screen->id );
747
748 list(
749 parent::$head_tags, // Used by WpssoAbstractWpMeta->is_meta_page().
750 parent::$head_info // Used by WpssoAbstractWpMeta->check_head_info().
751 ) = $this->p->util->cache->refresh_mod_head_meta( $mod );
752
753 /*
754 * Check for missing open graph image and description values.
755 */
756 if ( $mod[ 'id' ] && $mod[ 'is_public' ] ) { // Since WPSSO Core v7.0.0.
757
758 $this->check_head_info( $mod );
759 }
760 }
761
762 $action_query = 'wpsso-action';
763
764 if ( ! empty( $_GET[ $action_query ] ) ) {
765
766 $action_name = SucomUtil::sanitize_hookname( $_GET[ $action_query ] );
767
768 if ( $this->p->debug->enabled ) {
769
770 $this->p->debug->log( 'found action query: ' . $action_name );
771
772 }
773
774 if ( empty( $_GET[ WPSSO_NONCE_NAME ] ) ) { // WPSSO_NONCE_NAME is an md5() string
775
776 if ( $this->p->debug->enabled ) {
777
778 $this->p->debug->log( 'nonce token query field missing' );
779 }
780
781 } elseif ( ! wp_verify_nonce( $_GET[ WPSSO_NONCE_NAME ], WpssoAdmin::get_nonce_action() ) ) {
782
783 $this->p->notice->err( sprintf( __( 'Nonce token validation failed for %1$s action "%2$s".', 'wpsso' ), 'user', $action_name ) );
784
785 } else {
786
787 $_SERVER[ 'REQUEST_URI' ] = remove_query_arg( array( $action_query, WPSSO_NONCE_NAME ) );
788
789 switch ( $action_name ) {
790
791 default:
792
793 do_action( 'wpsso_load_meta_page_user_' . $action_name, $user_id, $screen->id );
794
795 break;
796 }
797 }
798 }
799 }
800
801 /*
802 * Use $rel = false to extend WpssoAbstractWpMeta->add_meta_boxes().
803 */
804 public function add_meta_boxes( $user_obj, $rel = false ) {
805
806 if ( $this->p->debug->enabled ) {
807
808 $this->p->debug->mark();
809 }
810
811 $user_id = empty( $user_obj->ID ) ? 0 : $user_obj->ID;
812
813 if ( empty( $this->p->options[ 'plugin_add_to_user_page' ] ) ) {
814
815 if ( $this->p->debug->enabled ) {
816
817 $this->p->debug->log( 'exiting early: cannot add metabox to user page' );
818 }
819
820 return;
821 }
822
823 $capability = 'edit_user';
824
825 if ( ! current_user_can( $capability, $user_id ) ) {
826
827 if ( $this->p->debug->enabled ) {
828
829 $this->p->debug->log( 'exiting early: cannot ' . $capability . ' for user id ' . $user_id );
830 }
831
832 return;
833 }
834
835 $metabox_id = $this->p->cf[ 'meta' ][ 'id' ];
836 $metabox_title = _x( $this->p->cf[ 'meta' ][ 'title' ], 'metabox title', 'wpsso' );
837 $metabox_screen = 'wpsso-user';
838 $metabox_context = 'normal';
839 $metabox_prio = 'default';
840 $callback_args = array( // Second argument passed to the callback.
841 'metabox_id' => $metabox_id,
842 'metabox_title' => $metabox_title,
843 '__block_editor_compatible_meta_box' => true,
844 );
845
846 if ( $this->p->debug->enabled ) {
847
848 $this->p->debug->log( 'adding metabox id wpsso_' . $metabox_id . ' for screen ' . $metabox_screen );
849 }
850
851 add_meta_box( 'wpsso_' . $metabox_id, $metabox_title, array( $this, 'show_metabox_' . $metabox_id ),
852 $metabox_screen, $metabox_context, $metabox_prio, $callback_args );
853 }
854
855 /*
856 * Show additional fields in the user profile About Yourself / About the user sections.
857 *
858 * Hooked to the 'show_password_fields' filter (not an action).
859 */
860 public function pre_password_fields( $show_password_fields, $user_obj ) {
861
862 if ( ! isset( $user_obj->ID ) ) { // Just in case.
863
864 /*
865 * This is a filter, so return the filter value unchanged.
866 */
867 return $show_password_fields;
868 }
869
870 if ( ! current_user_can( 'edit_user', $user_obj->ID ) ) { // Just in case.
871
872 /*
873 * This is a filter, so return the filter value unchanged.
874 */
875 return $show_password_fields;
876 }
877
878 $this->show_about_section( $user_obj->ID ); // Echo the additional input fields.
879
880 /*
881 * This is a filter, so return the filter value unchanged.
882 */
883 return $show_password_fields;
884 }
885
886 /*
887 * Called by the WpssoUser->pre_password_fields() and WpssoUsersAddPerson->show_post_body_settings_form() methods.
888 */
889 public function show_about_section( $user_id = 0 ) {
890
891 foreach ( $this->p->cf[ 'opt' ][ 'user_about' ] as $about_key => $about_label ) {
892
893 if ( empty( $this->p->options[ 'plugin_user_about_' . $about_key ] ) ) {
894
895 continue;
896 }
897
898 echo '<tr>';
899 echo '<th><label for="' . $about_key . '">' . esc_html( _x( $about_label, 'option label', 'wpsso' ) ) . '</label></th><td>';
900
901 foreach ( $this->get_user_about_meta_keys( $about_key ) as $meta_key ) {
902
903 $val = $user_id ? self::get_meta( $user_id, $meta_key, $single = true ) : '';
904
905 echo '<p><input type="text" class="regular-text" name="' . $meta_key . '" id="' . $meta_key . '" value="' . esc_attr( $val ) . '"></p>';
906 }
907
908 switch ( $about_key ) {
909
910 case 'honorific_prefix':
911
912 echo '<p class="description">' . __( 'Examples: Dr, Mrs, Mr.', 'wpsso' ) . '</p>';
913
914 break;
915
916 case 'honorific_suffix':
917
918 echo '<p class="description">' . __( 'Examples: CPA, Esq., M.D., P.E., PhD., PMP, RN', 'wpsso' ) . '</p>';
919
920 break;
921 }
922
923 echo '</td></tr>';
924 }
925 }
926
927 public function show_metaboxes( $user_obj ) {
928
929 if ( ! isset( $user_obj->ID ) ) { // Just in case.
930
931 return;
932 }
933
934 if ( empty( $this->p->options[ 'plugin_add_to_user_page' ] ) ) {
935
936 return;
937 }
938
939 if ( ! current_user_can( 'edit_user', $user_obj->ID ) ) { // Just in case.
940
941 return;
942 }
943
944 $metabox_screen = 'wpsso-user';
945 $metabox_context = 'normal';
946
947 echo '<div class="metabox-holder">' . "\n";
948
949 do_meta_boxes( $metabox_screen, $metabox_context, $user_obj );
950
951 echo "\n" . '</div><!-- .metabox-holder -->' . "\n";
952 }
953
954 public function ajax_get_metabox_sso() {
955
956 die( -1 ); // Nothing to do.
957 }
958
959 public function get_form_contact_fields( $fields = array() ) {
960
961 return array( 'none' => '[None]' ) + $this->add_contact_methods( array(
962 'author' => 'Author Archive',
963 'url' => 'WebSite'
964 ) );
965 }
966
967 public function add_contact_methods( $fields = array(), $user = null ) {
968
969 /*
970 * Unset built-in contact fields and/or update their labels.
971 */
972 if ( ! empty( $this->p->cf[ 'wp' ][ 'cm_names' ] ) && is_array( $this->p->cf[ 'wp' ][ 'cm_names' ] ) ) {
973
974 foreach ( $this->p->cf[ 'wp' ][ 'cm_names' ] as $id => $desc ) {
975
976 $cm_enabled_key = 'wp_cm_' . $id . '_enabled';
977 $cm_label_key = 'wp_cm_' . $id . '_label';
978
979 if ( isset( $this->p->options[ $cm_enabled_key ] ) ) {
980
981 if ( ! empty( $this->p->options[ $cm_enabled_key ] ) ) {
982
983 $cm_label_value = SucomUtilOptions::get_key_value( $cm_label_key, $this->p->options );
984
985 if ( ! empty( $cm_label_value ) ) { // Just in case.
986
987 $fields[ $id ] = $cm_label_value;
988 }
989
990 } else {
991
992 unset( $fields[ $id ] );
993 }
994 }
995 }
996 }
997
998 /*
999 * Loop through each social website option prefix.
1000 */
1001 foreach ( $this->p->cf[ 'opt' ][ 'cm_prefix' ] as $id => $opt_pre ) {
1002
1003 $cm_enabled_key = 'plugin_cm_' . $opt_pre . '_enabled';
1004 $cm_name_key = 'plugin_cm_' . $opt_pre . '_name';
1005 $cm_label_key = 'plugin_cm_' . $opt_pre . '_label';
1006
1007 if ( ! empty( $this->p->options[ $cm_enabled_key ] ) && ! empty( $this->p->options[ $cm_name_key ] ) ) {
1008
1009 $cm_label_value = SucomUtilOptions::get_key_value( $cm_label_key, $this->p->options );
1010
1011 if ( ! empty( $cm_label_value ) ) {
1012
1013 $fields[ $this->p->options[ $cm_name_key ] ] = $cm_label_value;
1014 }
1015 }
1016 }
1017
1018 uasort( $fields, 'strnatcmp' ); // Sort associative array by value.
1019
1020 return $fields;
1021 }
1022
1023 public function modify_fb_contact_label( $label ) {
1024
1025 return $label . '<br/><span class="description" style="font-weight:normal;">' .
1026 __( '(not a Facebook Pages URL)', 'wpsso' ) . '</span>';
1027 }
1028
1029 /*
1030 * Save the additional fields for the user profile About Yourself / About the user sections.
1031 *
1032 * Hooked to the 'edit_user_profile_update' and 'personal_options_update' actions.
1033 *
1034 * Also called by the WpssoUsersAddPerson->add_person() method.
1035 */
1036 public function save_about_section( $user_id ) {
1037
1038 static $do_once = array(); // Just in case - prevent recursion.
1039
1040 if ( isset( $do_once[ $user_id ] ) ) return; // Stop here.
1041
1042 $do_once[ $user_id ] = true;
1043
1044 if ( ! current_user_can( 'edit_user', $user_id ) ) {
1045
1046 return;
1047 }
1048
1049 foreach ( $this->p->cf[ 'opt' ][ 'user_about' ] as $about_key => $about_label ) {
1050
1051 if ( empty( $this->p->options[ 'plugin_user_about_' . $about_key ] ) ) {
1052
1053 continue;
1054 }
1055
1056 foreach ( $this->get_user_about_meta_keys( $about_key ) as $meta_key ) {
1057
1058 /*
1059 * Regular text input fields.
1060 *
1061 * See https://developer.wordpress.org/themes/theme-security/data-sanitization-escaping/.
1062 */
1063 if ( isset( $_POST[ $meta_key ] ) ) {
1064
1065 self::update_meta( $user_id, $meta_key, sanitize_text_field( $_POST[ $meta_key ] ) );
1066 }
1067 }
1068 }
1069 }
1070
1071 public function sanitize_submit_cm( $user_id ) {
1072
1073 static $do_once = array(); // Just in case - prevent recursion.
1074
1075 if ( isset( $do_once[ $user_id ] ) ) return; // Stop here.
1076
1077 $do_once[ $user_id ] = true;
1078
1079 if ( ! current_user_can( 'edit_user', $user_id ) ) {
1080
1081 return;
1082 }
1083
1084 foreach ( $this->p->cf[ 'opt' ][ 'cm_prefix' ] as $id => $opt_pre ) {
1085
1086 /*
1087 * Check that the social site has a contact field.
1088 */
1089 if ( isset( $this->p->options[ 'plugin_cm_' . $opt_pre . '_name' ] ) ) {
1090
1091 $cm_enabled_value = $this->p->options[ 'plugin_cm_' . $opt_pre . '_enabled' ];
1092 $cm_name_value = $this->p->options[ 'plugin_cm_' . $opt_pre . '_name' ];
1093
1094 /*
1095 * Sanitize values only for those enabled contact methods.
1096 */
1097 if ( isset( $_POST[ $cm_name_value ] ) && ! empty( $cm_enabled_value ) && ! empty( $cm_name_value ) ) {
1098
1099 $value = wp_filter_nohtml_kses( $_POST[ $cm_name_value ] );
1100
1101 if ( ! empty( $value ) ) {
1102
1103 switch ( $cm_name_value ) {
1104
1105 case $this->p->options[ 'plugin_cm_skype_name' ]:
1106
1107 /*
1108 * No change.
1109 */
1110
1111 break;
1112
1113 case $this->p->options[ 'plugin_cm_twitter_name' ]:
1114
1115 $value = SucomUtil::sanitize_twitter_name( $value );
1116
1117 break;
1118
1119 default:
1120
1121 /*
1122 * All other contact methods are assumed to be URLs.
1123 */
1124
1125 if ( false === strpos( $value, '://' ) ) {
1126
1127 $value = '';
1128 }
1129
1130 break;
1131 }
1132 }
1133
1134 $_POST[ $cm_name_value ] = $value;
1135 }
1136 }
1137 }
1138
1139 return $user_id;
1140 }
1141
1142 public function get_user_sameas( $user_id ) {
1143
1144 $user_sameas = array();
1145
1146 $contact_methods = WpssoUser::get_user_id_contact_methods( $user_id );
1147
1148 foreach ( $contact_methods as $cm_id => $cm_label ) {
1149
1150 $url = $this->get_author_meta( $user_id, $cm_id );
1151
1152 if ( empty( $url ) ) {
1153
1154 continue;
1155
1156 } elseif ( $cm_id === $this->p->options[ 'plugin_cm_twitter_name' ] ) { // Convert twitter name to url.
1157
1158 $url = 'https://twitter.com/' . SucomUtil::sanitize_twitter_name( $url, $add_at = false );
1159 }
1160
1161 if ( false === filter_var( $url, FILTER_VALIDATE_URL ) ) { // Just in case.
1162
1163 if ( $this->p->debug->enabled ) {
1164
1165 $this->p->debug->log( 'skipping ' . $cm_id . ': url "' . $url . '" is invalid' );
1166 }
1167
1168 } else $user_sameas[] = $url;
1169 }
1170
1171 return $user_sameas;
1172 }
1173
1174 /*
1175 * Convert user ID to user object and call wp_get_user_contact_methods().
1176 */
1177 public static function get_user_id_contact_methods( $user_id ) {
1178
1179 $user_obj = get_user_by( 'ID', $user_id );
1180
1181 return wp_get_user_contact_methods( $user_obj );
1182 }
1183
1184 /*
1185 * Returns the user id for a comment author, post author, or user module.
1186 */
1187 public static function get_author_id( array $mod ) {
1188
1189 $author_id = false;
1190
1191 if ( $mod[ 'is_user' ] ) {
1192
1193 $author_id = $mod[ 'id' ];
1194
1195 } elseif ( $mod[ 'is_comment' ] ) {
1196
1197 if ( $mod[ 'comment_author' ] ) {
1198
1199 $author_id = $mod[ 'comment_author' ];
1200 }
1201
1202 } elseif ( $mod[ 'is_post' ] ) {
1203
1204 if ( $mod[ 'post_author' ] ) {
1205
1206 $author_id = $mod[ 'post_author' ];
1207 }
1208 }
1209
1210 return $author_id;
1211 }
1212
1213 /*
1214 * Returns the display name for a user module, comment author, or post author.
1215 */
1216 public function get_author_name( array $mod ) {
1217
1218 $author_name = '';
1219
1220 if ( $mod[ 'is_user' ] ) {
1221
1222 $author_name = get_the_author_meta( 'display_name', $mod[ 'id' ] );
1223
1224 } elseif ( $mod[ 'is_comment' ] ) {
1225
1226 if ( $mod[ 'comment_author_name' ] ) {
1227
1228 $author_id = $mod[ 'comment_author_name' ];
1229 }
1230
1231 } elseif ( $author_id = self::get_author_id( $mod ) ) {
1232
1233 $author_name = get_the_author_meta( 'display_name', $author_id );
1234 }
1235
1236 return $author_name;
1237 }
1238
1239 /*
1240 * Returns the description for a user module or post author.
1241 */
1242 public function get_author_description( array $mod ) {
1243
1244 $author_desc = '';
1245
1246 if ( $mod[ 'is_user' ] ) {
1247
1248 $author_desc = get_the_author_meta( 'description', $mod[ 'id' ] );
1249
1250 } elseif ( $author_id = self::get_author_id( $mod ) ) {
1251
1252 $author_desc = get_the_author_meta( 'description', $author_id );
1253 }
1254
1255 return $author_desc;
1256 }
1257
1258 public function get_author_meta( $user_id, $meta_key ) {
1259
1260 if ( $this->p->debug->enabled ) {
1261
1262 $this->p->debug->log_args( array(
1263 'user_id' => $user_id,
1264 'meta_key' => $meta_key,
1265 ) );
1266 }
1267
1268 static $local_fifo = array();
1269
1270 if ( isset( $local_fifo[ $user_id ] ) ) {
1271
1272 if ( isset( $local_fifo[ $user_id ][ $meta_key ] ) ) {
1273
1274 return $local_fifo[ $user_id ][ $meta_key ];
1275 }
1276
1277 } else {
1278
1279 /*
1280 * Maybe limit the number of array elements.
1281 */
1282 $local_fifo = SucomUtil::array_slice_fifo( $local_fifo, WPSSO_CACHE_ARRAY_FIFO_MAX );
1283
1284 $local_fifo[ $user_id ] = array();
1285 }
1286
1287 $user_exists = SucomUtilWP::user_exists( $user_id );
1288
1289 $author_meta = '';
1290
1291 if ( $user_exists ) {
1292
1293 switch ( $meta_key ) {
1294
1295 case 'none':
1296
1297 break;
1298
1299 case 'fullname':
1300
1301 $author_meta = get_the_author_meta( 'first_name', $user_id ) . ' ' . get_the_author_meta( 'last_name', $user_id );
1302
1303 break;
1304
1305 case 'description':
1306
1307 $author_meta = preg_replace( '/[\s\n\r]+/s', ' ', get_the_author_meta( $meta_key, $user_id ) );
1308
1309 break;
1310
1311 case 'awards':
1312
1313 $author_meta = array();
1314
1315 foreach ( $this->get_user_about_meta_keys( 'award' ) as $meta_key ) {
1316
1317 $val = self::get_meta( $user_id, $meta_key, $single = true );
1318
1319 if ( SucomUtil::is_valid_option_value( $val ) ) {
1320
1321 $author_meta[] = $val;
1322 }
1323 }
1324
1325 break;
1326
1327 default:
1328
1329 $author_meta = get_the_author_meta( $meta_key, $user_id );
1330
1331 break;
1332 }
1333
1334 if ( is_string( $author_meta ) ) {
1335
1336 $author_meta = trim( $author_meta ); // Just in case.
1337 }
1338
1339 } elseif ( $this->p->debug->enabled ) {
1340
1341 $this->p->debug->log( 'user id ' . $user_id . ' is not a WordPress user' );
1342 }
1343
1344 $filter_name = 'wpsso_get_author_meta';
1345
1346 if ( $this->p->debug->enabled ) {
1347
1348 $this->p->debug->log( 'applying filters "' . $filter_name . '"' );
1349 }
1350
1351 $author_meta = apply_filters( $filter_name, $author_meta, $user_id, $meta_key, $user_exists );
1352
1353 return $local_fifo[ $user_id ][ $meta_key ] = $author_meta;
1354 }
1355
1356 public function get_author_website( $user_id, $meta_key = 'url' ) {
1357
1358 if ( $this->p->debug->enabled ) {
1359
1360 $this->p->debug->log_args( array(
1361 'user_id' => $user_id,
1362 'meta_key' => $meta_key,
1363 ) );
1364 }
1365
1366 static $local_fifo = array();
1367
1368 if ( isset( $local_fifo[ $user_id ] ) ) {
1369
1370 if ( isset( $local_fifo[ $user_id ][ $meta_key ] ) ) {
1371
1372 return $local_fifo[ $user_id ][ $meta_key ];
1373 }
1374
1375 } else {
1376
1377 /*
1378 * Maybe limit the number of array elements.
1379 */
1380 $local_fifo = SucomUtil::array_slice_fifo( $local_fifo, WPSSO_CACHE_ARRAY_FIFO_MAX );
1381
1382 $local_fifo[ $user_id ] = array();
1383 }
1384
1385 $user_exists = SucomUtilWP::user_exists( $user_id );
1386
1387 $website_url = '';
1388
1389 if ( $user_exists ) {
1390
1391 switch ( $meta_key ) {
1392
1393 case 'none':
1394
1395 break;
1396
1397 case 'archive':
1398
1399 $website_url = get_author_posts_url( $user_id );
1400
1401 break;
1402
1403 case 'twitter':
1404
1405 $website_url = get_the_author_meta( $meta_key, $user_id );
1406
1407 if ( false === filter_var( $website_url, FILTER_VALIDATE_URL ) ) {
1408
1409 $website_url = 'https://twitter.com/' . preg_replace( '/^@/', '', $website_url );
1410 }
1411
1412 break;
1413
1414 case 'url':
1415 case 'website':
1416
1417 $website_url = get_the_author_meta( 'url', $user_id );
1418
1419 break;
1420
1421 default:
1422
1423 $website_url = get_the_author_meta( $meta_key, $user_id );
1424
1425 break;
1426 }
1427
1428 $website_url = trim( $website_url ); // Just in case.
1429
1430 } elseif ( $this->p->debug->enabled ) {
1431
1432 $this->p->debug->log( 'user id ' . $user_id . ' is not a WordPress user' );
1433 }
1434
1435 $website_url = apply_filters( 'wpsso_get_author_website', $website_url, $user_id, $meta_key, $user_exists );
1436
1437 return $local_fifo[ $user_id ][ $meta_key ] = (string) $website_url;
1438 }
1439
1440 /*
1441 * WpssoUser class specific methods.
1442 *
1443 * Called by WpssoOpenGraph->get_array() for a single post author and (possibly) several coauthors.
1444 */
1445 public function get_authors_websites( $users_ids, $meta_key = 'url' ) {
1446
1447 $urls = array();
1448
1449 if ( empty( $users_ids ) ) { // Just in case.
1450
1451 return $urls;
1452 }
1453
1454 if ( ! is_array( $users_ids ) ) {
1455
1456 $users_ids = array( $users_ids );
1457 }
1458
1459 if ( $meta_key && 'none' !== $meta_key ) { // Just in case.
1460
1461 foreach ( $users_ids as $user_id ) {
1462
1463 if ( empty( $user_id ) ) {
1464
1465 continue;
1466 }
1467
1468 $value = $this->get_author_website( $user_id, $meta_key ); // Returns a single URL string.
1469
1470 if ( ! empty( $value ) ) { // Make sure we don't add empty values.
1471
1472 $urls[] = $value;
1473 }
1474 }
1475 }
1476
1477 return $urls;
1478 }
1479
1480 /*
1481 * Called by the WpssoRegister::uninstall_plugin() method.
1482 *
1483 * Do not use Wpsso::get_instance() since the Wpsso class may not exist.
1484 */
1485 public static function delete_metabox_prefs( $user_id = false, $slug_prefix = 'wpsso' ) {
1486
1487 $cf = WpssoConfig::get_config();
1488
1489 $user_id = empty( $user_id ) ? get_current_user_id() : $user_id;
1490
1491 $parent_slug = 'options-general.php';
1492
1493 if ( ! empty( $cf[ '*' ][ 'lib' ][ 'settings' ] ) ) {
1494
1495 foreach ( array_keys( $cf[ '*' ][ 'lib' ][ 'settings' ] ) as $lib_id ) {
1496
1497 $menu_slug = $slug_prefix . '-' . $lib_id;
1498
1499 self::delete_metabox_pagehook( $user_id, $menu_slug, $parent_slug );
1500 }
1501 }
1502
1503 if ( ! empty( $cf[ '*' ][ 'lib' ][ 'submenu' ] ) ) {
1504
1505 $parent_slug = $slug_prefix . '-' . key( $cf[ '*' ][ 'lib' ][ 'submenu' ] );
1506
1507 foreach ( array_keys( $cf[ '*' ][ 'lib' ][ 'submenu' ] ) as $lib_id ) {
1508
1509 $menu_slug = $slug_prefix . '-' . $lib_id;
1510
1511 self::delete_metabox_pagehook( $user_id, $menu_slug, $parent_slug );
1512 }
1513 }
1514 }
1515
1516 private static function delete_metabox_pagehook( $user_id, $menu_slug, $parent_slug ) {
1517
1518 $pagehook = get_plugin_page_hookname( $menu_slug, $parent_slug);
1519
1520 foreach ( array( 'meta-box-order', 'metaboxhidden', 'closedpostboxes' ) as $mb_state ) {
1521
1522 $meta_key = $mb_state . '_' . $pagehook;
1523
1524 if ( false === $user_id ) {
1525
1526 $users_args = array(
1527 'role' => 'contributor', // Contributors can delete_posts, edit_posts, read.
1528 'order' => 'DESC', // Newest user first.
1529 'orderby' => 'ID',
1530 'meta_key' => $meta_key, // The meta_key in the wp_usermeta table.
1531 'fields' => array( // Save memory and only return only specific fields.
1532 'ID',
1533 ),
1534 );
1535
1536 foreach ( get_users( $users_args ) as $user_obj ) {
1537
1538 if ( ! empty( $user_obj->ID ) ) { // Just in case.
1539
1540 /*
1541 * Deletes user option with global blog capability.
1542 *
1543 * User options are just like user metadata except that they have support
1544 * for global blog options. If the 'is_global' parameter is false, which it
1545 * is by default, it will prepend the WordPress table prefix to the option
1546 * name.
1547 */
1548 delete_user_option( $user_obj->ID, $meta_key, $global = false );
1549
1550 delete_user_option( $user_obj->ID, $meta_key, $global = true );
1551 }
1552 }
1553
1554 } elseif ( is_numeric( $user_id ) ) {
1555
1556 delete_user_option( $user_id, $meta_key, $global = false );
1557
1558 delete_user_option( $user_id, $meta_key, $global = true );
1559 }
1560 }
1561 }
1562
1563 public static function is_metabox_hidden( $metabox_id, $screen_id = '', $user_id = null ) {
1564
1565 $screen_id = empty( $screen_id ) ? SucomUtilWP::get_screen_id() : $screen_id;
1566 $user_id = empty( $user_id ) ? get_current_user_id() : $user_id;
1567 $md_key = SucomUtil::sanitize_hookname( 'metaboxhidden_' . $screen_id );
1568 $md = get_metadata( 'user', $user_id, $md_key, $single = true );
1569
1570 return is_array( $md ) && in_array( $metabox_id, $md ) ? true : false;
1571 }
1572
1573 public static function is_show_all( $user_id = false ) {
1574
1575 return self::show_opts( 'all', $user_id );
1576 }
1577
1578 public static function get_show_val( $user_id = false ) {
1579
1580 return self::show_opts( false, $user_id );
1581 }
1582
1583 /*
1584 * Returns the value for show_opts, or return true/false if a value to compare is provided.
1585 */
1586 public static function show_opts( $compare = false, $user_id = false ) {
1587
1588 $user_id = empty( $user_id ) ? get_current_user_id() : $user_id;
1589
1590 $show_opts = self::get_pref( 'show_opts' );
1591
1592 if ( $compare ) {
1593
1594 return $compare === $show_opts ? true : false;
1595 }
1596
1597 return $show_opts;
1598 }
1599
1600 public static function get_pref( $pref_key = false, $user_id = false ) {
1601
1602 $user_id = empty( $user_id ) ? get_current_user_id() : $user_id;
1603
1604 if ( ! isset( self::$cache_user_prefs[ $user_id ][ 'prefs_filtered' ] ) || ! self::$cache_user_prefs[ $user_id ][ 'prefs_filtered' ] ) {
1605
1606 $wpsso =& Wpsso::get_instance();
1607
1608 self::$cache_user_prefs[ $user_id ] = self::get_meta( $user_id, WPSSO_PREF_NAME, $single = true );
1609
1610 if ( ! is_array( self::$cache_user_prefs[ $user_id ] ) ) {
1611
1612 self::$cache_user_prefs[ $user_id ] = array();
1613 }
1614
1615 self::$cache_user_prefs[ $user_id ][ 'prefs_filtered' ] = true; // Set before calling filter to prevent recursion.
1616
1617 self::$cache_user_prefs[ $user_id ] = apply_filters( 'wpsso_get_user_pref', self::$cache_user_prefs[ $user_id ], $user_id );
1618
1619 if ( ! isset( self::$cache_user_prefs[ $user_id ][ 'show_opts' ] ) ) {
1620
1621 self::$cache_user_prefs[ $user_id ][ 'show_opts' ] = $wpsso->options[ 'plugin_show_opts' ];
1622 }
1623
1624 if ( $wpsso->debug->enabled ) {
1625
1626 $wpsso->debug->log_arr( 'cache_user_prefs', self::$cache_user_prefs[ $user_id ] );
1627 }
1628 }
1629
1630 if ( false !== $pref_key ) {
1631
1632 if ( isset( self::$cache_user_prefs[ $user_id ][ $pref_key ] ) ) {
1633
1634 return self::$cache_user_prefs[ $user_id ][ $pref_key ];
1635 }
1636
1637 return false;
1638
1639 }
1640
1641 return self::$cache_user_prefs[ $user_id ];
1642 }
1643
1644 public static function save_pref( $user_prefs, $user_id = false ) {
1645
1646 $user_id = empty( $user_id ) ? get_current_user_id() : $user_id;
1647
1648 if ( ! current_user_can( 'edit_user', $user_id ) ) {
1649
1650 return false;
1651 }
1652
1653 if ( ! is_array( $user_prefs ) || empty( $user_prefs ) ) {
1654
1655 return false;
1656 }
1657
1658 $old_prefs = self::get_pref( false, $user_id ); // get all prefs for user
1659
1660 $new_prefs = array_merge( $old_prefs, $user_prefs );
1661
1662 /*
1663 * Don't bother saving unless we have to.
1664 */
1665 if ( $old_prefs !== $new_prefs ) {
1666
1667 self::$cache_user_prefs[ $user_id ] = $new_prefs; // update the pref cache
1668
1669 unset( $new_prefs[ 'prefs_filtered' ] );
1670
1671 self::update_meta( $user_id, WPSSO_PREF_NAME, $new_prefs );
1672
1673 return true;
1674 }
1675
1676 return false;
1677 }
1678
1679 /*
1680 * Use $rel = false to extend WpssoAbstractWpMeta->clear_cache().
1681 */
1682 public function clear_cache( $user_id, $rel = false ) {
1683
1684 if ( $this->p->debug->enabled ) {
1685
1686 $this->p->debug->log_args( array(
1687 'user_id' => $user_id,
1688 ) );
1689 }
1690
1691 static $do_once = array(); // Just in case - prevent recursion.
1692
1693 if ( isset( $do_once[ $user_id ] ) ) return; // Stop here.
1694
1695 $do_once[ $user_id ] = true;
1696
1697 if ( empty( $user_id ) ) return; // Just in case.
1698
1699 $mod = $this->get_mod( $user_id );
1700
1701 $this->clear_mod_cache( $mod );
1702
1703 do_action( 'wpsso_clear_user_cache', $user_id, $mod );
1704 }
1705
1706 /*
1707 * Refresh the cache for a single user ID.
1708 *
1709 * Use $rel = false to extend WpssoAbstractWpMeta->refresh_cache().
1710 */
1711 public function refresh_cache( $user_id, $rel = false ) {
1712
1713 if ( $this->p->debug->enabled ) {
1714
1715 $this->p->debug->log_args( array(
1716 'user_id' => $user_id,
1717 ) );
1718 }
1719
1720 static $do_once = array(); // Just in case - prevent recursion.
1721
1722 if ( isset( $do_once[ $user_id ] ) ) return; // Stop here.
1723
1724 $do_once[ $user_id ] = true;
1725
1726 if ( empty( $user_id ) ) return; // Just in case.
1727
1728 $mod = $this->get_mod( $user_id );
1729
1730 $this->p->util->cache->refresh_mod_head_meta( $mod );
1731
1732 do_action( 'wpsso_refresh_user_cache', $user_id, $mod );
1733 }
1734
1735 /*
1736 * Check user capability for the user id.
1737 *
1738 * Use $rel = false to extend WpssoAbstractWpMeta->user_can_edit().
1739 */
1740 public function user_can_edit( $user_id, $rel = false ) {
1741
1742 $capability = 'edit_user';
1743
1744 if ( ! current_user_can( $capability, $user_id ) ) {
1745
1746 if ( $this->p->debug->enabled ) {
1747
1748 $this->p->debug->log( 'exiting early: cannot ' . $capability . ' for user id ' . $user_id );
1749 }
1750
1751 /*
1752 * Add notice only if the admin notices have not already been shown.
1753 */
1754 if ( $this->p->notice->is_admin_pre_notices() ) {
1755
1756 $this->p->notice->err( sprintf( __( 'Insufficient privileges to edit user ID %1$s.', 'wpsso' ), $user_id ) );
1757 }
1758
1759 return false;
1760 }
1761
1762 return true;
1763 }
1764
1765 /*
1766 * Returns an array of single image associative arrays.
1767 *
1768 * $md_pre can be a text string or array of prefixes.
1769 */
1770 public function get_og_images( $num, $size_names, $user_id, $md_pre = 'og', $mt_pre = 'og' ) {
1771
1772 if ( $this->p->debug->enabled ) {
1773
1774 $this->p->debug->mark();
1775 }
1776
1777 $mod = $this->get_mod( $user_id );
1778 $user_exists = SucomUtilWP::user_exists( $user_id ); // Check if this is a valid WordPress user.
1779 $filter_name = 'wpsso_get_other_user_images';
1780
1781 if ( $user_exists ) {
1782
1783 if ( $this->p->debug->enabled ) {
1784
1785 $this->p->debug->log( 'skipping filters "' . $filter_name . '" (user exists)' );
1786 }
1787
1788 return $this->get_md_images( $num, $size_names, $mod, $md_pre, $mt_pre );
1789 }
1790
1791 if ( $this->p->debug->enabled ) {
1792
1793 $this->p->debug->log( 'applying filters "' . $filter_name . '"' );
1794 }
1795
1796 return apply_filters( $filter_name, array(), $num, $size_names, $user_id, $md_pre );
1797 }
1798
1799 /*
1800 * Schedule the addition of user roles for self::get_public_ids().
1801 */
1802 public function schedule_add_person_role( $user_id = null ) {
1803
1804 $user_id = $this->p->util->maybe_change_user_id( $user_id ); // Maybe change textdomain for user id.
1805 $task_name = 'add the Person role';
1806 $task_name_transl = _x( 'add the Person role', 'task name', 'wpsso' );
1807 $event_time = time() + WPSSO_SCHEDULE_SINGLE_EVENT_TIME; // Default event time is now + 10 seconds.
1808 $human_time = human_time_diff( 0, WPSSO_SCHEDULE_SINGLE_EVENT_TIME );
1809 $event_hook = 'wpsso_add_person_role';
1810 $event_args = array( $user_id );
1811
1812 if ( $user_id ) { // Just in case.
1813
1814 $notice_msg = sprintf( __( 'A background task will begin in the next %1$s to %2$s for content creators.', 'wpsso' ),
1815 $human_time, $task_name_transl );
1816
1817 $notice_key = $task_name . '-scheduled';
1818
1819 $this->p->notice->upd( $notice_msg, $user_id, $notice_key );
1820 }
1821
1822 wp_schedule_single_event( $event_time, $event_hook, $event_args );
1823 }
1824
1825 public function add_admin_bar_menu_view_profile( &$wp_admin_bar ) { // Pass by reference OK.
1826
1827 if ( ! is_admin() ) { // Just in case.
1828
1829 return;
1830 }
1831
1832 $screen_id = SucomUtilWP::get_screen_id();
1833
1834 switch ( $screen_id ) {
1835
1836 case 'profile': // User profile page.
1837 case ( 0 === strpos( $screen_id, 'profile_page_' ) ? true : false ): // Your profile page.
1838 case ( 0 === strpos( $screen_id, 'users_page_' . $this->p->id ) ? true : false ): // Profile SSO page.
1839
1840 $user_id = SucomUtilWP::get_user_object( false, 'id' );
1841
1842 $mod = $this->get_mod( $user_id );
1843
1844 break;
1845
1846 default:
1847
1848 return false;
1849 }
1850
1851 if ( $mod[ 'is_user' ] ) {
1852
1853 $parent_id = 'wpsso-view-profile';
1854 $menu_title = _x( 'View Profile', 'toolbar menu title', 'wpsso' );
1855 $canonical_url = $this->p->util->get_canonical_url( $mod );
1856
1857 $wp_admin_bar->add_node( array(
1858 'id' => $parent_id,
1859 'title' => $menu_title,
1860 'parent' => false,
1861 'href' => $canonical_url,
1862 'group' => false,
1863 ) );
1864
1865 return $parent_id;
1866 }
1867
1868 return false;
1869 }
1870
1871 public function add_person_role( $user_id = null ) {
1872
1873 if ( $this->p->debug->enabled ) {
1874
1875 $this->p->debug->mark();
1876 }
1877
1878 $user_id = $this->p->util->maybe_change_user_id( $user_id ); // Maybe change textdomain for user id.
1879 $role_label_transl = _x( 'Person', 'user role', 'wpsso' );
1880 $task_name = 'add the Person role';
1881 $task_name_transl = _x( 'add the Person role', 'task name', 'wpsso' );
1882
1883 if ( ! $this->p->util->cache->task_start( $user_id, $task_name, WPSSO_ADD_ROLE_MAX_TIME ) ) {
1884
1885 return; // Stop here - background task already running.
1886 }
1887
1888 if ( $user_id ) {
1889
1890 $mtime_start = microtime( $get_float = true );
1891 $time_on_date = SucomUtil::sprintf_date_time( _x( '%2$s on %1$s', 'time on date', 'wpsso' ) );
1892 $notice_msg = sprintf( __( 'A task to %1$s to content creators was started at %2$s.', 'wpsso' ), $role_label_transl, $time_on_date );
1893 $notice_key = $task_name . '-task-info';
1894
1895 $this->p->notice->inf( $notice_msg, $user_id, $notice_key );
1896 }
1897
1898 if ( 0 === get_current_user_id() ) { // User is the scheduler.
1899
1900 $this->p->util->cache->set_task_limit( $user_id, $task_name, WPSSO_ADD_ROLE_MAX_TIME );
1901 }
1902
1903 if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
1904
1905 /*
1906 * Register image sizes and include WooCommerce front-end libs.
1907 */
1908 do_action( 'wpsso_scheduled_task_started', $user_id );
1909 }
1910
1911 $count = 0;
1912
1913 $users_ids = self::get_public_ids(); // Aka 'administrator', 'editor', 'author', and 'contributor'.
1914
1915 foreach ( $users_ids as $id ) {
1916
1917 $count += self::add_role_by_id( $id, $role = 'person' );
1918 }
1919
1920 unset( $users_ids );
1921
1922 if ( $user_id ) {
1923
1924 $mtime_total = microtime( $get_float = true ) - $mtime_start;
1925 $human_time = human_time_diff( 0, $mtime_total );
1926 $notice_msg = sprintf( __( 'The %1$s role has been added to %2$d content creators.', 'wpsso' ), $role_label_transl, $count ) . ' ';
1927 $notice_msg .= sprintf( __( 'The total execution time for this task was %s.', 'wpsso' ), $human_time ) . ' ';
1928 $notice_key = $task_name . '-task-info';
1929
1930 $this->p->notice->inf( $notice_msg, $user_id, $notice_key );
1931 }
1932
1933 $this->p->util->cache->task_end( $user_id, $task_name );
1934 }
1935
1936 /*
1937 * Schedule the removal of user roles for self::get_public_ids().
1938 */
1939 public function schedule_remove_person_role( $user_id = null ) {
1940
1941 $user_id = $this->p->util->maybe_change_user_id( $user_id ); // Maybe change textdomain for user id.
1942 $task_name = 'remove the Person role';
1943 $task_name_transl = _x( 'remove the Person role', 'task name', 'wpsso' );
1944 $event_time = time() + WPSSO_SCHEDULE_SINGLE_EVENT_TIME; // Default event time is now + 10 seconds.
1945 $human_time = human_time_diff( 0, WPSSO_SCHEDULE_SINGLE_EVENT_TIME );
1946 $event_hook = 'wpsso_remove_person_role';
1947 $event_args = array( $user_id );
1948
1949 if ( $user_id ) { // Just in case.
1950
1951 $notice_msg = sprintf( __( 'A background task will begin in the next %1$s to %2$s from all users.', 'wpsso' ),
1952 $human_time, $task_name_transl );
1953
1954 $notice_key = $task_name . '-scheduled';
1955
1956 $this->p->notice->upd( $notice_msg, $user_id, $notice_key );
1957 }
1958
1959 wp_schedule_single_event( $event_time, $event_hook, $event_args );
1960 }
1961
1962 public function remove_person_role( $user_id = null ) {
1963
1964 if ( $this->p->debug->enabled ) {
1965
1966 $this->p->debug->mark();
1967 }
1968
1969 $user_id = $this->p->util->maybe_change_user_id( $user_id ); // Maybe change textdomain for user id.
1970 $role_label_transl = _x( 'Person', 'user role', 'wpsso' );
1971 $task_name = 'remove the Person role';
1972 $task_name_transl = _x( 'remove the Person role', 'task name', 'wpsso' );
1973
1974 if ( ! $this->p->util->cache->task_start( $user_id, $task_name, WPSSO_REMOVE_ROLE_MAX_TIME ) ) {
1975
1976 return; // Stop here - background task already running.
1977 }
1978
1979 if ( $user_id ) {
1980
1981 $mtime_start = microtime( $get_float = true );
1982 $time_on_date = SucomUtil::sprintf_date_time( _x( '%2$s on %1$s', 'time on date', 'wpsso' ) );
1983 $notice_msg = sprintf( __( 'A task to %1$s from all users was started at %2$s.', 'wpsso' ), $role_label_transl, $time_on_date );
1984 $notice_key = $task_name . '-task-info';
1985
1986 $this->p->notice->inf( $notice_msg, $user_id, $notice_key );
1987 }
1988
1989 if ( 0 === get_current_user_id() ) { // User is the scheduler.
1990
1991 $this->p->util->cache->set_task_limit( $user_id, $task_name, WPSSO_REMOVE_ROLE_MAX_TIME );
1992 }
1993
1994 if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
1995
1996 /*
1997 * Register image sizes and include WooCommerce front-end libs.
1998 */
1999 do_action( 'wpsso_scheduled_task_started', $user_id );
2000 }
2001
2002 $count = 0;
2003
2004 $blog_id = get_current_blog_id();
2005
2006 while ( $users_ids = SucomUtilWP::get_users_ids( $blog_id, $role = '', $limit = 1000 ) ) { // Get a maximum of 1000 user ids at a time.
2007
2008 foreach ( $users_ids as $id ) {
2009
2010 $count += self::remove_role_by_id( $id, $role = 'person' );
2011 }
2012 }
2013
2014 unset( $users_ids );
2015
2016 if ( $user_id ) {
2017
2018 $mtime_total = microtime( $get_float = true ) - $mtime_start;
2019 $human_time = human_time_diff( 0, $mtime_total );
2020 $notice_msg = sprintf( __( 'The %1$s role has been removed from %2$d users.', 'wpsso' ), $role_label_transl, $count ) . ' ';
2021 $notice_msg .= sprintf( __( 'The total execution time for this task was %s.', 'wpsso' ), $human_time );
2022 $notice_key = $task_name . '-task-info';
2023
2024 $this->p->notice->inf( $notice_msg, $user_id, $notice_key );
2025 }
2026
2027 $this->p->util->cache->task_end( $user_id, $task_name );
2028 }
2029
2030 /*
2031 * Hooked to the 'wpmu_new_user' and 'user_register' actions, which only provides a single argument.
2032 */
2033 public static function add_role_by_id( $user_id, $role = 'person' ) {
2034
2035 $user_obj = get_user_by( 'ID', $user_id );
2036
2037 if ( ! in_array( $role, $user_obj->roles ) ) {
2038
2039 $user_obj->add_role( $role ); // Method does not return anything - assume it worked.
2040
2041 return 1;
2042 }
2043
2044 return 0;
2045 }
2046
2047 public static function remove_role_by_id( $user_id, $role = 'person' ) {
2048
2049 $user_obj = get_user_by( 'ID', $user_id );
2050
2051 if ( in_array( $role, $user_obj->roles ) ) {
2052
2053 $user_obj->remove_role( $role ); // Method does not return anything - assume it worked.
2054
2055 return 1;
2056 }
2057
2058 return 0;
2059 }
2060
2061 public static function get_persons_names( $add_none = true, $roles_id = 'person' ) {
2062
2063 $wpsso =& Wpsso::get_instance();
2064
2065 $users = array();
2066
2067 if ( isset( $wpsso->cf[ 'wp' ][ 'roles' ][ $roles_id ] ) ) { // Just in case.
2068
2069 $roles = $wpsso->cf[ 'wp' ][ 'roles' ][ $roles_id ];
2070 $users = SucomUtilWP::get_roles_users_select( $roles, $blog_id = null, $add_none );
2071 $users = array_slice( $users, 0, SucomUtil::get_const( 'WPSSO_SELECT_PERSON_NAMES_MAX', 200 ), $preserve_keys = true );
2072 }
2073
2074 return $users;
2075 }
2076
2077 public function add_person_view( $user_views ) {
2078
2079 $user_views = array_reverse( $user_views );
2080 $all_view_link = $user_views[ 'all' ];
2081
2082 unset( $user_views[ 'all' ], $user_views[ 'person' ] );
2083
2084 $role_label_transl = _x( 'Person', 'user role', 'wpsso' );
2085 $role_view = add_query_arg( 'role', 'person', admin_url( 'users.php' ) );
2086 $user_query = new WP_User_Query( array( 'role' => 'person' ) );
2087 $user_count = $user_query->get_total();
2088
2089 $user_views[ 'person' ] = '<a href="' . $role_view . '">' . $role_label_transl . '</a> (' . $user_count . ')';
2090 $user_views[ 'all' ] = $all_view_link;
2091
2092 $user_views = array_reverse( $user_views );
2093
2094 return $user_views;
2095 }
2096
2097 private function get_user_about_meta_keys( $about_key ) {
2098
2099 $meta_keys = array();
2100
2101 if ( 'award' === $about_key ) {
2102
2103 if ( $awards_max = SucomUtil::get_const( 'WPSSO_SCHEMA_AWARDS_MAX' ) ) {
2104
2105 foreach ( range( 0, $awards_max - 1, 1 ) as $key_num ) {
2106
2107 $meta_keys[] = $about_key . '_' . $key_num;
2108 }
2109 }
2110
2111 } else $meta_keys[] = $about_key;
2112
2113 return $meta_keys;
2114 }
2115
2116 /*
2117 * If $meta_key is en empty string, retrieves all metadata for the specified object ID.
2118 *
2119 * See https://developer.wordpress.org/reference/functions/get_metadata/.
2120 */
2121 public static function get_meta( $user_id, $meta_key = '', $single = false ) {
2122
2123 return get_metadata( 'user', $user_id, $meta_key, $single );
2124 }
2125
2126 public static function update_meta( $user_id, $meta_key, $value ) {
2127
2128 return update_metadata( 'user', $user_id, $meta_key, $value );
2129 }
2130
2131 public static function delete_meta( $user_id, $meta_key ) {
2132
2133 return delete_metadata( 'user', $user_id, $meta_key );
2134 }
2135 }
2136 }
2137