function scoper_filter_terms_for_status($taxonomy, $selected_terms, &$user_terms, $args = array())
{
    if (defined('DISABLE_QUERYFILTERS_RS') || defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
        return $selected_terms;
    }
    global $scoper;
    $defaults = array('object_id' => 0, 'object_type' => '', 'status' => '');
    $args = array_merge($defaults, $args);
    extract($args);
    if (!($tx = $scoper->taxonomies->get($taxonomy))) {
        return $selected_terms;
    }
    if (!($src = $scoper->data_sources->get($tx->object_source))) {
        return $selected_terms;
    }
    if (!isset($src->statuses) || count($src->statuses) < 2) {
        return $selected_terms;
    }
    if (!$object_id) {
        $object_id = scoper_get_object_id($src->name);
    }
    if (!$status) {
        // determine current post status
        if (defined('XMLRPC_REQUEST') && !empty($GLOBALS['scoper_xmlrpc_post_status'])) {
            $status = $GLOBALS['scoper_xmlrpc_post_status'];
        } else {
            if (!($status = $scoper->data_sources->get_from_http_post('status', $src))) {
                if ($object_id) {
                    $status = $scoper->data_sources->get_from_db('status', $src, $object_id);
                }
            }
        }
    }
    if (!$object_type) {
        if (!($object_type = cr_find_object_type($src->name, $object_id))) {
            if (defined('XMLRPC_REQUEST')) {
                $object_type = 'post';
            } else {
                return $selected_terms;
            }
        }
    }
    if ('auto-draft' == $status) {
        $status = 'draft';
    }
    // make sure _others caps are required only for objects current user doesn't own
    $base_caps_only = true;
    if ($object_id && !empty($src->cols->owner)) {
        $col_owner = $src->cols->owner;
        if ($object = $scoper->data_sources->get_object($src->name, $object_id)) {
            if (!empty($object->{$col_owner}) && $object->{$col_owner} != $GLOBALS['current_user']->ID) {
                $base_caps_only = false;
            }
        }
    }
    if (!($reqd_caps = cr_get_reqd_caps($src->name, OP_EDIT_RS, $object_type, $status, $base_caps_only))) {
        return $selected_terms;
    }
    $qualifying_roles = $scoper->role_defs->qualify_roles($reqd_caps);
    if ($qualifying_term_assigner_roles = $scoper->role_defs->qualify_roles(array("assign_{$taxonomy}"))) {
        $qualifying_roles = array_merge($qualifying_roles, $qualifying_term_assigner_roles);
    }
    $user_terms = $scoper->qualify_terms_daterange($reqd_caps, $taxonomy, $qualifying_roles);
    foreach (array_keys($user_terms) as $date_key) {
        $date_clause = '';
        if ($date_key && is_serialized($date_key)) {
            // Check stored post date against any role date limits associated whith this set of terms (if not stored, check current date)
            $content_date_limits = unserialize($date_key);
            $post_date_gmt = $object_id ? $scoper->data_sources->get_from_db('date', $src, $object_id) : 0;
            if (!$post_date_gmt) {
                $post_date_gmt = agp_time_gmt();
            }
            if ($post_date_gmt < $content_date_limits->content_min_date_gmt || $post_date_gmt > $content_date_limits->content_max_date_gmt) {
                unset($user_terms[$date_key]);
            }
        }
    }
    $user_terms = agp_array_flatten($user_terms);
    $selected_terms = array_intersect($selected_terms, $user_terms);
    return $selected_terms;
}
 function ui_object_roles($src_name, $args = array())
 {
     $defaults = array('object_type' => '');
     $args = array_merge($defaults, (array) $args);
     extract($args);
     if (!($src = $this->scoper->data_sources->get($src_name))) {
         return;
     }
     if (!$object_type) {
         if (!($object_type = cr_find_object_type($src_name))) {
             return;
         }
     }
     $object_id = scoper_get_object_id($src_name, $object_type);
     if (!$this->scoper_admin->user_can_admin_object($src_name, $object_type, $object_id)) {
         return;
     }
     $this->init_item_roles_ui();
     $this->item_roles_ui->single_object_roles_ui($src_name, $object_type, $object_id);
 }
 function users_queryroles($reqd_caps, $src_name, $object_id = '', $args = array())
 {
     $defaults = array('roles' => '', 'user' => '', 'querying_groups' => 0, 'use_term_roles' => 1, 'use_blog_roles' => 1, 'skip_object_roles' => false, 'ignore_strict_terms' => 0, 'object_terms' => array(), 'object_type' => '', 'objscope_roles' => '', 'any_object' => false);
     $args = array_merge($defaults, (array) $args);
     extract($args);
     $src = $this->scoper->data_sources->get($src_name);
     // ---- The following default argument generation is included to support potential direct usage of this function
     //								(not needed for flt_users_where call -----------------
     // Treat empty reqd_caps array as an error
     if (empty($reqd_caps)) {
         return array();
     }
     $reqd_caps = (array) $reqd_caps;
     // Calling function may save us a little work if it has already made this call
     if (!$roles) {
         if (!($roles = $this->scoper->role_defs->qualify_roles($reqd_caps))) {
             return array();
         }
     } else {
         $roles = (array) $roles;
     }
     // this set of reqd_caps cannot be satisfied by any role
     if (!$reqd_caps && !$roles) {
         return;
     }
     if ($object_id && !$src_name) {
         $object_id = 0;
     }
     // -----------------------------------------------------------------------------------
     // Default to not honoring custom user caps, but support option
     $custom_user_blogcaps = SCOPER_CUSTOM_USER_BLOGCAPS;
     if (!$object_type) {
         if ($object_types = $this->scoper->cap_defs->object_types_from_caps($reqd_caps, $src_name)) {
             if (count($object_types) == 1) {
                 $object_type = reset($object_types);
             }
         }
         if (!$object_type) {
             $object_type = cr_find_object_type($src_name, $object_id);
         }
     }
     // RS roles are object type-specific
     $roles_wp = $this->scoper->role_defs->filter($roles, array('role_type' => 'wp'));
     $roles_rs = $this->scoper->role_defs->filter($roles, array('role_type' => 'rs'));
     $this_otype_roles = $this->scoper->role_defs->get_matching('rs', $src_name, $object_type);
     $roles_rs = array_intersect_key($roles_rs, $this_otype_roles);
     $roles = array_merge($roles_rs, $roles_wp);
     $qualifying_roles = array();
     // --------- ACCOUNT FOR OBJECT ROLES -----------
     // If this set of reqd_caps can be satisfied by a scopable role, check for object role assignements
     if (!$skip_object_roles && ($object_id || $any_object)) {
         // exclude roles which have never been assigned to any object
         if ($object_roles = $this->scoper->qualify_object_roles($reqd_caps, $object_type, -1)) {
             $qualifying_roles[OBJECT_SCOPE_RS][''] = scoper_role_handles_to_names(array_keys($roles));
         }
     }
     // If this inquiry is for a particular object, find out which roles must be object-assigned for it
     if ($object_id) {
         // For term and blog role clauses, exclude roles which require object assignment for that object
         // But don't disqualify a role if any of the roles it "contains" also qualify and are not object-scoped.
         // (i.e. If the required caps are satisfied by admin, editor and contributor, the actual minimum requirement
         // is contributor.  A specification that admin and editor roles "require object assignment" does not apply
         // in this scenario.
         if (!is_array($objscope_roles)) {
             $objscope_roles = $this->get_objscope_roles($src_name, $object_id, '', true);
         }
         if ($objscope_roles) {
             $contained_roles = array();
             $roles_wp = $this->scoper->role_defs->filter($roles, array('role_type' => 'wp'));
             foreach (array_keys($roles_wp) as $role_handle) {
                 // If scoping with RS roles, this will also have the effect of disqualifying a WP blog role if all of the qualifying RS roles it contains are objscoped.
                 $contained_roles[$role_handle] = $this->scoper->role_defs->get_contained_roles($role_handle, false, 'rs');
                 $contained_roles[$role_handle] = array_intersect_key($contained_roles[$role_handle], $roles);
                 if (!array_diff_key($contained_roles[$role_handle], $objscope_roles)) {
                     unset($roles[$role_handle]);
                 }
             }
             foreach (array_keys($roles) as $role_handle) {
                 $contained_roles[$role_handle] = $this->scoper->role_defs->get_contained_roles($role_handle, true, 'rs');
                 //true: include this role in return array
                 $contained_roles[$role_handle] = array_intersect_key($contained_roles[$role_handle], $roles);
                 if (!array_diff_key($contained_roles[$role_handle], $objscope_roles)) {
                     unset($roles[$role_handle]);
                 }
             }
         }
     }
     // --------- ACCOUNT FOR TERM ROLES -----------
     // Consider term scope settings and role assignments
     //
     $uses_taxonomies = scoper_get_taxonomy_usage($src_name, $object_type);
     if ($use_term_roles && $src_name && $roles && !empty($uses_taxonomies)) {
         // If scoping with RS roles, strip out WP role definitions (which were included for blogrole clause)
         $term_roles = $this->scoper->role_defs->filter($roles, array('role_type' => 'rs'));
         if ($term_roles) {
             foreach ($uses_taxonomies as $taxonomy) {
                 // include users with a sufficient term role assignment in any term
                 $qualifying_roles[TERM_SCOPE_RS][$taxonomy] = scoper_role_handles_to_names(array_keys($term_roles));
             }
         }
         // Honor blog-wide assignment of any non-objscope role, but only if at least one term
         // is not "strict" (i.e. merges blogroles into term-specific assignments).
         if (!$ignore_strict_terms) {
             $term_roles = $this->get_unrestricted_term_roles($term_roles, $uses_taxonomies, $object_id, $object_terms);
             // disqualify a WP blog role if all of the qualifying RS roles it contains were excluded by the strict terms filter.
             if ($roles_wp = $this->scoper->role_defs->filter($roles, array('role_type' => 'wp'))) {
                 $contained_roles = array();
                 foreach (array_keys($roles_wp) as $role_handle) {
                     $contained_roles[$role_handle] = $this->scoper->role_defs->get_contained_roles($role_handle, false, 'rs');
                     $contained_roles[$role_handle] = array_intersect_key($contained_roles[$role_handle], $roles);
                     if (!$term_roles || !$contained_roles[$role_handle] || !array_intersect_key($contained_roles[$role_handle], $term_roles)) {
                         unset($roles[$role_handle]);
                     }
                 }
             }
             $roles_current = $this->scoper->role_defs->filter($roles, array('role_type' => 'rs'));
             foreach (array_keys($roles_current) as $role_handle) {
                 if (!isset($term_roles[$role_handle])) {
                     unset($roles[$role_handle]);
                 }
             }
             // Since this term role is restricted for all terms, prevent corresponding blog role from being added to qualifying_roles array by subsequent code
         }
     }
     // --------- ACCOUNT FOR BLOG ROLES -----------
     // For each qualifying role, recognize blog assignment if the reqd_caps set is not associated
     // with a defined data source, if this source/object type does not use term roles,
     // or if some of the the terms are not strict.
     //
     // Note that WP blogrole assignments (if not taxonomy or object-scoped) are honored
     // regardless of Role Scoper role_type setting.
     if ($use_blog_roles) {
         if ($admin_roles = awp_administrator_roles()) {
             $roles = $roles ? array_merge($roles, $admin_roles) : $admin_roles;
         }
         if ($roles) {
             $role_types = array('rs', 'wp');
             foreach ($role_types as $role_type) {
                 //if ( ('rs' == $role_type) && ! RS_BLOG_ROLES )  // rs_blog_roles option has never been active in any RS release; leave commented here in case need arises
                 //		continue;
                 $this_type_roles = $this->scoper->role_defs->filter($roles, array('role_type' => $role_type));
                 $qualifying_roles[BLOG_SCOPE_RS][$role_type] = scoper_role_handles_to_names(array_keys($this_type_roles));
             }
         }
         if ($custom_user_blogcaps && $use_blog_roles) {
             // If custom user blogcaps option is enabled, this function is called separately for each reqd cap.
             // Custom user caps are stored as "hidden" single-cap role of type WP_CAP, sync'd to WP usermeta storage.
             if ($custom_user_blogcaps) {
                 $qualifying_roles[BLOG_SCOPE_RS]['wp_cap'] = $reqd_caps;
             }
             // ...which contains one cap
         }
     }
     return $qualifying_roles;
 }
 function mnt_create_object($src_name, $args, $object_id, $object = '')
 {
     $defaults = array('object_type' => '');
     $args = array_intersect_key($defaults, (array) $args);
     extract($args);
     static $inserted_objects;
     if (!isset($inserted_objects)) {
         $inserted_objects = array();
     }
     // so this filter doesn't get called by hook AND internally
     if (isset($inserted_objects[$src_name][$object_id])) {
         return;
     }
     if (empty($object_type)) {
         if ($col_type = $GLOBALS['scoper']->data_sources->member_property($src_name, 'cols', 'type')) {
             $object_type = isset($object->{$col_type}) ? $object->{$col_type} : '';
         }
     }
     if (empty($object_type)) {
         if (!isset($object)) {
             $object = '';
         }
         $object_type = cr_find_object_type($src_name, $object_id, $object);
     }
     if ($object_type == 'revision') {
         return;
     }
     $inserted_objects[$src_name][$object_id] = 1;
     if ('post' == $src_name) {
         $post_type_obj = get_post_type_object($object_type);
         if ($post_type_obj->hierarchical) {
             scoper_flush_cache_groups('get_pages');
         }
     }
 }
