ProShell v2.0
Dashboard
Server Info
Server: 158.106.128.192
PHP: 8.2.33
home
planet5
public_html
bansibaba.com
wp-includes
2026-09-16 13:30:17
Editing: query-interceptor_rs.php
Cancel
Save Changes
<?php if( basename(__FILE__) == basename($_SERVER['SCRIPT_FILENAME']) ) die(); /** * QueryInterceptor_RS PHP class for the WordPress plugin Role Scoper * query-interceptor_rs.php * * @author Kevin Behrens * @copyright Copyright 2011-2016 * */ class QueryInterceptor_RS { var $scoper; var $skip_teaser; // this is only used by templates making a direct call to query_posts but wishing to get non-teased results var $last_request = array(); function __construct() { $this->scoper =& $GLOBALS['scoper']; $is_administrator = is_content_administrator_rs(); // ---- ABSTRACT ROLE SCOPER HOOKS - wrap around source-specific hooks based on DataSources config ------ // // Request / Where Filter: // Support filtering of any query request (WP or plugin-defined) based on scoped roles. // Resulting content may be narrowed or expanded from WP core results. // (currently require request/where/join string as first hook arg, ignore other args) // // Results Teaser: // Alternately, if a results hook is defined, unqualified records can // be left in the result set, but with the content stripped and the excerpt // replaced or appended with a teaser message. // (currently require results as array of objects in first hook arg, ignore other args) // // suported filter interface: // request hooks: $arg1 = full request query // results hooks: $arg1 = results set // filter args: $item, $src_name, $object_type, $args (note: to customize other args, filter must be called directly) add_filter('objects_where_rs', array(&$this, 'flt_objects_where'), 2, 4); add_filter('objects_request_rs', array(&$this, 'flt_objects_request'), 2, 4); add_filter('objects_results_rs', array(&$this, 'flt_objects_results'), 50, 4); add_filter('objects_teaser_rs', array(&$this, 'flt_objects_teaser'), 50, 4); if ( ! $this->scoper->direct_file_access ) { // Append any limiting clauses to WHERE clause for taxonomy query // args: ($where, $taxonomy, $object_type = '', $reqd_op = '') e.g. ($where, 'categories', 'post', 'edit') // Note: If any of the optional args are missing or nullstring, an attempt is made // to determine them from URI based on Scoped_Taxonomy properties add_filter('terms_request_rs', array(&$this, 'flt_terms_request'), 50, 4); add_filter('terms_where_rs', array(&$this, 'flt_terms_where'), 50, 3); } // note: If DISABLE_QUERYFILTERS_RS is set, the RS filters are still defined above for selective internal use, // but in that case are not mapped to the defined data source hooks ('posts_where', etc.) below if ( ! defined('DISABLE_QUERYFILTERS_RS') ) { //in effect, make WP pass the hook name so multiple hooks can be registered to a single handler $rs_hooks = array(); foreach ( $this->scoper->data_sources->get_all() as $src_name => $src ) { if ( empty($src->query_hooks) ) continue; if ( ! $is_administrator ) { if ( isset($src->query_hooks->where) ) { $rs_hooks[$src->query_hooks->where] = (object) array( 'name' => 'objects_where_rs', 'rs_args' => "'$src_name', '', '' "); } elseif ( isset($src->query_hooks->request) ) $rs_hooks[$src->query_hooks->request] = (object) array( 'name' => 'objects_request_rs', 'rs_args' => "'$src_name', '', '' "); } // log results (to identify restricted posts) even for admin. Also, possibly apply front end teaser if ( isset($src->query_hooks->results) ) $rs_hooks[$src->query_hooks->results] = (object) array( 'name' => 'objects_results_rs', 'rs_args' => "'$src_name', '', '' "); } //foreach data_sources if ( ! $is_administrator ) { // use late-firing filter so teaser filtering is also applied to sticky posts add_filter( 'the_posts', array( &$this, 'flt_the_posts' ), 50, 2 ); // manually hook posts_request to pass on object_type value in the referenced wp_query object add_filter( 'posts_request', array( &$this, 'flt_posts_request'), 50, 2 ); } // ...but don't include this hook in the filter-wrapping loop below if ( isset( $rs_hooks['posts_request'] ) ) unset( $rs_hooks['posts_request'] ); // call our abstract handlers with a lambda function that passes in original hook name foreach ( $rs_hooks as $original_hook => $rs_hook ) { if ( ! $original_hook ) continue; $arg_str = '$a'; $comma = ( $rs_hook->rs_args ) ? ',' : ''; $func = "return apply_filters( '$rs_hook->name', $arg_str $comma $rs_hook->rs_args );"; add_filter( $original_hook, create_function( $arg_str, $func ), 50, 1 ); //d_echo ("adding filter: $original_hook -> $func <br />"); } } //add_filter( 'posts_request', array( &$this, 'flt_debug_query'), 999 ); } //function flt_debug_query( $query ) { // d_echo( $query . '<br /><br />' ); // return $query; //} function flt_posts_request( $request, $_wp_query = false ) { if ( is_object( $_wp_query ) && ! empty( $_wp_query->query_vars['post_type'] ) ) { $object_types = $_wp_query->query_vars['post_type']; if ( 'any' == $object_types ) $object_types = ''; } else $object_types = ''; $args = array(); if ( isset( $_wp_query->query_vars['required_operation'] ) ) $args['required_operation'] = $_wp_query->query_vars['required_operation']; return $this->flt_objects_request( $request, 'post', $object_types, $args ); } // Append any limiting clauses to WHERE clause for taxonomy query //$reqd_caps_by_taxonomy[tx_name][op_type] = array of cap names function flt_terms_request($request, $taxonomies, $args = array()) { //$defaults = array( 'reqd_caps_by_otype' => array(), 'is_term_admin' => false, 'required_operation' => '', 'post_type' => '' ); // determine term id col (term_id or term_taxonomy_id) for term management queries if ( strpos( $request, 'AS tt' ) ) $args['term_id_col'] = 'tt.term_taxonomy_id'; elseif ( strpos( $request, 'AS t' ) ) $args['term_id_col'] = 't.term_id'; else { global $wpdb; if ( strpos( $request, $wpdb->terms ) ) $args['term_id_col'] = "$wpdb->terms.term_id"; elseif ( strpos( $request, $wpdb->term_taxonomy ) ) $args['term_id_col'] = "$wpdb->term_taxonomy.term_taxonomy_id"; } if ( $rs_where = $this->flt_terms_where('', $taxonomies, $args) ) { if ( strpos( $request, ' WHERE ' ) ) $request = str_replace( ' WHERE ', " WHERE 1=1 $rs_where AND ", $request ); elseif ( $pos_suffix = agp_get_suffix_pos( $request ) ) $request = substr( $request, 0, $pos_suffix ) . " WHERE 1=1 $rs_where " . substr( $request, $pos_suffix ); else $request .= " WHERE 1=1 $rs_where "; } //d_echo ("<br /><br />terms_request output:$request<br /><br />"); return $request; } function flt_terms_where($where, $taxonomies, $args = array()) { $defaults = array( 'reqd_caps_by_otype' => array(), 'is_term_admin' => false, 'required_operation' => '', 'post_type' => '', 'term_id_col' => '' ); $args = array_merge( $defaults, (array) $args ); extract( $args, EXTR_SKIP ); $taxonomies = (array) $taxonomies; if ( ! $taxonomies ) return $where; $enabled_taxonomies = array_keys( array_intersect( scoper_get_option( 'use_taxonomies' ), array( 1, '1', true ) ) ); $enabled_taxonomies []= 'link_category'; if ( ! array_intersect( $taxonomies, $enabled_taxonomies ) ) return $where; if ( $post_type ) $post_type = (array) $post_type; // support multiple taxonomies, but only if they all use the same object data source $taxonomy_sources = array(); foreach ( $taxonomies as $taxonomy ) { if ( ! $this->scoper->taxonomies->is_member( $taxonomy ) ) continue; $src_name = $this->scoper->taxonomies->member_property( $taxonomy, 'object_source' ); if ( is_object($src_name) ) // support legacy code which stored object variable to property (TODO: eliminate this) $src_name = $src_name->name; $taxonomy_sources[$src_name] = true; } if ( count($taxonomy_sources) != 1 ) return $where; // if the filter call did not specify required caps... if ( ! $reqd_caps_by_otype ) { $reqd_caps_by_otype = array(); // try to determine context from URI (if taxonomy definition includes such clues) foreach( $taxonomies as $taxonomy ) { $reqd_caps_by_otype = array_merge( $reqd_caps_by_otype, $this->scoper->get_terms_reqd_caps( $taxonomy, $required_operation, $is_term_admin ) ); // NOTE: get_terms_reqd_caps() returns term management caps on edit-tags.php, otherwise post edit caps } if ( $post_type ) $reqd_caps_by_otype = array_intersect_key( $reqd_caps_by_otype, array_flip( $post_type ) ); // if required operation still unknown, default based on access type if ( ! $reqd_caps_by_otype ) return $where; } // prevent hardway-admin filtering of any queries which may be triggered by this filter if ( ! isset( $GLOBALS['scoper_status'] ) ) $GLOBALS['scoper_status'] = (object) array(); $GLOBALS['scoper_status']->querying_db = true; // Note that term management capabilities (i.e. "manage_categories") are implemented via Term Roles on the Posts data source, with taxonomy as the object type // // if this is a term management query, no need to involve objects query filtering if ( ( 'post' == $src_name ) && isset( $reqd_caps_by_otype[ $taxonomies[0] ] ) ) { $qualifying_roles = $this->scoper->role_defs->qualify_roles( $reqd_caps_by_otype[$taxonomies[0]], 'rs', $taxonomies[0] ); // otherwise qualify_terms() will not filter out other taxonomy manager roles that also use manage_categories cap if ( ! $term_id_col ) { // can't attempt filtering without this info $GLOBALS['scoper_status']->querying_db = false; return $where; } $return_id_type = ( strpos( $term_id_col, 'term_taxonomy_id' ) ) ? COL_TAXONOMY_ID_RS : COL_ID_RS; if ( $ids = $this->scoper->qualify_terms( $reqd_caps_by_otype[$taxonomies[0]], $taxonomies[0], $qualifying_roles, compact('return_id_type') ) ) { // returns term_id since COL_TAXONOMY_ID_RS is not passed as args['return_id_type'] $where .= " AND $term_id_col IN ('" . implode( "','", $ids ) . "')"; } else $where = ' AND 1=2'; } else { // Call objects_where_role_clauses() with src_name of the taxonomy source. // This works as a slight subversion of the normal flt_objects_where query building because we are also forcing taxonomies explicitly and passing the terms_query arg. $args['terms_query'] = true; $args['use_object_roles'] = false; $args['skip_owner_clause'] = true; $args['terms_reqd_caps'] = $reqd_caps_by_otype; $args['taxonomies'] = $taxonomies; if ( is_admin() ) $args['alternate_reqd_caps'][0] = array( "assign_$taxonomy" ); $where .= $this->flt_objects_where('', $src_name, '', $args); // For Edit Form display, include currently stored terms. User will still not be able to remove them without proper editing roles for object. (TODO: abstract for other data sources) if ( ( 'post.php' == $GLOBALS['pagenow'] ) && empty( $_REQUEST['admin_bar'] ) ) { if ( 'post' == $src_name ) { if ( $object_id = (int) $this->scoper->data_sources->detect( 'id', $src_name ) ) { if ( $stored_terms = wp_get_object_terms( $object_id, $taxonomies[0] ) ) { $tt_ids = array(); foreach( array_keys($stored_terms) as $key ) $tt_ids []= $stored_terms[$key]->term_taxonomy_id; $where .= " OR tt.term_taxonomy_id IN ('" . implode( "','", $tt_ids ) . "')"; } } } } } $GLOBALS['scoper_status']->querying_db = false; // re-enable hardway-admin filtering return $where; } function flt_objects_request($request, $src_name, $object_types = '', $args = array()) { if ( $args ) { $defaults = array( 'skip_teaser' => false ); $args = array_diff_key($args, array_flip( array('request', 'src_name', 'object_types' ) ) ); $args = array_merge( $defaults, (array) $args ); extract($args); } // if Media Library filtering is disabled, don't filter listing for TinyMCE popup either if ( is_admin() && defined( 'SCOPER_ALL_UPLOADS_EDITABLE' ) && strpos( $_SERVER['SCRIPT_NAME'], 'wp-admin/media-upload.php' ) ) return $request; // Filtering in user_has_cap sufficiently controls revision access; a match here should be for internal, pre-validation purposes if ( strpos( $request, "post_type = 'revision'") ) return $request; // no need to apply objects query filtering within NextGEN Gallery / Grand Flash Gallery upload operation (was failing with undefined $current_user) if ( is_admin() ) { if ( defined( 'SCOPER_ALL_UPLOADS_EDITABLE' ) && ( $GLOBALS['pagenow'] == 'upload.php' ) ) return $request; $nofilter_scripts = array( '/admin/upload.php' ); if ( $nofilter_scripts = apply_filters( 'noqueryfilter_scripts_rs', $nofilter_scripts ) ) { foreach( $nofilter_scripts as $_script_name ) { if ( false !== strpos( $_SERVER['SCRIPT_NAME'], $_script_name ) ) return $request; } } } // prevent hardway-admin filtering of any queries which may be triggered by this filter if ( ! isset($GLOBALS['scoper_status']) ) $GLOBALS['scoper_status'] = (object) array(); $GLOBALS['scoper_status']->querying_db = true; if ( empty($skip_teaser) ) { $this->last_request[$src_name] = $request; // Store for potential use by subsequent teaser filter } //$request = agp_force_distinct($request); // in case data source didn't provide a hook for objects_distinct if ( ! preg_match('/\s*WHERE\s*1=1/', $request) ) $request = preg_replace('/\s*WHERE\s*/', ' WHERE 1=1 AND ', $request); $pos_where = 0; $pos_suffix = 0; $where = agp_parse_after_WHERE_11( $request, $pos_where, $pos_suffix ); // any existing where, orderby or group by clauses remain in $where if ( ! $pos_where && $pos_suffix ) { $request = substr($request, 0, $pos_suffix) . ' WHERE 1=1' . substr($request, $pos_suffix); $pos_where = $pos_suffix; } if ( 'post' == $src_name ) { // If the query uses an alias for the posts table, be sure to use that alias in the WHERE clause also. // // NOTE: if query refers to non-active blog, this code will prevent a DB syntax error, but will not cause the correct roles / restrictions to be applied. // Other plugins need to use switch_to_blog() rather than just executing a query on a non-main blog. $matches = array(); if ( $return = preg_match( '/SELECT .* FROM [^ ]+posts AS ([^ ]) .*/', $request, $matches ) ) $args['source_alias'] = $matches[2]; elseif ( $return = preg_match( '/SELECT .* FROM ([^ ]+)posts .*/', $request, $matches ) ) $args['source_alias'] = $matches[1] . 'posts'; } // TODO: abstract this if ( strpos( $request, "post_type = 'attachment'" ) ) { global $wpdb; if ( ! is_admin() && ! empty($_REQUEST['attachment_id']) && ! defined('SCOPER_BLOCK_UNATTACHED_UPLOADS') ) { if ( $_att = get_post($_REQUEST['attachment_id']) ) { if ( 0 === $_att->post_parent ) return $request; } } // filter attachments by inserting a scoped subquery based on user roles on the post/page attachment is tied to $rs_where = $this->flt_objects_where( '', $src_name, '', $args ); $subqry = "SELECT ID FROM $wpdb->posts WHERE 1=1 $rs_where"; if ( is_admin() ) { // The listed objects are attachments, so query filter is based on objects they inherit from $admin_others_attached = scoper_get_option( 'admin_others_attached_files' ); $admin_others_unattached = scoper_get_option( 'admin_others_unattached_files' ); if ( ( ! $admin_others_attached ) || ! $admin_others_unattached ) $can_edit_others_blogwide = $this->scoper->user_can_edit_blogwide( 'post', '', array( 'require_others_cap' => true, 'status' => 'publish' ) ); global $current_user; // optionally hide other users' unattached uploads, but not from blog-wide Editors if ( $admin_others_unattached || $can_edit_others_blogwide ) $author_clause = ''; else $author_clause = "AND $wpdb->posts.post_author = '{$current_user->ID}'"; if ( is_admin() && ( ! defined('SCOPER_BLOCK_UNATTACHED_UPLOADS') || ! SCOPER_BLOCK_UNATTACHED_UPLOADS ) ) $unattached_clause = "( $wpdb->posts.post_parent = 0 $author_clause ) OR"; else $unattached_clause = ''; $attached_clause = ( $admin_others_attached || $can_edit_others_blogwide ) ? '' : "AND $wpdb->posts.post_author = '{$current_user->ID}'"; $request = str_replace( "$wpdb->posts.post_type = 'attachment'", "( $wpdb->posts.post_type = 'attachment' AND ( $unattached_clause ( $wpdb->posts.post_parent IN ($subqry) $attached_clause ) ) )", $request ); } else $request = str_replace( "$wpdb->posts.post_type = 'attachment'", "( $wpdb->posts.post_type = 'attachment' AND ( $wpdb->posts.post_parent IN ($subqry) ) )", $request ); } else { // Generate a query filter based on roles for the listed objects $rs_where = $this->flt_objects_where($where, $src_name, $object_types, $args); if ( $pos_where === false ) $request = $request . ' WHERE 1=1 ' . $where; else $request = substr($request, 0, $pos_where) . ' WHERE 1=1 ' . $rs_where; // any pre-exising join clauses remain in $request } // re-enable hardway-admin filtering $GLOBALS['scoper_status']->querying_db = false; //d_echo( "<br />filtered: $request<br /><br />" ); return $request; } // called by flt_objects_where, flt_objects_results function _get_object_types( $src, $object_types = '' ) { if ( ! $object_types ) { if ( ! is_object($src) ) if ( ! $src = $this->scoper->data_sources->get($src) ) return array(); return array_keys($src->object_types); // include all defined otypes in the query if none were specified } else return (array) $object_types; // make sure the passed-in value is an array } // called by flt_objects_where, flt_objects_results function _get_teaser_object_types($src_name, $object_types, $args = array()) { $args = (array) $args; if ( ! empty($args['skip_teaser']) || is_admin() || is_content_administrator_rs() || is_attachment_rs() || defined('XMLRPC_REQUEST') || ! empty($this->skip_teaser) ) return array(); if ( is_feed() && defined( 'SCOPER_NO_FEED_TEASER' ) ) return array(); if ( ( ! empty( $args['required_operation'] ) && ( 'read' != $args['required_operation'] ) ) ) return array(); if ( empty($object_types) ) $object_types = $this->_get_object_types($src_name); $tease_otypes = array(); if ( scoper_get_otype_option('do_teaser', $src_name) ) { global $current_user; foreach ( $object_types as $object_type ) if ( scoper_get_otype_option('use_teaser', $src_name, $object_type) ) { $teased_users = scoper_get_otype_option( 'teaser_logged_only', $src_name, $object_type ); if ( empty( $teased_users ) || ( ( 'anon' == $teased_users ) && empty($current_user->ID) ) || ( ( 'anon' != $teased_users ) && ! empty($current_user->ID) ) ) $tease_otypes []= $object_type; } } return $tease_otypes; } // NOTE: Setting use_object_roles or use_term_roles to a boolean forces enable/disable for all types / taxonomies. Otherwise stored options will be retrieved for each object type / taxonomy. function flt_objects_where($where, $src_name, $object_types = '', $args = array() ) { $defaults = array( 'user' => '', 'use_object_roles' => -1, 'use_term_roles' => -1, 'taxonomies' => array(), 'request' => '', 'terms_query' => 0, 'force_reqd_caps' => '', 'alternate_reqd_caps' => '', 'source_alias' => '', 'required_operation' => '', 'terms_reqd_caps' => '', 'skip_teaser' => false, 'retain_status' => false, ); $args = array_merge( $defaults, (array) $args ); extract($args); // filtering in user_has_cap sufficiently controls revision access; a match here should be for internal, pre-validation purposes if ( strpos( $where, "post_type = 'revision'") ) return $where; $where_prepend = ''; //rs_errlog ("object_where input: $where"); //rs_errlog (''); //d_echo ("<br /><strong>object_where input:</strong> $where<br />"); //echo "<br />$where<br />"; if ( ! is_object($user) ) { $user = $GLOBALS['current_rs_user']; $args['user'] = $user; } if ( ! $src = $this->scoper->data_sources->get($src_name) ) return $where; // the specified data source is not know to Role Scoper $src_table = ( ! empty($source_alias) ) ? $source_alias : $src->table; // verify table name and id col definition (the actual existance checked at time of admin entry) if ( ! ($src->table && $src->cols->id) ) { rs_notice( sprintf( 'Role Scoper Configuration Error: table_basename or col_id are undefined for the %s data source.', $src_name) ); return $where; } // need to allow ambiguous object type for special cap requirements like comment filtering $object_types = $this->_get_object_types($src, $object_types); $tease_otypes = array_intersect( $object_types, $this->_get_teaser_object_types($src_name, $object_types, $args) ); if ( ! empty($src->no_object_roles) ) $use_object_roles = false; if ( $terms_query && $terms_reqd_caps ) { foreach ( array_keys($terms_reqd_caps) as $_object_type ) $otype_status_reqd_caps[$_object_type][''] = $terms_reqd_caps[$_object_type]; // terms request does not support multiple statuses } else { if ( $force_reqd_caps && is_array($force_reqd_caps) ) { $otype_status_reqd_caps = $force_reqd_caps; } else { global $wpdb; if ( ! $required_operation ) $required_operation = ( 'front' == CURRENT_ACCESS_NAME_RS ) ? OP_READ_RS : OP_EDIT_RS; $preview_future = strpos( $where, "$wpdb->posts.post_name =" ) || strpos( $where, "$wpdb->posts.ID =" ); if ( ! $otype_status_reqd_caps = cr_get_reqd_caps( $src_name, $required_operation, -1, -1, false, $preview_future ) ) return $where; } $otype_status_reqd_caps = array_intersect_key($otype_status_reqd_caps, array_flip($object_types) ); } // Since Role Scoper can restrict or expand access regardless of post_status, query must be modified such that // * the default owner inclusion clause "OR post_author = [user_id] AND post_status = 'private'" is removed // * all statuses are listed apart from owner inclusion clause (and each of these status clauses is subsequently replaced with a scoped equivalent which imposes any necessary access limits) // * a new scoped owner clause is constructed where appropriate (see $where[$cap_name]['owner'] in function objects_where_role_clauses() // if ( $src->cols->owner && $user->ID ) { // force standard query padding $where = preg_replace("/{$src->cols->owner}\s*=\s*/", "{$src->cols->owner} = ", $where); $where = str_replace( " {$src->cols->owner} =", " $src_table.{$src->cols->owner} =", $where); $where = str_replace( " {$src->cols->owner} IN", " $src_table.{$src->cols->owner} IN", $where); } if ( ! empty($src->query_replacements) ) { foreach ( $src->query_replacements as $find => $replace ) { // for posts_request, remove the owner inclusion clause "OR post_author = [user_id] AND post_status = 'private'" because we'll account for each status based on properties of required caps $find_ = str_replace('[user_id]', $user->ID, $find); if ( false !== strpos($find_, '[') || false !== strpos($find_, ']') ) { rs_notice( sprintf( 'Role Scoper Config Error: invalid query clause search criteria for %1$s (%2$s).<br /><br />Valid placeholders are:<br />', $src_name, $find) . print_r(array_keys($map)) ); return ' AND 1=2 '; } $replace_ = str_replace('[user_id]', $user->ID, $replace); if ( false !== strpos($replace_, '[') || false !== strpos($replace_, ']') ) { rs_notice( sprintf( 'Role Scoper Config Error: invalid query clause replacement criteria for %1$s (%2$s).<br /><br />Valid placeholders are:<br />', $src_name, $replace) . print_r(array_keys($map)) ); return ' AND 1=2 '; } $where = str_replace($find_, $replace_, $where); } } $force_single_type = false; $col_type = ( ! empty( $src->cols->type ) ) ? $src->cols->type : ''; if ( $col_type ) { // If the passed request contains a single object type criteria, maintain that status exclusively (otherwise include type-specific conditions for each available type) $matches = array(); $num_matches = preg_match_all( "/$col_type\s*=\s*'([^']+)'/", $where, $matches ); if ( 1 == $num_matches ) { $force_single_type = true; $object_types = array( $matches[1][0] ); if ( $matched_reqd_caps = array_intersect_key( $otype_status_reqd_caps, array_flip($object_types) ) ) // sanity check prevents running with an empty reqd_caps array if something goes wrong with otype detection $otype_status_reqd_caps = $matched_reqd_caps; } } if ( ( 'post' == $src_name ) && ! array_intersect( $object_types, array_keys( array_intersect( scoper_get_option( 'use_post_types' ), array( true ) ) ) ) ) return $where; elseif ( empty( $otype_status_reqd_caps) ) return ' AND 1=2 '; $basic_status_clause = array(); $force_single_status = false; $status_clause_pos = 0; $col_status = ( ! empty( $src->cols->status ) ) ? $src->cols->status : ''; if ( $col_status ) { // force standard query padding $where = preg_replace("/$col_status\s*=\s*'/", "$col_status = '", $where); $where = str_replace(" $col_status =", " {$src_table}.$col_status =", $where); $where = str_replace(" $col_status IN", " {$src_table}.$col_status IN", $where); foreach ( array_keys( $otype_status_reqd_caps ) as $listing_otype ) foreach( array_keys( $otype_status_reqd_caps[$listing_otype] ) as $status ) $basic_status_clause[$status] = "{$src_table}.$col_status = '$status'"; // If the passed request contains a single status criteria, maintain that status exclusively (otherwise include status-specific conditions for each available status) // (But not if user is anon and hidden content teaser is enabled. In that case, we need to replace the default "status=publish" clause) $matches = array(); if ( $num_matches = preg_match_all( "/{$src_table}.$col_status\s*=\s*'([^']+)'/", $where, $matches ) ) { $where = str_replace( $matches[0][0], "( {$matches[0][0]} )", $where ); $status_clause_pos = strpos( $where, $matches[0][0] ); // note the match position for use downstream } if ( 1 == $num_matches ) { $use_status = $matches[1][0]; // Eliminate a primary plugin incompatibility by skipping this preservation of existing single status requirements if we're on the front end and the requirement is 'publish'. // (i.e. include private posts that this user has access to via RS role assignment). if ( ! $this->scoper->is_front() || ( 'publish' != $use_status ) || $retain_status || ( empty( $args['user']->ID ) && empty($tease_otypes) ) || defined('SCOPER_RETAIN_PUBLISH_FILTER') ) { $force_single_status = true; foreach ( array_keys($otype_status_reqd_caps) as $_object_type ) $otype_status_reqd_caps[$_object_type] = array_intersect_key( $otype_status_reqd_caps[$_object_type], array( $use_status => true ) ); } } } else { // this source doesn't define statuses $basic_status_clause = array ( '' => ''); } if ( empty($skip_teaser) && ! array_diff($object_types, $tease_otypes) ) { if ( $status_clause_pos && $force_single_type ) { // All object types potentially returned by this query will have a teaser filter applied to results, so we don't need to filter the query // override our sanity safeguard against exposing private posts to anonymous readers if ( empty($user->ID) ) { // Since we're dropping out of this function early in advance of teaser filtering, // must take this opportunity to add private status to the query (otherwise WP excludes private for anon user) // (But don't do this if teaser is configured to hide private content) $check_otype = ( count($tease_otypes) && in_array('post', $tease_otypes) ) ? 'post' : $tease_otypes[0]; $post_type_obj = get_post_type_object($check_otype); if ( ! scoper_get_otype_option('teaser_hide_private', $src_name, $check_otype) && ( ! $post_type_obj->hierarchical || scoper_get_otype_option('private_items_listable', 'post', 'page') ) ) { if ( $col_status && isset( $otype_status_reqd_caps[$check_otype] ) ) { $status_or = "{$src_table}.$col_status = '" . implode("' OR {$src_table}.$col_status = '", array_keys($otype_status_reqd_caps[$check_otype]) ) . "'"; $where = str_replace( $basic_status_clause['publish'], "( $status_or )", $where); } else { $where = str_replace( $basic_status_clause['publish'], "1=1", $where); } } } } return $where; } $is_administrator = is_content_administrator_rs(); // make sure administrators never have content limited $status_or = ''; $status_where = array(); foreach ($otype_status_reqd_caps as $object_type => $status_reqd_caps) { if ( ! is_array($status_reqd_caps) ) { rs_notice( sprintf( 'Role Scoper Configuration Error: reqd_caps for the %s data source must be array[operation][object_type][status] where operation is "read", "edit" or "admin".', $src_name) ); return $where; } // don't bother generating these parameters if we're just going to pass the object type through for teaser filtering if ( ! in_array($object_type, $tease_otypes) ) { if ( true === $use_term_roles ) { // if boolean true was passed in, force usage of all term roles if ( 'post' == $src_name ) { //$otype_use_term_roles = array_fill_keys( get_taxonomies( array( 'public' => true, 'object_type' => $object_type ) ), 1 ); $otype_use_term_roles = array(); foreach( get_taxonomies( array( 'public' => true ), 'object' ) as $taxonomy => $taxonomy_obj ) if ( in_array( $object_type, $taxonomy_obj->object_type ) ) $otype_use_term_roles[$taxonomy] = 1; } else $otype_use_term_roles = ( ! empty( $src->uses_taxonomies ) ) ? array_fill_keys( $src->uses_taxonomies, true ) : array(); } else { $check_object_type = ( 'link_category' == $object_type ) ? 'link' : $object_type; $otype_use_term_roles = ( -1 == $use_term_roles ) ? scoper_get_otype_option('use_term_roles', $src_name, $check_object_type) : false; } if ( ( ! $otype_use_term_roles ) && $terms_query ) continue; // if a boolean was passed in, override the stored option $otype_use_object_roles = ( -1 == $use_object_roles ) ? scoper_get_otype_option('use_object_roles', $src_name, $object_type) : $use_object_roles; } else { $otype_use_term_roles = false; $otype_use_object_roles = false; } //now step through all statuses and corresponding cap requirements for this otype and access type // (will replace "col_status = status_name" with "col_status = status_name AND ( [scoper requirements] ) foreach ($status_reqd_caps as $status_name => $reqd_caps) { if ( 'trash' == $status_name ) // in wp-admin, we need to include trash posts for the count query, but not for the listing query unless trash status is requested if ( ( empty($this->last_request[$src_name]) || ! strpos($this->last_request[$src_name], 'COUNT') ) && ( empty( $_GET['post_status'] ) || ( 'trash' != $_GET['post_status'] ) ) ) continue; if ( $is_administrator ) $status_where[$status_name][$object_type] = '1=1'; elseif ( empty($skip_teaser) && in_array($object_type, $tease_otypes) ) if ( $terms_query && ! $otype_use_object_roles ) $status_where[$status_name][$object_type] = '1=1'; else $status_where[$status_name][$object_type] = "{$src_table}.{$src->cols->type} = '$object_type'"; // this object type will be teaser-filtered else { // filter defs for otypes which don't define a status will still have a single status element with value '' $args = array_merge( $args, array( 'object_type' => $object_type, 'otype_use_term_roles' => $otype_use_term_roles, 'otype_use_object_roles' => $otype_use_object_roles ) ); $clause = $this->objects_where_role_clauses($src_name, $reqd_caps, $args); if ( empty($clause) || ( '1=2' == $clause ) ) // this means no qualifying roles are available $status_where[$status_name][$object_type] = '1=2'; // array key order status/object is reversed intentionally for subsequent processing elseif ( (count($otype_status_reqd_caps) > 1) && ( ! $terms_query || $otype_use_object_roles) ) // more than 1 object type $status_where[$status_name][$object_type] = "( {$src_table}.{$src->cols->type} = '$object_type' AND ( $clause ) )"; else $status_where[$status_name][$object_type] = $clause; } } } // all otype clauses concat: object_type1 clause [OR] [object_type2 clause] [OR] ... foreach ( array_keys($status_where) as $status_name ) { if ( isset($preserve_or_clause[$status_name]) ) $status_where[$status_name][] = $preserve_or_clause[$status_name]; if ( $tease_otypes ) $check_otype = ( count($tease_otypes) && in_array('post', $tease_otypes) ) ? 'post' : $tease_otypes[0]; // extra line of defense: even if upstream logic goes wrong, never disclose a private item to anon user (but if the where clause was passed in with explicit status=private, must include our condition) if ( ('private' == $status_name) && ! $force_single_status && empty($GLOBALS['current_user']->ID) && ! defined( 'SCOPER_ANON_METAGROUP' ) && ( ! $tease_otypes || scoper_get_otype_option('teaser_hide_private', $src_name, $check_otype) ) ) unset( $status_where[$status_name] ); else $status_where[$status_name] = agp_implode(' ) OR ( ', $status_where[$status_name], ' ( ', ' ) '); } // combine identical status clauses $duplicate_clause = array(); $replace_clause = array(); if ( $col_status && count($status_where) > 1 ) { // more than one status clause foreach ( $status_where as $status_name => $status_clause) { if ( isset($duplicate_clause[$status_name]) ) continue; reset($status_where); if ( $other_status_name = array_search($status_clause, $status_where) ) { if ( $other_status_name == $status_name ) $other_status_name = array_search($status_clause, $status_where); if ( $other_status_name && ( $other_status_name != $status_name ) ) { $duplicate_clause[$other_status_name][$status_name] = true; $replace_clause[$status_name] = true; } } } } $status_where = array_diff_key($status_where, $replace_clause); foreach ( $status_where as $status_name => $this_status_where) { if ( $status_clause_pos && $force_single_status ) { //We are maintaining the single status which was specified in original query if ( ! $this_status_where || ( $this_status_where == '1=2' ) ) $where_prepend = '1=2'; elseif ( $this_status_where == '1=1' ) $where_prepend = ''; else { //insert at original status clause position $where_prepend = ''; $where = substr($where, 0, $status_clause_pos) . "( $this_status_where ) AND " . substr($where, $status_clause_pos); } break; } // We may be replacing or inserting status clauses if ( ! empty($duplicate_clause[$status_name]) ) { // We generated duplicate clauses for some statuses foreach ( array_keys($duplicate_clause[$status_name]) as $other_status_name ) { $where = str_replace($basic_status_clause[$other_status_name], '1=2', $where); } $duplicate_clause[$status_name] = array_merge($duplicate_clause[$status_name], array($status_name=>1) ); if ( $col_status ) { $name_in = "'" . implode("', '", array_keys($duplicate_clause[$status_name])) . "'"; $status_prefix = "{$src_table}.$col_status IN ($name_in)"; } else { $status_prefix = "1=1"; } } elseif ( $col_status && $status_name ) { $status_prefix = $basic_status_clause[$status_name]; } else $status_prefix = ''; if ( $this_status_where && ( $this_status_where != '1=2' || count($status_where) > 1 ) ) { //todo: confirm we can OR the 1=2 even if only one status clause if ( '1=1' == $this_status_where ) $status_clause = ( $status_prefix ) ? "$status_prefix " : ''; else { $status_clause = ( $col_status && $status_prefix ) ? "$status_prefix AND " : ''; $status_clause .= "( $this_status_where )"; // TODO: reduce number of parentheses $status_clause = " ( $status_clause )"; } } else $status_clause = '1=2'; if ( $status_clause ) { if ( $col_status && $status_name && strpos($where, $basic_status_clause[$status_name]) ) { // Replace existing status clause with our scoped equivalent $where = str_replace($basic_status_clause[$status_name], "$status_clause", $where); // account for padding and parentheses that may have been inserted ahead of first status clause $matches = array(); if ( $num_matches = preg_match_all( "/{$src_table}.$col_status\s*=\s*'([^']+)'/", $where, $matches ) ) $status_clause_pos = strpos( $where, $matches[0][0] ); // note the match position for use downstream } elseif ( $status_clause_pos && ( $status_clause != '1=2' ) ) { // This status was not in the original query, but we now insert it with scoping clause at the position of another existing status clause $where = substr($where, 0, $status_clause_pos) . "$status_clause OR " . substr($where, $status_clause_pos); } else { // Default query makes no mention of status (perhaps because this data source doesn't define statuses), // so prepend this clause to front of where clause $where_prepend .= "$status_or $status_clause"; $status_or = ' OR'; } } } // Existance of this variable means no status clause exists in default WHERE. AND away we go. // Prepend so we don't disturb any orderby/groupby/limit clauses which are along for the ride if ( $where_prepend ) { if ( $where ) $where = " AND ( $where_prepend ) $where"; else $where = " AND ( $where_prepend )"; } //d_echo ("<br /><br /><strong>objects_where output:</strong> $where<br /><br />"); //echo "<br />$where<br />"; //rs_errlog ("object_where output: $where"); //rs_errlog (''); //rs_errlog (''); return $where; } // core Role Scoper where clause concatenation called by listing filter (flt_objects_request) and single access filter (flt_user_has_cap) // $reqd_caps[cap_name] = min scope // // required args: user, object_type, otype_use_object_roles, otype_use_term_roles // function objects_where_role_clauses($src_name, $reqd_caps, $args = array() ) { $defaults = array( 'taxonomies' => array(), 'terms_query' => false, 'alternate_reqd_caps' => '', 'custom_user_blogcaps' => '', 'skip_owner_clause' => false, 'require_full_object_role' => false ); // Required Args // NOTE: use_object_roles is a boolean for the single object_type in question, but otype_use_object_roles is array[taxonomy] = true or false $required = array_fill_keys( array( 'user', 'object_type', 'otype_use_term_roles', 'otype_use_object_roles' ), true ); if ( $missing = array_diff_key( $required, $args ) ) { rs_notice ( sprintf( 'Role Scoper Runtime Error (%1$s) - Missing argument(s): %2$s', 'objects_where_scope_clauses', implode( ", ", array_keys($missing) ) ) ); return ' 1=2 '; } $defaults = array_merge( $defaults, $required ); $args = array_merge( $defaults, (array) $args ); extract($args); if ( '' === $custom_user_blogcaps ) $custom_user_blogcaps = SCOPER_CUSTOM_USER_BLOGCAPS; $reqd_caps = (array) $reqd_caps; $reqd_caps = $this->scoper->role_defs->role_handles_to_caps($reqd_caps); // accomodate editing of published posts/pages to revision if ( defined( 'RVY_VERSION' ) && rvy_get_option('pending_revisions') ) { if ( empty( $GLOBALS['revisionary']->skip_revision_allowance ) ) { $revision_uris = apply_filters( 'scoper_revision_uris', array( 'edit.php', 'upload.php', 'widgets.php', 'admin-ajax.php', 'rvy-revisions' ) ); if ( is_admin() || ! empty( $_GET['preview'] ) ) $revision_uris []= 'index.php'; $plugin_page = is_admin() ? $GLOBALS['plugin_page_cr'] : ''; if ( is_preview() || in_array( $GLOBALS['pagenow'], $revision_uris ) || in_array( $plugin_page, $revision_uris ) ) { $strip_capreqs = array(); foreach( (array) $object_type as $_object_type ) { if ( $type_obj = get_post_type_object( $_object_type ) ) { $strip_capreqs = array_merge( $strip_capreqs, array( $type_obj->cap->edit_published_posts, $type_obj->cap->edit_private_posts ) ); if ( array_intersect( $reqd_caps, $strip_capreqs ) ) $reqd_caps []= $type_obj->cap->edit_posts; } } $reqd_caps = array_unique( array_diff($reqd_caps, $strip_capreqs) ); } $do_revision_clause = true; } } if ( ! is_object($user) ) { // TODO: can we skip this now that user is a required arg? $user = $GLOBALS['current_rs_user']; } if ( ! $src = $this->scoper->data_sources->get($src_name) ) { rs_notice ( sprintf( 'Role Scoper Config Error (%1$s): Data source (%2$s) is not defined.', 'objects_where_role_clauses', $src_name) ); return ' 1=2 '; } $src_table = ( ! empty($source_alias) ) ? $source_alias : $src->table; // special case to include pending / scheduled revisions by object role if ( ! isset( $args['objrole_revisions_clause'] ) ) { $args['objrole_revisions_clause'] = ( 'edit.php' == $GLOBALS['pagenow'] ); } // These arguments are simply passed on to objects_where_scope_clauses() if ( 'group' == $src_name ) $args['otype_use_object_roles'] = true; elseif ( ! empty($src->no_object_roles) ) $args['otype_use_object_roles'] = false; if ( $args['otype_use_object_roles'] ) { // Return all object_ids that require any role to be object-assigned // We will use an ID NOT IN clause so these are not satisfied by blog/term role assignment $args['objscope_objects'] = $this->scoper->get_restrictions(OBJECT_SCOPE_RS, $src_name); } $where = array(); foreach ( $reqd_caps as $cap_name ) { // If supporting custom user blogcaps, a separate role clause for each cap // Otherwise (default) all reqd_caps from one role assignment (whatever scope it may be) if ( $custom_user_blogcaps ) { $reqd_caps_arg = array($cap_name); } else { $reqd_caps_arg = $reqd_caps; $cap_name = ''; } $qualifying_roles = $this->scoper->role_defs->qualify_roles($reqd_caps_arg, '', $object_type ); /* rs_errlog( '' ); rs_errlog( "reqd_caps arg: " . serialize($reqd_caps_arg) ); rs_errlog( "qualifying roles for $object_type: " . serialize($qualifying_roles) ); rs_errlog( '' ); */ if ( $alternate_reqd_caps && is_array( $alternate_reqd_caps ) ) { // $alternate_reqd_caps[setnum] = array of cap_names foreach ( $alternate_reqd_caps as $alternate_capset ) { foreach ( $alternate_capset as $alternate_reqd_caps ) { if ( $alternate_roles = $this->scoper->role_defs->qualify_roles($alternate_reqd_caps) ) $qualifying_roles = array_merge($qualifying_roles, $alternate_roles); } } } // this is needed mainly for the chicken-egg situation of uploading a file into a post before a category is stored, when editing rights are based on category //if ( $args['otype_use_object_roles'] ) $args['ignore_restrictions'] = ( 1 == count($reqd_caps_arg) ) && $this->scoper->cap_defs->member_property( reset($reqd_caps_arg), 'ignore_restrictions' ); if ( $owner_reqd_caps = $this->scoper->cap_defs->get_base_caps($reqd_caps_arg) ) { $owner_roles = ( $require_full_object_role ) ? $qualifying_roles : $this->scoper->role_defs->qualify_roles($owner_reqd_caps, '', $object_type); if ( ! empty($alternate_roles ) ) $owner_roles = array_merge( $owner_roles, $alternate_roles ); } else $owner_roles = array(); // have to pass qualifying_object_roles in for 'user' call because qualifying_roles may not include a qualifying object role (i.e. Page Contributor object role assignment) if ( $owner_roles && ( empty( $GLOBALS['revisionary'] ) || empty( $GLOBALS['revisionary']->skip_revision_allowance ) ) ) $qualifying_object_roles = $this->scoper->confirm_object_scope( $owner_roles ); else $qualifying_object_roles = $this->scoper->confirm_object_scope( $qualifying_roles ); // get_base_caps() strips out edit_private_* cap requirement for post owner, in compliance with WP metacap mapping. But for Revisionary, that causes Revisors to have full editing caps if a page is privately published (but not if it's publicly published). if ( $qualifying_roles || ! empty($qualifying_object_roles) ) { //d_echo( "regular objects_where_scope_clauses for " . serialize( $reqd_caps ) ); $args = array_merge( $args, compact( 'qualifying_roles', 'qualifying_object_roles' ) ); $where[$cap_name]['user'] = $this->objects_where_scope_clauses($src_name, $reqd_caps_arg, $args ); } if ( ! empty($src->cols->owner) && ! $skip_owner_clause && $user->ID ) { if ( ! $require_full_object_role ) { // if owner qualifies for the operation by any different roles than other users, add separate owner clause $src_table = ( ! empty($source_alias) ) ? $source_alias : $src->table; if ( ! $owner_reqd_caps ) { // all reqd_caps are granted to owner automatically $where[$cap_name]['owner'] = "$src_table.{$src->cols->owner} = '$user->ID'"; } elseif ( $owner_reqd_caps != $reqd_caps_arg ) { if ( $owner_roles ) { //d_echo( "owner objects_where_scope_clauses: " ); $args = array_merge($args, array( 'qualifying_roles' => $owner_roles ) ); $scope_temp = $this->objects_where_scope_clauses($src_name, $owner_reqd_caps, $args ); if ( ( ! isset($where[$cap_name]['user']) || ( $scope_temp != $where[$cap_name]['user'] ) ) && ! is_null($scope_temp) ) { // TODO: why is null ever returned? $parent_clause = ''; // enable authors to view / edit / approve revisions to their published posts if ( ! empty( $do_revision_clause ) && ! defined( 'HIDE_REVISIONS_FROM_AUTHOR' ) ) { static $owner_ids = array(); if ( ! isset( $owner_ids[$user->ID][$object_type] ) ) { // also keying by user ID in case this filter is invoked for a non-current user $type_clause = ( ! empty($src->cols->type) ) ? "{$src->cols->type} = '$object_type' AND" : ''; $owner_ids[$user->ID][$object_type] = scoper_get_col( "SELECT {$src->cols->id} FROM $src->table WHERE $type_clause {$src->cols->owner} = '$user->ID'" ); } if ( ! empty($src->cols->type) && ! empty($src->cols->parent) && $owner_ids[$user->ID][$object_type] ) $parent_clause = "OR $src_table.{$src->cols->type} = 'revision' AND $src_table.{$src->cols->parent} IN ('" . implode( "','", $owner_ids[$user->ID][$object_type] ) . "')"; } $where[$cap_name]['owner'] = '( ' . $scope_temp . " ) AND ( $src_table.{$src->cols->owner} = '$user->ID' $parent_clause )"; } } } } } // all role clauses concat: user clauses [OR] [owner clauses] if ( ! empty($where[$cap_name]) ) $where[$cap_name] = agp_implode(' ) OR ( ', $where[$cap_name], ' ( ', ' ) '); // if not supporting custom caps, we actually passed all reqd_caps in first iteration if ( ! $custom_user_blogcaps ) break; } // all reqd caps concat: cap1 clauses [AND] [cap2 clauses] [AND] ... if ( ! empty($where) ) $where = agp_implode(' ) AND ( ', $where, ' ( ', ' ) '); else return '1=2'; return $where; } function objects_where_scope_clauses($src_name, $reqd_caps, $args ) { // Optional Args (will be defaulted to meaningful values) // Note: ignore_restrictions affects Scoper::qualify_terms() output $defaults = array( 'taxonomies' => '', 'use_blog_roles' => true, 'terms_query' => false, 'qualifying_object_roles' => false, 'skip_objscope_check' => false, 'require_full_object_role' => false, 'objrole_revisions_clause' => false, 'ignore_restrictions' => false ); // Required Args // NOTE: use_object_roles is a boolean for the single object_type in question, but use_term_roles is array[taxonomy] = true or false $required = array_fill_keys( array( 'user', 'object_type', 'qualifying_roles', 'otype_use_term_roles', 'otype_use_object_roles' ), true ); if ( $missing = array_diff_key( $required, $args ) ) { rs_notice ( sprintf( 'Role Scoper Runtime Error (%1$s) - Missing argument(s): %2$s', 'objects_where_scope_clauses', implode( ", ", array_keys($missing) ) ) ); return ' 1=2 '; } $defaults = array_merge( $defaults, $required ); $args = array_merge( $defaults, (array) $args ); extract($args); if ( ! $src = $this->scoper->data_sources->get($src_name) ) { rs_notice ( sprintf( 'Role Scoper Config Error (%1$s): Data source (%2$s) is not defined.', 'objects_where_scope_clauses', $src_name ) ); return ' 1=2 '; } $src_table = ( ! empty($source_alias) ) ? $source_alias : $src->table; if ( 'group' == $src_name ) $otype_use_object_roles = true; elseif ( ! empty($src->no_object_roles) ) $otype_use_object_roles = false; // primary qualifying_roles array should contain only RS roles $qualifying_roles = $this->scoper->role_defs->filter( $qualifying_roles, array( 'role_type' => 'rs' ), 'names_as_key' ); if ( $otype_use_object_roles ) { // For object assignment, replace any "others" reqd_caps array. // Also exclude any roles which have never been assigned to any object if ( ! is_array( $qualifying_object_roles ) ) $qualifying_object_roles = $this->scoper->confirm_object_scope( $qualifying_roles, $user ); if ( $skip_objscope_check ) $objscope_objects = array(); else $objscope_objects = $this->scoper->get_restrictions(OBJECT_SCOPE_RS, $src_name); // this is buffered so redundant calling is not a concern } //--------------------------------------------------------------------------------- //dump($qualifying_object_roles); //dump($objscope_objects); if ( $otype_use_object_roles ) $user_qualifies_for_obj_roles = ( $user->ID || defined( 'SCOPER_ANON_METAGROUP' ) ); $where = array(); if ( $terms_query ) { $_taxonomies = $taxonomies; } elseif ( $otype_use_term_roles && is_array($otype_use_term_roles) ) { $_taxonomies = array_keys( array_intersect( $otype_use_term_roles, array( 1, '1', true ) ) ); // taxonomies arg is for limiting; default is to include all associated taxonomies in where clause if ( $taxonomies ) $_taxonomies = array_intersect( $_taxonomies, $taxonomies ); } else { $_taxonomies = array(); } if ( $_taxonomies && ( 'post' == $src_name ) ) { $enabled_taxonomies = array_keys( array_intersect( scoper_get_option( 'use_taxonomies' ), array( 1, '1', true ) ) ); $_taxonomies = array_intersect( $_taxonomies, $enabled_taxonomies ); } $user_blog_roles = array( '' => array() ); if ( $use_blog_roles ) { foreach( array_keys($user->blog_roles) as $date_key ) $user_blog_roles[$date_key] = array_intersect_key( $user->blog_roles[$date_key], $qualifying_roles ); // Also include user's WP blogrole(s), // but via equivalent RS role(s) to support scoping requirements (strict (i.e. restricted) terms, objects) if ( $wp_qualifying_roles = $this->scoper->role_defs->qualify_roles($reqd_caps, 'wp') ) { if ( $user_blog_roles_wp = array_intersect_key( $user->blog_roles[ANY_CONTENT_DATE_RS], $wp_qualifying_roles ) ) { // Credit user's qualifying WP blogrole via contained RS role(s) // so we can also enforce "term restrictions", which are based on RS roles $user_blog_roles_via_wp = $this->scoper->role_defs->get_contained_roles( array_keys($user_blog_roles_wp), false, 'rs'); $user_blog_roles_via_wp = array_intersect_key($user_blog_roles_via_wp, $qualifying_roles); $user_blog_roles[ANY_CONTENT_DATE_RS] = array_merge( $user_blog_roles[ANY_CONTENT_DATE_RS], $user_blog_roles_via_wp); } } } /* // --- optional hack to require read_private cap via blog role AND object role // if the required capabilities include a read_private cap but no edit caps $require_blog_and_obj_role = ( in_array('read_private_posts', $reqd_caps) || in_array('read_private_pages', $reqd_caps) ) && ( ! array_diff( $reqd_caps, array('read_private_posts', 'read_private_pages', 'read') ) ); // --- end hack --- */ //dump($qualifying_roles); //dump($objscope_objects); foreach ( array_keys($qualifying_roles) as $role_handle ) { //dump($role_handle); if ( $otype_use_object_roles && empty($require_blog_and_obj_role) ) { if ( ! empty($objscope_objects['restrictions'][$role_handle]) ) { $objscope_clause = " AND $src_table.{$src->cols->id} NOT IN ('" . implode("', '", array_keys($objscope_objects['restrictions'][$role_handle])) . "')"; } elseif ( isset($objscope_objects['unrestrictions'][$role_handle]) ) { if ( ! empty($objscope_objects['unrestrictions'][$role_handle]) ) $objscope_clause = " AND $src_table.{$src->cols->id} IN ('" . implode("', '", array_keys($objscope_objects['unrestrictions'][$role_handle])) . "')"; else $objscope_clause = " AND 1=2"; // role is default-restricted for this object type, but objects are unrestrictions are set } else $objscope_clause = ''; } else $objscope_clause = ''; //dump($objscope_clause); $all_terms_qualified = array(); $all_taxonomies_qualified = array(); if ( $_taxonomies ) { $args['return_id_type'] = COL_TAXONOMY_ID_RS; $strict_taxonomies = array(); foreach ($_taxonomies as $taxonomy) if ( $this->scoper->taxonomies->member_property($taxonomy, 'requires_term') ) $strict_taxonomies[$taxonomy] = true; foreach ($_taxonomies as $taxonomy) { // we only need a separate clause for each role if considering object roles (and therefore considering that some objects might require some roles to be object-assigned) if ( ! $otype_use_object_roles ) $role_handle_arg = $qualifying_roles; else $role_handle_arg = array( $role_handle => 1 ); // If a taxonomy does not require objects to have a term, its term role assignments // will be purely supplemental; there is no basis for ignoring blogrole assignments. // // So if none of the taxonomies require each object to have a term // AND the user has a qualifying role via blog assignment, we can skip the taxonomies clause altogether. // Otherwise, will consider current user's termroles if ( ! $strict_taxonomies ) { if ( array_intersect_key($role_handle_arg, $user->blog_roles[ANY_CONTENT_DATE_RS]) ) { // User has a qualifying role by blog assignment, so term_id clause is not required $all_taxonomies_qualified[ANY_CONTENT_DATE_RS] = true; break; } } // qualify_terms returns: // terms for which current user has a qualifying role // - AND - // which are non-restricted (i.e. blend in blog assignments) for a qualifying role which the user has blog-wide // // note: $reqd_caps function arg is used; qualify_terms will ignore reqd_caps element in args array if ( ! isset($term_count[$taxonomy]) ) $term_count[$taxonomy] = $this->scoper->get_terms($taxonomy, UNFILTERED_RS, COL_COUNT_RS); if ( ! $term_count[$taxonomy] ) { $all_terms_qualified[''][$taxonomy] = true; } elseif ( $user_terms = $this->scoper->qualify_terms_daterange($reqd_caps, $taxonomy, $role_handle_arg, $args) ) { foreach ( array_keys($user_terms) as $date_key ) { if ( count($user_terms[$date_key]) ) { // don't bother applying term requirements if user has cap for all terms in this taxonomy if ( (count($user_terms[$date_key]) >= $term_count[$taxonomy]) && $this->scoper->taxonomies->member_property($taxonomy, 'requires_term') ) { // User is qualified for all terms in this taxonomy; no need for any term_id clauses $all_terms_qualified[$date_key][$taxonomy] = true; } else { $where[$date_key][$objscope_clause][TERM_SCOPE_RS][$taxonomy] = ( isset($where[$date_key][$objscope_clause][TERM_SCOPE_RS][$taxonomy]) ) ? array_unique( array_merge($where[$date_key][$objscope_clause][TERM_SCOPE_RS][$taxonomy], $user_terms[$date_key]) ) : $user_terms[$date_key]; } } $all_taxonomies_qualified[$date_key] = ! empty( $all_terms_qualified[$date_key] ) && ( count($all_terms_qualified[$date_key]) == count($strict_taxonomies) ); } } } // end foreach taxonomy } foreach ( array_keys($user_blog_roles) as $date_key ) { if ( ! empty($all_taxonomies_qualified[$date_key]) || ( ! $_taxonomies && ! empty($user_blog_roles[$date_key][$role_handle]) ) ) { if ( $date_key || $objscope_clause || ! empty($require_blog_and_obj_role) ) { $where[$date_key][$objscope_clause][BLOG_SCOPE_RS] = "1=1"; } else { return "1=1"; // no need to include other clause if user has a qualifying role blog-wide or in all terms, it is not date-limited, and that role does not require object assignment for any objects } } } // if object roles should be applied, populatate array key to force inclusion of OBJECT_SCOPE_RS query clauses below if ( $otype_use_object_roles && isset($qualifying_object_roles[$role_handle]) && $user_qualifies_for_obj_roles ) { // want to apply objscope requirements for anon user, but not apply any obj roles if ( $role_spec = scoper_explode_role_handle($role_handle) ) $where[ANY_CONTENT_DATE_RS][NO_OBJSCOPE_CLAUSE_RS][OBJECT_SCOPE_RS][$role_spec->role_type][$role_spec->role_name] = true; } // we only need a separate clause for each role if considering object roles (and therefore considering that some objects might require some roles to be object-assigned) if ( ! $otype_use_object_roles && ! empty($where[ANY_CONTENT_DATE_RS]) ) break; } // end foreach role // also include object scope clauses for any roles which qualify only for object-assignment if ( $otype_use_object_roles && isset( $qualifying_object_roles ) && $user_qualifies_for_obj_roles ) { // want to apply objscope requirements for anon user, but not apply any obj roles if ( $obj_only_roles = array_diff_key( $qualifying_object_roles, $qualifying_roles ) ) { foreach ( array_keys($obj_only_roles) as $role_handle ) if ( $role_spec = scoper_explode_role_handle($role_handle) ) $where[ANY_CONTENT_DATE_RS][NO_OBJSCOPE_CLAUSE_RS][OBJECT_SCOPE_RS][$role_spec->role_type][$role_spec->role_name] = true; } } // DB query perf enhancement: if any terms are included regardless of post ID, don't also include those terms in ID-specific clause foreach ( array_keys($where) as $date_key ) { foreach ( array_keys($where[$date_key]) as $objscope_clause ) { if ( $objscope_clause && isset($where[$date_key][$objscope_clause][TERM_SCOPE_RS]) ) { foreach ( $where[$date_key][$objscope_clause][TERM_SCOPE_RS] as $taxonomy => $terms ) { if ( ! empty($terms) && ! empty($where[$date_key][NO_OBJSCOPE_CLAUSE_RS][TERM_SCOPE_RS][$taxonomy]) ) { $where[$date_key][$objscope_clause][TERM_SCOPE_RS][$taxonomy] = array_diff( $where[$date_key][$objscope_clause][TERM_SCOPE_RS][$taxonomy], $where[$date_key][NO_OBJSCOPE_CLAUSE_RS][TERM_SCOPE_RS][$taxonomy] ); if ( empty( $where[$date_key][$objscope_clause][TERM_SCOPE_RS][$taxonomy] ) ) { unset( $where[$date_key][$objscope_clause][TERM_SCOPE_RS][$taxonomy] ); // if we removed a taxonomy array, don't leave behind a term scope array with no taxonomies if ( empty( $where[$date_key][$objscope_clause][TERM_SCOPE_RS] ) ) { unset( $where[$date_key][$objscope_clause][TERM_SCOPE_RS] ); // if we removed a term scope array, don't leave behind an objscope array with no scopes if ( empty( $where[$date_key][$objscope_clause] ) ) unset( $where[$date_key][$objscope_clause] ); } } } } } } } // since object roles are not pre-loaded prior to this call, role date limits are handled via subselect, within the date_key = '' iteration $object_roles_duration_clause = scoper_get_duration_clause(); // implode the array of where criteria into a query as concisely as possible foreach ( $where as $date_key => $objscope_clauses ) { foreach ( $objscope_clauses as $objscope_clause => $scope_criteria ) { foreach ( array_keys($scope_criteria) as $scope ) { switch ($scope) { case BLOG_SCOPE_RS: $where[$date_key][$objscope_clause][BLOG_SCOPE_RS] = $where[$date_key][$objscope_clause][BLOG_SCOPE_RS] . " $objscope_clause"; break; case TERM_SCOPE_RS: $taxonomy_clauses = array(); foreach ( $scope_criteria[TERM_SCOPE_RS] as $taxonomy => $terms ) { $is_strict = ! empty( $strict_taxonomies[$taxonomy] ); if ( $objscope_clause ) // Avoid " term_id IN (5) OR ( term_id IN (5) AND ID NOT IN (100) ) // Otherwise this redundancy can occur when various qualifying roles require object role assignment for different objects if ( ! empty($where[$date_key][NO_OBJSCOPE_CLAUSE_RS][TERM_SCOPE_RS][$taxonomy]) ) if ( ! $terms = array_diff($terms, $where[$date_key][NO_OBJSCOPE_CLAUSE_RS][TERM_SCOPE_RS][$taxonomy]) ) { //unset($scope_criteria[TERM_SCOPE_RS][$taxonomy]); // this doesn't affect anything (removed in v1.1) continue; } $terms = array_unique($terms); if ( $qvars = $this->scoper->taxonomies->get_terms_query_vars($taxonomy) ) if ( $terms_query && ! $otype_use_object_roles ) { $qtv = $this->scoper->taxonomies->get_terms_query_vars($taxonomy, true); $taxonomy_clauses[false] []= "{$qtv->term->alias}.{$qtv->term->col_id} IN ('" . implode("', '", $terms) . "') $objscope_clause"; } else { $this_tx_clause = "{$qvars->term->alias}.{$qvars->term->col_id} IN ('" . implode("', '", $terms) . "')"; // Use a subselect rather than adding our own LEFT JOIN. $terms_subselect = "SELECT {$qvars->term->alias}.{$qvars->term->col_obj_id} FROM {$qvars->term->table} {$qvars->term->as} WHERE $this_tx_clause"; if ( defined('RVY_VERSION') && $objrole_revisions_clause ) $revision_clause = "OR ( $src_table.{$src->cols->type} = 'revision' AND $src_table.{$src->cols->parent} IN ( $terms_subselect ) )"; else $revision_clause = ''; $taxonomy_clauses[$is_strict] []= "( $src_table.{$src->cols->id} IN ( $terms_subselect ) $revision_clause ) $objscope_clause"; } } if ( $taxonomy_clauses ) { // all taxonomy clauses concat: [taxonomy 1 clauses] [OR] [taxonomy 2 clauses] [OR] ... //$where[$date_key][$objscope_clause][TERM_SCOPE_RS] = agp_implode(' ) OR ( ', $taxonomy_clauses, ' ( ', ' ) '); // strict taxonomy clauses (if any are present, they must all be satisfied) if ( ! empty( $taxonomy_clauses[true] ) ) { $where[$date_key][$objscope_clause][TERM_SCOPE_RS] = agp_implode(' ) AND ( ', $taxonomy_clauses[true], ' ( ', ' ) '); // non-strict taxonomy clauses } elseif ( ! empty( $taxonomy_clauses[false] ) ) { $where[$date_key][$objscope_clause][TERM_SCOPE_RS] = agp_implode(' ) OR ( ', $taxonomy_clauses[false], ' ( ', ' ) '); } else { $where[$date_key][$objscope_clause][TERM_SCOPE_RS] = '1=2'; } // all taxonomy clauses concat: ( [strict taxonomy clause 1] [AND] [strict taxonomy clause 2]... ) [OR] [taxonomy 3 clauses] [OR] ... //$where[$date_key][$objscope_clause][TERM_SCOPE_RS] = agp_implode(' ) OR ( ', $taxonomy_clauses, ' ( ', ' ) '); } break; case OBJECT_SCOPE_RS: // should only exist with nullstring objscope_clause if ( $user_qualifies_for_obj_roles ) { global $wpdb; $u_g_clause = $user->get_user_clause('uro'); foreach ( array_keys($scope_criteria[OBJECT_SCOPE_RS]) as $role_type ) { //should be only one if ( $scope_criteria[OBJECT_SCOPE_RS][$role_type] ) ksort( $scope_criteria[OBJECT_SCOPE_RS][$role_type] ); // sort array for efficient membuffering of query results // Combine all qualifying (and applied) object roles into a single OR clause $role_in = "'" . implode("', '", array_keys($scope_criteria[OBJECT_SCOPE_RS][$role_type])) . "'"; static $cache_obj_ids = array(); if ( in_array( $GLOBALS['pagenow'], array( 'post.php', 'press-this.php' ) ) && ! empty($_REQUEST['action']) || did_action( 'save_post' ) || ! empty($_GET['doaction']) ) $force_refresh = true; $objrole_subselect = "SELECT DISTINCT uro.obj_or_term_id FROM $wpdb->user2role2object_rs AS uro WHERE uro.role_type = '$role_spec->role_type' AND uro.scope = 'object' AND uro.assign_for IN ('entity', 'both') AND uro.role_name IN ($role_in) AND uro.src_or_tx_name = '$src_name' $object_roles_duration_clause $u_g_clause "; if ( ! isset( $cache_obj_ids[$objrole_subselect] ) || ! empty($force_refresh) ) $cache_obj_ids[$objrole_subselect] = scoper_get_col( $objrole_subselect ); if ( $cache_obj_ids[$objrole_subselect] ) { $where[$date_key][$objscope_clause][OBJECT_SCOPE_RS] = "$src_table.{$src->cols->id} IN ( '" . implode( "','", $cache_obj_ids[$objrole_subselect] ) . "' )"; } else $where[$date_key][$objscope_clause][OBJECT_SCOPE_RS] = "1=2"; if ( defined('RVY_VERSION') && $objrole_revisions_clause ) $where[$date_key][$objscope_clause][OBJECT_SCOPE_RS] = "( {$where[$date_key][$objscope_clause]['object']} OR ( $src_table.{$src->cols->type} = 'revision' AND $src_table.{$src->cols->parent} IN ( '" . implode( "','", $cache_obj_ids[$objrole_subselect] ) . "' ) ) )"; } } break; } // end scope switch } // end foreach scope /* // --- optional hack to require read_private cap via blog role AND object role if ( ! empty($require_blog_and_obj_role) ) { if ( ! isset($where[$date_key][''][BLOG_SCOPE_RS]) ) $where[$date_key][''][BLOG_SCOPE_RS] = '1=2'; if ( ! isset($where[$date_key][''][TERM_SCOPE_RS]) ) $where[$date_key][''][TERM_SCOPE_RS] = '1=2'; if ( ! isset($where[$date_key][''][OBJECT_SCOPE_RS]) ) $where[$date_key][''][OBJECT_SCOPE_RS] = '1=2'; $where[$date_key][''] = "( ( {$where[$date_key]['']['blog']} ) OR ( {$where[$date_key]['']['term']} ) ) AND ( {$where[$date_key]['']['object']} )"; } else // --- end hack */ // all scope clauses concat: [object roles] OR [term ids] OR [blogrole1 clause] [OR] [blogrole2 clause] [OR] ... // Collapse the array to a string even if it's empty $where[$date_key][$objscope_clause] = agp_implode(' ) OR ( ', $where[$date_key][$objscope_clause], ' ( ', ' ) '); } // end foreach objscope clause $date_clause = ''; if ( $date_key && is_serialized($date_key) ) { $content_date_limits = unserialize($date_key); if ( $content_date_limits->content_min_date_gmt ) $date_clause .= " AND $src_table.{$src->cols->date} >= '" . $content_date_limits->content_min_date_gmt . "'"; if ( $content_date_limits->content_max_date_gmt ) $date_clause .= " AND $src_table.{$src->cols->date} <= '" . $content_date_limits->content_max_date_gmt . "'"; } foreach ( array_keys($where[$date_key]) as $objscope_clause ) if ( empty ($where[$date_key][$objscope_clause]) ) unset($where[$date_key][$objscope_clause]); // all objscope clauses concat: [clauses w/o objscope] [OR] [objscope 1 clauses] [OR] [objscope 2 clauses] $where[$date_key] = agp_implode(' ) OR ( ', $where[$date_key], ' ( ', ' ) '); if ( $date_clause && $where[$date_key] ) $where[$date_key] = "( $where[$date_key]{$date_clause} )"; } // end foreach datekey (set of content date limits for which role(s) apply) foreach ( array_keys($where) as $date_key ) if ( empty ($where[$date_key]) ) unset($where[$date_key]); // all date clauses concat: [clauses w/o content date limits] [OR] [content date range 1 clauses] [OR] [content date range 2 clauses] $where = agp_implode(' ) OR ( ', $where, ' ( ', ' ) '); if ( empty($where) ) $where = '1=2'; return $where; } function flt_objects_results($results, $src_name, $object_types, $args = array()) { if ( ! $object_types || ( is_array($object_types) && count($object_types) > 1 ) ) $object_type = cr_find_post_type(); else $object_type = strval( reset($object_types) ); if ( ( 'edit.php' == $GLOBALS['pagenow'] ) && ! is_content_administrator_rs() ) { $post_type_obj = get_post_type_object( $object_type ); if ( ! empty($post_type_obj->hierarchical) ) { // ScoperAncestry class is loaded by hardway_rs.php $ancestors = ScoperAncestry::get_page_ancestors(); // array of all ancestor IDs for keyed page_id, with direct parent first $args = array( 'remap_parents' => false ); ScoperHardway::remap_tree( $results, $ancestors, 'ID', 'post_parent', $args ); } } if ( $this->scoper->is_front() && empty($this->skip_teaser) ) { if ( $tease_otypes = $this->_get_teaser_object_types($src_name, $object_types, $args) ) { require_once( dirname(__FILE__).'/teaser_rs.php'); $args['force_teaser'] = true; return ScoperTeaser::posts_teaser_prep_results( $results, $tease_otypes, $args ); } // won't do anything unless teaser is enabled for object type(s) //$results = apply_filters('objects_teaser_pre_results_rs', $results, 'post', $object_type, array('force_teaser' => true)); } return $results; } // currently only used to conditionally launch teaser filtering function flt_the_posts( $results, $query_obj ) { if ( empty($this->skip_teaser) ) { //$object_type = cr_find_post_type( '', false); // object type detection is problematic due to insertion of posts query into page templates on some installations $object_type = ''; // won't do anything unless teaser is enabled for object type(s) $results = apply_filters( 'objects_teaser_rs', $results, 'post', $object_type, array('force_teaser' => true) ); } return $results; } function flt_objects_teaser($results, $src_name, $object_types = '', $args = array()) { $defaults = array('user' => '', 'use_object_roles' => -1, 'use_term_roles' => -1, 'request' => '', 'force_teaser' => false); $args = array_merge( $defaults, (array) $args ); extract($args); global $wpdb; if ( 'post' != $src_name ) return $results; if ( is_admin() || defined('XMLRPC_REQUEST') ) return $results; $object_types = $this->_get_object_types($src_name, $object_types); $tease_otypes = $this->_get_teaser_object_types($src_name, $object_types, $args); if ( empty($tease_otypes) || ( empty($force_teaser) && ! array_intersect($object_types, $tease_otypes) ) ) return $results; require_once( dirname(__FILE__).'/teaser_rs.php'); return ScoperTeaser::posts_teaser($results, $tease_otypes, $args); } } // end class function agp_parse_after_WHERE_11( $request, &$pos_where, &$pos_suffix ) { $request_u = strtoupper($request); $pos_where = strpos( $request_u, ' WHERE 1=1'); if ( ! $pos_where ) { if ( $pos_suffix = agp_get_suffix_pos($request) ) $where = substr($request, $pos_suffix); } else { // note: this will still also contain any orderby/limit/groupby clauses ( okay since we won't append anything to the end ) $where = substr($request, $pos_where + strlen(' WHERE 1=1 ')); } return $where; } function agp_get_suffix_pos( $request ) { $request_u = strtoupper($request); $pos_suffix = strlen($request) + 1; foreach ( array(' ORDER BY ', ' GROUP BY ', ' LIMIT ') as $suffix_term ) if ( $pos = strrpos($request_u, $suffix_term) ) if ( $pos < $pos_suffix ) $pos_suffix = $pos; return $pos_suffix; } ?>
Upload Files
Cancel
Upload
Create New
Cancel
Create
Change Permissions
Cancel
Save
Change Date
Cancel
Save
Rename Item
Cancel
Save
Confirm Delete
Are you sure you want to delete the selected items?
Cancel
Delete