文章目录[隐藏]
详细指南:打造支持多用户的在线文件协作与审阅功能
引言:WordPress作为协作平台的潜力
在当今数字化工作环境中,在线文件协作与审阅已成为团队生产力的核心要素。虽然市面上已有许多成熟的协作工具,但将这一功能集成到现有WordPress网站中,能够为用户提供无缝的工作体验,同时保持品牌一致性。WordPress作为全球最受欢迎的内容管理系统,其强大的扩展性和开源特性使其成为实现定制化协作功能的理想平台。
本指南将详细介绍如何通过WordPress代码二次开发,构建一个支持多用户的在线文件协作与审阅系统。我们将从基础架构设计开始,逐步深入到具体实现,最终打造一个功能完善、用户体验优秀的协作工具。
第一部分:系统架构设计与技术选型
1.1 需求分析与功能规划
在开始开发之前,我们需要明确系统的基本需求:
- 多用户支持:不同角色(管理员、编辑者、审阅者、查看者)的权限管理
- 文件上传与管理:支持常见文档格式(DOC、DOCX、PDF、PPT等)
- 实时协作:多人同时编辑文档的能力
- 审阅与批注:添加评论、建议和修改跟踪
- 版本控制:文档修改历史记录与恢复
- 通知系统:活动提醒和更新通知
- 安全与隐私:数据加密和访问控制
1.2 技术栈选择
基于WordPress的生态系统,我们选择以下技术方案:
- 核心框架:WordPress 5.0+,使用REST API进行前后端通信
- 前端技术:React.js或Vue.js用于构建交互式界面(可选)
- 实时协作:WebSocket(通过Socket.io或Pusher服务)
- 文档处理:后端转换服务或前端库(如Mammoth.js、PDF.js)
- 数据库:MySQL(WordPress默认),考虑自定义表结构
- 文件存储:WordPress媒体库扩展或云存储集成(AWS S3、Google Cloud)
1.3 数据库设计
我们需要扩展WordPress的默认数据库结构,添加以下自定义表:
-- 协作项目表
CREATE TABLE wp_collab_projects (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
description TEXT,
owner_id BIGINT(20) UNSIGNED NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (owner_id) REFERENCES wp_users(ID)
);
-- 项目成员表
CREATE TABLE wp_collab_members (
id INT AUTO_INCREMENT PRIMARY KEY,
project_id INT NOT NULL,
user_id BIGINT(20) UNSIGNED NOT NULL,
role ENUM('owner', 'editor', 'reviewer', 'viewer') DEFAULT 'viewer',
joined_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (project_id) REFERENCES wp_collab_projects(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES wp_users(ID)
);
-- 文档表
CREATE TABLE wp_collab_documents (
id INT AUTO_INCREMENT PRIMARY KEY,
project_id INT NOT NULL,
title VARCHAR(255) NOT NULL,
file_path VARCHAR(500),
file_type VARCHAR(50),
file_size INT,
created_by BIGINT(20) UNSIGNED NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
version INT DEFAULT 1,
FOREIGN KEY (project_id) REFERENCES wp_collab_projects(id) ON DELETE CASCADE,
FOREIGN KEY (created_by) REFERENCES wp_users(ID)
);
-- 文档版本表
CREATE TABLE wp_collab_document_versions (
id INT AUTO_INCREMENT PRIMARY KEY,
document_id INT NOT NULL,
version_number INT NOT NULL,
file_path VARCHAR(500),
changes_summary TEXT,
created_by BIGINT(20) UNSIGNED NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (document_id) REFERENCES wp_collab_documents(id) ON DELETE CASCADE,
FOREIGN KEY (created_by) REFERENCES wp_users(ID)
);
-- 批注与评论表
CREATE TABLE wp_collab_comments (
id INT AUTO_INCREMENT PRIMARY KEY,
document_id INT NOT NULL,
version_id INT,
user_id BIGINT(20) UNSIGNED NOT NULL,
content TEXT NOT NULL,
position_data JSON, -- 存储批注在文档中的位置信息
resolved BOOLEAN DEFAULT FALSE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (document_id) REFERENCES wp_collab_documents(id) ON DELETE CASCADE,
FOREIGN KEY (version_id) REFERENCES wp_collab_document_versions(id),
FOREIGN KEY (user_id) REFERENCES wp_users(ID)
);
第二部分:核心功能实现
2.1 用户权限与角色管理
WordPress本身具有强大的用户角色系统,但我们需要扩展它以支持协作场景:
<?php
// 在插件激活时添加自定义角色
function collab_add_custom_roles() {
add_role('collab_editor', '协作文档编辑', array(
'read' => true,
'edit_posts' => true,
'upload_files' => true,
'collab_edit_document' => true,
'collab_comment' => true,
));
add_role('collab_reviewer', '协作文档审阅', array(
'read' => true,
'collab_comment' => true,
'collab_review' => true,
));
add_role('collab_viewer', '协作文档查看', array(
'read' => true,
'collab_view' => true,
));
}
register_activation_hook(__FILE__, 'collab_add_custom_roles');
// 添加自定义权限到现有角色
function collab_add_capabilities() {
$roles = array('administrator', 'editor');
foreach ($roles as $role_name) {
$role = get_role($role_name);
if ($role) {
$role->add_cap('collab_manage_projects');
$role->add_cap('collab_edit_document');
$role->add_cap('collab_comment');
$role->add_cap('collab_review');
$role->add_cap('collab_view');
}
}
}
add_action('admin_init', 'collab_add_capabilities');
// 项目级别的权限检查
function collab_check_project_permission($project_id, $user_id, $capability) {
global $wpdb;
// 检查用户是否为项目成员
$member = $wpdb->get_row($wpdb->prepare(
"SELECT role FROM {$wpdb->prefix}collab_members
WHERE project_id = %d AND user_id = %d",
$project_id, $user_id
));
if (!$member) {
return false;
}
// 根据角色检查权限
$role_permissions = array(
'owner' => array('collab_manage_projects', 'collab_edit_document', 'collab_comment', 'collab_review', 'collab_view'),
'editor' => array('collab_edit_document', 'collab_comment', 'collab_review', 'collab_view'),
'reviewer' => array('collab_comment', 'collab_review', 'collab_view'),
'viewer' => array('collab_view')
);
return in_array($capability, $role_permissions[$member->role]);
}
?>
2.2 文件上传与管理系统
扩展WordPress媒体处理功能以支持协作文档:
<?php
// 扩展允许上传的文件类型
function collab_allowed_mime_types($mimes) {
$mimes['doc'] = 'application/msword';
$mimes['docx'] = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
$mimes['ppt'] = 'application/vnd.ms-powerpoint';
$mimes['pptx'] = 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
$mimes['xls'] = 'application/vnd.ms-excel';
$mimes['xlsx'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
$mimes['pdf'] = 'application/pdf';
return $mimes;
}
add_filter('upload_mimes', 'collab_allowed_mime_types');
// 处理文档上传
function collab_handle_document_upload($project_id) {
if (!isset($_FILES['collab_document']) || $_FILES['collab_document']['error'] !== UPLOAD_ERR_OK) {
return new WP_Error('upload_failed', '文件上传失败');
}
// 检查用户权限
if (!collab_check_project_permission($project_id, get_current_user_id(), 'collab_edit_document')) {
return new WP_Error('permission_denied', '您没有权限上传文档');
}
// 使用WordPress媒体处理功能
require_once(ABSPATH . 'wp-admin/includes/file.php');
require_once(ABSPATH . 'wp-admin/includes/media.php');
require_once(ABSPATH . 'wp-admin/includes/image.php');
$file = $_FILES['collab_document'];
// 验证文件类型和大小
$max_size = 50 * 1024 * 1024; // 50MB
if ($file['size'] > $max_size) {
return new WP_Error('file_too_large', '文件大小不能超过50MB');
}
// 上传文件
$upload_overrides = array('test_form' => false);
$movefile = wp_handle_upload($file, $upload_overrides);
if ($movefile && !isset($movefile['error'])) {
// 创建文档记录
global $wpdb;
$document_data = array(
'project_id' => $project_id,
'title' => sanitize_text_field($_POST['document_title']),
'file_path' => $movefile['file'],
'file_url' => $movefile['url'],
'file_type' => $file['type'],
'file_size' => $file['size'],
'created_by' => get_current_user_id(),
'version' => 1
);
$wpdb->insert("{$wpdb->prefix}collab_documents", $document_data);
$document_id = $wpdb->insert_id;
// 创建初始版本记录
$version_data = array(
'document_id' => $document_id,
'version_number' => 1,
'file_path' => $movefile['file'],
'created_by' => get_current_user_id()
);
$wpdb->insert("{$wpdb->prefix}collab_document_versions", $version_data);
// 记录活动日志
collab_log_activity($project_id, 'document_uploaded', array(
'document_id' => $document_id,
'document_title' => $document_data['title'],
'user_id' => get_current_user_id()
));
return $document_id;
} else {
return new WP_Error('upload_failed', $movefile['error']);
}
}
// 文档列表获取
function collab_get_project_documents($project_id, $user_id) {
global $wpdb;
// 检查查看权限
if (!collab_check_project_permission($project_id, $user_id, 'collab_view')) {
return new WP_Error('permission_denied', '您没有权限查看文档');
}
$documents = $wpdb->get_results($wpdb->prepare(
"SELECT d.*, u.display_name as creator_name,
(SELECT COUNT(*) FROM {$wpdb->prefix}collab_comments c WHERE c.document_id = d.id AND c.resolved = 0) as unresolved_comments
FROM {$wpdb->prefix}collab_documents d
LEFT JOIN {$wpdb->users} u ON d.created_by = u.ID
WHERE d.project_id = %d
ORDER BY d.updated_at DESC",
$project_id
));
return $documents;
}
?>
2.3 实时协作功能实现
使用WebSocket实现实时协作功能:
<?php
// WebSocket服务器集成(使用PHP-WebSocket或外部服务)
// 这里我们以Pusher服务为例,但也可以使用Socket.io
class CollabRealtime {
private $pusher;
public function __construct() {
// 初始化Pusher
$this->pusher = new PusherPusher(
get_option('collab_pusher_app_key'),
get_option('collab_pusher_app_secret'),
get_option('collab_pusher_app_id'),
array('cluster' => get_option('collab_pusher_cluster'))
);
}
// 用户加入文档协作
public function user_joined_document($document_id, $user_id) {
$user = get_userdata($user_id);
$this->pusher->trigger("document-{$document_id}", 'user-joined', array(
'user_id' => $user_id,
'user_name' => $user->display_name,
'timestamp' => current_time('timestamp')
));
// 记录用户活动
$this->update_document_presence($document_id, $user_id, 'join');
}
// 用户离开文档协作
public function user_left_document($document_id, $user_id) {
$this->update_document_presence($document_id, $user_id, 'leave');
$this->pusher->trigger("document-{$document_id}", 'user-left', array(
'user_id' => $user_id,
'timestamp' => current_time('timestamp')
));
}
// 文档内容更新
public function document_updated($document_id, $changes, $user_id) {
$this->pusher->trigger("document-{$document_id}", 'content-updated', array(
'changes' => $changes,
'user_id' => $user_id,
'timestamp' => current_time('timestamp')
));
}
// 批注添加
public function comment_added($document_id, $comment_id, $user_id) {
$this->pusher->trigger("document-{$document_id}", 'comment-added', array(
'comment_id' => $comment_id,
'user_id' => $user_id,
'timestamp' => current_time('timestamp')
));
}
// 更新在线用户状态
private function update_document_presence($document_id, $user_id, $action) {
global $wpdb;
$table_name = "{$wpdb->prefix}collab_document_presence";
if ($action === 'join') {
$wpdb->replace($table_name, array(
'document_id' => $document_id,
'user_id' => $user_id,
'last_seen' => current_time('mysql')
));
} else {
$wpdb->delete($table_name, array(
'document_id' => $document_id,
'user_id' => $user_id
));
}
// 获取当前在线用户
$online_users = $wpdb->get_results($wpdb->prepare(
"SELECT p.user_id, u.display_name, p.last_seen
FROM {$table_name} p
LEFT JOIN {$wpdb->users} u ON p.user_id = u.ID
WHERE p.document_id = %d AND p.last_seen > %s",
$document_id, date('Y-m-d H:i:s', strtotime('-5 minutes'))
));
// 广播在线用户列表
$this->pusher->trigger("document-{$document_id}", 'presence-updated', array(
'online_users' => $online_users
));
}
}
// 前端JavaScript处理实时更新
function collab_enqueue_realtime_scripts() {
if (is_page('collab-document')) {
// 引入Pusher库
wp_enqueue_script('pusher', 'https://js.pusher.com/7.0/pusher.min.js', array(), '7.0', true);
// 自定义实时协作脚本
wp_enqueue_script('collab-realtime', plugin_dir_url(__FILE__) . 'js/collab-realtime.js', array('jquery', 'pusher'), '1.0', true);
// 传递配置到前端
wp_localize_script('collab-realtime', 'collab_realtime_config', array(
'pusher_key' => get_option('collab_pusher_app_key'),
'pusher_cluster' => get_option('collab_pusher_cluster'),
'current_user_id' => get_current_user_id(),
'current_user_name' => wp_get_current_user()->display_name,
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('collab_realtime_nonce')
));
}
}
add_action('wp_enqueue_scripts', 'collab_enqueue_realtime_scripts');
?>
前端实时协作JavaScript示例:
// collab-realtime.js
class CollabRealtimeClient {
constructor(documentId) {
this.documentId = documentId;
this.pusher = null;
this.channel = null;
this.initializePusher();
}
initializePusher() {
// 初始化Pusher连接
this.pusher = new Pusher(collab_realtime_config.pusher_key, {
cluster: collab_realtime_config.pusher_cluster,
authEndpoint: collab_realtime_config.ajax_url,
auth: {
params: {
this.documentId,
user_id: collab_realtime_config.current_user_id,
nonce: collab_realtime_config.nonce
}
}
});
// 订阅文档频道
this.channel = this.pusher.subscribe(`private-document-${this.documentId}`);
// 绑定事件处理
this.bindEvents();
// 通知服务器用户已加入
this.notifyJoined();
}
bindEvents() {
// 用户加入事件
this.channel.bind('user-joined', (data) => {
this.handleUserJoined(data);
});
// 用户离开事件
this.channel.bind('user-left', (data) => {
this.handleUserLeft(data);
});
// 内容更新事件
this.channel.bind('content-updated', (data) => {
this.handleContentUpdated(data);
});
// 批注添加事件
this.channel.bind('comment-added', (data) => {
this.handleCommentAdded(data);
});
// 在线用户更新
this.channel.bind('presence-updated', (data) => {
this.updateOnlineUsers(data.online_users);
});
}
notifyJoined() {
jQuery.ajax({
url: collab_realtime_config.ajax_url,
method: 'POST',
data: {
action: 'collab_user_joined',
document_id: this.documentId,
nonce: collab_realtime_config.nonce
}
});
}
handleUserJoined(data) {
// 显示用户加入通知
this.showNotification(`${data.user_name} 加入了文档`);
// 更新在线用户界面
this.addOnlineUser(data.user_id, data.user_name);
}
handleContentUpdated(data) {
// 如果不是当前用户的操作,应用更改
if (data.user_id !== collab_realtime_config.current_user_id) {
this.applyRemoteChanges(data.changes);
}
}
sendContentUpdate(changes) {
// 发送内容更新到服务器
jQuery.ajax({
url: collab_realtime_config.ajax_url,
method: 'POST',
data: {
action: 'collab_update_content',
document_id: this.documentId,
changes: JSON.stringify(changes),
nonce: collab_realtime_config.nonce
},
success: (response) => {
if (response.success) {
// 通过Pusher广播更新
this.channel.trigger('client-content-updated', {
changes: changes,
user_id: collab_realtime_config.current_user_id
});
}
}
});
}
disconnect() {
if (this.pusher) {
this.pusher.disconnect();
}
}
}
### 2.4 文档审阅与批注系统
实现完整的文档审阅功能:
<?php
// 批注系统处理
class CollabCommentSystem {
// 添加批注
public static function add_comment($document_id, $data) {
global $wpdb;
// 验证权限
if (!collab_check_project_permission(
self::get_project_id_from_document($document_id),
get_current_user_id(),
'collab_comment'
)) {
return new WP_Error('permission_denied', '您没有权限添加批注');
}
$comment_data = array(
'document_id' => $document_id,
'user_id' => get_current_user_id(),
'content' => sanitize_textarea_field($data['content']),
'position_data' => json_encode($data['position']),
'resolved' => 0
);
// 如果有版本号,关联到特定版本
if (!empty($data['version_id'])) {
$comment_data['version_id'] = intval($data['version_id']);
}
$wpdb->insert("{$wpdb->prefix}collab_comments", $comment_data);
$comment_id = $wpdb->insert_id;
// 触发实时通知
$realtime = new CollabRealtime();
$realtime->comment_added($document_id, $comment_id, get_current_user_id());
// 发送通知邮件
self::notify_comment_added($document_id, $comment_id);
return $comment_id;
}
// 获取文档批注
public static function get_document_comments($document_id, $version_id = null) {
global $wpdb;
$query = $wpdb->prepare(
"SELECT c.*, u.display_name as user_name, u.user_email,
(SELECT display_name FROM {$wpdb->users} WHERE ID = c.resolved_by) as resolved_by_name
FROM {$wpdb->prefix}collab_comments c
LEFT JOIN {$wpdb->users} u ON c.user_id = u.ID
WHERE c.document_id = %d",
$document_id
);
if ($version_id) {
$query .= $wpdb->prepare(" AND (c.version_id = %d OR c.version_id IS NULL)", $version_id);
}
$query .= " ORDER BY c.created_at ASC";
return $wpdb->get_results($query);
}
// 解决批注
public static function resolve_comment($comment_id, $user_id) {
global $wpdb;
$comment = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}collab_comments WHERE id = %d",
$comment_id
));
if (!$comment) {
return new WP_Error('not_found', '批注不存在');
}
// 检查权限
$project_id = self::get_project_id_from_document($comment->document_id);
if (!collab_check_project_permission($project_id, $user_id, 'collab_comment')) {
return new WP_Error('permission_denied', '您没有权限修改批注');
}
$wpdb->update(
"{$wpdb->prefix}collab_comments",
array(
'resolved' => 1,
'resolved_by' => $user_id,
'resolved_at' => current_time('mysql')
),
array('id' => $comment_id)
);
return true;
}
// 批注通知
private static function notify_comment_added($document_id, $comment_id) {
global $wpdb;
// 获取文档信息
$document = $wpdb->get_row($wpdb->prepare(
"SELECT d.title, p.id as project_id, p.title as project_title
FROM {$wpdb->prefix}collab_documents d
LEFT JOIN {$wpdb->prefix}collab_projects p ON d.project_id = p.id
WHERE d.id = %d",
$document_id
));
// 获取项目成员(除了批注作者)
$members = $wpdb->get_results($wpdb->prepare(
"SELECT m.user_id, u.user_email, u.display_name
FROM {$wpdb->prefix}collab_members m
LEFT JOIN {$wpdb->users} u ON m.user_id = u.ID
WHERE m.project_id = %d AND m.user_id != %d",
$document->project_id, get_current_user_id()
));
// 获取批注内容
$comment = $wpdb->get_row($wpdb->prepare(
"SELECT content FROM {$wpdb->prefix}collab_comments WHERE id = %d",
$comment_id
));
// 发送邮件通知
foreach ($members as $member) {
$subject = sprintf('新批注: %s - %s', $document->project_title, $document->title);
$message = sprintf(
"您好 %s,nn在项目 '%s' 的文档 '%s' 中有一条新批注:nn%snn请登录系统查看:%snn",
$member->display_name,
$document->project_title,
$document->title,
$comment->content,
get_permalink(get_page_by_path('collab-document')) . '?id=' . $document_id
);
wp_mail($member->user_email, $subject, $message);
}
}
private static function get_project_id_from_document($document_id) {
global $wpdb;
$result = $wpdb->get_var($wpdb->prepare(
"SELECT project_id FROM {$wpdb->prefix}collab_documents WHERE id = %d",
$document_id
));
return $result;
}
}
// AJAX处理批注
add_action('wp_ajax_collab_add_comment', 'collab_ajax_add_comment');
function collab_ajax_add_comment() {
check_ajax_referer('collab_nonce', 'nonce');
$document_id = intval($_POST['document_id']);
$content = sanitize_textarea_field($_POST['content']);
$position = json_decode(stripslashes($_POST['position']), true);
$comment_id = CollabCommentSystem::add_comment($document_id, array(
'content' => $content,
'position' => $position
));
if (is_wp_error($comment_id)) {
wp_send_json_error($comment_id->get_error_message());
} else {
wp_send_json_success(array('comment_id' => $comment_id));
}
}
?>
## 第三部分:版本控制与文档历史
### 3.1 版本控制系统实现
<?php
class CollabVersionControl {
// 创建新版本
public static function create_new_version($document_id, $user_id, $changes_summary = '') {
global $wpdb;
// 获取当前文档信息
$document = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}collab_documents WHERE id = %d",
$document_id
));
if (!$document) {
return new WP_Error('not_found', '文档不存在');
}
// 检查权限
if (!collab_check_project_permission($document->project_id, $user_id, 'collab_edit_document')) {
return new WP_Error('permission_denied', '您没有权限创建新版本');
}
// 获取下一个版本号
$next_version = $document->version + 1;
// 复制当前文件为新版本
$original_path = $document->file_path;
$new_filename = self::generate_version_filename($document->title, $next_version, pathinfo($original_path, PATHINFO_EXTENSION));
$new_path = self::get_versions_directory($document_id) . '/' . $new_filename;
// 复制文件
if (!copy($original_path, $new_path)) {
return new WP_Error('file_copy_failed', '无法创建版本副本');
}
// 创建版本记录
$version_data = array(
'document_id' => $document_id,
'version_number' => $next_version,
'file_path' => $new_path,
'changes_summary' => sanitize_text_field($changes_summary),
'created_by' => $user_id
);
$wpdb->insert("{$wpdb->prefix}collab_document_versions", $version_data);
$version_id = $wpdb->insert_id;
// 更新文档版本号
$wpdb->update(
"{$wpdb->prefix}collab_documents",
array('version' => $next_version, 'updated_at' => current_time('mysql')),
array('id' => $document_id)
);
// 记录活动
collab_log_activity($document->project_id, 'version_created', array(
'document_id' => $document_id,
'version' => $next_version,
'user_id' => $user_id
));
return array(
'version_id' => $version_id,
'version_number' => $next_version,
'file_path' => $new_path
);
}
// 获取版本列表
public static function get_document_versions($document_id) {
global $wpdb;
return $wpdb->get_results($wpdb->prepare(
"SELECT v.*, u.display_name as creator_name
FROM {$wpdb->prefix}collab_document_versions v
LEFT JOIN {$wpdb->users} u ON v.created_by = u.ID
WHERE v.document_id = %d
ORDER BY v.version_number DESC",
$document_id
));
}
// 恢复到指定版本
public static function restore_to_version($document_id, $version_id, $user_id) {
global $wpdb;
// 获取文档和版本信息
$document = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}collab_documents WHERE id = %d",
$document_id
));
$version = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}collab_document_versions
WHERE id = %d AND document_id = %d",
$version_id, $document_id
));
if (!$document || !$version) {
return new WP_Error('not_found', '文档或版本不存在');
}
// 检查权限
if (!collab_check_project_permission($document->project_id, $user_id, 'collab_edit_document')) {
return new WP_Error('permission_denied', '您没有权限恢复版本');
}
// 创建当前状态的备份
self::create_new_version($document_id, $user_id, '恢复版本前的备份');
// 复制版本文件到当前文档
if (!copy($version->file_path, $document->file_path)) {
return new WP_Error('restore_failed', '恢复文件失败');
}
// 更新文档信息
$wpdb->update(
"{$wpdb->prefix}collab_documents",
array(
'version' => $version->version_number,
'updated_at' => current_time('mysql')
),
array('id' => $document_id)
);
// 记录活动
collab_log_activity($document->project_id, 'version_restored', array(
'document_id' => $document_id,
'from_version' => $document->version,
'to_version' => $version->version_number,
'user_id' => $user_id
));
return true;
}
// 比较两个版本
public static function compare_versions($document_id, $version1_id, $version2_id) {
global $wpdb;
$version1 = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}collab_document_versions WHERE id = %d",
$version1_id
));
$version2 = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}collab_document_versions WHERE id = %d",
$version2_id
));
if (!$version1 || !$version2) {
return new WP_Error('not_found', '版本不存在');
}
// 根据文件类型使用不同的比较方法
$extension = strtolower(pathinfo($version1->file_path, PATHINFO_EXTENSION));
switch ($extension) {
case 'txt':
case 'md':
return self::compare_text_files($version1->file_path, $version2->file_path);
case 'docx':
return self::compare_docx_files($version1->file_path, $version2->file_path);
case 'pdf':
return self::compare_pdf_files($version1->file_path, $version2->file_path);
default:
return array(
'type' => 'binary',
'message' => '二进制文件,无法显示文本差异'
);
}
}
private static function compare_text_files($file1, $file2) {
$content1 = file_get_contents($file1);
$content2 = file_get_contents($file2);
// 使用文本差异算法
require_once(ABSPATH . 'wp-includes/Text/Diff.php');
require_once(ABSPATH . 'wp-includes/Text/Diff/Renderer.php');
$diff = new Text_Diff('auto', array(explode("n", $content1), explode("n", $content2)));
$renderer = new Text_Diff_Renderer_inline();
return array(
'type' => 'text',
'diff' => $renderer->render($diff)
);
}
private static function generate_version_filename($title, $version, $extension) {
$safe_title = sanitize_title($title);
return sprintf('%s-v%d-%s.%s',
$safe_title,
$version,
date('Ymd-His'),
$extension
);
}
private static function get_versions_directory($document_id) {
$upload_dir = wp_upload_dir();
$versions_dir = $upload_dir['basedir'] . '/collab-versions/' . $document_id;
if (!file_exists($versions_dir)) {
wp_mkdir_p($versions_dir);
}
return $versions_dir;
}
}
?>
## 第四部分:用户界面与体验优化
### 4.1 前端界面构建
使用React构建现代协作界面(简化示例):
// DocumentEditor.jsx - 主文档编辑器组件
import React, { useState, useEffect, useRef } from 'react';
import { CommentList, VersionHistory, OnlineUsers, Toolbar } from './components';
const DocumentEditor = ({ documentId, userId }) => {
const [document, setDocument] = useState(null);
const
