/wp-content/plugins/role-scoper/admin/filters-admin_rs.php
PHP | 800 lines | 529 code | 181 blank | 90 comment | 150 complexity | 6c4e1ae26d1d9d0f22b9e6f741a7ba76 MD5 | raw file
Possible License(s): AGPL-1.0, GPL-3.0, Apache-2.0, GPL-2.0, LGPL-2.1
- <?php
-
- if( basename(__FILE__) == basename($_SERVER['SCRIPT_FILENAME']) )
- die();
-
- require_once( dirname(__FILE__).'/admin_lib_rs.php' );
-
- /**
- * ScoperAdminFilters PHP class for the WordPress plugin Role Scoper
- * filters-admin_rs.php
- *
- * @author Kevin Behrens
- * @copyright Copyright 2012
- *
- */
- class ScoperAdminFilters
- {
- var $role_levels; // NOTE: user/role levels are used only for optional limiting of user edit, not for content filtering
- var $user_levels; // this is only populated for performance as a buffer for currently queried / listed users
- var $last_post_status = array();
-
- function ScoperAdminFilters() {
- global $scoper;
-
- // --------- CUSTOMIZABLE HOOK WRAPPING ---------
- //
- // Make all source-specific operation hooks trigger equivalent abstracted hook:
- // create_object_rs, edit_object_rs, save_object_rs, delete_object_rs,
- // create_term_rs, edit_term_rs, delete_term_rs (including custom non-WP taxonomies)
- //
- // These will be used by Role Scoper for role maintenance, but are also available for external use.
- $rs_filters = array();
- $rs_actions = array();
-
- // Register our abstract handlers to save_post, edit_post, delete_post and corresponding hooks from other data sources.
- // see core_default_data_sources() and Scoped_Data_Sources::process() for default hook names
- foreach ( $scoper->data_sources->get_all() as $src_name => $src ) {
- if ( ! empty($src->admin_actions) )
- foreach ( $src->admin_actions as $rs_hook => $original_hook ) {
- $rs_actions[$original_hook] = (object) array( 'name' => "{$rs_hook}_rs", 'rs_args' => "'$src_name', '' " );
- $rs_actions[$original_hook]->orig_num_args = ( 'save_post' == $original_hook ) ? 2 : 1;
- }
-
- if ( ! empty($src->admin_filters) )
- foreach ( $src->admin_filters as $rs_hook => $original_hook )
- $rs_filters[$original_hook] = (object) array( 'name' => "{$rs_hook}_rs", 'rs_args' => "'$src_name', '' " );
-
- // also register hooks that are specific to one object type
- foreach ( $src->object_types as $object_type => $otype_def ) {
- if ( ! empty($otype_def->admin_actions) ) {
- foreach ( $otype_def->admin_actions as $rs_hook => $original_hook ) {
- $rs_actions[$original_hook] = (object) array( 'name' => "{$rs_hook}_rs", 'rs_args' => "'$src_name', array( 'object_type' => '$object_type' ) " );
- $rs_actions[$original_hook]->orig_num_args = ( 'save_post' == $original_hook ) ? 2 : 1;
- }
- }
-
- if ( ! empty($otype_def->admin_filters) )
- foreach ( $otype_def->admin_filters as $rs_hook => $original_hook )
- $rs_filters[$original_hook] = (object) array( 'name' => "{$rs_hook}_rs", 'rs_args' => "'$src_name', array( 'object_type' => '$object_type' ) " );
- }
- } //foreach data_sources
-
- // Register our abstract handlers to create_category, edit_category, delete_category and corresponding hooks from other taxonomies.
- // (supports WP taxonomies AND custom taxonomies)
- // see core_default_taxonomies() and Scoped_Taxonomies::process() for default hook names
- foreach ( $scoper->taxonomies->get_all() as $taxonomy => $tx ) {
- if ( ! empty($tx->admin_actions) )
- foreach ( $tx->admin_actions as $rs_hook => $original_hook ) {
- if ( ! isset( $rs_actions[$original_hook] ) ) { // default term hooks changed from edit_{$taxonomy} to edit_term, etc. WP passes taxonomy. Keeping rs-passed taxonomy as 1st arg for back compat.
- $rs_actions[$original_hook] = (object) array( 'name' => "{$rs_hook}_rs", 'rs_args' => "'$taxonomy', '' " );
- $rs_actions[$original_hook]->orig_num_args = ( 'term_edit_ui' == $rs_hook ) ? 1 : 3;
- }
- }
- if ( ! empty($tx->admin_filters) )
- foreach ( $tx->admin_filters as $rs_hook => $original_hook )
- $rs_filters[$original_hook] = (object) array( 'name' => "{$rs_hook}_rs", 'rs_args' => "'$taxonomy', '' " );
- }
-
-
- // call our abstract handlers with a lambda function that passes in original hook name
- $hook_order = ( defined('WPCACHEHOME') ) ? -1 : 50; // WP Super Cache's early create_post / edit_post handlers clash with Role Scoper
-
- foreach ( $rs_actions as $original_hook => $rs_hook ) {
- if ( ! $original_hook ) continue;
- $arg_str = agp_get_lambda_argstring( $rs_hook->orig_num_args );
- $comma = ( $rs_hook->rs_args ) ? ',' : '';
- $func = "do_action( '$rs_hook->name', $rs_hook->rs_args $comma $arg_str );";
- //echo "adding action: $original_hook -> $func <br />";
- add_action( $original_hook, create_function( $arg_str, $func ), $hook_order, $rs_hook->orig_num_args );
- }
-
- foreach ( $rs_filters as $original_hook => $rs_hook ) {
- if ( ! $original_hook ) continue;
- $orig_hook_numargs = 1;
- $arg_str = agp_get_lambda_argstring($orig_hook_numargs);
- $comma = ( $rs_hook->rs_args ) ? ',' : '';
- $func = "return apply_filters( '$rs_hook->name', $arg_str $comma $rs_hook->rs_args );";
- //echo "adding filter: $original_hook -> $func <br />";
- add_filter( $original_hook, create_function( $arg_str, $func ), 50, $orig_hook_numargs );
- }
-
-
- // WP 2.5 throws a notice if plugins add their own hooks without prepping the global array
- // Source or taxonomy-specific hooks are mapped to these based on config member properties
- // in CR_Data_Sources::process() and CR_Taxonomies:process()
- $setargs = array( 'is_global' => true );
- $setkeys = array (
- 'create_object_rs', 'edit_object_rs', 'save_object_rs', 'delete_object_rs',
- 'create_term_rs', 'edit_term_rs', 'save_term_rs', 'delete_term_rs'
- );
-
- add_action('create_object_rs', array(&$this, 'mnt_create_object'), 10, 4);
- add_action('edit_object_rs', array(&$this, 'mnt_edit_object'), 10, 4);
- add_action('save_object_rs', array(&$this, 'mnt_save_object'), 10, 4);
- add_action('delete_object_rs', array(&$this, 'mnt_delete_object'), 10, 3);
-
- // these will be used even if the taxonomy in question is not a WP core taxonomy (i.e. even if uses a custom schema)
- add_action('create_term_rs', array(&$this, 'mnt_create_term'), 10, 4);
- add_action('edit_term_rs', array(&$this, 'mnt_edit_term'), 10, 4);
- add_action('delete_term_rs', array(&$this, 'mnt_delete_term'), 10, 3);
-
-
- // -------- Predefined WP User/Post/Page admin actions / filters ----------
- // user maintenace
- add_action('profile_update', array('ScoperAdminLib', 'sync_wproles') );
- add_action('set_user_role', array('ScoperAdminLib', 'schedule_role_sync') );
- add_filter('user_has_cap', array(&$this, 'flt_has_edit_user_cap'), 99, 3 );
- add_filter('editable_roles', array(&$this, 'flt_editable_roles'), 99 );
-
- if ( IS_MU_RS ) {
- add_action('remove_user_from_blog', array('ScoperAdminLib', 'delete_users'), 10, 2 );
- add_action('add_user_to_blog', array(&$this, 'act_schedule_user_sync'), 10, 3 ); // WP 3.0 multisite fires add_user_to_blog too early for us
- } else {
- //add_action('user_register', array('ScoperAdminLib', 'add_user') );
- add_action('user_register', array(&$this, 'act_schedule_user_sync'), 10, 3 );
- add_action('delete_user', array('ScoperAdminLib', 'delete_users') );
- }
-
- if ( GROUP_ROLES_RS )
- add_action('profile_update', array(&$this, 'act_update_user_groups'));
-
- // log post status transition to recognize new posts and status change to/from private
- add_action( 'transition_post_status', array(&$this, 'act_log_post_status'), 10, 3 );
-
- add_action( 'edit_post', array(&$this, 'act_log_updated_post') );
-
- // Filtering of Page Parent selection:
- add_filter('pre_post_status', array(&$this, 'flt_post_status'), 50, 1);
- add_filter('pre_post_parent', array(&$this, 'flt_page_parent'), 50, 1);
-
- add_filter( 'pre_post_tax_input', array(&$this, 'flt_tax_input'), 50, 1);
-
- if ( ( 'nav-menus.php' == $GLOBALS['pagenow'] ) ) {
- if ( scoper_get_option( 'admin_nav_menu_filter_items' ) ) {
- add_action( 'admin_head', array( &$this, 'act_nav_menu_header' ) );
-
- if ( ! empty( $_POST ) )
- add_action( 'pre_post_update', array(&$this, 'mnt_pre_post_update') );
- }
-
- add_action( 'admin_head', array( &$this, 'act_nav_menu_ui' ) );
- }
-
- add_action( 'check_admin_referer', array( &$this, 'act_nav_menu_guard_theme_locs' ) );
-
- // Filtering of terms selection:
- add_action('check_admin_referer', array(&$this, 'act_detect_post_presave')); // abuse referer check to work around a missing hook
-
- add_filter('pre_object_terms_rs', array(&$this, 'flt_pre_object_terms'), 50, 3);
-
- add_filter( 'save_post', array(&$this, 'custom_taxonomies_helper'), 5, 2);
-
- // TODO: also hook to "pre_option_default_{$taxonomy}"
- if ( ( 'options-writing.php' != $GLOBALS['pagenow'] ) && ! is_content_administrator_rs() )
- add_filter('pre_option_default_category', array(&$this, 'flt_default_term') );
-
- // Follow up on role creation / deletion by Role Manager, Capability Manager or other equivalent plugin
- // Role Manager / Capability Manager don't actually modify the stored role def until after the option update we're hooking on, so defer our maintenance operation
- global $wpdb;
- add_action( "update_option_{$wpdb->prefix}user_roles", array('ScoperAdminLib', 'schedule_role_sync') );
-
- add_filter( 'posts_fields', array(&$this, 'flt_posts_fields') );
-
- if ( scoper_get_option( 'group_ajax' ) ) {
- add_action( 'add_group_user_rs', array(&$this, 'new_group_user_notification'), 10, 3 );
- add_action( 'update_group_user_rs', array(&$this, 'edit_group_user_notification'), 10, 4 );
- }
-
- // TODO: make this optional
- // include private posts in the post count for each term
- global $wp_taxonomies;
- foreach ( $wp_taxonomies as $key => $t ) {
- if ( isset($t->update_count_callback) && ( '_update_post_term_count' == $t->update_count_callback ) )
- $wp_taxonomies[$key]->update_count_callback = 'scoper_update_post_term_count';
- }
-
- add_action( 'load-post.php', array( &$this, 'maybe_override_kses' ) );
- }
-
- function maybe_override_kses() {
- if ( ! empty($_POST) && ! empty($_POST['action']) && ( 'editpost' == $_POST['action'] ) ) {
- if ( current_user_can( 'unfiltered_html' ) ) // initial core cap check in kses_init() is unfilterable
- kses_remove_filters();
- }
- }
-
- // make sure pre filter is applied for all custom taxonomies regardless of term selection
- function custom_taxonomies_helper( $post_id, $post ) {
- require_once( dirname(__FILE__).'/filters-admin-save_rs.php' );
- scoper_force_custom_taxonomy_filters( $post_id, $post );
- }
-
- function act_log_post_status( $new_status, $old_status, $post ) {
- $this->last_post_status[$post->ID] = $old_status;
- }
-
- function act_log_updated_post( $post_id ) {
- $this->logged_post_update[$post_id] = true;
- }
-
- function new_group_user_notification ( $user_id, $group_id, $status ) {
- if ( 'active' == $status )
- return;
-
- require_once( dirname(__FILE__).'/group-notification_rs.php' );
-
- if ( 'requested' == $status )
- return ScoperGroupNotification::membership_request_notify( $user_id, $group_id );
- elseif ( 'recommended' == $status )
- return ScoperGroupNotification::membership_recommendation_notify( $user_id, $group_id );
- }
-
- function edit_group_user_notification ( $user_id, $group_id, $status, $prev_status ) {
- if ( $status == $prev_status )
- return;
-
- require_once( dirname(__FILE__).'/group-notification_rs.php' );
-
- if ( ! $prev_status )
- return $this->new_group_user_notification( $user_id, $group_id, $status );
- elseif ( 'active' == $status )
- return ScoperGroupNotification::membership_activation_notify( $user_id, $group_id, true );
- elseif ( 'recommended' == $status )
- return ScoperGroupNotification::membership_recommendation_notify( $user_id, $group_id, true );
- }
-
- // optional filter for WP role edit based on user level
- function flt_editable_roles( $roles ) {
- if ( defined( 'DISABLE_QUERYFILTERS_RS' ) || ! scoper_get_option('limit_user_edit_by_level') )
- return $roles;
-
- require_once( dirname(__FILE__).'/user_lib_rs.php' );
- return ScoperUserEdit::editable_roles( $roles );
- }
-
- // Optionally, prevent anyone from editing or deleting a user whose level is higher than their own
- function flt_has_edit_user_cap($wp_blogcaps, $orig_reqd_caps, $args) {
- if ( ! defined( 'DISABLE_QUERYFILTERS_RS' ) && ( in_array( 'edit_users', $orig_reqd_caps ) || in_array( 'delete_users', $orig_reqd_caps ) ) && ! empty($args[2]) ) {
- if ( scoper_get_option('limit_user_edit_by_level') ) {
- require_once( dirname(__FILE__).'/user_lib_rs.php' );
- $wp_blogcaps = ScoperUserEdit::has_edit_user_cap( $wp_blogcaps, $orig_reqd_caps, $args );
- }
- }
-
- return $wp_blogcaps;
- }
-
- function flt_posts_fields($cols) {
- if ( defined( 'DISABLE_QUERYFILTERS_RS' ) )
- return $cols;
-
- // possible TODO: reinstate support for separate activation of pages / posts lean (as of WP 2.9, all post types us edit.php)
- if ( ( defined( 'SCOPER_EDIT_PAGES_LEAN' ) || defined( 'SCOPER_EDIT_POSTS_LEAN' ) ) && ( 'edit.php' == $GLOBALS['pagenow'] ) ) {
- global $wpdb;
- $cols = "$wpdb->posts.ID, $wpdb->posts.post_author, $wpdb->posts.post_date, $wpdb->posts.post_date_gmt, $wpdb->posts.post_title, $wpdb->posts.post_status, $wpdb->posts.comment_status, $wpdb->posts.ping_status, $wpdb->posts.post_password, $wpdb->posts.post_name, $wpdb->posts.to_ping, $wpdb->posts.pinged, $wpdb->posts.post_parent, $wpdb->posts.post_modified, $wpdb->posts.post_modified_gmt, $wpdb->posts.guid, $wpdb->posts.post_type, $wpdb->posts.post_mime_type, $wpdb->posts.menu_order, $wpdb->posts.comment_count";
- }
-
- return $cols;
- }
-
- // Filtering of Page Parent selection.
- // This is a required after-the-fact operation for WP < 2.7 (due to inability to control inclusion of Main Page in UI dropdown)
- // For WP >= 2.7, it is an anti-hacking precaution
- //
- // There is currently no way to explictly restrict or grant Page Association rights to Main Page (root). Instead:
- // * Require blog-wide edit_others_pages cap for association of a page with Main
- // * If an unqualified user tries to associate or un-associate a page with Main Page,
- // revert page to previously stored parent if possible. Otherwise set status to "unpublished".
- function flt_post_status ($status) {
- if ( defined( 'DISABLE_QUERYFILTERS_RS' ) )
- return $status;
-
- require_once( dirname(__FILE__).'/filters-admin-save_rs.php');
- return scoper_flt_post_status($status);
- }
-
- // Enforce any page parent filtering which may have been dictated by the flt_post_status filter, which executes earlier.
- function flt_page_parent ($parent_id) {
- if ( defined( 'DISABLE_QUERYFILTERS_RS' ) )
- return $parent_id;
-
- require_once( dirname(__FILE__).'/filters-admin-save_rs.php');
- return scoper_flt_page_parent($parent_id);
- }
-
-
- function act_detect_post_presave($action) {
- // for post update with no post categories checked, insert a fake category so WP core doesn't force default category
- // (flt_pre_object_terms will first restore any existing postcats dropped due to user's lack of permissions)
- if ( 0 === strpos($action, 'update-post_') ) {
- if ( defined( 'DISABLE_QUERYFILTERS_RS' ) )
- return;
-
- if ( empty($_POST['post_category']) && ! is_content_administrator_rs() ) {
- $_POST['post_category'] = array(-1);
- }
- }
- }
-
- function flt_tax_input( $tax_input ) {
- if ( $tax_input && is_array($tax_input) ) {
- foreach( $tax_input as $taxonomy => $terms ) {
- if ( is_string($terms) ) // currently, don't restrict non-hierarchical tag assignments / removals per-user
- continue;
- //$terms = explode( ",", $terms );
-
- $tax_input[$taxonomy] = $this->flt_pre_object_terms( $terms, $taxonomy );
- }
- }
-
- return $tax_input;
- }
-
- function flt_pre_object_terms ($selected_terms, $taxonomy, $args = array()) {
- if ( defined( 'DISABLE_QUERYFILTERS_RS' ) || did_action('tdomf_create_post_start') ) // don't filter out a category that was added by TDO Mini Forms
- return $selected_terms;
-
- require_once( dirname(__FILE__).'/filters-admin-save_rs.php');
- return scoper_flt_pre_object_terms($selected_terms, $taxonomy, $args);
- }
-
- // This handler is meant to fire whenever an object is inserted or updated.
- // If the client does use such a hook, we will force it by calling internally from mnt_create and mnt_edit
- function mnt_save_object($src_name, $args, $object_id, $object = '') {
- //rs_errlog( 'mnt_save_object' );
- if ( defined( 'RVY_VERSION' ) ) {
- global $revisionary;
-
- if ( ! empty($revisionary->admin->revision_save_in_progress) ) {
- $revisionary->admin->revision_save_in_progress = false;
- return;
- }
- }
-
- require_once( dirname(__FILE__).'/filters-admin-save_rs.php');
- scoper_mnt_save_object($src_name, $args, $object_id, $object);
-
- if ( function_exists('relevanssi_query') ) {
- require_once( dirname(__FILE__).'/relevanssi-helper-admin_rs.php' );
- Relevanssi_Admin_Helper_RS::rvi_reindex();
- }
- }
-
- function _can_edit_theme_locs() {
- return is_content_administrator_rs() || ! empty( $GLOBALS['current_user']->allcaps['manage_nav_menus'] ) || ! empty( $GLOBALS['current_user']->allcaps['switch_themes'] );
- }
-
- // users with editing access to a limited subset of menus are not allowed to set menu for theme locations
- function act_nav_menu_ui() {
- if ( ! $this->_can_edit_theme_locs() ) {
- unset( $GLOBALS['wp_meta_boxes']['nav-menus']['side']['default']['nav-menu-theme-locations'] );
- }
- }
-
- // make sure theme locations are not wiped because logged user has editing access to a subset of menus
- function act_nav_menu_guard_theme_locs( $referer ) {
- if ( 'update-nav_menu' == $referer ) {
- if ( isset( $_POST['menu-locations'] ) ) {
- if ( ! $this->_can_edit_theme_locs() )
- unset( $_POST['menu-locations'] );
- }
- }
- }
-
- function act_nav_menu_header() {
- if ( ! is_content_administrator_rs() ) {
- require_once( dirname(__FILE__).'/filters-admin-nav_menus_rs.php' );
- _rs_disable_uneditable_items_ui();
- }
- }
-
- function mnt_pre_post_update( $object_id ) {
- if ( ! is_content_administrator_rs() ) {
- // don't allow modification of menu items for posts which user can't edit (not currently feasible since WP fires update for each menu item even if unmodified)
- require_once( dirname(__FILE__).'/filters-admin-nav_menus_rs.php' );
- _rs_mnt_modify_nav_menu_item( $object_id, 'edit' );
- }
- }
-
- // This handler is meant to fire only on updates, not new inserts
- function mnt_edit_object($src_name, $args, $object_id, $object = '') {
- static $edited_objects;
-
- if ( ! isset($edited_objects) )
- $edited_objects = array();
-
- // so this filter doesn't get called by hook AND internally
- if ( isset($edited_objects[$src_name][$object_id]) )
- return;
-
- $edited_objects[$src_name][$object_id] = 1;
-
- // call save handler directly in case it's not registered to a hook
- $this->mnt_save_object($src_name, $args, $object_id, $object);
- }
-
- function mnt_delete_object($src_name, $args, $object_id) {
- $object = '';
-
- $defaults = array( 'object_type' => '', 'object' => '' );
- $args = array_intersect_key( $defaults, (array) $args );
- extract($args);
-
- if ( ! $object_id )
- return;
-
- // don't allow deletion of menu items for posts which user can't edit
- if ( ( 'nav-menus.php' == $GLOBALS['pagenow'] ) && ! is_content_administrator_rs() && ! empty( $_POST ) && scoper_get_option( 'admin_nav_menu_filter_items' ) ) {
- require_once( dirname(__FILE__).'/filters-admin-nav_menus_rs.php' );
- _rs_mnt_modify_nav_menu_item( $object_id, 'delete' );
- }
-
- // could defer role/cache maint to speed potential bulk deletion, but script may be interrupted before admin_footer
- $this->item_deletion_aftermath( OBJECT_SCOPE_RS, $src_name, $object_id );
-
- if ( empty($object_type) )
- $object_type = cr_find_object_type($src_name, $object_id);
-
- if ( 'post' == $src_name ) {
- if ( $post_type_obj = get_post_type_object( $object_type ) ) {
- if ( $post_type_obj->hierarchical )
- scoper_flush_cache_groups('get_pages');
- }
- }
-
- scoper_flush_roles_cache(OBJECT_SCOPE_RS);
- }
-
- function mnt_create_object($src_name, $args, $object_id, $object = '') {
- $defaults = array( 'object_type' => '' );
- $args = array_intersect_key( $defaults, (array) $args );
- extract($args);
-
- static $inserted_objects;
-
- if ( ! isset($inserted_objects) )
- $inserted_objects = array();
-
- // so this filter doesn't get called by hook AND internally
- if ( isset($inserted_objects[$src_name][$object_id]) )
- return;
-
- if ( empty($object_type) )
- if ( $col_type = $GLOBALS['scoper']->data_sources->member_property($src_name, 'cols', 'type') )
- $object_type = ( isset($object->$col_type) ) ? $object->$col_type : '';
-
- if ( empty($object_type) ) {
- if ( ! isset( $object ) )
- $object = '';
-
- $object_type = cr_find_object_type($src_name, $object_id, $object);
- }
-
- if ( $object_type == 'revision' )
- return;
-
- $inserted_objects[$src_name][$object_id] = 1;
-
- if ( 'post' == $src_name ) {
- $post_type_obj = get_post_type_object( $object_type );
-
- if ( $post_type_obj->hierarchical )
- scoper_flush_cache_groups('get_pages');
- }
- }
-
- function mnt_create_term($deprecated_taxonomy, $args, $term_id, $unused_tt_id = '', $taxonomy = '') {
- if ( ! $taxonomy )
- $taxonomy = $deprecated_taxonomy;
-
- $this->mnt_save_term( $taxonomy, $args, $term_id, $unused_tt_id, $taxonomy );
-
- scoper_term_cache_flush();
- delete_option( "{$taxonomy}_children_rs" );
- }
-
- function mnt_edit_term($deprecated_taxonomy, $args, $term_ids, $unused_tt_id = '', $taxonomy = '') {
- if ( ! $taxonomy )
- $taxonomy = $deprecated_taxonomy;
-
- static $edited_terms;
-
- if ( ! isset($edited_terms) )
- $edited_terms = array();
-
- // bookmark edit passes an array of term_ids
- $term_ids = (array) $term_ids;
-
- foreach ( $term_ids as $term_id ) {
- // so this filter doesn't get called by hook AND internally
- if ( isset($edited_terms[$taxonomy][$term_id]) )
- return;
-
- $edited_terms[$taxonomy][$term_id] = 1;
-
- // call save handler directly in case it's not registered to a hook
- $this->mnt_save_term( $taxonomy, $args, $term_id, $unused_tt_id, $taxonomy );
- }
- }
-
- // This handler is meant to fire whenever a term is inserted or updated.
- // If the client does use such a hook, we will force it by calling internally from mnt_create and mnt_edit
- function mnt_save_term($deprecated_taxonomy, $args, $term_id, $unused_tt_id = '', $taxonomy = '') {
- require_once( dirname(__FILE__).'/filters-admin-save_rs.php');
- scoper_mnt_save_term( $deprecated_taxonomy, $args, $term_id, $unused_tt_id, $taxonomy );
- }
-
- function mnt_delete_term($deprecated_taxonomy, $args, $term_id, $unused_tt_id = '', $taxonomy = '') {
- global $wpdb;
-
- if ( ! $term_id )
- return;
-
- if ( ! $taxonomy )
- $taxonomy = $deprecated_taxonomy;
-
- // could defer role/cache maint to speed potential bulk deletion, but script may be interrupted before admin_footer
- $this->item_deletion_aftermath( TERM_SCOPE_RS, $taxonomy, $term_id );
-
- delete_option( "{$taxonomy}_children_rs" );
-
- scoper_term_cache_flush();
- scoper_flush_roles_cache(TERM_SCOPE_RS, '', '', $taxonomy);
- scoper_flush_cache_flag_once("rs_$taxonomy");
- }
-
- function item_deletion_aftermath( $scope, $src_or_tx_name, $obj_or_term_id ) {
- global $wpdb;
-
- // delete role assignments for deleted term
- if ( $ass_ids = scoper_get_col("SELECT assignment_id FROM $wpdb->user2role2object_rs WHERE src_or_tx_name = '$src_or_tx_name' AND scope = '$scope' AND obj_or_term_id = '$obj_or_term_id'") ) {
- $id_in = "'" . implode("', '", $ass_ids) . "'";
- scoper_query("DELETE FROM $wpdb->user2role2object_rs WHERE assignment_id IN ($id_in)");
-
- // Propagated roles will be converted to direct-assigned roles if the original progenetor goes away. Removal of a "link" in the parent/child propagation chain has no effect.
- scoper_query("UPDATE $wpdb->user2role2object_rs SET inherited_from = '0' WHERE inherited_from IN ($id_in)");
- }
-
- if ( $req_ids = scoper_get_col("SELECT requirement_id FROM $wpdb->role_scope_rs WHERE topic = '$scope' AND src_or_tx_name = '$src_or_tx_name' AND obj_or_term_id = '$obj_or_term_id'") ) {
- $id_in = "'" . implode("', '", $req_ids) . "'";
-
- scoper_query("DELETE FROM $wpdb->role_scope_rs WHERE requirement_id IN ($id_in)");
-
- // Propagated requirements will be converted to direct-assigned roles if the original progenetor goes away. Removal of a "link" in the parent/child propagation chain has no effect.
- scoper_query("UPDATE $wpdb->role_scope_rs SET inherited_from = '0' WHERE inherited_from IN ($id_in)");
- }
- }
-
- function act_update_user_groups($user_id) {
- if ( empty( $_POST['rs_editing_user_groups'] ) ) // otherwise we'd delete group assignments if another plugin calls do_action('profile_update') unexpectedly
- return;
-
- global $current_rs_user;
-
- $editable_group_ids = array();
- $stored_groups = array();
-
- if ( $user_id == $current_rs_user->ID )
- $stored_groups['active'] = $current_rs_user->groups;
- else {
- $user = rs_get_user($user_id, '', array( 'skip_role_merge' => 1 ) );
- $stored_groups['active'] = $user->groups;
- }
-
- // by retrieving filtered groups here, user will only modify membership for groups they can administer
- $editable_group_ids['active'] = ScoperAdminLib::get_all_groups(FILTERED_RS, COL_ID_RS, array( 'reqd_caps' => 'manage_groups' ) );
-
- if( scoper_get_option( 'group_ajax' ) ) {
- $this->update_user_groups_multi_status( $user_id, $stored_groups, $editable_group_ids );
- return;
- } else {
- $stored_groups = $stored_groups['active'];
- $editable_group_ids = $editable_group_ids['active'];
- }
-
- if ( ! empty($_POST['groups_csv']) ) {
- if ( $csv_for_item = ScoperAdminLib::agent_ids_from_csv( 'groups_csv', 'groups' ) )
- $posted_groups = array_merge($posted_groups, $csv_for_item);
- } else
- $posted_groups = ( isset($_POST['group']) ) ? $_POST['group'] : array();
-
- $posted_groups = array_unique( $posted_groups );
-
- foreach ($editable_group_ids as $group_id) {
- if( in_array($group_id, $posted_groups) ) { // checkbox is checked
- if( ! isset($stored_groups[$group_id]) )
- ScoperAdminLib::add_group_user($group_id, $user_id);
-
- } elseif( isset($stored_groups[$group_id]) ) {
- ScoperAdminLib::remove_group_user($group_id, $user_id);
- }
- }
- }
-
- function update_user_groups_multi_status( $user_id, $stored_groups, $editable_group_ids ) {
- global $current_rs_user;
-
- $posted_groups = array();
-
- $is_administrator = is_user_administrator_rs();
-
- $can_manage = $is_administrator || current_user_can( 'manage_groups' );
- $can_moderate = $can_manage || current_user_can( 'recommend_group_membership' );
-
- if ( ! $can_moderate && ! current_user_can( 'request_group_membership' ) )
- return;
-
- if ( $can_manage )
- $posted_groups['active'] = explode( ',', trim($_POST['current_agents_rs_csv'], '') );
- else
- $stored_groups = array_diff_key( $stored_groups, array( 'active' => true ) );
-
- if ( $can_moderate ) {
- $posted_groups['recommended'] = ! empty($_POST['recommended_agents_rs_csv']) ? explode( ',', trim($_POST['recommended_agents_rs_csv'], '') ) : array();
-
- $stored_groups['recommended'] = array_fill_keys( $current_rs_user->get_groups_for_user( $current_rs_user->ID, array( 'status' => 'recommended' ) ), true );
-
- $editable_group_ids['recommended'] = ScoperAdminLib::get_all_groups(FILTERED_RS, COL_ID_RS, array( 'reqd_caps' => 'recommend_group_membership' ) );
-
- if ( isset($editable_group_ids['active']) )
- $editable_group_ids['recommended'] = array_unique( $editable_group_ids['recommended'] + $editable_group_ids['active'] );
- }
-
- $stored_groups['requested'] = array_fill_keys( $current_rs_user->get_groups_for_user( $current_rs_user->ID, array( 'status' => 'requested' ) ), true );
-
- $editable_group_ids['requested'] = ScoperAdminLib::get_all_groups(FILTERED_RS, COL_ID_RS, array( 'reqd_caps' => 'request_group_membership' ) );
-
- if ( isset($editable_group_ids['recommended']) )
- $editable_group_ids['requested'] = array_unique( $editable_group_ids['requested'] + $editable_group_ids['recommended'] );
-
- $posted_groups['requested'] = ! empty($_POST['requested_agents_rs_csv']) ? explode( ',', trim($_POST['requested_agents_rs_csv'], '') ) : array();
-
- $all_posted_groups = agp_array_flatten( $posted_groups );
-
- $all_stored_groups = array();
- foreach ( array_keys($stored_groups) as $status )
- $all_stored_groups = $all_stored_groups + $stored_groups[$status];
-
- foreach ( $stored_groups as $status => $stored ) {
- if ( ! $editable_group_ids[$status] )
- continue;
-
- // remove group memberships which were not posted for any status, if logged user can edit the group
- foreach ( array_keys($stored) as $group_id ) {
- if ( ! in_array( $group_id, $all_posted_groups ) )
- if ( in_array( $group_id, $editable_group_ids[$status] ) )
- ScoperAdminLib::remove_group_user($group_id, $user_id);
- }
- }
-
- foreach ( $posted_groups as $status => $posted ) {
- if ( ! $editable_group_ids[$status] )
- continue;
-
- // insert or update group memberships as specified, if logged user can edit the group
- foreach ( $posted as $group_id ) {
- if ( in_array( $group_id, $editable_group_ids[$status] ) ) {
- if ( ! in_array( $group_id, $all_stored_groups ) )
- ScoperAdminLib::add_group_user($group_id, $user_id, $status);
- elseif ( ! in_array( $group_id, $stored_groups[$status] ) )
- ScoperAdminLib::update_group_user($group_id, $user_id, $status);
- }
- }
- }
- }
-
- function act_schedule_user_sync( $user_id, $role_name = '', $blog_id = '' ) {
- // ScoperAdminLib::add_user applies default group(s), calls sync_wproles
- $func = create_function( '', "ScoperAdminLib::add_user('" . $user_id . "');" );
- add_action( 'shutdown', $func );
- }
-
- function flt_default_term( $default_term_id, $taxonomy = 'category' ) {
- require_once( dirname(__FILE__).'/filters-admin-term-selection_rs.php');
-
- // support an array of default IDs (but don't require it)
- $term_ids = (array) $default_term_id;
-
- $user_terms = array(); // will be returned by filter_terms_for_status
-
- $term_ids = scoper_filter_terms_for_status($taxonomy, $term_ids, $user_terms);
-
- // if the default term is not in user's subset of usable terms, substitute first available
- if ( ( ( ! $term_ids ) || ! $term_ids[0] ) && $user_terms ) {
- if ( $GLOBALS['scoper']->taxonomies->member_property( $taxonomy, 'requires_term' ) )
- return $user_terms[0];
- else
- return;
- }
-
- if ( count($term_ids) > 1 ) // won't return an array unless an array was passed in and more than one of its elements is usable by this user
- return $term_ids;
- elseif( $term_ids )
- return reset( $term_ids ); // if a single term ID was passed in and is permitted, it is returned here
- }
-
- function user_can_associate_main( $post_type ) {
- if ( is_content_administrator_rs() )
- return true;
-
- if ( ! $post_type_obj = get_post_type_object($post_type) )
- return true;
-
- if ( ! $post_type_obj->hierarchical )
- return true;
-
- // currently used only for page type, or for all if constant is set
- $top_pages_locked = scoper_get_option( 'lock_top_pages' );
-
- if ( ( 'page' == $post_type ) || defined( 'SCOPER_LOCK_OPTION_ALL_TYPES' ) ) {
- if ( '1' === $top_pages_locked ) {
- // only administrators can change top level structure
- return false;
- } else {
- $reqd_caps = ( 'author' === $top_pages_locked ) ? array( $post_type_obj->cap->publish_posts ) : array( $post_type_obj->cap->edit_others_posts );
- $roles = $GLOBALS['scoper']->role_defs->qualify_roles($reqd_caps);
- return array_intersect_key($roles, $GLOBALS['current_rs_user']->blog_roles[ANY_CONTENT_DATE_RS]);
- }
- }
- }
- } // end class
-
-
- function init_role_assigner() {
- global $scoper_role_assigner;
-
- if ( ! isset($scoper_role_assigner) ) {
- require_once( dirname(__FILE__).'/role_assigner_rs.php');
- $scoper_role_assigner = new ScoperRoleAssigner();
- }
-
- return $scoper_role_assigner;
- }
-
- function scoper_flush_cache_flag_once ($cache_flag) {
- static $flushed_wpcache_flags;
-
- if ( ! isset($flushed_wpcache_flags) )
- $flushed_wpcache_flags = array();
-
- if ( ! isset( $flushed_wpcache_flags[$cache_flag]) ) {
- wpp_cache_flush_group($cache_flag);
- $flushed_wpcache_flags[$cache_flag] = true;
- }
- }
-
- // flush a specified portion of Role Scoper's persistant cache
- function scoper_flush_cache_groups($base_cache_flag) {
- $scoper_role_types = array('rs', 'wp', 'wp_cap');
-
- foreach ( $scoper_role_types as $role_type ) {
- scoper_flush_cache_flag_once($role_type . '_' . $base_cache_flag . '_for_groups' );
- scoper_flush_cache_flag_once($role_type . '_' . $base_cache_flag . '_for_user' );
- scoper_flush_cache_flag_once($role_type . '_' . $base_cache_flag . '_for_ug' );
- }
- }
-
- function scoper_term_cache_flush() {
- // flush_cache_groups will expand this base flag to "rs_get_terms_for_user", etc.
- scoper_flush_cache_groups('get_terms');
- scoper_flush_cache_groups('scoper_get_terms');
-
- scoper_flush_cache_flag_once('all_terms');
-
- // TODO: implement this for custom taxonomies that are hierarchical and use taxonomies?
- //if ( scoper_get_otype_option( 'use_term_roles', 'post', 'page' ) )
- // scoper_flush_cache_groups('get_pages');
- }
-
- // modifies WP core _update_post_term_count to include private posts in the count, since RS roles can grant access to them
- function scoper_update_post_term_count( $terms ) {
- global $wpdb;
-
- foreach ( (array) $terms as $term ) {
- $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships, $wpdb->posts WHERE $wpdb->posts.ID = $wpdb->term_relationships.object_id AND post_status IN ('publish', 'private') AND term_taxonomy_id = %d", $term ) );
- $wpdb->update( $wpdb->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) );
- }
- }
-
- ?>