433 lines
16 KiB
PHP
433 lines
16 KiB
PHP
<?php
|
|
|
|
namespace app\admin\controller;
|
|
|
|
use addons\docs\library\Service;
|
|
use app\common\controller\Backend;
|
|
use fast\Http;
|
|
use fast\Tree;
|
|
use RecursiveDirectoryIterator;
|
|
use RecursiveIteratorIterator;
|
|
use think\Exception;
|
|
use think\Validate;
|
|
use ZipArchive;
|
|
|
|
/**
|
|
*
|
|
* 文档管理
|
|
* @icon fa fa-circle-o
|
|
*/
|
|
class Docs extends Backend
|
|
{
|
|
protected $docs = [];
|
|
protected $docsdata = [];
|
|
protected $typeList = [];
|
|
protected $typeColor = [];
|
|
protected $noNeedRight = ["selectpage_type", "get_parent_options"];
|
|
|
|
public function _initialize()
|
|
{
|
|
parent::_initialize();
|
|
|
|
$this->docsdata = Service::getDocsData(false);
|
|
|
|
$config = get_addon_config('docs');
|
|
$this->typeList = $this->typeColor = [];
|
|
$langs = $config['lang'];
|
|
$colors = ["primary", "success", "danger", "warning", "info"];
|
|
foreach ($this->docsdata as $type => $pages) {
|
|
$this->typeList[$type] = $langs[$type] ?? $type;
|
|
$this->typeColor[$type] = array_pop($colors);
|
|
}
|
|
$this->view->assign('typeList', $this->typeList);
|
|
$this->assignconfig('typeList', $this->typeList);
|
|
|
|
$this->view->assign('typeColor', $this->typeColor);
|
|
$this->assignconfig('typeColor', $this->typeColor);
|
|
$this->assignconfig('htmlDetect', (bool)($config['htmldetect'] ?? false));
|
|
}
|
|
|
|
/**
|
|
* 查看
|
|
*/
|
|
public function index()
|
|
{
|
|
//设置过滤方法
|
|
$this->request->filter(['strip_tags']);
|
|
if ($this->request->isAjax()) {
|
|
$filter = $this->request->request('filter', '');
|
|
$filter = (array)json_decode($filter, true);
|
|
$filter['type'] = isset($filter['type']) && $filter['type'] ? $filter['type'] : '';
|
|
$search = $this->request->request("search");
|
|
list($where, $sort, $order, $offset, $limit) = $this->buildparams();
|
|
$list = [];
|
|
$docsdata = $this->docsdata;
|
|
if ($filter['type']) {
|
|
$docsdata = isset($docsdata[$filter['type']]) ? [$filter['type'] => $docsdata[$filter['type']]] : [];
|
|
}
|
|
foreach ($docsdata as $index => $docsdatum) {
|
|
$tree = Tree::instance();
|
|
$data = $tree->init($docsdatum, 'parent')->getTreeArray('');
|
|
$list = array_merge($list, $tree->getTreeList($data, 'title'));
|
|
}
|
|
$ids = [];
|
|
foreach ($list as $index => $item) {
|
|
$ids[] = $item['id'];
|
|
}
|
|
foreach ($docsdata as $index => $docsdatum) {
|
|
foreach ($docsdatum as $key => $item) {
|
|
if (!in_array($item['id'], $ids)) {
|
|
$list[] = $item;
|
|
}
|
|
}
|
|
}
|
|
//foreach ($this->docsdata as $type => $pages) {
|
|
// if ($filter['type'] && $type != $filter['type']) {
|
|
// continue;
|
|
// }
|
|
// foreach ($pages as $index => $page) {
|
|
// if (!$search || stripos($page['relative'], $search) !== false || stripos($page['title'], $search) !== false) {
|
|
// $list[] = $page;
|
|
// }
|
|
// }
|
|
//}
|
|
$total = count($list);
|
|
$result = array("total" => $total, "rows" => array_slice($list, $offset, $limit));
|
|
return json($result);
|
|
}
|
|
return $this->view->fetch();
|
|
}
|
|
|
|
/**
|
|
* 添加
|
|
*/
|
|
public function add()
|
|
{
|
|
if ($this->request->isPost()) {
|
|
$params = $this->request->post("row/a");
|
|
if ($params) {
|
|
try {
|
|
if (!$params['md']) {
|
|
throw new Exception("MD文件不能为空");
|
|
}
|
|
$config = get_addon_config('docs');
|
|
$params['md'] = trim($params['md']);
|
|
//未设置后缀
|
|
if (stripos($params['md'], ".{$config['suffix']}") === false) {
|
|
$params['md'] .= ".{$config['suffix']}";
|
|
}
|
|
if (mb_substr_count($params['md'], "/") > 1) {
|
|
throw new Exception("MD文件中只能包含一个/字符");
|
|
}
|
|
if (mb_substr_count($params['md'], ".") > 1) {
|
|
throw new Exception("MD文件中只能包含一个.字符");
|
|
}
|
|
if (!Validate::is($params['md'], "/^[A-Za-z0-9\-\_\.\/]+$/")) {
|
|
throw new Exception("MD文件只能是字母数字下划线");
|
|
}
|
|
$exist = Service::getMarkdownData($params['md']);
|
|
if ($exist) {
|
|
throw new Exception("对应的MD文件已经存在");
|
|
}
|
|
$parent = $params['parent'] ?? '';
|
|
$type = $params['type'] ?? '';
|
|
if ($parent) {
|
|
if ($parent == str_replace(".md", "", $params['md'])) {
|
|
$this->error("父级不能为当前文件");
|
|
}
|
|
if (!isset($this->docsdata[$type])) {
|
|
$this->error("未找到指定分类");
|
|
}
|
|
$nameArr = [];
|
|
foreach ($this->docsdata[$type] as $index => $docsdatum) {
|
|
$nameArr[] = $docsdatum['name'];
|
|
}
|
|
if (!in_array($parent, $nameArr)) {
|
|
$this->error("未找到指定父级数据");
|
|
}
|
|
}
|
|
Service::setMarkdownData($params['md'], $params);
|
|
} catch (Exception $e) {
|
|
$this->error($e->getMessage());
|
|
}
|
|
$this->success();
|
|
}
|
|
$this->error(__('Parameter %s can not be empty', ''));
|
|
}
|
|
return $this->view->fetch();
|
|
}
|
|
|
|
/**
|
|
* 编辑
|
|
*/
|
|
public function edit($ids = null)
|
|
{
|
|
$ids = $ids ? $ids : $this->request->request('ids');
|
|
$row = Service::getMarkdownData($ids, true, false);
|
|
if (!$row) {
|
|
$this->error(__('No Results were found'));
|
|
}
|
|
if ($this->request->isPost()) {
|
|
$params = $this->request->post("row/a");
|
|
if ($params) {
|
|
$config = get_addon_config('docs');
|
|
$params['md'] = trim(isset($params['md']) && $params['md'] ? $params['md'] : $row['relative']);
|
|
$md = $params['md'];
|
|
$parent = $params['parent'] ?? '';
|
|
$type = $params['type'] ?? '';
|
|
if ($parent) {
|
|
if ($parent == str_replace(".md", "", $ids)) {
|
|
$this->error("父级不能为当前文件");
|
|
}
|
|
if (!isset($this->docsdata[$type])) {
|
|
$this->error("未找到指定分类");
|
|
}
|
|
$nameArr = [];
|
|
foreach ($this->docsdata[$type] as $index => $docsdatum) {
|
|
$nameArr[] = $docsdatum['name'];
|
|
}
|
|
if (!in_array($parent, $nameArr)) {
|
|
$this->error("未找到指定父级数据");
|
|
}
|
|
}
|
|
//如果文件名有变更,删除原文件
|
|
if ($md != $row['relative']) {
|
|
//未设置后缀
|
|
if (stripos($params['md'], ".{$config['suffix']}") === false) {
|
|
$params['md'] .= ".{$config['suffix']}";
|
|
}
|
|
if (mb_substr_count($params['md'], "/") > 1) {
|
|
throw new Exception("MD文件中只能包含一个/字符");
|
|
}
|
|
if (mb_substr_count($params['md'], ".") > 1) {
|
|
throw new Exception("MD文件中只能包含一个.字符");
|
|
}
|
|
if (!Validate::is($params['md'], "/^[A-Za-z0-9\-\_\.\/]+$/")) {
|
|
throw new Exception("MD文件只能是字母数字下划线");
|
|
}
|
|
$exist = Service::getMarkdownData($params['md']);
|
|
if ($exist) {
|
|
throw new Exception("对应的MD文件已经存在");
|
|
}
|
|
}
|
|
try {
|
|
//如果变更了分类,则所有子文档分类都需要变更
|
|
if ($type != $row['type']) {
|
|
$docs = $this->docsdata[$row['type']] ?? [];
|
|
$childrenList = Tree::instance()->init($docs, 'parent')->getChildren($row['id']);
|
|
foreach ($childrenList as $index => $item) {
|
|
$data = Service::getMarkdownData($item['relative'], true);
|
|
$data['type'] = $type;
|
|
Service::setMarkdownData($item['relative'], $data);
|
|
}
|
|
}
|
|
Service::setMarkdownData($md, $params);
|
|
//如果文件名有变更,删除原文件
|
|
if ($md != $row['relative']) {
|
|
unlink(Service::getDocsSourceDir() . $row['relative']);
|
|
}
|
|
} catch (Exception $e) {
|
|
$this->error($e->getMessage());
|
|
}
|
|
$this->success();
|
|
}
|
|
$this->error(__('Parameter %s can not be empty', ''));
|
|
}
|
|
$allowEditName = true;
|
|
$parentList = isset($this->docsdata[$row['type']]) ? $this->docsdata[$row['type']] : [];
|
|
foreach ($parentList as $index => $item) {
|
|
if ($item['parent'] == $row['name']) {
|
|
$allowEditName = false;
|
|
break;
|
|
}
|
|
}
|
|
$parentOptions = $this->getParentList($row['type'], $row['id'], $row['parent']);
|
|
$this->view->assign("parentOptions", $parentOptions);
|
|
$this->view->assign("allowEditName", $allowEditName);
|
|
$this->view->assign("row", $row);
|
|
return $this->view->fetch();
|
|
}
|
|
|
|
/**
|
|
* 删除
|
|
*/
|
|
public function del($ids = "")
|
|
{
|
|
if (!$this->request->isPost()) {
|
|
$this->error(__("Invalid parameters"));
|
|
}
|
|
$ids = $ids ? $ids : $this->request->post("ids");
|
|
if ($ids) {
|
|
if (stripos($ids, ',') !== false) {
|
|
$this->error("文档不支持批量删除");
|
|
}
|
|
$row = Service::getMarkdownData($ids, false, false);
|
|
if (!$row) {
|
|
$this->error(__('No Results were found'));
|
|
}
|
|
try {
|
|
unlink(Service::getDocsSourceDir() . $row['relative']);
|
|
Service::refreshDocsData();
|
|
} catch (Exception $e) {
|
|
$this->error($e->getMessage());
|
|
}
|
|
$this->success();
|
|
}
|
|
$this->error(__('Parameter %s can not be empty', 'ids'));
|
|
}
|
|
|
|
/**
|
|
* 刷新
|
|
*/
|
|
public function refresh()
|
|
{
|
|
Service::refreshDocsData();
|
|
$this->success();
|
|
}
|
|
|
|
/**
|
|
* 导出
|
|
*/
|
|
public function export()
|
|
{
|
|
try {
|
|
//重新生成一次文档
|
|
Service::build();
|
|
} catch (\Exception $e) {
|
|
$this->error($e->getMessage());
|
|
}
|
|
$distDir = Service::getDocsDistDir();
|
|
$zipFile = RUNTIME_PATH . 'addons' . DS . 'export-docs-' . date('YmdHis') . '.zip';
|
|
|
|
$rootPath = realpath($distDir);
|
|
|
|
$zip = new ZipArchive();
|
|
$zip->open($zipFile, ZipArchive::CREATE | ZipArchive::OVERWRITE);
|
|
|
|
$files = new RecursiveIteratorIterator(
|
|
new RecursiveDirectoryIterator($rootPath),
|
|
RecursiveIteratorIterator::LEAVES_ONLY
|
|
);
|
|
|
|
foreach ($files as $name => $file) {
|
|
if (!$file->isDir()) {
|
|
$filePath = $file->getRealPath();
|
|
$relativePath = substr($filePath, strlen($rootPath) + 1);
|
|
$relativePath = str_replace('\\', '/', $relativePath);
|
|
|
|
$zip->addFile($filePath, $relativePath);
|
|
}
|
|
}
|
|
|
|
$zip->close();
|
|
Http::sendToBrowser($zipFile);
|
|
}
|
|
|
|
/**
|
|
* 批量更新
|
|
* @internal
|
|
*/
|
|
public function status($ids = "")
|
|
{
|
|
$ids = $ids ? $ids : $this->request->param("ids");
|
|
if ($ids) {
|
|
if ($this->request->has('params')) {
|
|
parse_str($this->request->post("params"), $values);
|
|
if ($values && isset($values['isnew'])) {
|
|
$row = Service::getMarkdownData($ids, true, false);
|
|
$row = array_merge($row, $values);
|
|
try {
|
|
Service::setMarkdownData($ids, $row);
|
|
} catch (Exception $e) {
|
|
$this->error($e->getMessage());
|
|
}
|
|
$this->success();
|
|
} else {
|
|
$this->error(__('You have no permission'));
|
|
}
|
|
}
|
|
}
|
|
$this->error(__('Parameter %s can not be empty', 'ids'));
|
|
}
|
|
|
|
/**
|
|
* 批量更新
|
|
* @internal
|
|
*/
|
|
public function multi($ids = "")
|
|
{
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* 动态下拉选择类型
|
|
* @internal
|
|
*/
|
|
public function selectpage_type()
|
|
{
|
|
$list = [];
|
|
$word = (array)$this->request->request("q_word/a");
|
|
$field = $this->request->request('showField');
|
|
$keyValue = $this->request->request('keyValue');
|
|
$config = get_addon_config('docs');
|
|
$typeList = [];
|
|
$langs = $config['lang'];
|
|
foreach ($this->docsdata as $type => $pages) {
|
|
$typeList[$type] = isset($langs[$type]) ? $langs[$type] : $type;
|
|
}
|
|
$typeArr = $typeList;
|
|
if (!$keyValue) {
|
|
if (array_filter($word)) {
|
|
foreach ($word as $k => $v) {
|
|
$list[] = ['id' => $v, $field => $v];
|
|
}
|
|
}
|
|
foreach ($typeArr as $index => $item) {
|
|
$list[] = ['id' => $index, $field => $item];
|
|
}
|
|
} else {
|
|
$list[] = ['id' => $keyValue, $field => $typeArr[$keyValue] ?? $keyValue];
|
|
}
|
|
return json(['total' => count($list), 'list' => $list]);
|
|
}
|
|
|
|
/**
|
|
* 获取父级options
|
|
* @internal
|
|
*/
|
|
public function get_parent_options()
|
|
{
|
|
$type = $this->request->request('type');
|
|
$origintype = $this->request->request('origintype');
|
|
$id = $parent = null;
|
|
if ($type == $origintype) {
|
|
$id = $this->request->request('id');
|
|
$parent = $this->request->request('parent');
|
|
}
|
|
$parentOptions = '<option value="">无</option>';
|
|
if ($type) {
|
|
$parentOptions .= $this->getParentList($type, $id, $parent);
|
|
}
|
|
$this->success("", '', ['options' => $parentOptions]);
|
|
}
|
|
|
|
protected function getParentList($type, $id = null, $parent = null)
|
|
{
|
|
$parentList = [];
|
|
$parentOptions = '';
|
|
if (!isset($this->docsdata[$type])) {
|
|
return $parentOptions;
|
|
}
|
|
foreach ($this->docsdata[$type] as $index => $item) {
|
|
$parentList[] = ['id' => $item['id'], 'pid' => $item['parent'], 'name' => $item['title']];
|
|
}
|
|
if ($parentList) {
|
|
$tree = Tree::instance()->init($parentList);
|
|
$disableIds = $id ? $tree->getChildrenIds($id, true) : [];
|
|
$selectIds = $parent ? [$parent] : [];
|
|
$parentOptions = $tree->getTree('', '<option value=@id @selected @disabled>@spacer@name</option>', $selectIds, $disableIds);
|
|
}
|
|
return $parentOptions;
|
|
}
|
|
}
|