wordpress 插件

For the first part of our tutorial series, we went through the basics of creating a WordPress plugin, using the WordPress Plugin Boilerplate, using its really handy generator, and learned how to add, sanitize, and save basic input types.

在本教程系列的第一部分中 ,我们介绍了使用WordPress插件样板创建WordPress插件的基础知识,使用了它真正方便的生成器 ,并学习了如何添加,清理和保存基本输入类型。

In case you missed that part, make sure you go and check it out - How to build a WordPress Plugin part 1

如果您错过了该部分,请确保您去检查一下- 如何构建WordPress插件的第1部分

In part 2, we will cover:

在第2部分中,我们将介绍:

  • Intenationalizing our plugin;
  • Adding different inputs types like color picker and file upload;
  • Dividing the whole page into multiple tabs;
  • Creating functions that will actually do something to the front/back-end;
  • Final testing on our Plugin;
  • And then sending our Plugin to the WordPress repository team for review.

We'll be working off the codebase from part 1, so if you haven't done it already, make sure you grab the code from part 1 on GitHub.

我们将处理第1部分中的代码库,因此,如果尚未完成,请确保从GitHub的第1部分中获取代码 。

(Making your plugin translatable (internationalization or il18n))

Before diving into the code, let me explain why it is important to make your plugin translatable.

在深入研究代码之前,让我解释一下为什么使您的插件可翻译很重要。

Adding a plugin to the WordPress repository will make you part of the WordPress community - which is huge! So, you probably can understand that many people from a full range of different countries might use it. Some of those people might not be fluent in english or your primary language. It makes a lot of sense to have your plugin easily tranlatable without having to touch its core coding.

将插件添加到WordPress存储库将使您成为WordPress社区的一部分-这是一个巨大的机会! 因此,您可能了解到,来自不同国家/地区的许多人都可能使用它。 其中一些人可能不会说流利的英语或母语。 让您的插件轻松进行翻译而无需触摸其核心代码非常有意义。

The plugin boilerplate comes with a languages folder and as you might remember from part one. This is where your plugin language files live. I won't go deep through the translation process here, but just know that some applications like poedit are here to help with this.

插件样板带有一个languages文件夹,您可能在第一部分中就已经记住了。 这是您的插件语言文件所在的位置。 在这里,我不会深入介绍翻译过程,只是知道像poedit之类的某些应用程序可在此方面提供帮助。

Let's make some change to our existing code so poedit will be able to find all our translatable strings:

让我们对现有代码进行一些更改,以便poedit能够找到我们所有可翻译的字符串:

Basically, we haven't changed a lot here, we have just wrapped all hard-coded strings with the following:

基本上,我们在这里没有做太多改变,我们只用以下代码包装了所有硬编码的字符串:

<?php _e('our string', $this->plugin_name);?>

All this does is just echo(_e) our string and assign it to our plugin ($this->plugin_name).

所有这些仅是echo( _e )我们的字符串并将其分配给我们的插件( $this->plugin_name )。

See the code below for the full code change:

有关完整的代码更改,请参见下面的代码:

<?php
/**
*
* admin/partials/wp-cbf-admin-display.php
*
**/

/**
 * Provide a admin area view for the plugin
 *
 * This file is used to markup the admin-facing aspects of the plugin.
 *
 * @link       http://lostwebdesigns.com
 * @since      1.0.0
 *
 * @package    Wp_Cbf
 * @subpackage Wp_Cbf/admin/partials
 */
?>

<!-- This file should primarily consist of HTML with a little bit of PHP. -->

<div class="wrap">

    <h2><?php echo esc_html( get_admin_page_title() ); ?></h2>

    <h2 class="nav-tab-wrapper">Clean up</h2>

    <form method="post" name="cleanup_options" action="options.php">

    <?php
    //Grab all options

        $options = get_option($this->plugin_name);

        // Cleanup
        $cleanup = $options['cleanup'];
        $comments_css_cleanup = $options['comments_css_cleanup'];
        $gallery_css_cleanup = $options['gallery_css_cleanup'];
        $body_class_slug = $options['body_class_slug'];
        $jquery_cdn = $options['jquery_cdn'];
        $cdn_provider = $options['cdn_provider'];

    ?>

    <?php
        settings_fields( $this->plugin_name );
        do_settings_sections( $this->plugin_name );
    ?>

    <!-- remove some meta and generators from the <head> -->
    <fieldset>
        <legend class="screen-reader-text"><span><?php _e('Clean WordPress head section', $this->plugin_name);?></span></legend>
        <label for="<?php echo $this->plugin_name;?>-cleanup">
            <input type="checkbox" id="<?php echo $this->plugin_name;?>-cleanup" name="<?php echo $this->plugin_name;?>[cleanup]" value="1" <?php checked( $cleanup, 1 ); ?> />
            <span><?php esc_attr_e( 'Clean up the head section', $this->plugin_name ); ?></span>
        </label>
    </fieldset>

    <!-- remove injected CSS from comments widgets -->
    <fieldset>
        <legend class="screen-reader-text"><span>Remove Injected CSS for comment widget</span></legend>
        <label for="<?php echo $this->plugin_name;?>-comments_css_cleanup">
            <input type="checkbox" id="<?php echo $this->plugin_name;?>-comments_css_cleanup" name="<?php echo $this->plugin_name;?>[comments_css_cleanup]" value="1" <?php checked( $comments_css_cleanup, 1 ); ?> />
            <span><?php esc_attr_e( 'Remove Injected CSS for comment widget', $this->plugin_name ); ?></span>
        </label>
    </fieldset>

    <!-- remove injected CSS from gallery -->
    <fieldset>
        <legend class="screen-reader-text"><span>Remove Injected CSS for galleries</span></legend>
        <label for="<?php echo $this->plugin_name;?>-gallery_css_cleanup">
            <input type="checkbox" id="<?php echo $this->plugin_name;?>-gallery_css_cleanup" name="<?php echo $this->plugin_name;?>[gallery_css_cleanup]" value="1" <?php checked( $gallery_css_cleanup, 1 ); ?>  />
            <span><?php esc_attr_e( 'Remove Injected CSS for galleries', $this->plugin_name ); ?></span>
        </label>
    </fieldset>

    <!-- add post,page or product slug class to body class -->
    <fieldset>
        <legend class="screen-reader-text"><span><?php _e('Add Post, page or product slug to body class', $this->plugin_name);?></span></legend>
        <label for="<?php echo $this->plugin_name;?>-body_class_slug">
            <input type="checkbox" id="<?php echo $this->plugin_name;?>-body_class_slug" name="<?php echo $this->plugin_name;?>[body_class_slug]" value="1" <?php checked( $body_class_slug, 1 ); ?>  />
            <span><?php esc_attr_e('Add Post slug to body class', $this->plugin_name);?></span>
        </label>
    </fieldset>

    <!-- load jQuery from CDN -->
    <fieldset>
        <legend class="screen-reader-text"><span><?php _e('Load jQuery from CDN instead of the basic wordpress script', $this->plugin_name);?></span></legend>
        <label for="<?php echo $this->plugin_name;?>-jquery_cdn">
            <input type="checkbox"  id="<?php echo $this->plugin_name;?>-jquery_cdn" name="<?php echo $this->plugin_name;?>[jquery_cdn]" value="1" <?php checked($jquery_cdn,1);?>/>
            <span><?php esc_attr_e('Load jQuery from CDN', $this->plugin_name);?></span>
        </label>
                <fieldset class="<?php if(1 != $jquery_cdn) echo 'hidden';?>">
                    <p>You can choose your own cdn provider and jQuery version(default will be Google Cdn and version 1.11.1)-Recommended CDN are <a href="https://cdnjs.com/libraries/jquery">CDNjs</a>, <a href="https://code.jquery.com/jquery/">jQuery official CDN</a>, <a href="https://developers.google.com/speed/libraries/#jquery">Google CDN</a> and <a href="http://www.asp.net/ajax/cdn#jQuery_Releases_on_the_CDN_0">Microsoft CDN</a></p>
                    <legend class="screen-reader-text"><span><?php _e('Choose your prefered cdn provider', $this->plugin_name);?></span></legend>
                    <input type="url" class="regular-text" id="<?php echo $this->plugin_name;?>-cdn_provider" name="<?php echo $this->plugin_name;?>[cdn_provider]" value="<?php if(!empty($cdn_provider)) echo $cdn_provider;?>"/>
                </fieldset>
    </fieldset>

    <?php submit_button(__('Save all changes', $this->plugin_name), 'primary','submit', TRUE); ?>

    </form>

