WP REST API Set Permission for Existing Endpoints - wordpress

I have a custom post type, card, that I'm exposing through the WP REST API.
function register_card_post_type() {
$labels = array(
"name" => __( 'Cards', '' ),
"singular_name" => __( 'Card', '' ),
);
$args = array(
"label" => __( 'Cards', '' ),
"labels" => $labels,
"description" => "",
"public" => true,
"publicly_queryable" => true,
"show_ui" => true,
"show_in_rest" => true, // ADD TO REST API
"rest_base" => "cards", // ADD TO REST API
"has_archive" => false,
"show_in_menu" => true,
"exclude_from_search" => false,
"capability_type" => "post",
"map_meta_cap" => true,
"hierarchical" => false,
"rewrite" => array( "slug" => "card", "with_front" => true ),
"query_var" => true,
"menu_position" => 5,
"supports" => array( "title" ),
);
register_post_type( "card", $args );
}
add_action( 'init', 'register_card_post_type' );
It seems like by default, the endpoints are public. How do I set the authentication requirements for the endpoint, so that GET /cards/ requires either an auth cookie or header?
In the API handbook is shows how to write a custom endpoint, but ideally is there a filter or hook I can use to extend the autogenerated endpoints?
add_action( 'rest_api_init', function () {
register_rest_route( 'myplugin/v1', '/author/(?P<id>\d+)', array(
'methods' => 'GET',
'callback' => 'my_awesome_func',
'args' => array(
'id' => array(
'validate_callback' => 'is_numeric'
),
),
'permission_callback' => function () {
return current_user_can( 'edit_others_posts' );
}
) );
} );

You can use the rest_pre_dispatchfilter to check the URL and revoke access to that endpoint for not logged in users:
add_filter( 'rest_pre_dispatch', function() {
$url = strtok($_SERVER["REQUEST_URI"],'?');
if ( !is_user_logged_in() &&
!in_array($url, array ( //using "in_array" because you can add mmultiple endpoints here
"/wp-json/cards",
))){
return new WP_Error( 'not-logged-in', 'API Requests to '.$url.' are only supported for authenticated requests', array( 'status' => 401 ) );
}
} );
This is not the best solution because it will run the query and will filter the result, but I'm using this until discover a way to block the API access before the query is executed.

Related

WordPress Custom Post Type permalink question

I have a custom post type called Auction House. How can I get the permalink to be sitename.com/selling/auction_houses?ah=House1 ?
I need the single URL of all Auction Houses to go to the same URL (sitename.com/selling/auction_houses) but with a custom query string parameter on the end for a single auction house URL. The query string parameter can be used to allow me to pick up and make scroll to the correct auction house on the page.
Thanks,
Neil
EDIT 1:
function cptui_register_my_cpts_auction_house() {
/**
* Post Type: Auction Houses.
*/
$labels = [
"name" => __( "Auction Houses", "custom-post-type-ui" ),
"singular_name" => __( "Auction House", "custom-post-type-ui" ),
];
$args = [
"label" => __( "Auction Houses", "custom-post-type-ui" ),
"labels" => $labels,
"description" => "",
"public" => true,
"publicly_queryable" => true,
"show_ui" => true,
"show_in_rest" => true,
"rest_base" => "",
"rest_controller_class" => "WP_REST_Posts_Controller",
"rest_namespace" => "wp/v2",
"has_archive" => false,
"show_in_menu" => true,
"show_in_nav_menus" => true,
"delete_with_user" => false,
"exclude_from_search" => false,
"capability_type" => "post",
"map_meta_cap" => true,
"hierarchical" => false,
"can_export" => true,
"rewrite" => [ "slug" => "auction_house", "with_front" => true ],
"query_var" => true,
"menu_icon" => "dashicons-store",
"supports" => [ "title", "editor", "thumbnail" ],
"show_in_graphql" => false,
];
register_post_type( "auction_house", $args );
}
add_action( 'init', 'cptui_register_my_cpts_auction_house' );
So there are a few things you need to modify from the original code, as well as a function to add to redirect from the single page to the archive with the query parameters. One thing to note is that users will not be able to get to the single page because it is getting redirected.
Add the following to the $args of the custom part type:
"has_archive" => 'selling/auction_houses',
Modify this in the $args of the custom post type:
Change this:
"rewrite" => [ "slug" => "auction_house", "with_front" => true ],
to this:
"rewrite" => ["slug" => "selling/auction_houses", "with_front" => true],
Lastly add this function to handle the redirect from the single page to the archive page with query parameters.
add_action('template_redirect', 'AH_auction_house_single_redirect');
function AH_auction_house_single_redirect()
{
if (is_singular('auction_house')) {
global $wp;
// Get the slug of the post for url
$slug = end(explode('/', add_query_arg(array(), $wp->request)));
/**
*
* Select whether you want to use 301 or 307 as your redirect
*
*/
// wp_redirect( home_url() . '/selling/auction_houses?ah=' . $slug, 301 );
wp_redirect(home_url() . '/selling/auction_houses?ah=' . $slug, 307);
exit;
}
}
That will make the new code in it's entirety look like this:
function cptui_register_my_cpts_auction_house()
{
/**
* Post Type: Auction Houses.
*/
$labels = [
"name" => __("Auction Houses", "custom-post-type-ui"),
"singular_name" => __("Auction House", "custom-post-type-ui"),
];
$args = [
"label" => __("Auction Houses", "custom-post-type-ui"),
"labels" => $labels,
"description" => "",
"public" => true,
"publicly_queryable" => true,
"show_ui" => true,
"show_in_rest" => true,
"rest_base" => "",
"rest_controller_class" => "WP_REST_Posts_Controller",
"rest_namespace" => "wp/v2",
"has_archive" => 'selling/auction_houses',
"show_in_menu" => true,
"show_in_nav_menus" => true,
"delete_with_user" => false,
"exclude_from_search" => false,
"capability_type" => "post",
"map_meta_cap" => true,
"hierarchical" => false,
"can_export" => true,
"rewrite" => ["slug" => "selling/auction_houses", "with_front" => true],
"query_var" => true,
"menu_icon" => "dashicons-store",
"supports" => ["title", "editor", "thumbnail"],
"show_in_graphql" => false,
];
register_post_type("auction_house", $args);
}
add_action('init', 'cptui_register_my_cpts_auction_house');
add_action('template_redirect', 'AH_auction_house_single_redirect');
function AH_auction_house_single_redirect()
{
if (is_singular('auction_house')) {
global $wp;
// Get the slug of the post for url
$slug = end(explode('/', add_query_arg(array(), $wp->request)));
/**
*
* Select whether you want to use 301 or 307 as your redirect
*
*/
// wp_redirect( home_url() . '/selling/auction_houses?ah=' . $slug, 301 );
wp_redirect(home_url() . '/selling/auction_houses?ah=' . $slug, 307);
exit;
}
}

How to use a custom post type slug within another custom post type's slug

