From 8bb4820c7650742eab337392ba2a8df77b422f0a Mon Sep 17 00:00:00 2001 From: boonebgorges Date: Tue, 19 Jul 2011 21:15:54 +0000 Subject: [PATCH] Filters Doc permalinks for more reliable Admin linkage. Replaces Recent Comments dashboard widget to protect non-public Doc comments from being visible to certain members. Fixes #115 git-svn-id: http://plugins.svn.wordpress.org/buddypress-docs/trunk@412207 b8457f37-d9ea-0310-8a92-e5e31aec5664 --- bp-docs.php | 9 +- includes/admin.php | 146 ++++++++++++++++++++++++++++++++ includes/integration-bp.php | 32 +++++++ includes/integration-groups.php | 103 +++++++++++++++++++--- includes/templatetags.php | 2 +- readme.txt | 2 + 6 files changed, 282 insertions(+), 12 deletions(-) create mode 100644 includes/admin.php diff --git a/bp-docs.php b/bp-docs.php index d0f13b84..d039ade7 100644 --- a/bp-docs.php +++ b/bp-docs.php @@ -127,6 +127,10 @@ function load_constants() { // The slug used when deleting a doc if ( !defined( 'BP_DOCS_DELETE_SLUG' ) ) define( 'BP_DOCS_DELETE_SLUG', 'delete' ); + + // By default, BP Docs will replace the Recent Comments WP Dashboard Widget + if ( !defined( 'BP_DOCS_REPLACE_RECENT_COMMENTS_DASHBOARD_WIDGET' ) ) + define( 'BP_DOCS_REPLACE_RECENT_COMMENTS_DASHBOARD_WIDGET', true ); } /** @@ -203,7 +207,6 @@ function register_post_type() { 'hierarchical' => false, 'supports' => array( 'title', 'editor', 'revisions', 'excerpt', 'comments' ), 'query_var' => true, - //'rewrite' => false 'rewrite' => false // Todo: This bites ) ); @@ -277,6 +280,10 @@ function includes() { // formatting.php contains filters and functions used to modify appearance only require_once( BP_DOCS_INCLUDES_PATH . 'formatting.php' ); + + // Dashboard-specific functions + if ( is_admin() ) + require_once( BP_DOCS_INCLUDES_PATH . 'admin.php' ); } /** diff --git a/includes/admin.php b/includes/admin.php new file mode 100644 index 00000000..580e7bc8 --- /dev/null +++ b/includes/admin.php @@ -0,0 +1,146 @@ +__construct(); + } + + /** + * PHP 5 constructor + * + * @package BuddyPress Docs + * @since 1.1.8 + */ + function __construct() { + // Replace the Dashboard widget + if ( !defined( BP_DOCS_REPLACE_RECENT_COMMENTS_DASHBOARD_WIDGET ) || !BP_DOCS_REPLACE_RECENT_COMMENTS_DASHBOARD_WIDGET ) { + add_action( 'wp_dashboard_setup', array( $this, 'replace_recent_comments_dashboard_widget' ) ); + } + } + + function replace_recent_comments_dashboard_widget() { + global $wp_meta_boxes; + + // Find the recent comments widget + foreach ( $wp_meta_boxes['dashboard'] as $context => $widgets ) { + if ( array_key_exists( 'dashboard_recent_comments', $widgets['core'] ) ) { + // Take note of the context for when we add our widget + $drc_widget_context = $context; + + // Store the widget so that we have access to its information + $drc_widget = $widgets['core']['dashboard_recent_comments']; + + // Store the array keys, so that we can reorder things later + $widget_order = array_keys( $widgets['core'] ); + + // Remove the core widget + remove_meta_box( 'dashboard_recent_comments', 'dashboard', $drc_widget_context ); + + // No need to continue the loop + break; + } + } + + // If we couldn't find the recent comments widget, it must have been removed. We'll + // assume this means we shouldn't add our own + if ( empty( $drc_widget ) ) + return; + + // Set up and add our widget + $recent_comments_title = __( 'Recent Comments' ); + + // Add our widget in the same location + wp_add_dashboard_widget( 'dashboard_recent_comments_bp_docs', $recent_comments_title, array( $this, 'wp_dashboard_recent_comments' ), 'wp_dashboard_recent_comments_control' ); + + // Restore the previous widget order. File this under "good citizenship" + $wp_meta_boxes['dashboard'][$context]['core']['dashboard_recent_comments'] = $wp_meta_boxes['dashboard'][$context]['core']['dashboard_recent_comments_bp_docs']; + + unset( $wp_meta_boxes['dashboard'][$context]['core']['dashboard_recent_comments_bp_docs'] ); + + // In order to inherit the styles, we're going to spoof the widget ID. Sadness + $wp_meta_boxes['dashboard'][$context]['core']['dashboard_recent_comments']['id'] = 'dashboard_recent_comments'; + } + + /** + * Replicates WP's native recent comments dashboard widget. + * + * @package BuddyPress Docs + * @since 1.1.8 + */ + function wp_dashboard_recent_comments() { + global $wpdb, $bp; + + if ( current_user_can('edit_posts') ) + $allowed_states = array('0', '1'); + else + $allowed_states = array('1'); + + // Select all comment types and filter out spam later for better query performance. + $comments = array(); + $start = 0; + + $widgets = get_option( 'dashboard_widget_options' ); + $total_items = isset( $widgets['dashboard_recent_comments'] ) && isset( $widgets['dashboard_recent_comments']['items'] ) + ? absint( $widgets['dashboard_recent_comments']['items'] ) : 5; + + while ( count( $comments ) < $total_items && $possible = $wpdb->get_results( "SELECT c.*, p.post_type AS comment_post_post_type FROM $wpdb->comments c LEFT JOIN $wpdb->posts p ON c.comment_post_ID = p.ID WHERE p.post_status != 'trash' ORDER BY c.comment_date_gmt DESC LIMIT $start, 50" ) ) { + + foreach ( $possible as $comment ) { + if ( count( $comments ) >= $total_items ) + break; + + // Is the user allowed to read this doc? + if ( $bp->bp_docs->post_type_name == $comment->comment_post_post_type && !bp_docs_user_can( 'read', get_current_user_ID(), $comment->comment_post_ID ) ) + continue; + + if ( in_array( $comment->comment_approved, $allowed_states ) && current_user_can( 'read_post', $comment->comment_post_ID ) ) + $comments[] = $comment; + } + + $start = $start + 50; + } + + if ( $comments ) : + ?> + +
+ + +
+ + + views(); ?> + + +

