$temp_file, 'name' => htmlentities($data['name']), 'ver' => str_replace(array(',',','), ', ', htmlentities($data['ver'])), 'author' => htmlentities($data['author']), 'home' => isset($data['home']) ? $data['home'] : '', 'preview' => $img_obj->ImageToBase64(file_exists($preview) ? $preview : $default_preview), 'is_delete' => ($temp_file == 'default') ? 0 : 1, ); } } closedir($dh); } } return $result; } /** * 模板上传 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @datetime 2018-12-19T00:53:45+0800 * @param [array] $params [输入参数] */ public static function ThemeUpload($params = []) { // 文件上传校验 $error = FileUploadError('theme'); if($error !== true) { return DataReturn($error, -1); } // 文件格式化校验 $type = ResourcesService::ZipExtTypeList(); if(!in_array($_FILES['theme']['type'], $type)) { return DataReturn('文件格式有误,请上传zip压缩包', -2); } // 上传处理 return self::ThemeUploadHandle($_FILES['theme']['tmp_name'], $params); } /** * 模板上传处理 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @datetime 2018-12-19T00:53:45+0800 * @param [string] $package_file [软件包地址] * @param [array] $params [输入参数] */ public static function ThemeUploadHandle($package_file, $params = []) { // 初始化 $ret = self::Init($params); if($ret['code'] != 0) { return $ret; } // 主题目录 $dir = self::$old_path.DS; // 目录是否有权限 if(!is_writable($dir)) { return DataReturn('视图目录没权限['.$dir.']', -10); } // 开始解压文件 $zip = new \ZipArchive(); $resource = $zip->open($package_file); if($resource != true) { return DataReturn('压缩包打开失败['.$resource.']', -11); } for($i=0; $i<$zip->numFiles; $i++) { // 资源文件 $file = $zip->getNameIndex($i); // 排除系统.开头的临时文件和目录 if(strpos($file, '/.') !== 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 = $dir.substr($file, 0, strrpos($file, '/')); // 路径不存在则创建 if(!is_dir($file_path)) { mkdir($file_path, 0777, true); } // 如果不是目录则写入文件 if(!is_dir($dir.$file)) { // 读取这个文件 $stream = $zip->getStream($file); if($stream !== false) { $file_content = stream_get_contents($stream); if($file_content !== false) { file_put_contents($dir.$file, $file_content); } fclose($stream); } } } // 关闭zip $zip->close(); return DataReturn('安装成功'); } /** * 模板删除 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @datetime 2018-12-19T00:46:02+0800 * @param [array] $params [输入参数] */ public static function ThemeDelete($params = []) { // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'id', 'error_msg' => '模板id有误', ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 初始化 $ret = self::Init($params); if($ret['code'] != 0) { return $ret; } // 防止路径回溯 $id = htmlentities(str_replace(array('.', '/', '\\', ':'), '', strip_tags($params['id']))); if(empty($id)) { return DataReturn('主题名称有误', -1); } // default不能删除 if($id == 'default') { return DataReturn('系统模板不能删除', -2); } // 不能删除正在使用的主题 if(self::$default_theme == $id) { return DataReturn('不能删除正在使用的主题', -2); } // 开始删除主题 if(\base\FileUtil::UnlinkDir(self::$old_path.DS.$id)) { return DataReturn('删除成功'); } return DataReturn('删除失败或资源不存在', -100); } /** * 主题打包 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2019-03-22 * @desc description * @param [array] $params [输入参数] */ public static function ThemeDownload($params = []) { // 请求参数 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'id', 'error_msg' => '模板id有误', ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } // 初始化 $ret = self::Init($params); if($ret['code'] != 0) { return $ret; } // 是否开启开发者模式 if(MyConfig('shopxo.is_develop') !== true) { return DataReturn('请先开启开发者模式', -1); } // 防止路径回溯 $theme = htmlentities(str_replace(array('.', '/', '\\', ':'), '', strip_tags($params['id']))); if(empty($theme)) { return DataReturn('主题名称有误', -1); } // 获取配置信息 $config_res = self::MiniThemeConfig($theme, $params); if($config_res['code'] != 0) { return $config_res; } $config = $config_res['data']; // 目录不存在则创建 $new_dir = ROOT.'runtime'.DS.'data'.DS.'theme_appmini_package'.DS.$theme; \base\FileUtil::CreateDir($new_dir); // 复制包目录 $old_dir = self::$old_path.DS.$theme; if(is_dir($old_dir)) { if(\base\FileUtil::CopyDir($old_dir, $new_dir) != true) { return DataReturn('项目包复制失败', -2); } } // 历史信息更新 $ret = self::HistoryUpdateHandle($new_dir); if(isset($ret['code']) && $ret['code'] != 0) { return $ret; } // 生成压缩包 $zip = new \base\ZipFolder(); if(!$zip->zip($new_dir.'.zip', $new_dir)) { return DataReturn('压缩包生成失败', -100); } // 生成成功删除目录 \base\FileUtil::UnlinkDir($new_dir); // 开始下载 $appmini_type = MyConst('common_appmini_type'); $application_name = array_key_exists(self::$application_name, $appmini_type) ? $appmini_type[self::$application_name]['name'].'-' : ''; if(\base\FileUtil::DownloadFile($new_dir.'.zip', $application_name.$config['name'].'_v'.$config['ver'].'.zip')) { @unlink($new_dir.'.zip'); } else { return DataReturn('下载失败', -100); } } /** * 主题配置信息 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2021-04-22 * @desc description * @param [string] $theme [主题标识] * @param [array] $params [输入参数] */ public static function MiniThemeConfig($theme, $params) { // 初始化 $ret = self::Init($params); if($ret['code'] != 0) { return $ret; } // 获取配置信息 $config_file = self::$old_path.DS.$theme.DS.'config.json'; if(!file_exists($config_file)) { return DataReturn('小程序主题配置文件不存在', -1); } $config = json_decode(file_get_contents($config_file), true); if(empty($config)) { return DataReturn('主小程序题配置信息有误', -1); } return DataReturn('success', 0, $config); } /** * 获取小程序下载包数据列表 * @author Devil * @blog http://gong.gg/ * @version 0.0.1 * @datetime 2017-05-10T10:24:40+0800 * @param [array] $params [输入参数] */ public static function DownloadDataList($params = []) { // 初始化 $ret = self::Init($params); if($ret['code'] != 0) { return $ret; } // 获取包列表 $result = []; if(is_dir(self::$new_path)) { if($dh = opendir(self::$new_path)) { while(($temp_file = readdir($dh)) !== false) { if($temp_file != '.' && $temp_file != '..') { $file_path = self::$new_path.DS.$temp_file; $url = __MY_PUBLIC_URL__.'download'.DS.'sourcecode'.DS.self::$application_name.DS.$temp_file; $result[] = [ 'name' => (substr($temp_file, -4) === '.zip') ? substr($temp_file, 0, strlen($temp_file)-4) : $temp_file, 'url' => substr($url, -4) == '.zip' ? $url : '', 'size' => FileSizeByteToUnit(filesize($file_path)), 'time' => date('Y-m-d H:i:s', filectime($file_path)), ]; } } closedir($dh); } } return DataReturn('success', 0, $result); } /** * 源码包生成 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-12-21 * @desc description * @param [array] $params [输入参数] */ public static function Created($params = []) { // 是否https if(MyConfig('shopxo.is_develop') !== true) { if(__MY_HTTP__ != 'https') { return DataReturn('请使用https协议', -1); } } // 初始化 $ret = self::Init($params); if($ret['code'] != 0) { return $ret; } // 配置内容 $title = MyC('common_app_mini_'.self::$application_name.'_title'); $describe = MyC('common_app_mini_'.self::$application_name.'_describe'); if(empty($title) || empty($describe)) { return DataReturn('配置信息不能为空', -1); } // 源码目录不存在则创建 \base\FileUtil::CreateDir(self::$new_root); // 源码目标目录是否存在 if(!is_dir(self::$new_root)) { return DataReturn('源码目标目录不存在['.self::$new_root.']', -1); } // 源码目标目录没有权限 if(!is_writable(self::$new_root)) { return DataReturn('源码目标目录没有权限['.self::$new_root.']', -1); } // 包目录 $old_dir = self::$old_path.DS.self::$default_theme; $new_dir = self::$new_path.DS.date('YmdHis'); // 源码包是否存在 if(!is_dir($old_dir)) { return DataReturn('源码包不存在['.self::$default_theme.']', -1); } // 目录不存在则创建 \base\FileUtil::CreateDir(self::$new_path); // 复制包目录 if(\base\FileUtil::CopyDir($old_dir, $new_dir) != true) { return DataReturn('项目包复制失败', -2); } // 校验基础文件是否存在 if(!file_exists($new_dir.DS.'app.js') || !file_exists($new_dir.DS.'app.json')) { return DataReturn('包基础文件不存在,请重新生成', -3); } // 替换内容 // app.js $search = [ '{{request_url}}', '{{application_title}}', '{{application_describe}}', '{{currency_symbol}}', ]; $replace = [ __MY_URL__, $title, $describe, MyConfig('shopxo.currency_symbol'), ]; $status = file_put_contents($new_dir.DS.'app.js', str_replace($search, $replace, file_get_contents($new_dir.DS.'app.js'))); if($status === false) { return DataReturn('基础配置替换失败', -4); } // app.json $status = file_put_contents($new_dir.DS.'app.json', str_replace(['{{application_title}}'], [$title], file_get_contents($new_dir.DS.'app.json'))); if($status === false) { return DataReturn('基础配置替换失败', -4); } // 小程序额外处理 switch(self::$application_name) { // 微信 case 'weixin' : $ret = self::ExtendHandleWeixin($new_dir); break; // 默认 default : $ret = DataReturn('无需处理', 0); } if(isset($ret['code']) && $ret['code'] != 0) { return $ret; } // 历史信息更新 $ret = self::HistoryUpdateHandle($new_dir); if(isset($ret['code']) && $ret['code'] != 0) { return $ret; } // 生成压缩包 $zip = new \base\ZipFolder(); if(!$zip->zip($new_dir.'.zip', $new_dir)) { return DataReturn('压缩包生成失败', -100); } // 生成成功删除目录 \base\FileUtil::UnlinkDir($new_dir); return DataReturn('生成成功', 0); } /** * 历史信息更新 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2021-02-18 * @desc description * @param [string] $new_dir [新源码包目录] */ public static function HistoryUpdateHandle($new_dir) { // 配置信息 $file = $new_dir.DS.'app.json'; $config = json_decode(file_get_contents($file), true); // 插件配置为空防止成为数组 if(array_key_exists('plugins', $config) && empty($config['plugins'])) { $config['plugins'] = (object) []; } // 历史信息 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($file, JsonFormat($config)) === false) { return DataReturn('新应用配置文件更新失败', -11); } } /** * 扩展处理 - 微信 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-12-21 * @desc description * @param [string] $new_dir [新源码包目录] */ private static function ExtendHandleWeixin($new_dir) { // 启用直播 if(MyC('common_app_weixin_liveplayer', 0) == 1) { // app.json $file = $new_dir.DS.'app.json'; $config = json_decode(file_get_contents($file), true); if(is_array($config) && isset($config['plugins'])) { $config['plugins']['live-player-plugin'] = [ 'version' => MyC('common_app_weixin_liveplayer_ver', '1.3.0', true), 'provider' => 'wx2b03c6e691cd7370', ]; if(file_put_contents($file, JsonFormat($config)) === false) { return DataReturn('直播配置失败', -50); } } } return DataReturn('配置成功', 0); } /** * 源码包删除 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-12-21 * @desc description * @param [array] $params [输入参数] */ public static function Delete($params = []) { // 参数是否有误 if(empty($params['ids'])) { return DataReturn('操作id有误', -1); } // 是否数组 if(!is_array($params['ids'])) { $params['ids'] = explode(',', $params['ids']); } // 初始化 $ret = self::Init($params); if($ret['code'] != 0) { return $ret; } // 循环操作 $sucs = 0; $fail = 0; foreach($params['ids'] as $id) { // 目录处理 $suffix = ''; if(substr($id, -4) === '.zip') { $name = substr($id, 0, strlen($id)-4); $suffix = '.zip'; } else { $name = $id; } // 防止路径回溯 $path_name = self::$new_path.DS.htmlentities(str_replace(array('.', '/', '\\', ':'), '', strip_tags($id))); $path_zip = $path_name.'.zip'; // 删除包 $statusz = \base\FileUtil::UnlinkFile($path_zip); $statusf = \base\FileUtil::UnlinkDir($path_name); if($statusz || $statusf) { $sucs++; } else { $fail++; } } // 成功 if($sucs == count($params['ids'])) { return DataReturn('删除成功'); } // 失败 if($fail == count($params['ids'])) { return DataReturn('删除失败', -100); } return DataReturn('成功['.$sucs.'],失败['.$fail.']'); } /** * 小程序主题更新信息 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2021-04-22 * @desc description * @param [array] $params [输入参数] */ public static function AppMiniUpgradeInfo($params = []) { if(!empty($params) && !empty($params['data']) && !empty($params['terminal'])) { // 数据处理 $data = []; foreach($params['data'] 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' => 'minitheme', 'plugins_data' => $data, 'plugins_terminal' => $params['terminal'], ]; $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('无插件数据', 0); } } ?>