个人感觉osc的博客不是很好用,主要是编辑器不是很符合我的习惯,允许上传图片图片太小,而且不少人有自己的博客,只是想给osc上做个文章的备份。

于是我决定写一个插件,实现以下功能:先在Wordpress写好文章,然后在发布文章时自动同步到osc的博客,并且发布一条动弹(可选)。有两种方式:一种是用模拟登录,另一种是使用osc 的open api。模拟登录最大的问题是不稳定,保不准osc哪天改版,模拟登录又要重写,维护非常麻烦.

open api 相对而言比较稳定和简单,并且有详细的文档:但缺点是:

  1. 调用次数的限制,未经审核的应用2000次(每日?);
  2. 未经审核的应用refresh token 有些问题,需要私信给osc相关人员申请,详情请见: OSC 的open API在刷新token时是不是有问题
  3. 我详细阅读了open api以后,发现跟博客相关的只有发布接口,并没有编辑,删除的接口,也就是说这个api不是完整的,希望osc的相关工作人员之后能完善一下。

把需求和步骤想清楚了,做起来就不会觉得太复杂了,我分为四个部分:

  1. 向osc申请一个应用:http://www.oschina.net/openapi/client (略)
  2. 插件设置页面,比如设置应用id,secret,同步的相关选项,如是否发布到动弹,允许文章前置/后置的插入相关内容等
  3. osc open api oauth授权,保存用户access token,refresh token等。
  4. 使用api同步文章到osc,主要调用osc博客接口和动弹接口

首先写插件的基本框架, 我们使用面向对象的写法。

<?php
/*
Plugin Name: OscPress
Plugin URI: https://www.cellmean.com/oscpress
Description: 将Wordprss站点更新文章时, 将内容同步到osc博客和动弹
Version: 1.0
Author: Falcon
Author URI: https://www.cellmean.com
*/

$oscPress = new OscPress();

class OscPress{

    /**
     * 初始化
     */
    public function __construct(){

        $this->api_site = 'https://www.oschina.net'; // osc api的基本前缀

        $this->dir = __DIR__; // 当前插件的物理地址

        $this->state = __CLASS__ . '_state'; // 用于oauth 授权的安全鉴权的参数

        $this->callback_url = add_query_arg('callback', 'oscpress', site_url());


    }

    
}

注意:这里的注释部分不要省略,否则wordpress无法识别和发现我们的插件。

我们先完成菜单的设置,先提供appid和appsecret的设置项,使用 wordpress内置的settings api完成,当然也可以自己使用常规的表单完成,不过要注意安全相关的内容,比如csrf.

代码完成后如下:

<?php
/*
Plugin Name: OscPress
Plugin URI: https://www.cellmean.com/oscpress
Description: Wordprss站点更新文章时, 将内容自动发布到osc博客和动弹
Version: 1.0
Author: Falcon
Author URI: https://www.cellmean.com
*/
$oscPress = new OscPress();

class OscPress{

    /**
     * 初始化
     */
    public function __construct(){

        $this->api_site = 'https://www.oschina.net'; // osc api的基本前缀

        $this->dir = __DIR__; // 当前插件的物理地址

        $this->state = __CLASS__ . '_state'; // 用于oauth 授权的安全鉴权的参数

        $this->callback_url = add_query_arg('callback', 'oscpress', site_url());

        add_action( 'admin_menu', array( $this,'admin_menu'));

        add_action( 'admin_init', array($this,'admin_init') );

    }

    public function admin_menu() {
        add_options_page('OscPress','OscPress','manage_options',"oscpress_admin_settings",array($this,'admin_setting'));
        // 添加设置菜单的另一种写法
        // add_submenu_page('options-general.php', 'OscPress','OscPress', 10, __FILE__, array($this,'admin_setting')); 
    }


    public function admin_init() {

        register_setting( 'oscpress_settings_group', 'oscpress_settings',array($this,'sanitize_callback') );

        /*
      This creates a “section” of settings.
      The first argument is simply a unique id for the section.
      The second argument is the title or name of the section (to be output on the page).
      The third is a function callback to display the guts of the section itself.
      The fourth is a page name. This needs to match the text we gave to the do_settings_sections function call.
       */
        add_settings_section('oscpress_settings', 'OscPress设置', array($this,'settings_section_text'), 'oscpress');

        /*
         The first argument is simply a unique id for the field.
        The second is a title for the field.
        The third is a function callback, to display the input box.
        The fourth is the page name that this is attached to (same as the do_settings_sections function call).
        The fifth is the id of the settings section that this goes into (same as the first argument to  add_settings_section).
        第六个参数是自定义传入回调函数的参数,数组类型
        */
        add_settings_field('appid', '应用ID', array($this,'input_text'), 'oscpress', 'oscpress_settings',array(
            "field" =>"appid", "label"=>"应用ID","placeholder"=>"请输入应用ID"
        ));
        add_settings_field('appsecret', '应用私钥', array($this,'input_text'), 'oscpress', 'oscpress_settings',array(
            "field" =>"appsecret", "label"=>"应用私钥","placeholder"=>"请输入应用私钥"
        ));
    }

    public function input_text($arr){

        $settings = (array) get_option( 'oscpress_settings' );
        $field = $arr['field'];
        $value = esc_attr( $settings[$field] );

        echo "<label class=\"oscpress_settings\" for=\"{$arr['field']}\"><input type='text' id='{$arr['field']}' name='oscpress_settings[$field]' value='$value' placeholder=\"{$arr['placeholder']}\" /></label>";


    }

    /**
     * 验证和洁净化函数
     */
    public function sanitize_callback($inputs){
        return $inputs;
    }

    public function settings_section_text(){
        echo "<hr/>";
        echo "<em>请填写app id及私钥</em>";

    }

    public function admin_setting(){
?>

        <div class="wrap">
            <form action="options.php" method="POST">
                <?php settings_fields('oscpress_settings_group'); ?>
                <?php do_settings_sections('oscpress'); ?>
                <?php submit_button(); ?>
            </form>
        </div>

    <?php
    }


}

屏幕截图 2016-07-08 03.08.10

好了,这就是设置页的基本内容.

我们简单说一下Wordpress的这套setting api吧,其中 register_setting 是注册一组选项。

第一个参数是 oscpress_settings_group ,这个是分组的名称,要跟settings_fields(‘oscpress_settings_group’);
的参数一致,第二个参数是 oscpress_settings ,即存储在options表里的meta_name,我们可以通过
get_option( ‘oscpress_settings’ ) 获取设置的内容。如果在设置页提交了表单,这个值是一个关联数组。

register_setting 的第三个参数,是对设置页表单字段进行验证和洁净化的函数,在这里即 sanitize_callback方法,这个函数接收一个参数,就是表单post过来的数组,可以理解为$_POST,比如说我们需要验证appid字段必须为20个字符,可以这样写

public function sanitize_callback($inputs){

    if( strlen($inputs['appid']) !== 20 ) {
        add_settings_error('oscpress_settings', 'oscpress_failed', "应用ID必须为20位", 'error');
        $settings = (array) get_option( 'oscpress_settings' );
        $inputs['appid'] = isset($settings['appid']) ? $settings['appid'] : "";
    }

    return $inputs;

}

如果填写错误,会出现一条错误提示:

屏幕截图 2016-07-08 11.55.58

其他的api函数如:add_settings_section,add_settings_field,do_settings_sections可参考注释和gist:
https://gist.github.com/annalinneajohansson/5290405

接下来我们会讲一下获取osc oauth授权。

- EOF -