</div>

The only change from the GIF here is for the submit_button(). You can see that the first parameter (which is the button text) has its text wrapped inside __(). This is the same as _e() except that this text will be returned instead of echoed.

与GIF相比,此处唯一的更改是submit_button() 。 您可以看到第一个参数(即按钮文本)的文本包含在__() 。 与_e()相同,除了此文本将被返回而不是回显。

So now, all our wrapped strings will be referred to our plugin and poedit will be able to grab those and insert it in its .pot file automatically.

因此,现在,所有包装的字符串都将被引用到我们的插件,而poedit将能够获取这些字符串并将其自动插入其.pot文件中。

You have now a fully translatable plugin, bravo!

您现在有了一个完全可翻译的插件,太棒了!

(Add more complex input types)

We have already added many files to our plugin, but only two types of inputs:

我们已经在插件中添加了许多文件,但是只有两种输入类型:

  • Checkboxes
  • Text inputs

We will now add 2 different and more complex inputs:

现在,我们将添加2个不同且更复杂的输入:

  • Color pickers
  • File/Media uploads

To do so, let's add a new section login page customization to our plugin inside the form. Insert this after the html we already are working from:

为此,让我们在form内的插件中添加新的部分login page customization 。 在我们已经在使用的html之后插入此代码:

/**
*
* admin/partials/wp-cbf-admin-display.php
*
**/

...

<form method="post" name="cleanup_options" action="options.php">

<?php
    //Grab all options      
    $options = get_option($this->plugin_name);

    // Cleanup
    $cleanup = $options['cleanup'];
    $comments_css_cleanup = $options['comments_css_cleanup'];
    $gallery_css_cleanup = $options['gallery_css_cleanup'];
    $body_class_slug = $options['body_class_slug'];
    $jquery_cdn = $options['jquery_cdn'];
    $cdn_provider = $options['cdn_provider'];

    // New Login customization vars
    $login_logo_id = $options['login_logo_id'];
    $login_logo = wp_get_attachment_image_src( $login_logo_id, 'thumbnail' );
    $login_logo_url = $login_logo[0];
    $login_background_color = $options['login_background_color'];
    $login_button_primary_color = $options['login_button_primary_color'];

?>

...

    <!-- Login page customizations -->

    <h2 class="nav-tab-wrapper"><?php _e('Login customization', $this->plugin_name);?></h2>

        <p><?php _e('Add logo to login form change buttons and background color', $this->plugin_name);?></p>

        <!-- add your logo to login -->
            <fieldset>
                <legend class="screen-reader-text"><span><?php esc_attr_e('Login Logo', $this->plugin_name);?></span></legend>
                <label for="<?php echo $this->plugin_name;?>-login_logo">
                    <input type="hidden" id="login_logo_id" name="<?php echo $this->plugin_name;?>[login_logo_id]" value="<?php echo $login_logo_id; ?>" />
                    <input id="upload_login_logo_button" type="button" class="button" value="<?php _e( 'Upload Logo', $this->plugin_name); ?>" />
                    <span><?php esc_attr_e('Login Logo', $this->plugin_name);?></span>
                </label>
                <div id="upload_logo_preview" class="wp_cbf-upload-preview <?php if(empty($login_logo_id)) echo 'hidden'?>">
                    <img src="<?php echo $login_logo_url; ?>" />
                    <button id="wp_cbf-delete_logo_button" class="wp_cbf-delete-image">X</button>
                </div>
            </fieldset>

        <!-- login background color-->
            <fieldset class="wp_cbf-admin-colors">
                <legend class="screen-reader-text"><span><?php _e('Login Background Color', $this->plugin_name);?></span></legend>
                <label for="<?php echo $this->plugin_name;?>-login_background_color">
                    <input type="text" class="<?php echo $this->plugin_name;?>-color-picker" id="<?php echo $this->plugin_name;?>-login_background_color" name="<?php echo $this->plugin_name;?>[login_background_color]"  value="<?php echo $login_background_color;?>"  />
                    <span><?php esc_attr_e('Login Background Color', $this->plugin_name);?></span>
                </label>
            </fieldset>

        <!-- login buttons and links primary color-->
            <fieldset class="wp_cbf-admin-colors">
                <legend class="screen-reader-text"><span><?php _e('Login Button and Links Color', $this->plugin_name);?></span></legend>
                <label for="<?php echo $this->plugin_name;?>-login_button_primary_color">
                    <input type="text" class="<?php echo $this->plugin_name;?>-color-picker" id="<?php echo $this->plugin_name;?>-login_button_primary_color" name="<?php echo $this->plugin_name;?>[login_button_primary_color]" value="<?php echo $login_button_primary_color;?>" />
                    <span><?php esc_attr_e('Login Button and Links Color', $this->plugin_name);?></span>
                </label>
            </fieldset>

        <?php submit_button(__('Save all changes', $this->plugin_name), 'primary','submit', TRUE); ?>

 </form>

We have now added our new variables and all our needed inputs. We now also have to add a variable to "cache" their value from the $options variable. They are here just to grab the logo image url as we will save the image id in our options:

现在,我们添加了新变量和所有需要的输入。 现在,我们还必须添加一个变量,以从$options变量“缓存”其值。 他们在这里只是为了获取徽标图片网址,因为我们会将图片ID保存在我们的选项中:

$login_logo = wp_get_attachment_image_src( $login_logo_id, 'thumbnail' );
$login_logo_url = $login_logo[0];

Nothing fancy is going on here. We are simply just using wp_get_attachment_image_src and passing it our login_logo_id and the size that we want. Then $login_logo_url will just give us the image url so we will be able to use it inside our img src attribute.

这里没有任何幻想。 我们只是使用wp_get_attachment_image_src并将其login_logo_id和所需的大小传递给它。 然后$login_logo_url将只给我们图像的URL,这样我们就可以在img src属性中使用它。

Looking at the result, it's not what we were expecting:

看结果,这不是我们所期望的:

To make this work, we need to add some Javascript and CSS files included in WordPress core to make it display and work properly.

要使此工作正常进行,我们需要添加一些WordPress核心中包含的Javascript和CSS文件,以使其显示和正常工作。

Open admin/class-wp-cbf-admin.php and add the following to the public function enqueue_styles() and public function enqueue_scripts():

