ThemeService.php 14.9 KB
Newer Older
D
v1.2.0  
devil_gong 已提交
1 2 3 4
<?php
// +----------------------------------------------------------------------
// | ShopXO 国内领先企业级B2C免费开源电商系统
// +----------------------------------------------------------------------
D
2.0  
Devil 已提交
5
// | Copyright (c) 2011~2099 http://shopxo.net All rights reserved.
D
v1.2.0  
devil_gong 已提交
6
// +----------------------------------------------------------------------
D
2.0  
Devil 已提交
7
// | Licensed ( https://opensource.org/licenses/mit-license.php )
D
v1.2.0  
devil_gong 已提交
8 9 10 11 12
// +----------------------------------------------------------------------
// | Author: Devil
// +----------------------------------------------------------------------
namespace app\service;

D
Devil 已提交
13
use think\facade\Db;
D
Devil 已提交
14
use app\service\ResourcesService;
D
v1.2.0  
devil_gong 已提交
15 16 17 18 19 20 21 22 23 24 25

/**
 * 主题服务层
 * @author   Devil
 * @blog     http://gong.gg/
 * @version  0.0.1
 * @datetime 2016-12-01T21:51:08+0800
 */
class ThemeService
{
    // 静态目录和html目录
D
Devil 已提交
26
    private static $html_path = 'app'.DS.'index'.DS.'view'.DS;
27
    private static $static_path = 'public'.DS.'static'.DS.'index'.DS;
D
v1.2.0  
devil_gong 已提交
28

D
devil 已提交
29
    // 排除的文件后缀
D
devil 已提交
30
    private static $exclude_ext = ['php'];
D
devil 已提交
31

D
devil 已提交
32 33 34 35 36 37 38 39 40 41 42 43 44
    /**
     * 默认主题标识符
     * @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);
    }

D
v1.2.0  
devil_gong 已提交
45 46 47 48 49 50 51 52 53
    /**
     * 获取模板列表
     * @author   Devil
     * @blog     http://gong.gg/
     * @version  0.0.1
     * @datetime 2017-05-10T10:24:40+0800
     * @param    [array]          $params [输入参数]
     * @return   [array]                  [模板列表]
     */
54
    public static function ThemeList($params = [])
D
v1.2.0  
devil_gong 已提交
55 56
    {
        $result = [];
57
        $dir = ROOT.self::$html_path;
D
v1.2.0  
devil_gong 已提交
58 59 60 61
        if(is_dir($dir))
        {
            if($dh = opendir($dir))
            {
D
devil_gong 已提交
62
                $default_preview = __MY_PUBLIC_URL__.'static'.DS.'common'.DS.'images'.DS.'default-preview.jpg';
D
v1.2.0  
devil_gong 已提交
63 64
                while(($temp_file = readdir($dh)) !== false)
                {
D
devil 已提交
65
                    if(substr($temp_file, 0, 1) == '.' || in_array($temp_file, ['index.html']))
D
devil_gong 已提交
66 67 68 69
                    {
                        continue;
                    }

D
v1.2.0  
devil_gong 已提交
70 71 72 73 74 75 76 77 78 79 80 81 82 83
                    $config = $dir.$temp_file.DS.'config.json';
                    if(!file_exists($config))
                    {
                        continue;
                    }

                    // 读取配置文件
                    $data = json_decode(file_get_contents($config), true);
                    if(!empty($data) && is_array($data))
                    {
                        if(empty($data['name']) || empty($data['ver']) || empty($data['author']))
                        {
                            continue;
                        }
84
                        $preview = ROOT.self::$static_path.$temp_file.DS.'images'.DS.'preview.jpg';
D
v1.2.0  
devil_gong 已提交
85 86 87 88 89 90
                        $result[] = array(
                            'theme'     =>  $temp_file,
                            'name'      =>  htmlentities($data['name']),
                            'ver'       =>  str_replace(array(',',','), ', ', htmlentities($data['ver'])),
                            'author'    =>  htmlentities($data['author']),
                            'home'      =>  isset($data['home']) ? $data['home'] : '',
D
devil_gong 已提交
91
                            'preview'   =>  file_exists($preview) ? __MY_PUBLIC_URL__.'static'.DS.'index'.DS.$temp_file.DS.'images'.DS.'preview.jpg' : $default_preview,
D
v1.2.0  
devil_gong 已提交
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
                            '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 [输入参数]
     */
110
    public static function ThemeUpload($params = [])
D
v1.2.0  
devil_gong 已提交
111 112 113 114 115 116 117 118 119
    {
        // 文件上传校验
        $error = FileUploadError('theme');
        if($error !== true)
        {
            return DataReturn($error, -1);
        }

        // 文件格式化校验
D
Devil 已提交
120
        $type = ResourcesService::ZipExtTypeList();
D
v1.2.0  
devil_gong 已提交
121 122 123 124 125
        if(!in_array($_FILES['theme']['type'], $type))
        {
            return DataReturn('文件格式有误,请上传zip压缩包', -2);
        }

D
Devil 已提交
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
        // 上传处理
        return self::ThemeUploadHandle($_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 ThemeUploadHandle($package_file, $params = [])
    {
D
v1.2.0  
devil_gong 已提交
142
        // 目录是否有权限
143
        if(!is_writable(ROOT.self::$html_path))
D
v1.2.0  
devil_gong 已提交
144
        {
D
devil 已提交
145
            return DataReturn('视图目录没权限['.ROOT.self::$html_path.']', -10);
D
v1.2.0  
devil_gong 已提交
146
        }
147
        if(!is_writable(ROOT.self::$static_path))
D
v1.2.0  
devil_gong 已提交
148
        {
D
devil 已提交
149
            return DataReturn('资源目录没权限['.self::$static_path.']', -10);
D
v1.2.0  
devil_gong 已提交
150 151
        }

D
devil_gong 已提交
152 153 154 155 156 157
        // 资源目录
        $dir_list = [
            '_html_'        => ROOT.self::$html_path,
            '_static_'      => ROOT.self::$static_path,
        ];

D
v1.2.0  
devil_gong 已提交
158
        // 开始解压文件
D
Devil 已提交
159 160 161
        $zip = new \ZipArchive();
        $resource = $zip->open($package_file);
        if($resource != true)
D
v1.2.0  
devil_gong 已提交
162
        {
D
Devil 已提交
163 164 165 166 167 168 169
            return DataReturn('压缩包打开失败['.$resource.']', -11);
        }
        $success = 0;
        for($i=0; $i<$zip->numFiles; $i++)
        {
            // 资源文件
            $file = $zip->getNameIndex($i);
D
v1.2.0  
devil_gong 已提交
170

D
Devil 已提交
171 172 173 174 175 176
            // 排除临时文件和临时目录
            if(strpos($file, '/.') === false && strpos($file, '__') === false)
            {
                // 文件包对应系统所在目录
                $is_has_find = false;
                foreach($dir_list as $dir_key=>$dir_value)
D
v1.2.0  
devil_gong 已提交
177
                {
D
Devil 已提交
178
                    if(strpos($file, $dir_key) !== false)
D
v1.2.0  
devil_gong 已提交
179
                    {
D
Devil 已提交
180 181 182 183
                        // 匹配成功文件路径处理、跳出循环
                        $new_file = str_replace($dir_key.'/', '', $dir_value.$file);
                        $is_has_find = true;
                        break;
D
v1.2.0  
devil_gong 已提交
184
                    }
D
Devil 已提交
185
                }
D
v1.2.0  
devil_gong 已提交
186

D
Devil 已提交
187 188 189 190 191
                // 没有匹配到则指定目录跳过
                if($is_has_find == false)
                {
                    continue;
                }
D
devil_gong 已提交
192

D
Devil 已提交
193 194 195 196 197 198
                // 排除后缀文件
                $pos = strripos($file, '.');
                if($pos !== false)
                {
                    $info = pathinfo($file);
                    if(isset($info['extension']) && in_array($info['extension'], self::$exclude_ext))
D
devil 已提交
199
                    {
D
Devil 已提交
200
                        continue;
D
devil 已提交
201
                    }
D
Devil 已提交
202
                }
D
devil 已提交
203

D
Devil 已提交
204 205
                // 截取文件路径
                $file_path = substr($new_file, 0, strrpos($new_file, '/'));
D
v1.2.0  
devil_gong 已提交
206

D
Devil 已提交
207 208 209 210 211
                // 路径不存在则创建
                if(!is_dir($file_path))
                {
                    mkdir($file_path, 0777, true);
                }
D
v1.2.0  
devil_gong 已提交
212

D
Devil 已提交
213 214 215 216 217 218
                // 如果不是目录则写入文件
                if(!is_dir($new_file))
                {
                    // 读取这个文件
                    $stream = $zip->getStream($file);
                    if($stream !== false)
D
v1.2.0  
devil_gong 已提交
219
                    {
D
Devil 已提交
220 221 222 223 224 225 226 227 228
                        $file_content = stream_get_contents($stream);
                        if($file_content !== false)
                        {
                            if(file_put_contents($new_file, $file_content))
                            {
                                $success++;
                            }
                        }
                        fclose($stream);
D
v1.2.0  
devil_gong 已提交
229 230 231 232
                    }
                }
            }
        }
D
Devil 已提交
233 234 235 236 237 238 239 240
        // 关闭zip  
        $zip->close();

        // 未匹配成功一个文件则认为插件包无效
        if($success <= 0)
        {
            return DataReturn('无效的主题包', -1);
        }
D
Devil 已提交
241
        return DataReturn('安装成功', 0);
D
v1.2.0  
devil_gong 已提交
242 243 244 245 246 247 248 249 250 251
    }

    /**
     * 模板删除
     * @author   Devil
     * @blog     http://gong.gg/
     * @version  1.0.0
     * @datetime 2018-12-19T00:46:02+0800
     * @param    [array]          $params [输入参数]
     */
252
    public static function ThemeDelete($params = [])
D
v1.2.0  
devil_gong 已提交
253
    {
D
devil 已提交
254 255 256 257 258 259 260 261 262 263
        // 请求参数
        $p = [
            [
                'checked_type'      => 'empty',
                'key_name'          => 'id',
                'error_msg'         => '模板id有误',
            ],
        ];
        $ret = ParamsChecked($params, $p);
        if($ret !== true)
D
v1.2.0  
devil_gong 已提交
264
        {
D
devil 已提交
265
            return DataReturn($ret, -1);
D
v1.2.0  
devil_gong 已提交
266
        }
G
gongfuxiang 已提交
267

268
        // 防止路径回溯
G
gongfuxiang 已提交
269
        $id = htmlentities(str_replace(array('.', '/', '\\', ':'), '', strip_tags($params['id'])));
D
v1.2.0  
devil_gong 已提交
270 271 272 273 274 275 276 277 278 279 280 281
        if(empty($id))
        {
            return DataReturn('主题名称有误', -1);
        }

        // default不能删除
        if($id == 'default')
        {
            return DataReturn('系统模板不能删除', -2);
        }

        // 不能删除正在使用的主题
D
devil 已提交
282
        if(self::DefaultTheme() == $id)
D
v1.2.0  
devil_gong 已提交
283 284 285 286 287
        {
            return DataReturn('不能删除正在使用的主题', -2);
        }

        // 开始删除主题
288
        if(\base\FileUtil::UnlinkDir(ROOT.self::$html_path.$id) && \base\FileUtil::UnlinkDir(ROOT.self::$static_path.$id))
D
v1.2.0  
devil_gong 已提交
289 290 291 292 293
        {
            return DataReturn('删除成功');
        }
        return DataReturn('删除失败或资源不存在', -100);
    }
G
gongfuxiang 已提交
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320

    /**
     * 主题打包
     * @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);
        }

        // 是否开启开发者模式
D
Devil 已提交
321
        if(MyConfig('shopxo.is_develop') !== true)
G
gongfuxiang 已提交
322 323 324 325 326 327 328 329 330 331 332
        {
            return DataReturn('请先开启开发者模式', -1); 
        }

        // 防止路径回溯
        $theme = htmlentities(str_replace(array('.', '/', '\\', ':'), '', strip_tags($params['id'])));
        if(empty($theme))
        {
            return DataReturn('主题名称有误', -1);
        }

D
Devil 已提交
333
        // 获取配置信息
D
Devil 已提交
334 335
        $config_res = self::ThemeConfig($theme);
        if($config_res['code'] != 0)
D
Devil 已提交
336
        {
D
Devil 已提交
337
            return $config_res;
D
Devil 已提交
338
        }
D
Devil 已提交
339
        $config = $config_res['data'];
D
Devil 已提交
340

G
gongfuxiang 已提交
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
        // 目录不存在则创建
        $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('项目包复制失败[视图]', -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('项目包复制失败[静态文件]', -2);
            }
        }

D
Devil 已提交
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
        // 配置文件历史信息更新
        $new_config_file = $new_dir.DS.'_html_'.DS.'config.json';
        if(!file_exists($new_config_file))
        {
            return DataReturn('新配置文件有误', -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('新应用配置文件更新失败', -11);
        }

G
gongfuxiang 已提交
386 387 388 389 390 391 392 393 394 395 396
        // 生成压缩包
        $zip = new \base\ZipFolder();
        if(!$zip->zip($new_dir.'.zip', $new_dir))
        {
            return DataReturn('压缩包生成失败', -100);
        }

        // 生成成功删除目录
        \base\FileUtil::UnlinkDir($new_dir);

        // 开始下载
D
Devil 已提交
397
        if(\base\FileUtil::DownloadFile($new_dir.'.zip', $config['name'].'_v'.$config['ver'].'.zip'))
G
gongfuxiang 已提交
398 399 400 401 402 403
        {
            @unlink($new_dir.'.zip');
        } else {
            return DataReturn('下载失败', -100);
        }
    }
D
Devil 已提交
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464

    /**
     * 主题配置信息
     * @author  Devil
     * @blog    http://gong.gg/
     * @version 1.0.0
     * @date    2021-04-22
     * @desc    description
     * @param   [string]          $theme [主题标识]
     */
    public static function ThemeConfig($theme)
    {
        // 获取配置信息
        $config_file = ROOT.self::$html_path.$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);
    }

    /**
     * web主题更新信息
     * @author  Devil
     * @blog    http://gong.gg/
     * @version 1.0.0
     * @date    2021-04-22
     * @desc    description
     * @param   [array]           $params [输入参数]
     */
    public static function ThemeUpgradeInfo($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);
G
gongfuxiang 已提交
465
                if(!empty($res['data']) && is_array($res['data']))
D
Devil 已提交
466 467 468 469 470 471 472 473 474
                {
                    $res['data'] = array_column($res['data'], null, 'plugins');
                }
                return $res;
            }
        }

        return DataReturn('无插件数据', 0);
    }
D
v1.2.0  
devil_gong 已提交
475 476
}
?>