2

I have a custom user role : Photograph.

This role should not be able to log into the back-office, but have to be able to delete their own posts from the front-end.

I am using this code to block all non-admin user from connecting to the backoffice with this (function.php) :

add_action( 'init', 'blockusers_init' );
function blockusers_init() {
    if ( is_admin() && ! current_user_can( 'administrator' ) &&
         ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
        wp_redirect( home_url() );
        exit;
    }
}

And I am using this code to delete posts (portfolio.php) :

<a href="<?php echo get_delete_post_link( $post->ID ) ?>">Delete post</a>

I have tried a few other options but have never been able to only allow to photographers to delete their posts (or globally posts because they only can see theirs anyway)

Thank you !

3 Answers 3

4
+50

Assuming you're talking about a custom user role named "photographer", I believe something like this should add the delete_posts capability to that role.

function add_delete_cap_to_photog_role() {
    $role = get_role( 'photographer' );

    $role->add_cap( 'delete_posts' );
}
add_action( 'admin_init', 'add_delete_cap_to_photog_role');

After adding the caps to the role, to solve the rest you could either

  1. keep blockusers_init and ditch get_delete_post_link for an ajax delete.
  2. ditch the blockusers_init function and do a conditional redirect.

I'll give some ideas on each. I prefer ditching get_delete_post_link in this instance.

Several steps below, so be aware the code is provided as a guide only. Rewrite and rename things as needed.


Ditch get_delete_post_link aka AJAX Delete

replace get_delete_post_link line with something like this:

<?php if( current_user_can( 'delete_post' ) ) : ?>
    <a href="#" data-id="<?php the_ID() ?>" data-nonce="<?php echo wp_create_nonce('ajax_delete_post_nonce') ?>" class="delete-post">delete</a>
<?php endif ?>

Enqueue some JS

in file: functions.php

function delete_post_ajax() {
    wp_enqueue_script(  'delete_ajax', get_template_directory_uri() . '/js/my_script.js', array( 'jquery' ), '1.0.0', true );
    wp_localize_script( 'delete_ajax', 'TheAjax', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ) ) );
}

add_action( 'wp_enqueue_scripts', 'delete_post_ajax' );

onClick to pass data to delete method

in file: /js/my_script.js

jQuery( document ).ready( function($) {
    $(document).on( 'click', '.delete-post', function() {
        var id = $(this).data('id');
        var nonce = $(this).data('nonce');
        var post = $(this).parents('.post:first');
        $.ajax({
            type: 'post',
            url: TheAjax.ajaxurl,
            data: {
                action: 'wpse_ajax_delete_post',
                nonce: nonce,
                id: id
            },
            success: function( result ) {
                if( result == 'success' ) {
                    post.fadeOut( function(){
                        post.remove();
                    });
                }
            }
        })
        return false;
    })
})

the delete method

in file: functions.php (the hook we need is just "wp_ajax" prepending the name of the action in js file)

add_action( 'wp_ajax_wpse_ajax_delete_post', 'wpse_ajax_delete_post_func' );
function wpse_ajax_delete_post_func(){

    $permission = check_ajax_referer( 'ajax_delete_post_nonce', 'nonce', false );
    if( $permission == false ) {
        echo 'error';
    }
    else {
        wp_delete_post( $_REQUEST['id'] );
        echo 'success';
    }

    die();

}

To do the above by passing uri params:

Change where we swapped out get_delete_posts_link for this:

<?php if( current_user_can( 'delete_post' ) ) : ?>
    <?php $nonce = wp_create_nonce('ajax_delete_post_nonce') ?>
    <a href="<?php echo admin_url( 'admin-ajax.php?action=wpse_ajax_delete_post&id=' . get_the_ID() . '&nonce=' . $nonce ) ?>" data-id="<?php the_ID() ?>" data-nonce="<?php echo $nonce ?>" class="delete-post">delete</a>
<?php endif ?>

See this for a more involved walkthrough explaining each step


Ditch blockusers_init (aka conditional redirect)

This sets a redirect on wp-admin if user does not have manage_options cap, which is an administrator role. But first it pseudo-hides things and adds a message to user with some css:

The CSS bit

in file: functions.php