打开admin/class-wp-cbf-admin.php并将以下内容添加到public function enqueue_styles()和public function enqueue_scripts() :

/**
*
* admin/class-wp-cbf-admin.php
*
**/

     public function enqueue_styles() {

          /**
           * This function is provided for demonstration purposes only.
           *
           * An instance of this class should be passed to the run() function
           * defined in Wp_Cbf_Loader as all of the hooks are defined
           * in that particular class.
           *
           * The Wp_Cbf_Loader will then create the relationship
           * between the defined hooks and the functions defined in this
           * class.
         */             
         if ( 'settings_page_wp-cbf' == get_current_screen() -> id ) {
             // CSS stylesheet for Color Picker
             wp_enqueue_style( 'wp-color-picker' );            
             wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/wp-cbf-admin.css', array( 'wp-color-picker' ), $this->version, 'all' );
         }

    }

    /**
     * Register the JavaScript for the admin area.
     *
     * @since    1.0.0
     */
    public function enqueue_scripts() {

        /**
         * This function is provided for demonstration purposes only.
         *
         * An instance of this class should be passed to the run() function
         * defined in Wp_Cbf_Loader as all of the hooks are defined
         * in that particular class.
         *
         * The Wp_Cbf_Loader will then create the relationship
         * between the defined hooks and the functions defined in this
         * class.
         */
        if ( 'settings_page_wp-cbf' == get_current_screen() -> id ) {
            wp_enqueue_media();   
            wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/wp-cbf-admin.js', array( 'jquery', 'wp-color-picker' ), $this->version, false );         
        }

    }

As you can see, we are adding 2 stylesheets: wp-color-picker and thickbox. These are enqueued only if we are on our plugin page because of the conditional if statement in place:

如您所见,我们将添加2个样式表: wp-color-picker和thickbox 。 仅当我们在插件页面上时才加入这些队列,因为有条件的if语句:

if ( 'settings_page_wp-cbf' == get_current_screen() -> id )

As you can see again, both our JS and CSS calls are loading the previously added dependencies. So any code that we will add in those will overwrite the default values:

再次您可以看到,我们的JS和CSS调用都正在加载先前添加的依赖项。 因此,我们将在其中添加的任何代码都将覆盖默认值:

wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/wp-cbf-admin.css', array('wp-clor-picker' ), $this->version, 'all' );

wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/wp-cbf-admin.js', array( 'jquery', 'wp-color-picker' ), $this->version, false );

If you look closely at the enqueue_scripts() function, you might wonder why there is not wp-color-picker, media-upload, or thickbox scripts enqueued here. Well the Iris color-picker script is loaded only as a javascript dependency of our plugin.

如果仔细查看enqueue_scripts()函数,您可能会想知道为什么这里没有排队wp-color-picker , media-upload或thickbox脚本。 好吧,Iris选色脚本仅作为插件的javascript依赖项加载。

Since WordPress v3.5, the Media Uploader is no longer using Thickbox. To load the new media uploader dependencies, we just have to add wp_enqueue_media() and it will load all the needed scripts.

从WordPress v3.5开始,媒体上载器不再使用Thinbox。 要加载新的媒体上传器依赖性,我们只需添加wp_enqueue_media() ,它将加载所有需要的脚本。

Check those documentation pages for more info wp_enqueue_media() and wp.media the Javascript Reference.

请查看这些文档页面以获取更多信息wp_enqueue_media()和wp.media Javascript Reference。

And again, we are just adding those files if we are on our plugin's settings page.

再说一次,如果我们在插件的设置页面上,我们只是添加这些文件。

wp_enqueue_script( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'js/wp-cbf-admin.js', array( 'jquery', 'wp-color-picker', 'media-upload' ), $this->version, false );

Let's now add some javascript to our plugin's JS file: admin/js/wp-cbf-admin.js

现在让我们向插件的JS文件中添加一些JavaScript: admin/js/wp-cbf-admin.js

/**
*
* admin/js/wp-cbf-admin.js
*
**/
(function( $ ) {
    'use strict';

    /**
     * All of the code for your admin-specific JavaScript source
     * should reside in this file.
     *
     * Note that this assume you're going to use jQuery, so it prepares
     * the $ function reference to be used within the scope of this
     * function.
     *
     * From here, you're able to define handlers for when the DOM is
     * ready:
     *
     * $(function() {
     *
     * });
     *
     * Or when the window is loaded:
     *
     * $( window ).load(function() {
     *
     * });
     *
     * ...and so on.
     *
     * Remember that ideally, we should not attach any more than a single DOM-ready or window-load handler
     * for any particular page. Though other scripts in WordPress core, other plugins, and other themes may
     * be doing this, we should try to minimize doing that in our own work.
     */

    $(function(){

         // Let's set up some variables for the image upload and removing the image     
         var frame,
             imgUploadButton = $( '#upload_login_logo_button' ),    
             imgContainer = $( '#upload_logo_preview' ),
             imgIdInput = $( '#login_logo_id' ),
             imgPreview = $('#upload_logo_preview'),        
             imgDelButton = $('#wp_cbf-delete_logo_button'),
             // Color Pickers Inputs
             colorPickerInputs = $( '.wp-cbf-color-picker' );

         // WordPress specific plugins - color picker and image upload
         $( '.wp-cbf-color-picker' ).wpColorPicker();

        // wp.media add Image
         imgUploadButton.on( 'click', function( event ){

            event.preventDefault();

            // If the media frame already exists, reopen it.
            if ( frame ) {
              frame.open();
              return;
            }

            // Create a new media frame
            frame = wp.media({
              title: 'Select or Upload Media for your Login Logo',
              button: {
                text: 'Use as my Login page Logo'
              },
              multiple: false  // Set to true to allow multiple files to be selected
            });
            // When an image is selected in the media frame...
            frame.on( 'select', function() {

              // Get media attachment details from the frame state
              var attachment = frame.state().get('selection').first().toJSON();                

              // Send the attachment URL to our custom image input field.
              imgPreview.find( 'img' ).attr( 'src', attachment.sizes.thumbnail.url );

              // Send the attachment id to our hidden input
              imgIdInput.val( attachment.id );

              // Unhide the remove image link
              imgPreview.removeClass( 'hidden' );
            });

            // Finally, open the modal on click
            frame.open();
        });

        // Erase image url and age preview
        imgDelButton.on('click', function(e){
            e.preventDefault();
            imgIdInput.val('');
            imgPreview.find( 'img' ).attr( 'src', '' );
            imgPreview.addClass('hidden');
        });

    }); // End of DOM Ready

})( jQuery );

As defined in the comments, we have enclosed our JS inside the DOM ready function: $(function(){ ...our code... }); which is itself enclosed in a self-invoking anonymous function.

如注释中所定义,我们已经将JS封装在DOM ready函数中: $(function(){ ...our code... }); 它本身包含在一个自调用匿名函数中。

First we define some vars that we will use in our Javascript, then for the color picker we just call the wpColorPicker() method to our color-picker fields - nothing really too complicated.

首先,我们定义一些将在Javascript中使用的变量,然后对于颜色选择器,我们仅将wpColorPicker()方法调用到颜色选择器字段中,没有什么太复杂了。

generatepress generatepress插件_java

For the image upload, we mostly use an adapted version of the wp.media Javascript Reference. It's all pretty much commented, but let's get through it a bit:

