I'm working on a Wordpress/Buddypress project and I would like to make the members list of group always visible whether or not the group is private and the logged member belong to the group.
I was thinking I had to change access and visibility of the nav items so I did that:
function change_access_group_nav_tabs() {
if(bp_is_group()) {
buddypress()->groups->nav->edit_nav( array('visibility' => 'public'), 'members', bp_current_item() );
buddypress()->groups->nav->edit_nav( array('access' => 'anyone'), 'members', bp_current_item() );
}
}
add_action( 'bp_actions', 'change_access_group_nav_tabs' );
But it didn't work…
Any suggestion how to proceed?
I found a workaround, I'am quite not completely satisfied but...
First of all, disable members list on group:
function change_access_group_nav_tabs() {
if(bp_is_group()) {
buddypress()->groups->nav->edit_nav( array( 'user_has_access' => false ), 'members', bp_current_item() );
}
}
add_action( 'bp_actions', 'change_access_group_nav_tabs' );`
(btw, setting the value to true actually make the the nav items always here, but we still can't access the group list on click)
And then I simply add a custom BP Group Extension to make my own members list:
class Group_Extension_List_Members extends BP_Group_Extension {
function __construct() {
$args = array(
'slug' => 'members-list',
'name' => 'Membres',
'access' => array( 'anyone'),
'show_tab' => array( 'anyone'),
'nav_item_position' => 12,
'screens' => array(
'create' => array(
'enabled' => false
),
'edit' => array(
'enabled' => false
),
)
);
parent::init( $args );
}
function display( $group_id = NULL ) {
//Remove user who do not belong to the group on members loop
function filter_for_groups( $members_template_has_members, $members_template, $r ) {
for ($i=sizeof($members_template->members)-1; $i >= 0 ; $i--) {
$user_id = $members_template->members[$i]->id;
if(!groups_is_user_member( $user_id, bp_get_group_id() )){
$members_template->member_count = $members_template->member_count-1;
array_splice($members_template->members, $i, 1);
}
}
if ($members_template->member_count <= 0) {
return '';
} else {
return $members_template_has_members;
}
};
add_filter( 'bp_has_members', 'filter_for_groups', 10, 3 );
require('/Your/theme/custom/members/loop/members-loop.php');
}
}
bp_register_group_extension( 'Group_Extension_List_Members' );
Hope it will help other in the future, And I'm still open to know the good way to proceed.
Related
Let's say you create a WordPress post and assign it a meta key foo with value bar. You only want to display an ACF field if foo is equal to bar. However, there's no built-in location rule in ACF to do this. How would you solve this problem by creating an ACF custom location rule?
In order to display ACF fields if a post meta key is equal or not equal to some value, use the following snippet. It's based off of the ACF Custom Location Rules guide.
if( ! defined( 'ABSPATH' ) ) exit;
class My_ACF_Location_Post_Meta extends ACF_Location {
public function initialize() {
$this->name = 'post_meta';
$this->label = __( "Post Meta", 'acf' );
$this->category = 'post';
$this->object_type = 'post';
}
public function rule_values($choices, $rule){
if(!acf_is_screen('acf-field-group') && !acf_is_ajax('acf/field_group/render_location_rule')){
return array(
$rule['meta_key'] => $rule['meta_key'],
$rule['meta_value'] => $rule['meta_value']
);
}
ob_start();
acf_render_field(array(
'type' => 'text',
'name' => 'meta_key',
'prefix' => 'acf_field_group[location]['.$rule['group'].']['.$rule['id'].']',
'value' => (isset($rule['meta_key']) ? $rule['meta_key'] : ''),
'placeholder' => 'Meta Key'
));
acf_render_field(array(
'type' => 'text',
'name' => 'meta_value',
'prefix' => 'acf_field_group[location]['.$rule['group'].']['.$rule['id'].']',
'value' => (isset($rule['meta_value']) ? $rule['meta_value'] : ''),
'placeholder' => 'Meta Value'
));
return ob_get_clean();
}
public function rule_operators($choices, $rule){
$choices = [];
$choices['key_is_equal_to_value'] = __('key is equal to value', 'acf');
$choices['key_is_not_equal_to_value'] = __('key is not equal to value', 'acf');
return $choices;
}
public function match( $rule, $screen, $field_group ) {
// Check screen args for "post_id" which will exist when editing a post.
// Return false for all other edit screens.
if( isset($screen['post_id']) ) {
$post_id = $screen['post_id'];
} elseif (isset($screen['attachment_id'])) {
$post_id = $screen['attachment_id'];
} else {
return false;
}
// Load the post meta object for this edit screen.
$post_meta_value = get_post_meta( $post_id, $rule['meta_key'], true );
if( !$post_meta_value ) {
return false;
}
// Compare the Post's meta value to rule meta value.
$result = ( strval($post_meta_value) == $rule['meta_value'] );
// Return result taking into account the operator type.
if( $rule['operator'] == 'key_is_not_equal_to_value' ) {
return !$result;
}
return $result;
}
}
add_action('acf/init', 'my_acf_init_location_types');
function my_acf_init_location_types() {
// Check function exists, then include and register the custom location type class.
if( function_exists('acf_register_location_type') ) {
acf_register_location_type( 'My_ACF_Location_Post_Meta' );
}
}
I created a custom endpoint for specific data from a custom table in my Wordpress plugin. It get's all the data from the table with the getHelpers() function. After that it will be merged by some user data. I would like to add the profile_image as a link to the response so we can get it with the embed parameter.
What is the best way to add the link to the response? I know the $response->add_link() function but this would add it to the response and not to each contributor.
I tried to add the link as an array but this won't react on the _embed parameter.
This is my code for the custom endpoint:
class VEMS_Rest_Contributors extends WP_REST_Controller {
protected $namespace = 'vems/v2';
protected $rest_base = 'contributors';
/**
* Register the routes for coupons.
*/
public function register_routes() {
register_rest_route( $this->namespace, '/' . $this->rest_base, array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_items' ),
'args' => $this->get_collection_params(),
) );
}
public function get_items( WP_REST_Request $request ) {
$project_id = $request->get_param( 'project_id' );
$contributors = array();
if( !empty($project_id) ) {
$project = new VEMS_Project( $request['project_id'] );
$helpers = $project->getHelpers();
foreach($helpers as $helper) {
$contributor = array();
if( !empty($helper->contributor_id) ) {
$user = get_user_by( 'ID', $helper->contributor_id );
$user_meta = get_user_meta( $helper->contributor_id );
$contributor['ID'] = $helper->contributor_id;
$contributor['user_nicename'] = $user->data->display_name;
$contributor['user_profile_image'] = $user_meta['contributor_profile_image'][0];
} else {
$contributor['user_nicename'] = $helper->name;
$contributor['user_profile_image'] = $helper->image_id;
}
$contributor['item_total'] = $helper->item_total;
$contributor['checked'] = $helper->checked;
$contributor['helper_date'] = $helper->helper_date;
/*
$contributor['_links']['profile_image'] = array(
'href' => rest_url( '/wp/v2/media/' . $contributor['user_profile_image'] ),
'embeddable' => true
);
*/
$contributors[] = $contributor;
}
}
$response = rest_ensure_response( $contributors );
return $response;
}
public function get_collection_params() {
$params['project_id'] = array(
'description' => __( 'Limit result set to contributors assigned a specific project.', 'vems' ),
'type' => 'integer',
'sanitize_callback' => 'absint',
'validate_callback' => 'rest_validate_request_arg',
);
return $params;
}
}
to handle links on route vems/v2/contributors?_embed, the element profile_image must be an array of links and then you can do that
$contributor['_links']['profile_image'] = [
[
'href' => rest_url( '/wp/v2/media/' . $contributor['ID'] ),
'embeddable' => true,
],
];
I have this REST data:
[{"id":215,"acf":{"stad":{"value":"barcelona","label":"barcelona"},"description":"","images":[{"ID":191,"id":191,"title":"logo-black.png","filename":"logo-black.png","filesize":3080,"url":"https:\/\/wordpress-132670-574369.cloudwaysapps.com\/wp-content\/uploads\/logo-black.png","link":"https:\/\/wordpress-132670-574369.cloudwaysapps.com\/logo-black-png\/","alt":"","author":"1","description":"","caption":"","name":"logo-black-png","status":"inherit","uploaded_to":0,"date":"2018-04-16 15:39:37","modified":"2018-08-05 15:19:12","menu_order":0,"mime_type":"image\/png","type":"image","subtype":"png","icon":"https:\/\/wordpress-132670-574369.cloudwaysapps.com\/wp-includes\/images\/media\/default.png","width":443,"height":98,"sizes":{"thumbnail":"https:\/\/wordpress-132670-574369.cloudwaysapps.com\/wp-content\/uploads\/logo-black-150x98.png","thumbnail-width":150,"thumbnail-height":98,"medium":"https:\/\/wordpress-132670-574369.cloudwaysapps.com\/wp-content\/uploads\/logo-black-300x66.png","medium-width":300,"medium-height":66,"medium_large":"https:\/\/wordpress-132670-574369.cloudwaysapps.com\/wp-content\/uploads\/logo-black.png","medium_large-width":443,"medium_large-height":98,"large":"https:\/\/wordpress-132670-574369.cloudwaysapps.com\/wp-content\/uploads\/logo-black.png","large-width":443,"large-height":98}}]}},{"id":205,"acf":{"stad":{"value":"oslo","label":"oslo"},"description":"","images":false,"myid":"333"}}]
I created a filter to query with the "myid" parameter like this:
/wp-json/wp/v2/hotels/?myid=333
This is the filter code I added to the functions.php:
add_filter('rest_hotels_vars', function ($valid_vars)
{
return array_merge($valid_vars, array('myid', 'meta_query'));
});
add_filter('rest_hotels_query', function($args, $request)
{
$myid = $request->get_param('myid');
if (!empty( $myid))
{
$args['meta_query'] = array(
array(
'key' => 'myid',
'value' => $myid,
'compare' => '=',
)
);
}
return $args;
}, 10, 2 );
Hotels is a custom post type.
The query always returns all the rows, the filter has no effect.
What's wrong here?
If you look into the filter closely you will understand the error.
apply_filters( "rest_{$this->post_type}_query", array $args, WP_REST_Request $request );
This is your code:
add_filter('rest_hotels_query', function($args, $request){
$myid = $request->get_param('myid');
if (!empty( $myid))
{
$args['meta_query'] = array(
array(
'key' => 'myid',
'value' => $myid,
'compare' => '=',
)
);
}
return $args;
}, 10, 2 );
Here rest_hotels_query : we need to put post type name please be careful, if your post type name is hotel then filter should be like : "rest_hotel_query"
This is the working code:
add_filter('rest_hotel_query', function($args, $request){
$myid = $request->get_param('myid');
if (!empty( $myid))
{
$args['meta_query'] = array(
array(
'key' => 'myid',
'value' => $myid,
'compare' => '=',
)
);
}
return $args;
}, 10, 2 );
Same case with the collection parameters for the posts controller:
apply_filters( "rest_{$this->post_type}_query", array $args, WP_REST_Request $request );
It should be :
add_filter('rest_hotel_collection_params', function ($valid_vars){
return array_merge($valid_vars, array('myid', 'meta_query'));
});
The rest_query_vars filter no longer exists. Take a look at https://developer.wordpress.org/reference/hooks/rest_this-post_type_collection_params/ and https://developer.wordpress.org/reference/hooks/rest_this-post_type_query/
So replace this
add_filter('rest_hotels_vars', function ($valid_vars)
{
return array_merge($valid_vars, array('myid', 'meta_query'));
});
with this
add_filter('rest_hotels_collection_params', function ($valid_vars)
{
return array_merge($valid_vars, array('myid', 'meta_query'));
});
and it should work.
I use custom plugin for Woocommerce shipping method. The plugin run a new class which extends WC_Shipping_MEthod, but the problem is the 'cost' price is calculated in other function outsite the class.
So in the current class I have:
public function calculate_shipping( $package=array() ) {
/* $this->add_rate( array(
'id' => $this->id . $this->instance_id,
'label' => $this->title,
'cost' => 0,
) ); */
$rate = array(
'id' => $this->id,
'label' => $this->title,
'cost' => 0,
'taxes' =>false,
//'calc_tax' => 'per_item'
);
// Register the rate
$this->add_rate( $rate );
}
Outside the class, in another file, I have a function, which calculates the cost for shipping:
//add shipping cost to checkout total
add_action('woocommerce_cart_calculate_fees', 'woo_add_cart_fee');
function woo_add_cart_fee() {
global $woocommerce;
$woocommerce->shipping;
$wc_econt = new WC_Econt_Shipping_Method;
if ( is_checkout() && (bool)$wc_econt->inc_shipping_cost == TRUE ) {
if(!isset($_SESSION)){
session_start();
}
//write_log('session econt customer shipping cost:'.$_SESSION['econt_shipping_cost']);
$extracost = (isset($_SESSION['econt_shipping_cost']) ? $_SESSION['econt_shipping_cost'] : 0 );
if ($extracost != '0') {
WC()->cart->add_fee(__('Econt Express Shipping Method','woocommerce-econt'), $extracost);
}
}
}
If just change 0 to $extracode in the class function it doesn't update the shipping cost on checkout page:
public function calculate_shipping( $package=array() ) {
/* $this->add_rate( array(
'id' => $this->id . $this->instance_id,
'label' => $this->title,
'cost' => $extracost,
) ); */
$rate = array(
'id' => $this->id,
'label' => $this->title,
'cost' => $extracost,
'taxes' =>false,
//'calc_tax' => 'per_item'
);
// Register the rate
$this->add_rate( $rate );
}
I guess the class run before the woo_add_cart_fee function, and that's why it cannot get the value of $extracost variable.
How to resolve this?
In the function to calculate the shipping cost, add a session variable with your new calculated value like this:
//add shipping cost to checkout total
add_action('woocommerce_cart_calculate_fees', 'woo_add_cart_fee');
function woo_add_cart_fee() {
global $woocommerce;
$woocommerce->shipping;
$wc_econt = new WC_Econt_Shipping_Method;
if ( is_checkout() && (bool)$wc_econt->inc_shipping_cost == TRUE ) {
if(!isset($_SESSION)){
session_start();
}
//write_log('session econt customer shipping cost:'.$_SESSION['econt_shipping_cost']);
$extracost = (isset($_SESSION['econt_shipping_cost']) ? $_SESSION['econt_shipping_cost'] : 0 );
if ($extracost != '0') {
WC()->session->set( 'Econt_Express_Shipping_Method' , $extracost );
}
}
}
Then on the calculate shipping function use the WC session variable to set the value for the cost like below:
public function calculate_shipping( $package=array() ) {
/* $this->add_rate( array(
'id' => $this->id . $this->instance_id,
'label' => $this->title,
'cost' => 0,
) ); */
$rate = array(
'id' => $this->id,
'label' => $this->title,
'cost' => WC()->session->get('Econt_Express_Shipping_Method' ),
'taxes' =>false,
//'calc_tax' => 'per_item'
);
// Register the rate
$this->add_rate( $rate );
}
Use the javascript below to update your new shipping value at checkout:
$('body').trigger('update_checkout', { update_shipping_method: true });
You may want to change the value of billing address or shipping address or country to be able to update your value if you use the above code more than once on checkout. This is also where I stuck and am forced to fake these changes so that Woocommerce can update my shipping value...am still looking for the solution though...lol :)
$("#billing_address_1").trigger("keypress").val(function(i,val){return val + 'a';});
I'm trying to get only my second level categories from a 3 depth category structure in woocommerce.
But it always returns the 3rd level aswell.
/**
* Get second level cat
*/
function get_second_cat_level($parent_id) {
$subcats = array();
$args = array(
'parent' => $parent_id,
'taxonomy' => 'product_cat',
'orderby' => 'name',
'show_count' => 0,
'pad_counts' => 0,
'hierarchical' => 1,
'hide_empty' => 0
);
$cats = get_categories($args);
foreach ($cats as $cat) {
$subcats[] = $cat;
var_dump($cat);
}
return $cats;
}
I assume that $parent_id is the string id of the parent_category.
This is just crazy.
As no one seems to have any solution for me, I will share one solution I used. But be carefull, this is very specific to 3 depth level category groups.
I created 2 functions :
one to get the category object from the id, the slug or the name of the given category :
function get_product_category($field, $value) {
$authorized_fields = array(
'term_id',
'name',
'slug'
);
// Check if field and value are set and not empty
if (!isset($field) || empty($field) || !isset($value) || empty($value)) {
$response = "Error : check your args, some are not set or are empty.";
}
else {
// Check if the specified field is part of the authorised ones
if (!in_array($field, $authorized_fields)) {
$response = "Unauthorised field $field"; }
else {
// init exists var to determine later if specified value matches
$exists = false;
$product_cats = get_terms('product_cat', array(
'hide_empty' => 0,
'orderby' => 'name'
));
// the loop will stop once it will have found the matching value in categories
foreach ($product_cats as $product_cat) {
if($product_cat->$field == $value) {
$response = $product_cat;
$exists = true;
break;
}
}
if ($exists == false) {
$response = array(
"message" => "Error with specified args",
"field" => "$field",
"value" => "$value"
);
}
}
}
return $response;
}
The second function uses the first one to return the 2nd level categories. It uses an argument $dep which, when is tested alone as false, returns another result which I needed somewhere else. So don't pay attention to it.
function get_first_child_cat_only ($cat_id, $dep = true) {
// Array which handle all the 2nd child sub cats
$subcats = array();
// $cat_id is the parent (1st level) cat id
$categories = get_term_children( $cat_id, 'product_cat' );
foreach ($categories as $sub_category) {
if ($dep == true && get_term_children( $sub_category, 'product_cat' )) {
$subcats[] = get_product_category('term_id', $sub_category);
}
elseif($dep == false) {
$subcats[] = get_product_category('term_id', $sub_category);
}
}
return $subcats;
}
Little explanation : the above function return only sub catégories which has categories children. So it ignores the last (3rd one) which have no children and returns the second one only.
This can certainly be improved, and I know I'll probably get some "criticisms" and actually, I hope so ! So don't hesitate :)