add_action('admin_head', 'hide_admin_via_css');
function hide_admin_via_css() {
    if (!current_user_can( 'manage_options' )) {
        echo '<style>body * {visibility:hidden !important;} body:before {content:"Give it a second...";}
';
    }
}

Enqueueing the JS file

in file: functions.php

function my_enqueue( $hook ) {
    if (!current_user_can( 'manage_options' )) {
        wp_enqueue_script( 'my_custom_script', get_template_directory_uri() '/js/block-admin.js' );
    }
}
add_action('admin_enqueue_scripts', 'my_enqueue');

Set the JS to just immediately redirect

file: theme_root/js/block-admin.js

window.location.href = "/";

OR a timed redirect

setTimeout(function () {
   window.location.href = "/";
}, 2000);

This approach came from clicknathan, and he offers more details here.

3
  • The photographer still gets redirected even if he has the delete capabilities. In fact, he gets redirected even if he has the admin capabilities.
    – Relisora
    Commented Jun 7, 2017 at 8:54
  • the above is just adding the ability for the role to delete posts. Since get_delete_post_link is an admin link, the block_users_init function is going to redirect it. I'll update with a way around this.
    – hwl
    Commented Jun 7, 2017 at 12:59
  • Becareful its not delete_post but delete_postS with 's' at the end Commented Feb 27, 2019 at 20:14
1

hwl's answer is correct, but I'm going to add a check if the current user is the author of the post.

<?php
$current_user = wp_get_current_user();
if ( $current_user->ID == $post->post_author ) { ?>
    <a href="<?php echo get_delete_post_link( $post->ID ) ?>">Delete post</a>
<?php } ?>

Since you might be adding in the ability to 'delete_posts', which could delete any post, this might be a safeguard. < That wasn't right.

That way the link only shows up on posts they can delete, their own.

3
  • 2
    the cap delete_posts means delete own posts, while the cap to be able to delete others' posts is delete_others_posts, yes?
    – hwl
    Commented Jun 6, 2017 at 22:39
  • Yep, you are correct. I'll edit my answer, but the I think it's good practice to only add the link on those posts they can delete.
    – socki03
    Commented Jun 6, 2017 at 22:57
  • 1
    I assumed some check had taken place with OP's "they only can see theirs anyway" line, but I don't disagree with you. Before offering a delete function, I would wrap it in such a conditional. :)
    – hwl
    Commented Jun 6, 2017 at 23:07
1

There are three requirements in your question:

  1. The role should not be able to log into the backend
  2. The role should be able to delete their own posts
  3. An implied requirement is that they should be able to delete published posts

Presumably, you have a method in place where the user can post and publish their posts without access to the backend. If so, then you can manipulate the role by:

  1. Removing the read capability
  2. Adding the delete_posts capability
  3. Add the delete_published_posts capabilty

Since the role doesn't have delete_others_posts, then they won't be able to delete other users posts - even if they're published.

Role capabilities are stored in the database, so you should add and remove them on plugin or theme activation. Here's a sample plugin that adds the required capabilities on activation and removes them on deactivation.

/**
 * Plugin Name: WordPress StackExchange Question 268755
 * Description: Allow subscribers to delete their own posts
 **/

//* On activation, add the capabilities to the subscriber role
register_activation_hook( __FILE__, 'wpse_268755_activation' );
function wpse_268755_activation() {
  $photograph = get_role( 'photograph' );
  $photograph->remove_cap( 'read' );
  $photograph->add_cap( 'delete_posts' );
  $photograph->add_cap( 'delete_published_posts' );
}

//* On deactivation, remove the capabilities from the subscriber role
register_deactivation_hook( __FILE__, 'wpse_268755_deactivation' );
function wpse_268755_deactivation() {
  $photograph = get_role( 'photograph' );
  $photograph->remove_cap( 'delete_posts' );
  $photograph->remove_cap( 'delete_published_posts' );
}

You then need a way to actually delete the post. If the user had access to the back-end you could use get_delete_post_link.

<a href="<?php echo get_delete_post_link( $post->ID ) ?>">Delete post</a>

So what you need to do is write some javascript that when that link is clicked it prevents the default behavior and sends an AJAX request to delete the post.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.