cms-manage/app/controller/backend/DesignController.php

668 lines
23 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace app\controller\backend;
use app\model\BigField;
use app\model\Category;
use app\model\CategorySubContent;
use app\model\SubContent;
use app\model\Theme;
use app\model\ThemeFile;
use app\model\Website;
use app\model\WebsiteSetting;
use app\service\ApiService;
use app\service\ThemeFileService;
use app\service\ThemeService;
use app\validate\WebsiteSettingValidate;
use think\exception\ValidateException;
use think\response\Json;
class DesignController extends BaseController
{
protected $designBase = 'public/design/designPage';
protected function sanitizeFileName($file)
{
//sanitize, remove double dot .. and remove get parameters if any
$file = CMS_ROOT . 'public/design/designPage/' . preg_replace('@\?.*$@', '', preg_replace('@\.{2,}@', '', preg_replace('@[^\/\\a-zA-Z0-9\-\._]@', '', $file)));
return $file;
}
public function save()
{
return $this->publish(1);
// $param = $this->request->param();
// // 数据验证
// try {
// validate(WebsiteSettingValidate::class)->scene('read')->check($param);
// } catch (ValidateException $e) {
// return jsonReturn(-1, $e->getError());
// }
//
// define('MAX_FILE_LIMIT', 1024 * 1024 * 2);//2 Megabytes max html file size
//
// $html = "";
// if (isset($_POST['startTemplateUrl']) && !empty($_POST['startTemplateUrl'])) {
// $startTemplateUrl = $this->sanitizeFileName($_POST['startTemplateUrl']);
// $html = file_get_contents($startTemplateUrl);
// } else if (isset($_POST['html'])) {
// $html = substr($_POST['html'], 0, MAX_FILE_LIMIT);
// }
//
// $where = [
// 'seller_id' => $this->admin['seller_id'],
// 'website_id' => $param['website_id'],
// 'lang' => $param['lang'],
// 'is_active' => 1
// ];
// $Theme = new Theme();
// $theme = $Theme->getActiveTheme($where)['data']->toArray();
// $themeName = $theme['theme'];
//
// $pathInfo = pathinfo($param['file']);
// $filename = $pathInfo['filename'];
//// $file = $this->sanitizeFileName($_POST['file']);
// $file = CMS_ROOT . "view/website/{$param['website_id']}/{$param['lang']}/{$themeName}/single_{$filename}.html";
//
// if (file_put_contents($file, $html)) {
// echo "保存成功 $file";
// } else {
// header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error', true, 500);
// echo "保存失败! $file\n可能的原因是缺少写权限或文件路径不正确";
// }
// exit();
}
/**
* @param $saveType 1保存2发布
* @return Json|void
*/
public function publish($saveType = 2)
{
$param = $this->request->param();
// 数据验证
try {
validate(WebsiteSettingValidate::class)->scene('read')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
define('MAX_FILE_LIMIT', 1024 * 1024 * 2);//2 Megabytes max html file size
$html = "";
if (isset($param['startTemplateUrl']) && !empty($param['startTemplateUrl'])) {
$startTemplateUrl = $this->sanitizeFileName($param['startTemplateUrl']);
$html = file_get_contents($startTemplateUrl);
} else if (isset($_POST['html'])) {
$html = substr($_POST['html'], 0, MAX_FILE_LIMIT);
}
preg_match_all( '~<img.*?src=["\']+(.*?)["\']+.*>~' , $html, $match );
$imgTagArr = $match[0];
$imgSrcArr = $match[1];
$maxImgSize = config('system.lazy_load_size'); // 读取文件大小,大于 配置文件 做懒加载
$oldImgTagArr = [];
$newImgTagArr = [];
foreach ($imgSrcArr as $key => $value) {
// 存在无需懒加载的标签,跳过
if (strpos($imgTagArr[$key], 'no-lazy') !== false) {
continue;
}
if (empty($value)) {
continue;
}
if (strpos($value, 'data:') === 0) {
continue;
}
if (strpos($value, 'http:') === 0) {
$fileUrl = $value;
$res = @get_headers($fileUrl,true);
if (!isset($res['Content-Length'])) {
continue;
}
$filesize = round($res['Content-Length']/1024,2);
}
else {
$publicPath = CMS_ROOT . 'public';
if (strpos($value, '/') !== 0) {
$publicPath .= '/';
}
$fileUrl = $publicPath . $value;
if (!file_exists($fileUrl)) {
continue;
}
$filesize = filesize($fileUrl);
$filesize /= pow(1024, 1);
}
if ($filesize > $maxImgSize) {
$imgTagArr[$key] = str_replace("data-src='{$value}'", '', $imgTagArr[$key]);
$imgTagReplace = str_replace($value, '/system_file/loading.png', $imgTagArr[$key]);
$replaceStr = " data-src='{$value}' ";
$imgTagReplace = substr_replace($imgTagReplace, $replaceStr, 4, 0);
$oldImgTagArr[] = $imgTagArr[$key];
$newImgTagArr[] = $imgTagReplace;
}
}
if (!empty($oldImgTagArr)) {
$html = str_replace($oldImgTagArr, $newImgTagArr, $html);
}
$jsStr = '
<script id="loadingJs" src="/system_file/js/huocms-loading.js"></script>
</head>';
if (strpos($html, '/system_file/js/huocms-loading.js') === false) {
$html = str_replace('</head>', $jsStr, $html);
}
$html = \phpQuery::newDocument($html);
// 根据section的顺序确定各个部分插入的顺序
$html['head #viewHelp']->remove();//移除可视化时用于方便界面编辑的css
$blockHtmlArr = [];
$where = [
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['website_id'],
'lang' => $param['lang'],
'is_active' => 1
];
$Theme = new Theme();
$themeFileModel = new ThemeFile();
$theme = $Theme->getActiveTheme($where)['data']->toArray();
$themeName = $theme['theme'];
$BigField = new BigField();
$version = $BigField->where([
['seller_id', '=', $this->admin['seller_id']],
['theme_id', '=', $theme['id']],
])->max('version');
$version = $version ?: 0;
$version++;
$version = intval($version);
\phpQuery::each($html['body .huocms-block'], function ($key, $item) use ($html, &$blockHtmlArr, $param, $themeName, $saveType, $theme, $version) {
$domObj = $html['.huocms-block:eq('.$key.')'];
$includePath = $domObj->attr('data-inclue-path'); // 自己有包含路径或某个子元素有包含路径,需要将代码替换掉
$components = $domObj->find('.huocms-components');
foreach ($components as $obj) {
$subIncludePath = pq($obj)->attr('data-inclue-path');
if (!empty($subIncludePath)) {
if (pq($obj)->hasClass('huocms-static-edit')) {
$content = pq($obj)->htmlOuter();
$themeFileName = "{$subIncludePath}.html";
$filename = $subIncludePath;
ThemeService::saveThemeFileHistory($param['website_id'], $param['lang'], $theme, $this->admin['seller_id'], $themeFileName, $filename, $content, '', '', $version);
}
$includeHtml = '
{include file="'.$subIncludePath.'"}
';
$next = pq($obj)->next();
$parent = pq($obj)->parent();
if (empty($next->html())) {
pq($obj)->remove();
$parent->append($includeHtml);
} else {
pq($obj)->remove();
$next->before($includeHtml);
}
}
}
$blockHtml = '';
if (!empty($includePath)) {
if ($domObj->hasClass('huocms-static-edit')) {
$content = $domObj->htmlOuter();
$themeFileName = "{$includePath}.html";
$filename = $includePath;
ThemeService::saveThemeFileHistory($param['website_id'], $param['lang'], $theme, $this->admin['seller_id'], $themeFileName, $filename, $content, '', '', $version);
}
$domObj = '
{include file="'.$includePath.'"}
';
$blockHtml = $domObj;
$blockHtmlArr[] = $blockHtml;
return $item;
}
$tmpTemplate = $domObj->attr('data-huocms-block');
$blockId = $domObj->attr('data-huocms-blockid');
$blockType = $this->getBlockType($domObj);
// echo $blockType . '<br/>';
if (!empty($tmpTemplate) && !empty($blockId)) {
$blockTemplatePath = CMS_ROOT . $this->designBase . DIRECTORY_SEPARATOR . $tmpTemplate;
$blockHtml = file_get_contents($blockTemplatePath);
$blockHtml = $this->replaceHtml($blockType, $blockId, $blockHtml, $domObj);
} else {
$blockHtml = $domObj;
}
$blockHtmlArr[] = $blockHtml;
return $item;
});
$html['body .huocms-block']->remove();
$html['body #think_page_trace']->remove();
$html['body #think_page_trace_open']->remove();
$blockHtmlArr = array_reverse($blockHtmlArr);
foreach ($blockHtmlArr as $value) {
$html['body']->prepend($value);
}
// exit();
//保存生成的html
$pathInfo = pathinfo($param['file']);
$filename = $pathInfo['filename'];
$filePath = CMS_ROOT . "public/themes/website/{$param['website_id']}/{$param['lang']}/{$themeName}/{$filename}.html";
$viewFilePath = CMS_ROOT . "public/themes/hc_original/{$themeName}/{$filename}.html";
// 替换SEO
$html['head meta[name="description"]']->remove();
$html['head meta[name="keywords"]']->remove();
$html['head title']->remove();
$html['head']->prepend($this->getSeoHtml());
$themeFileName = "{$filename}.html";
// 保存至版本数据库中
$content = $html->html();
ThemeService::saveThemeFileHistory($param['website_id'], $param['lang'], $theme, $this->admin['seller_id'], $themeFileName, $filename, $content, '', '', $version);
if ($saveType == 1) {
echo "保存成功 !";
return;
}
//更新theme_file
$themeService = new ThemeService();
$suffix = config('view.view_suffix');
$res = $themeService->publishThemeFile($theme['id'],$param['website_id'],$this->admin['seller_id'],$suffix, $version);
$res = $res->getData();
if ($res['code'] != 0) {
echo $res['msg'];
return;
}
$themeFileModel->updateThemeFile([
['file', '=', $themeFileName],
['website_id', '=', $param['website_id']],
['lang', '=', $param['lang']],
], ['is_design' => 1, 'design_path' => $themeFileName]);
echo "发布成功 !";
return;
}
protected function getBlockType($node) {
$classes = explode( ' ', $node->attr('class'));
$blockType = '';
foreach ($classes as $class) {
if ($class != 'huocms-components' && $class != 'huocms-block') {
$blockType = $class;
break;
}
}
return $blockType;
}
public function replaceHtml($blockType, $id, $html, $node)
{
if (strpos($blockType, 'block') !== false) {
// 替换模板里的navID
$blockHtml = str_replace('hcTaglib:category id="1"', 'hcTaglib:category id="' . $id . '"', $html);
} elseif ($blockType == 'huocms-nav') {
// 替换模板里的navID
$blockHtml = str_replace('hcTaglib:nav cid="2"', 'hcTaglib:nav cid="' . $id . '"', $html);
} elseif ($blockType == 'huocms-banner') {
// 替换模板里的navID
$blockHtml = str_replace('hcTaglib:slide cid="1"', 'hcTaglib:slide cid="' . $id . '"', $html);
} else {
$blockHtml = $node;
}
return $blockHtml;
}
public function getSeoHtml()
{
return '
<title>{$current_cate.seo_title|default=$current_cate.title}</title>
<meta name="description" content="{$current_cate.seo_description|default=$current_cate.title}">
<meta name="keywords" content="{$current_cate.seo_keywords|default=$current_cate.title}">';
}
// setting接口
public function getSetting()
{
}
/**
* 单个链接列表接口
* 基础链接客服链接icp备案链接公安备案链接
* 栏目链接
* 内容链接
* @return Json
*/
public function getLinkList()
{
$param = $this->request->param();
// 数据验证
try {
validate(WebsiteSettingValidate::class)->scene('read')->check($param);
} catch (ValidateException $e) {
return jsonReturn(-1, $e->getError());
}
$where = [
'seller_id' => $this->admin['seller_id'],
'website_id' => $param['website_id'],
'lang' => $param['lang'],
];
$websiteSetting = new WebsiteSetting();
$res = $websiteSetting->getWebsiteSetting($where);
$res['data'] = $res['data']->toArray();
$res['data']['setting'] = json_decode($res['data']['setting'], true);
$link = [
'name' => '基本链接',
'value' => '',
'child' => [
[
'name' => '客服链接',
'value' => $res['data']['setting']['customer_code'] ?? '',
],
[
'name' => 'icp备案链接',
'value' => $res['data']['setting']['icp_link'] ?? '',
],
[
'name' => '公网安备链接',
'value' => $res['data']['setting']['gwa_link'] ?? '',
],
]
];
$categoryList = $this->categoryList();
$cate = [
'name' => '栏目链接',
'value' => '',
'child' => $categoryList,
];
$data = [
'link' => $link,
'cate' => $cate,
'content' => [],
];
return jsonReturn(0, '成功', $data);
}
// 导航接口
public function getNavList()
{
}
// 友情链接接口
public function getFriendLink()
{
}
public function getCategoryList()
{
return jsonReturn(0, '成功', $this->categoryList());
}
public function categoryListSetWhere(&$where, $type)
{
switch ($type) {
case 'suq_block1':
case 'suq_block2_contact':
case 'suq_block3':
case 'suq_block4':
case 'suq_block5':
case 'suq_block6':
case 'suq_footer':
$where[] = ['type', 'in', [2,3]];
$where[] = ['id', '>', 1];
break;
case 'suq_block2':
$where[] = ['id', '>', 1];
break;
default:
break;
}
}
// 栏目列表接口
protected function categoryList()
{
$param = $this->request->param();
$where = [
['seller_id', '=', $this->admin['seller_id']],
['website_id', '=', $param['website_id']],
];
$lang = $param['lang'] ?? '';
if (empty($lang)) {
$lang = 'zh';
}
$Website = new Website();
$website = $Website->getWebsite(['id' => $param['website_id'], 'seller_id' => $this->admin['seller_id']])['data'];
$domain = $website['domain'];
if ($lang != 'zh') {
$domain = $domain . '/' . $lang;
}
$where[] = ['lang', '=', $lang];
if(isset($param['type'])) {
$this->categoryListSetWhere($where, $param['type']);
}
$cateModel = new Category();
$categoryList = $cateModel->where($where)->field('id,type,title,alias')->select()->each(function (&$item) use ($domain) {
if ($item['type'] == 1 || $item['type'] == 4) {
if (!empty($item['alias'])) {
if ($item['alias'] == '/') {
$item['fullUrl'] = 'http://' . $domain;
} else {
$item['fullUrl'] = 'http://' . $domain . $item['alias'] . '.' . config('route.url_html_suffix');
}
} else {
$item['fullUrl'] = 'http://' . $domain . '/list/index/' . $item['id'] . '.' . config('route.url_html_suffix');
}
} else if ($item['type'] == 3) {
$item['fullUrl'] = $item['alias'];
} else {
$item['fullUrl'] = 'javascript:;';
}
});
return $categoryList->toArray();
}
// 栏目内容接口
public function getCategoryData()
{
$param = $this->request->param();
$categoryModel = new Category();
$contentModel = new SubContent();
$category = [];
$subCategory = [];
//获取此栏目数据和下面所有子栏目数据,并且拿到所有栏目下的内容数据
if ($param['type'] == 'suq_block1') {
//获取选择栏目的的标题和副标题,再获取子栏目的名称和缩略图
//最后获取子栏目的内容列表
$category = $categoryModel->getCategory([
['id', '=', $param['category_id']],
['type', 'in', [2,3]],
], [], 'id, type, title, sub_title, alias')['data'];
$subCategory = $categoryModel->where([
['parent_id', '=', $param['category_id']],
['type', 'in', [2,3]],
])->with(['thumbnail'])->field('id, type, title, sub_title, desc, thumbnail, alias')->select();
foreach ($subCategory as &$subCate) {
$subCate['content'] = $contentModel->alias('a')
->join('category_sub_content b', 'b.sub_content_id = a.id', 'left')
->with(['thumbnail'])
->where([
['b.category_id', '=', $subCate['id']]
])
->field('a.id, title, thumbnail')
->select();
}
} else if ($param['type'] == 'suq_block2' ||
$param['type'] == 'suq_block3' ||
$param['type'] == 'suq_block4' ||
$param['type'] == 'suq_footer') {
$category = $categoryModel->getCategory([
['id', '=', $param['category_id']],
['type', 'in', [2,3]],
], ['thumbnail'], 'id, type, title, sub_title, desc, alias, thumbnail, description')['data'];
$contentList = $contentModel->alias('a')
->join('category_sub_content b', 'b.sub_content_id = a.id', 'left')
->with(['thumbnail'])
->where([
['b.category_id', '=', $param['category_id']]
])
->field('a.id, title, sub_title, description, thumbnail')
->select()->toArray();
foreach ($contentList as &$value) {
if (isset($value['thumbnail']['url'])) {
$value['thumbnail_url'] = $value['thumbnail']['url'];
}
if (in_array($category['type'], [1,2,4])) {
$value['href'] = hcUrl('detail/index', ['cid' => $param['category_id'], 'id' => $value['id']]);
} else {
$value['href'] = $category['alias'];
}
}
$category['content'] = $contentList;
} else if ($param['type'] == 'suq_block5' ||
$param['type'] == 'suq_block6' ||
$param['type'] == 'suq_block2_contact') {
$category = $categoryModel->getCategory([
['id', '=', $param['category_id']],
['type', 'in', [2,3]],
], ['thumbnail'], 'id, type, title, sub_title, desc, alias, thumbnail, description, seo_description')['data'];
}
$returnData = [
'category' => $category,
'subCategory' => $subCategory,
];
return jsonReturn(0, '成功', $returnData);
}
// 内容列表接口
public function getContentList()
{
}
// 内容详情接口
// 幻灯片接口
// 广告接口
// 获取模板文件历史版本
public function getHistoryList()
{
$param = $this->request->param();
$initPage = $param['init_page'] ?? 'index';
$initPageName = $initPage . '.html';
$param['limit'] = $param['limit'] ?? 100;
$themeModel = new Theme();
$themeId = $themeModel->where([
['website_id', '=', $param['website_id']],
['lang', '=', $param['lang']],
['is_active', '=', 1],
])->value('id');
if (empty($themeId)) {
return jsonReturn(-1, '当前站点没有激活的模板,请先去安装模板');
}
// 获取当前模板文件id
$themeFileModel = new ThemeFile();
$themeFileId = $themeFileModel->where([
['website_id', '=', $param['website_id']],
['lang', '=', $param['lang']],
['theme_id', '=', $themeId],
['file', '=', $initPageName],
])->value('id');
if (empty($themeFileId)) {
return jsonReturn(-2, '模板文件不存在');
}
$bigFieldModel = new BigField();
$list = $bigFieldModel->alias('a')
->join('admin b', 'b.id = a.admin_id', 'left')
->join('theme_file c', 'a.theme_file_id = c.id')
->where([
['a.theme_id', '=', $themeId],
['a.theme_file_id', '=', $themeFileId],
])->field('a.*, b.name as admin_name, c.file')->order('id desc')->paginate($param['limit']);
return json(pageReturn(dataReturn(0, '', $list)));
}
public function setPreviewPage()
{
$param = $this->request->param();
$initPage = $param['init_page'] ?? 'index';
$initPageName = $initPage . '.html';
$version = $param['version'] ?? 1; // 将此版本的所有文件都塞入view下
$themeModel = new Theme();
$theme = $themeModel->where([
['website_id', '=', $param['website_id']],
['lang', '=', $param['lang']],
['is_active', '=', 1],
])->field('id, theme')->find();
if (empty($theme['id'])) {
return jsonReturn(-1, '当前站点没有激活的模板,请先去安装模板');
}
ThemeService::updatePreviewFile($param, $version, $theme);
session('history-view', time());
return jsonReturn(0, '预览初始化成功');
}
}