对于图像上传,我们主要使用wp.media Javascript Reference的改编版本。 几乎所有的评论,但让我们来解决一下:

Obviously, when we click on the 'Upload Logo' button we will open the media upload frame, which is set by the last statement of the imgUploadButton.on( 'click', function( event ){ ... frame.open() });

显然,当我们单击“上传徽标”按钮时,我们将打开媒体上传框架,该框架由imgUploadButton.on( 'click', function( event ){ ... frame.open() });

The frame is a representation of wp.media which can take some options as:

该frame是wp.media的表示wp.media ,可以采用以下选项:

  • The title of the popup, here: title: 'Select or Upload Media for your Login Logo' 弹出窗口的标题在此处: title: 'Select or Upload Media for your Login Logo'
  • The bottom right button text, here: button: {text: 'Use as my Login page Logo'} 右下角的按钮文本位于: button: {text: 'Use as my Login page Logo'}
  • A multi-select option (if you want to be able to grab multiple images), here set to false: multiple: false 多选选项(如果您希望能够捕获多张图像),此处设置为false: multiple: false

We have then a frame.on( 'select', function() {}) which will be triggered once we will choose an uploaded image and return the result as a JSON object represented here by the attachment var:

然后,我们有一个frame.on( 'select', function() {}) ,一旦我们选择一个上传的图像并将结果作为JSON对象(由附件var表示)返回,就会触发该frame.on( 'select', function() {})

var attachment = frame.state().get('selection').first().toJSON();

//console.log(attachment)

You can console.log this object to see that it gives us a whole list of attributes from our image. We use it just after to give a src value to our img field (using attachment.sizes.thumbnail.url so we have a nice 150x150px image) and also the image id to our hidden field:

您可以console.log这个对象来查看它为我们提供了图像中属性的完整列表。 紧接着我们使用它为img字段提供src值(使用attachment.sizes.thumbnail.url因此我们有一个不错的150x150px图像),还为隐藏字段提供了图像ID:

<input type="hidden" id="login_logo_id" name="<?php echo $this->plugin_name;?>[login_logo_id]" value="<?php echo esc_url($login_logo_id); ?>`

Why give it an id value instead of the direct image url? Well, this will make the sanitization way easier as it should only be a number.

为什么给它一个id值而不是直接的图像URL? 好吧,这将使清理方法更容易,因为它应该只是一个数字。

Finally, we have the imgDelButton.on('click', function(e){}); which will remove the img source value. The hidden file id value and adds the hidden class back to the preview controller if clicked.

最后,我们有了imgDelButton.on('click', function(e){}); 这将删除img源值。 隐藏的文件ID值,如果单击,则将hidden类添加回预览控制器。

So with this little JS, you have now an almost fully functional settings page with some of coolest WordPress built in features.

因此,有了这个小JS,您现在拥有了一个几乎全功能的设置页面,其中包含一些最酷的WordPress内置功能。

You should have now the same result as the screenshots below which are the super nice Iris color picker. If you click on any of the color fields, the media uploader pop-up when clicking on the Upload and once the image is selected our new logo display nicely there.

现在,您应该获得与下面的屏幕截图相同的结果,这些屏幕截图是超级漂亮的Iris颜色选择器。 如果您单击任何颜色字段,则在单击“上传”时弹出媒体上传器,并且一旦选择了图像,我们的新徽标就会很好地显示在此处。

generatepress generatepress插件_java_02

Pretty neat, right!

很整洁,对!

But once again, if you try to save all those new values, nothing will happen. To save and be able to retrieve our new inputs values, we will have to go through sanitizing and saving/updating.

但是再一次,如果您尝试保存所有这些新值,则不会发生任何事情。 为了保存并能够检索我们的新输入值,我们将必须进行清理和保存/更新。

Let's do that now.

现在开始吧。

(Sanitizing and saving/updating those complex fields)

As you might remember from part one, register_setting( $this->plugin_name, $this->plugin_name, array($this, 'validate') ); will take care of the update/saving of our variables and values once validated from our validate function:

您可能在第一部分中还记得过register_setting( $this->plugin_name, $this->plugin_name, array($this, 'validate') ); 一旦通过我们的validate函数进行验证,将负责更新/保存我们的变量和值:

/**
*
* admin/class-wp-cbf-admin.php
*
**/

    public function validate($input) {
        // All checkboxes inputs
        $valid = array();

        //Cleanup
        $valid['cleanup'] = (isset($input['cleanup']) && !empty($input['cleanup'])) ? 1 : 0;

        $valid['comments_css_cleanup'] = (isset($input['comments_css_cleanup']) && !empty($input['comments_css_cleanup'])) ? 1: 0;

        $valid['gallery_css_cleanup'] = (isset($input['gallery_css_cleanup']) && !empty($input['gallery_css_cleanup'])) ? 1 : 0;

        $valid['body_class_slug'] = (isset($input['body_class_slug']) && !empty($input['body_class_slug'])) ? 1 : 0;

        $valid['jquery_cdn'] = (isset($input['jquery_cdn']) && !empty($input['jquery_cdn'])) ? 1 : 0;

        $valid['cdn_provider'] = esc_url($input['cdn_provider']);

                // Login Customization
                //First Color Picker
                $valid['login_background_color'] = (isset($input['login_background_color']) && !empty($input['login_background_color'])) ? sanitize_text_field($input['login_background_color']) : '';

                if ( !empty($valid['login_background_color']) && !preg_match( '/^#[a-f0-9]{6}$/i', $valid['login_background_color']  ) ) { // if user insert a HEX color with #
                    add_settings_error(
                            'login_background_color',                     // Setting title
                            'login_background_color_texterror',            // Error ID
                            'Please enter a valid hex value color',     // Error message
                            'error'                         // Type of message
                    );
                }

                //Second Color Picker
                $valid['login_button_primary_color'] = (isset($input['login_button_primary_color']) && !empty($input['login_button_primary_color'])) ? sanitize_text_field($input['login_button_primary_color']) : '';

                if ( !empty($valid['login_button_primary_color']) && !preg_match( '/^#[a-f0-9]{6}$/i', $valid['login_button_primary_color']  ) ) { // if user insert a HEX color with #
                    add_settings_error(
                            'login_button_primary_color',                     // Setting title
                            'login_button_primary_color_texterror',            // Error ID
                            'Please enter a valid hex value color',     // Error message
                            'error'                         // Type of message
                    );
                }

                //Logo image id
                $valid['login_logo_id'] = (isset($input['login_logo_id']) && !empty($input['login_logo_id'])) ? absint($input['login_logo_id']) : 0;

        return $valid;
    }

This is how our validate function will look like now - let's go through it a bit

这就是我们的validate函数现在的样子-让我们仔细看一下

For the login_logo_id there is not much to say. We just check if $input['login_logo_id'] is set, and, if not, we give it a value of 0(which is a false value when checked with empty()). If it is set, we then just make sure that this value is a positive integer with absint.

对于login_logo_id ,没有什么可说的。 我们只是检查$input['login_logo_id']是否设置,否则,我们给它赋值为0 (用empty()检查时为假值)。 如果已设置,则只需确保该值是带有absint的正整数。

Now, let's explain our Color picker validation as, as you can see is more elaborated:

现在,让我们解释一下颜色选择器的验证,因为您可以看到它更加详细:

First we grab our $input value and make sure it's set and not empty:

首先,我们获取$input值,并确保它已设置且不为空:

$valid['login_background_color'] = (isset($input['login_background_color']) && !empty($input['login_background_color'])) ? sanitize_text_field($input['login_background_color']) : '';

Then if the value is not empty (which means also not an empty string), we test it against a regex to make sure it's an hexadecimal string. This means it has to begin with a # and then include 6 characters that can be either an integer between 0-9 or a letter between a-f:

然后,如果该值不为空(这也意味着不是一个空字符串),我们将使用正则表达式对其进行测试,以确保它是十六进制字符串。 这意味着它必须以#开头,然后包含6个字符,可以是0-9之间的整数或af之间的字母:

/^#[a-f0-9]{6}$/i

If the regex test fails, we add some settings error which are part of the settings_api:

如果正则表达式测试失败,我们添加一些设置错误这是一部分settings_api :

add_settings_error('login_button_primary_color', // Setting title 'login_button_primary_color_texterror', // Error ID 'Please enter a valid hex value color', // Error message 'error' // Type of message);

As described in the add_settings_error documentation, the first argument is a unique identifier that must be related to our setting. Here: login_button_primary_color.

如add_settings_error文档中所述,第一个参数是必须与我们的设置相关的唯一标识符。 此处: login_button_primary_color 。

Then we have another slug kind of string that will be added to the error message class, here: login_button_primary_color_texterror.

然后我们有另一个slug一种字符串将被添加到错误消息的类型,在这里: login_button_primary_color_texterror 。

Then the message you want to display (it is quite important to make this explicit): Please enter a valid hex value color.

然后,您要显示的消息(明确显示此消息非常重要): Please enter a valid hex value color 。

And finally, then the type of error which is optional as the default value is error. It's always good to write it down so you can directly know what this error message is about.

最后,作为默认值的可选错误类型为error。 写下来总是好的,这样您就可以直接知道此错误消息的含义。

Below is an error screenshot when trying to save an non-hexadecimal string for the color picker:

下面是尝试为颜色选择器保存非十六进制字符串时的错误屏幕截图:

And if everything is right, we now have our options saved with the success notice instead:

如果一切正常,我们现在将选择保存在成功通知中:

Ok, so now we have a whole bunch of options with values properly sanitized and saved! It's time to give our plugin the ability to change the behavior of our website!

好的,现在我们有了一大堆选项,它们的值已正确清理和保存! 现在该让我们的插件能够更改我们网站的行为了!

(Create the functions that will change our theme/backend behaviour)

We haven't yet made our plugin interact with our WordPress website. This will change right now. We will divide this in 2 separate parts:

我们尚未使我们的插件与WordPress网站进行交互。 这将立即改变。 我们将其分为两个独立的部分:

  • The "Clean up" part will change our website on the front-end and the login
  • And then "Login Customizations" will apply to the backend

In order to keep our code nicely organized, we will then write each part in its related folder. We'll use public for the front-end, and we will use admin for the backend - which makes perfect sense.

为了使我们的代码井井有条,我们将每个部分写入其相关文件夹中。 我们将public用于前端,而将admin用于后端-这很合理。

(Our frontend functions)

Open the public/class-wp-cbf-public.php file and add the following after our enqueue_scripts() function:

打开public/class-wp-cbf-public.php文件,并将以下内容添加到我们的enqueue_scripts()函数之后:

/**
*
* public/class-wp-cbf-public.php
*
**/

    public function __construct( $plugin_name, $version ) {

        $this->plugin_name = $plugin_name;
        $this->version = $version;
        $this->wp_cbf_options = get_option($this->plugin_name);

    }

    /**
     * Cleanup functions depending on each checkbox returned value in admin
     *
     * @since    1.0.0
     */
    // Cleanup head
    public function wp_cbf_cleanup() {

        if($this->wp_cbf_options['cleanup']){

            remove_action( 'wp_head', 'rsd_link' );                 // RSD link
            remove_action( 'wp_head', 'feed_links_extra', 3 );            // Category feed link
            remove_action( 'wp_head', 'feed_links', 2 );                // Post and comment feed links
            remove_action( 'wp_head', 'index_rel_link' );
            remove_action( 'wp_head', 'wlwmanifest_link' );
            remove_action( 'wp_head', 'parent_post_rel_link', 10, 0 );        // Parent rel link
            remove_action( 'wp_head', 'start_post_rel_link', 10, 0 );       // Start post rel link
            remove_action( 'wp_head', 'rel_canonical', 10, 0 );
            remove_action( 'wp_head', 'wp_shortlink_wp_head', 10, 0 );
            remove_action( 'wp_head', 'adjacent_posts_rel_link_wp_head', 10, 0 ); // Adjacent post rel link
            remove_action( 'wp_head', 'wp_generator' );               // WP Version
            remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
            remove_action( 'wp_print_styles', 'print_emoji_styles' );

        }
    }   
    // Cleanup head
    public function wp_cbf_remove_x_pingback($headers) {
        if(!empty($this->wp_cbf_options['cleanup'])){
            unset($headers['X-Pingback']);
            return $headers;
        }
    }

    // Remove Comment inline CSS
    public function wp_cbf_remove_comments_inline_styles() {
        if(!empty($this->wp_cbf_options['comments_css_cleanup'])){
            global $wp_widget_factory;
            if ( has_filter( 'wp_head', 'wp_widget_recent_comments_style' ) ) {
                remove_filter( 'wp_head', 'wp_widget_recent_comments_style' );
            }

            if ( isset($wp_widget_factory->widgets['WP_Widget_Recent_Comments']) ) {
                remove_action( 'wp_head', array($wp_widget_factory->widgets['WP_Widget_Recent_Comments'], 'recent_comments_style') );
            }
        }
    }

    // Remove gallery inline CSS
    public function wp_cbf_remove_gallery_styles($css) {
        if(!empty($this->wp_cbf_options['gallery_css_cleanup'])){
            return preg_replace( "!<style type='text/css'>(.*?)</style>!s", '', $css );
        }

    }

    // Add post/page slug
    public function wp_cbf_body_class_slug( $classes ) {
        if(!empty($this->wp_cbf_options['body_class_slug'])){
            global $post;
            if(is_singular()){
                $classes[] = $post->post_name;
            }
        }
                return $classes;
    }

    // Load jQuery from CDN if available
    public function wp_cbf_cdn_jquery(){
        if(!empty($this->wp_cbf_options['jquery_cdn'])){
            if(!is_admin()){
                            if(!empty($this->wp_cbf_options['cdn_provider'])){
                                $link = $this->wp_cbf_options['cdn_provider'];
                            }else{
                                $link = 'http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js';
                            }
                            $try_url = @fopen($link,'r');
                            if( $try_url !== false ) {
                                wp_deregister_script( 'jquery' );
                                wp_register_script('jquery', $link, array(), null, false);
                            }
            }
        }
    }

First thing here is that we are adding a reference to our saved options:

首先,我们要添加对已保存选项的引用:

$this->wp_cbf_options = get_option($this->plugin_name);

So we will be able to use it inside our functions.

这样我们就可以在我们的函数中使用它。

Then we are just adding some public functions that each include an if condition checking if its related option has been checked (here comes our useful $this->wp_cbf_options reference).

然后,我们仅添加一些公共函数,每个公共函数都包含一个if条件检查是否已检查了其相关选项(这是我们有用的$this->wp_cbf_options参考)。

Let's take take the wp_cbf_bobody_class_slug function as an example, looking at the body_class documentation on the WordPress Codex and scrolling down to "Add Classes By Filters" gives us the following example:

让我们以wp_cbf_bobody_class_slug函数为例,查看WordPress Codex上的body_class文档,然后向下滚动到“按过滤器添加类”,我们得到以下示例:

/* From the WordPress Codex */

// Add specific CSS class by filter
add_filter( 'body_class', 'my_class_names' );
function my_class_names( $classes ) {
    // add 'class-name' to the $classes array
    $classes[] = 'class-name';
    // return the $classes array
    return $classes;
}

/* From our Plugin */

public function wp_cbf_body_class_slug( $classes ) {
    if(!empty($this->wp_cbf_options['body_class_slug'])){
        global $post;
        if(is_singular()){
            $classes[] = $post->post_name;
        }
    }
    return $classes;
}

We can see that we are using the same format with some change for our plugin as we want to add the post_name if we are on a page, an attachment page, or a single post is_singular. We can then return the $classes array augmented with our new class.

我们可以看到,我们使用了一些变化对我们的插件相同的格式,我们要添加的post_name如果我们是一个页面,附件页面,或单篇文章上is_singular 。 然后,我们可以返回用新类增强的$classes数组。

You might notice that in our example we are not applying the add_filter as in the Codex's example. This is because we are going to add all our front-end related filters and actions to the define_public_hooks private function and our backend related actions and filters to the define_admin_hooks. If you remember, these functions are in the includes folder in class-wp-cbf.php file.

您可能会注意到,在我们的示例中,我们没有像Codex的示例那样应用add_filter 。 这是因为我们要将所有与前端相关的过滤器和操作添加到define_public_hooks私有函数中,并将与后端相关的动作和过滤器添加到define_admin_hooks 。 如果您还记得的话,这些功能位于class-wp-cbf.php文件中的includes文件夹中。

Let's do this and add our actions and filters to includes/class-wpcbf.php

让我们这样做,并将我们的操作和过滤器添加到includes/class-wpcbf.php

/**
*
* includes/class-wp-cbf.php
*
**/

    /**
     * Register all of the hooks related to the public-facing functionality
     * of the plugin.
     *
     * @since    1.0.0
     * @access   private
     */
    private function define_public_hooks() {

        $plugin_public = new Wp_Cbf_Public( $this->get_plugin_name(), $this->get_version() );

        /* 
        *  The following actions are commented out as we won't need any added style or script to our theme
        $this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_styles' );
        $this->loader->add_action( 'wp_enqueue_scripts', $plugin_public, 'enqueue_scripts' );
        */

        // Below are our "public" frontend related actions and filters hooks

        // Cleanup - Actions and filters
          //Actions
        $this->loader->add_action( 'init', $plugin_public, 'wp_cbf_cleanup' );
        $this->loader->add_action( 'wp_loaded', $plugin_public, 'wp_cbf_remove_comments_inline_styles' );
        $this->loader->add_action( 'wp_loaded', $plugin_public, 'wp_cbf_remove_gallery_styles' );
        $this->loader->add_action('wp_enqueue_scripts', $plugin_public, 'wp_cbf_cdn_jquery', PHP_INT_MAX);

           //Filters
        $this->loader->add_filter('wp_headers', $plugin_public, 'wp_cbf_remove_x_pingback');
        $this->loader->add_filter( 'body_class', $plugin_public, 'wp_cbf_body_class_slug' );

    }

First you will notice that I have commented out the enqueue_styles, enqueue_scripts actions. This is because this plugin won't add any CSS or Javascript to our website. If you needed to add some style or interaction to your website with your plugin to the front-end, you will have to write some code into those files (public/css/wp-cbf-public.css, public/js/wp-cbf-public.js). We also left those 2 actions un-commented.

首先,您会注意到我已注释掉enqueue_styles和enqueue_scripts操作。 这是因为此插件不会向我们的网站添加任何CSS或Javascript。 如果您需要在前端使用插件向网站添加某种样式或进行交互,则必须在这些文件中编写一些代码( public/css/wp-cbf-public.css , public/js/wp-cbf-public.js )。 我们也没有评论这两个动作。

We have added 4 actions and 2 filters corresponding to our 6 functions. Let's explain one of the hook calls. We will keep our body_class slug example:

我们添加了4个动作和2个对应于6个功能的过滤器。 让我们解释一个钩子调用。 我们将保持body_class slug示例:

$this->loader->add_filter( 'body_class', $plugin_public, 'wp_cbf_body_class_slug' );

Looking at the add_filter function (which is what we are calling with $this->loader->add_filter) from the includes/class-wp-cbf-loader.php, we have the following:

从includes/class-wp-cbf-loader.php add_filter函数(这就是我们用$this->loader->add_filter调用的$this->loader->add_filter ),我们有以下内容:

public function add_filter( $hook, $component, $callback, $priority = 10, $accepted_args = 1 ) {

    $this->filters = $this->add( $this->filters, $hook, $component, $callback, $priority, $accepted_args );
}

So we easily can decrypt it as:

因此,我们可以轻松地将其解密为:

  • $hook is 'body_class', $hook是'body_class' ,
  • $component is $plugin_public defined at the beginning of the define_public_hook we will use it on all our "public"(frontend) hooks calls $component是在define_public_hook开头定义的$plugin_public ,我们将在所有“ public”(frontend)hooks调用中使用它
  • $callback here our function name wp_cbf_body_class_slug from class-wp-cbf-public.php $callback这里我们的函数名称wp_cbf_body_class_slug来自class-wp-cbf-public.php wp_cbf_body_class_slug class-wp-cbf-public.php
  • $priority with a default value of 10, we don't need to specify it from this call but sometimes you will need your hook to have an higher priority, so you will just have to add a number here (only integer) $priority的默认值为10,我们不需要在此调用中指定它,但是有时您需要挂钩来具有更高的优先级,因此您只需要在此处添加一个数字(仅整数)
  • $accepted_args with a default value of 1, same here, we have just one argument passed to our function so we don't need to specify it, but depending on the hook you want to call you will need to adjust it according to the documentation. $accepted_args的默认值为1,与此处相同,我们只向函数传递了一个参数,因此我们不需要指定它,但是根据要调用的钩子,您需要根据文档进行调整。

Of course depending on what you want to change, you will have to search the documentation to know which action/filter needs to be triggered and against which hook, all with what argument and priority.

当然,根据您要更改的内容,您将必须搜索文档以了解需要触发哪个操作/过滤器以及针对哪个钩子进行触发,所有操作均使用什么参数和优先级。

Sweet, let's see what the results are now.

亲爱的,让我们看看现在的结果。

Go back to your website admin and first uncheck 'Add Post slug to body class`. If it's checked, save and go to any page or single post. I will go to the default "Sample Page" from an initial WordPress install and check our page body class from the developer tools. Here's what you should have (more or less depending on the plugins you might have installed and activated already):

返回您的网站管理员,然后首先取消选中“将帖子添加到身体类别”。 如果已选中,请保存并转到任何页面或单个帖子。 我将在最初的WordPress安装中转到默认的“示例页面”,并从开发人员工具中检查我们的页面主体类。 这是您应该拥有的(或多或少取决于您可能已经安装并激活的插件):

Now, let's activate our "Add Post slug to body class" and then you should get the same result as the below screenshot. The post/page slug is now added to the body class:

现在,让我们激活“将帖子添加到主体类”,然后您将获得与以下屏幕截图相同的结果。 现在,post / page slug已添加到body类:

generatepress generatepress插件_javascript_03

Boom! Our plugin changes our front-end as we wanted. We now just have to check or uncheck a checkbox and we will be able to do that to every website we want now. Plugins rock!

繁荣! 我们的插件可以根据需要更改前端。 现在,我们只需要选中或取消选中一个复选框,就可以对我们现在想要的每个网站执行此操作。 插件摇滚!

Of course you can and you should check what happens with all of our other options. It's now time for us to add our backend functions.

当然可以,您应该检查所有其他选项的情况。 现在是时候添加后端功能了。

(Backend functions)

Open admin/class-wp-cbf-admin.php and after the validate() function, add as follow:

打开admin/class-wp-cbf-admin.php并在validate()函数之后,添加如下内容:

/**
*
* admin/class-wp-cbf-admin.php
*
**/

...

    public function __construct( $plugin_name, $version ) {

        $this->plugin_name = $plugin_name;
        $this->version = $version;
        $this->wp_cbf_options = get_option($this->plugin_name);

    }

...

    /**
     * Login page customizations Functions
     *
     * @since    1.0.0
     */
     private function wp_cbf_login_logo_css(){
         if(isset($this->wp_cbf_options['login_logo_id']) && !empty($this->wp_cbf_options['login_logo_id'])){
             $login_logo = wp_get_attachment_image_src($this->wp_cbf_options['login_logo_id'], 'thumbnail');
             $login_logo_url = $login_logo[0];
             $login_logo_css  = "body.login h1 a {background-image: url(".$login_logo_url."); width:253px; height:102px; background-size: contain;}";
             return $login_logo_css;
         }
     }

     // Get Background color is set and different from #fff return it's css
     private function wp_cbf_login_background_color(){
         if(isset($this->wp_cbf_options['login_background_color']) && !empty($this->wp_cbf_options['login_background_color']) ){
             $background_color_css  = "body.login{ background:".$this->wp_cbf_options['login_background_color']."!important;}";
             return $background_color_css;
         }
     }
     // Get Button and links color is set and different from #00A0D2 return it's css
     private function wp_cbf_login_button_color(){
         if(isset($this->wp_cbf_options['login_button_primary_color']) && !empty($this->wp_cbf_options['login_button_primary_color']) ){
             $button_color = $this->wp_cbf_options['login_button_primary_color'];
             $border_color = $this->sass_darken($button_color, 10);
             $message_color = $this->sass_lighten($button_color, 10);
             $button_color_css = "body.login #nav a, body.login #backtoblog a {
                                   color: ".$button_color." !important;
                  }
                  .login .message {
                   border-left: 4px solid ".$message_color.";
                  }
                  body.login #nav a:hover, body.login #backtoblog a:hover {
                        color: ". $border_color." !important;
                  }

                  body.login .button-primary {
                         background: ".$button_color."; /* Old browsers */
                         background: -moz-linear-gradient(top, ".$button_color." 0%, ". $border_color.", 10%) 100%); /* FF3.6+ */
                         background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,".$button_color."), color-stop(100%, ". $border_color.", 10%))); /* Chrome,Safari4+ */
                         background: -webkit-linear-gradient(top, ".$button_color." 0%, ". $border_color.", 10%) 100%); /* Chrome10+,Safari5.1+ */
                         background: -o-linear-gradient(top, ".$button_color." 0%, ". $border_color.", 10%) 100%); /* Opera 11.10+ */
                         background: -ms-linear-gradient(top, ".$button_color." 0%, ". $border_color.", 10%) 100%); /* IE10+ */
                         background: linear-gradient(to bottom, ".$button_color." 0%, ". $border_color.", 10%) 100%); /* W3C */

                         -webkit-box-shadow: none!important;
                         box-shadow: none !important;

                         border-color:". $border_color."!important;
                    }
                    body.login .button-primary:hover, body.login .button-primary:active {
                         background: ". $border_color."; /* Old browsers */
                         background: -moz-linear-gradient(top, ". $border_color." 0%, ". $border_color.", 10%) 100%); /* FF3.6+ */
                         background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,". $border_color."), color-stop(100%,". $border_color.", 10%))); /* Chrome,Safari4+ */
                         background: -webkit-linear-gradient(top, ". $border_color." 0%,". $border_color.", 10%) 100%); /* Chrome10+,Safari5.1+ */
                         background: -o-linear-gradient(top, ". $border_color." 0%,". $border_color.", 10%) 100%); /* Opera 11.10+ */
                         background: -ms-linear-gradient(top, ". $border_color." 0%,". $border_color.", 10%) 100%); /* IE10+ */
                         background: linear-gradient(to bottom, ". $border_color." 0%,". $border_color.", 10%) 100%); /* W3C */
                    }

                    body.login input[type=checkbox]:checked:before{
                          color:".$button_color."!important;
                    }

                    body.login input[type=checkbox]:focus,
                    body.login input[type=email]:focus,
                    body.login input[type=number]:focus,
                    body.login input[type=password]:focus,
                    body.login input[type=radio]:focus,
                    body.login input[type=search]:focus,
                    body.login input[type=tel]:focus,
                    body.login input[type=text]:focus,
                    body.login input[type=url]:focus,
                    body.login select:focus,
                    body.login textarea:focus {
                    border-color: ".$button_color."!important;
                    -webkit-box-shadow: 0 0 2px ".$button_color."!important;
                    box-shadow: 0 0 2px ".$button_color."!important;
             }";

             return $button_color_css;
         }
     }

     // Write the actually needed css for login customizations
     public function wp_cbf_login_css(){
         if( !empty($this->wp_cbf_options['login_logo_id']) || $this->wp_cbf_login_background_color() != null || $this->wp_cbf_login_button_color() != null){
             echo '<style>';
             if( !empty($this->wp_cbf_options['login_logo_id'])){
                   echo $this->wp_cbf_login_logo_css();
             }
             if($this->wp_cbf_login_background_color() != null){
                   echo $this->wp_cbf_login_background_color();
             }
             if($this->wp_cbf_login_button_color() != null){
                   echo $this->wp_cbf_login_button_color();
             }
             echo '</style>';
         }
     }

    /**
     * Utility functions
     *
     * @since    1.0.0
     */

     private function sass_darken($hex, $percent) {
         preg_match('/^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i', $hex, $primary_colors);
         str_replace('%', '', $percent);
         $color = "#";
         for($i = 1; $i <= 3; $i++) {
             $primary_colors[$i] = hexdec($primary_colors[$i]);
             $primary_colors[$i] = round($primary_colors[$i] * (100-($percent*2))/100);
             $color .= str_pad(dechex($primary_colors[$i]), 2, '0', STR_PAD_LEFT);
         }

         return $color;
     }

     private function sass_lighten($hex, $percent) {
         preg_match('/^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i', $hex, $primary_colors);
         str_replace('%', '', $percent);
         $color = "#";
         for($i = 1; $i <= 3; $i++) {
             $primary_colors[$i] = hexdec($primary_colors[$i]);
             $primary_colors[$i] = round($primary_colors[$i] * (100+($percent*2))/100);
             $color .= str_pad(dechex($primary_colors[$i]), 2, '0', STR_PAD_LEFT);
         }

         return $color;
     }

