Created
July 9, 2018 20:38
-
-
Save GaryJones/d1f138638b1832c0d4e708bcbd27a021 to your computer and use it in GitHub Desktop.
WordPress plugin to store custom and built-in user fields on a per-site basis for WP multisite. Good for multilingual setups. Customise to your needs.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Per-Site User Fields | |
* | |
* @package GaryJones\PerSiteUserfields | |
* @author Gary Jones | |
* @copyright 2018 Gary Jones, Gamajo | |
* @license GPL-2.0-or-later | |
* | |
* @wordpress-plugin | |
* Plugin Name: Per-Site User Fields | |
* Description: Handle user fields to work on a per-site basis. | |
* Version: 1.0.0 | |
* Author: Gary Jones, Giuseppe Mazzapica, and Sami Keijonen | |
* Author URI: https://garyjones.io | |
* Text Domain: per-site-user-fields | |
* License: GPL-2.0-or-later | |
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt | |
* Requires PHP: 7.1 | |
* Requires WP: 4.7 | |
*/ | |
declare( strict_types = 1 ); | |
namespace GaryJones\PerSiteUserFields; | |
// Exit if accessed directly | |
if ( ! \defined( 'ABSPATH' ) ) { | |
exit; | |
} | |
// 0. Define which fields we're targetting. Anything not already being sanitized elsewhere can have a callback here. | |
function per_site_user_settings() { | |
return [ | |
'job_title' => [ // Custom added by this plugin. | |
'sanitization' => function( $value ) { | |
return sanitize_text_field( $value ); | |
}, | |
], | |
'description' => [], // Built-in field (bio). | |
'intro_text' => [], // Added by Genesis. | |
]; | |
} | |
// 1. First, handle the custom field[s]. These are saved and retrieved as global user settings (user_meta). | |
\add_action( 'show_user_profile', __NAMESPACE__ . '\\add_user_field' ); | |
\add_action( 'edit_user_profile', __NAMESPACE__ . '\\add_user_field' ); | |
/** | |
* Add field to user profile. | |
* | |
* For this example, its just one field - job title, which we'll use on an author byline via code in our theme. | |
* | |
* @since 1.0.0 | |
*/ | |
function add_user_field( $user ) { | |
$job_title_field = 'job_title'; | |
$job_title = \get_user_meta( $user->ID, $job_title_field, true ); | |
?> | |
<h3><?php \esc_html_e( 'Bylines', 'per-site-user-fields' ); ?></h3> | |
<table class="form-table"> | |
<tbody> | |
<tr> | |
<th scope="row"><label for="<?php echo esc_attr( $job_title_field ); ?>"><?php \esc_html_e( 'Job Title', 'per-site-user-fields' ); ?></label></th> | |
<td> | |
<input type="text" name="<?php echo esc_attr( $job_title_field ); ?>" id="<?php echo \esc_attr( $job_title_field ); ?>" value="<?php if ( ! empty( $job_title ) ) esc_attr_e( $job_title ); ?>" class="medium-text" /> | |
</td> | |
</tr> | |
<tbody> | |
</table> | |
<?php | |
} | |
\add_action( 'personal_options_update', __NAMESPACE__ . '\\save_user_field' ); | |
\add_action( 'edit_user_profile_update', __NAMESPACE__ . '\\save_user_field' ); | |
/** | |
* Save field for user profile. | |
* | |
* Even though we're going to change _how_ we save the data, we still need to kickstart the saving process, | |
* so WP is aware of the field. | |
* | |
* @since 1.0.0 | |
*/ | |
function save_user_field( $user_id ) { | |
// Bail if we don't have permission to edit user. | |
if ( ! \current_user_can( 'edit_user', $user_id ) ) { | |
return false; | |
} | |
foreach ( per_site_user_settings() as $setting => $config ) { | |
if ( array_key_exists( 'sanitization', $config ) ) { // Is a custom field. | |
$value = isset( $_POST[ $setting ] ) ? call_user_func( $config['sanitization'], $_POST[ $setting ] ) : null; | |
} else { | |
// Default to text field sanitization. | |
$value = isset( $_POST[ $setting ] ) ? \sanitize_text_field( $_POST[ $setting ] ) : null; | |
} | |
// Save value as user meta - this makes it a standard global setting, | |
// but we overwrite this later to be per-site. | |
\update_user_meta( $user_id, $setting, $value ); | |
} | |
} | |
// 2. Now adjust the targetted built-in and custom fields so they are handled on a per-site basis. | |
\add_filter( | |
'update_user_metadata', // update_{$meta_type}_metadata hook. | |
function ( $check, $user_id, $meta_key, $meta_value ) { | |
if ( should_intercept_user_meta( $meta_key ) ) { | |
// Remove site-specific value if it's empty, so that it falls | |
// back to network value. | |
if ( null === $meta_value || '' === $meta_value ) { | |
\delete_user_option( $user_id, $meta_key ); | |
return true; | |
} | |
\update_user_option( $user_id, $meta_key, $meta_value ); | |
// Return a non-null value, so that the rest of update_user_meta() | |
// does NOT run, and the unprefixed meta key doesn't get updated as well. | |
return true; | |
} | |
// Effectively return null, so that non-primary sites, or other fields | |
// we're not interested in, will fully run update_user_meta(). | |
return $check; | |
}, | |
10, | |
4 | |
); | |
\add_filter( 'get_user_metadata', __NAMESPACE__ . '\\get_user_metadata', 10, 3 ); // get_{$meta_type}_metadata hook. | |
function get_user_metadata( $return, $user, $meta_key ) { | |
if ( should_intercept_user_meta( $meta_key ) ) { | |
// get_user_option() can call get_user_meta(), which would call this filter callback recursively, | |
// so temporarily disable this filter, then add it back afterwards. | |
\remove_filter( 'get_user_metadata', __NAMESPACE__ . '\\get_user_metadata', 10 ); | |
$return = \get_user_option( $meta_key, $user ) ?: ''; | |
\add_filter( 'get_user_metadata', __NAMESPACE__ . '\\get_user_metadata', 10, 3 ); | |
} | |
return $return; | |
} | |
function should_intercept_user_meta( $meta_key ) { | |
return \in_array( $meta_key, per_site_user_settings(), true ) | |
&& ! \is_network_admin() | |
&& \get_current_blog_id() !== \get_main_site_id(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment