I am writing a Wordpress plugin to create custom Gutenberg blocks. I am using a composer package called Carbon Fields. But when I try to use the package I am getting an Error:
Fatal error: Uncaught Error: Class 'Carbon_Fields\Block' not found in /my-path/my-plugin/my-plugin.php on line 10.
The strange thing is that I can use the Container and Field classes without problems.
Note
I have just included the basic file structure and code so that you can focus on the important things.
File structure
my-plugin
vendor
htmlburger
core
Block.php
Carbon_Fields.php
Container.php
Field.php
my-plugin.php
Code
my-plugin.php
add_action( 'after_setup_theme', 'carbon_fields_init' );
function carbon_fields_init() {
require_once plugin_dir_path( __FILE__ ) . 'vendor/autoload.php';
\Carbon_Fields\Carbon_Fields::boot();
}
use \Carbon_Fields\Block;
use \Carbon_Fields\Filed;
Block::make( __( 'Hero Image' ) )
->add_fields( array(
Field::make( 'text', 'heading', __( 'Block Heading' ) ),
) )
->set_render_callback( function ( $fields, $attributes, $inner_blocks ) {
?>
<div class="block">
<div class="block__heading">
<h1><?php echo esc_html( $fields['heading'] ); ?></h1>
</div><!-- /.block__heading -->
<?php
} );
Block.php
namespace Carbon_Fields;
class Block extends Container {
public static function make() {
return call_user_func_array( array( 'parent', 'make' ), array_merge( array( 'block' ), func_get_args() ) );
}
}
Finally I got the answer from a portuguese tutorial (https://www.youtube.com/watch?v=bKY-7_wR2n0). I had to wrap the block creation in a function and set that function as a callback for the carbon_fields_register_fields action hook.
use Carbon_Fields\Field;
use Carbon_Fields\Block;
add_action( 'after_setup_theme', 'crb_load' );
function crb_load() {
require_once( 'vendor/autoload.php' );
\Carbon_Fields\Carbon_Fields::boot();
}
add_action( 'carbon_fields_register_fields', 'crb_add_test_block' );
function crb_add_test_block() {
Block::make( __( 'My Shiny Gutenberg Block' ) )
->add_fields( array(
Field::make( 'text', 'heading', __( 'Block Heading' ) ),
Field::make( 'rich_text', 'content', __( 'Block Content' ) ),
) )
->set_render_callback( function ( $fields, $attributes, $inner_blocks ) {
?>
<div class="block">
<div class="block__heading">
<h1><?php echo esc_html( $fields['heading'] ); ?></h1>
</div><!-- /.block__heading -->
<div class="block__content">
<?php echo apply_filters( 'the_content', $fields['content'] ); ?>
Reload
</div><!-- /.block__content -->
</div><!-- /.block -->
<?php
} );
}
Related
I'm using the Wordpress Plugin Boilerplate as foundation for my own plugin. In the admin area I intend to display data using the WP_List_Table class of Wordpress. I know I have to create my own child class to access it. Doing so is not the issue, however I get the following error:
Fatal error: Uncaught Error: Call to a member function render_screen_reader_content() on
During my research I discovered a few cases with the same issue (1, 2), but none of the solutions worked in my case.
Using the structure of the boilerplate, I created the child class in a file inside the includes folder:
if ( !class_exists( 'WP_List_Table' ) ) {
require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
}
if ( !class_exists( 'Hedwig_tables' ) ) {
class Hedwig_tables extends WP_List_Table {
private array $hd_columns;
private array $hd_data;
private array $hd_hidden;
private array $hd_sortable;
private array $hd_column_names;
public function __construct() {
//parent::__construct();
}
public function set_column_names(array $column_names) {
$this->hd_column_names = $column_names;
}
public function set_columns(array $columns) {
$this->hd_columns = $columns;
}
public function set_data(array $data) {
$this->hd_data = $data;
}
public function set_hidden(array $hidden) {
$this->hd_hidden = $hidden;
}
public function set_sortable(array $sortable) {
$this->hd_sortable = $sortable;
}
public function prepare_items() {
$this->_column_headers = array($this->hd_columns, $this->hd_hidden, $this->hd_sortable);
$this->items = $this->hd_data;
}
public function column_default( $item, $column_name ): mixed {
if (in_array($column_name, $this->hd_column_names)) {
return $item[ $column_name ];
}
return print_r($item, true);
}
}
}
The file is then loaded in the boilerplate's load_dependencies() function in class-plugin-name.php inside the includes folder.
In the boilerplate's class-plugin-name-admin.php (inside the admin folder) I created a function which generates the admin menu entry.
public function add_hedwig_page() {
$this->plugin_screen_hook_suffix = add_menu_page(
__( 'Hedwig Settings', 'Hedwig' ),
__( 'Hedwig Settings', 'Hedwig' ),
'manage_options',
$this->plugin_name,
array( $this, 'hedwig_admin_display_page' ),
'dashicons-buddicons-activity'
);
$this->plugin_screen_hook_suffix = add_submenu_page(
$this->plugin_name,
__( 'Hedwig Settings', 'Hedwig' ),
__( 'Hedwig Settings', 'Hedwig' ),
'manage_options',
$this->plugin_name,
array( $this, 'hedwig_admin_display_page' )
);
public function hedwig_admin_display_page() {
include_once 'partials/hedwig-admin-display.php';
}
Inside the display.php a function is called which I created inside the class-plugin-name-admin.php which creates the object for the child class of WP_List_Table.
public function get_data() {
$hedwig_list_table = new Hedwig_tables();
$sql = "SELECT id, value FROM y";
$results = $this->wpdb->get_results($sql, ARRAY_A);
if (count($results)<=0) {
?>
<div class="hedwig-msg-error"><?php _e('No data found.','Hedwig');?></div>
<?php
return false;
}
$hedwig_list_table->set_columns(
array(
'id' => __('ID','Hedwig'),
'value' => __('Art','Hedwig')
)
);
$hedwig_list_table->set_column_names(
array(
'id',
'value'
)
);
$hedwig_list_table->set_data($results);
$hedwig_list_table->set_hidden(array());
$hedwig_list_table->set_sortable(array());
$hedwig_list_table->prepare_items();
$hedwig_list_table->display();
return true;
}
Based on my aforementioned research the issue must be somewhere along the line of when the object for the child class is created (see this answer). I tried using add_actions() at different places (on __construct of the admin class, inside the run() function of the plugin-name.php trying to either load it after the menu items are generated or loading the class as a $GLOBALS. Everything I came up with failed. I used to create some smaller plugins without a boilerplate, but in this project I actually want to do the switch to this OOP and get new plugins kickstarted this way.
Update #1
Still got no solution, but I stumbled upon another solution which looked promising. However, using a function when creating the menu item for initialising the child class doesn't work either.
public function add_hedwig_page() {
$this->plugin_screen_hook_suffix = add_menu_page(
__( 'Hedwig Settings', 'Hedwig' ),
__( 'Hedwig Settings', 'Hedwig' ),
'manage_options',
$this->plugin_name,
function() {
$this->hedwig_list_table = new Hedwig_tables();
$this->hedwig_admin_display_page();
},
'dashicons-buddicons-activity'
);
$this->plugin_screen_hook_suffix = add_submenu_page(
$this->plugin_name,
__( 'Hedwig Settings', 'Hedwig' ),
__( 'Hedwig Settings', 'Hedwig' ),
'manage_options',
$this->plugin_name,
function() {
$this->hedwig_list_table = new Hedwig_tables();
$this->hedwig_admin_display_page();
}
);
public function hedwig_admin_display_page() {
include_once 'partials/hedwig-admin-display.php';
}
You shouldn't load this class like your custom classes since this is an extension of a core class and it has many more dependencies to rely on. Don't use add_action or $GLOBALS to initialize this class at all.
If you load it in boilerplate's load_dependencies() function in class-plugin-name.php inside the includes folder it will be instantiated too early and it will not function properly.
Instead, call it only when needed, inside your partials/hedwig-admin-display.php that you will use to output your markup for that page.
Something like this should work in your example:
<?php $table = new Hedwig_tables(); ?>
<h1>Hedwig_tables</h1>
<div class="wrap">
<div id="poststuff">
<div id="post-body" class="metabox-holder">
<div id="post-body-content">
<div class="meta-box-sortables ui-sortable">
<?php $table->prepare_items();?>
<form method="get">
<?php $table->display(); ?>
</form>
</div>
</div>
</div>
<br class="clear">
</div>
</div>
I have the following folder structure for my theme:
theme
inc
theme
functions.php
init.php
functions.php
In inc/theme/functions.php, I'm placing all theme specific functions (i.e. removing taxonomies etc). In theme/functions.php, I have all my core functions.
With my current code, WordPress states "The site is experiencing technical difficulties.". If I delete everything in theme/functions.php, the content loads, but the code in inc/theme/functions.php is not being executed. For example, in inc/theme/functions.php, I have wp_enqueue_style( 'style', get_stylesheet_uri() ); and none of the styles are pulling through.
Can't seem to figure out:
Why theme/functions.php is causing a WordPress error.
Why inc/theme/functions.php is not being executed.
theme/functions.php
<?php
require_once trailingslashit( get_template_directory() ) . 'inc/init.php';
new theme_ThemeFunctions;
class theme_ThemeFunctions {
function __construct() {
load_theme_textdomain( 'theme' );
add_action( 'init', array( $this, 'post_types_taxonomies' ) );
add_action( 'init', array( $this, 'register_menus' ) );
}
public function post_types_taxonomies() {
register_post_type(
'case-studies',
build_post_args(
'case-study', 'Case study', 'Case studies',
array(
'menu_icon' => 'dashicons-book',
'menu_position' => 20,
'has_archive' => true
)
)
);
}
public function register_menus() {
register_nav_menus(
array(
'main' => __( 'Main Menu', 'theme' ),
)
);
}
}
?>
inc/theme/functions.php
<?php
function scriptAndStyles() {
wp_enqueue_style( 'style', get_stylesheet_uri() );
}
add_action( 'wp_enqueue_scripts', 'scriptAndStyles' );
function remove_editor() {
remove_post_type_support('page', 'editor');
}
add_action('admin_init', 'remove_editor');
// Remove featured image option from pages
function remove_thumbnail_box() {
remove_meta_box( 'postimagediv','page', 'side' );
}
add_action('do_meta_boxes', 'remove_thumbnail_box');
// Remove posts type option
function post_remove () {
remove_menu_page('edit.php');
}
add_action('admin_menu', 'post_remove');
?>
inc/init.php
<?php
$include_dir = trailingslashit( get_template_directory() ) . 'inc/';
// Load any custom functions
require_once $include_dir . 'theme/functions.php';
?>
You need use get_template_directory() for require your files.
And use new theme_ThemeFunctions below class itself.
Also use to view errors:
define('WP_DEBUG', 1);
define('WP_DEBUG_DISPLAY', 1);
I have install a new theme in wordpress after uploading but at the time of activation of the theme , getting the error like this:
Parse error: parse error in C:\wamp\www\wordpress\wp-content\themes\realhomes\framework\meta-box\inspiry-meta-box.php on line 11
Please help me through this:
add_action('admin_init',function(){ : is line 11 in my code.
add_action( 'admin_init', function() {
include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
// Meta Box Plugin
if ( is_plugin_active( 'meta-box/meta-box.php' ) ) {
deactivate_plugins( 'meta-box/meta-box.php' );
add_action( 'admin_notices', function () {
?>
<div class="update-nag notice is-dismissible">
<p><strong><?php _e( 'Meta Box plugin has been deactivated!', 'framework' ); ?></strong></p>
<p><?php _e( 'As now its functionality is embedded with in Real Homes theme.', 'framework' ); ?></p>
<p><em><?php _e( 'So, You should completely remove it from your plugins.', 'framework' ); ?></em></p>
</div>
<?php
} );
}
please check your brackets not over in your last line of code..
please add this in last line });
I am trying to write a custom widget to display our teamspeak server info. This is what I have so far:
<?php
/*
Plugin Name: Site Plugin for RealmReborn.co
Description: Site specific code changes for RealmReborn.co
*/
/* Start Adding Functions Below this Line */
// Creating the widget
class wpb_widget extends WP_Widget {
function __construct() {
parent::__construct(
// Base ID of your widget
'wpb_widget',
// Widget name will appear in UI
__('WPBeginner Widget', 'wpb_widget_domain'),
// Widget description
array( 'description' => __( 'Sample widget based on WPBeginner Tutorial', 'wpb_widget_domain' ), )
);
}
// Creating widget front-end
// This is where the action happens
public function widget( $args, $instance ) {
$title = apply_filters( 'widget_title', $instance['title'] );
// before and after widget arguments are defined by themes
echo $args['before_widget'];
if ( ! empty( $title ) )
echo $args['before_title'] . $title . $args['after_title'];
// This is where you run the code and display the output
?>
<iframe src="http://cache.www.gametracker.com/components/html0/?host=tsserver.co:9987&bgColor=333333&fontColor=CCCCCC&titleBgColor=222222&titleColor=FF9900&borderColor=555555&linkColor=FFCC00&borderLinkColor=222222&showMap=0¤tPlayersHeight=220&showCurrPlayers=1&showTopPlayers=0&showBlogs=0&width=265"
frameborder="0" scrolling="no" width="265" height="408"></iframe>
<?php
}
// Widget Backend
public function form( $instance ) {
if ( isset( $instance[ 'title' ] ) ) {
$title = $instance[ 'title' ];
}
else {
$title = __( 'New title', 'wpb_widget_domain' );
}
// Widget admin form
?>
<p>
<label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label>
<input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
</p>
<?php
}
// Updating widget replacing old instances with new
public function update( $new_instance, $old_instance ) {
$instance = array();
$instance['title'] = ( ! empty( $new_instance['title'] ) ) ? strip_tags( $new_instance['title'] ) : '';
return $instance;
}
} // Class wpb_widget ends here
// Register and load the widget
function wpb_load_widget() {
register_widget( 'wpb_widget' );
}
add_action( 'widgets_init', 'wpb_load_widget' );
/* Stop Adding Functions Below this Line */
// Creating the widget
?>
I got this code from a wordpress help site and tried to follow the instructions but I'm doing something wrong.
My site is realmreborn.co and I am trying to display the widget on the right side.
You will notice that the Teamspeak widget is wrapping all of the widgets below it on the right side:
I do not know what I did wrong, I've tried to make adjustments to the code but nothing I seem to do seems to fix it. Did I miss a bracket or not implement something correctly?
Any help would be greatly appreciated!
This is a total guess but maybe you need to do a echo $args['after_widget']; just before the closing brace after the iframe.
I think something wrong with html, check all tags are closed, check for echo $args['after_widget']; - I don't see it in code. It's all markup, some fixed and it would be all right)
In order to apply some changes to the default Recent Posts Widget I have copied and modified it to my needs. It is included and registered in my template's functions.php and when loading the widget-section in the admin panel its constructor is run.
The problem is that it does not appear in the list of available widgets and thus can not be used. The problem also occurs when I try the minimal example from http://www.darrenhoyt.com/2009/12/22/creating-custom-wordpress-widgets/, so I think I missed something important, but don't know what.
Until now my theme is a modified clone of https://wordpress.org/extend/themes/toolbox.
This explains the many occurrences of toolbox_ in the code.
Following are code excerpts from functions.php and
inc/widgets.php. I can provide more code if needed. Please let me
know in the comments.
Code:
<?php
// BEGIN functions.php
if ( ! function_exists( 'toolbox_setup' ) ):
/**
* Sets up theme defaults and registers support for various WordPress features.
*
* Note that this function is hooked into the after_setup_theme hook, which runs
* before the init hook. The init hook is too late for some features, such as indicating
* support post thumbnails.
*
* To override toolbox_setup() in a child theme, add your own toolbox_setup to your child theme's
* functions.php file.
*/
function toolbox_setup() {
// [...]
require( get_template_directory() . '/inc/widgets.php' );
// [...]
}
endif; // toolbox_setup
/**
* Tell WordPress to run toolbox_setup() when the 'after_setup_theme' hook is run.
*/
add_action( 'after_setup_theme', 'toolbox_setup' );
/**
* Register widgetized area and update sidebar with default widgets
*/
function toolbox_widgets_init() {
register_widget("My_Widget_Recent_Posts");
register_sidebar( array(
'name' => __( 'Sidebar 1', 'toolbox' ),
'id' => 'sidebar-1',
'before_widget' => '<aside id="%1$s" class="widget %2$s">',
'after_widget' => "</aside>",
'before_title' => '<h1 class="widget-title small">',
'after_title' => '</h1>',
) );
}
add_action( 'init', 'toolbox_widgets_init' );
// END functions.php
?>
<?php
// BEGIN inc/widgets.php
/**
* Recent_Posts widget class
*
* #since 2.8.0
*/
class My_Widget_Recent_Posts extends WP_Widget {
function __construct() {
$widget_ops = array('classname' => 'my_widget_recent_entries', 'description' => __( "The most recent posts on your site (modified)") );
parent::__construct('my-recent-posts', __('Recent Posts (modified)'), $widget_ops);
$this->alt_option_name = 'my_widget_recent_entries';
add_action( 'save_post', array(&$this, 'flush_widget_cache') );
add_action( 'deleted_post', array(&$this, 'flush_widget_cache') );
add_action( 'switch_theme', array(&$this, 'flush_widget_cache') );
}
function widget($args, $instance) {
$cache = wp_cache_get('my_widget_recent_posts', 'widget');
if ( !is_array($cache) )
$cache = array();
if ( ! isset( $args['widget_id'] ) )
$args['widget_id'] = $this->id;
if ( isset( $cache[ $args['widget_id'] ] ) ) {
echo $cache[ $args['widget_id'] ];
return;
}
ob_start();
extract($args);
$title = apply_filters('widget_title', empty($instance['title']) ? __('Recent Posts') : $instance['title'], $instance, $this->id_base);
if ( empty( $instance['number'] ) || ! $number = absint( $instance['number'] ) )
$number = 10;
$r = new WP_Query( apply_filters( 'widget_posts_args', array( 'posts_per_page' => $number, 'no_found_rows' => true, 'post_status' => 'publish', 'ignore_sticky_posts' => true ) ) );
if ($r->have_posts()) :
?>
<?php echo $before_widget; ?>
<?php if ( $title ) echo $before_title . $title . $after_title; ?>
<ul>
<?php while ($r->have_posts()) : $r->the_post(); ?>
<li><?php if ( get_the_title() ) the_title(); else the_ID(); ?>
<?php the_content(); ?></li>
<?php endwhile; ?>
</ul>
<?php echo $after_widget; ?>
<?php
// Reset the global $the_post as this query will have stomped on it
wp_reset_postdata();
endif;
$cache[$args['widget_id']] = ob_get_flush();
wp_cache_set('my_widget_recent_posts', $cache, 'widget');
}
function update( $new_instance, $old_instance ) {
$instance = $old_instance;
$instance['title'] = strip_tags($new_instance['title']);
$instance['number'] = (int) $new_instance['number'];
$this->flush_widget_cache();
$alloptions = wp_cache_get( 'alloptions', 'options' );
if ( isset($alloptions['my_widget_recent_entries']) )
delete_option('my_widget_recent_entries');
return $instance;
}
function flush_widget_cache() {
wp_cache_delete('my_widget_recent_posts', 'widget');
}
function form( $instance ) {
$title = isset($instance['title']) ? esc_attr($instance['title']) : '';
$number = isset($instance['number']) ? absint($instance['number']) : 5;
?>
<p><label for="<?php echo $this->get_field_id('title'); ?>"><?php _e('Title:'); ?></label>
<input class="widefat" id="<?php echo $this->get_field_id('title'); ?>" name="<?php echo $this->get_field_name('title'); ?>" type="text" value="<?php echo $title; ?>" /></p>
<p><label for="<?php echo $this->get_field_id('number'); ?>"><?php _e('Number of posts to show:'); ?></label>
<input id="<?php echo $this->get_field_id('number'); ?>" name="<?php echo $this->get_field_name('number'); ?>" type="text" value="<?php echo $number; ?>" size="3" /></p>
<?php
}
}
// END inc/widgets.php
?>
You have to hook register_widget to the widgets_init action, NOT to the init action. In your case it is something like this:
<?php
add_action( 'widgets_init', 'toolbox_widgets_init' );
function toolbox_widgets_init() {
register_widget("My_Widget_Recent_Posts");
}
?>
Ok lets do some debugging test:
echo "include widget file";
require( get_template_directory() . '/inc/widgets.php' );
Also:
echo "register widget";
register_widget("My_Widget_Recent_Posts");
Then:
function __construct() {
echo "widget class";
Check your HTML to see what parts echoed and what parts didn't you may get Headers already sent error, which you can ignore. Hopefully we can see which part is not being triggered to narrow down where the problem is.
Registering the widget as follows solved the problem for me, but is not very elegant in my opinion. Right behind the class definition I inserted the line
<?php
add_action( 'widgets_init', create_function('',
'return register_widget("My_Widget_Recent_Posts");') );
?>
And removed the registration from toolbox_widgets_init() where I think it belongs.
In case somebody can explain why it has to be this way round or what I could change so that it would work like I intended to do it before I would highly appreciate comments. But for now the actual problem is solved.