As you can it's fairly different in here, this has nothing to do with the fact that we are adding admin related functions. It's just because of how these particular functions are going to be used - so it's also a good example. Thing that don't change though is that we have to add a reference to our plugin options in the __construct function: $this->wp_cbf_options = get_option($this->plugin_name);

您可能会在这里有很大的不同,这与我们要添加与管理员相关的功能无关。 只是因为这些特定功能的使用方式-这也是一个很好的例子。 不过,没有改变的是,我们必须在__construct函数中添加对插件选项的引用: $this->wp_cbf_options = get_option($this->plugin_name);

Then we have here three private functions that are just returning a chunk of CSS code:

然后,我们这里有三个私有函数,它们仅返回一块CSS代码:

  • private function wp_cbf_login_logo_css() returns $login_logo_css private function wp_cbf_login_logo_css()返回$login_logo_css
  • private function wp_cbf_login_background_color() returns $background_color_css private function wp_cbf_login_background_color()返回$background_color_css
  • and a fairly longer bit of CSS is returned by private function wp_cbf_login_button_color() as $button_color_css private function wp_cbf_login_button_color()作为$button_color_css返回了相当长CSS

Below those three private functions we have the function that will be called as a callback in our hook definition.

在这三个私有函数的下面,我们具有在钩子定义中称为回调的函数。

