mirror of
https://github.com/typecho/typecho.git
synced 2025-03-31 00:02:25 +02:00
导入逻辑基本完成
This commit is contained in:
parent
3d8fbc8a61
commit
aa2eac4dcc
115
var/CLI.php
115
var/CLI.php
@ -1,115 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 此类主要帮助用户在命令行界面对博客进行管理
|
||||
*
|
||||
* @package Helper
|
||||
* @author joyqi
|
||||
* @version 1.0.0
|
||||
* @link http://typecho.org
|
||||
*/
|
||||
class CLI
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $_args = array();
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $_definition = array();
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $_help = "Usage: php index.php [options] [--] [args...]\n";
|
||||
|
||||
/**
|
||||
* CLI constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->_definition = array(
|
||||
'h' => array(_t('帮助信息')),
|
||||
'v' => array(_t('获取版本信息')),
|
||||
'x' => array(_t('导出数据')),
|
||||
'i' => array(_t('导入数据'))
|
||||
);
|
||||
|
||||
$this->parseArgs();
|
||||
$this->parseDefinition();
|
||||
|
||||
switch (true) {
|
||||
case !empty($this->_args['v']):
|
||||
$this->handleVersion();
|
||||
break;
|
||||
case !empty($this->_args['h']):
|
||||
default:
|
||||
echo $this->_help;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取版本信息
|
||||
*/
|
||||
private function handleVersion()
|
||||
{
|
||||
echo 'Typecho ' . Typecho_Common::VERSION . "\n";
|
||||
echo 'PHP ' . phpversion() . "\n";
|
||||
echo Typecho_Db::get()->getVersion() . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析帮助信息
|
||||
*/
|
||||
private function parseDefinition()
|
||||
{
|
||||
$splitted = false;
|
||||
|
||||
foreach ($this->_definition as $key => $val) {
|
||||
$placeholder = isset($val[1]) ? " <{$val[1]}>" : '';
|
||||
$prefix = strlen($key) > 1 ? '--' : '-';
|
||||
|
||||
if ($prefix == '--' && !$splitted) {
|
||||
$this->_help .= "\n";
|
||||
$splitted = true;
|
||||
}
|
||||
|
||||
$this->_help .= "\n " . str_pad($prefix . $key . $placeholder, 28, ' ', STR_PAD_RIGHT) . $val[0];
|
||||
}
|
||||
|
||||
$this->_help .= "\n\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析命令行参数
|
||||
*/
|
||||
private function parseArgs()
|
||||
{
|
||||
global $argv;
|
||||
|
||||
if ($argv[0] == $_SERVER['PHP_SELF']) {
|
||||
array_shift($argv);
|
||||
}
|
||||
|
||||
$last = NULL;
|
||||
|
||||
foreach ($argv as $arg) {
|
||||
if (preg_match("/^\-\-([_a-z0-9-]+)(=(.+))?$/i", $arg, $matches)) {
|
||||
$last = $matches[1];
|
||||
$val = isset($matches[3]) ? $matches[3] : true;
|
||||
|
||||
$this->_args[$last] = $val;
|
||||
} else if (preg_match("/^\-([a-z0-9])(.*)$/i", $arg, $matches)) {
|
||||
$last = $matches[1];
|
||||
$val = $matches[2];
|
||||
|
||||
$this->_args[$last] = strlen($val) == 0 ? true : $val;
|
||||
} else if (!empty($last)) {
|
||||
$this->_args[$last] = $arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -14,17 +14,29 @@ if (!defined('__TYPECHO_ROOT_DIR__')) exit;
|
||||
*/
|
||||
class Widget_Backup extends Widget_Abstract_Options implements Widget_Interface_Do
|
||||
{
|
||||
const HEADER = '%TYPECHO_BACKUP_FILE%';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $_types = [
|
||||
private $_types = array(
|
||||
'contents' => 1,
|
||||
'comments' => 2,
|
||||
'metas' => 3,
|
||||
'relationships' => 4,
|
||||
'users' => 5,
|
||||
'fields' => 6
|
||||
];
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $_cleared = array();
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $_login = false;
|
||||
|
||||
/**
|
||||
* @param $type
|
||||
@ -41,15 +53,126 @@ class Widget_Backup extends Widget_Abstract_Options implements Widget_Interface_
|
||||
$schema[$key] = strlen($val);
|
||||
$body .= $val;
|
||||
}
|
||||
$schemaHeader = json_encode($schema);
|
||||
$schemaHeader = Json::encode($schema);
|
||||
|
||||
$buffer .= pack('C', $type); // 写入类型
|
||||
$buffer .= pack('v', $type); // 写入类型
|
||||
$buffer .= pack('v', strlen($schemaHeader)); // 写入头
|
||||
$buffer .= pack('v', strlen($body)); // 写入头
|
||||
$buffer .= $schemaHeader . $body;
|
||||
|
||||
return $buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析数据
|
||||
*
|
||||
* @param $file
|
||||
*/
|
||||
private function extractData($file)
|
||||
{
|
||||
$fp = @fopen($file, 'rb');
|
||||
|
||||
if (!$fp) {
|
||||
$this->widget('Widget_Notice')->set(_t('无法读取备份文件'), 'error');
|
||||
$this->response->goBack();
|
||||
}
|
||||
|
||||
$fileHeader = @fread($fp, strlen(self::HEADER));
|
||||
|
||||
if (!$fileHeader || $fileHeader != self::HEADER) {
|
||||
$this->widget('Widget_Notice')->set(_t('备份文件格式错误'), 'error');
|
||||
$this->response->goBack();
|
||||
}
|
||||
|
||||
while (!feof($fp)) {
|
||||
$type = unpack('v', fread($fp, 2))[0];
|
||||
$headerLen = unpack('v', fread($fp, 2))[0];
|
||||
$bodyLen = unpack('v', fread($fp, 2))[0];
|
||||
$header = fread($fp, $headerLen);
|
||||
$body = fread($fp, $bodyLen);
|
||||
|
||||
$this->processData($type, $header, $body);
|
||||
}
|
||||
|
||||
$this->widget('Widget_Notice')->set(_t('备份数据倒入完成'), 'success');
|
||||
$this->response->goBack();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $type
|
||||
* @param $header
|
||||
* @param $body
|
||||
*/
|
||||
private function processData($type, $header, $body)
|
||||
{
|
||||
$table = array_search($type, $this->_types);
|
||||
|
||||
if (!empty($table)) {
|
||||
$schema = Json::decode($header, true);
|
||||
$data = [];
|
||||
$offset = 0;
|
||||
|
||||
foreach ($schema as $key => $val) {
|
||||
$data[$key] = substr($body, $offset, $val);
|
||||
$offset += $val;
|
||||
}
|
||||
|
||||
$this->importData($table, $data);
|
||||
} else {
|
||||
Typecho_Plugin::factory(__CLASS__)->import($type, $header, $body);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 倒入单条数据
|
||||
*
|
||||
* @param $table
|
||||
* @param $data
|
||||
*/
|
||||
private function importData($table, $data)
|
||||
{
|
||||
$db = $this->db;
|
||||
|
||||
try {
|
||||
if (empty($this->_cleared[$table])) {
|
||||
// 清除数据
|
||||
$db->query($db->delete('table.' . $table));
|
||||
$this->_cleared[$table] = true;
|
||||
}
|
||||
|
||||
if (!$this->_login && 'users' == $table && $data['group'] == 'administrator') {
|
||||
// 重新登录
|
||||
$this->reLogin($data);
|
||||
}
|
||||
|
||||
$db->query($db->insert('table.' . $table)->rows($data));
|
||||
} catch (Exception $e) {
|
||||
$this->widget('Widget_Notice')->set(_t('备份过程中遇到如下错误: %s', $e->getMessage()), 'error');
|
||||
$this->response->goBack();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 备份过程会重写用户数据
|
||||
* 所以需要重新登录当前用户
|
||||
*
|
||||
* @param $user
|
||||
*/
|
||||
private function reLogin(&$user)
|
||||
{
|
||||
if (empty($user['authCode'])) {
|
||||
$user['authCode'] = function_exists('openssl_random_pseudo_bytes') ?
|
||||
bin2hex(openssl_random_pseudo_bytes(16)) : sha1(Typecho_Common::randString(20));
|
||||
}
|
||||
|
||||
$user['activated'] = $this->options->gmtTime;
|
||||
$user['logged'] = $user['activated'];
|
||||
|
||||
Typecho_Cookie::set('__typecho_uid', $user['uid']);
|
||||
Typecho_Cookie::set('__typecho_authCode', Typecho_Common::hash($user['authCode']));
|
||||
$this->_login = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出数据
|
||||
*/
|
||||
@ -60,8 +183,8 @@ class Widget_Backup extends Widget_Abstract_Options implements Widget_Interface_
|
||||
$this->response->setHeader('Content-Disposition', 'attachment; filename="'
|
||||
. date('Ymd') . '_' . $host . '_' . uniqid() . '.dat"');
|
||||
|
||||
$buffer = '';
|
||||
$db = Typecho_Db::get();
|
||||
$buffer = self::HEADER;
|
||||
$db = $this->db;
|
||||
|
||||
foreach ($this->_types as $type => $val) {
|
||||
$page = 1;
|
||||
@ -89,6 +212,34 @@ class Widget_Backup extends Widget_Abstract_Options implements Widget_Interface_
|
||||
Typecho_Plugin::factory(__CLASS__)->export();
|
||||
}
|
||||
|
||||
/**
|
||||
* 倒入数据
|
||||
*/
|
||||
private function import()
|
||||
{
|
||||
$path = NULL;
|
||||
|
||||
if (!empty($_FILES)) {
|
||||
$file = array_pop($_FILES);
|
||||
|
||||
if (0 == $file['error'] && is_uploaded_file($file['tmp_name'])) {
|
||||
$path = $file['tmp_name'];
|
||||
} else {
|
||||
$this->widget('Widget_Notice')->set(_t('备份文件上传失败'), 'error');
|
||||
$this->response->goBack();
|
||||
}
|
||||
} else if ($this->request->is('file')) {
|
||||
$path = __TYPECHO_BACKUP_DIR__ . '/' . $this->request->get('file');
|
||||
|
||||
if (!file_exists($path)) {
|
||||
$this->widget('Widget_Notice')->set(_t('备份文件不存在'), 'error');
|
||||
$this->response->goBack();
|
||||
}
|
||||
}
|
||||
|
||||
$this->extractData($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定动作
|
||||
*/
|
||||
|
@ -23,10 +23,6 @@ class Widget_Init extends Typecho_Widget
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
if (php_sapi_name() == 'cli') {
|
||||
define('__TYPECHO_CLI__', true);
|
||||
}
|
||||
|
||||
/** 对变量赋值 */
|
||||
$options = $this->widget('Widget_Options');
|
||||
|
||||
@ -36,10 +32,9 @@ class Widget_Init extends Typecho_Widget
|
||||
Typecho_I18n::setLang($dir . '/' . $options->lang . '.mo');
|
||||
}
|
||||
|
||||
/** 进入命令行格式 */
|
||||
if (defined('__TYPECHO_CLI__')) {
|
||||
new CLI();
|
||||
exit(0);
|
||||
/** 备份文件目录初始化 */
|
||||
if (!defined('__TYPECHO_BACKUP_DIR__')) {
|
||||
define('__TYPECHO_BACKUP_DIR__', __TYPECHO_ROOT_DIR__ . '/usr/backups');
|
||||
}
|
||||
|
||||
/** cookie初始化 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user