Copot pemasangan, Aktifkan, Nonaktifkan plugin: fitur khas & caranya

100

Saya sedang membuat plugin wordpress. Apa sajakah hal khas yang harus saya sertakan dalam fitur uninstall?

Misalnya, haruskah saya menghapus tabel yang saya buat di fungsi instal?

Apakah saya membersihkan entri opsi saya?

Ada yang lain?

redconservatory
sumber
Saya menghabiskan banyak waktu untuk membuatnya bekerja. Masalahnya adalah init hook tidak berfungsi di dalam register hook. Saya kira tidak satu kait (aksi atau filter) tidak akan berfungsi sepagi ini. Baca catatan dengan tautan di bawah ini. codex.wordpress.org/Function_Reference/register_activation_hook Dikatakan: "Mendaftarkan kait di dalam kait plugins_loaded Anda sudah terlambat dan tidak akan berjalan! (Bahkan ketika tampaknya berfungsi untuk register_deactivation_hook sampai kait wp_loaded.)"
Anton
Saya yang memperbarui kodeks ke apa yang Anda sebutkan, jadi ini dianggap dalam jawaban ↑ di atas. :)
kaiser

Jawaban:

150

Ada tiga kait berbeda . Mereka memicu dalam kasus berikut:

  • Copot pemasangan
  • Penonaktifan
  • Pengaktifan

Cara memicu fungsi dengan aman selama skenario

Berikut ini menunjukkan cara yang tepat untuk mengaitkan fungsi panggilan balik dengan aman yang dipicu selama tindakan yang disebutkan.

Anda bisa menggunakan kode ini di plugin yang menggunakan

  • fungsi polos,
  • kelas atau
  • kelas eksternal,

Saya akan menunjukkan tiga plugin demo berbeda yang dapat Anda periksa dan kemudian mengimplementasikan kode di plugin Anda sendiri.

Catatan penting di muka!

Karena topik ini sangat sulit dan sangat terperinci dan memiliki selusin kasus + tepi, jawaban ini tidak akan pernah sempurna. Saya akan terus meningkatkannya dari waktu ke waktu, jadi periksa kembali secara teratur.

(1) Aktifkan / Nonaktifkan / Hapus plugin.

Callback pengaturan plugin dipicu oleh core dan Anda tidak memiliki pengaruh pada bagaimana core melakukan ini. Ada beberapa hal yang perlu diingat:

  • Tidak pernah, echo/printapa pun (!) Selama callback pengaturan. Ini akan mengarah ke headers already sentpesan dan inti akan merekomendasikan untuk menonaktifkan dan menghapus plugin Anda ... jangan tanya: Saya tahu ...
  • Anda tidak akan melihat output visual. Tapi saya menambahkan exit()pernyataan ke semua callback yang berbeda sehingga Anda bisa mendapatkan wawasan tentang apa yang sebenarnya terjadi. Batalkan komentar mereka untuk melihat hal-hal berfungsi.
  • Sangat penting bagi Anda untuk memeriksa apakah __FILE__ != WP_PLUGIN_INSTALLdan (jika tidak: batalkan!) Untuk melihat apakah ada yang benar-benar menghapus plugin. Saya akan merekomendasikan untuk hanya memicu on_deactivation()callback selama pengembangan, sehingga Anda menghemat waktu yang Anda butuhkan untuk mendapatkan semuanya kembali. Setidaknya ini yang saya lakukan.
  • Saya juga melakukan beberapa hal keamanan. Beberapa dilakukan oleh inti juga, tapi hei! Lebih baik aman daripada menyesal! .
    • Pertama saya melarang akses file langsung ketika core tidak dimuat: defined( 'ABSPATH' ) OR exit;
    • Kemudian saya memeriksa apakah pengguna saat ini diizinkan untuk melakukan tugas ini.
    • Sebagai tugas terakhir, saya memeriksa pengarah. Catatan: Mungkin ada hasil yang tidak terduga dengan wp_die()layar meminta izin yang tepat (dan jika Anda ingin mencoba lagi ... ya, tentu ), ketika Anda mendapat kesalahan. Hal ini terjadi sebagai inti pengalihan Anda, set saat ini $GLOBALS['wp_list_table']->current_action();untuk error_scrapedan kemudian memeriksa pengarah untuk check_admin_referer('plugin-activation-error_' . $plugin);, di mana $pluginadalah $_REQUEST['plugin']. Jadi pengalihan terjadi pada setengah halaman memuat dan Anda mendapatkan bilah gulir kabel ini dan layar mati wawasan admin kuning / kotak pesan. Jika ini terjadi: Tetap tenang dan cari kesalahan dengan beberapa exit()dan langkah-demi-langkah debugging.

(A) Plugin fungsi polos

