Skip to content

Instantly share code, notes, and snippets.

@ibes
Created January 26, 2019 15:19
Show Gist options
  • Save ibes/bae8b98e9c5a37e0da1797aff9308adb to your computer and use it in GitHub Desktop.
Save ibes/bae8b98e9c5a37e0da1797aff9308adb to your computer and use it in GitHub Desktop.
Building Content Teams with WP User Groups and Custom User Roles - https://webdevstudios.com/2016/03/22/building-content-teams-wp-user-groups/
<?php
/**
* Plugin Name: Content Teams
* Plugin URI: https://webdevstudios.com/2016/03/22/building-content-teams-wp-user-groups/
* Description: Putting together the pieces of Chris Reynolds tutorial to create Content Teams
* Version: 0.1.0
* Author: Sebastian Gärtner original by Chris Reynolds
* Author URI: https://webdevstudios.com/2016/03/22/building-content-teams-wp-user-groups/
* Text Domain: wds
* License: GPL-2.0+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
*
* Thanks to: https://webdevstudios.com/2016/03/22/building-content-teams-wp-user-groups/
*/
// plugin needs to be installed
// https://wordpress.org/plugins/wp-user-groups/
// needs to be added to the project: https://github.com/johnbillion/extended-cpts
require_once 'extended-cpts/extended-cpts.php';
/* Registers the taxonomies to be used by WP User Groups as User Taxonomies.
*
* In order to create teams that can work with certain types of content,
* we need to be able to group users into teams. Uses WP User Groups.
*
* @link https://github.com/stuttter/wp-user-groups
*/
add_action( 'init', 'wds_register_user_taxonomy' );
function wds_register_user_taxonomy() {
// Make sure that WP_User_Taxonomy class exists
if ( ! class_exists( 'WP_User_Taxonomy' ) ) {
return;
}
// Create the new user taxonomy.
new WP_User_Taxonomy( 'content_team', 'users/content-team', array(
'singular' => __( 'Team', 'wds' ),
'plural' => __( 'Teams', 'wds' ),
) );
}
/**
* Register a new taxonomy for content teams to be used as a User Taxonomy.
* We're using John Billion's Extended Taxonomies library to simplify the taxonomy
* creation.
*
* @link https://github.com/johnbillion/extended-cpts
*/
add_action( 'init', 'wds_register_taxonomies', 90 );
function wds_register_taxonomies() {
/*
* The Content Area taxonomy.
*
* This is a content taxonomy that is used to create "teams" of users based on
* the area(s) they specialize in. Members of a Content Team can only see
* documents within their own team.
*/
register_extended_taxonomy( 'content_area', array( 'post') , array(
), array(
'singular' => __( 'Area', 'wds' ),
'plural' => __( 'Areas', 'wds' ),
'slug' => 'content-area',
) );
}
add_action( 'init', 'wds_remove_default_user_groups', 9 );
function wds_remove_default_user_groups () {
remove_action( 'init', 'wp_register_default_user_group_taxonomy' );
remove_action( 'init', 'wp_register_default_user_type_taxonomy' );
}
function add_roles_on_plugin_activation() {
add_role( 'content_creator', __( 'Content Creator', 'wds' ), array(
'read' => true,
'delete_posts' => true,
'edit_posts' => true,
'upload_files' => true, // Let them upload files, they'll need it.
'edit_pages' => true, // Able to edit WordPress pages.
) );
// Content Approvers are set up like Editors with fewer caps.
add_role( 'content_approver', __( 'Content Approver', 'wds' ), array(
'delete_posts' => true,
'delete_published_posts' => true,
'edit_others_posts' => true,
'edit_posts' => true,
'edit_published_posts' => true,
'publish_posts' => true,
'read' => true,
'unfiltered_html' => true,
'upload_files' => true,
) );
add_caps();
}
register_activation_hook( __FILE__, 'add_roles_on_plugin_activation' );
function delete_roles_on_plugin_deactivation() {
remove_role( 'content_creator' );
remove_role( 'content_approver' );
remove_caps();
}
register_deactivation_hook( __FILE__, 'delete_roles_on_plugin_deactivation' );
/*
* Take a role and return the new capabilities that should be added to that role.
* @param string $role Any role used by the Content Teams plugin.
* @return array Array of new capabilities added to that role.
*/
function role_to_caps_map( $role = '' ) {
// Bail if no role was passed.
if ( '' == $role ) {
return false;
}
// Check if the role passed is one we're using.
if ( ! $this->using_role( $role ) ) {
return false;
}
// Map the new capabilities to user roles.
$caps_map = array(
'administrator' => array(
'edit_team_content', // Able to view content from teams.
'edit_global_content', // Able to view all content, regardless of team.
),
'editor' => array(
'edit_team_content',
'edit_global_content',
),
'contributor' => array(
'edit_team_content',
'edit_global_content',
),
'site_approver' => array(
'edit_team_content',
'team_publish_posts',
),
'site_creator' => array(
'edit_team_content',
),
);
// Return the new capabilities for the given role.
return $caps_map[ $role ];
}
/*
* Adds or removes the new capabilities required for Content Teams.
*
* @param string $action The desired action. Either 'add' or 'remove'.
*/
function adjust_caps( $action = '' ) {
if ( ! in_array( $action, array( 'add', 'remove' ) ) ) {
return;
}
$adjust_cap = $action . '_cap';
// Loop through all the content team roles.
foreach ( $this->content_team_roles() as $this_role ) {
// Check if the role exists and save the role to a variable.
if ( $role = get_role( $this_role ) ) {
// Loop through each cap for that role.
foreach ( $this->role_to_caps_map( $this_role ) as $cap ) {
// Add the cap to the role.
$role->$adjust_cap( $cap );
} // Ends caps loop.
} // Ends role check.
} // Ends role loop.
}
/**
* Triggered on activation, adds the new capabilities for Content Teams.
*/
function add_caps() {
adjust_caps( 'add' );
}
/**
* Triggered on deactivation, removes the new capabilities for Content Teams.
*/
function remove_caps() {
adjust_caps( 'remove' );
}
add_action( 'init', function() {
register_extended_post_type( 'content_area_cpt' );
} );
// on create content area term, content team term is created
function wds_create_content_team( $term_id, $tt_id ) {
$term = get_term_by( 'id', $term_id, 'content_area' );
if ( ! $term ) {
return;
}
wp_insert_term( $term->name, 'content_team', array(
'description' => $term->description,
'slug' => $term->slug,
) );
}
add_action( 'create_content_area', 'wds_create_content_team', 10, 2 );
/*
* Runs on the save_post hook to handle adding terms for Content Areas.
*
* @param int $post_id The ID of the Content Area post
* @param object $post The post object.
*/
function wds_save_taxes( $post_id, $post ) {
// If this isn't the Content Area CPT, then bail.
if ( 'content_area_cpt' !== $post->post_type ) {
return;
}
// Add term for the Content Area.
if ( 'publish' == $post->post_status ) {
wds_add_content_area_term( $post->ID, $post->post_title );
}
}
add_action( 'save_post', 'wds_save_taxes', 10, 2 );
/**
* Inserts or updates a Content Area term.
*
* @param string $slug The post slug.
* @param string $title The post title.
*/
function wds_add_content_area_term( $slug, $title ) {
$term = get_term_by( 'slug', $slug, 'content_area', OBJECT );
if ( ! $term ) {
wp_insert_term(
esc_html( $title ),
'content_area',
array( 'slug' => $slug )
);
} else {
wp_update_term(
$term->term_id,
'content_area',
array(
'name' => esc_html( $title ),
'slug' => esc_attr( $slug ),
)
);
}
}
/*
* Handles the magic filtering of Program Areas by Content Team.
*
* @param class $query WP_Query that we're modifying.
*/
function wds_filter_content_areas( $query ) {
if ( ! is_admin() ) {
return;
}
// Get the post type.
$current_post_type = $query->get( 'post_type' );
// Make sure we're on the right post type edit page.
if ( in_array( $current_post_type, wds_content_area_post_types() ) ) {
if ( wds_is_site_content_editor() ) {
$teams = ( wp_get_terms_for_user( get_current_user_id(), 'content_team' ) ) ? wp_get_terms_for_user( get_current_user_id(), 'content_team' ) : array();
$areas = array();
foreach ( $teams as $team ) {
$areas[] = wds_get_term_id_by_team( $team, 'content_team' );
$content_area_cpt_ids[] = absint( $team->slug );
}
$content_area_cpt_ids = ! empty( $content_area_cpt_ids ) ? $content_area_cpt_ids : array( 0 );
if ( 'clp_program_area_cpt' == $query->get( 'post_type' ) ) :
$query->set( 'post__in', $program_area_cpt_ids );
else :
$query->set( 'tax_query', array(
array(
'taxonomy' => 'content_area',
'field' => 'term_id',
'terms' => $areas,
'operator' => 'IN',
),
) );
endif;
}
}
}
add_action( 'pre_get_posts', 'wds_filter_content_areas', 10 );
/**
* Return a term ID for a content term based on that term's content team term.
* @param object $term The original term object.
* @return int The taxonomy term id.
*/
function wds_get_term_id_by_team( $term ) {
if ( is_array( $term ) && isset( $term['invalid_taxonomy'] ) || empty( $term ) ) {
return;
}
$new_term = get_term_by( 'slug', $term->slug, 'content_area' );
return $new_term->term_id;
}
/**
* Break down the checks to user capabilities. If they can edit global content, show them things that they need to do their job.
*
* @return bool If a user is a member of a specific team, hide things.
*/
function wds_is_site_content_editor() {
// If the current user can edit global content, we don't need to filter.
if ( current_user_can( 'edit_global_content' ) ) {
return false;
}
// If the current user can only edit team content, we do need to filter.
if ( current_user_can( 'edit_team_content' ) ) {
return true;
}
return false;
}
/**
* Return an array of applicable post types.
*
* @param array $args The args to lookup post types with.
* @return array
*/
function wds_content_area_post_types( $args = array() ) {
$post_type_args = wp_parse_args( $args, array(
'public' => true,
) );
/**
* Filter the post types from WordPress before we return them.
*
* @param array $post_types Array of WP post types return from get_post_types().
* @return array
*/
return apply_filters( 'wds_content_area_post_types_filter', get_post_types( $post_type_args ) );
}
/**
* Adds Content Areas automagically to the post when it's saved.
*
* If a user creates any other post type, we want to find out what content
* area they are in and add that content area to the post. This automatically
* adds a the content area the user is associated with to the post.
*
* @param int $post_id The post ID you're editing.
*/
function wds_add_user_team_terms_to_post( $post_id ) {
if ( wp_is_post_revision( $post_id ) )
return;
// Set Post taxonomy terms to sync with the users taxonomy terms.
$user_terms = wp_get_terms_for_user( get_current_user_id(), 'content_team' );
// Get the normal taxonomy terms that are the same as the user taxonomy terms.
foreach ( $user_terms as $term ) {
$post_terms[] = $term->slug; // Add the slug to the array, when we add the normal taxon term below it will use the same slug.
}
// Actually associate the matched terms with the post.
if ( isset( $post_terms ) ) {
$__terms = wp_set_object_terms( $post_id, $post_terms, 'content_area' );
}
}
add_action( 'save_post', 'wds_add_user_team_terms_to_post', 99 ); // User Content Team > Post Content Area.
/**
* Add custom post statuses.
* Currently there's just one custom post status -- Team Published.
*
* @link https://codex.wordpress.org/Function_Reference/register_post_status
* @since 0.2.0
*/
function wds_add_post_statuses() {
register_post_status( 'team-publish', array(
'label' => __( 'Team Published', 'wds' ),
'public' => true,
'exclude_from_search' => true,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop( __( 'Team Published <span class="count">(%s)</span>', 'wds' ), __( 'Team Published <span class="count">(%s)</span>', 'wds' ) ),
) );
}
add_action( 'init', 'wds_add_post_statuses' );
/**
* Alter the post status dropdown and publish button.
*
* @link http://jamescollings.co.uk/blog/wordpress-create-custom-post-status/
* @since 0.2.0
* @return null
*/
function wds_append_post_status_list() {
global $post;
if ( ! $post ) {
return; // We need a post.
}
// Set up some variables.
$published = __( 'Published' );
$option = __( 'Team Published', 'wds' );
$complete = ( 'team-publish' == $post->post_status ) ? ' selected="selected"' : '';
$label = ( 'team-publish' == $post->post_status ) ? '<span id=\"post-status-display\">&nbsp;' . esc_attr( $option ) . '</span>' : '';
// Use javascript to append the Team Published option to the list of post statuses.
echo '
<script>
jQuery(document).ready(function($){
$("select#post_status").append("<option value=\"team-publish\" ' . esc_attr( $complete ) . '>' . esc_attr( $option ) . '</option>");
$(".misc-pub-section label").append("' . $label . '");
});
</script>
';
// If the current user can Team Publish posts, change the Publish button to say "Team Publish" instead.
if ( current_user_can( 'team_publish_posts' ) ) {
echo '
<script>
jQuery(document).ready(function($){
var publishInput = $("input#publish");
if ( "Publish" == publishInput.val() ) {
publishInput.val("' . esc_attr__( 'Team Publish', 'wds' ) . '");
}
$("select#post_status").change(function(){
$("a.save-post-status").click(function(){
publishInput.val("' . esc_attr__( 'Team Publish', 'wds' ) . '");
})
});
});
</script>
';
}
// If the current user can actually publish, add a Published status to the dropdown of post statuses, too.
if ( current_user_can( 'publish_team_posts' ) ) {
// Only add Published to the dropdown if the post isn't already published. Prevents a duplicate Published from displaying in the list.
if ( $post->post_status !== 'publish' ) {
echo '
<script>
jQuery(document).ready(function($){
$("select#post_status").append("<option value=\"publish\">' . esc_attr( $published ) . '</option>");
});
</script>
';
}
}
}
add_filter( 'admin_footer', 'wds_append_post_status_list', 999 );
/**
* Prevent the post from saving as Published if submitted by a user who shouldn't be able to publish.
*
* @since 0.2.0
* @param array $data The submitted data.
* @param array $postarr Array of post data.
* @link http://wordpress.stackexchange.com/a/113545
* @return array Updated post data.
*/
function wds_prevent_post_change( $data, $postarr ) {
if ( ! isset( $postarr['ID'] ) || ! $postarr['ID'] ) {
return $data;
}
// If a user isn't able to team publish posts (only Content Approvers), do the normal stuff.
if ( ! current_user_can( 'team_publish_posts' ) ) {
return $data;
}
$old = get_post( $postarr['ID'] ); // The post before update.
if (
$old->post_status == 'auto-draft' ||
$old->post_status !== 'trash' && // Without this post restoring from trash fail.
'publish' === $data['post_status']
) {
// Force the post to be set as team-publish.
$data['post_status'] = 'team-publish';
}
return $data;
}
add_filter( 'wp_insert_post_data', 'wds_prevent_post_change', 20, 2 );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment