I can already unset (remove specifics from normal posts) in the json returned from the WordPress API. I actually use the following below from this example: https://css-tricks.com/using-the-wp-api-to-fetch-posts/
What I am having trouble with and can't figure out, is how to change this so it unsets data from a Custom Post Type
Thoughts?
function qod_remove_extra_data( $data, $post, $context ) {
// We only want to modify the 'view' context, for reading posts
if ( $context !== 'view' || is_wp_error( $data ) ) {
return $data;
}
// Here, we unset any data we don't want to see on the front end:
unset( $data['author'] );
unset( $data['status'] );
unset( $data['featured_image'] );
//etc etc
return $data;
}
add_filter( 'json_prepare_post', 'qod_remove_extra_data', 12, 3 );
custom post type example filter:
function projectPost_remove_extra_data( $data, $post, $context ) {
if ( $context !== 'view' || is_wp_error( $data ) ) {
return $data;
}
// Here, we unset any data we don't want to see on the front end:
unset( $data['author'] );
return $data;
}
add_filter( 'json_prepare_project', 'projectPost_remove_extra_data', 12, 3 );
For wp-api v1.x, you need to extend WP_JSON_CustomPostType. There is an example in the pages file (class-wp-json-pages.php)
<?php
/**
* Page post type handlers
*
* #package WordPress
* #subpackage JSON API
*/
/**
* Page post type handlers
*
* This class serves as a small addition on top of the basic post handlers to
* add small functionality on top of the existing API.
*
* In addition, this class serves as a sample implementation of building on top
* of the existing APIs for custom post types.
*
* #package WordPress
* #subpackage JSON API
*/
class WP_JSON_Pages extends WP_JSON_CustomPostType {
/**
* Base route
*
* #var string
*/
protected $base = '/pages';
/**
* Post type
*
* #var string
*/
protected $type = 'page';
/**
* Register the page-related routes
*
* #param array $routes Existing routes
* #return array Modified routes
*/
public function register_routes( $routes ) {
$routes = parent::register_routes( $routes );
$routes = parent::register_revision_routes( $routes );
$routes = parent::register_comment_routes( $routes );
// Add post-by-path routes
$routes[ $this->base . '/(?P<path>.+)'] = array(
array( array( $this, 'get_post_by_path' ), WP_JSON_Server::READABLE ),
array( array( $this, 'edit_post_by_path' ), WP_JSON_Server::EDITABLE | WP_JSON_Server::ACCEPT_JSON ),
array( array( $this, 'delete_post_by_path' ), WP_JSON_Server::DELETABLE ),
);
return $routes;
}
/**
* Retrieve a page by path name
*
* #param string $path
* #param string $context
*
* #return array|WP_Error
*/
public function get_post_by_path( $path, $context = 'view' ) {
$post = get_page_by_path( $path, ARRAY_A );
if ( empty( $post ) ) {
return new WP_Error( 'json_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
}
return $this->get_post( $post['ID'], $context );
}
/**
* Edit a page by path name
*
* #param $path
* #param $data
* #param array $_headers
*
* #return true|WP_Error
*/
public function edit_post_by_path( $path, $data, $_headers = array() ) {
$post = get_page_by_path( $path, ARRAY_A );
if ( empty( $post ) ) {
return new WP_Error( 'json_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
}
return $this->edit_post( $post['ID'], $data, $_headers );
}
/**
* Delete a page by path name
*
* #param $path
* #param bool $force
*
* #return true|WP_Error
*/
public function delete_post_by_path( $path, $force = false ) {
$post = get_page_by_path( $path, ARRAY_A );
if ( empty( $post ) ) {
return new WP_Error( 'json_post_invalid_id', __( 'Invalid post ID.' ), array( 'status' => 404 ) );
}
return $this->delete_post( $post['ID'], $force );
}
/**
* Prepare post data
*
* #param array $post The unprepared post data
* #param string $context The context for the prepared post. (view|view-revision|edit|embed|single-parent)
* #return array The prepared post data
*/
protected function prepare_post( $post, $context = 'view' ) {
$_post = parent::prepare_post( $post, $context );
// Override entity meta keys with the correct links
$_post['meta']['links']['self'] = json_url( $this->base . '/' . get_page_uri( $post['ID'] ) );
if ( ! empty( $post['post_parent'] ) ) {
$_post['meta']['links']['up'] = json_url( $this->base . '/' . get_page_uri( (int) $post['post_parent'] ) );
}
return apply_filters( 'json_prepare_page', $_post, $post, $context );
}
}
Replace "Pages" with "MyCustomPostTypes" and page with "mycustomposttype". Just be careful not to rename internal WordPress code that also uses the term page
Note: probably best to add this as a plugin rather than change the JSON-WP-API plugin
/**
* Plugin Name: MyCustom JSON App API
* Description: MyCustomPost handler for the JSON API
* Dependency: This plugin requires JSON-WP-API Plugin!!!!
* Author:
* Author URI:
* Version:
* Plugin URI:
*/
If possible, only the examples shown in internet is:
function qod_remove_extra_data ($ data, $ post, $ context) {
// We only want to modify the 'view' context, for reading posts
if ($ context! == 'view' || is_wp_error ($ data)) {
return $ data;
}
// Here, we unset any data we do not want to see on the front end:
unset ($data ['author']);
unset ($data ['status']);
// Continue unsetting whatever other fields you want return $ data;
}
add_filter ('json_prepare_post' 'qod remove extra_data', 12, 3);
and right is:
qod_remove_extra_data function ($ data, $ post, $ context) {
// We only want to modify the 'view' context, for reading posts
if ($ context! == 'view' || is_wp_error ($ data)) {
unset ( $data->data ['excerpt']); //Example
unset ($data->data ['content']); //Example
unset ($data->data ['name field to remove'])
//or
unset ($data->data ['name field to remove'] ['name subfield if you only want to delete the sub-field of field' ])
return $data;
}
}
add_filter ('rest_prepare_post' 'qod_remove_extra_data', 12, 3);
IMPORTANT:
Is:
add_filter ('rest_prepare_post' 'qod_remove_extra_data', 12, 3);
Not:
add_filter ('json_prepare_post' 'qod remove extra_data', 12, 3); //WRONG
If is Custom Post Type:
add_filter ('rest_prepare_{$post_type}' 'qod_remove_extra_data', 12, 3);
EXAMPLE: Name post type = product;
add_filter ('rest_prepare_product' 'qod_remove_extra_data', 12, 3);
With this code can remove the fields that you want the JSON. By using rest_prepare} _ {$ post_type decide that you eliminated every post_type fields, thus only affected the post_type you want and not all.
It should be no different to remove data from custom post types than from the built-in post types. Have you confirmed that your API call is actually returning your CPTs? First, you should look at the value of what is returned from: http://yourwebsite.com/wp-json/posts/types. Assuming that your CPT type shows up there, you should be able to query for items of that type, e.g. product, by calling: http://yourwebsite.com/wp-json/posts?type=product.
In other words, you should not change the name of the filter: you still want to tie into json_prepare_post. If you want to make your filter sensitive to post type and only remove certain fields if you have a CPT you could do something like:
function my_remove_extra_product_data( $data, $post, $context ) {
// make sure you've got the right custom post type
if ( 'product' !== $data[ 'type' ] ) {
return $data;
}
// now proceed as you saw in the other examples
if ( $context !== 'view' || is_wp_error( $data ) ) {
return $data;
}
// unset unwanted fields
unset( $data[ 'author' ] );
// finally, return the filtered data
return $data;
}
// make sure you use the SAME filter hook as for regular posts
add_filter( 'json_prepare_post', 'my_remove_extra_product_data', 12, 3 );
You can find more documentation in the WP API Getting Started Guide.
Related
Can't seem to figure out the PHP code to add a new column on the woocommcerce orders admin page to show the customers user role?
Thanks for your time
Darren
Strugglinjg to get any PHP snippets ive found to work
Here is the code snippet you can add to your functions.php:
/**
* Add custom column.
*
* #param array $columns Columns.
* #return array
*/
function set_custom_edit_shop_order_columns( $columns ) {
$columns['user_role'] = __( 'Role', 'your-text-domain' );
return $columns;
}
add_filter( 'manage_shop_order_posts_columns', 'set_custom_edit_shop_order_columns', 100, 1 );
/**
* Add data to custom column.
*
* #param string $column Column slug.
* #param id $post_id Post ID.
*/
function custom_shop_order_column( $column, $post_id ) {
if ( 'user_role' === $column ) {
$order_user = get_post_meta( $post_id, '_customer_user', true );
if ( ! empty( $order_user ) ) {
$user = get_user_by( 'ID', $order_user );
if ( ! empty( $user ) ) {
echo implode( ', ', ( array ) $user->roles );
}
}
}
}
add_action( 'manage_shop_order_posts_custom_column', 'custom_shop_order_column', 100, 2 );
This will only work if you have not opted in for High-Performance Order Storage (HPOS), otherwise you may have to tweak custom_shop_order_column() method to retrieve data from new custom order meta table.
first of all thx to everyone who put answers here and to all the staff of stackoverflow.
I write a class(copy paste and mod) of WC_duplicate_product off woocommerce in order to put a meta on duplicates and to generate a slug wit base as "https://website.com/slug-base/product-name", but I cant acomplish the part of slug-base. If anybody can help me i'll appreciate.
This is my code, feel free to use and mod:
/**
* Function to create the duplicate of the product.
*
* #param WC_Product $product The product to duplicate.
* #return WC_Product The duplicate.
*/
public function product_duplicate( $product ) {
/**
* Filter to allow us to exclude meta keys from product duplication..
*
* #param array $exclude_meta The keys to exclude from the duplicate.
* #param array $existing_meta_keys The meta keys that the product already has.
* #since 2.6
*/
$meta_to_exclude = array_filter(
apply_filters(
'woocommerce_duplicate_product_exclude_meta',
array(),
array_map(
function ( $datum ) {
return $datum->key;
},
$product->get_meta_data()
)
)
);
$duplicate = clone $product;
$duplicate->set_id( 0 );
$char = 47;
/* translators: %s contains the name of the original product. */
$duplicate->set_name( sprintf( '%s', $duplicate->get_name()) );
$duplicate->set_total_sales( 0 );
if ( '' !== $product->get_sku( 'edit' ) ) {
$duplicate->set_sku( wc_product_generate_unique_sku( 0, $product->get_sku( 'edit' ) ) );
}
$duplicate->set_status( 'draft' );
$duplicate->set_date_created( null );
$duplicate->set_slug( 'vendor'.sprintf( '%c', $char).'' );
$duplicate->set_rating_counts( 0 );
$duplicate->set_average_rating( 0 );
$duplicate->set_review_count( 0 );
$duplicate->add_meta_data( '_is_clone', 'true' );
foreach ( $meta_to_exclude as $meta_key ) {
$duplicate->delete_meta_data( $meta_key );
}
/**
* This action can be used to modify the object further before it is created - it will be passed by reference.
*
* #since 3.0
*/
do_action( 'woocommerce_product_duplicate_before_save', $duplicate, $product );
// Save parent product.
$duplicate->save();
// Duplicate children of a variable product.
if ( ! apply_filters( 'woocommerce_duplicate_product_exclude_children', false, $product ) && $product->is_type( 'variable' ) ) {
foreach ( $product->get_children() as $child_id ) {
$child = wc_get_product( $child_id );
$child_duplicate = clone $child;
$child_duplicate->set_parent_id( $duplicate->get_id() );
$child_duplicate->set_id( 0 );
$child_duplicate->set_date_created( null );
// If we wait and let the insertion generate the slug, we will see extreme performance degradation
// in the case where a product is used as a template. Every time the template is duplicated, each
// variation will query every consecutive slug until it finds an empty one. To avoid this, we can
// optimize the generation ourselves, avoiding the issue altogether.
$this->generate_unique_slug( $child_duplicate );
if ( '' !== $child->get_sku( 'edit' ) ) {
$child_duplicate->set_sku( wc_product_generate_unique_sku( 0, $child->get_sku( 'edit' ) ) );
}
foreach ( $meta_to_exclude as $meta_key ) {
$child_duplicate->delete_meta_data( $meta_key );
}
/**
* This action can be used to modify the object further before it is created - it will be passed by reference.
*
* #since 3.0
*/
do_action( 'woocommerce_product_duplicate_before_save', $child_duplicate, $child );
$child_duplicate->save();
}
// Get new object to reflect new children.
$duplicate = wc_get_product( $duplicate->get_id() );
}
return $duplicate;
}
this is teh part of code that I need to polish to get the desired slug, so when i run this clas, always obtain https://website.com/slug-baseproduct-name
It seems with version 2.6 WooCommerce have changed the way endpoints and custom profile tabs are made. More infor here https://woocommerce.wordpress.com/2016/04/21/tabbed-my-account-pages-in-2-6/ and and https://github.com/woothemes/woocommerce/wiki/2.6-Tabbed-My-Account-page
My code bellow is almost similar to the one in these links but clicking on the new profile tab keeps showing the 404 Not Found error. I tried Permalinks refresh and flush_rewrite_rules() but othing seems to work....
if ( !class_exists('My_WC_User_Company') ) {
class My_WC_User_Company {
/**
* Custom endpoint name.
*
* #var string
*/
public static $endpoint = 'my-company';
/**
* Plugin actions.
*/
public function __construct() {
// Actions used to insert a new endpoint in the WordPress.
add_action( 'init', array( $this, 'add_endpoints' ) );
add_filter( 'query_vars', array( $this, 'add_query_vars' ), 0 );
// Change the My Accout page title.
add_filter( 'the_title', array( $this, 'endpoint_title' ) );
// Insering your new tab/page into the My Account page.
add_filter( 'woocommerce_account_menu_items', array( $this, 'new_menu_items' ) );
add_action( 'woocommerce_account_' . self::$endpoint . '_endpoint', array( $this, 'endpoint_content' ) );
}
/**
* Register new endpoint to use inside My Account page.
*
* #see https://developer.wordpress.org/reference/functions/add_rewrite_endpoint/
*/
public function add_endpoints() {
add_rewrite_endpoint( self::$endpoint, EP_ROOT | EP_PAGES );
}
/**
* Add new query var.
*
* #param array $vars
* #return array
*/
public function add_query_vars( $vars ) {
$vars[] = self::$endpoint;
return $vars;
}
/**
* Set endpoint title.
*
* #param string $title
* #return string
*/
public function endpoint_title( $title ) {
global $wp_query;
$is_endpoint = isset( $wp_query->query_vars[ self::$endpoint ] );
if ( $is_endpoint && ! is_admin() && is_main_query() && in_the_loop() && is_account_page() ) {
// New page title.
$title = __( 'My Company', 'domain' );
remove_filter( 'the_title', array( $this, 'endpoint_title' ) );
}
return $title;
}
/**
* Insert the new endpoint into the My Account menu.
*
* #param array $items
* #return array
*/
public function new_menu_items( $items ) {
// Remove the logout menu item.
$logout = $items['customer-logout'];
unset( $items['customer-logout'] );
// Insert your custom endpoint.
$items[ self::$endpoint ] = __( 'My Company', 'domain' );
// Insert back the logout item.
$items['customer-logout'] = $logout;
return $items;
}
/**
* Endpoint HTML content.
*/
public function endpoint_content() {
ob_start();
// here is some content ?>
<?php
$output = ob_get_clean();
echo $output;
}
}
}
add_action('init', '_action_ssd_wp_user_company_init');
if( !( function_exists('_action_ssd_wp_user_company_init')) ){
function _action_ssd_wp_user_company_init(){
if ( get_current_user_id() && get_user_meta( get_current_user_id(), 'user_company', true ) == 'yes' ) {
new My_WC_User_Company();
}
}
}
Does anyone have any ideas what couldbe the issue?
there's something wrong with your hook... wrong timings...
these works... with 0 priority or using woocommerce_init
add_action('init', '_action_ssd_wp_user_company_init', 0 );
or
add_action('woocommerce_init', '_action_ssd_wp_user_company_init');
instead of add_action('init', '_action_ssd_wp_user_company_init');
you need to refresh permalink settings for this to work.
I'm trying to use wp_list_table by making an extended class. I have two actions to run through on the bulk-update functionality provided. However I don't quite know how to write the proper case switch statement to set the resulting checkbox to apply for either array actions based on the action taken...(see I know what I need to do, but I don't quite know how to do it).
This is the smallest bit of relatable code I have to work with:
/**
* Render the bulk edit checkbox
*
* #param array $item
*
* #return string
*/
function column_cb( $item ) {
return sprintf(
'<input type="checkbox" name="bulk-reset[]" value="%s" />', $item['id']
);
}
/**
* Method for name column
*
* #param array $item an array of DB data
*
* #return string
*/
function column_name( $item ) {
$delete_nonce = wp_create_nonce( 'sp_delete_customer' );
$reset_nonce = wp_create_nonce( 'sp_reset_payouts' );
$title = '<strong>' . $item['name'] . '</strong>';
$actions = [
'delete' => sprintf( 'Delete', esc_attr( $_REQUEST['page'] ), 'delete', absint( $item['id'] ), $delete_nonce ),
'reset' => sprintf( 'Reset', esc_attr( $_REQUEST['page'] ), 'reset', absint( $item['id'] ), $reset_nonce )
];
return $title . $this->row_actions( $actions );
}
/**
* Returns an associative array containing the bulk action
*
* #return array
*/
public function get_bulk_actions() {
$actions = array(
'bulk-delete' => 'Delete',
'bulk-reset' => 'Reset'
);
return $actions;
}
Reviewing this one last time, I think I need a function that is called on the name property as an escaped php echo right? Then that function should return either bulk-reset[] or bulk-delete[] based on $_post['delete-*'] right???
Since mulling this over, I've tried to pen together the solution, but for whatever reason that action_type function I made does not get called:
/**
* Render the bulk edit checkbox
*
* #param array $item
*
* #return string
*/
function column_cb( $item ) {
global $action_type;
return sprintf(
'<input type="checkbox" name="'.$action_type.'" value="%s" />', $item['id']
);
}
/**
* Pick the Checkbox Value based on post Value
*
*
* #return string
*
*/
public function action_type() {
if ( ( isset( $_POST['action'] ) && $_POST['action'] == 'bulk-delete' )
|| ( isset( $_POST['action2'] ) && $_POST['action2'] == 'bulk-delete' )
) {
$output = "bulk-delete[]";
} else {
$output = "bulk-reset[]";
}
return $output;
}
Edit 2: realizing that won't work because this sets the value post and I needed it set before page load...I guess this means I need to have the array name be generic and the post action handles how to deal with the data... maybe for simplicity sake I should set the edit action to the same bulk-delete data set as its ultimately the same set of checkboxes.
Edit 3: I can't do what I suggest at the end of edit2 because the portion of code to run the actions depends on the array name of the post and that array name is set on page load. I don't quite know what to do.
try this three function in " class My_Example_List_Table extends WP_List_Table " class and modification as per your requirement
function column_id($item){
$actions = array(
/* 'edit' => sprintf('Edit',$_REQUEST['page'],'edit',$item['ID']),*/
'delete' => sprintf('Delete',$_REQUEST['page'],'delete',$item['id']),
);
return sprintf('%1$s %2$s', $item['id'], $this->row_actions($actions) );
}
function get_bulk_actions() {
$actions = array(
'delete' => 'Delete'
);
return $actions;
}
function process_bulk_action(){
global $wpdb;
$table_name = $wpdb->prefix."tablename";
if ('delete' === $this->current_action()) {
$ids = isset($_REQUEST['id']) ? $_REQUEST['id'] : array();
if (is_array($ids)) $ids = implode(',', $ids);
if (!empty($ids)) {
$wpdb->query("DELETE FROM $table_name WHERE id IN($ids)");
}
}
}
Here's how I solved this:
Change (or not 😎 ) the checkbox array value to something generic. I chose myCheckboxes[]
this part is not shown in the answer above but the portion of the code executing the update or delete queries was the point where the data was set...I saw that changing the value there allows my checkbox column to be applied to all actions. (I'm planning to refactor the code further once I solve a related issue so this isn't the entire code snippet (ie. this is NOT a drop in copy/paste replacement, just context for how I solved this)
Here is the relevant missing bottom portion that you can see was changed to match the new checkbox value:
// // If the delete bulk action is triggered
if ( ( isset( $_POST['action'] ) && $_POST['action'] == 'bulk-delete' )
|| ( isset( $_POST['action2'] ) && $_POST['action2'] == 'bulk-delete' )
) {
$delete_ids = esc_sql( $_POST['my_CheckBoxes'] );
// loop over the array of record IDs and delete them
foreach ( $delete_ids as $id ) {
self::delete_customer( $id );
}
wp_redirect( esc_url( add_query_arg() ) );
exit;
}
I am currently using the 's' parameter for my pages.
Wordpress automatically makes it a search.
I want to disable the 's' parameter. I dont need the search function on my site.
Tried to find an answer or a direction to go into, but no luck.
If you cant write it yourself there is a plugin for that, it unsets all the s parameters and the sets a 404:
<?php
/**
* Unsets all search-related variables in WP_Query object and sets the
* request as a 404 if a search was attempted.
*
* #param object $obj A WP_Query object
* #return null
*/
public static function parse_query( $obj ) {
if ( $obj->is_search && $obj->is_main_query() ) {
unset( $_GET['s'] );
unset( $_POST['s'] );
unset( $_REQUEST['s'] );
unset( $obj->query['s'] );
$obj->set( 's', '' );
$obj->is_search = false;
$obj->set_404();
}
}
?>