You might then wonder what are the last two private functions. Those are just helpers to emulate sass darken and lighten HSL functions so we can get a bunch of slightly different colors for our hover or active state on button or link.

然后,您可能想知道最后两个私有函数是什么。 这些只是模拟Sass darken和lighten HSL功能的助手,因此我们可以在按钮或链接上的hover或active状态下获得一堆略有不同的颜色。

So basically here we just have a function that will write a <style>...our CSS code...</style> tag to our login page with the CSS code returned by the first three private functions.

所以基本上,这里我们只有一个函数,该函数将使用前三个私有函数返回CSS代码将<style>...our CSS code...</style>标记写入登录页面。

Let's add our hooks in includes/class-wp-cbf.php inside define_admin_hooks() function:

让我们在define_admin_hooks()函数内部的includes/class-wp-cbf.php添加钩子:

/**
*
* includes/class-wp-cbf.php
*
**/

        //Admin Customizations
        $this->loader->add_action( 'login_enqueue_scripts', $plugin_admin, 'wp_cbf_login_css' );

And yes it's a one liner! Here we use the exact same $this->loader->add_action definition, the only change is on the $component, we are here calling $plugin_admin instead of $plugin_public.

是的,这是一个班轮! 这里我们使用完全相同的$this->loader->add_action定义,唯一的变化是在$component ,我们在这里调用$plugin_admin而不是$plugin_public 。