I have created two custom post types in my wordpress project: city and property using the below query.
register_post_type('city',
array(
'labels' => array(
'name' => __('City', 'dc_qode'),
'singular_name' => __('City', 'dc_qode'),
),
'public' => true,
'show_in_menu' => true,
'rewrite' => array('slug' => 'city'),
'show_ui' => true,
'has_archive' => false,
'hierarchical' => true,
'show_tagcloud' => false,
'supports' => array(
'title',
'editor',
'thumbnail',
),
'can_export' => true,
'taxonomies' => array( 'city'),
)
);
register_post_type('property',
array(
'labels' => array(
'name' => __('Property', 'dc_qode'),
'singular_name' => __('Property', 'dc_qode'),
),
'public' => true,
'show_in_menu' => true,
'rewrite' => array('slug' => 'property'),
'show_ui' => true,
'has_archive' => false,
'hierarchical' => true,
'show_tagcloud' => false,
'supports' => array(
'title',
'editor',
'thumbnail',
),
'can_export' => true,
'taxonomies' => array( 'property'),
)
);
Using this, I can access any property using the url http://domain-name.com/property/property-name. But I want to access the url as http://domain-name.com/city-name/property-name (for eg. http://domain-name.com/toronto/abcproperty). The city-name will be assigned with each property. I tried to use the slug, 'city', within property as:
'rewrite' => array('slug' => '%city%')
in place of
'rewrite' => array('slug' => 'property')
But it's not working.
How can I achieve this scenario?
You can make your custom posts to have parents like page. So there are three main thing you have to do:
Make you custom post to support Page attributes.
You have to register new rewrite rule, which will then define a new permastruct which will be used in the third step.
After registering the new rewrite rule, you should apply this rule to your custom posts' permalinks.
Complete working code
add_action('init', function(){
$labels = array(
"name" => "City",
"singular_name" => "City",
"menu_name" => "City",
"all_items" => "All City",
"add_new" => "Add New",
"add_new_item" => "Add New City",
"edit" => "Edit",
"edit_item" => "Edit City",
"new_item" => "New City",
"view" => "View",
"view_item" => "View City",
"search_items" => "Search City",
"not_found" => "No City Found",
"not_found_in_trash" => "No City Found in Trash",
"parent" => "Parent City",
);
$args = array(
"labels" => $labels,
"description" => "",
"public" => true,
"show_ui" => true,
"has_archive" => true,
"show_in_menu" => true,
"exclude_from_search" => false,
"capability_type" => "post",
"map_meta_cap" => true,
"hierarchical" => true,
"rewrite" => array( "slug" => "city", "with_front" => true ),
"query_var" => true,
"supports" => array( "title", "revisions", "thumbnail" )
);
register_post_type( "city", $args );
$labels = array(
"name" => "Properties",
"singular_name" => "Property",
);
$args = array(
"labels" => $labels,
"description" => "",
"public" => true,
"show_ui" => true,
"has_archive" => true,
"show_in_menu" => true,
"exclude_from_search" => false,
"capability_type" => "post",
"map_meta_cap" => true,
"hierarchical" => false,
"rewrite" => array( "slug" => "city/%city_name%", "with_front" => true ),
"query_var" => true,
"supports" => array( "title", "revisions", "thumbnail" )
);
register_post_type( "properties", $args );
});
add_action('add_meta_boxes', function() {
add_meta_box('properties-parent', 'City', 'properties_attributes_meta_box', 'properties', 'side', 'default');
});
function properties_attributes_meta_box($post) {
$pages = wp_dropdown_pages(array('post_type' => 'city', 'selected' => $post->post_parent, 'name' => 'parent_id', 'show_option_none' => __('(no parent)'), 'sort_column'=> 'menu_order, post_title', 'echo' => 0));
if ( ! empty($pages) ) {
echo $pages;
} // end empty pages check
}
add_action( 'init', function() {
add_rewrite_rule( '^city/(.*)/([^/]+)/?$','index.php?properties=$matches[2]','top' );
});
add_filter( 'post_type_link', function( $link, $post ) {
if ( 'properties' == get_post_type( $post ) ) {
//Lets go to get the parent city name
if( $post->post_parent ) {
$parent = get_post( $post->post_parent );
if( !empty($parent->post_name) ) {
return str_replace( '%city_name%', $parent->post_name, $link );
}
} else {
//This seems to not work. It is intented to build pretty permalinks
//when properties has not parent, but it seems that it would need
//additional rewrite rules
//return str_replace( '/%city_name%', '', $link );
}
}
return $link;
}, 10, 2 );

Elementor access settings inside _register_controls function

I am using Elementor. In one section user can create categories with repeater field. In other section I would like to list these categories in Select2 control. How could I access category_data in _register_controls function? I know I can access it in render function.
protected function _register_controls() {
$this->start_controls_section(
'content_section',
[
'label' => __( 'Content', 'plugin-name' ),
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
]
);
$repeater->add_control(
'category_title', [
'label' => esc_html__( 'Title', 'plugin-name'),
'type' => \Elementor\Controls_Manager::TEXT,
'label_block' => false,
]
);
$repeater->add_control(
'category_slug', [
'label' => esc_html__( 'Slug', 'plugin-name'),
'type' => \Elementor\Controls_Manager::TEXT,
'label_block' => false,
]
);
$this->add_control(
'category_data',
[
'label' => esc_html__( 'Category', 'plugin-name'),
'type' => \Elementor\Controls_Manager::REPEATER,
'fields' => $repeater->get_controls(),
'prevent_empty' => false,
'title_field' => '{{{ category_title }}}',
]
);
$this->end_controls_section();
$this->start_controls_section(
'category_section',
[
'label' => __( 'Category', 'plugin-name' ),
'tab' => \Elementor\Controls_Manager::TAB_CONTENT,
]
);
//how could I access category_data here?
$settings = $this->get_settings_for_display();
$category_data = $settings['category_data'];
$cd_arr = [];
foreach ($category_data as $cd) {
$cd_arr[] = $cd['category_slug'];
}
if(count($cd_arr) > 0){
$this->add_control(
'media_category',
[
'label' => esc_html__( 'Categories', 'plugin-name'),
'type' => \Elementor\Controls_Manager::SELECT2,
'multiple' => true,
'options' => $cd_arr,
]
);
}
$this->end_controls_section();
}

Error redicrect to home page when rewrite url custom post type

