I want to add a new custom filter in WordPress network admin
restrict_manage_users for network admin in the user panel
I want a custom filter option not a custom column, i just want to add one filter dropdown with roles
You will need to hook on the ‘restrict_manage_posts‘ filters to add your filter (dropdown) and to ‘parse_query‘ to alter the query according to the filter selection.
For example, if you want to add a filter to display a list of years for the user to select, add the following code in your functions.php file:
if (is_admin()){
//this hook will create a new filter on the admin area for the specified post type
add_action( 'restrict_manage_posts', function(){
global $wpdb, $table_prefix;
$post_type = (isset($_GET['post_type'])) ? quote_smart($_GET['post_type'], true) : 'post';
//only add filter to post type you want
if ($post_type == 'YOUR_POST_TYPE_HERE'){
//query database to get a list of years for the specific post type:
$values = array();
$query_years = $wpdb->get_results("SELECT year(post_date) as year from ".$table_prefix."posts
where post_type='".$post_type."'
group by year(post_date)
order by post_date");
foreach ($query_years as &$data){
$values[$data->year] = $data->year;
}
//give a unique name in the select field
?><select name="admin_filter_year">
<option value="">All years</option>
<?php
$current_v = isset($_GET['admin_filter_year'])? $_GET['admin_filter_year'] : '';
foreach ($values as $label => $value) {
printf(
'<option value="%s"%s>%s</option>',
$value,
$value == $current_v? ' selected="selected"':'',
$label
);
}
?>
</select>
<?php
}
});
//this hook will alter the main query according to the user's selection of the custom filter we created above:
add_filter( 'parse_query', function($query){
global $pagenow;
$post_type = (isset($_GET['post_type'])) ? quote_smart($_GET['post_type'], true) : 'post';
if ($post_type == 'YOUR_POST_TYPE_HERE' && $pagenow=='edit.php' && isset($_GET['admin_filter_year']) && !empty($_GET['admin_filter_year'])) {
$query->query_vars['year'] = $_GET['admin_filter_year'];
}
});
}
Using this technique you can actually add any filter you want.
I hope this will solve your problem.
add_filter( 'wpmu_users_columns', 'my_awesome_new_column' );
add_action( 'manage_users_custom_column', 'my_awesome_column_data', 10, 3 );
// Creates a new column in the network users table and puts it before a chosen column
function my_awesome_new_column( $columns ) {
return my_awesome_add_element_to_array( $columns, 'my-awesome-column', 'Awesome', 'registered' );
}
// Adds data to our new column
function my_awesome_column_data( $value, $column_name, $user_id ) {
// If this our column, we return our data
if ( 'my-awesome-column' == $column_name ) {
return 'Awesome user ID ' . intval( $user_id );
}
// If this is not any of our custom columns we just return the normal data
return $value;
}
// Adds a new element in an array on the exact place we want (if possible).
function my_awesome_add_element_to_array( $original_array, $add_element_key, $add_element_value, $add_before_key ) {
// This variable shows if we were able to add the element where we wanted
$added = 0;
// This will be the new array, it will include our element placed where we want
$new_array = array();
// We go through all the current elements and we add our new element on the place we want
foreach( $original_array as $key => $value ) {
// We put the element before the key we want
if ( $key == $add_before_key ) {
$new_array[ $add_element_key ] = $add_element_value;
// We were able to add the element where we wanted so no need to add it again later
$added = 1;
}
// All the normal elements remain and are added to the new array we made
$new_array[ $key ] = $value;
}
// If we failed to add the element earlier (because the key we tried to add it in front of is gone) we add it now to the end
if ( 0 == $added ) {
$new_array[ $add_element_key ] = $add_element_value;
}
// We return the new array we made
return $new_array;
}
Here is an example : https://wordpress.stackexchange.com/questions/299801/custom-column-under-all-users-multisite-network-admin
Related
I try to send a customized email template when a customer has a ticket (custom product type) in cart.
I have the following:
function bc_customer_completed_order_template($template, $template_name, $template_path)
{
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$product = wc_get_product( $cart_item['product_id'] );
$type = get_class($product);
if ( $type == 'WC_Product_Tickets' && 'customer-completed-order.php' === basename($template) ) {
$template = trailingslashit(plugin_dir_path( __FILE__ )) . 'templates/customer-completed-order.php';
}
}
return $template;
}
add_filter('woocommerce_locate_template', 'bc_customer_completed_order_template', 10, 3);
The conditionals are working (on cart and checkout page for example), but when the order is placed, the new template is not used.
Anybody?
Your email template will look like this:
Code snippets:
// Suppose orders don't have ticket products.
$has_tickets = false;
// Loop order items.
foreach ( $order->get_items() as $item_id => $item ) {
// Get product object from order item.
$_product = $item->get_product();
// Check if the product object is valid and the class is `WC_Product_Tickets`
if ( $_product && 'WC_Product_Tickets' === get_class( $_product ) ) {
// Change the flag.
$has_tickets = true;
// Break the loop as we alreay have true flag.
break;
}
}
// Check if order have tickets items.
if ( $has_tickets ) {
// Load custom email template.
wc_get_template( 'templates/custom-customer-completed-order.php' );
// Return as we don't need the below code.
return;
}
It turned out, although the above solution is correct in its idea, in reality one cannot load a template and make use of the $order without extending the woocommerce email class.
Therefore i loaded the function inside the email template itself and made an if - else statement so for situation A the layout is different then for situation b.
Like so:
$has_tickets = false;
// Loop order items.
foreach ( $order->get_items() as $item_id => $item ) {
// Get product object from order item.
$_product = $item->get_product();
// Check if the product object is valid and the class is `WC_Product_Tickets`
if ( $_product && 'WC_Product_Tickets' === get_class( $_product ) ) {
// Change the flag.
$has_tickets = true;
// Break the loop as we alreay have true flag.
break;
}
}
// Check if order have tickets items.
if ( $has_tickets ) {
do_action( 'woocommerce_email_header', $email_heading, $email ); ?>
//custom email layout here//
}
else{
//Regular email template here
}
Goal:
I'm trying to accomplish 2 things.
Change the permalink structure of a custom post type
Output a list of linked posts (of that CPT) based on the post's parent-child relationship via shortcode
On the main page, list states
On the state pages, list cities of that state
On the city pages, list cities in the same state
What's working:
The correct permalink is displayed on the post's edit page.
The shortcode displays the proper items.
The issue: The list's links are wrong. The links were correct until I added the "post_type_link" modification.
The permalinks are created using the data of the current page. On the main page and state pages, the permalinks are clearly incorrect. On the city pages, all of the permalinks are for that specific page.
Example: On a page for Houston, TX, Dallas and Ft Worth are listed but their links are for Houston. Specifically, it should be "domain.com/company/texas/dallas" and "domain.com/company/texas/ft-worth", but I'm getting "domain.com/company/texas/houston" on the Houston page or an error on the Texas page.
How do I ensure the proper data is being used?
Code summary:
The filter
There are 3 patterns I want to use: "/company","/company/state", and "/company/state/city". Company is constant. State and city are stored as meta data. (States are an array with name and abbreviation.) The desired pattern should be easily identified by a taxonomy (page_groups).
The shortcode
It's pretty self-explanatory. The post structure is hierarchical. The company page's children are states. And the states' children are cities.
The list is generated via a query based on parent ID. The query returns an array of OBJs. The link is generated in a foreach loop using get_post_permalink().
I am using "global $post". The shortcode doesn't work without it. I'm guessing this is part of the issue. I could manually build the list item links, but that's a terrible long term solution.
EDIT: I updated the code to use get_the_ID and removed "global $post". No change to the shortcode output.
Code:
add_filter( 'post_type_link', 'company_tag_rewrite', 10, 2 );
function company_tag_rewrite( $permalink, $post ) {
if( $post->post_type == 'company-page' ) {
// main
if( has_term( 'company-main', 'page_groups', $post) ){
return get_home_url() ."/pest-control-service/company/";
}
// state
if( has_term( 'company-state', 'page_groups', $post) ){
$state = get_field('state');
if( $state && 'All' != $state['label'] ){
$label = strtolower($state['label']);
return get_home_url() ."/pest-control-service/company/$label";
}else{
return get_home_url() ."/pest-control-service/fix-the-state-data-city/";
}
}
// city
if( has_term( 'company-city', 'page_groups', $post) ){
$state = get_field('state');
$city = strtolower(get_field('city'));
if( !$state || 'All' == $state['label'] ){
return get_home_url() ."/pest-control-service/fix-the-state-data/";
}
if( $city ){
$label = strtolower($state['label']);
return get_home_url() ."/pest-control-service/company/$label/$city/";
}else{
return get_home_url() ."/pest-control-service/fix-the-city-data/";
}
}
return get_home_url() ."/pest-control-service/set_the_page_group/";
}else{
return $permalink;
}
}
function company_page_footer_fn($atts = array(), $content = null) {
ob_start();
// start output ----------
// determine mode and set query
global $post;
$ancestors = get_post_ancestors($post->ID);
$ancestor_count = count($ancestors);
$city = get_field('city'); //? do i need this?
$state_array = get_field('state');
$state = $state_array['label'];
// determine mode and set widget title and query
if (0 == $ancestor_count){
//main
$widget_title = "Top Locations";
$target = $post->ID;
}elseif(1 == $ancestor_count){
//state
$widget_title = "company has offices throughout $state including:";
$target = $post->ID;
}elseif(2 == $ancestor_count){
$widget_title = "company has offices throughout $state including:";
$target = wp_get_post_parent_id( );
//city
}else{
wp_reset_postdata();
return "<div style='background-color: white; color:red;font-weight: 700;padding: 1rem;'>You might want to check this post's data.</div>";
}
$post_kids = get_children(array(
// returns array of objects
"post_parent" => $target,
"post_status" => "publish",
),'OBJECT'
);
// get data
foreach($post_kids as $kid) {
$link = get_post_permalink($kid);
if (0 == $ancestor_count){
$state_array = get_field('state',$kid->ID);
$title = $state_array['label'];
}else{
$title = get_field('city',$kid->ID).", $state";
}
$data[] = array(
'title' => $title,
'link' => $link,
);
}
// sort
function cmp($a, $b)
{
return strcmp($a["title"], $b["title"]);
}
uasort($data, 'cmp');
// output
?><style>
</style><?php
echo "<div class='location-list-container'>";
echo "<H2>$widget_title</H2>";
echo "<div class='location-list'>";
foreach($data as $item){
$this_title = $item['title'];
$this_link = $item['link'];
echo "<h3><a href='$this_link'>$this_title</a></h3>";
}
echo "</div";
echo "</div";
//return output ----------
wp_reset_postdata();
$thisOutput = ob_get_clean();
return $thisOutput;
}
add_shortcode( 'company-page-footer', 'company_page_footer_fn' );
I have created a custom field true/false, and I want when true is selected in a product not to be displayed in the eshop.
I want to insert the code inside the functions.php
example
if ( in_array( 'subscriber', (array) $user->roles ) || !is_user_logged_in() ) {
$postid = get_the_ID();
$prd_only_for_Customers = get_field('prd_clients', $postid); // The ACF true/false field }
Can anyone help ?
As Howard said your question is incomplete but you can use the following method to set product hidden.
You can use pre_get_posts hook in your functions.php. Since Woocommerce 3 the products visibility is now handled by the 'product_visibility' custom taxonomy for the terms 'exclude-from-catalog' and 'exclude-from-search'… See this thread or this one too.
So you should use instead the WC_Product CRUD setter methods set_catalog_visibility() this way:
function get_post_ids_by_meta_key_and_value($key, $value) {
global $wpdb;
$meta = $wpdb->get_results("SELECT post_id FROM `".$wpdb->postmeta."` WHERE meta_key='".$wpdb->escape($key)."' AND meta_value='".$wpdb->escape($value)."'");
$post_ids = [];
foreach( $meta as $m ) {
$post_ids[] = $m->post_id;
}
return $post_ids;
}
add_action('pre_get_posts', function( $query ){
if ( $query->is_main_query() && is_woocommerce() && !is_user_logged_in() ) {
$product_ids = get_post_ids_by_meta_key_and_value('prd_clients', 1);
foreach($product_ids as $id){
// Get an instance of the product
$product = wc_get_product($id);
// Change the product visibility
$product->set_catalog_visibility('hidden');
// Save and sync the product visibility
$product->save();
}
}
});
This code isn't tested, let me know if it worked or you faced any problem.
This is my final code if anyone needs something like this
// Specific products show only for Customer and administrator role
add_action('pre_get_posts', function( $query ){
$user = wp_get_current_user();
if ( $query->is_main_query() && is_woocommerce()) {
if (!check_user_role(array('customer','administrator')) || !is_user_logged_in() ) {
$product_ids = get_post_ids_by_meta_key_and_value('prd_clients', 1);
foreach($product_ids as $id){
// Get an instance of the product
$product = wc_get_product($id);
// Change the product visibility
$product->set_catalog_visibility('hidden');
// Save and sync the product visibility
$product->save();
}
}
else{
$product_ids = get_post_ids_by_meta_key_and_value('prd_clients', 1);
foreach($product_ids as $id){
// Get an instance of the product
$product = wc_get_product($id);
// Change the product visibility
$product->set_catalog_visibility('visible');
// Save and sync the product visibility
$product->save();
}
}
}
});
tldr:// how to clone an existing WC_Order_Item_Product to a new order?
I have looked through a lot of existing subscription plugins but didnt really get warm with them. So I decided to create something as a learning project. Its not for a customer, I dont expect it to be commercially successful product, but I guess its good to step up my game.
So I have created a custom table already that saves target dates and would like to create new orders from the existing WC_Order_Item_Product-Objects. By simply adding the original Object it will just move them from the old order, which is something I do not want to happen. I wonder how I could 'clone' the object and remove the protected [order_id] which would be overridden by WC_Order->add_item anyway without altering the original database entries. Maybe thats the wrong approach or a bad idea?
I have also tried to find some up2date hint on how to create a full custom WC_Order_Item_Product but was not really successful on that.
What I got atm:
function update ($reminder_id = null) {
global $woocommerce;
global $wpdb;
global $aboprodukt_table_name;
// TODO if reminder id
if ( ! $reminder_id) {
$neworders = [];
// get all reminders from db and loop through them
$aboprodukt_reminders = $wpdb->get_results( 'SELECT * FROM ' . $aboprodukt_table_name . ' ORDER BY ' . $aboprodukt_table_name . '.targetdate ASC');
foreach ($aboprodukt_reminders as $item ) {
// get original order item object to abstract
$order_item = new \WC_Order_Item_Product($item->orderitem);
// get order object
$order = $order_item->get_order();
// get customer id
$customer = $order->get_customer_id();
// TODO abstract order items
// assign user, adresses if not existing yet, copy from last order
if ( ! $neworders[$customer] ) {
// check all reminders
$customer_array = $order->get_data();
$args = array(
'customer_id' => $customer,
'created_via' => 'Aboprodukt',
'add_order_note' => __('Aboprodukt Custom Order', 'aboprodukt'),
);
$neworders[$customer] = \wc_create_order($args);
// TODO Shipping
}
// add order items
$neworders[$customer]->add_item($order_item);
$neworders[$customer]->calculate_totals();
$neworders[$customer]->save();
}
return print_r($neworders,true);}
Not sure if it helps so. but this is the way I've managed to do so.
For understanding: I got an extra "reminder"-table in the database that stores: date, order-item-id. It is removed after its used to create a new order.
function update ($reminder_id = null) {
global $woocommerce;
global $wpdb;
global $aboprodukt_table_name;
// TODO if order id
if ( ! $reminder_id) {
$neworders = [];
// get all reminders from db and loop through them
$aboprodukt_reminders = $wpdb->get_results( 'SELECT * FROM ' . $aboprodukt_table_name . ' ORDER BY ' . $aboprodukt_table_name . '.targetdate ASC');
foreach ($aboprodukt_reminders as $item ) {
// get original order item object to abstract
$order_item = new \WC_Order_Item_Product($item->orderitem);
$order = $order_item->get_order();
$customer = $order->get_customer_id();
// assign user, adresses if not existing yet, copy from last order
if ( ! $neworders[$customer] ) {
// check all reminders
$args = array(
'customer_id' => $customer,
'created_via' => 'Aboprodukt',
'add_order_note' => __('Aboprodukt Custom Order', 'aboprodukt'),
);
$neworders[$customer] = \wc_create_order($args);
// order
$customer_object = new \WC_Customer( $customer );
$types = array('billing','shipping');
foreach ($types as $type) {
foreach ( $customer_object->{"get_{$type}"}() as $key => $value) {
if ( ! empty($value) ) {
if ( is_callable( array( $neworders[$customer], "set_{$type}_{$key}" ) ) ) {
$neworders[$customer]->{"set_{$type}_{$key}"}( $value );
}
}
}
}
// Set Shipping
$shippingold = $order->get_shipping_methods();
$oldshippingitem = new \WC_Order_Item_Shipping( reset($shippingold) );
$newshippingitem = new \WC_Order_Item_Shipping();
$newshippingitem->set_method_id( $oldshippingitem->get_method_id() );
$newshippingitem->set_instance_id( $oldshippingitem->get_instance_id() );
$newshippingitem->set_method_title( $oldshippingitem->get_method_title() );
$newshippingitem->set_total( $oldshippingitem->get_total() );
$neworders[$customer]->add_item( $newshippingitem );
}
// if variation_id > 0 then is simple product, get product
$item_variation_id = $order_item->get_variation_id();
$item_id = $neworders[$customer]->add_product(
wc_get_product(isset( $item_variation_id ) && $item_variation_id > 0 ? $item_variation_id : $order_item->get_product_id() ),
$order_item->get_quantity()
);
// copy metadata for future use
$neworderitem = new \WC_Order_Item_Product($item_id);
$neworderitem->add_meta_data( '_aboprodukt_order', $order_item->get_meta('_aboprodukt_order'), true );
$neworderitem->add_meta_data( '_aboprodukt_timespan', $order_item->get_meta('_aboprodukt_timespan'), true );
$neworderitem->add_meta_data( '_aboprodukt_type', $order_item->get_meta('_aboprodukt_type'), true );
$neworderitem->save();
$neworders[$customer]->calculate_totals();
$neworders[$customer]->save();
$wpdb->query(
$wpdb->prepare(
"DELETE FROM $aboprodukt_table_name
WHERE id = %d",
$item->id
)
);
}
}
// send emails
foreach ( $neworders as $customers => $neworder ) {
$mailer = \WC()->mailer();
\WC()->mailer()->emails['WC_Email_Customer_Invoice']->trigger($neworder->get_id());
$neworder->add_order_note( sprintf( __('%s email notification manually sent.', 'woocommerce'), $mail->title), false, true);
}
return $neworders;
}
I've been trying to do this for a time, unfortunately I've found only solutions for the store(frontend) in cases the category is empty and so on.
What I basically want to do, is when I create a new product, at the Prodct Categories section
I want to show/hide some categories.
So far I haven't found anything for this, any idea how can it be done?
Thanks.
Try out below code :
function is_edit_page($new_edit = null){
global $pagenow;
//make sure we are on the backend
if (!is_admin()) return false;
if($new_edit == "edit")
return in_array( $pagenow, array( 'post.php', ) );
elseif($new_edit == "new") //check for new post page
return in_array( $pagenow, array( 'post-new.php' ) );
else //check for either new or edit
return in_array( $pagenow, array( 'post.php', 'post-new.php' ) );
}
function so_28055266_filterGetTermArgs($args, $taxonomies) {
global $typenow;
if ($typenow == 'product') {
// check whether we're currently filtering selected taxonomy
if (implode('', $taxonomies) == 'product_cat' && is_edit_page()) {
//Add categories term ID that you want to show
$cats = array(9,10,11,12); // List of category(term ID) that you want to add as an array
if (empty($cats))
$args['include'] = array(99999999); // no available categories
else
$args['include'] = $cats; //It will only show the category that you mentioned in above array
}
}
return $args;
}
if (is_admin()) {
add_filter('get_terms_args', 'so_28055266_filterGetTermArgs', 10, 2);
}
Let me know the output.