WP2WPQAQ WordPress插件開發同步文章插件

# WORDPRESS插件開發 #–

WP2WPQAQ 插件是一個根據WordPress中的xmlrpc進行編寫的實現兩個WordPress站點進行文章同步的插件。目前該插件實現了文章的同步,媒體文件上傳(上傳到媒體庫),但是分類(category),標籤(Tag)和自定義字段(custom_fields)的同步功能未能實現。

該插件由於存在大部分不符合目前項目的具體需求,且因爲缺乏WordPress對應API詳細案例學習,導致開發不完善,沒能實現如下功能:

1.非媒體庫文件上傳到非媒體庫.
eg.從下面的ablog站點將order/目錄的image.jpeg圖片上傳到bblog的order/目錄中。
目前實現的上傳功能是這樣的,假設當前時間是2017年12月份,則ablog的order/image.jpeg圖片將會上傳到bblog的wp-content/uploads/2017/12/image.jpeg。

ablog—-
    |– order/
        |– image.jpeg
    |–wp-content/
        |–uploads
            |–2017/
                |–11/
                |–12/
    |–wp-includes/
    |–wp-admin/


eg.想上傳到order/結果卻是上傳到wp-content/uploads/2017/12/目錄。

bblog—-
    |– order/
        |– images2.png
    |–wp-content/
        |–uploads
            |–2017/
                |–11/
                |–12/
                  |–image.jpeg
    |–wp-includes/
    |–wp-admin/

2.custom_fields字段無法按照API中所述可以更新custom_fields字段,所以導致使用的插件ALL-IN-ONE-SEO-PACK的信息無法同步到目標站點。

3.分類同步,假設在ablog站點中,存在分類目錄叫A,而bblog中沒有A分類,則需要實現同步A分類到bblog中,目前是因爲不知道如何檢查bblog中是否存在A分類,如果直接使用API中的wp.newCategory是否會重複添加分類。

4.標籤同步,API中不提供標籤的新增,只存在wp.getTags的實現。

插件目錄結構:

WP2WPQAQ
    |-asset/
      |– js/
        |– wp2wp-sync.js
      |– css/
        |– wp2wp-admin.css
    |–lang/
    |– wp2wpqaq.php —-插件主入口文件
    |– wp2wp-options.php —-配置同步目標站點頁
    |– wp2wp-ajax.php —-異步實現同步操作實現功能
    |– uninstall.php —-卸載插件
  

  • wp2wpqaq.php 代碼如下:

    <?php 
    /**
     * Plugin Name: WP2WPQAQ
     * Plugin URI:  #
     * Description: Synchronize articles between two WordPress sites.
     * Author: DHM(huimingdeng)
     * Author URI: #
     * Version: 0.0.1
     */
    // Nothing in this plugin works outside of the admin area, so don't bother loading it if we're not looking at the admin panel...
    if(is_admin()){
    
        define( 'WP_OPTIONS_VERSION', 1 );
    
        add_action( 'admin_menu', 'wp2wp_menu' );//Add Menu
        // add_action('admin_head','wp2wp_active_style');
        add_action( 'admin_init', 'wp2wp_register_settings' );
        add_action('add_meta_boxes','my_meta_box');
        add_filter( 'plugin_action_links', 'wp2wp_settings_link', 10, 2 );
    
        // add_action('add_meta_boxes', array('sync-details-container', 'wp2wp_view'));
    
        // Activate the style sheets and scripts.
        function wp2wp_active_style(){
            $css = plugins_url('assets/css/',__FILE__).'wp2wp-admin.css';
            $js = plugins_url('assets/js/',__FILE__).'wp2wp-sync.js';//echo $js;
            wp_register_script('wp2wp-sync',$js,array());
            wp_register_style('wp2wp-admin',$css,array());
            wp_enqueue_script('wp2wp-sync');
            wp_enqueue_style('wp2wp-admin');
    
        }
        // add a meta box...
        function my_meta_box(){
            $screen = get_current_screen();
            if('add' !== $screen->action){
                add_meta_box('wp2wp_view','WP2WPQAQ-synchronize','wp2wp_view','post','side','high');
                add_action('admin_enqueue_scripts','wp2wp_active_style');
            }
        }
    
        // Create a template...
        function wp2wp_view(){
            ?>
            <div id="sync-details-container">
                <div class="sync-span-body"><span><?php _e('Push content to Target: <br>', 'wp2wpqaq');?><span title="配置頁設置的同步對象."><b><?php $server=get_option("wp2wp_server_option"); echo $server['host'];?></b></span></span></div>
                <div id="sync-content">
                </div>
                <button type="button" class="btn button-primary" onclick="wp2wppush(<?php the_ID();?>)">Push to Target</button>
                <input type="hidden" name="ajaxurl" id="ajaxurl" value="<?php echo WP_PLUGIN_URL . '/' . dirname(plugin_basename(__FILE__)) . '/wp2wp-ajax.php'; ?>">
            </div>
            <?php
        }
        // Add submenu...
        function wp2wp_menu() {
            add_submenu_page( 'options-general.php', 'WP2WPQAQ Settings', 'WP2WPQAQ', 'manage_options', 'wp2wpqaq', 'wp2wp_setting' );
        }
        // Setting page...
        function wp2wp_setting(){
            $css_uri=plugins_url('assets/css/wp2wp-admin.css',__FILE__);
            // wp_enqueue_style('wp2wp-admin.css',$css_uri);
            require_once( 'wp2wp-options.php' );
        }
        // Register valid admin options...
        function wp2wp_register_settings(){
            register_setting( "wp2wpqaq_options", "wp2wp_server_option" , "wp2wp_option_save");
        }
        // Save option...
        function wp2wp_option_save($options){
            if(!empty($options)){
                $remoteServer = trim( $options['host'] );
                if ( ( 'http://' != substr( $remoteServer, 0, 7 ) ) && ( 'https://' != substr( $remoteServer, 0, 8 ) ) ) {
                    $remoteServer = 'http://'.$remoteServer;
                }
                if ( '/' != substr( $remoteServer, -1 ) ) {
                    $remoteServer .= '/';
                }
                if ( include_once(  ABSPATH . WPINC . '/class-IXR.php' ) ) {
                    if ( include_once(  ABSPATH . WPINC . '/class-wp-http-ixr-client.php' ) ) {
                        $xmlrpc = new WP_HTTP_IXR_CLIENT( $remoteServer.'xmlrpc.php' );
                        $xmlrpc->query( 'wp.getOptions', array( 0, $options['user'], $options['pass'], array( 'software_name', 'software_version', 'so_api' ) ) );
                        $xmlrpcResponse = $xmlrpc->getResponse();
                        if ( null == $xmlrpcResponse ) {
                            if ( -32300 == $xmlrpc->getErrorCode() ) {
                                $options['authenticated'] = false;
                                $newOptions['group'][$groupId]['servers'][$serverKey]['api'] = 'API Unavailable';
                            } else {
                                $options['authenticated'] = false;
                                $options['api'] = 'Unknown';
                            }
                        }else{
                            if ( isset( $xmlrpcResponse['faultString'] ) ) {
                                $options['authenticated'] = false;
                                $options['api'] = __( trim( $xmlrpcResponse['faultString'], ' .' ), 'WP2WPQAQ' );
                            }else{
                                $options['authenticated'] = true;
                                if ( isset( $xmlrpcResponse['so_api'] ) ) {
                                    $options['api'] = sprintf( __( 'WP2WPQAQ Synchronize API v%s', 'WP2WPQAQ' ), $xmlrpcResponse['so_api']['value'] );
                                } else {
                                    $options['api'] = $xmlrpcResponse['software_name']['value'].' '.$xmlrpcResponse['software_version']['value'];
                                }
                            }
                        }
                    }
                }
            }
            return $options;
        }
    
        // Settings link on plugins page...
        function wp2wp_settings_link( $links, $file ) {
            if ( plugin_basename( __FILE__ ) == $file ) {
                array_push( $links, '<a href="options-general.php?page=wp2wpqaq">'.__( 'Settings', 'wp2wpqaq' ).'</a>' );
            }
            return $links;
    
        }
    }
    
  • wp2wp-options.php 代碼如下:

    <div class="wrap">
    <h2>WP2WPQAQ Settings:</h2>
    <div>
        <form action="options.php" method="post">
        <?php settings_fields( 'wp2wpqaq_options' ); ?>
        <?php $options=get_option("wp2wp_server_option");?>
            <table class="form-table">
                <tbody>
                    <tr>    
                        <th>Host Name of Target:</th>
                        <td><input type="text" name="wp2wp_server_option[host]" id="wp2wp_server_option" value="<?php echo $options['host'];?>"></td>
                    </tr>
                    <tr>
                        <th>Username on Target:</th>
                        <td><input type="text" name="wp2wp_server_option[user]" id="wp2wp_server_option" autocomplete="off" value="<?php echo $options['user'];?>"></td>
                    </tr>
                    <tr>
                        <th>Password on Target:</th>
                        <td><input type="password" name="wp2wp_server_option[pass]" id="wp2wp_server_option" autocomplete="new-password" value="<?php echo $options['pass'];?>"><span class="dashicons dashicons-yes"><?php if(isset($options['authenticated'])){echo 'auth:'.$options['authenticated'];}else{echo 'auth:'.$options['api'];}?></span></td>
                    </tr>
                </tbody>
            </table>
            <!-- <input type="submit" name="submit" class="button button-primary" value="保存更改"> -->
            <?php submit_button(); ?>
        </form>
    </div>
    

  • wp2wp-ajax.php 代碼如下:

    <?php 
    require_once(dirname(__FILE__)."/../../../wp-config.php");
    include_once(ABSPATH . WPINC . '/class-IXR.php');
    global $wpdb;
    $action = $_REQUEST['action'];
    if($action=='push'){
        $postId = $_POST['postId'];
        // $postId=142;//116;
    
        // Test 
        $server = get_host();
        if($server['authenticated']){
            $post_tab = $table_prefix."posts";
            $sql = "SELECT * FROM $post_tab where ID=$postId";
            $post=$wpdb->get_row($sql,ARRAY_A);
            $seo_tab = $table_prefix."postmeta";
            $seo = "SELECT meta_key,meta_value FROM $seo_tab WHERE post_id = $postId AND (meta_key LIKE '_aioseop_%')";
            $custom_fields = $wpdb->get_results($seo,ARRAY_A);
            $host = addslashes(site_url().'/');
            //設置遠程站點信息
            $target = $server['host'];
            if ( '/' != substr( $target, -1 ) ) {
                $target .= '/';
            }
            $username = $server['user'];
            $password = $server['pass'];
            $result=sync_post($post,$username,$password,$target,$host,$custom_fields);
        }else{
            $result=array('status'=>500,'msg'=>$server['api']);
        }
        echo json_encode($result);
    
    }else{
        echo json_encode(array('status'=>404,'msg'=>'No operation.'));
    }
    
    // 替換源站點附件
    function replace_host($string,$target,$host){
        if('' == $target || '' == $host) return false;
        // 判斷連接是否爲圖片,PDF附件等
        $allowedExts=array('jpeg','jpg','png','gif','txt','pdf');
        $pattern = "(http://[a-zA-Z0-9\./]+)";
        preg_match_all($pattern, $string, $urls);
        //如果源站點的附件等才替換,其他的不替換
        if(!empty($urls)&&!empty($urls[0])){
            foreach ($urls[0] as $v) {
                $ext = strtolower(pathinfo($v,PATHINFO_EXTENSION));
                if(in_array($ext, $allowedExts)){
                    $tmp=preg_replace('('.$host.')',$target,$v);
                    // echo $tmp."\r\n";
                    $string=str_replace($v,$tmp,$string);//替換掉附件的url域名,非附件不替換
                    // echo $v."\r\n";
                    // $string = $tmp;
                }
            }
        }
        // echo $string;
        return $string;
        //測試
        // return preg_replace('('.$host.')',$target,$string);
    }
    /**
     * 遠程同步文章(A->B)
     * @param  array  $post 需要同步的文章信息
     * @param  string $user 同步站點的用戶(登錄用)
     * @param  string $pass 同步站點的用戶的登陸密碼
     * @param  string $target   目標站點域名(同步站點)
     * @param  string $host 來源站點域名(本站點)
     * @param  array  $custom_fields 需要同步的自定義字段
     * @return array   返回消息記錄(成功200失敗400)
     */
    function sync_post($post,$user,$pass,$target,$host,$custom_fields){
    
        $xmlrpcurl=$target.'xmlrpc.php';
        $blogid=get_current_blog_id();
        $GLOBALS['xmlrpc_internalencoding'] = 'UTF-8';
        $client = new IXR_Client($xmlrpcurl);
        //先獲取文章主體內容,用於上傳內容中的附件
        $file=$post['post_content'];
        // $post['post_content']=replace_host($post['post_content'],$target,$host);//替換成目標站點url
        $post['guid']='';
        if(!empty($custom_fields)){//如果用戶自定義非空,則添加到$post中
            $cs_fields=array();
            foreach ($custom_fields as $k => $v) {
                $tmp_fields['key']=$v['meta_key'];
                $tmp_fields['value']=$v['meta_value'];
                // $tmp_fields=array($v['meta_key'],$v['meta_value']);
                $cs_fields[]=$tmp_fields;
    
            }
            $post['custom_fields']=$cs_fields;
    
            // $post['custom_fields']=array(array('key' => '_aioseop_title', 'value' => '還是測試'));
        }
        // 新的文章則進行新增,否則修改 
        $target_id = get_option("target_postid_".$post['ID']);
        if(!$target_id){
    
            $client->query("wp.newPost",$blogid,$user,$pass,$post);
            $response=$client->getResponse();
            // 成功後設置
            if ($response['faultCode']==0){
                if( is_a( $client->message, '\IXR_Message' ) ){
                    // 需要設置 target_postid_{the_ID} => 返回的post_id
                    if(!$client->message->message->params[0]){//如果無法獲取post_id,則根據返回的xml獲取
                        if(preg_match('/<string>(\d+)<\/string>/', $client->message->message,$rs)){
                            //如果正則匹配成功
                            if(!empty($rs)&&!empty($rs[1])){
                                update_option("target_postid_".$post['ID'],$rs[1]);
                                /*if(!empty($custom_fields)){//同步SEO元素
                                    foreach ($custom_fields as $k => $v) {
    
                                    }
                                }*/
                            }
                        }
                    }else{
                        update_option("target_postid_".$post['ID'],$client->message->message->params[0]);
                    }
                }
                $msg = "The article uploaded successfully.";
                $result=update_media($file,$target,$host,$user,$pass);
                $is_true=200;      
            } else {
                $is_true=400;//文章同步失敗
                $msg = 'Fail:' . $response['faultString'];
            }
        }else{//echo "yes";
            $post['ID'] = intval($target_id);//換成目標網站的文章id
            $post['post_modified'] = $post['post_modified_gmt'] = date("Y-m-d H:i:s");
            // 需要修改的字段
            $content = array(
                'post_status' => $post['post_status'],//文章狀態
                'post_title' => $post['post_title'],//標題
                'post_excerpt' => $post['post_excerpt'],//摘要
                'post_content' => $post['post_content'],//正文
                'post_name' => $post['post_name'],
                'post_password' => $post['post_password'],
                'post_thumbnail' => intval($post['post_thumbnail']),
                // 'custom_fields' => $post['custom_fields'],
            );
            $client->query("wp.editPost",$blogid,$user,$pass,intval($target_id),$content);
            $response=$client->getResponse();
            if($response['faultCode']==0){
                $msg = "The article uploaded successfully.";
                $result=update_media($file,$target,$host,$user,$pass);
                $is_true=200;//  
                /*if(!empty($custom_fields)){//同步SEO元素
                    foreach ($custom_fields as $k => $v) {
    
                    }
                }*/ 
            } else {
                $is_true=400;//文章同步失敗
                $msg = 'Fail:' . $response['faultString'];
            }
        }   //  exit;
        return array("status"=>$is_true,"msg"=>$msg);
        // return $is_true;
    }
    // 獲取目標站點的域名
    function get_host(){
        $option = get_option('wp2wp_server_option');//獲取設置中保存的配置項
        return $option;
    }
    
    
    // 附件處理,只能上傳到目標站點的/wp-content/uploads/{Year}/{Month} {Year},{Month}爲當前的年月
    // 返回值中 status 的值大於1則成功
    function update_media($postContent,$target,$host,$user,$pass){
        $is_true=0;
        $xmlrpcurl=$target.'xmlrpc.php';
        $client = new IXR_Client($xmlrpcurl);
        // Determine whether the connection is a picture, PDF attachment, etc
        $allowedExts=array('jpeg','jpg','png','gif','txt','pdf');
        // reference:http://www.jb51.net/article/23511.htm
        $mimetype=array(
            'jpg'=>'image/jpeg',
            'gif'=>'image/gif',
            'png'=>'image/png',
            'bmp'=>'image/bmp',
            'pdf'=>'application/pdf',
            'txt'=>'text/plain'
            );
        // Matching gets the url.
        $pattern = "(http://[a-zA-Z0-9\./]+)";
        preg_match_all($pattern, $postContent, $urls);
        $msg = "";
        // print_r($urls);
        // Gets the file suffix name for the url. pathinfo($fileInfo['name'],PATHINFO_EXTENSION)
        if(!empty($urls)&&!empty($urls[0])){
            foreach ($urls[0] as $v) {
                $ext = strtolower(pathinfo($v,PATHINFO_EXTENSION));
                if(in_array($ext, $allowedExts)){
                    $tmp=preg_replace('('.$host.')','',$v);
                    // $attach[]=$tmp;
                    $arr=explode('/',$v);
                    $filename=end($arr);
                    // echo $filename."\r\n";
                    //替換目錄,得到附件
                    $path = str_replace($filename,'',$tmp);
                    // echo $path."\r\n";
                    $upload_file = $tmp;
                    // echo $upload_file."\r\n";
                    $attachment = array(
                        'name' => $filename,//pathinfo($filename,PATHINFO_FILENAME),
                        'type' => $mimetype[$ext],
                        'bits' => new IXR_Base64( file_get_contents( ABSPATH.$path.$filename )),
                        'overwrite' => true
                        );
                    $client->query('wp.uploadFile','',$user,$pass,$attachment);
                    // $clinet->query('metaWeblog.newMediaObject','',$user,$pass,$attachment);
                    $response=$client->getResponse();
                    // print_r($client);
                    if($response['faultCode']==0){
                        $is_true +=1;
                        $msg .= $filename." upload success. \r\n";
                    }else{
                        $is_true -= 1;//不成功則減一
                        $msg .= $filename." upload failured... \r\n";
                    }
                }
            }
        }
        // print_r($attachment);
        // echo $msg;
        return array("status"=>$is_true,"msg"=>$msg);
    }
    
  • wp2wp-sync.js 代碼如下:

    function wp2wppush(postId){
        var ajaxurl = $('#ajaxurl').val();
        var data = {postId:postId,action:'push'};
        $.ajax({
            url:ajaxurl,
            data:data,
            type:'POST',
            dataType:'json',
            beforeSend:function(){
                $("#sync-content").html("<span class='sync-loading'>Loding....</span>");
            },
            success:function(msg){
                if(msg.status==200){
                // if(msg==200){
                    // alert(msg.status);
                    $("#sync-content").html("<span class='sync-success'>當前文章同步成功!</span>").css({"margin-bottom":"10px","border":"1px dashed gray"});
                }else{
                    var html = "<span class='sync-error'>同步失敗!</span>";
                    html+="<div class='error-msg'>"+msg.msg+"</div>";
                    $("#sync-content").html(html).css("margin-bottom","10px");
                }
            },
            error:function(msg) {
                html="<span>Sorry,unknown error.";
                $("#sync-content").html(html);
            }
        })
        // alert(postId);
    }
    

希望有WordPress的大神,能提供您寶貴的建議,幫助完善插件功能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章