I have many plugins that I wrote for WordPress, and now I want to adapt them to MU.
What are the considerations / best practices / workflow / functions / pitfalls that I have to follow / avoid / adapt in order to 'upgrade' my plugins to support also Multisite installations?
For example, but not limited to:
Enqueue scripts/register
Incuding files (php, images)
Paths for custom files uploads
$wpdb
Activation, uninstall, deactivation
Handling of admin specific pages
In the Codex, there are sometimes remarks about Multisite in single function description, but I did not find any one-stop page that address this subject.
As for enqueuing and including, things go as normal. Plugin path and URL are the same.
I never dealt with anything related to upload paths in Multisite and I guess normally WP takes care of this.
$wpdb
There is a commonly used snippet to iterate through all blogs:
global $wpdb;
$blogs = $wpdb->get_results("
SELECT blog_id
FROM {$wpdb->blogs}
WHERE site_id = '{$wpdb->siteid}'
AND spam = '0'
AND deleted = '0'
AND archived = '0'
");
$original_blog_id = get_current_blog_id();
foreach ( $blogs as $blog_id )
{
switch_to_blog( $blog_id->blog_id );
// do something in the blog, like:
// update_option()
}
switch_to_blog( $original_blog_id );
You may find examples where restore_current_blog() is used instead of switch_to_blog( $original_blog_id ). But here's why switch is more reliable: restore_current_blog() vs switch_to_blog().
$blog_id
Execute some function or hook according to the blog ID:
global $blog_id;
if( $blog_id != 3 )
add_image_size( 'category-thumb', 300, 9999 ); //300 pixels wide (and unlimited height)
Or maybe:
if(
'child.multisite.com' === $_SERVER['SERVER_NAME']
||
'domain-mapped-child.com' === $_SERVER['SERVER_NAME']
)
{
// do_something();
}
Install - Network Activation only
Using the plugin header Network: true (see: Sample Plugin) will only display the plugin in the page /wp-admin/network/plugins.php. With this header in place, we can use the following to block certain actions meant to happen if the plugin is Network only.
function my_plugin_block_something()
{
$plugin = plugin_basename( __FILE__ );
if( !is_network_only_plugin( $plugin ) )
wp_die(
'Sorry, this action is meant for Network only',
'Network only',
array(
'response' => 500,
'back_link' => true
)
);
}
Uninstall
For (De)Activation, it depends on each plugin. But, for Uninstalling, this is the code I use in the file uninstall.php:
<?php
/**
* Uninstall plugin - Single and Multisite
* Source: https://wordpress.stackexchange.com/q/80350/12615
*/
// Make sure that we are uninstalling
if ( !defined( 'WP_UNINSTALL_PLUGIN' ) )
exit();
// Leave no trail
$option_name = 'HardCodedOptionName';
if ( !is_multisite() )
{
delete_option( $option_name );
}
else
{
global $wpdb;
$blog_ids = $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
$original_blog_id = get_current_blog_id();
foreach ( $blog_ids as $blog_id )
{
switch_to_blog( $blog_id );
delete_option( $option_name );
}
switch_to_blog( $original_blog_id );
}
Admin Pages
1) Adding an admin page
To add an administration menu we check if is_multisite() and modify the hook accordingly:
$hook = is_multisite() ? 'network_' : '';
add_action( "{$hook}admin_menu", 'unique_prefix_function_callback' );
2) Check for Multisite dashboard and modify the admin URL:
// Check for MS dashboard
if( is_network_admin() )
$url = network_admin_url( 'plugins.php' );
else
$url = admin_url( 'plugins.php' );
3) Workaround to show interface elements only in main site
Without creating a Network Admin Menu (action hook network_admin_menu), it is possible to show some part of the plugin only in the main site.
I started to include some Multisite functionality in my biggest plugin and did the following to restrict one part of the plugin options to the main site. Meaning, if the plugin is activated in a sub site, the option won't show up.
$this->multisite = is_multisite()
? ( is_super_admin() && is_main_site() ) // must meet this 2 conditions to be "our multisite"
: false;
Looking at this again, maybe it can be simply: is_multisite() && is_super_admin() && is_main_site(). Note that the last two return true in single sites.
And then:
if( $this->multisite )
echo "Something only for the main site, i.e.: Super Admin!";
4) Collection of useful hooks and functions.
Hooks: network_admin_menu, wpmu_new_blog, signup_blogform, wpmu_blogs_columns, manage_sites_custom_column, manage_blogs_custom_column, wp_dashboard_setup, network_admin_notices, site_option_active_sitewide_plugins, {$hook}admin_menu
Functions: is_multisite, is_super_admin is_main_site, get_blogs_of_user, update_blog_option, is_network_admin, network_admin_url, is_network_only_plugin
PS: I rather link to WordPress Answers than to the Codex, as there'll be more examples of working code.
Sample plugin
I've just rolled a Multisite plugin, Network Deactivated but Active Elsewhere, and made a non-working resumed annotated version bellow (see GitHub for the finished full working version). The finished plugin is purely functional, there's no settings interface.
Note that the plugin header has Network: true. It prevents the plugin from showing in child sites.
<?php
/**
* Plugin Name: Network Deactivated but Active Elsewhere
* Network: true
*/
/**
* Start the plugin only if in Admin side and if site is Multisite
*/
if( is_admin() && is_multisite() )
{
add_action(
'plugins_loaded',
array ( B5F_Blog_Active_Plugins_Multisite::get_instance(), 'plugin_setup' )
);
}
/**
* Based on Plugin Class Demo - https://gist.github.com/toscho/3804204
*/
class B5F_Blog_Active_Plugins_Multisite
{
protected static $instance = NULL;
public $blogs = array();
public $plugin_url = '';
public $plugin_path = '';
public static function get_instance()
{
NULL === self::$instance and self::$instance = new self;
return self::$instance;
}
/**
* Plugin URL and Path work as normal
*/
public function plugin_setup()
{
$this->plugin_url = plugins_url( '/', __FILE__ );
$this->plugin_path = plugin_dir_path( __FILE__ );
add_action(
'load-plugins.php',
array( $this, 'load_blogs' )
);
}
public function __construct() {}
public function load_blogs()
{
/**
* Using "is_network" property from $current_screen global variable.
* Run only in /wp-admin/network/plugins.php
*/
global $current_screen;
if( !$current_screen->is_network )
return;
/**
* A couple of Multisite-only filter hooks and a regular one.
*/
add_action(
'network_admin_plugin_action_links',
array( $this, 'list_plugins' ),
10, 4
);
add_filter(
'views_plugins-network', // 'views_{$current_screen->id}'
array( $this, 'inactive_views' ),
10, 1
);
add_action(
'admin_print_scripts',
array( $this, 'enqueue')
);
/**
* This query is quite frequent to retrieve all blog IDs.
*/
global $wpdb;
$this->blogs = $wpdb->get_results(
" SELECT blog_id, domain
FROM {$wpdb->blogs}
WHERE site_id = '{$wpdb->siteid}'
AND spam = '0'
AND deleted = '0'
AND archived = '0' "
);
}
/**
* Enqueue script and style normally.
*/
public function enqueue()
{
wp_enqueue_script(
'ndbae-js',
$this->plugin_url . '/ndbae.js',
array(),
false,
true
);
wp_enqueue_style(
'ndbae-css',
$this->plugin_url . '/ndbae.css'
);
}
/**
* Check if plugin is active in any blog
* Using Multisite function get_blog_option
*/
private function get_network_plugins_active( $plug )
{
$active_in_blogs = array();
foreach( $this->blogs as $blog )
{
$the_plugs = get_blog_option( $blog['blog_id'], 'active_plugins' );
foreach( $the_plugs as $value )
{
if( $value == $plug )
$active_in_blogs[] = $blog['domain'];
}
}
return $active_in_blogs;
}
}
Other resources - e-books
Not directly related to plugin development, but kind of essential to Multisite management.
The e-books are written by no less than two giants of Multisite: Mika Epstein (aka Ipstenu) and Andrea Rennick.
WordPress Multisite 101
WordPress Multisite 110
Related
I use the following code to turn off all plugins without losing auth-keys or settings:
<?php add_filter('pre_option_active_plugins', '__return_empty_array'); add_filter('pre_site_option_active_sitewide_plugins', '__return_empty_array');
But now I want to do the same but de-activate all plugins not returned in my array, but first I need to check if they are even installed on the WP site first so as to not cause problems. I thought the following would work but I get a 502 error:
add_filter('pre_option_active_plugins', 'atlas_default_plugins');
add_filter('pre_site_option_active_sitewide_plugins', 'atlas_default_plugins');
function atlas_default_plugins() {
$default_plugins = array();
$plugins_to_check = array(
"example-plugin1",
"example-plugin2",
"example-plugin3",
);
$active_plugins = get_option( 'active_plugins' );
foreach( $plugins_to_check as $plugin ) {
if( in_array( $plugin, $active_plugins ) ) {
$default_plugins[] = $plugin;
}
}
return $default_plugins;
}
I thought it might be because the line $active_plugins = get_option( 'active_plugins' ); is being called too soon but I am not sure. I need it so that when the file is deleted from the folder, the plugins are active as before. Thanks
I initially thought it was just taking too long so I disable Long-Process Killer in WP but that did not solve it. I also switched PHP versions being used on WP but that also did not work.
It works if I do not the following lines of code:
$active_plugins = get_option( 'active_plugins' );
foreach( $plugins_to_check as $plugin ) {
if( in_array( $plugin, $active_plugins ) ) {
$default_plugins[] = $plugin;
}
}
But I need that check or else it breaks the site if I run it without checking first.
i'm trying to make a plugin for WordPress, which is has got an admin section for some basic settings, and also registers some shortcode to display some HTML, which is basically a form.
Here is my main plugin file, plugins/my-plugin/my-plugin.php:
/**
* Plugin Name: Pathway
* Plugin URI: http://www.martynleeball.com/
* Description: Pathway integration.
* Version: 1.0
* Author: Martyn Lee Ball
* Author URI: https://www.martynleeball.com/
**/
define('PATHWAY_VERSION', '0.0.8');
define('PATHWAY_AUTHOR', 'Martyn Lee Ball');
define('PATHWAY__MINIMUM_WP_VERSION', '4.*');
define('PATHWAY_CONTACT', 'martynleeball#gmail.com');
add_action(
'plugins_loaded',
array ( Pathway::get_instance(), 'plugin_setup' )
);
class Pathway
{
protected static $instance = NULL;
public $plugin_url = '';
private $cpt = 'post'; # Adjust the CPT
public function __construct() {}
public static function get_instance()
{
NULL === self::$instance and self::$instance = new self;
return self::$instance;
}
public function plugin_setup()
{
$this->plugin_url = '';
add_action( 'wp_enqueue_scripts', array( $this, 'enqueue' ) );
// if (is_admin()) {
//
// require_once( $this->plugin_url . 'admin/index.php' );
//
// register_activation_hook( __FILE__, 'install' );
//
// return;
// }
add_shortcode( 'pathway', array($this, 'shortcode'));
add_action( 'wp_ajax_ajax_login', array( $this, 'ajax_login' ) );
add_action( 'wp_ajax_nopriv_ajax_login', array( $this, 'ajax_login' ) );
add_action( 'wp_ajax_ajax_register', array( $this, 'ajax_register' ) );
add_action( 'wp_ajax_nopriv_ajax_register', array( $this, 'ajax_register' ) );
}
public function enqueue()
{
wp_enqueue_script( 'vuejs', 'https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js' );
wp_enqueue_script(
'ajax-handle-form',
"{$this->plugin_url}/wp-content/plugins/pathway/frontend/js/scripts.js"
);
wp_localize_script(
'ajax-handle-form',
'wp_ajax',
array(
'ajaxurl' => admin_url( 'admin-ajax.php' ),
'ajaxnonce' => wp_create_nonce( 'ajax_post_validation' )
)
);
}
public function ajax_login()
{
echo 'login';exit;
}
public function ajax_register()
{
echo 'register';exit;
}
public function shortcode()
{
if (!isset($_SESSION['pathway_login'])) {
self::view('forms/login');
}
}
public static function view( $name, array $args = array() ) {
foreach ( $args AS $key => $val ) {
$$key = $val;
}
// $file = $this->plugin_url . 'views/'. $name . '.php';
$file = 'views/'. $name . '.php';
include( $file );
}
}
Please correct me if i'm going wrong somewhere, there's so many mixed guides online showing different ways. Within this file i'm basically:
Adding my scripts and assigning the PHP values.
I would be then starting the admin section however has to comment this out for the AJAX call, this is my issue.
Registering my shortcode.
Adding the actions for the AJAX form submit.
Obviously my issue is that when I hit the is_admin from the AJAX call it is returning true, when it should be false as an public visitor can submit this form. The wp_ajax_nopriv action doesn't appear to work which would solve the issue, this is probably due to me being logged into WordPress.
I have tried logging out of WordPress but the is_admin still returns true!
Can someone advise?
is_admin will return true on all ajax calls.
It is not actually a useful function to check the user as it checks the uri rather than the user details, i.e. if on a admin page = true, if not false.
Now I was a little confused about your question here, it appears you want the is_admin to return false if its actually an ajax call?
if ( is_admin() && ! wp_doing_ajax() ) {}
It will return false on ajax calls.
If you are checking there is an "admin" logged in, as in can edit posts, see the other capabilities here
if ( current_user_can( 'edit_post' ) ) {}
The no_priv hook will not work when logged in, its not called.
I'm currently using Foobox lightbox (free) plugin, and apparently the plugins' files are loaded on every page regardless of whether it's used or not. However I would like to change this, but I can't seem to find any handles to dequeue the scripts through.
It seems that the scripts are called by the code beneath, which to me at least, doesn't show any handles. I've tried using remove_action(...) to "counter" them as well as various inputs in wp_dequeue_script() to try and target the files directly - e.g. wp_dequeue_script('foobox.free.min.js')
class Foobox_Free extends Foo_Plugin_Base_v2_1 {
const JS = 'foobox.free.min.js';
const CSS = 'foobox.free.min.css';
const CSS_NOIE7 = 'foobox.noie7.min.css';
const FOOBOX_URL = 'http://fooplugins.com/plugins/foobox/?utm_source=fooboxfreeplugin&utm_medium=fooboxfreeprolink&utm_campaign=foobox_free_pro_tab';
const BECOME_AFFILIATE_URL = 'http://fooplugins.com/affiliate-program/';
private static $instance;
public static function get_instance() {
if ( ! isset( self::$instance ) && ! ( self::$instance instanceof Foobox_Free ) ) {
self::$instance = new Foobox_Free();
}
return self::$instance;
}
/**
* Initialize the plugin by setting localization, filters, and administration functions.
*/
private function __construct() {
//init FooPluginBase
$this->init( FOOBOXFREE_FILE, FOOBOXFREE_SLUG, FOOBOX_BASE_VERSION, 'FooBox FREE' );
if (is_admin()) {
add_action('admin_head', array($this, 'admin_inline_content'));
add_action('foobox-free-settings_custom_type_render', array($this, 'custom_admin_settings_render'));
new FooBox_Free_Settings();
add_action( FOOBOX_ACTION_ADMIN_MENU_RENDER_GETTING_STARTED, array( $this, 'render_page_getting_started' ) );
add_action( FOOBOX_ACTION_ADMIN_MENU_RENDER_SETTINGS, array( $this, 'render_page_settings' ) );
add_action( 'admin_notices', array( $this, 'admin_notice_foogallery_lightboxes' ) );
add_action( 'wp_ajax_foobox_foogallery_lightboxes_ignore_notice', array( $this, 'admin_notice_foogallery_lightboxes_ignore' ) );
add_action( 'wp_ajax_foobox_foogallery_lightboxes_update', array( $this, 'admin_notice_foogallery_lightboxes_update' ) );
add_action( 'admin_print_scripts', array( $this, 'admin_notice_foogallery_lightboxes_inline_js' ), 999 );
add_filter( 'foobox-free-has_settings_page', '__return_false' );
} else {
// Render JS to the front-end pages
add_action('wp_enqueue_scripts', array($this, 'frontend_print_scripts'), 20);
add_action('foobox-free_inline_scripts', array($this, 'inline_dynamic_js'));
// Render CSS to the front-end pages
add_action('wp_enqueue_scripts', array($this, 'frontend_print_styles'));
if ( $this->is_option_checked('disable_others') ) {
add_action('wp_footer', array($this, 'disable_other_lightboxes'), 200);
}
}
}
How, if possible, do I dequeue these scripts (and styles) without editing the plugin file directly?
Edit
Below I've added the things I've tried doing to remove the scripts (all added to functions.php):
add_action( 'wp_enqueue_scripts', 'remove_foobox_scripts', 100 );
function remove_foobox_scripts() {
if ( !is_page('my-page') ) {
wp_deregister_script( 'foobox.free.min.js' );
wp_dequeue_script( 'foobox.free.min.js' );
}
}
Also tried the below, which is just a straight copy from the foobox file:
remove_action('wp_enqueue_scripts', array($this, 'frontend_print_scripts'), 20);
remove_action('foobox-free_inline_scripts', array($this, 'inline_dynamic_js'));
remove_action('wp_enqueue_scripts', array($this, 'frontend_print_styles'));
Also tried the below, where the array( part is removed:
remove_action('wp_enqueue_scripts','frontend_print_scripts', 20);
remove_action('foobox-free_inline_scripts', 'inline_dynamic_js');
remove_action('wp_enqueue_scripts', 'frontend_print_styles');
The problem with the way you are trying to do this stems from the order in which things happen in Wordpress.
You are relying on the conditional tags like is_page('my-page') to determine whether or not to load the plugin. These conditional tags do not become available until Wordpress has parsed the URL for the current query, and at this point all the plugins and your theme have already been loaded. Even if you parse the URL yourself instead of using the conditional tags you cannot be sure your code will run before the plugins are loaded.
The solution is to add your code as an mu-plugin. These are loaded before normal plugins so you can use an option (option name) filter here to alter the plugins you want to be loaded.
Option filters pass an array to your function containing the values which are set for that option, so in this case you want to hook option_active_plugins.
You can find the values to use for by running print_r(get_option('active_plugins')); or look through the plugins folder of your wordpress install.
The following example is specific to your question, but you could modify it to make a more comprehensive set of rules, adding and removing multiple plugins on different pages based on many conditions.
My function checks you are not in wp-admin and then has 2 conditions. The first disables a normally active plugin on the specified pages, the second enables a normally disabled plugin on the specified pages.
<?php
add_filter( 'option_active_plugins', 'add_or_remove_plugins' );
function add_or_remove_plugins( $plugins ) {
if (strpos( $_SERVER['REQUEST_URI'], '/wp-admin/' ) !== 0) { // Disable in admin pages or admin plugin settings stop working properly
if (strpos( $_SERVER['REQUEST_URI'], '/remove-plugin-here/' ) === 0) { // Conditonal tags still unavailable so you have to parse urls yourself
$k = array_search( 'foobox-image-lightbox/foobox-free.php', $plugins ); // This will stop an active plugin from loading
if( false !== $k ){
unset( $plugins[$k] );
}
}
if (strpos( $_SERVER['REQUEST_URI'], '/add-plugin-here/' ) === 0) {
$plugins[] = 'foobox-image-lightbox/foobox-free.php'; // This will load the plugin along with all the active plugins
}
}
return $plugins;
}
?>
To install just change the values to suit, paste into a file and upload into your mu-plugins folder
EDIT
Looks like your inline js is added to wp_head during the constructor of the Foobox_Free class. You could try adding this to your functions.php:
add_action( 'wp_head', 'remove_dynamic_js' );
function remove_dynamic_js(){
$foo = Foobox_Free::getInstance();
remove_action('wp_head', array($foo, 'inline_dynamic_js'));
}
or if that doesn't work then maybe this:
add_action( 'wp_head', 'remove_dynamic_js' );
function remove_dynamic_js(){
remove_action('wp_head', array('Foobox_Free', 'inline_dynamic_js'));
}
The action is added inside a private function, so I don't know if either of those will actually work. Give it a shot. If not my first answer will as it stops the plugin from loading at all on the specified pages.
UPDATE
Well, I was close... Here's the code to remove the scripts, as supplied by the plugin author.
$foobox = Foobox_Free::get_instance();
remove_action('foobox-free_inline_scripts', array($foobox, 'inline_dynamic_js'));
I have previously used a solution described here: remove_action From PHP Class for removing an action in the WooCommerce membership plugin.
However, the solution no longer works, as WooComemerce have changed the code behind the membership plugin.
So this is the new code.
Main woocommerce-memberships.php
public function includes() {
// load post types
require_once( $this->get_plugin_path() . '/includes/class-wc-memberships-post-types.php' );
// load user messages helper
require_once( $this->get_plugin_path() . '/includes/class-wc-memberships-user-messages.php' );
// load helper functions
require_once( $this->get_plugin_path() . '/includes/functions/wc-memberships-functions.php' );
// init general classes
$this->rules = $this->load_class( '/includes/class-wc-memberships-rules.php', 'WC_Memberships_Rules' );
$this->plans = $this->load_class( '/includes/class-wc-memberships-membership-plans.php', 'WC_Memberships_Membership_Plans' );
$this->emails = $this->load_class( '/includes/class-wc-memberships-emails.php', 'WC_Memberships_Emails' );
$this->user_memberships = $this->load_class( '/includes/class-wc-memberships-user-memberships.php', 'WC_Memberships_User_Memberships' );
$this->capabilities = $this->load_class( '/includes/class-wc-memberships-capabilities.php', 'WC_Memberships_Capabilities' );
$this->member_discounts = $this->load_class( '/includes/class-wc-memberships-member-discounts.php', 'WC_Memberships_Member_Discounts' );
$this->restrictions = $this->load_class( '/includes/class-wc-memberships-restrictions.php', 'WC_Memberships_Restrictions' );
Main instance
function wc_memberships() {
return WC_Memberships::instance();
}
From included class-wc-memberships-restrictions.php file
/**
* Returns the general content restrictions handler.
*
* #since 1.9.0
*
* #return null|\WC_Memberships_Posts_Restrictions
*/
public function get_posts_restrictions_instance() {
if ( ! $this->posts_restrictions instanceof WC_Memberships_Posts_Restrictions ) {
$this->posts_restrictions = wc_memberships()->load_class( '/includes/frontend/class-wc-memberships-posts-restrictions.php', 'WC_Memberships_Posts_Restrictions' );
}
return $this->posts_restrictions;
}
Then in class-wc-memberships-posts-restrictions.php
public function __construct() {
// decide whether attempting to access restricted content has to be redirected
add_action( 'wp', array( $this, 'handle_restriction_modes' ) );
// restrict the post by filtering the post object and replacing the content with a message and maybe excerpt
add_action( 'the_post', array( $this, 'restrict_post' ), 0 );
How do i remove the 'the_post' action?
So far i have the following in functions.php theme file:
function weteach_remove_actions(){
if(is_singular( 'post' )) {
if( function_exists( 'wc_memberships' ) ){
remove_action( 'the_post', array( wc_memberships()->restrictions, 'restrict_post' ));
}
}
return;
}
add_action( 'the_post', 'weteach_remove_actions', 1 );
Which gives me a "blank-page"-error.
Could you tell us what the error message was? My guess is that restrictions and post_restrictions aren't the same property and so you aren't finding the restrict_post method in the right class.
Edited now that I have looked at Memberships, this seems to work for me:
function so_41431558_remove_membership_post_restrictions(){
if( function_exists( 'wc_memberships' ) && version_compare( WC_Memberships::VERSION, '1.9.0', '>=' ) && is_singular( 'post' ) ){
remove_action( 'the_post', array( wc_memberships()->get_restrictions_instance()->get_posts_restrictions_instance(), 'restrict_post' ), 0 );
}
}
add_action( 'wp_head', 'so_41431558_remove_membership_post_restrictions', 1 );
Your add_action attempt is happening on priority 1, which is after the function has already run the Memberships method on priority 0, so even if the rest of your code was correct it would be too late.
So 1. I think we need to go to an earlier hook.
And 2. I think we need to use the new method for accessing the post restrictions class instance.
edited to add
and 3. I've switched to a direct version compare condition
and 4. I misread where the get_posts_restrictions_instance() method was... it is accessed via wc_memberships()->get_restrictions_instance()->get_posts_restrictions_instance()
This question already has answers here:
The plugin generated X characters of unexpected output during activation (WordPress)
(25 answers)
Closed 4 years ago.
I want to create a wordpress plugin by just following the example listed here based on a class OOP architecture with an external setup object, and adapting the source code on my own way like this:
main plugin file:
<?php
/*
Plugin Name: My Plugin
Description: My Plugin
Version: 1.0
Author: drwhite
Author URL: drwhite-site
Plugin URL: drwhite-site/video-ad-overlay
*/
register_activation_hook(__FILE__, array( 'VAO_Setup_File_Inc', 'on_activation'));
register_deactivation_hook(__FILE__, array( 'VAO_Setup_File_Inc', 'on_deactivation'));
register_uninstall_hook(__FILE__, array( 'VAO_Setup_File_Inc', 'on_uninstall'));
add_action('plugins_loaded', array( 'VAO_Setup_File', 'init'));
class VAO_Setup_File{
protected static $instance;
public static function init()
{
is_null( self::$instance ) AND self::$instance = new self;
return self::$instance;
}
public function __construct()
{
add_action( current_filter(), array( $this, 'load_files' ));
}
public function load_files()
{
foreach ( glob( plugin_dir_path( __FILE__ ).'includes/*.php' ) as $file ){
include_once $file;
}
}
}
In my plugin root directory i have created a subdirectory called includes within i put the setup file to be loaded on plugin load called setup.class.php:
<?php
class VAO_Setup_File_Inc
{
public static function on_activation()
{
if ( ! current_user_can( 'activate_plugins' ) )
return;
$plugin = isset( $_REQUEST['plugin'] ) ? $_REQUEST['plugin'] : '';
check_admin_referer( "activate-plugin_{$plugin}" );
}
public static function on_deactivation()
{
if ( ! current_user_can( 'activate_plugins' ) )
return;
$plugin = isset( $_REQUEST['plugin'] ) ? $_REQUEST['plugin'] : '';
check_admin_referer( "deactivate-plugin_{$plugin}" );
}
public static function on_uninstall()
{
if ( ! current_user_can( 'activate_plugins' ) )
return;
check_admin_referer( 'bulk-plugins' );
// Important: Check if the file is the one
// that was registered during the uninstall hook.
if ( __FILE__ != WP_UNINSTALL_PLUGIN )
return;
}
}
When i activate the plugin i got an error like the following:
I have read several questions posted by other users here and this may be duplicated question, but any of suggested answer worked for me including:
Remove space from start of tags and even remove the php end tag: nothing changed
in wp_config.php file i set wp_DEBUG to true , but it doesn't show errors
I have converted the file to UTF8 (without BOM) nothing changed
Have you put the eye in the issue ?
You are getting this error because your plugin is generating a PHP error that is being outputted to the page and causing the headers sent error you see... The problem with your code is that your function
public function load_files()
{
foreach ( glob( plugin_dir_path( __FILE__ ).'includes/*.php' ) as $file ){
include_once $file;
}
}
is not being called in time, so
register_activation_hook(__FILE__, array( 'VAO_Setup_File_Inc', 'on_activation'));
is looking for a function that doesn't exist, inside a class that doesn't exist. Move your
foreach ( glob( plugin_dir_path( __FILE__ ).'includes/*.php' ) as $file ){
include_once $file;
}
outside the class altogether, and it'll load just fine. It may require you to rethink your use of
add_action('plugins_loaded', array( 'VAO_Setup_File', 'init'));
and the way your plugin is being created, but it's a step in the right direction. If you copy and paste the code from the link you got this code from, his code displays the same problem...