diff --git a/app/admin/module/change/2020.08.03.00.md b/app/admin/module/change/2020.08.03.00.md
new file mode 100644
index 000000000..d594c7fec
--- /dev/null
+++ b/app/admin/module/change/2020.08.03.00.md
@@ -0,0 +1,2 @@
+# 系统模块初始化成功
+* 这次更新了许多内容哦
\ No newline at end of file
diff --git a/app/admin/ver.php b/app/admin/module/version.php
similarity index 70%
rename from app/admin/ver.php
rename to app/admin/module/version.php
index 651dfbf37..0697ad8d8 100644
--- a/app/admin/ver.php
+++ b/app/admin/module/version.php
@@ -13,20 +13,9 @@
// | github 代码仓库:https://github.com/zoujingli/ThinkAdmin
// +----------------------------------------------------------------------
-// 模块配置文件
return [
'name' => 'admin',
'author' => 'Anyon',
- 'version' => '2020.08.05.00',
+ 'version' => '2020.08.03.00',
'content' => 'ThinkAdmin 系统基础模块',
- 'changes' => [
- '2020.08.05.00' => [
- 'content' => '优化系统模块管理',
- 'database' => ['select version()'],
- ],
- '2020.08.03.00' => [
- 'content' => '系统模块初始化提交',
- 'database' => ['select version()'],
- ],
- ],
];
\ No newline at end of file
diff --git a/app/wechat/module/change/2020.08.03.00.md b/app/wechat/module/change/2020.08.03.00.md
new file mode 100644
index 000000000..4ae5bfdba
--- /dev/null
+++ b/app/wechat/module/change/2020.08.03.00.md
@@ -0,0 +1,2 @@
+# 微信模块初始化成功
+* 这次更新了许多内容哦
\ No newline at end of file
diff --git a/app/wechat/module/database/2020.08.03.00_install.sql b/app/wechat/module/database/2020.08.03.00_install.sql
new file mode 100644
index 000000000..de64207f6
--- /dev/null
+++ b/app/wechat/module/database/2020.08.03.00_install.sql
@@ -0,0 +1,131 @@
+-- ----------------------------
+-- Table structure for wechat_fans
+-- ----------------------------
+DROP TABLE IF EXISTS `_PREFIX_wechat_fans`;
+CREATE TABLE `_PREFIX_wechat_fans` (
+ `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `appid` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '公众号APPID',
+ `unionid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '粉丝unionid',
+ `openid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '粉丝openid',
+ `tagid_list` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '粉丝标签id',
+ `is_black` tinyint(1) UNSIGNED NULL DEFAULT 0 COMMENT '是否为黑名单状态',
+ `subscribe` tinyint(1) UNSIGNED NULL DEFAULT 0 COMMENT '关注状态(0未关注,1已关注)',
+ `nickname` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '用户昵称',
+ `sex` tinyint(1) UNSIGNED NULL DEFAULT 0 COMMENT '用户性别(1男性,2女性,0未知)',
+ `country` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '用户所在国家',
+ `province` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '用户所在省份',
+ `city` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '用户所在城市',
+ `language` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '用户的语言(zh_CN)',
+ `headimgurl` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '用户头像',
+ `subscribe_time` bigint(20) UNSIGNED NULL DEFAULT 0 COMMENT '关注时间',
+ `subscribe_at` datetime NULL DEFAULT NULL COMMENT '关注时间',
+ `remark` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '备注',
+ `subscribe_scene` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '扫码关注场景',
+ `qr_scene` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '二维码场景值',
+ `qr_scene_str` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '二维码场景内容',
+ `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ PRIMARY KEY (`id`) USING BTREE,
+ INDEX `index_wechat_fans_openid`(`openid`) USING BTREE,
+ INDEX `index_wechat_fans_unionid`(`unionid`) USING BTREE,
+ INDEX `index_wechat_fans_is_back`(`is_black`) USING BTREE,
+ INDEX `index_wechat_fans_subscribe`(`subscribe`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '微信-粉丝';
+
+-- ----------------------------
+-- Table structure for wechat_fans_tags
+-- ----------------------------
+DROP TABLE IF EXISTS `_PREFIX_wechat_fans_tags`;
+CREATE TABLE `_PREFIX_wechat_fans_tags` (
+ `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '标签ID',
+ `appid` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '公众号APPID',
+ `name` varchar(35) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '标签名称',
+ `count` bigint(20) UNSIGNED NULL DEFAULT 0 COMMENT '总数',
+ `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',
+ INDEX `index_wechat_fans_tags_id`(`id`) USING BTREE,
+ INDEX `index_wechat_fans_tags_appid`(`appid`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '微信-标签';
+
+-- ----------------------------
+-- Table structure for wechat_keys
+-- ----------------------------
+DROP TABLE IF EXISTS `_PREFIX_wechat_keys`;
+CREATE TABLE `_PREFIX_wechat_keys` (
+ `id` bigint(20) NOT NULL AUTO_INCREMENT,
+ `appid` char(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '公众号APPID',
+ `type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '类型(text,image,news)',
+ `keys` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '关键字',
+ `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '文本内容',
+ `image_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '图片链接',
+ `voice_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '语音链接',
+ `music_title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '音乐标题',
+ `music_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '音乐链接',
+ `music_image` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '缩略图片',
+ `music_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '音乐描述',
+ `video_title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '视频标题',
+ `video_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '视频URL',
+ `video_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '视频描述',
+ `news_id` bigint(20) UNSIGNED NULL DEFAULT NULL COMMENT '图文ID',
+ `sort` bigint(20) UNSIGNED NULL DEFAULT 0 COMMENT '排序字段',
+ `status` tinyint(1) UNSIGNED NULL DEFAULT 1 COMMENT '状态(0禁用,1启用)',
+ `create_by` bigint(20) UNSIGNED NULL DEFAULT 0 COMMENT '创建人',
+ `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
+ PRIMARY KEY (`id`) USING BTREE,
+ INDEX `index_wechat_keys_appid`(`appid`) USING BTREE,
+ INDEX `index_wechat_keys_type`(`type`) USING BTREE,
+ INDEX `index_wechat_keys_keys`(`keys`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '微信-关键字';
+
+-- ----------------------------
+-- Table structure for wechat_media
+-- ----------------------------
+DROP TABLE IF EXISTS `_PREFIX_wechat_media`;
+CREATE TABLE `_PREFIX_wechat_media` (
+ `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `appid` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '公众号ID',
+ `md5` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '文件md5',
+ `type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '媒体类型',
+ `media_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '永久素材MediaID',
+ `local_url` varchar(300) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '本地文件链接',
+ `media_url` varchar(300) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '远程图片链接',
+ `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ PRIMARY KEY (`id`) USING BTREE,
+ INDEX `index_wechat_media_appid`(`appid`) USING BTREE,
+ INDEX `index_wechat_media_md5`(`md5`) USING BTREE,
+ INDEX `index_wechat_media_type`(`type`) USING BTREE,
+ INDEX `index_wechat_media_media_id`(`media_id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '微信-素材';
+
+-- ----------------------------
+-- Table structure for wechat_news
+-- ----------------------------
+DROP TABLE IF EXISTS `_PREFIX_wechat_news`;
+CREATE TABLE `_PREFIX_wechat_news` (
+ `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `media_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '永久素材MediaID',
+ `local_url` varchar(300) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '永久素材外网URL',
+ `article_id` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '关联图文ID(用英文逗号做分割)',
+ `is_deleted` tinyint(1) UNSIGNED NULL DEFAULT 0 COMMENT '删除状态(0未删除,1已删除)',
+ `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ `create_by` bigint(20) NULL DEFAULT NULL COMMENT '创建人',
+ PRIMARY KEY (`id`) USING BTREE,
+ INDEX `index_wechat_news_artcle_id`(`article_id`) USING BTREE,
+ INDEX `index_wechat_news_media_id`(`media_id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '微信-图文';
+
+-- ----------------------------
+-- Table structure for wechat_news_article
+-- ----------------------------
+DROP TABLE IF EXISTS `_PREFIX_wechat_news_article`;
+CREATE TABLE `_PREFIX_wechat_news_article` (
+ `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
+ `title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '素材标题',
+ `local_url` varchar(300) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '永久素材显示URL',
+ `show_cover_pic` tinyint(4) UNSIGNED NULL DEFAULT 0 COMMENT '显示封面(0不显示,1显示)',
+ `author` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '文章作者',
+ `digest` varchar(300) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '摘要内容',
+ `content` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '图文内容',
+ `content_source_url` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '原文地址',
+ `read_num` bigint(20) UNSIGNED NULL DEFAULT 0 COMMENT '阅读数量',
+ `create_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+ PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '微信-文章';
\ No newline at end of file
diff --git a/app/wechat/module/database/2020.08.03.00_unset.sql b/app/wechat/module/database/2020.08.03.00_unset.sql
new file mode 100644
index 000000000..1d9e3dd3f
--- /dev/null
+++ b/app/wechat/module/database/2020.08.03.00_unset.sql
@@ -0,0 +1,6 @@
+DROP TABLE IF EXISTS `_PREFIX_wechat_fans`;
+DROP TABLE IF EXISTS `_PREFIX_wechat_fans_tags`;
+DROP TABLE IF EXISTS `_PREFIX_wechat_keys`;
+DROP TABLE IF EXISTS `_PREFIX_wechat_media`;
+DROP TABLE IF EXISTS `_PREFIX_wechat_news`;
+DROP TABLE IF EXISTS `_PREFIX_wechat_news_article`;
\ No newline at end of file
diff --git a/app/wechat/module/database/2020.08.03.00_update.sql b/app/wechat/module/database/2020.08.03.00_update.sql
new file mode 100644
index 000000000..b3d9bbc7f
--- /dev/null
+++ b/app/wechat/module/database/2020.08.03.00_update.sql
@@ -0,0 +1 @@
+ 'wechat',
'author' => 'Anyon',
'version' => '2020.08.03.01',
'content' => 'ThinkAdmin 微信基础模块',
- 'changes' => [
- '2020.08.03.00' => [
- 'content' => '模块初始化提交',
- 'database' => [
- 'select version()',
- ],
- ],
- ],
];
\ No newline at end of file
diff --git a/composer.lock b/composer.lock
index 862510038..3f9d0b6e7 100644
--- a/composer.lock
+++ b/composer.lock
@@ -937,12 +937,12 @@
"source": {
"type": "git",
"url": "https://github.com/zoujingli/ThinkLibrary.git",
- "reference": "08aff82bffe0e4b6356373c65ddf7c2d521598a5"
+ "reference": "822d461c4f23e5ab3cd3489cd740a7171706e367"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/zoujingli/ThinkLibrary/zipball/08aff82bffe0e4b6356373c65ddf7c2d521598a5",
- "reference": "08aff82bffe0e4b6356373c65ddf7c2d521598a5",
+ "url": "https://api.github.com/repos/zoujingli/ThinkLibrary/zipball/822d461c4f23e5ab3cd3489cd740a7171706e367",
+ "reference": "822d461c4f23e5ab3cd3489cd740a7171706e367",
"shasum": "",
"mirrors": [
{
@@ -956,6 +956,7 @@
"ext-gd": "*",
"ext-iconv": "*",
"ext-json": "*",
+ "ext-mbstring": "*",
"topthink/framework": "^6.0"
},
"type": "library",
@@ -986,7 +987,7 @@
],
"description": "ThinkPHP v6.0 Development Library",
"homepage": "http://thinkadmin.top",
- "time": "2020-08-14T09:41:49+00:00"
+ "time": "2020-08-17T07:02:36+00:00"
},
{
"name": "zoujingli/wechat-developer",
diff --git a/config/database.php b/config/database.php
index a7c3df48d..a3bcc8a16 100644
--- a/config/database.php
+++ b/config/database.php
@@ -28,7 +28,7 @@ return [
// 数据库类型
'type' => 'mysql',
// 服务器地址
- 'hostname' => '127.0.0.1',
+ 'hostname' => 'server.cuci.cc',
// 数据库名
'database' => 'admin_v6',
// 用户名
diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
index 8c50a3f7f..a846263da 100644
--- a/vendor/composer/autoload_classmap.php
+++ b/vendor/composer/autoload_classmap.php
@@ -298,6 +298,7 @@ return array(
'think\\admin\\service\\CaptchaService' => $vendorDir . '/zoujingli/think-library/src/service/CaptchaService.php',
'think\\admin\\service\\ExpressService' => $vendorDir . '/zoujingli/think-library/src/service/ExpressService.php',
'think\\admin\\service\\InstallService' => $vendorDir . '/zoujingli/think-library/src/service/InstallService.php',
+ 'think\\admin\\service\\MarkdownService' => $vendorDir . '/zoujingli/think-library/src/service/MarkdownService.php',
'think\\admin\\service\\MenuService' => $vendorDir . '/zoujingli/think-library/src/service/MenuService.php',
'think\\admin\\service\\MessageService' => $vendorDir . '/zoujingli/think-library/src/service/MessageService.php',
'think\\admin\\service\\ModuleService' => $vendorDir . '/zoujingli/think-library/src/service/ModuleService.php',
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
index fc47d0280..482b5dee5 100644
--- a/vendor/composer/autoload_static.php
+++ b/vendor/composer/autoload_static.php
@@ -431,6 +431,7 @@ class ComposerStaticInitb911c14a0826c73d9f097343fd33a252
'think\\admin\\service\\CaptchaService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/CaptchaService.php',
'think\\admin\\service\\ExpressService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/ExpressService.php',
'think\\admin\\service\\InstallService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/InstallService.php',
+ 'think\\admin\\service\\MarkdownService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/MarkdownService.php',
'think\\admin\\service\\MenuService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/MenuService.php',
'think\\admin\\service\\MessageService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/MessageService.php',
'think\\admin\\service\\ModuleService' => __DIR__ . '/..' . '/zoujingli/think-library/src/service/ModuleService.php',
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
index 4673200a5..f45475293 100644
--- a/vendor/composer/installed.json
+++ b/vendor/composer/installed.json
@@ -963,12 +963,12 @@
"source": {
"type": "git",
"url": "https://github.com/zoujingli/ThinkLibrary.git",
- "reference": "08aff82bffe0e4b6356373c65ddf7c2d521598a5"
+ "reference": "822d461c4f23e5ab3cd3489cd740a7171706e367"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/zoujingli/ThinkLibrary/zipball/08aff82bffe0e4b6356373c65ddf7c2d521598a5",
- "reference": "08aff82bffe0e4b6356373c65ddf7c2d521598a5",
+ "url": "https://api.github.com/repos/zoujingli/ThinkLibrary/zipball/822d461c4f23e5ab3cd3489cd740a7171706e367",
+ "reference": "822d461c4f23e5ab3cd3489cd740a7171706e367",
"shasum": "",
"mirrors": [
{
@@ -982,9 +982,10 @@
"ext-gd": "*",
"ext-iconv": "*",
"ext-json": "*",
+ "ext-mbstring": "*",
"topthink/framework": "^6.0"
},
- "time": "2020-08-14T09:41:49+00:00",
+ "time": "2020-08-17T07:02:36+00:00",
"type": "library",
"extra": {
"think": {
diff --git a/vendor/services.php b/vendor/services.php
index 0f79115a8..c13f3c074 100644
--- a/vendor/services.php
+++ b/vendor/services.php
@@ -1,5 +1,5 @@
'think\\admin\\Library',
diff --git a/vendor/zoujingli/think-library/composer.json b/vendor/zoujingli/think-library/composer.json
index a01e4cf91..6d3ab8ba0 100644
--- a/vendor/zoujingli/think-library/composer.json
+++ b/vendor/zoujingli/think-library/composer.json
@@ -15,6 +15,7 @@
"ext-curl": "*",
"ext-json": "*",
"ext-iconv": "*",
+ "ext-mbstring": "*",
"topthink/framework": "^6.0"
},
"autoload": {
diff --git a/vendor/zoujingli/think-library/src/command/Queue.php b/vendor/zoujingli/think-library/src/command/Queue.php
index 44e8b72cf..b7e7fe3cd 100644
--- a/vendor/zoujingli/think-library/src/command/Queue.php
+++ b/vendor/zoujingli/think-library/src/command/Queue.php
@@ -94,7 +94,7 @@ class Queue extends Command
$host = $this->input->getOption('host') ?: '127.0.0.1';
$root = $this->app->getRootPath() . 'public' . DIRECTORY_SEPARATOR;
$command = "php -S {$host}:{$port} -t {$root} {$root}router.php";
- $this->output->highlight("># {$command}");
+ $this->output->comment("># {$command}");
if (count($result = $this->process->query($command)) > 0) {
if ($this->process->iswin()) $this->process->exec("start http://{$host}:{$port}");
$this->output->writeln(">> WebServer process already exist for pid {$result[0]['pid']}");
@@ -116,7 +116,7 @@ class Queue extends Command
{
$root = $this->app->getRootPath() . 'public' . DIRECTORY_SEPARATOR;
if (count($result = $this->process->query("-t {$root} {$root}router.php")) > 0) {
- $this->output->highlight("># {$result[0]['cmd']}");
+ $this->output->comment("># {$result[0]['cmd']}");
$this->output->writeln(">> WebServer process {$result[0]['pid']} running");
} else {
$this->output->writeln(">> The WebServer process is not running");
@@ -144,7 +144,7 @@ class Queue extends Command
{
$this->app->db->name($this->table)->count();
$command = $this->process->think('xadmin:queue listen');
- $this->output->highlight("># {$command}");
+ $this->output->comment("># {$command}");
if (count($result = $this->process->query($command)) > 0) {
$this->output->writeln(">> Asynchronous daemons already exist for pid {$result[0]['pid']}");
} else {
@@ -234,7 +234,7 @@ class Queue extends Command
[$start, $where] = [microtime(true), [['status', '=', 1], ['exec_time', '<=', time()]]];
foreach ($this->app->db->name($this->table)->where($where)->order('exec_time asc')->select()->toArray() as $vo) try {
$command = $this->process->think("xadmin:queue dorun {$vo['code']} -");
- $this->output->highlight("># {$command}");
+ $this->output->comment("># {$command}");
if (count($this->process->query($command)) > 0) {
$this->output->writeln(">> Already in progress -> [{$vo['code']}] {$vo['title']}");
} else {
diff --git a/vendor/zoujingli/think-library/src/extend/Parsedown.php b/vendor/zoujingli/think-library/src/extend/Parsedown.php
new file mode 100644
index 000000000..07d321232
--- /dev/null
+++ b/vendor/zoujingli/think-library/src/extend/Parsedown.php
@@ -0,0 +1,1504 @@
+DefinitionData = [];
+ # standardize line breaks
+ $text = str_replace(["\r\n", "\r"], "\n", $text);
+ # remove surrounding line breaks
+ $text = trim($text, "\n");
+ # split text into lines
+ $lines = explode("\n", $text);
+ # iterate through lines to identify blocks
+ $markup = $this->lines($lines);
+ # trim line breaks
+ $markup = trim($markup, "\n");
+ return $markup;
+ }
+
+ #
+ # Setters
+ #
+
+ function setBreaksEnabled($breaksEnabled)
+ {
+ $this->breaksEnabled = $breaksEnabled;
+ return $this;
+ }
+
+ protected $breaksEnabled;
+
+ function setMarkupEscaped($markupEscaped)
+ {
+ $this->markupEscaped = $markupEscaped;
+ return $this;
+ }
+
+ protected $markupEscaped;
+
+ function setUrlsLinked($urlsLinked)
+ {
+ $this->urlsLinked = $urlsLinked;
+ return $this;
+ }
+
+ protected $urlsLinked = true;
+
+ function setSafeMode($safeMode)
+ {
+ $this->safeMode = (bool)$safeMode;
+ return $this;
+ }
+
+ protected $safeMode;
+
+ protected $safeLinksWhitelist = [
+ 'http://',
+ 'https://',
+ 'ftp://',
+ 'ftps://',
+ 'mailto:',
+ 'data:image/png;base64,',
+ 'data:image/gif;base64,',
+ 'data:image/jpeg;base64,',
+ 'irc:',
+ 'ircs:',
+ 'git:',
+ 'ssh:',
+ 'news:',
+ 'steam:',
+ ];
+
+ #
+ # Lines
+ #
+
+ protected $BlockTypes = [
+ '#' => ['Header'],
+ '*' => ['Rule', 'List'],
+ '+' => ['List'],
+ '-' => ['SetextHeader', 'Table', 'Rule', 'List'],
+ '0' => ['List'],
+ '1' => ['List'],
+ '2' => ['List'],
+ '3' => ['List'],
+ '4' => ['List'],
+ '5' => ['List'],
+ '6' => ['List'],
+ '7' => ['List'],
+ '8' => ['List'],
+ '9' => ['List'],
+ ':' => ['Table'],
+ '<' => ['Comment', 'Markup'],
+ '=' => ['SetextHeader'],
+ '>' => ['Quote'],
+ '[' => ['Reference'],
+ '_' => ['Rule'],
+ '`' => ['FencedCode'],
+ '|' => ['Table'],
+ '~' => ['FencedCode'],
+ ];
+
+ # ~
+
+ protected $unmarkedBlockTypes = ['Code'];
+
+ #
+ # Blocks
+ #
+
+ protected function lines(array $lines)
+ {
+ $CurrentBlock = null;
+ foreach ($lines as $line) {
+ if (chop($line) === '') {
+ if (isset($CurrentBlock)) {
+ $CurrentBlock['interrupted'] = true;
+ }
+ continue;
+ }
+ if (strpos($line, "\t") !== false) {
+ $parts = explode("\t", $line);
+ $line = $parts[0];
+ unset($parts[0]);
+ foreach ($parts as $part) {
+ $shortage = 4 - mb_strlen($line, 'utf-8') % 4;
+ $line .= str_repeat(' ', $shortage);
+ $line .= $part;
+ }
+ }
+ $indent = 0;
+ while (isset($line[$indent]) and $line[$indent] === ' ') {
+ $indent++;
+ }
+ $text = $indent > 0 ? substr($line, $indent) : $line;
+ $Line = ['body' => $line, 'indent' => $indent, 'text' => $text];
+ if (isset($CurrentBlock['continuable'])) {
+ $Block = $this->{'block' . $CurrentBlock['type'] . 'Continue'}($Line, $CurrentBlock);
+ if (isset($Block)) {
+ $CurrentBlock = $Block;
+ continue;
+ } else {
+ if ($this->isBlockCompletable($CurrentBlock['type'])) {
+ $CurrentBlock = $this->{'block' . $CurrentBlock['type'] . 'Complete'}($CurrentBlock);
+ }
+ }
+ }
+ $marker = $text[0];
+ $blockTypes = $this->unmarkedBlockTypes;
+ if (isset($this->BlockTypes[$marker])) {
+ foreach ($this->BlockTypes[$marker] as $blockType) {
+ $blockTypes [] = $blockType;
+ }
+ }
+ foreach ($blockTypes as $blockType) {
+ $Block = $this->{'block' . $blockType}($Line, $CurrentBlock);
+ if (isset($Block)) {
+ $Block['type'] = $blockType;
+ if (!isset($Block['identified'])) {
+ $Blocks [] = $CurrentBlock;
+ $Block['identified'] = true;
+ }
+ if ($this->isBlockContinuable($blockType)) {
+ $Block['continuable'] = true;
+ }
+ $CurrentBlock = $Block;
+ continue 2;
+ }
+ }
+
+ # ~
+
+ if (isset($CurrentBlock) and !isset($CurrentBlock['type']) and !isset($CurrentBlock['interrupted'])) {
+ $CurrentBlock['element']['text'] .= "\n" . $text;
+ } else {
+ $Blocks [] = $CurrentBlock;
+ $CurrentBlock = $this->paragraph($Line);
+ $CurrentBlock['identified'] = true;
+ }
+ }
+
+ if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type'])) {
+ $CurrentBlock = $this->{'block' . $CurrentBlock['type'] . 'Complete'}($CurrentBlock);
+ }
+
+ $Blocks [] = $CurrentBlock;
+
+ unset($Blocks[0]);
+
+ # ~
+
+ $markup = '';
+
+ foreach ($Blocks as $Block) {
+ if (isset($Block['hidden'])) {
+ continue;
+ }
+
+ $markup .= "\n";
+ $markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']);
+ }
+
+ $markup .= "\n";
+
+ # ~
+
+ return $markup;
+ }
+
+ protected function isBlockContinuable($Type)
+ {
+ return method_exists($this, 'block' . $Type . 'Continue');
+ }
+
+ protected function isBlockCompletable($Type)
+ {
+ return method_exists($this, 'block' . $Type . 'Complete');
+ }
+
+ #
+ # Code
+
+ protected function blockCode($Line, $Block = null)
+ {
+ if (isset($Block) and !isset($Block['type']) and !isset($Block['interrupted'])) {
+ return;
+ }
+
+ if ($Line['indent'] >= 4) {
+ $text = substr($Line['body'], 4);
+
+ $Block = [
+ 'element' => [
+ 'name' => 'pre',
+ 'handler' => 'element',
+ 'text' => [
+ 'name' => 'code',
+ 'text' => $text,
+ ],
+ ],
+ ];
+
+ return $Block;
+ }
+ }
+
+ protected function blockCodeContinue($Line, $Block)
+ {
+ if ($Line['indent'] >= 4) {
+ if (isset($Block['interrupted'])) {
+ $Block['element']['text']['text'] .= "\n";
+
+ unset($Block['interrupted']);
+ }
+
+ $Block['element']['text']['text'] .= "\n";
+
+ $text = substr($Line['body'], 4);
+
+ $Block['element']['text']['text'] .= $text;
+
+ return $Block;
+ }
+ }
+
+ protected function blockCodeComplete($Block)
+ {
+ $text = $Block['element']['text']['text'];
+
+ $Block['element']['text']['text'] = $text;
+
+ return $Block;
+ }
+
+ #
+ # Comment
+
+ protected function blockComment($Line)
+ {
+ if ($this->markupEscaped or $this->safeMode) {
+ return;
+ }
+
+ if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!') {
+ $Block = [
+ 'markup' => $Line['body'],
+ ];
+
+ if (preg_match('/-->$/', $Line['text'])) {
+ $Block['closed'] = true;
+ }
+
+ return $Block;
+ }
+ }
+
+ protected function blockCommentContinue($Line, array $Block)
+ {
+ if (isset($Block['closed'])) {
+ return;
+ }
+
+ $Block['markup'] .= "\n" . $Line['body'];
+
+ if (preg_match('/-->$/', $Line['text'])) {
+ $Block['closed'] = true;
+ }
+
+ return $Block;
+ }
+
+ #
+ # Fenced Code
+
+ protected function blockFencedCode($Line)
+ {
+ if (preg_match('/^[' . $Line['text'][0] . ']{3,}[ ]*([^`]+)?[ ]*$/', $Line['text'], $matches)) {
+ $Element = [
+ 'name' => 'code',
+ 'text' => '',
+ ];
+
+ if (isset($matches[1])) {
+ /**
+ * https://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes
+ * Every HTML element may have a class attribute specified.
+ * The attribute, if specified, must have a value that is a set
+ * of space-separated tokens representing the various classes
+ * that the element belongs to.
+ * [...]
+ * The space characters, for the purposes of this specification,
+ * are U+0020 SPACE, U+0009 CHARACTER TABULATION (tab),
+ * U+000A LINE FEED (LF), U+000C FORM FEED (FF), and
+ * U+000D CARRIAGE RETURN (CR).
+ */
+ $language = substr($matches[1], 0, strcspn($matches[1], " \t\n\f\r"));
+
+ $class = 'language-' . $language;
+
+ $Element['attributes'] = [
+ 'class' => $class,
+ ];
+ }
+
+ $Block = [
+ 'char' => $Line['text'][0],
+ 'element' => [
+ 'name' => 'pre',
+ 'handler' => 'element',
+ 'text' => $Element,
+ ],
+ ];
+
+ return $Block;
+ }
+ }
+
+ protected function blockFencedCodeContinue($Line, $Block)
+ {
+ if (isset($Block['complete'])) {
+ return;
+ }
+
+ if (isset($Block['interrupted'])) {
+ $Block['element']['text']['text'] .= "\n";
+
+ unset($Block['interrupted']);
+ }
+
+ if (preg_match('/^' . $Block['char'] . '{3,}[ ]*$/', $Line['text'])) {
+ $Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1);
+
+ $Block['complete'] = true;
+
+ return $Block;
+ }
+
+ $Block['element']['text']['text'] .= "\n" . $Line['body'];
+
+ return $Block;
+ }
+
+ protected function blockFencedCodeComplete($Block)
+ {
+ $text = $Block['element']['text']['text'];
+
+ $Block['element']['text']['text'] = $text;
+
+ return $Block;
+ }
+
+ #
+ # Header
+
+ protected function blockHeader($Line)
+ {
+ if (isset($Line['text'][1])) {
+ $level = 1;
+
+ while (isset($Line['text'][$level]) and $Line['text'][$level] === '#') {
+ $level++;
+ }
+
+ if ($level > 6) {
+ return;
+ }
+
+ $text = trim($Line['text'], '# ');
+
+ $Block = [
+ 'element' => [
+ 'name' => 'h' . min(6, $level),
+ 'text' => $text,
+ 'handler' => 'line',
+ ],
+ ];
+
+ return $Block;
+ }
+ }
+
+ #
+ # List
+
+ protected function blockList($Line)
+ {
+ [$name, $pattern] = $Line['text'][0] <= '-' ? ['ul', '[*+-]'] : ['ol', '[0-9]+[.]'];
+
+ if (preg_match('/^(' . $pattern . '[ ]+)(.*)/', $Line['text'], $matches)) {
+ $Block = [
+ 'indent' => $Line['indent'],
+ 'pattern' => $pattern,
+ 'element' => [
+ 'name' => $name,
+ 'handler' => 'elements',
+ ],
+ ];
+
+ if ($name === 'ol') {
+ $listStart = stristr($matches[0], '.', true);
+
+ if ($listStart !== '1') {
+ $Block['element']['attributes'] = ['start' => $listStart];
+ }
+ }
+
+ $Block['li'] = [
+ 'name' => 'li',
+ 'handler' => 'li',
+ 'text' => [
+ $matches[2],
+ ],
+ ];
+
+ $Block['element']['text'] [] = &$Block['li'];
+
+ return $Block;
+ }
+ }
+
+ protected function blockListContinue($Line, array $Block)
+ {
+ if ($Block['indent'] === $Line['indent'] and preg_match('/^' . $Block['pattern'] . '(?:[ ]+(.*)|$)/', $Line['text'], $matches)) {
+ if (isset($Block['interrupted'])) {
+ $Block['li']['text'] [] = '';
+
+ $Block['loose'] = true;
+
+ unset($Block['interrupted']);
+ }
+
+ unset($Block['li']);
+
+ $text = isset($matches[1]) ? $matches[1] : '';
+
+ $Block['li'] = [
+ 'name' => 'li',
+ 'handler' => 'li',
+ 'text' => [
+ $text,
+ ],
+ ];
+
+ $Block['element']['text'] [] = &$Block['li'];
+
+ return $Block;
+ }
+
+ if ($Line['text'][0] === '[' and $this->blockReference($Line)) {
+ return $Block;
+ }
+
+ if (!isset($Block['interrupted'])) {
+ $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
+
+ $Block['li']['text'] [] = $text;
+
+ return $Block;
+ }
+
+ if ($Line['indent'] > 0) {
+ $Block['li']['text'] [] = '';
+
+ $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
+
+ $Block['li']['text'] [] = $text;
+
+ unset($Block['interrupted']);
+
+ return $Block;
+ }
+ }
+
+ protected function blockListComplete(array $Block)
+ {
+ if (isset($Block['loose'])) {
+ foreach ($Block['element']['text'] as &$li) {
+ if (end($li['text']) !== '') {
+ $li['text'] [] = '';
+ }
+ }
+ }
+
+ return $Block;
+ }
+
+ #
+ # Quote
+
+ protected function blockQuote($Line)
+ {
+ if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) {
+ $Block = [
+ 'element' => [
+ 'name' => 'blockquote',
+ 'handler' => 'lines',
+ 'text' => (array)$matches[1],
+ ],
+ ];
+
+ return $Block;
+ }
+ }
+
+ protected function blockQuoteContinue($Line, array $Block)
+ {
+ if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) {
+ if (isset($Block['interrupted'])) {
+ $Block['element']['text'] [] = '';
+
+ unset($Block['interrupted']);
+ }
+
+ $Block['element']['text'] [] = $matches[1];
+
+ return $Block;
+ }
+
+ if (!isset($Block['interrupted'])) {
+ $Block['element']['text'] [] = $Line['text'];
+
+ return $Block;
+ }
+ }
+
+ #
+ # Rule
+
+ protected function blockRule($Line)
+ {
+ if (preg_match('/^([' . $Line['text'][0] . '])([ ]*\1){2,}[ ]*$/', $Line['text'])) {
+ $Block = [
+ 'element' => [
+ 'name' => 'hr',
+ ],
+ ];
+
+ return $Block;
+ }
+ }
+
+ #
+ # Setext
+
+ protected function blockSetextHeader($Line, array $Block = null)
+ {
+ if (!isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) {
+ return;
+ }
+
+ if (chop($Line['text'], $Line['text'][0]) === '') {
+ $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2';
+
+ return $Block;
+ }
+ }
+
+ #
+ # Markup
+
+ protected function blockMarkup($Line)
+ {
+ if ($this->markupEscaped or $this->safeMode) {
+ return;
+ }
+
+ if (preg_match('/^<(\w[\w-]*)(?:[ ]*' . $this->regexHtmlAttribute . ')*[ ]*(\/)?>/', $Line['text'], $matches)) {
+ $element = strtolower($matches[1]);
+
+ if (in_array($element, $this->textLevelElements)) {
+ return;
+ }
+
+ $Block = [
+ 'name' => $matches[1],
+ 'depth' => 0,
+ 'markup' => $Line['text'],
+ ];
+
+ $length = strlen($matches[0]);
+
+ $remainder = substr($Line['text'], $length);
+
+ if (trim($remainder) === '') {
+ if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) {
+ $Block['closed'] = true;
+
+ $Block['void'] = true;
+ }
+ } else {
+ if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) {
+ return;
+ }
+
+ if (preg_match('/<\/' . $matches[1] . '>[ ]*$/i', $remainder)) {
+ $Block['closed'] = true;
+ }
+ }
+
+ return $Block;
+ }
+ }
+
+ protected function blockMarkupContinue($Line, array $Block)
+ {
+ if (isset($Block['closed'])) {
+ return;
+ }
+
+ if (preg_match('/^<' . $Block['name'] . '(?:[ ]*' . $this->regexHtmlAttribute . ')*[ ]*>/i', $Line['text'])) # open
+ {
+ $Block['depth']++;
+ }
+
+ if (preg_match('/(.*?)<\/' . $Block['name'] . '>[ ]*$/i', $Line['text'], $matches)) # close
+ {
+ if ($Block['depth'] > 0) {
+ $Block['depth']--;
+ } else {
+ $Block['closed'] = true;
+ }
+ }
+
+ if (isset($Block['interrupted'])) {
+ $Block['markup'] .= "\n";
+
+ unset($Block['interrupted']);
+ }
+
+ $Block['markup'] .= "\n" . $Line['body'];
+
+ return $Block;
+ }
+
+ #
+ # Reference
+
+ protected function blockReference($Line)
+ {
+ if (preg_match('/^\[(.+?)\]:[ ]*(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches)) {
+ $id = strtolower($matches[1]);
+
+ $Data = [
+ 'url' => $matches[2],
+ 'title' => null,
+ ];
+
+ if (isset($matches[3])) {
+ $Data['title'] = $matches[3];
+ }
+
+ $this->DefinitionData['Reference'][$id] = $Data;
+
+ $Block = [
+ 'hidden' => true,
+ ];
+
+ return $Block;
+ }
+ }
+
+ #
+ # Table
+
+ protected function blockTable($Line, array $Block = null)
+ {
+ if (!isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) {
+ return;
+ }
+
+ if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '') {
+ $alignments = [];
+
+ $divider = $Line['text'];
+
+ $divider = trim($divider);
+ $divider = trim($divider, '|');
+
+ $dividerCells = explode('|', $divider);
+
+ foreach ($dividerCells as $dividerCell) {
+ $dividerCell = trim($dividerCell);
+
+ if ($dividerCell === '') {
+ continue;
+ }
+
+ $alignment = null;
+
+ if ($dividerCell[0] === ':') {
+ $alignment = 'left';
+ }
+
+ if (substr($dividerCell, -1) === ':') {
+ $alignment = $alignment === 'left' ? 'center' : 'right';
+ }
+
+ $alignments [] = $alignment;
+ }
+
+ # ~
+
+ $HeaderElements = [];
+
+ $header = $Block['element']['text'];
+
+ $header = trim($header);
+ $header = trim($header, '|');
+
+ $headerCells = explode('|', $header);
+
+ foreach ($headerCells as $index => $headerCell) {
+ $headerCell = trim($headerCell);
+
+ $HeaderElement = [
+ 'name' => 'th',
+ 'text' => $headerCell,
+ 'handler' => 'line',
+ ];
+
+ if (isset($alignments[$index])) {
+ $alignment = $alignments[$index];
+
+ $HeaderElement['attributes'] = [
+ 'style' => 'text-align: ' . $alignment . ';',
+ ];
+ }
+
+ $HeaderElements [] = $HeaderElement;
+ }
+
+ # ~
+
+ $Block = [
+ 'alignments' => $alignments,
+ 'identified' => true,
+ 'element' => [
+ 'name' => 'table',
+ 'handler' => 'elements',
+ ],
+ ];
+
+ $Block['element']['text'] [] = [
+ 'name' => 'thead',
+ 'handler' => 'elements',
+ ];
+
+ $Block['element']['text'] [] = [
+ 'name' => 'tbody',
+ 'handler' => 'elements',
+ 'text' => [],
+ ];
+
+ $Block['element']['text'][0]['text'] [] = [
+ 'name' => 'tr',
+ 'handler' => 'elements',
+ 'text' => $HeaderElements,
+ ];
+
+ return $Block;
+ }
+ }
+
+ protected function blockTableContinue($Line, array $Block)
+ {
+ if (isset($Block['interrupted'])) {
+ return;
+ }
+
+ if ($Line['text'][0] === '|' or strpos($Line['text'], '|')) {
+ $Elements = [];
+
+ $row = $Line['text'];
+
+ $row = trim($row);
+ $row = trim($row, '|');
+
+ preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches);
+
+ foreach ($matches[0] as $index => $cell) {
+ $cell = trim($cell);
+
+ $Element = [
+ 'name' => 'td',
+ 'handler' => 'line',
+ 'text' => $cell,
+ ];
+
+ if (isset($Block['alignments'][$index])) {
+ $Element['attributes'] = [
+ 'style' => 'text-align: ' . $Block['alignments'][$index] . ';',
+ ];
+ }
+
+ $Elements [] = $Element;
+ }
+
+ $Element = [
+ 'name' => 'tr',
+ 'handler' => 'elements',
+ 'text' => $Elements,
+ ];
+
+ $Block['element']['text'][1]['text'] [] = $Element;
+
+ return $Block;
+ }
+ }
+
+ #
+ # ~
+ #
+
+ protected function paragraph($Line)
+ {
+ $Block = [
+ 'element' => [
+ 'name' => 'p',
+ 'text' => $Line['text'],
+ 'handler' => 'line',
+ ],
+ ];
+
+ return $Block;
+ }
+
+ #
+ # Inline Elements
+ #
+
+ protected $InlineTypes = [
+ '"' => ['SpecialCharacter'],
+ '!' => ['Image'],
+ '&' => ['SpecialCharacter'],
+ '*' => ['Emphasis'],
+ ':' => ['Url'],
+ '<' => ['UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'],
+ '>' => ['SpecialCharacter'],
+ '[' => ['Link'],
+ '_' => ['Emphasis'],
+ '`' => ['Code'],
+ '~' => ['Strikethrough'],
+ '\\' => ['EscapeSequence'],
+ ];
+
+ # ~
+
+ protected $inlineMarkerList = '!"*_&[:<>`~\\';
+
+ #
+ # ~
+ #
+
+ public function line($text, $nonNestables = [])
+ {
+ $markup = '';
+
+ # $excerpt is based on the first occurrence of a marker
+
+ while ($excerpt = strpbrk($text, $this->inlineMarkerList)) {
+ $marker = $excerpt[0];
+
+ $markerPosition = strpos($text, $marker);
+
+ $Excerpt = ['text' => $excerpt, 'context' => $text];
+
+ foreach ($this->InlineTypes[$marker] as $inlineType) {
+ # check to see if the current inline type is nestable in the current context
+
+ if (!empty($nonNestables) and in_array($inlineType, $nonNestables)) {
+ continue;
+ }
+
+ $Inline = $this->{'inline' . $inlineType}($Excerpt);
+
+ if (!isset($Inline)) {
+ continue;
+ }
+
+ # makes sure that the inline belongs to "our" marker
+
+ if (isset($Inline['position']) and $Inline['position'] > $markerPosition) {
+ continue;
+ }
+
+ # sets a default inline position
+
+ if (!isset($Inline['position'])) {
+ $Inline['position'] = $markerPosition;
+ }
+
+ # cause the new element to 'inherit' our non nestables
+
+ foreach ($nonNestables as $non_nestable) {
+ $Inline['element']['nonNestables'][] = $non_nestable;
+ }
+
+ # the text that comes before the inline
+ $unmarkedText = substr($text, 0, $Inline['position']);
+
+ # compile the unmarked text
+ $markup .= $this->unmarkedText($unmarkedText);
+
+ # compile the inline
+ $markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']);
+
+ # remove the examined text
+ $text = substr($text, $Inline['position'] + $Inline['extent']);
+
+ continue 2;
+ }
+
+ # the marker does not belong to an inline
+
+ $unmarkedText = substr($text, 0, $markerPosition + 1);
+
+ $markup .= $this->unmarkedText($unmarkedText);
+
+ $text = substr($text, $markerPosition + 1);
+ }
+
+ $markup .= $this->unmarkedText($text);
+
+ return $markup;
+ }
+
+ #
+ # ~
+ #
+
+ protected function inlineCode($Excerpt)
+ {
+ $marker = $Excerpt['text'][0];
+
+ if (preg_match('/^(' . $marker . '+)[ ]*(.+?)[ ]*(? strlen($matches[0]),
+ 'element' => [
+ 'name' => 'code',
+ 'text' => $text,
+ ],
+ ];
+ }
+ }
+
+ protected function inlineEmailTag($Excerpt)
+ {
+ if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches)) {
+ $url = $matches[1];
+
+ if (!isset($matches[2])) {
+ $url = 'mailto:' . $url;
+ }
+
+ return [
+ 'extent' => strlen($matches[0]),
+ 'element' => [
+ 'name' => 'a',
+ 'text' => $matches[1],
+ 'attributes' => [
+ 'href' => $url,
+ ],
+ ],
+ ];
+ }
+ }
+
+ protected function inlineEmphasis($Excerpt)
+ {
+ if (!isset($Excerpt['text'][1])) {
+ return;
+ }
+
+ $marker = $Excerpt['text'][0];
+
+ if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches)) {
+ $emphasis = 'strong';
+ } elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches)) {
+ $emphasis = 'em';
+ } else {
+ return;
+ }
+
+ return [
+ 'extent' => strlen($matches[0]),
+ 'element' => [
+ 'name' => $emphasis,
+ 'handler' => 'line',
+ 'text' => $matches[1],
+ ],
+ ];
+ }
+
+ protected function inlineEscapeSequence($Excerpt)
+ {
+ if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters)) {
+ return [
+ 'markup' => $Excerpt['text'][1],
+ 'extent' => 2,
+ ];
+ }
+ }
+
+ protected function inlineImage($Excerpt)
+ {
+ if (!isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[') {
+ return;
+ }
+
+ $Excerpt['text'] = substr($Excerpt['text'], 1);
+
+ $Link = $this->inlineLink($Excerpt);
+
+ if ($Link === null) {
+ return;
+ }
+
+ $Inline = [
+ 'extent' => $Link['extent'] + 1,
+ 'element' => [
+ 'name' => 'img',
+ 'attributes' => [
+ 'src' => $Link['element']['attributes']['href'],
+ 'alt' => $Link['element']['text'],
+ ],
+ ],
+ ];
+
+ $Inline['element']['attributes'] += $Link['element']['attributes'];
+
+ unset($Inline['element']['attributes']['href']);
+
+ return $Inline;
+ }
+
+ protected function inlineLink($Excerpt)
+ {
+ $Element = [
+ 'name' => 'a',
+ 'handler' => 'line',
+ 'nonNestables' => ['Url', 'Link'],
+ 'text' => null,
+ 'attributes' => [
+ 'href' => null,
+ 'title' => null,
+ ],
+ ];
+
+ $extent = 0;
+
+ $remainder = $Excerpt['text'];
+
+ if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches)) {
+ $Element['text'] = $matches[1];
+
+ $extent += strlen($matches[0]);
+
+ $remainder = substr($remainder, $extent);
+ } else {
+ return;
+ }
+
+ if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*"|\'[^\']*\'))?\s*[)]/', $remainder, $matches)) {
+ $Element['attributes']['href'] = $matches[1];
+
+ if (isset($matches[2])) {
+ $Element['attributes']['title'] = substr($matches[2], 1, -1);
+ }
+
+ $extent += strlen($matches[0]);
+ } else {
+ if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches)) {
+ $definition = strlen($matches[1]) ? $matches[1] : $Element['text'];
+ $definition = strtolower($definition);
+
+ $extent += strlen($matches[0]);
+ } else {
+ $definition = strtolower($Element['text']);
+ }
+
+ if (!isset($this->DefinitionData['Reference'][$definition])) {
+ return;
+ }
+
+ $Definition = $this->DefinitionData['Reference'][$definition];
+
+ $Element['attributes']['href'] = $Definition['url'];
+ $Element['attributes']['title'] = $Definition['title'];
+ }
+
+ return [
+ 'extent' => $extent,
+ 'element' => $Element,
+ ];
+ }
+
+ protected function inlineMarkup($Excerpt)
+ {
+ if ($this->markupEscaped or $this->safeMode or strpos($Excerpt['text'], '>') === false) {
+ return;
+ }
+
+ if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w[\w-]*[ ]*>/s', $Excerpt['text'], $matches)) {
+ return [
+ 'markup' => $matches[0],
+ 'extent' => strlen($matches[0]),
+ ];
+ }
+
+ if ($Excerpt['text'][1] === '!' and preg_match('/^/s', $Excerpt['text'], $matches)) {
+ return [
+ 'markup' => $matches[0],
+ 'extent' => strlen($matches[0]),
+ ];
+ }
+
+ if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w[\w-]*(?:[ ]*' . $this->regexHtmlAttribute . ')*[ ]*\/?>/s', $Excerpt['text'], $matches)) {
+ return [
+ 'markup' => $matches[0],
+ 'extent' => strlen($matches[0]),
+ ];
+ }
+ }
+
+ protected function inlineSpecialCharacter($Excerpt)
+ {
+ if ($Excerpt['text'][0] === '&' and !preg_match('/^?\w+;/', $Excerpt['text'])) {
+ return [
+ 'markup' => '&',
+ 'extent' => 1,
+ ];
+ }
+
+ $SpecialCharacter = ['>' => 'gt', '<' => 'lt', '"' => 'quot'];
+
+ if (isset($SpecialCharacter[$Excerpt['text'][0]])) {
+ return [
+ 'markup' => '&' . $SpecialCharacter[$Excerpt['text'][0]] . ';',
+ 'extent' => 1,
+ ];
+ }
+ }
+
+ protected function inlineStrikethrough($Excerpt)
+ {
+ if (!isset($Excerpt['text'][1])) {
+ return;
+ }
+
+ if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches)) {
+ return [
+ 'extent' => strlen($matches[0]),
+ 'element' => [
+ 'name' => 'del',
+ 'text' => $matches[1],
+ 'handler' => 'line',
+ ],
+ ];
+ }
+ }
+
+ protected function inlineUrl($Excerpt)
+ {
+ if ($this->urlsLinked !== true or !isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/') {
+ return;
+ }
+
+ if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)) {
+ $url = $matches[0][0];
+
+ $Inline = [
+ 'extent' => strlen($matches[0][0]),
+ 'position' => $matches[0][1],
+ 'element' => [
+ 'name' => 'a',
+ 'text' => $url,
+ 'attributes' => [
+ 'href' => $url,
+ ],
+ ],
+ ];
+
+ return $Inline;
+ }
+ }
+
+ protected function inlineUrlTag($Excerpt)
+ {
+ if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches)) {
+ $url = $matches[1];
+
+ return [
+ 'extent' => strlen($matches[0]),
+ 'element' => [
+ 'name' => 'a',
+ 'text' => $url,
+ 'attributes' => [
+ 'href' => $url,
+ ],
+ ],
+ ];
+ }
+ }
+
+ # ~
+
+ protected function unmarkedText($text)
+ {
+ if ($this->breaksEnabled) {
+ $text = preg_replace('/[ ]*\n/', "
\n", $text);
+ } else {
+ $text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "
\n", $text);
+ $text = str_replace(" \n", "\n", $text);
+ }
+
+ return $text;
+ }
+
+ #
+ # Handlers
+ #
+
+ protected function element(array $Element)
+ {
+ if ($this->safeMode) {
+ $Element = $this->sanitiseElement($Element);
+ }
+
+ $markup = '<' . $Element['name'];
+
+ if (isset($Element['attributes'])) {
+ foreach ($Element['attributes'] as $name => $value) {
+ if ($value === null) {
+ continue;
+ }
+
+ $markup .= ' ' . $name . '="' . self::escape($value) . '"';
+ }
+ }
+
+ $permitRawHtml = false;
+
+ if (isset($Element['text'])) {
+ $text = $Element['text'];
+ }
+ // very strongly consider an alternative if you're writing an
+ // extension
+ elseif (isset($Element['rawHtml'])) {
+ $text = $Element['rawHtml'];
+ $allowRawHtmlInSafeMode = isset($Element['allowRawHtmlInSafeMode']) && $Element['allowRawHtmlInSafeMode'];
+ $permitRawHtml = !$this->safeMode || $allowRawHtmlInSafeMode;
+ }
+
+ if (isset($text)) {
+ $markup .= '>';
+
+ if (!isset($Element['nonNestables'])) {
+ $Element['nonNestables'] = [];
+ }
+
+ if (isset($Element['handler'])) {
+ $markup .= $this->{$Element['handler']}($text, $Element['nonNestables']);
+ } elseif (!$permitRawHtml) {
+ $markup .= self::escape($text, true);
+ } else {
+ $markup .= $text;
+ }
+
+ $markup .= '' . $Element['name'] . '>';
+ } else {
+ $markup .= ' />';
+ }
+
+ return $markup;
+ }
+
+ protected function elements(array $Elements)
+ {
+ $markup = '';
+
+ foreach ($Elements as $Element) {
+ $markup .= "\n" . $this->element($Element);
+ }
+
+ $markup .= "\n";
+
+ return $markup;
+ }
+
+ # ~
+
+ protected function li($lines)
+ {
+ $markup = $this->lines($lines);
+
+ $trimmedMarkup = trim($markup);
+
+ if (!in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '
') { + $markup = $trimmedMarkup; + $markup = substr($markup, 3); + + $position = strpos($markup, "
"); + + $markup = substr_replace($markup, '', $position, 4); + } + + return $markup; + } + + # + # Deprecated Methods + # + + function parse($text) + { + $markup = $this->text($text); + + return $markup; + } + + protected function sanitiseElement(array $Element) + { + static $goodAttribute = '/^[a-zA-Z0-9][a-zA-Z0-9-_]*+$/'; + static $safeUrlNameToAtt = [ + 'a' => 'href', + 'img' => 'src', + ]; + + if (isset($safeUrlNameToAtt[$Element['name']])) { + $Element = $this->filterUnsafeUrlInAttribute($Element, $safeUrlNameToAtt[$Element['name']]); + } + + if (!empty($Element['attributes'])) { + foreach ($Element['attributes'] as $att => $val) { + # filter out badly parsed attribute + if (!preg_match($goodAttribute, $att)) { + unset($Element['attributes'][$att]); + } # dump onevent attribute + elseif (self::striAtStart($att, 'on')) { + unset($Element['attributes'][$att]); + } + } + } + + return $Element; + } + + protected function filterUnsafeUrlInAttribute(array $Element, $attribute) + { + foreach ($this->safeLinksWhitelist as $scheme) { + if (self::striAtStart($Element['attributes'][$attribute], $scheme)) { + return $Element; + } + } + + $Element['attributes'][$attribute] = str_replace(':', '%3A', $Element['attributes'][$attribute]); + + return $Element; + } + + # + # Static Methods + # + + protected static function escape($text, $allowQuotes = false) + { + return htmlspecialchars($text, $allowQuotes ? ENT_NOQUOTES : ENT_QUOTES, 'UTF-8'); + } + + protected static function striAtStart($string, $needle) + { + $len = strlen($needle); + + if ($len > strlen($string)) { + return false; + } else { + return strtolower(substr($string, 0, $len)) === strtolower($needle); + } + } + + static function instance($name = 'default') + { + if (isset(self::$instances[$name])) { + return self::$instances[$name]; + } + + $instance = new static(); + + self::$instances[$name] = $instance; + + return $instance; + } + + private static $instances = []; + + # + # Fields + # + + protected $DefinitionData; + + # + # Read-Only + + protected $specialCharacters = [ + '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|', + ]; + + protected $StrongRegex = [ + '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s', + '_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us', + ]; + + protected $EmRegex = [ + '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s', + '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us', + ]; + + protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?'; + + protected $voidElements = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source']; + + protected $textLevelElements = [ + 'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont', + 'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing', + 'i', 'rp', 'del', 'code', 'strike', 'marquee', + 'q', 'rt', 'ins', 'font', 'strong', + 's', 'tt', 'kbd', 'mark', + 'u', 'xm', 'sub', 'nobr', + 'sup', 'ruby', + 'var', 'span', + 'wbr', 'time', + ]; +} \ No newline at end of file diff --git a/vendor/zoujingli/think-library/src/service/ModuleService.php b/vendor/zoujingli/think-library/src/service/ModuleService.php index 670f747ba..15d8f2a82 100644 --- a/vendor/zoujingli/think-library/src/service/ModuleService.php +++ b/vendor/zoujingli/think-library/src/service/ModuleService.php @@ -16,6 +16,7 @@ namespace think\admin\service; use think\admin\extend\HttpExtend; +use think\admin\extend\Parsedown; use think\admin\Service; /** @@ -104,9 +105,12 @@ class ModuleService extends Service public function getModules(array $data = []): array { foreach (NodeService::instance()->getModules() as $name) { - if (is_array($version = $this->getModuleVersion($name))) { - if (preg_match('|^\d{4}\.\d{2}\.\d{2}\.\d{2}$|', $version['version'])) { - $data[$name] = $version; + if (is_array($vars = $this->getModuleVersion($name)) && isset($vars['version'])) { + if (preg_match('|^\d{4}\.\d{2}\.\d{2}\.\d{2}$|', $vars['version'])) { + $data[$name] = $vars; + foreach (glob("{$this->app->getBasePath()}{$name}/module/change/*.md") as $file) { + $data[$name]['change'][pathinfo($file, PATHINFO_FILENAME)] = Parsedown::instance()->parse(file_get_contents($file)); + } } } } @@ -153,7 +157,7 @@ class ModuleService extends Service */ private function getModuleVersion($name) { - $file = $this->app->getBasePath() . $name . DIRECTORY_SEPARATOR . 'ver.php'; + $file = "{$this->app->getBasePath()}{$name}/module/version.php"; if (file_exists($file) && is_file($file) && is_array($vars = @include $file)) { return isset($vars['name']) && isset($vars['version']) ? $vars : null; } else {