I´m woring on a costum meta-box for wordpress. Trouble is wordpress only seems to retain/save some of the values I enter in the fields.. I can´t really find a pattern either.. so here's the code:
<?php
function add_products_metaboxes() {
add_meta_box('sra_product_info', 'Product Information', 'sra_products_info', 'product', 'side', 'default');
}
// The Productinfo Metabox
function sra_products_info() {
//get access to the post object
global $post;
// Noncename needed to verify where the data originated
echo '<input type="hidden" name="productmeta_noncename" id="productmeta_noncename" value="' .
wp_create_nonce( plugin_basename(__FILE__) ) . '" />';
// Get the data from the field if its already been entered
$name = get_post_meta($post->ID, '_name', true);
$price = get_post_meta($post->ID, '_price', true);
$includes = get_post_meta($post->ID, '_includes', true);
$supports = get_post_meta($post->ID, '_supports', true);
$version = get_post_meta($post->ID, '_version' , true);
$extrainfo = get_post_meta($post->ID, '_extrainfo', true);
// Echo out the form
echo '<form>';
echo '<label for="_name">Name</label>' . '<input type="text" name="_name" value="' . $name . '"/>';
echo '<label for="_price">Price</label>' . '<input type="text" name="_price" value="' . $price . '"/>';
echo '<label for="_includes">Includes</label> <textarea name="_includes" rows="4" cols="10">' . $includes . '</textarea>';
echo '<label for="_supports">Supports</label> <input type="text" name="_supports" value="' . $supports . '"/>';
echo '<label for="_version">Version</label>' . '<input type="text" name="_version" value="' . $version . '"/>';
echo '<label for="_extrainfo">Extras</label> <textarea name="_extrainfo" rows="4" cols="10">' . $extrainfo . '</textarea>';
echo '</form>';
}
// Save the Metabox Data
function sra_save_product_meta($post_id, $post) {
// verify this came from the our screen and with proper authorization,
// because save_post can be triggered at other times
if ( !wp_verify_nonce( $_POST['productmeta_noncename'], plugin_basename(__FILE__) )) {
return $post->ID;
}
// Is the user allowed to edit the post or page?
if ( !current_user_can( 'edit_post', $post->ID ))
return $post->ID;
// OK, we're authenticated: we need to find and save the data
// check if the field exists in the posts array - if it does, then put cintent in $product_meta.
// this code needs to be refactored!
if (isset($_POST['_name'])) {
$product_meta['_name'] = $_POST['_name'];
}
if (isset($_POST['_price'])) {
$product_meta['_price'] = $_POST['_price'];
}
if (isset($_POST['_includes'])) {
$product_meta['_includes'] = $_POST['_includes'];
}
if (isset($_POST['_supports'])) {
$product_meta['_supports'] = $_POST['_supports'];
}
if (isset($_POST['_version'])) {
$product_meta['_version'] = $_POST['_version'];
}
if (isset($_POST['_extrainfo'])) {
$product_meta['_extrainfo'] = $_POST['_extrainfo'];
}
// Add values of $prpduct_meta as custom fields
foreach ($product_meta as $key => $value) { // Cycle through the $product_meta array!
if( $post->post_type == 'revision' ) return; // Don't store custom data twice
$value = implode(',', (array)$value); // If $value is an array, make it a CSL (unlikely)
if(get_post_meta($post->ID, $key, FALSE)) { // If the custom field already has a value
update_post_meta($post->ID, $key, $value);
} else { // If the custom field doesn't have a value
add_post_meta($post->ID, $key, $value);
}
if(!$value) delete_post_meta($post->ID, $key); // Delete if blank
}
}
add_action('save_post', 'sra_save_product_meta', 1, 2); // save the custom fields
Do you see any obvious mistakes? I think I've become a bit blind to my own mistakes in this code..
In general, I would advise to use prefixes for your field names. Values like _name have too much chances getting in conflict with other values with the same name elsewhere. Use things like _product_name, etc. Can you try that? If your code works with pages, it should have an impact.
And why not adding the 4th parameter with 'true' to add_post_meta() and previous value for update_post_meta()? See WordPress codex about these functions: http://codex.wordpress.org/Function_Reference/add_post_meta. So it would go:
if(get_post_meta($post->ID, $key, FALSE)) { // If the custom field already has a value
update_post_meta($post->ID, $key, $value, /***OLDVALUE***/ ); // See http://codex.wordpress.org/Function_Reference/update_post_meta for old value
} else { // If the custom field doesn't have a value
add_post_meta($post->ID, $key, $value, true);
}
I think it is clear you have a conflict with a metakey with the same name. Not in pages, but in posts. So try to use this 4th parameter to ensure you are referencing a unique key (and my advice, still, use clear prefixes to differenciate from anything else, be it plugins, core, etc.)
Related
Here's the long and short:
I'm looking to add a global controller for post meta tags in Wordpress. I already have an established Wordpress custom admin page within our theme. I also already have added meta boxes to individual pages which store key value pairs in a more polished fashion. I have been able to call all key value pairs in a foreach loop so that they display in the administrative page. I'm having difficulty getting the input from these values on the admin panel page to properly update when the submit_button(); is clicked. I've gotten the update_post_meta function to outright erase the previous values but I have not been able to get the update_post_meta function to properly pass the $_POST value into the value at the database.
I know that the following code is not working as it's halfway through two attempts. Any help is appreciated:
$pages = get_pages();
foreach ( $pages as $page ) {
$post_id = $page->ID;
$meta = get_post_meta($post_id);
/* $value1 = get_post_meta( $post_id, 'value1', true );
$value2 = get_post_meta( $post_id, 'value2', true ); */
echo '<h3>' . $page->post_title . '</h3>';
echo '<label for="options_value1_' . $post_id . '">Value 1:</label>';
echo '<input type="text" id="options_value1_' . $post_id . '" name="options_value1" value="' . $meta['value1'][0] . '" size="25" /><br />';
echo '<label for="options_value2_' . $post_id . '">Value 2:</label>';
echo '<input type="text" id="options_value2_' . $post_id . '" name="options_value2" value="' . $meta['value2'][0] . '" size="25" /><hr />';
}
foreach ( $pages as $page ) {
update_post_meta($post_id, 'value1', $_POST('value1');
update_post_meta($post_id, 'value2', $_POST('value2');
}
Hope this helps:
$post_id is defined in the first loop, but not in the second loop.
Also you have a syntax error in the second loop. You're not closing the parenthesis of update_post_meta() and you're not using braces for $_POST. It should be $_POST[], not $_POST().
Also, the POST parameter you're looking for is options_value1 and options_value 2, because those are the names of the fields from the first loop.
Second loop should go like this:
foreach ( $pages as $page ) {
$post_id = $page->ID;
update_post_meta($post_id, 'value1', $_POST['options_value1']);
update_post_meta($post_id, 'value2', $_POST['options_value2']);
}
Hope it helps.
I'm trying to save a post meta data on the database, using custom post type. The problem is I can't store the meta_key, the only one storing is the meta-value. I have two textbox there, one for event date and one for event name.. I'm trying to store the date on the meta key and the event on the value. the problem is that it only store on the meta value, and i'm not sure on the meta-key. Also how do you store multiple meta-keys and meta-values on the same post? Any idea which part of this syntax is wrong? thanks :)
function add_calendar_metaboxes() {
add_meta_box('wpt_calendar_location', 'Event Date', 'wpt_calendar_location', 'calendar_holiday', 'normal', 'default');
}
// The Event Location Metabox
function wpt_calendar_location() {
global $post;
echo "<form method=\"POST\">";
// Noncename needed to verify where the data originated
echo '<input type="hidden" name="eventmeta_noncename" id="eventmeta_noncename" value="' .
wp_create_nonce( plugin_basename(__FILE__) ) . '" />';
// Get the location data if its already been entered
$event_name = get_post_meta($post->ID, '_event_name', true);
$event_date = get_post_meta($post->ID, '_event_date', true);
echo '<label>Event Date</label><input type="text" name="_event_date" value="' . $event_date . '" />';
echo '<label>Event Name</label><input type="text" name="_event_name" value="' . $event_name . '" />';
echo '<input type="submit" name="Submit">';
echo '</form>';
}
// Save the Metabox Data
function wpt_save_events_meta($post_id, $post) {
if ( !wp_verify_nonce( $_POST['eventmeta_noncename'], plugin_basename(__FILE__) )) {
return $post->ID;
}
if ( !current_user_can( 'edit_post', $post->ID ))
return $post->ID;
$events_meta[] = array($_POST['_event_date'] => $_POST['_event_name']);
foreach ($events_meta as $key => $value)
{
if( $post->post_type == 'revision' ) return;
$value = implode(',', (array)$value);
if(get_post_meta($post->ID, $key, FALSE))
{
update_post_meta($post->ID, $key, $value);
}
else
{
add_post_meta($post->ID, $key, $value);
}
if(!$value) delete_post_meta($post->ID, $key);
}
}
add_action('save_post', 'wpt_save_events_meta', 1, 2);
Why do you treat it like array ??
Try this simple function first
function wpt_save_events_meta() {
global $post;
update_post_meta($post->ID, "_event_date", $_POST["_event_date"]);
update_post_meta($post->ID, "_event_name", $_POST["_event_name"]);
}
If this is working for you, then you can put all your validation stuff .
( IF you still find a problem, try $post_id instead of $post->ID )
EDIT I after comment
When you do update_post_meta($post->ID, $_POST["_event_date"], $_POST["_event_name"] );
You are actually saving the _event_date as KEY and the _event_name as VALUE.
Unless you specifically know what you are doing - this is wrong
The point is to save 2 fields.
One named "_event_date" to hold the dates
One named "_event_name" to hold the names
In the case you wrote in the comments you will have something like ( I do not know the real data structure ..)
( post_id=1 ) 2014.03.04 => my_event_name
( post_id=2 ) 2014.03.05 => my_event_name2
( post_id=3 ) 2014.03.06 => my_event_name3
In the correct code you will have
( post_id=1 ) _event_date => 2014.03.04
_event_name => my_event_name
( post_id=2 ) _event_date => 2014.03.05
_event_name => my_event_name2
( post_id=3 ) _event_date => 2014.03.06
_event_name => my_event_name3
You can see that in the first code , it will be really hard to actually use the data in a search for example as in the second code - it has a structure .
You should treat each form field ( meatbox field ) as an independent set of key % value - each by it´s own.. Just try the code posted above .
I'm debugging my wordpress theme and I keep getting this notification in my functions.php file:
Undefined variable: post in (myfolders)/functions.php on line 961
This is line 961
echo apply_filters('the_content', get_post_meta($post->ID, 'mpc_projects_description', true));
The code is for a custom tinymce for my custom post type(Portfolio) project description.
What is causing variable to be undefined? Here is the code in it's entirety:
add_action( 'add_meta_boxes', 'mpc_add_description_meta_box' );
// Add the projects Meta Box
function mpc_add_description_meta_box() {
add_meta_box('mpc_add_description_meta_box', 'Project Description', 'mpc_add_description_meta_box_callback', 'portfolio', 'normal', 'high');
}
// the description output
function mpc_add_description_meta_box_callback() {
global $post;
// Noncename needed to verify where the data originated
echo '<input type="hidden" name="mpc_projects_description_noncename" id="mpc_projects_description_noncename" value="'.wp_create_nonce(plugin_basename(__FILE__)).'" />';
// Get the location data if its already been entered
$input = get_post_meta($post->ID, 'mpc_projects_description', true);
// echo '<input type="text" name="mpc_projects_description" value="' . $input . '" " />';
wp_editor($input, 'mpc_projects_description', array('textarea_name' => 'mpc_projects_description', 'editor_css' => '<style>#wp-mpc_projects_description-editor-container{background-color:white;style="width:100%;}</style>'));
echo '<table id="post-status-info" cellspacing="0"><tbody><tr><td id="wp-word-count">Word count: <span class="word-count_2">';
$words = strip_tags($input);
$count = str_word_count($words, 0);
echo $count;
echo '</span></td>
<td class="autosave-info">
<span class="autosave-message"> </span>
</td>
</tr></tbody></table>';
}
//save the data
add_action('save_post', 'mpc_save_description_meta', 1, 2); // save the custom fields
function mpc_save_description_meta($post_id, $post) {
// verify this came from the our screen and with proper authorization,
// because save_post can be triggered at other times
if ( !wp_verify_nonce( $_POST['mpc_projects_description_noncename'], plugin_basename(__FILE__) )) {
return $post->ID;
}
// Is the user allowed to edit the post or page?
if ( !current_user_can( 'edit_post', $post->ID ))
return $post->ID;
// OK, we're authenticated: we need to find and save the data
// We'll put it into an array to make it easier to loop though.
$mpc_description_meta['mpc_projects_description'] = $_POST['mpc_projects_description'];
// Add values of $mpc_description_meta as custom fields
foreach ($mpc_description_meta as $key => $value) { // Cycle through the $mpc_description_meta array!
if( $post->post_type == 'revision' ) return; // Don't store custom data twice
$value = implode(',', (array)$value); // If $value is an array, make it a CSV (unlikely)
if(get_post_meta($post->ID, $key, FALSE)) { // If the custom field already has a value
update_post_meta($post->ID, $key, $value);
} else { // If the custom field doesn't have a value
add_post_meta($post->ID, $key, $value);
}
if(!$value) delete_post_meta($post->ID, $key); // Delete if blank
}
}
echo apply_filters('the_content', get_post_meta($post->ID, 'mpc_projects_description', true));
The post global is available inside the functions but you haven't set it outside.
Change:
echo apply_filters('the_content', get_post_meta($post->ID, 'mpc_projects_description', true));
To:
global $post;
echo apply_filters('the_content', get_post_meta($post->ID, 'mpc_projects_description', true));
This could also depend on where you use this code. If you fire the code before the post object is set then you'll get this error.
If it's just in functions.php then change it to:
function wpse_post_test() {
global $post;
echo apply_filters('the_content', get_post_meta($post->ID, 'mpc_projects_description', true));
}
add_action( 'wp', 'wpse_post_test' );
After reviewing your question again it appears you are attempting to output this inside functions.php. What are you trying to achieve?
So I've been playing around with some custom metaboxes and following tutorials etc - I've got to the point where the top metabox field is saving correctly but the second one (width) doesn't save - is there anything obvious I'm doing wrong here? I've tried separating the second field's save in to a separate function to test but that also did not work.
add_action( 'add_meta_boxes', 'youtube_metaboxes' );
function youtube_metaboxes() {
add_meta_box('wpt_youtube', 'youtube URL', 'wpt_youtube', 'product', 'side', 'default');
}
function wpt_youtube() {
global $post;
echo '<input type="hidden" name="youtubemeta_noncename" id="youtubemeta_noncename" value="' .
wp_create_nonce( plugin_basename(__FILE__) ) . '" />';
$addyoutube = get_post_meta($post->ID, '_youtube', true);
echo '<input type="text" name="_youtube" value="' . $addyoutube . '" class="widefat" />';
$youtubeWidth = get_post_meta($post->ID, '_width', true);
echo 'Width: <br /><input type="text" name="_width" value="' . $youtubeWidth . '" class="widefat" />';
}
// Save the Metabox Data
function wpt_save_youtube_meta($post_id, $post) {
if ( !wp_verify_nonce( $_POST['youtubemeta_noncename'], plugin_basename(__FILE__) )) {
return $post->ID;
}
if ( !current_user_can( 'edit_post', $post->ID ))
return $post->ID;
$addyoutube_meta['_youtube'] = $_POST['_youtube'];
foreach ($addyoutube_meta as $key => $value) {
if( $post->post_type == 'revision' ) return;
$value = implode(',', (array)$value); // If $value is an array, make it a CSV (unlikely)
if(get_post_meta($post->ID, $key, FALSE)) {
update_post_meta($post->ID, $key, $value);
} else {
add_post_meta($post->ID, $key, $value);
}
if(!$value) delete_post_meta($post->ID, $key);
}
$addWidth_meta['_width'] = $_POST['_width'];
foreach ($addWidth_meta as $key => $value) {
if( $post->post_type == 'revision' ) return;
$value = implode(',', (array)$value); // If $value is an array, make it a CSV (unlikely)
if(get_post_meta($post->ID, $key, FALSE)) {
update_post_meta($post->ID, $key, $value);
} else {
add_post_meta($post->ID, $key, $value);
}
if(!$value) delete_post_meta($post->ID, $key);
}
}
add_action('save_post', 'wpt_save_youtube_meta', 1, 2); // save the custom fields
I had a look through this article:
http://wp.tutsplus.com/tutorials/reusable-custom-meta-boxes-part-1-intro-and-basic-fields/
Seems like a much better way of creating custom meta boxes.
I am working on some custom post types. I finished the first one and realized that the metabox code I was using could be re-used by other, future, custom post types (as well as pages, posts, etc). So I put the class in its own php file and included it from the functions.php file of my child theme. I've never used classes before, but I thought I could then call that class from wherever I wanted since that is what I do for functions. instead I called it from my post-types.php file and got the following error:
"Fatal error: Class 'My_meta_box' not found in D:\helga\xampp\htdocs\plagueround\wp-content\themes\plagueround_new2\functions\post-types.php on line 73"
In the functions.php of my child theme I've included the file where my class lives:
define('CHILDTHEME_DIRECTORY', get_stylesheet_directory() . '/');
// Meta Box Class - makes meta boxes
require_once(CHILDTHEME_DIRECTORY . 'functions/meta_box_class.php');
here's my class
//My Meta Box CLASS
//from: http://www.deluxeblogtips.com/2010/05/howto-meta-box-wordpress.html
class My_meta_box {
protected $_meta_box;
// create meta box based on given data
function __construct($meta_box) {
$this->_meta_box = $meta_box;
add_action('admin_menu', array(&$this, 'add'));
add_action('save_post', array(&$this, 'save'));
}
/// Add meta box for multiple post types
function add() {
foreach ($this->_meta_box['pages'] as $page) {
add_meta_box($this->_meta_box['id'], $this->_meta_box['title'], array(&$this, 'show'), $page, $this->_meta_box['context'], $this->_meta_box['priority']);
}
}
// Callback function to show fields in meta box
function show() {
global $post;
// Use nonce for verification
echo '<input type="hidden" name="mytheme_meta_box_nonce" value="', wp_create_nonce(basename(__FILE__)), '" />';
echo '<table class="form-table">';
foreach ($this->_meta_box['fields'] as $field) {
// get current post meta data
$meta = get_post_meta($post->ID, $field['id'], true);
echo '<tr>',
'<th style="width:20%"><label for="', $field['id'], '">', $field['name'], '</label></th>',
'<td>';
switch ($field['type']) {
case 'text':
echo '<input type="text" name="', $field['id'], '" id="', $field['id'], '" value="', $meta ? $meta : $field['std'], '" size="30" style="width:97%" />',
'<br />', $field['desc'];
break;
case 'textarea':
echo '<textarea name="', $field['id'], '" id="', $field['id'], '" cols="60" rows="4" style="width:97%">', $meta ? $meta : $field['std'], '</textarea>',
'<br />', $field['desc'];
break;
case 'select':
echo '<select name="', $field['id'], '" id="', $field['id'], '">';
foreach ($field['options'] as $option) {
echo '<option', $meta == $option ? ' selected="selected"' : '', '>', $option, '</option>';
}
echo '</select>';
break;
case 'select2': //for when value and display text don't match
//$options array must be multidimensional with both the 'value' and the 'text' for each option
//for example = array( array(text=>text1,value=>value1),array(text=>text2,value=>value2))
//then in <option> value = $option['value'] and text = $option['text']
echo '<select name="', $field['id'], '" id="', $field['id'], '">';
foreach ($field['options'] as $option) {
echo '<option value="',$option['value'],'"', $meta == $option['value'] ? ' selected="selected"' : '', '>', $option['desc'], '</option>';
}
echo '</select>';
break;
case 'radio':
foreach ($field['options'] as $option) {
echo '<input type="radio" name="', $field['id'], '" value="', $option['value'], '"', $meta == $option['value'] ? ' checked="checked"' : '', ' />', $option['name'];
}
break;
case 'checkbox':
echo '<input type="checkbox" name="', $field['id'], '" id="', $field['id'], '"', $meta ? ' checked="checked"' : '', ' />';
break;
}
echo '<td>',
'</tr>';
}
echo '</table>';
}
// Save data from meta box
function save($post_id) {
// verify nonce
if (!wp_verify_nonce($_POST['mytheme_meta_box_nonce'], basename(__FILE__))) {
return $post_id;
}
// check autosave
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
return $post_id;
}
// check permissions
if ('page' == $_POST['post_type']) {
if (!current_user_can('edit_page', $post_id)) {
return $post_id;
}
} elseif (!current_user_can('edit_post', $post_id)) {
return $post_id;
}
foreach ($this->_meta_box['fields'] as $field) {
$old = get_post_meta($post_id, $field['id'], true);
$new = $_POST[$field['id']];
if ($new && $new != $old) {
update_post_meta($post_id, $field['id'], $new);
} elseif ('' == $new && $old) {
delete_post_meta($post_id, $field['id'], $old);
}
}
}
}
then in my post-types.php file where i am defining some meta boxes for a custom post type i have:
$prefix = '_events_';
$post_id = $_GET['post'] ? $_GET['post'] : $_POST['post_ID'] ;
$events_meta_box = array(
'id' => 'wpt_events',
'title' => 'Events Options',
'pages' => array('events'), // multiple post types possible
'context' => 'side',
'priority' => 'low',
'fields' => array(
array(
'name' => 'Headline',
'desc' => 'Enter the heading you\'d like to appear before the video',
'id' => $prefix . 'heading1',
'type' => 'text',
'std' => ''
),
)
);
$my_box = new My_meta_box($events_meta_box);
So- how do I reference classes from other files?
EDITS/CONTINUATION
simply moving the require_once statement to the post-types.php (the one trying to call the class) seemed to work. but for the heck of it i tried the autoload script
function __autoload($className)
{
require(CHILDTHEME_DIRECTORY .'functions/class_' . $className . '.php') ;
}
where I renamed my class file to class_My_meta_box.php and My_meta_box is the class name
which results in the following errors:
Warning: require(D:\helga\xampp\htdocs\plagueround/wp-content/themes/plagueround_new/functions/class_WP_User_Search.php) [function.require]: failed to open stream: No such file or directory in D:\helga\xampp\htdocs\plagueround\wp-content\themes\plagueround_new\functions.php on line 66
Fatal error: require() [function.require]: Failed opening required 'D:\helga\xampp\htdocs\plagueround/wp-content/themes/plagueround_new/functions/class_WP_User_Search.php' (include_path='.;D:\helga\xampp\php\PEAR') in D:\helga\xampp\htdocs\plagueround\wp-content\themes\plagueround_new\functions.php on line 66
i don't totally understand what it says, BUT i think it is trying to autoload some default wordpress class... and is looking in my directory where it obviously doesn't exist.
Actually, it doesnt matter if the file contains a class or something else. For PHP being able to execute code from other files, PHP has to know where the file is, so it's your regular require or include.
Example:
// foo.class.php
class Foo {}
// main.php
require_once '/path/to/foo.class.php';
$foo = new Foo;
When using classes, it actually makes sense to use a Naming Convention and use an Autoloader, so PHP will try to include the class on it's own, without you having to require or include it.
You can use the constant WP_PLUGIN_DIR to describe the path.
// Foo.class.php
class Foo {}
// main.php
require_once WP_PLUGIN_DIR.'/Foo.class.php';
$foo = new Foo;
Just use the absolute path:
require_once("fullpath/functions/meta_box_class.php") or die ("Could Not load file");
It looks like meta_box_class.php isn't being included. To find out what files have been included at any point, try:
var_dump(get_included_files());