给wordpress后台文章列表添加一个复制按钮

需求概述:

实现对指定文章类型开启一键复制功能,即在文章列表页面各文章条目的快速编辑区域添加一个复制按钮,点击该按钮即可复制一篇新文章。

应用场景:

指定文章类型中,如果有一些条目的主要内容比较一致,仅仅是少量参数的修改,那么就很适合复制重复使用。

设计思路:

并不是所有的文章类型都适合开启条目复制功能,该功能仅适合那些内容可以重复使用的情况,所以我们要确保开启该功能的文章类型是可以被设置(指定)。

接下来就是添加一个复制按钮,该按钮显示在置顶文章类型的文章列表页,具体就是每个条目的快捷编辑栏

最后也是最核心的,就是数据复制,这里需要考虑哪些数据(字段)需要被复制到新的条目中去。


那么现在我们来尝试使用代码的方式来实现上述的功能。

首先是指定开启复制功能的文章类型。这个功能我希望在设置页面中进行勾选,其界面如下图所示:

其代码过程如下:

// 第一步:写一个可复用的复选框功能模块,每个复选框对应一个文章类型,样式借用后台的默认样式

function xcm_checkbox_HTML($optionMeta,$otips){
    $options   = get_option($optionMeta, []);
    if (!is_array($options)) $options   = [];
    $postTypes = get_post_types(['public' => true], 'objects');//遍历所有存在的文章类型
    foreach ($postTypes as $type) {
        printf('<label><input type="checkbox" name="'.$optionMeta.'[]" value="' . $type->name . '"');
        checked(in_array($type->name, $options), 1);
        printf('/><span style="margin-right:20px">' . $type->label . '</span></label>');
    }
    printf('<br/><p class="description">' . __($otips, 'xcm') . '</p>');        
}

// 第二步:在options-writting.php(设置→撰写)页面注册字段xcm_AllowDuplicate_PostTypes,并加载复选框模块
class xcm_ModSlugEnabled_setting
{
    public function __construct()
    {
        add_action('admin_init', array($this, 'xcm_ModSlugEnabled_init'));
    }

    public function xcm_ModSlugEnabled_init()//注册设置字段xcm_AllowDuplicate_PostTypes
    {
        register_setting('writing', 'xcm_AllowDuplicate_PostTypes', ['type' => 'array']);//duplicate
        add_settings_field(
            'allow_duplicate_post_type',
            '<label for="allow_duplicate_post_type">' . __('快速复制生成', 'xcm') . '</label>',
            array($this, 'xcm_allow_duplicate_callback'),
            'writing'
        );
    }

    public function xcm_allow_duplicate_callback() //加载复选框模块
    {
        xcm_checkbox_HTML('xcm_AllowDuplicate_PostTypes','勾选后的文章类型可通过快捷菜单快速复制。');
    }  
}
new xcm_ModSlugEnabled_setting();

至此,指定勾选文章类型的内容我们已经完成了。需要注意的重点就是如何获取存在的所有文章类型以及如何保存数据。在上述代码中,我们提取指定一个或多个文章类型的name值,并将其以数组的形式存入数据库中的xcm_AllowDuplicate_PostTypes字段。

接下来,我们需要在文章列表页放置”复制“按钮,这是一个相对简单的工作量,其效果见下图:

其代码实现如下:

//row-actions links
function xiiar_duplicate_post_link($actions, $post)
{
    $options  = get_option('xcm_AllowDuplicate_PostTypes', []);
    if (is_array($options) && in_array($post->post_type, $options)) {
        $url = admin_url( 'edit.php?post_type='.$post->post_type.'&post=' . $post->ID );
        $edit_link = add_query_arg( array( 'do_action' => 'duplicate' ), $url );
        $actions['duplicate'] = '<a href="'.wp_nonce_url( $edit_link, 'DuplicatePost' ).'">复制</a>';
    }
    return $actions;
}
add_filter('post_row_actions', 'xiiar_duplicate_post_link', 10, 2);

上述代码首先需要判断该条目所属的文章类型是否勾选了复制功能,继而以此决定是否在页面上显示复制按钮。此外这里是通过GET方式传递数据,我们设置了一个叫做duplicate的动作(action),用来决定是否执行复制的动作。需要注意这里的wp_nonce_url()函数,该函数在链接中生成一个随机数以作密钥,后续将核验这个密钥以判断链接是否安全。参考资料:https://developer.wordpress.org/reference/functions/wp_nonce_url/

最后一个部分就是数据处理了。我们直接列出代码:

//复制条目 
function xiiar_duplicate($post_id) {
  $oldpost = get_post($post_id);
  $options = get_option('xcm_AllowDuplicate_PostTypes', []);
  if (!in_array($oldpost->post_type, $options)) return;//验证是否开启了复制功能
  $post    = array(//下边通过设定条目的标题、状态、文章类型以及作者数据来插入一条新文章
    'post_title' => $oldpost->post_title.'-复制',
    'post_status' => 'draft',
    'post_type' => $oldpost->post_type,
    'post_author' => get_current_user_id()
  );

  $new_post_id = wp_insert_post($post);

  $data = get_post_custom($post_id); // 收集原文章的所有自定义字段数据
  foreach ( $data as $key => $values) {
    foreach ($values as $value) {
      is_numeric( $value )?
      update_post_meta( $new_post_id, $key, $value ):
      update_post_meta( $new_post_id, $key, unserialize($value) );// 存储数据到新文章
    }
  }
  return $new_post_id;
}

数据处理需要明确哪些数据要被复制,上述代码就舍弃了条目的正文数据,但是复制了所有的自定义字段数据。功能函数已经设定好了,接下来,我们需要确保在按钮被点击时激发这一功能。我们把这个函数挂载在admin_init这一时序之中。

add_action( 'admin_init', 'xiiar_duplicate_post' );   
function xiiar_duplicate_post() {  
    global $pagenow;
    if ($pagenow == 'edit.php' //限定链接发挥作用的页面
    && isset( $_GET['_wpnonce'], $_GET['do_action'], $_GET['post'] ) 
    && $_GET['do_action'] == 'duplicate' 
    && (int)$_GET['post'] 
    && wp_verify_nonce($_GET['_wpnonce'], 'DuplicatePost')){
      wp_redirect(admin_url('edit.php?post_type='.get_post_type($_GET['post'])));//重定向至干净URL,避免误刷新导致重复提交
      xiiar_duplicate($_GET['post']);
    }
}

与前边的wp_nonce_url()函数相呼应,我们用wp_verify_nonce()函数来核验随机数以确保链接来源的安全性;此外为了避免误刷新或者重复提交,我们应尽量限定链接发挥作用的页面范围。至此,我们完成了所有工作。