Checkbox array in Wordpress widget? - wordpress

I'm trying to create a widget that enables user to select which categories will be displayed.
The following is piece of codes I created but the changes of the checkbox state can't be saved. There are only two values : the title and list of selected categories.
function form($instance) {
$instance = (array)$instance;
if( empty($instance['title']) ) $instance['title'] = 'Category';
$selected_categories = (array)$instance['category'];
var_dump($selected_categories);
....
$categories = get_categories( array('exclude'=> 1) );
foreach($categories as $category) : ?>
<div>
<input type="checkbox" name="<?php echo $this->get_field_name('category'); ?>"
value="<?php echo $category->cat_ID; ?>"
<?php echo $category->cat_name; ?>
</div>
<?php endforeach; ?>
}
function update($new_instance, $old_instance) {
$instance = $old_instance;
$instance['title'] = strip_tags($new_instance['title']);
$instance['category'] = $new_instance['category'];
return $instance;
}
I observe the changes through var_dump($selected_categories). The value is always array(size=0) ignoring how many checkbox I checked.
I have no idea how to passing array in $instance variable.
Thanks in advance.

Do you have a validation function that fills the values in the array if they are not set? That is the »update« method of the widget. Perhaps you should add:
'<input name="..." value="..." id="'.$this->get_field_id( 'category' ).'" />';
to your input element.

