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
"; 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
"; 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'] ) && 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') ); } // 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'; } } // 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); } 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 ) { $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'] = $current_rs_user->get_groups_for_user( $current_rs_user->ID, array( 'status' => 'recommended' ) ); $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'] = $current_rs_user->get_groups_for_user( $current_rs_user->ID, array( 'status' => 'requested' ) ); $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]; /* dump($_POST); dump($editable_group_ids); dump($stored_groups); dump($all_stored_groups); dump($posted_groups); dump($all_posted_groups); die; */ 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 ) ); } } ?>