vr-shopxo-plugin/shopxo/app/service/ThemeAdminService.php

796 lines
27 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
// +----------------------------------------------------------------------
// | ShopXO 国内领先企业级B2C免费开源电商系统
// +----------------------------------------------------------------------
// | Copyright (c) 2011~2099 http://shopxo.net All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( https://opensource.org/licenses/mit-license.php )
// +----------------------------------------------------------------------
// | Author: Devil
// +----------------------------------------------------------------------
namespace app\service;
use think\facade\Db;
use app\service\ResourcesService;
use app\service\ConfigService;
use app\service\StoreService;
use app\service\ThemeDataService;
/**
* 主题管理服务层
* @author Devil
* @blog http://gong.gg/
* @version 0.0.1
* @datetime 2016-12-01T21:51:08+0800
*/
class ThemeAdminService
{
// 静态目录和html目录
private static $html_path = 'app'.DS.'index'.DS.'view'.DS;
private static $static_path = 'public'.DS.'static'.DS.'index'.DS;
// 排除的文件后缀
private static $exclude_ext = ['php'];
/**
* 默认主题标识符
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2020-11-20
* @desc description
*/
public static function DefaultTheme()
{
return MyC('common_default_theme', 'default', true);
}
/**
* 主题首页是否主题数据模式
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2024-04-05
* @desc description
* @param [string] $theme [主题标识]
*/
public static function HomeIsThemeDataMode($theme)
{
$ret = self::ThemeAdminConfig($theme);
return (!empty($ret['data']) && isset($ret['data']['home_is_theme_data_mode']) && $ret['data']['home_is_theme_data_mode'] == 1);
}
/**
* 获取模板列表
* @author Devil
* @blog http://gong.gg/
* @version 0.0.1
* @datetime 2017-05-10T10:24:40+0800
* @param [array] $params [输入参数]
* @return [array] [模板列表]
*/
public static function ThemeAdminList($params = [])
{
$result = [];
$dir = ROOT.self::$html_path;
if(is_dir($dir))
{
if($dh = opendir($dir))
{
$default_preview = __MY_PUBLIC_URL__.'static'.DS.'common'.DS.'images'.DS.'default-preview.jpg';
while(($temp_file = readdir($dh)) !== false)
{
if(substr($temp_file, 0, 1) == '.' || in_array($temp_file, ['index.html']) || is_dir($temp_file))
{
continue;
}
$config = $dir.$temp_file.DS.'config.json';
if(!file_exists($config))
{
continue;
}
// 读取配置文件
$config = json_decode(file_get_contents($config), true);
if(!empty($config) && is_array($config))
{
if(empty($config['name']) || empty($config['ver']) || empty($config['author']))
{
continue;
}
$preview = ROOT.self::$static_path.$temp_file.DS.'images'.DS.'preview.jpg';
$result[] = array(
'theme' => $temp_file,
'name' => htmlentities($config['name']),
'ver' => str_replace(array('',','), ', ', htmlentities($config['ver'])),
'author' => htmlentities($config['author']),
'home' => isset($config['home']) ? $config['home'] : '',
'preview' => file_exists($preview) ? __MY_PUBLIC_URL__.'static'.DS.'index'.DS.$temp_file.DS.'images'.DS.'preview.jpg' : $default_preview,
'is_theme_data_admin' => (isset($config['is_theme_data_admin']) && $config['is_theme_data_admin'] == 1) ? 1 : 0,
'home_is_theme_data_mode' => (isset($config['home_is_theme_data_mode']) && $config['home_is_theme_data_mode'] == 1) ? 1 : 0,
'is_delete' => ($temp_file == 'default') ? 0 : 1,
);
}
}
closedir($dh);
}
}
return $result;
}
/**
* 主题数据
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2024-12-20
* @desc description
* @param [array] $params [输入参数]
*/
public static function ThemeAdminData($params = [])
{
$data = [];
if(!empty($params['id']))
{
$list = self::ThemeAdminList($params);
if(!empty($list))
{
$list = array_column($list, null, 'theme');
if(array_key_exists($params['id'], $list))
{
$data = $list[$params['id']];
}
}
}
return $data;
}
/**
* 模板上传
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @datetime 2018-12-19T00:53:45+0800
* @param [array] $params [输入参数]
*/
public static function ThemeAdminUpload($params = [])
{
// 文件上传校验
$error = FileUploadError('theme');
if($error !== true)
{
return DataReturn($error, -1);
}
// 文件格式化校验
$type = ResourcesService::ZipExtTypeList();
if(!in_array($_FILES['theme']['type'], $type))
{
return DataReturn(MyLang('form_upload_zip_message'), -2);
}
// 上传处理
return self::ThemeAdminUploadHandle($_FILES['theme']['tmp_name'], $params);
}
/**
* 模板上传处理
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2018-09-17
* @desc description
* @param [string] $package_file [软件包地址]
* @param [array] $params [输入参数]
*/
public static function ThemeAdminUploadHandle($package_file, $params = [])
{
// 目录是否有权限
if(!is_writable(ROOT.self::$html_path))
{
return DataReturn(MyLang('common_service.themeadmin.view_dir_no_power_tips').'['.ROOT.self::$html_path.']', -10);
}
if(!is_writable(ROOT.self::$static_path))
{
return DataReturn(MyLang('common_service.themeadmin.static_dir_no_power_tips').'['.self::$static_path.']', -10);
}
// 资源目录
$dir_list = [
'_html_' => ROOT.self::$html_path,
'_static_' => ROOT.self::$static_path,
];
// 开始解压文件
$zip = new \ZipArchive();
$resource = $zip->open($package_file);
if($resource !== true)
{
return DataReturn(MyLang('form_open_zip_message').'['.$resource.']', -11);
}
// 配置信息
for($i=0; $i<$zip->numFiles; $i++)
{
$file = $zip->getNameIndex($i);
if(stripos($file, 'config.json') !== false)
{
// 打开文件资源
$stream = $zip->getStream($file);
if($stream === false)
{
$zip->close();
return DataReturn(MyLang('common_service.themeadmin.upload_config_file_get_fail_tips'), -1);
}
// 获取配置信息并解析
$file_content = stream_get_contents($stream);
$config = empty($file_content) ? [] : json_decode($file_content, true);
if(empty($config))
{
$zip->close();
return DataReturn(MyLang('common_service.themeadmin.config_file_no_exist_tips'), -1);
}
// 主题名称
$theme = substr($file, 0, strpos($file, '/'));
// 安全验证
$ret = self::ThemeAdminLegalCheck($theme, $config);
if($ret['code'] != 0)
{
$zip->close();
return $ret;
}
}
}
// 处理文件
$success = 0;
for($i=0; $i<$zip->numFiles; $i++)
{
// 资源文件
$file = $zip->getNameIndex($i);
// 排除临时文件和临时目录
if(strpos($file, '/.') === false && strpos($file, '__') === false)
{
// 文件包对应系统所在目录
$is_has_find = false;
foreach($dir_list as $dir_key=>$dir_value)
{
if(strpos($file, $dir_key) !== false)
{
// 匹配成功文件路径处理、跳出循环
$new_file = str_replace($dir_key.'/', '', $dir_value.$file);
$is_has_find = true;
break;
}
}
// 没有匹配到则指定目录跳过
if($is_has_find == false)
{
continue;
}
// 排除后缀文件
$pos = strripos($file, '.');
if($pos !== false)
{
$info = pathinfo($file);
if(isset($info['extension']) && in_array(strtolower($info['extension']), self::$exclude_ext))
{
continue;
}
}
// 截取文件路径
$file_path = substr($new_file, 0, strrpos($new_file, '/'));
// 路径不存在则创建
if(!is_dir($file_path))
{
mkdir($file_path, 0777, true);
}
// 如果不是目录则写入文件
if(!is_dir($new_file))
{
// 读取这个文件
$stream = $zip->getStream($file);
if($stream !== false)
{
$file_content = stream_get_contents($stream);
if($file_content !== false)
{
if(file_put_contents($new_file, $file_content))
{
$success++;
}
}
fclose($stream);
}
}
}
}
// 关闭zip
$zip->close();
// 未匹配成功一个文件则认为插件包无效
if($success <= 0)
{
return DataReturn(MyLang('common_service.themeadmin.package_invalid_tips'), -1);
}
// 主题数据处理
ThemeDataService::ThemeDataUploadHandle($package_file, array_merge($params, ['is_theme_upload'=>1]));
// 返回成功
return DataReturn(MyLang('install_success'), 0);
}
/**
* 主题切换保存
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2023-05-26
* @desc description
* @param [array] $params [输入参数]
*/
public static function ThemeAdminSwitch($params = [])
{
// 主题标识
$theme = empty($params['theme']) ? 'default' : $params['theme'];
// 安全判断
$ret = self::ThemeAdminLegalCheck($theme);
if($ret['code'] != 0)
{
return $ret;
}
// 切换配置
$params['common_default_theme'] = $theme;
$ret = ConfigService::ConfigSave($params);
if($ret['code'] == 0)
{
// 同步前端主题session数据
DefaultTheme($theme);
}
return $ret;
}
/**
* 模板删除
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @datetime 2018-12-19T00:46:02+0800
* @param [array] $params [输入参数]
*/
public static function ThemeAdminDelete($params = [])
{
// 请求参数
$p = [
[
'checked_type' => 'empty',
'key_name' => 'id',
'error_msg' => MyLang('common_service.themeadmin.template_id_error_tips'),
],
];
$ret = ParamsChecked($params, $p);
if($ret !== true)
{
return DataReturn($ret, -1);
}
// 防止路径回溯
$id = htmlentities(str_replace(array('.', '/', '\\', ':'), '', strip_tags($params['id'])));
if(empty($id))
{
return DataReturn(MyLang('common_service.themeadmin.theme_name_error_tips'), -1);
}
// default不能删除
if($id == 'default')
{
return DataReturn(MyLang('common_service.themeadmin.system_theme_not_delete_tips'), -2);
}
// 不能删除正在使用的主题
if(self::DefaultTheme() == $id)
{
return DataReturn(MyLang('common_service.themeadmin.current_use_theme_error_tips'), -2);
}
// 开始删除主题
if(\base\FileUtil::UnlinkDir(ROOT.self::$html_path.$id) && \base\FileUtil::UnlinkDir(ROOT.self::$static_path.$id))
{
return DataReturn(MyLang('delete_success'), 0);
}
return DataReturn(MyLang('delete_fail'), -100);
}
/**
* 主题打包
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2019-03-22
* @desc description
* @param [array] $params [输入参数]
*/
public static function ThemeAdminDownload($params = [])
{
// 生成下载包
$package = self::ThemeAdminDownloadHandle($params);
if($package['code'] != 0)
{
return $package;
}
// 开始下载
if(\base\FileUtil::DownloadFile($package['data']['file'], $package['data']['config']['name'].'_v'.$package['data']['config']['ver'].'.zip', true))
{
return DataReturn(MyLang('download_success'), 0);
}
return DataReturn(MyLang('download_fail'), -100);
}
/**
* 上传到应用商店
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2023-08-17
* @desc description
* @param [array] $params [输入参数]
*/
public static function ThemeAdminStoreUpload($params = [])
{
// 请求参数
$p = [
[
'checked_type' => 'empty',
'key_name' => 'version_new',
'error_msg' => MyLang('common_service.themeadmin.form_item_version_message'),
],
[
'checked_type' => 'empty',
'key_name' => 'describe',
'error_msg' => MyLang('common_service.themeadmin.form_item_desc_message'),
],
[
'checked_type' => 'empty',
'key_name' => 'apply_version',
'error_msg' => MyLang('common_service.themeadmin.form_item_apply_version_message'),
],
];
$ret = ParamsChecked($params, $p);
if($ret !== true)
{
return DataReturn($ret, -1);
}
// 生成下载包
$package = self::ThemeAdminDownloadHandle($params);
if($package['code'] != 0)
{
return $package;
}
// 帐号信息
$user = StoreService::AccountsData();
if(empty($user['accounts']) || empty($user['password']))
{
return DataReturn(MyLang('store_account_not_bind_tips'), -300);
}
// 上传到远程
$params['theme'] = $package['data']['theme'];
$params['file'] = new \CURLFile($package['data']['file']);
$ret = StoreService::RemoteStoreData($user['accounts'], $user['password'], MyConfig('shopxo.store_themeadmin_upload_url'), $params, 2);
// 是个与否都删除本地生成的zip包
@unlink($package['data']['file']);
// 返回数据
return $ret;
}
/**
* 主题打包处理
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2019-03-22
* @desc description
* @param [array] $params [输入参数]
*/
public static function ThemeAdminDownloadHandle($params = [])
{
// 请求参数
$p = [
[
'checked_type' => 'empty',
'key_name' => 'id',
'error_msg' => MyLang('common_service.themeadmin.template_id_error_tips'),
],
];
$ret = ParamsChecked($params, $p);
if($ret !== true)
{
return DataReturn($ret, -1);
}
// 是否开启开发者模式
if(MyConfig('shopxo.is_develop') !== true)
{
return DataReturn(MyLang('not_open_developer_mode_tips'), -1);
}
// 防止路径回溯
$theme = htmlentities(str_replace(array('.', '/', '\\', ':'), '', strip_tags($params['id'])));
if(empty($theme))
{
return DataReturn(MyLang('common_service.themeadmin.theme_name_error_tips'), -1);
}
// 获取配置信息
$config_res = self::ThemeAdminConfig($theme);
if($config_res['code'] != 0)
{
return $config_res;
}
$config = $config_res['data'];
// 安全判断
$ret = self::ThemeAdminLegalCheck($theme, $config);
if($ret['code'] != 0)
{
return $ret;
}
// 目录不存在则创建
$new_dir = ROOT.'runtime'.DS.'data'.DS.'theme_package'.DS.$theme;
\base\FileUtil::CreateDir($new_dir);
// 复制包目录 - 视图
$old_dir = ROOT.self::$html_path.$theme;
if(is_dir($old_dir))
{
if(\base\FileUtil::CopyDir($old_dir, $new_dir.DS.'_html_') != true)
{
return DataReturn(MyLang('project_copy_fail_tips').'['.MyLang('common_service.themeadmin.theme_copy_view_fail_tips').']', -2);
}
}
// 复制包目录 - 静态文件
$old_dir = ROOT.self::$static_path.$theme;
if(is_dir($old_dir))
{
if(\base\FileUtil::CopyDir($old_dir, $new_dir.DS.'_static_') != true)
{
return DataReturn(MyLang('project_copy_fail_tips').'['.MyLang('common_service.themeadmin.theme_copy_static_fail_tips').']', -2);
}
}
// 配置文件历史信息更新
$new_config_file = $new_dir.DS.'_html_'.DS.'config.json';
if(!file_exists($new_config_file))
{
return DataReturn(MyLang('common_service.themeadmin.theme_new_config_error_tips'), -10);
}
if(empty($config['history']))
{
$config['history'] = [];
}
$config['history'][] = [
'host' => __MY_HOST__,
'url' => __MY_URL__,
'ip' => __MY_ADDR__,
'time' => date('Y-m-d H:i:s'),
];
if(@file_put_contents($new_config_file, JsonFormat($config)) === false)
{
return DataReturn(MyLang('common_service.themeadmin.theme_new_config_update_fail_tips'), -11);
}
// 主题数据包同步到主题里面一起下载
ThemeDataService::ThemeDownloadPackageHandle(['theme'=>$theme, 'dir'=>$new_dir.DS.'_theme_data_'.DS]);
// 生成压缩包
$zip = new \base\ZipFolder();
if(!$zip->zip($new_dir.'.zip', $new_dir))
{
return DataReturn(MyLang('form_generate_zip_message'), -100);
}
// 生成成功删除目录
\base\FileUtil::UnlinkDir($new_dir);
// 返回数据
return DataReturn('success', 0, [
'file' => $new_dir.'.zip',
'config' => $config,
'theme' => $theme,
]);
}
/**
* 主题安全判断
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2023-05-26
* @desc description
* @param [string] $theme [主题标识]
* @param [array] $data [主题数据]
*/
public static function ThemeAdminLegalCheck($theme, $data = [])
{
if(RequestModule() == 'admin')
{
$key = 'theme_legal_check_'.$theme;
$ret = MyCache($key);
if(empty($ret))
{
if(empty($data))
{
$config_res = self::ThemeAdminConfig($theme);
if($config_res['code'] != 0)
{
return $config_res;
}
$data = $config_res['data'];
}
$check_params = [
'type' => 'webtheme',
'config' => $data,
'plugins' => $theme,
'author' => isset($data['author']) ? $data['author'] : '',
'ver' => isset($data['version']) ? $data['version'] : (isset($data['ver']) ? $data['ver'] : ''),
];
$ret = StoreService::PluginsLegalCheck($check_params);
MyCache($key, $ret, 3600);
}
if(!in_array($ret['code'], [0, -9999]))
{
return $ret;
}
}
return DataReturn('success', 0);
}
/**
* 主题配置信息
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2021-04-22
* @desc description
* @param [string] $theme [主题标识]
*/
public static function ThemeAdminConfig($theme)
{
// 获取配置信息
$config_file = ROOT.self::$html_path.$theme.DS.'config.json';
if(!file_exists($config_file))
{
return DataReturn(MyLang('common_service.themeadmin.config_file_no_exist_tips'), -1);
}
$config = json_decode(file_get_contents($config_file), true);
if(empty($config))
{
return DataReturn(MyLang('common_service.themeadmin.config_error_tips'), -1);
}
return DataReturn('success', 0, $config);
}
/**
* web主题更新信息
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2021-04-22
* @desc description
* @param [array] $params [输入参数]
*/
public static function ThemeAdminUpgradeInfo($params = [])
{
if(!empty($params))
{
// 数据处理
$data = [];
foreach($params as $v)
{
if(!empty($v['name']) && !empty($v['ver']) && !empty($v['theme']) && !empty($v['author']))
{
$data[] = [
'plugins' => $v['theme'],
'name' => $v['name'],
'ver' => $v['ver'],
'author' => $v['author'],
];
}
}
if(!empty($data))
{
// 获取更新信息
$request_params = [
'plugins_type' => 'webtheme',
'plugins_data' => $data,
];
$res = StoreService::PluginsUpgradeInfo($request_params);
if(!empty($res['data']) && is_array($res['data']))
{
$res['data'] = array_column($res['data'], null, 'plugins');
}
return $res;
}
}
return DataReturn(MyLang('plugins_no_data_tips'), 0);
}
/**
* 主题数据管理url
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2024-04-11
* @desc description
* @param [array] $params [输入参数]
*/
public static function ThemeAdminDataUrl($params = [])
{
// 链接数据
$data = [
'themedata' => MyUrl('admin/themedata/saveinfo', ['is_theme_data_admin' => 1]),
'goods' => MyUrl('admin/goods/saveinfo', ['is_theme_data_admin' => 1]),
'goods_category' => MyUrl('admin/goodscategory/index', ['is_theme_data_admin' => 1]),
'brand' => MyUrl('admin/brand/saveinfo', ['is_theme_data_admin' => 1]),
'brand_list' => MyUrl('admin/brand/index', ['is_theme_data_admin' => 1]),
'article' => MyUrl('admin/article/saveinfo', ['is_theme_data_admin' => 1]),
'article_list' => MyUrl('admin/article/index', ['is_theme_data_admin' => 1]),
'article_category' => MyUrl('admin/articlecategory/index', ['is_theme_data_admin' => 1]),
'link_list' => MyUrl('admin/link/index', ['is_theme_data_admin' => 1]),
'navigation_header' => MyUrl('admin/navigation/index', ['type' => 'header', 'is_theme_data_admin' => 1]),
'navigation_footer' => MyUrl('admin/navigation/index', ['type' => 'footer', 'is_theme_data_admin' => 1]),
'slide_list' => MyUrl('admin/slide/index', ['is_theme_data_admin' => 1]),
'store_config' => MyUrl('admin/config/store', ['is_theme_data_admin' => 1]),
'user_login' => MyUrl('admin/site/index', ['nav_type' => 'login', 'is_theme_data_admin' => 1]),
'user_register' => MyUrl('admin/site/index', ['nav_type' => 'register', 'is_theme_data_admin' => 1]),
'user_forget' => MyUrl('admin/site/index', ['nav_type' => 'forgetpwd', 'is_theme_data_admin' => 1]),
'screening_price' => MyUrl('admin/screeningprice/index', ['is_theme_data_admin' => 1]),
'site_base' => MyUrl('admin/site/index', ['nav_type'=>'base', 'switch'=>'site-base', 'is_theme_data_admin' => 1]),
'site_filing' => MyUrl('admin/site/index', ['nav_type'=>'base', 'switch'=>'site-filing', 'is_theme_data_admin' => 1]),
];
// 主题数据管理url钩子
$hook_name = 'plugins_service_theme_admin_data_url';
MyEventTrigger($hook_name, [
'hook_name' => $hook_name,
'is_backend' => true,
'data' => &$data,
]);
return $data;
}
/**
* 模板市场
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2022-04-19
* @desc description
* @param [array] $params [输入参数]
*/
public static function ThemeAdminMarket($params = [])
{
$params['type'] = 'webtheme';
return StoreService::PackageDataList($params);
}
}
?>