I spent a lot of time on this exact issue. Searches online on this topic were less than satisfying. I wanted to understand how the Widget extends WP_Widget object works. Like the original post I too wanted to allow my admins to select from a dynamic check list of all post categories. In my website, I'm using this for promos, and I expect to have lots of instances of this puppy. A robust instance storage system is essential for me.
A couple of stupid questions that were bugging me.
1) What's up with the funky label for=.... Category Name /label things I see on each and every example online. (Note 'label' is surrounded by left/right symbols.. can't do that here..) Is that required, or merely somebody copying what's been done before (within the file defaults-widgets.php in the WordPress source code)? Ans: total waste of time. Less is more, code it right.
2) I really really wanted to understand the mechanism of how the system saves a users selections. There appears to be no obvious coding on the backend page to capture the selections. I spent a long time here searching, including attempting to step thru the code on a debugger, with lousy results. I spent a lot of time with var_dump($instance) to understand results. The system works, but I don't really understand why.
My logic in making this system work was to create a single checkbox variable, get that to work. Next step was to create an array of checkbox variables but only use one of them and get that to work. Finally utilize the entire array of checkbox variables. Success.
Code extract follows:
/*Saves the settings. */
function update( $new_instance, $old_instance ){
$args = array(
//your selections here.
);
$categories = get_categories( $args ); // returns an array of category objects
$arrlength=count($categories);
for($x=0;$x<$arrlength;$x++){
$tempArray[$categories[$x]->slug] = '';
}
$instance = $old_instance;
$new_instance = wp_parse_args( (array) $new_instance, $tempArray );
for($x=0;$x<$arrlength;$x++){
$instance[$categories[$x]->slug] = $new_instance[$categories[$x]->slug] ? 1 : 0;
}
return $instance;
}
/*Creates the form for the widget in the admin back-end. */
function form( $instance ){
echo '<p>Choose your categories of interest. Multiple selections are fine.</p> ';
$args = array(
//your selections here. Use same args as update function, duh.
);
$categories = get_categories( $args ); // returns an array of category objects
$arrlength=count($categories);
for($x=0;$x<$arrlength;$x++){
$tempArray[$this->get_field_id($categories[$x]->slug)] = '';
}
$instance = wp_parse_args( (array) $instance, $tempArray );
for($x=0;$x<$arrlength;$x++){
$tempCheckFlag[$categories[$x]->slug] = $instance[$categories[$x]->slug] ? 'checked="checked"' : '';
// could also use 'checked()' function
// Yup, this could be combined with the for loop below,
// listed here seperately to enhance understanding of what's going on.
}
for($x=0;$x<$arrlength;$x++)
{
echo '<p><input class ="checkbox" type="checkbox" value="1" id="'.$this->get_field_id($categories[$x]->slug).'" name="'.$this->get_field_name($categories[$x]->slug).'"'.$tempCheckFlag[$categories[$x]->slug].'>'.$categories[$x]->name.' (Category # '.$categories[$x]->term_id.' )</p>';
}
}
When I need to use the selections in my widget function, I retrieve the admin's selections from the $instance variable.
/* Displays the Widget in the front-end */
function widget( $args, $instance ){
//....
foreach($instance as $key=>$value)
{
if ($value){
$category_array[]=$key;
}
}
// Now I have a simple array of just those categories that had a check mark...

Here is a second solution that uses an array of checkbox.
I solved this problem with a checkbox array in the new version 1.8 of my widget recently updated posts widget.
I use the second form of foreach
foreach (array_expression as $key => $value).
$key allows to obtain a unique id for each checkbox.
Here $id is a number incremented by the foreach itself.
function form($instance) {
$term_taxonomy_id = (isset($instance['term_taxonomy_id']) ? array_map('absint', $instance['term_taxonomy_id']) : array("0"));
<?php
$categ = get_categories();
foreach($categ as $id => $item) :
?>
<br/>
<input type="checkbox"
id="<?php echo $this->get_field_id('term_taxonomy_id') . $id; ?>"
name="<?php echo $this->get_field_name('term_taxonomy_id'); ?>[]"
<?php if (isset($item->term_taxonomy_id)) {
if (in_array($item->term_taxonomy_id,$term_taxonomy_id))
echo 'checked';
};
?>
value="<?php echo $item->term_taxonomy_id; ?>" />
<label for="<?php echo $this->get_field_id('term_taxonomy_id') . $id; ?>" >
<?php echo $item->name ?>
</label>
<?php endforeach; ?>
I stored term_taxonomy_id instead cat_ID, for my plugin, but you can pretty much store cat_ID, it will work as well.
The array data is sanitized by the php function array_map ().The update function is:
function update($new_instance, $old_instance) {
// Par défaut, aucune catégorie n'est exclue
$instance['term_taxonomy_id'] = (isset($new_instance['term_taxonomy_id']) ? array_map( 'absint', $new_instance['term_taxonomy_id']) : array('0'));
For their use in a MySQL query, I implodes it into a set of values:
// écriture de la liste des objects sélectionnées en langage MySQL
if (isset($instance['term_taxonomy_id']))
$selected_object = "('".implode(array_map( 'absint',$instance['term_taxonomy_id']),"','")."')";
else $selected_object = "('0')";

Related

create shortcode that changes url endpoint

I am calling data from an api and looping through it as an array. The problem is that I want to only call one individual profile at a time and have a shortcode for each individual profile. I have created the shortcode function and it does work. However, I have to either call all the profiles in the loop or only one through an if statement. This is obviously not what I want. I want to be able to add: player_number=664 (for example) to the end of the endpoint url.
I have the shortcode working but not as I need it.
function individualPlayer(){
$html .= '<div class="s-players">
<div class="container">';
$responseindividualPlayer = wp_remote_get('http://api-address-hidden-for-security/statsajax.php?action=rankedplayerslist&eventid=5');
$array = json_decode(utf8_encode($responseindividualPlayer['body']),TRUE);
foreach($array as $player){
if($player['Numero'] == 707) {
$html .= '
<p>'.$player['Evento'].'</p>
<p>'.(int)$player['Numero'].'</p>
<p>'.$player['Jugador'].'</p>';
}
}
return $html .'</div></div>';
}
add_shortcode('individualPlayer', 'individualPlayer');
I want to remove the if statement.
The URL gives the event ID followed by ?player_number= then the player number.
I would love to have it [shortcode 'player_number=123'] if that is possible. If it is not possible, could someone please help orient me in the right direction?
Thank you in advance.
Erik Robles
First, you would need to pass the player number as a parameter to the individualPlayer function. This can be accomplished as follows:
function individualPlayer($attrs = []){
# Normalize the case
$attrs = array_change_key_case((array)$attrs, CASE_LOWER);
$playerId = $attrs['playerid'];
Shortcode call:
[individualPlayer playerid="123"]
Next, we need to filter the results to just the player you want. If the API supports filtering by the player number, pass $playerId to the endpoint in the required format. For example, if the API accepts the player id as a query string parameter named pid, we can set up the endpoint as follows:
$responseindividualPlayer = wp_remote_get('http://api-address-hidden-for-security/statsajax.php?action=rankedplayerslist&eventid=5&pid=' . $playerId);
If it does not support API-side filtering, then you will have to do it on your side (loop through the results and pick the record with a matching id).
foreach($array as $player){
if($player['Numero'] == $playerId) {
# etc.
Credit goes to Krzysiek Dróżdż for helping me with this answer.
class Remote_Player_API {
private static $instance;
private $remote_data;
public static function init() {
if ( ! self::$instance ) {
self::$instance = new Remote_Player_API();
}
}
protected function __construct() {
$this->register_shortcodes();
}
protected function get_remote_api_data() {
// you can also use transients here and cache remote responses for some time to optimize API calls even more
if ( ! $this->remote_data ) { // obtain remote data only, if we haven't done it already, so the request will be done only once
$response = wp_remote_get('http://api-address-hidden-for-security/statsajax.php?action=rankedplayerslist&eventid=5');
$this->remote_data = json_decode( utf8_encode( $response['body'] ), TRUE );
}
return $this->remote_data;
}
protected function register_shortcodes() {
add_shortcode( 'individualPlayer', array( $this, 'shortcode_callback_individualPlayer' ) );
}
public function shortcode_callback_individualPlayer( $atts ) {
$atts = shortcode_atts( array(
'player_number' => 0, // you have to pass player_number as attribute
), $atts );
ob_start();
?>
<div class="s-players">
<div class="container">
<?php
foreach ( $this->get_remote_api_data() as $player ) :
if ( $player['Numero'] != $atts['player_number'] ) continue;
?>
<p><?php echo esc_html( $player['Evento'] ); ?></p>
<p><?php echo (int)$player['Numero']; ?></p>
<p><?php echo esc_html( $player['Jugador'] ); ?></p>
<?php endforeach; ?>
</div>
</div>
<?php
return ob_get_clean();
}
}
Remote_Player_API::init();

Wordpress Ultimate Member: show profile info on account page

I made a custom registration form with a few extra fields using the ultimate member plugin. I want to display these extra fields on the account page. I already created an extra tab for it with hooks (see code below), but I can’t seem to find how to get the data from the extra fields of the register form. Does anyone has an idea?
Kind regards
Davy
/* Ultimate member */
/* tab Stormbee registration */
add_filter('um_account_page_default_tabs_hook', 'my_custom_tab_in_um', 100 );
function my_custom_tab_in_um( $tabs ) {
$tabs[800]['mytab']['icon'] = 'um-faicon-pencil';
$tabs[800]['mytab']['title'] = 'Stormbee registration';
$tabs[800]['mytab']['custom'] = true;
return $tabs;
}
/* make our new tab hookable */
add_action('um_account_tab__mytab', 'um_account_tab__mytab');
function um_account_tab__mytab( $info ) {
global $ultimatemember;
extract( $info );
$output = $ultimatemember->account->get_tab_output('mytab');
if ( $output ) { echo $output; }
}
/* Finally we add some content in the tab */
add_filter('um_account_content_hook_mytab', 'um_account_content_hook_mytab');
function um_account_content_hook_mytab( $output ){
ob_start();
?>
<div class="um-field">
<?php echo um_user('display_name'); ?> <br />
<?php
?>
</div>
<?php
$output .= ob_get_contents();
ob_end_clean();
return $output;
}
/* Ultimate member */
Ultimate Member stores the data from the fields in the User Meta table. In order to display the saved field values in the front end, you can use Wordpress's built-in get_user_meta function:
echo get_user_meta( $user_ID, $key, true );
You could use get_current_user_id() to retrieve the current user's ID. But since you are planning on displaying this on the Ultimate Member Account Page (which admins can access), I recommend you use UM()->user()->target_id instead. This will pull the ID of the user whose profile/account page you are viewing.
Next, all you need to do is figure out the key of the field. Usually, this is all lowercase with underscores. So something like first_name.
Here's what the final code would look like:
$user_ID = UM()->user()->target_id;
echo get_user_meta( $user_ID, 'description', true );

How to change add-to-cart form?

Is there a way to change the WooCommerce add-to-cart form through functions.php?
The goal is to add a checkbox for an additional product. When the checkbox is checked this product will also be added to the cart after a click on the add to cart button.
I am looking for a solution which doesn't rely on javascript.
A better title would be "WooCommerce up-sells as checkboxes".
A lot of research and several strategies to tackle this problem lead me to a solution which I thought was not even possible in the beginning.
The solution is now exactly what I wanted. A non-JavaScript, no-template-override, but a simple and pure addition to functions.php. It works for simple and variable products (and probably with grouped and external products too).
It misses some nice features still. It won't work yet if an up-sell is a variable product. Quantity selection and limiting up-sells per item or order would be nice additions too. Based on the code below adding those features should not be a big deal anymore.
// create the checkbox form fields and add them before the cart button
add_action( 'woocommerce_before_add_to_cart_button', 'action_woocommerce_before_add_to_cart_form', 10, 0 );
function action_woocommerce_before_add_to_cart_form(){
global $woocommerce, $product;
// get the product up-sells
$upsells = $product->get_upsells();
// store the number of up-sells and pass it on to the add-to-cart hook
?>
<input type="hidden" name="upsells_size" value="<?php echo(sizeof($upsells)); ?>">
<div id="wb-upsell-div">
<?php
// iterate through all upsells and add an input field for each
$i = 1;
foreach( $upsells as $value ){
$product_id = $value;
?>
<input id="wb-upsell-checkboxes" type="checkbox" name="upsell_<?php echo($i) ?>" value="<?php echo($product_id); ?>"><?php echo( '' . get_the_title( $product_id ) . "". " ($" . get_post_meta( $product_id, '_regular_price', true) . ")"); ?><br>
<?php
$i++;
}
?>
</div>
<?php
}
// function to add all up-sells, where the checkbox have been checked, to the cart
add_action('woocommerce_add_to_cart', 'custom_add_to_cart', 10, 3);
function custom_add_to_cart() {
global $woocommerce;
// get the number of up-sells to iterate through
$upsell_size = $_POST['upsells_size'];
// iterate through up-sell fields
for ($i=1; $i<=$upsell_size; $i++){
// get the product id of the up-sell
$product_id = $_POST['upsell_' . $i];
$found = false;
//check if product already in cart
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
if ( $_product->id == $product_id )
$found = true;
}
// if product not found, add it
if ( ! $found )
WC()->cart->add_to_cart( $product_id );
} else {
// if no products in cart, add it
WC()->cart->add_to_cart( $product_id );
}
}
}
And here is the CSS for formatting the <div>and the checkboxes. It goes into the style.css file:
#wb-upsell-div {
margin-top: 10px;
margin-bottom: 20px;
}
#wb-upsell-checkboxes{
}
So there's an actual answer to this question, you can add whatever you want inside the add to cart <form> using hooks. For example:
add_action( 'woocommerce_before_add_to_cart_button', 'so_34115452_add_input' );
function so_34115452_add_input(){
echo '<input type="checkbox" name="something"/>' . __( 'Some Checkbox', 'text-domain' );
}

How to add placeholder in buddypress registration ? How to make field description as input placeholder?

How to add placeholder in buddypress registration?
Can we use field description as placeholder for input field?
In register.php under bp-template.These two lines of code make xprofile
$field_type = bp_xprofile_create_field_type( bp_get_the_profile_field_type() );
$field_type->edit_field_html();
How to edit them.Where these lines are connected.How to edit x-profile field.
Or even better yet, combining these two answers. Simply add this to your functions.php:
function wp_add_placeholder($elements){
$elements['placeholder'] = bp_get_the_profile_field_name();
return $elements;
}
add_action('bp_xprofile_field_edit_html_elements','wp_add_placeholder');
There is always a better way to do it then editing the core:
function wp_add_placeholder($elements){
$elements['placeholder'] = 'Placeholder';
return $elements;
}
add_action('bp_xprofile_field_edit_html_elements','wp_add_placeholder');
just struggled with this issue and fastest thing i came up were override classes that handles different fields:
First you need to find class named BP_XProfile_Field_Type_Textbox (for textbox). In bp v2.0.2 it can be found on bp-xprofile/bp-xprofile-class.php:2358. Copy whole class in your functions and rename class name as you desire. let's say i renamed it as CUSTOM_BP_XProfile_Field_Type_Textbox. Inside this class there are function called public function edit_field_html( array $raw_properties = array() ).
Replace:
$html = $this->get_edit_field_html_elements( array_merge(
array(
'type' => 'text',
'value' => bp_get_the_profile_field_edit_value(),
),
$raw_properties
) );
?>
<label for="<?php bp_the_profile_field_input_name(); ?>"><?php bp_the_profile_field_name(); ?> <?php if ( bp_get_the_profile_field_is_required() ) : ?><?php esc_html_e( '(required)', 'buddypress' ); ?><?php endif; ?></label>
<?php do_action( bp_get_the_profile_field_errors_action() ); ?>
<input <?php echo $html; ?>>
<?php
}
With that:
$required = bp_get_the_profile_field_is_required() ? ' ' . esc_html__( '(required)', 'buddypress' ) : '';
$html = $this->get_edit_field_html_elements( array_merge(
array(
'type' => 'text',
'value' => bp_get_the_profile_field_edit_value(),
'placeholder' => bp_get_the_profile_field_name() . $required
),
$raw_properties
) );
?>
<?php do_action( bp_get_the_profile_field_errors_action() ); ?>
<input <?php echo $html; ?>>
<?php
}
Next thing you need to do is override function that hendles field classes. Copy function called bp_xprofile_get_field_types into your theme and rename it. let's say i renamed it as custom_bp_xprofile_get_field_types.
In fields array rename 'textbox' value from BP_XProfile_Field_Type_Textbox to CUSTOM_BP_XProfile_Field_Type_Textbox (the class you created and modified).
Finl thing you need to do is override function that print final result. Copy function called bp_xprofile_create_field_type into your theme and rename it. let's say i renamed it as custom_bp_xprofile_create_field_type.
In this function replace:
$field = bp_xprofile_get_field_types();
with:
$field = custom_bp_xprofile_get_field_types();
to use your modified field output
In register.php use just created new function instead of original buddypresses one so the final result will be:
$field_type = custom_bp_xprofile_create_field_type( bp_get_the_profile_field_type() );
$field_type->edit_field_html();
It is good to copy register.php to YOUR_THEME/buddypress/members/register.php to not lose your changes after updating the bp plugin.
If you like to modify other fields just rename field's class in classes array in custom_bp_xprofile_get_field_types and copy bp's class of that field and rename it as in fields array.
Hope that helps. There might be better way, but i didn't find any.