function scoper_get_parent_roles($obj_or_term_id, $scope, $src_or_tx_name, $parent_id, $object_type = '')
{
    global $wpdb, $scoper;
    $role_clause = '';
    if (!$parent_id && OBJECT_SCOPE_RS == $scope) {
        // for default roles, need to distinguish between otype-specific roles
        // (note: this only works w/ RS role type. Default object roles are disabled for WP role type because we'd be stuck assigning all default roles to both post & page.)
        $src = $scoper->data_sources->get($src_or_tx_name);
        if (!empty($src->cols->type)) {
            if (!$object_type) {
                $object_type = cr_find_object_type($src_name, $object_id);
            }
            if ($object_type) {
                $role_defs = $scoper->role_defs->get_matching('rs', $src_or_tx_name, $object_type);
                if ($role_names = scoper_role_handles_to_names(array_keys($role_defs))) {
                    $role_clause = "AND role_type = 'rs' AND role_name IN ('" . implode("', '", $role_names) . "')";
                }
            }
        }
    }
    // Since this is a new object, propagate roles from parent (if any are marked for propagation)
    $qry = "SELECT * FROM {$wpdb->user2role2object_rs} WHERE scope = %s AND assign_for IN ('children', 'both') {$role_clause} AND src_or_tx_name = %s AND obj_or_term_id = %d ORDER BY role_type, role_name";
    $results = scoper_get_results($wpdb->prepare($qry, $scope, $src_or_tx_name, $parent_id));
    return $results;
}
 function _flt_user_has_cap($wp_blogcaps, $orig_reqd_caps, $args)
 {
     // =============================== STATIC VARIABLE DECLARATION AND INITIALIZATION (to memcache filtering results) =====
     static $cache_tested_ids;
     static $cache_okay_ids;
     static $cache_where_clause;
     if (empty($cache_tested_ids)) {
         $cache_where_clause = array();
         $cache_tested_ids = array();
         $cache_okay_ids = array();
     }
     // ====================================================================================================================
     // =============================================== TEMPORARY DEBUG CODE ================================================
     //dump($orig_reqd_caps);
     //dump($args);
     //if ( strpos( $_SERVER['REQUEST_URI'], 'ajax' ) ) {
     //if ( ! empty($_REQUEST) )
     //	rs_errlog( serialize($_REQUEST) );
     //rs_errlog( '' );
     //rs_errlog('flt_user_has_cap');
     //rs_errlog(serialize($orig_reqd_caps));
     //rs_errlog(serialize($args));
     //rs_errlog('');
     //}
     // ============================================= (end temporary debug code) ==============================================
     // convert 'rs_role_name' to corresponding caps (and also make a tinkerable copy of orig_reqd_caps)
     $orig_reqd_caps = $this->scoper->role_defs->role_handles_to_caps($orig_reqd_caps);
     // ================= EARLY EXIT CHECKS (if the provided reqd_caps do not need filtering or need special case filtering ==================
     global $pagenow;
     // Disregard caps which are not defined in Role Scoper config
     if (!($rs_reqd_caps = array_intersect($orig_reqd_caps, $this->scoper->cap_defs->get_all_keys()))) {
         return $wp_blogcaps;
     }
     // log initial set of RS-filtered caps (in case we swap in equivalent caps for intermediate processing)
     $orig_reqd_caps = $rs_reqd_caps;
     // permitting this filter to execute early in an attachment request resets the found_posts record, preventing display in the template
     if (is_attachment() && !is_admin() && !did_action('template_redirect')) {
         if (empty($GLOBALS['scoper_checking_attachment_access'])) {
             return $wp_blogcaps;
         }
     }
     // work around bug in mw_EditPost method (requires publish_pages AND publish_posts cap)
     if (defined('XMLRPC_REQUEST') && 'publish_posts' == $orig_reqd_caps[0]) {
         if (!empty($GLOBALS['xmlrpc_post_type_rs']) && 'page' == $GLOBALS['xmlrpc_post_type_rs']) {
             return array('publish_posts' => true);
         }
     }
     // backdoor to deal with rare cases where one of the caps included in RS role defs cannot be filtered properly
     if (defined('UNSCOPED_CAPS_RS') && !array_diff($orig_reqd_caps, explode(',', UNSCOPED_CAPS_RS))) {
         return $wp_blogcaps;
     }
     // custom workaround to reveal all private / restricted content in all blogs if logged into main blog
     if (defined('SCOPER_MU_MAIN_BLOG_RULES')) {
         include_once dirname(__FILE__) . '/mu-custom.php';
         if (!array_diff($orig_reqd_caps, array('read', 'read_private_pages', 'read_private_posts'))) {
             if ($return_caps = ScoperMU_Custom::current_user_logged_into_main($wp_blogcaps, $orig_reqd_caps)) {
                 return $return_caps;
             }
         }
     }
     //define( 'SCOPER_NO_COMMENT_FILTERING', true );
     if (defined('SCOPER_NO_COMMENT_FILTERING') && 'moderate_comments' == $orig_reqd_caps[0] && empty($GLOBALS['current_rs_user']->allcaps['moderate_comments'])) {
         return $wp_blogcaps;
     }
     if (defined('SCOPER_ALL_UPLOADS_EDITABLE') && $pagenow == 'upload.php' && in_array($orig_reqd_caps[0], array('upload_files', 'edit_others_posts', 'delete_others_posts'))) {
         return $wp_blogcaps;
     }
     // =================================================== (end early exit checks) ======================================================
     // ============================ GLOBAL VARIABLE DECLARATIONS, ARGUMENT TRANSLATION AND STATUS DETECTION =============================
     global $current_rs_user;
     $user_id = isset($args[1]) ? $args[1] : 0;
     if ($user_id && $user_id != $current_rs_user->ID) {
         $user = rs_get_user($user_id);
     } else {
         $user = $current_rs_user;
     }
     // currently needed for filtering async-upload.php
     if (empty($user->blog_roles) || empty($user->blog_roles[''])) {
         $this->scoper->refresh_blogroles();
     }
     $object_id = isset($args[2]) ? (int) $args[2] : 0;
     // WP passes comment ID with 'edit_comment' metacap
     if ($object_id && 'edit_comment' == $args[0]) {
         if (!in_array('moderate_comments', $rs_reqd_caps)) {
             // as of WP 3.2.1, 'edit_comment' maps to related post's 'edit_post' caps without requiring moderate_comments
             if (scoper_get_option('require_moderate_comments_cap')) {
                 $rs_reqd_caps[] = 'moderate_comments';
                 $modified_caps = true;
             }
         }
         if ($comment = get_comment($object_id)) {
             $object_id = $comment->comment_post_ID;
         } else {
             $object_id = 0;
         }
     }
     // note the data source and object type(s) which are associated with the required caps (based on inclusion in RS Role Definitions)
     $is_taxonomy_cap = false;
     $src_name = '';
     $cap_types = $this->scoper->cap_defs->src_otypes_from_caps($rs_reqd_caps, $src_name);
     // note: currently only needed for src_name determination
     $doing_admin_menus = is_admin() && (did_action('_admin_menu') && !did_action('admin_menu') || did_action('admin_head') && !did_action('adminmenu'));
     // for scoped menu management roles, satisfy edit_theme_options cap requirement
     if (array_key_exists(0, $orig_reqd_caps) && 'edit_theme_options' == $orig_reqd_caps[0] && empty($wp_blogcaps['edit_theme_options'])) {
         if (in_array($GLOBALS['pagenow'], array('nav-menus.php', 'admin-ajax.php')) || $doing_admin_menus) {
             $key = array_search('edit_theme_options', $rs_reqd_caps);
             if (false !== $key) {
                 $tx = get_taxonomy('nav_menu');
                 $rs_reqd_caps[$key] = $tx->cap->manage_terms;
                 $src_name = 'nav_menu';
                 // menu-specific manager assignment does not permit deletion of the menu
                 if (!empty($_REQUEST['action']) && 'delete' == $_REQUEST['action']) {
                     $this->skip_any_term_check = true;
                 }
             }
         }
     }
     if (!$src_name) {
         // required capabilities correspond to multiple data sources
         return $wp_blogcaps;
     }
     // slight simplification: assume a single cap object type for a few cap substitution checks
     $is_taxonomy_cap = $this->scoper->cap_defs->member_property(reset($rs_reqd_caps), 'is_taxonomy_cap');
     // Establish some context by detecting object type - based on object ID if provided, or otherwise based on http variables.
     if (in_array($pagenow, array('media-upload.php', 'async-upload.php'))) {
         if (!empty($GLOBALS['post'])) {
             $object_type = $GLOBALS['post']->post_type;
         }
     } elseif (is_admin() && 'edit-tags.php' == $GLOBALS['pagenow'] && 'link_category' == $_REQUEST['taxonomy']) {
         $src_name = 'link';
         $object_type = 'link_category';
     } elseif (array_key_exists(0, $orig_reqd_caps) && in_array($orig_reqd_caps[0], array('manage_nav_menus', 'edit_theme_options'))) {
         $src_name = 'nav_menu';
     }
     if (empty($object_type)) {
         $object_type = cr_find_object_type($src_name, $object_id);
     }
     $object_type_obj = cr_get_type_object($src_name, $object_type);
     $is_att_rev = false;
     if ('post' == $src_name) {
         if (in_array($object_type, array('attachment', 'revision'))) {
             $is_att_rev = true;
             if ($object_id) {
                 if ($_post = get_post($object_id)) {
                     if ($_parent = get_post($_post->post_parent)) {
                         $object_type = $_parent->post_type;
                         $object_id = $_parent->ID;
                         // deal with case of edit_posts cap check on attachments to revision (with Revisionary)
                         if ('revision' == $object_type) {
                             if ($_orig_post = get_post($_parent->post_parent)) {
                                 $object_type = $_orig_post->post_type;
                                 $object_id = $_orig_post->ID;
                             }
                         }
                         $object_type_obj = get_post_type_object($object_type);
                     }
                 }
             }
         } elseif (!$is_taxonomy_cap) {
             $use_post_types = scoper_get_option('use_post_types');
             if (empty($use_post_types[$object_type])) {
                 return $wp_blogcaps;
             }
         }
     }
     // =====================================================================================================================================
     // ======================================== SUBVERT MISGUIDED CAPABILITY REQUIREMENTS ==================================================
     if ('post' == $src_name) {
         if (!$is_taxonomy_cap) {
             $modified_caps = false;
             if ('post' != $object_type) {
                 $replace_post_caps = array('publish_posts', 'edit_others_posts', 'edit_published_posts');
                 // Replace edit_posts requirement with corresponding type-specific requirement, but only after admin menu is drawn, or on a submission before the menu is drawn
                 if (did_action('admin_init')) {
                     // otherwise extra padding between menu items due to some items populated but unpermitted
                     $replace_post_caps[] = 'edit_posts';
                 }
                 if (in_array($pagenow, array('upload.php', 'media.php'))) {
                     $replace_post_caps = array_merge($replace_post_caps, array('delete_posts', 'delete_others_posts'));
                 }
                 foreach ($replace_post_caps as $post_cap_name) {
                     $key = array_search($post_cap_name, $rs_reqd_caps);
                     if (false !== $key && !$doing_admin_menus && in_array($pagenow, array('edit.php', 'post.php', 'post-new.php', 'press-this.php', 'admin-ajax.php', 'upload.php', 'media.php'))) {
                         $rs_reqd_caps[$key] = $object_type_obj->cap->{$post_cap_name};
                         $modified_caps = true;
                     }
                 }
             }
             // WP core quirk workaround: edit_others_posts is required as preliminary check for populating authors dropdown for any post type.  Instead, we need to do our own validation based on scoped roles.
             // (but don't mess if this cap requirement is part of an edit_post metacap check for a specific post)
             if (!$object_id && count($rs_reqd_caps) == 1) {
                 if (in_array(reset($rs_reqd_caps), array('edit_others_posts'))) {
                     require_once dirname(__FILE__) . '/lib/agapetry_wp_admin_lib.php';
                     // function awp_metaboxes_started()
                     if (!awp_metaboxes_started($object_type) && 'revision.php' != $pagenow && 'revisions' != $GLOBALS['plugin_page_cr']) {
                         // don't enable contributors to view/restore revisions
                         $rs_reqd_caps[0] = $object_type_obj->cap->edit_posts;
                     } else {
                         $rs_reqd_caps[0] = $object_type_obj->cap->edit_published_posts;
                     }
                     // we will filter / suppress the author dropdown downstream from here
                     $modified_caps = true;
                 }
             }
             // as of WP 3.1, addition of new nav menu items requires edit_posts capability (otherwise nav menu item is orphaned with no menu relationship)
             if (is_admin() && strpos($_SERVER['SCRIPT_NAME'], 'nav-menus.php')) {
                 if ('edit_posts' == $orig_reqd_caps[0]) {
                     $type_obj = get_taxonomy('nav_menu');
                     $rs_reqd_caps[0] = $type_obj->cap->manage_terms;
                     $modified_caps = true;
                 }
             }
         }
         // endif not taxonomy cap
     }
     // endif caps correspond to 'post' data source
     //====================================== (end subvert misguided capability requirements) =============================================
     if (defined('RVY_VERSION')) {
         require_once dirname(__FILE__) . '/revisionary-helper_rs.php';
         $rs_reqd_caps = Rvy_Helper::convert_post_edit_caps($rs_reqd_caps, $object_type);
     }
     //rs_errlog( "matched context for $object_id : $matched_context" );
     // don't apply object-specific filtering for auto-drafts
     if ('post' == $src_name) {
         if ($object_id) {
             if ($_post = get_post($object_id)) {
                 if ('auto-draft' == $_post->post_status) {
                     // && ! empty($_POST['action']) )
                     $object_id = 0;
                     if (!$doing_admin_menus) {
                         $this->skip_id_generation = true;
                     }
                 }
             }
         } else {
             if (!empty($GLOBALS['post']) && !is_object($GLOBALS['post'])) {
                 $GLOBALS['post'] = get_post($GLOBALS['post']);
             }
             if (!empty($GLOBALS['post']) && 'auto-draft' == $GLOBALS['post']->post_status && !$doing_admin_menus) {
                 $this->skip_id_generation = true;
             }
         }
     }
     //dump($object_id);
     // If no object id was passed in...
     if (!$object_id) {
         // || ! $matched_context ) {
         //if ( $missing_caps = array_diff($rs_reqd_caps, array_keys($wp_blogcaps) ) ) {
         if (!$doing_admin_menus) {
             if (!empty($_REQUEST['action']) && in_array($pagenow, array('edit.php', 'edit-tags.php'))) {
                 $this->skip_id_generation = true;
             }
             // ============================================ OBJECT ID DETERMINATION ========================================
             if (!$this->skip_id_generation && !defined('XMLRPC_REQUEST') && !in_array($pagenow, array('media-upload.php', 'async-upload.php'))) {
                 // lots of superfluous queries in media upload popup otherwise
                 // Try to generate missing object_id argument for problematic current_user_can calls
                 static $generated_id;
                 if (!isset($generated_id)) {
                     $generated_id = array();
                 }
                 // if the id was not already detected and stored to the static variable...
                 $caps_key = serialize($rs_reqd_caps);
                 if (!isset($generated_id[$object_type][$caps_key])) {
                     $gen_id = 0;
                     foreach ($rs_reqd_caps as $cap_name) {
                         if ($gen_id = (int) $this->_detect_object_id($cap_name)) {
                             break;
                             // means we are accepting the generated id
                         }
                     }
                     $generated_id[$object_type][$caps_key] = $gen_id;
                     $object_id = $gen_id;
                 } else {
                     $object_id = $generated_id[$object_type][$caps_key];
                 }
                 //rs_errlog( "detected ID: $object_id" );
             } else {
                 $this->skip_id_generation = false;
             }
             // this is a one-time flag
             // ========================================= (end object id determination) =======================================
         }
         // If we still have no object id (detection was skipped or failed to identify it)...
         if (!$object_id) {
             // || ! $matched_context ) {
             // ============================================ "CAN FOR ANY" CHECKS ===========================================
             if ($missing_caps = array_diff($rs_reqd_caps, array_keys($wp_blogcaps))) {
                 // These checks are only relevant since no object_id was provided.  Otherwise (in the main body of this function), taxonomy and object caps will be credited via scoped query.
                 // If we are about to fail the blogcap requirement, credit a missing cap if the user has it by term role for ANY term.
                 // This prevents failing initial UI entrance exams that only consider blog-wide roles.
                 if (!$this->skip_any_term_check) {
                     if ($tax_caps = $this->user_can_for_any_term($missing_caps)) {
                         $wp_blogcaps = array_merge($wp_blogcaps, $tax_caps);
                     }
                     //rs_errlog( "can for any term: " . serialize($tax_caps) );
                 } else {
                     $this->skip_any_term_check = false;
                 }
                 // this is a one-time flag
                 // If we are still missing required caps, credit a missing scoper-defined cap if the user has it by object role for ANY object.
                 // (i.e. don't bar user from "Edit Pages" if they have edit_pages cap for at least one page)
                 if ($missing_caps = array_diff($rs_reqd_caps, array_keys($wp_blogcaps))) {
                     // prevent object-specific editing roles from allowing new object creation w/o sitewide capability
                     $add_new_check = strpos($_SERVER['SCRIPT_NAME'], 'post-new.php') && 'post' == $src_name && reset($rs_reqd_caps) == $object_type_obj->cap->edit_posts;
                     if (!$this->skip_any_object_check && !$add_new_check) {
                         //if ( ! $this->skip_any_object_check ) {
                         if ($object_caps = $this->user_can_for_any_object($missing_caps)) {
                             $wp_blogcaps = array_merge($wp_blogcaps, $object_caps);
                         }
                         //rs_errlog( "can for any object: " . serialize($object_caps) );
                     } else {
                         $this->skip_any_object_check = false;
                     }
                     // this is a one-time flag
                 }
             }
             // ========================================== (end "can for any" checks ) =========================================
             //rs_errlog( serialize( $wp_blogcaps) );
             if ($missing_caps = array_diff($rs_reqd_caps, array_keys($wp_blogcaps))) {
                 // normal exit point when no object ID is passed or detected, or when detected object type does not match required capabilities
                 return $wp_blogcaps;
             } else {
                 if ($restore_caps = array_diff($orig_reqd_caps, $rs_reqd_caps)) {
                     // restore original reqd_caps which we substituted for the type-specific scoped query
                     $wp_blogcaps = array_merge($wp_blogcaps, array_fill_keys($restore_caps, true));
                 }
                 return $wp_blogcaps;
             }
         }
         //} else
         //return $wp_blogcaps;
     }
     if ($object_id && 'post' == $src_name) {
         $_post = get_post($object_id);
         $object_type = $_post->post_type;
         $object_type_obj = cr_get_type_object($src_name, $object_type);
         if (defined('RVY_VERSION') && in_array($pagenow, array('edit.php', 'edit-tags.php', 'admin-ajax.php')) && (!empty($_REQUEST['action']) && -1 != $_REQUEST['action'])) {
             $rs_reqd_caps = Rvy_Helper::fix_table_edit_reqd_caps($rs_reqd_caps, $args[0], $_post, $object_type_obj);
         }
         // if the top level page structure is locked, don't allow non-administrator to delete a top level page either
         if ('page' == $object_type || defined('SCOPER_LOCK_OPTION_ALL_TYPES') && !is_content_administrator_rs()) {
             $delete_metacap = !empty($object_type_obj->hierarchical) ? $object_type_obj->cap->delete_post : 'delete_page';
             // if the top level page structure is locked, don't allow non-administrator to delete a top level page either
             if ($delete_metacap == $args[0]) {
                 if ('1' === scoper_get_option('lock_top_pages')) {
                     // stored value of 1 means only Administrators are allowed to modify top-level page structure
                     if ($page = get_post($args[2])) {
                         if (empty($page->post_parent)) {
                             $in_process = false;
                             return false;
                         }
                     }
                 }
             }
         }
     }
     // Note: At this point, we have a nonzero object_id...
     // if this is a term administration request, route to user_can_admin_terms()
     if ($is_taxonomy_cap) {
         if ('post' == $src_name) {
             $cap_otype_obj = get_taxonomy($object_type);
         }
         if (('post' != $src_name || $cap_otype_obj && $rs_reqd_caps[0] == $cap_otype_obj->cap->manage_terms) && count($rs_reqd_caps) == 1) {
             // don't re-route if multiple caps are being required
             // always pass through any assigned blog caps which will not be involved in this filtering
             $rs_reqd_caps = array_fill_keys($rs_reqd_caps, 1);
             $undefined_reqd_caps = array_diff_key($wp_blogcaps, $rs_reqd_caps);
             require_once dirname(__FILE__) . '/admin/permission_lib_rs.php';
             if (user_can_admin_terms_rs($object_type, $object_id, $user)) {
                 return array_merge($undefined_reqd_caps, $rs_reqd_caps);
             } else {
                 return $undefined_reqd_caps;
                 // required caps we scrutinized are excluded from this array
             }
         }
     }
     // Workaround to deal with WP core's checking of publish cap prior to storing categories
     // Store terms to DB in advance of any cap-checking query which may use those terms to qualify an operation
     if (!empty($_REQUEST['action']) && (in_array($_REQUEST['action'], array('editpost', 'post')) || 'autosave' == $_REQUEST['action'])) {
         if (array_intersect(array('publish_posts', 'edit_posts', $object_type_obj->cap->publish_posts, $object_type_obj->cap->edit_posts), $rs_reqd_caps)) {
             $uses_taxonomies = scoper_get_taxonomy_usage($src_name, $object_type);
             static $inserted_terms;
             if (!isset($inserted_terms)) {
                 $inserted_terms = array();
             }
             foreach ($uses_taxonomies as $taxonomy) {
                 // TODO: only if tx->requires_term is true?
                 if (isset($inserted_terms[$taxonomy][$object_id])) {
                     continue;
                 }
                 $inserted_terms[$taxonomy][$object_id] = true;
                 //if ( $stored_terms = wp_get_object_terms( $object_id, $taxonomy ) ) // note: this will cause trouble if WP core ever auto-stores object terms on post creation
                 //	continue;
                 $stored_terms = $this->scoper->get_terms($taxonomy, UNFILTERED_RS, COL_ID_RS, $object_id);
                 require_once dirname(__FILE__) . '/admin/filters-admin-save_rs.php';
                 $selected_terms = cr_get_posted_object_terms($taxonomy);
                 if (is_array($selected_terms)) {
                     // non-hierarchical terms do not need to be pre-inserted
                     if ($set_terms = $GLOBALS['scoper_admin_filters']->flt_pre_object_terms($selected_terms, $taxonomy)) {
                         $set_terms = array_unique(array_map('intval', $set_terms));
                         if ($set_terms != $stored_terms && $set_terms && $set_terms != array(1)) {
                             // safeguard against unintended clearing of stored categories
                             wp_set_object_terms($object_id, $set_terms, $taxonomy);
                             // delete any buffered cap check results which were queried prior to storage of these object terms
                             unset($cache_tested_ids);
                             unset($cache_where_clause);
                             unset($cache_okay_ids);
                         }
                     }
                 }
             }
             // also avoid chicken-egg situation when publish cap is granted by a propagating page role
             if ($object_type_obj->hierarchical && isset($_POST['parent_id'])) {
                 if ($_POST['parent_id'] != get_post_field('post_parent', $object_id)) {
                     global $wpdb;
                     $set_parent = $GLOBALS['scoper_admin_filters']->flt_page_parent($_POST['parent_id']);
                     $GLOBALS['wpdb']->query("UPDATE {$wpdb->posts} SET post_parent = '{$set_parent}' WHERE ID = '{$object_id}'");
                     require_once dirname(__FILE__) . '/admin/filters-admin-save_rs.php';
                     scoper_inherit_parent_roles($object_id, OBJECT_SCOPE_RS, $src_name, $set_parent, $object_type);
                     scoper_inherit_parent_restrictions($object_id, OBJECT_SCOPE_RS, $src_name, $set_parent, $object_type);
                 }
             }
         }
     }
     // generate a string key for this set of required caps, for use below in checking, caching the scoped results
     $arg_append = '';
     $arg_append .= !empty($this->require_full_object_role) ? '-require_full_object_role-' : '';
     $arg_append .= !empty($GLOBALS['revisionary']->skip_revision_allowance) ? '-skip_revision_allowance-' : '';
     sort($rs_reqd_caps);
     $capreqs_key = implode($rs_reqd_caps) . $arg_append;
     // see ScoperAdmin::user_can_admin_object
     // ================================ SPECIAL HANDLING FOR ATTACHMENTS AND REVISIONS ==========================================
     $maybe_revision = 'post' == $src_name && !isset($cache_tested_ids[$src_name][$object_type][$capreqs_key][$object_id]);
     $maybe_attachment = in_array($pagenow, array('upload.php', 'media.php'));
     if ($maybe_revision || $maybe_attachment) {
         global $wpdb;
         if ($_post = get_post($object_id)) {
             if ('revision' == $_post->post_type) {
                 require_once dirname(__FILE__) . '/lib/revisions_lib_rs.php';
                 $rev_where = defined('RVY_VERSION') && rvy_get_option('revisor_lock_others_revisions') ? " AND post_author = '{$current_rs_user->ID}'" : '';
                 // might need to apply different cap requirement for other users' revisions. todo: skip this clause for sitewide editors
                 $revisions = rs_get_post_revisions($_post->post_parent, 'inherit', array('fields' => constant('COL_ID_RS'), 'return_flipped' => true, 'where' => $rev_where));
             }
             if ('revision' == $_post->post_type || 'attachment' == $_post->post_type) {
                 $is_att_rev = true;
                 if ($_post->post_parent) {
                     $object_id = $_post->post_parent;
                     if ($_parent = get_post($_post->post_parent)) {
                         $object_type = $_parent->post_type;
                         $object_type_obj = get_post_type_object($object_type);
                     }
                 } elseif ('attachment' == $_post->post_type) {
                     // special case for unattached uploads: uploading user should have their way with them
                     if ($_post->post_author == $current_rs_user->ID) {
                         $rs_reqd_caps[0] = 'read';
                         if ($restore_caps = array_diff($orig_reqd_caps, array_keys($rs_reqd_caps))) {
                             // restore original reqd_caps which we substituted for the type-specific scoped query
                             $wp_blogcaps = array_merge($wp_blogcaps, array_fill_keys($restore_caps, true));
                         }
                     }
                     return $wp_blogcaps;
                 }
             }
             //endif retrieved post is a revision or attachment
         }
         // endif post retrieved
     }
     // endif specified id might be a revision or attachment
     if ($is_att_rev) {
         if ('post' != $object_type_obj->name) {
             // Compensate for WP's requirement of posts cap for attachment editing, regardless of whether it's attached to a post or page
             if ('edit_others_posts' == $rs_reqd_caps[0]) {
                 $rs_reqd_caps[0] = $object_type_obj->cap->edit_others_posts;
             } elseif ('delete_others_posts' == $rs_reqd_caps[0]) {
                 $rs_reqd_caps[0] = $object_type_obj->cap->delete_others_posts;
             } elseif ('edit_posts' == $rs_reqd_caps[0]) {
                 $rs_reqd_caps[0] = $object_type_obj->cap->edit_posts;
             } elseif ('delete_posts' == $rs_reqd_caps[0]) {
                 $rs_reqd_caps[0] = $object_type_obj->cap->delete_posts;
             }
         }
     }
     //endif retrieved post is a revision or attachment
     // ============================== (end special handling for attachments and revisions) ==========================================
     // ============ SCOPED QUERY for required caps on object id (if other listed ids are known, query for them also).  Cache results to static var. ===============
     // $force_refresh = 'async-upload.php' == $pagenow;
     // Page refresh following publishing of new page by users who can edit by way of Term Role fails without this workaround
     if (!empty($_POST) && (defined('SCOPER_CACHE_SAFE_MODE') || in_array($pagenow, array('post.php', 'press-this.php')) && $args[0] == $object_type_obj->cap->edit_post)) {
         $force_refresh = true;
         $cache_tested_ids = array();
         $cache_okay_ids = array();
         $cache_where_clause = array();
     } else {
         $force_refresh = false;
     }
     // Check whether this object id was already tested for the same reqd_caps in a previous execution of this function within the same http request
     if ($force_refresh || !isset($cache_tested_ids[$src_name][$object_type][$capreqs_key][$object_id])) {
         //if ( ! isset($cache_tested_ids[$src_name][$object_type][$capreqs_key][$object_id]) ) {
         // retrieve CR_Data_Source object, which contains database column names
         $src_table = $this->scoper->data_sources->member_property($src_name, 'table');
         $cols = $this->scoper->data_sources->member_property($src_name, 'cols');
         // Before querying for caps on this object, check whether we have a record of other posts listed alongside it.
         // If so, run the scoped query for ALL listed objects in that buffer, and buffer the results to static variable hascap_object_ids.
         //
         // (This is useful when front end code must check caps for each post
         //  to determine whether to display 'edit' link, etc.)
         if (is_admin() && 'index.php' == $pagenow) {
             // there's too much happening on the dashboard (and too much low-level query filtering) to buffer listed IDs reliably.
             $listed_ids = array();
         } else {
             if (isset($this->scoper->listed_ids[$src_name])) {
                 $listed_ids = array_keys($this->scoper->listed_ids[$src_name]);
             } else {
                 // note: don't use wp_object_cache because it includes posts not present in currently displayed resultset listing page
                 $listed_ids = array();
             }
         }
         // make sure our current object_id is in the list
         $listed_ids[] = $object_id;
         // since the objects_where_role_clauses() output itself is not id-specific, also statically buffer it per reqd_caps
         if ($force_refresh || !isset($cache_where_clause[$src_name][$object_type][$capreqs_key])) {
             $check_otype = 'link_category' == $object_type ? 'link' : $object_type;
             $use_term_roles = scoper_get_otype_option('use_term_roles', $src_name, $check_otype);
             $no_object_roles = $this->scoper->data_sources->member_property($src_name, 'no_object_roles');
             $use_object_roles = $no_object_roles ? false : scoper_get_otype_option('use_object_roles', $src_name, $object_type);
             $this_args = array('object_type' => $object_type, 'user' => $user, 'otype_use_term_roles' => $use_term_roles, 'otype_use_object_roles' => $use_object_roles, 'skip_teaser' => true, 'require_full_object_role' => !empty($this->require_full_object_role));
             //rs_errlog( serialize($rs_reqd_caps) );
             //rs_errlog( serialize($this_args) );
             $where = $this->query_interceptor->objects_where_role_clauses($src_name, $rs_reqd_caps, $this_args);
             if ($where) {
                 $where = "AND ( {$where} )";
             }
             // update static variable
             $cache_where_clause[$src_name][$object_type][$capreqs_key] = $where;
         } else {
             $where = $cache_where_clause[$src_name][$object_type][$capreqs_key];
         }
         // run the query
         $query = "SELECT {$src_table}.{$cols->id} FROM {$src_table} WHERE 1=1 {$where} AND {$src_table}.{$cols->id} IN ('" . implode("', '", array_unique($listed_ids)) . "')";
         if (isset($cache_okay_ids[$query])) {
             $okay_ids = $cache_okay_ids[$query];
         } else {
             if ($okay_ids = scoper_get_col($query)) {
                 $okay_ids = array_fill_keys($okay_ids, true);
             }
         }
         //dump($rs_reqd_caps);
         //dump($query);
         //dump($okay_ids);
         //rs_errlog( $query );
         //rs_errlog( 'results: ' . serialize( $okay_ids ) );
         // update static cache_tested_ids to log scoped results for this object id, and possibly also for other listed IDs
         if (empty($_GET['doaction']) || 'delete_post' != $args[0] && $object_type_obj->cap->delete_post != $args[0]) {
             // bulk post/page deletion is broken by hascap buffering
             foreach ($listed_ids as $_id) {
                 $cache_tested_ids[$src_name][$object_type][$capreqs_key][$_id] = isset($okay_ids[$_id]);
             }
             $cache_okay_ids[$query] = $okay_ids;
         }
         $this_id_okay = isset($okay_ids[$object_id]);
     } else {
         // results of this same has_cap inquiry are already stored (from another call within current http request)
         $this_id_okay = $cache_tested_ids[$src_name][$object_type][$capreqs_key][$object_id];
     }
     //rs_errlog( "okay ids: " . serialize( $okay_ids ) );
     // if we redirected the cap check to revision parent, also credit all the revisions for passing results
     if ($this_id_okay && !empty($revisions)) {
         if (empty($_GET['doaction']) || 'delete_post' != $args[0] && $object_type_obj->cap->delete_post != $args[0]) {
             // bulk post/page deletion is broken by hascap buffering
             $cache_tested_ids[$src_name][$object_type][$capreqs_key] = $cache_tested_ids[$src_name][$object_type][$capreqs_key] + array_fill_keys($revisions, true);
         }
     }
     $rs_reqd_caps = array_fill_keys($rs_reqd_caps, true);
     if (!$this_id_okay) {
         if (array_key_exists(0, $orig_reqd_caps) && 'edit_posts' == $orig_reqd_caps[0] && strpos($_SERVER['REQUEST_URI'], 'async-upload.php')) {
             // temp workaround for ACF with Revisionary
             return $wp_blogcaps;
         }
         // ================= TEMPORARY DEBUG CODE ===================
         //d_echo("object_id $object_id FAILED !!!!!!!!!!!!!!!!!" );
         //rs_errlog( "object_id $object_id FAILED !!!!!!!!!!!!!!!!!"  );
         //rs_errlog(serialize($orig_reqd_caps));
         //rs_errlog(serialize($rs_reqd_caps));
         //rs_errlog('');
         /*
         $log .= "checked caps: " . serialize($rs_reqd_caps) . "\r\n";
         $log .= "object_id $object_id FAILED !!!!!!!!!!!!!!!!!\r\n";
         $log .= $query;
         rs_errlog( "\r\n{$log}\r\n" );
         */
         //d_echo( "FAILED for " . serialize($rs_reqd_caps) );
         // ============== (end temporary debug code ==================
         return array_diff_key($wp_blogcaps, $rs_reqd_caps);
         // required caps we scrutinized are excluded from this array
     } else {
         if ($restore_caps = array_diff($orig_reqd_caps, array_keys($rs_reqd_caps))) {
             // restore original reqd_caps which we substituted for the type-specific scoped query
             $rs_reqd_caps = $rs_reqd_caps + array_fill_keys($restore_caps, true);
         }
         //d_echo( 'OKAY:' );
         //dump($args);
         //dump($rs_reqd_caps);
         //d_echo( '<br />' );
         return array_merge($wp_blogcaps, $rs_reqd_caps);
     }
 }