Add a missing response from Learndash API - wordpress

After playing around with the Learndash API, I realised it was not possible to get the user progression on the courses he is enrolled to.
I contacted the Learndash support that confirmed it was not possible.
I am new to wordpress dev (I am more familiar with app development), and I would like to know how to implement this feature by myself ? Do I have, for example, to create a custom endpoint ?
I would like to know how to add some 'responses' missing for a plugin API.
What I understand, is that everything from Learndash (courses, topics etc) are like custom posts, so I am guessing it is like adding custom endpoints for posts.
Thanks

I have done this for the course list API. I wanted to remove the "_links" parameter from the response.
I have worked with the object-oriented concept and here is my code:
/* Filter sfwd-courses object */
add_filter( 'rest_prepare_sfwd-courses', array( $this, 'prepare_ld_courses' ), 999, 3 );
public function prepare_ld_courses( $response, $object, $request ) {
/* Remove all links from response */
foreach ( $response->get_links() as $k_l => $v_k ) {
$response->remove_link( $k_l );
}
$data = $response->get_data();
$base_course_id = $data['id'];
$content = array( 'ID' => $course_id, 'parent_id' => (int) $base_course_id, 'title' => $course_obj->post_title, 'description' => $course_obj->post_content, 'cover_image' => null, 'thumbnail' => null);
/* Set thumbnail and cover_image */
$content = EI_Rest_Course::setup_images( $content, $base_course_id );
$response->set_data( $content );
return $response;
}
public static function setup_images( $content, $course_id ) {
/* logic to get course image - $image */
$content['thumbnail'] = $image;
/* logic to get course cover_image - $cover_image */
$content['cover_image'] = $cover_image;
return $content;
}

Related

Wordpress REST response shows taxonomy categories only with ID's