+ + \ No newline at end of file diff --git a/includes/integration-bp.php b/includes/integration-bp.php index 4475dc25..9fea11a4 100644 --- a/includes/integration-bp.php +++ b/includes/integration-bp.php @@ -60,6 +60,9 @@ function __construct() { // Keep comment notifications from being sent add_filter( 'comment_post', array( $this, 'check_comment_type' ) ); + // Make sure that comment links are correct. Can't use $wp_rewrite bc of assoc items + add_filter( 'post_type_link', array( $this, 'filter_permalinks' ), 10, 4 ); + // AJAX handler for removing the edit lock when a user clicks away from Edit mode add_action( 'wp_ajax_remove_edit_lock', array( $this, 'remove_edit_lock' ) ); @@ -596,6 +599,35 @@ function check_comment_type( $comment_id ) { } } + /** + * Display the proper permalink for Docs + * + * This function filters 'post_type_link', which in turn powers get_permalink() and related + * functions. + * + * In brief, the purpose is to make sure that Doc permalinks point to the proper place. + * Ideally I would use a rewrite rule to accomplish this, but it's impossible to write + * regex that will be able to tell which group/user a Doc should be associated with. + * + * @package BuddyPress Docs + * @since 1.1.8 + * + * @param str $link The permalink + * @param obj $post The post object + * @param bool $leavename + * @param bool $sample See get_post_permalink() for an explanation of these two params + * @return str $link The filtered permalink + */ + function filter_permalinks( $link, $post, $leavename, $sample ) { + global $bp; + + if ( $bp->bp_docs->post_type_name == $post->post_type ) { + $link = bp_docs_get_doc_link( $post->ID ); + } + + return $link; + } + /** * AJAX handler for remove_edit_lock option * diff --git a/includes/integration-groups.php b/includes/integration-groups.php index fa9884c1..daf9a896 100644 --- a/includes/integration-groups.php +++ b/includes/integration-groups.php @@ -53,7 +53,7 @@ function __construct() { add_action( 'bp_docs_taxonomy_save_item_terms', array( $this, 'save_group_terms' ) ); // Filter the core user_can_edit function for group-specific functionality - add_filter( 'bp_docs_user_can', array( $this, 'user_can' ), 10, 3 ); + add_filter( 'bp_docs_user_can', array( $this, 'user_can' ), 10, 4 ); // Add group-specific settings to the doc settings box add_filter( 'bp_docs_doc_settings_markup', array( $this, 'doc_settings_markup' ) ); @@ -208,29 +208,39 @@ function save_group_terms( $terms ) { } /** - * Determine whether a user can edit the group doc is question + * Determine whether a user can edit the group doc in question * * @package BuddyPress Docs * @since 1.0-beta * * @param bool $user_can The default perms passed from bp_docs_user_can_edit() - * @param str $action At the moment, 'edit', 'manage', 'create' + * @param str $action At the moment, 'edit', 'manage', 'create', 'read' * @param int $user_id The user id whose perms are being tested + * @param int $doc_id Optional. The id of the doc being checked. Defaults to current */ - function user_can( $user_can, $action, $user_id ) { + function user_can( $user_can, $action, $user_id, $doc_id = false ) { global $bp, $post; $user_can = false; + // If a doc_id is provided, check it against the current post before querying + if ( $doc_id && isset( $post->ID ) && $doc_id == $post->ID ) { + $doc = $post; + } + if ( empty( $post->ID ) ) - $post = !empty( $bp->bp_docs->current_post ) ? $bp->bp_docs->current_post : false; + $doc = !empty( $bp->bp_docs->current_post ) ? $bp->bp_docs->current_post : false; // Keep on trying to set up a post - if ( empty( $post->ID ) ) - $post = bp_docs_get_current_doc(); + if ( empty( $doc ) ) + $doc = bp_docs_get_current_doc(); + + // If we still haven't got a post by now, query based on doc id + if ( empty( $doc ) ) + $doc = get_post( $doc_id ); - if ( !empty( $post->ID ) ) { - $doc_settings = get_post_meta( $post->ID, 'bp_docs_settings', true ); + if ( !empty( $doc ) ) { + $doc_settings = get_post_meta( $doc->ID, 'bp_docs_settings', true ); // Manage settings don't always get set on doc creation, so we need a default if ( empty( $doc_settings['manage'] ) ) @@ -245,9 +255,33 @@ function user_can( $user_can, $action, $user_id ) { $doc_settings['read_comments'] = 'anyone'; } - $group_id = $bp->groups->current_group->id; + // Default to the current group, but get the associated doc if not + if ( isset( $bp->groups->current_group->id ) ) { + $group_id = $bp->groups->current_group->id; + $group = $bp->groups->current_group; + } else { + $group_id = bp_docs_get_associated_group_id( $doc->ID, $doc ); + + if ( is_array( $group_id ) ) { + $group_id = $group_id[0]; // todo: make this a loop + $group = new BP_Groups_Group( $group_id ); + } + } switch ( $action ) { + case 'read' : + // At the moment, read permissions are entirely based on group + // membership and privacy level + if ( 'public' != $group->status ) { + if ( groups_is_user_member( $user_id, $group_id ) ) { + $user_can = true; + } + } else { + $user_can = true; + } + + break; + case 'create' : $group_settings = groups_get_groupmeta( $group_id, 'bp-docs' ); @@ -1090,4 +1124,53 @@ function bp_docs_is_docs_enabled_for_group( $group_id = false ) { return apply_filters( 'bp_docs_is_docs_enabled_for_group', $docs_is_enabled, $group_id ); } +/** + * Get the group associated with a Doc + * + * In order to be forward-compatible, this function will return an array when more than one group + * is found. + * + * @package BuddyPress Docs + * @since 1.1.8 + * + * @param int $doc_id The id of the Doc + * @param obj $doc The Doc post object. If you've already got this, send it along to avoid another + * query + * @param bool $single_array This is a funky one. If only a single group_id is found, should it be + * returned as a singleton array, or as an int? Defaults to the latter. + * @return mixed $group_id Either an array or a string of the group id(s) + */ +function bp_docs_get_associated_group_id( $doc_id, $doc = false, $single_array = false ) { + global $bp; + + if ( !$doc ) { + $doc = get_post( $doc_id ); + } + + if ( !$doc ) { + return false; + } + + $post_terms = wp_get_post_terms( $doc_id, $bp->bp_docs->associated_item_tax_name ); + + $group_ids = array(); + + foreach( $post_terms as $post_term ) { + // Make sure this is a group term + $parent_term = get_term( $post_term->parent, $bp->bp_docs->associated_item_tax_name ); + + if ( 'group' == $parent_term->slug ) { + $group_ids[] = $post_term->name; + } + } + + if ( !$single_array && ( count( $group_ids ) <= 1 ) ) { + $return = implode( ',', $group_ids ); + } else { + $return = $group_ids; + } + + return apply_filters( 'bp_docs_get_associated_group_id', $group_ids, $doc_id, $doc, $single_array ); +} + ?> diff --git a/includes/templatetags.php b/includes/templatetags.php index ed31fc18..fbda7ba9 100644 --- a/includes/templatetags.php +++ b/includes/templatetags.php @@ -400,7 +400,7 @@ function bp_docs_user_can( $action = 'edit', $user_id = false, $doc_id = false ) $user_id = bp_loggedin_user_id(); // Only certain actions are checked against doc_ids - $need_doc_ids_actions = apply_filters( 'bp_docs_need_doc_ids_actions', array( 'edit', 'manage', 'view_history' ) ); + $need_doc_ids_actions = apply_filters( 'bp_docs_need_doc_ids_actions', array( 'edit', 'manage', 'view_history', 'read' ) ); if ( in_array( $action, $need_doc_ids_actions ) ) { if ( !$doc_id ) { diff --git a/readme.txt b/readme.txt index efd81ddf..b55d686b 100644 --- a/readme.txt +++ b/readme.txt @@ -36,6 +36,8 @@ This plugin is in active development. For feature requests and bug reports, visi == Changelog == = 1.1.8 = +* Filters get_post_permalink() so that Doc permalinks in the Admin point to the proper place +* Modifies Recent Comments dashboard widget in order to prevent non-allowed people from seeing certain Doc comments * Adds Print button to TinyMCE * Adds Brazilian Portuguese localization.