Let's test it! Once you have added a logo image and chosen a color for your login background and primary button and link color, save and just log out and once directed to the login page you will see your logo, background and buttons and link colors changed according to your choices in the plugin setting page.

让我们测试一下! 添加徽标图像并为登录背景和主按钮和链接颜色选择颜色后,保存并退出,然后定向到登录页面,您将看到徽标,背景和按钮以及链接颜色根据您的更改而改变插件设置页面中的选择。

Bravo! You have now a fully functioning plugin. You will be able to re-use that on all websites by just uploading it and configuring its settings.

太棒了! 您现在有了一个功能齐全的插件。 您只需上传并配置其设置,便可以在所有网站上重复使用它。

(Testing Our Plugin)

So we are now happy with our plugin, we should have tested all it's functionalities but if you want to investigate further I recommend you to add the Developer Plugin to your plugins while developing, it will give you all the needed tools for testing your plugin/theme as deprecated notices, a PHP/MYSQL console, and much more.

因此,我们现在对插件感到满意,我们应该已经测试了其所有功能,但是如果您想进一步研究,我建议您在开发时将Developer Plugin添加到插件中,它将为您提供测试插件/所需的所有工具。主题(已弃用的通知),PHP / MYSQL控制台等。

Another interesting step would be to go with some BDD or Behavior Driven Development, this is not the subject of this tutorial, but might be an interesting future post, if you want to check it out yourself, make sure to take a look at Codeception.

另一个有趣的步骤是进行一些BDD或“行为驱动的开发”,这不是本教程的主题,但将来可能会是一个有趣的帖子,如果您想自己检查一下,请务必看一下Codeception 。

Anyways, once you are sure your plugin is working perfectly, with no Errors or Notice, you are now ready to send it to be reviewed by the WordPress team

无论如何,一旦您确定插件可以正常工作,没有错误或通知,您现在就可以将其发送给WordPress团队进行审查

(Sending our plugin to be reviewed by the WordPress repository team)

Well, you have done all this work and want to share it with the world, this is great but your plugin will need to be reviewed by WordPress before it can be hosted on the WordPress Plugin Repository.

好了,您已经完成了所有这些工作并希望与世界分享,这很棒,但是您的插件需要经过WordPress的审查,然后才能托管在WordPress插件存储库中。

Before you go, read on how to send your plugin for review, you can already make it an Archive(.zip) and have it ready to share(on your Dropbox or Google Drive for example).

在开始之前,请阅读如何发送插件以供审查 ,您已经可以将其存档(.zip)并准备好进行共享(例如,在Dropbox或Google云端硬盘上)。

You can also create an account on WordPress.org if it's not already done.

您还可以在WordPress.org上创建一个帐户(如果尚未完成)。

Then after submission you will just have to be patient, those guys sometimes have a huge amount of plugins to review.

然后,提交后,您只需要耐心等待,这些家伙有时会审核大量的插件。

Once approved, you will then have to send your plugin to the [WordPress SVN repository][29] but before, make sure your `readme.txt file is valid

一旦获得批准,您就必须将插件发送到[WordPress SVN信息库] [29],但在此之前,请确保您的`readme.txt文件有效

(Conclusion)

So this is it. We have built a fully functional WordPress plugin from scratch (or almost) thanks to the WordPress plugin boilerplate. We have covered a lot here, from why to build a plugin and where to begin, to the plugin coding itself and how to keep it organized and clean.

就是这样。 多亏了WordPress插件样板 ,我们从头(或几乎)构建了一个功能齐全的WordPress插件 。 我们在这里介绍了很多内容,从为什么构建插件以及从何处开始,到插件编码本身以及如何使其井井有条。

I hope this will be helpful for you guys and that you have enjoyed it as much as I enjoyed covering the subject.

我希望这对你们有帮助,并且您喜欢它,就像我喜欢这个主题一样。

A future interesting post following on this would really be going with some Behaviour Driven Development (BDD), but I would be happy to hear what you guys think first.

以后有关此问题的未来有趣的帖子实际上将与一些行为驱动开发(BDD)一起使用,但是我很高兴听到你们首先想到的是什么。

A smaller post could also go through plugin settings page styling and adding some interaction with a little bit of Javascript as adding tabs as an example.

较小的帖子也可以通过插件设置页面样式进行设置,并添加一些与Javascript的交互,例如添加标签。

And of course to learn more about WordPress plugins make sure to check the WordPress Plugin Guideline.

当然,要了解有关WordPress插件的更多信息,请务必查看WordPress插件指南 。

You can find the full plugin on the WordPress repository.

您可以在WordPress存储库中找到完整的插件 。

Cheers!

干杯!

翻译自: https://scotch.io/tutorials/how-to-build-a-wordpress-plugin-part-2

wordpress 插件