Wordpress Settings API error

Hi I am trying to creating some custom options for a template I am developing but I seem to be getting an error:
Warning: Illegal string offset 'show_header' in C:\xampp\htdocs\wordpress\wp-content\themes\01MyWork\includes\theme-options.php on line 62
This is the line that seems to be throwing the error:
$html = '<input type="checkbox" id="show_header" name="thanathos_theme_display_options[show_header]" value="1" ' . checked(1, $options['show_header'], false) . '/>';
And this is the entire code:
<?php
function thanatos_theme_menu(){
add_theme_page(
"Thanathos Theme Options",
"Thanathos Theme",
"administrator",
"thanathos_theme_options",
"thanathos_theme_display_callback"
);
}
add_action('admin_menu' , 'thanatos_theme_menu');
function thanathos_theme_display_callback(){
?>
<div class="wrap">
<div id="icon-themes" class="icon32"></div>
<h2>Sandbox Theme Options</h2>
<?php settings_errors(); ?>
<!--Create the form that will be used to render our options-->
<form method="post" action="options.php">
<?php settings_fields('thanathos_theme_display_options'); ?>
<?php do_settings_sections( 'thanathos_theme_display_options' ); ?>
<?php submit_button(); ?>
</form>
</div>
<?php
}
add_action('admin_init' , 'thanatos_initializa_theme_options');
function thanatos_initializa_theme_options(){
if( false == get_option( 'thanathos_theme_display_options' ) ) {
add_option( 'thanathos_theme_display_options' );
}
add_settings_section(
'general_settings_section',
'Thanatos Options',
'thanatos_general_options_callback',
'thanathos_theme_display_options'
);
add_settings_field(
'show_header',
'Header',
'thanathos_field_header_callback',
'thanathos_theme_display_options',
'general_settings_section',
array( // The array of arguments to pass to the callback. In this case, just a description.
'Activate this setting to display the header.'
)
);
register_setting('thanathos_theme_display_options', 'thanathos_theme_display_options');
}
function thanatos_general_options_callback(){
echo 'mergem la mare';
}
function thanathos_field_header_callback($args){
// First, we read the options collection
$options = get_option('thanathos_theme_display_options');
// Next, we update the name attribute to access this element's ID in the context of the display options array
// We also access the show_header element of the options collection in the call to the checked() helper function
$html = '<input type="checkbox" id="show_header" name="thanathos_theme_display_options[show_header]" value="1" ' . checked(1, $options['show_header'], false) . '/>';
// Here, we'll take the first argument of the array and add it to a label next to the checkbox
$html .= '<label for="show_header"> ' . $args[0] . '</label>';
echo $html;
}
?>
Yes, that's the problematic part:
if( false == get_option( 'thanathos_theme_display_options' ) ) {
add_option( 'thanathos_theme_display_options' );
}
That's the initial if statement at the beginning of the thanatos_initializa_theme_options() function.
You can find the solution in the very neat Theme Options API tut at http://wp.tutsplus.com/tutorials/theme-development/the-complete-guide-to-the-wordpress-settings-api-part-4-on-theme-options/#post-684925289
Or, more exactly more exactly in this article's comments section.
I'm pasting the Steve Bondy's smart solution here because for some reason making the page scroll to the appropriate comment doesn't work for me (in Chrome at least).
START quote
(...)One little issue I had - I followed along through reordering the code, up to just before adding the Social options. At that point I discovered that the code was broken. I would get an error like
Warning: Illegal string offset 'show_header' in ...\themes\Sandbox\functions.php
Warning: Illegal string offset 'show_content' in ...\themes\Sandbox\functions.php
Warning: Illegal string offset 'show_footer' in ...\themes\Sandbox\functions.php
It turns out that this error was caused by adding 'sandbox_theme_display_options' to the options table without giving it a value. If you change sandbox_initialize_theme_options as follows it will create and initialize the options, avoiding the error experienced by myself and others.
function sandbox_initialize_theme_options() {
// If the theme options don't exist, create them.
if( false == get_option( 'sandbox_theme_display_options' ) ) {
$options = array("show_header" => TRUE, "show_content" => TRUE, "show_footer" => TRUE);
add_option( 'sandbox_theme_display_options', $options);
} // end if
(...)
If the old code has been run the empty 'sandbox_theme_display_options' value must be deleted from the database first. Alternatively, the following code will also detect this case and correct it.
function sandbox_initialize_theme_options() {
// See if the options exist, and initialize them if they don't
$options = get_option( 'sandbox_theme_display_options' );
if( false == $options or $options == "" ) {
$options = array("show_header" => TRUE, "show_content" => TRUE, "show_footer" => TRUE);
update_option( 'sandbox_theme_display_options', $options);
} // end if
(...)
This checks for non-existant or empty options values and initializes the options using update_option instead of add_option.
EOF quote
You are using name="thanathos_theme_display_options[show_header]" in plain HTML. Probably you want to parse by PHP the string [show_header].
You may have to run:
delete_option('thanathos_theme_display_options');
during 'admin_init' if you think you have old plugin information mucking about in the database and need a fresh start.

Resources