在我的一个项目中,需要在wordpress前台构建一个在线投稿的功能,为了提供丰富的在线编辑功能,我使用了kindeditor作为投稿页面文章内容的编辑器。但和我们一般的项目不同的是,我是在BAE(百度云)上进行的php开发,因此无法完成kindeditor的直接上传。(包括SAE在内的云空间都没有可写权限。)因此我选择了使用wordpress本身的wp_handle_upload来实现上传。于此同时的另一个问题是,如何将kindeditor的上传与wordpress结合起来,也就是说kindeditor上传的附件也可以在wordpress后台进行编辑和使用?解决这个问题的思路也很清晰:将kindeditor的上传处理文件进行修改,使用wordpress的上传机制(这个机制在我以前的文章中已经阐述过,可以在本博客中搜索相关文章),既可以解决BAE上传的问题,同时实现问题二的功能。
首先,kindeditor的上传机制非常简单。 ↑
在kindeditor中进行上传,通过点击编辑器中的图片或附件按钮,激活上传窗口。这个过程全部由脚本来实现。我们可以通过修改KE的kindeditor\plugins\image\image.js来修改弹出窗口的布局和内容。在选择好图片开始上传之后,这个窗口通过脚本的形式像一个隐藏的iframe发送$_POST内容。上传就是这样来完成的。iframe中使用了一个php文件来处理接收到的信息,并以JSON的形式返回处理结果。上传弹出窗口接收到处理结果之后,做出错误提示,或者将上传好的图片插入到文本编辑器中。
其次,实现KE整合到WP的后端处理。 ↑
其实这个后端处理就是修改上文所指的那个iframe中的php处理文件,修改之后,这个php完成文件文件的上传和数据库的写入。这个文件即kindeditor\php\upload_json.php。我们打开之后发现里面已经有很多注释,根据这些注释我们可以知道它们都完成哪些工作。但是我们要修改它,以实现我们自己的目的。我把修改好的文件贴在下面,以提供参考:
[payfor price="2"]array('gif', 'jpg', 'jpeg', 'png', 'bmp'), //'flash' => array('swf', 'flv'), //'media' => array('swf', 'flv', 'mp3', 'wav', 'wma', 'wmv', 'mid', 'avi', 'mpg', 'asf', 'rm', 'rmvb'), 'file' => array('doc','docx','xls','xlsx','ppt','txt','zip','rar','wps','pdf') ); //最大文件大小 $max_size = 1000000; //PHP上传失败 if (!empty($_FILES['imgFile']['error'])) { switch($_FILES['imgFile']['error']){ case '1': $error = '超过php.ini允许的大小。'; break; case '2': $error = '超过表单允许的大小。'; break; case '3': $error = '图片只有部分被上传。'; break; case '4': $error = '请选择图片。'; break; case '6': $error = '找不到临时目录。'; break; case '7': $error = '写文件到硬盘出错。'; break; case '8': $error = 'File upload stopped by extension。'; break; case '999': default: $error = '未知错误。'; } alert($error); } //检查文件大小 if($_FILES['imgFile']['size'] > $max_size)alert("上传文件大小超过限制。\n最大能上传".int($max_size/1000)."kb的文件。"); //获得文件扩展名 $temp_arr = explode(".",$_FILES['imgFile']['name']); $file_ext = array_pop($temp_arr); $file_ext = trim($file_ext); $file_ext = strtolower($file_ext); //检查扩展名 if(in_array($file_ext, $ext_arr['image']) === false && in_array($file_ext, $ext_arr['file']) === false){ alert("上传文件扩展名是不允许的扩展名。\n只允许" . implode(",", $ext_arr['image']) .'和'.implode(",", $ext_arr['file']).'格式。'); } // 上传成功,处理图片 if(empty($_FILES['imgFile']) === false && $_FILES['imgFile']['error'] == 0) : if($_FILES['imgFile']['size'] == 0)alert('上传的文件是空的,或已经被破坏,请检查后重新上传。'); include_once(ABSPATH.'/wp-admin/includes/file.php'); $filename = $_FILES['imgFile']['name']; $_FILES['imgFile']['name'] = iconv('UTF-8','GBK',$_FILES['imgFile']['name']); $uploaded_file = wp_handle_upload($_FILES['imgFile'],array('test_form' => false)); $file = $uploaded_file['file']; $new_file = iconv('GBK','UTF-8',$file); $url = iconv('GBK','UTF-8',$uploaded_file['url']); $type = $uploaded_file['type']; if(isset($uploaded_file['error']))alert($uploaded_file['error']); $attachment = array( 'guid' => $url, 'post_mime_type' => $type, 'post_title' => $filename, 'post_content' => '', 'post_status' => 'inherit' ); wp_update_post(array( 'ID' => $_POST['post_id'], 'post_title' => $filename, 'post_status' => 'pending' )); $attach_id = wp_insert_attachment($attachment,$new_file,$_POST['post_id']); include_once(ABSPATH.'wp-admin/includes/image.php'); $attach_data = wp_generate_attachment_metadata($attach_id,$file); $attach_data['file'] = iconv('GBK','UTF-8',$attach_data['file']); if(!empty($attach_data['sizes']))foreach($attach_data['sizes'] as $key => $sizes){ $sizes['file'] = iconv('GBK','UTF-8',$sizes['file']); $attach_data['sizes'][$key]['file'] = $sizes['file']; } wp_update_attachment_metadata($attach_id,$attach_data); // 要将最后的结果打印给浏览器 header('Content-type: text/html; charset=UTF-8'); $json = new Services_JSON(); echo $json->encode(array('error' => 0, 'url' => $url)); exit; else : alert('可能内部出错,请稍后重试。'); endif; function alert($msg) { header('Content-type: text/html; charset=UTF-8'); $json = new Services_JSON(); echo $json->encode(array('error' => 1, 'message' => $msg)); exit; }[/payfor]
上面这段php代码就是我的修改之后的文件的所有代码。读者可以自己仔细研读。而之所以要使用wp_handle_upload,是因为BAE没有可写权限,使用KE的上传铁定是不行,而有开发者根据BAE的bucket存储机制,修改了wp_handle_upload,以完成图片的上传,我们无需去了解BAE上具体怎么完成这个动作,只需要使用这个函数来上传文件即可。
第三,前台需要更多的配合。 ↑
虽然后端处理的思路是非常清晰的,然而这需要前端的配合。投稿页面需要付出更多的努力,才能让后端放心大胆的去完成自己的图片和数据库处理。在我的项目中,前端还需要做这样一些工作:
1、获取当前投稿文章的ID号。虽然一个投稿还没有发布,但是wordpress提供了预创建的机制,即这个投稿没有发布的情况下,建立的默认的数据库记录,我要获得这个记录的ID号,这样才能确认上传的图片将会属于哪一篇文章。这在后台》多媒体中你可以清晰的看到每张图片所在的文章。
2、要将这个ID号传递到处理页。看上去这件事很好办,但我尝试了多种方法,最终均宣告失败,只能选择一种最笨的方法,即使用javascript(jQuery)的setInterval来为弹出窗口中的特定部分增加一个input[type=hidden,value=post_id],这种方法其实是最笨的。
3、当心你的安全性漏洞。在自己撰写的程序中,漏洞常常非常多,包括:情况考虑不到,出现提示性错误;代码执行效率低,内存溢出;未知领域,黑客后门漏洞;等。当然,像由于手误敲错了单词引起的错误我们都没有算在内。wordpress本身其实还存在一些漏洞,因此在自己撰写程序时,应当非常注意这些问题,以防发生毁灭性的灾害。
最后,远远不止这些,还有很多需要自己开发。 ↑
看上去上面的代码已经非常完整了(完整的一个php文档),然而其实远远不止。如果你不是在寻找资料,你一定不知道需要怎么去完成这个项目,当然限于篇幅,我也不可能在这里将项目全部写下来。就来简单阐释下前端应该注意的几个问题。
首先,作为有权限的投稿,在文档开头应该首先判断用户是否登录,并检查他是否拥有投稿的权限,如果没有的话,应该提示登录或提升等级。其次,需要有比较可靠的文章发布机制,使用wp_update_post来处理这种发布,但并不提供文章的public权限,而是pending待审状态。再次,在文章内容的地方,使用stripslashes($getPost->post_content)而非不处理的输出,这样才能保证KE编辑器读取时显示正确。再次,我们通过下面的一段jQuery代码来实现上文所述的post:post_id的功能。
setInterval(function(){ if($('input[name=imgFile]').length > 0)$('input[name=imgFile]').after(''); },500);