File: /home/u547966/brikov.ru/www/wp-content/plugins/groups/lib/access/class-groups-access-meta-boxes.php
<?php
/**
* class-groups-access-meta-boxes.php
*
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
*
* This code is released under the GNU General Public License.
* See COPYRIGHT.txt and LICENSE.txt.
*
* This code is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This header and all notices must be kept intact.
*
* @author Karim Rahimpur
* @package groups
* @since groups 1.0.0
*/
if ( !defined( 'ABSPATH' ) ) {
exit;
}
/**
* Adds meta boxes to edit screens.
*
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
*/
class Groups_Access_Meta_Boxes {
const CAPABILITY_NONCE = 'groups-meta-box-capability-nonce';
const SET_CAPABILITY = 'set-capability';
const READ_ACCESS = 'read-access';
const CAPABILITY = 'capability';
const SHOW_GROUPS = 'access-meta-box-show-groups';
const NONCE = 'groups-meta-box-nonce';
const SET_GROUPS = 'set-groups';
const GROUPS_READ = 'groups-read';
const READ = 'read';
/**
* Sets up an init hook where actions and filters are added.
*/
public static function init() {
add_action( 'init', array( __CLASS__, 'wp_init' ) );
add_action( 'admin_init', array(__CLASS__,'admin_init' ) );
}
/**
* Hooks for capabilities meta box and saving options.
*/
public static function wp_init() {
if ( Groups_User::current_user_can( GROUPS_ACCESS_GROUPS ) ) {
require_once GROUPS_VIEWS_LIB . '/class-groups-uie.php';
add_action( 'add_meta_boxes', array( __CLASS__, 'add_meta_boxes' ), 10, 2 );
add_action( 'save_post', array( __CLASS__, 'save_post' ), 10, 2 );
add_filter( 'wp_insert_post_empty_content', array( __CLASS__, 'wp_insert_post_empty_content' ), 10, 2 );
add_filter( 'attachment_fields_to_edit', array( __CLASS__, 'attachment_fields_to_edit' ), 10, 2 );
add_filter( 'attachment_fields_to_save', array( __CLASS__, 'attachment_fields_to_save' ), 10, 2 );
add_action( 'wp_enqueue_editor', array( __CLASS__, 'wp_enqueue_editor' ) ); // @since 2.7.2
}
}
/**
* Hooked on admin_init to register our action on admin_enqueue_scripts.
*/
public static function admin_init() {
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_enqueue_scripts' ) );
}
/**
* Hooked on admin_enqueue_scripts to timely enqueue resources required
* on the media upload / attachment popup.
*/
public static function admin_enqueue_scripts() {
global $pagenow;
if ( isset( $pagenow ) ) {
switch ( $pagenow ) {
case 'upload.php' :
case 'customize.php' :
case 'edit-tags.php' : // @since 2.7.1 [1]
case 'term.php' : // @since 2.7.1 [1]
Groups_UIE::enqueue( 'select' );
break;
}
}
// [1] For cases when attachments can be added to terms, e.g. WC Product Category Thumbnail.
}
/**
* Triggered by init() to add capability meta box.
*/
public static function add_meta_boxes( $post_type, $post = null ) {
global $wp_version;
$post_type_object = get_post_type_object( $post_type );
if ( $post_type_object && $post_type != 'attachment' ) {
$post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
if (
Groups_Post_Access::handles_post_type( $post_type ) &&
(
!isset( $post_types_option[$post_type]['add_meta_box'] ) ||
$post_types_option[$post_type]['add_meta_box']
)
) {
add_meta_box(
'groups-permissions',
_x( 'Groups', 'Meta box title', 'groups' ),
array( __CLASS__, 'groups' ),
null,
'side',
'high'
);
Groups_UIE::enqueue( 'select' );
if ( self::user_can_restrict() ) {
if ( $screen = get_current_screen() ) {
// help tab for group-based access restrictions
$screen->add_help_tab( array(
'id' => 'groups-groups',
'title' => _x( 'Groups', 'Help tab title', 'groups' ),
'content' =>
'<p>' .
'<strong>' . _x( 'Groups', 'Help heading', 'groups' ) . '</strong>' .
'</p>' .
'<p>' .
wp_kses(
__( 'Use the <em>Groups</em> box to limit the visibility of posts, pages and other post types.', 'groups' ),
array( 'em' => array() )
) .
'</p>' .
'<p>' .
esc_html__( 'You can select one or more groups to restrict access to its members.', 'groups' ) .
( !Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ?
' ' .
esc_html__( 'Note that you must be a member of a group to use it to restrict access.', 'groups' )
:
''
) .
'</p>' .
'<p>' .
'<strong>' . esc_html__( 'Example:', 'groups' ) . '</strong>' .
'</p>' .
wp_kses(
__( 'Let\'s assume that you want to limit the visibility of a post to members of the <em>Premium</em> group.', 'groups' ),
array( 'em' => array() )
) .
'<p>' .
' ' .
'</p>' .
wp_kses(
__( 'Choose or enter <em>Premium</em> in the <em>Read</em> field located in the <em>Groups</em> box and save or update the post (or hit Enter).', 'groups' ),
array( 'em' => array() )
) .
'<p>' .
( Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ?
'<p>' .
esc_html__( 'In the same field, you can create a new group and restrict access. Group names are case-sensitive. In order to be able to use the new group, your user account will be assigned to it.', 'groups' ) .
'</p>'
:
''
)
) );
}
}
}
}
}
/**
* Render meta box for groups.
*
* @see do_meta_boxes()
*
* @param Object $object
* @param Object $box
*/
public static function groups( $object = null, $box = null ) {
$output = '';
$post_id = isset( $object->ID ) ? $object->ID : null;
$post_type = isset( $object->post_type ) ? $object->post_type : null;
$post_singular_name = __( 'Post', 'groups' );
if ( $post_type !== null ) {
$post_type_object = get_post_type_object( $post_type );
$labels = isset( $post_type_object->labels ) ? $post_type_object->labels : null;
if ( $labels !== null ) {
if ( isset( $labels->singular_name ) ) {
$post_singular_name = $labels->singular_name; // // this is already translated
}
}
}
$output .= wp_nonce_field( self::SET_GROUPS, self::NONCE, true, false );
$output .= apply_filters( 'groups_access_meta_boxes_groups_before_read_groups', '', $object, $box );
$output .= '<div class="select-read-groups-container">';
if ( self::user_can_restrict() ) {
$include = self::get_user_can_restrict_group_ids();
$groups = Groups_Group::get_groups( apply_filters( 'groups_access_meta_boxes_groups_get_groups_options', array( 'order_by' => 'name', 'order' => 'ASC', 'include' => $include ), $object, $box ) );
$groups_read = Groups_Post_Access::get_read_group_ids( $post_id );
$read_help = sprintf(
/* translators: 1: post type name 2: post type name 3: post type name */
__( 'You can restrict the visibility of this %1$s to group members. Choose one or more groups that are allowed to read this %2$s. If no groups are chosen, the %3$s is visible to anyone.', 'groups' ),
$post_singular_name,
$post_singular_name,
$post_singular_name
);
if ( Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
$read_help .= ' ' . __( 'You can create a new group by indicating the group\'s name.', 'groups' );
}
$output .= sprintf(
'<label title="%s">',
esc_attr( $read_help )
);
$output .= esc_html__( 'Read', 'groups' );
$output .= ' ';
$output .= sprintf(
'<select class="select groups-read" name="%s" multiple="multiple" placeholder="%s" data-placeholder="%s" title="%s">',
self::GROUPS_READ . '[]',
esc_attr( __( 'Anyone …', 'groups' ) ),
esc_attr( __( 'Anyone …', 'groups' ) ),
esc_attr( $read_help )
);
$output .= '<option value=""></option>';
foreach( $groups as $group ) {
$output .= sprintf( '<option value="%s" %s>', esc_attr( $group->group_id ), in_array( $group->group_id, $groups_read ) ? ' selected="selected" ' : '' );
$output .= $group->name ? stripslashes( wp_filter_nohtml_kses( $group->name ) ) : '';
$output .= '</option>';
}
$output .= '</select>';
$output .= '</label>';
$output .= Groups_UIE::render_select(
'.select.groups-read',
true,
true,
Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS )
);
$output .= '<p class="description">';
$output .= sprintf(
/* translators: post type name */
esc_html__( 'Restricts the visibility of this %s to members of the chosen groups.', 'groups' ),
esc_html( $post_singular_name )
);
$output .= '</p>';
} else {
$output .= '<p class="description">';
$output .= esc_html__( 'You cannot set any access restrictions.', 'groups' );
$style = 'cursor:help;vertical-align:middle;';
if ( Groups_User::current_user_can( GROUPS_ADMINISTER_OPTIONS ) ) {
$style = 'cursor:pointer;vertical-align:middle;';
$output .= sprintf( '<a href="%s">', esc_url( admin_url( 'admin.php?page=groups-admin-options' ) ) );
}
$output .= sprintf( '<img style="%s" alt="?" title="%s" src="%s" />', $style, esc_attr( __( 'You need to have permission to set access restrictions.', 'groups' ) ), esc_attr( GROUPS_PLUGIN_URL . 'images/help.png' ) );
if ( Groups_User::current_user_can( GROUPS_ADMINISTER_OPTIONS ) ) {
$output .= '</a>';
}
$output .= '</p>';
}
$output .= '</div>'; // .select-read-groups-container
$output .= apply_filters( 'groups_access_meta_boxes_groups_after_read_groups', '', $object, $box );
$output = apply_filters( 'groups_access_meta_boxes_groups', $output, $object, $box );
echo $output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
* Invokes our save_post() if the post content is considered empty.
* This is required because even on an empty post, we want to allow to
* quick-create group and category as well as assign capabilities.
* At WordPress 3.6.1, this is the only way we can achieve that, because
* the save_post action is not invoked if the post content is considered
* empty.
*
* @param boolean $maybe_empty
* @param array $postarr
*
* @return boolean
*/
public static function wp_insert_post_empty_content( $maybe_empty, $postarr ) {
// Only consider invoking save_post() here, if the post content is
// considered to be empty at this stage. This is so we don't end up
// having save_post() invoked twice when the post is not empty.
if ( $maybe_empty ) {
$post_id = null;
if ( !empty( $postarr['ID'] ) ) {
$post_id = intval( $postarr['ID'] );
} else if ( !empty( $postarr['post_ID'] ) ) {
$post_id = intval( $postarr['post_ID'] );
}
if ( $post_id !== null ) {
self::save_post( $post_id );
}
}
return $maybe_empty;
}
/**
* Save the group access restriction.
*
* @param int $post_id
* @param mixed $post post data (not used here)
*/
public static function save_post( $post_id = null, $post = null ) {
// This is called multiple times and if a new post is created and a new group is requested*,
// we can end up without the new group being assigned to the post unless we duely check
// for revision and autosave:
// (* on the second call, the new group exists and it will bail out on "if ( !( $group = Groups_Group::read_by_name( $name ) ) ) { ...")
if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE || wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) ) {
} else {
$post_type = get_post_type( $post_id );
$post_type_object = get_post_type_object( $post_type );
if ( $post_type_object && $post_type != 'attachment' ) {
$post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
if ( self::user_can_restrict() ) {
if ( isset( $_POST[self::NONCE] ) && wp_verify_nonce( $_POST[self::NONCE], self::SET_GROUPS ) ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$post_type = isset( $_POST['post_type'] ) ? sanitize_text_field( $_POST['post_type'] ) : null;
if ( $post_type !== null ) {
// See http://codex.wordpress.org/Function_Reference/current_user_can 20130119 WP 3.5
// "... Some capability checks (like 'edit_post' or 'delete_page') require this [the post ID] be provided."
// If the post ID is not provided, it will throw:
// PHP Notice: Undefined offset: 0 in /var/www/groups-forums/wp-includes/capabilities.php on line 1067
$edit_post_type = 'edit_' . $post_type;
if ( $post_type_object = get_post_type_object( $post_type ) ) {
if ( !isset( $post_type_object->capabilities ) ) {
// get_post_type_capabilities() (WP 3.8) will throw a warning
// when trying to merge the missing property otherwise. It's either a
// bug or the function's documentation should make it clear that you
// have to provide that.
$post_type_object->capabilities = array(); // @phpstan-ignore property.notFound
}
$caps_object = get_post_type_capabilities( $post_type_object );
if ( isset( $caps_object->edit_post ) ) {
$edit_post_type = $caps_object->edit_post;
}
}
if ( Groups_User::current_user_can( $edit_post_type, $post_id ) ) {
$include = self::get_user_can_restrict_group_ids();
$groups = Groups_Group::get_groups( array( 'order_by' => 'name', 'order' => 'ASC', 'include' => $include ) );
$user_group_ids_deep = array();
foreach( $groups as $group ) {
$user_group_ids_deep[] = $group->group_id;
}
$group_ids = array();
$submitted_group_ids = !empty( $_POST[self::GROUPS_READ] ) && is_array( $_POST[self::GROUPS_READ] ) ? $_POST[self::GROUPS_READ] : array(); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
// assign requested groups and create and assign new groups if allowed
foreach( $submitted_group_ids as $group_id ) {
if ( is_numeric( $group_id ) ) {
if ( in_array( $group_id, $user_group_ids_deep ) ) {
$group_ids[] = $group_id;
}
} else {
if ( Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
$creator_id = get_current_user_id();
$datetime = date( 'Y-m-d H:i:s', time() ); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
$name = ucwords( strtolower( trim( preg_replace( '/\s+/', ' ', $group_id ) ) ) );
if ( strlen( $name ) > 0 ) {
if ( !( $group = Groups_Group::read_by_name( $name ) ) ) {
if ( $group_id = Groups_Group::create( compact( 'creator_id', 'datetime', 'name' ) ) ) {
if ( Groups_User_Group::create( array( 'user_id' => $creator_id, 'group_id' => $group_id ) ) ) {
$group_ids[] = $group_id;
}
}
}
}
}
}
}
do_action( 'groups_access_meta_boxes_before_groups_read_update', $post_id, $group_ids );
$update_result = Groups_Post_Access::update( array( 'post_id' => $post_id, 'groups_read' => $group_ids ) );
do_action( 'groups_access_meta_boxes_after_groups_read_update', $post_id, $group_ids, $update_result );
}
}
}
}
}
}
}
}
/**
* Enqueue scripts and styles.
*/
private static function enqueue() {
global $groups_version;
if ( !wp_script_is( 'selectize' ) ) {
wp_enqueue_script( 'selectize', GROUPS_PLUGIN_URL . 'js/selectize/selectize.min.js', array( 'jquery' ), $groups_version, false );
}
if ( !wp_style_is( 'selectize' ) ) {
wp_enqueue_style( 'selectize', GROUPS_PLUGIN_URL . 'css/selectize/selectize.bootstrap2.css', array(), $groups_version );
}
}
/**
* Render groups box for attachment post type (Media).
*
* @param array $form_fields
* @param object $post
*
* @return array
*/
public static function attachment_fields_to_edit( $form_fields, $post ) {
Groups_UIE::enqueue( 'select' );
$post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
if ( !isset( $post_types_option['attachment']['add_meta_box'] ) || $post_types_option['attachment']['add_meta_box'] ) {
if ( self::user_can_restrict() ) {
$include = self::get_user_can_restrict_group_ids();
$groups = Groups_Group::get_groups( array( 'order_by' => 'name', 'order' => 'ASC', 'include' => $include ) );
$groups_read = Groups_Post_Access::get_read_group_ids( $post->ID );
$output = '';
// $post_singular_name = __( 'Media', 'groups' );
$output .= esc_html__( 'Read', 'groups' );
// On attachments edited within the 'Insert Media' popup, the update is triggered too soon and we end up with only the last capability selected.
// This occurs when using normal checkboxes as well as the select below (Chosen and Selectize tested).
// With checkboxes it's even more confusing, it's actually better to have it using a select as below,
// because the visual feedback corresponds with what is assigned.
// See http://wordpress.org/support/topic/multiple-access-restrictions-for-media-items-are-not-saved-in-grid-view
// and https://core.trac.wordpress.org/ticket/28053 - this is an issue with multiple value fields and should
// be fixed within WordPress.
// $output .= '<div style="padding:0 1em;margin:1em 0;border:1px solid #ccc;border-radius:4px;">';
// $output .= '<ul>';
// foreach( $groups as $group ) {
// $checked = in_array( $group->group_id, $groups_read ) ? ' checked="checked" ' : '';
// $output .= '<li>';
// $output .= '<label>';
// $output .= '<input name="attachments[' . $post->ID . '][' . self::GROUPS_READ . '][]" ' . $checked . ' type="checkbox" value="' . esc_attr( $group->group_id ) . '" />';
// $output .= wp_filter_nohtml_kses( $group->name );
// $output .= '</label>';
// $output .= '</li>';
// }
// $output .= '</ul>';
// $output .= '</div>';
$output .= '<div class="select-groups-container">';
$select_id = 'attachments-' . $post->ID . '-' . self::GROUPS_READ;
$output .= sprintf(
'<select id="%s" class="select groups-read" name="%s" multiple="multiple" placeholder="%s" data-placeholder="%s" title="%s">',
esc_attr( $select_id ),
'attachments[' . esc_attr( $post->ID ) . '][' . esc_attr( self::GROUPS_READ ) . '][]',
esc_attr( __( 'Anyone …', 'groups' ) ),
esc_attr( __( 'Anyone …', 'groups' ) ),
esc_attr__( 'You can restrict the visibility to group members. Choose one or more groups to restrict access. If no groups are chosen, this entry is visible to anyone.', 'groups' ) .
Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ? ' ' . esc_attr__( 'You can create a new group by indicating the group\'s name.', 'groups' ) : ''
);
$output .= '<option value=""></option>';
foreach( $groups as $group ) {
$output .= sprintf( '<option value="%s" %s>', esc_attr( $group->group_id ), in_array( $group->group_id, $groups_read ) ? ' selected="selected" ' : '' );
$output .= $group->name ? stripslashes( wp_filter_nohtml_kses( $group->name ) ) : '';
$output .= '</option>';
}
$output .= '</select>';
$output .= Groups_UIE::render_select( '#'.$select_id, true, true, Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) );
$output .= '</div>';
$output .= '<p class="description">';
$output .= esc_html__( 'Restricts the visibility of this entry to members of the chosen groups.', 'groups' );
$output .= '</p>';
$output .= '<p class="description">';
$output .= esc_html__( 'The attachment page is restricted to authorized users, but due to technical limitations, the file can still be accessed directly via its URL.', 'groups' );
$output .= ' ';
$output .= sprintf(
/* translators: extension name */
esc_html__( 'Please use %s for files that require complete protection.', 'groups' ),
'<a href="https://www.itthinx.com/shop/groups-file-access/" target="_blank">Groups File Access</a>'
);
$output .= '</p>';
$form_fields['groups_read'] = array(
'label' => _x( 'Groups', 'Attachment field label', 'groups' ),
'input' => 'html',
'html' => $output
);
}
}
return $form_fields;
}
/**
* Save groups for attachment post type (Media).
* When multiple attachments are saved, this is called once for each.
*
* @param array $post post data
* @param array $attachment attachment field data
*
* @return array
*/
public static function attachment_fields_to_save( $post, $attachment ) {
$post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
if ( !isset( $post_types_option['attachment']['add_meta_box'] ) || $post_types_option['attachment']['add_meta_box'] ) {
// if we're here, we assume the user is allowed to edit attachments,
// but we still need to check if the user can restrict access
if ( self::user_can_restrict() ) {
$post_id = null;
if ( isset( $post['ID'] ) ) {
$post_id = $post['ID'];
} else if ( isset( $post['post_ID'] ) ) {
$post_id = $post['post_ID'];
}
if ( $post_id !== null ) {
$include = self::get_user_can_restrict_group_ids();
$groups = Groups_Group::get_groups( array( 'order_by' => 'name', 'order' => 'ASC', 'include' => $include ) );
$group_ids = array();
if ( !empty( $attachment[self::GROUPS_READ] ) && is_array( $attachment[self::GROUPS_READ] ) ) {
foreach( $groups as $group ) {
if ( in_array( $group->group_id, $attachment[self::GROUPS_READ] ) ) {
$group_ids[] = $group->group_id;
}
}
}
do_action( 'groups_access_meta_boxes_before_groups_read_update', $post_id, $group_ids );
$update_result = Groups_Post_Access::update( array( 'post_id' => $post_id, 'groups_read' => $group_ids ) );
do_action( 'groups_access_meta_boxes_after_groups_read_update', $post_id, $group_ids, $update_result );
}
}
}
return $post;
}
/**
* Hooked on wp_enqueue_editor to enqueue our UIE scripts if access for attachments is enabled.
*
* @param array $to_load
*/
public static function wp_enqueue_editor( $to_load ) {
$media_upload = wp_script_is( 'media-upload' );
if ( $media_upload ) {
$post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
if ( !isset( $post_types_option['attachment']['add_meta_box'] ) || $post_types_option['attachment']['add_meta_box'] ) {
Groups_UIE::enqueue( 'select' );
}
}
}
/**
* Returns true if the user can restrict access to posts. The current user is
* assumed by default unless a user ID is provided.
*
* @param int $user_id indicates the desired user, otherwise for the current user
*
* @return boolean
*/
public static function user_can_restrict( $user_id = null ) {
if ( $user_id === null ) {
$user_id = get_current_user_id();
}
$user = new Groups_User( $user_id);
return $user->can( GROUPS_RESTRICT_ACCESS );
}
/**
* Returns the group IDs of the groups that the user can use to restrict access.
*
* If the user can GROUPS_RESTRICT_ACCESS, the following group IDs are returned:
* - If the user can GROUPS_ADMINISTER_GROUPS, this will return the IDs of all groups.
* - Otherwise it will return the IDs of all groups that the user belongs to, directly
* or indirectly by group inheritance.
*
* If the user can not GROUPS_RESTRICT_ACCESS, an empty array is returned.
*
* @param int $user_id if provided, retrieve results for the user indicated by user ID, otherwise for the current user
*
* @return array of int with the group IDs
*/
public static function get_user_can_restrict_group_ids( $user_id = null ) {
$group_ids = array();
if ( $user_id === null ) {
$user_id = get_current_user_id();
}
if ( self::user_can_restrict( $user_id ) ) {
if ( Groups_User::current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
$group_ids = Groups_Group::get_group_ids();
} else {
$user = new Groups_User( $user_id );
$group_ids = $user->get_group_ids_deep();
}
if ( !empty( $group_ids ) && is_array( $group_ids ) ) {
$group_ids = array_map (array( 'Groups_Utility','id'), $group_ids );
}
}
return apply_filters( 'groups_access_meta_boxes_user_can_restrict_group_ids', $group_ids, $user_id );
}
/**
* @deprecated
*
* @return array of valid read capabilities for the current or given user
*/
public static function get_valid_read_caps_for_user( $user_id = null ) {
require_once GROUPS_LEGACY_LIB . '/access/class-groups-access-meta-boxes-legacy.php';
return Groups_Access_Meta_Boxes_Legacy::get_valid_read_caps_for_user( $user_id );
}
}
Groups_Access_Meta_Boxes::init();