I'm using WP as a headless CMS with ACF en ACF-2-REST plugins. I've added categories to a post-type and when I make a GET call, it shows me all the information of a particular post including the categories, but only the ID's. If I want to match it, I have to do another call to Categories to get the information of that categories (name, parent etc).
How can I show that information instead of just the ID in a post call?
How the JSON looks now at the /activities call:
{
"id":111,
"date":"2020-01-18T15:39:27",
"date_gmt":"2020-01-18T15:39:27",
"guid":{"rendered":"https:\/\/url.be\/?post_type=activities&p=111"},
"modified":"2020-01-18T15:39:27",
"modified_gmt":"2020-01-18T15:39:27",
"slug":"walking-on-wood",
"status":"publish",
"type":"activities",
"link":"https:\/\/url.be\/activities\/walking-on-wood\/",
"title":{"rendered":"Walking on wood"},
"template":"",
"categories":[14,25,13,2,18,21,6,24],
"acf":{...}
}
What I want to show in the "categories" instead of just the numbers (from the categories call)
{
"id":3,
"count":1,
"description":"",
"link":"https:\/\/url.be\/category\/duration\/lower-than-30-min\/",
"name":"< 30 min.",
"slug":"lower-than-30-min",
"taxonomy":"category",
"parent":2,"meta":[],
"acf":[],
"_links":{"self":[{"href":"https:\/\/url.be\/wp-json\/wp\/v2\/categories\/3"}],
"collection":[{"href":"https:\/\/url.be\/wp-json\/wp\/v2\/categories"}],
"about":[{"href":"https:\/\/url.be\/wp-json\/wp\/v2\/taxonomies\/category"}],
"up":[{"embeddable":true,"href":"https:\/\/url.be\/wp-json\/wp\/v2\/categories\/2"}],
"wp:post_type":[{"href":"https:\/\/url.be\/wp-json\/wp\/v2\/posts?categories=3"},{"href":"https:\/\/url.be\/wp-json\/wp\/v2\/activities?categories=3"}],
"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}
}
Can't find any solution on the internet how I could manipulate the structure of that JSON with a custom function, would appreciate it a lot if someone point me to the right direction. Thanks!
As discussed in the comments section, a solution to this question is to use a custom endpoint method for the WP REST API and perform extra queries in there to get the data you need. This way, you can do all the data manipulation to get the perfect response, resulting in one REST call.
As taken from the official developer docs
Define an endpoint method and add some extra data
<?php
add_action( 'rest_api_init', function () {
register_rest_route( 'myplugin/v1', '/author/(?P<id>\d+)', array(
'methods' => 'GET',
'callback' => 'my_awesome_func', //note that this is the method it will fire
'args' => array(
'id' => array(
'validate_callback' => function($param, $request, $key) {
return is_numeric( $param );
}
),
),
) );
/**
* Grab latest post by an author along with its category!
*
* #param array $data Options for the function.
* #return array Post,
*/
function my_awesome_func( $data ) {
$posts = get_posts( array(
'author' => $data['id'],
) );
if(!empty($posts)) {
//Example of appending extra data
foreach($posts as $post) {
$category = wp_get_post_terms($post->ID, 'category');
$post['category'] = $category;
}
return $posts;
} else {
return new WP_Error( 'no_author', 'Invalid author', array( 'status' => 404 ) );
}
}

Custom API Endpoint get data from category

im pretty stuck.. Im trying to get all post data from a specific category ID, using Wordpress REST API. But it dosen't output the content, only the ID & Title.. Cant see what I am doing wrong..
Heres the code:
/* Register Route http://dev.mpblogg.se/wp-json/api/v1/feedposts/id */
add_action( 'rest_api_init', function () {
register_rest_route( 'api/v1', '/feedposts/(?P<id>\d+)', array(
'methods' => 'GET',
'callback' => 'getAllPostsfromCategory',
));
});
/* Get all posts from the specific Caetgory */
function getAllPostsfromCategory( $data ) {
$secret = '2lpMh5EHaEiavhMONpWD';
$qs = explode('&', $_SERVER['QUERY_STRING'])[0];
$qs = explode('=', $qs)[1];
if($qs != $secret){
return false;
}
$posts = get_posts( array(
'category' => $data['id'],
));
$returnArray = array();
foreach($posts as $post) {
array_push($returnArray, array(
'id' => $post->ID,
'title' => $post->post_title,
'content' => $post->post_content
));
}
// die();
if(empty($posts)){
return null;
}
return $returnArray;
}
The JSON output looks like this:
After changing to array_push($returnArray,$post); it looks like this:
Okay... here's a clue that I got from the array_push image that you shared. The last string of the JSON output reads:
"filer": "raw"
This means that the resultant value would be stored as a raw, unfiltered content of the post.
So, you can try adding apply_filters() to the extracted content and see if it helps you display the required content.
$content = apply_filters('the_content', $content);
$content = str_replace(']]>', ']]>', $content);
echo $content;
This is used to apply the content filters to raw unfiltered post content, which usually comes from the use of $post->post_content .
As usual the answer why it didnt work is so dumb. Im not using the the_content.. Im using a custom field from ACF called post_excerpt. So I just added:
'excerpt' => get_field('post_excerpt', $post->ID),
Now it works.. Sorry all, but thank's for all the help.

Custom product attributes is blank even though I add it [duplicate]

The products in my clients website require certain attributes which I have added via Products -> Attributes in the Wordpress administration. In this import script I'm coding I need to use the function update_post_meta($post_id, $meta_key, $meta_value) to import the proper attributes and values.
Currently I have the function like so:
update_post_meta( $post_id, '_product_attributes', array());
However I'm not sure how to properly pass along the attributes and their values?
Right so it took me a while to figure it out myself but I finally managed to do this by writing the following function:
// #param int $post_id - The id of the post that you are setting the attributes for
// #param array[] $attributes - This needs to be an array containing ALL your attributes so it can insert them in one go
function wcproduct_set_attributes($post_id, $attributes) {
$i = 0;
// Loop through the attributes array
foreach ($attributes as $name => $value) {
$product_attributes[$i] = array (
'name' => htmlspecialchars( stripslashes( $name ) ), // set attribute name
'value' => $value, // set attribute value
'position' => 1,
'is_visible' => 1,
'is_variation' => 1,
'is_taxonomy' => 0
);
$i++;
}
// Now update the post with its new attributes
update_post_meta($post_id, '_product_attributes', $product_attributes);
}
// Example on using this function
// The attribute parameter that you pass along must contain all attributes for your product in one go
// so that the wcproduct_set_attributes function can insert them into the correct meta field.
$my_product_attributes = array('hdd_size' => $product->hdd_size, 'ram_size' => $product->ram_size);
// After inserting post
wcproduct_set_attributes($post_id, $my_product_attributes);
// Woohay done!
I hope this function will help other people if they need to import multiple attributes pro-grammatically in WooCommerce!
I tried Daniel's answer, and it didn't work for me. It might be that the Wordpress/Woocommerce code has changed since, or perhaps I didn't quite understand how to do it, but either way that code did nothing for me. After a lot of work using it as a base, however, I came up with this snippet of code and put it on my theme's functions.php:
function wcproduct_set_attributes($id) {
$material = get_the_terms( $id, 'pa_material');
$material = $material[0]->name;
// Now update the post with its new attributes
update_post_meta($id, '_material', $material);
}
// After inserting post
add_action( 'save_post_product', 'wcproduct_set_attributes', 10);
With this, I can take what I set as "material" on my WooCommerce install as a custom attribute and add it to the formal meta as _material. This in turn allows me to use another snippet of code so the WooCommerce search function extends to meta fields, meaning I can search for a material in the WooCommerce search field and have all items with that material appear.
I hope this is useful to somebody.
#Daniels's answer works, won't decide on right or wrong, however if you want to add the values as a taxonomy term under attributes you have to adapt the code as below (set is_taxonomy = 1). Otherwise Woocommerce sees it as custom meta field(?). It still adds the value under attributes. This will only work for strings. For values that are arrays the code has to be adapted.
Additionally it uses the wp_set_object_terms that #Anand suggests as well. I was using that, because all the documentation I could find led to believe that had to be used. However if one only uses the wp_set_object_terms then I couldn't see the attributes in the edit product screen. Using the information from both answers and reading on the subject resulted in the solution.
You will need to tweak the code for things such as product variations.
/*
* Save Woocommerce custom attributes
*/
function save_wc_custom_attributes($post_id, $custom_attributes) {
$i = 0;
// Loop through the attributes array
foreach ($custom_attributes as $name => $value) {
// Relate post to a custom attribute, add term if it does not exist
wp_set_object_terms($post_id, $value, $name, true);
// Create product attributes array
$product_attributes[$i] = array(
'name' => $name, // set attribute name
'value' => $value, // set attribute value
'is_visible' => 1,
'is_variation' => 0,
'is_taxonomy' => 1
);
$i++;
}
// Now update the post with its new attributes
update_post_meta($post_id, '_product_attributes', $product_attributes);
}
Then call the function:
$custom_attributes = array('pa_name_1' => $value_1, 'pa_name_2' => $value_2, 'pa_name_3' => $value_3);
save_wc_custom_attributes($post_id, $custom_attributes);
Thank you for posting the code Daniel & Anand. It helped me a great deal.
Don't know if this is the "correct" way to do this... But I needed a function to add ACF repeater fields with a date value as a attribute on post save, so this was the function I came up with:
add_action( 'save_post', 'ed_save_post_function', 10, 3 );
function ed_save_post_function( $post_ID, $post, $update ) {
//print_r($post);
if($post->post_type == 'product')
{
$dates = get_field('course_dates', $post->ID);
//print_r($dates);
if($dates)
{
$date_arr = array();
$val = '';
$i = 0;
foreach($dates as $d)
{
if($i > 0)
{
$val .= ' | '.date('d-m-Y', strtotime($d['date']));
}
else{
$val .= date('d-m-Y', strtotime($d['date']));
}
$i++;
}
$entry = array(
'course-dates' => array(
'name' => 'Course Dates',
'value' => $val,
'position' => '0',
'is_visible' => 1,
'is_variation' => 1,
'is_taxonomy' => 0
)
);
update_post_meta($post->ID, '_product_attributes', $entry);
}
}
}
Hope this helps someone.

Custom permalink structure: /%custom-post-type%/%custom-taxonomy%/%post-name%/

I'm trying to create a custom permalink structure that will allow me to accomplish the following.
I have a custom post type called "projects"
I have a custom taxonomy called "project-category" that is assigned to the CPT "projects"
I want my permalink structure to look like this:
projects/category/project-name
or
/%custom-post-type%/%custom-taxonomy%/%post-name%/
I've been able to succesfully use /%category%/ in permalinks for normal, out-of-the-box WP posts, but not for CPTs.
How would creating such a permalink structure affect the URLs or other pages? Is it possible de define a custom permalink structure and restrict it to a single CPT?
Thanks
Lucky for you, I just had to do this for a client project. I used this answer on the WordPress Stackexchange as a guide:
/**
* Tell WordPress how to interpret our project URL structure
*
* #param array $rules Existing rewrite rules
* #return array
*/
function so23698827_add_rewrite_rules( $rules ) {
$new = array();
$new['projects/([^/]+)/(.+)/?$'] = 'index.php?cpt_project=$matches[2]';
$new['projects/(.+)/?$'] = 'index.php?cpt_project_category=$matches[1]';
return array_merge( $new, $rules ); // Ensure our rules come first
}
add_filter( 'rewrite_rules_array', 'so23698827_add_rewrite_rules' );
/**
* Handle the '%project_category%' URL placeholder
*
* #param str $link The link to the post
* #param WP_Post object $post The post object
* #return str
*/
function so23698827_filter_post_type_link( $link, $post ) {
if ( $post->post_type == 'cpt_project' ) {
if ( $cats = get_the_terms( $post->ID, 'cpt_project_category' ) ) {
$link = str_replace( '%project_category%', current( $cats )->slug, $link );
}
}
return $link;
}
add_filter( 'post_type_link', 'so23698827_filter_post_type_link', 10, 2 );
When registering the custom post type and taxonomy, be sure to use the following settings:
// Used for registering cpt_project custom post type
$post_type_args = array(
'rewrite' => array(
'slug' => 'projects/%project_category%',
'with_front' => true
)
);
// Some of the args being passed to register_taxonomy() for 'cpt_project_category'
$taxonomy_args = array(
'rewrite' => array(
'slug' => 'projects',
'with_front' => true
)
);
Of course, be sure to flush rewrite rules when you're done. Good luck!
Since WordPress changed a lot in the recent years there is a new solution for this.
// Used for registering cpt_project custom post type
$post_type_args = array(
'rewrite' => array(
'slug' => '/%custom-post-type%/%custom-taxonomy%/%postname%/',
'with_front' => true
'walk_dirs' => false
)
);
%custom-post-type% must match the name for your custom post type
%custom-taxonomy% must match the name for your taxonomy
that WordPress automatically creates the right rewrite rules and links
with 'walk_dirs' => false you prevent WP from creating crazy rules like with only [^/]+/ cause your link starts with a custom-post-type
and often this dir walk isn't even needed cause u access only the sites in your structure or separate taxonomy sites.
With this your rewrite rules are as precise as possible, and you don't need to fetch the rules with
add_filter( 'rewrite_rules_array', 'so23698827_add_rewrite_rules' );
and prepend them later on with
add_filter( 'post_type_link', 'so23698827_filter_post_type_link', 10, 2 );
as mentioned in the accepted answer. This saves memory and execution time!
Hope this helps anyone, who is searching for this Problem with WP Versions > 5.X

Buddypress bp_activity_add(activity_action) hides the link "target"

I'm developing a social network with Buddypress, I created a RSS plugin to pull the RSS feed from the specified websites.
Everything is working, except when the RSS is posted to the activity stream. When I create the activity content to print a link, I set the link target to "_new" to open it in a new page.
Here's the code:
function wprss_add_to_activity_feed($item, $inserted_ID) {
$permalink = $item->get_permalink();
$title = $item->get_title();
$admin = get_user_by('login', 'admin');
# Generates the link
$activity_action = sprintf( __( '%s published a new RSS link: %s - ', 'buddypress'), bp_core_get_userlink( $admin->ID ), '' . attribute_escape( wprss_limit_rss_title_chars($title) ) . '');
/* Record this in activity streams */
bp_activity_add( array(
'user_id' => $admin->ID,
'item_id' => $inserted_ID,
'action' => $activity_action,
'component' => 'rss',
'primary_link' => $permalink,
'type' => 'activity_update',
'hide_sitewide' => false
));
}
It should come up with something like that:
Test
But it prints like that:
Test
Why is this happening?
The 'target' attribute is probably getting stripped by BuddyPress's implementation of the kses filters. You can whitelist the attribute as follows:
function se16329156_whitelist_target_in_activity_action( $allowedtags ) {
$allowedtags['a']['target'] = array();
return $allowedtags;
}
add_filter( 'bp_activity_allowed_tags', 'se16329156_whitelist_target_in_activity_action' );
This probably won't retroactively fix the issue for existing activity items - it's likely that they had the offending attribute stripped before being stored in the database. But it should help for future items.

Resources