Ingat bahwa ini mungkin tidak berfungsi jika Anda menghubungkan callback sebelum definisi fungsi.

<?php
defined( 'ABSPATH' ) OR exit;
/**
 * Plugin Name: (WCM) Activate/Deactivate/Uninstall - Functions
 * Description: Example Plugin to show activation/deactivation/uninstall callbacks for plain functions.
 * Author:      Franz Josef Kaiser/wecodemore
 * Author URL:  http://unserkaiser.com
 * Plugin URL:  http://wordpress.stackexchange.com/questions/25910/uninstall-activate-deactivate-a-plugin-typical-features-how-to/25979#25979
 */

function WCM_Setup_Demo_on_activation()
{
    if ( ! current_user_can( 'activate_plugins' ) )
        return;
    $plugin = isset( $_REQUEST['plugin'] ) ? $_REQUEST['plugin'] : '';
    check_admin_referer( "activate-plugin_{$plugin}" );

    # Uncomment the following line to see the function in action
    # exit( var_dump( $_GET ) );
}

function WCM_Setup_Demo_on_deactivation()
{
    if ( ! current_user_can( 'activate_plugins' ) )
        return;
    $plugin = isset( $_REQUEST['plugin'] ) ? $_REQUEST['plugin'] : '';
    check_admin_referer( "deactivate-plugin_{$plugin}" );

    # Uncomment the following line to see the function in action
    # exit( var_dump( $_GET ) );
}

function WCM_Setup_Demo_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;

    # Uncomment the following line to see the function in action
    # exit( var_dump( $_GET ) );
}

register_activation_hook(   __FILE__, 'WCM_Setup_Demo_on_activation' );
register_deactivation_hook( __FILE__, 'WCM_Setup_Demo_on_deactivation' );
register_uninstall_hook(    __FILE__, 'WCM_Setup_Demo_on_uninstall' );

(B) Arsitektur berbasis kelas / OOP

Ini adalah contoh paling umum dalam plugin saat ini.

<?php
defined( 'ABSPATH' ) OR exit;
/**
 * Plugin Name: (WCM) Activate/Deactivate/Uninstall - CLASS
 * Description: Example Plugin to show activation/deactivation/uninstall callbacks for classes/objects.
 * Author:      Franz Josef Kaiser/wecodemore
 * Author URL:  http://unserkaiser.com
 * Plugin URL:  http://wordpress.stackexchange.com/questions/25910/uninstall-activate-deactivate-a-plugin-typical-features-how-to/25979#25979
 */


register_activation_hook(   __FILE__, array( 'WCM_Setup_Demo_Class', 'on_activation' ) );
register_deactivation_hook( __FILE__, array( 'WCM_Setup_Demo_Class', 'on_deactivation' ) );
register_uninstall_hook(    __FILE__, array( 'WCM_Setup_Demo_Class', 'on_uninstall' ) );

add_action( 'plugins_loaded', array( 'WCM_Setup_Demo_Class', 'init' ) );
class WCM_Setup_Demo_Class
{
    protected static $instance;

    public static function init()
    {
        is_null( self::$instance ) AND self::$instance = new self;
        return self::$instance;
    }

    public static function on_activation()
    {
        if ( ! current_user_can( 'activate_plugins' ) )
            return;
        $plugin = isset( $_REQUEST['plugin'] ) ? $_REQUEST['plugin'] : '';
        check_admin_referer( "activate-plugin_{$plugin}" );

        # Uncomment the following line to see the function in action
        # exit( var_dump( $_GET ) );
    }

    public static function on_deactivation()
    {
        if ( ! current_user_can( 'activate_plugins' ) )
            return;
        $plugin = isset( $_REQUEST['plugin'] ) ? $_REQUEST['plugin'] : '';
        check_admin_referer( "deactivate-plugin_{$plugin}" );

        # Uncomment the following line to see the function in action
        # exit( var_dump( $_GET ) );
    }

    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;

        # Uncomment the following line to see the function in action
        # exit( var_dump( $_GET ) );
    }

    public function __construct()
    {
        # INIT the plugin: Hook your callbacks
    }
}

(C) Arsitektur berbasis kelas / OOP dengan objek pengaturan eksternal

Skenario ini mengasumsikan bahwa Anda punya file plugin yang utama dan file kedua bernama setup.phpdalam subdirektori plugin bernama inc: ~/wp-content/plugins/your_plugin/inc/setup.php. Ini akan berfungsi juga ketika folder plugin berada di luar struktur folder WP default, juga ketika direktori konten diganti namanya atau dalam kasus di mana file setup Anda dinamai berbeda. Hanya incfolder yang harus memiliki nama & lokasi yang relatif sama dari direktori root plugins.

