How to allow a custom user role to create, edit, delete a specific custom post type only?

But I want to allow basic traveller role users to create and edit travellers posts only. How can I do that?

The problem is that you have two different post types (posts and traveller), many roles (default roles plus basic_traveller), but only one set of capabilities (default capabilities).

What you need to do is define a new set of capabilities that will map to your new role. Here’s a basic plugin that will add a new role, define new capabilities, map those capabilities to the new role, and assign all the new capabilities to administrators.

stackexchange-sample.php:

This is the main plugin file for all my plugins. All it does is add hooks to activation, deactivation, and plugins_loaded.

<?php
/**
 * Plugin Name: Stackexchange Sample
 * Author: Nathan Johnson
 * Licence: GPL2+
 * Licence URI: https://www.gnu.org/licenses/gpl-2.0.en.html
 * Domain Path: /languages
 * Text Domain: stackexchange-sample
 */

//* Don't access this file directly
defined( 'ABSPATH' ) or die();

//* Start bootstraping the plugin
require( dirname( __FILE__ ) . '/bootstrap.php' );
add_action( 'plugins_loaded',
  [ $bootstrap = new \Stackexchange\bootstrap(), 'register' ] );

//* Register activation and deactivation hooks
register_activation_hook( __FILE__ , [ $bootstrap, 'activation' ] );
register_deactivation_hook( __FILE__ , [ $bootstrap, 'deactivation' ] );

bootstrap.php

In the bootstrap file, we have methods for activation and deactivation. In the activation method, we add a new role, assign it some capabilities, and then make sure that administrators can do everything with the new capabilities. The deactivation method does the reverse.

The only other method is register. Here we add a hook to register our custom post type.

<?php
namespace Stackexchange;

class bootstrap {

  public function register() {
    require( dirname( __FILE__ ) . '/custom-post-type.php' );
    add_action( 'init', [ new custom_post_type(), 'register' ] );
  }

  public function activation() {
    require( dirname( __FILE__ ) . '/capabilities.php' );

    $caps = new capabilities( 'traveller' );
    $caps->add( [
      'edit_',
      'read_',
      'delete_',
      'edit_s',
      'publish_s',
      'edit_published_s',
    ] );

    add_role( 'basic_traveller', __( 'Basic Traveller' ), $caps->caps() );

    $admin = get_role( 'administrator' );
    foreach( $caps->caps() as $cap => $val )
      $admin->add_cap( $cap );
  }

  public function deactivation() {
    remove_role( 'basic_traveller' );

    $admin = get_role( 'administrator' );
    $caps = new capabilities( 'traveller' );
    foreach( $caps->caps() as $cap => $val ) {
      $admin->remove_cap( $cap );
    }
  }
}

capabilities.php

The capabilities class just allows use to easily define all the capabilities needed to interact with a new post type.

<?php
namespace Stackexchange;

class capabilities {

  protected $capabilities = [
    //* Meta capabilities
    'edit_'              => false,
    'read_'              => false,
    'delete_'            => false,

    //* Primitive capabilities used outside of map_meta_cap()
    'edit_s'             => false,
    'edit_others_s'      => false,
    'publish_s'          => false,
    'read_private_s'     => false,

    //* Primitive capabilities used within of map_meta_cap()
    'delete_s'           => false,
    'delete_private_s'   => false,
    'delete_published_s' => false,
    'delete_others_s'    => false,
    'edit_private_s'     => false,
    'edit_published_s'   => false,
  ];

  public function __construct( $name ) {
    $this->name = $name;
    foreach( $this->capabilities as $key => $val ) {
      $capabilities[ $this->strl_replace( "_", "_{$this->name}", $key ) ] = $val;
    }
    $this->capabilities = $capabilities;
  }

  public function add( $caps = [] ) {
    foreach( $caps as $key ) {
      $this->capabilities[ $this->strl_replace( "_", "_{$this->name}", $key ) ] = true;
    }
  }

  public function caps() {
    return $this->capabilities;
  }

  protected function strl_replace( $search, $replace, $subject ) {
    $pos = strrpos( $subject, $search );
    if( false !== $pos ) {
      $key = substr_replace( $subject, $replace, $pos, strlen( $search ) );
    }
    return $key;
  }
}

custom-post-type.php

Finally, we register our custom post type. Notice map_meta_cap is true.

<?php
namespace Stackexchange;

class custom_post_type {
  public function register() {
    $labels = [
      'name'          => __( 'Traveller Posts' ),
      'singular_name' => __( 'Traveller_Post' ),
    ];
    $args = [
      'labels'          => $labels,
      'public'          => true,
      'has_archive'     => true,
      'capability_type' => 'traveller',
      'map_meta_cap'    => true,
    ];
    register_post_type( 'travellers', $args );
  }
}

User of the basic_traveller role will be able to publish and edit traveller posts, but will not be able to do anything with any other type of post.