个人感觉osc的博客不是很好用,主要是编辑器不是很符合我的习惯,允许上传图片图片太小,而且不少人有自己的博客,只是想给osc上做个文章的备份。
于是我决定写一个插件,实现以下功能:先在Wordpress写好文章,然后在发布文章时自动同步到osc的博客,并且发布一条动弹(可选)。有两种方式:一种是用模拟登录,另一种是使用osc 的open api。模拟登录最大的问题是不稳定,保不准osc哪天改版,模拟登录又要重写,维护非常麻烦.
open api 相对而言比较稳定和简单,并且有详细的文档:但缺点是:
- 调用次数的限制,未经审核的应用2000次(每日?);
- 未经审核的应用refresh token 有些问题,需要私信给osc相关人员申请,详情请见: OSC 的open API在刷新token时是不是有问题
- 我详细阅读了open api以后,发现跟博客相关的只有发布接口,并没有编辑,删除的接口,也就是说这个api不是完整的,希望osc的相关工作人员之后能完善一下。
把需求和步骤想清楚了,做起来就不会觉得太复杂了,我分为四个部分:
- 向osc申请一个应用:http://www.oschina.net/openapi/client (略)
- 插件设置页面,比如设置应用id,secret,同步的相关选项,如是否发布到动弹,允许文章前置/后置的插入相关内容等
- osc open api oauth授权,保存用户access token,refresh token等。
- 使用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 } }
好了,这就是设置页的基本内容.
我们简单说一下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; }
如果填写错误,会出现一条错误提示:
其他的api函数如:add_settings_section,add_settings_field,do_settings_sections可参考注释和gist:
https://gist.github.com/annalinneajohansson/5290405
接下来我们会讲一下获取osc oauth授权。