Catatan: Anda cukup mengambil tiga register_*_hook()*fungsi dan kelas dan memasukkannya ke dalam plugin Anda.

File plugin utama:

<?php
defined( 'ABSPATH' ) OR exit;
/**
 * Plugin Name: (WCM) Activate/Deactivate/Uninstall - FILE/CLASS
 * Description: Example Plugin
 * Author:      Franz Josef Kaiser/wecodemore
 * Author URL:  http://unserkaiser.com
 * Plugin URL:  http://wordpress.stackexchange.com/questions/25910/uninstall-activate-deactivate-a-plugin-typical-features-how-to/25979#25979
 */


register_activation_hook(   __FILE__, array( 'WCM_Setup_Demo_File_Inc', 'on_activation' ) );
register_deactivation_hook( __FILE__, array( 'WCM_Setup_Demo_File_Inc', 'on_deactivation' ) );
register_uninstall_hook(    __FILE__, array( 'WCM_Setup_Demo_File_Inc', 'on_uninstall' ) );

add_action( 'plugins_loaded', array( 'WCM_Setup_Demo_File', 'init' ) );
class WCM_Setup_Demo_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' ), 30 );
    }

    public function load_files()
    {
        foreach ( glob( plugin_dir_path( __FILE__ ).'inc/*.php' ) as $file )
            include_once $file;
    }
}

File pengaturan:

<?php
defined( 'ABSPATH' ) OR exit;

class WCM_Setup_Demo_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}" );

        # Uncomment the following line to see the function in action
        # exit( var_dump( $_GET ) );
    }

    public static function on_deactivation()
    {
        if ( ! current_user_can( 'activate_plugins' ) )
            return;
        $plugin = isset( $_REQUEST['plugin'] ) ? $_REQUEST['plugin'] : '';
        check_admin_referer( "deactivate-plugin_{$plugin}" );

        # Uncomment the following line to see the function in action
        # exit( var_dump( $_GET ) );
    }

    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;

        # Uncomment the following line to see the function in action
        # exit( var_dump( $_GET ) );
    }
}

(2) Pembaruan plugin

Jika Anda menulis plugin yang memiliki tabel atau opsi DB sendiri, mungkin ada skenario di mana Anda perlu mengubah atau meningkatkan hal-hal.

Sayangnya sejauh ini tidak ada kemungkinan untuk menjalankan sesuatu pada plugin / theme install atau perbarui / upgrade. Dengan senang hati ada penyelesaian: Kaitkan fungsi kustom ke opsi kustom (ya, itu lumpuh - tetapi berfungsi).

function prefix_upgrade_plugin() 
{
    $v = 'plugin_db_version';
    $update_option = null;
    // Upgrade to version 2
    if ( 2 !== get_option( $v ) ) 
    {
        if ( 2 < get_option( $v ) )
        {
            // Callback function must return true on success
            $update_option = custom_upgrade_cb_fn_v3();

            // Only update option if it was an success
            if ( $update_option )
                update_option( $v, 2 );
        }
    }

    // Upgrade to version 3, runs just after upgrade to version 2
    if ( 3 !== get_option( $v ) ) 
    {
        // re-run from beginning if previous update failed
        if ( 2 < get_option( $v ) )
            return prefix_upgrade_plugin();

        if ( 3 < get_option( $v ) )
        {
            // Callback function must return true on success
            $update_option = custom_upgrade_cb_fn_v3();

            // Only update option if it was an success
            if ( $update_option )
                update_option( $v, 3 );
        }
    }

    // Return the result from the update cb fn, so we can test for success/fail/error
    if ( $update_option )
        return $update_option;

return false;
}
add_action('admin_init', 'prefix_upgrade_plugin' );

Sumber

Fungsi pembaruan ini adalah contoh yang tidak terlalu bagus / ditulis dengan baik, tetapi seperti yang dikatakan: Ini adalah contoh dan tekniknya bekerja dengan baik. Akan memperbaikinya dengan pembaruan nanti.