I hava custom post type
function anime_post_type() {
register_post_type('anime', array(
'labels' => array(
'name' => 'Anime',
'singular_name' => 'Anime',
'add_new' => _x('Thêm Anime', 'animefrost'),
'add_new_item' => 'Thêm Anime Mới',
),
'show_ui' => true,
'public' => true,
'menu_icon' => 'dashicons-video-alt3',
'exclude_from_search' => true,
'hierarchical' => false,
'rewrite' => array('slug' => 'story', 'with_front' => FALSE),
'supports' => array('title', 'editor', 'thumbnail')
)
);
//flush_rewrite_rules( false );
}
And i want to rewrite
http://localhost/Anime/story/one-pice/?ep=1 to http://localhost/Anime/story/one-pice/ep/1
With my code
function add_query_vars_filter( $vars ){
$vars[] = "ep";
return $vars;
}
add_filter( 'query_vars', 'add_query_vars_filter' );
add_action('init', 'dcc_rewrite_rules');
function dcc_rewrite_rules() {
add_rewrite_rule('^story/([^/]+)/ep/([0-9]+)$','index.php?post_type=anime&ep=$matches[2]','top');
flush_rewrite_rules(false);
}
What is error in my code ???
Please help me , thank you.
Rewrite your rules on rewrite_rules_array filter
add_filter('rewrite_rules_array', 'dcc_rewrite_rules');
Now add your new rules and return them
function dcc_rewrite_rules() {
$aNewRules = array('story/([^/]+)/?$' => 'index.php?pagename=story&ep=$matches[1]');
$aRules = $aNewRules + $aRules;
return $aRules;
}
check this
Don't forget to save the permalinks
You can access the query string via $wp_query->query_vars['ep']

Batching with Google Places: Is it possible?

I was wondering if it's possible to send batched requests to maps.googleapis.com. As far as I can tell, it isn't.
I was using the Google API Client Library with supports batching, but it's only for www.googleapis.com. I went ahead and hacked it so that I could call the Places API, and it worked fine for normal calls, but when I actually tried to batch them, I got a 404 error:
"The requested URL /batch was not found on this server. That’s all we know."
So it appears that maps.googleapis.com does not support batching, but I wanted to be sure this is true. If anyone knows otherwise, please tell me how. Thanks!
inside google-api-php-client/src/Google/Config.php:
- 'base_path' => 'https://www.googleapis.com',
+ 'base_path' => 'https://maps.googleapis.com',
google-api-php-client/src/Google/Service/Maps.php:
(I added this file to make Places calls possible.)
<?php
class Google_Service_Maps extends Google_Service
{
const MAPS = "https://maps.googleapis.com/auth/maps";
public function __construct(Google_Client $client)
{
parent::__construct($client);
$this->servicePath = 'maps/api/';
$this->version = 'v3';
$this->serviceName = 'maps';
$this->places = new Google_Service_Maps_Places_Resource(
$this,
$this->serviceName,
'places',
array(
'methods' => array(
'autocomplete' => array(
'path' => 'place/autocomplete/json',
'httpMethod' => 'GET',
'parameters' => array(
'input' => array(
'location' => 'query',
'type' => 'string',
'required' => true,
),
'sensor' => array(
'location' => 'query',
'type' => 'boolean',
'required' => true,
),
'location' => array(
'location' => 'query',
'type' => 'string',
),
'radius' => array(
'location' => 'query',
'type' => 'integer',
),
),
),
)
)
);
}
}
class Google_Service_Maps_Places_Resource extends Google_Service_Resource
{
public function autocomplete($input, $lat, $lng, $radius, $optParams = array())
{
$params = array('input' => $input, 'location' => "$lat,$lng", 'radius' => $radius, 'sensor' => false);
$params = array_merge($params, $optParams);
return $this->call('autocomplete', array($params));
}
}
API batch calling code:
<?php
const API_KEY = 'MY_API_KEY';
set_include_path("google-api-php-client/src/" . PATH_SEPARATOR . get_include_path());
require_once 'Google/Client.php';
require_once 'Google/Service/Maps.php';
require_once 'Google/Http/Batch.php';
$client = new Google_Client();
$client->setApplicationName("Client_Library_Examples");
$client->setDeveloperKey(API_KEY);
$client->setUseBatch(true);
$batch = new Google_Http_Batch($client);
$service = new Google_Service_Maps($client);
$inputs = array(
'Dolore',
'MacAl',
'App Aca'
);
foreach($inputs as $input) {
$req = $service->places->autocomplete($input, 37.7833, -122.4167, 500);
$batch->add($req, $input);
}
$results = $batch->execute();
print_r($results);
print_r($req);

Resources