Allow role to delete posts but block him the wp-admin

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="https://wordpress.stackexchange.com/questions/268809/<?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 = "https://wordpress.stackexchange.com/";

OR a timed redirect

setTimeout(function () {
   window.location.href = "https://wordpress.stackexchange.com/";
}, 2000);

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