function attachment_access() { global $post, $wpdb; if (empty($post)) { global $wp_query; if (!empty($wp_query->query_vars['attachment_id'])) { $post = scoper_get_row("SELECT * FROM {$wpdb->posts} WHERE post_type = 'attachment' AND ID = '{$wp_query->query_vars['attachment_id']}'"); } elseif (!empty($wp_query->query_vars['attachment'])) { $post = scoper_get_row("SELECT * FROM {$wpdb->posts} WHERE post_type = 'attachment' AND post_name = '{$wp_query->query_vars['attachment']}'"); } } if (!empty($post)) { $object_type = scoper_get_var("SELECT post_type FROM {$wpdb->posts} WHERE ID = '{$post->post_parent}'"); // default to 'post' object type if retrieval failed for some reason if (empty($object_type)) { $object_type = 'post'; } if ($post->post_parent) { if (!current_user_can("read_{$object_type}", $post->post_parent)) { if (scoper_get_otype_option('do_teaser', 'post')) { if ($use_teaser_type = scoper_get_otype_option('use_teaser', 'post', $object_type)) { AttachmentTemplate_RS::impose_post_teaser($post, $object_type, $use_teaser_type); } else { unset($post); } } else { unset($post); } // WordPress generates 404 if teaser is not enabled } } elseif (defined('SCOPER_BLOCK_UNATTACHED_UPLOADS') && SCOPER_BLOCK_UNATTACHED_UPLOADS) { unset($post); } } }
function otype_option_checkboxes($option_name, $caption, $tab_name, $section_name, $hint_text, $trailing_html, $args = array()) { global $scoper, $scoper_admin; $defaults = array('caption_header' => true); $args = array_merge($defaults, $args); extract($args); $return = array('in_scope' => false, 'val' => array()); if (in_array($option_name, $this->form_options[$tab_name][$section_name])) { $this->all_otype_options[] = $option_name; if (isset($this->def_otype_options[$option_name])) { if (!($return['val'] = scoper_get_option($option_name, $this->sitewide, $this->customize_defaults))) { $return['val'] = array(); } $return['val'] = array_merge($this->def_otype_options[$option_name], $return['val']); $label_property = isset($args['label_property']) ? $args['label_property'] : 'name'; $first_pass = true; foreach ($return['val'] as $src_otype => $val) { if ($caption_header && $first_pass) { printf($caption, $scoper_admin->interpret_src_otype($src_otype, $label_property)); echo '<br /><div style="margin-left: 2em">'; $first_pass = false; } $arr_src_otype = explode(':', $src_otype); if (!scoper_get_otype_option('use_object_roles', $arr_src_otype[0], $arr_src_otype[1])) { continue; } $item_label = $scoper_admin->interpret_src_otype($src_otype, $label_property); //arg: use plural display name $id = str_replace(':', '_', $option_name . '-' . $src_otype); echo "<label for='{$id}'>"; echo "<input name='{$id}' type='checkbox' id='{$id}' value='1' "; checked('1', $val); echo " /> "; if ($caption_header) { echo $item_label; } else { printf($caption, $item_label); } echo '</label><br />'; } // end foreach src_otype if ($caption_header) { echo '</div>'; } if ($hint_text && $this->display_hints) { echo "<span class='rs-subtext'>" . $hint_text . "</span>"; } if ($trailing_html) { echo $trailing_html; } } // endif default option isset $return['in_scope'] = true; } // endif in this option is controlled in this scope return $return; }
function scoper_admin_init() { global $pagenow; if (in_array($pagenow, array('update.php', 'plugin-install.php', 'update-core.php', 'plugins.php'))) { require_once dirname(__FILE__) . '/plugin-update-watch_rs.php'; RS_UpdateWatch::update_watch(); } if (!empty($_POST['rs_submit']) || !empty($_POST['rs_defaults']) || !empty($_POST['rs_flush_cache'])) { // For 'options' and 'realm' admin panels, handle updated options right after current_user load (and before scoper init). // By then, check_admin_referer is available, but Scoper config and WP admin menu has not been loaded yet. require_once SCOPER_ABSPATH . '/submittee_rs.php'; $handler = new Scoper_Submittee(); if (isset($_POST['rs_submit'])) { $sitewide = isset($_POST['rs_options_doing_sitewide']); $customize_defaults = isset($_POST['rs_options_customize_defaults']); $handler->handle_submission('update', $sitewide, $customize_defaults); } elseif (isset($_POST['rs_defaults'])) { $sitewide = isset($_POST['rs_options_doing_sitewide']); $customize_defaults = isset($_POST['rs_options_customize_defaults']); $handler->handle_submission('default', $sitewide, $customize_defaults); } elseif (isset($_POST['rs_flush_cache'])) { $handler->handle_submission('flush'); } } // work around conflict with Simple Fields plugin uploader if (defined('EASY_FIELDS_URL')) { if (strpos($_SERVER['SCRIPT_NAME'], '/wp-admin/media-upload.php') || strpos($_SERVER['SCRIPT_NAME'], '/wp-admin/async-upload.php')) { define('DISABLE_QUERYFILTERS_RS', true); } } if (defined('SSEO_VERSION')) { require_once dirname(__FILE__) . '/eyes-only-admin_rs.php'; } global $pagenow; // prevent default_private option from forcing a draft/pending post into private publishing if (in_array($pagenow, array('post.php', 'post-new.php'))) { if (empty($_POST['publish']) && isset($_POST['post_status']) && isset($_POST['post_type']) && scoper_get_otype_option('default_private', 'post', $_POST['post_type'])) { $stati = get_post_stati(array('public' => true, 'private' => true), 'names', 'or'); if ('private' == $_POST['visibility'] && !in_array($_POST['hidden_post_status'], $stati)) { $_POST['post_status'] = $_POST['hidden_post_status']; $_REQUEST['post_status'] = $_REQUEST['hidden_post_status']; $_POST['visibility'] = 'public'; $_REQUEST['visibility'] = 'public'; } } } }
function flt_user_has_cap($wp_blogcaps, $orig_reqd_caps, $args) { if (empty($args[2])) { return $wp_blogcaps; } // 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; } $user_id = isset($args[1]) ? $args[1] : 0; global $current_rs_user; if ($user_id && $user_id != $current_rs_user->ID) { $user = rs_get_user($user_id); } else { $user = $current_rs_user; } $object_id = (int) $args[2]; if (!($post_type = get_post_field('post_type', $object_id))) { return $wp_blogcaps; } global $wpdb; $use_term_roles = scoper_get_otype_option('use_term_roles', 'post', $post_type); $use_object_roles = empty($src->no_object_roles) ? scoper_get_otype_option('use_object_roles', 'post', $post_type) : false; $this_args = array('object_type' => $post_type, 'user' => $user, 'otype_use_term_roles' => $use_term_roles, 'otype_use_object_roles' => $use_object_roles, 'skip_teaser' => true); $where = $this->query_interceptor->objects_where_role_clauses('post', $rs_reqd_caps, $this_args); if ($where) { $where = "AND ( {$where} )"; } $id_ok = scoper_get_var("SELECT {$wpdb->posts}.ID FROM {$wpdb->posts} WHERE 1=1 {$where} AND {$wpdb->posts}.ID = '{$object_id}' LIMIT 1"); $rs_reqd_caps = array_fill_keys($rs_reqd_caps, true); if (!$id_ok) { //d_echo("object_id $object_id not okay!" ); //rs_errlog( "object_id $object_id not okay!" ); 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))) { $rs_reqd_caps = $rs_reqd_caps + array_fill_keys($restore_caps, true); } //rs_errlog( 'RETURNING:' ); //rs_errlog( serialize(array_merge($wp_blogcaps, $rs_reqd_caps)) ); return array_merge($wp_blogcaps, $rs_reqd_caps); } }
function flt_manage_posts_columns($defaults) { global $current_user, $scoper, $scoper_role_usage; $object_type = cr_find_post_type(); if ($blogwide_role_requirement = scoper_get_option('role_admin_blogwide_editor_only')) { if ('admin' == $blogwide_role_requirement && !is_user_administrator_rs()) { return $defaults; } elseif ('content_admin' == $blogwide_role_requirement && !is_content_administrator_rs()) { return $defaults; } elseif ($blogwide_role_requirement) { if (!$scoper->user_can_edit_blogwide('post', $object_type, array('require_others_cap' => true))) { return $defaults; } } } $use_object_roles = scoper_get_otype_option('use_object_roles', 'post', $object_type); $use_term_roles = scoper_get_otype_option('use_term_roles', 'post', $object_type); if ($use_term_roles && !empty($scoper_role_usage->any_restricted_terms) || $use_object_roles && !empty($scoper_role_usage->any_restricted_objects)) { if (scoper_get_otype_option('restrictions_column', 'post', $object_type)) { $defaults['restricted'] = __('Restrict', 'scoper'); } } if (!empty($scoper_role_usage->have_termrole_ids['post'])) { if (scoper_get_otype_option('term_roles_column', 'post', $object_type)) { $defaults['termroles'] = __('Term Roles', 'scoper'); } } if ($use_object_roles && !empty($scoper_role_usage->have_objrole_ids['post'])) { if (scoper_get_otype_option('object_roles_column', 'post', $object_type)) { $otype_display_name = $scoper->data_sources->member_property('post', 'object_types', $object_type, 'display_name'); //$defaults['objroles'] = sprintf( _ x('%s Roles', 'Post or Page', 'scoper'), $otype_display_name); $defaults['objroles'] = sprintf(__('%s Roles', 'scoper'), $otype_display_name); } } return $defaults; }
function scoper_get_taxonomy_usage($src_name, $object_types = '') { $taxonomies = array(); $object_types = (array) $object_types; foreach ($object_types as $object_type) { if (taxonomy_exists($object_type)) { $use_taxonomies = array($object_type => 1); } else { $use_taxonomies = scoper_get_otype_option('use_term_roles', $src_name, $object_type); } $taxonomies = array_merge($taxonomies, array_intersect((array) $use_taxonomies, array(1))); // array cast prevents PHP warning on first-time execution following update to RS 1.2 } if ($taxonomies) { // make sure we indicate non-usage of term roles for taxonomies that are completely disabled for RS if ('post' == $src_name) { $use_taxonomies = scoper_get_option('use_taxonomies'); $taxonomies = array_intersect_key($taxonomies, array_intersect($use_taxonomies, array(1))); } return array_keys($taxonomies); } else { return array(); } }
function get_terms_reqd_caps($taxonomy, $operation = '', $is_term_admin = false) { global $pagenow; if (!($src_name = $this->taxonomies->member_property($taxonomy, 'object_source'))) { if (taxonomy_exists($taxonomy)) { $src_name = 'post'; } } $return_caps = array(); $is_term_admin = $is_term_admin || in_array($pagenow, array('edit-tags.php')) || ('nav_menu' == $taxonomy && 'nav-menus.php' == $pagenow || 'admin-ajax.php' == $pagenow && (!empty($_REQUEST['action']) && in_array($_REQUEST['action'], array('add-menu-item', 'menu-locations-save')))); // possible TODO: abstract for non-WP taxonomies if ($is_term_admin) { // query pertains to the management of terms if ('post' == $src_name) { $taxonomy_obj = get_taxonomy($taxonomy); $return_caps[$taxonomy] = array($taxonomy_obj->cap->manage_terms); } elseif ('link_category' == $taxonomy) { $return_caps[$taxonomy] = array('manage_categories'); } else { global $scoper; $cap_defs = $scoper->cap_defs->get_matching($src_name, $taxonomy, OP_ADMIN_RS); $return_caps[$taxonomy] = $cap_defs ? array_keys($cap_defs) : array(); } } else { // query pertains to reading or editing content within certain terms, or adding terms to content $base_caps_only = true; if ('post' == $src_name) { if (!$operation) { $operation = $this->is_front() || 'profile.php' == $pagenow || is_admin() && 's2' == $GLOBALS['plugin_page'] ? 'read' : 'edit'; } // hack to support subscribe2 categories checklist $status = 'read' == $operation ? 'publish' : 'draft'; // terms query should be limited to a single object type for post.php, post-new.php, so only return caps for that object type (TODO: do this in wp-admin regardless of URI ?) if (in_array($pagenow, array('post.php', 'post-new.php'))) { $object_type = cr_find_post_type(); } } else { if (!$operation) { $operation = $this->is_front() ? 'read' : 'edit'; } $status = ''; } // The return array will indicate term role enable / disable, as well as associated capabilities if (!empty($object_type)) { $check_object_types = array($object_type); } else { if ($check_object_types = (array) $this->data_sources->member_property($src_name, 'object_types')) { $check_object_types = array_keys($check_object_types); } } if ('post' == $src_name) { $use_post_types = scoper_get_option('use_post_types'); } $enabled_object_types = array(); foreach ($check_object_types as $_object_type) { if ($use_term_roles = scoper_get_otype_option('use_term_roles', $src_name, $_object_type)) { if (!empty($use_term_roles[$taxonomy])) { if ('post' != $src_name || !empty($use_post_types[$_object_type])) { $enabled_object_types[] = $_object_type; } } } } foreach ($enabled_object_types as $object_type) { $return_caps[$object_type] = cr_get_reqd_caps($src_name, $operation, $object_type, $status, $base_caps_only); } } return $return_caps; }
function display_ui_user_roles($user, $groups_only = false) { global $scoper; $blog_roles = array(); $term_roles = array(); $blog_roles = $user->get_blog_roles_daterange('rs', array('include_role_duration_key' => true, 'enforce_duration_limits' => false)); // arg: return array with additional key dimension for role duration // for Administrators, display any custom post General Roles which were auto-assigned to maintain default editing rights global $current_rs_user; if ($current_rs_user->ID == $user->ID) { if (is_content_administrator_rs()) { $blog_roles[''][''] = isset($blog_roles['']['']) ? array_merge($current_rs_user->assigned_blog_roles['']) : $current_rs_user->assigned_blog_roles['']; } } foreach ($this->scoper->taxonomies->get_all() as $taxonomy => $tx) { $term_roles[$taxonomy] = $user->get_term_roles_daterange($taxonomy, 'rs', array('include_role_duration_key' => true, 'enforce_duration_limits' => false)); } // arg: return array with additional key dimension for role duration $duration_limits_enabled = scoper_get_option('role_duration_limits'); $content_date_limits_enabled = scoper_get_option('role_content_date_limits'); $html = ''; if ($groups_only) { if (IS_MU_RS && scoper_get_option('mu_sitewide_groups', true)) { global $blog_id; $list = scoper_get_blog_list(0, 'all'); $blog_path = ''; foreach ($list as $blog) { if ($blog['blog_id'] == $blog_id) { $blog_path = $blog['path']; break; } } $group_caption = sprintf(__('Group Roles %1$s(for %2$s)%3$s', 'scoper'), '<span style="font-weight: normal">', rtrim($blog_path, '/'), '</span>'); } else { $group_caption = __('Group Roles', 'scoper'); } } else { $html .= "<div id='userprofile_rolesdiv_rs' class='rs-scoped_role_profile'>"; $html .= "<h3>" . __('Scoped Roles', 'scoper') . "</h3>"; $wp_blog_roles = array_intersect_key($user->assigned_blog_roles[''], $scoper->role_defs->get_matching('wp')); if (!empty($wp_blog_roles)) { $display_names = array(); foreach (array_keys($wp_blog_roles) as $role_handle) { $display_names[] = $scoper->role_defs->get_display_name($role_handle); } $html .= sprintf(__("<strong>Assigned WordPress Role:</strong> %s", 'scoper'), implode(", ", $display_names)); if ($contained_roles = $this->scoper->role_defs->get_contained_roles(array_keys($wp_blog_roles), false, 'rs')) { $display_names = array(); foreach (array_keys($contained_roles) as $role_handle) { $display_names[] = $this->scoper->role_defs->get_display_name($role_handle); } $html .= '<br /><span class="rs-gray">'; $html .= sprintf(__("(contains %s)", 'scoper'), implode(", ", $display_names)); $html .= '</span>'; } } $html .= '<br /><br />'; } $display_names = array(); foreach (array_keys($blog_roles) as $duration_key) { if (is_serialized($duration_key)) { $role_date_limits = unserialize($duration_key); $role_date_limits->date_limited = true; } else { $role_date_limits = array(); } foreach (array_keys($blog_roles[$duration_key]) as $date_key) { $display_names = array(); if (is_serialized($date_key)) { $content_date_limits = unserialize($date_key); $content_date_limits->content_date_limited = true; } else { $content_date_limits = array(); } $date_caption = ''; if ($role_date_limits || $content_date_limits) { $limit_class = ''; // unused byref arg $limit_style = ''; // unused byref arg $link_class = ''; // unused byref arg ScoperAdminUI::set_agent_formatting(array_merge((array) $role_date_limits, (array) $content_date_limits), $date_caption, $limit_class, $link_class, $limit_style, false); // arg: no title='' wrapper around date_caption $date_caption = '<span class="rs-gray"> ' . trim($date_caption) . '</span>'; } if ($rs_blog_roles = $this->scoper->role_defs->filter($blog_roles[$duration_key][$date_key], array('role_type' => 'rs'))) { foreach (array_keys($rs_blog_roles) as $role_handle) { $display_names[] = $this->scoper->role_defs->get_display_name($role_handle); } $url = "admin.php?page=rs-general_roles"; $linkopen = "<strong><a href='{$url}'>"; $linkclose = "</a></strong>"; $list = implode(", ", $display_names); if ($groups_only) { $html .= sprintf(_n('<strong>%1$sGeneral Role%2$s</strong>%4$s: %3$s', '<strong>%1$sGeneral Roles%2$s</strong>%4$s: %3$s', count($display_names), 'scoper'), $linkopen, $linkclose, $list, $date_caption); } else { $html .= sprintf(_n('<strong>Additional %1$sGeneral Role%2$s</strong>%4$s: %3$s', '<strong>Additional %1$sGeneral Roles%2$s</strong>%4$s: %3$s', count($display_names), 'scoper'), $linkopen, $linkclose, $list, $date_caption); } if ($contained_roles = $this->scoper->role_defs->get_contained_roles(array_keys($rs_blog_roles), false, 'rs')) { $display_names = array(); foreach (array_keys($contained_roles) as $role_handle) { $display_names[] = $this->scoper->role_defs->get_display_name($role_handle); } $html .= '<br /><span class="rs-gray">'; $html .= sprintf(__("(contains %s)", 'scoper'), implode(", ", $display_names)); $html .= '</span>'; } $html .= '<br /><br />'; } } // end foreach content date range } // end foreach role duration date range $disable_role_admin = false; global $profileuser; $viewing_own_profile = !empty($profileuser) && $profileuser->ID == $current_rs_user->ID; if (!$viewing_own_profile) { if ($require_blogwide_editor = scoper_get_option('role_admin_blogwide_editor_only')) { if ('admin' == $require_blogwide_editor && !is_user_administrator_rs()) { return false; } if ('admin_content' == $require_blogwide_editor && !is_content_administrator_rs()) { return false; } $disable_role_admin = !$scoper->user_can_edit_blogwide('post', '', array('require_others_cap' => true, 'status' => 'publish')); } } foreach ($this->scoper->taxonomies->get_all() as $taxonomy => $tx) { if (empty($term_roles[$taxonomy])) { continue; } $val = ORDERBY_HIERARCHY_RS; $args = array('order_by' => $val); if (!($terms = $this->scoper->get_terms($taxonomy, UNFILTERED_RS, COLS_ALL_RS, 0, $args))) { continue; } $object_types = array(); $obj_src = $this->scoper->data_sources->get($tx->object_source); if (!$obj_src || !is_array($obj_src->object_types)) { continue; } foreach (array_keys($obj_src->object_types) as $object_type) { if (scoper_get_otype_option('use_term_roles', $tx->object_source, $object_type)) { $object_types[] = $object_type; } } if (!$object_types) { continue; } $object_types[] = $taxonomy; $admin_terms = $disable_role_admin ? array() : $this->scoper->get_terms($taxonomy, ADMIN_TERMS_FILTER_RS, COL_ID_RS); $strict_terms = $this->scoper->get_restrictions(TERM_SCOPE_RS, $taxonomy); $role_defs = $this->scoper->role_defs->get_matching('rs', $tx->object_source, $object_types); $tx_src = $this->scoper->data_sources->get($tx->source); $col_id = $tx_src->cols->id; $col_name = $tx_src->cols->name; $term_names = array(); foreach ($terms as $term) { $term_names[$term->{$col_id}] = $term->{$col_name}; } foreach (array_keys($term_roles[$taxonomy]) as $duration_key) { if (is_serialized($duration_key)) { $role_date_limits = unserialize($duration_key); $role_date_limits->date_limited = true; } else { $role_date_limits = array(); } foreach (array_keys($term_roles[$taxonomy][$duration_key]) as $date_key) { if (is_serialized($date_key)) { $content_date_limits = unserialize($date_key); $content_date_limits->content_date_limited = true; } else { $content_date_limits = array(); } $title = ''; $date_caption = ''; $limit_class = ''; $limit_style = ''; $link_class = ''; $style = ''; if ($role_date_limits || $content_date_limits) { ScoperAdminUI::set_agent_formatting(array_merge((array) $role_date_limits, (array) $content_date_limits), $date_caption, $limit_class, $link_class, $limit_style); $title = "title='{$date_caption}'"; $date_caption = '<span class="rs-gray"> ' . trim($date_caption) . '</span>'; } if ($admin_terms) { $url = "admin.php?page=rs-{$taxonomy}-roles_t"; //$html .= ("\n<h4><a href='$url'>" . sprintf(_ x('%1$s Roles%2$s:', 'Category Roles, content date range', 'scoper'), $tx->display_name, '</a><span style="font-weight:normal">' . $date_caption) . '</span></h4>' ); $html .= "\n<h4><a href='{$url}'>" . sprintf(__('%1$s Roles%2$s:', 'scoper'), $tx->labels->singular_name, '</a><span style="font-weight:normal">' . $date_caption) . '</span></h4>'; } else { $html .= "\n<h4>" . sprintf(__('%1$s Roles%2$s:', 'scoper'), $tx->labels->singular_name, $date_caption) . '</h4>'; } //$html .= ("\n<h4>" . sprintf(_ x('%1$s Roles%2$s:', 'Category Roles, content date range', 'scoper'), $tx->display_name, $date_caption) . '</h4>' ); $html .= '<ul class="rs-termlist" style="padding-left:0.1em;">'; $html .= '<li>'; $html .= '<table class="widefat"><thead><tr class="thead">'; $html .= '<th class="rs-tightcol">' . __awp('Role') . '</th>'; $html .= '<th>' . $tx->labels->name . '</th>'; $html .= '</tr></thead><tbody>'; foreach (array_keys($role_defs) as $role_handle) { if (isset($term_roles[$taxonomy][$duration_key][$date_key][$role_handle])) { $role_terms = $term_roles[$taxonomy][$duration_key][$date_key][$role_handle]; $role_display = $this->scoper->role_defs->get_display_name($role_handle); $term_role_list = array(); foreach ($role_terms as $term_id) { if (!in_array($term_id, $admin_terms)) { $term_role_list[] = $term_names[$term_id]; } elseif (isset($strict_terms['restrictions'][$role_handle][$term_id]) || isset($strict_terms['unrestrictions'][$role_handle]) && is_array($strict_terms['unrestrictions'][$role_handle]) && !isset($strict_terms['unrestrictions'][$role_handle][$term_id])) { $term_role_list[] = "<span class='rs-backylw'><a {$title}{$limit_style}class='{$link_class}{$limit_class}' href='{$url}#item-{$term_id}'>" . $term_names[$term_id] . '</a></span>'; } else { $term_role_list[] = "<a {$title}{$limit_style}class='{$link_class}{$limit_class}' href='{$url}#item-{$term_id}'>" . $term_names[$term_id] . '</a>'; } } $html .= "\r\n" . "<tr{$style}>" . "<td>" . str_replace(' ', ' ', $role_display) . "</td>" . '<td>' . implode(', ', $term_role_list) . '</td>' . "</tr>"; $style = ' class="alternate"' == $style ? ' class="rs-backwhite"' : ' class="alternate"'; } } $html .= '</tbody></table>'; $html .= '</li></ul><br />'; } // end foreach content date range } // end foreach role duration date range } // end foreach taxonomy require_once dirname(__FILE__) . '/object_roles_list.php'; $html .= scoper_object_roles_list($user, array('enforce_duration_limits' => false, 'is_user_profile' => $viewing_own_profile, 'echo' => false)); if ($groups_only) { //if ( empty($rs_blog_roles) && empty($term_role_list) && empty($got_obj_roles) ) if ($html) { echo '<div>'; echo "<h3>{$group_caption}</h3>"; echo $html; echo '</div>'; if (IS_MU_RS) { echo '<br /><hr /><br />'; } } //echo '<p>' . __('No roles are assigned to this group.', 'scoper'), '</p>'; } else { echo $html; echo '</div>'; } }
function flt_objects_listing($results, $src_name, $object_types, $args = array()) { global $wpdb; // it's not currently necessary or possible to log listed revisions from here //if ( isset($wpdb->last_query) && strpos( $wpdb->last_query, "post_type = 'revision'") ) // return $results; // if currently listed IDs are not already in post_cache, make our own equivalent memcache // ( create this cache for any data source, front end or admin ) if ('post' == $src_name) { global $wp_object_cache; } $listed_ids = array(); //if ( ('post' != $src_name) || empty($wp_object_cache->cache['posts']) ) { if (empty($this->scoper->listed_ids[$src_name])) { if ($col_id = $this->scoper->data_sources->member_property($src_name, 'cols', 'id')) { $listed_ids = array(); // In edit.php, WP forces all objects into recordset for hierarchical post types. But for perf enchancement, we need to know IDs of items which are actually listed if ('edit.php' == $GLOBALS['pagenow']) { $post_type = !empty($_GET['post_type']) ? sanitize_key($_GET['post_type']) : 'post'; $determine_listed_ids = !is_content_administrator_rs() && is_post_type_hierarchical($post_type) && !empty($GLOBALS['query_interceptor']->last_request[$src_name]) && !strpos($GLOBALS['query_interceptor']->last_request[$src_name], 'LIMIT '); if ($determine_listed_ids) { // mimic recordset paging used in edit.php $pagenum = isset($_GET['paged']) ? absint($_GET['paged']) : 0; if (empty($pagenum)) { $pagenum = 1; } $edit_per_page = 'edit_' . $post_type . '_per_page'; $per_page = (int) get_user_option($edit_per_page); if (empty($per_page) || $per_page < 1) { $per_page = 20; } $per_page = apply_filters($edit_per_page, $per_page); $per_page = apply_filters('edit_posts_per_page', $per_page, $post_type); if (count($results) <= $per_page) { $determine_listed_ids = false; } } } else { $determine_listed_ids = false; } if ($determine_listed_ids) { // Construct and execute a secondary query (for IDs only) which includes the paging clause that would be used if edit.php did not defeat it $pgstrt = ($pagenum - 1) * $per_page . ', '; $limits = ' LIMIT ' . $pgstrt . $per_page; global $wpdb; $qry = $GLOBALS['query_interceptor']->last_request[$src_name] . $limits; $qry = str_replace("{$wpdb->posts}.*", "{$wpdb->posts}.ID", $qry); $_results = scoper_get_results($qry); foreach ($_results as $row) { if (isset($row->{$col_id})) { $listed_ids[$row->{$col_id}] = true; } } } else { // No secondary query, just buffer all IDs in the results set foreach ($results as $row) { if (isset($row->{$col_id})) { $listed_ids[$row->{$col_id}] = true; } } } if (empty($this->scoper->listed_ids)) { $this->scoper->listed_ids = array(); } $this->scoper->listed_ids[$src_name] = $listed_ids; } } else { return $results; } //} // now determine what restrictions were in place on these results // (currently only for post data source, front end or manage posts/pages) // // possible todo: support other data sources, WP role type if ('edit.php' == $GLOBALS['pagenow']) { if (scoper_get_otype_option('restrictions_column', 'post') || scoper_get_otype_option('term_roles_column', 'post') || scoper_get_otype_option('object_roles_column', 'post')) { global $scoper_role_usage; require_once dirname(__FILE__) . '/role_usage_rs.php'; $scoper_role_usage = new Role_Usage_RS(); $scoper_role_usage->determine_role_usage_rs('post', $listed_ids); } } return $results; }
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); $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)) { $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 || 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}'"; } } 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'; } 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) && (!$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); } 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; }
function determine_role_usage_rs($src_name = 'post', $listed_ids = '') { global $scoper, $wpdb; if ('post' != $src_name) { return; } if (empty($listed_ids)) { if (!empty($scoper->listed_ids[$src_name])) { $listed_ids = $scoper->listed_ids[$src_name]; } else { return; } } if (empty($this->checked_ids[$src_name])) { $this->checked_ids[$src_name] = array(); } else { if (!array_diff_key($this->checked_ids[$src_name], $listed_ids)) { return; } } $this->checked_ids[$src_name] = $this->checked_ids[$src_name] + $listed_ids; $src = $scoper->data_sources->get($src_name); $col_id = $src->cols->id; $col_type = isset($src->cols->type) ? $src->cols->type : ''; if ($viewing_object_type = cr_find_post_type()) { $object_types = (array) $viewing_object_type; } else { $object_types = array_diff_key(get_post_types(array('public' => true)), array('attachment')); } // For now, only determine restricted posts if using RS role type. // Backing this out will be more convoluted for WP role type; may need to just list which roles are restricted rather than trying to give an Restricted Read/Edit summary $roles = array(); if (is_admin()) { foreach ($object_types as $_post_type) { $roles["edit"][$_post_type] = array("publish" => "rs_{$_post_type}_editor", "private" => "rs_{$_post_type}_editor", "draft" => "rs_{$_post_type}_contributor", "pending" => "rs_{$_post_type}_contributor", "future" => "rs_{$_post_type}_editor", "trash" => "rs_{$_post_type}_editor"); $roles["read"][$_post_type] = array("publish" => "rs_{$_post_type}_reader", "private" => "rs_private_{$_post_type}_reader", "draft" => "rs_{$_post_type}_reader", "pending" => "rs_{$_post_type}_reader", "future" => "rs_{$_post_type}_reader", "trash" => "rs_{$_post_type}_editor"); } } else { foreach ($object_types as $_post_type) { $roles["read"][$_post_type] = array("publish" => "rs_{$_post_type}_reader", "private" => "rs_private_{$_post_type}_reader"); } } // which of these results ignore blog role assignments? $uses_taxonomies = scoper_get_taxonomy_usage($src_name, $object_types); if (!empty($uses_taxonomies)) { foreach ($uses_taxonomies as $taxonomy) { $tx_object_types = $object_types; foreach ($tx_object_types as $key => $object_type) { // ignore term restrictions / roles for object types which have them disabled $_use_term_roles = scoper_get_otype_option('use_term_roles', $src_name, $object_type); if (empty($_use_term_roles[$taxonomy])) { unset($tx_object_types[$key]); } } if (!$tx_object_types) { continue; } if (!$scoper->taxonomies->is_member($taxonomy)) { continue; } $qvars = $scoper->taxonomies->get_terms_query_vars($taxonomy); $term_join = " INNER JOIN {$qvars->term->table} {$qvars->term->as} ON {$src->table}.{$src->cols->id} = {$qvars->term->alias}.{$qvars->term->col_obj_id} "; // ======== Log term restrictions ======== // if ($scoper->taxonomies->member_property($taxonomy, 'requires_term')) { if ($strict_terms = $scoper->get_restrictions(TERM_SCOPE_RS, $taxonomy)) { $this->any_restricted_terms = true; } $all_terms = $scoper->get_terms($taxonomy, UNFILTERED_RS, COL_ID_RS); foreach (array_keys($roles) as $op_type) { $status_where = array(); foreach ($tx_object_types as $object_type) { $term_clauses = array(); foreach ($roles[$op_type][$object_type] as $status => $check_role) { if (isset($strict_terms['restrictions'][$check_role]) && is_array($strict_terms['restrictions'][$check_role])) { $this_strict_terms = array_keys($strict_terms['restrictions'][$check_role]); } elseif (isset($strict_terms['unrestrictions'][$check_role]) && is_array($strict_terms['unrestrictions'][$check_role])) { $this_strict_terms = array_diff($all_terms, array_keys($strict_terms['unrestrictions'][$check_role])); } else { $this_strict_terms = array(); } if (!$this_strict_terms) { // no terms in this taxonomy have restricted roles $term_clauses[$status] = '1=2'; } elseif (count($this_strict_terms) < count($all_terms)) { // some (but not all) terms in this taxonomy honor blog-wide assignment of the pertinent role $term_clauses[$status] = " {$qvars->term->alias}.{$qvars->term->col_id} IN ('" . implode("', '", $this_strict_terms) . "')"; } else { $term_clauses[$status] = '1=1'; } if (isset($term_clauses[$status])) { $status_where[$object_type][$status] = " {$src->cols->status} = '{$status}' AND ( {$term_clauses[$status]} ) "; } } // end foreach statuses if (isset($status_where[$object_type])) { // object_type='type_val' AND ( (status 1 clause) OR (status 2 clause) ... $status_where[$object_type] = " {$src->cols->type} = '{$object_type}' AND ( " . agp_implode(' ) OR ( ', $status_where[$object_type], ' ( ', ' ) ') . " )"; } } // end foreach tx_object_types // NOTE: we are querying for posts/pages which HAVE restrictions that apply to their current post_status // if ($status_where) { // (object type 1 clause) OR (object type 2 clause) ... $where = ' AND (' . agp_implode(' ) OR ( ', $status_where, ' ( ', ' ) ') . ' )'; $where .= " AND {$src->table}.{$col_id} IN ('" . implode("', '", array_keys($listed_ids)) . "')"; $query = "SELECT DISTINCT {$col_id} FROM {$src->table} {$term_join} WHERE 1=1 {$where}"; if (isset($query_results[$query])) { $restricted_ids = $query_results[$query]; } else { $restricted_ids = scoper_get_col($query); $query_results[$query] = $restricted_ids; } foreach ($restricted_ids as $id) { $this->termscoped_ids[$src_name][$id][$op_type] = true; $this->restricted_ids[$src_name][$id][$op_type] = true; } } } // end foreach op_type (read/edit) } // end term restrictions logging // ======== Log term roles ======== // if (is_admin() && !empty($qvars)) { if ($src_roles = $scoper->role_defs->get_matching('rs', 'post', $tx_object_types)) { $otype_role_names = array(); foreach (array_keys($src_roles) as $role_handle) { $otype_role_names[] = $src_roles[$role_handle]->name; } $role_clause = "AND uro.role_name IN ('" . implode("', '", $otype_role_names) . "')"; $join_assigned = $term_join . " INNER JOIN {$wpdb->user2role2object_rs} AS uro ON uro.obj_or_term_id = {$qvars->term->alias}.{$qvars->term->col_id}" . " AND uro.scope = 'term' AND uro.role_type = 'rs' {$role_clause} AND uro.src_or_tx_name = '{$taxonomy}'"; $where = " AND {$src->table}.{$col_id} IN ('" . implode("', '", array_keys($listed_ids)) . "')"; $query = "SELECT DISTINCT {$col_id}, uro.role_name FROM {$src->table} {$join_assigned} WHERE 1=1 {$where}"; $role_results = scoper_get_results($query); foreach ($role_results as $row) { $role_handle = scoper_get_role_handle($row->role_name, 'rs'); $this->have_termrole_ids[$src_name][$row->{$col_id}][$role_handle] = true; } } } // end term roles logging } // end foreach of this data source's taxonomies } // endif this data source uses taxonomies // which of these results ignore blog AND term role assignments? if ($objscope_objects = $scoper->get_restrictions(OBJECT_SCOPE_RS, $src_name)) { $this->any_restricted_objects = true; } foreach (array_keys($roles) as $op_type) { foreach ($object_types as $object_type) { if (!scoper_get_otype_option('use_object_roles', $src_name, $object_type)) { continue; } if (is_array($roles[$op_type][$object_type])) { foreach (array_keys($listed_ids) as $id) { foreach ($roles[$op_type][$object_type] as $check_role) { // If a restriction is set for this object and role, // OR if the role is default-restricted with no unrestriction for this object... if (isset($objscope_objects['restrictions'][$check_role][$id]) || isset($objscope_objects['unrestrictions'][$check_role]) && is_array($objscope_objects['unrestrictions'][$check_role]) && !isset($objscope_objects['unrestrictions'][$check_role][$id])) { $this->objscoped_ids[$src_name][$id][$op_type] = true; $this->restricted_ids[$src_name][$id][$op_type] = true; } } } //end foreach listed ids } // endif any applicable roles defined } // end forach object type } // end foreach op_type (read/edit) // query for object role assignments if (is_admin()) { if ($scoper->get_applied_object_roles()) { //$this->any_object_roles = true; $join_assigned = " INNER JOIN {$wpdb->user2role2object_rs} AS uro ON uro.obj_or_term_id = {$src->table}.{$col_id}" . " AND uro.src_or_tx_name = '{$src_name}' AND uro.scope = 'object' AND uro.role_type = 'rs'"; $where = " AND {$src->table}.{$col_id} IN ('" . implode("', '", array_keys($listed_ids)) . "')"; $query = "SELECT DISTINCT {$col_id}, uro.role_name FROM {$src->table} {$join_assigned} WHERE 1=1 {$where}"; $role_results = scoper_get_results($query); foreach ($role_results as $row) { $role_handle = scoper_get_role_handle($row->role_name, 'rs'); $this->have_objrole_ids[$src_name][$row->{$col_id}][$role_handle] = true; //$this->any_object_roles = true; } } } }
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); } }
function build_menu() { if (strpos($_SERVER['REQUEST_URI'], 'wp-admin/network/')) { return; } global $plugin_page_cr; if (!defined('USER_ROLES_RS') && isset($_POST['enable_group_roles'])) { scoper_use_posted_init_options(); } global $current_user; $is_option_administrator = is_option_administrator_rs(); $is_user_administrator = is_user_administrator_rs(); $is_content_administrator = is_content_administrator_rs(); /* // optional hack to prevent roles / restrictions menu for non-Administrators // // This is now handled as a Role Scoper Option. // In Roles > Options > Features > Content Maintenance, set "Roles and Restrictions can be set" to "Administrators only" // // To prevent Role Scoper from filtering the backend at all, go to Roles > Options > Realm > Access Types and deselect "editing and administering content" // // end optional hack */ $require_blogwide_editor = scoper_get_option('role_admin_blogwide_editor_only'); if (!$is_content_administrator && 'admin_content' == $require_blogwide_editor) { if (!$is_option_administrator) { return; } } if (!$is_user_administrator && 'admin' == $require_blogwide_editor) { if (!$is_option_administrator) { return; } } $can_admin_objects = array(); $can_admin_terms = array(); $use_post_types = scoper_get_option('use_post_types'); $use_taxonomies = scoper_get_option('use_taxonomies'); // which object types does this user have any administration over? foreach ($this->scoper->data_sources->get_all() as $src_name => $src) { if (!empty($src->no_object_roles) || !empty($src->taxonomy_only) || 'group' == $src_name) { continue; } $object_types = isset($src->object_types) ? $src->object_types : array($src_name => true); foreach (array_keys($object_types) as $object_type) { if ('post' == $src_name && empty($use_post_types[$object_type])) { continue; } if (is_administrator_rs($src, 'user') || $this->user_can_admin_object($src_name, $object_type, 0, true)) { if (scoper_get_otype_option('use_object_roles', "{$src_name}:{$object_type}")) { $can_admin_objects[$src_name][$object_type] = true; } } } } // which taxonomies does this user have any administration over? foreach ($this->scoper->taxonomies->get_all() as $taxonomy => $tx) { if (taxonomy_exists($taxonomy) && empty($use_taxonomies[$taxonomy]) && 'post' == $tx->object_source) { continue; } if (is_taxonomy_used_rs($taxonomy) && (is_administrator_rs($tx->source, 'user') || $this->user_can_admin_terms($taxonomy))) { $can_admin_terms[$taxonomy] = true; } } // Users Tab if (DEFINE_GROUPS_RS) { $can_manage_groups = DEFINE_GROUPS_RS && ($is_user_administrator || current_user_can('recommend_group_membership')); $cap_req = $can_manage_groups ? 'read' : 'manage_groups'; $groups_caption = defined('GROUPS_CAPTION_RS') ? GROUPS_CAPTION_RS : __('Role Groups', 'scoper'); if (!IS_MU_RS || !scoper_get_site_option('mu_sitewide_groups')) { add_submenu_page('users.php', $groups_caption, $groups_caption, $cap_req, 'rs-groups', array(&$this, 'menu_handler')); } elseif (IS_MU_RS && !awp_ver('3.1')) { add_submenu_page("ms-admin.php", $groups_caption, $groups_caption, $cap_req, 'rs-groups', array(&$this, 'menu_handler')); } // satisfy WordPress' demand that all admin links be properly defined in menu if ('rs-default_groups' == $plugin_page_cr) { add_submenu_page('users.php', __('User Groups', 'scoper'), __('Default Groups', 'scoper'), $cap_req, 'rs-default_groups', array(&$this, 'menu_handler')); } if ('rs-group_members' == $plugin_page_cr) { add_submenu_page('users.php', __('User Groups', 'scoper'), __('Group Members', 'scoper'), $cap_req, 'rs-group_members', array(&$this, 'menu_handler')); } } // the rest of this function pertains to Roles and Restrictions menus if (!$is_user_administrator && !$can_admin_terms && !$is_user_administrator && !$can_admin_objects) { return; } $general_roles = $is_user_administrator; // && scoper_get_option('rs_blog_roles'); // rs_blog_roles option has never been active in any RS release; leave commented here in case need arises // determine the official WP-registered URL for roles and restrictions menus $object_submenus_first = false; $use_users_menu = defined('OZH_MENU_VER') && !defined('SCOPER_FORCE_ROLES_MENU') || defined('SCOPER_FORCE_USERS_MENU'); $tweak_menu = false; if ($use_users_menu) { $roles_menu = 'users.php'; $restrictions_menu = 'users.php'; if ($is_option_administrator) { add_submenu_page($roles_menu, __('Role Options', 'scoper'), __('Role Options', 'scoper'), 'read', 'rs-options', array(&$this, 'menu_handler')); } } else { if (!empty($can_admin_terms['category'])) { $roles_menu = 'rs-category-roles_t'; $restrictions_menu = 'rs-category-restrictions_t'; } elseif (!empty($can_admin_objects['post']['post'])) { $roles_menu = 'rs-post-roles'; $restrictions_menu = 'rs-post-restrictions'; $object_submenus_first = true; } elseif (!empty($can_admin_objects['post']['page'])) { // TODO: handle custom types here? $roles_menu = 'rs-page-roles'; $restrictions_menu = 'rs-page-restrictions'; $object_submenus_first = true; } elseif ($can_admin_terms && $this->scoper->taxonomies->member_property(key($can_admin_terms), 'requires_term')) { $taxonomy = key($can_admin_terms); $roles_menu = "rs-{$taxonomy}-roles_t"; $restrictions_menu = "rs-{$taxonomy}-restrictions_t"; } elseif ($can_admin_objects) { $src_name = key($can_admin_objects); $object_type = key($can_admin_objects[$src_name]); if ($src_name != $object_type && 'post' != $src_name) { $roles_menu = "rs-{$object_type}-roles_{$src_name}"; $restrictions_menu = "rs-{$object_type}-restrictions_{$src_name}"; } else { $roles_menu = "rs-{$object_type}-roles"; $restrictions_menu = "rs-{$object_type}-restrictions"; } $object_submenus_first = true; } else { // shouldn't ever need this $roles_menu = 'rs-roles-post'; $restrictions_menu = 'rs-restrictions-post'; $object_submenus_first = true; } if ($general_roles) { $roles_menu = 'rs-general_roles'; } if ($is_option_administrator) { $roles_menu = 'rs-options'; } // option administrators always have RS Options as top level roles submenu if ($is_user_administrator) { if (empty($restrictions_menu)) { $restrictions_menu = 'rs-category-restrictions_t'; } // If RS Realms are customized, the can_admin_terms / can_admin_objects result can override this default, even for user administrators } // Register the menus with WP using URI and links determined above global $menu; // Manually set menu indexes for positioning below Users menu, // but not if Flutter (a.k.a. Fresh Page) plugin is active. It re-indexes menu items if (!defined('SCOPER_DISABLE_MENU_TWEAK')) { //if ( awp_ver('2.9') ) { // review each WP version for menu indexes until there's a clean way to force menu proximity to 'Users' if (isset($menu[70]) && $menu[70][2] == 'users.php') { // WP 2.9 and 3.0 $tweak_menu = true; $restrictions_menu_key = 71; $roles_menu_key = 72; } //} } $roles_cap = 'read'; // we apply other checks within this function to confirm the menu is valid for current user $restrictions_caption = __('Restrictions', 'scoper'); $roles_caption = __('Roles', 'scoper'); if ($tweak_menu) { add_menu_page($restrictions_caption, __('Restrictions', 'scoper'), 'read', $restrictions_menu, array(&$this, 'menu_handler'), SCOPER_URLPATH . '/admin/images/menu/restrictions.png', $restrictions_menu_key); add_menu_page($roles_caption, __('Roles', 'scoper'), $roles_cap, $roles_menu, array(&$this, 'menu_handler'), SCOPER_URLPATH . '/admin/images/menu/roles.png', $roles_menu_key); } else { add_menu_page($restrictions_caption, __('Restrictions', 'scoper'), 'read', $restrictions_menu, array(&$this, 'menu_handler'), SCOPER_URLPATH . '/admin/images/menu/restrictions.png'); add_menu_page($roles_caption, __('Roles', 'scoper'), $roles_cap, $roles_menu, array(&$this, 'menu_handler'), SCOPER_URLPATH . '/admin/images/menu/roles.png'); } } // endif putting roles and restrictions links in Users menu if ($general_roles) { $menu_label = $use_users_menu ? __('General Roles', 'scoper') : __('General', 'scoper'); add_submenu_page($roles_menu, __('General Roles', 'scoper'), $menu_label, 'read', 'rs-general_roles', array(&$this, 'menu_handler')); } $first_pass = true; $submenu_types = $object_submenus_first ? array('object', 'term') : array('term', 'object'); foreach ($submenu_types as $scope) { if ('term' == $scope) { // Term Roles and Restrictions (will only display objects user can edit) if ($can_admin_terms) { // Will only allow assignment to terms for which current user has admin cap // Term Roles page also prevents assignment or removal of roles current user doesn't have foreach ($this->scoper->taxonomies->get_all() as $taxonomy => $tx) { if (empty($can_admin_terms[$taxonomy])) { continue; } $show_roles_menu = true; $menu_label = $use_users_menu ? sprintf(__('%s Roles', 'scoper'), $tx->labels->singular_name) : $tx->labels->name; add_submenu_page($roles_menu, sprintf(__('%s Roles', 'scoper'), $tx->labels->singular_name), $menu_label, 'read', "rs-{$taxonomy}-roles_t", array(&$this, 'menu_handler')); if (!empty($tx->requires_term)) { $show_restrictions_menu = true; $menu_label = $use_users_menu ? sprintf(__('%s Restrictions', 'scoper'), $tx->labels->singular_name) : $tx->labels->name; add_submenu_page($restrictions_menu, sprintf(__('%s Restrictions', 'scoper'), $tx->labels->singular_name), $menu_label, 'read', "rs-{$taxonomy}-restrictions_t", array(&$this, 'menu_handler')); } } // end foreach taxonomy } // endif can admin terms } else { // Object Roles (will only display objects user can edit) if ($can_admin_objects) { foreach ($this->scoper->data_sources->get_all() as $src_name => $src) { if (!empty($src->no_object_roles) || !empty($src->taxonomy_only) || 'group' == $src_name) { continue; } $object_types = isset($src->object_types) ? $src->object_types : array($src_name => true); foreach (array_keys($object_types) as $object_type) { if (empty($can_admin_objects[$src_name][$object_type])) { continue; } if ($require_blogwide_editor) { if (!$this->scoper->user_can_edit_blogwide($src_name, $object_type, array('require_others_cap' => true))) { continue; } } $show_roles_menu = true; $show_restrictions_menu = true; if ($src_name != $object_type && 'post' != $src_name) { $roles_page = "rs-{$object_type}-roles_{$src_name}"; $restrictions_page = "rs-{$object_type}-restrictions_{$src_name}"; } else { $roles_page = "rs-{$object_type}-roles"; $restrictions_page = "rs-{$object_type}-restrictions"; } $src_otype = isset($src->object_types) ? "{$src_name}:{$object_type}" : $src_name; $item_label_singular = $this->interpret_src_otype($src_otype, 'singular_name'); $item_label = $this->interpret_src_otype($src_otype); $menu_label = $use_users_menu ? sprintf(__('%s Roles', 'scoper'), $item_label_singular) : $item_label; add_submenu_page($roles_menu, sprintf(__('%s Roles', 'scoper'), $item_label_singular), $menu_label, 'read', $roles_page, array(&$this, 'menu_handler')); $menu_label = $use_users_menu ? sprintf(__('%s Restrictions', 'scoper'), $item_label_singular) : $item_label; add_submenu_page($restrictions_menu, sprintf(__('%s Restrictions', 'scoper'), $item_label_singular), $menu_label, 'read', $restrictions_page, array(&$this, 'menu_handler')); } // end foreach obj type } // end foreach data source } // endif can admin objects } // endif drawing object scope submenus } // end foreach submenu scope if ($is_user_administrator) { add_submenu_page($roles_menu, __('About Role Scoper', 'scoper'), __('About', 'scoper'), 'read', 'rs-about', array(&$this, 'menu_handler')); } global $submenu; // Change Role Scoper Options submenu title from default "Roles" to "Options" if ($is_option_administrator) { if (isset($submenu[$roles_menu][0][2]) && $roles_menu == $submenu[$roles_menu][0][2]) { $submenu[$roles_menu][0][0] = __awp('Options'); } // satisfy WordPress' demand that all admin links be properly defined in menu if ('rs-attachments_utility' == $plugin_page_cr) { add_submenu_page($roles_menu, __('Attachment Utility', 'scoper'), __('Attachment Utility', 'scoper'), 'read', 'rs-attachments_utility', array(&$this, 'menu_handler')); } } elseif (empty($show_restrictions_menu) || empty($show_roles_menu)) { // Remove Roles or Restrictions menu if it has no submenu if ($tweak_menu) { // since we forced the menu keys, no need to loop through menu looking for them if (empty($show_restrictions_menu) && isset($menu[$restrictions_menu_key])) { unset($menu[$restrictions_menu_key]); } if (empty($show_roles_menu) && isset($menu[$roles_menu_key])) { unset($menu[$roles_menu_key]); } } else { global $menu; foreach (array_keys($menu) as $key) { if (isset($menu[$key][0])) { if (empty($show_roles_menu) && $roles_caption == $menu[$key][0]) { unset($menu[$key]); } elseif (empty($show_restrictions_menu) && $restrictions_caption == $menu[$key][0]) { unset($menu[$key]); } } } } } // WP MU site options if (!awp_ver('3.1') && $is_option_administrator && IS_MU_RS) { scoper_mu_site_menu(); } // satisfy WordPress' demand that all admin links be properly defined in menu if ('rs-object_role_edit' == $plugin_page_cr) { add_submenu_page($roles_menu, __('Object Role Edit', 'scoper'), __('Object Role Edit', 'scoper'), 'read', 'rs-object_role_edit', array(&$this, 'menu_handler')); } }
function flt_users_where($where, $reqd_caps = '', $object_src_name = '', $object_id = '', $args = array()) { if (!USER_ROLES_RS && !GROUP_ROLES_RS) { return $where; } global $wpdb; static $stored_owner_id; if (!isset($stored_owner_id)) { $stored_owner_id = array(); } $defaults = array('use_term_roles' => 1, 'use_blog_roles' => 1, 'skip_object_roles' => 0, 'querying_groups' => 0, 'ignore_group_roles' => false, 'ignore_user_roles' => false, 'object_type' => '', 'objscope_roles' => '', 'preserve_or_clause' => '', 'enforce_duration_limits' => true, 'enforce_content_date_limits' => true); $args = array_merge($defaults, (array) $args); extract($args); // Default to not honoring custom user caps, but support option $custom_user_blogcaps = SCOPER_CUSTOM_USER_BLOGCAPS; // if reqd_caps are missing, try to determine context from URI if (!$reqd_caps) { return $where; } // no basis for filtering without required caps $reqd_caps = (array) $reqd_caps; // if rolenames are intermingled with caps in reqd_caps array, convert them to caps $reqd_caps = $this->scoper->role_defs->role_handles_to_caps($reqd_caps, true); //arg: also check for unprefixed WP rolenames if ($object_id && !$object_src_name) { $object_id = 0; } if ($object_id) { foreach ($reqd_caps as $cap_name) { if ($meta_caps = apply_filters('map_meta_cap_rs', (array) $cap_name, $cap_name, -1, $object_id)) { $reqd_caps = array_diff($reqd_caps, array($cap_name)); $reqd_caps = array_unique(array_merge($reqd_caps, $meta_caps)); } } if ('post' == $object_src_name && ($use_term_roles || $use_blog_roles)) { if ($post = get_post($object_id)) { $object_date_gmt = $post->post_date_gmt; } } else { $object_date_gmt = ''; } } $owner_has_all_caps = true; // IMPORTANT: set this false downstream as appropriate $rs_where = array(); // Group the required caps by object type (as defined by $scoper->cap_defs). // The 2nd arg causes caps without an otype association to be included with a nullstring src_name key // The 3rd arg forces caps with a data source other than $object_src to be also lumped in with sourceless caps // $caps_by_otype[src_name][object_type] = array of cap names $caps_by_otype = $this->scoper->cap_defs->organize_caps_by_otype($reqd_caps, true, $object_src_name, $object_type); foreach ($caps_by_otype as $src_name => $otypes) { if ($object_type) { $otypes = array_intersect_key($otypes, array($object_type => 1)); } // Cap reqs that pertain to other data sources or have no data source association // will only be satisfied by blog roles. $args['use_term_roles'] = $use_term_roles && $src_name == $object_src_name; $args['skip_object_roles'] = $skip_object_roles || $src_name != $object_src_name; $this_src_object_id = $src_name == $object_src_name ? $object_id : 0; if ($src_name) { if (!($src = $this->scoper->data_sources->get($src_name))) { continue; } $uses_taxonomies = scoper_get_taxonomy_usage($src_name, array_keys($otypes)); if ($this_src_object_id && $args['use_term_roles'] && !empty($uses_taxonomies)) { $args['object_terms'] = array(); foreach ($uses_taxonomies as $taxonomy) { $args['object_terms'][$taxonomy] = $this->scoper->get_terms($taxonomy, UNFILTERED_RS, COL_ID_RS, $this_src_object_id); } } } foreach ($otypes as $object_type => $this_otype_caps) { $qry_roles = array(); $args['use_term_roles'] = $args['use_term_roles'] && scoper_get_otype_option('use_term_roles', $src_name, $object_type); //$caps_by_op = $this->scoper->cap_defs->organize_caps_by_op($this_otype_caps, true); //arg: retain caps which are not scoper-defined //foreach ( $caps_by_op as $op => $this_op_caps ) { foreach ($this_otype_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 = $this_otype_caps; $cap_name = ''; } // 'blog' argument forces inclusion of qualifying WP roles even if scoping with RS roles // (will later strip out non-scopable roles for term role / object role clauses) $args['roles'] = $this->scoper->role_defs->qualify_roles($reqd_caps_arg, '', '', array('all_wp_caps' => true)); if ($args['roles'] || !$src_name) { if (USER_ROLES_RS && !$ignore_user_roles) { $qry_roles[$cap_name]['general'][ROLE_BASIS_USER] = $this->users_queryroles($reqd_caps_arg, $src_name, $this_src_object_id, $args); } if (GROUP_ROLES_RS && !$ignore_group_roles) { $qry_roles[$cap_name]['general'][ROLE_BASIS_GROUPS] = $this->users_queryroles($reqd_caps_arg, $src_name, $this_src_object_id, $args); } } // potentially, a separate set of role clauses for object owner if ($this_src_object_id && $src->cols->owner) { $owner_needs_caps = $this->scoper->cap_defs->get_base_caps($reqd_caps_arg); //returns array of caps the owner needs, after removing those which are credited to owners automatically if ($owner_needs_caps) { $owner_has_all_caps = false; } if ($owner_needs_caps != $reqd_caps_arg) { if (!isset($stored_owner_id[$src_name][$this_src_object_id])) { // DON'T initialize this at top of function $stored_owner_id[$src_name][$this_src_object_id] = scoper_get_var("SELECT {$src->cols->owner} FROM {$src->table} WHERE {$src->cols->id} = '{$object_id}' LIMIT 1"); } if ($stored_owner_id[$src_name][$this_src_object_id]) { $owner_roles = $this->scoper->role_defs->qualify_roles($owner_needs_caps); if ($args['roles'] = array_diff_key($owner_roles, $args['roles'])) { // if owners (needing fewer caps) qualify under different roles than other users: if (GROUP_ROLES_RS && !$ignore_group_roles) { if (!isset($owner_groups)) { $owner_groups = WP_Scoped_User::get_groups_for_user($stored_owner_id[$src_name][$this_src_object_id]); } //$owner_groups = scoper_get_col("SELECT $wpdb->user2group_gid_col FROM $wpdb->user2group_rs WHERE $wpdb->user2group_uid_col = '{$stored_owner_id[$src_name][$this_src_object_id]}'"); if ($owner_groups) { $qry_roles[$cap_name]['owner'][ROLE_BASIS_GROUPS] = $this->users_queryroles($owner_needs_caps, $src_name, $this_src_object_id, $args); } } if (USER_ROLES_RS && !$ignore_user_roles) { $qry_roles[$cap_name]['owner'][ROLE_BASIS_USER] = $this->users_queryroles($owner_needs_caps, $src_name, $this_src_object_id, $args); } } // endif owner needs any caps assigned by role } //endif stored owner_id found } // endif any required caps are automatically granted to owner } // endif request is for a specific object from a data source which stores owner_id // If not supporting custom blogcaps, we actually passed all of this object type's caps together if (!$custom_user_blogcaps) { break; } } // end foreach this_otype_caps //d_echo ('scope data'); //dump($qry_roles); // ------------ Construct this object type's where clause from $qry_roles: ----------------- // ( note: if custom user blogcaps are not enabled, all roles stored into one cap_name dimension ) // $qry_roles[cap_name][general/owner][user/groups]['object'][''] = array of role handles // $qry_roles[cap_name][general/owner][user/groups]['term'][taxonomy] = array of role handles // $qry_roles[cap_name][general/owner][user/groups]['blog'][role_type] = array of role handles // now construct the query for this iteration's operation type $table_aliases = array(ROLE_BASIS_USER => 'uro', ROLE_BASIS_GROUPS => 'gro'); foreach ($qry_roles as $cap_name => $user_types) { // note: default is to put qualifying roles from all reqd_caps into a single "cap_name" element $ot_where = array(); if (!empty($stored_owner_id) && $owner_has_all_caps && USER_ROLES_RS && !$ignore_user_roles) { $ot_where['owner'][ROLE_BASIS_USER] = "uro.user_id = '{$stored_owner_id[$src_name][$this_src_object_id]}'"; } foreach ($user_types as $user_type => $role_bases) { foreach ($role_bases as $role_basis => $scopes) { $alias = $table_aliases[$role_basis]; $content_date_comparison = $enforce_content_date_limits && !empty($object_date_gmt) ? "'{$object_date_gmt}'" : ''; $duration_clause = scoper_get_duration_clause($content_date_comparison, $alias, $enforce_duration_limits); // arg: skip duration clause foreach ($scopes as $scope => $keys) { foreach ($keys as $key => $role_names) { if (empty($role_names)) { continue; } $role_in = "'" . implode("','", $role_names) . "'"; switch ($scope) { case OBJECT_SCOPE_RS: $id_clause = $object_id ? "AND {$alias}.obj_or_term_id = '{$object_id}'" : ''; $ot_where[$user_type][$role_basis][$scope][$key] = "{$alias}.scope = 'object' AND {$alias}.assign_for IN ('entity', 'both') AND {$alias}.src_or_tx_name = '{$src_name}' AND {$alias}.role_type = 'rs' AND {$alias}.role_name IN ({$role_in}) {$duration_clause} {$id_clause}"; break; case TERM_SCOPE_RS: $terms_clause = $object_id && $args['object_terms'][$key] ? "AND {$alias}.obj_or_term_id IN ('" . implode("', '", $args['object_terms'][$key]) . "')" : ''; $ot_where[$user_type][$role_basis][$scope][$key] = "{$alias}.scope = 'term' AND {$alias}.assign_for IN ('entity', 'both') AND {$alias}.src_or_tx_name = '{$key}' {$terms_clause} AND {$alias}.role_type = 'rs' AND {$alias}.role_name IN ({$role_in}) {$duration_clause}"; break; case BLOG_SCOPE_RS: $ot_where[$user_type][$role_basis][$scope][$key] = "{$alias}.scope = 'blog' AND {$alias}.role_type = '{$key}' AND {$alias}.role_name IN ({$role_in}) {$duration_clause}"; break; } // end scope switch } // end foreach key if (!empty($ot_where[$user_type][$role_basis][$scope])) { // [key 1 clause] [OR] [key 2 clause] [OR] ... $ot_where[$user_type][$role_basis][$scope] = agp_implode(' ) OR ( ', $ot_where[$user_type][$role_basis][$scope], ' ( ', ' ) '); } } // end foreach scope if (!empty($ot_where[$user_type][$role_basis])) { // [object scope clauses] [OR] [taxonomy scope clauses] [OR] [blog scope clauses] $ot_where[$user_type][$role_basis] = agp_implode(' ) OR ( ', $ot_where[$user_type][$role_basis], ' ( ', ' ) '); if ('owner' == $user_type) { switch ($role_basis) { case ROLE_BASIS_GROUPS: $ot_where[$user_type][$role_basis] .= "AND gro.group_id IN ('" . implode("', '", $owner_groups) . "')"; break; case ROLE_BASIS_USER: $ot_where[$user_type][$role_basis] .= "AND uro.user_id = '{$stored_owner_id[$src_name][$this_src_object_id]}'"; } // end role basis switch } // endif owner } // endif any role clauses for this user_type/role_basis } // end foreach role basis (user or groups) } // end foreach user type (general or owner) foreach ($ot_where as $user_type => $arr) { foreach ($arr as $role_basis => $val) { if (!empty($ot_where[$user_type])) { // [group role clauses] [OR] [user role clauses] $ot_where[$user_type] = agp_implode(' ) OR ( ', $ot_where[$user_type], ' ( ', ' ) '); } } } if (!empty($ot_where)) { // [general user clauses] [OR] [owner clauses] $rs_where[$src_name][$object_type][$cap_name] = agp_implode(' ) OR ( ', $ot_where, ' ( ', ' ) '); } } // end foreach cap name (for optional support of custom user blogcaps) if (!empty($rs_where[$src_name][$object_type])) { // [cap1 clauses] [AND] [cap2 clauses] $rs_where[$src_name][$object_type] = agp_implode(' ) AND ( ', $rs_where[$src_name][$object_type], ' ( ', ' ) '); } } // end foreach otypes if (isset($rs_where[$src_name])) { // object_type1 clauses [AND] [object_type2 clauses] [AND] ... $rs_where[$src_name] = agp_implode(' ) AND ( ', $rs_where[$src_name], ' ( ', ' ) '); } } // end foreach data source // data_source 1 clauses [AND] [data_source 2 clauses] [AND] ... $rs_where = agp_implode(' ) AND ( ', $rs_where, ' ( ', ' ) '); if ($rs_where) { if (false !== strpos($where, $rs_where)) { return $where; } if (!empty($preserve_or_clause)) { $rs_where = "( ( {$rs_where} ) OR ( {$preserve_or_clause} ) )"; } if ($where) { $where = " AND ( {$rs_where} ) {$where}"; } else { $where = " AND {$rs_where}"; } } else { // if no valid role clauses were constructed, required caps are invalid; no users can do it $where = ' AND 1=2'; } return $where; }
function rs_tally_term_counts(&$terms, $taxonomy, $args = array()) { global $wpdb, $scoper; $defaults = array('pad_counts' => true, 'skip_teaser' => false, 'post_type' => ''); $args = array_merge($defaults, (array) $args); extract($args); if (!$terms) { return; } $term_items = array(); $terms_by_id = array(); foreach ($terms as $key => $term) { $terms_by_id[$term->term_id] =& $terms[$key]; $term_ids[$term->term_taxonomy_id] = $term->term_id; // key and value will match for non-taxonomy category types } $tx_obj = get_taxonomy($taxonomy); $post_types = array_unique((array) $tx_obj->object_type); $enabled_types = array(); foreach ($post_types as $_post_type) { if (scoper_get_otype_option('use_term_roles', 'post', $_post_type)) { $enabled_types[] = $_post_type; } } if (!$enabled_types) { return; } if ($post_type) { $post_type = (array) $post_type; $enabled_types = array_intersect($enabled_types, $post_type); } // Get the object and term ids and stick them in a lookup table $request = "SELECT DISTINCT {$wpdb->posts}.ID, tt.term_taxonomy_id, tt.term_id, tr.object_id" . " FROM {$wpdb->posts}" . " INNER JOIN {$wpdb->term_relationships} AS tr ON {$wpdb->posts}.ID = tr.object_id " . " INNER JOIN {$wpdb->term_taxonomy} AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id " . " WHERE tt.taxonomy = '{$taxonomy}' AND tt.term_id IN ('" . implode("','", $term_ids) . "') " . " AND {$wpdb->posts}.post_type IN ('" . implode("','", $enabled_types) . "')"; // no need to pass any parameters which do not pertain to the objects_request filter $args = array_intersect_key($args, array_flip(array('skip_teaser'))); //$post_type = reset($enabled_types); $post_type = $enabled_types; // note: don't pass in a taxonomies arg because we need to consider restrictions associated with any taxonomy to determine readable objects for terms of this taxonomy $request = apply_filters('objects_request_rs', $request, 'post', $post_type, $args); $results = scoper_get_results($request); foreach ($results as $row) { $id = $term_ids[$row->term_taxonomy_id]; if (isset($term_items[$id][$row->object_id])) { ++$term_items[$id][$row->object_id]; } else { $term_items[$id][$row->object_id] = 1; } } // credit each term for every object contained in any of its descendant terms if ($pad_counts && ScoperAncestry::get_terms_children($taxonomy)) { foreach ($term_ids as $term_id) { $child_term_id = $term_id; while (isset($terms_by_id[$child_term_id]->parent)) { if (!($parent_term_id = $terms_by_id[$child_term_id]->parent)) { break; } if (!empty($term_items[$term_id])) { foreach (array_keys($term_items[$term_id]) as $item_id) { $term_items[$parent_term_id][$item_id] = 1; } } $child_term_id = $parent_term_id; } } } // Tally and apply the item credits foreach ($term_items as $term_id => $items) { if (isset($terms_by_id[$term_id])) { $terms_by_id[$term_id]->count = count($items); } } // update count property for zero-item terms too foreach (array_keys($terms_by_id) as $term_id) { if (!isset($term_items[$term_id])) { if (is_object($terms_by_id[$term_id])) { $terms_by_id[$term_id]->count = 0; } } } }
function doing_teaser($args) { $fields = isset($args['actual_args']['fields']) ? $args['actual_args']['fields'] : $args['fields']; return 'all' == $fields && $GLOBALS['scoper']->is_front() && empty($args['skip_teaser']) && scoper_get_otype_option('do_teaser', 'post'); }
function flt_nav_menu_items($items, $menu_name, $args) { global $wpdb; $item_types = array(); foreach ($items as $key => $item) { if (!isset($item_types[$item->type])) { $item_types["{$item->type}"] = array(); } if (!isset($item_types[$item->type][$item->object])) { $item_types[$item->type][$item->object] = array($key => $item->object_id); } else { $item_types[$item->type][$item->object][$key] = $item->object_id; } } $teaser_enabled = scoper_get_otype_option('do_teaser', 'post'); // remove unreadable terms if (isset($item_types['taxonomy'])) { foreach ($item_types['taxonomy'] as $taxonomy => $item_ids) { if ($teaser_enabled) { if ($taxonomy_obj = get_taxonomy($taxonomy)) { foreach ($taxonomy_obj->object_type as $post_type) { // don't remove a term if it is associated with a post type that's being teased if (scoper_get_otype_option('use_teaser', 'post', $post_type)) { continue 2; } } } } /* $query_base = "SELECT t.term_id FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE 1=1 AND tt.taxonomy = '$taxonomy'"; $query = apply_filters( 'terms_request_rs', $query_base, $taxonomy ); //, array( 'skip_teaser' => true ) ); $okay_ids = scoper_get_col($query); */ $hide_empty = isset($args['hide_empty']) ? $args['hide_empty'] : 0; $okay_ids = get_terms($taxonomy, "fields=ids&hierarchical=0&hide_empty={$hide_empty}"); if ($remove_ids = array_diff($item_ids, $okay_ids)) { $items = array_diff_key($items, $remove_ids); } } } // remove unreadable posts if (isset($item_types['post_type'])) { foreach ($item_types['post_type'] as $post_type => $item_ids) { $where = apply_filters('objects_where_rs', '', 'post', $post_type, array('skip_teaser' => true)); $okay_ids = scoper_get_col("SELECT ID FROM {$wpdb->posts} WHERE post_type = '{$post_type}' {$where} AND ID IN ('" . implode("','", $item_ids) . "')"); if ($remove_ids = array_diff($item_ids, $okay_ids)) { if ($teaser_enabled && scoper_get_otype_option('use_teaser', 'post', $post_type)) { require_once dirname(__FILE__) . '/teaser_rs.php'; $teaser_prepend = ScoperTeaser::get_teaser_text('prepend', 'name', 'post', $post_type); $teaser_append = ScoperTeaser::get_teaser_text('append', 'name', 'post', $post_type); foreach (array_keys($remove_ids) as $key) { $items[$key]->title = $teaser_prepend . $items[$key]->title . $teaser_append; } } else { $items = array_diff_key($items, $remove_ids); } } } } return $items; }
function scoper_admin_section_restrictions($taxonomy) { global $scoper, $scoper_admin; $tx = $scoper->taxonomies->get($taxonomy); if (empty($tx) || empty($tx->requires_term)) { wp_die(__('Invalid taxonomy', 'scoper')); } $is_administrator = is_administrator_rs($tx, 'user'); if (!$scoper_admin->user_can_admin_terms($taxonomy)) { wp_die(__awp('Cheatin’ uh?')); } require_once dirname(__FILE__) . '/admin-bulk_rs.php'; $role_assigner = init_role_assigner(); $nonce_id = 'scoper-assign-roles'; $role_codes = ScoperAdminBulk::get_role_codes(); echo '<a name="scoper_top"></a>'; // retrieve all terms to track hierarchical relationship, even though some may not be adminable by current user $val = ORDERBY_HIERARCHY_RS; $args = array('order_by' => $val); $all_terms = $scoper->get_terms($taxonomy, UNFILTERED_RS, COLS_ALL_RS, 0, $args); // =========================== Submission Handling ========================= if (isset($_POST['rs_submit'])) { $err = ScoperAdminBulk::role_submission(TERM_SCOPE_RS, ROLE_RESTRICTION_RS, '', $taxonomy, $role_codes, '', $nonce_id); if (scoper_get_option('file_filtering')) { scoper_flush_file_rules(); } } else { $err = 0; } // =========================== Prepare Data =============================== $tx_src = $scoper->data_sources->get($tx->source); if ($col_id = $tx_src->cols->id) { // determine which terms current user can admin if ($admin_terms = $scoper->get_terms($taxonomy, ADMIN_TERMS_FILTER_RS, COL_ID_RS)) { $admin_terms = array_fill_keys($admin_terms, true); } } else { $admin_terms = array(); } // =========================== Display UI =============================== ?> <div class="wrap agp-width97"> <?php $tx_label = $tx->labels->singular_name; $src_label = $scoper->data_sources->member_property($tx->object_source, 'labels', 'singular_name'); echo '<h2>' . sprintf(__('%s Restrictions', 'scoper'), $tx_label); echo ' <span style="font-size: 0.6em; font-style: normal">(<a href="#scoper_notes">' . __('see notes', 'scoper') . '</a>)</span></h2>'; if (scoper_get_option('display_hints')) { echo '<div class="rs-hint">'; if ('category' == $taxonomy && scoper_get_otype_option('use_object_roles', 'post', 'post')) { printf(__('Reduce access by requiring some role(s) to be %1$s%2$s-assigned%3$s (or %4$s-assigned). Corresponding General Roles (whether assigned by WordPress or Role Scoper) are ignored.', 'scoper'), "<a href='admin.php?page=rs-{$taxonomy}-roles_t'>", $tx_label, '</a>', $src_label); } else { printf(__('Reduce access by requiring some role(s) to be %1$s%2$s-assigned%3$s. Corresponding General Role assignments are ignored.', 'scoper'), "<a href='admin.php?page=rs-{$taxonomy}-roles_t'>", $tx_label, '</a>'); } echo '</div>'; } if (!($role_defs_by_otype = $scoper->role_defs->get_for_taxonomy($tx->object_source, $taxonomy))) { echo '<br />' . sprintf(__('Role definition error (taxonomy: %s).', 'scoper'), $taxonomy); echo '</div>'; return; } if (empty($admin_terms)) { echo '<br />' . sprintf(__('Either you do not have permission to administer any %s, or none exist.', 'scoper'), $tx->labels->name); echo '</div>'; return; } ?> <form action="" method="post" name="role_scope" id="role_assign"> <?php wp_nonce_field($nonce_id); echo '<br /><div id="rs-term-scroll-links">'; echo ScoperAdminBulkLib::taxonomy_scroll_links($tx, $all_terms, $admin_terms); echo '</div><hr />'; // ============ Assignment Mode Selection Display ================ // TODO: is Link Category label handled without workaround now? $tx_label = agp_strtolower($tx->labels->name); $tx_label_singular = agp_strtolower($tx->labels->singular_name); $parent_col = $tx_src->cols->parent; if (!$parent_col || !empty($tx->uses_standard_schema) && empty($tx->hierarchical)) { $assignment_modes = array(ASSIGN_FOR_ENTITY_RS => sprintf(__('for selected %s', 'scoper'), $tx_label)); } else { $assignment_modes = array(ASSIGN_FOR_ENTITY_RS => sprintf(__('for selected %s', 'scoper'), $tx_label), ASSIGN_FOR_CHILDREN_RS => sprintf(__('for sub-%s of selected', 'scoper'), $tx_label), ASSIGN_FOR_BOTH_RS => sprintf(__('for selected and sub-%s', 'scoper'), $tx_label)); } $max_scopes = array('term' => __('Restrict selected roles', 'scoper'), 'blog' => __('Unrestrict selected roles', 'scoper')); $args = array('max_scopes' => $max_scopes, 'scope' => TERM_SCOPE_RS); ScoperAdminBulk::display_inputs(ROLE_RESTRICTION_RS, $assignment_modes, $args); ScoperAdminBulk::item_tree_jslinks(ROLE_RESTRICTION_RS); // IE (6 at least) won't obey link color directive in a.classname CSS $ie_link_style = strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false ? ' style="color:white;"' : ''; $args = array('include_child_restrictions' => true, 'return_array' => true, 'role_type' => 'rs', 'force_refresh' => true); $strict_terms = $scoper->get_restrictions(TERM_SCOPE_RS, $taxonomy, $args); //strict_terms[taxonomy][role name][term_id] = array: terms which require Role Scoper assignment for specified role (user blog roles ignored, required caps may be supplied by scoper term role or object-specific assignment) // (for other terms, Role Scoper role assignment is optional (term role assignments will supplement blog caps) $editable_roles = array(); foreach ($all_terms as $term) { $id = $term->{$col_id}; foreach ($role_defs_by_otype as $object_type => $role_defs) { foreach (array_keys($role_defs) as $role_handle) { if ($role_assigner->user_has_role_in_term($role_handle, $taxonomy, $id, '', array('src_name' => $tx->object_source, 'object_type' => $object_type))) { $editable_roles[$id][$role_handle] = true; } } } } $default_restrictions = $scoper->get_default_restrictions(TERM_SCOPE_RS); $default_strict_roles = !empty($default_restrictions[$taxonomy]) ? array_flip(array_keys($default_restrictions[$taxonomy])) : array(); $table_captions = ScoperAdminUI::restriction_captions(TERM_SCOPE_RS, $tx, $tx_label_singular, $tx_label); $args = array('admin_items' => $admin_terms, 'editable_roles' => $editable_roles, 'default_strict_roles' => $default_strict_roles, 'ul_class' => 'rs-termlist', 'ie_link_style' => $ie_link_style, 'err' => $err, 'table_captions' => $table_captions); ScoperAdminBulk::item_tree(TERM_SCOPE_RS, ROLE_RESTRICTION_RS, $tx_src, $tx, $all_terms, '', $strict_terms, $role_defs_by_otype, $role_codes, $args); echo '<a href="#scoper_top">' . __('top', 'scoper') . '</a>'; echo '<hr />'; echo '<h4 style="margin-bottom:0.1em"><a name="scoper_notes"></a>' . __("Notes", 'scoper') . ':</h4><ul class="rs-notes">'; $osrc = $scoper->data_sources->get($tx->object_source); if (empty($osrc->no_object_roles)) { echo '<li>'; printf(__('Any %1$s Restriction causes the specified role to be granted only via %1$s Role assignment, regardless of these %2$s settings.', 'scoper'), $osrc->labels->singular_name, $tx->labels->singular_name); echo '</li></ul>'; } ?> </form> </div> <?php }
function flt_get_pages($results, $args = array()) { $results = (array) $results; global $wpdb; // === BEGIN Role Scoper ADDITION: global var; various special case exemption checks === // global $scoper, $current_rs_user; // need to skip cache retrieval if QTranslate is filtering get_pages with a priority of 1 or less $no_cache = !defined('SCOPER_QTRANSLATE_COMPAT') && awp_is_plugin_active('qtranslate'); // buffer titles in case they were filtered previously $titles = scoper_get_property_array($results, 'ID', 'post_title'); // depth is not really a get_pages arg, but remap exclude arg to exclude_tree if wp_list_terms called with depth=1 if (!empty($args['exclude']) && empty($args['exclude_tree']) && !empty($args['depth']) && 1 == $args['depth']) { if (0 !== strpos($args['exclude'], ',')) { // work around wp_list_pages() bug of attaching leading comma if a plugin uses wp_list_pages_excludes filter $args['exclude_tree'] = $args['exclude']; } } // // === END Role Scoper ADDITION === // ================================= $defaults = array('child_of' => 0, 'sort_order' => 'ASC', 'sort_column' => 'post_title', 'hierarchical' => 1, 'exclude' => array(), 'include' => array(), 'meta_key' => '', 'meta_value' => '', 'authors' => '', 'parent' => -1, 'exclude_tree' => '', 'number' => '', 'offset' => 0, 'post_type' => 'page', 'post_status' => 'publish', 'depth' => 0, 'suppress_filters' => 0, 'remap_parents' => -1, 'enforce_actual_depth' => -1, 'remap_thru_excluded_parent' => -1); // Role Scoper arguments added above // === BEGIN Role Scoper ADDITION: support front-end optimization $post_type = isset($args['post_type']) ? $args['post_type'] : $defaults['post_type']; $use_post_types = (array) scoper_get_option('use_post_types'); if (empty($use_post_types[$post_type])) { return $results; } if ($scoper->is_front()) { if ('page' == $post_type && defined('SCOPER_GET_PAGES_LEAN')) { // custom types are likely to have custom fields $defaults['fields'] = "{$wpdb->posts}.ID, {$wpdb->posts}.post_title, {$wpdb->posts}.post_parent, {$wpdb->posts}.post_date, {$wpdb->posts}.post_date_gmt, {$wpdb->posts}.post_status, {$wpdb->posts}.post_name, {$wpdb->posts}.post_modified, {$wpdb->posts}.post_modified_gmt, {$wpdb->posts}.guid, {$wpdb->posts}.menu_order, {$wpdb->posts}.comment_count"; } else { $defaults['fields'] = "{$wpdb->posts}.*"; if (!defined('SCOPER_FORCE_PAGES_CACHE')) { $no_cache = true; } // serialization / unserialization of post_content for all pages is too memory-intensive for sites with a lot of pages } } else { // required for xmlrpc getpagelist method $defaults['fields'] = "{$wpdb->posts}.*"; if (!defined('SCOPER_FORCE_PAGES_CACHE')) { $no_cache = true; } } // === END Role Scoper MODIFICATION === $r = wp_parse_args($args, $defaults); extract($r, EXTR_SKIP); $number = (int) $number; $offset = (int) $offset; $child_of = (int) $child_of; // Role Scoper modification: null value will confuse children array check // Make sure the post type is hierarchical $hierarchical_post_types = get_post_types(array('public' => true, 'hierarchical' => true)); if (!in_array($post_type, $hierarchical_post_types)) { return $results; } // Make sure we have a valid post status if (!in_array($post_status, get_post_stati())) { return $results; } // for the page parent dropdown, return no available selections for a published main page if the logged user isn't allowed to de-associate it from Main if (!empty($name) && 'parent_id' == $name) { global $post; if (!$post->post_parent && !$GLOBALS['scoper_admin_filters']->user_can_associate_main($post_type)) { $status_obj = get_post_status_object($post->post_status); if ($status_obj->public || $status_obj->private) { return array(); } } if (!empty($post) && $post_type == $post->post_type) { if ($post->post_parent) { $append_page = get_post($post->post_parent); } $exclude_tree = $post->ID; } } //$scoper->last_get_pages_args = $r; // don't copy entire args array unless it proves necessary $scoper->last_get_pages_depth = $depth; $scoper->last_get_pages_suppress_filters = $suppress_filters; if ($suppress_filters) { return $results; } // === BEGIN Role Scoper MODIFICATION: wp-cache key and flag specific to access type and user/groups // if (!scoper_get_otype_option('use_object_roles', 'post', $post_type)) { return $results; } $key = md5(serialize(compact(array_keys($defaults)))); $ckey = md5($key . CURRENT_ACCESS_NAME_RS); $cache_flag = 'rs_get_pages'; $cache = $current_rs_user->cache_get($cache_flag); if (false !== $cache) { if (!is_array($cache)) { $cache = array(); } if (!$no_cache && isset($cache[$ckey])) { // alternate filter name (WP core already applied get_pages filter) return apply_filters('get_pages_rs', $cache[$ckey], $r); } } // // === END Role Scoper MODIFICATION === // ==================================== $inclusions = ''; if (!empty($include)) { $child_of = 0; //ignore child_of, parent, exclude, meta_key, and meta_value params if using include $parent = -1; $exclude = ''; $meta_key = ''; $meta_value = ''; $hierarchical = false; $incpages = wp_parse_id_list($include); if (!empty($incpages)) { foreach ($incpages as $incpage) { if (empty($inclusions)) { $inclusions = ' AND ( ID = ' . intval($incpage) . ' '; } else { $inclusions .= ' OR ID = ' . intval($incpage) . ' '; } } } } if (!empty($inclusions)) { $inclusions .= ')'; } $exclusions = ''; if (!empty($exclude)) { $expages = wp_parse_id_list($exclude); if (!empty($expages)) { foreach ($expages as $expage) { if (empty($exclusions)) { $exclusions = ' AND ( ID <> ' . intval($expage) . ' '; } else { $exclusions .= ' AND ID <> ' . intval($expage) . ' '; } } } } if (!empty($exclusions)) { $exclusions .= ')'; } $author_query = ''; if (!empty($authors)) { $post_authors = wp_parse_id_list($authors); if (!empty($post_authors)) { foreach ($post_authors as $post_author) { //Do we have an author id or an author login? if (0 == intval($post_author)) { $post_author = get_userdatabylogin($post_author); if (empty($post_author)) { continue; } if (empty($post_author->ID)) { continue; } $post_author = $post_author->ID; } if ('' == $author_query) { $author_query = ' post_author = ' . intval($post_author) . ' '; } else { $author_query .= ' OR post_author = ' . intval($post_author) . ' '; } } if ('' != $author_query) { $author_query = " AND ({$author_query})"; } } } $join = ''; $where = "{$exclusions} {$inclusions} "; if (!empty($meta_key) || !empty($meta_value)) { $join = " INNER JOIN {$wpdb->postmeta} ON {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id"; // Role Scoper modification: was LEFT JOIN in WP 3.0 core (TODO: would that botch uro join results? // meta_key and meta_value might be slashed $meta_key = stripslashes($meta_key); $meta_value = stripslashes($meta_value); if (!empty($meta_key)) { $where .= $wpdb->prepare(" AND {$wpdb->postmeta}.meta_key = %s", $meta_key); } if (!empty($meta_value)) { $where .= $wpdb->prepare(" AND {$wpdb->postmeta}.meta_value = %s", $meta_value); } } if ($parent >= 0) { $where .= $wpdb->prepare(' AND post_parent = %d ', $parent); } // === BEGIN Role Scoper MODIFICATION: // allow pages of multiple statuses to be displayed (requires default status=publish to be ignored) // $where_post_type = $wpdb->prepare("post_type = '%s'", $post_type); $where_status = ''; $is_front = $scoper->is_front(); $is_teaser_active = $scoper->is_front() && scoper_get_otype_option('do_teaser', 'post') && scoper_get_otype_option('use_teaser', 'post', $post_type); $private_teaser = $is_teaser_active && scoper_get_otype_option('use_teaser', 'post', $post_type) && !scoper_get_otype_option('teaser_hide_private', 'post', $post_type); if ($is_front && (!empty($current_rs_user->ID) || $private_teaser)) { $frontend_list_private = scoper_get_otype_option('private_items_listable', 'post', 'page'); } else { $frontend_list_private = false; } // WP core does not include private pages in query. Include private statuses in anticipation of user-specific filtering if ($post_status && ('publish' != $post_status || $is_front && !$frontend_list_private)) { $where_status = $wpdb->prepare("post_status = '%s'", $post_status); } else { // since we will be applying status clauses based on content-specific roles and restrictions, only a sanity check safeguard is needed when post_status is unspecified or defaulted to "publish" $safeguard_statuses = array(); foreach (get_post_stati(array('internal' => false), 'object') as $status_name => $status_obj) { if (!$is_front || $status_obj->private || $status_obj->public) { $safeguard_statuses[] = $status_name; } } $where_status = "post_status IN ('" . implode("','", $safeguard_statuses) . "')"; } $query = "SELECT {$fields} FROM {$wpdb->posts} {$join} WHERE 1=1 AND {$where_post_type} AND ( {$where_status} {$where} {$author_query} ) ORDER BY {$sort_column} {$sort_order}"; if (!empty($number)) { $query .= ' LIMIT ' . $offset . ',' . $number; } if ($is_teaser_active && !defined('SCOPER_TEASER_HIDE_PAGE_LISTING')) { // We are in the front end and the teaser is enabled for pages $query = apply_filters('objects_request_rs', $query, 'post', $post_type, array('force_teaser' => true)); $pages = scoper_get_results($query); // execute unfiltered query // Pass results of unfiltered query through the teaser filter. // If listing private pages is disabled, they will be omitted completely, but restricted published pages // will still be teased. This is a slight design compromise to satisfy potentially conflicting user goals without yet another option $pages = apply_filters('objects_results_rs', $pages, 'post', (array) $post_type, array('request' => $query, 'force_teaser' => true, 'object_type' => $post_type)); // restore buffered titles in case they were filtered previously scoper_restore_property_array($pages, $titles, 'ID', 'post_title'); $pages = apply_filters('objects_teaser_rs', $pages, 'post', $post_type, array('request' => $query, 'force_teaser' => true)); if ($frontend_list_private) { if (!scoper_get_otype_option('teaser_hide_private', 'post', $post_type)) { $tease_all = true; } } } else { $_args = array('skip_teaser' => true); if (in_array($GLOBALS['pagenow'], array('post.php', 'post-new.php'))) { if ($post_type_obj = get_post_type_object($post_type)) { $plural_name = plural_name_from_cap_rs($post_type_obj); $_args['alternate_reqd_caps'][0] = array("create_child_{$plural_name}"); } } // Pass query through the request filter $query = apply_filters('objects_request_rs', $query, 'post', $post_type, $_args); // Execute the filtered query $pages = scoper_get_results($query); // restore buffered titles in case they were filtered previously scoper_restore_property_array($pages, $titles, 'ID', 'post_title'); } if (empty($pages)) { // alternate hook name (WP core already applied get_pages filter) return apply_filters('get_pages_rs', array(), $r); } // // === END Role Scoper MODIFICATION === // ==================================== // Role Scoper note: WP core get_pages has already updated wp_cache and pagecache with unfiltered results. update_page_cache($pages); // === BEGIN Role Scoper MODIFICATION: Support a disjointed pages tree with some parents hidden ======== if ($child_of || empty($tease_all)) { // if we're including all pages with teaser, no need to continue thru tree remapping $ancestors = ScoperAncestry::get_page_ancestors(); // array of all ancestor IDs for keyed page_id, with direct parent first $orderby = $sort_column; if ($parent > 0 || !$hierarchical) { $remap_parents = false; } else { // if these settings were passed into this get_pages call, use them if (-1 === $remap_parents) { $remap_parents = scoper_get_option('remap_page_parents'); } if ($remap_parents) { if (-1 === $enforce_actual_depth) { $enforce_actual_depth = scoper_get_option('enforce_actual_page_depth'); } if (-1 === $remap_thru_excluded_parent) { $remap_thru_excluded_parent = scoper_get_option('remap_thru_excluded_page_parent'); } } } $remap_args = compact('child_of', 'parent', 'exclude', 'depth', 'orderby', 'remap_parents', 'enforce_actual_depth', 'remap_thru_excluded_parent'); // one or more of these args may have been modified after extraction ScoperHardway::remap_tree($pages, $ancestors, 'ID', 'post_parent', $remap_args); } // === END Role Scoper MODIFICATION === // ==================================== if (!empty($exclude_tree)) { $exclude = array(); $exclude = (int) $exclude_tree; $children = get_page_children($exclude, $pages); // RS note: okay to use unfiltered function here since it's only used for excluding $excludes = array(); foreach ($children as $child) { $excludes[] = $child->ID; } $excludes[] = $exclude; $total = count($pages); for ($i = 0; $i < $total; $i++) { if (in_array($pages[$i]->ID, $excludes)) { unset($pages[$i]); } } } if (!empty($append_page) && !empty($pages)) { $found = false; foreach (array_keys($pages) as $key) { if ($post->post_parent == $pages[$key]->ID) { $found = true; break; } } if (empty($found)) { $pages[] = $append_page; } } // re-index the array, just in case anyone cares $pages = array_values($pages); // === BEGIN Role Scoper MODIFICATION: cache key and flag specific to access type and user/groups // if (!$no_cache) { $cache[$ckey] = $pages; $current_rs_user->cache_set($cache, $cache_flag); } // alternate hook name (WP core already applied get_pages filter) $pages = apply_filters('get_pages_rs', $pages, $r); // // === END Role Scoper MODIFICATION === // ==================================== return $pages; }
function flt_get_terms($results, $taxonomies, $args) { global $wpdb; $empty_array = array(); //d_echo( 'flt_get_terms input:' ); $single_taxonomy = false; if (!is_array($taxonomies)) { $single_taxonomy = true; $taxonomies = array($taxonomies); } elseif (count($taxonomies) < 2) { $single_taxonomy = true; } // === END Role Scoper MODIFICATION === foreach ((array) $taxonomies as $taxonomy) { if (!taxonomy_exists($taxonomy)) { // === BEGIN Role Scoper MODIFICATION: this caused plugin activation error in some situations (though at that time, the error object was created and return on a single line, not byRef as now) === // //$error = & new WP_Error('invalid_taxonomy', __awp('Invalid Taxonomy')); //return $error; return array(); // // === END Role Scoper MODIFICATION === } } // === BEGIN Role Scoper ADDITION: global var; various special case exemption checks === // global $scoper; if ($tx_obj = get_taxonomy($taxonomies[0])) { // don't require use_taxonomies setting for link_categories or other non-post taxonomies if (array_intersect($tx_obj->object_type, get_post_types(array('public' => true)))) { $use_taxonomies = scoper_get_option('use_taxonomies'); if (empty($use_taxonomies[$taxonomy])) { return $results; } } } // no backend filter for administrators $parent_or = ''; if (is_admin() || defined('XMLRPC_REQUEST')) { if (is_content_administrator_rs()) { return $results; } else { if ($tx = $scoper->taxonomies->get($taxonomies[0])) { // is a Category Edit form being displayed? if (!empty($tx->uri_vars)) { $term_id = $scoper->data_sources->detect('id', $tx); } else { $term_id = $scoper->data_sources->detect('id', $tx->source); } if ($term_id) { // don't filter current parent category out of selection UI even if current user can't manage it $parent_or = " OR t.term_id = (SELECT parent FROM {$wpdb->term_taxonomy} WHERE term_id = '{$term_id}') "; } } } } // need to skip cache retrieval if QTranslate is filtering get_terms with a priority of 1 or less static $no_cache; if (!isset($no_cache)) { $no_cache = defined('SCOPER_NO_TERMS_CACHE') || !defined('SCOPER_QTRANSLATE_COMPAT') && awp_is_plugin_active('qtranslate'); } // this filter currently only supports a single taxonomy for each get_terms call // (although the terms_where filter does support multiple taxonomies and this function could be made to do so) if (!$single_taxonomy) { return $results; } // link category roles / restrictions are only scoped for management (TODO: abstract this) if ($single_taxonomy && 'link_category' == $taxonomies[0] && $scoper->is_front()) { return $results; } // depth is not really a get_terms arg, but remap exclude arg to exclude_tree if wp_list_terms called with depth=1 if (!empty($args['exclude']) && empty($args['exclude_tree']) && !empty($args['depth']) && 1 == $args['depth']) { $args['exclude_tree'] = $args['exclude']; } // don't offer to set a category as its own parent if ('edit-tags.php' == $GLOBALS['pagenow']) { if ($tx_obj->hierarchical) { if ($editing_cat_id = $scoper->data_sources->get_from_uri('id', 'term')) { if (!empty($args['exclude'])) { $args['exclude'] .= ','; } $args['exclude'] .= $editing_cat_id; } } } // we'll need this array in most cases, to support a disjointed tree with some parents missing (note alternate function call - was _get_term_hierarchy) $children = ScoperAncestry::get_terms_children($taxonomies[0]); // // === END Role Scoper ADDITION === // ================================= $in_taxonomies = "'" . implode("', '", $taxonomies) . "'"; $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'exclude' => '', 'exclude_tree' => '', 'include' => '', 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'pad_counts' => false, 'offset' => '', 'search' => '', 'skip_teaser' => false, 'depth' => 0, 'remap_parents' => -1, 'enforce_actual_depth' => -1, 'remap_thru_excluded_parent' => -1, 'post_type' => ''); // Role Scoper arguments added above $args = wp_parse_args($args, $defaults); $args['number'] = (int) $args['number']; $args['offset'] = absint($args['offset']); $args['child_of'] = (int) $args['child_of']; // Role Scoper modification: null value will confuse children array check if (!$single_taxonomy || !is_taxonomy_hierarchical($taxonomies[0]) || '' !== $args['parent']) { $args['child_of'] = 0; $args['hierarchical'] = false; $args['pad_counts'] = false; } if ('all' == $args['get']) { $args['child_of'] = 0; $args['hide_empty'] = 0; $args['hierarchical'] = false; $args['pad_counts'] = false; } extract($args, EXTR_SKIP); // === BEGIN Role Scoper MODIFICATION: use the $children array we already have === // if ('nav-menus.php' == $GLOBALS['pagenow']) { if ('nav_menu' != $taxonomies[0]) { if (!scoper_get_option('admin_nav_menu_filter_items')) { return $results; } else { $hide_empty = 1; } } } if ($child_of && !isset($children[$child_of])) { return array(); } if ($parent && !isset($children[$parent])) { return array(); } if ($post_type && is_string($post_type)) { $post_type = explode(',', $post_type); } // // === END Role Scoper MODIFICATION === // ==================================== $is_term_admin = in_array($GLOBALS['pagenow'], array('edit-tags.php', 'edit-link-categories.php')); $filter_key = has_filter('list_terms_exclusions') ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : ''; $key = md5(serialize(compact(array_keys($defaults))) . serialize($taxonomies) . $filter_key); // === BEGIN Role Scoper MODIFICATION: cache key specific to access type and user/groups === // support Quick Post Widget plugin if (isset($name) && 'quick_post_cat' == $name) { $required_operation = 'edit'; $post_type = 'post'; $remap_parents = true; } elseif (isset($name) && 'quick_post_new_cat_parent' == $name) { $is_term_admin = true; $required_operation = ''; $remap_parents = true; } else { $required_operation = ''; } $object_src_name = $scoper->taxonomies->member_property($taxonomies[0], 'object_source', 'name'); $ckey = md5($key . serialize($scoper->get_terms_reqd_caps($taxonomies[0], $required_operation, $is_term_admin))); global $current_rs_user; $cache_flag = 'rs_get_terms'; $cache = $current_rs_user->cache_get($cache_flag); if (false !== $cache) { if (!is_array($cache)) { $cache = array(); } if (!$no_cache && isset($cache[$ckey])) { // RS Modification: alternate filter name (get_terms filter is already applied by WP) remove_filter('get_terms', array('ScoperHardwayTaxonomy', 'flt_get_terms'), 0, 3); $terms = apply_filters('get_terms', $cache[$ckey], $taxonomies, $args); $terms = apply_filters('get_terms_rs', $terms, $taxonomies, $args); add_filter('get_terms', array('ScoperHardwayTaxonomy', 'flt_get_terms'), 0, 3); return $terms; } } // buffer term names in case they were filtered previously if ('all' == $fields) { $term_names = scoper_get_property_array($results, 'term_id', 'name'); } // // === END Role Scoper MODIFICATION === // ===================================== $_orderby = strtolower($orderby); if ('count' == $_orderby) { $orderby = 'tt.count'; } else { if ('name' == $_orderby) { $orderby = 't.name'; } else { if ('slug' == $_orderby) { $orderby = 't.slug'; } else { if ('term_group' == $_orderby) { $orderby = 't.term_group'; } else { if ('none' == $_orderby) { $orderby = ''; $order = ''; } else { if (empty($_orderby) || 'id' == $_orderby) { $orderby = 't.term_id'; } elseif ('order' == $_orderby) { $orderby = 't.term_order'; } else { $orderby = 't.name'; } } } } } } $orderby = apply_filters('get_terms_orderby', $orderby, $args); if (!empty($orderby)) { $orderby = "ORDER BY {$orderby}"; } $where = ''; // === Role Scoper MODIFICATION: if an include argument is provided, strip out non-matching terms after filtering is done. === /* $inclusions = ''; if ( !empty($include) ) { $exclude = ''; $exclude_tree = ''; $interms = wp_parse_id_list($include); if ( count($interms) ) { foreach ( $interms as $interm ) { if (empty($inclusions)) $inclusions = ' AND ( t.term_id = ' . intval($interm) . ' '; else $inclusions .= ' OR t.term_id = ' . intval($interm) . ' '; } } } if ( !empty($inclusions) ) $inclusions .= ')'; $where .= $inclusions; */ // === END Role Scoper MODIFICATION === $exclusions = ''; if (!empty($exclude_tree)) { // === BEGIN Role Scoper MODIFICATION: temporarily unhook this filter for unfiltered get_terms calls === remove_filter('get_terms', array('ScoperHardwayTaxonomy', 'flt_get_terms'), 0, 3); // === END Role Scoper MODIFICATION === $excluded_trunks = wp_parse_id_list($exclude_tree); foreach ((array) $excluded_trunks as $extrunk) { $excluded_children = (array) get_terms($taxonomies[0], array('child_of' => intval($extrunk), 'fields' => 'ids')); $excluded_children[] = $extrunk; foreach ($excluded_children as $exterm) { if (empty($exclusions)) { $exclusions = ' AND ( t.term_id <> ' . intval($exterm) . ' '; } else { $exclusions .= ' AND t.term_id <> ' . intval($exterm) . ' '; } } } // === BEGIN Role Scoper MODIFICATION: re-hook this filter add_filter('get_terms', array('ScoperHardwayTaxonomy', 'flt_get_terms'), 0, 3); // === END Role Scoper MODIFICATION === } if (!empty($exclude)) { $exterms = wp_parse_id_list($exclude); foreach ($exterms as $exterm) { if (empty($exclusions)) { $exclusions = ' AND ( t.term_id <> "' . intval($exterm) . '" '; } else { $exclusions .= ' AND t.term_id <> "' . intval($exterm) . '" '; } } } if (!empty($exclusions)) { $exclusions .= ')'; } // WPML attempts to pull taxonomy out of debug_backtrace() unless set in $_GET or $_POST; previous filter execution throws it off if (defined('ICL_SITEPRESS_VERSION') && !isset($_GET['taxonomy'])) { $_GET['taxonomy'] = current($taxonomies); } $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args); $where .= $exclusions; if (!empty($slug)) { $slug = sanitize_title($slug); $where .= " AND t.slug = '{$slug}'"; } if (!empty($name__like)) { $where .= " AND t.name LIKE '{$name__like}%'"; } if ('' !== $parent) { $parent = (int) $parent; // === BEGIN Role Scoper MODIFICATION: otherwise termroles only work if parent terms also have role if ($parent || 'ids' != $fields) { $where .= " AND tt.parent = '{$parent}'"; } // === END Role Scoper MODIFICATION === } // === BEGIN Role Scoper MODIFICATION: instead, manually remove truly empty cats at the bottom of this function, so we don't exclude cats with private but readable posts //if ( $hide_empty && !$hierarchical ) // $where .= ' AND tt.count > 0'; // === END Role Scoper MODIFICATION === // don't limit the query results when we have to descend the family tree if (!empty($number) && !$hierarchical && empty($child_of) && '' == $parent) { if ($offset) { $limit = 'LIMIT ' . $offset . ',' . $number; } else { $limit = 'LIMIT ' . $number; } } else { $limit = ''; } if (!empty($search)) { $search = like_escape($search); $where .= " AND (t.name LIKE '%{$search}%')"; } $selects = array(); switch ($fields) { case 'all': $selects = array('t.*', 'tt.*'); break; case 'ids': case 'id=>parent': $selects = array('t.term_id', 'tt.term_taxonomy_id', 'tt.parent', 'tt.count'); break; case 'names': $selects = array('t.term_id', 'tt.term_taxonomy_id', 'tt.parent', 'tt.count', 't.name'); break; case 'count': $orderby = ''; $order = ''; $selects = array('COUNT(*)'); } $select_this = implode(', ', apply_filters('get_terms_fields', $selects, $args)); // === BEGIN Role Scoper MODIFICATION: run the query through scoping filter // $query_base = "SELECT DISTINCT {$select_this} FROM {$wpdb->terms} AS t INNER JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id WHERE 1=1 AND tt.taxonomy IN ({$in_taxonomies}) {$where} {$parent_or} {$orderby} {$order} {$limit}"; // only force application of scoped query filter if we're NOT doing a teaser if ('all' == $fields) { $do_teaser = $scoper->is_front() && empty($skip_teaser) && scoper_get_otype_option('do_teaser', 'post'); } else { $do_teaser = false; } $query = apply_filters('terms_request_rs', $query_base, $taxonomies[0], array('skip_teaser' => !$do_teaser, 'is_term_admin' => $is_term_admin, 'required_operation' => $required_operation, 'post_type' => $post_type)); // if no filering was applied because the teaser is enabled, prevent a redundant query if (!empty($exclude_tree) || $query_base != $query || $parent || 'all' != $fields) { $terms = scoper_get_results($query); } else { $terms = $results; } if ('count' == $fields) { $term_count = $wpdb->get_var($query); return $term_count; } if ('all' == $fields && empty($include)) { update_term_cache($terms); } // RS: don't cache an empty array, just in case something went wrong if (empty($terms)) { return array(); } // // === END Role Scoper MODIFICATION === // ==================================== // === BEGIN Role Scoper ADDITION: Support a disjointed terms tree with some parents hidden // if ('all' == $fields) { $ancestors = ScoperAncestry::get_term_ancestors($taxonomy); // array of all ancestor IDs for keyed term_id, with direct parent first if ($parent > 0 || !$hierarchical) { // in Category Edit form, need to list all editable cats even if parent is not editable $remap_parents = false; $enforce_actual_depth = true; $remap_thru_excluded_parent = false; } else { // if these settings were passed into this get_terms call, use them if (is_admin()) { $remap_parents = true; } else { if (-1 === $remap_parents) { $remap_parents = scoper_get_option('remap_term_parents'); } if ($remap_parents) { if (-1 === $enforce_actual_depth) { $enforce_actual_depth = scoper_get_option('enforce_actual_term_depth'); } if (-1 === $remap_thru_excluded_parent) { $remap_thru_excluded_parent = scoper_get_option('remap_thru_excluded_term_parent'); } } } } $remap_args = compact('child_of', 'parent', 'depth', 'orderby', 'remap_parents', 'enforce_actual_depth', 'remap_thru_excluded_parent'); // one or more of these args may have been modified after extraction ScoperHardway::remap_tree($terms, $ancestors, 'term_id', 'parent', $remap_args); } // // === END Role Scoper ADDITION === // ================================ // === BEGIN Role Scoper MODIFICATION: call alternate functions // rs_tally_term_counts() replaces _pad_term_counts() // rs_get_term_descendants replaces _get_term_children() // if (($child_of || $hierarchical) && !empty($children)) { $terms = rs_get_term_descendants($child_of, $terms, $taxonomies[0]); } if (!$terms) { return array(); } // Replace DB-stored term counts with actual number of posts this user can read. // In addition, without the rs_tally_term_counts call, WP will hide categories that have no public posts (even if this user can read some of the pvt posts). // Post counts will be incremented to include child categories only if $pad_counts is true if (!defined('XMLRPC_REQUEST') && in_array($fields, array('all', 'ids', 'names')) && !$is_term_admin) { if (!is_admin() || !in_array($GLOBALS['pagenow'], array('post.php', 'post-new.php'))) { //-- RoleScoper Modification - alternate function call (was _pad_term_counts) --// rs_tally_term_counts($terms, $taxonomies[0], array('pad_counts' => $pad_counts, 'skip_teaser' => !$do_teaser, 'post_type' => $post_type)); } } // Make sure we show empty categories that have children. if ($hierarchical && $hide_empty) { foreach ($terms as $k => $term) { if (!$term->count) { //-- RoleScoper Modification - call alternate function (was _get_term_children) --// if ($children = rs_get_term_descendants($term->term_id, $terms, $taxonomies[0])) { foreach ($children as $child) { if ($child->count) { continue 2; } } } // It really is empty unset($terms[$k]); } } } reset($terms); // // === END Role Scoper MODIFICATION === // ==================================== // === BEGIN Role Scoper ADDITION: hide empty cats based on actual query result instead of 'count > 0' clause, so we don't exclude cats with private but readable posts if ($terms && empty($hierarchical) && !empty($hide_empty)) { foreach ($terms as $key => $term) { if (!$term->count) { unset($terms[$key]); } } } // // === END Role Scoper ADDITION === // ================================ if (!empty($include)) { $interms = wp_parse_id_list($include); foreach ($terms as $key => $term) { if (!in_array($term->term_id, $interms)) { unset($terms[$key]); } } } $_terms = array(); if ('id=>parent' == $fields) { while ($term = array_shift($terms)) { $_terms[$term->term_id] = $term->parent; } $terms = $_terms; } elseif ('ids' == $fields) { while ($term = array_shift($terms)) { $_terms[] = $term->term_id; } $terms = $_terms; } elseif ('names' == $fields) { while ($term = array_shift($terms)) { $_terms[] = $term->name; } $terms = $_terms; } if (0 < $number && intval(@count($terms)) > $number) { $terms = array_slice($terms, $offset, $number); } // === BEGIN Role Scoper MODIFICATION: cache key is specific to user/group // if (!$no_cache) { $cache[$ckey] = $terms; $current_rs_user->cache_set($cache, $cache_flag); } // RS Modification: alternate filter name (get_terms filter is already applied by WP) remove_filter('get_terms', array('ScoperHardwayTaxonomy', 'flt_get_terms'), 0, 3); $terms = apply_filters('get_terms', $terms, $taxonomies, $args); $terms = apply_filters('get_terms_rs', $terms, $taxonomies, $args); add_filter('get_terms', array('ScoperHardwayTaxonomy', 'flt_get_terms'), 0, 3); // restore buffered term names in case they were filtered previously if ('all' == $fields) { scoper_restore_property_array($terms, $term_names, 'term_id', 'name'); } // // === END Role Scoper MODIFICATION === // ==================================== //dump($terms); return $terms; }
function parse_query_for_direct_access(&$query) { if (empty($query->query_vars['attachment']) || false === strpos($_SERVER['QUERY_STRING'], 'rs_rewrite')) { return; } $file = $query->query_vars['attachment']; require_once dirname(__FILE__) . '/uploads_rs.php'; $uploads = scoper_get_upload_info(); $return_attachment_id = 0; $matched_published_post = array(); if (AttachmentFilters_RS::_user_can_read_file($file, $return_attachment_id, $matched_published_post, $uploads)) { agp_return_file($file, $return_attachment_id); return; } // File access was not granted. Since a 404 page will now be displayed, add filters which (for performance) were suppressed on the direct file access request global $scoper; $scoper->direct_file_access = false; $scoper->add_main_filters(); $scoper->add_hardway_filters(); //Determine if teaser message should be triggered if (file_exists($uploads['basedir'] . "/{$file}")) { if ($matched_published_post && scoper_get_otype_option('do_teaser', 'post')) { foreach (array_keys($matched_published_post) as $object_type) { if ($use_teaser_type = scoper_get_otype_option('use_teaser', 'post', $object_type)) { if ($matched_published_post[$object_type]) { if (!defined('SCOPER_QUIET_FILE_404')) { // note: subsequent act_attachment_access will call impose_post_teaser() $will_tease = true; // will_tease flag only used within this function $wp_query->query_vars['attachment'] = $matched_published_post[$object_type]; break; } } } } } status_header(401); // Unauthorized if (empty($will_tease)) { // User is not qualified to access the requested attachment, and no teaser will apply // Normally, allow the function to return for WordPress 404 handling // But end script execution here if requested attachment is a media type (or if definition set) // Linking pages won't want WP html returned in place of inaccessable image / video if (defined('SCOPER_QUIET_FILE_404')) { exit; } // this may not be necessary $wp_query->is_404 = true; $wp_query->is_single = true; $wp_query->is_singular = true; $wp_query->query_vars['is_single'] = true; } } }
function add_meta_boxes() { /* // optional hack to prevent role assignment boxes for non-Editors // // This is now handled as a Role Scoper Option. // On the Advanced tab, Hidden Editing Elements section: select "Role administration requires a blog-wide Editor role" // // end optional hack */ // ========= register WP-rendered metaboxes ============ $src_name = 'post'; // TODO: different handling for edit-tags.php $object_type = cr_find_post_type(); $require_blogwide_editor = scoper_get_option('role_admin_blogwide_editor_only'); if ('admin' == $require_blogwide_editor && !is_user_administrator_rs()) { return; } if ('admin_content' == $require_blogwide_editor && !is_content_administrator_rs()) { return; } if (!scoper_get_otype_option('use_object_roles', $src_name, $object_type)) { return; } if ($require_blogwide_editor) { if (!$this->scoper->user_can_edit_blogwide($src_name, $object_type, array('require_others_cap' => true))) { return; } } $role_defs = $this->scoper->role_defs->get_matching('rs', $src_name, $object_type); foreach ($role_defs as $role_handle => $role_def) { if (!isset($role_def->valid_scopes[OBJECT_SCOPE_RS])) { continue; } $box_id = $role_handle; add_meta_box($box_id, $this->scoper->role_defs->get_abbrev($role_handle, OBJECT_UI_RS), array(&$this, 'draw_object_roles_content'), $object_type); $this->meta_box_ids[$role_handle] = $box_id; } }
function single_object_roles_ui($src_name, $object_type, $object_id, $args = array()) { $defaults = array('html_inserts' => ''); $args = array_merge($defaults, (array) $args); extract($args); if (!scoper_get_otype_option('use_object_roles', $src_name, $object_type)) { return; } if (!$html_inserts) { if (!($otype_def = $this->scoper->data_sources->member_property($src_name, 'object_types', $object_type))) { return; } if (isset($otype_def->admin_inserts->bottom)) { $html_inserts = $otype_def->admin_inserts->bottom; } elseif (!($html_inserts = $src->admin_inserts->bottom)) { if (!is_object($html_inserts)) { $html_inserts = (object) array(); } // TODO: CSS $html_inserts->open = (object) array('container' => '<br />', 'headline' => '<h3 style="margin-bottom: 0">', 'content' => '<div style="border:2px solid #ccc; margin-top: 0; padding: 0 0.2em 0 0.2em;">'); $html_inserts->close = (object) array('container' => '', 'headline' => '</h3>', 'content' => '</div>'); } } if (!isset($this->all_agents) || $src_name != $this->loaded_src_name || $object_type != $this->loaded_object_type || $object_id != $this->loaded_object_id) { $this->load_roles($src_name, $object_type, $object_id); } $group_members = array(); $role_defs = $this->scoper->role_defs->get_matching('rs', $src_name, $object_type); foreach ($role_defs as $role_handle => $role_def) { if (!isset($role_def->valid_scopes[OBJECT_SCOPE_RS]) || !$this->scoper_admin->user_can_admin_role($role_handle, $object_id, $src_name, $object_type)) { continue; } echo "\r\n" . sprintf($html_inserts->open->container, "objrole_{$role_handle}"); $count_sfx = $this->get_rolecount_caption($role_handle); echo $html_inserts->open->headline . $this->scoper->role_defs->get_abbrev($role_handle, OBJECT_UI_RS) . $count_sfx . $html_inserts->close->headline . $html_inserts->open->content; $this->draw_object_roles_content($src_name, $object_type, $role_handle, $object_id, true); // arg: skip_user_validation echo "\r\n" . $html_inserts->close->content . "\r\n" . $html_inserts->close->container . "\r\n"; } // end foreach role }
function ui_hide_add_menu() { $tx_obj = get_taxonomy('nav_menu'); if (!empty($GLOBALS['current_user']->allcaps['edit_theme_options'])) { $use_term_roles = scoper_get_otype_option('use_term_roles', 'post'); if (empty($use_term_roles['nav_menu'])) { return; } } if (cr_user_can($tx_obj->cap->manage_terms, BLOG_SCOPE_RS)) { return; } ?> <script type="text/javascript"> /* <![CDATA[ */ jQuery(document).ready( function($) { $('.menu-add-new').hide(); }); /* ]]> */ </script> <?php }
function get_teaser_text($teaser_operation, $variable, $src_name, $object_type, $user = '') { if (!is_object($user)) { global $current_user; $user = $current_user; } $anon = $user->ID == 0 ? '_anon' : ''; if ($msg = scoper_get_otype_option("teaser_{$teaser_operation}_{$variable}{$anon}", 'post', $object_type, CURRENT_ACCESS_NAME_RS)) { if (defined('SCOPER_TRANSLATE_TEASER')) { scoper_load_textdomain(); // otherwise this is only loaded for wp-admin $msg = translate($msg, 'scoper'); if (!empty($msg) && !is_null($msg) && is_string($msg)) { $msg = htmlspecialchars_decode($msg); } } return $msg; } }
function act_check_admin_referer($referer_name) { if (!empty($_POST['tag_ID']) && 'update-tag_' . $_POST['tag_ID'] == $referer_name) { // filter category parent selection for Category editing if (!isset($_POST['tag_ID'])) { return; } $taxonomy = $_POST['taxonomy']; if (!($tx = get_taxonomy($taxonomy))) { return; } if (!$tx->hierarchical) { return; } $stored_term = get_term_by('id', $_POST['tag_ID'], $taxonomy); $selected_parent = $_POST['parent']; if (-1 == $selected_parent) { $selected_parent = 0; } if ($stored_term->parent != $selected_parent) { global $scoper; if ($tx_obj = get_taxonomy($taxonomy)) { if ($selected_parent) { $user_terms = $scoper->qualify_terms($tx_obj->cap->manage_terms, $taxonomy); $permit = in_array($selected_parent, $user_terms); } else { $permit = cr_user_can($tx_obj->cap->manage_terms, 0, 0, array('skip_id_generation' => true, 'skip_any_term_check' => true)); } } if (!$permit) { wp_die(__('You do not have permission to select that Category Parent', 'scoper')); } } } elseif ('update-nav_menu' == $referer_name) { $tx = get_taxonomy('nav_menu'); $use_term_roles = scoper_get_otype_option('use_term_roles', 'post', 'nav_menu'); if (empty($GLOBALS['current_user']->allcaps['edit_theme_options']) || !empty($use_term_roles['nav_menu'])) { if (!cr_user_can($tx->cap->manage_terms, $_REQUEST['menu'], 0, array('skip_id_generation' => true, 'skip_any_term_check' => true))) { if ($_REQUEST['menu']) { wp_die(__('You do not have permission to update that Navigation Menu', 'scoper')); } else { wp_die(__('You do not have permission to create new Navigation Menus', 'scoper')); } } } } elseif (false !== strpos($referer_name, 'delete-menu_item_')) { if (scoper_get_option('admin_nav_menu_filter_items')) { $menu_item_id = substr($referer_name, strlen('delete-menu_item_')); require_once SCOPER_ABSPATH . '/admin/filters-admin-nav_menus_rs.php'; _rs_mnt_modify_nav_menu_item($menu_item_id, 'delete'); } } elseif ($referer_name == 'move-menu_item') { if (scoper_get_option('admin_nav_menu_filter_items')) { require_once SCOPER_ABSPATH . '/admin/filters-admin-nav_menus_rs.php'; _rs_mnt_modify_nav_menu_item($_REQUEST['menu-item'], 'move'); } } elseif ('add-bookmark' == $referer_name) { require_once dirname(__FILE__) . '/hardway-admin-links_rs.php'; $link_category = !empty($_POST['link_category']) ? $_POST['link_category'] : array(); $_POST['link_category'] = scoper_flt_newlink_category($link_category); } elseif (0 === strpos($referer_name, 'update-bookmark_')) { require_once dirname(__FILE__) . '/hardway-admin-links_rs.php'; $link_category = !empty($_POST['link_category']) ? $_POST['link_category'] : array(); $_POST['link_category'] = scoper_flt_link_category($link_category); } }
function get_for_taxonomy($src, $taxonomy = '', $args = array()) { $defaults = array('one_otype_per_role' => true, 'ignore_usage_settings' => false); $args = array_merge($defaults, (array) $args); extract($args); if (!is_object($src)) { $src = $GLOBALS['scoper']->data_sources->get($src); } if (!$src) { return; } $otype_roles = array(); if (!in_array($taxonomy, array('category', 'post_tag')) && $one_otype_per_role) { if ($tx = get_taxonomy($taxonomy)) { if (!empty($tx->object_type)) { $use_otypes = array_unique((array) $tx->object_type); } } } if (empty($use_otypes)) { $use_otypes = array_keys($src->object_types); } foreach ($use_otypes as $object_type) { $use_term_roles = scoper_get_otype_option('use_term_roles', $src->name, $object_type); if (!$ignore_usage_settings && empty($use_term_roles[$taxonomy])) { continue; } if ($roles = $this->get_matching('rs', $src->name, $object_type)) { if ($one_otype_per_role) { foreach (array_keys($otype_roles) as $existing_object_type) { $roles = array_diff_key($roles, $otype_roles[$existing_object_type]); } } $otype_roles[$object_type] = $roles; } } //note: term roles are defined with src_name property corresponding to their object source (i.e. manage_categories has src_name 'post') if ($taxonomy) { if ($roles = $this->get_matching('rs', $src->name, $taxonomy)) { if ($one_otype_per_role) { foreach (array_keys($otype_roles) as $object_type) { $roles = array_diff_key($roles, $otype_roles[$object_type]); } } if ($roles) { $otype_roles[$taxonomy] = $roles; } } } return $otype_roles; }