CmsService.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: Administrator
  5. * Date: 2022/1/12
  6. * Time: 14:15
  7. */
  8. namespace app\common\service;
  9. use app\common\model\Article;
  10. use app\common\model\ArticleCate;
  11. use app\common\model\Catalog;
  12. use app\common\model\Templet;
  13. use app\common\model\Webconfig;
  14. use file\DirHelper;
  15. use file\FileHelper;
  16. use file\PathHelper;
  17. use paginate\Bootstrap;
  18. use think\Controller;
  19. use think\Log;
  20. use think\Request;
  21. class CmsService extends Controller
  22. {
  23. /**
  24. * 输出调试信息
  25. * @param $msg
  26. */
  27. public function info($msg)
  28. {
  29. if ($this->request->isCli()) {
  30. echo $msg . PHP_EOL;
  31. } else {
  32. Log::log($msg);
  33. }
  34. }
  35. /**
  36. * 记录错误信息日志
  37. * @param $e \Exception
  38. */
  39. public static function errorlog($e)
  40. {
  41. Log::error("异常信息:" . $e->getMessage());
  42. Log::error('请求数据:' . var_export(Request::instance()->param(), true));
  43. Log::error($e->getTraceAsString());
  44. }
  45. /**
  46. * 输出错误页面
  47. * @param $basePath
  48. * @return string
  49. */
  50. public static function errorHtml($basePath)
  51. {
  52. $basePath = str_replace(Templet::getRootDir(), '', $basePath);
  53. return "<html><head><meta charset=\"utf-8\"></head><body>" . $basePath . ",此栏目下没有文章,请添加文章后再尝试生成</body></html>";
  54. }
  55. /**
  56. * 替换预览内容
  57. * @return array
  58. */
  59. public static function getArticlePerViewReplace()
  60. {
  61. return [
  62. '_UPLOADS_' => '/uploads/q1464674022',
  63. '_ROOT_' => '/?path=',
  64. ];
  65. }
  66. /**
  67. * 替换生成内容
  68. * @return array
  69. */
  70. public static function getArticleReplace()
  71. {
  72. return [
  73. '_UPLOADS_' => '',
  74. '_ROOT_' => '',
  75. ];
  76. }
  77. /**
  78. * 导出上传目录
  79. */
  80. public function copyUploads()
  81. {
  82. //创建目录
  83. $destPath = Templet::getRootDir() . 'uploads';
  84. DirHelper::makeDir($destPath);
  85. $this->info("导出上传目录" . $destPath);
  86. $path = Templet::UPLOAD_PATH . 'admin';
  87. if (file_exists($path)) {
  88. DirHelper::copyDir($path, $destPath . DS . 'admin', false);//不覆盖同名文件
  89. }
  90. $path = Templet::UPLOAD_PATH . 'ueditor';
  91. if (file_exists($path)) {
  92. DirHelper::copyDir($path, $destPath . DS . 'ueditor', false);//不覆盖同名文件
  93. }
  94. //导出模板文件
  95. // $path = APP_PATH . 'admin' . DS . 'view' . DS . 'q1464674022';
  96. // if (file_exists($path)) {
  97. // copyDir($path, $destPath . DS . 'template');
  98. // }
  99. }
  100. /**
  101. * 执行文件生成
  102. * @param $filePath
  103. * @param $content
  104. */
  105. public function createFile($filePath, $content)
  106. {
  107. if (!ifContain($filePath, Templet::getRootDir())) {
  108. return;//不允许执行外部路径
  109. }
  110. if ($this->request->has('delete')) {
  111. if (file_exists($filePath)) {
  112. unlink($filePath);//删除
  113. }
  114. } elseif ($this->request->has('cover')) {
  115. FileHelper::save($filePath, $content);//覆盖
  116. } else {
  117. //默认不覆盖,文件不存在或内容修改才覆盖
  118. if (!file_exists($filePath)) {
  119. FileHelper::save($filePath, $content);
  120. } else {
  121. $change = str_replace(file_get_contents($filePath), '', $content);
  122. if (!empty($change)) {
  123. FileHelper::save($filePath, $content);
  124. }
  125. }
  126. }
  127. }
  128. /**
  129. * 分页url处理
  130. * @param $catalog
  131. * @param $tid
  132. * @return mixed
  133. */
  134. public static function getPaginatePath($catalog, $tid)
  135. {
  136. $rule = str_replace("{page}", "[PAGE]", $catalog->articlelist_rule);
  137. return replaceUrlDS(ArticleCate::getRulePath($catalog->path, $rule, $catalog->id, $tid));
  138. }
  139. /**
  140. * 栏目预览
  141. * @param $catalog Catalog
  142. * @param $cate ArticleCate
  143. * @param int $page
  144. * @return mixed|string
  145. */
  146. public function perviewCatalog($catalog, $cate = null, $page = 1)
  147. {
  148. $tid = $cate ? $cate->id : 0;
  149. if ($catalog) {
  150. try {
  151. $replace = self::getArticlePerViewReplace();
  152. //先预览静态文件
  153. // $filepath = Templet::getRootDir() . $catalog->getUri();
  154. // if (file_exists($filepath) && ifContain($filepath, '.html')) {
  155. // return $this->fetch($filepath, [], $replace);
  156. // }
  157. //全局对象
  158. $cataLogModel = new Catalog();
  159. $articleModel = new Article();
  160. $articleCateModel = new ArticleCate();
  161. $this->assign([
  162. 'cataLogModel' => $cataLogModel,
  163. 'articleCateModel' => $articleCateModel,
  164. 'articleModel' => $articleModel,
  165. ]);
  166. if ($catalog->type == Catalog::TYPE_CATALOG) {
  167. return $this->fetch($catalog->getCatalogTemplet()->getRealPath(), ['catalog' => $catalog], $replace);
  168. } elseif ($catalog->type == Catalog::TYPE_ARTICLE_LIST) {
  169. if ($catalog->article_cate_count > 0) {
  170. $where = [
  171. 'article_cate_id' => $tid,
  172. 'status' => $articleModel::STATUS_OPEN,
  173. ];
  174. } else {
  175. $where = [
  176. 'catalog_id' => $catalog->id,
  177. 'status' => $articleModel::STATUS_OPEN,
  178. ];
  179. }
  180. $total = $articleModel->where($where)->where($catalog->articlelist_where)->count();
  181. $listRows = $catalog->articlelist_rows??10;//分页
  182. $orderby = $catalog->articlelist_sort??'create_time desc';//排序
  183. $prefix = isset($replace['_ROOT_']) ? $replace['_ROOT_'] : '_ROOT_';
  184. $articleList = $articleModel->where($where)->where($catalog->articlelist_where)->page($page, $listRows)->order($orderby)->select();
  185. $paginate = new Bootstrap($articleList, $listRows, $page, $total, false, ["path" => $prefix . self::getPaginatePath($catalog, $tid)]);
  186. return $this->fetch($catalog->getCatalogTemplet()->getRealPath(), ['catalog' => $catalog, 'articleCate' => $cate, 'paginate' => $paginate], $replace);
  187. }
  188. return "";
  189. } catch (\Exception $e) {
  190. $this->error('预览失败:' . $e->getMessage());
  191. }
  192. }
  193. abort(404, "预览失败:catelog is null");
  194. }
  195. /**
  196. * 分类预览
  197. * @param $articleCate ArticleCate
  198. * @param int $page
  199. * @return mixed|string
  200. */
  201. public function perviewArticleCate($articleCate, $page = 1)
  202. {
  203. if ($articleCate) {
  204. try {
  205. //先预览静态文件
  206. // $filepath = Templet::getRootDir() . $articleCate->getUri();
  207. // if (file_exists($filepath)) {
  208. // return $this->fetch($filepath, [], $replace);
  209. // }
  210. $catalog = $articleCate->getFirstCatalog();
  211. return $this->perviewCatalog($catalog, $articleCate, $page);
  212. } catch (\Exception $e) {
  213. $this->error('预览失败:' . $e->getMessage());
  214. }
  215. }
  216. abort(404, "预览失败:articleCate is null");
  217. }
  218. /**
  219. * 文章预览
  220. * @param $article Article
  221. * @return mixed
  222. */
  223. public function perviewArticle($article)
  224. {
  225. if ($article) {
  226. try {
  227. $replace = self::getArticlePerViewReplace();
  228. //先预览静态文件
  229. // $filepath = Templet::getRootDir() . $article->getUri();
  230. // if (file_exists($filepath) && ifContain($filepath, '.html')) {
  231. // return $this->fetch($filepath, [], $replace);
  232. // }
  233. //全局对象
  234. $cataLogModel = new Catalog();
  235. $articleModel = new Article();
  236. $articleCateModel = new ArticleCate();
  237. $this->assign([
  238. 'cataLogModel' => $cataLogModel,
  239. 'articleCateModel' => $articleCateModel,
  240. 'articleModel' => $articleModel,
  241. ]);
  242. $catalog = $article->catalog_id ? $article->catalog : $article->getFirstCatalog();
  243. $cate = $article->catalog_id ? null : $article->cate;
  244. /** @var Catalog $catalog */
  245. return $this->fetch($catalog->getArticleTemplet()->getRealPath(), ['catalog' => $catalog, 'article' => $article, 'articleCate' => $cate], $replace);
  246. } catch (\Exception $e) {
  247. $this->error('预览失败:' . $e->getMessage());
  248. }
  249. }
  250. abort(404, "预览失败:article is null");
  251. }
  252. /**
  253. * 预览网站静态文件
  254. * @return string
  255. */
  256. public function perviewHtmlSite()
  257. {
  258. if ($this->request->has('path')) {
  259. $filepath = Templet::getRootDir() . $this->request->get('path');
  260. if (!ifContain($filepath, '.html')) {
  261. $filepath .= DS . 'index.html';
  262. }
  263. } else {
  264. $filepath = Templet::getRootDir() . 'index.html';
  265. }
  266. if (file_exists($filepath)) {
  267. $replace = self::getArticlePerViewReplace();
  268. $html = $this->fetch($filepath, [], $replace);
  269. //todo auto replace assets path
  270. exit($html);
  271. } else {
  272. if ($this->request->has('path'))
  273. abort(404);
  274. }
  275. }
  276. /**
  277. * 预览网站模板
  278. */
  279. public function perviewSite()
  280. {
  281. $path = $this->request->get('path', '/index.html');
  282. if (!ifContain($path, '.html')) {
  283. $path .= '/index.html';
  284. }
  285. $path = str_replace('//', '/', appendStartDS($path, '/'));
  286. $catalogModel = new Catalog();
  287. $catalog = $catalogModel->where('path', $path)->find();
  288. if ($catalog) {
  289. exit($this->perviewCatalog($catalog));
  290. }
  291. $path = deleteStartDS($path, '/');
  292. $catalogs = $catalogModel->field('article_rule,articlelist_rule')->distinct(true)->where('article_rule', '<>', '')->select();
  293. foreach ($catalogs as $catalog) {
  294. $matches = $this->matchePath($catalog->article_rule, $path);
  295. if ($matches) {
  296. if (isset($matches['{aid}'])) {
  297. $article = Article::get($matches['{aid}']);
  298. exit($this->perviewArticle($article));
  299. }
  300. }
  301. $matches = $this->matchePath($catalog->articlelist_rule, $path);
  302. if ($matches) {
  303. if (isset($matches['{tid}'])) {
  304. $articlecate = (new ArticleCate())->where('id', $matches['{tid}'])->find();
  305. } elseif (isset($matches['{tpath}'])) {
  306. $articlecate = (new ArticleCate())->where('en_name', $matches['{tpath}'])->find();
  307. } else {
  308. continue;
  309. }
  310. $page = $matches['{page}'] ?? 1;
  311. exit($this->perviewArticleCate($articlecate, $page));
  312. }
  313. }
  314. }
  315. /**
  316. * 解析path
  317. * @param $rule
  318. * @param $path
  319. * @return mixed
  320. */
  321. private function matchePath($rule, $path)
  322. {
  323. $rule_arr = explode('/', $rule);
  324. $path_arr = explode('/', $path);
  325. if (ifContain($path, 'index.html')) {
  326. array_pop($rule_arr);
  327. array_pop($path_arr);
  328. $rule = implode('/', $rule_arr);
  329. $path = implode('/', $path_arr);
  330. }
  331. $pattern2p = str_replace(['/', '{cpath}', '{tpath}', '{Y}', '{M}', '{D}', '{aid}', '{page}', '{cid}', '{tid}'], ['\/', '(.*?)', '(.*?)', '(\d{4})', '(\d{2})', '(\d{2})', '(\d*?)', '(\d*?)', '(\d*?)', '(\d*?)'], $rule);
  332. $pattern2r = str_replace(['/', '{cpath}', '{tpath}', '{Y}', '{M}', '{D}', '{aid}', '{page}', '{cid}', '{tid}'], ['\/', '({cpath})', '({tpath})', '({Y})', '({M})', '({D})', '({aid})', '({page})', '({cid})', '({tid})'], $rule);
  333. if (preg_match('/^' . $pattern2p . '$/', $path, $matches2p)) {
  334. if (preg_match('/^' . $pattern2r . '$/', $rule, $matches2r)) {
  335. $res = [];
  336. foreach ($matches2r as $i => $item) {
  337. if ($i > 0)
  338. $res[$item] = $matches2p[$i];
  339. }
  340. return $res;
  341. }
  342. }
  343. }
  344. }