kaisar
sumber
1
Ini bagus, TETAPI yang benar-benar ingin saya ketahui adalah hal-hal yang harus saya sertakan dalam metode penonaktifan saya ... misalnya, haruskah saya menghapus tabel saya di basis data atau membiarkannya seandainya pengguna berubah pikiran dan mengaktifkan kembali plugin ?
redconservatory
1
Iklan "TAPI": Saya menyebutkan bahwa ada 3 metode. Satu untuk aktivasi, satu untuk penonaktifan sementara dan satu untuk tidak menginstal. Imho "uninstall" mengatakan "Hapus saya dan semua yang saya lakukan", sementara "nonaktifkan" adalah keadaan sementara dan mungkin akan diulang. Tetapi: Lihat pembaruan. Saya menambahkan komentar tentang Q + Anda memperpanjangnya dengan beberapa rekomendasi pengembangan.
kaiser
3
Ah saya mengerti sekarang. Hanya sebuah pertanyaan, kapan dihapus instal dipanggil? Kapan file dihapus ??
redconservatory
1
@endrew Mereka hanya digunakan di samping check_admin_referer(). Mereka tidak perlu disanitasi karena core tidak melakukannya sendiri dan akan membandingkannya dengan $_REQUESTnilai-nilai yang tidak disanitasi . Tetapi jika mereka mulai menangis seperti gadis kecil karena itu, gunakan saja filter_var()atau esc_attr()di atasnya.
kaiser
2
Anda seharusnya tidak memeriksa WP_UNINSTALL_PLUGIN dalam fungsi panggilan balik jika menggunakan wp_register_uninstall_hook, hanya jika Anda menggunakan uninstall.php
paul
17

Untuk menguji sistem saat ini untuk fitur-fitur yang diperlukan seperti versi PHP atau ekstensi yang diinstal, Anda dapat menggunakan sesuatu seperti itu:

<?php  # -*- coding: utf-8 -*-
/**
 * Plugin Name: T5 Check Plugin Requirements
 * Description: Test for PHP version and installed extensions
 * Plugin URI:
 * Version:     2013.03.31
 * Author:      Thomas Scholz
 * Author URI:  http://toscho.de
 * Licence:     MIT
 * License URI: http://opensource.org/licenses/MIT
 */

/*
 * Don't start on every page, the plugin page is enough.
 */
if ( ! empty ( $GLOBALS['pagenow'] ) && 'plugins.php' === $GLOBALS['pagenow'] )
    add_action( 'admin_notices', 't5_check_admin_notices', 0 );

/**
 * Test current system for the features the plugin needs.
 *
 * @return array Errors or empty array
 */
function t5_check_plugin_requirements()
{
    $php_min_version = '5.4';
    // see http://www.php.net/manual/en/extensions.alphabetical.php
    $extensions = array (
        'iconv',
        'mbstring',
        'id3'
    );
    $errors = array ();

    $php_current_version = phpversion();

    if ( version_compare( $php_min_version, $php_current_version, '>' ) )
        $errors[] = "Your server is running PHP version $php_current_version but
            this plugin requires at least PHP $php_min_version. Please run an upgrade.";

    foreach ( $extensions as $extension )
        if ( ! extension_loaded( $extension ) )
            $errors[] = "Please install the extension $extension to run this plugin.";

    return $errors;

}

/**
 * Call t5_check_plugin_requirements() and deactivate this plugin if there are error.
 *
 * @wp-hook admin_notices
 * @return  void
 */
function t5_check_admin_notices()
{
    $errors = t5_check_plugin_requirements();

    if ( empty ( $errors ) )
        return;

    // Suppress "Plugin activated" notice.
    unset( $_GET['activate'] );

    // this plugin's name
    $name = get_file_data( __FILE__, array ( 'Plugin Name' ), 'plugin' );

    printf(
        '<div class="error"><p>%1$s</p>
        <p><i>%2$s</i> has been deactivated.</p></div>',
        join( '</p><p>', $errors ),
        $name[0]
    );
    deactivate_plugins( plugin_basename( __FILE__ ) );
}

Tes dengan cek untuk PHP 5.5:

masukkan deskripsi gambar di sini

fuxia
sumber
Sentuhan bingung, jadi pada dasarnya tidak ada panggilan ke register_activation_hooksini - mengapa tidak menggunakannya? Akankah ini dinyalakan sebelum atau sesudahnya register_activation_hookdan akan register_activation_hookmemecat sekalipun hal di atas tidak berlalu?
orionrush
Ini berjalan setelah kait aktivasi hanya pada halaman plugin.
fuxia
Saya mengerti - tetapi jika plugin diaktifkan di luar halaman plugin (katakanlah sebagai bagian dari ketergantungan tema) maka cek Anda akan dilewati no? Jadi saya mencoba pindah add_action( 'admin_notices', 't5_check_admin_notices', 0 );ke kait aktivasi dan plugin aktif tanpa melakukan pemeriksaan. . .
orionrush
@kaiser telah menjelaskan cara kerja kait aktivasi, saya ingin menunjukkan alternatif. Jika plugin tidak diaktifkan per halaman plugin, kesalahan fatal dapat terjadi, ya. Pendekatan ini tidak dapat berfungsi pada kait aktivasi tanpa penulisan ulang yang serius, karena kait itu muncul setelahnya admin_notices.
fuxia
Sebenarnya hanya tersandung pada cara mudah: stackoverflow.com/a/13927297/362445
orionrush